Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: or keyword #315

Merged
merged 1 commit into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .github/workflows/pr-extensions.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: tests/release

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

env:
CARGO_TERM_COLOR: always

jobs:
test:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- name: Build only std
run: cargo build -r --example regorus --no-default-features --features "std,rego-extensions"
- name: Doc Tests
run: cargo test -r --doc --features rego-extensions
- name: Run tests
run: cargo test -r --features rego-extensions
- name: Run example
run: cargo run --example regorus --features rego-extensions -- eval -d examples/server/allowed_server.rego -i examples/server/input.json data.example
- name: Run tests (ACI)
run: cargo test -r --test aci --features rego-extensions
- name: Run tests (KATA)
run: cargo test -r --test kata --features rego-extensions
- name: Run tests (OPA Conformance)
run: >-
cargo test -r --test opa --features opa-testutil,serde_json/arbitrary_precision,rego-extensions -- $(tr '\n' ' ' < tests/opa.passing)
7 changes: 6 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,9 @@ full-opa = [
"time",
"uuid",
"urlquery",
"yaml"
"yaml",

#"rego-extensions"
]

# Features that can be used in no_std environments.
Expand All @@ -89,6 +91,9 @@ opa-no-std = [
"lazy_static/spin_no_std"
]

# Rego language extensions
rego-extensions = []

# This feature enables some testing utils for OPA tests.
opa-testutil = []
rand = ["dep:rand"]
Expand Down
6 changes: 3 additions & 3 deletions docs/grammar.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ in-expr ::= in-expr 'in' 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
set-union-expr ::= set-union-expr '|' set-intersection-expr
| set-intersection-expr
set-intersection-expr ::= set-intersection-expr '&' arith-expr
| arith-expr
arith-expr ::= arith-expr ('+' | '-') mul-div-expr
| mul-div-expr
Expand Down
7 changes: 6 additions & 1 deletion scripts/pre-push
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ if [ -f Cargo.toml ]; then
cargo test -r --test aci
cargo test -r --test kata

# Ensure that all tests pass with extensions
cargo test -r --features rego-extensions
cargo test -r --test aci rego-extensions
cargo test -r --test kata rego-extensions

# Ensure that OPA conformance tests don't regress.
cargo test -r --features opa-testutil,serde_json/arbitrary_precision --test opa -- $(tr '\n' ' ' < tests/opa.passing)
cargo test -r --features opa-testutil,serde_json/arbitrary_precision,rego-extensions --test opa -- $(tr '\n' ' ' < tests/opa.passing)
fi
13 changes: 11 additions & 2 deletions src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ use core::{cmp, fmt, ops::Deref};
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "ast", derive(serde::Serialize))]
pub enum BinOp {
And,
Or,
Intersection,
Union,
}

#[derive(Debug, PartialEq, Eq, Clone)]
Expand Down Expand Up @@ -209,6 +209,13 @@ pub enum Expr {
value: Ref<Expr>,
collection: Ref<Expr>,
},

#[cfg(feature = "rego-extensions")]
OrExpr {
span: Span,
lhs: Ref<Expr>,
rhs: Ref<Expr>,
},
}

impl Expr {
Expand All @@ -232,6 +239,8 @@ impl Expr {
| ArithExpr { span, .. }
| AssignExpr { span, .. }
| Membership { span, .. } => span,
#[cfg(feature = "rego-extensions")]
OrExpr { span, .. } => span,
}
}
}
Expand Down
21 changes: 19 additions & 2 deletions src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,12 @@ impl Interpreter {
self.hoist_loops_impl(rhs, loops);
}

#[cfg(feature = "rego-extensions")]
OrExpr { lhs, rhs, .. } => {
self.hoist_loops_impl(lhs, loops);
self.hoist_loops_impl(rhs, loops);
}

