diff options
| author | s-ol <s-ol@users.noreply.github.com> | 2020-08-21 11:30:16 +0000 |
|---|---|---|
| committer | s-ol <s+removethis@s-ol.nu> | 2025-03-02 14:24:49 +0000 |
| commit | 950dd72215f375fe37d8dc5d78152ccf452d5e46 (patch) | |
| tree | a17591a435eac84f85a0b5933ca14336322a5e1e | |
| parent | dirty (remove) implementation (diff) | |
| download | alive-950dd72215f375fe37d8dc5d78152ccf452d5e46.tar.gz alive-950dd72215f375fe37d8dc5d78152ccf452d5e46.zip | |
move array ops into library, implement concat and size
| -rw-r--r-- | alv-lib/array.moon | 243 | ||||
| -rw-r--r-- | spec/lang/array_spec.moon | 72 | ||||
| -rw-r--r-- | spec/test_setup.moon | 6 |
3 files changed, 292 insertions, 29 deletions
diff --git a/alv-lib/array.moon b/alv-lib/array.moon new file mode 100644 index 0000000..94499bc --- /dev/null +++ b/alv-lib/array.moon @@ -0,0 +1,243 @@ +import Array, Op, PureOp, Constant, Error, const, sig, evt from require 'alv.base' + +get = Constant.meta + meta: + name: 'get' + summary: "Index into Arrays." + examples: { '(get array i)' } + description: "Get the value at index `i` (starting at 0). + +`i` has to be a constant expression." + + value: class extends PureOp + pattern: (sig! / evt!) + const.num + type: (inputs) => + { array, i } = inputs + array\type!\get i.result! + + tick: => + { array, i } = @unwrap_all! + @out\set array[i + 1] + +set = Constant.meta + meta: + name: 'set' + summary: "Update values in Arrays." + examples: { '(set array i val)' } + description: "Set the value for `i` to `val`. + +`i` has to be a constant expression. This is a pure op, so at most one of +`array` and `val` may be a !-stream." + + value: class extends PureOp + pattern: (sig! / evt!) + const.num + (sig! / evt!) + type: (inputs) => + { array, i, val } = inputs + type = array\type! + expected = type\get i.result! + + if expected ~= val\type! + msg = string.format "expected value of type %s, not %s", + expected, val\type! + error Error 'argument', msg + + type + + tick: => + { array, key, val } = @unwrap_all! + + array = [v for v in *array] + array[key + 1] = val + + @out\set array + +head = Constant.meta + meta: + name: 'head' + summary: "Get the first element from an array." + examples: { '(head array)' } + + value: class extends PureOp + pattern: (sig! / evt!)*1 + type: (inputs) => + type = inputs[1]\type! + + assert type.__class == Array, Error 'argument', "expected an Array" + assert type.size > 0, Error 'argument', "cannot get head of empty Array" + + type.type + + tick: => + { array } = @unwrap_all! + @out\set array[1] + +tail = Constant.meta + meta: + name: 'tail' + summary: "Get everything except the first element from an array." + examples: { '(tail array)' } + + value: class extends PureOp + pattern: (sig! / evt!)*1 + type: (inputs) => + type = inputs[1]\type! + + assert type.__class == Array, Error 'argument', "expected an Array" + assert type.size > 0, Error 'argument', "cannot get tail of empty Array" + + Array type.size - 1, type.type + + tick: => + { array } = @unwrap_all! + @out\set [v for v in *array[2,]] + +prepend = Constant.meta + meta: + name: 'prepend' + summary: "Prepend a new value at the start of an Array." + examples: { '(prepend array val)' } + description: "Prepend `val` to `array` at index `0`, moving other values back. + +This is a pure op, so at most one of `array` and `val` may be a !-stream." + + value: class extends PureOp + pattern: (sig! / evt!) + (sig! / evt!) + type: (inputs) => + { array, val } = inputs + type = array\type! + + if val\type! ~= type.type + msg = string.format "expected value of type %s, not %s", + type.type, val\type! + error Error 'argument', msg + + Array type.size + 1, type.type + + tick: => + { array, val } = @unwrap_all! + + array = [v for v in *array] + table.insert array, 1, val + + @out\set array + +insert = Constant.meta + meta: + name: 'insert' + summary: "Insert new values into Arrays." + examples: { '(insert array i val)' } + description: "Insert `val` into `array` at `i`, moving other values back if +necessary. + +`i` has to be a constant expression. This is a pure op, so at most one of +`array` and `val` may be a !-stream." + + value: class extends PureOp + pattern: (sig! / evt!) + const.num + (sig! / evt!) + type: (inputs) => + { array, i, val } = inputs + type = array\type! + i = i.result! + + if i > type.size or i < 0 + error Error 'argument', "index '#{i}' out of range!" + if val\type! ~= type.type + msg = string.format "expected value of type %s, not %s", + type.type, val\type! + error Error 'argument', msg + + Array type.size + 1, type.type + + tick: => + { array, i, val } = @unwrap_all! + + array = [v for v in *array] + table.insert array, i + 1, val + + @out\set array + +remove = Constant.meta + meta: + name: 'remove' + summary: "Remove values from Arrays." + examples: { '(remove array i)' } + description: "Removes the value at index `i` from `array`. + +`i` has to be a constant expression." + + value: class extends PureOp + pattern: (sig! / evt!) + const.num + type: (inputs) => + { array, i } = inputs + type = array\type! + + -- check index range + type\get i.result! + + Array type.size - 1, type.type + + tick: => + { array, i, val } = @unwrap_all! + + array = [v for v in *array] + table.remove array, i + 1 + + @out\set array + +size = Constant.meta + meta: + name: 'size' + summary: "Get Array size" + examples: { '(size array)' } + + value: class extends Op + setup: (inputs) => + super {} + + assert #inputs == 1, Error 'argument', "expected exactly one argument" + type = inputs[1]\type! + assert type.__class == Array, Error 'argument', "expected an Array" + + @out = Constant.num type.size + +concat = Constant.meta + meta: + name: 'concat' + summary: "Concatenate Arrays" + examples: { '(concat arr1 arr2 [arr3…])' } + + value: class extends PureOp + pattern: (sig! / evt!)\rep 2 + type: (inputs) => + size = 0 + type = inputs[1]\type!.type + + for input in *inputs + array = input\type! + + if array.type ~= type + msg = string.format "Cannot concatenate different arrays %s, %s", + inputs[1]\type!, array + error Error 'argument', msg + + size += array.size + + Array size, type + + tick: => + arrays = @unwrap_all! + out = {} + + for array in *arrays + for val in *array + table.insert out, val + + @out\set out + +{ + :get, :set + :head, :tail, :prepend + :insert, :remove + + :size, :concat +} diff --git a/spec/lang/array_spec.moon b/spec/lang/array_spec.moon index 2dd2894..fc7ce43 100644 --- a/spec/lang/array_spec.moon +++ b/spec/lang/array_spec.moon @@ -2,7 +2,7 @@ import TestPilot from require 'spec.test_setup' import T, Array, Constant from require 'alv' describe "array", -> - test = TestPilot '' + test = TestPilot '', '(import* array)\n' svec3 = Array 3, T.str @@ -16,15 +16,6 @@ describe "array", -> err = assert.has.error -> COPILOT\eval_once '(array 1 false)' assert.matches "argument error: couldn't match arguments", err - it "length can be read using (len)", -> - rt = COPILOT\eval_once '(len (array 1))' - assert.is.true rt\is_const! - assert.is.equal (Constant.num 1), rt.result - - rt = COPILOT\eval_once '(len (array 1 2 3))' - assert.is.true rt\is_const! - assert.is.equal (Constant.num 3), rt.result - describe "(set)", -> it "can swap values", -> rt = COPILOT\eval_once '(set (array "f" "b" "c") 0 "a")' @@ -33,7 +24,7 @@ describe "array", -> it "checks value type", -> err = assert.has.error -> COPILOT\eval_once '(set (array 1) 0 "a")' - assert.matches "expected value for key '0' to be num, not str", err + assert.matches "expected value of type num, not str", err it "checks index range", -> err = assert.has.error -> COPILOT\eval_once '(set (array 1 2) -1 0)' @@ -47,18 +38,13 @@ describe "array", -> assert.matches "index '2' out of range!", err describe "(get)", -> - it "can peek a value", -> - rt = COPILOT\eval_once '(get (array 1 2))' - assert.is.true rt\is_const! - assert.is.equal (Constant.num 2), rt.result - it "can get a value", -> rt = COPILOT\eval_once '(get (array 1 2) 0)' assert.is.true rt\is_const! assert.is.equal (Constant.num 1), rt.result it "checks index range", -> - err = assert.has.error -> COPILOT\eval_once '(get (array 1 2) -1 0)' + err = assert.has.error -> COPILOT\eval_once '(get (array 1 2) -1)' assert.matches "index '%-1' out of range!", err COPILOT\eval_once '(get (array 1 2) 0)' @@ -68,12 +54,42 @@ describe "array", -> err = assert.has.error -> COPILOT\eval_once '(get (array 1 2) 2)' assert.matches "index '2' out of range!", err - describe "(insert)", -> - it "can append a value", -> - rt = COPILOT\eval_once '(insert (array "a" "b") "c")' + describe '(head)', -> + it "can peek a value", -> + rt = COPILOT\eval_once '(head (array 1 2))' assert.is.true rt\is_const! - assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result + assert.is.equal (Constant.num 1), rt.result + + describe '(tail)', -> + it "gets rest of an array", -> + rt = COPILOT\eval_once '(tail (array 1))' + assert.is.true rt\is_const! + assert.is.same (Array 0, T.num), rt.result.type + assert.is.same {}, rt.result! + + rt = COPILOT\eval_once '(tail (array 1 2))' + assert.is.true rt\is_const! + assert.is.same (Array 1, T.num), rt.result.type + assert.is.same { 2 }, rt.result! + rt = COPILOT\eval_once '(tail (array 1 2 3 4))' + assert.is.true rt\is_const! + assert.is.same (Array 3, T.num), rt.result.type + assert.is.same { 2, 3, 4 }, rt.result! + + describe '(prepend)', -> + it "prepends to array", -> + rt = COPILOT\eval_once '(prepend (array 2) 1)' + assert.is.true rt\is_const! + assert.is.same (Array 2, T.num), rt.result.type + assert.is.same { 1, 2 }, rt.result! + + rt = COPILOT\eval_once '(prepend (array 2 3 4) 1)' + assert.is.true rt\is_const! + assert.is.same (Array 4, T.num), rt.result.type + assert.is.same { 1, 2, 3, 4 }, rt.result! + + describe "(insert)", -> it "can insert a value", -> rt = COPILOT\eval_once '(insert (array "b" "c") 0 "a")' assert.is.true rt\is_const! @@ -101,11 +117,6 @@ describe "array", -> assert.matches "index '3' out of range!", err describe "(remove)", -> - it "can pop a value", -> - rt = COPILOT\eval_once '(remove (array "a" "b" "c" "d"))' - assert.is.true rt\is_const! - assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result - it "can remove a value", -> rt = COPILOT\eval_once '(remove (array "d" "a" "b" "c") 0)' assert.is.true rt\is_const! @@ -126,3 +137,12 @@ describe "array", -> rt = COPILOT\eval_once '(concat (array "a" "b") (array "c"))' assert.is.true rt\is_const! assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result + + it "size can be read using (size)", -> + rt = COPILOT\eval_once '(size (array 1))' + assert.is.true rt\is_const! + assert.is.equal (Constant.num 1), rt.result + + rt = COPILOT\eval_once '(size (array 1 2 3))' + assert.is.true rt\is_const! + assert.is.equal (Constant.num 3), rt.result diff --git a/spec/test_setup.moon b/spec/test_setup.moon index 2bcd9df..92756a8 100644 --- a/spec/test_setup.moon +++ b/spec/test_setup.moon @@ -15,13 +15,13 @@ os.time = do export COPILOT class TestPilot extends Copilot - new: (code) => + new: (code, @preamble) => super! COPILOT = @ if code - @active_module = StringModule 'main', code + @active_module = StringModule 'main', @preamble .. code @last_modules.__root = @active_module @tick! else @@ -33,7 +33,7 @@ class TestPilot extends Copilot next_tick: => @T += 1 eval_once: (code) => - @active_module\spit code + @active_module\spit @preamble .. code @tick! @active_module.root |
