diff options
Diffstat (limited to 'alv-lib/array.moon')
| -rw-r--r-- | alv-lib/array.moon | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/alv-lib/array.moon b/alv-lib/array.moon new file mode 100644 index 0000000..bbc1cbd --- /dev/null +++ b/alv-lib/array.moon @@ -0,0 +1,295 @@ +import Array, Op, PureOp, Builtin, RTNode, Constant, Error, const, any, T from require 'alv.base' +import Cell, Tag, Dummy from require 'alv.ast' +builtins = require 'alv.builtins' + +unpack or= table.unpack + +get = Constant.meta + meta: + name: 'get' + summary: "Index into an array." + 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: any! + 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 a value in an array." + 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: any! + const.num + any! + 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: any!*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: any!*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: any! + any! + 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 a new value into an array." + 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: any! + const.num + any! + 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 a value from an Array." + 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: any! + 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 the size of an array." + 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: any!\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 + +array_constr = builtins!\get('mkarray').result +map = Constant.meta + meta: + name: 'map' + summary: "Apply an function to each value in an array." + examples: { '(map array fn)' } + description: " +Invokes `fn` once for each element in `array` and returns an array of the results. +`fn` must take one argument and return the same type consistently." + + value: class extends Builtin + eval: (scope, tail) => + L\trace "evaling #{@}" + assert #tail == 2, "'map' takes exactly two arguments" + tail = [L\push t\eval, scope for t in *tail] + { array, fn } = tail + + assert fn\type! == T.fndef, "fn has to be a fndef" + + array_type = array\type! + assert array_type.__class == Array, Error 'argument', "expected an Array" + + invocations = for i=1, array_type.size + tag_o = @tag\clone Tag.parse tostring i + tag_i = @tag\clone tag_o + Cell tag_o, { + Dummy fn\make_ref! + Cell tag_i, { + Dummy.literal T.opdef, get! + Dummy array\make_ref! + Constant.num i-1 + } + } + + tag = @tag\clone Tag.parse '-1' + inner = Cell tag, { + Dummy.literal T.opdef, array_constr + unpack invocations + } + + node = inner\eval scope + super RTNode children: { array, fn, node }, result: node.result + +Constant.meta + meta: + name: 'array' + summary: "Utilities for dealing with arrays." + + value: + :get, :set + :head, :tail, :prepend + :insert, :remove + :map + + :size, :concat |
