mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
More OPA conformance; in-progress: ability to trace interpreter (#63)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
This commit is contained in:
committed by
GitHub
parent
73ee18f002
commit
577e1aa8db
@@ -73,6 +73,7 @@ fn cast_array(span: &Span, params: &[Ref<Expr>], args: &[Value], strict: bool) -
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
match &args[0] {
|
||||
Value::Array(_) => Ok(args[0].clone()),
|
||||
Value::Set(s) => Ok(Value::from_array(s.iter().cloned().collect())),
|
||||
_ if strict => bail!(params[0].span().error("array required")),
|
||||
_ => Ok(Value::Undefined),
|
||||
}
|
||||
@@ -113,6 +114,7 @@ fn cast_set(span: &Span, params: &[Ref<Expr>], args: &[Value], strict: bool) ->
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
match &args[0] {
|
||||
Value::Set(_) => Ok(args[0].clone()),
|
||||
Value::Array(a) => Ok(Value::from_set(a.iter().cloned().collect())),
|
||||
_ if strict => bail!(params[0].span().error("set required")),
|
||||
_ => Ok(Value::Undefined),
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
|
||||
m.insert("ceil", (ceil, 1));
|
||||
m.insert("floor", (floor, 1));
|
||||
m.insert("numbers.range", (range, 2));
|
||||
m.insert("numbers.range_step", (range_step, 3));
|
||||
m.insert("rand.intn", (intn, 2));
|
||||
m.insert("round", (round, 1));
|
||||
}
|
||||
@@ -29,6 +30,7 @@ pub fn arithmetic_operation(
|
||||
expr2: &Expr,
|
||||
v1: Value,
|
||||
v2: Value,
|
||||
strict: bool,
|
||||
) -> Result<Value> {
|
||||
let op_name = format!("{:?}", op).to_lowercase();
|
||||
let v1 = ensure_numeric(op_name.as_str(), expr1, &v1)?;
|
||||
@@ -38,9 +40,11 @@ pub fn arithmetic_operation(
|
||||
ArithOp::Add => v1.add(&v2)?,
|
||||
ArithOp::Sub => v1.sub(&v2)?,
|
||||
ArithOp::Mul => v1.mul(&v2)?,
|
||||
ArithOp::Div if v2 == Number::from(0u64) => bail!(span.error("divide by zero")),
|
||||
ArithOp::Div if strict && v2 == Number::from(0u64) => bail!(span.error("divide by zero")),
|
||||
ArithOp::Div if v2 == Number::from(0u64) => return Ok(Value::Undefined),
|
||||
ArithOp::Div => v1.divide(&v2)?,
|
||||
ArithOp::Mod if v2 == Number::from(0u64) => bail!(span.error("modulo by zero")),
|
||||
ArithOp::Mod if strict && v2 == Number::from(0u64) => bail!(span.error("modulo by zero")),
|
||||
ArithOp::Mod if v2 == Number::from(0u64) => return Ok(Value::Undefined),
|
||||
ArithOp::Mod if !v1.is_integer() || !v2.is_integer() => {
|
||||
bail!(span.error("modulo on floating-point number"))
|
||||
}
|
||||
@@ -69,25 +73,33 @@ fn floor(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Re
|
||||
))
|
||||
}
|
||||
|
||||
fn range(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
ensure_args_count(span, "numbers.range", params, args, 2)?;
|
||||
let v1 = ensure_numeric("numbers.range", ¶ms[0], &args[0].clone())?;
|
||||
let v2 = ensure_numeric("numbers.range", ¶ms[1], &args[1].clone())?;
|
||||
fn range(span: &Span, params: &[Ref<Expr>], args: &[Value], strict: bool) -> Result<Value> {
|
||||
let name = "numbers.range";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
let v1 = ensure_numeric(name, ¶ms[0], &args[0].clone())?;
|
||||
let v2 = ensure_numeric(name, ¶ms[1], &args[1].clone())?;
|
||||
|
||||
let (incr, num_elements) = match (v1.as_i64(), v2.as_i64()) {
|
||||
(Some(v1), Some(v2)) if v2 > v1 => (1, v2 + 1 - v1),
|
||||
(Some(v1), Some(v2)) => (-1, v1 + 1 - v2),
|
||||
_ => {
|
||||
// TODO: OPA returns undefined here.
|
||||
// Can we emit a warning?
|
||||
return Ok(Value::Undefined);
|
||||
}
|
||||
match v1.is_integer() {
|
||||
false if strict => bail!(params[0].span().error("operand must be integer")),
|
||||
false => return Ok(Value::Undefined),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
match v2.is_integer() {
|
||||
false if strict => bail!(params[1].span().error("operand must be integer")),
|
||||
false => return Ok(Value::Undefined),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let (incr, num_elements) = match v2.sub(&v1)?.as_i64() {
|
||||
Some(v) if v >= 0 => (1i64, v as usize + 1),
|
||||
Some(v) => (-1i64, (-v + 1) as usize),
|
||||
_ => bail!(span.error("could not determine number of elements")),
|
||||
};
|
||||
|
||||
let mut values = Vec::with_capacity(num_elements as usize);
|
||||
|
||||
let mut values = Vec::with_capacity(num_elements);
|
||||
let mut v = v1;
|
||||
let incr = Number::from(incr as i64);
|
||||
let incr = Number::from(incr);
|
||||
while v != v2 {
|
||||
values.push(Value::from(v.clone()));
|
||||
v.add_assign(&incr)?;
|
||||
@@ -96,10 +108,51 @@ fn range(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Re
|
||||
Ok(Value::from_array(values))
|
||||
}
|
||||
|
||||
fn range_step(span: &Span, params: &[Ref<Expr>], args: &[Value], strict: bool) -> Result<Value> {
|
||||
let name = "numbers.range_step";
|
||||
ensure_args_count(span, name, params, args, 3)?;
|
||||
let v1 = ensure_numeric(name, ¶ms[0], &args[0].clone())?;
|
||||
let v2 = ensure_numeric(name, ¶ms[1], &args[1].clone())?;
|
||||
let incr = ensure_numeric(name, ¶ms[2], &args[2].clone())?;
|
||||
|
||||
match v1.is_integer() {
|
||||
false if strict => bail!(params[0].span().error("operand must be integer")),
|
||||
false => return Ok(Value::Undefined),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
match v2.is_integer() {
|
||||
false if strict => bail!(params[1].span().error("operand must be integer")),
|
||||
false => return Ok(Value::Undefined),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
if strict && (!incr.is_integer() || incr <= Number::from(0u64)) {
|
||||
bail!(params[2].span().error("step must be a positive integer"))
|
||||
}
|
||||
|
||||
let (incr, num_elements) = match (v2.sub(&v1)?.as_i64(), incr.as_i64()) {
|
||||
(Some(v), Some(incr)) if v >= 0 => (incr, v / incr + 1),
|
||||
(Some(v), Some(incr)) => (-incr, -v / incr + 1),
|
||||
_ => bail!(span.error("could not determine number of elements")),
|
||||
};
|
||||
|
||||
let mut values = Vec::with_capacity(num_elements as usize);
|
||||
let incr = Number::from(incr);
|
||||
let mut v = v1;
|
||||
while (v <= v2 && incr.is_positive()) || (v >= v2 && !incr.is_positive()) {
|
||||
values.push(Value::from(v.clone()));
|
||||
v.add_assign(&incr)?;
|
||||
}
|
||||
|
||||
Ok(Value::from_array(values))
|
||||
}
|
||||
|
||||
fn round(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
ensure_args_count(span, "round", params, args, 1)?;
|
||||
let name = "round";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
Ok(Value::from(
|
||||
ensure_numeric("round", ¶ms[0], &args[0])?.round(),
|
||||
ensure_numeric(name, ¶ms[0], &args[0])?.round(),
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
@@ -35,9 +35,18 @@ fn compare(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) ->
|
||||
Ok(Value::from(result as i64))
|
||||
}
|
||||
|
||||
fn is_valid(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
fn is_valid(span: &Span, params: &[Ref<Expr>], args: &[Value], strict: bool) -> Result<Value> {
|
||||
let name = "semver.is_valid";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
let v = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
Ok(Value::Bool(Version::parse(&v).is_ok()))
|
||||
Ok(Value::Bool(
|
||||
Version::parse(&if strict {
|
||||
ensure_string(name, ¶ms[0], &args[0])?
|
||||
} else {
|
||||
match &args[0] {
|
||||
Value::String(s) => s.clone(),
|
||||
_ => return Ok(Value::Bool(false)),
|
||||
}
|
||||
})
|
||||
.is_ok(),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -157,6 +157,41 @@ fn split(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Re
|
||||
))
|
||||
}
|
||||
|
||||
fn to_string(v: &Value, unescape: bool) -> String {
|
||||
match v {
|
||||
Value::Null => "null".to_owned(),
|
||||
Value::Bool(b) => b.to_string(),
|
||||
Value::String(s) if unescape => serde_json::to_string(&s).unwrap_or(s.as_ref().to_string()),
|
||||
Value::String(s) => s.as_ref().to_string(),
|
||||
Value::Number(n) => n.format_decimal(),
|
||||
Value::Array(a) => {
|
||||
"[".to_owned()
|
||||
+ &a.iter()
|
||||
.map(|e| to_string(e, true))
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ")
|
||||
+ "]"
|
||||
}
|
||||
Value::Set(s) => {
|
||||
"{".to_owned()
|
||||
+ &s.iter()
|
||||
.map(|e| to_string(e, true))
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ")
|
||||
+ "}"
|
||||
}
|
||||
Value::Object(o) => {
|
||||
"{".to_owned()
|
||||
+ &o.iter()
|
||||
.map(|(k, v)| to_string(k, true) + ": " + &to_string(v, true))
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ")
|
||||
+ "}"
|
||||
}
|
||||
Value::Undefined => "#undefined".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn sprintf(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "sprintf";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
@@ -219,6 +254,9 @@ fn sprintf(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) ->
|
||||
// Handle Golang printing verbs.
|
||||
// https://pkg.go.dev/fmt
|
||||
match (verb, arg) {
|
||||
('s', Value::String(sv)) => s += sv.as_ref(),
|
||||
('s', v) => s += &to_string(v, false),
|
||||
|
||||
('v', _) => s += format!("{arg}").as_str(),
|
||||
('b', Value::Number(f)) if f.is_integer() => {
|
||||
let (sign, v) = get_sign_value(f);
|
||||
@@ -315,11 +353,6 @@ fn sprintf(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) ->
|
||||
bail!(args_span.error("floating-point number specified for format verb {verb}."));
|
||||
}
|
||||
|
||||
('s', Value::String(sv)) => s += sv.as_ref(),
|
||||
('s', _) => {
|
||||
bail!(args_span.error("invalid non string argument specified for format verb %s"));
|
||||
}
|
||||
|
||||
('+', _) if chars.next() == Some('v') => {
|
||||
bail!(args_span.error("Go-syntax fields names format verm %#v is not supported."));
|
||||
}
|
||||
|
||||
@@ -147,6 +147,7 @@ impl Engine {
|
||||
self.interpreter.set_current_module(prev_module)?;
|
||||
}
|
||||
|
||||
self.interpreter.create_rule_prefixes()?;
|
||||
Ok(self.interpreter.get_data_mut().clone())
|
||||
}
|
||||
|
||||
|
||||
@@ -114,7 +114,7 @@ impl Interpreter {
|
||||
module: None,
|
||||
schedule: None,
|
||||
current_module_path: String::default(),
|
||||
input: Value::Null,
|
||||
input: Value::Undefined,
|
||||
data: Value::new_object(),
|
||||
init_data: Value::new_object(),
|
||||
with_document: Value::new_object(),
|
||||
@@ -288,7 +288,11 @@ impl Interpreter {
|
||||
Value::String(s) => s.to_string(),
|
||||
_ => index.to_string(),
|
||||
};
|
||||
let ref_path = ref_path + "." + &index + "." + &path.join("");
|
||||
let ref_path = if path.is_empty() {
|
||||
ref_path + "." + &index
|
||||
} else {
|
||||
ref_path + "." + &index + "." + &path.join(".")
|
||||
};
|
||||
self.ensure_rule_evaluated(ref_path)?;
|
||||
}
|
||||
}
|
||||
@@ -483,7 +487,15 @@ impl Interpreter {
|
||||
(ArithOp::Sub, Value::Set(_), _) | (ArithOp::Sub, _, Value::Set(_)) => {
|
||||
builtins::sets::difference(lhs, rhs, lhs_value, rhs_value)
|
||||
}
|
||||
_ => builtins::numbers::arithmetic_operation(span, op, lhs, rhs, lhs_value, rhs_value),
|
||||
_ => builtins::numbers::arithmetic_operation(
|
||||
span,
|
||||
op,
|
||||
lhs,
|
||||
rhs,
|
||||
lhs_value,
|
||||
rhs_value,
|
||||
self.strict_builtin_errors,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -998,6 +1010,7 @@ impl Interpreter {
|
||||
)?,
|
||||
_ => self.eval_expr(expr)?,
|
||||
};
|
||||
|
||||
if let Some(ctx) = self.contexts.last_mut() {
|
||||
if let Some(result) = &mut ctx.result {
|
||||
result
|
||||
@@ -1011,14 +1024,10 @@ impl Interpreter {
|
||||
Literal::SomeVars { span, vars, .. } => {
|
||||
for var in vars {
|
||||
let name = var.source_str();
|
||||
if let Ok(variable) = self.add_variable_or(&name) {
|
||||
if variable != Value::Undefined {
|
||||
return Err(anyhow!(
|
||||
"duplicated definition of local variable {}",
|
||||
name
|
||||
));
|
||||
}
|
||||
if self.current_scope()?.get(&name).is_some() {
|
||||
bail!("duplicated definition of local variable {}", name);
|
||||
}
|
||||
self.add_variable_or(&name)?;
|
||||
}
|
||||
if let Some(ctx) = self.contexts.last_mut() {
|
||||
if let Some(result) = &mut ctx.result {
|
||||
@@ -1064,6 +1073,13 @@ impl Interpreter {
|
||||
}
|
||||
|
||||
fn eval_stmt(&mut self, stmt: &LiteralStmt, stmts: &[&LiteralStmt]) -> Result<bool> {
|
||||
debug_new_group!(
|
||||
"eval_stmt {}:{} {}",
|
||||
stmt.span.line,
|
||||
stmt.span.col,
|
||||
stmt.span.text()
|
||||
);
|
||||
|
||||
let mut skip_exec = false;
|
||||
let saved_state = if !stmt.with_mods.is_empty() {
|
||||
// Save state;
|
||||
@@ -1185,6 +1201,9 @@ impl Interpreter {
|
||||
if loop_expr_value[&idx] != Value::Undefined {
|
||||
result = self.eval_stmts_in_loop(stmts, &loops[1..])? || result;
|
||||
return Ok(result);
|
||||
} else if idx != Value::Undefined {
|
||||
// The index is not valid for this collection.
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1240,11 +1259,8 @@ impl Interpreter {
|
||||
result = false;
|
||||
}
|
||||
_ => {
|
||||
return Err(loop_expr.span.source.error(
|
||||
loop_expr.span.line,
|
||||
loop_expr.span.col,
|
||||
"item cannot be indexed",
|
||||
));
|
||||
// The item is not a collection.
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1854,6 +1870,7 @@ impl Interpreter {
|
||||
self.default_rules.get(&fcn_path).cloned()
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(rules) = rules {
|
||||
for (rule, _) in rules.iter() {
|
||||
if let Rule::Default { value, .. } = rule.as_ref() {
|
||||
@@ -1967,6 +1984,7 @@ impl Interpreter {
|
||||
fn lookup_var(&mut self, span: &Span, fields: &[&str], no_error: bool) -> Result<Value> {
|
||||
let name = span.source_str();
|
||||
|
||||
debug_new_group!("lookup_var: name={name}, fields={fields:?}, no_error={no_error}");
|
||||
// Return local variable/argument.
|
||||
if let Some(v) = self.lookup_local_var(&name) {
|
||||
return Ok(Self::get_value_chained(v, fields));
|
||||
@@ -1992,6 +2010,7 @@ impl Interpreter {
|
||||
// If the rule has already been evaluated or specified via a with modifier,
|
||||
// use that value.
|
||||
if v != Value::Undefined {
|
||||
debug!("returning v = {v}");
|
||||
return Ok(v);
|
||||
}
|
||||
|
||||
@@ -2051,6 +2070,13 @@ impl Interpreter {
|
||||
}
|
||||
|
||||
fn eval_expr(&mut self, expr: &ExprRef) -> Result<Value> {
|
||||
debug_new_group!(
|
||||
"eval_expr: {}:{} {}",
|
||||
expr.span().line,
|
||||
expr.span().col,
|
||||
expr.span().text()
|
||||
);
|
||||
|
||||
match expr.as_ref() {
|
||||
Expr::Null(_) => Ok(Value::Null),
|
||||
Expr::True(_) => Ok(Value::Bool(true)),
|
||||
@@ -2292,7 +2318,7 @@ impl Interpreter {
|
||||
comps.push(*v.text());
|
||||
expr = None;
|
||||
}
|
||||
_ => bail!("internal error: not a simple ref"),
|
||||
_ => bail!(format!("internal error: not a simplee ref {expr:?}")),
|
||||
}
|
||||
}
|
||||
if let Some(d) = document {
|
||||
@@ -2650,11 +2676,110 @@ impl Interpreter {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_rule_path_components(mut refr: &Ref<Expr>) -> Result<Vec<Rc<str>>> {
|
||||
let mut components: Vec<Rc<str>> = vec![];
|
||||
loop {
|
||||
refr = match refr.as_ref() {
|
||||
Expr::Var(v) => {
|
||||
components.push((*v.text().as_ref()).into());
|
||||
break;
|
||||
}
|
||||
Expr::RefBrack { refr, index, .. } => {
|
||||
if let Expr::String(s) = index.as_ref() {
|
||||
components.push((*s.text().as_ref()).into());
|
||||
}
|
||||
refr
|
||||
}
|
||||
Expr::RefDot { refr, field, .. } => {
|
||||
components.push((*field.text().as_ref()).into());
|
||||
refr
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
components.reverse();
|
||||
Ok(components)
|
||||
}
|
||||
|
||||
pub fn create_rule_prefixes(&mut self) -> Result<()> {
|
||||
debug_new_group!("create_rule_prefixes");
|
||||
debug!("data before: {}", self.data);
|
||||
|
||||
for module in self.modules.clone() {
|
||||
let module_path = Self::get_rule_path_components(&module.package.refr)?;
|
||||
debug!("processing module {module_path:?}");
|
||||
|
||||
for rule in &module.policy {
|
||||
let mut rule_refr = Self::get_rule_refr(rule);
|
||||
debug!("rule refr: {}", rule_refr.span().text());
|
||||
debug!("rule : {:?}", rule);
|
||||
if let Rule::Spec {
|
||||
head:
|
||||
RuleHead::Set {
|
||||
refr, key: None, ..
|
||||
},
|
||||
..
|
||||
} = rule.as_ref()
|
||||
{
|
||||
rule_refr = match refr.as_ref() {
|
||||
Expr::RefDot { refr, .. } => refr,
|
||||
|
||||
_ => refr,
|
||||
}
|
||||
}
|
||||
|
||||
let mut prefix_path = module_path.clone();
|
||||
prefix_path.append(&mut Self::get_rule_path_components(rule_refr)?);
|
||||
let prefix_path: Vec<&str> = prefix_path[0..prefix_path.len() - 1]
|
||||
.iter()
|
||||
.map(|s| s.as_ref())
|
||||
.collect();
|
||||
|
||||
if Self::get_value_chained(self.data.clone(), &prefix_path) == Value::Undefined {
|
||||
self.update_data(
|
||||
rule_refr.span(),
|
||||
rule_refr,
|
||||
&prefix_path,
|
||||
Value::new_object(),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
debug!("data after: {}", self.data);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn record_rule(&mut self, refr: &Ref<Expr>, rule: Ref<Rule>) -> Result<()> {
|
||||
let path = get_root_var(refr)?;
|
||||
let path = path.text();
|
||||
let path = self.current_module_path.clone() + "." + path;
|
||||
match self.rules.entry(path) {
|
||||
Entry::Occupied(o) => {
|
||||
o.into_mut().push(rule.clone());
|
||||
}
|
||||
Entry::Vacant(v) => {
|
||||
v.insert(vec![rule.clone()]);
|
||||
}
|
||||
}
|
||||
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.clone());
|
||||
}
|
||||
Entry::Vacant(v) => {
|
||||
v.insert(vec![rule.clone()]);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn gather_rules(&mut self) -> Result<()> {
|
||||
for module in self.modules.clone() {
|
||||
let prev_module = self.set_current_module(Some(module.clone()))?;
|
||||
for rule in &module.policy {
|
||||
let refr = Self::get_rule_refr(rule);
|
||||
|
||||
if let Rule::Spec { .. } = rule.as_ref() {
|
||||
// Adjust refr to ensure simple ref.
|
||||
// TODO: refactor.
|
||||
@@ -2667,18 +2792,7 @@ impl Interpreter {
|
||||
Expr::RefBrack { refr, .. } => refr,
|
||||
_ => refr,
|
||||
};
|
||||
//let path = Self::get_path_string(refr, None)?;
|
||||
let path = get_root_var(refr)?;
|
||||
let path = path.text();
|
||||
let path = self.current_module_path.clone() + "." + path;
|
||||
match self.rules.entry(path) {
|
||||
Entry::Occupied(o) => {
|
||||
o.into_mut().push(rule.clone());
|
||||
}
|
||||
Entry::Vacant(v) => {
|
||||
v.insert(vec![rule.clone()]);
|
||||
}
|
||||
}
|
||||
self.record_rule(refr, rule.clone())?;
|
||||
} else if let Rule::Default { .. } = rule.as_ref() {
|
||||
let (refr, index) = match refr.as_ref() {
|
||||
// TODO: Validate the index
|
||||
@@ -2698,10 +2812,8 @@ impl Interpreter {
|
||||
_ => (refr, None),
|
||||
};
|
||||
|
||||
//let path = Self::get_path_string(refr, None)?;
|
||||
let path = get_root_var(refr)?;
|
||||
let path = path.text();
|
||||
let path = self.current_module_path.clone() + "." + path;
|
||||
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() {
|
||||
|
||||
@@ -282,6 +282,12 @@ impl Number {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_positive(&self) -> bool {
|
||||
match self {
|
||||
Big(b) => b.d.is_sign_positive(),
|
||||
}
|
||||
}
|
||||
|
||||
fn ensure_integers(a: &Number, b: &Number) -> Option<(BigInt, BigInt)> {
|
||||
match (a, b) {
|
||||
(Big(a), Big(b)) if a.d.is_integer() && b.d.is_integer() => {
|
||||
|
||||
71
src/utils.rs
71
src/utils.rs
@@ -9,6 +9,77 @@ use std::collections::BTreeMap;
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
macro_rules! debug {
|
||||
($($arg:tt)+) => {
|
||||
{
|
||||
if log::log_enabled!(log::Level::Debug) {
|
||||
print!("{}:{}:", file!(), line!());
|
||||
crate::utils::NESTING.with(|f| {
|
||||
print!("{}", " ".repeat(*f.borrow() as usize));
|
||||
});
|
||||
println!($($arg)+);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
macro_rules! debug {
|
||||
($($arg:tt)+) => {};
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) use debug;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
#[allow(unused)]
|
||||
macro_rules! debug_new_group {
|
||||
($($arg:tt)+) => {
|
||||
debug!($($arg)+);
|
||||
let _group = DebugNesting::new();
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
macro_rules! debug_new_group {
|
||||
($($arg:tt)+) => {};
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) use debug_new_group;
|
||||
|
||||
#[allow(unused)]
|
||||
pub struct DebugNesting {}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
thread_local!(pub static NESTING: std::cell::RefCell<u32> = std::cell::RefCell::new(1));
|
||||
|
||||
impl DebugNesting {
|
||||
#[cfg(debug_assertions)]
|
||||
#[allow(unused)]
|
||||
pub fn new() -> DebugNesting {
|
||||
NESTING.with(|f| {
|
||||
*f.borrow_mut() += 1;
|
||||
});
|
||||
DebugNesting {}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
impl Drop for DebugNesting {
|
||||
#[cfg(debug_assertions)]
|
||||
fn drop(&mut self) {
|
||||
NESTING.with(|f| {
|
||||
*f.borrow_mut() -= 1;
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
fn drop(&mut self) {}
|
||||
}
|
||||
|
||||
pub fn get_path_string(refr: &Expr, document: Option<&str>) -> Result<String> {
|
||||
let mut comps: Vec<&str> = vec![];
|
||||
let mut expr = Some(refr);
|
||||
|
||||
@@ -25,11 +25,12 @@ pub enum Value {
|
||||
Number(Number),
|
||||
String(Rc<str>),
|
||||
Array(Rc<Vec<Value>>),
|
||||
Object(Rc<BTreeMap<Value, Value>>),
|
||||
|
||||
// Extra rego data type
|
||||
Set(Rc<BTreeSet<Value>>),
|
||||
|
||||
Object(Rc<BTreeMap<Value, Value>>),
|
||||
|
||||
// Indicate that a value is undefined
|
||||
Undefined,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user