Membership {
key,
value,
Expand Down Expand Up @@ -554,8 +560,8 @@ impl Interpreter {
}

match op {
BinOp::Or => builtins::sets::union(lhs, rhs, lhs_value, rhs_value),
BinOp::And => builtins::sets::intersection(lhs, rhs, lhs_value, rhs_value),
BinOp::Union => builtins::sets::union(lhs, rhs, lhs_value, rhs_value),
BinOp::Intersection => builtins::sets::intersection(lhs, rhs, lhs_value, rhs_value),
}
}

Expand Down Expand Up @@ -2831,6 +2837,15 @@ impl Interpreter {
..
} => self.eval_membership(key, value, collection),

#[cfg(feature = "rego-extensions")]
Expr::OrExpr { lhs, rhs, .. } => {
let lhs = self.eval_expr(lhs)?;
match lhs {
Value::Bool(false) | Value::Null | Value::Undefined => self.eval_expr(rhs),
_ => Ok(lhs),
}
}

// Creation expression
Expr::Array { items, .. } => self.eval_array(items),
Expr::Object { fields, .. } => self.eval_object(fields),
Expand Down Expand Up @@ -3126,6 +3141,8 @@ impl Interpreter {
ArithExpr { span, .. } => ("arithexpr", span),
AssignExpr { span, .. } => ("assignexpr", span),
Membership { span, .. } => ("membership", span),
#[cfg(feature = "rego-extensions")]
OrExpr { span, .. } => ("orexpr", span),
};

