github.com/muhammadn/cortex@v1.9.1-0.20220510110439-46bb7000d03d/pkg/configs/legacy_promql/parse_test.go (about)

     1  // Copyright 2015 The Prometheus Authors
     2  // Licensed under the Apache License, Version 2.0 (the "License");
     3  // you may not use this file except in compliance with the License.
     4  // You may obtain a copy of the License at
     5  //
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package promql
    15  
    16  import (
    17  	"fmt"
    18  	"math"
    19  	"reflect"
    20  	"strings"
    21  	"testing"
    22  	"time"
    23  
    24  	"github.com/prometheus/common/model"
    25  	"github.com/prometheus/prometheus/pkg/labels"
    26  	"github.com/stretchr/testify/require"
    27  )
    28  
    29  var testExpr = []struct {
    30  	input    string // The input to be parsed.
    31  	expected Expr   // The expected expression AST.
    32  	fail     bool   // Whether parsing is supposed to fail.
    33  	errMsg   string // If not empty the parsing error has to contain this string.
    34  }{
    35  	// Scalars and scalar-to-scalar operations.
    36  	{
    37  		input:    "1",
    38  		expected: &NumberLiteral{1},
    39  	}, {
    40  		input:    "+Inf",
    41  		expected: &NumberLiteral{math.Inf(1)},
    42  	}, {
    43  		input:    "-Inf",
    44  		expected: &NumberLiteral{math.Inf(-1)},
    45  	}, {
    46  		input:    ".5",
    47  		expected: &NumberLiteral{0.5},
    48  	}, {
    49  		input:    "5.",
    50  		expected: &NumberLiteral{5},
    51  	}, {
    52  		input:    "123.4567",
    53  		expected: &NumberLiteral{123.4567},
    54  	}, {
    55  		input:    "5e-3",
    56  		expected: &NumberLiteral{0.005},
    57  	}, {
    58  		input:    "5e3",
    59  		expected: &NumberLiteral{5000},
    60  	}, {
    61  		input:    "0xc",
    62  		expected: &NumberLiteral{12},
    63  	}, {
    64  		input:    "0755",
    65  		expected: &NumberLiteral{493},
    66  	}, {
    67  		input:    "+5.5e-3",
    68  		expected: &NumberLiteral{0.0055},
    69  	}, {
    70  		input:    "-0755",
    71  		expected: &NumberLiteral{-493},
    72  	}, {
    73  		input:    "1 + 1",
    74  		expected: &BinaryExpr{itemADD, &NumberLiteral{1}, &NumberLiteral{1}, nil, false},
    75  	}, {
    76  		input:    "1 - 1",
    77  		expected: &BinaryExpr{itemSUB, &NumberLiteral{1}, &NumberLiteral{1}, nil, false},
    78  	}, {
    79  		input:    "1 * 1",
    80  		expected: &BinaryExpr{itemMUL, &NumberLiteral{1}, &NumberLiteral{1}, nil, false},
    81  	}, {
    82  		input:    "1 % 1",
    83  		expected: &BinaryExpr{itemMOD, &NumberLiteral{1}, &NumberLiteral{1}, nil, false},
    84  	}, {
    85  		input:    "1 / 1",
    86  		expected: &BinaryExpr{itemDIV, &NumberLiteral{1}, &NumberLiteral{1}, nil, false},
    87  	}, {
    88  		input:    "1 == bool 1",
    89  		expected: &BinaryExpr{itemEQL, &NumberLiteral{1}, &NumberLiteral{1}, nil, true},
    90  	}, {
    91  		input:    "1 != bool 1",
    92  		expected: &BinaryExpr{itemNEQ, &NumberLiteral{1}, &NumberLiteral{1}, nil, true},
    93  	}, {
    94  		input:    "1 > bool 1",
    95  		expected: &BinaryExpr{itemGTR, &NumberLiteral{1}, &NumberLiteral{1}, nil, true},
    96  	}, {
    97  		input:    "1 >= bool 1",
    98  		expected: &BinaryExpr{itemGTE, &NumberLiteral{1}, &NumberLiteral{1}, nil, true},
    99  	}, {
   100  		input:    "1 < bool 1",
   101  		expected: &BinaryExpr{itemLSS, &NumberLiteral{1}, &NumberLiteral{1}, nil, true},
   102  	}, {
   103  		input:    "1 <= bool 1",
   104  		expected: &BinaryExpr{itemLTE, &NumberLiteral{1}, &NumberLiteral{1}, nil, true},
   105  	}, {
   106  		input: "+1 + -2 * 1",
   107  		expected: &BinaryExpr{
   108  			Op:  itemADD,
   109  			LHS: &NumberLiteral{1},
   110  			RHS: &BinaryExpr{
   111  				Op: itemMUL, LHS: &NumberLiteral{-2}, RHS: &NumberLiteral{1},
   112  			},
   113  		},
   114  	}, {
   115  		input: "1 + 2/(3*1)",
   116  		expected: &BinaryExpr{
   117  			Op:  itemADD,
   118  			LHS: &NumberLiteral{1},
   119  			RHS: &BinaryExpr{
   120  				Op:  itemDIV,
   121  				LHS: &NumberLiteral{2},
   122  				RHS: &ParenExpr{&BinaryExpr{
   123  					Op: itemMUL, LHS: &NumberLiteral{3}, RHS: &NumberLiteral{1},
   124  				}},
   125  			},
   126  		},
   127  	}, {
   128  		input: "1 < bool 2 - 1 * 2",
   129  		expected: &BinaryExpr{
   130  			Op:         itemLSS,
   131  			ReturnBool: true,
   132  			LHS:        &NumberLiteral{1},
   133  			RHS: &BinaryExpr{
   134  				Op:  itemSUB,
   135  				LHS: &NumberLiteral{2},
   136  				RHS: &BinaryExpr{
   137  					Op: itemMUL, LHS: &NumberLiteral{1}, RHS: &NumberLiteral{2},
   138  				},
   139  			},
   140  		},
   141  	}, {
   142  		input: "-some_metric",
   143  		expected: &UnaryExpr{
   144  			Op: itemSUB,
   145  			Expr: &VectorSelector{
   146  				Name: "some_metric",
   147  				LabelMatchers: []*labels.Matcher{
   148  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "some_metric"),
   149  				},
   150  			},
   151  		},
   152  	}, {
   153  		input: "+some_metric",
   154  		expected: &UnaryExpr{
   155  			Op: itemADD,
   156  			Expr: &VectorSelector{
   157  				Name: "some_metric",
   158  				LabelMatchers: []*labels.Matcher{
   159  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "some_metric"),
   160  				},
   161  			},
   162  		},
   163  	}, {
   164  		input:  "",
   165  		fail:   true,
   166  		errMsg: "no expression found in input",
   167  	}, {
   168  		input:  "# just a comment\n\n",
   169  		fail:   true,
   170  		errMsg: "no expression found in input",
   171  	}, {
   172  		input:  "1+",
   173  		fail:   true,
   174  		errMsg: "no valid expression found",
   175  	}, {
   176  		input:  ".",
   177  		fail:   true,
   178  		errMsg: "unexpected character: '.'",
   179  	}, {
   180  		input:  "2.5.",
   181  		fail:   true,
   182  		errMsg: "could not parse remaining input \".\"...",
   183  	}, {
   184  		input:  "100..4",
   185  		fail:   true,
   186  		errMsg: "could not parse remaining input \".4\"...",
   187  	}, {
   188  		input:  "0deadbeef",
   189  		fail:   true,
   190  		errMsg: "bad number or duration syntax: \"0de\"",
   191  	}, {
   192  		input:  "1 /",
   193  		fail:   true,
   194  		errMsg: "no valid expression found",
   195  	}, {
   196  		input:  "*1",
   197  		fail:   true,
   198  		errMsg: "no valid expression found",
   199  	}, {
   200  		input:  "(1))",
   201  		fail:   true,
   202  		errMsg: "could not parse remaining input \")\"...",
   203  	}, {
   204  		input:  "((1)",
   205  		fail:   true,
   206  		errMsg: "unclosed left parenthesis",
   207  	}, {
   208  		input:  "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999",
   209  		fail:   true,
   210  		errMsg: "out of range",
   211  	}, {
   212  		input:  "(",
   213  		fail:   true,
   214  		errMsg: "unclosed left parenthesis",
   215  	}, {
   216  		input:  "1 and 1",
   217  		fail:   true,
   218  		errMsg: "set operator \"and\" not allowed in binary scalar expression",
   219  	}, {
   220  		input:  "1 == 1",
   221  		fail:   true,
   222  		errMsg: "parse error at char 7: comparisons between scalars must use BOOL modifier",
   223  	}, {
   224  		input:  "1 or 1",
   225  		fail:   true,
   226  		errMsg: "set operator \"or\" not allowed in binary scalar expression",
   227  	}, {
   228  		input:  "1 unless 1",
   229  		fail:   true,
   230  		errMsg: "set operator \"unless\" not allowed in binary scalar expression",
   231  	}, {
   232  		input:  "1 !~ 1",
   233  		fail:   true,
   234  		errMsg: "could not parse remaining input \"!~ 1\"...",
   235  	}, {
   236  		input:  "1 =~ 1",
   237  		fail:   true,
   238  		errMsg: "could not parse remaining input \"=~ 1\"...",
   239  	}, {
   240  		input:  `-"string"`,
   241  		fail:   true,
   242  		errMsg: `unary expression only allowed on expressions of type scalar or instant vector, got "string"`,
   243  	}, {
   244  		input:  `-test[5m]`,
   245  		fail:   true,
   246  		errMsg: `unary expression only allowed on expressions of type scalar or instant vector, got "range vector"`,
   247  	}, {
   248  		input:  `*test`,
   249  		fail:   true,
   250  		errMsg: "no valid expression found",
   251  	}, {
   252  		input:  "1 offset 1d",
   253  		fail:   true,
   254  		errMsg: "offset modifier must be preceded by an instant or range selector",
   255  	}, {
   256  		input:  "a - on(b) ignoring(c) d",
   257  		fail:   true,
   258  		errMsg: "parse error at char 11: no valid expression found",
   259  	},
   260  	// Vector binary operations.
   261  	{
   262  		input: "foo * bar",
   263  		expected: &BinaryExpr{
   264  			Op: itemMUL,
   265  			LHS: &VectorSelector{
   266  				Name: "foo",
   267  				LabelMatchers: []*labels.Matcher{
   268  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "foo"),
   269  				},
   270  			},
   271  			RHS: &VectorSelector{
   272  				Name: "bar",
   273  				LabelMatchers: []*labels.Matcher{
   274  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "bar"),
   275  				},
   276  			},
   277  			VectorMatching: &VectorMatching{Card: CardOneToOne},
   278  		},
   279  	}, {
   280  		input: "foo == 1",
   281  		expected: &BinaryExpr{
   282  			Op: itemEQL,
   283  			LHS: &VectorSelector{
   284  				Name: "foo",
   285  				LabelMatchers: []*labels.Matcher{
   286  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "foo"),
   287  				},
   288  			},
   289  			RHS: &NumberLiteral{1},
   290  		},
   291  	}, {
   292  		input: "foo == bool 1",
   293  		expected: &BinaryExpr{
   294  			Op: itemEQL,
   295  			LHS: &VectorSelector{
   296  				Name: "foo",
   297  				LabelMatchers: []*labels.Matcher{
   298  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "foo"),
   299  				},
   300  			},
   301  			RHS:        &NumberLiteral{1},
   302  			ReturnBool: true,
   303  		},
   304  	}, {
   305  		input: "2.5 / bar",
   306  		expected: &BinaryExpr{
   307  			Op:  itemDIV,
   308  			LHS: &NumberLiteral{2.5},
   309  			RHS: &VectorSelector{
   310  				Name: "bar",
   311  				LabelMatchers: []*labels.Matcher{
   312  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "bar"),
   313  				},
   314  			},
   315  		},
   316  	}, {
   317  		input: "foo and bar",
   318  		expected: &BinaryExpr{
   319  			Op: itemLAND,
   320  			LHS: &VectorSelector{
   321  				Name: "foo",
   322  				LabelMatchers: []*labels.Matcher{
   323  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "foo"),
   324  				},
   325  			},
   326  			RHS: &VectorSelector{
   327  				Name: "bar",
   328  				LabelMatchers: []*labels.Matcher{
   329  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "bar"),
   330  				},
   331  			},
   332  			VectorMatching: &VectorMatching{Card: CardManyToMany},
   333  		},
   334  	}, {
   335  		input: "foo or bar",
   336  		expected: &BinaryExpr{
   337  			Op: itemLOR,
   338  			LHS: &VectorSelector{
   339  				Name: "foo",
   340  				LabelMatchers: []*labels.Matcher{
   341  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "foo"),
   342  				},
   343  			},
   344  			RHS: &VectorSelector{
   345  				Name: "bar",
   346  				LabelMatchers: []*labels.Matcher{
   347  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "bar"),
   348  				},
   349  			},
   350  			VectorMatching: &VectorMatching{Card: CardManyToMany},
   351  		},
   352  	}, {
   353  		input: "foo unless bar",
   354  		expected: &BinaryExpr{
   355  			Op: itemLUnless,
   356  			LHS: &VectorSelector{
   357  				Name: "foo",
   358  				LabelMatchers: []*labels.Matcher{
   359  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "foo"),
   360  				},
   361  			},
   362  			RHS: &VectorSelector{
   363  				Name: "bar",
   364  				LabelMatchers: []*labels.Matcher{
   365  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "bar"),
   366  				},
   367  			},
   368  			VectorMatching: &VectorMatching{Card: CardManyToMany},
   369  		},
   370  	}, {
   371  		// Test and/or precedence and reassigning of operands.
   372  		input: "foo + bar or bla and blub",
   373  		expected: &BinaryExpr{
   374  			Op: itemLOR,
   375  			LHS: &BinaryExpr{
   376  				Op: itemADD,
   377  				LHS: &VectorSelector{
   378  					Name: "foo",
   379  					LabelMatchers: []*labels.Matcher{
   380  						mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "foo"),
   381  					},
   382  				},
   383  				RHS: &VectorSelector{
   384  					Name: "bar",
   385  					LabelMatchers: []*labels.Matcher{
   386  						mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "bar"),
   387  					},
   388  				},
   389  				VectorMatching: &VectorMatching{Card: CardOneToOne},
   390  			},
   391  			RHS: &BinaryExpr{
   392  				Op: itemLAND,
   393  				LHS: &VectorSelector{
   394  					Name: "bla",
   395  					LabelMatchers: []*labels.Matcher{
   396  						mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "bla"),
   397  					},
   398  				},
   399  				RHS: &VectorSelector{
   400  					Name: "blub",
   401  					LabelMatchers: []*labels.Matcher{
   402  						mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "blub"),
   403  					},
   404  				},
   405  				VectorMatching: &VectorMatching{Card: CardManyToMany},
   406  			},
   407  			VectorMatching: &VectorMatching{Card: CardManyToMany},
   408  		},
   409  	}, {
   410  		// Test and/or/unless precedence.
   411  		input: "foo and bar unless baz or qux",
   412  		expected: &BinaryExpr{
   413  			Op: itemLOR,
   414  			LHS: &BinaryExpr{
   415  				Op: itemLUnless,
   416  				LHS: &BinaryExpr{
   417  					Op: itemLAND,
   418  					LHS: &VectorSelector{
   419  						Name: "foo",
   420  						LabelMatchers: []*labels.Matcher{
   421  							mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "foo"),
   422  						},
   423  					},
   424  					RHS: &VectorSelector{
   425  						Name: "bar",
   426  						LabelMatchers: []*labels.Matcher{
   427  							mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "bar"),
   428  						},
   429  					},
   430  					VectorMatching: &VectorMatching{Card: CardManyToMany},
   431  				},
   432  				RHS: &VectorSelector{
   433  					Name: "baz",
   434  					LabelMatchers: []*labels.Matcher{
   435  						mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "baz"),
   436  					},
   437  				},
   438  				VectorMatching: &VectorMatching{Card: CardManyToMany},
   439  			},
   440  			RHS: &VectorSelector{
   441  				Name: "qux",
   442  				LabelMatchers: []*labels.Matcher{
   443  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "qux"),
   444  				},
   445  			},
   446  			VectorMatching: &VectorMatching{Card: CardManyToMany},
   447  		},
   448  	}, {
   449  		// Test precedence and reassigning of operands.
   450  		input: "bar + on(foo) bla / on(baz, buz) group_right(test) blub",
   451  		expected: &BinaryExpr{
   452  			Op: itemADD,
   453  			LHS: &VectorSelector{
   454  				Name: "bar",
   455  				LabelMatchers: []*labels.Matcher{
   456  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "bar"),
   457  				},
   458  			},
   459  			RHS: &BinaryExpr{
   460  				Op: itemDIV,
   461  				LHS: &VectorSelector{
   462  					Name: "bla",
   463  					LabelMatchers: []*labels.Matcher{
   464  						mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "bla"),
   465  					},
   466  				},
   467  				RHS: &VectorSelector{
   468  					Name: "blub",
   469  					LabelMatchers: []*labels.Matcher{
   470  						mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "blub"),
   471  					},
   472  				},
   473  				VectorMatching: &VectorMatching{
   474  					Card:           CardOneToMany,
   475  					MatchingLabels: []string{"baz", "buz"},
   476  					On:             true,
   477  					Include:        []string{"test"},
   478  				},
   479  			},
   480  			VectorMatching: &VectorMatching{
   481  				Card:           CardOneToOne,
   482  				MatchingLabels: []string{"foo"},
   483  				On:             true,
   484  			},
   485  		},
   486  	}, {
   487  		input: "foo * on(test,blub) bar",
   488  		expected: &BinaryExpr{
   489  			Op: itemMUL,
   490  			LHS: &VectorSelector{
   491  				Name: "foo",
   492  				LabelMatchers: []*labels.Matcher{
   493  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "foo"),
   494  				},
   495  			},
   496  			RHS: &VectorSelector{
   497  				Name: "bar",
   498  				LabelMatchers: []*labels.Matcher{
   499  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "bar"),
   500  				},
   501  			},
   502  			VectorMatching: &VectorMatching{
   503  				Card:           CardOneToOne,
   504  				MatchingLabels: []string{"test", "blub"},
   505  				On:             true,
   506  			},
   507  		},
   508  	}, {
   509  		input: "foo * on(test,blub) group_left bar",
   510  		expected: &BinaryExpr{
   511  			Op: itemMUL,
   512  			LHS: &VectorSelector{
   513  				Name: "foo",
   514  				LabelMatchers: []*labels.Matcher{
   515  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "foo"),
   516  				},
   517  			},
   518  			RHS: &VectorSelector{
   519  				Name: "bar",
   520  				LabelMatchers: []*labels.Matcher{
   521  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "bar"),
   522  				},
   523  			},
   524  			VectorMatching: &VectorMatching{
   525  				Card:           CardManyToOne,
   526  				MatchingLabels: []string{"test", "blub"},
   527  				On:             true,
   528  			},
   529  		},
   530  	}, {
   531  		input: "foo and on(test,blub) bar",
   532  		expected: &BinaryExpr{
   533  			Op: itemLAND,
   534  			LHS: &VectorSelector{
   535  				Name: "foo",
   536  				LabelMatchers: []*labels.Matcher{
   537  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "foo"),
   538  				},
   539  			},
   540  			RHS: &VectorSelector{
   541  				Name: "bar",
   542  				LabelMatchers: []*labels.Matcher{
   543  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "bar"),
   544  				},
   545  			},
   546  			VectorMatching: &VectorMatching{
   547  				Card:           CardManyToMany,
   548  				MatchingLabels: []string{"test", "blub"},
   549  				On:             true,
   550  			},
   551  		},
   552  	}, {
   553  		input: "foo and on() bar",
   554  		expected: &BinaryExpr{
   555  			Op: itemLAND,
   556  			LHS: &VectorSelector{
   557  				Name: "foo",
   558  				LabelMatchers: []*labels.Matcher{
   559  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "foo"),
   560  				},
   561  			},
   562  			RHS: &VectorSelector{
   563  				Name: "bar",
   564  				LabelMatchers: []*labels.Matcher{
   565  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "bar"),
   566  				},
   567  			},
   568  			VectorMatching: &VectorMatching{
   569  				Card:           CardManyToMany,
   570  				MatchingLabels: []string{},
   571  				On:             true,
   572  			},
   573  		},
   574  	}, {
   575  		input: "foo and ignoring(test,blub) bar",
   576  		expected: &BinaryExpr{
   577  			Op: itemLAND,
   578  			LHS: &VectorSelector{
   579  				Name: "foo",
   580  				LabelMatchers: []*labels.Matcher{
   581  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "foo"),
   582  				},
   583  			},
   584  			RHS: &VectorSelector{
   585  				Name: "bar",
   586  				LabelMatchers: []*labels.Matcher{
   587  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "bar"),
   588  				},
   589  			},
   590  			VectorMatching: &VectorMatching{
   591  				Card:           CardManyToMany,
   592  				MatchingLabels: []string{"test", "blub"},
   593  			},
   594  		},
   595  	}, {
   596  		input: "foo and ignoring() bar",
   597  		expected: &BinaryExpr{
   598  			Op: itemLAND,
   599  			LHS: &VectorSelector{
   600  				Name: "foo",
   601  				LabelMatchers: []*labels.Matcher{
   602  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "foo"),
   603  				},
   604  			},
   605  			RHS: &VectorSelector{
   606  				Name: "bar",
   607  				LabelMatchers: []*labels.Matcher{
   608  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "bar"),
   609  				},
   610  			},
   611  			VectorMatching: &VectorMatching{
   612  				Card:           CardManyToMany,
   613  				MatchingLabels: []string{},
   614  			},
   615  		},
   616  	}, {
   617  		input: "foo unless on(bar) baz",
   618  		expected: &BinaryExpr{
   619  			Op: itemLUnless,
   620  			LHS: &VectorSelector{
   621  				Name: "foo",
   622  				LabelMatchers: []*labels.Matcher{
   623  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "foo"),
   624  				},
   625  			},
   626  			RHS: &VectorSelector{
   627  				Name: "baz",
   628  				LabelMatchers: []*labels.Matcher{
   629  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "baz"),
   630  				},
   631  			},
   632  			VectorMatching: &VectorMatching{
   633  				Card:           CardManyToMany,
   634  				MatchingLabels: []string{"bar"},
   635  				On:             true,
   636  			},
   637  		},
   638  	}, {
   639  		input: "foo / on(test,blub) group_left(bar) bar",
   640  		expected: &BinaryExpr{
   641  			Op: itemDIV,
   642  			LHS: &VectorSelector{
   643  				Name: "foo",
   644  				LabelMatchers: []*labels.Matcher{
   645  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "foo"),
   646  				},
   647  			},
   648  			RHS: &VectorSelector{
   649  				Name: "bar",
   650  				LabelMatchers: []*labels.Matcher{
   651  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "bar"),
   652  				},
   653  			},
   654  			VectorMatching: &VectorMatching{
   655  				Card:           CardManyToOne,
   656  				MatchingLabels: []string{"test", "blub"},
   657  				On:             true,
   658  				Include:        []string{"bar"},
   659  			},
   660  		},
   661  	}, {
   662  		input: "foo / ignoring(test,blub) group_left(blub) bar",
   663  		expected: &BinaryExpr{
   664  			Op: itemDIV,
   665  			LHS: &VectorSelector{
   666  				Name: "foo",
   667  				LabelMatchers: []*labels.Matcher{
   668  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "foo"),
   669  				},
   670  			},
   671  			RHS: &VectorSelector{
   672  				Name: "bar",
   673  				LabelMatchers: []*labels.Matcher{
   674  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "bar"),
   675  				},
   676  			},
   677  			VectorMatching: &VectorMatching{
   678  				Card:           CardManyToOne,
   679  				MatchingLabels: []string{"test", "blub"},
   680  				Include:        []string{"blub"},
   681  			},
   682  		},
   683  	}, {
   684  		input: "foo / ignoring(test,blub) group_left(bar) bar",
   685  		expected: &BinaryExpr{
   686  			Op: itemDIV,
   687  			LHS: &VectorSelector{
   688  				Name: "foo",
   689  				LabelMatchers: []*labels.Matcher{
   690  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "foo"),
   691  				},
   692  			},
   693  			RHS: &VectorSelector{
   694  				Name: "bar",
   695  				LabelMatchers: []*labels.Matcher{
   696  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "bar"),
   697  				},
   698  			},
   699  			VectorMatching: &VectorMatching{
   700  				Card:           CardManyToOne,
   701  				MatchingLabels: []string{"test", "blub"},
   702  				Include:        []string{"bar"},
   703  			},
   704  		},
   705  	}, {
   706  		input: "foo - on(test,blub) group_right(bar,foo) bar",
   707  		expected: &BinaryExpr{
   708  			Op: itemSUB,
   709  			LHS: &VectorSelector{
   710  				Name: "foo",
   711  				LabelMatchers: []*labels.Matcher{
   712  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "foo"),
   713  				},
   714  			},
   715  			RHS: &VectorSelector{
   716  				Name: "bar",
   717  				LabelMatchers: []*labels.Matcher{
   718  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "bar"),
   719  				},
   720  			},
   721  			VectorMatching: &VectorMatching{
   722  				Card:           CardOneToMany,
   723  				MatchingLabels: []string{"test", "blub"},
   724  				Include:        []string{"bar", "foo"},
   725  				On:             true,
   726  			},
   727  		},
   728  	}, {
   729  		input: "foo - ignoring(test,blub) group_right(bar,foo) bar",
   730  		expected: &BinaryExpr{
   731  			Op: itemSUB,
   732  			LHS: &VectorSelector{
   733  				Name: "foo",
   734  				LabelMatchers: []*labels.Matcher{
   735  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "foo"),
   736  				},
   737  			},
   738  			RHS: &VectorSelector{
   739  				Name: "bar",
   740  				LabelMatchers: []*labels.Matcher{
   741  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "bar"),
   742  				},
   743  			},
   744  			VectorMatching: &VectorMatching{
   745  				Card:           CardOneToMany,
   746  				MatchingLabels: []string{"test", "blub"},
   747  				Include:        []string{"bar", "foo"},
   748  			},
   749  		},
   750  	}, {
   751  		input:  "foo and 1",
   752  		fail:   true,
   753  		errMsg: "set operator \"and\" not allowed in binary scalar expression",
   754  	}, {
   755  		input:  "1 and foo",
   756  		fail:   true,
   757  		errMsg: "set operator \"and\" not allowed in binary scalar expression",
   758  	}, {
   759  		input:  "foo or 1",
   760  		fail:   true,
   761  		errMsg: "set operator \"or\" not allowed in binary scalar expression",
   762  	}, {
   763  		input:  "1 or foo",
   764  		fail:   true,
   765  		errMsg: "set operator \"or\" not allowed in binary scalar expression",
   766  	}, {
   767  		input:  "foo unless 1",
   768  		fail:   true,
   769  		errMsg: "set operator \"unless\" not allowed in binary scalar expression",
   770  	}, {
   771  		input:  "1 unless foo",
   772  		fail:   true,
   773  		errMsg: "set operator \"unless\" not allowed in binary scalar expression",
   774  	}, {
   775  		input:  "1 or on(bar) foo",
   776  		fail:   true,
   777  		errMsg: "vector matching only allowed between instant vectors",
   778  	}, {
   779  		input:  "foo == on(bar) 10",
   780  		fail:   true,
   781  		errMsg: "vector matching only allowed between instant vectors",
   782  	}, {
   783  		input:  "foo and on(bar) group_left(baz) bar",
   784  		fail:   true,
   785  		errMsg: "no grouping allowed for \"and\" operation",
   786  	}, {
   787  		input:  "foo and on(bar) group_right(baz) bar",
   788  		fail:   true,
   789  		errMsg: "no grouping allowed for \"and\" operation",
   790  	}, {
   791  		input:  "foo or on(bar) group_left(baz) bar",
   792  		fail:   true,
   793  		errMsg: "no grouping allowed for \"or\" operation",
   794  	}, {
   795  		input:  "foo or on(bar) group_right(baz) bar",
   796  		fail:   true,
   797  		errMsg: "no grouping allowed for \"or\" operation",
   798  	}, {
   799  		input:  "foo unless on(bar) group_left(baz) bar",
   800  		fail:   true,
   801  		errMsg: "no grouping allowed for \"unless\" operation",
   802  	}, {
   803  		input:  "foo unless on(bar) group_right(baz) bar",
   804  		fail:   true,
   805  		errMsg: "no grouping allowed for \"unless\" operation",
   806  	}, {
   807  		input:  `http_requests{group="production"} + on(instance) group_left(job,instance) cpu_count{type="smp"}`,
   808  		fail:   true,
   809  		errMsg: "label \"instance\" must not occur in ON and GROUP clause at once",
   810  	}, {
   811  		input:  "foo + bool bar",
   812  		fail:   true,
   813  		errMsg: "bool modifier can only be used on comparison operators",
   814  	}, {
   815  		input:  "foo + bool 10",
   816  		fail:   true,
   817  		errMsg: "bool modifier can only be used on comparison operators",
   818  	}, {
   819  		input:  "foo and bool 10",
   820  		fail:   true,
   821  		errMsg: "bool modifier can only be used on comparison operators",
   822  	},
   823  	// Test Vector selector.
   824  	{
   825  		input: "foo",
   826  		expected: &VectorSelector{
   827  			Name:   "foo",
   828  			Offset: 0,
   829  			LabelMatchers: []*labels.Matcher{
   830  				mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "foo"),
   831  			},
   832  		},
   833  	}, {
   834  		input: "foo offset 5m",
   835  		expected: &VectorSelector{
   836  			Name:   "foo",
   837  			Offset: 5 * time.Minute,
   838  			LabelMatchers: []*labels.Matcher{
   839  				mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "foo"),
   840  			},
   841  		},
   842  	}, {
   843  		input: `foo:bar{a="bc"}`,
   844  		expected: &VectorSelector{
   845  			Name:   "foo:bar",
   846  			Offset: 0,
   847  			LabelMatchers: []*labels.Matcher{
   848  				mustLabelMatcher(labels.MatchEqual, "a", "bc"),
   849  				mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "foo:bar"),
   850  			},
   851  		},
   852  	}, {
   853  		input: `foo{NaN='bc'}`,
   854  		expected: &VectorSelector{
   855  			Name:   "foo",
   856  			Offset: 0,
   857  			LabelMatchers: []*labels.Matcher{
   858  				mustLabelMatcher(labels.MatchEqual, "NaN", "bc"),
   859  				mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "foo"),
   860  			},
   861  		},
   862  	}, {
   863  		input: `foo{a="b", foo!="bar", test=~"test", bar!~"baz"}`,
   864  		expected: &VectorSelector{
   865  			Name:   "foo",
   866  			Offset: 0,
   867  			LabelMatchers: []*labels.Matcher{
   868  				mustLabelMatcher(labels.MatchEqual, "a", "b"),
   869  				mustLabelMatcher(labels.MatchNotEqual, "foo", "bar"),
   870  				mustLabelMatcher(labels.MatchRegexp, "test", "test"),
   871  				mustLabelMatcher(labels.MatchNotRegexp, "bar", "baz"),
   872  				mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "foo"),
   873  			},
   874  		},
   875  	}, {
   876  		input:  `{`,
   877  		fail:   true,
   878  		errMsg: "unexpected end of input inside braces",
   879  	}, {
   880  		input:  `}`,
   881  		fail:   true,
   882  		errMsg: "unexpected character: '}'",
   883  	}, {
   884  		input:  `some{`,
   885  		fail:   true,
   886  		errMsg: "unexpected end of input inside braces",
   887  	}, {
   888  		input:  `some}`,
   889  		fail:   true,
   890  		errMsg: "could not parse remaining input \"}\"...",
   891  	}, {
   892  		input:  `some_metric{a=b}`,
   893  		fail:   true,
   894  		errMsg: "unexpected identifier \"b\" in label matching, expected string",
   895  	}, {
   896  		input:  `some_metric{a:b="b"}`,
   897  		fail:   true,
   898  		errMsg: "unexpected character inside braces: ':'",
   899  	}, {
   900  		input:  `foo{a*"b"}`,
   901  		fail:   true,
   902  		errMsg: "unexpected character inside braces: '*'",
   903  	}, {
   904  		input: `foo{a>="b"}`,
   905  		fail:  true,
   906  		// TODO(fabxc): willingly lexing wrong tokens allows for more precrise error
   907  		// messages from the parser - consider if this is an option.
   908  		errMsg: "unexpected character inside braces: '>'",
   909  	}, {
   910  		input:  "some_metric{a=\"\xff\"}",
   911  		fail:   true,
   912  		errMsg: "parse error at char 15: invalid UTF-8 rune",
   913  	}, {
   914  		input:  `foo{gibberish}`,
   915  		fail:   true,
   916  		errMsg: "expected label matching operator but got }",
   917  	}, {
   918  		input:  `foo{1}`,
   919  		fail:   true,
   920  		errMsg: "unexpected character inside braces: '1'",
   921  	}, {
   922  		input:  `{}`,
   923  		fail:   true,
   924  		errMsg: "vector selector must contain label matchers or metric name",
   925  	}, {
   926  		input:  `{x=""}`,
   927  		fail:   true,
   928  		errMsg: "vector selector must contain at least one non-empty matcher",
   929  	}, {
   930  		input:  `{x=~".*"}`,
   931  		fail:   true,
   932  		errMsg: "vector selector must contain at least one non-empty matcher",
   933  	}, {
   934  		input:  `{x!~".+"}`,
   935  		fail:   true,
   936  		errMsg: "vector selector must contain at least one non-empty matcher",
   937  	}, {
   938  		input:  `{x!="a"}`,
   939  		fail:   true,
   940  		errMsg: "vector selector must contain at least one non-empty matcher",
   941  	}, {
   942  		input:  `foo{__name__="bar"}`,
   943  		fail:   true,
   944  		errMsg: "metric name must not be set twice: \"foo\" or \"bar\"",
   945  		// }, {
   946  		// 	input:  `:foo`,
   947  		// 	fail:   true,
   948  		// 	errMsg: "bla",
   949  	},
   950  	// Test matrix selector.
   951  	{
   952  		input: "test[5s]",
   953  		expected: &MatrixSelector{
   954  			Name:   "test",
   955  			Offset: 0,
   956  			Range:  5 * time.Second,
   957  			LabelMatchers: []*labels.Matcher{
   958  				mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "test"),
   959  			},
   960  		},
   961  	}, {
   962  		input: "test[5m]",
   963  		expected: &MatrixSelector{
   964  			Name:   "test",
   965  			Offset: 0,
   966  			Range:  5 * time.Minute,
   967  			LabelMatchers: []*labels.Matcher{
   968  				mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "test"),
   969  			},
   970  		},
   971  	}, {
   972  		input: "test[5h] OFFSET 5m",
   973  		expected: &MatrixSelector{
   974  			Name:   "test",
   975  			Offset: 5 * time.Minute,
   976  			Range:  5 * time.Hour,
   977  			LabelMatchers: []*labels.Matcher{
   978  				mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "test"),
   979  			},
   980  		},
   981  	}, {
   982  		input: "test[5d] OFFSET 10s",
   983  		expected: &MatrixSelector{
   984  			Name:   "test",
   985  			Offset: 10 * time.Second,
   986  			Range:  5 * 24 * time.Hour,
   987  			LabelMatchers: []*labels.Matcher{
   988  				mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "test"),
   989  			},
   990  		},
   991  	}, {
   992  		input: "test[5w] offset 2w",
   993  		expected: &MatrixSelector{
   994  			Name:   "test",
   995  			Offset: 14 * 24 * time.Hour,
   996  			Range:  5 * 7 * 24 * time.Hour,
   997  			LabelMatchers: []*labels.Matcher{
   998  				mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "test"),
   999  			},
  1000  		},
  1001  	}, {
  1002  		input: `test{a="b"}[5y] OFFSET 3d`,
  1003  		expected: &MatrixSelector{
  1004  			Name:   "test",
  1005  			Offset: 3 * 24 * time.Hour,
  1006  			Range:  5 * 365 * 24 * time.Hour,
  1007  			LabelMatchers: []*labels.Matcher{
  1008  				mustLabelMatcher(labels.MatchEqual, "a", "b"),
  1009  				mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "test"),
  1010  			},
  1011  		},
  1012  	}, {
  1013  		input:  `foo[5mm]`,
  1014  		fail:   true,
  1015  		errMsg: "bad duration syntax: \"5mm\"",
  1016  	}, {
  1017  		input:  `foo[0m]`,
  1018  		fail:   true,
  1019  		errMsg: "duration must be greater than 0",
  1020  	}, {
  1021  		input:  `foo[5m30s]`,
  1022  		fail:   true,
  1023  		errMsg: "bad duration syntax: \"5m3\"",
  1024  	}, {
  1025  		input:  `foo[5m] OFFSET 1h30m`,
  1026  		fail:   true,
  1027  		errMsg: "bad number or duration syntax: \"1h3\"",
  1028  	}, {
  1029  		input: `foo["5m"]`,
  1030  		fail:  true,
  1031  	}, {
  1032  		input:  `foo[]`,
  1033  		fail:   true,
  1034  		errMsg: "missing unit character in duration",
  1035  	}, {
  1036  		input:  `foo[1]`,
  1037  		fail:   true,
  1038  		errMsg: "missing unit character in duration",
  1039  	}, {
  1040  		input:  `some_metric[5m] OFFSET 1`,
  1041  		fail:   true,
  1042  		errMsg: "unexpected number \"1\" in offset, expected duration",
  1043  	}, {
  1044  		input:  `some_metric[5m] OFFSET 1mm`,
  1045  		fail:   true,
  1046  		errMsg: "bad number or duration syntax: \"1mm\"",
  1047  	}, {
  1048  		input:  `some_metric[5m] OFFSET`,
  1049  		fail:   true,
  1050  		errMsg: "unexpected end of input in offset, expected duration",
  1051  	}, {
  1052  		input:  `some_metric OFFSET 1m[5m]`,
  1053  		fail:   true,
  1054  		errMsg: "could not parse remaining input \"[5m]\"...",
  1055  	}, {
  1056  		input:  `(foo + bar)[5m]`,
  1057  		fail:   true,
  1058  		errMsg: "could not parse remaining input \"[5m]\"...",
  1059  	},
  1060  	// Test aggregation.
  1061  	{
  1062  		input: "sum by (foo)(some_metric)",
  1063  		expected: &AggregateExpr{
  1064  			Op: itemSum,
  1065  			Expr: &VectorSelector{
  1066  				Name: "some_metric",
  1067  				LabelMatchers: []*labels.Matcher{
  1068  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "some_metric"),
  1069  				},
  1070  			},
  1071  			Grouping: []string{"foo"},
  1072  		},
  1073  	}, {
  1074  		input: "avg by (foo)(some_metric)",
  1075  		expected: &AggregateExpr{
  1076  			Op: itemAvg,
  1077  			Expr: &VectorSelector{
  1078  				Name: "some_metric",
  1079  				LabelMatchers: []*labels.Matcher{
  1080  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "some_metric"),
  1081  				},
  1082  			},
  1083  			Grouping: []string{"foo"},
  1084  		},
  1085  	}, {
  1086  		input: "max by (foo)(some_metric)",
  1087  		expected: &AggregateExpr{
  1088  			Op: itemMax,
  1089  			Expr: &VectorSelector{
  1090  				Name: "some_metric",
  1091  				LabelMatchers: []*labels.Matcher{
  1092  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "some_metric"),
  1093  				},
  1094  			},
  1095  			Grouping: []string{"foo"},
  1096  		},
  1097  	}, {
  1098  		input: "sum without (foo) (some_metric)",
  1099  		expected: &AggregateExpr{
  1100  			Op:      itemSum,
  1101  			Without: true,
  1102  			Expr: &VectorSelector{
  1103  				Name: "some_metric",
  1104  				LabelMatchers: []*labels.Matcher{
  1105  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "some_metric"),
  1106  				},
  1107  			},
  1108  			Grouping: []string{"foo"},
  1109  		},
  1110  	}, {
  1111  		input: "sum (some_metric) without (foo)",
  1112  		expected: &AggregateExpr{
  1113  			Op:      itemSum,
  1114  			Without: true,
  1115  			Expr: &VectorSelector{
  1116  				Name: "some_metric",
  1117  				LabelMatchers: []*labels.Matcher{
  1118  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "some_metric"),
  1119  				},
  1120  			},
  1121  			Grouping: []string{"foo"},
  1122  		},
  1123  	}, {
  1124  		input: "stddev(some_metric)",
  1125  		expected: &AggregateExpr{
  1126  			Op: itemStddev,
  1127  			Expr: &VectorSelector{
  1128  				Name: "some_metric",
  1129  				LabelMatchers: []*labels.Matcher{
  1130  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "some_metric"),
  1131  				},
  1132  			},
  1133  		},
  1134  	}, {
  1135  		input: "stdvar by (foo)(some_metric)",
  1136  		expected: &AggregateExpr{
  1137  			Op: itemStdvar,
  1138  			Expr: &VectorSelector{
  1139  				Name: "some_metric",
  1140  				LabelMatchers: []*labels.Matcher{
  1141  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "some_metric"),
  1142  				},
  1143  			},
  1144  			Grouping: []string{"foo"},
  1145  		},
  1146  	}, {
  1147  		input: "sum by ()(some_metric)",
  1148  		expected: &AggregateExpr{
  1149  			Op: itemSum,
  1150  			Expr: &VectorSelector{
  1151  				Name: "some_metric",
  1152  				LabelMatchers: []*labels.Matcher{
  1153  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "some_metric"),
  1154  				},
  1155  			},
  1156  			Grouping: []string{},
  1157  		},
  1158  	}, {
  1159  		input: "topk(5, some_metric)",
  1160  		expected: &AggregateExpr{
  1161  			Op: itemTopK,
  1162  			Expr: &VectorSelector{
  1163  				Name: "some_metric",
  1164  				LabelMatchers: []*labels.Matcher{
  1165  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "some_metric"),
  1166  				},
  1167  			},
  1168  			Param: &NumberLiteral{5},
  1169  		},
  1170  	}, {
  1171  		input: "count_values(\"value\", some_metric)",
  1172  		expected: &AggregateExpr{
  1173  			Op: itemCountValues,
  1174  			Expr: &VectorSelector{
  1175  				Name: "some_metric",
  1176  				LabelMatchers: []*labels.Matcher{
  1177  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "some_metric"),
  1178  				},
  1179  			},
  1180  			Param: &StringLiteral{"value"},
  1181  		},
  1182  	}, {
  1183  		// Test usage of keywords as label names.
  1184  		input: "sum without(and, by, avg, count, alert, annotations)(some_metric)",
  1185  		expected: &AggregateExpr{
  1186  			Op:      itemSum,
  1187  			Without: true,
  1188  			Expr: &VectorSelector{
  1189  				Name: "some_metric",
  1190  				LabelMatchers: []*labels.Matcher{
  1191  					mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "some_metric"),
  1192  				},
  1193  			},
  1194  			Grouping: []string{"and", "by", "avg", "count", "alert", "annotations"},
  1195  		},
  1196  	}, {
  1197  		input:  "sum without(==)(some_metric)",
  1198  		fail:   true,
  1199  		errMsg: "unexpected <op:==> in grouping opts, expected label",
  1200  	}, {
  1201  		input:  `sum some_metric by (test)`,
  1202  		fail:   true,
  1203  		errMsg: "unexpected identifier \"some_metric\" in aggregation, expected \"(\"",
  1204  	}, {
  1205  		input:  `sum (some_metric) by test`,
  1206  		fail:   true,
  1207  		errMsg: "unexpected identifier \"test\" in grouping opts, expected \"(\"",
  1208  	}, {
  1209  		input:  `sum (some_metric) by test`,
  1210  		fail:   true,
  1211  		errMsg: "unexpected identifier \"test\" in grouping opts, expected \"(\"",
  1212  	}, {
  1213  		input:  `sum () by (test)`,
  1214  		fail:   true,
  1215  		errMsg: "no valid expression found",
  1216  	}, {
  1217  		input:  "MIN keep_common (some_metric)",
  1218  		fail:   true,
  1219  		errMsg: "parse error at char 5: unexpected identifier \"keep_common\" in aggregation, expected \"(\"",
  1220  	}, {
  1221  		input:  "MIN (some_metric) keep_common",
  1222  		fail:   true,
  1223  		errMsg: "could not parse remaining input \"keep_common\"...",
  1224  	}, {
  1225  		input:  `sum (some_metric) without (test) by (test)`,
  1226  		fail:   true,
  1227  		errMsg: "could not parse remaining input \"by (test)\"...",
  1228  	}, {
  1229  		input:  `sum without (test) (some_metric) by (test)`,
  1230  		fail:   true,
  1231  		errMsg: "could not parse remaining input \"by (test)\"...",
  1232  	}, {
  1233  		input:  `topk(some_metric)`,
  1234  		fail:   true,
  1235  		errMsg: "parse error at char 17: unexpected \")\" in aggregation, expected \",\"",
  1236  	}, {
  1237  		input:  `topk(some_metric, other_metric)`,
  1238  		fail:   true,
  1239  		errMsg: "parse error at char 32: expected type scalar in aggregation parameter, got instant vector",
  1240  	}, {
  1241  		input:  `count_values(5, other_metric)`,
  1242  		fail:   true,
  1243  		errMsg: "parse error at char 30: expected type string in aggregation parameter, got scalar",
  1244  	},
  1245  	// Test function calls.
  1246  	{
  1247  		input: "time()",
  1248  		expected: &Call{
  1249  			Func: mustGetFunction("time"),
  1250  		},
  1251  	}, {
  1252  		input: `floor(some_metric{foo!="bar"})`,
  1253  		expected: &Call{
  1254  			Func: mustGetFunction("floor"),
  1255  			Args: Expressions{
  1256  				&VectorSelector{
  1257  					Name: "some_metric",
  1258  					LabelMatchers: []*labels.Matcher{
  1259  						mustLabelMatcher(labels.MatchNotEqual, "foo", "bar"),
  1260  						mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "some_metric"),
  1261  					},
  1262  				},
  1263  			},
  1264  		},
  1265  	}, {
  1266  		input: "rate(some_metric[5m])",
  1267  		expected: &Call{
  1268  			Func: mustGetFunction("rate"),
  1269  			Args: Expressions{
  1270  				&MatrixSelector{
  1271  					Name: "some_metric",
  1272  					LabelMatchers: []*labels.Matcher{
  1273  						mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "some_metric"),
  1274  					},
  1275  					Range: 5 * time.Minute,
  1276  				},
  1277  			},
  1278  		},
  1279  	}, {
  1280  		input: "round(some_metric)",
  1281  		expected: &Call{
  1282  			Func: mustGetFunction("round"),
  1283  			Args: Expressions{
  1284  				&VectorSelector{
  1285  					Name: "some_metric",
  1286  					LabelMatchers: []*labels.Matcher{
  1287  						mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "some_metric"),
  1288  					},
  1289  				},
  1290  			},
  1291  		},
  1292  	}, {
  1293  		input: "round(some_metric, 5)",
  1294  		expected: &Call{
  1295  			Func: mustGetFunction("round"),
  1296  			Args: Expressions{
  1297  				&VectorSelector{
  1298  					Name: "some_metric",
  1299  					LabelMatchers: []*labels.Matcher{
  1300  						mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "some_metric"),
  1301  					},
  1302  				},
  1303  				&NumberLiteral{5},
  1304  			},
  1305  		},
  1306  	}, {
  1307  		input:  "floor()",
  1308  		fail:   true,
  1309  		errMsg: "expected 1 argument(s) in call to \"floor\", got 0",
  1310  	}, {
  1311  		input:  "floor(some_metric, other_metric)",
  1312  		fail:   true,
  1313  		errMsg: "expected 1 argument(s) in call to \"floor\", got 2",
  1314  	}, {
  1315  		input:  "floor(1)",
  1316  		fail:   true,
  1317  		errMsg: "expected type instant vector in call to function \"floor\", got scalar",
  1318  	}, {
  1319  		input:  "non_existent_function_far_bar()",
  1320  		fail:   true,
  1321  		errMsg: "unknown function with name \"non_existent_function_far_bar\"",
  1322  	}, {
  1323  		input:  "rate(some_metric)",
  1324  		fail:   true,
  1325  		errMsg: "expected type range vector in call to function \"rate\", got instant vector",
  1326  	}, {
  1327  		input:  "label_replace(a, `b`, `c\xff`, `d`, `.*`)",
  1328  		fail:   true,
  1329  		errMsg: "parse error at char 23: invalid UTF-8 rune",
  1330  	},
  1331  	// Fuzzing regression tests.
  1332  	{
  1333  		input:  "-=",
  1334  		fail:   true,
  1335  		errMsg: `no valid expression found`,
  1336  	}, {
  1337  		input:  "++-++-+-+-<",
  1338  		fail:   true,
  1339  		errMsg: `no valid expression found`,
  1340  	}, {
  1341  		input:  "e-+=/(0)",
  1342  		fail:   true,
  1343  		errMsg: `no valid expression found`,
  1344  	}, {
  1345  		input:  "-If",
  1346  		fail:   true,
  1347  		errMsg: `no valid expression found`,
  1348  	},
  1349  	// String quoting and escape sequence interpretation tests.
  1350  	{
  1351  		input: `"double-quoted string \" with escaped quote"`,
  1352  		expected: &StringLiteral{
  1353  			Val: "double-quoted string \" with escaped quote",
  1354  		},
  1355  	}, {
  1356  		input: `'single-quoted string \' with escaped quote'`,
  1357  		expected: &StringLiteral{
  1358  			Val: "single-quoted string ' with escaped quote",
  1359  		},
  1360  	}, {
  1361  		input: "`backtick-quoted string`",
  1362  		expected: &StringLiteral{
  1363  			Val: "backtick-quoted string",
  1364  		},
  1365  	}, {
  1366  		input: `"\a\b\f\n\r\t\v\\\" - \xFF\377\u1234\U00010111\U0001011111☺"`,
  1367  		expected: &StringLiteral{
  1368  			Val: "\a\b\f\n\r\t\v\\\" - \xFF\377\u1234\U00010111\U0001011111☺",
  1369  		},
  1370  	}, {
  1371  		input: `'\a\b\f\n\r\t\v\\\' - \xFF\377\u1234\U00010111\U0001011111☺'`,
  1372  		expected: &StringLiteral{
  1373  			Val: "\a\b\f\n\r\t\v\\' - \xFF\377\u1234\U00010111\U0001011111☺",
  1374  		},
  1375  	}, {
  1376  		input: "`" + `\a\b\f\n\r\t\v\\\"\' - \xFF\377\u1234\U00010111\U0001011111☺` + "`",
  1377  		expected: &StringLiteral{
  1378  			Val: `\a\b\f\n\r\t\v\\\"\' - \xFF\377\u1234\U00010111\U0001011111☺`,
  1379  		},
  1380  	}, {
  1381  		input:  "`\\``",
  1382  		fail:   true,
  1383  		errMsg: "could not parse remaining input",
  1384  	}, {
  1385  		input:  `"\`,
  1386  		fail:   true,
  1387  		errMsg: "escape sequence not terminated",
  1388  	}, {
  1389  		input:  `"\c"`,
  1390  		fail:   true,
  1391  		errMsg: "unknown escape sequence U+0063 'c'",
  1392  	}, {
  1393  		input:  `"\x."`,
  1394  		fail:   true,
  1395  		errMsg: "illegal character U+002E '.' in escape sequence",
  1396  	},
  1397  }
  1398  
  1399  func TestParseExpressions(t *testing.T) {
  1400  	for _, test := range testExpr {
  1401  		expr, err := ParseExpr(test.input)
  1402  
  1403  		// Unexpected errors are always caused by a bug.
  1404  		if err == errUnexpected {
  1405  			t.Fatalf("unexpected error occurred")
  1406  		}
  1407  
  1408  		if !test.fail && err != nil {
  1409  			t.Errorf("error in input '%s'", test.input)
  1410  			t.Fatalf("could not parse: %s", err)
  1411  		}
  1412  
  1413  		if test.fail && err != nil {
  1414  			if !strings.Contains(err.Error(), test.errMsg) {
  1415  				t.Errorf("unexpected error on input '%s'", test.input)
  1416  				t.Fatalf("expected error to contain %q but got %q", test.errMsg, err)
  1417  			}
  1418  			continue
  1419  		}
  1420  
  1421  		if !reflect.DeepEqual(expr, test.expected) {
  1422  			t.Errorf("error on input '%s'", test.input)
  1423  			t.Fatalf("no match\n\nexpected:\n%s\ngot: \n%s\n", Tree(test.expected), Tree(expr))
  1424  		}
  1425  	}
  1426  }
  1427  
  1428  // NaN has no equality. Thus, we need a separate test for it.
  1429  func TestNaNExpression(t *testing.T) {
  1430  	expr, err := ParseExpr("NaN")
  1431  	if err != nil {
  1432  		t.Errorf("error on input 'NaN'")
  1433  		t.Fatalf("could not parse: %s", err)
  1434  	}
  1435  
  1436  	nl, ok := expr.(*NumberLiteral)
  1437  	if !ok {
  1438  		t.Errorf("error on input 'NaN'")
  1439  		t.Fatalf("expected number literal but got %T", expr)
  1440  	}
  1441  
  1442  	if !math.IsNaN(float64(nl.Val)) {
  1443  		t.Errorf("error on input 'NaN'")
  1444  		t.Fatalf("expected 'NaN' in number literal but got %v", nl.Val)
  1445  	}
  1446  }
  1447  
  1448  var testStatement = []struct {
  1449  	input    string
  1450  	expected Statements
  1451  	fail     bool
  1452  }{
  1453  	{
  1454  		// Test a file-like input.
  1455  		input: `
  1456  			# A simple test recording rule.
  1457  			dc:http_request:rate5m = sum(rate(http_request_count[5m])) by (dc)
  1458  
  1459  			# A simple test alerting rule.
  1460  			ALERT GlobalRequestRateLow IF(dc:http_request:rate5m < 10000) FOR 5m
  1461  			  LABELS {
  1462  			    service = "testservice"
  1463  			    # ... more fields here ...
  1464  			  }
  1465  			  ANNOTATIONS {
  1466  			    summary     = "Global request rate low",
  1467  			    description = "The global request rate is low"
  1468  			  }
  1469  			  
  1470  			foo = bar{label1="value1"}
  1471  
  1472  			ALERT BazAlert IF foo > 10
  1473  			  ANNOTATIONS {
  1474  			    description = "BazAlert",
  1475  			    runbook     = "http://my.url",
  1476  			    summary     = "Baz",
  1477  			  }
  1478  		`,
  1479  		expected: Statements{
  1480  			&RecordStmt{
  1481  				Name: "dc:http_request:rate5m",
  1482  				Expr: &AggregateExpr{
  1483  					Op:       itemSum,
  1484  					Grouping: []string{"dc"},
  1485  					Expr: &Call{
  1486  						Func: mustGetFunction("rate"),
  1487  						Args: Expressions{
  1488  							&MatrixSelector{
  1489  								Name: "http_request_count",
  1490  								LabelMatchers: []*labels.Matcher{
  1491  									mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "http_request_count"),
  1492  								},
  1493  								Range: 5 * time.Minute,
  1494  							},
  1495  						},
  1496  					},
  1497  				},
  1498  				Labels: nil,
  1499  			},
  1500  			&AlertStmt{
  1501  				Name: "GlobalRequestRateLow",
  1502  				Expr: &ParenExpr{&BinaryExpr{
  1503  					Op: itemLSS,
  1504  					LHS: &VectorSelector{
  1505  						Name: "dc:http_request:rate5m",
  1506  						LabelMatchers: []*labels.Matcher{
  1507  							mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "dc:http_request:rate5m"),
  1508  						},
  1509  					},
  1510  					RHS: &NumberLiteral{10000},
  1511  				}},
  1512  				Labels:   labels.FromStrings("service", "testservice"),
  1513  				Duration: 5 * time.Minute,
  1514  				Annotations: labels.FromStrings(
  1515  					"summary", "Global request rate low",
  1516  					"description", "The global request rate is low",
  1517  				),
  1518  			},
  1519  			&RecordStmt{
  1520  				Name: "foo",
  1521  				Expr: &VectorSelector{
  1522  					Name: "bar",
  1523  					LabelMatchers: []*labels.Matcher{
  1524  						mustLabelMatcher(labels.MatchEqual, "label1", "value1"),
  1525  						mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "bar"),
  1526  					},
  1527  				},
  1528  			},
  1529  			&AlertStmt{
  1530  				Name: "BazAlert",
  1531  				Expr: &BinaryExpr{
  1532  					Op: itemGTR,
  1533  					LHS: &VectorSelector{
  1534  						Name: "foo",
  1535  						LabelMatchers: []*labels.Matcher{
  1536  							mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "foo"),
  1537  						},
  1538  					},
  1539  					RHS: &NumberLiteral{10},
  1540  				},
  1541  				Annotations: labels.FromStrings(
  1542  					"summary", "Baz",
  1543  					"description", "BazAlert",
  1544  					"runbook", "http://my.url",
  1545  				),
  1546  			},
  1547  		},
  1548  	}, {
  1549  		input: `foo{x="", a="z"} = bar{a="b", x=~"y"}`,
  1550  		expected: Statements{
  1551  			&RecordStmt{
  1552  				Name: "foo",
  1553  				Expr: &VectorSelector{
  1554  					Name: "bar",
  1555  					LabelMatchers: []*labels.Matcher{
  1556  						mustLabelMatcher(labels.MatchEqual, "a", "b"),
  1557  						mustLabelMatcher(labels.MatchRegexp, "x", "y"),
  1558  						mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "bar"),
  1559  					},
  1560  				},
  1561  				Labels: labels.FromStrings("x", "", "a", "z"),
  1562  			},
  1563  		},
  1564  	}, {
  1565  		input: `ALERT SomeName IF some_metric > 1
  1566  			LABELS {}
  1567  			ANNOTATIONS {
  1568  				summary = "Global request rate low",
  1569  				description = "The global request rate is low",
  1570  			}
  1571  		`,
  1572  		expected: Statements{
  1573  			&AlertStmt{
  1574  				Name: "SomeName",
  1575  				Expr: &BinaryExpr{
  1576  					Op: itemGTR,
  1577  					LHS: &VectorSelector{
  1578  						Name: "some_metric",
  1579  						LabelMatchers: []*labels.Matcher{
  1580  							mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "some_metric"),
  1581  						},
  1582  					},
  1583  					RHS: &NumberLiteral{1},
  1584  				},
  1585  				Labels: labels.Labels{},
  1586  				Annotations: labels.FromStrings(
  1587  					"summary", "Global request rate low",
  1588  					"description", "The global request rate is low",
  1589  				),
  1590  			},
  1591  		},
  1592  	}, {
  1593  		input: `
  1594  			# A simple test alerting rule.
  1595  			ALERT GlobalRequestRateLow IF(dc:http_request:rate5m < 10000) FOR 5
  1596  			  LABELS {
  1597  			    service = "testservice"
  1598  			    # ... more fields here ...
  1599  			  }
  1600  			  ANNOTATIONS {
  1601  			    summary = "Global request rate low"
  1602  			    description = "The global request rate is low"
  1603  			  }
  1604  	  	`,
  1605  		fail: true,
  1606  	}, {
  1607  		input:    "",
  1608  		expected: Statements{},
  1609  	}, {
  1610  		input: "foo = time()",
  1611  		expected: Statements{
  1612  			&RecordStmt{
  1613  				Name:   "foo",
  1614  				Expr:   &Call{Func: mustGetFunction("time")},
  1615  				Labels: nil,
  1616  			}},
  1617  	}, {
  1618  		input: "foo = 1",
  1619  		expected: Statements{
  1620  			&RecordStmt{
  1621  				Name:   "foo",
  1622  				Expr:   &NumberLiteral{1},
  1623  				Labels: nil,
  1624  			}},
  1625  	}, {
  1626  		input: "foo = bar[5m]",
  1627  		fail:  true,
  1628  	}, {
  1629  		input: `foo = "test"`,
  1630  		fail:  true,
  1631  	}, {
  1632  		input: `foo = `,
  1633  		fail:  true,
  1634  	}, {
  1635  		input: `foo{a!="b"} = bar`,
  1636  		fail:  true,
  1637  	}, {
  1638  		input: `foo{a=~"b"} = bar`,
  1639  		fail:  true,
  1640  	}, {
  1641  		input: `foo{a!~"b"} = bar`,
  1642  		fail:  true,
  1643  	},
  1644  	// Fuzzing regression tests.
  1645  	{
  1646  		input: `I=-/`,
  1647  		fail:  true,
  1648  	},
  1649  	{
  1650  		input: `I=3E8/-=`,
  1651  		fail:  true,
  1652  	},
  1653  	{
  1654  		input: `M=-=-0-0`,
  1655  		fail:  true,
  1656  	},
  1657  }
  1658  
  1659  func TestParseStatements(t *testing.T) {
  1660  	for _, test := range testStatement {
  1661  		stmts, err := ParseStmts(test.input)
  1662  
  1663  		// Unexpected errors are always caused by a bug.
  1664  		if err == errUnexpected {
  1665  			t.Fatalf("unexpected error occurred")
  1666  		}
  1667  
  1668  		if !test.fail && err != nil {
  1669  			t.Errorf("error in input: \n\n%s\n", test.input)
  1670  			t.Fatalf("could not parse: %s", err)
  1671  		}
  1672  		if test.fail && err != nil {
  1673  			continue
  1674  		}
  1675  
  1676  		if !reflect.DeepEqual(stmts, test.expected) {
  1677  			t.Errorf("error in input: \n\n%s\n", test.input)
  1678  			t.Fatalf("no match\n\nexpected:\n%s\ngot: \n%s\n", Tree(test.expected), Tree(stmts))
  1679  		}
  1680  	}
  1681  }
  1682  
  1683  func mustLabelMatcher(mt labels.MatchType, name, val string) *labels.Matcher {
  1684  	m, err := labels.NewMatcher(mt, name, val)
  1685  	if err != nil {
  1686  		panic(err)
  1687  	}
  1688  	return m
  1689  }
  1690  
  1691  func mustGetFunction(name string) *Function {
  1692  	f, ok := getFunction(name)
  1693  	if !ok {
  1694  		panic(fmt.Errorf("function %q does not exist", name))
  1695  	}
  1696  	return f
  1697  }
  1698  
  1699  var testSeries = []struct {
  1700  	input          string
  1701  	expectedMetric labels.Labels
  1702  	expectedValues []sequenceValue
  1703  	fail           bool
  1704  }{
  1705  	{
  1706  		input:          `{} 1 2 3`,
  1707  		expectedMetric: labels.Labels{},
  1708  		expectedValues: newSeq(1, 2, 3),
  1709  	}, {
  1710  		input:          `{a="b"} -1 2 3`,
  1711  		expectedMetric: labels.FromStrings("a", "b"),
  1712  		expectedValues: newSeq(-1, 2, 3),
  1713  	}, {
  1714  		input:          `my_metric 1 2 3`,
  1715  		expectedMetric: labels.FromStrings(labels.MetricName, "my_metric"),
  1716  		expectedValues: newSeq(1, 2, 3),
  1717  	}, {
  1718  		input:          `my_metric{} 1 2 3`,
  1719  		expectedMetric: labels.FromStrings(labels.MetricName, "my_metric"),
  1720  		expectedValues: newSeq(1, 2, 3),
  1721  	}, {
  1722  		input:          `my_metric{a="b"} 1 2 3`,
  1723  		expectedMetric: labels.FromStrings(labels.MetricName, "my_metric", "a", "b"),
  1724  		expectedValues: newSeq(1, 2, 3),
  1725  	}, {
  1726  		input:          `my_metric{a="b"} 1 2 3-10x4`,
  1727  		expectedMetric: labels.FromStrings(labels.MetricName, "my_metric", "a", "b"),
  1728  		expectedValues: newSeq(1, 2, 3, -7, -17, -27, -37),
  1729  	}, {
  1730  		input:          `my_metric{a="b"} 1 2 3-0x4`,
  1731  		expectedMetric: labels.FromStrings(labels.MetricName, "my_metric", "a", "b"),
  1732  		expectedValues: newSeq(1, 2, 3, 3, 3, 3, 3),
  1733  	}, {
  1734  		input:          `my_metric{a="b"} 1 3 _ 5 _x4`,
  1735  		expectedMetric: labels.FromStrings(labels.MetricName, "my_metric", "a", "b"),
  1736  		expectedValues: newSeq(1, 3, none, 5, none, none, none, none),
  1737  	}, {
  1738  		input: `my_metric{a="b"} 1 3 _ 5 _a4`,
  1739  		fail:  true,
  1740  	},
  1741  }
  1742  
  1743  // For these tests only, we use the smallest float64 to signal an omitted value.
  1744  const none = math.SmallestNonzeroFloat64
  1745  
  1746  func newSeq(vals ...float64) (res []sequenceValue) {
  1747  	for _, v := range vals {
  1748  		if v == none {
  1749  			res = append(res, sequenceValue{omitted: true})
  1750  		} else {
  1751  			res = append(res, sequenceValue{value: v})
  1752  		}
  1753  	}
  1754  	return res
  1755  }
  1756  
  1757  func TestParseSeries(t *testing.T) {
  1758  	for _, test := range testSeries {
  1759  		metric, vals, err := parseSeriesDesc(test.input)
  1760  
  1761  		// Unexpected errors are always caused by a bug.
  1762  		if err == errUnexpected {
  1763  			t.Fatalf("unexpected error occurred")
  1764  		}
  1765  
  1766  		if test.fail {
  1767  			if err != nil {
  1768  				continue
  1769  			}
  1770  			t.Errorf("error in input: \n\n%s\n", test.input)
  1771  			t.Fatalf("failure expected, but passed")
  1772  		} else {
  1773  			if err != nil {
  1774  				t.Errorf("error in input: \n\n%s\n", test.input)
  1775  				t.Fatalf("could not parse: %s", err)
  1776  			}
  1777  		}
  1778  
  1779  		require.Equal(t, test.expectedMetric, metric)
  1780  		require.Equal(t, test.expectedValues, vals)
  1781  
  1782  		if !reflect.DeepEqual(vals, test.expectedValues) || !reflect.DeepEqual(metric, test.expectedMetric) {
  1783  			t.Errorf("error in input: \n\n%s\n", test.input)
  1784  			t.Fatalf("no match\n\nexpected:\n%s %s\ngot: \n%s %s\n", test.expectedMetric, test.expectedValues, metric, vals)
  1785  		}
  1786  	}
  1787  }
  1788  
  1789  func TestRecoverParserRuntime(t *testing.T) {
  1790  	var p *parser
  1791  	var err error
  1792  	defer p.recover(&err)
  1793  
  1794  	// Cause a runtime panic.
  1795  	var a []int
  1796  	a[123] = 1
  1797  
  1798  	if err != errUnexpected {
  1799  		t.Fatalf("wrong error message: %q, expected %q", err, errUnexpected)
  1800  	}
  1801  }
  1802  
  1803  func TestRecoverParserError(t *testing.T) {
  1804  	var p *parser
  1805  	var err error
  1806  
  1807  	e := fmt.Errorf("custom error")
  1808  
  1809  	defer func() {
  1810  		if err.Error() != e.Error() {
  1811  			t.Fatalf("wrong error message: %q, expected %q", err, e)
  1812  		}
  1813  	}()
  1814  	defer p.recover(&err)
  1815  
  1816  	panic(e)
  1817  }