From 7c248d412aa845974a3528bfaeef24f25bd1787f Mon Sep 17 00:00:00 2001 From: Anand Krishnamoorthi Date: Thu, 11 Jan 2024 12:25:45 -0800 Subject: [PATCH] Update READEME.md with current status, grammar etc. 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 | 1014 +++++++++++++++-- 64 files changed, 5093 insertions(+), 115 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 489433b8..4fa8af8a 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 00000000..9d1e83c7 --- /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 00000000..645c4e57 --- /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 00000000..3fb18ea7 --- /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 00000000..607bd596 --- /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 00000000..64870d97 --- /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 00000000..8c2a0605 --- /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 00000000..13a2d43d --- /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 00000000..3a5abfc5 --- /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 00000000..ac17e76e --- /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 00000000..b48bde79 --- /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 00000000..2e40b706 --- /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 00000000..d58936ae --- /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 00000000..8ad1c606 --- /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 00000000..cefcb803 --- /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 00000000..9233d8f3 --- /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 00000000..2e84d739 --- /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 00000000..ae3089a5 --- /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 00000000..30aede9d --- /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 00000000..a7ddc62e --- /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 00000000..9b616875 --- /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 00000000..e3f0bb57 --- /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 00000000..98e68487 --- /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 00000000..d0f0aa7b --- /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 00000000..16141e32 --- /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 00000000..1e767182 --- /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 00000000..0428aeb0 --- /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 00000000..545ff950 --- /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 00000000..cf34bc1e --- /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 00000000..aa6918e5 --- /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 00000000..1a6b9568 --- /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 00000000..a45b338a --- /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 00000000..1cd04da7 --- /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 00000000..57126b00 --- /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 00000000..93ac5def --- /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 00000000..63156074 --- /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 00000000..6d829b87 --- /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 00000000..6e930c67 --- /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 00000000..9951f7dd --- /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 00000000..3d3fad05 --- /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 00000000..8737efb6 --- /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 00000000..6698b26e --- /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 00000000..742de3bf --- /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 00000000..e21668f5 --- /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 00000000..9464133e --- /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 00000000..ed5d2ad0 --- /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 00000000..86700044 --- /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 00000000..d0ae6ef2 --- /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 00000000..c58fc83d --- /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 00000000..32429839 --- /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 00000000..29a2df10 --- /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 00000000..ec074310 --- /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 00000000..8c6d4e80 --- /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 00000000..fb7a0db0 --- /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 00000000..2be76d61 --- /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 00000000..d16f0ad9 --- /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 00000000..2888e965 --- /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 00000000..5c6d09b1 --- /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 00000000..4d65ff8c --- /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 00000000..1d313eaf --- /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 00000000..d1045978 --- /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 00000000..9ab54ba1 --- /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 00000000..90dcc4a8 --- /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 fd1598c4..c6f49de2 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 +``` +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) -default-rule: "default" rule-ref assign-op term +``` +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 +``` -spec-rule: rule-head rule-bodies +referenced by: -rule-head: func-rule -| contains-rule -| object-rule -| set-rule -| compr-rule +* rule-head -func-rule: rule-ref "(" term { "," term } [","] ")" [ rule-assign ] +**set-rule:** -contains-rule: rule-ref "contains" or-expr +![set-rule](diagram/set-rule.svg) -object-rule: rule-ref NO_WS "[" membership-expr "]" rule-assign +``` +set-rule ::= rule-ref NO_WS '[' membership-expr ']' +``` -set-rule: rule-ref NO_WS "[" membership-expr "]" +referenced by: -compr-rule: rule-ref [ rule-assign ] +* rule-head -rule-ref: rule-ref NO_WS "." NO_WS var -| path-ref NO_WS "[" membership-expr "]" -| var +**compr-rule:** -rule-assign: assign-op membership-expr +![compr-rule](diagram/compr-rule.svg) -rule-bodies: "if" "{" query "}" alternatives -| "if" literal-stmt alternatives -| "{" query "}" alternatives +``` +compr-rule + ::= rule-ref rule-assign? +``` -alternatives: query-blocks -| else-blocks +referenced by: -query-blocks: { "{" query "}" } +* rule-head -else-blocks: { else-block } +**rule-ref:** -else-block: "else" [rule-assign] "if" "{" query "}" -| "else" [rule-assign] "if" literal-stmt -| "else" [rule-assign] "{" query "}" +![rule-ref](diagram/rule-ref.svg) -assign-op: "=" | ":=" +``` +rule-ref ::= ( path-ref NO_WS '[' membership-expr ']' | var ) ( NO_WS '.' NO_WS var )* +``` -query: literal-stmt { sep literal-stmt } +referenced by: -sep: ";" | "\n" | "\r\n" +* compr-rule +* contains-rule +* default-rule +* func-rule +* object-rule +* set-rule -literal-stmt: literal with-modifiers +**rule-assign:** -with-modifiers: { "with" path-ref "as" in-expr } +![rule-assign](diagram/rule-assign.svg) -literal: some -| every -| expr -| not-expr +``` +rule-assign + ::= assign-op membership-expr +``` -some: some-vars -| some-in +referenced by: -some-vars: "some" var { "," var } +* compr-rule +* else-block +* func-rule +* object-rule -some-in: "some" ref [ "," ref ] "in" +**rule-bodies:** -every: "every" var [ "," var ] "in" bool-expr "{" query "}" +![rule-bodies](diagram/rule-bodies.svg) -expr: assign-expr +``` +rule-bodies + ::= ( 'if' ( '{' query '}' | literal-stmt ) | '{' query '}' ) alternatives +``` -not-expr: "not" assign-expr +referenced by: -assign-expr: ref assign-op membership-expr +* spec-rule -membership-expr: membership-expr "in" bool-expr -| bool-expr "," bool-expr -| bool-expr +**alternatives:** -in-expr: in-expr "in" bool-expr -| bool-expr +![alternatives](diagram/alternatives.svg) -bool-expr: bool-expr bool-op or-expr -| or-expr +``` +alternatives + ::= query-blocks + | else-blocks +``` -bool-op: "<" | "<=" | "==" | ">=" | ">" | "!=" +referenced by: -or-expr: or-expr "|" and-expr -| and-expr +* rule-bodies -and-expr: and-expr "&" arith-expr -| arith-expr +**query-blocks:** -arith-expr: arith-expr ("+" | "-") mul-div-expr -| mul-div-expr +![query-blocks](diagram/query-blocks.svg) -mul-div-expr: mul-div-expr ("*" | "/") term -| term +``` +query-blocks + ::= ( '{' query '}' )* +``` -term: ref +referenced by: -ref: scalar-or-var -| compr-set-or-object -| compr-or-array -| unary-expr -| parens-expr -| ref-dot -| ref-brack -| call-expr +* alternatives -ref-dot: ref NO_WS "." NO_WS var +**else-blocks:** -ref-brack: ref NO_WS "[" in-expr "]" +![else-blocks](diagram/else-blocks.svg) -call-expr: path-ref NO_WS "(" call-args [","] ")" +``` +else-blocks + ::= else-block* +``` -call-args: in-expr { "," in-expr } +referenced by: -parens-expr: "(" membership-expr ")" +* alternatives -unary-expr: "-" in-expr +**else-block:** -compr-set-or-object: set-compr -| set -| object-compr -| object +![else-block](diagram/else-block.svg) -set-compr: "{" compr "}" +``` +else-block + ::= 'else' rule-assign? ( 'if' ( '{' query '}' | literal-stmt ) | '{' query '}' ) +``` -# Set must have at least one item. -set: "{" in-expr { "," in-expr } [","] "}" -| "set(" ")" # empty set +referenced by: -object: "{" field { "," field } [","] "}" -| "{" "}" # empty object +* else-blocks -field: in-expr ":" in-expr +**assign-op:** -# Comprehension or array -compr-or-array: array-compr -| array +![assign-op](diagram/assign-op.svg) -array-compr: "[" compr "]" +``` +assign-op + ::= '=' + | ':=' +``` -array: "[" in-expr { "," in-expr } [","] "]" -| "[" "]" # Empty array +referenced by: -# Comprehension -compr: ref "|" query +* assign-expr +* default-rule +* rule-assign -scalar-or-var: var -| NUMBER -| STRING -| RAWSTRING -| "null" -| "true" -| "false" +**query:** -var: IDENT -| non-imported-future-keyword +![query](diagram/query.svg) -non-imported-future-future-keyword: "contains" | "every" | "if" | "in" ``` +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