diff --git a/src/builtins/arrays.rs b/src/builtins/arrays.rs index 781a2f2..4984a89 100644 --- a/src/builtins/arrays.rs +++ b/src/builtins/arrays.rs @@ -3,7 +3,7 @@ use crate::ast::Expr; use crate::builtins; -use crate::builtins::utils::{ensure_args_count, ensure_array}; +use crate::builtins::utils::{ensure_args_count, ensure_array, ensure_numeric}; use crate::lexer::Span; use crate::value::Value; @@ -15,6 +15,7 @@ use std::rc::Rc; pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) { m.insert("array.concat", concat); m.insert("array.reverse", reverse); + m.insert("array.slice", slice); } fn concat(span: &Span, params: &[Expr], args: &[Value]) -> Result { @@ -29,13 +30,36 @@ fn concat(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)?; + ensure_args_count(span, name, params, args, 1)?; let mut v1 = ensure_array(name, ¶ms[0], args[0].clone())?; Rc::make_mut(&mut v1).reverse(); Ok(Value::Array(v1)) } -/* fn slice(span: &Span, params: &[Expr], args: &[Value]) -> Result { -*/ + let name = "array.slice"; + ensure_args_count(span, name, params, args, 3)?; + + let array = ensure_array(name, ¶ms[0], args[0].clone())?; + let start = ensure_numeric(name, ¶ms[1], &args[1].clone())?; + let stop = ensure_numeric(name, ¶ms[2], &args[2].clone())?; + + if start != start.floor() || stop != stop.floor() { + return Ok(Value::Undefined); + } + + // TODO: usize conversion checks. + let start = start as usize; + let stop = match stop as usize { + s if s > array.len() => array.len(), + s => s, + }; + + if start >= stop { + return Ok(Value::new_array()); + } + + let slice = &array[start..stop]; + Ok(Value::from_array(slice.to_vec())) +} diff --git a/tests/interpreter/cases/builtins/arrays/concat.yaml b/tests/interpreter/cases/builtins/arrays/concat.yaml new file mode 100644 index 0000000..1888f86 --- /dev/null +++ b/tests/interpreter/cases/builtins/arrays/concat.yaml @@ -0,0 +1,63 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +cases: + - note: all + data: {} + modules: + - | + package test + + x = [ + array.concat([1, 2, 3], [4, 5, 6]), + array.concat([], [1]), + array.concat([1], []), + array.concat([], []), + ] + # Undefined + r { false } + u1 = array.concat([1], r) + u2 = array.concat(r, r) + query: data.test + want_result: + x : + - [1, 2, 3, 4, 5, 6] + - [1] + - [1] + - [] + + - note: concat-invalid-arg1 + data: {} + modules: + - | + package test + x = array.concat(1, [2]) + query: data.test + error: "`array.concat` expects array argument." + + - note: concat-invalid-arg2 + data: {} + modules: + - | + package test + x = array.concat([2], 1) + query: data.test + error: "`array.concat` expects array argument." + + - note: concat-less-args + data: {} + modules: + - | + package test + x = array.concat([2]) + query: data.test + error: "`array.concat` expects 2 arguments" + + - note: concat-more-args + data: {} + modules: + - | + package test + x = array.concat([2], [3], [4]) + query: data.test + error: "`array.concat` expects 2 arguments" diff --git a/tests/interpreter/cases/builtins/arrays/reverse.yaml b/tests/interpreter/cases/builtins/arrays/reverse.yaml new file mode 100644 index 0000000..98be4df --- /dev/null +++ b/tests/interpreter/cases/builtins/arrays/reverse.yaml @@ -0,0 +1,51 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +cases: + - note: all + data: {} + modules: + - | + package test + y = [ + array.reverse([1, 2, 3]), + array.reverse([]), + array.reverse(array.reverse([1, 2, 3])), + ] + + # Undefined + r { false } + u3 = array.reverse(r) + query: data.test + want_result: + y : + - [3, 2,1] + - [] + - [1, 2, 3] + + - note: reverse-invalid-arg + data: {} + modules: + - | + package test + x = array.reverse(1) + query: data.test + error: "`array.reverse` expects array argument." + + - note: reverse-less-args + data: {} + modules: + - | + package test + x = array.reverse() + query: data.test + error: "at least one argument required for function calls" + + - note: reverse-more-args + data: {} + modules: + - | + package test + x = array.reverse([2], [3]) + query: data.test + error: "`array.reverse` expects 1 argument" diff --git a/tests/interpreter/cases/builtins/arrays/slice.yaml b/tests/interpreter/cases/builtins/arrays/slice.yaml new file mode 100644 index 0000000..481a025 --- /dev/null +++ b/tests/interpreter/cases/builtins/arrays/slice.yaml @@ -0,0 +1,86 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +cases: + - note: all + data: {} + modules: + - | + package test + + z = [ + array.slice([1, 2, 3], 0, 1), + array.slice([1, 2, 3], 0, 2), + array.slice([1, 2, 3], 2, 3), + array.slice([1, 2, 3], 0, 5), + array.slice([1, 2, 3], 5, 5), + array.slice([1, 2, 3], -5, 5), + array.slice([1, 2, 3], -5, 0), + array.slice([1, 2, 3], -5, 1), + array.slice([], -5, 1), + ] + + # Undefined + r { false } + u1 = array.slice(r, 1, 2) + u2 = array.slice([1], r, 2) + u3 = array.slice([1], 1, r) + u4 = array.slice([5], 1.1, 2) + u5 = array.slice([5], 1, 2.1) + query: data.test + want_result: + z : + - [1] + - [1, 2] + - [3] + - [1, 2, 3] + - [] + - [1, 2, 3] + - [] + - [1] + - [] + + - note: invalid-arg1 + data: {} + modules: + - | + package test + x = array.slice(1, 2, 3) + query: data.test + error: "`array.slice` expects array argument." + + - note: invalid-arg2 + data: {} + modules: + - | + package test + x = array.slice([2], "1", 2) + query: data.test + error: "`array.slice` expects numeric argument." + + - note: invalid-arg3 + data: {} + modules: + - | + package test + x = array.slice([2], 1, "2") + query: data.test + error: "`array.slice` expects numeric argument." + + - note: less-args + data: {} + modules: + - | + package test + x = array.slice([2], 1) + query: data.test + error: "`array.slice` expects 3 arguments" + + - note: more-args + data: {} + modules: + - | + package test + x = array.slice([2], 1, 2, 3) + query: data.test + error: "`array.slice` expects 3 arguments"