From ad79a4748f02e0718a79561af9e8efe415ea6925 Mon Sep 17 00:00:00 2001 From: Ziyang Hu Date: Thu, 1 Sep 2022 20:07:33 +0800 Subject: [PATCH] start documentation --- docs/README.md | 4 +- docs/_sidebar.md | 4 +- docs/datatypes.md | 31 +++++- docs/functions.md | 9 ++ src/data/expr.rs | 190 ++++++++++++++++++------------------ src/data/mod.rs | 3 + src/data/tests/functions.rs | 73 ++++++++++++++ src/data/tests/mod.rs | 1 + 8 files changed, 216 insertions(+), 99 deletions(-) create mode 100644 docs/functions.md create mode 100644 src/data/tests/functions.rs create mode 100644 src/data/tests/mod.rs diff --git a/docs/README.md b/docs/README.md index e54e32d5..eca96645 100644 --- a/docs/README.md +++ b/docs/README.md @@ -4,6 +4,6 @@ Today we start. It is a great start, -```rust -let x = y +``` +let x = 1; ``` \ No newline at end of file diff --git a/docs/_sidebar.md b/docs/_sidebar.md index cc3c6370..483760e5 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -1,2 +1,4 @@ * [Home](/) -* [Guide](datatypes.md) \ No newline at end of file +* Manual + * [Datatypes](datatypes.md) + * [Functions](functions.md) \ No newline at end of file diff --git a/docs/datatypes.md b/docs/datatypes.md index ac2a9de9..5100c1c4 100644 --- a/docs/datatypes.md +++ b/docs/datatypes.md @@ -1,3 +1,32 @@ # Datatypes -Today we are going to learn about datatypes. Yeah! We don't get it! \ No newline at end of file +A runtime value in Cozo can be of the following _value-types_: +* Null +* Bool +* Number +* String +* Bytes +* List + +Number can be Float (double precision) or Int (signed, 64 bits). Cozo will do auto promotion from Int to Float when necessary. + +List can contain any number of mixed-type values, including other lists. + +Cozo defines a total order for all values according to the above order. Lists are ordered lexicographically by their elements. Strings are ordered lexicographically by their UTF-8 byte representation. + +In schema definition, the required type for a value can be specified by any of the following _schema-types_ + +* Ref +* Component +* Int +* Float +* Bool +* String +* Bytes +* List + +When retrieving values of triples, values of the first three schema-types (Ref, Component, Int) are all represented by the value-type Number (actually Int). + +Note the absence of Null type in schema-types. + +When asserting (inserting or updating) triples, if a value given is not of the correct schema-type, Cozo will first try to coerce the value and will only error out if no known coercion methods exist. \ No newline at end of file diff --git a/docs/functions.md b/docs/functions.md new file mode 100644 index 00000000..bbe54e15 --- /dev/null +++ b/docs/functions.md @@ -0,0 +1,9 @@ +# Functions + +All functions in Cozo are stateless. + +All except random functions in Cozo are deterministic. + +## Basic arithmetics + +The four basic arithmetic operators `+`, `-`, `*`, `/` do what you expect, with the usual operator precedence. The precedence can be overridden by parentheses `()`, as usual. \ No newline at end of file diff --git a/src/data/expr.rs b/src/data/expr.rs index 436d022e..cc65e5d9 100644 --- a/src/data/expr.rs +++ b/src/data/expr.rs @@ -401,7 +401,7 @@ pub(crate) fn op_is_in(args: &[DataValue]) -> Result { } define_op!(OP_NEQ, 2, false, true); -fn op_neq(args: &[DataValue]) -> Result { +pub(crate) fn op_neq(args: &[DataValue]) -> Result { Ok(DataValue::Bool(match (&args[0], &args[1]) { (DataValue::Number(Number::Float(f)), DataValue::Number(Number::Int(i))) | (DataValue::Number(Number::Int(i)), DataValue::Number(Number::Float(f))) => { @@ -412,27 +412,27 @@ fn op_neq(args: &[DataValue]) -> Result { } define_op!(OP_GT, 2, false, true); -fn op_gt(args: &[DataValue]) -> Result { +pub(crate) fn op_gt(args: &[DataValue]) -> Result { Ok(DataValue::Bool(args[0] > args[1])) } define_op!(OP_GE, 2, false, true); -fn op_ge(args: &[DataValue]) -> Result { +pub(crate) fn op_ge(args: &[DataValue]) -> Result { Ok(DataValue::Bool(args[0] >= args[1])) } define_op!(OP_LT, 2, false, true); -fn op_lt(args: &[DataValue]) -> Result { +pub(crate) fn op_lt(args: &[DataValue]) -> Result { Ok(DataValue::Bool(args[0] < args[1])) } define_op!(OP_LE, 2, false, true); -fn op_le(args: &[DataValue]) -> Result { +pub(crate) fn op_le(args: &[DataValue]) -> Result { Ok(DataValue::Bool(args[0] <= args[1])) } define_op!(OP_ADD, 0, true, false); -fn op_add(args: &[DataValue]) -> Result { +pub(crate) fn op_add(args: &[DataValue]) -> Result { let mut i_accum = 0i64; let mut f_accum = 0.0f64; for arg in args { @@ -450,7 +450,7 @@ fn op_add(args: &[DataValue]) -> Result { } define_op!(OP_MAX, 0, true, false); -fn op_max(args: &[DataValue]) -> Result { +pub(crate) fn op_max(args: &[DataValue]) -> Result { let res = args .iter() .try_fold(None, |accum, nxt| match (accum, nxt) { @@ -467,7 +467,7 @@ fn op_max(args: &[DataValue]) -> Result { } define_op!(OP_MIN, 0, true, false); -fn op_min(args: &[DataValue]) -> Result { +pub(crate) fn op_min(args: &[DataValue]) -> Result { let res = args .iter() .try_fold(None, |accum, nxt| match (accum, nxt) { @@ -484,7 +484,7 @@ fn op_min(args: &[DataValue]) -> Result { } define_op!(OP_SUB, 2, false, false); -fn op_sub(args: &[DataValue]) -> Result { +pub(crate) fn op_sub(args: &[DataValue]) -> Result { Ok(match (&args[0], &args[1]) { (DataValue::Number(Number::Int(a)), DataValue::Number(Number::Int(b))) => { DataValue::Number(Number::Int(*a - *b)) @@ -503,7 +503,7 @@ fn op_sub(args: &[DataValue]) -> Result { } define_op!(OP_MUL, 0, true, false); -fn op_mul(args: &[DataValue]) -> Result { +pub(crate) fn op_mul(args: &[DataValue]) -> Result { let mut i_accum = 1i64; let mut f_accum = 1.0f64; for arg in args { @@ -521,7 +521,7 @@ fn op_mul(args: &[DataValue]) -> Result { } define_op!(OP_DIV, 2, false, false); -fn op_div(args: &[DataValue]) -> Result { +pub(crate) fn op_div(args: &[DataValue]) -> Result { Ok(match (&args[0], &args[1]) { (DataValue::Number(Number::Int(a)), DataValue::Number(Number::Int(b))) => { DataValue::Number(Number::Float((*a as f64) / (*b as f64))) @@ -540,7 +540,7 @@ fn op_div(args: &[DataValue]) -> Result { } define_op!(OP_MINUS, 1, false, false); -fn op_minus(args: &[DataValue]) -> Result { +pub(crate) fn op_minus(args: &[DataValue]) -> Result { Ok(match &args[0] { DataValue::Number(Number::Int(i)) => DataValue::Number(Number::Int(-(*i))), DataValue::Number(Number::Float(f)) => DataValue::Number(Number::Float(-(*f))), @@ -549,7 +549,7 @@ fn op_minus(args: &[DataValue]) -> Result { } define_op!(OP_ABS, 1, false, false); -fn op_abs(args: &[DataValue]) -> Result { +pub(crate) fn op_abs(args: &[DataValue]) -> Result { Ok(match &args[0] { DataValue::Number(Number::Int(i)) => DataValue::Number(Number::Int(i.abs())), DataValue::Number(Number::Float(f)) => DataValue::Number(Number::Float(f.abs())), @@ -558,7 +558,7 @@ fn op_abs(args: &[DataValue]) -> Result { } define_op!(OP_SIGNUM, 1, false, false); -fn op_signum(args: &[DataValue]) -> Result { +pub(crate) fn op_signum(args: &[DataValue]) -> Result { Ok(match &args[0] { DataValue::Number(Number::Int(i)) => DataValue::Number(Number::Int(i.signum())), DataValue::Number(Number::Float(f)) => DataValue::Number(Number::Float(f.signum())), @@ -567,7 +567,7 @@ fn op_signum(args: &[DataValue]) -> Result { } define_op!(OP_FLOOR, 1, false, false); -fn op_floor(args: &[DataValue]) -> Result { +pub(crate) fn op_floor(args: &[DataValue]) -> Result { Ok(match &args[0] { DataValue::Number(Number::Int(i)) => DataValue::Number(Number::Int(*i)), DataValue::Number(Number::Float(f)) => DataValue::Number(Number::Float(f.floor())), @@ -576,7 +576,7 @@ fn op_floor(args: &[DataValue]) -> Result { } define_op!(OP_CEIL, 1, false, false); -fn op_ceil(args: &[DataValue]) -> Result { +pub(crate) fn op_ceil(args: &[DataValue]) -> Result { Ok(match &args[0] { DataValue::Number(Number::Int(i)) => DataValue::Number(Number::Int(*i)), DataValue::Number(Number::Float(f)) => DataValue::Number(Number::Float(f.ceil())), @@ -585,7 +585,7 @@ fn op_ceil(args: &[DataValue]) -> Result { } define_op!(OP_ROUND, 1, false, false); -fn op_round(args: &[DataValue]) -> Result { +pub(crate) fn op_round(args: &[DataValue]) -> Result { Ok(match &args[0] { DataValue::Number(Number::Int(i)) => DataValue::Number(Number::Int(*i)), DataValue::Number(Number::Float(f)) => DataValue::Number(Number::Float(f.round())), @@ -594,7 +594,7 @@ fn op_round(args: &[DataValue]) -> Result { } define_op!(OP_EXP, 1, false, false); -fn op_exp(args: &[DataValue]) -> Result { +pub(crate) fn op_exp(args: &[DataValue]) -> Result { let a = match &args[0] { DataValue::Number(Number::Int(i)) => *i as f64, DataValue::Number(Number::Float(f)) => *f, @@ -604,7 +604,7 @@ fn op_exp(args: &[DataValue]) -> Result { } define_op!(OP_EXP2, 1, false, false); -fn op_exp2(args: &[DataValue]) -> Result { +pub(crate) fn op_exp2(args: &[DataValue]) -> Result { let a = match &args[0] { DataValue::Number(Number::Int(i)) => *i as f64, DataValue::Number(Number::Float(f)) => *f, @@ -614,7 +614,7 @@ fn op_exp2(args: &[DataValue]) -> Result { } define_op!(OP_LN, 1, false, false); -fn op_ln(args: &[DataValue]) -> Result { +pub(crate) fn op_ln(args: &[DataValue]) -> Result { let a = match &args[0] { DataValue::Number(Number::Int(i)) => *i as f64, DataValue::Number(Number::Float(f)) => *f, @@ -624,7 +624,7 @@ fn op_ln(args: &[DataValue]) -> Result { } define_op!(OP_LOG2, 1, false, false); -fn op_log2(args: &[DataValue]) -> Result { +pub(crate) fn op_log2(args: &[DataValue]) -> Result { let a = match &args[0] { DataValue::Number(Number::Int(i)) => *i as f64, DataValue::Number(Number::Float(f)) => *f, @@ -634,7 +634,7 @@ fn op_log2(args: &[DataValue]) -> Result { } define_op!(OP_LOG10, 1, false, false); -fn op_log10(args: &[DataValue]) -> Result { +pub(crate) fn op_log10(args: &[DataValue]) -> Result { let a = match &args[0] { DataValue::Number(Number::Int(i)) => *i as f64, DataValue::Number(Number::Float(f)) => *f, @@ -644,7 +644,7 @@ fn op_log10(args: &[DataValue]) -> Result { } define_op!(OP_SIN, 1, false, false); -fn op_sin(args: &[DataValue]) -> Result { +pub(crate) fn op_sin(args: &[DataValue]) -> Result { let a = match &args[0] { DataValue::Number(Number::Int(i)) => *i as f64, DataValue::Number(Number::Float(f)) => *f, @@ -654,7 +654,7 @@ fn op_sin(args: &[DataValue]) -> Result { } define_op!(OP_COS, 1, false, false); -fn op_cos(args: &[DataValue]) -> Result { +pub(crate) fn op_cos(args: &[DataValue]) -> Result { let a = match &args[0] { DataValue::Number(Number::Int(i)) => *i as f64, DataValue::Number(Number::Float(f)) => *f, @@ -664,7 +664,7 @@ fn op_cos(args: &[DataValue]) -> Result { } define_op!(OP_TAN, 1, false, false); -fn op_tan(args: &[DataValue]) -> Result { +pub(crate) fn op_tan(args: &[DataValue]) -> Result { let a = match &args[0] { DataValue::Number(Number::Int(i)) => *i as f64, DataValue::Number(Number::Float(f)) => *f, @@ -674,7 +674,7 @@ fn op_tan(args: &[DataValue]) -> Result { } define_op!(OP_ASIN, 1, false, false); -fn op_asin(args: &[DataValue]) -> Result { +pub(crate) fn op_asin(args: &[DataValue]) -> Result { let a = match &args[0] { DataValue::Number(Number::Int(i)) => *i as f64, DataValue::Number(Number::Float(f)) => *f, @@ -684,7 +684,7 @@ fn op_asin(args: &[DataValue]) -> Result { } define_op!(OP_ACOS, 1, false, false); -fn op_acos(args: &[DataValue]) -> Result { +pub(crate) fn op_acos(args: &[DataValue]) -> Result { let a = match &args[0] { DataValue::Number(Number::Int(i)) => *i as f64, DataValue::Number(Number::Float(f)) => *f, @@ -694,7 +694,7 @@ fn op_acos(args: &[DataValue]) -> Result { } define_op!(OP_ATAN, 1, false, false); -fn op_atan(args: &[DataValue]) -> Result { +pub(crate) fn op_atan(args: &[DataValue]) -> Result { let a = match &args[0] { DataValue::Number(Number::Int(i)) => *i as f64, DataValue::Number(Number::Float(f)) => *f, @@ -704,7 +704,7 @@ fn op_atan(args: &[DataValue]) -> Result { } define_op!(OP_ATAN2, 2, false, false); -fn op_atan2(args: &[DataValue]) -> Result { +pub(crate) fn op_atan2(args: &[DataValue]) -> Result { let a = match &args[0] { DataValue::Number(Number::Int(i)) => *i as f64, DataValue::Number(Number::Float(f)) => *f, @@ -720,7 +720,7 @@ fn op_atan2(args: &[DataValue]) -> Result { } define_op!(OP_SINH, 1, false, false); -fn op_sinh(args: &[DataValue]) -> Result { +pub(crate) fn op_sinh(args: &[DataValue]) -> Result { let a = match &args[0] { DataValue::Number(Number::Int(i)) => *i as f64, DataValue::Number(Number::Float(f)) => *f, @@ -730,7 +730,7 @@ fn op_sinh(args: &[DataValue]) -> Result { } define_op!(OP_COSH, 1, false, false); -fn op_cosh(args: &[DataValue]) -> Result { +pub(crate) fn op_cosh(args: &[DataValue]) -> Result { let a = match &args[0] { DataValue::Number(Number::Int(i)) => *i as f64, DataValue::Number(Number::Float(f)) => *f, @@ -740,7 +740,7 @@ fn op_cosh(args: &[DataValue]) -> Result { } define_op!(OP_TANH, 1, false, false); -fn op_tanh(args: &[DataValue]) -> Result { +pub(crate) fn op_tanh(args: &[DataValue]) -> Result { let a = match &args[0] { DataValue::Number(Number::Int(i)) => *i as f64, DataValue::Number(Number::Float(f)) => *f, @@ -750,7 +750,7 @@ fn op_tanh(args: &[DataValue]) -> Result { } define_op!(OP_ASINH, 1, false, false); -fn op_asinh(args: &[DataValue]) -> Result { +pub(crate) fn op_asinh(args: &[DataValue]) -> Result { let a = match &args[0] { DataValue::Number(Number::Int(i)) => *i as f64, DataValue::Number(Number::Float(f)) => *f, @@ -760,7 +760,7 @@ fn op_asinh(args: &[DataValue]) -> Result { } define_op!(OP_ACOSH, 1, false, false); -fn op_acosh(args: &[DataValue]) -> Result { +pub(crate) fn op_acosh(args: &[DataValue]) -> Result { let a = match &args[0] { DataValue::Number(Number::Int(i)) => *i as f64, DataValue::Number(Number::Float(f)) => *f, @@ -770,7 +770,7 @@ fn op_acosh(args: &[DataValue]) -> Result { } define_op!(OP_ATANH, 1, false, false); -fn op_atanh(args: &[DataValue]) -> Result { +pub(crate) fn op_atanh(args: &[DataValue]) -> Result { let a = match &args[0] { DataValue::Number(Number::Int(i)) => *i as f64, DataValue::Number(Number::Float(f)) => *f, @@ -780,7 +780,7 @@ fn op_atanh(args: &[DataValue]) -> Result { } define_op!(OP_POW, 2, false, false); -fn op_pow(args: &[DataValue]) -> Result { +pub(crate) fn op_pow(args: &[DataValue]) -> Result { let a = match &args[0] { DataValue::Number(Number::Int(i)) => *i as f64, DataValue::Number(Number::Float(f)) => *f, @@ -795,7 +795,7 @@ fn op_pow(args: &[DataValue]) -> Result { } define_op!(OP_MOD, 2, false, false); -fn op_mod(args: &[DataValue]) -> Result { +pub(crate) fn op_mod(args: &[DataValue]) -> Result { Ok(match (&args[0], &args[1]) { (DataValue::Number(Number::Int(a)), DataValue::Number(Number::Int(b))) => { DataValue::Number(Number::Int(a.rem(b))) @@ -814,7 +814,7 @@ fn op_mod(args: &[DataValue]) -> Result { } define_op!(OP_AND, 0, true, true); -fn op_and(args: &[DataValue]) -> Result { +pub(crate) fn op_and(args: &[DataValue]) -> Result { for arg in args { if let DataValue::Bool(b) = arg { if !b { @@ -828,7 +828,7 @@ fn op_and(args: &[DataValue]) -> Result { } define_op!(OP_OR, 0, true, true); -fn op_or(args: &[DataValue]) -> Result { +pub(crate) fn op_or(args: &[DataValue]) -> Result { for arg in args { if let DataValue::Bool(b) = arg { if *b { @@ -842,7 +842,7 @@ fn op_or(args: &[DataValue]) -> Result { } define_op!(OP_NOT, 1, false, true); -fn op_not(args: &[DataValue]) -> Result { +pub(crate) fn op_not(args: &[DataValue]) -> Result { if let DataValue::Bool(b) = &args[0] { Ok(DataValue::Bool(!*b)) } else { @@ -851,7 +851,7 @@ fn op_not(args: &[DataValue]) -> Result { } define_op!(OP_BIT_AND, 2, false, false); -fn op_bit_and(args: &[DataValue]) -> Result { +pub(crate) fn op_bit_and(args: &[DataValue]) -> Result { match (&args[0], &args[1]) { (DataValue::Bytes(left), DataValue::Bytes(right)) => { ensure!( @@ -871,7 +871,7 @@ fn op_bit_and(args: &[DataValue]) -> Result { } define_op!(OP_BIT_OR, 2, false, false); -fn op_bit_or(args: &[DataValue]) -> Result { +pub(crate) fn op_bit_or(args: &[DataValue]) -> Result { match (&args[0], &args[1]) { (DataValue::Bytes(left), DataValue::Bytes(right)) => { ensure!( @@ -891,7 +891,7 @@ fn op_bit_or(args: &[DataValue]) -> Result { } define_op!(OP_BIT_NOT, 1, false, false); -fn op_bit_not(args: &[DataValue]) -> Result { +pub(crate) fn op_bit_not(args: &[DataValue]) -> Result { match &args[0] { DataValue::Bytes(arg) => { let mut ret = arg.clone(); @@ -905,7 +905,7 @@ fn op_bit_not(args: &[DataValue]) -> Result { } define_op!(OP_BIT_XOR, 2, false, false); -fn op_bit_xor(args: &[DataValue]) -> Result { +pub(crate) fn op_bit_xor(args: &[DataValue]) -> Result { match (&args[0], &args[1]) { (DataValue::Bytes(left), DataValue::Bytes(right)) => { ensure!( @@ -925,7 +925,7 @@ fn op_bit_xor(args: &[DataValue]) -> Result { } define_op!(OP_UNPACK_BITS, 1, false, false); -fn op_unpack_bits(args: &[DataValue]) -> Result { +pub(crate) fn op_unpack_bits(args: &[DataValue]) -> Result { if let DataValue::Bytes(bs) = &args[0] { let mut ret = vec![false; bs.len() * 8]; for (chunk, byte) in bs.iter().enumerate() { @@ -947,7 +947,7 @@ fn op_unpack_bits(args: &[DataValue]) -> Result { } define_op!(OP_PACK_BITS, 1, false, false); -fn op_pack_bits(args: &[DataValue]) -> Result { +pub(crate) fn op_pack_bits(args: &[DataValue]) -> Result { if let DataValue::List(v) = &args[0] { let l = (v.len() as f64 / 8.).ceil() as usize; let mut res = vec![0u8; l]; @@ -981,7 +981,7 @@ fn op_pack_bits(args: &[DataValue]) -> Result { } define_op!(OP_STR_CAT, 0, true, false); -fn op_str_cat(args: &[DataValue]) -> Result { +pub(crate) fn op_str_cat(args: &[DataValue]) -> Result { let mut ret: String = Default::default(); for arg in args { if let DataValue::String(s) = arg { @@ -994,7 +994,7 @@ fn op_str_cat(args: &[DataValue]) -> Result { } define_op!(OP_STR_INCLUDES, 2, false, true); -fn op_str_includes(args: &[DataValue]) -> Result { +pub(crate) fn op_str_includes(args: &[DataValue]) -> Result { match (&args[0], &args[1]) { (DataValue::String(l), DataValue::String(r)) => { Ok(DataValue::Bool(l.find(r as &str).is_some())) @@ -1004,7 +1004,7 @@ fn op_str_includes(args: &[DataValue]) -> Result { } define_op!(OP_LOWERCASE, 1, false, false); -fn op_lowercase(args: &[DataValue]) -> Result { +pub(crate) fn op_lowercase(args: &[DataValue]) -> Result { match &args[0] { DataValue::String(s) => Ok(DataValue::String(s.to_lowercase().into())), v => bail!("cannot apply 'lowercase' to {:?}", v), @@ -1012,7 +1012,7 @@ fn op_lowercase(args: &[DataValue]) -> Result { } define_op!(OP_UPPERCASE, 1, false, false); -fn op_uppercase(args: &[DataValue]) -> Result { +pub(crate) fn op_uppercase(args: &[DataValue]) -> Result { match &args[0] { DataValue::String(s) => Ok(DataValue::String(s.to_uppercase().into())), v => bail!("cannot apply 'uppercase' to {:?}", v), @@ -1020,7 +1020,7 @@ fn op_uppercase(args: &[DataValue]) -> Result { } define_op!(OP_TRIM, 1, false, false); -fn op_trim(args: &[DataValue]) -> Result { +pub(crate) fn op_trim(args: &[DataValue]) -> Result { match &args[0] { DataValue::String(s) => Ok(DataValue::String(s.trim().into())), v => bail!("cannot apply 'trim' to {:?}", v), @@ -1028,7 +1028,7 @@ fn op_trim(args: &[DataValue]) -> Result { } define_op!(OP_TRIM_START, 1, false, false); -fn op_trim_start(args: &[DataValue]) -> Result { +pub(crate) fn op_trim_start(args: &[DataValue]) -> Result { match &args[0] { DataValue::String(s) => Ok(DataValue::String(s.trim_start().into())), v => bail!("cannot apply 'trim_start' to {:?}", v), @@ -1036,7 +1036,7 @@ fn op_trim_start(args: &[DataValue]) -> Result { } define_op!(OP_TRIM_END, 1, false, false); -fn op_trim_end(args: &[DataValue]) -> Result { +pub(crate) fn op_trim_end(args: &[DataValue]) -> Result { match &args[0] { DataValue::String(s) => Ok(DataValue::String(s.trim_end().into())), v => bail!("cannot apply 'trim_end' to {:?}", v), @@ -1044,7 +1044,7 @@ fn op_trim_end(args: &[DataValue]) -> Result { } define_op!(OP_STARTS_WITH, 2, false, true); -fn op_starts_with(args: &[DataValue]) -> Result { +pub(crate) fn op_starts_with(args: &[DataValue]) -> Result { let a = match &args[0] { DataValue::String(s) => s, v => bail!("unexpected arg {:?} for OP_STARTS_WITH", v), @@ -1057,7 +1057,7 @@ fn op_starts_with(args: &[DataValue]) -> Result { } define_op!(OP_ENDS_WITH, 2, false, true); -fn op_ends_with(args: &[DataValue]) -> Result { +pub(crate) fn op_ends_with(args: &[DataValue]) -> Result { let a = match &args[0] { DataValue::String(s) => s, v => bail!("unexpected arg {:?} for OP_ENDS_WITH", v), @@ -1070,7 +1070,7 @@ fn op_ends_with(args: &[DataValue]) -> Result { } define_op!(OP_REGEX, 1, false, false); -fn op_regex(args: &[DataValue]) -> Result { +pub(crate) fn op_regex(args: &[DataValue]) -> Result { Ok(match &args[0] { r @ DataValue::Regex(_) => r.clone(), DataValue::String(s) => DataValue::Regex(RegexWrapper(regex::Regex::new(s)?)), @@ -1079,7 +1079,7 @@ fn op_regex(args: &[DataValue]) -> Result { } define_op!(OP_REGEX_MATCHES, 2, false, true); -fn op_regex_matches(args: &[DataValue]) -> Result { +pub(crate) fn op_regex_matches(args: &[DataValue]) -> Result { match (&args[0], &args[1]) { (DataValue::String(s), DataValue::Regex(r)) => Ok(DataValue::Bool(r.0.is_match(s))), v => bail!("cannot apply 'regex_matches' to {:?}", v), @@ -1087,7 +1087,7 @@ fn op_regex_matches(args: &[DataValue]) -> Result { } define_op!(OP_REGEX_REPLACE, 3, false, false); -fn op_regex_replace(args: &[DataValue]) -> Result { +pub(crate) fn op_regex_replace(args: &[DataValue]) -> Result { match (&args[0], &args[1], &args[2]) { (DataValue::String(s), DataValue::Regex(r), DataValue::String(rp)) => { Ok(DataValue::String(r.0.replace(s, rp as &str).into())) @@ -1097,7 +1097,7 @@ fn op_regex_replace(args: &[DataValue]) -> Result { } define_op!(OP_REGEX_EXTRACT, 2, false, false); -fn op_regex_extract(args: &[DataValue]) -> Result { +pub(crate) fn op_regex_extract(args: &[DataValue]) -> Result { match (&args[0], &args[1]) { (DataValue::String(s), DataValue::Regex(r)) => { let found = @@ -1111,7 +1111,7 @@ fn op_regex_extract(args: &[DataValue]) -> Result { } define_op!(OP_REGEX_EXTRACT_FIRST, 2, false, false); -fn op_regex_extract_first(args: &[DataValue]) -> Result { +pub(crate) fn op_regex_extract_first(args: &[DataValue]) -> Result { match (&args[0], &args[1]) { (DataValue::String(s), DataValue::Regex(r)) => { let found = r.0.find(s).map(|v| DataValue::String(v.as_str().into())); @@ -1122,12 +1122,12 @@ fn op_regex_extract_first(args: &[DataValue]) -> Result { } define_op!(OP_IS_NULL, 1, false, true); -fn op_is_null(args: &[DataValue]) -> Result { +pub(crate) fn op_is_null(args: &[DataValue]) -> Result { Ok(DataValue::Bool(matches!(args[0], DataValue::Null))) } define_op!(OP_IS_INT, 1, false, true); -fn op_is_int(args: &[DataValue]) -> Result { +pub(crate) fn op_is_int(args: &[DataValue]) -> Result { Ok(DataValue::Bool(matches!( args[0], DataValue::Number(Number::Int(_)) @@ -1135,7 +1135,7 @@ fn op_is_int(args: &[DataValue]) -> Result { } define_op!(OP_IS_FLOAT, 1, false, true); -fn op_is_float(args: &[DataValue]) -> Result { +pub(crate) fn op_is_float(args: &[DataValue]) -> Result { Ok(DataValue::Bool(matches!( args[0], DataValue::Number(Number::Float(_)) @@ -1143,7 +1143,7 @@ fn op_is_float(args: &[DataValue]) -> Result { } define_op!(OP_IS_NUM, 1, false, true); -fn op_is_num(args: &[DataValue]) -> Result { +pub(crate) fn op_is_num(args: &[DataValue]) -> Result { Ok(DataValue::Bool(matches!( args[0], DataValue::Number(Number::Int(_)) | DataValue::Number(Number::Float(_)) @@ -1151,17 +1151,17 @@ fn op_is_num(args: &[DataValue]) -> Result { } define_op!(OP_IS_STRING, 1, false, true); -fn op_is_string(args: &[DataValue]) -> Result { +pub(crate) fn op_is_string(args: &[DataValue]) -> Result { Ok(DataValue::Bool(matches!(args[0], DataValue::String(_)))) } define_op!(OP_IS_LIST, 1, false, true); -fn op_is_list(args: &[DataValue]) -> Result { +pub(crate) fn op_is_list(args: &[DataValue]) -> Result { Ok(DataValue::Bool(matches!(args[0], DataValue::List(_)))) } define_op!(OP_APPEND, 2, false, false); -fn op_append(args: &[DataValue]) -> Result { +pub(crate) fn op_append(args: &[DataValue]) -> Result { match &args[0] { DataValue::List(l) => { let mut l = l.clone(); @@ -1173,12 +1173,12 @@ fn op_append(args: &[DataValue]) -> Result { } define_op!(OP_IS_BYTES, 1, false, true); -fn op_is_bytes(args: &[DataValue]) -> Result { +pub(crate) fn op_is_bytes(args: &[DataValue]) -> Result { Ok(DataValue::Bool(matches!(args[0], DataValue::Bytes(_)))) } define_op!(OP_LENGTH, 1, false, false); -fn op_length(args: &[DataValue]) -> Result { +pub(crate) fn op_length(args: &[DataValue]) -> Result { Ok(DataValue::from(match &args[0] { DataValue::Set(s) => s.len() as i64, DataValue::List(l) => l.len() as i64, @@ -1189,7 +1189,7 @@ fn op_length(args: &[DataValue]) -> Result { } define_op!(OP_SORT, 1, false, false); -fn op_sort(args: &[DataValue]) -> Result { +pub(crate) fn op_sort(args: &[DataValue]) -> Result { let mut arg = args[0] .get_list() .ok_or_else(|| anyhow!("cannot apply 'sort' to {:?}", args))? @@ -1199,17 +1199,17 @@ fn op_sort(args: &[DataValue]) -> Result { } define_op!(OP_PI, 0, false, false); -fn op_pi(_args: &[DataValue]) -> Result { +pub(crate) fn op_pi(_args: &[DataValue]) -> Result { Ok(DataValue::from(f64::PI())) } define_op!(OP_E, 0, false, false); -fn op_e(_args: &[DataValue]) -> Result { +pub(crate) fn op_e(_args: &[DataValue]) -> Result { Ok(DataValue::from(f64::E())) } define_op!(OP_HAVERSINE, 4, false, false); -fn op_haversine(args: &[DataValue]) -> Result { +pub(crate) fn op_haversine(args: &[DataValue]) -> Result { let gen_err = || anyhow!("cannot computer haversine distance for {:?}", args); let lat1 = args[0].get_float().ok_or_else(gen_err)?; let lon1 = args[1].get_float().ok_or_else(gen_err)?; @@ -1224,7 +1224,7 @@ fn op_haversine(args: &[DataValue]) -> Result { } define_op!(OP_HAVERSINE_DEG_INPUT, 4, false, false); -fn op_haversine_deg_input(args: &[DataValue]) -> Result { +pub(crate) fn op_haversine_deg_input(args: &[DataValue]) -> Result { let gen_err = || anyhow!("cannot computer haversine distance for {:?}", args); let lat1 = args[0].get_float().ok_or_else(gen_err)? * f64::PI() / 180.; let lon1 = args[1].get_float().ok_or_else(gen_err)? * f64::PI() / 180.; @@ -1239,7 +1239,7 @@ fn op_haversine_deg_input(args: &[DataValue]) -> Result { } define_op!(OP_DEG_TO_RAD, 1, false, false); -fn op_deg_to_rad(args: &[DataValue]) -> Result { +pub(crate) fn op_deg_to_rad(args: &[DataValue]) -> Result { let x = args[0] .get_float() .ok_or_else(|| anyhow!("cannot convert to radian: {:?}", args))?; @@ -1247,7 +1247,7 @@ fn op_deg_to_rad(args: &[DataValue]) -> Result { } define_op!(OP_RAD_TO_DEG, 1, false, false); -fn op_rad_to_deg(args: &[DataValue]) -> Result { +pub(crate) fn op_rad_to_deg(args: &[DataValue]) -> Result { let x = args[0] .get_float() .ok_or_else(|| anyhow!("cannot convert to degrees: {:?}", args))?; @@ -1255,7 +1255,7 @@ fn op_rad_to_deg(args: &[DataValue]) -> Result { } define_op!(OP_FIRST, 1, false, false); -fn op_first(args: &[DataValue]) -> Result { +pub(crate) fn op_first(args: &[DataValue]) -> Result { Ok(args[0] .get_list() .ok_or_else(|| anyhow!("cannot compute 'first' of {:?}", args))? @@ -1265,7 +1265,7 @@ fn op_first(args: &[DataValue]) -> Result { } define_op!(OP_LAST, 1, false, false); -fn op_last(args: &[DataValue]) -> Result { +pub(crate) fn op_last(args: &[DataValue]) -> Result { Ok(args[0] .get_list() .ok_or_else(|| anyhow!("cannot compute 'last' of {:?}", args))? @@ -1275,7 +1275,7 @@ fn op_last(args: &[DataValue]) -> Result { } define_op!(OP_CHUNKS, 2, false, false); -fn op_chunks(args: &[DataValue]) -> Result { +pub(crate) fn op_chunks(args: &[DataValue]) -> Result { let arg = args[0].get_list().ok_or_else(|| { anyhow!( "first argument of 'chunks' must be a list, got {:?}", @@ -1301,7 +1301,7 @@ fn op_chunks(args: &[DataValue]) -> Result { } define_op!(OP_CHUNKS_EXACT, 2, false, false); -fn op_chunks_exact(args: &[DataValue]) -> Result { +pub(crate) fn op_chunks_exact(args: &[DataValue]) -> Result { let arg = args[0].get_list().ok_or_else(|| { anyhow!( "first argument of 'chunks_exact' must be a list, got {:?}", @@ -1327,7 +1327,7 @@ fn op_chunks_exact(args: &[DataValue]) -> Result { } define_op!(OP_WINDOWS, 2, false, false); -fn op_windows(args: &[DataValue]) -> Result { +pub(crate) fn op_windows(args: &[DataValue]) -> Result { let arg = args[0].get_list().ok_or_else(|| { anyhow!( "first argument of 'windows' must be a list, got {:?}", @@ -1369,7 +1369,7 @@ fn get_index(mut i: i64, total: usize) -> Result { } define_op!(OP_NTH, 2, false, false); -fn op_nth(args: &[DataValue]) -> Result { +pub(crate) fn op_nth(args: &[DataValue]) -> Result { let l = args[0] .get_list() .ok_or_else(|| anyhow!("first argument to 'nth' mut be a list, got args {:?}", args))?; @@ -1384,7 +1384,7 @@ fn op_nth(args: &[DataValue]) -> Result { } define_op!(OP_MAYBE_NTH, 2, false, false); -fn op_maybe_nth(args: &[DataValue]) -> Result { +pub(crate) fn op_maybe_nth(args: &[DataValue]) -> Result { let l = args[0] .get_list() .ok_or_else(|| anyhow!("first argument to 'nth' mut be a list, got args {:?}", args))?; @@ -1402,7 +1402,7 @@ fn op_maybe_nth(args: &[DataValue]) -> Result { } define_op!(OP_SLICE, 3, false, false); -fn op_slice(args: &[DataValue]) -> Result { +pub(crate) fn op_slice(args: &[DataValue]) -> Result { let l = args[0].get_list().ok_or_else(|| { anyhow!( "first argument to 'slice' mut be a list, got args {:?}", @@ -1427,7 +1427,7 @@ fn op_slice(args: &[DataValue]) -> Result { } define_op!(OP_CHARS, 1, false, false); -fn op_chars(args: &[DataValue]) -> Result { +pub(crate) fn op_chars(args: &[DataValue]) -> Result { Ok(DataValue::List( args[0] .get_string() @@ -1443,7 +1443,7 @@ fn op_chars(args: &[DataValue]) -> Result { } define_op!(OP_NTH_CHAR, 2, false, false); -fn op_nth_char(args: &[DataValue]) -> Result { +pub(crate) fn op_nth_char(args: &[DataValue]) -> Result { let l = args[0].get_string().ok_or_else(|| { anyhow!( "first argument to 'nth_char' mut be a string, got args {:?}", @@ -1464,7 +1464,7 @@ fn op_nth_char(args: &[DataValue]) -> Result { } define_op!(OP_MAYBE_NTH_CHAR, 2, false, false); -fn op_maybe_nth_char(args: &[DataValue]) -> Result { +pub(crate) fn op_maybe_nth_char(args: &[DataValue]) -> Result { let l = args[0].get_string().ok_or_else(|| { anyhow!( "first argument to 'nth_char' mut be a string, got args {:?}", @@ -1488,7 +1488,7 @@ fn op_maybe_nth_char(args: &[DataValue]) -> Result { } define_op!(OP_STR_SLICE, 3, false, false); -fn op_str_slice(args: &[DataValue]) -> Result { +pub(crate) fn op_str_slice(args: &[DataValue]) -> Result { let l = args[0].get_string().ok_or_else(|| { anyhow!( "first argument to 'str_slice' mut be a string, got args {:?}", @@ -1515,7 +1515,7 @@ fn op_str_slice(args: &[DataValue]) -> Result { } define_op!(OP_ENCODE_BASE64, 1, false, false); -fn op_encode_base64(args: &[DataValue]) -> Result { +pub(crate) fn op_encode_base64(args: &[DataValue]) -> Result { match &args[0] { DataValue::Bytes(b) => { let s = base64::encode(b); @@ -1526,7 +1526,7 @@ fn op_encode_base64(args: &[DataValue]) -> Result { } define_op!(OP_DECODE_BASE64, 1, false, false); -fn op_decode_base64(args: &[DataValue]) -> Result { +pub(crate) fn op_decode_base64(args: &[DataValue]) -> Result { match &args[0] { DataValue::String(s) => { let b = base64::decode(s)?; @@ -1537,7 +1537,7 @@ fn op_decode_base64(args: &[DataValue]) -> Result { } define_op!(OP_TO_FLOAT, 1, false, false); -fn op_to_float(args: &[DataValue]) -> Result { +pub(crate) fn op_to_float(args: &[DataValue]) -> Result { Ok(match &args[0] { DataValue::Number(n) => n.get_float().into(), DataValue::String(t) => match t as &str { @@ -1551,12 +1551,12 @@ fn op_to_float(args: &[DataValue]) -> Result { } define_op!(OP_RAND_FLOAT, 0, false, false); -fn op_rand_float(_args: &[DataValue]) -> Result { +pub(crate) fn op_rand_float(_args: &[DataValue]) -> Result { Ok(thread_rng().gen::().into()) } define_op!(OP_RAND_BERNOULLI, 0, true, false); -fn op_rand_bernoulli(args: &[DataValue]) -> Result { +pub(crate) fn op_rand_bernoulli(args: &[DataValue]) -> Result { let prob = match args.get(0) { None => 0.5, Some(DataValue::Number(n)) => { @@ -1577,7 +1577,7 @@ fn op_rand_bernoulli(args: &[DataValue]) -> Result { } define_op!(OP_RAND_INT, 2, false, false); -fn op_rand_int(args: &[DataValue]) -> Result { +pub(crate) fn op_rand_int(args: &[DataValue]) -> Result { let lower = &args[0].get_int().ok_or_else(|| { anyhow!( "first argument to 'rand_int' must be an integer, got args {:?}", @@ -1594,7 +1594,7 @@ fn op_rand_int(args: &[DataValue]) -> Result { } define_op!(OP_RAND_CHOOSE, 1, false, true); -fn op_rand_choose(args: &[DataValue]) -> Result { +pub(crate) fn op_rand_choose(args: &[DataValue]) -> Result { match &args[0] { DataValue::List(l) => Ok(l .choose(&mut thread_rng()) diff --git a/src/data/mod.rs b/src/data/mod.rs index 062e06c8..f7a6385e 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -11,3 +11,6 @@ pub(crate) mod expr; pub(crate) mod program; pub(crate) mod aggr; +#[cfg(test)] +mod tests; + diff --git a/src/data/tests/functions.rs b/src/data/tests/functions.rs new file mode 100644 index 00000000..b271eb68 --- /dev/null +++ b/src/data/tests/functions.rs @@ -0,0 +1,73 @@ +use crate::data::expr::{op_add, op_div, op_mul, op_sub}; +use crate::data::value::DataValue; + +#[test] +fn test_add() { + assert_eq!(op_add(&[]).unwrap(), DataValue::from(0)); + assert_eq!(op_add(&[DataValue::from(1)]).unwrap(), DataValue::from(1)); + assert_eq!( + op_add(&[DataValue::from(1), DataValue::from(2)]).unwrap(), + DataValue::from(3) + ); + assert_eq!( + op_add(&[DataValue::from(1), DataValue::from(2.5)]).unwrap(), + DataValue::from(3.5) + ); + assert_eq!( + op_add(&[DataValue::from(1.5), DataValue::from(2.5)]).unwrap(), + DataValue::from(4.0) + ); +} + +#[test] +fn test_sub() { + assert_eq!( + op_sub(&[DataValue::from(1), DataValue::from(2)]).unwrap(), + DataValue::from(-1) + ); + assert_eq!( + op_sub(&[DataValue::from(1), DataValue::from(2.5)]).unwrap(), + DataValue::from(-1.5) + ); + assert_eq!( + op_sub(&[DataValue::from(1.5), DataValue::from(2.5)]).unwrap(), + DataValue::from(-1.0) + ); +} + +#[test] +fn test_mul() { + assert_eq!(op_mul(&[]).unwrap(), DataValue::from(1)); + assert_eq!( + op_mul(&[DataValue::from(2), DataValue::from(3)]).unwrap(), + DataValue::from(6) + ); + assert_eq!( + op_mul(&[DataValue::from(0.5), DataValue::from(0.25)]).unwrap(), + DataValue::from(0.125) + ); + assert_eq!( + op_mul(&[DataValue::from(0.5), DataValue::from(3)]).unwrap(), + DataValue::from(1.5) + ); +} + +#[test] +fn test_div() { + assert_eq!( + op_div(&[DataValue::from(1), DataValue::from(1)]).unwrap(), + DataValue::from(1.0) + ); + assert_eq!( + op_div(&[DataValue::from(1), DataValue::from(2)]).unwrap(), + DataValue::from(0.5) + ); + assert_eq!( + op_div(&[DataValue::from(0), DataValue::from(0)]).unwrap(), + DataValue::from(f64::NAN) + ); + assert_eq!( + op_div(&[DataValue::from(7.0), DataValue::from(0.5)]).unwrap(), + DataValue::from(14.0) + ); +} diff --git a/src/data/tests/mod.rs b/src/data/tests/mod.rs new file mode 100644 index 00000000..87ccb640 --- /dev/null +++ b/src/data/tests/mod.rs @@ -0,0 +1 @@ +mod functions; \ No newline at end of file