Err(span.error(format!("invalid `{kind}` in default value").as_str()))
Expand Down
62 changes: 47 additions & 15 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,7 @@ impl<'source> Parser<'source> {

fn parse_parens_expr(&mut self) -> Result<Expr> {
self.next_token()?;
let expr = self.parse_membership_expr()?;
let expr = self.parse_expr()?;
self.expect(")", "while parsing parenthesized expression")?;
//TODO: if needed introduce a parens-expr node or adjust expr's span.
Ok(expr)
Expand Down Expand Up @@ -700,7 +700,7 @@ impl<'source> Parser<'source> {
}
}

fn parse_and_expr(&mut self) -> Result<Expr> {
fn parse_set_intersection_expr(&mut self) -> Result<Expr> {
let start = self.tok.1.start;
let mut expr = self.parse_arith_expr()?;

Expand All @@ -712,27 +712,27 @@ impl<'source> Parser<'source> {
span.end = self.end;
expr = Expr::BinExpr {
span,
op: BinOp::And,
op: BinOp::Intersection,
lhs: Ref::new(expr),
rhs: Ref::new(right),
};
}
Ok(expr)
}

fn parse_or_expr(&mut self) -> Result<Expr> {
fn parse_set_union_expr(&mut self) -> Result<Expr> {
let start = self.tok.1.start;
let mut expr = self.parse_and_expr()?;
let mut expr = self.parse_set_intersection_expr()?;

while self.token_text() == "|" {
let mut span = self.tok.1.clone();
span.start = start;
self.next_token()?;
let right = self.parse_and_expr()?;
let right = self.parse_set_intersection_expr()?;
span.end = self.end;
expr = Expr::BinExpr {
span,
op: BinOp::Or,
op: BinOp::Union,
lhs: Ref::new(expr),
rhs: Ref::new(right),
};
Expand All @@ -742,7 +742,7 @@ impl<'source> Parser<'source> {

fn parse_bool_expr(&mut self) -> Result<Expr> {
let start = self.tok.1.start;
let mut expr = self.parse_or_expr()?;
let mut expr = self.parse_set_union_expr()?;
loop {
let mut span = self.tok.1.clone();
span.start = start;
Expand All @@ -756,7 +756,7 @@ impl<'source> Parser<'source> {
_ => break,
};
self.next_token()?;
let right = self.parse_or_expr()?;
let right = self.parse_set_union_expr()?;
span.end = self.end;
expr = Expr::BoolExpr {
span,
Expand Down Expand Up @@ -811,6 +811,32 @@ impl<'source> Parser<'source> {
Ok(expr)
}

pub fn parse_expr(&mut self) -> Result<Expr> {
#[cfg(feature = "rego-extensions")]
return self.parse_or_expr();

#[cfg(not(feature = "rego-extensions"))]
return self.parse_membership_expr();
}

#[cfg(feature = "rego-extensions")]
pub fn parse_or_expr(&mut self) -> Result<Expr> {
let start = self.tok.1.start;
let mut expr = self.parse_membership_expr()?;
while self.token_text() == "or" {
let mut span = self.tok.1.clone();
span.start = start;
self.next_token()?;
let rhs = self.parse_membership_expr()?;
expr = Expr::OrExpr {
span,
lhs: Ref::new(expr),
rhs: Ref::new(rhs),
};
}
Ok(expr)
}

pub fn parse_membership_expr(&mut self) -> Result<Expr> {
let start = self.tok.1.start;
let mut expr = self.parse_bool_expr()?;
Expand Down Expand Up @@ -851,12 +877,12 @@ impl<'source> Parser<'source> {
":=" => AssignOp::ColEq,
_ => {
*self = state;
return self.parse_membership_expr();
return self.parse_expr();
}
};

self.next_token()?;
let right = self.parse_membership_expr()?;
let right = self.parse_expr()?;
span.end = self.end;
Ok(Expr::AssignExpr {
span,
Expand Down Expand Up @@ -1048,7 +1074,13 @@ impl<'source> Parser<'source> {
// Treat { 1 | 1 } as a comprehension instead of a
// set of 1 element.
if let Literal::Expr { expr: e, .. } = &stmt.literal {
if matches!(e.as_ref(), Expr::BinExpr { op: BinOp::Or, .. }) {
if matches!(
e.as_ref(),
Expr::BinExpr {
op: BinOp::Union,
..
}
) {
*self = state;
bail!("try parse as comprehension");
}
Expand Down Expand Up @@ -1116,7 +1148,7 @@ impl<'source> Parser<'source> {
_ => return Ok(None),
};

let expr = Ref::new(self.parse_membership_expr()?);
let expr = Ref::new(self.parse_expr()?);
span.end = self.end;
Ok(Some(RuleAssign {
span,
Expand Down Expand Up @@ -1264,7 +1296,7 @@ impl<'source> Parser<'source> {
}
"[" => {
self.next_token()?;
let index = self.parse_membership_expr()?;
let index = self.parse_expr()?;
span.end = self.end;
self.expect("]", "while parsing bracketed reference")?;
term = Expr::RefBrack {
Expand Down Expand Up @@ -1312,7 +1344,7 @@ impl<'source> Parser<'source> {
}
"contains" => {
self.next_token()?;
let key = Ref::new(self.parse_membership_expr()?);
let key = Ref::new(self.parse_expr()?);
span.end = self.end;
Ok(RuleHead::Set {
span,
Expand Down
6 changes: 6 additions & 0 deletions src/scheduler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,12 @@ pub fn traverse(expr: &Ref<Expr>, f: &mut dyn FnMut(&Ref<Expr>) -> Result<bool>)
traverse(rhs, f)?;
}

#[cfg(feature = "rego-extensions")]
OrExpr { lhs, rhs, .. } => {
traverse(lhs, f)?;
traverse(rhs, f)?;
}

Membership {
key,
value,
Expand Down
5 changes: 5 additions & 0 deletions src/tests/interpreter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,11 @@ fn yaml_test_impl(file: &str) -> Result<()> {
}

fn yaml_test(file: &str) -> Result<()> {
#[cfg(not(feature = "rego-extensions"))]
if file.contains("rego-extensions") {
return Ok(());
}

match yaml_test_impl(file) {
Ok(_) => Ok(()),
Err(e) => {
Expand Down
Loading
Loading