// 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::value::*; use anyhow::{anyhow, bail, Result}; use log::info; 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>>, current_module_path: String, input: Value, 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>>, } #[derive(Debug)] struct Variable { value: Value, partial: bool, _has_default: bool, } #[derive(Debug, Clone)] struct Context<'source> { key_expr: Option<&'source Expr<'source>>, output_expr: Option<&'source Expr<'source>>, value: Value, } #[derive(Debug)] struct LoopExpr<'source> { span: &'source Span<'source>, expr: &'source Expr<'source>, value: &'source Expr<'source>, } impl<'source> Interpreter<'source> { pub fn new(modules: Vec<&'source Module<'source>>) -> Result> { Ok(Interpreter { modules, module: None, current_module_path: String::default(), input: Value::new_object(), 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![], }) } fn current_module(&self) -> Result<&'source Module<'source>> { match &self.module { Some(m) => Ok(m), _ => bail!("internal error: current module not set"), } } #[inline(always)] fn add_variable( &mut self, name: &str, partial: bool, default: Option, ) -> Result<(String, Value)> { let name = name.to_string(); // Only add the variable if the key is not "_" let value = if name != "_" { let (value, _has_default) = if let Some(default) = default { (default, true) } else { (Value::Undefined, false) }; let variable = Variable { value: value.clone(), partial, _has_default, }; match self.scopes.last_mut() { Some(scope) => { scope.insert(name.to_string(), variable); } _ => bail!("internal error: no active scope"), } value } else { Value::Undefined }; Ok((name, value)) } fn add_variable_or( &mut self, name: &str, partial: bool, default: Option, ) -> Result<(String, Value, bool)> { for scope in self.scopes.iter().rev() { if let Some(variable) = scope.get(&name.to_string()) { return Ok((name.to_string(), variable.value.clone(), variable.partial)); } } let (name, value) = self.add_variable(name, partial, default)?; Ok((name, value, partial)) } // TODO: optimize this fn variables_assignment(&mut self, name: &str, value: &Value) -> Result<()> { match self.scopes.last_mut() { Some(scope) => { if let Some(variable) = scope.get_mut(name) { variable.value = value.clone(); } else { return Err(anyhow!("variable {} is undefined", name)); } } _ => bail!("internal error: no active scope"), } Ok(()) } 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 { match expr { // Stop path collection upon encountering the leading variable. Expr::Var(v) => { path.reverse(); return self.lookup_var(v.text(), &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_var(&self, ident: &str) -> bool { // TODO: check for vars that are declared using some-vars // TODO: check for vars that are not declared and dont exist in any scope including global. ident == "_" } 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_var(ident.text()) => loops.push(LoopExpr { span, expr, //var: ident.text(), value: refr, }), _ => { // 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, .. } => { self.hoist_loops_impl(key, loops); if let Some(value) = value.as_ref() { 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, .. } => { self.hoist_loops_impl(key, &mut loops); if let Some(value) = value { 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)?; Ok(builtins::compare(op, &lhs, &rhs)) } fn eval_bin_expr( &mut self, op: &BinOp, lhs: &'source Expr<'source>, rhs: &'source Expr<'source>, ) -> Result { let lhs = self.eval_expr(lhs)?; let rhs = self.eval_expr(rhs)?; let lhs = if let Value::Set(set) = lhs { set } else { return Err(anyhow!("expect {:?} to be a set", lhs)); }; let rhs = if let Value::Set(set) = rhs { set } else { return Err(anyhow!("expect {:?} to be a set", rhs)); }; info!( "eval_bin_expr, op: {:?}, lhs: {:?}, rhs: {:?}", op, lhs, rhs ); Ok(Value::from_set(match op { BinOp::Or => lhs.union(&rhs).cloned().collect(), BinOp::And => lhs.intersection(&rhs).cloned().collect(), })) } fn eval_arith_expr( &mut self, op: &ArithOp, lhs: &'source Expr<'source>, rhs: &'source Expr<'source>, ) -> Result { let lhs = self.eval_expr(lhs)?; let rhs = self.eval_expr(rhs)?; // Handle special case for set difference. if let (Value::Set(lhs), ArithOp::Sub, Value::Set(rhs)) = (&lhs, op, &rhs) { return Ok(Value::from_set(lhs.difference(rhs).cloned().collect())); } let lhs = if let Value::Number(number) = lhs { number.0 } else { return Err(anyhow!("expect {:?} to be a number", lhs)); }; let rhs = if let Value::Number(number) = rhs { number.0 } else { return Err(anyhow!("expect {:?} to be a number", rhs)); }; let result = match op { ArithOp::Add => lhs + rhs, ArithOp::Sub => lhs - rhs, ArithOp::Mul => lhs * rhs, ArithOp::Div => lhs / rhs, }; info!( "eval_arith_expr, op: {:?}, lhs: {:?}, rhs: {:?}", op, lhs, rhs ); Ok(Value::Number(Number(result))) } fn eval_assign_expr( &mut self, op: &AssignOp, lhs: &'source Expr<'source>, rhs: &'source Expr<'source>, ) -> Result { let lhs = if let Expr::Var(span) = lhs { span.text() } else { return Err(anyhow!("expect a variable, got: {:?}", lhs)); }; let (_, variable, _) = self.add_variable_or(lhs, false, None)?; let rhs = self.eval_expr(rhs)?; // TODO: handle iterations if variable[0] != Value::Undefined { return Err(anyhow!("Redefinition for variable {:?}", lhs)); } // TODO: optimize this self.variables_assignment(lhs, &rhs)?; info!( "eval_assign_expr before, op: {:?}, lhs: {:?}, rhs: {:?}", op, lhs, rhs ); Ok(Value::Bool(true)) } fn eval_stmt(&mut self, stmt: &'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 { expr, .. } => { let value = self.eval_expr(expr)?; 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::SomeVars { vars, .. } => { for var in vars { let name = var.text(); if let Ok((_, variable, _)) = self.add_variable_or(name, false, None) { if variable != Value::Undefined { return Err(anyhow!( "duplicated definition of local variable {}", name )); } } } true } Literal::SomeIn { key, value, collection, .. } => { let value = self.eval_membership(key, value, collection)?; if let Value::Bool(bool) = value { bool } else { panic!(); } } Literal::NotExpr { expr, .. } => matches!(self.eval_expr(expr)?, Value::Bool(false)), _ => unimplemented!(), }); 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])? { return Ok(false); } self.eval_stmts(&stmts[1..]) } else { self.eval_stmts(stmts) } } else { 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_stmts_in_loop(stmts, &loops[1..])? || result; } } Value::Set(items) => { for v in items.iter() { self.loop_var_values.insert(loop_expr.expr, v.clone()); result = self.eval_stmts_in_loop(stmts, &loops[1..])? || result; } } Value::Object(obj) => { for (_, v) in obj.iter() { self.loop_var_values.insert(loop_expr.expr, v.clone()); result = self.eval_stmts_in_loop(stmts, &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); // 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 { 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 { ctx.value = Value::Undefined; } } // No output expression. // TODO: should we just push a Bool(true)? _ => (), } // Push the context back so that it is available to the caller. // self.contexts.push(ctx); 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)?; } 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 r = self.eval_stmts(&query.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 Expr<'source>, value: &'source Option>, collection: &'source Expr<'source>, ) -> Result { let key = self.eval_expr(key)?; let collection = self.eval_expr(collection)?; let result = match &collection { Value::Array(array) => { if let Some(value) = value { let value = self.eval_expr(value)?; collection[&key] == value } else { array.iter().any(|item| *item == key) } } Value::Object(object) => { if let Some(value) = value { let value = self.eval_expr(value)?; collection[&key] == value } else { object.values().into_iter().any(|item| *item == key) } } Value::Set(set) => { if value.is_some() { false //return Err(anyhow!("key-value pair is not supported for set")); } else { set.contains(&key) } } _ => { return Err(anyhow!("\"{}\" must be array, object, or set", collection)); } }; 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(), }); // 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(), }); 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(), }); 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<&'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!("function not found") } } } fn eval_call( &mut self, span: &'source Span<'source>, fcn: &'source Expr<'source>, params: &'source Vec>, ) -> Result { let fcn_rule = match self.lookup_function(fcn) { Ok(r) => r, _ => { return Err(span .source .error(span.line, span.col, "could not find function")) } }; 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(), Variable { value: self.eval_expr(¶ms[idx])?, partial: false, _has_default: false, }, ); } let ctx = Context { key_expr: None, output_expr, value: Value::new_set(), }; // 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 = self.eval_rule_bodies(ctx, span, bodies)?; let result = match &value { Value::Set(s) if s.len() == 1 => Ok(s.iter().next().unwrap().clone()), Value::Set(s) if !s.is_empty() => 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() => Ok(Value::Bool(true)), // If the function execution resulted in undefined, then propagate it. Value::Undefined => Ok(Value::Undefined), _ => bail!("internal error: function did not return set {value:?}"), }; // Restore local variables for current context. self.scopes = scopes; result } fn get_var_value(&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.value.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, name: &str, fields: &[&str]) -> Result { // Return local variable/argument. if let Some(v) = self.get_var_value(name) { return Ok(Self::get_value_chained(v, fields)); } // Handle input. if name == "input" { return Ok(Self::get_value_chained(self.input.clone(), fields)); } // 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 { // 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)) } } 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 { .. } => match self.loop_var_values.get(expr) { Some(v) => Ok(v.clone()), _ => 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), } } 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, }, 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(), }, 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 mut result = true; self.scopes.push(Scope::new()); if bodies.is_empty() { self.contexts.push(ctx.clone()); result = self.eval_output_expr()?; } else { for body in bodies { self.contexts.push(ctx.clone()); result = self.eval_query(&body.query)?; // The body evaluated successfully. if result { break; } if bodies.len() > 1 { unimplemented!("else bodies"); } } } let ctx = match self.contexts.pop() { Some(ctx) => ctx, _ => bail!("internal error: rule's context already popped"), }; // Drop local variables and leave the local scope self.scopes.pop(); 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"), }, 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!("make: not an object {obj:?}"), } } pub fn merge_value(span: &Span<'source>, value: &mut Value, mut new: Value) -> Result<()> { match (value, &mut new) { (v @ Value::Undefined, _) => *v = new, (Value::Set(ref mut set), Value::Set(new)) => { Rc::make_mut(set).append(Rc::make_mut(new)) } (Value::Object(map), Value::Object(new)) => { for (k, v) in new.iter() { match map.get(k) { Some(pv) if *pv != *v => { return Err(span.source.error( span.line, span.col, format!( "value for key `{}` generated multiple times: `{}` and `{}`", serde_json::to_string_pretty(&k)?, serde_json::to_string_pretty(&pv)?, serde_json::to_string_pretty(&v)?, ) .as_str(), )); } _ => Rc::make_mut(map).insert(k.clone(), v.clone()), }; } } _ => bail!("could not merge value"), }; Ok(()) } 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!("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()))?; self.functions.insert(full_path, 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(&self, 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(()), Var(span) => ("var", span), // 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(()); } // Check each statement in comprehensions ArrayCompr { term, query, .. } | SetCompr { term, query, .. } => { self.check_default_value(term)?; for stmt in &query.stmts { self.check_default_value_in_stmt(stmt)?; } return Ok(()); } ObjectCompr { key, value, query, .. } => { self.check_default_value(key)?; self.check_default_value(value)?; for stmt in &query.stmts { self.check_default_value_in_stmt(stmt)?; } return Ok(()); } 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_value_in_stmt(&self, stmt: &'source LiteralStmt<'source>) -> Result<()> { for m in &stmt.with_mods { self.check_default_value(&m.refr)?; self.check_default_value(&m.r#as)?; } match &stmt.literal { Literal::SomeVars { span, .. } => { Err(span.error("invalid `some vars` in default value")) } Literal::SomeIn { key, value, collection, .. } => { self.check_default_value(key)?; if let Some(value) = &value { self.check_default_value(value)?; } self.check_default_value(collection) } Literal::Expr { expr, .. } | Literal::NotExpr { expr, .. } => { self.check_default_value(expr) } Literal::Every { span, .. } => Err(span.error("invalid `every` in default value")), } } 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(()); } match rule { Rule::Default { span, refr, value, .. } => { 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!("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); } _ => println!("not a default 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 { .. }) { return Ok(()); } 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)?; } } _ => bail!("internal error: unexpected"), } self.set_current_module(prev_module)?; self.processed.insert(rule); match self.active_rules.pop() { Some(r) if r == rule => Ok(()), _ => bail!("internal error: current rule not active"), } } pub fn eval(&mut self, data: &Option, input: &Option) -> Result { if let Some(input) = input { self.input = input.clone(); info!("input: {:#?}", self.input); } if let Some(data) = data { self.data = data.clone(); } self.check_default_rules()?; self.update_function_table()?; self.gather_rules()?; 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_query_snippet(&mut self, snippet: &'source Expr<'source>) -> Result { // Create a new scope for evaluating the expression. self.scopes.push(Scope::new()); let prev_module = self.set_current_module(self.modules.last().copied())?; let value = self.eval_expr(snippet)?; // Pop the scope. let scope = self.scopes.pop(); let r = match snippet { Expr::AssignExpr { .. } => { if let Some(scope) = scope { let mut r = Value::new_object(); let map = r.as_object_mut()?; // Capture each binding. for (name, v) in scope { map.insert(Value::String(name), v.value); } Ok(r) } else { bail!("internal error: expression scope not found"); } } _ => Ok(value), }; self.set_current_module(prev_module)?; r } 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 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!("multiple default rules for the variable with the same index"); } } else { bail!("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(()) } }