Skip to content

Commit a3a20a1

Browse files
authored
feat!: Rego -> RVM Compiler and extensive testsuite (microsoft#506)
# RVM compiler test cases Coverage: - arithmetic - arrays - chained lookups - comparisons - comprehensions - default rules - destructuring - function rules - loops/quantifiers - multiple entrypoints - objects/sets - variables - negative/edge scenarios such as data/rule conflicts - virtual data lookups - etc # Modify interpreter and compiled policy for RVM Compilation - Interpreter::eval_default_rule_for_compiler: evaluates a named default rule in isolation - allows compiler to emit a constant value instead of instructions for the default value # feat: Rego Compiler Scaffolding - Introduce the rego::compiler module surface and entry point wiring - Add the core compiler concepts: - register allocator - scope tracking - literal/builtin tables - rule worklists - instruction emit helpers - compiler-specific error types - context structs for rules, comprehensions, and loops to support later lowering passes. # feat: Compile Rules/Queries - add compiler::compile_from_policy workflow plus rule worklist, entry-point wiring, and recursion checks - implement query lowering: - scheduling-aware statement ordering - loop hoisting - “every/some” semantics - context yields - literal assertions - finalize Program construction # feat: Expression Lowering - add compile_rego_expr and helpers to translate every AST expression into RVM instructions, - interop with binding plans, comprehensions, and membership checks. - implement collection literal builders (ArrayCreate, SetCreate, ObjectCreate) - dedupe literal keys and handle mixed literal/dynamic fields via instruction data blocks. - operations: - arithmetic/boolean/bin operators - membership - unary minus - set unions/intersections - etc - user-defined and builtin function calls - reference handling - analyse chained refs - distinguishe data/input/local roots - perform rule dispatch or virtual document lookups - emits optimized Index/ChainedIndex instructions. # feat: Comprehensions & Loops - shared comprehension emitter - wraps array/set/object comprehensions with ComprehensionBegin/End - context management - loop lowering utilities - read hoisting metadata - emit LoopStart/LoopNext - some in lowering - every quantifiers - index iteration - propagate binding plans into stored registers so downstream statements see bound variables. # feat: Destructuring Lowering - destructuring planner integration - assignment/parameter/loop bindings use hoisted plans instead of re-walking ASTs. - handle :=, =, wildcard matches, and equality - evaluate RHS - applying destructuring plans - emit assert condition as needed - support nested array/object destructuring, dynamic keys, and some ... in forms # test: Shared Testing + RVM Suites - move YAML test helpers into test_utils.rs and re-export via common.rs for use by interpreter and vm test suites - comprehensive compiler test suite - compiles policies with the new Rego→RVM compiler - runs them through RegoVM - compares against interpreter behavior - supports multiple entry points - provides assembly listings - filterable YAML suites. Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
1 parent 688e612 commit a3a20a1

43 files changed

Lines changed: 6945 additions & 139 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/compiled_policy.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,29 @@ pub(crate) type InferredResourceTypes = BTreeMap<Ref<Query>, ResourceTypeInfo>;
2525
/// Wrapper around CompiledPolicyData that holds an Rc reference.
2626
#[derive(Debug, Clone)]
2727
pub struct CompiledPolicy {
28-
inner: Rc<CompiledPolicyData>,
28+
pub(crate) inner: Rc<CompiledPolicyData>,
2929
}
3030

3131
impl CompiledPolicy {
3232
/// Create a new CompiledPolicy from CompiledPolicyData.
3333
pub(crate) fn new(inner: Rc<CompiledPolicyData>) -> Self {
3434
Self { inner }
3535
}
36+
37+
/// Get access to the rules in the compiled policy for downstream consumers like the RVM compiler.
38+
pub fn get_rules(&self) -> &Map<String, Vec<Ref<Rule>>> {
39+
&self.inner.rules
40+
}
41+
42+
/// Get access to the modules in the compiled policy.
43+
pub fn get_modules(&self) -> &Vec<Ref<Module>> {
44+
self.inner.modules.as_ref()
45+
}
46+
47+
/// Returns true when the compiled policy should use Rego v0 semantics.
48+
pub fn is_rego_v0(&self) -> bool {
49+
!self.inner.modules.iter().any(|module| module.rego_v1)
50+
}
3651
}
3752

3853
impl CompiledPolicy {

src/interpreter.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3371,6 +3371,39 @@ impl Interpreter {
33713371
Ok(())
33723372
}
33733373

3374+
/// Evaluate a default rule and return the resulting value for compiler consumers.
3375+
pub fn eval_default_rule_for_compiler(&mut self, rule_path: &str) -> Result<Value> {
3376+
self.input = Value::Undefined;
3377+
self.data = Value::Undefined;
3378+
3379+
let default_rules = self.compiled_policy.default_rules.get(rule_path).cloned();
3380+
3381+
if let Some(rules) = default_rules {
3382+
for (rule, _) in rules {
3383+
for module in self.compiled_policy.modules.iter() {
3384+
if module.policy.contains(&rule) {
3385+
let prev_module = self.set_current_module(Some(module.clone()))?;
3386+
let result = self.eval_default_rule(&rule);
3387+
self.set_current_module(prev_module)?;
3388+
3389+
if result.is_ok() {
3390+
let components: Vec<&str> = rule_path.split('.').skip(1).collect();
3391+
let value = Self::get_value_chained(self.data.clone(), &components);
3392+
3393+
if value != Value::Undefined {
3394+
return Ok(value);
3395+
}
3396+
}
3397+
3398+
return result.map(|_| Value::Undefined);
3399+
}
3400+
}
3401+
}
3402+
}
3403+
3404+
bail!("Could not find default rule for path: {}", rule_path);
3405+
}
3406+
33743407
fn update_data(
33753408
&mut self,
33763409
span: &Span,
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
use super::{CompilationContext, Compiler, ComprehensionType, ContextType, Register, Result};
5+
use crate::ast::{ExprRef, Query};
6+
use crate::lexer::Span;
7+
use crate::rvm::instructions::{ComprehensionBeginParams, ComprehensionMode};
8+
use crate::rvm::Instruction;
9+
10+
impl<'a> Compiler<'a> {
11+
fn compile_comprehension(
12+
&mut self,
13+
mode: ComprehensionMode,
14+
context_type: ComprehensionType,
15+
key_expr: Option<&ExprRef>,
16+
value_expr: Option<&ExprRef>,
17+
query: &Query,
18+
span: &Span,
19+
) -> Result<Register> {
20+
let result_reg = self.alloc_register();
21+
let key_reg = self.alloc_register();
22+
let value_reg = self.alloc_register();
23+
24+
let params_index = self
25+
.program
26+
.add_comprehension_begin_params(ComprehensionBeginParams {
27+
mode,
28+
collection_reg: result_reg,
29+
result_reg,
30+
key_reg,
31+
value_reg,
32+
body_start: 0,
33+
comprehension_end: 0,
34+
});
35+
36+
self.emit_instruction(Instruction::ComprehensionBegin { params_index }, span);
37+
38+
let body_start = self.program.instructions.len() as u16;
39+
40+
let context = CompilationContext {
41+
context_type: ContextType::Comprehension(context_type),
42+
dest_register: result_reg,
43+
key_expr: key_expr.cloned(),
44+
value_expr: value_expr.cloned(),
45+
span: span.clone(),
46+
key_value_loops_hoisted: false,
47+
};
48+
self.push_context(context);
49+
self.compile_query(query)?;
50+
self.pop_context();
51+
52+
self.emit_instruction(Instruction::ComprehensionEnd {}, span);
53+
let comprehension_end = self.program.instructions.len() as u16;
54+
55+
self.program
56+
.update_comprehension_begin_params(params_index, |params| {
57+
params.body_start = body_start;
58+
params.comprehension_end = comprehension_end;
59+
});
60+
61+
Ok(result_reg)
62+
}
63+
64+
pub(super) fn compile_array_comprehension(
65+
&mut self,
66+
term: &ExprRef,
67+
query: &Query,
68+
span: &Span,
69+
) -> Result<Register> {
70+
self.compile_comprehension(
71+
ComprehensionMode::Array,
72+
ComprehensionType::Array,
73+
None,
74+
Some(term),
75+
query,
76+
span,
77+
)
78+
}
79+
80+
pub(super) fn compile_set_comprehension(
81+
&mut self,
82+
term: &ExprRef,
83+
query: &Query,
84+
span: &Span,
85+
) -> Result<Register> {
86+
self.compile_comprehension(
87+
ComprehensionMode::Set,
88+
ComprehensionType::Set,
89+
None,
90+
Some(term),
91+
query,
92+
span,
93+
)
94+
}
95+
96+
pub(super) fn compile_object_comprehension(
97+
&mut self,
98+
key: &ExprRef,
99+
value: &ExprRef,
100+
query: &Query,
101+
span: &Span,
102+
) -> Result<Register> {
103+
self.compile_comprehension(
104+
ComprehensionMode::Object,
105+
ComprehensionType::Object,
106+
Some(key),
107+
Some(value),
108+
query,
109+
span,
110+
)
111+
}
112+
}

0 commit comments

Comments
 (0)