From aabf329088e1c0de03a2427549e52f22b8b24450 Mon Sep 17 00:00:00 2001 From: Anand Krishnamoorthi <35780660+anakrish@users.noreply.github.com> Date: Thu, 11 Jan 2024 13:12:15 -0800 Subject: [PATCH] Update READEME.md with current status, grammar etc. (#102) Signed-off-by: Anand Krishnamoorthi --- README.md | 60 +- docs/diagram/Railroad-Diagram-Generator.svg | 57 + docs/diagram/alternatives.svg | 52 + docs/diagram/and-expr.svg | 53 + docs/diagram/arith-expr.svg | 61 + docs/diagram/array-compr.svg | 61 + docs/diagram/array.svg | 77 ++ docs/diagram/assign-expr.svg | 59 + docs/diagram/assign-op.svg | 54 + docs/diagram/bool-expr.svg | 52 + docs/diagram/bool-op.svg | 86 ++ docs/diagram/call-args.svg | 53 + docs/diagram/call-expr.svg | 83 ++ docs/diagram/compr-or-array.svg | 52 + docs/diagram/compr-rule.svg | 52 + docs/diagram/compr-set-or-object.svg | 66 + docs/diagram/compr.svg | 60 + docs/diagram/contains-rule.svg | 60 + docs/diagram/default-rule.svg | 67 + docs/diagram/else-block.svg | 114 ++ docs/diagram/else-blocks.svg | 45 + docs/diagram/every.svg | 106 ++ docs/diagram/expr.svg | 44 + docs/diagram/field.svg | 60 + docs/diagram/func-rule.svg | 91 ++ docs/diagram/imports.svg | 68 + docs/diagram/in-expr.svg | 53 + docs/diagram/literal-stmt.svg | 52 + docs/diagram/literal.svg | 66 + docs/diagram/membership-expr.svg | 75 ++ docs/diagram/module.svg | 59 + docs/diagram/mul-div-expr.svg | 61 + .../non-imported-future-future-keyword.svg | 70 + docs/diagram/not-expr.svg | 53 + docs/diagram/object-rule.svg | 82 ++ docs/diagram/object.svg | 77 ++ docs/diagram/or-expr.svg | 53 + docs/diagram/package.svg | 53 + docs/diagram/parens-expr.svg | 61 + docs/diagram/path-ref.svg | 97 ++ docs/diagram/query-blocks.svg | 61 + docs/diagram/query.svg | 52 + docs/diagram/ref-brack.svg | 75 ++ docs/diagram/ref-dot.svg | 74 ++ docs/diagram/ref.svg | 94 ++ docs/diagram/rule-assign.svg | 52 + docs/diagram/rule-bodies.svg | 106 ++ docs/diagram/rule-head.svg | 73 ++ docs/diagram/rule-ref.svg | 111 ++ docs/diagram/rule.svg | 52 + docs/diagram/scalar-or-var.svg | 90 ++ docs/diagram/sep.svg | 62 + docs/diagram/set-compr.svg | 61 + docs/diagram/set-rule.svg | 75 ++ docs/diagram/set.svg | 93 ++ docs/diagram/some-in.svg | 76 ++ docs/diagram/some-vars.svg | 61 + docs/diagram/some.svg | 52 + docs/diagram/spec-rule.svg | 52 + docs/diagram/term.svg | 44 + docs/diagram/unary-expr.svg | 53 + docs/diagram/var.svg | 52 + docs/diagram/with-modifiers.svg | 68 + docs/grammar.md | 1126 ++++++++++++++--- 64 files changed, 5149 insertions(+), 171 deletions(-) create mode 100644 docs/diagram/Railroad-Diagram-Generator.svg create mode 100644 docs/diagram/alternatives.svg create mode 100644 docs/diagram/and-expr.svg create mode 100644 docs/diagram/arith-expr.svg create mode 100644 docs/diagram/array-compr.svg create mode 100644 docs/diagram/array.svg create mode 100644 docs/diagram/assign-expr.svg create mode 100644 docs/diagram/assign-op.svg create mode 100644 docs/diagram/bool-expr.svg create mode 100644 docs/diagram/bool-op.svg create mode 100644 docs/diagram/call-args.svg create mode 100644 docs/diagram/call-expr.svg create mode 100644 docs/diagram/compr-or-array.svg create mode 100644 docs/diagram/compr-rule.svg create mode 100644 docs/diagram/compr-set-or-object.svg create mode 100644 docs/diagram/compr.svg create mode 100644 docs/diagram/contains-rule.svg create mode 100644 docs/diagram/default-rule.svg create mode 100644 docs/diagram/else-block.svg create mode 100644 docs/diagram/else-blocks.svg create mode 100644 docs/diagram/every.svg create mode 100644 docs/diagram/expr.svg create mode 100644 docs/diagram/field.svg create mode 100644 docs/diagram/func-rule.svg create mode 100644 docs/diagram/imports.svg create mode 100644 docs/diagram/in-expr.svg create mode 100644 docs/diagram/literal-stmt.svg create mode 100644 docs/diagram/literal.svg create mode 100644 docs/diagram/membership-expr.svg create mode 100644 docs/diagram/module.svg create mode 100644 docs/diagram/mul-div-expr.svg create mode 100644 docs/diagram/non-imported-future-future-keyword.svg create mode 100644 docs/diagram/not-expr.svg create mode 100644 docs/diagram/object-rule.svg create mode 100644 docs/diagram/object.svg create mode 100644 docs/diagram/or-expr.svg create mode 100644 docs/diagram/package.svg create mode 100644 docs/diagram/parens-expr.svg create mode 100644 docs/diagram/path-ref.svg create mode 100644 docs/diagram/query-blocks.svg create mode 100644 docs/diagram/query.svg create mode 100644 docs/diagram/ref-brack.svg create mode 100644 docs/diagram/ref-dot.svg create mode 100644 docs/diagram/ref.svg create mode 100644 docs/diagram/rule-assign.svg create mode 100644 docs/diagram/rule-bodies.svg create mode 100644 docs/diagram/rule-head.svg create mode 100644 docs/diagram/rule-ref.svg create mode 100644 docs/diagram/rule.svg create mode 100644 docs/diagram/scalar-or-var.svg create mode 100644 docs/diagram/sep.svg create mode 100644 docs/diagram/set-compr.svg create mode 100644 docs/diagram/set-rule.svg create mode 100644 docs/diagram/set.svg create mode 100644 docs/diagram/some-in.svg create mode 100644 docs/diagram/some-vars.svg create mode 100644 docs/diagram/some.svg create mode 100644 docs/diagram/spec-rule.svg create mode 100644 docs/diagram/term.svg create mode 100644 docs/diagram/unary-expr.svg create mode 100644 docs/diagram/var.svg create mode 100644 docs/diagram/with-modifiers.svg 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 @@ + + + + + + + + + + + R + R + + 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 @@ + + + + + + + + + + + query-blocks + + + + + else-blocks + + + + + 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 @@ + + + + + + + + + + + arith-expr + + + + & + + + + 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 @@ + + + + + + + + + + + mul-div-expr + + + + + + + + - + + + + 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 @@ + + + + + + + + + + [ + + + + compr + + + + ] + + + + 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 @@ + + + + + + + + + + [ + + + + in-expr + + + + , + + + , + + + ] + + + + 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 @@ + + + + + + + + + + + ref + + + + + assign-op + + + + + membership-expr + + + + + 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 @@ + + + + + + + + + + + or-expr + + + + + bool-op + + + + + 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 @@ + + + + + + + + + + + in-expr + + + + , + + + + 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 @@ + + + + + + + + + + + path-ref + + + + + NO_WS + + + + ( + + + + call-args + + + + , + + + ) + + + + 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 @@ + + + + + + + + + + + array-compr + + + + + array + + + + + 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 @@ + + + + + + + + + + + rule-ref + + + + + rule-assign + + + + + 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 @@ + + + + + + + + + + + set-compr + + + + + set + + + + + object-compr + + + + + object + + + + + 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 @@ + + + + + + + + + + + ref + + + + | + + + + query + + + + + 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 @@ + + + + + + + + + + + rule-ref + + + + contains + + + + or-expr + + + + + 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 @@ + + + + + + + + + + default + + + + rule-ref + + + + + assign-op + + + + + term + + + + + 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 @@ + + + + + + + + + + else + + + + rule-assign + + + + if + + + { + + + + query + + + + } + + + + literal-stmt + + + + { + + + + query + + + + } + + + + 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 @@ + + + + + + + + + + + else-block + + + + + 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 @@ + + + + + + + + + + every + + + + var + + + + , + + + + var + + + + in + + + + bool-expr + + + + { + + + + query + + + + } + + + + 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 @@ + + + + + + + + + + + assign-expr + + + + + 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 @@ + + + + + + + + + + + in-expr + + + + : + + + + in-expr + + + + + 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 @@ + + + + + + + + + + + rule-ref + + + + ( + + + + term + + + + , + + + , + + + ) + + + + rule-assign + + + + + 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 @@ + + + + + + + + + + import + + + + path-ref + + + + as + + + + var + + + + + 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 @@ + + + + + + + + + + + bool-expr + + + + in + + + + 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 @@ + + + + + + + + + + + literal + + + + + with-modifiers + + + + + 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 @@ + + + + + + + + + + + some + + + + + every + + + + + expr + + + + + not-expr + + + + + 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 @@ + + + + + + + + + + + bool-expr + + + + , + + + + bool-expr + + + + in + + + + bool-expr + + + + + 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 @@ + + + + + + + + + + + package + + + + + imports + + + + + rule + + + + + 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 @@ + + + + + + + + + + + term + + + + * + + + / + + + + 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 @@ + + + + + + + + + + contains + + + every + + + if + + + in + + + + 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 @@ + + + + + + + + + + not + + + + assign-expr + + + + + 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 @@ + + + + + + + + + + + rule-ref + + + + + NO_WS + + + + [ + + + + membership-expr + + + + ] + + + + rule-assign + + + + + 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 @@ + + + + + + + + + + { + + + + field + + + + , + + + , + + + } + + + + 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 @@ + + + + + + + + + + + and-expr + + + + | + + + + 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 @@ + + + + + + + + + + package + + + + path-ref + + + + + 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 @@ + + + + + + + + + + ( + + + + membership-expr + + + + ) + + + + 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 @@ + + + + + + + + + + + IDENT + + + + + NO_WS + + + + . + + + + NO_WS + + + + + IDENT + + + + [ + + + + STRING + + + + ] + + + + 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 @@ + + + + + + + + + + { + + + + query + + + + } + + + + 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 @@ + + + + + + + + + + + literal-stmt + + + + + sep + + + + + 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 @@ + + + + + + + + + + + ref + + + + + NO_WS + + + + [ + + + + in-expr + + + + ] + + + + 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 @@ + + + + + + + + + + + ref + + + + + NO_WS + + + + . + + + + NO_WS + + + + + var + + + + + 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 @@ + + + + + + + + + + + scalar-or-var + + + + + compr-set-or-object + + + + + compr-or-array + + + + + unary-expr + + + + + parens-expr + + + + + ref-dot + + + + + ref-brack + + + + + call-expr + + + + + 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 @@ + + + + + + + + + + + assign-op + + + + + membership-expr + + + + + 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 @@ + + + + + + + + + + if + + + { + + + + query + + + + } + + + + literal-stmt + + + + { + + + + query + + + + } + + + + alternatives + + + + + 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 @@ + + + + + + + + + + + func-rule + + + + + contains-rule + + + + + object-rule + + + + + set-rule + + + + + compr-rule + + + + + 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 @@ + + + + + + + + + + + path-ref + + + + + NO_WS + + + + [ + + + + membership-expr + + + + ] + + + + var + + + + + NO_WS + + + + . + + + + NO_WS + + + + + var + + + + + 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 @@ + + + + + + + + + + + default-rule + + + + + spec-rule + + + + + 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 @@ + + + + + + + + + + + var + + + + + NUMBER + + + + + STRING + + + + + RAWSTRING + + + + null + + + true + + + false + + + + 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 @@ + + + + + + + + + + ; + + + \n + + + \r\n + + + + 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 @@ + + + + + + + + + + { + + + + compr + + + + } + + + + 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 @@ + + + + + + + + + + + rule-ref + + + + + NO_WS + + + + [ + + + + membership-expr + + + + ] + + + + 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 @@ + + + + + + + + + + { + + + + in-expr + + + + , + + + , + + + } + + + set( + + + ) + + + + 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 @@ + + + + + + + + + + some + + + + ref + + + + , + + + + ref + + + + in + + + + 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 @@ + + + + + + + + + + some + + + + var + + + + , + + + + 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 @@ + + + + + + + + + + + some-vars + + + + + some-in + + + + + 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 @@ + + + + + + + + + + + rule-head + + + + + rule-bodies + + + + + 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 @@ + + + + + + + + + + + ref + + + + + 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 @@ + + + + + + + + + + - + + + + in-expr + + + + + 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 @@ + + + + + + + + + + + IDENT + + + + + non-imported-future-keyword + + + + + 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 @@ + + + + + + + + + + with + + + + path-ref + + + + as + + + + in-expr + + + + + 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 +![module](diagram/module.svg) -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](diagram/package.svg) + +``` +package ::= 'package' path-ref +``` + +referenced by: + +* module + +**imports:** + +![imports](diagram/imports.svg) + +``` +imports ::= ( 'import' path-ref ( 'as' var )? )* +``` + +referenced by: + +* module + +**path-ref:** + +![path-ref](diagram/path-ref.svg) + +``` +path-ref ::= IDENT ( NO_WS ( '.' NO_WS IDENT | '[' STRING ']' ) )* +``` + +referenced by: + +* call-expr +* imports +* package +* rule-ref +* with-modifiers + +**rule:** + +![rule](diagram/rule.svg) + +``` +rule ::= default-rule + | spec-rule +``` + +referenced by: + +* module + +**default-rule:** + +![default-rule](diagram/default-rule.svg) + +``` +default-rule + ::= 'default' rule-ref assign-op term +``` + +referenced by: + +* rule + +**spec-rule:** + +![spec-rule](diagram/spec-rule.svg) + +``` +spec-rule + ::= rule-head rule-bodies +``` + +referenced by: + +* rule + +**rule-head:** + +![rule-head](diagram/rule-head.svg) + +``` +rule-head + ::= func-rule + | contains-rule + | object-rule + | set-rule + | compr-rule +``` + +referenced by: + +* spec-rule + +**func-rule:** + +![func-rule](diagram/func-rule.svg) + +``` +func-rule + ::= rule-ref '(' term ( ',' term )* ','? ')' rule-assign? +``` + +referenced by: + +* rule-head + +**contains-rule:** + +![contains-rule](diagram/contains-rule.svg) + +``` +contains-rule + ::= rule-ref 'contains' or-expr +``` + +referenced by: + +* rule-head + +**object-rule:** + +![object-rule](diagram/object-rule.svg) + +``` +object-rule + ::= rule-ref NO_WS '[' membership-expr ']' rule-assign +``` + +referenced by: + +* rule-head + +**set-rule:** + +![set-rule](diagram/set-rule.svg) + +``` +set-rule ::= rule-ref NO_WS '[' membership-expr ']' +``` + +referenced by: + +* rule-head + +**compr-rule:** + +![compr-rule](diagram/compr-rule.svg) + +``` +compr-rule + ::= rule-ref rule-assign? +``` + +referenced by: + +* rule-head + +**rule-ref:** + +![rule-ref](diagram/rule-ref.svg) + +``` +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](diagram/rule-assign.svg) + +``` +rule-assign + ::= assign-op membership-expr +``` + +referenced by: + +* compr-rule +* else-block +* func-rule +* object-rule + +**rule-bodies:** + +![rule-bodies](diagram/rule-bodies.svg) + +``` +rule-bodies + ::= ( 'if' ( '{' query '}' | literal-stmt ) | '{' query '}' ) alternatives +``` + +referenced by: + +* spec-rule + +**alternatives:** + +![alternatives](diagram/alternatives.svg) + +``` +alternatives + ::= query-blocks + | else-blocks +``` + +referenced by: + +* rule-bodies + +**query-blocks:** + +![query-blocks](diagram/query-blocks.svg) + +``` +query-blocks + ::= ( '{' query '}' )* +``` + +referenced by: + +* alternatives + +**else-blocks:** + +![else-blocks](diagram/else-blocks.svg) + +``` +else-blocks + ::= else-block* +``` + +referenced by: + +* alternatives + +**else-block:** + +![else-block](diagram/else-block.svg) + +``` +else-block + ::= 'else' rule-assign? ( 'if' ( '{' query '}' | literal-stmt ) | '{' query '}' ) +``` + +referenced by: + +* else-blocks + +**assign-op:** + +![assign-op](diagram/assign-op.svg) + +``` +assign-op + ::= '=' + | ':=' +``` + +referenced by: + +* assign-expr +* default-rule +* rule-assign + +**query:** + +![query](diagram/query.svg) + +``` +query ::= literal-stmt ( sep literal-stmt )* +``` + +referenced by: + +* compr +* else-block +* every +* query-blocks +* rule-bodies + +**sep:** + +![sep](diagram/sep.svg) + +``` +sep ::= ';' + | '\n' + | '\r\n' +``` + +referenced by: + +* query + +**literal-stmt:** + +![literal-stmt](diagram/literal-stmt.svg) + +``` +literal-stmt + ::= literal with-modifiers +``` + +referenced by: + +* else-block +* query +* rule-bodies + +**with-modifiers:** + +![with-modifiers](diagram/with-modifiers.svg) + +``` +with-modifiers + ::= ( 'with' path-ref 'as' in-expr )* +``` + +referenced by: + +* literal-stmt + +**literal:** + +![literal](diagram/literal.svg) + +``` +literal ::= some + | every + | expr + | not-expr +``` + +referenced by: + +* literal-stmt + +**some:** + +![some](diagram/some.svg) + +``` +some ::= some-vars + | some-in +``` + +referenced by: + +* literal + +**some-vars:** + +![some-vars](diagram/some-vars.svg) + +``` +some-vars + ::= 'some' var ( ',' var )* +``` + +referenced by: + +* some + +**some-in:** + +![some-in](diagram/some-in.svg) + +``` +some-in ::= 'some' ref ( ',' ref )? 'in' +``` + +referenced by: + +* some + +**every:** + +![every](diagram/every.svg) + +``` +every ::= 'every' var ( ',' var )? 'in' bool-expr '{' query '}' +``` + +referenced by: + +* literal + +**expr:** + +![expr](diagram/expr.svg) + +``` +expr ::= assign-expr +``` + +referenced by: + +* literal + +**not-expr:** + +![not-expr](diagram/not-expr.svg) + +``` +not-expr ::= 'not' assign-expr +``` + +referenced by: + +* literal + +**assign-expr:** + +![assign-expr](diagram/assign-expr.svg) + +``` +assign-expr + ::= ref assign-op membership-expr +``` + +referenced by: + +* expr +* not-expr + +**membership-expr:** + +![membership-expr](diagram/membership-expr.svg) + +``` +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](diagram/in-expr.svg) + +``` +in-expr ::= bool-expr ( 'in' bool-expr )* +``` + +referenced by: + +* array +* call-args +* field +* ref-brack +* set +* unary-expr +* with-modifiers + +**bool-expr:** + +![bool-expr](diagram/bool-expr.svg) + +``` +bool-expr + ::= or-expr ( bool-op or-expr )* +``` + +referenced by: + +* every +* in-expr +* membership-expr + +**bool-op:** + +![bool-op](diagram/bool-op.svg) + +``` +bool-op ::= '<' + | '<=' + | '==' + | '>=' + | '>' + | '!=' +``` + +referenced by: + +* bool-expr + +**or-expr:** + +![or-expr](diagram/or-expr.svg) + +``` +or-expr ::= and-expr ( '|' and-expr )* +``` + +referenced by: + +* bool-expr +* contains-rule + +**and-expr:** + +![and-expr](diagram/and-expr.svg) + +``` +and-expr ::= arith-expr ( '&' arith-expr )* +``` + +referenced by: + +* or-expr + +**arith-expr:** + +![arith-expr](diagram/arith-expr.svg) + +``` +arith-expr + ::= mul-div-expr ( ( '+' | '-' ) mul-div-expr )* +``` + +referenced by: + +* and-expr + +**mul-div-expr:** + +![mul-div-expr](diagram/mul-div-expr.svg) + +``` +mul-div-expr + ::= term ( ( '*' | '/' ) term )* +``` + +referenced by: + +* arith-expr + +**term:** + +![term](diagram/term.svg) + +``` +term ::= ref +``` + +referenced by: + +* default-rule +* func-rule +* mul-div-expr + +**ref:** + +![ref](diagram/ref.svg) + +``` +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](diagram/ref-dot.svg) + +``` +ref-dot ::= ref NO_WS '.' NO_WS var +``` + +referenced by: + +* ref + +**ref-brack:** + +![ref-brack](diagram/ref-brack.svg) + +``` +ref-brack + ::= ref NO_WS '[' in-expr ']' +``` + +referenced by: + +* ref + +**call-expr:** + +![call-expr](diagram/call-expr.svg) + +``` +call-expr + ::= path-ref NO_WS '(' call-args ','? ')' +``` + +referenced by: + +* ref + +**call-args:** + +![call-args](diagram/call-args.svg) + +``` +call-args + ::= in-expr ( ',' in-expr )* +``` + +referenced by: + +* call-expr + +**parens-expr:** + +![parens-expr](diagram/parens-expr.svg) + +``` +parens-expr + ::= '(' membership-expr ')' +``` + +referenced by: + +* ref + +**unary-expr:** + +![unary-expr](diagram/unary-expr.svg) + +``` +unary-expr + ::= '-' in-expr +``` + +referenced by: + +* ref + +**compr-set-or-object:** + +![compr-set-or-object](diagram/compr-set-or-object.svg) + +``` +compr-set-or-object + ::= set-compr + | set + | object-compr + | object +``` + +referenced by: + +* ref + +**set-compr:** + +![set-compr](diagram/set-compr.svg) + +``` +set-compr + ::= '{' compr '}' +``` + +referenced by: + +* compr-set-or-object + +**set:** + +![set](diagram/set.svg) + +``` +set ::= '{' in-expr ( ',' in-expr )* ','? '}' + | 'set(' ')' +``` + +referenced by: + +* compr-set-or-object + +**object:** + +![object](diagram/object.svg) + +``` +object ::= '{' ( field ( ',' field )* ','? )? '}' +``` + +referenced by: + +* compr-set-or-object + +**field:** + +![field](diagram/field.svg) + +``` +field ::= in-expr ':' in-expr +``` + +referenced by: + +* object + +**compr-or-array:** + +![compr-or-array](diagram/compr-or-array.svg) + +``` +compr-or-array + ::= array-compr + | array +``` + +referenced by: + +* ref + +**array-compr:** + +![array-compr](diagram/array-compr.svg) + +``` +array-compr + ::= '[' compr ']' +``` + +referenced by: + +* compr-or-array + +**array:** + +![array](diagram/array.svg) + +``` +array ::= '[' ( in-expr ( ',' in-expr )* ','? )? ']' +``` + +referenced by: + +* compr-or-array + +**compr:** + +![compr](diagram/compr.svg) + +``` +compr ::= ref '|' query +``` + +referenced by: + +* array-compr +* set-compr + +**scalar-or-var:** + +![scalar-or-var](diagram/scalar-or-var.svg) + +``` +scalar-or-var + ::= var + | NUMBER + | STRING + | RAWSTRING + | 'null' + | 'true' + | 'false' +``` + +referenced by: + +* ref + +**var:** + +![var](diagram/var.svg) + +``` +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](diagram/non-imported-future-future-keyword.svg) + +``` +non-imported-future-future-keyword + ::= 'contains' + | 'every' + | 'if' + | 'in' +``` + +## +![Railroad-Diagram-Generator](diagram/Railroad-Diagram-Generator.svg) generated by [RR - Railroad Diagram Generator][RR] + +[RR]: https://www.bottlecaps.de/rr/ui