diff --git a/README.md b/README.md
index 489433b..4fa8af8 100644
--- a/README.md
+++ b/README.md
@@ -8,10 +8,8 @@
Regorus is available as a library that can be easily integrated into your Rust projects.
-> **Warning**
-> While Regorus is highly performant and can interpret complex Rego policies, it does not yet pass the full [OPA test-suite](https://www.openpolicyagent.org/docs/latest/ir/#test-suite).
-> We are actively working to achieve full OPA compliance. Meanwhile, Regorus should be considered
-> **experimental and used with discretion**.
+Regorus passes the [OPA v0.60.0 test-suite](https://www.openpolicyagent.org/docs/latest/ir/#test-suite) barring a few builtins.
+See [OPA Conformance][#opa-conformance] below.
## Getting Started
@@ -119,6 +117,60 @@ Compare it with OPA
Range (min … max): 43.8 ms … 46.7 ms 62 runs
+## OPA Conformance
+
+Regorus has been verified to be compliant with [OPA v0.60.0](https://github.com/open-policy-agent/opa/releases/tag/v0.60.0)
+using a [test driver](tests/opa.rs) that loads and runs the OPA testsuite using Regorus, and verifies that expected outputs
+are produced.
+
+The test driver can be invoked by running:
+
+```
+cargo test -r --test opa
+```
+
+Currently, Regorus passes all the non-builtin specific tests. See [passing tests suites](tests/opa.passing).
+
+The following test suites don't pass fully due to mising builtins:
+- `cryptoparsersaprivatekeys`
+- `cryptox509parseandverifycertificates`
+- `cryptox509parsecertificaterequest`
+- `cryptox509parsecertificates`
+- `cryptox509parsekeypair`
+- `cryptox509parsersaprivatekey`
+- `globsmatch`
+- `graphql`
+- `invalidkeyerror`
+- `jsonpatch`
+- `jwtbuiltins`
+- `jwtdecodeverify`
+- `jwtencodesign`
+- `jwtencodesignraw`
+- `jwtverifyhs256`
+- `jwtverifyhs384`
+- `jwtverifyhs512`
+- `jwtverifyrsa`
+- `netcidrcontains`
+- `netcidrcontainsmatches`
+- `netcidrexpand`
+- `netcidrintersects`
+- `netcidrisvalid`
+- `netcidrmerge`
+- `netcidroverlap`
+- `netlookupipaddr`
+- `providers-aws`
+- `regometadatachain`
+- `regometadatarule`
+- `regoparsemodule`
+- `rendertemplate`
+- `time`
+
+They are captured in the following [github issues](https://github.com/microsoft/regorus/issues?q=is%3Aopen+is%3Aissue+label%3Alib).
+
+### Grammar
+
+The grammar used by Regorus to parse Rego policies is described in [grammar.md](docs/grammar.md) in both EBNF and RailRoad Diagram formats.
+
## Contributing
This project welcomes contributions and suggestions. Most contributions require you to agree to a
diff --git a/docs/diagram/Railroad-Diagram-Generator.svg b/docs/diagram/Railroad-Diagram-Generator.svg
new file mode 100644
index 0000000..9d1e83c
--- /dev/null
+++ b/docs/diagram/Railroad-Diagram-Generator.svg
@@ -0,0 +1,57 @@
+
+
diff --git a/docs/diagram/alternatives.svg b/docs/diagram/alternatives.svg
new file mode 100644
index 0000000..645c4e5
--- /dev/null
+++ b/docs/diagram/alternatives.svg
@@ -0,0 +1,52 @@
+
+
diff --git a/docs/diagram/and-expr.svg b/docs/diagram/and-expr.svg
new file mode 100644
index 0000000..3fb18ea
--- /dev/null
+++ b/docs/diagram/and-expr.svg
@@ -0,0 +1,53 @@
+
+
diff --git a/docs/diagram/arith-expr.svg b/docs/diagram/arith-expr.svg
new file mode 100644
index 0000000..607bd59
--- /dev/null
+++ b/docs/diagram/arith-expr.svg
@@ -0,0 +1,61 @@
+
+
diff --git a/docs/diagram/array-compr.svg b/docs/diagram/array-compr.svg
new file mode 100644
index 0000000..64870d9
--- /dev/null
+++ b/docs/diagram/array-compr.svg
@@ -0,0 +1,61 @@
+
+
diff --git a/docs/diagram/array.svg b/docs/diagram/array.svg
new file mode 100644
index 0000000..8c2a060
--- /dev/null
+++ b/docs/diagram/array.svg
@@ -0,0 +1,77 @@
+
+
diff --git a/docs/diagram/assign-expr.svg b/docs/diagram/assign-expr.svg
new file mode 100644
index 0000000..13a2d43
--- /dev/null
+++ b/docs/diagram/assign-expr.svg
@@ -0,0 +1,59 @@
+
+
diff --git a/docs/diagram/assign-op.svg b/docs/diagram/assign-op.svg
new file mode 100644
index 0000000..3a5abfc
--- /dev/null
+++ b/docs/diagram/assign-op.svg
@@ -0,0 +1,54 @@
+
+
diff --git a/docs/diagram/bool-expr.svg b/docs/diagram/bool-expr.svg
new file mode 100644
index 0000000..ac17e76
--- /dev/null
+++ b/docs/diagram/bool-expr.svg
@@ -0,0 +1,52 @@
+
+
diff --git a/docs/diagram/bool-op.svg b/docs/diagram/bool-op.svg
new file mode 100644
index 0000000..b48bde7
--- /dev/null
+++ b/docs/diagram/bool-op.svg
@@ -0,0 +1,86 @@
+
+
diff --git a/docs/diagram/call-args.svg b/docs/diagram/call-args.svg
new file mode 100644
index 0000000..2e40b70
--- /dev/null
+++ b/docs/diagram/call-args.svg
@@ -0,0 +1,53 @@
+
+
diff --git a/docs/diagram/call-expr.svg b/docs/diagram/call-expr.svg
new file mode 100644
index 0000000..d58936a
--- /dev/null
+++ b/docs/diagram/call-expr.svg
@@ -0,0 +1,83 @@
+
+
diff --git a/docs/diagram/compr-or-array.svg b/docs/diagram/compr-or-array.svg
new file mode 100644
index 0000000..8ad1c60
--- /dev/null
+++ b/docs/diagram/compr-or-array.svg
@@ -0,0 +1,52 @@
+
+
diff --git a/docs/diagram/compr-rule.svg b/docs/diagram/compr-rule.svg
new file mode 100644
index 0000000..cefcb80
--- /dev/null
+++ b/docs/diagram/compr-rule.svg
@@ -0,0 +1,52 @@
+
+
diff --git a/docs/diagram/compr-set-or-object.svg b/docs/diagram/compr-set-or-object.svg
new file mode 100644
index 0000000..9233d8f
--- /dev/null
+++ b/docs/diagram/compr-set-or-object.svg
@@ -0,0 +1,66 @@
+
+
diff --git a/docs/diagram/compr.svg b/docs/diagram/compr.svg
new file mode 100644
index 0000000..2e84d73
--- /dev/null
+++ b/docs/diagram/compr.svg
@@ -0,0 +1,60 @@
+
+
diff --git a/docs/diagram/contains-rule.svg b/docs/diagram/contains-rule.svg
new file mode 100644
index 0000000..ae3089a
--- /dev/null
+++ b/docs/diagram/contains-rule.svg
@@ -0,0 +1,60 @@
+
+
diff --git a/docs/diagram/default-rule.svg b/docs/diagram/default-rule.svg
new file mode 100644
index 0000000..30aede9
--- /dev/null
+++ b/docs/diagram/default-rule.svg
@@ -0,0 +1,67 @@
+
+
diff --git a/docs/diagram/else-block.svg b/docs/diagram/else-block.svg
new file mode 100644
index 0000000..a7ddc62
--- /dev/null
+++ b/docs/diagram/else-block.svg
@@ -0,0 +1,114 @@
+
+
diff --git a/docs/diagram/else-blocks.svg b/docs/diagram/else-blocks.svg
new file mode 100644
index 0000000..9b61687
--- /dev/null
+++ b/docs/diagram/else-blocks.svg
@@ -0,0 +1,45 @@
+
+
diff --git a/docs/diagram/every.svg b/docs/diagram/every.svg
new file mode 100644
index 0000000..e3f0bb5
--- /dev/null
+++ b/docs/diagram/every.svg
@@ -0,0 +1,106 @@
+
+
diff --git a/docs/diagram/expr.svg b/docs/diagram/expr.svg
new file mode 100644
index 0000000..98e6848
--- /dev/null
+++ b/docs/diagram/expr.svg
@@ -0,0 +1,44 @@
+
+
diff --git a/docs/diagram/field.svg b/docs/diagram/field.svg
new file mode 100644
index 0000000..d0f0aa7
--- /dev/null
+++ b/docs/diagram/field.svg
@@ -0,0 +1,60 @@
+
+
diff --git a/docs/diagram/func-rule.svg b/docs/diagram/func-rule.svg
new file mode 100644
index 0000000..16141e3
--- /dev/null
+++ b/docs/diagram/func-rule.svg
@@ -0,0 +1,91 @@
+
+
diff --git a/docs/diagram/imports.svg b/docs/diagram/imports.svg
new file mode 100644
index 0000000..1e76718
--- /dev/null
+++ b/docs/diagram/imports.svg
@@ -0,0 +1,68 @@
+
+
diff --git a/docs/diagram/in-expr.svg b/docs/diagram/in-expr.svg
new file mode 100644
index 0000000..0428aeb
--- /dev/null
+++ b/docs/diagram/in-expr.svg
@@ -0,0 +1,53 @@
+
+
diff --git a/docs/diagram/literal-stmt.svg b/docs/diagram/literal-stmt.svg
new file mode 100644
index 0000000..545ff95
--- /dev/null
+++ b/docs/diagram/literal-stmt.svg
@@ -0,0 +1,52 @@
+
+
diff --git a/docs/diagram/literal.svg b/docs/diagram/literal.svg
new file mode 100644
index 0000000..cf34bc1
--- /dev/null
+++ b/docs/diagram/literal.svg
@@ -0,0 +1,66 @@
+
+
diff --git a/docs/diagram/membership-expr.svg b/docs/diagram/membership-expr.svg
new file mode 100644
index 0000000..aa6918e
--- /dev/null
+++ b/docs/diagram/membership-expr.svg
@@ -0,0 +1,75 @@
+
+
diff --git a/docs/diagram/module.svg b/docs/diagram/module.svg
new file mode 100644
index 0000000..1a6b956
--- /dev/null
+++ b/docs/diagram/module.svg
@@ -0,0 +1,59 @@
+
+
diff --git a/docs/diagram/mul-div-expr.svg b/docs/diagram/mul-div-expr.svg
new file mode 100644
index 0000000..a45b338
--- /dev/null
+++ b/docs/diagram/mul-div-expr.svg
@@ -0,0 +1,61 @@
+
+
diff --git a/docs/diagram/non-imported-future-future-keyword.svg b/docs/diagram/non-imported-future-future-keyword.svg
new file mode 100644
index 0000000..1cd04da
--- /dev/null
+++ b/docs/diagram/non-imported-future-future-keyword.svg
@@ -0,0 +1,70 @@
+
+
diff --git a/docs/diagram/not-expr.svg b/docs/diagram/not-expr.svg
new file mode 100644
index 0000000..57126b0
--- /dev/null
+++ b/docs/diagram/not-expr.svg
@@ -0,0 +1,53 @@
+
+
diff --git a/docs/diagram/object-rule.svg b/docs/diagram/object-rule.svg
new file mode 100644
index 0000000..93ac5de
--- /dev/null
+++ b/docs/diagram/object-rule.svg
@@ -0,0 +1,82 @@
+
+
diff --git a/docs/diagram/object.svg b/docs/diagram/object.svg
new file mode 100644
index 0000000..6315607
--- /dev/null
+++ b/docs/diagram/object.svg
@@ -0,0 +1,77 @@
+
+
diff --git a/docs/diagram/or-expr.svg b/docs/diagram/or-expr.svg
new file mode 100644
index 0000000..6d829b8
--- /dev/null
+++ b/docs/diagram/or-expr.svg
@@ -0,0 +1,53 @@
+
+
diff --git a/docs/diagram/package.svg b/docs/diagram/package.svg
new file mode 100644
index 0000000..6e930c6
--- /dev/null
+++ b/docs/diagram/package.svg
@@ -0,0 +1,53 @@
+
+
diff --git a/docs/diagram/parens-expr.svg b/docs/diagram/parens-expr.svg
new file mode 100644
index 0000000..9951f7d
--- /dev/null
+++ b/docs/diagram/parens-expr.svg
@@ -0,0 +1,61 @@
+
+
diff --git a/docs/diagram/path-ref.svg b/docs/diagram/path-ref.svg
new file mode 100644
index 0000000..3d3fad0
--- /dev/null
+++ b/docs/diagram/path-ref.svg
@@ -0,0 +1,97 @@
+
+
diff --git a/docs/diagram/query-blocks.svg b/docs/diagram/query-blocks.svg
new file mode 100644
index 0000000..8737efb
--- /dev/null
+++ b/docs/diagram/query-blocks.svg
@@ -0,0 +1,61 @@
+
+
diff --git a/docs/diagram/query.svg b/docs/diagram/query.svg
new file mode 100644
index 0000000..6698b26
--- /dev/null
+++ b/docs/diagram/query.svg
@@ -0,0 +1,52 @@
+
+
diff --git a/docs/diagram/ref-brack.svg b/docs/diagram/ref-brack.svg
new file mode 100644
index 0000000..742de3b
--- /dev/null
+++ b/docs/diagram/ref-brack.svg
@@ -0,0 +1,75 @@
+
+
diff --git a/docs/diagram/ref-dot.svg b/docs/diagram/ref-dot.svg
new file mode 100644
index 0000000..e21668f
--- /dev/null
+++ b/docs/diagram/ref-dot.svg
@@ -0,0 +1,74 @@
+
+
diff --git a/docs/diagram/ref.svg b/docs/diagram/ref.svg
new file mode 100644
index 0000000..9464133
--- /dev/null
+++ b/docs/diagram/ref.svg
@@ -0,0 +1,94 @@
+
+
diff --git a/docs/diagram/rule-assign.svg b/docs/diagram/rule-assign.svg
new file mode 100644
index 0000000..ed5d2ad
--- /dev/null
+++ b/docs/diagram/rule-assign.svg
@@ -0,0 +1,52 @@
+
+
diff --git a/docs/diagram/rule-bodies.svg b/docs/diagram/rule-bodies.svg
new file mode 100644
index 0000000..8670004
--- /dev/null
+++ b/docs/diagram/rule-bodies.svg
@@ -0,0 +1,106 @@
+
+
diff --git a/docs/diagram/rule-head.svg b/docs/diagram/rule-head.svg
new file mode 100644
index 0000000..d0ae6ef
--- /dev/null
+++ b/docs/diagram/rule-head.svg
@@ -0,0 +1,73 @@
+
+
diff --git a/docs/diagram/rule-ref.svg b/docs/diagram/rule-ref.svg
new file mode 100644
index 0000000..c58fc83
--- /dev/null
+++ b/docs/diagram/rule-ref.svg
@@ -0,0 +1,111 @@
+
+
diff --git a/docs/diagram/rule.svg b/docs/diagram/rule.svg
new file mode 100644
index 0000000..3242983
--- /dev/null
+++ b/docs/diagram/rule.svg
@@ -0,0 +1,52 @@
+
+
diff --git a/docs/diagram/scalar-or-var.svg b/docs/diagram/scalar-or-var.svg
new file mode 100644
index 0000000..29a2df1
--- /dev/null
+++ b/docs/diagram/scalar-or-var.svg
@@ -0,0 +1,90 @@
+
+
diff --git a/docs/diagram/sep.svg b/docs/diagram/sep.svg
new file mode 100644
index 0000000..ec07431
--- /dev/null
+++ b/docs/diagram/sep.svg
@@ -0,0 +1,62 @@
+
+
diff --git a/docs/diagram/set-compr.svg b/docs/diagram/set-compr.svg
new file mode 100644
index 0000000..8c6d4e8
--- /dev/null
+++ b/docs/diagram/set-compr.svg
@@ -0,0 +1,61 @@
+
+
diff --git a/docs/diagram/set-rule.svg b/docs/diagram/set-rule.svg
new file mode 100644
index 0000000..fb7a0db
--- /dev/null
+++ b/docs/diagram/set-rule.svg
@@ -0,0 +1,75 @@
+
+
diff --git a/docs/diagram/set.svg b/docs/diagram/set.svg
new file mode 100644
index 0000000..2be76d6
--- /dev/null
+++ b/docs/diagram/set.svg
@@ -0,0 +1,93 @@
+
+
diff --git a/docs/diagram/some-in.svg b/docs/diagram/some-in.svg
new file mode 100644
index 0000000..d16f0ad
--- /dev/null
+++ b/docs/diagram/some-in.svg
@@ -0,0 +1,76 @@
+
+
diff --git a/docs/diagram/some-vars.svg b/docs/diagram/some-vars.svg
new file mode 100644
index 0000000..2888e96
--- /dev/null
+++ b/docs/diagram/some-vars.svg
@@ -0,0 +1,61 @@
+
+
diff --git a/docs/diagram/some.svg b/docs/diagram/some.svg
new file mode 100644
index 0000000..5c6d09b
--- /dev/null
+++ b/docs/diagram/some.svg
@@ -0,0 +1,52 @@
+
+
diff --git a/docs/diagram/spec-rule.svg b/docs/diagram/spec-rule.svg
new file mode 100644
index 0000000..4d65ff8
--- /dev/null
+++ b/docs/diagram/spec-rule.svg
@@ -0,0 +1,52 @@
+
+
diff --git a/docs/diagram/term.svg b/docs/diagram/term.svg
new file mode 100644
index 0000000..1d313ea
--- /dev/null
+++ b/docs/diagram/term.svg
@@ -0,0 +1,44 @@
+
+
diff --git a/docs/diagram/unary-expr.svg b/docs/diagram/unary-expr.svg
new file mode 100644
index 0000000..d104597
--- /dev/null
+++ b/docs/diagram/unary-expr.svg
@@ -0,0 +1,53 @@
+
+
diff --git a/docs/diagram/var.svg b/docs/diagram/var.svg
new file mode 100644
index 0000000..9ab54ba
--- /dev/null
+++ b/docs/diagram/var.svg
@@ -0,0 +1,52 @@
+
+
diff --git a/docs/diagram/with-modifiers.svg b/docs/diagram/with-modifiers.svg
new file mode 100644
index 0000000..90dcc4a
--- /dev/null
+++ b/docs/diagram/with-modifiers.svg
@@ -0,0 +1,68 @@
+
+
diff --git a/docs/grammar.md b/docs/grammar.md
index fd1598c..c6f49de 100644
--- a/docs/grammar.md
+++ b/docs/grammar.md
@@ -1,174 +1,966 @@
+Regorus uses the following grammar that has been derived from the official [OPA grammar](https://www.openpolicyagent.org/docs/latest/policy-reference/#grammar) as well as observed OPA behavior.
+
+```ebnf
+
+module ::= package imports rule*
+package ::= 'package' path-ref
+imports ::= ( 'import' path-ref ( 'as' var )? )*
+path-ref ::= path-ref NO_WS '.' NO_WS IDENT
+ | path-ref NO_WS '[' STRING ']'
+ | IDENT
+rule ::= default-rule
+ | spec-rule
+default-rule ::= 'default' rule-ref assign-op term
+spec-rule ::= rule-head rule-bodies
+rule-head ::= func-rule
+ | contains-rule
+ | object-rule
+ | set-rule
+ | compr-rule
+func-rule ::= rule-ref '(' term ( ',' term )* ','? ')' rule-assign?
+contains-rule ::= rule-ref 'contains' or-expr
+object-rule ::= rule-ref NO_WS '[' membership-expr ']' rule-assign
+set-rule ::= rule-ref NO_WS '[' membership-expr ']'
+compr-rule ::= rule-ref rule-assign?
+rule-ref ::= rule-ref NO_WS '.' NO_WS var
+ | path-ref NO_WS '[' membership-expr ']'
+ | var
+rule-assign ::= assign-op membership-expr
+rule-bodies ::= 'if' '{' query '}' alternatives
+ | 'if' literal-stmt alternatives
+ | '{' query '}' alternatives
+alternatives ::= query-blocks
+ | else-blocks
+query-blocks ::= ( '{' query '}' )*
+else-blocks ::= ( else-block )*
+else-block ::= 'else' rule-assign? 'if' '{' query '}'
+ | 'else' rule-assign? 'if' literal-stmt
+ | 'else' rule-assign? '{' query '}'
+assign-op ::= '=' | ':='
+query ::= literal-stmt ( sep literal-stmt )*
+sep ::= ';' | '\n' | '\r\n'
+literal-stmt ::= literal with-modifiers
+with-modifiers ::= ( 'with' path-ref 'as' in-expr )*
+literal ::= some
+ | every
+ | expr
+ | not-expr
+some ::= some-vars
+ | some-in
+some-vars ::= 'some' var ( ',' var )*
+some-in ::= 'some' ref ( ',' ref )? 'in'
+every ::= 'every' var ( ',' var )? 'in' bool-expr '{' query '}'
+expr ::= assign-expr
+not-expr ::= 'not' assign-expr
+assign-expr ::= ref assign-op membership-expr
+membership-expr ::= membership-expr 'in' bool-expr
+ | bool-expr ',' bool-expr
+ | bool-expr
+in-expr ::= in-expr 'in' bool-expr
+ | bool-expr
+bool-expr ::= bool-expr bool-op or-expr
+ | or-expr
+bool-op ::= '<' | '<=' | '==' | '>=' | '>' | '!='
+or-expr ::= or-expr '|' and-expr
+ | and-expr
+and-expr ::= and-expr '&' arith-expr
+ | arith-expr
+arith-expr ::= arith-expr ('+' | '-') mul-div-expr
+ | mul-div-expr
+mul-div-expr ::= mul-div-expr ('*' | '/') term
+ | term
+term ::= ref
+ref ::= scalar-or-var
+ | compr-set-or-object
+ | compr-or-array
+ | unary-expr
+ | parens-expr
+ | ref-dot
+ | ref-brack
+ | call-expr
+ref-dot ::= ref NO_WS '.' NO_WS var
+ref-brack ::= ref NO_WS '[' in-expr ']'
+call-expr ::= path-ref NO_WS '(' call-args ','? ')'
+call-args ::= in-expr ( ',' in-expr )*
+parens-expr ::= '(' membership-expr ')'
+unary-expr ::= '-' in-expr
+compr-set-or-object ::= set-compr
+ | set
+ | object-compr
+ | object
+set-compr ::= '{' compr '}'
+set ::= '{' in-expr ( ',' in-expr )* ','? '}'
+ | 'set(' ')'
+object ::= '{' field ( ',' field )* ','? '}'
+ | '{' '}'
+field ::= in-expr ':' in-expr
+compr-or-array ::= array-compr
+ | array
+array-compr ::= '[' compr ']'
+array ::= '[' in-expr ( ',' in-expr )* ','? ']'
+ | '[' ']'
+compr ::= ref '|' query
+scalar-or-var ::= var
+ | NUMBER
+ | STRING
+ | RAWSTRING
+ | 'null'
+ | 'true'
+ | 'false'
+var ::= IDENT
+ | non-imported-future-keyword
+non-imported-future-future-keyword ::= 'contains' | 'every' | 'if' | 'in'
```
-module: package imports { rule }
-package: "package" path-ref
+Below is the RailRoad Diagram for the grammar.
-imports: { "import" path-ref [ "as" var ] }
+**module:**
-path-ref: path-ref NO_WS "." NO_WS IDENT
-| path-ref NO_WS "[" STRING "]"
-| IDENT
+
-rule: default-rule
-| spec-rule
-
-default-rule: "default" rule-ref assign-op term
-
-spec-rule: rule-head rule-bodies
-
-rule-head: func-rule
-| contains-rule
-| object-rule
-| set-rule
-| compr-rule
-
-func-rule: rule-ref "(" term { "," term } [","] ")" [ rule-assign ]
-
-contains-rule: rule-ref "contains" or-expr
-
-object-rule: rule-ref NO_WS "[" membership-expr "]" rule-assign
-
-set-rule: rule-ref NO_WS "[" membership-expr "]"
-
-compr-rule: rule-ref [ rule-assign ]
-
-rule-ref: rule-ref NO_WS "." NO_WS var
-| path-ref NO_WS "[" membership-expr "]"
-| var
-
-rule-assign: assign-op membership-expr
-
-rule-bodies: "if" "{" query "}" alternatives
-| "if" literal-stmt alternatives
-| "{" query "}" alternatives
-
-alternatives: query-blocks
-| else-blocks
-
-query-blocks: { "{" query "}" }
-
-else-blocks: { else-block }
-
-else-block: "else" [rule-assign] "if" "{" query "}"
-| "else" [rule-assign] "if" literal-stmt
-| "else" [rule-assign] "{" query "}"
-
-assign-op: "=" | ":="
-
-query: literal-stmt { sep literal-stmt }
-
-sep: ";" | "\n" | "\r\n"
-
-literal-stmt: literal with-modifiers
-
-with-modifiers: { "with" path-ref "as" in-expr }
-
-literal: some
-| every
-| expr
-| not-expr
-
-some: some-vars
-| some-in
-
-some-vars: "some" var { "," var }
-
-some-in: "some" ref [ "," ref ] "in"
-
-every: "every" var [ "," var ] "in" bool-expr "{" query "}"
-
-expr: assign-expr
-
-not-expr: "not" assign-expr
-
-assign-expr: ref assign-op membership-expr
-
-membership-expr: membership-expr "in" bool-expr
-| bool-expr "," bool-expr
-| bool-expr
-
-in-expr: in-expr "in" bool-expr
-| bool-expr
-
-bool-expr: bool-expr bool-op or-expr
-| or-expr
-
-bool-op: "<" | "<=" | "==" | ">=" | ">" | "!="
-
-or-expr: or-expr "|" and-expr
-| and-expr
-
-and-expr: and-expr "&" arith-expr
-| arith-expr
-
-arith-expr: arith-expr ("+" | "-") mul-div-expr
-| mul-div-expr
-
-mul-div-expr: mul-div-expr ("*" | "/") term
-| term
-
-term: ref
-
-ref: scalar-or-var
-| compr-set-or-object
-| compr-or-array
-| unary-expr
-| parens-expr
-| ref-dot
-| ref-brack
-| call-expr
-
-ref-dot: ref NO_WS "." NO_WS var
-
-ref-brack: ref NO_WS "[" in-expr "]"
-
-call-expr: path-ref NO_WS "(" call-args [","] ")"
-
-call-args: in-expr { "," in-expr }
-
-parens-expr: "(" membership-expr ")"
-
-unary-expr: "-" in-expr
-
-compr-set-or-object: set-compr
-| set
-| object-compr
-| object
-
-set-compr: "{" compr "}"
-
-# Set must have at least one item.
-set: "{" in-expr { "," in-expr } [","] "}"
-| "set(" ")" # empty set
-
-object: "{" field { "," field } [","] "}"
-| "{" "}" # empty object
-
-field: in-expr ":" in-expr
-
-# Comprehension or array
-compr-or-array: array-compr
-| array
-
-array-compr: "[" compr "]"
-
-array: "[" in-expr { "," in-expr } [","] "]"
-| "[" "]" # Empty array
-
-# Comprehension
-compr: ref "|" query
-
-scalar-or-var: var
-| NUMBER
-| STRING
-| RAWSTRING
-| "null"
-| "true"
-| "false"
-
-var: IDENT
-| non-imported-future-keyword
-
-non-imported-future-future-keyword: "contains" | "every" | "if" | "in"
```
+module ::= package imports rule*
+```
+
+**package:**
+
+
+
+```
+package ::= 'package' path-ref
+```
+
+referenced by:
+
+* module
+
+**imports:**
+
+
+
+```
+imports ::= ( 'import' path-ref ( 'as' var )? )*
+```
+
+referenced by:
+
+* module
+
+**path-ref:**
+
+
+
+```
+path-ref ::= IDENT ( NO_WS ( '.' NO_WS IDENT | '[' STRING ']' ) )*
+```
+
+referenced by:
+
+* call-expr
+* imports
+* package
+* rule-ref
+* with-modifiers
+
+**rule:**
+
+
+
+```
+rule ::= default-rule
+ | spec-rule
+```
+
+referenced by:
+
+* module
+
+**default-rule:**
+
+
+
+```
+default-rule
+ ::= 'default' rule-ref assign-op term
+```
+
+referenced by:
+
+* rule
+
+**spec-rule:**
+
+
+
+```
+spec-rule
+ ::= rule-head rule-bodies
+```
+
+referenced by:
+
+* rule
+
+**rule-head:**
+
+
+
+```
+rule-head
+ ::= func-rule
+ | contains-rule
+ | object-rule
+ | set-rule
+ | compr-rule
+```
+
+referenced by:
+
+* spec-rule
+
+**func-rule:**
+
+
+
+```
+func-rule
+ ::= rule-ref '(' term ( ',' term )* ','? ')' rule-assign?
+```
+
+referenced by:
+
+* rule-head
+
+**contains-rule:**
+
+
+
+```
+contains-rule
+ ::= rule-ref 'contains' or-expr
+```
+
+referenced by:
+
+* rule-head
+
+**object-rule:**
+
+
+
+```
+object-rule
+ ::= rule-ref NO_WS '[' membership-expr ']' rule-assign
+```
+
+referenced by:
+
+* rule-head
+
+**set-rule:**
+
+
+
+```
+set-rule ::= rule-ref NO_WS '[' membership-expr ']'
+```
+
+referenced by:
+
+* rule-head
+
+**compr-rule:**
+
+
+
+```
+compr-rule
+ ::= rule-ref rule-assign?
+```
+
+referenced by:
+
+* rule-head
+
+**rule-ref:**
+
+
+
+```
+rule-ref ::= ( path-ref NO_WS '[' membership-expr ']' | var ) ( NO_WS '.' NO_WS var )*
+```
+
+referenced by:
+
+* compr-rule
+* contains-rule
+* default-rule
+* func-rule
+* object-rule
+* set-rule
+
+**rule-assign:**
+
+
+
+```
+rule-assign
+ ::= assign-op membership-expr
+```
+
+referenced by:
+
+* compr-rule
+* else-block
+* func-rule
+* object-rule
+
+**rule-bodies:**
+
+
+
+```
+rule-bodies
+ ::= ( 'if' ( '{' query '}' | literal-stmt ) | '{' query '}' ) alternatives
+```
+
+referenced by:
+
+* spec-rule
+
+**alternatives:**
+
+
+
+```
+alternatives
+ ::= query-blocks
+ | else-blocks
+```
+
+referenced by:
+
+* rule-bodies
+
+**query-blocks:**
+
+
+
+```
+query-blocks
+ ::= ( '{' query '}' )*
+```
+
+referenced by:
+
+* alternatives
+
+**else-blocks:**
+
+
+
+```
+else-blocks
+ ::= else-block*
+```
+
+referenced by:
+
+* alternatives
+
+**else-block:**
+
+
+
+```
+else-block
+ ::= 'else' rule-assign? ( 'if' ( '{' query '}' | literal-stmt ) | '{' query '}' )
+```
+
+referenced by:
+
+* else-blocks
+
+**assign-op:**
+
+
+
+```
+assign-op
+ ::= '='
+ | ':='
+```
+
+referenced by:
+
+* assign-expr
+* default-rule
+* rule-assign
+
+**query:**
+
+
+
+```
+query ::= literal-stmt ( sep literal-stmt )*
+```
+
+referenced by:
+
+* compr
+* else-block
+* every
+* query-blocks
+* rule-bodies
+
+**sep:**
+
+
+
+```
+sep ::= ';'
+ | '\n'
+ | '\r\n'
+```
+
+referenced by:
+
+* query
+
+**literal-stmt:**
+
+
+
+```
+literal-stmt
+ ::= literal with-modifiers
+```
+
+referenced by:
+
+* else-block
+* query
+* rule-bodies
+
+**with-modifiers:**
+
+
+
+```
+with-modifiers
+ ::= ( 'with' path-ref 'as' in-expr )*
+```
+
+referenced by:
+
+* literal-stmt
+
+**literal:**
+
+
+
+```
+literal ::= some
+ | every
+ | expr
+ | not-expr
+```
+
+referenced by:
+
+* literal-stmt
+
+**some:**
+
+
+
+```
+some ::= some-vars
+ | some-in
+```
+
+referenced by:
+
+* literal
+
+**some-vars:**
+
+
+
+```
+some-vars
+ ::= 'some' var ( ',' var )*
+```
+
+referenced by:
+
+* some
+
+**some-in:**
+
+
+
+```
+some-in ::= 'some' ref ( ',' ref )? 'in'
+```
+
+referenced by:
+
+* some
+
+**every:**
+
+
+
+```
+every ::= 'every' var ( ',' var )? 'in' bool-expr '{' query '}'
+```
+
+referenced by:
+
+* literal
+
+**expr:**
+
+
+
+```
+expr ::= assign-expr
+```
+
+referenced by:
+
+* literal
+
+**not-expr:**
+
+
+
+```
+not-expr ::= 'not' assign-expr
+```
+
+referenced by:
+
+* literal
+
+**assign-expr:**
+
+
+
+```
+assign-expr
+ ::= ref assign-op membership-expr
+```
+
+referenced by:
+
+* expr
+* not-expr
+
+**membership-expr:**
+
+
+
+```
+membership-expr
+ ::= bool-expr ( ',' bool-expr )? ( 'in' bool-expr )*
+```
+
+referenced by:
+
+* assign-expr
+* object-rule
+* parens-expr
+* rule-assign
+* rule-ref
+* set-rule
+
+**in-expr:**
+
+
+
+```
+in-expr ::= bool-expr ( 'in' bool-expr )*
+```
+
+referenced by:
+
+* array
+* call-args
+* field
+* ref-brack
+* set
+* unary-expr
+* with-modifiers
+
+**bool-expr:**
+
+
+
+```
+bool-expr
+ ::= or-expr ( bool-op or-expr )*
+```
+
+referenced by:
+
+* every
+* in-expr
+* membership-expr
+
+**bool-op:**
+
+
+
+```
+bool-op ::= '<'
+ | '<='
+ | '=='
+ | '>='
+ | '>'
+ | '!='
+```
+
+referenced by:
+
+* bool-expr
+
+**or-expr:**
+
+
+
+```
+or-expr ::= and-expr ( '|' and-expr )*
+```
+
+referenced by:
+
+* bool-expr
+* contains-rule
+
+**and-expr:**
+
+
+
+```
+and-expr ::= arith-expr ( '&' arith-expr )*
+```
+
+referenced by:
+
+* or-expr
+
+**arith-expr:**
+
+
+
+```
+arith-expr
+ ::= mul-div-expr ( ( '+' | '-' ) mul-div-expr )*
+```
+
+referenced by:
+
+* and-expr
+
+**mul-div-expr:**
+
+
+
+```
+mul-div-expr
+ ::= term ( ( '*' | '/' ) term )*
+```
+
+referenced by:
+
+* arith-expr
+
+**term:**
+
+
+
+```
+term ::= ref
+```
+
+referenced by:
+
+* default-rule
+* func-rule
+* mul-div-expr
+
+**ref:**
+
+
+
+```
+ref ::= scalar-or-var
+ | compr-set-or-object
+ | compr-or-array
+ | unary-expr
+ | parens-expr
+ | ref-dot
+ | ref-brack
+ | call-expr
+```
+
+referenced by:
+
+* assign-expr
+* compr
+* ref-brack
+* ref-dot
+* some-in
+* term
+
+**ref-dot:**
+
+
+
+```
+ref-dot ::= ref NO_WS '.' NO_WS var
+```
+
+referenced by:
+
+* ref
+
+**ref-brack:**
+
+
+
+```
+ref-brack
+ ::= ref NO_WS '[' in-expr ']'
+```
+
+referenced by:
+
+* ref
+
+**call-expr:**
+
+
+
+```
+call-expr
+ ::= path-ref NO_WS '(' call-args ','? ')'
+```
+
+referenced by:
+
+* ref
+
+**call-args:**
+
+
+
+```
+call-args
+ ::= in-expr ( ',' in-expr )*
+```
+
+referenced by:
+
+* call-expr
+
+**parens-expr:**
+
+
+
+```
+parens-expr
+ ::= '(' membership-expr ')'
+```
+
+referenced by:
+
+* ref
+
+**unary-expr:**
+
+
+
+```
+unary-expr
+ ::= '-' in-expr
+```
+
+referenced by:
+
+* ref
+
+**compr-set-or-object:**
+
+
+
+```
+compr-set-or-object
+ ::= set-compr
+ | set
+ | object-compr
+ | object
+```
+
+referenced by:
+
+* ref
+
+**set-compr:**
+
+
+
+```
+set-compr
+ ::= '{' compr '}'
+```
+
+referenced by:
+
+* compr-set-or-object
+
+**set:**
+
+
+
+```
+set ::= '{' in-expr ( ',' in-expr )* ','? '}'
+ | 'set(' ')'
+```
+
+referenced by:
+
+* compr-set-or-object
+
+**object:**
+
+
+
+```
+object ::= '{' ( field ( ',' field )* ','? )? '}'
+```
+
+referenced by:
+
+* compr-set-or-object
+
+**field:**
+
+
+
+```
+field ::= in-expr ':' in-expr
+```
+
+referenced by:
+
+* object
+
+**compr-or-array:**
+
+
+
+```
+compr-or-array
+ ::= array-compr
+ | array
+```
+
+referenced by:
+
+* ref
+
+**array-compr:**
+
+
+
+```
+array-compr
+ ::= '[' compr ']'
+```
+
+referenced by:
+
+* compr-or-array
+
+**array:**
+
+
+
+```
+array ::= '[' ( in-expr ( ',' in-expr )* ','? )? ']'
+```
+
+referenced by:
+
+* compr-or-array
+
+**compr:**
+
+
+
+```
+compr ::= ref '|' query
+```
+
+referenced by:
+
+* array-compr
+* set-compr
+
+**scalar-or-var:**
+
+
+
+```
+scalar-or-var
+ ::= var
+ | NUMBER
+ | STRING
+ | RAWSTRING
+ | 'null'
+ | 'true'
+ | 'false'
+```
+
+referenced by:
+
+* ref
+
+**var:**
+
+
+
+```
+var ::= IDENT
+ | non-imported-future-keyword
+```
+
+referenced by:
+
+* every
+* imports
+* ref-dot
+* rule-ref
+* scalar-or-var
+* some-vars
+
+**non-imported-future-future-keyword:**
+
+
+
+```
+non-imported-future-future-keyword
+ ::= 'contains'
+ | 'every'
+ | 'if'
+ | 'in'
+```
+
+##
+ generated by [RR - Railroad Diagram Generator][RR]
+
+[RR]: https://www.bottlecaps.de/rr/ui