diff --git a/src/builtins/aggregates.rs b/src/builtins/aggregates.rs index f563eed..d7c73ec 100644 --- a/src/builtins/aggregates.rs +++ b/src/builtins/aggregates.rs @@ -2,13 +2,25 @@ // Licensed under the MIT License. use crate::ast::Expr; +use crate::builtins; use crate::builtins::utils::{ensure_args_count, ensure_numeric}; use crate::lexer::Span; use crate::value::{Float, Value}; +use std::collections::HashMap; + use anyhow::{bail, Result}; -pub fn count(span: &Span, params: &[Expr], args: &[Value]) -> Result { +pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) { + m.insert("count", count); + m.insert("max", max); + m.insert("min", min); + m.insert("product", product); + m.insert("sort", sort); + m.insert("sum", sum); +} + +fn count(span: &Span, params: &[Expr], args: &[Value]) -> Result { ensure_args_count(span, "count", params, args, 1)?; Ok(Value::from_float(match &args[0] { @@ -25,7 +37,7 @@ pub fn count(span: &Span, params: &[Expr], args: &[Value]) -> Result { })) } -pub fn max(span: &Span, params: &[Expr], args: &[Value]) -> Result { +fn max(span: &Span, params: &[Expr], args: &[Value]) -> Result { ensure_args_count(span, "max", params, args, 1)?; Ok(match &args[0] { @@ -40,7 +52,7 @@ pub fn max(span: &Span, params: &[Expr], args: &[Value]) -> Result { }) } -pub fn min(span: &Span, params: &[Expr], args: &[Value]) -> Result { +fn min(span: &Span, params: &[Expr], args: &[Value]) -> Result { ensure_args_count(span, "min", params, args, 1)?; Ok(match &args[0] { @@ -55,7 +67,7 @@ pub fn min(span: &Span, params: &[Expr], args: &[Value]) -> Result { }) } -pub fn product(span: &Span, params: &[Expr], args: &[Value]) -> Result { +fn product(span: &Span, params: &[Expr], args: &[Value]) -> Result { ensure_args_count(span, "min", params, args, 1)?; let mut v = 1 as Float; @@ -80,7 +92,7 @@ pub fn product(span: &Span, params: &[Expr], args: &[Value]) -> Result { }) } -pub fn sort(span: &Span, params: &[Expr], args: &[Value]) -> Result { +fn sort(span: &Span, params: &[Expr], args: &[Value]) -> Result { ensure_args_count(span, "sort", params, args, 1)?; Ok(match &args[0] { Value::Array(a) => { @@ -96,7 +108,7 @@ pub fn sort(span: &Span, params: &[Expr], args: &[Value]) -> Result { }) } -pub fn sum(span: &Span, params: &[Expr], args: &[Value]) -> Result { +fn sum(span: &Span, params: &[Expr], args: &[Value]) -> Result { ensure_args_count(span, "sum", params, args, 1)?; let mut v = 0 as Float; diff --git a/src/builtins/arrays.rs b/src/builtins/arrays.rs index a8730a1..781a2f2 100644 --- a/src/builtins/arrays.rs +++ b/src/builtins/arrays.rs @@ -2,14 +2,22 @@ // Licensed under the MIT License. use crate::ast::Expr; +use crate::builtins; use crate::builtins::utils::{ensure_args_count, ensure_array}; use crate::lexer::Span; use crate::value::Value; +use std::collections::HashMap; + use anyhow::Result; use std::rc::Rc; -pub fn concat(span: &Span, params: &[Expr], args: &[Value]) -> Result { +pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) { + m.insert("array.concat", concat); + m.insert("array.reverse", reverse); +} + +fn concat(span: &Span, params: &[Expr], args: &[Value]) -> Result { let name = "array.concat"; ensure_args_count(span, name, params, args, 2)?; let mut v1 = ensure_array(name, ¶ms[0], args[0].clone())?; @@ -19,7 +27,7 @@ pub fn concat(span: &Span, params: &[Expr], args: &[Value]) -> Result { Ok(Value::Array(v1)) } -pub fn reverse(span: &Span, params: &[Expr], args: &[Value]) -> Result { +fn reverse(span: &Span, params: &[Expr], args: &[Value]) -> Result { let name = "array.reverse"; ensure_args_count(span, name, params, args, 2)?; @@ -29,5 +37,5 @@ pub fn reverse(span: &Span, params: &[Expr], args: &[Value]) -> Result { } /* -pub fn slice(span: &Span, params: &[Expr], args: &[Value]) -> Result { +fn slice(span: &Span, params: &[Expr], args: &[Value]) -> Result { */ diff --git a/src/builtins/mod.rs b/src/builtins/mod.rs index 78cf943..ef5b9aa 100644 --- a/src/builtins/mod.rs +++ b/src/builtins/mod.rs @@ -6,6 +6,7 @@ pub mod arrays; pub mod comparison; pub mod numbers; pub mod sets; +pub mod types; pub mod utils; use crate::ast::Expr; @@ -24,29 +25,9 @@ lazy_static! { pub static ref BUILTINS: HashMap<&'static str, BuiltinFcn> = { let mut m : HashMap<&'static str, BuiltinFcn> = HashMap::new(); - // numbers - m.insert("abs", numbers::abs); - m.insert("ceil", numbers::ceil); - m.insert("floor", numbers::floor); - m.insert("numbers.range", numbers::range); - m.insert("rand.intn", numbers::intn); - m.insert("round", numbers::round); - - // aggregates - m.insert("count", aggregates::count); - m.insert("max", aggregates::max); - m.insert("min", aggregates::min); - m.insert("product", aggregates::product); - m.insert("sort", aggregates::sort); - m.insert("sum", aggregates::sum); - - // arrays - m.insert("array.concat", arrays::concat); - m.insert("array.reverse", arrays::reverse); - - // sets - m.insert("intersection", sets::intersection_of_set_of_sets); - m.insert("union", sets::union_of_set_of_sets); + numbers::register(&mut m); + aggregates::register(&mut m); + arrays::register(&mut m); m }; diff --git a/src/builtins/numbers.rs b/src/builtins/numbers.rs index be6424b..08eff66 100644 --- a/src/builtins/numbers.rs +++ b/src/builtins/numbers.rs @@ -2,82 +2,23 @@ // Licensed under the MIT License. use crate::ast::{ArithOp, Expr}; +use crate::builtins; use crate::builtins::utils::{ensure_args_count, ensure_numeric, ensure_string}; use crate::lexer::Span; use crate::value::{Float, Value}; +use std::collections::HashMap; + use anyhow::Result; use rand::{thread_rng, Rng}; -pub fn abs(span: &Span, params: &[Expr], args: &[Value]) -> Result { - ensure_args_count(span, "abs", params, args, 1)?; - Ok(Value::from_float( - ensure_numeric("abs", ¶ms[0], &args[0])?.abs(), - )) -} - -pub fn ceil(span: &Span, params: &[Expr], args: &[Value]) -> Result { - ensure_args_count(span, "ceil", params, args, 1)?; - Ok(Value::from_float( - ensure_numeric("ceil", ¶ms[0], &args[0])?.ceil(), - )) -} - -pub fn floor(span: &Span, params: &[Expr], args: &[Value]) -> Result { - ensure_args_count(span, "floor", params, args, 1)?; - Ok(Value::from_float( - ensure_numeric("floor", ¶ms[0], &args[0])?.floor(), - )) -} - -pub fn range(span: &Span, params: &[Expr], args: &[Value]) -> Result { - 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())?; - - if v1 != v1.floor() || v2 != v2.floor() { - // TODO: OPA returns undefined here. - // Can we emit a warning? - return Ok(Value::Undefined); - } - let incr = if v2 >= v1 { 1 } else { -1 } as Float; - - let mut values = vec![]; - values.reserve((v2 - v1).abs() as usize + 1); - - let mut v = v1; - while v != v2 { - values.push(Value::from_float(v)); - v += incr; - } - values.push(Value::from_float(v)); - Ok(Value::from_array(values)) -} - -pub fn round(span: &Span, params: &[Expr], args: &[Value]) -> Result { - ensure_args_count(span, "round", params, args, 1)?; - Ok(Value::from_float( - ensure_numeric("round", ¶ms[0], &args[0])?.round(), - )) -} - -pub fn intn(span: &Span, params: &[Expr], args: &[Value]) -> Result { - let fcn = "rand.intn"; - ensure_args_count(span, fcn, params, args, 2)?; - let _ = ensure_string(fcn, ¶ms[0], &args[0])?; - let n = ensure_numeric(fcn, ¶ms[0], &args[1])?; - if n != n.floor() || n < 0 as Float { - return Ok(Value::Undefined); - } - - if n == 0.0 { - return Ok(Value::from_float(0 as Float)); - } - - // TODO: bounds checking; arbitrary precision - let mut rng = thread_rng(); - let v = rng.gen_range(0..n as u64); - Ok(Value::from_float(v as f64)) +pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) { + m.insert("abs", abs); + m.insert("ceil", ceil); + m.insert("floor", floor); + m.insert("numbers.range", range); + m.insert("rand.intn", intn); + m.insert("round", round); } pub fn arithmetic_operation( @@ -103,3 +44,74 @@ pub fn arithmetic_operation( ArithOp::Mod => v1 % v2, })) } + +fn abs(span: &Span, params: &[Expr], args: &[Value]) -> Result { + ensure_args_count(span, "abs", params, args, 1)?; + Ok(Value::from_float( + ensure_numeric("abs", ¶ms[0], &args[0])?.abs(), + )) +} + +fn ceil(span: &Span, params: &[Expr], args: &[Value]) -> Result { + ensure_args_count(span, "ceil", params, args, 1)?; + Ok(Value::from_float( + ensure_numeric("ceil", ¶ms[0], &args[0])?.ceil(), + )) +} + +fn floor(span: &Span, params: &[Expr], args: &[Value]) -> Result { + ensure_args_count(span, "floor", params, args, 1)?; + Ok(Value::from_float( + ensure_numeric("floor", ¶ms[0], &args[0])?.floor(), + )) +} + +fn range(span: &Span, params: &[Expr], args: &[Value]) -> Result { + 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())?; + + if v1 != v1.floor() || v2 != v2.floor() { + // TODO: OPA returns undefined here. + // Can we emit a warning? + return Ok(Value::Undefined); + } + let incr = if v2 >= v1 { 1 } else { -1 } as Float; + + let mut values = vec![]; + values.reserve((v2 - v1).abs() as usize + 1); + + let mut v = v1; + while v != v2 { + values.push(Value::from_float(v)); + v += incr; + } + values.push(Value::from_float(v)); + Ok(Value::from_array(values)) +} + +fn round(span: &Span, params: &[Expr], args: &[Value]) -> Result { + ensure_args_count(span, "round", params, args, 1)?; + Ok(Value::from_float( + ensure_numeric("round", ¶ms[0], &args[0])?.round(), + )) +} + +fn intn(span: &Span, params: &[Expr], args: &[Value]) -> Result { + let fcn = "rand.intn"; + ensure_args_count(span, fcn, params, args, 2)?; + let _ = ensure_string(fcn, ¶ms[0], &args[0])?; + let n = ensure_numeric(fcn, ¶ms[0], &args[1])?; + if n != n.floor() || n < 0 as Float { + return Ok(Value::Undefined); + } + + if n == 0.0 { + return Ok(Value::from_float(0 as Float)); + } + + // TODO: bounds checking; arbitrary precision + let mut rng = thread_rng(); + let v = rng.gen_range(0..n as u64); + Ok(Value::from_float(v as f64)) +} diff --git a/src/builtins/sets.rs b/src/builtins/sets.rs index d843b9e..de5e65b 100644 --- a/src/builtins/sets.rs +++ b/src/builtins/sets.rs @@ -2,18 +2,18 @@ // Licensed under the MIT License. use crate::ast::Expr; +use crate::builtins; use crate::builtins::utils::{ensure_args_count, ensure_set}; use crate::lexer::Span; use crate::value::Value; -use std::collections::BTreeSet; +use std::collections::{BTreeSet, HashMap}; use anyhow::{bail, Result}; -pub fn difference(expr1: &Expr, expr2: &Expr, v1: Value, v2: Value) -> Result { - let s1 = ensure_set("difference", expr1, v1)?; - let s2 = ensure_set("difference", expr2, v2)?; - Ok(Value::from_set(s1.difference(&s2).cloned().collect())) +pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) { + m.insert("intersection", intersection_of_set_of_sets); + m.insert("union", union_of_set_of_sets); } pub fn intersection(expr1: &Expr, expr2: &Expr, v1: Value, v2: Value) -> Result { @@ -28,7 +28,13 @@ pub fn union(expr1: &Expr, expr2: &Expr, v1: Value, v2: Value) -> Result Ok(Value::from_set(s1.union(&s2).cloned().collect())) } -pub fn intersection_of_set_of_sets(span: &Span, params: &[Expr], args: &[Value]) -> Result { +pub fn difference(expr1: &Expr, expr2: &Expr, v1: Value, v2: Value) -> Result { + let s1 = ensure_set("difference", expr1, v1)?; + let s2 = ensure_set("difference", expr2, v2)?; + Ok(Value::from_set(s1.difference(&s2).cloned().collect())) +} + +fn intersection_of_set_of_sets(span: &Span, params: &[Expr], args: &[Value]) -> Result { let name = "intersection"; ensure_args_count(span, name, params, args, 1)?; let set = ensure_set(name, ¶ms[0], args[0].clone())?; @@ -55,7 +61,7 @@ pub fn intersection_of_set_of_sets(span: &Span, params: &[Expr], args: &[Value]) Ok(Value::from_set(res)) } -pub fn union_of_set_of_sets(span: &Span, params: &[Expr], args: &[Value]) -> Result { +fn union_of_set_of_sets(span: &Span, params: &[Expr], args: &[Value]) -> Result { let name = "union"; ensure_args_count(span, name, params, args, 1)?; let set = ensure_set(name, ¶ms[0], args[0].clone())?; diff --git a/src/builtins/types.rs b/src/builtins/types.rs new file mode 100644 index 0000000..cc18dd3 --- /dev/null +++ b/src/builtins/types.rs @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::ast::Expr; +use crate::builtins; +use crate::builtins::utils::ensure_args_count; +use crate::lexer::Span; +use crate::value::Value; + +use std::collections::HashMap; + +use anyhow::Result; + +pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) { + m.insert("is_array", is_array); + m.insert("is_boolean", is_boolean); + m.insert("is_null", is_null); + m.insert("is_number", is_number); + m.insert("is_object", is_object); + m.insert("is_set", is_set); + m.insert("is_string", is_string); + m.insert("type_name", type_name); +} + +fn is_array(span: &Span, params: &[Expr], args: &[Value]) -> Result { + ensure_args_count(span, "is_array", params, args, 1)?; + Ok(Value::Bool(matches!(&args[0], Value::Array(_)))) +} + +fn is_boolean(span: &Span, params: &[Expr], args: &[Value]) -> Result { + ensure_args_count(span, "is_boolean", params, args, 1)?; + Ok(Value::Bool(matches!(&args[0], Value::Bool(_)))) +} + +fn is_null(span: &Span, params: &[Expr], args: &[Value]) -> Result { + ensure_args_count(span, "is_null", params, args, 1)?; + Ok(Value::Bool(matches!(&args[0], Value::Null))) +} + +fn is_number(span: &Span, params: &[Expr], args: &[Value]) -> Result { + ensure_args_count(span, "is_number", params, args, 1)?; + Ok(Value::Bool(matches!(&args[0], Value::Number(_)))) +} + +fn is_object(span: &Span, params: &[Expr], args: &[Value]) -> Result { + ensure_args_count(span, "is_object", params, args, 1)?; + Ok(Value::Bool(matches!(&args[0], Value::Object(_)))) +} + +fn is_set(span: &Span, params: &[Expr], args: &[Value]) -> Result { + ensure_args_count(span, "is_set", params, args, 1)?; + Ok(Value::Bool(matches!(&args[0], Value::Set(_)))) +} + +fn is_string(span: &Span, params: &[Expr], args: &[Value]) -> Result { + ensure_args_count(span, "is_string", params, args, 1)?; + Ok(Value::Bool(matches!(&args[0], Value::String(_)))) +} + +fn type_name(span: &Span, params: &[Expr], args: &[Value]) -> Result { + ensure_args_count(span, "type_name", params, args, 1)?; + Ok(Value::String( + match &args[0] { + Value::Null => "null", + Value::Bool(_) => "boolean", + Value::Number(_) => "number", + Value::String(_) => "string", + Value::Array(_) => "array", + Value::Object(_) => "object", + Value::Set(_) => "set", + Value::Undefined => "undefined", + } + .to_string(), + )) +}