More OPA conformant semantics (#62)

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
This commit is contained in:
Anand Krishnamoorthi
2023-12-11 09:00:06 -08:00
committed by GitHub
parent 8a73b4bef9
commit 73ee18f002
32 changed files with 542 additions and 205 deletions

View File

@@ -321,6 +321,7 @@ pub enum Rule {
Default {
span: Span,
refr: Ref<Expr>,
args: Vec<Ref<Expr>>,
op: AssignOp,
value: Ref<Expr>,
},

View File

@@ -21,7 +21,7 @@ pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
m.insert("sum", (sum, 1));
}
fn count(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn count(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
ensure_args_count(span, "count", params, args, 1)?;
Ok(Value::from(Number::from(match &args[0] {
@@ -38,7 +38,7 @@ fn count(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
})))
}
fn max(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn max(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
ensure_args_count(span, "max", params, args, 1)?;
Ok(match &args[0] {
@@ -53,7 +53,7 @@ fn max(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
})
}
fn min(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn min(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
ensure_args_count(span, "min", params, args, 1)?;
Ok(match &args[0] {
@@ -68,7 +68,7 @@ fn min(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
})
}
fn product(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn product(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
ensure_args_count(span, "product", params, args, 1)?;
let mut v = Number::from(1_u64);
@@ -93,7 +93,7 @@ fn product(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
}))
}
fn sort(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn sort(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
ensure_args_count(span, "sort", params, args, 1)?;
Ok(match &args[0] {
Value::Array(a) => {
@@ -110,7 +110,7 @@ fn sort(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
})
}
fn sum(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn sum(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
ensure_args_count(span, "sum", params, args, 1)?;
let mut v = Number::from(0_u64);

View File

@@ -18,7 +18,7 @@ pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
m.insert("array.slice", (slice, 3));
}
fn concat(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn concat(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "array.concat";
ensure_args_count(span, name, params, args, 2)?;
let mut v1 = ensure_array(name, &params[0], args[0].clone())?;
@@ -28,7 +28,7 @@ fn concat(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
Ok(Value::Array(v1))
}
fn reverse(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn reverse(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "array.reverse";
ensure_args_count(span, name, params, args, 1)?;
@@ -37,7 +37,7 @@ fn reverse(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
Ok(Value::Array(v1))
}
fn slice(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn slice(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "array.slice";
ensure_args_count(span, name, params, args, 3)?;

View File

@@ -21,7 +21,7 @@ pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
m.insert("bits.xor", (xor, 2));
}
fn and(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn and(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "bits.and";
ensure_args_count(span, name, params, args, 2)?;
@@ -34,7 +34,7 @@ fn and(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
})
}
fn lsh(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn lsh(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "bits.lsh";
ensure_args_count(span, name, params, args, 2)?;
@@ -47,7 +47,7 @@ fn lsh(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
})
}
fn negate(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn negate(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "bits.negate";
ensure_args_count(span, name, params, args, 1)?;
@@ -59,7 +59,7 @@ fn negate(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
})
}
fn or(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn or(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "bits.or";
ensure_args_count(span, name, params, args, 2)?;
@@ -72,7 +72,7 @@ fn or(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
})
}
fn rsh(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn rsh(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "bits.rsh";
ensure_args_count(span, name, params, args, 2)?;
@@ -85,7 +85,7 @@ fn rsh(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
})
}
fn xor(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn xor(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "bits.xor";
ensure_args_count(span, name, params, args, 2)?;

View File

@@ -15,12 +15,13 @@ pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
m.insert("to_number", (to_number, 1));
}
fn to_number(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn to_number(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "to_number";
ensure_args_count(span, name, params, args, 1)?;
let span = params[0].span();
Ok(match &args[0] {
Value::Null => Value::from(0u64),
Value::Bool(true) => Value::from(1u64),
Value::Bool(false) => Value::from(0u64),
Value::Number(_) => args[0].clone(),
@@ -34,7 +35,9 @@ fn to_number(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value>
}
},
_ => {
bail!(span.error(format!("`{name}` expects bool/number/string argument.").as_str()));
bail!(
span.error(format!("`{name}` expects bool/number/string/null argument.").as_str())
);
}
})
}

View File

@@ -28,7 +28,12 @@ pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
m.insert("crypto.sha256", (crypto_sha256, 1));
}
fn hmac_equal_fixed_time(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn hmac_equal_fixed_time(
span: &Span,
params: &[Ref<Expr>],
args: &[Value],
_strict: bool,
) -> Result<Value> {
let name = "crypto.hmac.equal";
ensure_args_count(span, name, params, args, 2)?;
@@ -41,7 +46,7 @@ fn hmac_equal_fixed_time(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> R
)))
}
fn hmac_md5(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn hmac_md5(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "crypto.hmac.md5";
ensure_args_count(span, name, params, args, 2)?;
@@ -57,7 +62,7 @@ fn hmac_md5(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value>
Ok(Value::String(hex::encode(result.into_bytes()).into()))
}
fn hmac_sha1(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn hmac_sha1(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "crypto.hmac.sha1";
ensure_args_count(span, name, params, args, 2)?;
@@ -73,7 +78,7 @@ fn hmac_sha1(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value>
Ok(Value::String(hex::encode(result.into_bytes()).into()))
}
fn hmac_sha256(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn hmac_sha256(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "crypto.hmac.sha256";
ensure_args_count(span, name, params, args, 2)?;
@@ -89,7 +94,7 @@ fn hmac_sha256(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Valu
Ok(Value::String(hex::encode(result.into_bytes()).into()))
}
fn hmac_sha512(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn hmac_sha512(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "crypto.hmac.sha512";
ensure_args_count(span, name, params, args, 2)?;
@@ -105,7 +110,7 @@ fn hmac_sha512(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Valu
Ok(Value::String(hex::encode(result.into_bytes()).into()))
}
fn crypto_md5(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn crypto_md5(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "crypto.md5";
ensure_args_count(span, name, params, args, 1)?;
@@ -119,7 +124,7 @@ fn crypto_md5(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value
Ok(Value::String(hex::encode(result).into()))
}
fn crypto_sha1(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn crypto_sha1(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "crypto.sha1";
ensure_args_count(span, name, params, args, 1)?;
@@ -133,7 +138,12 @@ fn crypto_sha1(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Valu
Ok(Value::String(hex::encode(result).into()))
}
fn crypto_sha256(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn crypto_sha256(
span: &Span,
params: &[Ref<Expr>],
args: &[Value],
_strict: bool,
) -> Result<Value> {
let name = "crypto.sha256";
ensure_args_count(span, name, params, args, 1)?;

View File

@@ -20,7 +20,7 @@ pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
// Symbol analyzer must ensure that vars used by print are defined before
// the print statement. Scheduler must ensure the above constraint.
// Additionally interpreter must allow undefined inputs to print.
fn print(span: &Span, _params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn print(span: &Span, _params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
if args.len() > MAX_ARGS as usize {
bail!(span.error("print supports up to 100 arguments"));
}

View File

@@ -19,7 +19,13 @@ lazy_static! {
let mut m : HashMap<&'static str, BuiltinFcn> = HashMap::new();
m.insert("all", (all, 1));
m.insert("any", (any, 1));
m.insert("any", (any, 1));
m.insert("cast_array", (cast_array, 1));
m.insert("cast_boolean", (cast_boolean, 1));
m.insert("cast_null", (cast_null, 1));
m.insert("cast_object", (cast_object, 1));
m.insert("cast_set", (cast_set, 1));
m.insert("cast_string", (cast_string, 1));
m.insert("set_diff", (set_diff, 2));
#[cfg(feature = "crypto")]
@@ -28,7 +34,7 @@ lazy_static! {
};
}
fn all(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn all(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
ensure_args_count(span, "all", params, args, 1)?;
Ok(Value::Bool(match &args[0] {
@@ -41,7 +47,7 @@ fn all(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
}))
}
fn any(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn any(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
ensure_args_count(span, "any", params, args, 1)?;
Ok(Value::Bool(match &args[0] {
@@ -54,10 +60,70 @@ fn any(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
}))
}
fn set_diff(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn set_diff(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "set_diff";
ensure_args_count(span, name, params, args, 2)?;
let s1 = ensure_set(name, &params[0], args[0].clone())?;
let s2 = ensure_set(name, &params[1], args[1].clone())?;
Ok(Value::from_set(s1.difference(&s2).cloned().collect()))
}
fn cast_array(span: &Span, params: &[Ref<Expr>], args: &[Value], strict: bool) -> Result<Value> {
let name = "cast_array";
ensure_args_count(span, name, params, args, 1)?;
match &args[0] {
Value::Array(_) => Ok(args[0].clone()),
_ if strict => bail!(params[0].span().error("array required")),
_ => Ok(Value::Undefined),
}
}
fn cast_boolean(span: &Span, params: &[Ref<Expr>], args: &[Value], strict: bool) -> Result<Value> {
let name = "cast_boolean";
ensure_args_count(span, name, params, args, 1)?;
match &args[0] {
Value::Bool(_) => Ok(args[0].clone()),
_ if strict => bail!(params[0].span().error("boolean required")),
_ => Ok(Value::Undefined),
}
}
fn cast_null(span: &Span, params: &[Ref<Expr>], args: &[Value], strict: bool) -> Result<Value> {
let name = "cast_null";
ensure_args_count(span, name, params, args, 1)?;
match &args[0] {
Value::Null => Ok(Value::Null),
_ if strict => bail!(params[0].span().error("null required")),
_ => Ok(Value::Undefined),
}
}
fn cast_object(span: &Span, params: &[Ref<Expr>], args: &[Value], strict: bool) -> Result<Value> {
let name = "cast_object";
ensure_args_count(span, name, params, args, 1)?;
match &args[0] {
Value::Object(_) => Ok(args[0].clone()),
_ if strict => bail!(params[0].span().error("object required")),
_ => Ok(Value::Undefined),
}
}
fn cast_set(span: &Span, params: &[Ref<Expr>], args: &[Value], strict: bool) -> Result<Value> {
let name = "cast_set";
ensure_args_count(span, name, params, args, 1)?;
match &args[0] {
Value::Set(_) => Ok(args[0].clone()),
_ if strict => bail!(params[0].span().error("set required")),
_ => Ok(Value::Undefined),
}
}
fn cast_string(span: &Span, params: &[Ref<Expr>], args: &[Value], strict: bool) -> Result<Value> {
let name = "cast_string";
ensure_args_count(span, name, params, args, 1)?;
match &args[0] {
Value::String(_) => Ok(args[0].clone()),
_ if strict => bail!(params[0].span().error("string required")),
_ => Ok(Value::Undefined),
}
}

View File

@@ -26,7 +26,12 @@ pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
}
}
fn base64_decode(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn base64_decode(
span: &Span,
params: &[Ref<Expr>],
args: &[Value],
_strict: bool,
) -> Result<Value> {
let name = "base64.decode";
ensure_args_count(span, name, params, args, 1)?;
@@ -38,7 +43,12 @@ fn base64_decode(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Va
}
#[cfg(feature = "yaml")]
fn yaml_is_valid(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn yaml_is_valid(
span: &Span,
params: &[Ref<Expr>],
args: &[Value],
_strict: bool,
) -> Result<Value> {
let name = "yaml.is_valid";
ensure_args_count(span, name, params, args, 1)?;
@@ -47,7 +57,7 @@ fn yaml_is_valid(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Va
}
#[cfg(feature = "yaml")]
fn yaml_marshal(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn yaml_marshal(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "yaml.marshal";
ensure_args_count(span, name, params, args, 1)?;
Ok(Value::String(
@@ -58,14 +68,24 @@ fn yaml_marshal(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Val
}
#[cfg(feature = "yaml")]
fn yaml_unmarshal(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn yaml_unmarshal(
span: &Span,
params: &[Ref<Expr>],
args: &[Value],
_strict: bool,
) -> Result<Value> {
let name = "yaml.unmarshal";
ensure_args_count(span, name, params, args, 1)?;
let yaml_str = ensure_string(name, &params[0], &args[0])?;
Value::from_yaml_str(&yaml_str).with_context(|| span.error("could not deserialize yaml."))
}
fn json_is_valid(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn json_is_valid(
span: &Span,
params: &[Ref<Expr>],
args: &[Value],
_strict: bool,
) -> Result<Value> {
let name = "json.is_valid";
ensure_args_count(span, name, params, args, 1)?;
@@ -73,7 +93,7 @@ fn json_is_valid(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Va
Ok(Value::Bool(Value::from_json_str(&json_str).is_ok()))
}
fn json_marshal(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn json_marshal(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "json.marshal";
ensure_args_count(span, name, params, args, 1)?;
Ok(Value::String(
@@ -83,7 +103,12 @@ fn json_marshal(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Val
))
}
fn json_unmarshal(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn json_unmarshal(
span: &Span,
params: &[Ref<Expr>],
args: &[Value],
_strict: bool,
) -> Result<Value> {
let name = "json.unmarshal";
ensure_args_count(span, name, params, args, 1)?;
let json_str = ensure_string(name, &params[0], &args[0])?;

View File

@@ -55,7 +55,7 @@ fn make_glob<'a>(pattern: &'a str, span: &'a Span) -> Result<Glob<'a>> {
Glob::new(pattern).or_else(|_| bail!(span.error("invalid glob")))
}
fn glob_match(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn glob_match(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "glob.match";
ensure_args_count(span, name, params, args, 3)?;
@@ -101,7 +101,7 @@ fn glob_match(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value
Ok(Value::Bool(glob.is_match(&value[..])))
}
fn quote_meta(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn quote_meta(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "glob.quote_meta";
ensure_args_count(span, name, params, args, 1)?;

View File

@@ -38,7 +38,7 @@ use std::collections::HashMap;
use anyhow::Result;
use lazy_static::lazy_static;
pub type BuiltinFcn = (fn(&Span, &[Ref<Expr>], &[Value]) -> Result<Value>, u8);
pub type BuiltinFcn = (fn(&Span, &[Ref<Expr>], &[Value], bool) -> Result<Value>, u8);
#[cfg(feature = "deprecated")]
pub use deprecated::DEPRECATED;

View File

@@ -48,28 +48,28 @@ pub fn arithmetic_operation(
}))
}
fn abs(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn abs(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
ensure_args_count(span, "abs", params, args, 1)?;
Ok(Value::from(
ensure_numeric("abs", &params[0], &args[0])?.abs(),
))
}
fn ceil(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn ceil(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
ensure_args_count(span, "ceil", params, args, 1)?;
Ok(Value::from(
ensure_numeric("ceil", &params[0], &args[0])?.ceil(),
))
}
fn floor(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn floor(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
ensure_args_count(span, "floor", params, args, 1)?;
Ok(Value::from(
ensure_numeric("floor", &params[0], &args[0])?.floor(),
))
}
fn range(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
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", &params[0], &args[0].clone())?;
let v2 = ensure_numeric("numbers.range", &params[1], &args[1].clone())?;
@@ -96,14 +96,14 @@ fn range(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
Ok(Value::from_array(values))
}
fn round(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn round(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
ensure_args_count(span, "round", params, args, 1)?;
Ok(Value::from(
ensure_numeric("round", &params[0], &args[0])?.round(),
))
}
fn intn(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn intn(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let fcn = "rand.intn";
ensure_args_count(span, fcn, params, args, 2)?;
let _ = ensure_string(fcn, &params[0], &args[0])?;

View File

@@ -122,7 +122,7 @@ fn merge_filters(
Ok(filters)
}
fn json_filter(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn json_filter(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "json.filter";
ensure_args_count(span, name, params, args, 2)?;
ensure_object(name, &params[0], args[0].clone())?;
@@ -136,7 +136,7 @@ fn json_filter(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Valu
Ok(json_filter_impl(&args[0], &filters))
}
fn filter(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn filter(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "object.filter";
ensure_args_count(span, name, params, args, 2)?;
let mut obj = ensure_object(name, &params[0], args[0].clone())?;
@@ -154,7 +154,7 @@ fn filter(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
Ok(Value::Object(obj))
}
fn get(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn get(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "object.get";
ensure_args_count(span, name, params, args, 3)?;
let obj = ensure_object(name, &params[0], args[0].clone())?;
@@ -179,14 +179,14 @@ fn get(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
})
}
fn keys(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn keys(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "object.keys";
ensure_args_count(span, name, params, args, 1)?;
let obj = ensure_object(name, &params[0], args[0].clone())?;
Ok(Value::from_set(obj.keys().cloned().collect()))
}
fn remove(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn remove(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "object.remove";
ensure_args_count(span, name, params, args, 2)?;
let mut obj = ensure_object(name, &params[0], args[0].clone())?;
@@ -225,7 +225,7 @@ fn is_subset(sup: &Value, sub: &Value) -> bool {
}
}
fn subset(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn subset(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "object.subset";
ensure_args_count(span, name, params, args, 2)?;

View File

@@ -26,7 +26,12 @@ pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
m.insert("regex.template_match", (regex_template_match, 4));
}
fn find_all_string_submatch_n(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn find_all_string_submatch_n(
span: &Span,
params: &[Ref<Expr>],
args: &[Value],
_strict: bool,
) -> Result<Value> {
let name = "regex.find_all_string_submatch_n";
ensure_args_count(span, name, params, args, 3)?;
@@ -68,7 +73,7 @@ fn find_all_string_submatch_n(span: &Span, params: &[Ref<Expr>], args: &[Value])
))
}
fn find_n(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn find_n(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "regex.find_n";
ensure_args_count(span, name, params, args, 3)?;
@@ -98,14 +103,19 @@ fn find_n(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
))
}
fn is_valid(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn is_valid(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "regex.is_valid";
ensure_args_count(span, name, params, args, 1)?;
Ok(ensure_string(name, &params[0], &args[0])
.map_or(Value::Bool(false), |p| Value::Bool(Regex::new(&p).is_ok())))
}
pub fn regex_match(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
pub fn regex_match(
span: &Span,
params: &[Ref<Expr>],
args: &[Value],
_strict: bool,
) -> Result<Value> {
let name = "regex.match";
ensure_args_count(span, name, params, args, 2)?;
let pattern = ensure_string(name, &params[0], &args[0])?;
@@ -116,7 +126,12 @@ pub fn regex_match(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<
Ok(Value::Bool(pattern.is_match(&value)))
}
fn regex_replace(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn regex_replace(
span: &Span,
params: &[Ref<Expr>],
args: &[Value],
_strict: bool,
) -> Result<Value> {
let name = "regex.replace";
ensure_args_count(span, name, params, args, 3)?;
@@ -135,7 +150,7 @@ fn regex_replace(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Va
))
}
fn regex_split(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn regex_split(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "regex.split";
ensure_args_count(span, name, params, args, 2)?;
let pattern = ensure_string(name, &params[0], &args[0])?;
@@ -151,7 +166,12 @@ fn regex_split(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Valu
))
}
fn regex_template_match(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn regex_template_match(
span: &Span,
params: &[Ref<Expr>],
args: &[Value],
_strict: bool,
) -> Result<Value> {
let name = "regex.template_match";
ensure_args_count(span, name, params, args, 4)?;
let template = ensure_string(name, &params[0], &args[0])?;

View File

@@ -19,7 +19,7 @@ pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
m.insert("semver.is_valid", (is_valid, 1));
}
fn compare(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn compare(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "semver.compare";
ensure_args_count(span, name, params, args, 2)?;
@@ -35,7 +35,7 @@ fn compare(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
Ok(Value::from(result as i64))
}
fn is_valid(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> 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, &params[0], &args[0])?;

View File

@@ -34,7 +34,12 @@ pub fn difference(expr1: &Expr, expr2: &Expr, v1: Value, v2: Value) -> Result<Va
Ok(Value::from_set(s1.difference(&s2).cloned().collect()))
}
fn intersection_of_set_of_sets(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn intersection_of_set_of_sets(
span: &Span,
params: &[Ref<Expr>],
args: &[Value],
_strict: bool,
) -> Result<Value> {
let name = "intersection";
ensure_args_count(span, name, params, args, 1)?;
let set = ensure_set(name, &params[0], args[0].clone())?;
@@ -61,7 +66,12 @@ fn intersection_of_set_of_sets(span: &Span, params: &[Ref<Expr>], args: &[Value]
Ok(Value::from_set(res))
}
fn union_of_set_of_sets(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn union_of_set_of_sets(
span: &Span,
params: &[Ref<Expr>],
args: &[Value],
_strict: bool,
) -> Result<Value> {
let name = "union";
ensure_args_count(span, name, params, args, 1)?;
let set = ensure_set(name, &params[0], args[0].clone())?;

View File

@@ -42,7 +42,7 @@ pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
m.insert("upper", (upper, 1));
}
fn concat(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn concat(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "concat";
ensure_args_count(span, name, params, args, 2)?;
let delimiter = ensure_string(name, &params[0], &args[0])?;
@@ -50,7 +50,7 @@ fn concat(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
Ok(Value::String(collection.join(&delimiter).into()))
}
fn contains(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn contains(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "contains";
ensure_args_count(span, name, params, args, 2)?;
let s1 = ensure_string(name, &params[0], &args[0])?;
@@ -58,7 +58,7 @@ fn contains(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value>
Ok(Value::Bool(s1.contains(s2.as_ref())))
}
fn endswith(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn endswith(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "endswith";
ensure_args_count(span, name, params, args, 2)?;
let s1 = ensure_string(name, &params[0], &args[0])?;
@@ -76,7 +76,7 @@ fn format_number(n: &Number, base: u64) -> String {
}
}
fn format_int(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn format_int(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "format_int";
ensure_args_count(span, name, params, args, 2)?;
let mut n = ensure_numeric(name, &params[0], &args[0])?;
@@ -97,7 +97,7 @@ fn format_int(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value
))
}
fn indexof(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn indexof(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "indexof";
ensure_args_count(span, name, params, args, 2)?;
let s1 = ensure_string(name, &params[0], &args[0])?;
@@ -109,7 +109,7 @@ fn indexof(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
}
#[allow(dead_code)]
fn indexof_n(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn indexof_n(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "indexof_n";
ensure_args_count(span, name, params, args, 2)?;
let s1 = ensure_string(name, &params[0], &args[0])?;
@@ -128,14 +128,14 @@ fn indexof_n(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value>
Ok(Value::from_array(positions))
}
fn lower(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn lower(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "lower";
ensure_args_count(span, name, params, args, 1)?;
let s = ensure_string(name, &params[0], &args[0])?;
Ok(Value::String(s.to_lowercase().into()))
}
fn replace(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn replace(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "replace";
ensure_args_count(span, name, params, args, 3)?;
let s = ensure_string(name, &params[0], &args[0])?;
@@ -144,7 +144,7 @@ fn replace(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
Ok(Value::String(s.replace(old.as_ref(), new.as_ref()).into()))
}
fn split(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn split(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "replace";
ensure_args_count(span, name, params, args, 2)?;
let s = ensure_string(name, &params[0], &args[0])?;
@@ -157,7 +157,7 @@ fn split(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
))
}
fn sprintf(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn sprintf(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "sprintf";
ensure_args_count(span, name, params, args, 2)?;
let fmt = ensure_string(name, &params[0], &args[0])?;
@@ -345,7 +345,12 @@ fn sprintf(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
Ok(Value::String(s.into()))
}
fn any_prefix_match(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn any_prefix_match(
span: &Span,
params: &[Ref<Expr>],
args: &[Value],
_strict: bool,
) -> Result<Value> {
let name = "strings.any_prefix_match";
ensure_args_count(span, name, params, args, 2)?;
@@ -376,7 +381,12 @@ fn any_prefix_match(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result
))
}
fn any_suffix_match(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn any_suffix_match(
span: &Span,
params: &[Ref<Expr>],
args: &[Value],
_strict: bool,
) -> Result<Value> {
let name = "strings.any_suffix_match";
ensure_args_count(span, name, params, args, 2)?;
@@ -407,7 +417,7 @@ fn any_suffix_match(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result
))
}
fn startswith(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn startswith(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "startswith";
ensure_args_count(span, name, params, args, 2)?;
let s1 = ensure_string(name, &params[0], &args[0])?;
@@ -415,7 +425,7 @@ fn startswith(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value
Ok(Value::Bool(s1.starts_with(s2.as_ref())))
}
fn replace_n(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn replace_n(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "trim";
ensure_args_count(span, name, params, args, 2)?;
let obj = ensure_object(name, &params[0], args[0].clone())?;
@@ -438,14 +448,14 @@ fn replace_n(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value>
Ok(Value::String(s.clone()))
}
fn reverse(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn reverse(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "reverse";
ensure_args_count(span, name, params, args, 1)?;
let s = ensure_string(name, &params[0], &args[0])?;
Ok(Value::String(s.chars().rev().collect::<String>().into()))
}
fn substring(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn substring(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "substring";
ensure_args_count(span, name, params, args, 3)?;
let s = ensure_string(name, &params[0], &args[0])?;
@@ -470,7 +480,7 @@ fn substring(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value>
}
}
fn trim(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn trim(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "trim";
ensure_args_count(span, name, params, args, 2)?;
let s1 = ensure_string(name, &params[0], &args[0])?;
@@ -478,7 +488,7 @@ fn trim(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
Ok(Value::String(s1.trim_matches(|c| s2.contains(c)).into()))
}
fn trim_left(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn trim_left(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "trim_left";
ensure_args_count(span, name, params, args, 2)?;
let s1 = ensure_string(name, &params[0], &args[0])?;
@@ -488,7 +498,7 @@ fn trim_left(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value>
))
}
fn trim_prefix(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn trim_prefix(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "trim_prefix";
ensure_args_count(span, name, params, args, 2)?;
let s1 = ensure_string(name, &params[0], &args[0])?;
@@ -499,7 +509,7 @@ fn trim_prefix(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Valu
}))
}
fn trim_right(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn trim_right(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "trim_right";
ensure_args_count(span, name, params, args, 2)?;
let s1 = ensure_string(name, &params[0], &args[0])?;
@@ -509,14 +519,14 @@ fn trim_right(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value
))
}
fn trim_space(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn trim_space(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "trim_space";
ensure_args_count(span, name, params, args, 1)?;
let s = ensure_string(name, &params[0], &args[0])?;
Ok(Value::String(s.trim().into()))
}
fn trim_suffix(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn trim_suffix(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "trim_suffix";
ensure_args_count(span, name, params, args, 2)?;
let s1 = ensure_string(name, &params[0], &args[0])?;
@@ -527,7 +537,7 @@ fn trim_suffix(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Valu
}))
}
fn upper(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn upper(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "upper";
ensure_args_count(span, name, params, args, 1)?;
let s = ensure_string(name, &params[0], &args[0])?;

View File

@@ -16,7 +16,7 @@ pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
m.insert("time.now_ns", (now_ns, 0));
}
fn now_ns(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn now_ns(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "time.now_ns";
ensure_args_count(span, name, params, args, 0)?;

View File

@@ -17,7 +17,7 @@ pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
// Symbol analyzer must ensure that vars used by trace are defined before
// the trace statement. Scheduler must ensure the above constraint.
fn trace(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn trace(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "trace";
ensure_args_count(span, name, params, args, 1)?;
let msg = ensure_string(name, &params[0], &args[0])?;

View File

@@ -22,37 +22,37 @@ pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
m.insert("type_name", (type_name, 1));
}
fn is_array(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn is_array(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
ensure_args_count(span, "is_array", params, args, 1)?;
Ok(Value::Bool(matches!(&args[0], Value::Array(_))))
}
fn is_boolean(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn is_boolean(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
ensure_args_count(span, "is_boolean", params, args, 1)?;
Ok(Value::Bool(matches!(&args[0], Value::Bool(_))))
}
fn is_null(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn is_null(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
ensure_args_count(span, "is_null", params, args, 1)?;
Ok(Value::Bool(matches!(&args[0], Value::Null)))
}
fn is_number(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn is_number(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
ensure_args_count(span, "is_number", params, args, 1)?;
Ok(Value::Bool(matches!(&args[0], Value::Number(_))))
}
fn is_object(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn is_object(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
ensure_args_count(span, "is_object", params, args, 1)?;
Ok(Value::Bool(matches!(&args[0], Value::Object(_))))
}
fn is_set(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn is_set(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
ensure_args_count(span, "is_set", params, args, 1)?;
Ok(Value::Bool(matches!(&args[0], Value::Set(_))))
}
fn is_string(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn is_string(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
ensure_args_count(span, "is_string", params, args, 1)?;
Ok(Value::Bool(matches!(&args[0], Value::String(_))))
}
@@ -70,7 +70,12 @@ pub fn get_type(value: &Value) -> &str {
}
}
pub fn type_name(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
pub fn type_name(
span: &Span,
params: &[Ref<Expr>],
args: &[Value],
_strict: bool,
) -> Result<Value> {
ensure_args_count(span, "type_name", params, args, 1)?;
Ok(Value::String(get_type(&args[0]).into()))
}

View File

@@ -66,7 +66,7 @@ fn two_exp(suffix: &str) -> Option<i32> {
})
}
fn parse(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn parse(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "units.parse";
ensure_args_count(span, name, params, args, 1)?;
let string = ensure_string(name, &params[0], &args[0])?;
@@ -143,7 +143,7 @@ fn tenb_exp(suffix: &str) -> Option<i32> {
})
}
fn parse_bytes(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
fn parse_bytes(span: &Span, params: &[Ref<Expr>], args: &[Value], strict: bool) -> Result<Value> {
let name = "units.parse_bytes";
ensure_args_count(span, name, params, args, 1)?;
let string = ensure_string(name, &params[0], &args[0])?;
@@ -166,12 +166,15 @@ fn parse_bytes(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Valu
_ => (string, ""),
};
let v: Value = if number_part.starts_with('.') {
let v: Value = match if number_part.starts_with('.') {
serde_json::from_str(format!("0{number_part}").as_str())
} else {
serde_json::from_str(number_part)
}
.with_context(|| span.error("could not parse number"))?;
} {
Ok(v) => v,
Err(_) if strict => bail!(span.error("could not parse number")),
_ => return Ok(Value::Undefined),
};
let mut n = match v {
Value::Number(n) => n.clone(),

View File

@@ -71,6 +71,10 @@ impl Engine {
&self.modules
}
pub fn set_strict_builtin_errors(&mut self, b: bool) {
self.interpreter.set_strict_builtin_errors(b)
}
fn prepare_for_eval(&mut self, enable_tracing: bool) -> Result<()> {
self.interpreter.set_traces(enable_tracing);

View File

@@ -44,6 +44,7 @@ pub struct Interpreter {
no_rules_lookup: bool,
traces: Option<Vec<std::rc::Rc<str>>>,
allow_deprecated: bool,
strict_builtin_errors: bool,
}
impl Default for Interpreter {
@@ -130,6 +131,7 @@ impl Interpreter {
no_rules_lookup: false,
traces: None,
allow_deprecated: true,
strict_builtin_errors: true,
}
}
@@ -168,14 +170,18 @@ impl Interpreter {
};
}
pub fn set_strict_builtin_errors(&mut self, b: bool) {
self.strict_builtin_errors = b;
}
pub fn set_input(&mut self, input: Value) {
self.input = input;
info!("input: {:#?}", self.input);
}
pub fn init_with_document(&mut self) -> Result<()> {
*Self::make_or_get_value_mut(&mut self.with_document, &["data"])? = Value::new_object();
*Self::make_or_get_value_mut(&mut self.with_document, &["input"])? = Value::new_object();
*Self::make_or_get_value_mut(&mut self.with_document, &["data"])? = self.init_data.clone();
*Self::make_or_get_value_mut(&mut self.with_document, &["input"])? = self.input.clone();
Ok(())
}
@@ -789,6 +795,7 @@ impl Interpreter {
// OPA raises the error sometimes in static scenarios, but doesn't
// raise in scenarios due to data/input
(Expr::Array { .. }, _) | (Expr::Object { .. }, _) => Ok(false),
_ => {
let expr_value = self.lookup_or_eval_expr(cache, expr)?;
if expr_value == Value::Undefined {
@@ -1096,18 +1103,28 @@ impl Interpreter {
};
if path[0] == "input" || path[0] == "data" {
if path.len() > 1 {
let vref =
Self::make_or_get_value_mut(&mut self.with_document, &path[0..1])?;
match *vref {
Value::Object(_) => (),
_ => *vref = Value::new_object(),
}
}
*Self::make_or_get_value_mut(&mut self.with_document, &path[..])? = value;
} /* else if path.len() == 1 {
// TODO: handle var in current module.
} else {
// TODO: error about input, data
} */
}
/* else if path.len() == 1 {
// TODO: handle var in current module.
} else {
// TODO: error about input, data
} */
}
self.data = self.with_document["data"].clone();
self.input = self.with_document["input"].clone();
self.processed.clear();
(with_document, input, data, processed, with_functions)
} else {
(
@@ -1651,7 +1668,7 @@ impl Interpreter {
}
}
let v = builtin.0(span, params, &args[..])?;
let v = builtin.0(span, params, &args[..], self.strict_builtin_errors)?;
// Handle trace function.
// TODO: with modifier.
@@ -1694,18 +1711,29 @@ impl Interpreter {
_ => orig_fcn_path.clone(),
};
let empty = vec![];
let fcns_rules = match self.lookup_function_by_name(&fcn_path) {
Some(r) => r,
_ => {
if self.default_rules.get(&fcn_path).is_some()
|| self
.default_rules
.get(&get_path_string(fcn, Some(&self.current_module_path))?)
.is_some()
{
// process default functions later.
&empty
}
// Look up builtin function.
if let Ok(Some(builtin)) = self.lookup_builtin(span, &fcn_path) {
else if let Ok(Some(builtin)) = self.lookup_builtin(span, &fcn_path) {
let r = self.eval_builtin_call(span, &fcn_path.clone(), *builtin, params);
if orig_fcn_path != fcn_path {
self.with_functions.insert(orig_fcn_path, fcn_path);
}
return r;
} else {
bail!(span.error(format!("could not find function {fcn_path}").as_str()));
}
bail!(span.error(format!("could not find function {fcn_path}").as_str()));
}
};
@@ -1757,10 +1785,10 @@ impl Interpreter {
let mut type_match = BTreeSet::new();
for (idx, a) in args.iter().enumerate() {
if self
.make_bindings(false, &mut type_match, &mut cache, a, &param_values[idx])
.is_err()
{
let b =
self.make_bindings(false, &mut type_match, &mut cache, a, &param_values[idx]);
if b.ok() != Some(true) {
self.scopes = scopes;
continue 'outer;
}
@@ -1799,7 +1827,9 @@ impl Interpreter {
// If the function execution resulted in undefined, then propagate it.
Value::Undefined => Value::Undefined,
_ => bail!("internal error: function did not return set {value:?}"),
// Function returned a non set value
v => v.clone(),
};
// Restore local variables for current context.
@@ -1810,6 +1840,34 @@ impl Interpreter {
}
}
if results.is_empty() {
// Back up local variables of current function and empty
// the local variables of callee function.
let scopes = std::mem::take(&mut self.scopes);
if errors.is_empty() {
// Check if any default rules can be evaluated.
// TODO: with mod
let rules = match self.default_rules.get(&fcn_path).cloned() {
Some(rules) => Some(rules),
None => {
let fcn_path = get_path_string(fcn, Some(&self.current_module_path))?;
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() {
match self.eval_expr(value) {
Ok(v) => results.push(v),
Err(e) => errors.push(e),
}
}
}
}
}
self.scopes = scopes;
}
if results.is_empty() {
if errors.is_empty() {
return Ok(Value::Undefined);
@@ -1937,15 +1995,28 @@ impl Interpreter {
return Ok(v);
}
// Evaluate rule corresponding to longest matching path.
for i in (1..fields.len() + 1).rev() {
if fields.is_empty() {
bail!(span.error("this results in recursive evaluation of data."))
}
// Find the rule to which the var being looked up corresponds to. This is the prefix for
// which rules exist.
let mut found = false;
for i in 1..fields.len() + 1 {
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)?;
found = true;
break;
}
}
// TODO: emit this error only if the var can belong to a rego module; not data specified via json/yaml.
//if !no_error && !found {
// bail!(span.error("var is unsafe"));
//}
let _ = found;
Ok(Self::get_value_chained(self.data.clone(), fields))
} else if !self.modules.is_empty() {
let path = Parser::get_path_ref_components(&self.module.clone().unwrap().package.refr)?;
@@ -1960,10 +2031,8 @@ impl Interpreter {
return Ok(v);
}
// 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 rule_path = module_path + "." + name.text();
// Ensure that all the rules having common prefix (name) are evaluated.
let rule_path = "data.".to_owned() + &path.join(".");
if !no_error
&& self.rules.get(&rule_path).is_none()
@@ -1971,6 +2040,7 @@ impl Interpreter {
{
bail!(span.error("var is unsafe"));
}
self.ensure_rule_evaluated(rule_path)?;
let value = Self::get_value_chained(self.data.clone(), &path[..]);
@@ -2318,9 +2388,18 @@ impl Interpreter {
}
if let Rule::Default {
span, refr, value, ..
span,
refr,
value,
args,
..
} = rule.as_ref()
{
if !args.is_empty() {
// Non-zero function defaults are evaluated differently.
return Ok(());
}
let scopes = std::mem::take(&mut self.scopes);
let mut path =
@@ -2588,8 +2667,10 @@ impl Interpreter {
Expr::RefBrack { refr, .. } => refr,
_ => refr,
};
let path = Self::get_path_string(refr, None)?;
let path = self.current_module_path.clone() + "." + &path;
//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());
@@ -2617,8 +2698,10 @@ impl Interpreter {
_ => (refr, None),
};
let path = Self::get_path_string(refr, None)?;
let path = self.current_module_path.clone() + "." + &path;
//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.default_rules.entry(path) {
Entry::Occupied(o) => {
for (_, i) in o.get() {

View File

@@ -209,7 +209,9 @@ impl<'source> Parser<'source> {
match self.tok.0 {
TokenKind::Ident
if self.is_keyword(*span.text())
|| self.is_imported_future_keyword(*span.text()) =>
|| (self.is_imported_future_keyword(*span.text())
// contains can be the name of a builtin even when a keyword
&& *span.text() != "contains") =>
{
Err(self.source.error(
self.tok.1.line,
@@ -1425,6 +1427,25 @@ impl<'source> Parser<'source> {
self.expect("default", "while parsing default rule")?;
let rule_ref = Ref::new(self.parse_rule_ref()?);
let mut args = vec![];
if *self.token_text() == "(" {
self.next_token()?;
if *self.token_text() != ")" {
loop {
let arg = self.parse_ident()?;
if *arg.text() != "_" && args.iter().any(|a: &Span| *a.text() == *arg.text()) {
bail!(arg.error("repeating parameter name"));
}
args.push(arg);
if *self.token_text() == ")" || self.tok.0 == TokenKind::Eof {
break;
}
self.expect(",", "while parsing default rule parameters")?;
}
}
self.expect(")", "while parsing default rule parameters")?;
}
let op = match *self.token_text() {
"=" => AssignOp::Eq,
":=" => AssignOp::ColEq,
@@ -1443,6 +1464,7 @@ impl<'source> Parser<'source> {
Ok(Rule::Default {
span,
refr: rule_ref,
args: args.into_iter().map(|a| Ref::new(Expr::Var(a))).collect(),
op,
value,
})

View File

@@ -205,7 +205,8 @@ pub fn schedule<Str: Clone + std::cmp::Ord + std::fmt::Debug>(
#[derive(Clone, Default, Debug)]
pub struct Scope {
pub locals: BTreeSet<SourceStr>,
pub locals: BTreeMap<SourceStr, Span>,
pub unscoped: BTreeSet<SourceStr>,
pub inputs: BTreeSet<SourceStr>,
}
@@ -269,8 +270,24 @@ fn traverse(expr: &Ref<Expr>, f: &mut dyn FnMut(&Ref<Expr>) -> Result<bool>) ->
Ok(())
}
fn var_exists(name: &SourceStr, parent_scopes: &[Scope]) -> bool {
parent_scopes.iter().rev().any(|s| s.locals.contains(name))
fn var_exists(var: &Span, parent_scopes: &[Scope]) -> bool {
let name = var.source_str();
for pscope in parent_scopes.iter().rev() {
if pscope.unscoped.contains(&name) {
return true;
}
// Check parent scope vars defined using :=.
if let Some(s) = pscope.locals.get(&name) {
// Note: Since a rule cannot span multiple files, it is safe to check only
// the line numbers.
if s.line <= var.line {
// The variable was defined in parent scope prior to current comprehension.
return true;
}
}
}
false
}
fn gather_assigned_vars(
@@ -285,19 +302,19 @@ fn gather_assigned_vars(
// Record local var that can shadow input var.
Var(v) if can_shadow => {
scope.locals.insert(v.source_str());
scope.locals.insert(v.source_str(), v.clone());
Ok(false)
}
// Record input vars.
Var(v) if var_exists(&v.source_str(), parent_scopes) => {
Var(v) if var_exists(v, parent_scopes) => {
scope.inputs.insert(v.source_str());
Ok(false)
}
// Record local var.
Var(v) => {
scope.locals.insert(v.source_str());
scope.unscoped.insert(v.source_str());
Ok(false)
}
@@ -309,11 +326,8 @@ fn gather_assigned_vars(
fn gather_input_vars(expr: &Ref<Expr>, parent_scopes: &[Scope], scope: &mut Scope) -> Result<()> {
traverse(expr, &mut |e| match e.as_ref() {
Var(v) if var_exists(&v.source_str(), parent_scopes) => {
let var = v.source_str();
if !scope.locals.contains(&var) {
scope.inputs.insert(var);
}
Var(v) if !scope.unscoped.contains(&v.source_str()) && var_exists(v, parent_scopes) => {
scope.inputs.insert(v.source_str());
Ok(false)
}
_ => Ok(true),
@@ -322,14 +336,12 @@ fn gather_input_vars(expr: &Ref<Expr>, parent_scopes: &[Scope], scope: &mut Scop
fn gather_loop_vars(expr: &Ref<Expr>, parent_scopes: &[Scope], scope: &mut Scope) -> Result<()> {
traverse(expr, &mut |e| match e.as_ref() {
Var(v) if var_exists(&v.source_str(), parent_scopes) => Ok(false),
Var(v) if var_exists(v, parent_scopes) => Ok(false),
RefBrack { index, .. } => {
if let Var(v) = index.as_ref() {
if !matches!(*v.text(), "_" | "input" | "data")
&& !var_exists(&v.source_str(), parent_scopes)
{
if !matches!(*v.text(), "_" | "input" | "data") && !var_exists(v, parent_scopes) {
// Treat this as an index var.
scope.locals.insert(v.source_str());
scope.unscoped.insert(v.source_str());
}
}
Ok(true)
@@ -367,7 +379,7 @@ fn gather_vars(
pub struct Analyzer {
packages: BTreeMap<String, Scope>,
locals: BTreeMap<Ref<Query>, Scope>,
scope_table: BTreeMap<Ref<Query>, Scope>,
scopes: Vec<Scope>,
order: BTreeMap<Ref<Query>, Vec<u16>>,
functions: FunctionTable,
@@ -390,7 +402,7 @@ impl Analyzer {
pub fn new() -> Analyzer {
Analyzer {
packages: BTreeMap::new(),
locals: BTreeMap::new(),
scope_table: BTreeMap::new(),
scopes: vec![],
order: BTreeMap::new(),
functions: FunctionTable::new(),
@@ -407,7 +419,7 @@ impl Analyzer {
}
Ok(Schedule {
scopes: self.locals,
scopes: self.scope_table,
order: self.order,
})
}
@@ -421,7 +433,7 @@ impl Analyzer {
self.analyze_query(None, None, query, Scope::default())?;
Ok(Schedule {
scopes: self.locals,
scopes: self.scope_table,
order: self.order,
})
}
@@ -441,7 +453,7 @@ impl Analyzer {
..
} => get_root_var(refr)?,
};
scope.locals.insert(var);
scope.unscoped.insert(var);
}
}
@@ -529,9 +541,12 @@ impl Analyzer {
RuleHead::Set { key, .. } => (key.clone(), None, scope),
RuleHead::Func { args, assign, .. } => {
for a in args.iter() {
if let Var(v) = a.as_ref() {
scope.locals.insert(v.source_str());
}
traverse(a, &mut |e| {
if let Var(v) = e.as_ref() {
scope.unscoped.insert(v.source_str());
}
Ok(true)
})?;
}
(None, assign.as_ref().map(|a| a.value.clone()), scope)
}
@@ -549,7 +564,7 @@ impl Analyzer {
for stmt in &query.stmts {
match &stmt.literal {
Literal::SomeVars { vars, .. } => vars.iter().for_each(|v| {
scope.locals.insert(v.source_str());
scope.locals.insert(v.source_str(), v.clone());
}),
Literal::SomeIn {
key,
@@ -597,8 +612,9 @@ impl Analyzer {
}
// Remove input vars that are shadowed.
for v in &scope.locals {
for v in scope.locals.keys() {
scope.inputs.remove(v);
scope.unscoped.remove(v);
}
Ok(())
@@ -621,7 +637,7 @@ impl Analyzer {
_ => false,
};
if scope.locals.contains(&name)
if scope.locals.contains_key(&name) || scope.unscoped.contains(&name)
/*|| scope.inputs.contains(name) */
{
if !is_extra_arg {
@@ -637,7 +653,7 @@ impl Analyzer {
RefBrack { refr, index, .. } => {
if let Var(v) = index.as_ref() {
let var = v.source_str();
if scope.locals.contains(&var) {
if scope.locals.contains_key(&var) || scope.unscoped.contains(&var) {
let (rb_used_vars, rb_comprs) = Self::gather_used_vars_comprs_index_vars(
refr,
scope,
@@ -681,7 +697,7 @@ impl Analyzer {
let compr_scope = match compr.as_ref() {
Expr::ArrayCompr { query, term, .. } | Expr::SetCompr { query, term, .. } => {
self.analyze_query(None, Some(term.clone()), query, Scope::default())?;
self.locals.get(query)
self.scope_table.get(query)
}
Expr::ObjectCompr {
query, key, value, ..
@@ -692,7 +708,7 @@ impl Analyzer {
query,
Scope::default(),
)?;
self.locals.get(query)
self.scope_table.get(query)
}
_ => break,
};
@@ -700,7 +716,7 @@ impl Analyzer {
// Record vars used by the comprehension scope.
if let Some(compr_scope) = compr_scope {
for iv in &compr_scope.inputs {
if scope.locals.contains(iv) {
if scope.locals.contains_key(iv) || scope.unscoped.contains(iv) {
// Record possible first use of current scope's local var.
first_use.entry(iv.clone()).or_insert(compr.span().clone());
used_vars.push(iv.clone());
@@ -727,11 +743,13 @@ impl Analyzer {
traverse(expr, &mut |e| match e.as_ref() {
Var(v) => {
let var = v.source_str();
if scope.locals.contains(&var) {
if scope.locals.contains_key(&var) {
if check_first_use {
Self::check_first_use(v, first_use)?;
}
vars.push(var);
} else if scope.unscoped.contains(&var) {
vars.push(var);
}
Ok(false)
}
@@ -890,7 +908,7 @@ impl Analyzer {
non_vars: &mut Vec<Ref<Expr>>,
) -> Result<()> {
traverse(expr, &mut |e| match e.as_ref() {
Var(v) if scope.locals.contains(&v.source_str()) => {
Var(v) if scope.locals.contains_key(&v.source_str()) => {
vars.push(v.source_str());
Ok(false)
}
@@ -1012,8 +1030,8 @@ impl Analyzer {
let mut extras_scope = Scope::default();
gather_assigned_vars(ea, false, &self.scopes, &mut extras_scope)?;
for var in &extras_scope.locals {
scope.locals.insert(var.clone());
for var in &extras_scope.unscoped {
scope.unscoped.insert(var.clone());
}
// Gather vars being used.
@@ -1022,7 +1040,7 @@ impl Analyzer {
&mut scope,
&mut first_use,
&mut definitions,
&Some(&extras_scope.locals),
&Some(&extras_scope.unscoped),
)?;
self.process_comprs(
@@ -1032,8 +1050,8 @@ impl Analyzer {
&mut used_vars,
)?;
if !extras_scope.locals.is_empty() {
for var in extras_scope.locals {
if !extras_scope.unscoped.is_empty() {
for var in extras_scope.unscoped {
definitions.push(Definition {
var,
used_vars: used_vars.clone(),
@@ -1076,9 +1094,9 @@ impl Analyzer {
self.scopes.push(scope.clone());
let mut e_scope = Scope::default();
if let Some(key) = key {
e_scope.locals.insert(key.source_str());
e_scope.locals.insert(key.source_str(), key.clone());
}
e_scope.locals.insert(value.source_str());
e_scope.locals.insert(value.source_str(), value.clone());
self.scopes.push(e_scope);
// TODO: mark first use of key, value so that they cannot be := assigned
@@ -1112,7 +1130,7 @@ impl Analyzer {
}
_ => (),
}
self.locals.insert(query.clone(), scope);
self.scope_table.insert(query.clone(), scope);
Ok(())
}