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),
|
||||
}
|
||||
|
||||
+72
-19
@@ -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(),
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
+12
-3
@@ -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(),
|
||||
))
|
||||
}
|
||||
|
||||
+38
-5
@@ -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."));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user