// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. use crate::ast::*; use crate::builtins; use crate::lexer::Span; use crate::parser::Parser; use crate::scheduler::*; use crate::utils::*; use crate::value::*; use anyhow::{anyhow, bail, Result}; use log::info; use serde::Serialize; use std::collections::{hash_map::Entry, BTreeMap, BTreeSet, HashMap}; use std::rc::Rc; type Scope = BTreeMap; pub struct Interpreter<'source> { modules: Vec<&'source Module<'source>>, module: Option<&'source Module<'source>>, schedule: Option>, current_module_path: String, prepared: bool, input: Value, data: Value, init_data: Value, scopes: Vec, // TODO: handle recursive calls where same expr could have different values. loop_var_values: BTreeMap<&'source Expr<'source>, Value>, contexts: Vec>, functions: HashMap>>, rules: HashMap>>, default_rules: HashMap, Option)>>, processed: BTreeSet<&'source Rule<'source>>, active_rules: Vec<&'source Rule<'source>>, builtins_cache: BTreeMap<(&'static str, Vec), Value>, no_rules_lookup: bool, traces: Option>, } #[derive(Debug, Clone, Serialize)] pub struct QueryResult { // Expressions is shown first to match OPA. pub expressions: Vec, #[serde(skip_serializing_if = "Value::is_empty_object")] pub bindings: Value, } impl Default for QueryResult { fn default() -> Self { Self { bindings: Value::new_object(), expressions: vec![], } } } #[derive(Debug, Clone, Default, Serialize)] pub struct QueryResults { pub result: Vec, } #[derive(Debug, Clone)] struct Context<'source> { key_expr: Option<&'source Expr<'source>>, output_expr: Option<&'source Expr<'source>>, value: Value, result: Option, results: QueryResults, } #[derive(Debug)] struct LoopExpr<'source> { span: &'source Span<'source>, expr: &'source Expr<'source>, value: &'source Expr<'source>, index: &'source str, } impl<'source> Interpreter<'source> { pub fn new(modules: Vec<&'source Module<'source>>) -> Result> { Ok(Interpreter { modules, module: None, schedule: None, current_module_path: String::default(), prepared: false, input: Value::new_object(), data: Value::new_object(), init_data: Value::new_object(), scopes: vec![Scope::new()], contexts: vec![], loop_var_values: BTreeMap::new(), functions: HashMap::new(), rules: HashMap::new(), default_rules: HashMap::new(), processed: BTreeSet::new(), active_rules: vec![], builtins_cache: BTreeMap::new(), no_rules_lookup: false, traces: None, }) } pub fn get_modules(&mut self) -> &mut Vec<&'source Module<'source>> { &mut self.modules } pub fn set_data(&mut self, data: Value) { self.data = data; } pub fn get_data(&mut self) -> &mut Value { &mut self.data } fn clean_internal_evaluation_state(&mut self) { self.data = self.init_data.clone(); self.processed.clear(); self.loop_var_values.clear(); self.scopes = vec![Scope::new()]; self.contexts = vec![]; } fn checks_for_eval(&mut self, input: &Option, enable_tracing: bool) -> Result<()> { if !self.prepared { bail!("prepare_for_eval should be called before eval_modules"); } self.traces = match enable_tracing { true => Some(vec![]), false => None, }; if let Some(input) = input { self.input = input.clone(); info!("input: {:#?}", self.input); } Ok(()) } fn current_module(&self) -> Result<&'source Module<'source>> { self.module .ok_or_else(|| anyhow!("internal error: current module not set")) } fn current_scope(&mut self) -> Result<&Scope> { self.scopes .last() .ok_or_else(|| anyhow!("internal error: no active scope")) } fn current_scope_mut(&mut self) -> Result<&mut Scope> { self.scopes .last_mut() .ok_or_else(|| anyhow!("internal error: no active scope")) } #[inline(always)] fn add_variable(&mut self, name: &str, value: Value) -> Result<()> { let name = name.to_string(); // Only add the variable if the key is not "_" if name != "_" { self.current_scope_mut()?.insert(name, value); } Ok(()) } fn add_variable_or(&mut self, name: &str) -> Result { for scope in self.scopes.iter().rev() { if let Some(variable) = scope.get(&name.to_string()) { return Ok(variable.clone()); } } self.add_variable(name, Value::Undefined)?; Ok(Value::Undefined) } // TODO: optimize this fn variables_assignment(&mut self, name: &str, value: &Value) -> Result<()> { if let Some(variable) = self.current_scope_mut()?.get_mut(name) { *variable = value.clone(); Ok(()) } else if name == "_" { Ok(()) } else { bail!("variable {} is undefined", name) } } fn eval_chained_ref_dot_or_brack(&mut self, mut expr: &'source Expr<'source>) -> Result { // Collect a chaing of '.field' or '["field"]' let mut path = vec![]; loop { if let Some(v) = self.loop_var_values.get(expr) { path.reverse(); return Ok(Self::get_value_chained(v.clone(), &path[..])); } match expr { // Stop path collection upon encountering the leading variable. Expr::Var(v) => { path.reverse(); return self.lookup_var(v, &path[..]); } // Accumulate chained . field accesses. Expr::RefDot { refr, field, .. } => { expr = refr; path.push(field.text()); } Expr::RefBrack { refr, index, .. } => match index.as_ref() { // refr["field"] is the same as refr.field Expr::String(s) => { expr = refr; path.push(s.text()); } // Handle other forms of refr. // Note, we have the choice to evaluate a non-string index _ => { path.reverse(); let obj = self.eval_expr(refr)?; let index = self.eval_expr(index)?; return Ok(Self::get_value_chained(obj[&index].clone(), &path[..])); } }, _ => { path.reverse(); return Ok(Self::get_value_chained(self.eval_expr(expr)?, &path[..])); } } } } fn is_loop_index_var(&self, ident: &str) -> bool { // TODO: check for vars that are declared using some-vars match ident { "_" => true, _ => match self.lookup_local_var(ident) { // If ident is a local var (in current or parent scopes), // then it is not a loop var. Some(_) => false, None => { // Check if ident is a rule. let path = self.current_module_path.clone() + "." + ident; self.rules.get(&path).is_none() } }, } } fn hoist_loops_impl(&self, expr: &'source Expr<'source>, loops: &mut Vec>) { use Expr::*; match expr { RefBrack { refr, index, span } => { // First hoist any loops in refr self.hoist_loops_impl(refr, loops); // Then hoist the current bracket operation. match index.as_ref() { Var(ident) if self.is_loop_index_var(ident.text()) => loops.push(LoopExpr { span, expr, value: refr, index: ident.text(), }), _ => { // hoist any loops in index expression. self.hoist_loops_impl(index, loops); } } } // Primitives String(_) | RawString(_) | Number(_) | True(_) | False(_) | Null(_) | Var(_) => (), // Recurse into expressions in other variants. Array { items, .. } | Set { items, .. } | Call { params: items, .. } => { for item in items { self.hoist_loops_impl(item, loops); } } Object { fields, .. } => { for (_, key, value) in fields { self.hoist_loops_impl(key, loops); self.hoist_loops_impl(value, loops); } } RefDot { refr: expr, .. } | UnaryExpr { expr, .. } => { self.hoist_loops_impl(expr, loops) } BinExpr { lhs, rhs, .. } | BoolExpr { lhs, rhs, .. } | ArithExpr { lhs, rhs, .. } | AssignExpr { lhs, rhs, .. } => { self.hoist_loops_impl(lhs, loops); self.hoist_loops_impl(rhs, loops); } Membership { key, value, collection, .. } => { if let Some(key) = key.as_ref() { self.hoist_loops_impl(key, loops); } self.hoist_loops_impl(value, loops); self.hoist_loops_impl(collection, loops); } // The output expressions of comprehensions must be subject to hoisting // only after evaluating the body of the comprehensions since the output // expressions may depend on variables defined within the body. ArrayCompr { .. } | SetCompr { .. } | ObjectCompr { .. } => (), } } fn hoist_loops(&self, literal: &'source Literal<'source>) -> Vec> { let mut loops = vec![]; use Literal::*; match literal { SomeVars { .. } => (), SomeIn { key, value, collection, .. } => { if let Some(key) = key { self.hoist_loops_impl(key, &mut loops); } self.hoist_loops_impl(value, &mut loops); self.hoist_loops_impl(collection, &mut loops); } Every { domain: collection, .. } => self.hoist_loops_impl(collection, &mut loops), Expr { expr, .. } | NotExpr { expr, .. } => self.hoist_loops_impl(expr, &mut loops), } loops } fn eval_bool_expr( &mut self, op: &BoolOp, lhs_expr: &'source Expr<'source>, rhs_expr: &'source Expr<'source>, ) -> Result { let lhs = self.eval_expr(lhs_expr)?; let rhs = self.eval_expr(rhs_expr)?; if lhs == Value::Undefined || rhs == Value::Undefined { return Ok(Value::Undefined); } builtins::comparison::compare(op, &lhs, &rhs) } fn eval_bin_expr( &mut self, op: &BinOp, lhs: &'source Expr<'source>, rhs: &'source Expr<'source>, ) -> Result { let lhs_value = self.eval_expr(lhs)?; let rhs_value = self.eval_expr(rhs)?; if lhs_value == Value::Undefined || rhs_value == Value::Undefined { return Ok(Value::Undefined); } match op { BinOp::Or => builtins::sets::union(lhs, rhs, lhs_value, rhs_value), BinOp::And => builtins::sets::intersection(lhs, rhs, lhs_value, rhs_value), } } fn eval_arith_expr( &mut self, op: &ArithOp, lhs: &'source Expr<'source>, rhs: &'source Expr<'source>, ) -> Result { let lhs_value = self.eval_expr(lhs)?; let rhs_value = self.eval_expr(rhs)?; if lhs_value == Value::Undefined || rhs_value == Value::Undefined { return Ok(Value::Undefined); } match (op, &lhs_value, &rhs_value) { (ArithOp::Sub, Value::Set(_), _) | (ArithOp::Sub, _, Value::Set(_)) => { builtins::sets::difference(lhs, rhs, lhs_value, rhs_value) } _ => builtins::numbers::arithmetic_operation(op, lhs, rhs, lhs_value, rhs_value), } } fn eval_assign_expr( &mut self, op: &AssignOp, lhs: &'source Expr<'source>, rhs: &'source Expr<'source>, ) -> Result { let (name, value) = match op { AssignOp::Eq => { match (lhs, rhs) { (Expr::Var(lhs_span), Expr::Var(rhs_span)) => { let (lhs_name, lhs_var) = (lhs_span.text(), self.eval_expr(lhs)?); let (rhs_name, rhs_var) = (rhs_span.text(), self.eval_expr(rhs)?); match (&lhs_var, &rhs_var) { (Value::Undefined, Value::Undefined) => { bail!(lhs.span().error("both operands are unsafe")) } (Value::Undefined, _) => (lhs_name, rhs_var), (_, Value::Undefined) => (rhs_name, lhs_var), // TODO: avoid reeval _ => return self.eval_bool_expr(&BoolOp::Eq, lhs, rhs), } } (Expr::Var(lhs_span), _) => { let (name, var) = (lhs_span.text(), self.eval_expr(lhs)?); // TODO: Check this // Allow variable overwritten inside a loop if !matches!(var, Value::Undefined) && self.loop_var_values.get(rhs).is_none() { return self.eval_bool_expr(&BoolOp::Eq, lhs, rhs); } (name, self.eval_expr(rhs)?) } (_, Expr::Var(rhs_span)) => { let (name, var) = (rhs_span.text(), self.eval_expr(rhs)?); // TODO: Check this // Allow variable overwritten inside a loop if !matches!(var, Value::Undefined) && self.loop_var_values.get(lhs).is_none() { return self.eval_bool_expr(&BoolOp::Eq, lhs, rhs); } (name, self.eval_expr(lhs)?) } // Treat the assignment as comparison if neither lhs nor rhs is a variable _ => return self.eval_bool_expr(&BoolOp::Eq, lhs, rhs), } } AssignOp::ColEq => { let name = if let Expr::Var(span) = lhs { span.text() } else { bail!("internal error: unexpected"); }; // TODO: Check this // Allow variable overwritten inside a loop if self.lookup_local_var(name).is_some() && self.loop_var_values.get(rhs).is_none() { bail!(rhs .span() .error(&format!("redefinition for variable {}", name))); } (name, self.eval_expr(rhs)?) } }; self.add_variable_or(name)?; // TODO: optimize this self.variables_assignment(name, &value)?; info!( "eval_assign_expr before, op: {:?}, lhs: {:?}, rhs: {:?}", op, lhs, rhs ); Ok(Value::Bool(true)) } fn eval_every( &mut self, _span: &'source Span<'source>, key: &'source Option>, value: &'source Span<'source>, domain: &'source Expr<'source>, query: &'source Query<'source>, ) -> Result { let domain = self.eval_expr(domain)?; self.scopes.push(Scope::new()); self.contexts.push(Context { key_expr: None, output_expr: None, value: Value::new_set(), result: None, results: QueryResults::default(), }); let mut r = true; match domain { Value::Array(a) => { for (idx, v) in a.iter().enumerate() { self.add_variable(value.text(), v.clone())?; if let Some(key) = key { self.add_variable(key.text(), Value::from_float(idx as Float))?; } if !self.eval_query(query)? { r = false; break; } } } Value::Set(s) => { for v in s.iter() { self.add_variable(value.text(), v.clone())?; if let Some(key) = key { self.add_variable(key.text(), v.clone())?; } if !self.eval_query(query)? { r = false; break; } } } Value::Object(o) => { for (k, v) in o.iter() { self.add_variable(value.text(), v.clone())?; if let Some(key) = key { self.add_variable(key.text(), k.clone())?; } if !self.eval_query(query)? { r = false; break; } } } // Other types cause every to evaluate to true even though // it is supposed to happen only for empty domain. _ => (), }; self.contexts.pop(); self.scopes.pop(); Ok(r) } fn lookup_or_eval_expr( &mut self, cache: &mut BTreeMap<&'source Expr<'source>, Value>, expr: &'source Expr<'source>, ) -> Result { match cache.get(expr) { Some(v) => Ok(v.clone()), _ => { let v = self.eval_expr(expr)?; cache.insert(expr, v.clone()); Ok(v) } } } fn make_bindings_impl( &mut self, is_last: bool, type_match: &mut BTreeSet<&'source Expr<'source>>, cache: &mut BTreeMap<&'source Expr<'source>, Value>, expr: &'source Expr<'source>, value: &Value, ) -> Result { // Propagate undefined. if value == &Value::Undefined { return Ok(false); } let span = expr.span(); let raise_error = is_last && type_match.get(expr).is_none(); match (expr, value) { (Expr::Var(ident), _) => { self.add_variable(ident.text(), value.clone())?; Ok(true) } // Destructure arrays (Expr::Array { items, .. }, Value::Array(a)) => { if items.len() != a.len() { if raise_error { return Err(span.error( format!( "array length mismatch. Expected {} got {}.", items.len(), a.len() ) .as_str(), )); } return Ok(false); } type_match.insert(expr); let mut r = false; for (idx, item) in items.iter().enumerate() { r = self.make_bindings(is_last, type_match, cache, item, &a[idx])? || r; } Ok(true) } // Destructure objects (Expr::Object { fields, .. }, Value::Object(_)) => { let mut r = true; for (_, key_expr, value_expr) in fields.iter() { // Rego does not support bindings in keys. // Therefore, just eval key_expr. let key = self.lookup_or_eval_expr(cache, key_expr)?; let field_value = &value[&key]; if field_value == &Value::Undefined { if raise_error { return Err(span.error("Expected value, got undefined.")); } return Ok(false); } // Match patterns in value_expr r = r && self.make_bindings( is_last, type_match, cache, value_expr, field_value, )?; } type_match.insert(expr); Ok(r) } _ => { let expr_value = self.lookup_or_eval_expr(cache, expr)?; if expr_value == Value::Undefined { return Ok(false); } if raise_error { let expr_t = builtins::types::get_type(&expr_value); let value_t = builtins::types::get_type(value); if expr_t != value_t { return Err(span.error( format!("Cannot bind pattern of type `{expr_t}` with value of type `{value_t}`. Value is {value}.").as_str())); } } type_match.insert(expr); Ok(&expr_value == value) } } } fn make_bindings( &mut self, is_last: bool, type_match: &mut BTreeSet<&'source Expr<'source>>, cache: &mut BTreeMap<&'source Expr<'source>, Value>, expr: &'source Expr<'source>, value: &Value, ) -> Result { let prev = self.no_rules_lookup; self.no_rules_lookup = true; let r = self.make_bindings_impl(is_last, type_match, cache, expr, value); self.no_rules_lookup = prev; r } fn make_key_value_bindings( &mut self, is_last: bool, type_match: &mut BTreeSet<&'source Expr<'source>>, cache: &mut BTreeMap<&'source Expr<'source>, Value>, exprs: (&'source Option>, &'source Expr<'source>), values: (&Value, &Value), ) -> Result { let (key_expr, value_expr) = exprs; let (key, value) = values; if let Some(key_expr) = key_expr { if !self.make_bindings(is_last, type_match, cache, key_expr, key)? { return Ok(false); } } self.make_bindings(is_last, type_match, cache, value_expr, value) } fn eval_some_in( &mut self, _span: &'source Span<'source>, key_expr: &'source Option>, value_expr: &'source Expr<'source>, collection: &'source Expr<'source>, stmts: &[&'source LiteralStmt<'source>], ) -> Result { let scope_saved = self.current_scope()?.clone(); let mut type_match = BTreeSet::new(); let mut cache = BTreeMap::new(); let mut count = 0; match self.eval_expr(collection)? { Value::Array(a) => { for (idx, value) in a.iter().enumerate() { if !self.make_key_value_bindings( idx == a.len() - 1, &mut type_match, &mut cache, (key_expr, value_expr), (&Value::from_float(idx as Float), value), )? { continue; } if self.eval_stmts(stmts)? { count += 1; } *self.current_scope_mut()? = scope_saved.clone(); } } Value::Set(s) => { for (idx, value) in s.iter().enumerate() { if !self.make_key_value_bindings( idx == s.len() - 1, &mut type_match, &mut cache, (key_expr, value_expr), (value, value), )? { continue; } if self.eval_stmts(stmts)? { count += 1; } *self.current_scope_mut()? = scope_saved.clone(); } } Value::Object(o) => { for (idx, (key, value)) in o.iter().enumerate() { if !self.make_key_value_bindings( idx == o.len() - 1, &mut type_match, &mut cache, (key_expr, value_expr), (key, value), )? { continue; } if self.eval_stmts(stmts)? { count += 1; } *self.current_scope_mut()? = scope_saved.clone(); } } Value::Undefined => (), v => { let span = collection.span(); bail!(span.error( format!("`some .. in collection` expects array/set/object. Got `{v}`").as_str() )) } } Ok(count > 0) } fn make_expression_result(span: &Span, v: &Value) -> Value { let mut loc = BTreeMap::new(); loc.insert( Value::String("row".to_string()), Value::from_float(span.line as f64), ); loc.insert( Value::String("col".to_string()), Value::from_float(span.col as f64), ); let mut expr = BTreeMap::new(); expr.insert(Value::String("value".to_string()), v.clone()); expr.insert(Value::String("location".to_string()), Value::from_map(loc)); expr.insert( Value::String("text".to_string()), Value::String(span.text().to_string()), ); Value::from_map(expr) } fn eval_stmt( &mut self, stmt: &'source LiteralStmt<'source>, stmts: &[&'source LiteralStmt<'source>], ) -> Result { let mut to_restore = vec![]; for wm in &stmt.with_mods { // Evaluate value and ref let value = self.eval_expr(&wm.r#as)?; let path = Parser::get_path_ref_components(&wm.refr)?; let mut path: Vec<&str> = path.iter().map(|s| s.text()).collect(); // TODO: multiple modules and qualified path if path.len() > 2 && format!("{}.{}", path[0], path[1]) == self.current_module_path { path = path[1..].to_vec(); } // Set new values in modifications table let mut saved = false; for (i, _) in path.iter().enumerate() { let vref = Self::make_or_get_value_mut(&mut self.data, &path[0..i])?; if vref == &Value::Undefined { to_restore.push((path[0..i].to_vec(), vref.clone())); saved = false; break; } } // TODO: input let vref = Self::make_or_get_value_mut(&mut self.data, &path[..])?; if !saved { to_restore.push((path, vref.clone())); } *vref = value; } let r = Ok(match &stmt.literal { Literal::Expr { span, expr, .. } => { let value = match expr { Expr::Call { span, fcn, params } => self.eval_call( span, fcn, params, get_extra_arg(expr, &HashMap::new()), true, )?, _ => self.eval_expr(expr)?, }; if let Some(ctx) = self.contexts.last_mut() { if let Some(result) = &mut ctx.result { result .expressions .push(Self::make_expression_result(span, &value)) } } if let Value::Bool(bool) = value { bool } else { // panic!(); // TODO: confirm this // For non-booleans, treat anything other than undefined as true value != Value::Undefined } } Literal::NotExpr { span, expr, .. } => { let value = match expr { // Extra parameter is allowed; but a return argument is not allowed. Expr::Call { span, fcn, params } => self.eval_call( span, fcn, params, get_extra_arg(expr, &HashMap::new()), false, )?, _ => self.eval_expr(expr)?, }; if let Some(ctx) = self.contexts.last_mut() { if let Some(result) = &mut ctx.result { result .expressions .push(Self::make_expression_result(span, &Value::Bool(true))) } } // https://github.com/open-policy-agent/opa/issues/1622#issuecomment-520547385 matches!(value, Value::Bool(false) | Value::Undefined) } Literal::SomeVars { span, vars, .. } => { for var in vars { let name = var.text(); if let Ok(variable) = self.add_variable_or(name) { if variable != Value::Undefined { return Err(anyhow!( "duplicated definition of local variable {}", name )); } } } if let Some(ctx) = self.contexts.last_mut() { if let Some(result) = &mut ctx.result { result .expressions .push(Self::make_expression_result(span, &Value::Bool(true))) } } true } Literal::SomeIn { span, key, value, collection, } => { if let Some(ctx) = self.contexts.last_mut() { if let Some(result) = &mut ctx.result { result .expressions .push(Self::make_expression_result(span, &Value::Bool(true))) } } self.eval_some_in(span, key, value, collection, stmts)? } Literal::Every { span, key, value, domain, query, } => { if let Some(ctx) = self.contexts.last_mut() { if let Some(result) = &mut ctx.result { result .expressions .push(Self::make_expression_result(span, &Value::Bool(true))) } } self.eval_every(span, key, value, domain, query)? } }); for (path, value) in to_restore.into_iter().rev() { if value == Value::Undefined { unimplemented!("handle undefined restore"); } else { let vref = Self::make_or_get_value_mut(&mut self.data, &path[..])?; *vref = value; } } r } fn eval_stmts_in_loop( &mut self, stmts: &[&'source LiteralStmt<'source>], loops: &[LoopExpr<'source>], ) -> Result { if loops.is_empty() { if !stmts.is_empty() { // Evaluate the current statement whose loop expressions have been hoisted. if self.eval_stmt(stmts[0], &stmts[1..])? { if !matches!(&stmts[0].literal, Literal::SomeIn { .. }) { self.eval_stmts(&stmts[1..]) } else { Ok(true) } } else { Ok(false) } } else { self.eval_stmts(stmts) } } else { let loop_expr = &loops[0]; let mut result = false; let loop_expr_value = self.eval_expr(loop_expr.value)?; // If the loop's index variable has already been assigned a value // (this can happen if the same index is used for two different collections), // then evaluate statements only if the index applies to this collection. if let Some(idx) = self.lookup_local_var(loop_expr.index) { if loop_expr_value[&idx] != Value::Undefined { result = self.eval_stmts_in_loop(stmts, &loops[1..])? || result; } return Ok(result); } // Save the current scope and restore it after evaluating the statements so // that the effects of the current loop iteration are cleared. let scope_saved = self.current_scope()?.clone(); let query_result = self.get_current_context()?.result.clone(); match loop_expr_value { Value::Array(items) => { for (idx, v) in items.iter().enumerate() { self.loop_var_values.insert(loop_expr.expr, v.clone()); self.add_variable(loop_expr.index, Value::from_float(idx as Float))?; result = self.eval_stmts_in_loop(stmts, &loops[1..])? || result; self.loop_var_values.remove(loop_expr.expr); *self.current_scope_mut()? = scope_saved.clone(); if let Some(ctx) = self.contexts.last_mut() { ctx.result = query_result.clone(); } } } Value::Set(items) => { for v in items.iter() { self.loop_var_values.insert(loop_expr.expr, v.clone()); // For sets, index is also the value. self.add_variable(loop_expr.index, v.clone())?; result = self.eval_stmts_in_loop(stmts, &loops[1..])? || result; self.loop_var_values.remove(loop_expr.expr); *self.current_scope_mut()? = scope_saved.clone(); if let Some(ctx) = self.contexts.last_mut() { ctx.result = query_result.clone(); } } } Value::Object(obj) => { for (k, v) in obj.iter() { self.loop_var_values.insert(loop_expr.expr, v.clone()); // For objects, index is key. self.add_variable(loop_expr.index, k.clone())?; result = self.eval_stmts_in_loop(stmts, &loops[1..])? || result; self.loop_var_values.remove(loop_expr.expr); *self.current_scope_mut()? = scope_saved.clone(); if let Some(ctx) = self.contexts.last_mut() { ctx.result = query_result.clone(); } } } _ => { return Err(loop_expr.span.source.error( loop_expr.span.line, loop_expr.span.col, "item cannot be indexed", )); } } // Return true if at least on iteration returned true Ok(result) } } fn eval_output_expr_in_loop(&mut self, loops: &[LoopExpr<'source>]) -> Result { if loops.is_empty() { let (key_expr, output_expr) = self.get_exprs_from_context()?; match (key_expr, output_expr) { (Some(ke), Some(oe)) => { let key = self.eval_expr(ke)?; let value = self.eval_expr(oe)?; let ctx = self.contexts.last_mut().unwrap(); if key != Value::Undefined && value != Value::Undefined { let map = ctx.value.as_object_mut()?; match map.get(&key) { Some(pv) if *pv != value => { let span = ke.span(); return Err(span.source.error( span.line, span.col, format!( "value for key `{}` generated multiple times: `{}` and `{}`", serde_json::to_string_pretty(&key)?, serde_json::to_string_pretty(&pv)?, serde_json::to_string_pretty(&value)?, ) .as_str(), )); } _ => map.insert(key, value), }; } else { match &ctx.value { Value::Object(_) => (), _ => ctx.value = Value::Undefined, } }; } (None, Some(oe)) => { let output = self.eval_expr(oe)?; let ctx = self.contexts.last_mut().unwrap(); if output != Value::Undefined { match &mut ctx.value { Value::Array(a) => { Rc::make_mut(a).push(output); } Value::Set(ref mut s) => { Rc::make_mut(s).insert(output); } _ => bail!("internal error: invalid context value"), } } else { match &ctx.value { Value::Set(_) => (), _ => ctx.value = Value::Undefined, } } } // No output expression. // TODO: should we just push a Bool(true)? _ => (), } // If a query snippet is being run, gather results. let ctx = self.contexts.last_mut().expect("no current context"); if let Some(result) = &ctx.result { let mut result = result.clone(); if let Some(scope) = self.scopes.last() { for (name, value) in scope.iter() { result .bindings .as_object_mut()? .insert(Value::String(name.to_string()), value.clone()); } } ctx.results.result.push(result); } return Ok(true); } // Try out values in current loop expr. let loop_expr = &loops[0]; let mut result = false; match self.eval_expr(loop_expr.value)? { Value::Array(items) => { for v in items.iter() { self.loop_var_values.insert(loop_expr.expr, v.clone()); result = self.eval_output_expr_in_loop(&loops[1..])? || result; } } Value::Set(items) => { for v in items.iter() { self.loop_var_values.insert(loop_expr.expr, v.clone()); result = self.eval_output_expr_in_loop(&loops[1..])? || result; } } Value::Object(obj) => { for (_, v) in obj.iter() { self.loop_var_values.insert(loop_expr.expr, v.clone()); result = self.eval_output_expr_in_loop(&loops[1..])? || result; } } _ => { return Err(loop_expr.span.source.error( loop_expr.span.line, loop_expr.span.col, "item cannot be indexed", )); } } self.loop_var_values.remove(loop_expr.expr); Ok(result) } fn get_current_context(&self) -> Result<&Context<'source>> { match self.contexts.last() { Some(ctx) => Ok(ctx), _ => bail!("internal error: no active context found"), } } fn get_exprs_from_context( &self, ) -> Result<( Option<&'source Expr<'source>>, Option<&'source Expr<'source>>, )> { let ctx = self.get_current_context()?; Ok((ctx.key_expr, ctx.output_expr)) } fn eval_output_expr(&mut self) -> Result { // Evaluate output expression after all the statements have been executed. let (key_expr, output_expr) = self.get_exprs_from_context()?; let mut loops = vec![]; if let Some(ke) = &key_expr { self.hoist_loops_impl(ke, &mut loops); } if let Some(oe) = &output_expr { self.hoist_loops_impl(oe, &mut loops); } self.eval_output_expr_in_loop(&loops[..])?; let ctx = self.get_current_context()?; if let Some(_oe) = ctx.output_expr { // Ensure that at least one output was generated. Ok(ctx.value != Value::Undefined) } else { Ok(true) } } fn eval_stmts(&mut self, stmts: &[&'source LiteralStmt<'source>]) -> Result { let mut result = true; for (idx, stmt) in stmts.iter().enumerate() { if !result { break; } let loop_exprs = self.hoist_loops(&stmt.literal); if !loop_exprs.is_empty() { // If there are hoisted loop expressions, execute subsequent statements // within loops. return self.eval_stmts_in_loop(&stmts[idx..], &loop_exprs[..]); } result = self.eval_stmt(stmt, &stmts[idx + 1..])?; if matches!(&stmt.literal, Literal::SomeIn { .. }) { return Ok(result); } } if result { result = self.eval_output_expr()?; } Ok(result) } fn eval_query(&mut self, query: &'source Query<'source>) -> Result { // Execute the query in a new scope self.scopes.push(Scope::new()); let ordered_stmts: Vec<&'source LiteralStmt<'source>> = if let Some(schedule) = &self.schedule { match schedule.order.get(query) { Some(ord) => ord.iter().map(|i| &query.stmts[*i as usize]).collect(), // TODO _ => bail!(query .span .error("statements not scheduled in query {query:?}")), } } else { query.stmts.iter().collect() }; let r = self.eval_stmts(&ordered_stmts); self.scopes.pop(); r } fn eval_array(&mut self, items: &'source Vec>) -> Result { let mut array = Vec::new(); for item in items { let term = self.eval_expr(item)?; if term == Value::Undefined { return Ok(Value::Undefined); } array.push(term); } Ok(Value::from_array(array)) } fn eval_object(&mut self, fields: &'source Vec<(Span, Expr, Expr)>) -> Result { let mut object = BTreeMap::new(); for (_, key, value) in fields { // TODO: check this // While the grammar defines a object-item as // ( scalar | ref | var ) ":" term, the OPA // implementation is more like expr ":" expr let key = self.eval_expr(key)?; let value = self.eval_expr(value)?; object.insert(key, value); } Ok(Value::from_map(object)) } fn eval_set(&mut self, items: &'source Vec>) -> Result { let mut set = BTreeSet::new(); for item in items { let term = self.eval_expr(item)?; if term == Value::Undefined { return Ok(Value::Undefined); } set.insert(term); } Ok(Value::from_set(set)) } fn eval_membership( &mut self, key: &'source Option>, value: &'source Expr<'source>, collection: &'source Expr<'source>, ) -> Result { let value = self.eval_expr(value)?; let collection = self.eval_expr(collection)?; let result = match &collection { Value::Array(array) => { if let Some(key) = key { let key = self.eval_expr(key)?; collection[&key] == value } else { array.iter().any(|item| *item == value) } } Value::Object(object) => { if let Some(key) = key { let key = self.eval_expr(key)?; collection[&key] == value } else { object.values().any(|item| *item == value) } } Value::Set(set) => { if key.is_some() { false } else { set.contains(&value) } } _ => { false //bail!(collection_expr.span().error("collection must be array, object or set")); } }; Ok(Value::Bool(result)) } fn eval_array_compr( &mut self, term: &'source Expr<'source>, query: &'source Query<'source>, ) -> Result { // Push new context self.contexts.push(Context { key_expr: None, output_expr: Some(term), value: Value::new_array(), result: None, results: QueryResults::default(), }); // Evaluate body first. self.eval_query(query)?; match self.contexts.pop() { Some(ctx) => Ok(ctx.value), None => bail!("internal error: context already popped"), } } fn eval_set_compr( &mut self, term: &'source Expr<'source>, query: &'source Query<'source>, ) -> Result { // Push new context self.contexts.push(Context { key_expr: None, output_expr: Some(term), value: Value::new_set(), result: None, results: QueryResults::default(), }); self.eval_query(query)?; match self.contexts.pop() { Some(ctx) => Ok(ctx.value), None => bail!("internal error: context already popped"), } } fn eval_object_compr( &mut self, key: &'source Expr<'source>, value: &'source Expr<'source>, query: &'source Query<'source>, ) -> Result { // Push new context self.contexts.push(Context { key_expr: Some(key), output_expr: Some(value), value: Value::new_object(), result: None, results: QueryResults::default(), }); self.eval_query(query)?; match self.contexts.pop() { Some(ctx) => Ok(ctx.value), None => bail!("internal error: context already popped"), } } fn lookup_function(&self, fcn: &'source Expr<'source>) -> Result<&Vec<&'source Rule<'source>>> { let mut path = Self::get_path_string(fcn, None)?; if !path.starts_with("data.") { path = self.current_module_path.clone() + "." + &path; } match self.functions.get(&path) { Some(r) => Ok(r), _ => { bail!(fcn.span().error("function not found")) } } } fn eval_builtin_call( &mut self, span: &'source Span<'source>, name: String, builtin: builtins::BuiltinFcn, params: &'source [Expr<'source>], ) -> Result { let mut args = vec![]; let allow_undefined = name == "print"; // TODO: with modifier for p in params { match self.eval_expr(p)? { // If any argument is undefined, then the call is undefined. Value::Undefined if !allow_undefined => return Ok(Value::Undefined), p => args.push(p), } } let cache = builtins::must_cache(name.as_str()); if let Some(name) = &cache { if let Some(v) = self.builtins_cache.get(&(name, args.clone())) { return Ok(v.clone()); } } let v = builtin.0(span, params, &args[..])?; // Handle trace function. // TODO: with modifier. if let (Some(traces), Value::String(msg)) = (&mut self.traces, &v) { traces.push(msg.clone()); return Ok(Value::Bool(true)); }; if let Some(name) = cache { self.builtins_cache.insert((name, args), v.clone()); } Ok(v) } fn eval_call_impl( &mut self, span: &'source Span<'source>, fcn: &'source Expr<'source>, params: &'source [Expr<'source>], ) -> Result { let fcns_rules = match self.lookup_function(fcn) { Ok(r) => r, _ => { // Look up builtin function. // TODO: handle with modifier if let Ok(path) = Self::get_path_string(fcn, None) { if let Some(builtin) = builtins::BUILTINS.get(path.as_str()) { return self.eval_builtin_call(span, path, *builtin, params); } } return Err(span .source .error(span.line, span.col, "could not find function")); } }; let fcns = fcns_rules.clone(); let mut results: Vec = Vec::new(); let mut errors: Vec = Vec::new(); for fcn_rule in fcns { let (args, output_expr, bodies) = match fcn_rule { Rule::Spec { head: RuleHead::Func { args, assign, .. }, bodies, .. } => (args, assign.as_ref().map(|a| &a.value), bodies), _ => bail!("internal error not a function"), }; if args.len() != params.len() { return Err(span.source.error( span.line, span.col, format!( "mismatch in number of arguments. supplied {}, expected {}", params.len(), args.len() ) .as_str(), )); } let mut args_scope = Scope::new(); for (idx, a) in args.iter().enumerate() { let a = match a { Expr::Var(s) => s.text(), _ => unimplemented!("destructuring function arguments"), }; //TODO: check call in params args_scope.insert(a.to_string(), self.eval_expr(¶ms[idx])?); } let ctx = Context { key_expr: None, output_expr, value: Value::new_set(), result: None, results: QueryResults::default(), }; // Back up local variables of current function and empty // the local variables of callee function. let scopes = std::mem::take(&mut self.scopes); // Set the arguments scope. self.scopes.push(args_scope); let value = match self.eval_rule_bodies(ctx, span, bodies) { Ok(v) => v, Err(e) => { // If the rule produces an error, save the error. errors.push(e); self.scopes = scopes; continue; } }; let result = match &value { Value::Set(s) if s.len() == 1 => s.iter().next().unwrap().clone(), Value::Set(s) if !s.is_empty() => { return Err(span.source.error( span.line, span.col, format!("function produced multiple outputs {value:?}").as_str(), )) } // If the function successfully executed, but did not return any value, then return true. Value::Set(s) if s.is_empty() && output_expr.is_none() => Value::Bool(true), // If the function execution resulted in undefined, then propagate it. Value::Undefined => Value::Undefined, _ => bail!("internal error: function did not return set {value:?}"), }; // Restore local variables for current context. self.scopes = scopes; if result != Value::Undefined { results.push(result); } } if results.is_empty() { if errors.is_empty() { return Ok(Value::Undefined); } else { return Err(anyhow!(errors[0].to_string())); } } // all defined values should be the equal to the same value that should be returned if !results.windows(2).all(|w| w[0] == w[1]) { return Err(span.source.error( span.line, span.col, "functions must not produce multiple outputs for same inputs", )); } Ok(results[0].clone()) } fn eval_call( &mut self, span: &'source Span<'source>, fcn: &'source Expr<'source>, params: &'source Vec>, extra_arg: Option<&'source Expr<'source>>, allow_return_arg: bool, ) -> Result { // TODO: global var check; interop with `some var` match extra_arg { Some(Expr::Var(var)) if allow_return_arg && self.lookup_local_var(var.text()).is_none() => { let value = self.eval_call_impl(span, fcn, ¶ms[..params.len() - 1])?; if var.text() != "_" { self.add_variable(var.text(), value)?; } Ok(Value::Bool(true)) } Some(expr) => { let ret_value = self.eval_call_impl(span, fcn, ¶ms[..params.len() - 1])?; let value = self.eval_expr(expr)?; Ok(Value::Bool(ret_value == value)) } None => self.eval_call_impl(span, fcn, params), } } fn lookup_local_var(&self, name: &str) -> Option { // Lookup local variables and arguments. for scope in self.scopes.iter().rev() { if let Some(v) = scope.get(name) { return Some(v.clone()); } } None } fn ensure_rule_evaluated(&mut self, path: String) -> Result<()> { if let Some(rules) = self.rules.get(&path) { for r in rules.clone() { if !self.processed.contains(r) { let module = self.get_rule_module(r)?; self.eval_rule(module, r)?; } } } // Evaluate the associated default rules after non-default rules if let Some(rules) = self.default_rules.get(&path) { for (r, _) in rules.clone() { if !self.processed.contains(r) { let module = self.get_rule_module(r)?; let prev_module = self.set_current_module(Some(module))?; self.eval_default_rule(r)?; self.set_current_module(prev_module)?; } } } Ok(()) } fn lookup_var(&mut self, span: &'source Span<'source>, fields: &[&str]) -> Result { let name = span.text(); // Return local variable/argument. if let Some(v) = self.lookup_local_var(name) { return Ok(Self::get_value_chained(v, fields)); } // Handle input. if name == "input" { return Ok(Self::get_value_chained(self.input.clone(), fields)); } // TODO: should we return before checking for input? if self.no_rules_lookup { return Err(span.error("undefined var")); } // Ensure that rules are evaluated if name == "data" { // Evaluate rule corresponding to longest matching path. for i in (1..fields.len() + 1).rev() { let path = "data.".to_owned() + &fields[0..i].join("."); if self.rules.get(&path).is_some() || self.default_rules.get(&path).is_some() { self.ensure_rule_evaluated(path)?; break; } } Ok(Self::get_value_chained(self.data.clone(), fields)) } else if !self.modules.is_empty() { // Add module prefix and ensure that any matching rule is evaluated. let module_path = Self::get_path_string(&self.current_module()?.package.refr, Some("data"))?; let path = module_path + "." + name; self.ensure_rule_evaluated(path)?; let mut path: Vec<&str> = Parser::get_path_ref_components(&self.module.unwrap().package.refr)? .iter() .map(|s| s.text()) .collect(); path.push(name); let value = Self::get_value_chained(self.data.clone(), &path[..]); Ok(Self::get_value_chained(value, fields)) } else { Ok(Value::Undefined) } } fn eval_expr(&mut self, expr: &'source Expr<'source>) -> Result { match expr { Expr::Null(_) => Ok(Value::Null), Expr::True(_) => Ok(Value::Bool(true)), Expr::False(_) => Ok(Value::Bool(false)), Expr::Number(span) => match serde_json::from_str::(span.text()) { Ok(v) => Ok(v), Err(e) => Err(span.source.error( span.line, span.col, format!("could not parse number. {e}").as_str(), )), }, // TODO: Handle string vs rawstring Expr::String(span) => Ok(Value::String(span.text().to_string())), Expr::RawString(span) => Ok(Value::String(span.text().to_string())), // TODO: Handle undefined variables Expr::Var(_) => self.eval_chained_ref_dot_or_brack(expr), Expr::RefDot { .. } => self.eval_chained_ref_dot_or_brack(expr), Expr::RefBrack { .. } => self.eval_chained_ref_dot_or_brack(expr), // Expressions with operators Expr::ArithExpr { op, lhs, rhs, .. } => self.eval_arith_expr(op, lhs, rhs), Expr::AssignExpr { op, lhs, rhs, .. } => self.eval_assign_expr(op, lhs, rhs), Expr::BinExpr { op, lhs, rhs, .. } => self.eval_bin_expr(op, lhs, rhs), Expr::BoolExpr { op, lhs, rhs, .. } => self.eval_bool_expr(op, lhs, rhs), Expr::Membership { key, value, collection, .. } => self.eval_membership(key, value, collection), // Creation expression Expr::Array { items, .. } => self.eval_array(items), Expr::Object { fields, .. } => self.eval_object(fields), Expr::Set { items, .. } => self.eval_set(items), // Comprehensions Expr::ArrayCompr { term, query, .. } => self.eval_array_compr(term, query), Expr::ObjectCompr { key, value, query, .. } => self.eval_object_compr(key, value, query), Expr::SetCompr { term, query, .. } => self.eval_set_compr(term, query), Expr::UnaryExpr { .. } => unimplemented!("unar expr is umplemented"), Expr::Call { span, fcn, params } => self.eval_call(span, fcn, params, None, false), } } fn make_rule_context( &self, head: &'source RuleHead<'source>, ) -> Result<(Context<'source>, Vec>)> { //TODO: include "data" ? let mut path = Parser::get_path_ref_components(&self.module.unwrap().package.refr)?; match head { RuleHead::Compr { refr, assign, .. } => { let output_expr = assign.as_ref().map(|assign| &assign.value); let (refr, key_expr, value) = match refr { Expr::RefBrack { refr, index, .. } => { (refr.as_ref(), Some(index.as_ref()), Value::new_object()) } _ => (refr, None, Value::new_array()), }; Parser::get_path_ref_components_into(refr, &mut path)?; Ok(( Context { key_expr, output_expr, value, result: None, results: QueryResults::default(), }, path, )) } RuleHead::Set { refr, key, .. } => { Parser::get_path_ref_components_into(refr, &mut path)?; Ok(( Context { key_expr: None, output_expr: key.as_ref(), value: Value::new_set(), result: None, results: QueryResults::default(), }, path, )) } _ => unimplemented!("unhandled rule ref type"), } } fn get_rule_module(&self, rule: &'source Rule<'source>) -> Result<&'source Module<'source>> { for m in &self.modules { if m.policy.contains(rule) { return Ok(m); } } bail!("internal error: could not find module for rule"); } fn eval_rule_bodies( &mut self, ctx: Context<'source>, span: &'source Span<'source>, bodies: &'source Vec>, ) -> Result { let n_scopes = self.scopes.len(); let result = if bodies.is_empty() { self.contexts.push(ctx.clone()); self.eval_output_expr() } else { let mut result = Ok(true); for body in bodies { self.contexts.push(ctx.clone()); result = self.eval_query(&body.query); if matches!(&result, Ok(true) | Err(_)) { break; } // TODO: Manage other scoped data. if bodies.len() > 1 { unimplemented!("else bodies"); } } result }; let ctx = match self.contexts.pop() { Some(ctx) => ctx, _ => bail!("internal error: rule's context already popped"), }; let result = match result { Ok(r) => r, Err(e) => return Err(e), }; assert_eq!(self.scopes.len(), n_scopes); Ok(match result { true => match &ctx.value { Value::Object(_) => ctx.value, Value::Array(a) if a.len() == 1 => a[0].clone(), Value::Array(a) if a.is_empty() => Value::Bool(true), Value::Array(_) => { return Err(span.source.error( span.line, span.col, "complete rules should not produce multiple outputs", )) } Value::Set(_) => ctx.value, _ => unimplemented!("todo fix this: ctx.value = {:?}", ctx.value), }, false => Value::Undefined, }) } fn get_value_chained(mut obj: Value, path: &[&str]) -> Value { for p in path { obj = obj[&Value::String(p.to_string())].clone(); } obj } #[inline] fn make_or_get_value_mut<'a>(obj: &'a mut Value, paths: &[&str]) -> Result<&'a mut Value> { if paths.is_empty() { return Ok(obj); } let key = Value::String(paths[0].to_owned()); if obj == &Value::Undefined { *obj = Value::new_object(); } if let Value::Object(map) = obj { if map.get(&key).is_none() { Rc::make_mut(map).insert(key.clone(), Value::Undefined); } } match obj { Value::Object(map) => match Rc::make_mut(map).get_mut(&key) { Some(v) if paths.len() == 1 => Ok(v), Some(v) => Self::make_or_get_value_mut(v, &paths[1..]), _ => bail!("internal error: unexpected"), }, Value::Undefined if paths.len() > 1 => { *obj = Value::new_object(); Self::make_or_get_value_mut(obj, paths) } Value::Undefined => Ok(obj), _ => bail!("internal error: make: not an object {obj:?}"), } } pub fn merge_value(span: &Span<'source>, value: &mut Value, new: Value) -> Result<()> { match value.merge(new) { Ok(()) => Ok(()), Err(err) => return Err(span.error(format!("{err}").as_str())), } } pub fn get_path_string(refr: &Expr, document: Option<&str>) -> Result { let mut comps = vec![]; let mut expr = Some(refr); while expr.is_some() { match expr { Some(Expr::RefDot { refr, field, .. }) => { comps.push(field.text()); expr = Some(refr); } Some(Expr::RefBrack { refr, index, .. }) if matches!(index.as_ref(), Expr::String(_)) => { if let Expr::String(s) = index.as_ref() { comps.push(s.text()); expr = Some(refr); } } Some(Expr::Var(v)) => { comps.push(v.text()); expr = None; } _ => bail!("internal error: not a simple ref"), } } if let Some(d) = document { comps.push(d); }; comps.reverse(); Ok(comps.join(".")) } fn set_current_module( &mut self, module: Option<&'source Module<'source>>, ) -> Result>> { let m = self.module; if let Some(m) = module { self.current_module_path = Self::get_path_string(&m.package.refr, Some("data"))?; } self.module = module; Ok(m) } pub fn update_function_table(&mut self) -> Result<()> { for module in self.modules.clone() { let prev_module = self.set_current_module(Some(module))?; let module_path = Self::get_path_string(&self.current_module()?.package.refr, Some("data"))?; for rule in &module.policy { if let Rule::Spec { head: RuleHead::Func { refr, .. }, .. } = rule { let mut path = Parser::get_path_ref_components(&self.current_module()?.package.refr)?; Parser::get_path_ref_components_into(refr, &mut path)?; let path: Vec<&str> = path.iter().map(|s| s.text()).collect(); if path.len() > 1 { let value = Self::make_or_get_value_mut(&mut self.data, &path[0..path.len() - 1])?; if value == &Value::Undefined { *value = Value::new_object(); } } let full_path = Self::get_path_string(refr, Some(module_path.as_str()))?; if let Some(functions) = self.functions.get_mut(&full_path) { functions.push(rule); } else { self.functions.insert(full_path, vec![rule]); } } } self.set_current_module(prev_module)?; } Ok(()) } fn get_rule_refr(rule: &'source Rule<'source>) -> &'source Expr<'source> { match rule { Rule::Spec { head, .. } => match &head { RuleHead::Compr { refr, .. } | RuleHead::Set { refr, .. } | RuleHead::Func { refr, .. } => refr, }, Rule::Default { refr, .. } => refr, } } fn check_default_value(expr: &'source Expr<'source>) -> Result<()> { use Expr::*; let (kind, span) = match expr { // Scalars are supported String(_) | RawString(_) | Number(_) | True(_) | False(_) | Null(_) => return Ok(()), // Uminus of number is treated as a single expression, UnaryExpr { expr, .. } if matches!(expr.as_ref(), Number(_)) => return Ok(()), // Comprehensions are supported since they won't evaluate to undefined. ArrayCompr { .. } | SetCompr { .. } | ObjectCompr { .. } => return Ok(()), // Check each item in array/set. Array { items, .. } | Set { items, .. } => { for item in items { Self::check_default_value(item)?; } return Ok(()); } // Check each field in object Object { fields, .. } => { for (_, key, value) in fields { Self::check_default_value(key)?; Self::check_default_value(value)?; } return Ok(()); } // The following may evaluate to undefined. Var(span) => ("var", span), Call { span, .. } => ("call", span), UnaryExpr { span, .. } => ("unaryexpr", span), RefDot { span, .. } => ("ref", span), RefBrack { span, .. } => ("ref", span), BinExpr { span, .. } => ("binexpr", span), BoolExpr { span, .. } => ("boolexpr", span), ArithExpr { span, .. } => ("arithexpr", span), AssignExpr { span, .. } => ("assignexpr", span), Membership { span, .. } => ("membership", span), }; Err(span.error(format!("invalid `{kind}` in default value").as_str())) } fn check_default_rules(&self) -> Result<()> { for module in &self.modules { for rule in &module.policy { if let Rule::Default { value, .. } = rule { Self::check_default_value(value)?; } } } Ok(()) } fn eval_default_rule(&mut self, rule: &'source Rule<'source>) -> Result<()> { // Skip reprocessing rule. if self.processed.contains(rule) { return Ok(()); } if let Rule::Default { span, refr, value, .. } = rule { let mut path = Parser::get_path_ref_components(&self.module.unwrap().package.refr)?; let (refr, index) = match refr { Expr::RefBrack { refr, index, .. } => (refr.as_ref(), Some(index.as_ref())), Expr::Var(_) => (refr, None), _ => bail!(refr.span().error(&format!( "invalid token {:?} with the default keyword", refr ))), }; Parser::get_path_ref_components_into(refr, &mut path)?; let paths: Vec<&str> = path.iter().map(|s| s.text()).collect(); Self::check_default_value(value)?; let value = self.eval_expr(value)?; // Assume at this point that all the non-default rules have been evaluated. // Merge the default value only if // 1. The corresponding variable does not have value yet // 2. The corresponding index in the object does not have value yet if let Some(index) = index { let index = self.eval_expr(index)?; let mut object = Value::new_object(); object.as_object_mut()?.insert(index.clone(), value); let vref = Self::make_or_get_value_mut(&mut self.data, &paths)?; if let Value::Object(btree) = &vref { if !btree.contains_key(&index) { Self::merge_value(span, vref, object)?; } } else if let Value::Undefined = vref { Self::merge_value(span, vref, object)?; } } else { let vref = Self::make_or_get_value_mut(&mut self.data, &paths)?; if let Value::Undefined = &vref { Self::merge_value(span, vref, value)?; } }; self.processed.insert(rule); } Ok(()) } fn eval_rule( &mut self, module: &'source Module<'source>, rule: &'source Rule<'source>, ) -> Result<()> { // Skip reprocessing rule if self.processed.contains(rule) { return Ok(()); } // Skip default rules if let Rule::Default { .. } = rule { return Ok(()); } self.active_rules.push(rule); if self.active_rules.iter().filter(|&r| r == &rule).count() == 2 { let mut msg = String::default(); for r in &self.active_rules { let refr = Self::get_rule_refr(r); let span = refr.span(); msg += span .source .message(span.line, span.col, "depends on", "") .as_str(); } msg += "cyclic evaluation"; let refr = Self::get_rule_refr(rule); let span = refr.span(); return Err(span.source.error( span.line, span.col, format!("recursion detected when evaluating rule:{msg}").as_str(), )); } let prev_module = self.set_current_module(Some(module))?; match rule { Rule::Spec { span, head: rule_head, bodies: rule_body, } => { if !matches!(rule_head, RuleHead::Func { .. }) { let (ctx, mut path) = self.make_rule_context(rule_head)?; let special_set = matches!((ctx.output_expr, &ctx.value), (None, Value::Set(_))); let value = match self.eval_rule_bodies(ctx, span, rule_body)? { Value::Set(_) if special_set => { let entry = path[path.len() - 1].text(); let mut s = BTreeSet::new(); s.insert(Value::String(entry.to_owned())); path = path[0..path.len() - 1].to_vec(); Value::from_set(s) } v => v, }; if value != Value::Undefined { let paths: Vec<&str> = path.iter().map(|s| s.text()).collect(); let vref = Self::make_or_get_value_mut(&mut self.data, &paths[..])?; Self::merge_value(span, vref, value)?; } self.processed.insert(rule); } } _ => bail!("internal error: unexpected"), } self.set_current_module(prev_module)?; match self.active_rules.pop() { Some(r) if r == rule => Ok(()), _ => bail!("internal error: current rule not active"), } } pub fn eval_rule_with_input( &mut self, module: &'source Module<'source>, rule: &'source Rule<'source>, input: &Option, enable_tracing: bool, ) -> Result { self.checks_for_eval(input, enable_tracing)?; self.clean_internal_evaluation_state(); self.eval_rule(module, rule)?; Ok(self.data.clone()) } pub fn prepare_for_eval( &mut self, schedule: Option>, data: &Option, ) -> Result<()> { self.schedule = schedule; self.builtins_cache.clear(); if let Some(data) = data { self.data = data.clone(); } // Ensure that each module has an empty object for m in &self.modules { let path = Parser::get_path_ref_components(&m.package.refr)?; let path: Vec<&str> = path.iter().map(|s| s.text()).collect(); let vref = Self::make_or_get_value_mut(&mut self.data, &path[..])?; if *vref == Value::Undefined { *vref = Value::new_object(); } } self.check_default_rules()?; self.update_function_table()?; self.gather_rules()?; self.init_data = self.data.clone(); self.prepared = true; Ok(()) } pub fn eval_module( &mut self, module: &'source Module<'source>, input: &Option, enable_tracing: bool, ) -> Result { self.checks_for_eval(input, enable_tracing)?; self.clean_internal_evaluation_state(); for rule in &module.policy { self.eval_rule(module, rule)?; } // Defer the evaluation of the default rules to here let prev_module = self.set_current_module(Some(module))?; for rule in &module.policy { self.eval_default_rule(rule)?; } self.set_current_module(prev_module)?; Ok(self.data.clone()) } pub fn eval_modules(&mut self, input: &Option, enable_tracing: bool) -> Result { self.checks_for_eval(input, enable_tracing)?; self.clean_internal_evaluation_state(); for module in self.modules.clone() { for rule in &module.policy { self.eval_rule(module, rule)?; } } // Defer the evaluation of the default rules to here for module in self.modules.clone() { let prev_module = self.set_current_module(Some(module))?; for rule in &module.policy { self.eval_default_rule(rule)?; } self.set_current_module(prev_module)?; } Ok(self.data.clone()) } pub fn eval( &mut self, data: &Option, input: &Option, enable_tracing: bool, schedule: Option>, ) -> Result { self.prepare_for_eval(schedule, data)?; self.eval_modules(input, enable_tracing) } pub fn eval_user_query( &mut self, query: &'source Query<'source>, schedule: &Schedule<'source>, enable_tracing: bool, ) -> Result { self.traces = match enable_tracing { true => Some(vec![]), false => None, }; // Add schedules for queries. if let Some(self_schedule) = &mut self.schedule { for (k, v) in schedule.order.iter() { self_schedule.order.insert(k, v.clone()); } } // Push new context. self.contexts.push(Context { key_expr: None, output_expr: None, value: Value::new_set(), // Request that results be gathered. result: Some(QueryResult::default()), results: QueryResults::default(), }); let prev_module = self.set_current_module(self.modules.last().copied())?; // Eval the query. let query_r = self.eval_query(query); let mut results = match self.contexts.pop() { Some(ctx) => ctx.results, _ => bail!("internal error: no context"), }; // Restore schedules. if let Some(self_schedule) = &mut self.schedule { for (k, ord) in schedule.order.iter() { if k == &query { for idx in 0..results.result.len() { let mut ordered_expressions = vec![Value::Undefined; ord.len()]; for (expr_idx, value) in results.result[idx].expressions.iter().enumerate() { let orig_idx = ord[expr_idx] as usize; ordered_expressions[orig_idx] = value.clone(); } results.result[idx].expressions = ordered_expressions; } } self_schedule.order.remove(k); } } self.set_current_module(prev_module)?; match query_r { Ok(_) => Ok(results), Err(e) => Err(e), } } fn gather_rules(&mut self) -> Result<()> { for module in self.modules.clone() { let prev_module = self.set_current_module(Some(module))?; for rule in &module.policy { let refr = Self::get_rule_refr(rule); if let Rule::Spec { .. } = rule { // Adjust refr to ensure simple ref. // TODO: refactor. let refr = match refr { Expr::RefBrack { index, .. } if matches!(index.as_ref(), Expr::String(_)) => { refr } Expr::RefBrack { refr, .. } => refr, _ => refr, }; let path = Self::get_path_string(refr, None)?; let path = self.current_module_path.clone() + "." + &path; match self.rules.entry(path) { Entry::Occupied(o) => { o.into_mut().push(rule); } Entry::Vacant(v) => { v.insert(vec![rule]); } } } else if let Rule::Default { .. } = rule { let (refr, index) = match refr { // TODO: Validate the index Expr::RefBrack { refr, index, .. } => { if !matches!( index.as_ref(), Expr::True(_) | Expr::False(_) | Expr::Number(_) | Expr::String(_) ) { // OPA's behavior is ignoring the non-scalar index bail!(index.span().error("index is not a scalar value")); } let index = self.eval_expr(index)?; (refr.as_ref(), Some(index.to_string())) } _ => (refr, None), }; let path = Self::get_path_string(refr, None)?; let path = self.current_module_path.clone() + "." + &path; match self.default_rules.entry(path) { Entry::Occupied(o) => { for (_, i) in o.get() { if index.is_some() && i.is_some() { let old = i.as_ref().unwrap(); let new = index.as_ref().unwrap(); if old == new { bail!(refr.span().error("multiple default rules for the variable with the same index")); } } else { bail!(refr .span() .error("conflict type with the default rules")); } } o.into_mut().push((rule, index)); } Entry::Vacant(v) => { v.insert(vec![(rule, index)]); } } } } self.set_current_module(prev_module)?; } Ok(()) } }