From d84096024d335c3bcfa50c080c39fbd22b29f58c Mon Sep 17 00:00:00 2001 From: Anand Krishnamoorthi Date: Sat, 18 Feb 2023 05:04:35 -0800 Subject: [PATCH] Arrays and Aggregates Signed-off-by: Anand Krishnamoorthi --- src/builtins/aggregates.rs | 122 +++++++++++++++++++++++++++++++++++++ src/builtins/arrays.rs | 33 ++++++++++ src/builtins/mod.rs | 18 +++++- src/builtins/utils.rs | 10 +++ 4 files changed, 181 insertions(+), 2 deletions(-) create mode 100644 src/builtins/aggregates.rs create mode 100644 src/builtins/arrays.rs diff --git a/src/builtins/aggregates.rs b/src/builtins/aggregates.rs new file mode 100644 index 0000000..f563eed --- /dev/null +++ b/src/builtins/aggregates.rs @@ -0,0 +1,122 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::ast::Expr; +use crate::builtins::utils::{ensure_args_count, ensure_numeric}; +use crate::lexer::Span; +use crate::value::{Float, Value}; + +use anyhow::{bail, Result}; + +pub fn count(span: &Span, params: &[Expr], args: &[Value]) -> Result { + ensure_args_count(span, "count", params, args, 1)?; + + Ok(Value::from_float(match &args[0] { + Value::Array(a) => a.len() as Float, + Value::Set(a) => a.len() as Float, + Value::Object(a) => a.len() as Float, + Value::String(a) => a.len() as Float, + a => { + let span = params[0].span(); + bail!(span.error( + format!("`count` requires array/object/set/string argument. Got `{a}`.").as_str() + )) + } + })) +} + +pub fn max(span: &Span, params: &[Expr], args: &[Value]) -> Result { + ensure_args_count(span, "max", params, args, 1)?; + + Ok(match &args[0] { + Value::Array(a) if a.is_empty() => Value::Undefined, + Value::Array(a) => a.iter().max().unwrap().clone(), + Value::Set(a) if a.is_empty() => Value::Undefined, + Value::Set(a) => a.iter().max().unwrap().clone(), + a => { + let span = params[0].span(); + bail!(span.error(format!("`max` requires array/set argument. Got `{a}`.").as_str())) + } + }) +} + +pub fn min(span: &Span, params: &[Expr], args: &[Value]) -> Result { + ensure_args_count(span, "min", params, args, 1)?; + + Ok(match &args[0] { + Value::Array(a) if a.is_empty() => Value::Undefined, + Value::Array(a) => a.iter().max().unwrap().clone(), + Value::Set(a) if a.is_empty() => Value::Undefined, + Value::Set(a) => a.iter().max().unwrap().clone(), + a => { + let span = params[0].span(); + bail!(span.error(format!("`min` requires array/set argument. Got `{a}`.").as_str())) + } + }) +} + +pub fn product(span: &Span, params: &[Expr], args: &[Value]) -> Result { + ensure_args_count(span, "min", params, args, 1)?; + + let mut v = 1 as Float; + Ok(match &args[0] { + Value::Array(a) => { + for e in a.iter() { + v *= ensure_numeric("product", ¶ms[0], e)?; + } + Value::from_float(v) + } + + Value::Set(a) => { + for e in a.iter() { + v *= ensure_numeric("product", ¶ms[0], e)?; + } + Value::from_float(v) + } + a => { + let span = params[0].span(); + bail!(span.error(format!("`product` requires array/set argument. Got `{a}`.").as_str())) + } + }) +} + +pub fn sort(span: &Span, params: &[Expr], args: &[Value]) -> Result { + ensure_args_count(span, "sort", params, args, 1)?; + Ok(match &args[0] { + Value::Array(a) => { + let mut ac = (**a).clone(); + ac.sort(); + Value::from_array(ac) + } + Value::Set(a) => Value::from_set(a.iter().cloned().collect()), + a => { + let span = params[0].span(); + bail!(span.error(format!("`sort` requires array/set argument. Got `{a}`.").as_str())) + } + }) +} + +pub fn sum(span: &Span, params: &[Expr], args: &[Value]) -> Result { + ensure_args_count(span, "sum", params, args, 1)?; + + let mut v = 0 as Float; + Ok(match &args[0] { + Value::Array(a) => { + for e in a.iter() { + v += ensure_numeric("sum", ¶ms[0], e)?; + } + Value::from_float(v) + } + + Value::Set(a) => { + for e in a.iter() { + v += ensure_numeric("sum", ¶ms[0], e)?; + } + Value::from_float(v) + } + a => { + let span = params[0].span(); + bail!(span.error(format!("`sum` requires array/set argument. Got `{a}`.").as_str())) + } + }) +} diff --git a/src/builtins/arrays.rs b/src/builtins/arrays.rs new file mode 100644 index 0000000..a8730a1 --- /dev/null +++ b/src/builtins/arrays.rs @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::ast::Expr; +use crate::builtins::utils::{ensure_args_count, ensure_array}; +use crate::lexer::Span; +use crate::value::Value; + +use anyhow::Result; +use std::rc::Rc; + +pub 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())?; + let mut v2 = ensure_array(name, ¶ms[1], args[1].clone())?; + + Rc::make_mut(&mut v1).append(Rc::make_mut(&mut v2)); + Ok(Value::Array(v1)) +} + +pub fn reverse(span: &Span, params: &[Expr], args: &[Value]) -> Result { + let name = "array.reverse"; + ensure_args_count(span, name, params, args, 2)?; + + let mut v1 = ensure_array(name, ¶ms[0], args[0].clone())?; + Rc::make_mut(&mut v1).reverse(); + Ok(Value::Array(v1)) +} + +/* +pub fn slice(span: &Span, params: &[Expr], args: &[Value]) -> Result { +*/ diff --git a/src/builtins/mod.rs b/src/builtins/mod.rs index 1ce5844..78cf943 100644 --- a/src/builtins/mod.rs +++ b/src/builtins/mod.rs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +pub mod aggregates; +pub mod arrays; pub mod comparison; pub mod numbers; pub mod sets; @@ -28,12 +30,24 @@ lazy_static! { m.insert("floor", numbers::floor); m.insert("numbers.range", numbers::range); m.insert("rand.intn", numbers::intn); - m.insert("round", numbers::round); + 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); - + m }; } diff --git a/src/builtins/utils.rs b/src/builtins/utils.rs index b5f30d3..f1f8ddc 100644 --- a/src/builtins/utils.rs +++ b/src/builtins/utils.rs @@ -53,6 +53,16 @@ pub fn ensure_string(fcn: &str, arg: &Expr, v: &Value) -> Result { }) } +pub fn ensure_array(fcn: &str, arg: &Expr, v: Value) -> Result>> { + Ok(match v { + Value::Array(a) => a, + _ => { + let span = arg.span(); + bail!(span.error(format!("`{fcn}` expects array argument. Got `{v}` instead").as_str())) + } + }) +} + pub fn ensure_set(fcn: &str, arg: &Expr, v: Value) -> Result>> { Ok(match v { Value::Set(s) => s,