github.com/rajeev159/opa@v0.45.0/ast/parser_test.go (about) 1 // Copyright 2016 The OPA Authors. All rights reserved. 2 // Use of this source code is governed by an Apache2 3 // license that can be found in the LICENSE file. 4 5 package ast 6 7 import ( 8 "bytes" 9 "encoding/json" 10 "fmt" 11 "reflect" 12 "strings" 13 "testing" 14 15 "github.com/open-policy-agent/opa/ast/internal/tokens" 16 ) 17 18 const ( 19 testModule = ` 20 # This policy module belongs the opa.example package. 21 package opa.examples 22 23 # Refer to data.servers as servers. 24 import data.servers 25 # Refer to the data.networks as networks. 26 import data.networks 27 # Refer to the data.ports as ports. 28 import data.ports 29 30 # A server exists in the violations set if... 31 violations[server] { 32 # ...the server exists 33 server = servers[i] 34 # ...and any of the server’s protocols is HTTP 35 server.protocols[j] = "http" 36 # ...and the server is public. 37 public_servers[server] 38 } 39 40 # A server exists in the public_servers set if... 41 public_servers[server] { 42 # Semicolons are optional. Can group expressions onto one line. 43 server = servers[i]; server.ports[j] = ports[k].id # ...and the server is connected to a port 44 ports[k].networks[l] = networks[m].id; # ...and the port is connected to a network 45 networks[m].public = true # ...and the network is public. 46 }` 47 ) 48 49 func TestNumberTerms(t *testing.T) { 50 51 tests := []struct { 52 input string 53 expected string 54 }{ 55 {"0", "0"}, 56 {"100", "100"}, 57 {"-1", "-1"}, 58 {"1e6", "1e6"}, 59 {"1.1e6", "1.1e6"}, 60 {"-1e-6", "-1e-6"}, 61 {"1E6", "1E6"}, 62 {"0.1", "0.1"}, 63 {".1", "0.1"}, 64 {".0001", "0.0001"}, 65 {"-.1", "-0.1"}, 66 {"-0.0001", "-0.0001"}, 67 {"1e1000", "1e1000"}, 68 {"0e1", "0"}, 69 {"-0.1", "-0.1"}, 70 } 71 72 for _, tc := range tests { 73 result, err := ParseTerm(tc.input) 74 if err != nil { 75 t.Errorf("Unexpected error for %v: %v", tc.input, err) 76 } else { 77 e := NumberTerm(json.Number(tc.expected)) 78 if !result.Equal(e) { 79 t.Errorf("Expected %v for %v but got: %v", e, tc.input, result) 80 } 81 } 82 } 83 } 84 85 func TestStringTerms(t *testing.T) { 86 tests := []struct { 87 input string 88 expected string 89 }{ 90 {`""`, ""}, // empty 91 {`" "`, " "}, // whitespace 92 {`"\""`, `"`}, // escaped quote 93 {`"http:\/\/"`, `http://`}, // escaped solidus 94 {`"\u0001"`, "\x01"}, // control code 95 {`"foo\u005C"`, "foo\u005c"}, // unicode (upper hex) 96 {`"foo\u005c"`, "foo\u005C"}, // unicode (lower hex) 97 {`"\uD834\uDD1E"`, `𝄞`}, // g-clef 98 {"`hi\\there`", `hi\there`}, // basic raw string 99 {"`foo\nbar\n baz`", `foo 100 bar 101 baz`}, // multi-line raw string 102 } 103 104 for _, tc := range tests { 105 result, err := ParseTerm(tc.input) 106 if err != nil { 107 t.Errorf("Unexpected error for %v: %v", tc.input, err) 108 } else { 109 s := StringTerm(tc.expected) 110 if !result.Equal(s) { 111 t.Errorf("Expected %v for %v but got: %v", s, tc.input, result) 112 } 113 } 114 } 115 } 116 117 func TestScalarTerms(t *testing.T) { 118 assertParseOneTerm(t, "null", "null", NullTerm()) 119 assertParseOneTerm(t, "true", "true", BooleanTerm(true)) 120 assertParseOneTerm(t, "false", "false", BooleanTerm(false)) 121 assertParseOneTerm(t, "integer", "53", IntNumberTerm(53)) 122 assertParseOneTerm(t, "integer2", "-53", IntNumberTerm(-53)) 123 assertParseOneTerm(t, "float", "16.7", FloatNumberTerm(16.7)) 124 assertParseOneTerm(t, "float2", "-16.7", FloatNumberTerm(-16.7)) 125 assertParseOneTerm(t, "exponent", "6e7", FloatNumberTerm(6e7)) 126 assertParseOneTerm(t, "string", "\"a string\"", StringTerm("a string")) 127 assertParseOneTerm(t, "string", "\"a string u6abc7def8abc0def with unicode\"", StringTerm("a string u6abc7def8abc0def with unicode")) 128 assertParseErrorContains(t, "hex", "6abc", "illegal number format") 129 assertParseErrorContains(t, "non-terminated", "\"foo", "non-terminated string") 130 assertParseErrorContains(t, "non-terminated-raw", "`foo", "non-terminated string") 131 assertParseErrorContains(t, "non-string", "'a string'", "illegal token") 132 assertParseErrorContains(t, "non-number", "6zxy", "illegal number format") 133 assertParseErrorContains(t, "non-number2", "6d7", "illegal number format") 134 assertParseErrorContains(t, "non-number3", "6\"foo\"", "expected exactly one statement") // ?? 135 assertParseErrorContains(t, "non-number4", "6true", "illegal number format") 136 assertParseErrorContains(t, "non-number5", "6false", "illegal number format") 137 assertParseErrorContains(t, "non-number6", "6[null, null]", "illegal ref (head cannot be number)") // ?? 138 assertParseErrorContains(t, "non-number7", "6{\"foo\": \"bar\"}", "expected exactly one statement") 139 assertParseErrorContains(t, "non-number8", ".0.", "expected fraction") 140 assertParseErrorContains(t, "non-number9", "0e", "expected exponent") 141 assertParseErrorContains(t, "non-number10", "0e.", "expected exponent") 142 assertParseErrorContains(t, "non-number11", "0F", "illegal number format") 143 assertParseErrorContains(t, "non-number12", "00", "expected number") 144 assertParseErrorContains(t, "non-number13", "00.1", "expected number") 145 assertParseErrorContains(t, "non-number14", "-00", "expected number") 146 assertParseErrorContains(t, "non-number15", "-00.1", "expected number") 147 assertParseErrorContains(t, "non-number16", "-00.01", "expected number") 148 assertParseErrorContains(t, "non-number17", "00e1", "expected number") 149 assertParseErrorContains(t, "non-number18", "-00e1", "expected number") 150 assertParseErrorContains(t, "parsing float fails", "7e3000000000", "invalid float") 151 assertParseErrorContains(t, "float is +inf", "10245423601e680507880", "number too big") 152 assertParseErrorContains(t, "float is -inf", "-10245423601e680507880", "number too big") 153 154 // f := big.NewFloat(1); f.SetMantExp(f, -1e6); f.String() // => 1.010034059e-301030 (this takes ~9s) 155 assertParseErrorContains(t, "float exp < -1e5", "1.010034059e-301030", "number too big") 156 157 // g := big.NewFloat(1); g.SetMantExp(g, 1e6); g.String() // => 9.900656229e+301029 158 assertParseErrorContains(t, "float exp > 1e5", "9.900656229e+301029", "number too big") 159 } 160 161 func TestVarTerms(t *testing.T) { 162 assertParseOneTerm(t, "var", "foo", VarTerm("foo")) 163 assertParseOneTerm(t, "var", "foo_bar", VarTerm("foo_bar")) 164 assertParseOneTerm(t, "var", "foo0", VarTerm("foo0")) 165 assertParseOneTerm(t, "import prefix", "imports", VarTerm("imports")) 166 assertParseOneTerm(t, "not prefix", "not_foo", VarTerm("not_foo")) 167 assertParseOneTerm(t, `package prefix`, "packages", VarTerm("packages")) 168 assertParseOneTerm(t, `true prefix`, "trueish", VarTerm("trueish")) 169 assertParseOneTerm(t, `false prefix`, "false_flag", VarTerm("false_flag")) 170 assertParseOneTerm(t, `null prefix`, "nullable", VarTerm("nullable")) 171 assertParseError(t, "illegal token", `墳`) 172 assertParseError(t, "not keyword", "not") 173 assertParseError(t, `package keyword`, "package") 174 assertParseError(t, "import keyword", "import") 175 assertParseError(t, "import invalid path", "import x.") 176 } 177 178 func TestRefTerms(t *testing.T) { 179 assertParseOneTerm(t, "constants", "foo.bar.baz", RefTerm(VarTerm("foo"), StringTerm("bar"), StringTerm("baz"))) 180 assertParseOneTerm(t, "constants 2", "foo.bar[0].baz", RefTerm(VarTerm("foo"), StringTerm("bar"), IntNumberTerm(0), StringTerm("baz"))) 181 assertParseOneTerm(t, "variables", "foo.bar[0].baz[i]", RefTerm(VarTerm("foo"), StringTerm("bar"), IntNumberTerm(0), StringTerm("baz"), VarTerm("i"))) 182 assertParseOneTerm(t, "spaces", "foo[\"white space\"].bar", RefTerm(VarTerm("foo"), StringTerm("white space"), StringTerm("bar"))) 183 assertParseOneTerm(t, "nested", "foo[baz[1][borge[i]]].bar", RefTerm( 184 VarTerm("foo"), 185 RefTerm( 186 VarTerm("baz"), IntNumberTerm(1), RefTerm( 187 VarTerm("borge"), VarTerm("i"), 188 ), 189 ), 190 StringTerm("bar"), 191 )) 192 assertParseOneTerm(t, "composite operand 1", "foo[[1,2,3]].bar", RefTerm(VarTerm("foo"), ArrayTerm(NumberTerm("1"), NumberTerm("2"), NumberTerm("3")), StringTerm("bar"))) 193 assertParseOneTerm(t, "composite operand 2", `foo[{"foo": 2}].bar`, RefTerm(VarTerm("foo"), ObjectTerm(Item(StringTerm("foo"), NumberTerm("2"))), StringTerm("bar"))) 194 195 assertParseError(t, "missing component 1", "foo.") 196 assertParseError(t, "missing component 2", "foo[].bar") 197 assertParseError(t, "invalid composite operand", "foo[1,2]") 198 assertParseError(t, "invalid call", "bar(..") 199 assertParseError(t, "invalid ref", "bar[..") 200 assertParseError(t, "invalid ref head type number", "0[0]") 201 assertParseError(t, "invalid ref head type boolean", "true[0]") 202 assertParseError(t, "invalid ref head type string", `"foo"[0]`) 203 assertParseError(t, "invalid ref head type null", `null[0]`) 204 } 205 206 func TestObjectWithScalars(t *testing.T) { 207 assertParseOneTerm(t, "number", "{\"abc\": 7, \"def\": 8}", ObjectTerm(Item(StringTerm("abc"), IntNumberTerm(7)), Item(StringTerm("def"), IntNumberTerm(8)))) 208 assertParseOneTerm(t, "bool", "{\"abc\": false, \"def\": true}", ObjectTerm(Item(StringTerm("abc"), BooleanTerm(false)), Item(StringTerm("def"), BooleanTerm(true)))) 209 assertParseOneTerm(t, "string", "{\"abc\": \"foo\", \"def\": \"bar\"}", ObjectTerm(Item(StringTerm("abc"), StringTerm("foo")), Item(StringTerm("def"), StringTerm("bar")))) 210 assertParseOneTerm(t, "mixed", "{\"abc\": 7, \"def\": null}", ObjectTerm(Item(StringTerm("abc"), IntNumberTerm(7)), Item(StringTerm("def"), NullTerm()))) 211 assertParseOneTerm(t, "number key", "{8: 7, \"def\": null}", ObjectTerm(Item(IntNumberTerm(8), IntNumberTerm(7)), Item(StringTerm("def"), NullTerm()))) 212 assertParseOneTerm(t, "number key 2", "{8.5: 7, \"def\": null}", ObjectTerm(Item(FloatNumberTerm(8.5), IntNumberTerm(7)), Item(StringTerm("def"), NullTerm()))) 213 assertParseOneTerm(t, "bool key", "{true: false}", ObjectTerm(Item(BooleanTerm(true), BooleanTerm(false)))) 214 assertParseOneTerm(t, "trailing comma", `{"a": "bar", "b": 64, }`, ObjectTerm(Item(StringTerm("a"), StringTerm("bar")), Item(StringTerm("b"), IntNumberTerm(64)))) 215 assertParseOneTerm(t, "leading comma", `{, "a": "bar", "b": 64 }`, ObjectTerm(Item(StringTerm("a"), StringTerm("bar")), Item(StringTerm("b"), IntNumberTerm(64)))) 216 assertParseOneTerm(t, "leading comma not comprehension", `{, 1 | 1: "bar"}`, ObjectTerm(Item(CallTerm(RefTerm(VarTerm("or")), NumberTerm("1"), NumberTerm("1")), StringTerm("bar")))) 217 } 218 219 func TestObjectWithVars(t *testing.T) { 220 assertParseOneTerm(t, "var keys", "{foo: \"bar\", bar: 64}", ObjectTerm(Item(VarTerm("foo"), StringTerm("bar")), Item(VarTerm("bar"), IntNumberTerm(64)))) 221 assertParseOneTerm(t, "nested var keys", "{baz: {foo: \"bar\", bar: qux}}", ObjectTerm(Item(VarTerm("baz"), ObjectTerm(Item(VarTerm("foo"), StringTerm("bar")), Item(VarTerm("bar"), VarTerm("qux")))))) 222 assertParseOneTerm(t, "ambiguous or", `{ a: b+c | d }`, ObjectTerm(Item(VarTerm("a"), CallTerm(RefTerm(VarTerm("or")), CallTerm(RefTerm(VarTerm("plus")), VarTerm("b"), VarTerm("c")), VarTerm("d"))))) 223 } 224 225 func TestObjectWithRelation(t *testing.T) { 226 assertParseOneTerm(t, "relation term value", `{"x": 1+1}`, ObjectTerm( 227 Item(StringTerm("x"), CallTerm(RefTerm(VarTerm("plus")), IntNumberTerm(1), IntNumberTerm(1))), 228 )) 229 assertParseError(t, "invalid relation term value", `{"x": 0= }`) 230 } 231 232 func TestObjectFail(t *testing.T) { 233 assertParseError(t, "non-terminated 1", "{foo: bar, baz: [], qux: corge") 234 assertParseError(t, "non-terminated 2", "{foo: bar, baz: [], qux: ") 235 assertParseError(t, "non-terminated 3", "{foo: bar, baz: [], qux ") 236 assertParseError(t, "non-terminated 4", "{foo: bar, baz: [], ") 237 assertParseError(t, "missing separator", "{foo: bar baz: []}") 238 assertParseError(t, "missing start", "foo: bar, baz: [], qux: corge}") 239 assertParseError(t, "double comma", "{a:1,,b:2}") 240 assertParseError(t, "leading double comma", "{,,a:1}") 241 assertParseError(t, "trailing double comma", "{a:1,,}") 242 } 243 244 func TestArrayWithScalars(t *testing.T) { 245 assertParseOneTerm(t, "number", "[1,2,3,4.5]", ArrayTerm(IntNumberTerm(1), IntNumberTerm(2), IntNumberTerm(3), FloatNumberTerm(4.5))) 246 assertParseOneTerm(t, "bool", "[true, false, true]", ArrayTerm(BooleanTerm(true), BooleanTerm(false), BooleanTerm(true))) 247 assertParseOneTerm(t, "string", "[\"foo\", \"bar\"]", ArrayTerm(StringTerm("foo"), StringTerm("bar"))) 248 assertParseOneTerm(t, "mixed", "[null, true, 42]", ArrayTerm(NullTerm(), BooleanTerm(true), IntNumberTerm(42))) 249 assertParseOneTerm(t, "trailing comma - one element", "[null, ]", ArrayTerm(NullTerm())) 250 assertParseOneTerm(t, "trailing comma", "[null, true, ]", ArrayTerm(NullTerm(), BooleanTerm(true))) 251 assertParseOneTerm(t, "leading comma", "[, null, true]", ArrayTerm(NullTerm(), BooleanTerm(true))) 252 assertParseOneTerm(t, "leading comma not comprehension", "[, 1 | 1]", ArrayTerm(CallTerm(RefTerm(VarTerm("or")), NumberTerm("1"), NumberTerm("1")))) 253 assertParseOneTerm(t, "ambiguous or", "[ 1 + 2 | 3 ]", ArrayTerm(CallTerm(RefTerm(VarTerm("or")), CallTerm(RefTerm(VarTerm("plus")), NumberTerm("1"), NumberTerm("2")), NumberTerm("3")))) 254 } 255 256 func TestArrayWithVars(t *testing.T) { 257 assertParseOneTerm(t, "var elements", "[foo, bar, 42]", ArrayTerm(VarTerm("foo"), VarTerm("bar"), IntNumberTerm(42))) 258 assertParseOneTerm(t, "nested var elements", "[[foo, true], [null, bar], 42]", ArrayTerm(ArrayTerm(VarTerm("foo"), BooleanTerm(true)), ArrayTerm(NullTerm(), VarTerm("bar")), IntNumberTerm(42))) 259 } 260 261 func TestArrayFail(t *testing.T) { 262 assertParseError(t, "non-terminated 1", "[foo, bar") 263 assertParseError(t, "non-terminated 2", "[foo, bar, ") 264 assertParseError(t, "missing separator", "[foo bar]") 265 assertParseError(t, "missing start", "foo, bar, baz]") 266 assertParseError(t, "bad term", "[!!!]") 267 assertParseError(t, "double comma", "[a,,b]") 268 assertParseError(t, "leading double comma", "[,,a]") 269 assertParseError(t, "trailing double comma", "[a,,]") 270 } 271 272 func TestSetWithScalars(t *testing.T) { 273 assertParseOneTerm(t, "number", "{1,2,3,4.5}", SetTerm(IntNumberTerm(1), IntNumberTerm(2), IntNumberTerm(3), FloatNumberTerm(4.5))) 274 assertParseOneTerm(t, "bool", "{true, false, true}", SetTerm(BooleanTerm(true), BooleanTerm(false), BooleanTerm(true))) 275 assertParseOneTerm(t, "string", "{\"foo\", \"bar\"}", SetTerm(StringTerm("foo"), StringTerm("bar"))) 276 assertParseOneTerm(t, "mixed", "{null, true, 42}", SetTerm(NullTerm(), BooleanTerm(true), IntNumberTerm(42))) 277 assertParseOneTerm(t, "trailing comma", "{null, true,}", SetTerm(NullTerm(), BooleanTerm(true))) 278 assertParseOneTerm(t, "leading comma", "{, null, true}", SetTerm(NullTerm(), BooleanTerm(true))) 279 assertParseOneTerm(t, "leading comma not comprehension", "{, 1 | 1}", SetTerm(CallTerm(RefTerm(VarTerm("or")), NumberTerm("1"), NumberTerm("1")))) 280 assertParseOneTerm(t, "ambiguous or", "{ 1 + 2 | 3}", SetTerm(CallTerm(RefTerm(VarTerm("or")), CallTerm(RefTerm(VarTerm("plus")), NumberTerm("1"), NumberTerm("2")), NumberTerm("3")))) 281 } 282 283 func TestSetWithVars(t *testing.T) { 284 assertParseOneTerm(t, "var elements", "{foo, bar, 42}", SetTerm(VarTerm("foo"), VarTerm("bar"), IntNumberTerm(42))) 285 assertParseOneTerm(t, "nested var elements", "{[foo, true], {null, bar}, set()}", SetTerm(ArrayTerm(VarTerm("foo"), BooleanTerm(true)), SetTerm(NullTerm(), VarTerm("bar")), SetTerm())) 286 } 287 288 func TestSetFail(t *testing.T) { 289 assertParseError(t, "non-terminated 1", "set(") 290 assertParseError(t, "non-terminated 2", "{foo, bar") 291 assertParseError(t, "non-terminated 3", "{foo, bar, ") 292 assertParseError(t, "missing separator", "{foo bar}") 293 assertParseError(t, "missing start", "foo, bar, baz}") 294 assertParseError(t, "bad term", "{!!!}") 295 assertParseError(t, "double comma", "{a,,b}") 296 assertParseError(t, "leading double comma", "{,,a}") 297 assertParseError(t, "trailing double comma", "{a,,}") 298 } 299 300 func TestEmptyComposites(t *testing.T) { 301 assertParseOneTerm(t, "empty object", "{}", ObjectTerm()) 302 assertParseOneTerm(t, "empty array", "[]", ArrayTerm()) 303 assertParseOneTerm(t, "empty set", "set()", SetTerm()) 304 } 305 306 func TestNestedComposites(t *testing.T) { 307 assertParseOneTerm(t, "nested composites", "[{foo: [\"bar\", {baz}]}]", ArrayTerm(ObjectTerm(Item(VarTerm("foo"), ArrayTerm(StringTerm("bar"), SetTerm(VarTerm("baz"))))))) 308 } 309 310 func TestCompositesWithRefs(t *testing.T) { 311 ref1 := RefTerm(VarTerm("a"), VarTerm("i"), StringTerm("b")) 312 ref2 := RefTerm(VarTerm("c"), IntNumberTerm(0), StringTerm("d"), StringTerm("e"), VarTerm("j")) 313 assertParseOneTerm(t, "ref keys", "[{a[i].b: 8, c[0][\"d\"].e[j]: f}]", ArrayTerm(ObjectTerm(Item(ref1, IntNumberTerm(8)), Item(ref2, VarTerm("f"))))) 314 assertParseOneTerm(t, "ref values", "[{8: a[i].b, f: c[0][\"d\"].e[j]}]", ArrayTerm(ObjectTerm(Item(IntNumberTerm(8), ref1), Item(VarTerm("f"), ref2)))) 315 assertParseOneTerm(t, "ref values (sets)", `{a[i].b, {c[0]["d"].e[j]}}`, SetTerm(ref1, SetTerm(ref2))) 316 } 317 318 func TestArrayComprehensions(t *testing.T) { 319 320 nestedTerm := `[{"x": [a[i] | xs = [{"a": ["baz", j]} | q[p]; p.a != "bar"; j = "foo"]; xs[j].a[k] = "foo"]}]` 321 nestedExpected := ArrayTerm( 322 ObjectTerm(Item( 323 StringTerm("x"), 324 ArrayComprehensionTerm( 325 RefTerm(VarTerm("a"), VarTerm("i")), 326 NewBody( 327 Equality.Expr( 328 VarTerm("xs"), 329 ArrayComprehensionTerm( 330 ObjectTerm(Item(StringTerm("a"), ArrayTerm(StringTerm("baz"), VarTerm("j")))), 331 NewBody( 332 NewExpr(RefTerm(VarTerm("q"), VarTerm("p"))), 333 NotEqual.Expr(RefTerm(VarTerm("p"), StringTerm("a")), StringTerm("bar")), 334 Equality.Expr(VarTerm("j"), StringTerm("foo")), 335 ), 336 ), 337 ), 338 Equality.Expr( 339 RefTerm(VarTerm("xs"), VarTerm("j"), StringTerm("a"), VarTerm("k")), 340 StringTerm("foo"), 341 ), 342 ), 343 ), 344 )), 345 ) 346 assertParseOneTerm(t, "nested", nestedTerm, nestedExpected) 347 assertParseOneTerm(t, "ambiguous or", "[ a | b ]", ArrayComprehensionTerm( 348 VarTerm("a"), 349 MustParseBody("b"), 350 )) 351 } 352 353 func TestObjectComprehensions(t *testing.T) { 354 nestedTerm := `[{"x": {a[i]: b[i] | xs = {"foo":{"a": ["baz", j]} | q[p]; p.a != "bar"; j = "foo"}; xs[j].a[k] = "foo"}}]` 355 nestedExpected := ArrayTerm( 356 ObjectTerm(Item( 357 StringTerm("x"), 358 ObjectComprehensionTerm( 359 RefTerm(VarTerm("a"), VarTerm("i")), 360 RefTerm(VarTerm("b"), VarTerm("i")), 361 NewBody( 362 Equality.Expr( 363 VarTerm("xs"), 364 ObjectComprehensionTerm( 365 StringTerm("foo"), 366 ObjectTerm(Item(StringTerm("a"), ArrayTerm(StringTerm("baz"), VarTerm("j")))), 367 NewBody( 368 NewExpr(RefTerm(VarTerm("q"), VarTerm("p"))), 369 NotEqual.Expr(RefTerm(VarTerm("p"), StringTerm("a")), StringTerm("bar")), 370 Equality.Expr(VarTerm("j"), StringTerm("foo")), 371 ), 372 ), 373 ), 374 Equality.Expr( 375 RefTerm(VarTerm("xs"), VarTerm("j"), StringTerm("a"), VarTerm("k")), 376 StringTerm("foo"), 377 ), 378 ), 379 ), 380 )), 381 ) 382 assertParseOneTerm(t, "nested", nestedTerm, nestedExpected) 383 assertParseOneTerm(t, "ambiguous or", "{ 1+2: 3 | 4}", ObjectComprehensionTerm( 384 CallTerm(RefTerm(VarTerm("plus")), NumberTerm("1"), NumberTerm("2")), 385 NumberTerm("3"), 386 MustParseBody("4"), 387 )) 388 } 389 390 func TestObjectComprehensionError(t *testing.T) { 391 assertParseError(t, "bad body", "{x: y|!!!}") 392 } 393 394 func TestSetComprehensions(t *testing.T) { 395 nestedTerm := `[{"x": {a[i] | xs = {{"a": ["baz", j]} | q[p]; p.a != "bar"; j = "foo"}; xs[j].a[k] = "foo"}}]` 396 nestedExpected := ArrayTerm( 397 ObjectTerm(Item( 398 StringTerm("x"), 399 SetComprehensionTerm( 400 RefTerm(VarTerm("a"), VarTerm("i")), 401 NewBody( 402 Equality.Expr( 403 VarTerm("xs"), 404 SetComprehensionTerm( 405 ObjectTerm(Item(StringTerm("a"), ArrayTerm(StringTerm("baz"), VarTerm("j")))), 406 NewBody( 407 NewExpr(RefTerm(VarTerm("q"), VarTerm("p"))), 408 NotEqual.Expr(RefTerm(VarTerm("p"), StringTerm("a")), StringTerm("bar")), 409 Equality.Expr(VarTerm("j"), StringTerm("foo")), 410 ), 411 ), 412 ), 413 Equality.Expr( 414 RefTerm(VarTerm("xs"), VarTerm("j"), StringTerm("a"), VarTerm("k")), 415 StringTerm("foo"), 416 ), 417 ), 418 ), 419 )), 420 ) 421 422 assertParseOneTerm(t, "nested", nestedTerm, nestedExpected) 423 assertParseOneTerm(t, "ambiguous or", "{ a | b }", SetComprehensionTerm( 424 VarTerm("a"), 425 MustParseBody("b"), 426 )) 427 } 428 429 func TestSetComprehensionError(t *testing.T) { 430 assertParseError(t, "bad body", "{x|!!!}") 431 } 432 433 func TestSetComprehensionsAlone(t *testing.T) { 434 input := `{k | a = [1,2,3]; a[k]}` 435 436 expected := SetComprehensionTerm( 437 VarTerm("k"), 438 NewBody( 439 Equality.Expr( 440 VarTerm("a"), 441 ArrayTerm(NumberTerm("1"), NumberTerm("2"), NumberTerm("3")), 442 ), 443 &Expr{ 444 Terms: RefTerm(VarTerm("a"), VarTerm("k")), 445 }, 446 ), 447 ) 448 449 assertParseOneTerm(t, "alone", input, expected) 450 } 451 452 func TestCalls(t *testing.T) { 453 454 assertParseOneExpr(t, "ne", "100 != 200", NotEqual.Expr(IntNumberTerm(100), IntNumberTerm(200))) 455 assertParseOneExpr(t, "gt", "17.4 > \"hello\"", GreaterThan.Expr(FloatNumberTerm(17.4), StringTerm("hello"))) 456 assertParseOneExpr(t, "lt", "17.4 < \"hello\"", LessThan.Expr(FloatNumberTerm(17.4), StringTerm("hello"))) 457 assertParseOneExpr(t, "gte", "17.4 >= \"hello\"", GreaterThanEq.Expr(FloatNumberTerm(17.4), StringTerm("hello"))) 458 assertParseOneExpr(t, "lte", "17.4 <= \"hello\"", LessThanEq.Expr(FloatNumberTerm(17.4), StringTerm("hello"))) 459 460 left2 := ArrayTerm(ObjectTerm(Item(FloatNumberTerm(14.2), BooleanTerm(true)), Item(StringTerm("a"), NullTerm()))) 461 right2 := ObjectTerm(Item(VarTerm("foo"), ObjectTerm(Item(RefTerm(VarTerm("a"), StringTerm("b"), IntNumberTerm(0)), ArrayTerm(IntNumberTerm(10)))))) 462 assertParseOneExpr(t, "composites", "[{14.2: true, \"a\": null}] != {foo: {a.b[0]: [10]}}", NotEqual.Expr(left2, right2)) 463 464 assertParseOneExpr(t, "plus", "1 + 2", Plus.Expr(IntNumberTerm(1), IntNumberTerm(2))) 465 assertParseOneExpr(t, "minus", "1 - 2", Minus.Expr(IntNumberTerm(1), IntNumberTerm(2))) 466 assertParseOneExpr(t, "mul", "1 * 2", Multiply.Expr(IntNumberTerm(1), IntNumberTerm(2))) 467 assertParseOneExpr(t, "div", "1 / 2", Divide.Expr(IntNumberTerm(1), IntNumberTerm(2))) 468 assertParseOneExpr(t, "rem", "3 % 2", Rem.Expr(IntNumberTerm(3), IntNumberTerm(2))) 469 assertParseOneExpr(t, "and", "{1,2,3} & {2,3,4}", And.Expr(SetTerm(IntNumberTerm(1), IntNumberTerm(2), IntNumberTerm(3)), SetTerm(IntNumberTerm(2), IntNumberTerm(3), IntNumberTerm(4)))) 470 assertParseOneExpr(t, "or", "{1,2,3} | {3,4,5}", Or.Expr(SetTerm(IntNumberTerm(1), IntNumberTerm(2), IntNumberTerm(3)), SetTerm(IntNumberTerm(3), IntNumberTerm(4), IntNumberTerm(5)))) 471 472 assertParseOneExpr(t, "call", "count([true, false])", Count.Expr(ArrayTerm(BooleanTerm(true), BooleanTerm(false)))) 473 assertParseOneExpr(t, "call-ref", "foo.bar(1)", NewExpr( 474 []*Term{RefTerm(VarTerm("foo"), StringTerm("bar")), 475 IntNumberTerm(1)})) 476 assertParseOneExpr(t, "call-void", "foo()", NewExpr( 477 []*Term{RefTerm(VarTerm("foo"))})) 478 479 opts := ParserOptions{FutureKeywords: []string{"in"}} 480 assertParseOneExpr(t, "internal.member_2", "x in xs", Member.Expr(VarTerm("x"), VarTerm("xs")), opts) 481 assertParseOneExpr(t, "internal.member_3", "x, y in xs", MemberWithKey.Expr(VarTerm("x"), VarTerm("y"), VarTerm("xs")), opts) 482 } 483 484 func TestInfixExpr(t *testing.T) { 485 assertParseOneExpr(t, "scalars 1", "true = false", Equality.Expr(BooleanTerm(true), BooleanTerm(false))) 486 assertParseOneExpr(t, "scalars 2", "3.14 = null", Equality.Expr(FloatNumberTerm(3.14), NullTerm())) 487 assertParseOneExpr(t, "scalars 3", "42 = \"hello world\"", Equality.Expr(IntNumberTerm(42), StringTerm("hello world"))) 488 assertParseOneExpr(t, "vars 1", "hello = world", Equality.Expr(VarTerm("hello"), VarTerm("world"))) 489 assertParseOneExpr(t, "vars 2", "42 = hello", Equality.Expr(IntNumberTerm(42), VarTerm("hello"))) 490 491 ref1 := RefTerm(VarTerm("foo"), IntNumberTerm(0), StringTerm("bar"), VarTerm("x")) 492 ref2 := RefTerm(VarTerm("baz"), BooleanTerm(false), StringTerm("qux"), StringTerm("hello")) 493 assertParseOneExpr(t, "refs 1", "foo[0].bar[x] = baz[false].qux[\"hello\"]", Equality.Expr(ref1, ref2)) 494 495 left1 := ObjectTerm(Item(VarTerm("a"), ArrayTerm(ref1))) 496 right1 := ArrayTerm(ObjectTerm(Item(IntNumberTerm(42), BooleanTerm(true)))) 497 assertParseOneExpr(t, "composites", "{a: [foo[0].bar[x]]} = [{42: true}]", Equality.Expr(left1, right1)) 498 499 assertParseOneExpr(t, "plus", "x = 1 + 2", Equality.Expr(VarTerm("x"), Plus.Call(IntNumberTerm(1), IntNumberTerm(2)))) 500 assertParseOneExpr(t, "plus reverse", "1 + 2 = x", Equality.Expr(Plus.Call(IntNumberTerm(1), IntNumberTerm(2)), VarTerm("x"))) 501 502 assertParseOneExpr(t, "call", "count([true, false]) = x", Equality.Expr(Count.Call(ArrayTerm(BooleanTerm(true), BooleanTerm(false))), VarTerm("x"))) 503 assertParseOneExpr(t, "call-reverse", "x = count([true, false])", Equality.Expr(VarTerm("x"), Count.Call(ArrayTerm(BooleanTerm(true), BooleanTerm(false))))) 504 } 505 506 func TestNegatedExpr(t *testing.T) { 507 assertParseOneTermNegated(t, "scalars 1", "not true", BooleanTerm(true)) 508 assertParseOneTermNegated(t, "scalars 2", "not \"hello\"", StringTerm("hello")) 509 assertParseOneTermNegated(t, "scalars 3", "not 100", IntNumberTerm(100)) 510 assertParseOneTermNegated(t, "scalars 4", "not null", NullTerm()) 511 assertParseOneTermNegated(t, "var", "not x", VarTerm("x")) 512 assertParseOneTermNegated(t, "ref", "not x[y].z", RefTerm(VarTerm("x"), VarTerm("y"), StringTerm("z"))) 513 assertParseOneExprNegated(t, "vars", "not x = y", Equality.Expr(VarTerm("x"), VarTerm("y"))) 514 515 ref1 := RefTerm(VarTerm("x"), VarTerm("y"), StringTerm("z"), VarTerm("a")) 516 517 assertParseOneExprNegated(t, "membership", "not x[y].z[a] = \"b\"", Equality.Expr(ref1, StringTerm("b"))) 518 assertParseOneExprNegated(t, "misc. builtin", "not sorted(x[y].z[a])", NewExpr([]*Term{RefTerm(VarTerm("sorted")), ref1})) 519 } 520 521 func TestExprWith(t *testing.T) { 522 assertParseOneExpr(t, "input", "data.foo with input as baz", &Expr{ 523 Terms: MustParseTerm("data.foo"), 524 With: []*With{ 525 { 526 Target: NewTerm(InputRootRef), 527 Value: VarTerm("baz"), 528 }, 529 }, 530 }) 531 532 assertParseOneExpr(t, "builtin/ref target/composites", `plus(data.foo, 1, x) with input.com.acmecorp.obj as {"count": [{1,2,3}]}`, &Expr{ 533 Terms: MustParseExpr("plus(data.foo, 1, x)").Terms, 534 With: []*With{ 535 { 536 Target: MustParseTerm("input.com.acmecorp.obj"), 537 Value: MustParseTerm(`{"count": [{1,2,3}]}`), 538 }, 539 }, 540 }) 541 542 assertParseOneExpr(t, "multiple", `data.foo with input.obj as baz with input.com.acmecorp.obj as {"count": [{1,2,3}]}`, &Expr{ 543 Terms: MustParseTerm("data.foo"), 544 With: []*With{ 545 { 546 Target: MustParseTerm("input.obj"), 547 Value: VarTerm("baz"), 548 }, 549 { 550 Target: MustParseTerm("input.com.acmecorp.obj"), 551 Value: MustParseTerm(`{"count": [{1,2,3}]}`), 552 }, 553 }, 554 }) 555 556 assertParseOneExpr(t, "variable target", "true with x as 1", &Expr{ 557 Terms: BooleanTerm(true), 558 With: []*With{ 559 { 560 Target: VarTerm("x"), 561 Value: IntNumberTerm(1), 562 }, 563 }, 564 }) 565 } 566 567 func TestExprWithLocation(t *testing.T) { 568 cases := []struct { 569 note string 570 input string 571 expected []*Location 572 }{ 573 { 574 note: "base", 575 input: "a with b as c", 576 expected: []*Location{ 577 { 578 Row: 1, 579 Col: 3, 580 Offset: 2, 581 Text: []byte("with b as c"), 582 }, 583 }, 584 }, 585 { 586 note: "with line break", 587 input: "a with b\nas c", 588 expected: []*Location{ 589 { 590 Row: 1, 591 Col: 3, 592 Offset: 2, 593 Text: []byte("with b\nas c"), 594 }, 595 }, 596 }, 597 { 598 note: "multiple withs on single line", 599 input: "a with b as c with d as e", 600 expected: []*Location{ 601 { 602 Row: 1, 603 Col: 3, 604 Offset: 2, 605 Text: []byte("with b as c"), 606 }, 607 { 608 Row: 1, 609 Col: 15, 610 Offset: 14, 611 Text: []byte("with d as e"), 612 }, 613 }, 614 }, 615 { 616 note: "multiple withs on multiple line", 617 input: "a with b as c\n\t\twith d as e", 618 expected: []*Location{ 619 { 620 Row: 1, 621 Col: 3, 622 Offset: 2, 623 Text: []byte("with b as c"), 624 }, 625 { 626 Row: 2, 627 Col: 3, 628 Offset: 16, 629 Text: []byte("with d as e"), 630 }, 631 }, 632 }, 633 } 634 635 for _, tc := range cases { 636 t.Run(tc.note, func(t *testing.T) { 637 parsed, err := ParseStatement(tc.input) 638 if err != nil { 639 t.Errorf("Unexpected error on %s: %s", tc.input, err) 640 return 641 } 642 643 body := parsed.(Body) 644 if len(body) != 1 { 645 t.Errorf("Parser returned multiple expressions: %v", body) 646 return 647 } 648 expr := body[0] 649 if len(expr.With) != len(tc.expected) { 650 t.Fatalf("Expected %d with statements, got %d", len(expr.With), len(tc.expected)) 651 } 652 for i, with := range expr.With { 653 if !with.Location.Equal(tc.expected[i]) { 654 t.Errorf("Expected location %+v for '%v' but got %+v ", *(tc.expected[i]), with.String(), *with.Location) 655 } 656 } 657 }) 658 } 659 } 660 661 func TestSomeDeclExpr(t *testing.T) { 662 opts := ParserOptions{FutureKeywords: []string{"in"}} 663 664 assertParseOneExpr(t, "one", "some x", &Expr{ 665 Terms: &SomeDecl{ 666 Symbols: []*Term{ 667 VarTerm("x"), 668 }, 669 }, 670 }) 671 672 assertParseOneExpr(t, "internal.member_2", "some x in xs", &Expr{ 673 Terms: &SomeDecl{ 674 Symbols: []*Term{ 675 Member.Call( 676 VarTerm("x"), 677 VarTerm("xs"), 678 ), 679 }, 680 }, 681 }, opts) 682 683 assertParseOneExpr(t, "internal.member_3", "some x, y in xs", &Expr{ 684 Terms: &SomeDecl{ 685 Symbols: []*Term{ 686 MemberWithKey.Call( 687 VarTerm("x"), 688 VarTerm("y"), 689 VarTerm("xs"), 690 ), 691 }, 692 }, 693 }, opts) 694 695 assertParseErrorContains(t, "not some", "not some x, y in xs", 696 "unexpected some keyword: illegal negation of 'some'", 697 opts) 698 699 assertParseErrorContains(t, "some + function call", "some f(x)", 700 "expected `x in xs` or `x, y in xs` expression") 701 702 assertParseOneExpr(t, "multiple", "some x, y", &Expr{ 703 Terms: &SomeDecl{ 704 Symbols: []*Term{ 705 VarTerm("x"), 706 VarTerm("y"), 707 }, 708 }, 709 }, opts) 710 711 assertParseOneExpr(t, "multiple split across lines", `some x, y, 712 z`, &Expr{ 713 Terms: &SomeDecl{ 714 Symbols: []*Term{ 715 VarTerm("x"), 716 VarTerm("y"), 717 VarTerm("z"), 718 }, 719 }, 720 }) 721 722 assertParseRule(t, "whitespace separated", ` 723 724 p[x] { 725 some x 726 q[x] 727 } 728 `, &Rule{ 729 Head: NewHead(Var("p"), VarTerm("x")), 730 Body: NewBody( 731 NewExpr(&SomeDecl{Symbols: []*Term{VarTerm("x")}}), 732 NewExpr(RefTerm(VarTerm("q"), VarTerm("x"))), 733 ), 734 }) 735 736 assertParseRule(t, "whitespace separated, following `in` rule ref", ` 737 p[x] { 738 some x 739 in[x] 740 } 741 `, &Rule{ 742 Head: NewHead(Var("p"), VarTerm("x")), 743 Body: NewBody( 744 NewExpr(&SomeDecl{Symbols: []*Term{VarTerm("x")}}), 745 NewExpr(RefTerm(VarTerm("in"), VarTerm("x"))), 746 ), 747 }) 748 749 assertParseErrorContains(t, "some x in ... usage is hinted properly", ` 750 p[x] { 751 some x in {"foo": "bar"} 752 }`, 753 "unexpected ident token: expected \\n or ; or } (hint: `import future.keywords.in` for `some x in xs` expressions)") 754 755 assertParseErrorContains(t, "some x, y in ... usage is hinted properly", ` 756 p[y] = x { 757 some x, y in {"foo": "bar"} 758 }`, 759 "unexpected ident token: expected \\n or ; or } (hint: `import future.keywords.in` for `some x in xs` expressions)") 760 761 assertParseRule(t, "whitespace terminated", ` 762 763 p[x] { 764 some x 765 x 766 } 767 `, &Rule{ 768 Head: NewHead(Var("p"), VarTerm("x")), 769 Body: NewBody( 770 NewExpr(&SomeDecl{Symbols: []*Term{VarTerm("x")}}), 771 NewExpr(VarTerm("x")), 772 ), 773 }) 774 775 assertParseOneExpr(t, "with modifier on expr", "some x, y in input with input as []", 776 &Expr{ 777 Terms: &SomeDecl{ 778 Symbols: []*Term{ 779 MemberWithKey.Call( 780 VarTerm("x"), 781 VarTerm("y"), 782 NewTerm(MustParseRef("input")), 783 ), 784 }, 785 }, 786 With: []*With{{Value: ArrayTerm(), Target: NewTerm(MustParseRef("input"))}}, 787 }, opts) 788 789 assertParseErrorContains(t, "invalid domain (internal.member_2)", "some internal.member_2()", "illegal domain", opts) 790 assertParseErrorContains(t, "invalid domain (internal.member_3)", "some internal.member_3()", "illegal domain", opts) 791 792 } 793 794 func TestEvery(t *testing.T) { 795 opts := ParserOptions{unreleasedKeywords: true, FutureKeywords: []string{"every"}} 796 assertParseOneExpr(t, "simple", "every x in xs { true }", 797 &Expr{ 798 Terms: &Every{ 799 Value: VarTerm("x"), 800 Domain: VarTerm("xs"), 801 Body: []*Expr{ 802 NewExpr(BooleanTerm(true)), 803 }, 804 }, 805 }, 806 opts) 807 808 assertParseOneExpr(t, "with key", "every k, v in [1,2] { true }", 809 &Expr{ 810 Terms: &Every{ 811 Key: VarTerm("k"), 812 Value: VarTerm("v"), 813 Domain: ArrayTerm(IntNumberTerm(1), IntNumberTerm(2)), 814 Body: []*Expr{ 815 NewExpr(BooleanTerm(true)), 816 }, 817 }, 818 }, opts) 819 820 assertParseErrorContains(t, "arbitrary term", "every 10", "expected `x[, y] in xs { ... }` expression", opts) 821 assertParseErrorContains(t, "non-var value", "every 10 in xs { true }", "unexpected { token: expected value to be a variable", opts) 822 assertParseErrorContains(t, "non-var key", "every 10, x in xs { true }", "unexpected { token: expected key to be a variable", opts) 823 assertParseErrorContains(t, "arbitrary call", "every f(10)", "expected `x[, y] in xs { ... }` expression", opts) 824 assertParseErrorContains(t, "no body", "every x in xs", "missing body", opts) 825 assertParseErrorContains(t, "invalid body", "every x in xs { + }", "unexpected plus token", opts) 826 assertParseErrorContains(t, "not every", "not every x in xs { true }", "unexpected every keyword: illegal negation of 'every'", opts) 827 828 assertParseOneExpr(t, `"every" kw implies "in" kw`, "x in xs", Member.Expr( 829 VarTerm("x"), 830 VarTerm("xs"), 831 ), opts) 832 833 assertParseOneExpr(t, "with modifier on expr", "every x in input { x } with input as []", 834 &Expr{ 835 Terms: &Every{ 836 Value: VarTerm("x"), 837 Domain: NewTerm(MustParseRef("input")), 838 Body: []*Expr{ 839 NewExpr(VarTerm("x")), 840 }, 841 }, 842 With: []*With{{Value: ArrayTerm(), Target: NewTerm(MustParseRef("input"))}}, 843 }, opts) 844 845 assertParseErrorContains(t, "every x, y in ... usage is hinted properly", ` 846 p { 847 every x, y in {"foo": "bar"} { is_string(x); is_string(y) } 848 }`, 849 "unexpected ident token: expected \\n or ; or } (hint: `import future.keywords.every` for `every x in xs { ... }` expressions)") 850 851 assertParseErrorContains(t, "not every 'every' gets a hint", ` 852 p { 853 every x 854 }`, 855 "unexpected ident token: expected \\n or ; or }\n\tevery x\n", // this asserts that the tail of the error message doesn't contain a hint 856 ) 857 858 assertParseErrorContains(t, "invalid domain (internal.member_2)", "every internal.member_2()", "illegal domain", opts) 859 assertParseErrorContains(t, "invalid domain (internal.member_3)", "every internal.member_3()", "illegal domain", opts) 860 } 861 862 func TestNestedExpressions(t *testing.T) { 863 864 n1 := IntNumberTerm(1) 865 n2 := IntNumberTerm(2) 866 n3 := IntNumberTerm(3) 867 n4 := IntNumberTerm(4) 868 n6 := IntNumberTerm(6) 869 x := VarTerm("x") 870 y := VarTerm("y") 871 z := VarTerm("z") 872 w := VarTerm("w") 873 f := RefTerm(VarTerm("f")) 874 g := RefTerm(VarTerm("g")) 875 876 tests := []struct { 877 note string 878 input string 879 expected *Expr 880 }{ 881 {"associativity", "1 + 2 * 6 / 3", 882 Plus.Expr( 883 n1, 884 Divide.Call( 885 Multiply.Call( 886 n2, 887 n6), 888 n3))}, 889 {"associativity - factors", "x * y / z % w", 890 Rem.Expr(Divide.Call(Multiply.Call(x, y), z), w)}, 891 {"associativity - factors", "w % z / x * y", 892 Multiply.Expr(Divide.Call(Rem.Call(w, z), x), y)}, 893 {"associativity - arithetic", "x + y - z", 894 Minus.Expr(Plus.Call(x, y), z)}, 895 {"associativity - arithmetic", "z - x + y", 896 Plus.Expr(Minus.Call(z, x), y)}, 897 {"associativity - and", "z & x & y", 898 And.Expr(And.Call(z, x), y)}, 899 {"associativity - or", "z | x | y", 900 Or.Expr(Or.Call(z, x), y)}, 901 {"associativity - relations", "x == y != z", 902 NotEqual.Expr(Equal.Call(x, y), z)}, 903 {"grouping", "(1 + 2 * 6 / 3) > 4", 904 GreaterThan.Expr( 905 Plus.Call( 906 n1, 907 Divide.Call( 908 Multiply.Call( 909 n2, 910 n6), 911 n3)), 912 n4)}, 913 {"nested parens", "(((1 + 2) * (6 / (3))) > 4) != false", 914 NotEqual.Expr( 915 GreaterThan.Call( 916 Multiply.Call( 917 Plus.Call( 918 n1, 919 n2), 920 Divide.Call( 921 n6, 922 n3)), 923 n4, 924 ), 925 BooleanTerm(false))}, 926 {"bitwise or", "x + 1 | 2", Or.Expr(Plus.Call(x, n1), n2)}, 927 {"bitwise and", "x + 1 | 2 & 3", Or.Expr(Plus.Call(x, n1), And.Call(n2, n3))}, 928 {"array", "[x + 1, y > 2, z]", NewExpr(ArrayTerm(Plus.Call(x, n1), GreaterThan.Call(y, n2), z))}, 929 {"object", "{x * 2: y < 2, z[3]: 1 + 6/2}", NewExpr( 930 ObjectTerm( 931 Item(Multiply.Call(x, n2), LessThan.Call(y, n2)), 932 Item(RefTerm(z, n3), Plus.Call(n1, Divide.Call(n6, n2))), 933 ), 934 )}, 935 {"set", "{x + 1, y + 2, set()}", NewExpr( 936 SetTerm( 937 Plus.Call(x, n1), 938 Plus.Call(y, n2), 939 SetTerm(), 940 ), 941 )}, 942 {"ref", `x[1][y + z[w + 1]].b`, NewExpr( 943 RefTerm( 944 x, 945 n1, 946 Plus.Call( 947 y, 948 RefTerm( 949 z, 950 Plus.Call(w, n1))), 951 StringTerm("b"), 952 ), 953 )}, 954 {"call void", "f()", NewExpr([]*Term{f})}, 955 {"call unary", "f(x)", NewExpr([]*Term{f, x})}, 956 {"call binary", "f(x, y)", NewExpr([]*Term{f, x, y})}, 957 {"call embedded", "f([g(x), y+1])", NewExpr([]*Term{ 958 f, 959 ArrayTerm( 960 CallTerm(g, x), 961 Plus.Call(y, n1))})}, 962 {"call fqn", "foo.bar(1)", NewExpr([]*Term{ 963 RefTerm(VarTerm("foo"), StringTerm("bar")), 964 n1, 965 })}, 966 {"unify", "x = 1", Equality.Expr(x, n1)}, 967 {"unify embedded", "1 + x = 2 - y", Equality.Expr(Plus.Call(n1, x), Minus.Call(n2, y))}, 968 {"not keyword", "not x = y", Equality.Expr(x, y).Complement()}, 969 {"with keyword", "x with p[q] as f([x+1])", NewExpr(x).IncludeWith( 970 RefTerm(VarTerm("p"), VarTerm("q")), 971 CallTerm(f, ArrayTerm(Plus.Call(x, n1))), 972 )}, 973 } 974 for _, tc := range tests { 975 t.Run(tc.note, func(t *testing.T) { 976 expr, err := ParseExpr(tc.input) 977 if err != nil { 978 t.Fatal(err) 979 } 980 if !expr.Equal(tc.expected) { 981 t.Fatalf("Expected %v but got %v", tc.expected, expr) 982 } 983 }) 984 } 985 } 986 987 func TestChainedCall(t *testing.T) { 988 result, err := ParseExpr("foo.bar(1)[0](1).baz") 989 if err != nil { 990 t.Fatal(err) 991 } 992 993 exp := NewExpr(RefTerm( 994 CallTerm( 995 RefTerm( 996 CallTerm( 997 RefTerm(VarTerm("foo"), StringTerm("bar")), 998 IntNumberTerm(1)), 999 IntNumberTerm(0)), 1000 IntNumberTerm(1)), 1001 StringTerm("baz"))) 1002 1003 if !result.Equal(exp) { 1004 t.Fatalf("expected %v but got: %v", exp, result) 1005 } 1006 } 1007 1008 func TestMultiLineBody(t *testing.T) { 1009 1010 input1 := ` 1011 x = 1 1012 y = 2 1013 z = [ i | [x,y] = arr 1014 arr[_] = i] 1015 ` 1016 1017 body1, err := ParseBody(input1) 1018 if err != nil { 1019 t.Fatalf("Unexpected parse error on enclosed body: %v", err) 1020 } 1021 1022 expected1 := MustParseBody(`x = 1; y = 2; z = [i | [x,y] = arr; arr[_] = i]`) 1023 1024 if !body1.Equal(expected1) { 1025 t.Errorf("Expected enclosed body to equal %v but got: %v", expected1, body1) 1026 } 1027 1028 // Check that parser can handle multiple expressions w/o enclosing braces. 1029 input2 := ` 1030 x = 1 ; # comment after semicolon 1031 y = 2 # comment without semicolon 1032 z = [ i | [x,y] = arr # comment in comprehension 1033 arr[_] = i] 1034 ` 1035 1036 body2, err := ParseBody(input2) 1037 if err != nil { 1038 t.Fatalf("Unexpected parse error on enclosed body: %v", err) 1039 } 1040 1041 if !body2.Equal(expected1) { 1042 t.Errorf("Expected unenclosed body to equal %v but got: %v", expected1, body1) 1043 } 1044 1045 assertParseOneBody(t, "whitespace following call", "f(x)\t\n [1]", NewBody( 1046 NewExpr( 1047 []*Term{ 1048 RefTerm(VarTerm("f")), 1049 VarTerm("x"), 1050 }, 1051 ), 1052 NewExpr( 1053 ArrayTerm(IntNumberTerm(1)), 1054 ), 1055 )) 1056 1057 assertParseOneBody(t, "whitespace following array", "[1]\t\n [2]", NewBody( 1058 NewExpr( 1059 ArrayTerm(IntNumberTerm(1)), 1060 ), 1061 NewExpr( 1062 ArrayTerm(IntNumberTerm(2)), 1063 ), 1064 )) 1065 1066 assertParseOneBody(t, "whitespace following set", "{1}\t\n {2}", NewBody( 1067 NewExpr( 1068 SetTerm(IntNumberTerm(1)), 1069 ), 1070 NewExpr( 1071 SetTerm(IntNumberTerm(2)), 1072 ), 1073 )) 1074 } 1075 1076 func TestBitwiseOrVsComprehension(t *testing.T) { 1077 1078 x := VarTerm("x") 1079 y := VarTerm("y") 1080 z := VarTerm("z") 1081 a := VarTerm("a") 1082 b := VarTerm("b") 1083 1084 tests := []struct { 1085 note string 1086 input string 1087 exp *Term 1088 }{ 1089 { 1090 note: "array containing bitwise or", 1091 input: "[x|y,z]", 1092 exp: ArrayTerm(Or.Call(x, y), z), 1093 }, 1094 { 1095 note: "array containing bitwise or - last element", 1096 input: "[z,x|y]", 1097 exp: ArrayTerm(z, Or.Call(x, y)), 1098 }, 1099 { 1100 note: "array containing bitwise or - middle", 1101 input: "[z,x|y,a]", 1102 exp: ArrayTerm(z, Or.Call(x, y), a), 1103 }, 1104 { 1105 note: "array containing single bitwise or", 1106 input: "[x|y,]", 1107 exp: ArrayTerm(Or.Call(x, y)), 1108 }, 1109 { 1110 note: "set containing bitwise or", 1111 input: "{x|y,z}", 1112 exp: SetTerm(Or.Call(x, y), z), 1113 }, 1114 { 1115 note: "set containing bitwise or - last element", 1116 input: "{z,x|y}", 1117 exp: SetTerm(z, Or.Call(x, y)), 1118 }, 1119 { 1120 note: "set containing bitwise or - middle", 1121 input: "{z,x|y,a}", 1122 exp: SetTerm(z, Or.Call(x, y), a), 1123 }, 1124 { 1125 note: "set containing single bitwise or", 1126 input: "{x|y,}", 1127 exp: SetTerm(Or.Call(x, y)), 1128 }, 1129 { 1130 note: "object containing bitwise or", 1131 input: "{x:y|z,a:b}", 1132 exp: ObjectTerm([2]*Term{x, Or.Call(y, z)}, [2]*Term{a, b}), 1133 }, 1134 { 1135 note: "object containing single bitwise or", 1136 input: "{x:y|z,}", 1137 exp: ObjectTerm([2]*Term{x, Or.Call(y, z)}), 1138 }, 1139 } 1140 1141 for _, tc := range tests { 1142 t.Run(tc.note, func(t *testing.T) { 1143 1144 term, err := ParseTerm(tc.input) 1145 if err != nil { 1146 t.Fatal(err) 1147 } 1148 1149 if !term.Equal(tc.exp) { 1150 t.Fatalf("Expected %v but got %v", tc.exp, term) 1151 } 1152 }) 1153 } 1154 1155 } 1156 1157 func TestPackage(t *testing.T) { 1158 ref1 := RefTerm(DefaultRootDocument, StringTerm("foo")) 1159 assertParsePackage(t, "single", `package foo`, &Package{Path: ref1.Value.(Ref)}) 1160 ref2 := RefTerm(DefaultRootDocument, StringTerm("f00"), StringTerm("bar_baz"), StringTerm("qux")) 1161 assertParsePackage(t, "multiple", `package f00.bar_baz.qux`, &Package{Path: ref2.Value.(Ref)}) 1162 ref3 := RefTerm(DefaultRootDocument, StringTerm("foo"), StringTerm("bar baz")) 1163 assertParsePackage(t, "space", `package foo["bar baz"]`, &Package{Path: ref3.Value.(Ref)}) 1164 assertParseError(t, "non-ground ref", "package foo[x]") 1165 assertParseError(t, "non-string value", "package foo.bar[42].baz") 1166 assertParseError(t, "invalid term", "package 42") 1167 assertParseError(t, "scanner error", "package foo.") 1168 assertParseError(t, "non-string first value", "package e().s") 1169 } 1170 1171 func TestImport(t *testing.T) { 1172 foo := RefTerm(VarTerm("input"), StringTerm("foo")) 1173 foobarbaz := RefTerm(VarTerm("input"), StringTerm("foo"), StringTerm("bar"), StringTerm("baz")) 1174 whitespace := RefTerm(VarTerm("input"), StringTerm("foo"), StringTerm("bar"), StringTerm("white space")) 1175 assertParseImport(t, "single-input", "import input", &Import{Path: RefTerm(InputRootDocument)}) 1176 assertParseImport(t, "single-data", "import data", &Import{Path: RefTerm(DefaultRootDocument)}) 1177 assertParseImport(t, "multiple", "import input.foo.bar.baz", &Import{Path: foobarbaz}) 1178 assertParseImport(t, "single alias", "import input.foo as bar", &Import{Path: foo, Alias: Var("bar")}) 1179 assertParseImport(t, "multiple alias", "import input.foo.bar.baz as qux", &Import{Path: foobarbaz, Alias: Var("qux")}) 1180 assertParseImport(t, "white space", "import input.foo.bar[\"white space\"]", &Import{Path: whitespace}) 1181 assertParseErrorContains(t, "non-ground ref", "import data.foo[x]", "rego_parse_error: unexpected var token: expecting string") 1182 assertParseErrorContains(t, "non-string", "import input.foo[0]", "rego_parse_error: unexpected number token: expecting string") 1183 assertParseErrorContains(t, "unknown root", "import foo.bar", "rego_parse_error: unexpected import path, must begin with one of: {data, future, input}, got: foo") 1184 assertParseErrorContains(t, "bad variable term", "import input as A(", "rego_parse_error: unexpected eof token: expected var") 1185 1186 _, _, err := ParseStatements("", "package foo\nimport bar.data\ndefault foo=1") 1187 if err == nil { 1188 t.Fatalf("Expected error, but got nil") 1189 } 1190 if len(err.(Errors)) > 1 { 1191 t.Fatalf("Expected a single error, got %s", err) 1192 } 1193 txt := err.(Errors)[0].Details.Lines()[0] 1194 expected := "import bar.data" 1195 if txt != expected { 1196 t.Fatalf("Expected error detail text '%s' but got '%s'", expected, txt) 1197 } 1198 } 1199 1200 func TestFutureImports(t *testing.T) { 1201 assertParseErrorContains(t, "future", "import future", "invalid import, must be `future.keywords`") 1202 assertParseErrorContains(t, "future.a", "import future.a", "invalid import, must be `future.keywords`") 1203 assertParseErrorContains(t, "unknown keyword", "import future.keywords.xyz", "unexpected keyword, must be one of [contains every if in]") 1204 assertParseErrorContains(t, "all keyword import + alias", "import future.keywords as xyz", "`future` imports cannot be aliased") 1205 assertParseErrorContains(t, "keyword import + alias", "import future.keywords.in as xyz", "`future` imports cannot be aliased") 1206 1207 assertParseImport(t, "import kw with kw in options", 1208 "import future.keywords.in", &Import{Path: RefTerm(VarTerm("future"), StringTerm("keywords"), StringTerm("in"))}, 1209 ParserOptions{FutureKeywords: []string{"in"}}) 1210 assertParseImport(t, "import kw with all kw in options", 1211 "import future.keywords.in", &Import{Path: RefTerm(VarTerm("future"), StringTerm("keywords"), StringTerm("in"))}, 1212 ParserOptions{AllFutureKeywords: true}) 1213 1214 mod := ` 1215 package p 1216 import future.keywords 1217 import future.keywords.in 1218 ` 1219 parsed := Module{ 1220 Package: MustParseStatement(`package p`).(*Package), 1221 Imports: []*Import{ 1222 MustParseStatement("import future.keywords").(*Import), 1223 MustParseStatement("import future.keywords.in").(*Import), 1224 }, 1225 } 1226 assertParseModule(t, "multiple imports, all kw in options", mod, &parsed, ParserOptions{AllFutureKeywords: true}) 1227 assertParseModule(t, "multiple imports, single in options", mod, &parsed, ParserOptions{FutureKeywords: []string{"in"}}) 1228 } 1229 1230 func TestFutureImportsExtraction(t *testing.T) { 1231 // These tests assert that "import future..." statements in policies cause 1232 // the proper keywords to be added to the parser's list of known keywords. 1233 tests := []struct { 1234 note, imp string 1235 exp map[string]tokens.Token 1236 }{ 1237 { 1238 note: "simple import", 1239 imp: "import future.keywords.in", 1240 exp: map[string]tokens.Token{"in": tokens.In}, 1241 }, 1242 { 1243 note: "all keywords imported", 1244 imp: "import future.keywords", 1245 exp: map[string]tokens.Token{"in": tokens.In}, 1246 }, 1247 { 1248 note: "all keywords + single keyword imported", 1249 imp: ` 1250 import future.keywords 1251 import future.keywords.in`, 1252 exp: map[string]tokens.Token{"in": tokens.In}, 1253 }, 1254 } 1255 for _, tc := range tests { 1256 t.Run(tc.note, func(t *testing.T) { 1257 parser := NewParser().WithFilename("").WithReader(bytes.NewBufferString(tc.imp)) 1258 _, _, errs := parser.Parse() 1259 if exp, act := 0, len(errs); exp != act { 1260 t.Fatalf("expected %d errors, got %d: %v", exp, act, errs) 1261 } 1262 for kw, exp := range tc.exp { 1263 act := parser.s.s.Keyword(kw) 1264 if act != exp { 1265 t.Errorf("expected keyword %q to yield token %v, got %v", kw, exp, act) 1266 } 1267 } 1268 }) 1269 } 1270 } 1271 1272 func TestIsValidImportPath(t *testing.T) { 1273 tests := []struct { 1274 path string 1275 expected error 1276 }{ 1277 {"[1,2,3]", fmt.Errorf("invalid path [1, 2, 3]: path must be ref or var")}, 1278 } 1279 1280 for _, tc := range tests { 1281 path := MustParseTerm(tc.path).Value 1282 result := IsValidImportPath(path) 1283 if tc.expected == nil && result != nil { 1284 t.Errorf("Unexpected error for %v: %v", path, result) 1285 } else if !reflect.DeepEqual(tc.expected, result) { 1286 t.Errorf("For %v expected %v but got: %v", path, tc.expected, result) 1287 } 1288 } 1289 1290 } 1291 1292 func TestRule(t *testing.T) { 1293 1294 assertParseRule(t, "constant", `p = true { true }`, &Rule{ 1295 Head: NewHead(Var("p"), nil, BooleanTerm(true)), 1296 Body: NewBody( 1297 &Expr{Terms: BooleanTerm(true)}, 1298 ), 1299 }) 1300 1301 assertParseRule(t, "set", `p[x] { x = 42 }`, &Rule{ 1302 Head: NewHead(Var("p"), VarTerm("x")), 1303 Body: NewBody( 1304 Equality.Expr(VarTerm("x"), IntNumberTerm(42)), 1305 ), 1306 }) 1307 1308 assertParseRule(t, "object", `p[x] = y { x = 42; y = "hello" }`, &Rule{ 1309 Head: NewHead(Var("p"), VarTerm("x"), VarTerm("y")), 1310 Body: NewBody( 1311 Equality.Expr(VarTerm("x"), IntNumberTerm(42)), 1312 Equality.Expr(VarTerm("y"), StringTerm("hello")), 1313 ), 1314 }) 1315 1316 assertParseRule(t, "constant composite", `p = [{"foo": [1, 2, 3, 4]}] { true }`, &Rule{ 1317 Head: NewHead(Var("p"), nil, ArrayTerm( 1318 ObjectTerm(Item(StringTerm("foo"), ArrayTerm(IntNumberTerm(1), IntNumberTerm(2), IntNumberTerm(3), IntNumberTerm(4)))))), 1319 Body: NewBody( 1320 &Expr{Terms: BooleanTerm(true)}, 1321 ), 1322 }) 1323 1324 assertParseRule(t, "true", `p = true { true }`, &Rule{ 1325 Head: NewHead(Var("p"), nil, BooleanTerm(true)), 1326 Body: NewBody( 1327 &Expr{Terms: BooleanTerm(true)}, 1328 ), 1329 }) 1330 1331 assertParseRule(t, "composites in head", `p[[{"x": [a, b]}]] { a = 1; b = 2 }`, &Rule{ 1332 Head: NewHead(Var("p"), ArrayTerm( 1333 ObjectTerm( 1334 Item(StringTerm("x"), ArrayTerm(VarTerm("a"), VarTerm("b"))), 1335 ), 1336 )), 1337 Body: NewBody( 1338 Equality.Expr(VarTerm("a"), IntNumberTerm(1)), 1339 Equality.Expr(VarTerm("b"), IntNumberTerm(2)), 1340 ), 1341 }) 1342 1343 assertParseRule(t, "refs in head", `p = data.foo[x] { x = 1 }`, &Rule{ 1344 Head: NewHead(Var("p"), nil, &Term{ 1345 Value: MustParseRef("data.foo[x]"), 1346 }), 1347 Body: MustParseBody("x = 1"), 1348 }) 1349 1350 assertParseRule(t, "refs in head", `p[data.foo[x]] { true }`, &Rule{ 1351 Head: NewHead(Var("p"), &Term{ 1352 Value: MustParseRef("data.foo[x]"), 1353 }), 1354 Body: MustParseBody("true"), 1355 }) 1356 1357 assertParseRule(t, "refs in head", `p[data.foo[x]] = data.bar[y] { true }`, &Rule{ 1358 Head: NewHead(Var("p"), &Term{ 1359 Value: MustParseRef("data.foo[x]"), 1360 }, &Term{ 1361 Value: MustParseRef("data.bar[y]"), 1362 }), 1363 Body: MustParseBody("true"), 1364 }) 1365 1366 assertParseRule(t, "data", `data = true { true }`, &Rule{ 1367 Head: NewHead(Var("data"), nil, MustParseTerm("true")), 1368 Body: MustParseBody("true"), 1369 }) 1370 1371 assertParseRule(t, "input", `input = true { true }`, &Rule{ 1372 Head: NewHead(Var("input"), nil, MustParseTerm("true")), 1373 Body: MustParseBody("true"), 1374 }) 1375 1376 assertParseRule(t, "default", `default allow = false`, &Rule{ 1377 Default: true, 1378 Head: NewHead(Var("allow"), nil, MustParseTerm("false")), 1379 Body: NewBody(NewExpr(BooleanTerm(true))), 1380 }) 1381 1382 assertParseRule(t, "default w/ assignment", `default allow := false`, &Rule{ 1383 Default: true, 1384 Head: &Head{ 1385 Name: "allow", 1386 Value: BooleanTerm(false), 1387 Assign: true, 1388 }, 1389 Body: NewBody(NewExpr(BooleanTerm(true))), 1390 }) 1391 1392 assertParseRule(t, "default w/ comprehension", `default widgets = [x | x = data.fooz[_]]`, &Rule{ 1393 Default: true, 1394 Head: NewHead(Var("widgets"), nil, MustParseTerm(`[x | x = data.fooz[_]]`)), 1395 Body: NewBody(NewExpr(BooleanTerm(true))), 1396 }) 1397 1398 assertParseRule(t, "one line with braces", `p[x] { x = data.a[_]; count(x, 3) }`, &Rule{ 1399 Head: NewHead(Var("p"), VarTerm("x")), 1400 Body: MustParseBody(`x = data.a[_]; count(x, 3)`), 1401 }) 1402 1403 assertParseRule(t, "multiple lines with braces", `p[[x, y]] { [data.a[0]] = [{"x": x}]; count(x, 3); sum(x, y); y > 100 }`, 1404 1405 &Rule{ 1406 Head: NewHead(Var("p"), MustParseTerm("[x, y]")), 1407 Body: MustParseBody(`[data.a[0]] = [{"x": x}]; count(x, 3); sum(x, y); y > 100`), 1408 }) 1409 1410 fxy := &Head{ 1411 Name: Var("f"), 1412 Args: Args{VarTerm("x")}, 1413 Value: VarTerm("y"), 1414 } 1415 1416 assertParseRule(t, "identity", `f(x) = y { y = x }`, &Rule{ 1417 Head: fxy, 1418 Body: NewBody( 1419 Equality.Expr(VarTerm("y"), VarTerm("x")), 1420 ), 1421 }) 1422 1423 assertParseRule(t, "composite arg", `f([x, y]) = z { split(x, y, z) }`, &Rule{ 1424 Head: &Head{ 1425 Name: Var("f"), 1426 Args: Args{ArrayTerm(VarTerm("x"), VarTerm("y"))}, 1427 Value: VarTerm("z"), 1428 }, 1429 Body: NewBody( 1430 Split.Expr(VarTerm("x"), VarTerm("y"), VarTerm("z")), 1431 ), 1432 }) 1433 1434 assertParseRule(t, "composite result", `f(1) = [x, y] { split("foo.bar", x, y) }`, &Rule{ 1435 Head: &Head{ 1436 Name: Var("f"), 1437 Args: Args{IntNumberTerm(1)}, 1438 Value: ArrayTerm(VarTerm("x"), VarTerm("y")), 1439 }, 1440 Body: NewBody( 1441 Split.Expr(StringTerm("foo.bar"), VarTerm("x"), VarTerm("y")), 1442 ), 1443 }) 1444 1445 assertParseRule(t, "expr terms: key", `p[f(x) + g(x)] { true }`, &Rule{ 1446 Head: &Head{ 1447 Name: Var("p"), 1448 Key: Plus.Call( 1449 CallTerm(RefTerm(VarTerm("f")), VarTerm("x")), 1450 CallTerm(RefTerm(VarTerm("g")), VarTerm("x")), 1451 ), 1452 }, 1453 Body: NewBody(NewExpr(BooleanTerm(true))), 1454 }) 1455 1456 assertParseRule(t, "expr terms: value", `p = f(x) + g(x) { true }`, &Rule{ 1457 Head: &Head{ 1458 Name: Var("p"), 1459 Value: Plus.Call( 1460 CallTerm(RefTerm(VarTerm("f")), VarTerm("x")), 1461 CallTerm(RefTerm(VarTerm("g")), VarTerm("x")), 1462 ), 1463 }, 1464 Body: NewBody(NewExpr(BooleanTerm(true))), 1465 }) 1466 1467 assertParseRule(t, "expr terms: args", `p(f(x) + g(x)) { true }`, &Rule{ 1468 Head: &Head{ 1469 Name: Var("p"), 1470 Args: Args{ 1471 Plus.Call( 1472 CallTerm(RefTerm(VarTerm("f")), VarTerm("x")), 1473 CallTerm(RefTerm(VarTerm("g")), VarTerm("x")), 1474 ), 1475 }, 1476 Value: BooleanTerm(true), 1477 }, 1478 Body: NewBody(NewExpr(BooleanTerm(true))), 1479 }) 1480 1481 assertParseRule(t, "assignment operator", `x := 1 { true }`, &Rule{ 1482 Head: &Head{ 1483 Name: Var("x"), 1484 Value: IntNumberTerm(1), 1485 Assign: true, 1486 }, 1487 Body: NewBody(NewExpr(BooleanTerm(true))), 1488 }) 1489 1490 assertParseRule(t, "else assignment", `x := 1 { false } else := 2`, &Rule{ 1491 Head: &Head{ 1492 Name: "x", 1493 Value: IntNumberTerm(1), 1494 Assign: true, 1495 }, 1496 Body: NewBody(NewExpr(BooleanTerm(false))), 1497 Else: &Rule{ 1498 Head: &Head{ 1499 Name: "x", 1500 Value: IntNumberTerm(2), 1501 Assign: true, 1502 }, 1503 Body: NewBody(NewExpr(BooleanTerm(true))), 1504 }, 1505 }) 1506 1507 assertParseRule(t, "partial assignment", `p[x] := y { true }`, &Rule{ 1508 Head: &Head{ 1509 Name: "p", 1510 Value: VarTerm("y"), 1511 Key: VarTerm("x"), 1512 Assign: true, 1513 }, 1514 Body: NewBody(NewExpr(BooleanTerm(true))), 1515 }) 1516 1517 assertParseRule(t, "function assignment", `f(x) := y { true }`, &Rule{ 1518 Head: &Head{ 1519 Name: "f", 1520 Value: VarTerm("y"), 1521 Args: Args{ 1522 VarTerm("x"), 1523 }, 1524 Assign: true, 1525 }, 1526 Body: NewBody(NewExpr(BooleanTerm(true))), 1527 }) 1528 1529 // TODO: expect expressions instead? 1530 assertParseErrorContains(t, "empty body", `f(_) = y {}`, "rego_parse_error: found empty body") 1531 assertParseErrorContains(t, "empty rule body", "p {}", "rego_parse_error: found empty body") 1532 assertParseErrorContains(t, "unmatched braces", `f(x) = y { trim(x, ".", y) `, `rego_parse_error: unexpected eof token: expected \n or ; or }`) 1533 1534 // TODO: how to highlight that assignment is incorrect here? 1535 assertParseErrorContains(t, "no output", `f(_) = { "foo" = "bar" }`, "rego_parse_error: unexpected eq token: expected rule value term") 1536 assertParseErrorContains(t, "no output", `f(_) := { "foo" = "bar" }`, "rego_parse_error: unexpected assign token: expected function value term") 1537 assertParseErrorContains(t, "no output", `f := { "foo" = "bar" }`, "rego_parse_error: unexpected assign token: expected rule value term") 1538 assertParseErrorContains(t, "no output", `f[_] := { "foo" = "bar" }`, "rego_parse_error: unexpected assign token: expected partial rule value term") 1539 assertParseErrorContains(t, "no output", `default f :=`, "rego_parse_error: unexpected assign token: expected default rule value term") 1540 1541 // TODO(tsandall): improve error checking here. This is a common mistake 1542 // and the current error message is not very good. Need to investigate if the 1543 // parser can be improved. 1544 assertParseError(t, "dangling semicolon", "p { true; false; }") 1545 1546 assertParseErrorContains(t, "default invalid rule name", `default 0[0`, "unexpected default keyword") 1547 assertParseErrorContains(t, "default invalid rule value", `default a[0`, "illegal default rule (must have a value)") 1548 assertParseRule(t, "default missing value", `default a`, &Rule{ 1549 Default: true, 1550 Head: &Head{ 1551 Name: Var("a"), 1552 Value: BooleanTerm(true), 1553 }, 1554 Body: NewBody(NewExpr(BooleanTerm(true))), 1555 }) 1556 assertParseRule(t, "empty arguments", `f() { x := 1 }`, &Rule{ 1557 Head: &Head{ 1558 Name: "f", 1559 Value: BooleanTerm(true), 1560 }, 1561 Body: MustParseBody(`x := 1`), 1562 }) 1563 1564 assertParseErrorContains(t, "default invalid rule head ref", `default a = b.c.d`, "illegal default rule (value cannot contain ref)") 1565 assertParseErrorContains(t, "default invalid rule head call", `default a = g(x)`, "illegal default rule (value cannot contain call)") 1566 assertParseErrorContains(t, "default invalid rule head builtin call", `default a = upper("foo")`, "illegal default rule (value cannot contain call)") 1567 assertParseErrorContains(t, "default invalid rule head call", `default a = b`, "illegal default rule (value cannot contain var)") 1568 1569 assertParseError(t, "extra braces", `{ a := 1 }`) 1570 assertParseError(t, "invalid rule name dots", `a.b = x { x := 1 }`) 1571 assertParseError(t, "invalid rule name dots and call", `a.b(x) { x := 1 }`) 1572 assertParseError(t, "invalid rule name hyphen", `a-b = x { x := 1 }`) 1573 1574 assertParseRule(t, "wildcard name", `_ { x == 1 }`, &Rule{ 1575 Head: &Head{ 1576 Name: "$0", 1577 Value: BooleanTerm(true), 1578 }, 1579 Body: MustParseBody(`x == 1`), 1580 }) 1581 1582 assertParseRule(t, "partial object array key", `p[[a, 1, 2]] = x { a := 1; x := "foo" }`, &Rule{ 1583 Head: &Head{ 1584 Name: "p", 1585 Key: ArrayTerm(VarTerm("a"), NumberTerm("1"), NumberTerm("2")), 1586 Value: VarTerm("x"), 1587 }, 1588 Body: MustParseBody(`a := 1; x := "foo"`), 1589 }) 1590 assertParseError(t, "invalid rule body no separator", `p { a = "foo"bar }`) 1591 assertParseError(t, "invalid rule body no newline", `p { a b c }`) 1592 } 1593 1594 func TestRuleContains(t *testing.T) { 1595 opts := ParserOptions{FutureKeywords: []string{"contains"}} 1596 1597 tests := []struct { 1598 note string 1599 rule string 1600 exp *Rule 1601 }{ 1602 { 1603 note: "simple", 1604 rule: `p contains "x" { true }`, 1605 exp: &Rule{ 1606 Head: NewHead(Var("p"), StringTerm("x")), 1607 Body: NewBody(NewExpr(BooleanTerm(true))), 1608 }, 1609 }, 1610 { 1611 note: "no body", 1612 rule: `p contains "x"`, 1613 exp: &Rule{ 1614 Head: NewHead(Var("p"), StringTerm("x")), 1615 Body: NewBody(NewExpr(BooleanTerm(true))), 1616 }, 1617 }, 1618 { 1619 note: "set with var element", 1620 rule: `deny contains msg { msg := "nonono" }`, 1621 exp: &Rule{ 1622 Head: NewHead(Var("deny"), VarTerm("msg")), 1623 Body: MustParseBody(`msg := "nonono"`), 1624 }, 1625 }, 1626 { 1627 note: "set with object elem", 1628 rule: `deny contains {"allow": false, "msg": msg} { msg := "nonono" }`, 1629 exp: &Rule{ 1630 Head: NewHead(Var("deny"), MustParseTerm(`{"allow": false, "msg": msg}`)), 1631 Body: MustParseBody(`msg := "nonono"`), 1632 }, 1633 }, 1634 } 1635 1636 for _, tc := range tests { 1637 t.Run(tc.note, func(t *testing.T) { 1638 assertParseRule(t, tc.note, tc.rule, tc.exp, opts) 1639 }) 1640 } 1641 } 1642 1643 func TestRuleIf(t *testing.T) { 1644 opts := ParserOptions{FutureKeywords: []string{"contains", "if", "every"}} 1645 1646 tests := []struct { 1647 note string 1648 rule string 1649 exp *Rule 1650 }{ 1651 { 1652 note: "complete", 1653 rule: `p if { true }`, 1654 exp: &Rule{ 1655 Head: NewHead(Var("p"), nil, BooleanTerm(true)), 1656 Body: NewBody(NewExpr(BooleanTerm(true))), 1657 }, 1658 }, 1659 { 1660 note: "complete, normal body", 1661 rule: `p if { x := 10; x > y }`, 1662 exp: &Rule{ 1663 Head: NewHead(Var("p"), nil, BooleanTerm(true)), 1664 Body: MustParseBody(`x := 10; x > y`), 1665 }, 1666 }, 1667 { 1668 note: "complete+else, normal bodies, assign", 1669 rule: `p := "yes" if { 10 > y } else := "no" { 10 <= y }`, 1670 exp: &Rule{ 1671 Head: &Head{ 1672 Name: Var("p"), 1673 Value: StringTerm("yes"), 1674 Assign: true, 1675 }, 1676 Body: MustParseBody(`10 > y`), 1677 Else: &Rule{ 1678 Head: &Head{ 1679 Name: Var("p"), 1680 Value: StringTerm("no"), 1681 Assign: true, 1682 }, 1683 Body: MustParseBody(`10 <= y`), 1684 }, 1685 }, 1686 }, 1687 { 1688 note: "complete, shorthand", 1689 rule: `p if true`, 1690 exp: &Rule{ 1691 Head: NewHead(Var("p"), nil, BooleanTerm(true)), 1692 Body: NewBody(NewExpr(BooleanTerm(true))), 1693 }, 1694 }, 1695 { 1696 note: "complete+not, shorthand", 1697 rule: `p if not q`, 1698 exp: &Rule{ 1699 Head: NewHead(Var("p"), nil, BooleanTerm(true)), 1700 Body: MustParseBody(`not q`), 1701 }, 1702 }, 1703 { 1704 note: "complete+else, shorthand", 1705 rule: `p if 1 > 2 else = 42 { 2 > 1 }`, 1706 exp: &Rule{ 1707 Head: NewHead(Var("p"), nil, BooleanTerm(true)), 1708 Body: MustParseBody(`1 > 2`), 1709 Else: &Rule{ 1710 Head: &Head{ 1711 Name: Var("p"), 1712 Value: NumberTerm("42"), 1713 }, 1714 Body: MustParseBody(`2 > 1`), 1715 }, 1716 }, 1717 }, 1718 { 1719 note: "complete+call, shorthand", 1720 rule: `p if count(q) > 0`, 1721 exp: &Rule{ 1722 Head: NewHead(Var("p"), nil, BooleanTerm(true)), 1723 Body: MustParseBody(`count(q) > 0`), 1724 }, 1725 }, 1726 { 1727 note: "function, shorthand", 1728 rule: `f(x) = y if y := x + 1`, 1729 exp: &Rule{ 1730 Head: &Head{ 1731 Name: Var("f"), 1732 Args: []*Term{VarTerm("x")}, 1733 Value: VarTerm("y"), 1734 }, 1735 Body: MustParseBody(`y := x + 1`), 1736 }, 1737 }, 1738 { 1739 note: "function+every, shorthand", 1740 rule: `f(xs) if every x in xs { x != 0 }`, 1741 exp: &Rule{ 1742 Head: &Head{ 1743 Name: Var("f"), 1744 Args: []*Term{VarTerm("xs")}, 1745 Value: BooleanTerm(true), 1746 }, 1747 Body: MustParseBodyWithOpts(`every x in xs { x != 0 }`, opts), 1748 }, 1749 }, 1750 { 1751 note: "object", 1752 rule: `p["foo"] = "bar" if { true }`, 1753 exp: &Rule{ 1754 Head: NewHead(Var("p"), StringTerm("foo"), StringTerm("bar")), 1755 Body: NewBody(NewExpr(BooleanTerm(true))), 1756 }, 1757 }, 1758 { 1759 note: "object, shorthand", 1760 rule: `p["foo"] = "bar" if true`, 1761 exp: &Rule{ 1762 Head: NewHead(Var("p"), StringTerm("foo"), StringTerm("bar")), 1763 Body: NewBody(NewExpr(BooleanTerm(true))), 1764 }, 1765 }, 1766 { 1767 note: "object with vars", 1768 rule: `p[x] = y if { 1769 x := "foo" 1770 y := "bar" 1771 }`, 1772 exp: &Rule{ 1773 Head: NewHead(Var("p"), VarTerm("x"), VarTerm("y")), 1774 Body: MustParseBody(`x := "foo"; y := "bar"`), 1775 }, 1776 }, 1777 { 1778 note: "set", 1779 rule: `p contains "foo" if { true }`, 1780 exp: &Rule{ 1781 Head: NewHead(Var("p"), StringTerm("foo")), 1782 Body: NewBody(NewExpr(BooleanTerm(true))), 1783 }, 1784 }, 1785 { 1786 note: "set, shorthand", 1787 rule: `p contains "foo" if true`, 1788 exp: &Rule{ 1789 Head: NewHead(Var("p"), StringTerm("foo")), 1790 Body: NewBody(NewExpr(BooleanTerm(true))), 1791 }, 1792 }, 1793 { 1794 note: "set+var+shorthand", 1795 rule: `p contains x if { x := "foo" }`, 1796 exp: &Rule{ 1797 Head: NewHead(Var("p"), VarTerm("x")), 1798 Body: MustParseBody(`x := "foo"`), 1799 }, 1800 }, 1801 } 1802 1803 for _, tc := range tests { 1804 t.Run(tc.note, func(t *testing.T) { 1805 assertParseRule(t, tc.note, tc.rule, tc.exp, opts) 1806 }) 1807 } 1808 1809 errors := []struct { 1810 note string 1811 rule string 1812 err string 1813 }{ 1814 { 1815 note: "partial set+if, shorthand", 1816 rule: `p[x] if x := 1`, 1817 err: "rego_parse_error: unexpected if keyword: invalid for partial set rule p (use `contains`)", 1818 }, 1819 { 1820 note: "partial set+if", 1821 rule: `p[x] if { x := 1 }`, 1822 err: "rego_parse_error: unexpected if keyword: invalid for partial set rule p (use `contains`)", 1823 }, 1824 } 1825 for _, tc := range errors { 1826 t.Run(tc.note, func(t *testing.T) { 1827 assertParseErrorContains(t, tc.note, tc.rule, tc.err, opts) 1828 }) 1829 } 1830 } 1831 1832 func TestRuleElseKeyword(t *testing.T) { 1833 mod := `package test 1834 1835 p { 1836 "p0" 1837 } 1838 1839 p { 1840 "p1" 1841 } else { 1842 "p1_e1" 1843 } else = [null] { 1844 "p1_e2" 1845 } else = x { 1846 x = "p1_e3" 1847 } 1848 1849 p { 1850 "p2" 1851 } 1852 1853 f(x) { 1854 x < 100 1855 } else = false { 1856 x > 200 1857 } else { 1858 x != 150 1859 } 1860 1861 _ { 1862 x > 0 1863 } else { 1864 x == -1 1865 } else { 1866 x > -100 1867 } 1868 1869 nobody = 1 { 1870 false 1871 } else = 7 1872 1873 nobody_f(x) = 1 { 1874 false 1875 } else = 7 1876 ` 1877 1878 parsed, err := ParseModule("", mod) 1879 if err != nil { 1880 t.Fatalf("Unexpected parse error: %v", err) 1881 } 1882 1883 name := Var("p") 1884 tr := BooleanTerm(true) 1885 head := &Head{Name: name, Value: tr} 1886 1887 expected := &Module{ 1888 Package: MustParsePackage(`package test`), 1889 Rules: []*Rule{ 1890 { 1891 Head: head, 1892 Body: MustParseBody(`"p0"`), 1893 }, 1894 { 1895 Head: head, 1896 Body: MustParseBody(`"p1"`), 1897 Else: &Rule{ 1898 Head: head, 1899 Body: MustParseBody(`"p1_e1"`), 1900 Else: &Rule{ 1901 Head: &Head{ 1902 Name: Var("p"), 1903 Value: ArrayTerm(NullTerm()), 1904 }, 1905 Body: MustParseBody(`"p1_e2"`), 1906 Else: &Rule{ 1907 Head: &Head{ 1908 Name: name, 1909 Value: VarTerm("x"), 1910 }, 1911 Body: MustParseBody(`x = "p1_e3"`), 1912 }, 1913 }, 1914 }, 1915 }, 1916 { 1917 Head: head, 1918 Body: MustParseBody(`"p2"`), 1919 }, 1920 { 1921 Head: &Head{ 1922 Name: Var("f"), 1923 Args: Args{VarTerm("x")}, 1924 Value: BooleanTerm(true), 1925 }, 1926 Body: MustParseBody(`x < 100`), 1927 Else: &Rule{ 1928 Head: &Head{ 1929 Name: Var("f"), 1930 Args: Args{VarTerm("x")}, 1931 Value: BooleanTerm(false), 1932 }, 1933 Body: MustParseBody(`x > 200`), 1934 Else: &Rule{ 1935 Head: &Head{ 1936 Name: Var("f"), 1937 Args: Args{VarTerm("x")}, 1938 Value: BooleanTerm(true), 1939 }, 1940 Body: MustParseBody(`x != 150`), 1941 }, 1942 }, 1943 }, 1944 1945 { 1946 Head: &Head{ 1947 Name: Var("$0"), 1948 Value: BooleanTerm(true), 1949 }, 1950 Body: MustParseBody(`x > 0`), 1951 Else: &Rule{ 1952 Head: &Head{ 1953 Name: Var("$0"), 1954 Value: BooleanTerm(true), 1955 }, 1956 Body: MustParseBody(`x == -1`), 1957 Else: &Rule{ 1958 Head: &Head{ 1959 Name: Var("$0"), 1960 Value: BooleanTerm(true), 1961 }, 1962 Body: MustParseBody(`x > -100`), 1963 }, 1964 }, 1965 }, 1966 { 1967 Head: &Head{ 1968 Name: Var("nobody"), 1969 Value: IntNumberTerm(1), 1970 }, 1971 Body: MustParseBody("false"), 1972 Else: &Rule{ 1973 Head: &Head{ 1974 Name: Var("nobody"), 1975 Value: IntNumberTerm(7), 1976 }, 1977 Body: MustParseBody("true"), 1978 }, 1979 }, 1980 { 1981 Head: &Head{ 1982 Name: Var("nobody_f"), 1983 Args: Args{VarTerm("x")}, 1984 Value: IntNumberTerm(1), 1985 }, 1986 Body: MustParseBody("false"), 1987 Else: &Rule{ 1988 Head: &Head{ 1989 Name: Var("nobody_f"), 1990 Args: Args{VarTerm("x")}, 1991 Value: IntNumberTerm(7), 1992 }, 1993 Body: MustParseBody("true"), 1994 }, 1995 }, 1996 }, 1997 } 1998 1999 if parsed.Compare(expected) != 0 { 2000 t.Fatalf("Expected:\n%v\n\nGot:\n%v", expected, parsed) 2001 } 2002 2003 notExpected := &Module{ 2004 Package: MustParsePackage(`package test`), 2005 Rules: []*Rule{ 2006 { 2007 Head: head, 2008 Body: MustParseBody(`"p0"`), 2009 }, 2010 { 2011 Head: head, 2012 Body: MustParseBody(`"p1"`), 2013 Else: &Rule{ 2014 Head: head, 2015 Body: MustParseBody(`"p1_e1"`), 2016 Else: &Rule{ 2017 Head: &Head{ 2018 Name: Var("p"), 2019 Value: ArrayTerm(NullTerm()), 2020 }, 2021 Body: MustParseBody(`"p1_e2"`), 2022 Else: &Rule{ 2023 Head: &Head{ 2024 Name: name, 2025 Value: VarTerm("x"), 2026 }, 2027 Body: MustParseBody(`x = "p1_e4"`), 2028 }, 2029 }, 2030 }, 2031 }, 2032 { 2033 Head: head, 2034 Body: MustParseBody(`"p2"`), 2035 }, 2036 }, 2037 } 2038 2039 if parsed.Compare(notExpected) != -1 { 2040 t.Fatalf("Expected not equal:\n%v\n\nGot:\n%v", parsed, notExpected) 2041 } 2042 2043 _, err = ParseModule("", ` 2044 package test 2045 p[1] { false } else { true } 2046 `) 2047 2048 if err == nil || !strings.Contains(err.Error(), "else keyword cannot be used on partial rules") { 2049 t.Fatalf("Expected parse error but got: %v", err) 2050 } 2051 2052 _, err = ParseModule("", ` 2053 package test 2054 p { false } { false } else { true } 2055 `) 2056 2057 if err == nil || !strings.Contains(err.Error(), "unexpected else keyword") { 2058 t.Fatalf("Expected parse error but got: %v", err) 2059 } 2060 2061 _, err = ParseModule("", ` 2062 package test 2063 p { false } else { false } { true } 2064 `) 2065 2066 if err == nil || !strings.Contains(err.Error(), "expected else keyword") { 2067 t.Fatalf("Expected parse error but got: %v", err) 2068 } 2069 2070 } 2071 2072 func TestMultipleEnclosedBodies(t *testing.T) { 2073 2074 result, err := ParseModule("", `package ex 2075 2076 p[x] = y { 2077 x = "a" 2078 y = 1 2079 } { 2080 x = "b" 2081 y = 2 2082 } 2083 2084 q = 1 2085 2086 f(x) { 2087 x < 10 2088 } { 2089 x > 1000 2090 } 2091 `, 2092 ) 2093 2094 if err != nil { 2095 t.Fatalf("Unexpected parse error: %v", err) 2096 } 2097 2098 expected := MustParseModule(`package ex 2099 2100 p[x] = y { x = "a"; y = 1 } 2101 p[x] = y { x = "b"; y = 2 } 2102 q = 1 { true } 2103 f(x) { x < 10 } 2104 f(x) { x > 1000 }`, 2105 ) 2106 2107 if !expected.Equal(result) { 2108 t.Fatal("Expected modules to be equal but got:\n\n", result, "\n\nExpected:\n\n", expected) 2109 } 2110 2111 } 2112 2113 func TestEmptyModule(t *testing.T) { 2114 r, err := ParseModule("", " ") 2115 if err == nil { 2116 t.Error("Expected error for empty module") 2117 return 2118 } 2119 if r != nil { 2120 t.Errorf("Expected nil for empty module: %v", r) 2121 } 2122 } 2123 2124 func TestComments(t *testing.T) { 2125 2126 testModule := `package a.b.c 2127 2128 import input.e.f as g # end of line 2129 import input.h 2130 2131 # by itself 2132 2133 p[x] = y { y = "foo"; 2134 # inside a rule 2135 x = "bar"; 2136 x != y; 2137 q[x] 2138 } 2139 2140 import input.xyz.abc 2141 2142 q # interrupting 2143 2144 [a] # the head of a rule 2145 2146 { m = [1,2, 2147 3, ]; 2148 a = m[i] 2149 2150 } 2151 2152 r[x] { x = [ a | # inside comprehension 2153 a = z[i] 2154 b[i].a = a ] 2155 2156 y = { a | # inside set comprehension 2157 a = z[i] 2158 b[i].a = a} 2159 2160 z = {a: i | # inside object comprehension 2161 a = z[i] 2162 b[i].a = a} 2163 }` 2164 2165 assertParseModule(t, "module comments", testModule, &Module{ 2166 Package: MustParseStatement(`package a.b.c`).(*Package), 2167 Imports: []*Import{ 2168 MustParseStatement("import input.e.f as g").(*Import), 2169 MustParseStatement("import input.h").(*Import), 2170 MustParseStatement("import input.xyz.abc").(*Import), 2171 }, 2172 Rules: []*Rule{ 2173 MustParseStatement(`p[x] = y { y = "foo"; x = "bar"; x != y; q[x] }`).(*Rule), 2174 MustParseStatement(`q[a] { m = [1, 2, 3]; a = m[i] }`).(*Rule), 2175 MustParseStatement(`r[x] { x = [a | a = z[i]; b[i].a = a]; y = {a | a = z[i]; b[i].a = a}; z = {a: i | a = z[i]; b[i].a = a} }`).(*Rule), 2176 }, 2177 }) 2178 2179 module, err := ParseModule("test.rego", testModule) 2180 if err != nil { 2181 t.Fatal("Unexpected error:", err) 2182 } 2183 2184 exp := []struct { 2185 text string 2186 row int 2187 col int 2188 }{ 2189 {text: "end of line", row: 3, col: 28}, 2190 {text: "by itself", row: 6, col: 5}, 2191 {text: "inside a rule", row: 9, col: 9}, 2192 {text: "interrupting", row: 17, col: 7}, 2193 {text: "the head of a rule", row: 19, col: 6}, 2194 {text: "inside comprehension", row: 27, col: 19}, 2195 {text: "inside set comprehension", row: 31, col: 13}, 2196 {text: "inside object comprehension", row: 35, col: 15}, 2197 } 2198 2199 if len(module.Comments) != len(exp) { 2200 t.Fatalf("Expected %v comments but got %v", len(exp), len(module.Comments)) 2201 } 2202 2203 for i := range exp { 2204 2205 expc := &Comment{ 2206 Text: []byte(" " + exp[i].text), 2207 Location: &Location{ 2208 File: "test.rego", 2209 Text: []byte("# " + exp[i].text), 2210 Row: exp[i].row, 2211 Col: exp[i].col, 2212 }, 2213 } 2214 2215 if !expc.Equal(module.Comments[i]) { 2216 comment := module.Comments[i] 2217 fmt.Printf("comment: %v %v %v %v\n", comment.Location.File, comment.Location.Text, comment.Location.Col, comment.Location.Row) 2218 fmt.Printf("expcomm: %v %v %v %v\n", expc.Location.File, expc.Location.Text, expc.Location.Col, expc.Location.Row) 2219 t.Errorf("Expected %q but got: %q (want: %d:%d, got: %d:%d)", expc, comment, exp[i].row, exp[i].col, comment.Location.Row, comment.Location.Col) 2220 } 2221 } 2222 } 2223 2224 func TestCommentsWhitespace(t *testing.T) { 2225 cases := []struct { 2226 note string 2227 module string 2228 expected []string 2229 }{ 2230 { 2231 note: "trailing spaces", 2232 module: "# a comment \t \n", 2233 expected: []string{" a comment \t "}, 2234 }, 2235 { 2236 note: "trailing carriage return", 2237 module: "# a comment\r\n", 2238 expected: []string{" a comment"}, 2239 }, 2240 { 2241 note: "trailing carriage return double newline", 2242 module: "# a comment\r\n\n", 2243 expected: []string{" a comment"}, 2244 }, 2245 { 2246 note: "double trailing carriage return newline", 2247 module: "#\r\r\n", 2248 expected: []string{"\r"}, 2249 }, 2250 { 2251 note: "double trailing carriage return", 2252 module: "#\r\r", 2253 expected: []string{"\r"}, 2254 }, 2255 { 2256 note: "carriage return", 2257 module: "#\r", 2258 expected: []string{""}, 2259 }, 2260 { 2261 note: "carriage return in comment", 2262 module: "# abc\rdef\r\n", 2263 expected: []string{" abc\rdef"}, 2264 }, 2265 } 2266 2267 for _, tc := range cases { 2268 t.Run(tc.note, func(t *testing.T) { 2269 _, comments, err := ParseStatements("", tc.module) 2270 if err != nil { 2271 t.Fatalf("Unexpected parse error: %s", err) 2272 } 2273 2274 for i, exp := range tc.expected { 2275 actual := string(comments[i].Text) 2276 if exp != actual { 2277 t.Errorf("Expected comment text (len %d):\n\n\t%q\n\nbut got (len %d):\n\n\t%q\n\n", len(exp), exp, len(actual), actual) 2278 } 2279 } 2280 }) 2281 } 2282 } 2283 2284 func TestExample(t *testing.T) { 2285 assertParseModule(t, "example module", testModule, &Module{ 2286 Package: MustParseStatement(`package opa.examples`).(*Package), 2287 Imports: []*Import{ 2288 MustParseStatement("import data.servers").(*Import), 2289 MustParseStatement("import data.networks").(*Import), 2290 MustParseStatement("import data.ports").(*Import), 2291 }, 2292 Rules: []*Rule{ 2293 MustParseStatement(`violations[server] { server = servers[i]; server.protocols[j] = "http"; public_servers[server] }`).(*Rule), 2294 MustParseStatement(`public_servers[server] { server = servers[i]; server.ports[j] = ports[k].id; ports[k].networks[l] = networks[m].id; networks[m].public = true }`).(*Rule), 2295 }, 2296 }) 2297 } 2298 2299 func TestModuleParseErrors(t *testing.T) { 2300 input := ` 2301 x = 1 # expect package 2302 package a # unexpected package 2303 1 = 2 # non-var head 2304 1 != 2 # non-equality expr 2305 x = y; x = 1 # multiple exprs 2306 ` 2307 2308 mod, err := ParseModule("test.rego", input) 2309 if err == nil { 2310 t.Fatalf("Expected error but got: %v", mod) 2311 } 2312 2313 errs, ok := err.(Errors) 2314 if !ok { 2315 panic("unexpected error value") 2316 } 2317 2318 if len(errs) != 5 { 2319 t.Fatalf("Expected exactly 5 errors but got: %v", err) 2320 } 2321 } 2322 2323 func TestLocation(t *testing.T) { 2324 mod, err := ParseModule("test", testModule) 2325 if err != nil { 2326 t.Errorf("Unexpected error while parsing test module: %v", err) 2327 return 2328 } 2329 expr := mod.Rules[0].Body[0] 2330 if expr.Location.Col != 5 { 2331 t.Errorf("Expected column of %v to be 5 but got: %v", expr, expr.Location.Col) 2332 } 2333 if expr.Location.Row != 15 { 2334 t.Errorf("Expected row of %v to be 8 but got: %v", expr, expr.Location.Row) 2335 } 2336 if expr.Location.File != "test" { 2337 t.Errorf("Expected file of %v to be test but got: %v", expr, expr.Location.File) 2338 } 2339 } 2340 2341 func TestRuleFromBody(t *testing.T) { 2342 testModule := `package a.b.c 2343 2344 pi = 3.14159 2345 p[x] { x = 1 } 2346 greeting = "hello" 2347 cores = [{0: 1}, {1: 2}] 2348 wrapper = cores[0][1] 2349 pi = [3, 1, 4, x, y, z] 2350 foo["bar"] = "buz" 2351 foo["9"] = "10" 2352 foo.buz = "bar" 2353 bar[1] 2354 bar[[{"foo":"baz"}]] 2355 bar.qux 2356 input = 1 2357 data = 2 2358 f(1) = 2 2359 f(1) 2360 d1 := 1234 2361 ` 2362 2363 assertParseModule(t, "rules from bodies", testModule, &Module{ 2364 Package: MustParseStatement(`package a.b.c`).(*Package), 2365 Rules: []*Rule{ 2366 MustParseRule(`pi = 3.14159 { true }`), 2367 MustParseRule(`p[x] { x = 1 }`), 2368 MustParseRule(`greeting = "hello" { true }`), 2369 MustParseRule(`cores = [{0: 1}, {1: 2}] { true }`), 2370 MustParseRule(`wrapper = cores[0][1] { true }`), 2371 MustParseRule(`pi = [3, 1, 4, x, y, z] { true }`), 2372 MustParseRule(`foo["bar"] = "buz" { true }`), 2373 MustParseRule(`foo["9"] = "10" { true }`), 2374 MustParseRule(`foo["buz"] = "bar" { true }`), 2375 MustParseRule(`bar[1] { true }`), 2376 MustParseRule(`bar[[{"foo":"baz"}]] { true }`), 2377 MustParseRule(`bar["qux"] { true }`), 2378 MustParseRule(`input = 1 { true }`), 2379 MustParseRule(`data = 2 { true }`), 2380 MustParseRule(`f(1) = 2 { true }`), 2381 MustParseRule(`f(1) = true { true }`), 2382 MustParseRule("d1 := 1234 { true }"), 2383 }, 2384 }) 2385 2386 // Verify the rule and rule and rule head col/loc values 2387 module, err := ParseModule("test.rego", testModule) 2388 if err != nil { 2389 t.Fatal(err) 2390 } 2391 2392 for i := range module.Rules { 2393 col := module.Rules[i].Location.Col 2394 if col != 1 { 2395 t.Fatalf("expected rule %v column to be 1 but got %v", module.Rules[i].Head.Name, col) 2396 } 2397 row := module.Rules[i].Location.Row 2398 if row != 3+i { // 'pi' rule stats on row 3 2399 t.Fatalf("expected rule %v row to be %v but got %v", module.Rules[i].Head.Name, 3+i, row) 2400 } 2401 col = module.Rules[i].Head.Location.Col 2402 if col != 1 { 2403 t.Fatalf("expected rule head %v column to be 1 but got %v", module.Rules[i].Head.Name, col) 2404 } 2405 row = module.Rules[i].Head.Location.Row 2406 if row != 3+i { // 'pi' rule stats on row 3 2407 t.Fatalf("expected rule head %v row to be %v but got %v", module.Rules[i].Head.Name, 3+i, row) 2408 } 2409 } 2410 2411 mockModule := `package ex 2412 2413 input = {"foo": 1} 2414 data = {"bar": 2}` 2415 2416 assertParseModule(t, "rule name: input/data", mockModule, &Module{ 2417 Package: MustParsePackage(`package ex`), 2418 Rules: []*Rule{ 2419 MustParseRule(`input = {"foo": 1} { true }`), 2420 MustParseRule(`data = {"bar": 2} { true }`), 2421 }, 2422 }) 2423 2424 multipleExprs := ` 2425 package a.b.c 2426 2427 pi = 3.14159; pi > 3 2428 ` 2429 2430 nonEquality := ` 2431 package a.b.c 2432 2433 pi > 3 2434 ` 2435 2436 nonVarName := ` 2437 package a.b.c 2438 2439 "pi" = 3 2440 ` 2441 2442 withExpr := ` 2443 package a.b.c 2444 2445 foo = input with input as 1 2446 ` 2447 2448 badRefLen1 := ` 2449 package a.b.c 2450 2451 p["x"].y = 1` 2452 2453 badRefLen2 := ` 2454 package a.b.c 2455 2456 p["x"].y` 2457 2458 negated := ` 2459 package a.b.c 2460 2461 not p = 1` 2462 2463 nonRefTerm := ` 2464 package a.b.c 2465 2466 p` 2467 2468 zeroArgs := ` 2469 package a.b.c 2470 2471 p()` 2472 2473 assignToTerm := ` 2474 package a.b.c 2475 2476 "foo" := 1` 2477 2478 someDecl := ` 2479 package a 2480 2481 some x` 2482 2483 arrayTerm := ` 2484 package a 2485 [][0] 2486 ` 2487 2488 callWithRuleKeyPartialSet := ` 2489 package a 2490 f(x)[x] { true }` 2491 2492 callWithRuleKeyPartialObject := ` 2493 package a 2494 f(x)[x] = x { true }` 2495 2496 assignNoOperands := ` 2497 package a 2498 assign()` 2499 2500 assignOneOperand := ` 2501 package a 2502 assign(x)` 2503 2504 eqNoOperands := ` 2505 package a 2506 eq()` 2507 2508 eqOneOperand := ` 2509 package a 2510 eq(x)` 2511 2512 assertParseModuleError(t, "multiple expressions", multipleExprs) 2513 assertParseModuleError(t, "non-equality", nonEquality) 2514 assertParseModuleError(t, "non-var name", nonVarName) 2515 assertParseModuleError(t, "with expr", withExpr) 2516 assertParseModuleError(t, "bad ref (too long)", badRefLen1) 2517 assertParseModuleError(t, "bad ref (too long)", badRefLen2) 2518 assertParseModuleError(t, "negated", negated) 2519 assertParseModuleError(t, "non ref term", nonRefTerm) 2520 assertParseModuleError(t, "zero args", zeroArgs) 2521 assertParseModuleError(t, "assign to term", assignToTerm) 2522 assertParseModuleError(t, "some decl", someDecl) 2523 assertParseModuleError(t, "array term", arrayTerm) 2524 assertParseModuleError(t, "call in ref partial set", "package test\nf().x {}") 2525 assertParseModuleError(t, "call in ref partial object", "package test\nf().x = y {}") 2526 assertParseModuleError(t, "number in ref", "package a\n12[3]()=4") 2527 assertParseModuleError(t, "rule with args and key", callWithRuleKeyPartialObject) 2528 assertParseModuleError(t, "rule with args and key", callWithRuleKeyPartialSet) 2529 assertParseModuleError(t, "assign without operands", assignNoOperands) 2530 assertParseModuleError(t, "assign with only one operand", assignOneOperand) 2531 assertParseModuleError(t, "eq without operands", eqNoOperands) 2532 assertParseModuleError(t, "eq with only one operand", eqOneOperand) 2533 2534 if _, err := ParseRuleFromExpr(&Module{}, &Expr{ 2535 Terms: struct{}{}, 2536 }); err == nil { 2537 t.Fatal("expected error for unknown expression term type") 2538 } 2539 } 2540 2541 func TestWildcards(t *testing.T) { 2542 2543 assertParseOneTerm(t, "ref", "a.b[_].c[_]", RefTerm( 2544 VarTerm("a"), 2545 StringTerm("b"), 2546 VarTerm("$0"), 2547 StringTerm("c"), 2548 VarTerm("$1"), 2549 )) 2550 2551 assertParseOneTerm(t, "nested", `[{"a": a[_]}, _, {"b": _}]`, ArrayTerm( 2552 ObjectTerm( 2553 Item(StringTerm("a"), RefTerm(VarTerm("a"), VarTerm("$0"))), 2554 ), 2555 VarTerm("$1"), 2556 ObjectTerm( 2557 Item(StringTerm("b"), VarTerm("$2")), 2558 ), 2559 )) 2560 2561 assertParseOneExpr(t, "expr", `_ = [a[_]]`, Equality.Expr( 2562 VarTerm("$0"), 2563 ArrayTerm( 2564 RefTerm(VarTerm("a"), VarTerm("$1")), 2565 ))) 2566 2567 assertParseOneExpr(t, "comprehension", `_ = [x | a = a[_]]`, Equality.Expr( 2568 VarTerm("$0"), 2569 ArrayComprehensionTerm( 2570 VarTerm("x"), 2571 NewBody( 2572 Equality.Expr( 2573 VarTerm("a"), 2574 RefTerm(VarTerm("a"), VarTerm("$1")), 2575 ), 2576 ), 2577 ))) 2578 2579 assertParseRule(t, "functions", `f(_) = y { true }`, &Rule{ 2580 Head: &Head{ 2581 Name: Var("f"), 2582 Args: Args{ 2583 VarTerm("$0"), 2584 }, 2585 Value: VarTerm("y"), 2586 }, 2587 Body: NewBody(NewExpr(BooleanTerm(true))), 2588 }) 2589 } 2590 2591 func TestRuleModulePtr(t *testing.T) { 2592 mod := `package test 2593 2594 p { true } 2595 p { true } 2596 q { true } 2597 r = 1 2598 default s = 2 2599 ` 2600 2601 parsed, err := ParseModule("", mod) 2602 if err != nil { 2603 t.Fatalf("Unexpected parse error: %v", err) 2604 } 2605 2606 for _, rule := range parsed.Rules { 2607 if rule.Module != parsed { 2608 t.Fatalf("Expected module ptr to be %p but got %p", parsed, rule.Module) 2609 } 2610 } 2611 } 2612 2613 func TestNoMatchError(t *testing.T) { 2614 mod := `package test 2615 2616 p { true; 2617 1 != 0; # <-- parse error: no match 2618 }` 2619 2620 _, err := ParseModule("foo.rego", mod) 2621 2622 expected := "1 error occurred: foo.rego:5: rego_parse_error: unexpected } token" 2623 2624 if !strings.HasPrefix(err.Error(), expected) { 2625 t.Fatalf("Bad parse error, expected %v but got: %v", expected, err) 2626 } 2627 2628 mod = `package test 2629 2630 p { true // <-- parse error: no match` 2631 2632 _, err = ParseModule("foo.rego", mod) 2633 2634 loc := NewLocation([]byte{'/'}, "foo.rego", 3, 12) 2635 2636 if !loc.Equal(err.(Errors)[0].Location) { 2637 t.Fatalf("Expected %v but got: %v", loc, err) 2638 } 2639 } 2640 2641 func TestBraceBracketParenMatchingErrors(t *testing.T) { 2642 // Checks to prevent regression on issue #4672. 2643 // Error location is important here, which is why we check 2644 // the error strings directly. 2645 tests := []struct { 2646 note string 2647 err string 2648 input string 2649 }{ 2650 { 2651 note: "Unmatched ')' case", 2652 err: `1 error occurred: test.rego:4: rego_parse_error: unexpected , token: expected \n or ; or } 2653 y := contains("a"), "b") 2654 ^`, 2655 input: `package test 2656 p { 2657 x := 5 2658 y := contains("a"), "b") 2659 }`, 2660 }, 2661 { 2662 note: "Unmatched '}' case", 2663 err: `1 error occurred: test.rego:4: rego_parse_error: unexpected , token: expected \n or ; or } 2664 y := {"a", "b", "c"}, "a"} 2665 ^`, 2666 input: `package test 2667 p { 2668 x := 5 2669 y := {"a", "b", "c"}, "a"} 2670 }`, 2671 }, 2672 { 2673 note: "Unmatched ']' case", 2674 err: `1 error occurred: test.rego:4: rego_parse_error: unexpected , token: expected \n or ; or } 2675 y := ["a", "b", "c"], "a"] 2676 ^`, 2677 input: `package test 2678 p { 2679 x := 5 2680 y := ["a", "b", "c"], "a"] 2681 }`, 2682 }, 2683 { 2684 note: "Unmatched '(' case", 2685 err: `1 error occurred: test.rego:5: rego_parse_error: unexpected } token: expected "," or ")" 2686 } 2687 ^`, 2688 input: `package test 2689 p { 2690 x := 5 2691 y := contains("a", "b" 2692 }`, 2693 }, 2694 { 2695 note: "Unmatched '{' case", 2696 2697 err: `1 error occurred: test.rego:5: rego_parse_error: unexpected eof token: expected \n or ; or } 2698 } 2699 ^`, 2700 input: `package test 2701 p { 2702 x := 5 2703 y := {{"a", "b", "c"}, "a" 2704 }`, 2705 }, 2706 { 2707 note: "Unmatched '[' case", 2708 err: `1 error occurred: test.rego:5: rego_parse_error: unexpected } token: expected "," or "]" 2709 } 2710 ^`, 2711 input: `package test 2712 p { 2713 x := 5 2714 y := [["a", "b", "c"], "a" 2715 }`, 2716 }, 2717 } 2718 2719 for _, tc := range tests { 2720 t.Run(tc.note, func(t *testing.T) { 2721 _, err := ParseModule("test.rego", tc.input) 2722 if err == nil { 2723 t.Fatal("Expected error") 2724 } 2725 if tc.err != "" && tc.err != err.Error() { 2726 t.Fatalf("Expected error string %q but got: %q", tc.err, err.Error()) 2727 } 2728 }) 2729 } 2730 } 2731 2732 func TestParseErrorDetails(t *testing.T) { 2733 2734 tests := []struct { 2735 note string 2736 exp *ParserErrorDetail 2737 err string 2738 input string 2739 }{ 2740 { 2741 note: "no match: bad rule name", 2742 exp: &ParserErrorDetail{ 2743 Line: ".", 2744 Idx: 0, 2745 }, 2746 input: ` 2747 package test 2748 .`, 2749 }, 2750 { 2751 note: "no match: bad termination for comprehension", 2752 exp: &ParserErrorDetail{ 2753 Line: "p = [true | true}", 2754 Idx: 16, 2755 }, 2756 input: ` 2757 package test 2758 p = [true | true}`}, 2759 { 2760 note: "no match: non-terminated comprehension", 2761 exp: &ParserErrorDetail{ 2762 Line: "p = [true | true", 2763 Idx: 15, 2764 }, 2765 input: ` 2766 package test 2767 p = [true | true`}, 2768 { 2769 note: "no match: expected expression", 2770 exp: &ParserErrorDetail{ 2771 Line: "p { true; }", 2772 Idx: 10, 2773 }, 2774 input: ` 2775 package test 2776 p { true; }`}, 2777 { 2778 note: "empty body", 2779 exp: &ParserErrorDetail{ 2780 Line: "p { }", 2781 Idx: 4, 2782 }, 2783 input: ` 2784 package test 2785 p { }`}, 2786 { 2787 note: "non-terminated string", 2788 exp: &ParserErrorDetail{ 2789 Line: `p = "foo`, 2790 Idx: 4, 2791 }, 2792 input: ` 2793 package test 2794 p = "foo`}, 2795 { 2796 note: "rule with error begins with one tab", 2797 exp: &ParserErrorDetail{ 2798 Line: "\tas", 2799 Idx: 1, 2800 }, 2801 input: ` 2802 package test 2803 as`, 2804 err: `1 error occurred: test.rego:3: rego_parse_error: unexpected as keyword 2805 as 2806 ^`}, 2807 { 2808 note: "rule term with error begins with two tabs", 2809 exp: &ParserErrorDetail{ 2810 Line: "\t\tas", 2811 Idx: 2, 2812 }, 2813 input: ` 2814 package test 2815 p = true { 2816 as 2817 }`, 2818 err: `1 error occurred: test.rego:4: rego_parse_error: unexpected as keyword 2819 as 2820 ^`}, 2821 { 2822 note: "input is tab and space tokens only", 2823 exp: &ParserErrorDetail{ 2824 Line: "\t\v\f ", 2825 Idx: 0, 2826 }, 2827 input: "\t\v\f ", 2828 // NOTE(sr): With the unprintable control characters, the output is pretty 2829 // useless. But it's also quite an edge case. 2830 err: "1 error occurred: test.rego:1: rego_parse_error: illegal token\n\t\v\f \n\t^", 2831 }, 2832 } 2833 2834 for _, tc := range tests { 2835 t.Run(tc.note, func(t *testing.T) { 2836 _, err := ParseModule("test.rego", tc.input) 2837 if err == nil { 2838 t.Fatal("Expected error") 2839 } 2840 detail := err.(Errors)[0].Details 2841 if !reflect.DeepEqual(detail, tc.exp) { 2842 t.Errorf("Expected %v but got: %v", tc.exp, detail) 2843 } 2844 if tc.err != "" && tc.err != err.Error() { 2845 t.Fatalf("Expected error string %q but got: %q", tc.err, err.Error()) 2846 } 2847 }) 2848 } 2849 } 2850 2851 func TestNamespacedBuiltins(t *testing.T) { 2852 2853 tests := []struct { 2854 expr string 2855 expected *Term 2856 wantErr bool 2857 }{ 2858 {`foo.bar.baz(1, 2)`, MustParseTerm("foo.bar.baz"), false}, 2859 {`foo.(1,2)`, nil, true}, 2860 {`foo.#.bar(1,2)`, nil, true}, 2861 } 2862 2863 for _, tc := range tests { 2864 expr, err := ParseExpr(tc.expr) 2865 if !tc.wantErr { 2866 if err != nil { 2867 t.Fatalf("Unexpected parse error: %v", err) 2868 } 2869 terms, ok := expr.Terms.([]*Term) 2870 if !ok { 2871 t.Fatalf("Expected terms not: %T", expr.Terms) 2872 } 2873 if !terms[0].Equal(tc.expected) { 2874 t.Fatalf("Expected builtin-name to equal %v but got: %v", tc.expected, terms) 2875 } 2876 } else if err == nil { 2877 t.Fatalf("Expected error from %v but got: %v", tc.expr, expr) 2878 } 2879 } 2880 } 2881 2882 func TestRuleHeadLocation(t *testing.T) { 2883 2884 const input = `package pkg 2885 2886 p[x] { 2887 x = "hi" 2888 } { 2889 x = "bye" 2890 } 2891 2892 f(x) { 2893 false 2894 } else = false { 2895 true 2896 } 2897 ` 2898 2899 module := MustParseModule(input) 2900 2901 for _, tc := range []struct { 2902 note string 2903 location *Location 2904 expectedRow int 2905 expectedText string 2906 }{ 2907 { 2908 note: "partial rule", 2909 location: module.Rules[0].Location, 2910 expectedRow: 3, 2911 expectedText: ` 2912 p[x] { 2913 x = "hi" 2914 } 2915 `, 2916 }, 2917 { 2918 note: "partial rule head", 2919 location: module.Rules[0].Head.Location, 2920 expectedRow: 3, 2921 expectedText: `p[x]`, 2922 }, 2923 { 2924 note: "partial rule head key", 2925 location: module.Rules[0].Head.Key.Location, 2926 expectedRow: 3, 2927 expectedText: `x`, 2928 }, 2929 { 2930 note: "chained rule", 2931 location: module.Rules[1].Location, 2932 expectedRow: 5, 2933 expectedText: ` 2934 { 2935 x = "bye" 2936 } 2937 `, 2938 }, 2939 { 2940 note: "chained rule head", 2941 location: module.Rules[1].Head.Location, 2942 expectedRow: 5, 2943 expectedText: ` 2944 { 2945 x = "bye" 2946 } 2947 `, 2948 }, 2949 { 2950 note: "chained rule head key", 2951 location: module.Rules[1].Head.Key.Location, 2952 expectedRow: 5, 2953 expectedText: ` 2954 { 2955 x = "bye" 2956 } 2957 `, 2958 }, 2959 { 2960 note: "rule with args", 2961 location: module.Rules[2].Location, 2962 expectedRow: 9, 2963 expectedText: ` 2964 f(x) { 2965 false 2966 } else = false { 2967 true 2968 } 2969 `, 2970 }, 2971 { 2972 note: "rule with args head", 2973 location: module.Rules[2].Head.Location, 2974 expectedRow: 9, 2975 expectedText: `f(x)`, 2976 }, 2977 { 2978 note: "rule with args head arg 0", 2979 location: module.Rules[2].Head.Args[0].Location, 2980 expectedRow: 9, 2981 expectedText: `x`, 2982 }, 2983 { 2984 note: "else with args", 2985 location: module.Rules[2].Else.Location, 2986 expectedRow: 11, 2987 expectedText: ` 2988 else = false { 2989 true 2990 } 2991 `, 2992 }, 2993 { 2994 note: "else with args head", 2995 location: module.Rules[2].Else.Head.Location, 2996 expectedRow: 11, 2997 expectedText: `else = false`, 2998 }, 2999 { 3000 note: "else with args head arg 0", 3001 location: module.Rules[2].Else.Head.Args[0].Location, 3002 expectedRow: 9, 3003 expectedText: `x`, 3004 }, 3005 } { 3006 t.Run(tc.note, func(t *testing.T) { 3007 if tc.location.Row != tc.expectedRow { 3008 t.Errorf("Expected %d but got %d", tc.expectedRow, tc.location.Row) 3009 } 3010 exp := strings.TrimSpace(tc.expectedText) 3011 if string(tc.location.Text) != exp { 3012 t.Errorf("Expected text:\n%s\n\ngot:\n%s\n\n", exp, tc.location.Text) 3013 } 3014 }) 3015 } 3016 } 3017 3018 func TestParserText(t *testing.T) { 3019 3020 tests := []struct { 3021 note string 3022 input string 3023 want string 3024 }{ 3025 { 3026 note: "relational term", 3027 input: `(1 == (2 > 3))`, 3028 }, 3029 { 3030 note: "array - empty", 3031 input: `[ ]`, 3032 }, 3033 { 3034 note: "array - one element", 3035 input: `[ 1 ]`, 3036 }, 3037 { 3038 note: "array - multiple elements", 3039 input: `[1 , 2 , 3]`, 3040 }, 3041 { 3042 note: "object - empty", 3043 input: `{ }`, 3044 }, 3045 { 3046 note: "object - one element", 3047 input: `{ "foo": 1 }`, 3048 }, 3049 { 3050 note: "object - multiple elements", 3051 input: `{"foo": 1, "bar": 2}`, 3052 }, 3053 { 3054 note: "set - one element", 3055 input: `{ 1 }`, 3056 }, 3057 { 3058 note: "set - multiple elements", 3059 input: `{1 , 2 , 3}`, 3060 }, 3061 { 3062 note: "idents", 3063 input: "foo", 3064 }, 3065 { 3066 note: "ref", 3067 input: `data.foo[x].bar`, 3068 }, 3069 { 3070 note: "call", 3071 input: `data.foo.bar(x)`, 3072 }, 3073 { 3074 note: "ref and call", 3075 input: `data.foo[1](x).bar(y)[z]`, 3076 }, 3077 { 3078 note: "infix", 3079 input: "input = 1", 3080 }, 3081 { 3082 note: "negated", 3083 input: "not x = 1", 3084 }, 3085 { 3086 note: "expr with statements", 3087 input: "x = 1 with input as 2 with input as 3", 3088 }, 3089 } 3090 3091 for _, tc := range tests { 3092 t.Run(tc.note, func(t *testing.T) { 3093 for _, suffix := range []string{"", "\t\n "} { 3094 input := tc.input + suffix 3095 3096 stmts, _, err := ParseStatements("test.rego", input) 3097 if err != nil { 3098 t.Fatal(err) 3099 } 3100 3101 if len(stmts) != 1 { 3102 t.Fatal("expected exactly one statement but got:", stmts) 3103 } 3104 3105 result := string(stmts[0].Loc().Text) 3106 3107 if result != tc.input { 3108 t.Fatalf("expected %q but got: %q", tc.input, result) 3109 } 3110 } 3111 }) 3112 } 3113 } 3114 3115 func TestRuleText(t *testing.T) { 3116 input := ` package test 3117 3118 r[x] = y { 3119 x = input.a 3120 x = "foo" 3121 } { 3122 x = input.b 3123 x = "bar" 3124 } { 3125 x = input.c 3126 x = "baz" 3127 } 3128 3129 r[x] = y { 3130 x = input.d 3131 x = "qux" 3132 } 3133 ` 3134 3135 mod := MustParseModule(input) 3136 rules := mod.Rules 3137 3138 if len(rules) != 4 { 3139 t.Fatalf("Expected 4 rules, got %d", len(rules)) 3140 } 3141 3142 expectedRuleText := []string{ 3143 ` 3144 r[x] = y { 3145 x = input.a 3146 x = "foo" 3147 } 3148 `, 3149 ` 3150 { 3151 x = input.b 3152 x = "bar" 3153 } 3154 `, 3155 ` 3156 { 3157 x = input.c 3158 x = "baz" 3159 } 3160 `, 3161 ` 3162 r[x] = y { 3163 x = input.d 3164 x = "qux" 3165 } 3166 `, 3167 } 3168 3169 assertLocationText(t, strings.TrimSpace(expectedRuleText[0]), rules[0].Location) 3170 assertLocationText(t, "r[x] = y", rules[0].Head.Location) 3171 assertLocationText(t, "y", rules[0].Head.Value.Location) 3172 3173 // Chained rules recursively set text on heads to be the full rule 3174 for i := 1; i < len(expectedRuleText)-1; i++ { 3175 text := strings.TrimSpace(expectedRuleText[i]) 3176 assertLocationText(t, text, rules[i].Location) 3177 assertLocationText(t, text, rules[i].Head.Location) 3178 assertLocationText(t, text, rules[i].Head.Value.Location) 3179 } 3180 3181 assertLocationText(t, strings.TrimSpace(expectedRuleText[3]), rules[3].Location) 3182 assertLocationText(t, "r[x] = y", rules[3].Head.Location) 3183 assertLocationText(t, "y", rules[3].Head.Value.Location) 3184 } 3185 3186 func TestRuleElseText(t *testing.T) { 3187 input := ` 3188 r1 = x { 3189 a == "foo" 3190 } else = y { 3191 b == "bar" 3192 } 3193 3194 else { 3195 c == "baz" 3196 } 3197 3198 else = { 3199 "k1": 1, 3200 "k2": 2 3201 } { 3202 true 3203 } 3204 ` 3205 3206 rule := MustParseRule(input) 3207 assertLocationText(t, strings.TrimSpace(input), rule.Location) 3208 assertLocationText(t, "r1 = x", rule.Head.Location) 3209 assertLocationText(t, "x", rule.Head.Value.Location) 3210 3211 curElse := rule.Else 3212 if curElse == nil { 3213 t.Fatalf("Expected an else block, got nil") 3214 } 3215 assertLocationText(t, strings.TrimSpace(` 3216 else = y { 3217 b == "bar" 3218 } 3219 3220 else { 3221 c == "baz" 3222 } 3223 3224 else = { 3225 "k1": 1, 3226 "k2": 2 3227 } { 3228 true 3229 } 3230 `), curElse.Location) 3231 assertLocationText(t, "else = y", curElse.Head.Location) 3232 assertLocationText(t, "y", curElse.Head.Value.Location) 3233 3234 curElse = curElse.Else 3235 if curElse == nil { 3236 t.Fatalf("Expected an else block, got nil") 3237 } 3238 assertLocationText(t, strings.TrimSpace(` 3239 else { 3240 c == "baz" 3241 } 3242 3243 else = { 3244 "k1": 1, 3245 "k2": 2 3246 } { 3247 true 3248 } 3249 `), curElse.Location) 3250 assertLocationText(t, "else", curElse.Head.Location) 3251 if curElse.Head.Value.Location != nil { 3252 t.Errorf("Expected a nil location") 3253 } 3254 3255 curElse = curElse.Else 3256 if curElse == nil { 3257 t.Fatalf("Expected an else block, got nil") 3258 } 3259 assertLocationText(t, strings.TrimSpace(` 3260 else = { 3261 "k1": 1, 3262 "k2": 2 3263 } { 3264 true 3265 } 3266 `), curElse.Location) 3267 assertLocationText(t, strings.TrimSpace(` 3268 else = { 3269 "k1": 1, 3270 "k2": 2 3271 } 3272 `), curElse.Head.Location) 3273 assertLocationText(t, strings.TrimSpace(` 3274 { 3275 "k1": 1, 3276 "k2": 2 3277 } 3278 `), curElse.Head.Value.Location) 3279 } 3280 3281 func TestAnnotations(t *testing.T) { 3282 3283 dataServers := MustParseRef("data.servers") 3284 dataNetworks := MustParseRef("data.networks") 3285 dataPorts := MustParseRef("data.ports") 3286 3287 schemaServers := MustParseRef("schema.servers") 3288 schemaNetworks := MustParseRef("schema.networks") 3289 schemaPorts := MustParseRef("schema.ports") 3290 3291 stringSchemaAsMap := map[string]interface{}{ 3292 "type": "string", 3293 } 3294 var stringSchema interface{} = stringSchemaAsMap 3295 3296 tests := []struct { 3297 note string 3298 module string 3299 expNumComments int 3300 expAnnotations []*Annotations 3301 expError string 3302 }{ 3303 { 3304 note: "Single valid annotation", 3305 module: ` 3306 package opa.examples 3307 3308 import data.servers 3309 import data.networks 3310 import data.ports 3311 3312 # METADATA 3313 # scope: rule 3314 # schemas: 3315 # - data.servers: schema.servers 3316 public_servers[server] { 3317 server = servers[i]; server.ports[j] = ports[k].id 3318 ports[k].networks[l] = networks[m].id; 3319 networks[m].public = true 3320 }`, 3321 expNumComments: 4, 3322 expAnnotations: []*Annotations{ 3323 { 3324 Schemas: []*SchemaAnnotation{ 3325 {Path: dataServers, Schema: schemaServers}, 3326 }, 3327 Scope: annotationScopeRule, 3328 }, 3329 }, 3330 }, 3331 { 3332 note: "Multiple annotations on multiple lines", 3333 module: ` 3334 package opa.examples 3335 3336 import data.servers 3337 import data.networks 3338 import data.ports 3339 3340 # METADATA 3341 # scope: rule 3342 # schemas: 3343 # - data.servers: schema.servers 3344 # - data.networks: schema.networks 3345 # - data.ports: schema.ports 3346 public_servers[server] { 3347 server = servers[i]; server.ports[j] = ports[k].id 3348 ports[k].networks[l] = networks[m].id; 3349 networks[m].public = true 3350 }`, 3351 expNumComments: 6, 3352 expAnnotations: []*Annotations{ 3353 { 3354 Schemas: []*SchemaAnnotation{ 3355 {Path: dataServers, Schema: schemaServers}, 3356 {Path: dataNetworks, Schema: schemaNetworks}, 3357 {Path: dataPorts, Schema: schemaPorts}, 3358 }, 3359 Scope: annotationScopeRule, 3360 }, 3361 }, 3362 }, 3363 { 3364 note: "Comment in between metadata and rule (valid)", 3365 module: ` 3366 package opa.examples 3367 3368 import data.servers 3369 import data.networks 3370 import data.ports 3371 3372 # METADATA 3373 # scope: rule 3374 # schemas: 3375 # - data.servers: schema.servers 3376 # - data.networks: schema.networks 3377 # - data.ports: schema.ports 3378 3379 # This is a comment after the metadata YAML 3380 public_servers[server] { 3381 server = servers[i]; server.ports[j] = ports[k].id 3382 ports[k].networks[l] = networks[m].id; 3383 networks[m].public = true 3384 }`, 3385 expNumComments: 7, 3386 expAnnotations: []*Annotations{ 3387 { 3388 Schemas: []*SchemaAnnotation{ 3389 {Path: dataServers, Schema: schemaServers}, 3390 {Path: dataNetworks, Schema: schemaNetworks}, 3391 {Path: dataPorts, Schema: schemaPorts}, 3392 }, 3393 Scope: annotationScopeRule, 3394 }, 3395 }, 3396 }, 3397 { 3398 note: "Empty comment line in between metadata and rule (valid)", 3399 module: ` 3400 package opa.examples 3401 3402 import data.servers 3403 import data.networks 3404 import data.ports 3405 3406 # METADATA 3407 # scope: rule 3408 # schemas: 3409 # - data.servers: schema.servers 3410 # - data.networks: schema.networks 3411 # - data.ports: schema.ports 3412 # 3413 public_servers[server] { 3414 server = servers[i]; server.ports[j] = ports[k].id 3415 ports[k].networks[l] = networks[m].id; 3416 networks[m].public = true 3417 }`, 3418 expNumComments: 7, 3419 expAnnotations: []*Annotations{ 3420 { 3421 Schemas: []*SchemaAnnotation{ 3422 {Path: dataServers, Schema: schemaServers}, 3423 {Path: dataNetworks, Schema: schemaNetworks}, 3424 {Path: dataPorts, Schema: schemaPorts}, 3425 }, 3426 Scope: annotationScopeRule, 3427 }, 3428 }, 3429 }, 3430 { 3431 note: "Ill-structured (invalid) metadata start", 3432 module: ` 3433 package opa.examples 3434 3435 import data.servers 3436 import data.networks 3437 import data.ports 3438 3439 # METADATA 3440 # scope: rule 3441 # schemas: 3442 # - data.servers: schema.servers 3443 # - data.networks: schema.networks 3444 # - data.ports: schema.ports 3445 # METADATA 3446 public_servers[server] { 3447 server = servers[i]; server.ports[j] = ports[k].id 3448 ports[k].networks[l] = networks[m].id; 3449 networks[m].public = true 3450 }`, 3451 expError: "test.rego:14: rego_parse_error: yaml: line 7: could not find expected ':'", 3452 }, 3453 { 3454 note: "Ill-structured (invalid) annotation document path", 3455 module: ` 3456 package opa.examples 3457 3458 import data.servers 3459 import data.networks 3460 import data.ports 3461 3462 # METADATA 3463 # scope: rule 3464 # schemas: 3465 # - data/servers: schema.servers 3466 public_servers[server] { 3467 server = servers[i]; server.ports[j] = ports[k].id 3468 ports[k].networks[l] = networks[m].id; 3469 networks[m].public = true 3470 }`, 3471 expNumComments: 4, 3472 expError: "rego_parse_error: invalid document reference", 3473 }, 3474 { 3475 note: "Ill-structured (invalid) annotation schema path", 3476 module: ` 3477 package opa.examples 3478 3479 import data.servers 3480 import data.networks 3481 import data.ports 3482 3483 # METADATA 3484 # scope: rule 3485 # schemas: 3486 # - data.servers: schema/servers 3487 public_servers[server] { 3488 server = servers[i]; server.ports[j] = ports[k].id 3489 ports[k].networks[l] = networks[m].id; 3490 networks[m].public = true 3491 }`, 3492 expNumComments: 4, 3493 expError: "rego_parse_error: invalid schema reference", 3494 }, 3495 { 3496 note: "Ill-structured (invalid) annotation", 3497 module: ` 3498 package opa.examples 3499 3500 import data.servers 3501 import data.networks 3502 import data.ports 3503 3504 # METADATA 3505 # scope: rule 3506 # schemas: 3507 # - data.servers= schema 3508 public_servers[server] { 3509 server = servers[i]; server.ports[j] = ports[k].id 3510 ports[k].networks[l] = networks[m].id; 3511 networks[m].public = true 3512 }`, 3513 expNumComments: 5, 3514 expError: "rego_parse_error: yaml: unmarshal errors:\n line 3: cannot unmarshal !!str", 3515 }, 3516 { 3517 note: "Indentation error in yaml", 3518 module: ` 3519 package opa.examples 3520 3521 import data.servers 3522 import data.networks 3523 import data.ports 3524 3525 # METADATA 3526 # scope: rule 3527 # schemas: 3528 # - data.servers: schema.servers 3529 # - data.networks: schema.networks 3530 # - data.ports: schema.ports 3531 public_servers[server] { 3532 server = servers[i]; server.ports[j] = ports[k].id 3533 ports[k].networks[l] = networks[m].id; 3534 networks[m].public = true 3535 }`, 3536 expNumComments: 6, 3537 expError: "rego_parse_error: yaml: line 3: did not find expected key", 3538 }, 3539 { 3540 note: "Multiple rules with and without metadata", 3541 module: ` 3542 package opa.examples 3543 3544 import data.servers 3545 import data.networks 3546 import data.ports 3547 3548 # METADATA 3549 # scope: rule 3550 # schemas: 3551 # - data.servers: schema.servers 3552 # - data.networks: schema.networks 3553 # - data.ports: schema.ports 3554 public_servers[server] { 3555 server = servers[i]; server.ports[j] = ports[k].id 3556 ports[k].networks[l] = networks[m].id; 3557 networks[m].public = true 3558 } 3559 3560 public_servers_1[server] { 3561 server = servers[i]; server.ports[j] = ports[k].id 3562 ports[k].networks[l] = networks[m].id; 3563 networks[m].public = true 3564 server.typo # won't catch this type error since rule has no schema metadata 3565 }`, 3566 expNumComments: 7, 3567 expAnnotations: []*Annotations{ 3568 { 3569 Schemas: []*SchemaAnnotation{ 3570 {Path: dataServers, Schema: schemaServers}, 3571 {Path: dataNetworks, Schema: schemaNetworks}, 3572 {Path: dataPorts, Schema: schemaPorts}, 3573 }, 3574 Scope: annotationScopeRule, 3575 }, 3576 }, 3577 }, 3578 { 3579 note: "Multiple rules with metadata", 3580 module: ` 3581 package opa.examples 3582 3583 import data.servers 3584 import data.networks 3585 import data.ports 3586 3587 # METADATA 3588 # scope: rule 3589 # schemas: 3590 # - data.servers: schema.servers 3591 public_servers[server] { 3592 server = servers[i] 3593 } 3594 3595 # METADATA 3596 # scope: rule 3597 # schemas: 3598 # - data.networks: schema.networks 3599 # - data.ports: schema.ports 3600 public_servers_1[server] { 3601 ports[k].networks[l] = networks[m].id; 3602 networks[m].public = true 3603 }`, 3604 expNumComments: 9, 3605 expAnnotations: []*Annotations{ 3606 { 3607 Schemas: []*SchemaAnnotation{ 3608 {Path: dataServers, Schema: schemaServers}, 3609 }, 3610 Scope: annotationScopeRule, 3611 node: MustParseRule(`public_servers[server] { server = servers[i] }`), 3612 }, 3613 { 3614 Schemas: []*SchemaAnnotation{ 3615 3616 {Path: dataNetworks, Schema: schemaNetworks}, 3617 {Path: dataPorts, Schema: schemaPorts}, 3618 }, 3619 Scope: annotationScopeRule, 3620 node: MustParseRule(`public_servers_1[server] { ports[k].networks[l] = networks[m].id; networks[m].public = true }`), 3621 }, 3622 }, 3623 }, 3624 { 3625 note: "multiple metadata blocks on a single rule", 3626 module: `package test 3627 3628 # METADATA 3629 # title: My rule 3630 3631 # METADATA 3632 # title: My rule 2 3633 p { input = "str" }`, 3634 expNumComments: 4, 3635 expAnnotations: []*Annotations{ 3636 { 3637 Scope: annotationScopeRule, 3638 Title: "My rule", 3639 }, 3640 { 3641 Scope: annotationScopeRule, 3642 Title: "My rule 2", 3643 }, 3644 }, 3645 }, 3646 { 3647 note: "Empty annotation error due to whitespace following METADATA hint", 3648 module: `package test 3649 3650 # METADATA 3651 3652 # scope: rule 3653 p { input.x > 7 }`, 3654 expError: "test.rego:3: rego_parse_error: expected METADATA block, found whitespace", 3655 }, 3656 { 3657 note: "Annotation on constant", 3658 module: ` 3659 package test 3660 3661 # METADATA 3662 # scope: rule 3663 p := 7`, 3664 expNumComments: 2, 3665 expAnnotations: []*Annotations{ 3666 {Scope: annotationScopeRule}, 3667 }, 3668 }, 3669 { 3670 note: "annotation on package", 3671 module: `# METADATA 3672 # title: My package 3673 package test 3674 3675 p { input = "str" }`, 3676 expNumComments: 2, 3677 expAnnotations: []*Annotations{ 3678 { 3679 Scope: annotationScopePackage, 3680 Title: "My package", 3681 }, 3682 }, 3683 }, 3684 { 3685 note: "annotation on import", 3686 module: `package test 3687 3688 # METADATA 3689 # title: My import 3690 import input.foo 3691 3692 p { input = "str" }`, 3693 expNumComments: 2, 3694 expError: "1 error occurred: test.rego:3: rego_parse_error: invalid annotation scope 'import'", 3695 }, 3696 { 3697 note: "Default rule scope", 3698 module: ` 3699 package test 3700 3701 # METADATA 3702 # {} 3703 p := 7`, 3704 expNumComments: 2, 3705 expAnnotations: []*Annotations{ 3706 {Scope: annotationScopeRule}, 3707 }, 3708 }, 3709 { 3710 note: "Unknown scope", 3711 module: ` 3712 package test 3713 3714 # METADATA 3715 # scope: deadbeef 3716 p := 7`, 3717 expNumComments: 2, 3718 expError: "invalid annotation scope 'deadbeef'", 3719 }, 3720 { 3721 note: "Invalid rule scope/attachment", 3722 module: ` 3723 # METADATA 3724 # scope: rule 3725 package test 3726 3727 p := 7`, 3728 expNumComments: 2, 3729 expError: "test.rego:2: rego_parse_error: annotation scope 'rule' must be applied to rule (have package)", 3730 }, 3731 { 3732 note: "Scope attachment error: document on import", 3733 module: `package test 3734 # METADATA 3735 # scope: document 3736 import data.foo.bar`, 3737 expError: "test.rego:2: rego_parse_error: annotation scope 'document' must be applied to rule (have import)", 3738 }, 3739 { 3740 note: "Scope attachment error: unattached", 3741 module: `package test 3742 3743 # METADATA 3744 # scope: package`, 3745 expError: "test.rego:3: rego_parse_error: annotation scope 'package' must be applied to package", 3746 }, 3747 { 3748 note: "Scope attachment error: package on non-package", 3749 module: `package test 3750 # METADATA 3751 # scope: package 3752 import data.foo`, 3753 expError: "test.rego:2: rego_parse_error: annotation scope 'package' must be applied to package (have import)", 3754 }, 3755 { 3756 note: "Inline schema definition", 3757 module: `package test 3758 3759 # METADATA 3760 # schemas: 3761 # - input: {"type": "string"} 3762 p { input = "str" }`, 3763 expNumComments: 3, 3764 expAnnotations: []*Annotations{ 3765 { 3766 Schemas: []*SchemaAnnotation{ 3767 {Path: InputRootRef, Definition: &stringSchema}, 3768 }, 3769 Scope: annotationScopeRule, 3770 }, 3771 }, 3772 }, 3773 { 3774 note: "Rich meta", 3775 module: `package test 3776 3777 # METADATA 3778 # title: My rule 3779 # description: | 3780 # My rule has a 3781 # multiline description. 3782 # organizations: 3783 # - Acme Corp. 3784 # - Soylent Corp. 3785 # - Tyrell Corp. 3786 # related_resources: 3787 # - https://example.com 3788 # - 3789 # ref: http://john:123@do.re/mi?foo=bar#baz 3790 # description: foo bar 3791 # authors: 3792 # - John Doe <john@example.com> 3793 # - name: Jane Doe 3794 # email: jane@example.com 3795 # custom: 3796 # list: 3797 # - a 3798 # - b 3799 # map: 3800 # a: 1 3801 # b: 2.2 3802 # c: 3803 # "3": d 3804 # "4": e 3805 # number: 42 3806 # string: foo bar baz 3807 # flag: 3808 p { input = "str" }`, 3809 expNumComments: 31, 3810 expAnnotations: []*Annotations{ 3811 { 3812 Scope: annotationScopeRule, 3813 Title: "My rule", 3814 Description: "My rule has a\nmultiline description.\n", 3815 Organizations: []string{"Acme Corp.", "Soylent Corp.", "Tyrell Corp."}, 3816 RelatedResources: []*RelatedResourceAnnotation{ 3817 { 3818 Ref: mustParseURL("https://example.com"), 3819 }, 3820 { 3821 Ref: mustParseURL("http://john:123@do.re/mi?foo=bar#baz"), 3822 Description: "foo bar", 3823 }, 3824 }, 3825 Authors: []*AuthorAnnotation{ 3826 { 3827 Name: "John Doe", 3828 Email: "john@example.com", 3829 }, 3830 { 3831 Name: "Jane Doe", 3832 Email: "jane@example.com", 3833 }, 3834 }, 3835 Custom: map[string]interface{}{ 3836 "list": []interface{}{ 3837 "a", "b", 3838 }, 3839 "map": map[string]interface{}{ 3840 "a": 1, 3841 "b": 2.2, 3842 "c": map[string]interface{}{ 3843 "3": "d", 3844 "4": "e", 3845 }, 3846 }, 3847 "number": 42, 3848 "string": "foo bar baz", 3849 "flag": nil, 3850 }, 3851 }, 3852 }, 3853 }, 3854 } 3855 3856 for _, tc := range tests { 3857 t.Run(tc.note, func(t *testing.T) { 3858 mod, err := ParseModuleWithOpts("test.rego", tc.module, ParserOptions{ 3859 ProcessAnnotation: true, 3860 }) 3861 if err != nil { 3862 if tc.expError == "" || !strings.Contains(err.Error(), tc.expError) { 3863 t.Fatalf("Unexpected parse error when getting annotations: %v", err) 3864 } 3865 return 3866 } else if tc.expError != "" { 3867 t.Fatalf("Expected err: %v but no error from parse module", tc.expError) 3868 } 3869 3870 if len(mod.Comments) != tc.expNumComments { 3871 t.Fatalf("Expected %v comments but got %v", tc.expNumComments, len(mod.Comments)) 3872 } 3873 3874 if annotationsCompare(tc.expAnnotations, mod.Annotations) != 0 { 3875 t.Fatalf("expected %v but got %v", tc.expAnnotations, mod.Annotations) 3876 } 3877 }) 3878 } 3879 } 3880 3881 func TestAuthorAnnotation(t *testing.T) { 3882 tests := []struct { 3883 note string 3884 raw interface{} 3885 expected interface{} 3886 }{ 3887 { 3888 note: "no name", 3889 raw: "", 3890 expected: fmt.Errorf("author is an empty string"), 3891 }, 3892 { 3893 note: "only whitespaces", 3894 raw: " \t", 3895 expected: fmt.Errorf("author is an empty string"), 3896 }, 3897 { 3898 note: "one name only", 3899 raw: "John", 3900 expected: AuthorAnnotation{Name: "John"}, 3901 }, 3902 { 3903 note: "multiple names", 3904 raw: "John Jr.\tDoe", 3905 expected: AuthorAnnotation{Name: "John Jr. Doe"}, 3906 }, 3907 { 3908 note: "email only", 3909 raw: "<john@example.com>", 3910 expected: AuthorAnnotation{Email: "john@example.com"}, 3911 }, 3912 { 3913 note: "name and email", 3914 raw: "John Doe <john@example.com>", 3915 expected: AuthorAnnotation{Name: "John Doe", Email: "john@example.com"}, 3916 }, 3917 { 3918 note: "empty email", 3919 raw: "John Doe <>", 3920 expected: AuthorAnnotation{Name: "John Doe"}, 3921 }, 3922 { 3923 note: "name with reserved characters", 3924 raw: "John Doe < >", 3925 expected: AuthorAnnotation{Name: "John Doe < >"}, 3926 }, 3927 { 3928 note: "name with reserved characters (email with space)", 3929 raw: "<john@ example.com>", 3930 expected: AuthorAnnotation{Name: "<john@ example.com>"}, 3931 }, 3932 { 3933 note: "map with name", 3934 raw: map[string]interface{}{ 3935 "name": "John Doe", 3936 }, 3937 expected: AuthorAnnotation{Name: "John Doe"}, 3938 }, 3939 { 3940 note: "map with email", 3941 raw: map[string]interface{}{ 3942 "email": "john@example.com", 3943 }, 3944 expected: AuthorAnnotation{Email: "john@example.com"}, 3945 }, 3946 { 3947 note: "map with name and email", 3948 raw: map[string]interface{}{ 3949 "name": "John Doe", 3950 "email": "john@example.com", 3951 }, 3952 expected: AuthorAnnotation{Name: "John Doe", Email: "john@example.com"}, 3953 }, 3954 { 3955 note: "map with extra entry", 3956 raw: map[string]interface{}{ 3957 "name": "John Doe", 3958 "email": "john@example.com", 3959 "foo": "bar", 3960 }, 3961 expected: AuthorAnnotation{Name: "John Doe", Email: "john@example.com"}, 3962 }, 3963 { 3964 note: "empty map", 3965 raw: map[string]interface{}{}, 3966 expected: fmt.Errorf("'name' and/or 'email' values required in object"), 3967 }, 3968 { 3969 note: "map with empty name", 3970 raw: map[string]interface{}{ 3971 "name": "", 3972 }, 3973 expected: fmt.Errorf("'name' and/or 'email' values required in object"), 3974 }, 3975 { 3976 note: "map with email and empty name", 3977 raw: map[string]interface{}{ 3978 "name": "", 3979 "email": "john@example.com", 3980 }, 3981 expected: AuthorAnnotation{Email: "john@example.com"}, 3982 }, 3983 { 3984 note: "map with empty email", 3985 raw: map[string]interface{}{ 3986 "email": "", 3987 }, 3988 expected: fmt.Errorf("'name' and/or 'email' values required in object"), 3989 }, 3990 { 3991 note: "map with name and empty email", 3992 raw: map[string]interface{}{ 3993 "name": "John Doe", 3994 "email": "", 3995 }, 3996 expected: AuthorAnnotation{Name: "John Doe"}, 3997 }, 3998 } 3999 4000 for _, tc := range tests { 4001 t.Run(tc.note, func(t *testing.T) { 4002 parsed, err := parseAuthor(tc.raw) 4003 4004 switch expected := tc.expected.(type) { 4005 case AuthorAnnotation: 4006 if err != nil { 4007 t.Fatal(err) 4008 } 4009 4010 if parsed.Compare(&expected) != 0 { 4011 t.Fatalf("expected %v but got %v", tc.expected, parsed) 4012 } 4013 case error: 4014 if err == nil { 4015 t.Fatalf("expected '%v' error but got %v", tc.expected, parsed) 4016 } 4017 4018 if strings.Compare(expected.Error(), err.Error()) != 0 { 4019 t.Fatalf("expected %v but got %v", tc.expected, err) 4020 } 4021 default: 4022 t.Fatalf("Unexpected result type: %T", expected) 4023 } 4024 }) 4025 } 4026 } 4027 4028 func TestRelatedResourceAnnotation(t *testing.T) { 4029 tests := []struct { 4030 note string 4031 raw interface{} 4032 expected interface{} 4033 }{ 4034 { 4035 note: "empty ref URL", 4036 raw: "", 4037 expected: fmt.Errorf("ref URL may not be empty string"), 4038 }, 4039 { 4040 note: "only whitespaces in ref URL", 4041 raw: " \t", 4042 expected: fmt.Errorf("parse \" \\t\": net/url: invalid control character in URL"), 4043 }, 4044 { 4045 note: "invalid ref URL", 4046 raw: "https://foo:bar", 4047 expected: fmt.Errorf("parse \"https://foo:bar\": invalid port \":bar\" after host"), 4048 }, 4049 { 4050 note: "ref URL as string", 4051 raw: "https://example.com/foo?bar#baz", 4052 expected: RelatedResourceAnnotation{Ref: mustParseURL("https://example.com/foo?bar#baz")}, 4053 }, 4054 { 4055 note: "map with only ref", 4056 raw: map[string]interface{}{ 4057 "ref": "https://example.com/foo?bar#baz", 4058 }, 4059 expected: RelatedResourceAnnotation{Ref: mustParseURL("https://example.com/foo?bar#baz")}, 4060 }, 4061 { 4062 note: "map with only description", 4063 raw: map[string]interface{}{ 4064 "description": "foo bar", 4065 }, 4066 expected: fmt.Errorf("'ref' value required in object"), 4067 }, 4068 { 4069 note: "map with ref and description", 4070 raw: map[string]interface{}{ 4071 "ref": "https://example.com/foo?bar#baz", 4072 "description": "foo bar", 4073 }, 4074 expected: RelatedResourceAnnotation{ 4075 Ref: mustParseURL("https://example.com/foo?bar#baz"), 4076 Description: "foo bar", 4077 }, 4078 }, 4079 { 4080 note: "map with ref and description", 4081 raw: map[string]interface{}{ 4082 "ref": "https://example.com/foo?bar#baz", 4083 "description": "foo bar", 4084 "foo": "bar", 4085 }, 4086 expected: RelatedResourceAnnotation{ 4087 Ref: mustParseURL("https://example.com/foo?bar#baz"), 4088 Description: "foo bar", 4089 }, 4090 }, 4091 { 4092 note: "empty map", 4093 raw: map[string]interface{}{}, 4094 expected: fmt.Errorf("'ref' value required in object"), 4095 }, 4096 { 4097 note: "map with empty ref", 4098 raw: map[string]interface{}{ 4099 "ref": "", 4100 }, 4101 expected: fmt.Errorf("'ref' value required in object"), 4102 }, 4103 { 4104 note: "map with only whitespace in ref", 4105 raw: map[string]interface{}{ 4106 "ref": " \t", 4107 }, 4108 expected: fmt.Errorf("'ref' value required in object"), 4109 }, 4110 } 4111 4112 for _, tc := range tests { 4113 t.Run(tc.note, func(t *testing.T) { 4114 parsed, err := parseRelatedResource(tc.raw) 4115 4116 switch expected := tc.expected.(type) { 4117 case RelatedResourceAnnotation: 4118 if err != nil { 4119 t.Fatal(err) 4120 } 4121 4122 if parsed.Compare(&expected) != 0 { 4123 t.Fatalf("expected %v but got %v", tc.expected, parsed) 4124 } 4125 case error: 4126 if err == nil { 4127 t.Fatalf("expected '%v' error but got %v", tc.expected, parsed) 4128 } 4129 4130 if strings.Compare(expected.Error(), err.Error()) != 0 { 4131 t.Fatalf("expected %v but got %v", tc.expected, err) 4132 } 4133 default: 4134 t.Fatalf("Unexpected result type: %T", expected) 4135 } 4136 }) 4137 } 4138 } 4139 4140 func assertLocationText(t *testing.T, expected string, actual *Location) { 4141 t.Helper() 4142 if actual == nil || actual.Text == nil { 4143 t.Errorf("Expected a non nil location and text") 4144 return 4145 } 4146 if string(actual.Text) != expected { 4147 t.Errorf("Unexpected Location text, got:\n%s\n\nExpected:\n%s\n\n", actual.Text, expected) 4148 } 4149 } 4150 4151 func assertParseError(t *testing.T, msg string, input string) { 4152 t.Helper() 4153 t.Run(msg, func(t *testing.T) { 4154 assertParseErrorFunc(t, msg, input, func(string) {}) 4155 }) 4156 } 4157 4158 func assertParseErrorContains(t *testing.T, msg string, input string, expected string, opts ...ParserOptions) { 4159 t.Helper() 4160 assertParseErrorFunc(t, msg, input, func(result string) { 4161 t.Helper() 4162 if !strings.Contains(result, expected) { 4163 t.Errorf("Error on test \"%s\": expected parse error to contain:\n\n%v\n\nbut got:\n\n%v", msg, expected, result) 4164 } 4165 }, opts...) 4166 } 4167 4168 func assertParseErrorFunc(t *testing.T, msg string, input string, f func(string), opts ...ParserOptions) { 4169 t.Helper() 4170 opt := ParserOptions{} 4171 if len(opts) == 1 { 4172 opt = opts[0] 4173 } 4174 stmts, _, err := ParseStatementsWithOpts("", input, opt) 4175 if err == nil && len(stmts) != 1 { 4176 err = fmt.Errorf("expected exactly one statement") 4177 } 4178 if err == nil { 4179 t.Errorf("Error on test \"%s\": expected parse error on %s: expected no statements, got %d: %v", msg, input, len(stmts), stmts) 4180 return 4181 } 4182 result := err.Error() 4183 // error occurred: <line>:<col>: <message> 4184 parts := strings.SplitN(result, ":", 4) 4185 result = strings.TrimSpace(parts[len(parts)-1]) 4186 f(result) 4187 } 4188 4189 func assertParseImport(t *testing.T, msg string, input string, correct *Import, opts ...ParserOptions) { 4190 t.Helper() 4191 assertParseOne(t, msg, input, func(parsed interface{}) { 4192 t.Helper() 4193 imp := parsed.(*Import) 4194 if !imp.Equal(correct) { 4195 t.Errorf("Error on test \"%s\": imports not equal: %v (parsed), %v (correct)", msg, imp, correct) 4196 } 4197 }, opts...) 4198 } 4199 4200 func assertParseModule(t *testing.T, msg string, input string, correct *Module, opts ...ParserOptions) { 4201 opt := ParserOptions{} 4202 if len(opts) == 1 { 4203 opt = opts[0] 4204 } 4205 m, err := ParseModuleWithOpts("", input, opt) 4206 if err != nil { 4207 t.Errorf("Error on test \"%s\": parse error on %s: %s", msg, input, err) 4208 return 4209 } 4210 4211 if !m.Equal(correct) { 4212 t.Errorf("Error on test %s: modules not equal: %v (parsed), %v (correct)", msg, m, correct) 4213 } 4214 4215 } 4216 4217 func assertParseModuleError(t *testing.T, msg, input string) { 4218 m, err := ParseModule("", input) 4219 if err == nil { 4220 t.Errorf("Error on test \"%s\": expected parse error: %v (parsed)", msg, m) 4221 } 4222 } 4223 4224 func assertParsePackage(t *testing.T, msg string, input string, correct *Package) { 4225 assertParseOne(t, msg, input, func(parsed interface{}) { 4226 pkg := parsed.(*Package) 4227 if !pkg.Equal(correct) { 4228 t.Errorf("Error on test \"%s\": packages not equal: %v (parsed), %v (correct)", msg, pkg, correct) 4229 } 4230 }) 4231 } 4232 4233 func assertParseOne(t *testing.T, msg string, input string, correct func(interface{}), opts ...ParserOptions) { 4234 t.Helper() 4235 opt := ParserOptions{} 4236 if len(opts) == 1 { 4237 opt = opts[0] 4238 } 4239 stmts, _, err := ParseStatementsWithOpts("", input, opt) 4240 if err != nil { 4241 t.Errorf("Error on test \"%s\": parse error on %s: %s", msg, input, err) 4242 return 4243 } 4244 if len(stmts) != 1 { 4245 t.Errorf("Error on test \"%s\": parse error on %s: expected exactly one statement, got %d: %v", msg, input, len(stmts), stmts) 4246 return 4247 } 4248 correct(stmts[0]) 4249 } 4250 4251 func assertParseOneBody(t *testing.T, msg string, input string, correct Body) { 4252 t.Helper() 4253 body, err := ParseBody(input) 4254 if err != nil { 4255 t.Fatal(err) 4256 } 4257 if !body.Equal(correct) { 4258 t.Fatalf("Error on test \"%s\": bodies not equal:\n%v (parsed)\n%v (correct)", msg, body, correct) 4259 } 4260 } 4261 4262 func assertParseOneExpr(t *testing.T, msg string, input string, correct *Expr, opts ...ParserOptions) { 4263 t.Helper() 4264 assertParseOne(t, msg, input, func(parsed interface{}) { 4265 t.Helper() 4266 body := parsed.(Body) 4267 if len(body) != 1 { 4268 t.Errorf("Error on test \"%s\": parser returned multiple expressions: %v", msg, body) 4269 return 4270 } 4271 expr := body[0] 4272 if !expr.Equal(correct) { 4273 t.Errorf("Error on test \"%s\": expressions not equal:\n%v (parsed)\n%v (correct)", msg, expr, correct) 4274 } 4275 }, opts...) 4276 } 4277 4278 func assertParseOneExprNegated(t *testing.T, msg string, input string, correct *Expr) { 4279 correct.Negated = true 4280 assertParseOneExpr(t, msg, input, correct) 4281 } 4282 4283 func assertParseOneTerm(t *testing.T, msg string, input string, correct *Term) { 4284 t.Helper() 4285 t.Run(msg, func(t *testing.T) { 4286 assertParseOneExpr(t, msg, input, &Expr{Terms: correct}) 4287 }) 4288 } 4289 4290 func assertParseOneTermNegated(t *testing.T, msg string, input string, correct *Term) { 4291 t.Helper() 4292 assertParseOneExprNegated(t, msg, input, &Expr{Terms: correct}) 4293 } 4294 4295 func assertParseRule(t *testing.T, msg string, input string, correct *Rule, opts ...ParserOptions) { 4296 t.Helper() 4297 assertParseOne(t, msg, input, func(parsed interface{}) { 4298 t.Helper() 4299 rule := parsed.(*Rule) 4300 if !rule.Equal(correct) { 4301 t.Errorf("Error on test \"%s\": rules not equal: %v (parsed), %v (correct)", msg, rule, correct) 4302 } 4303 }, 4304 opts...) 4305 }