From b6cbc69461dc054019f50f8971704f6ef9de63ab Mon Sep 17 00:00:00 2001 From: s-ol Date: Tue, 18 Mar 2025 12:47:22 +0100 Subject: language: [array] and {struct} literals --- alv-lib/array-.moon | 295 ------------------------------------ alv-lib/array.moon | 295 ++++++++++++++++++++++++++++++++++++ alv-lib/math.moon | 4 +- alv-lib/struct-.moon | 128 ---------------- alv-lib/struct.moon | 128 ++++++++++++++++ alv/ast.moon | 4 +- alv/builtins.moon | 14 +- alv/cell.moon | 34 ++++- alv/parsing.moon | 22 +-- docs/reference/05-1_arrays.md | 12 +- docs/reference/05-2_structs.md | 10 +- examples/love.alv | 14 +- spec/internal/cell_spec.moon | 6 +- spec/lib/array_spec.moon | 76 +++++----- spec/lib/builtins/cond_spec.moon | 10 +- spec/lib/builtins/literal_spec.moon | 4 +- spec/lib/logic_spec.moon | 56 +++---- spec/lib/math_spec.moon | 162 ++++++++++---------- spec/lib/string_spec.moon | 26 ++-- spec/lib/struct_spec.moon | 20 +-- spec/lib/testing_spec.moon | 4 +- 21 files changed, 678 insertions(+), 646 deletions(-) delete mode 100644 alv-lib/array-.moon create mode 100644 alv-lib/array.moon delete mode 100644 alv-lib/struct-.moon create mode 100644 alv-lib/struct.moon diff --git a/alv-lib/array-.moon b/alv-lib/array-.moon deleted file mode 100644 index e2d2233..0000000 --- a/alv-lib/array-.moon +++ /dev/null @@ -1,295 +0,0 @@ -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('array').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 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 diff --git a/alv-lib/math.moon b/alv-lib/math.moon index 33f215c..2cf6e26 100644 --- a/alv-lib/math.moon +++ b/alv-lib/math.moon @@ -291,13 +291,13 @@ also work componentwise with vectors (`num[X]`) and matrices (`num[X][Y]`). All operators are PureOps. (+ 1 2 3) #() - (+ (array 1 2) (array 3 4)) #() + (+ [1 2] [3 4]) #() The arguments for an operator generally have to be of the same type. However it is also okay to pass in scalar numbers together with a different type. The scalars will be repeated as necessary to fit the shape of other arguments: - (* (array (array 1 2) (array 3 4)) + (* [[1 2] [3 4]] 2) #() diff --git a/alv-lib/struct-.moon b/alv-lib/struct-.moon deleted file mode 100644 index b064e81..0000000 --- a/alv-lib/struct-.moon +++ /dev/null @@ -1,128 +0,0 @@ -import Struct, Op, PureOp, Constant, Error, const, any from require 'alv.base' - -key_type = const.str / const.sym - -get = Constant.meta - meta: - name: 'get' - summary: "Index into a struct." - examples: { '(get struct key)' } - description: "Get the value at `key`. - -`key` has to be a constant expression." - - value: class extends PureOp - pattern: any! + key_type - type: (inputs) => - { struct, key } = inputs - struct\type!\get key.result! - - tick: => - { struct, key } = @unwrap_all! - @out\set struct[key] - -set = Constant.meta - meta: - name: 'set' - summary: "Update values in a struct." - examples: { '(set struct key val)' } - description: "Set the value for `key` to `val`. - -`key` has to be a constant expression. This is a pure op, so at most one of -`struct` and `val` may be a !-stream." - - value: class extends PureOp - pattern: any! + key_type + any! - type: (inputs) => - { struct, key, val } = inputs - type = struct\type! - expected = type\get key.result! - - if expected ~= val\type! - msg = string.format "expected value for key '%s' to be %s, not %s", - key.result!, expected, val\type! - error Error 'argument', msg - - type - - tick: => - { struct, key, val } = @unwrap_all! - - struct = {k,v for k,v in pairs struct} - struct[key] = val - - @out\set struct - -insert = Constant.meta - meta: - name: 'insert' - summary: "Insert a new value into a struct." - examples: { '(insert struct key val)' } - description: "Insert `val` into `struct` at `key`. - -`key` has to be a constant expression. This is a pure op, so at most one of -`struct` and `val` may be a !-stream." - - value: class extends PureOp - pattern: any! + key_type + any! - type: (inputs) => - { struct, key, val } = inputs - type = struct\type! - key = key.result! - - if type.types[key] - msg = string.format "key '%s' already exists in value of type %s", - key, type - error Error 'argument', msg - - types = {k,v for k,v in pairs type.types} - types[key] = val\type! - Struct types - - tick: => - { struct, key, val } = @unwrap_all! - - struct = {k,v for k,v in pairs struct} - struct[key] = val - - @out\set struct - -remove = Constant.meta - meta: - name: 'remove' - summary: "Remove values from a struct." - examples: { '(remove struct key)' } - description: "Removes the value at index `key` from `struct`. - -`key` has to be a constant expression." - - value: class extends PureOp - pattern: any! + key_type - type: (inputs) => - { struct, key } = inputs - type = struct\type! - key = key.result! - - -- check key exists - type\get key - - types = {k,v for k,v in pairs type.types} - types[key] = nil - Struct types - - tick: => - { struct, key, val } = @unwrap_all! - - struct = {k,v for k,v in pairs struct} - struct[key] = nil - - @out\set struct - -Constant.meta - meta: - name: 'struct' - summary: "Utilities for dealing with structs." - - value: - :get, :set - :insert, :remove diff --git a/alv-lib/struct.moon b/alv-lib/struct.moon new file mode 100644 index 0000000..b064e81 --- /dev/null +++ b/alv-lib/struct.moon @@ -0,0 +1,128 @@ +import Struct, Op, PureOp, Constant, Error, const, any from require 'alv.base' + +key_type = const.str / const.sym + +get = Constant.meta + meta: + name: 'get' + summary: "Index into a struct." + examples: { '(get struct key)' } + description: "Get the value at `key`. + +`key` has to be a constant expression." + + value: class extends PureOp + pattern: any! + key_type + type: (inputs) => + { struct, key } = inputs + struct\type!\get key.result! + + tick: => + { struct, key } = @unwrap_all! + @out\set struct[key] + +set = Constant.meta + meta: + name: 'set' + summary: "Update values in a struct." + examples: { '(set struct key val)' } + description: "Set the value for `key` to `val`. + +`key` has to be a constant expression. This is a pure op, so at most one of +`struct` and `val` may be a !-stream." + + value: class extends PureOp + pattern: any! + key_type + any! + type: (inputs) => + { struct, key, val } = inputs + type = struct\type! + expected = type\get key.result! + + if expected ~= val\type! + msg = string.format "expected value for key '%s' to be %s, not %s", + key.result!, expected, val\type! + error Error 'argument', msg + + type + + tick: => + { struct, key, val } = @unwrap_all! + + struct = {k,v for k,v in pairs struct} + struct[key] = val + + @out\set struct + +insert = Constant.meta + meta: + name: 'insert' + summary: "Insert a new value into a struct." + examples: { '(insert struct key val)' } + description: "Insert `val` into `struct` at `key`. + +`key` has to be a constant expression. This is a pure op, so at most one of +`struct` and `val` may be a !-stream." + + value: class extends PureOp + pattern: any! + key_type + any! + type: (inputs) => + { struct, key, val } = inputs + type = struct\type! + key = key.result! + + if type.types[key] + msg = string.format "key '%s' already exists in value of type %s", + key, type + error Error 'argument', msg + + types = {k,v for k,v in pairs type.types} + types[key] = val\type! + Struct types + + tick: => + { struct, key, val } = @unwrap_all! + + struct = {k,v for k,v in pairs struct} + struct[key] = val + + @out\set struct + +remove = Constant.meta + meta: + name: 'remove' + summary: "Remove values from a struct." + examples: { '(remove struct key)' } + description: "Removes the value at index `key` from `struct`. + +`key` has to be a constant expression." + + value: class extends PureOp + pattern: any! + key_type + type: (inputs) => + { struct, key } = inputs + type = struct\type! + key = key.result! + + -- check key exists + type\get key + + types = {k,v for k,v in pairs type.types} + types[key] = nil + Struct types + + tick: => + { struct, key, val } = @unwrap_all! + + struct = {k,v for k,v in pairs struct} + struct[key] = nil + + @out\set struct + +Constant.meta + meta: + name: 'struct' + summary: "Utilities for dealing with structs." + + value: + :get, :set + :insert, :remove diff --git a/alv/ast.moon b/alv/ast.moon index 4d0ecae..52117c6 100644 --- a/alv/ast.moon +++ b/alv/ast.moon @@ -32,7 +32,7 @@ -- @function stringify -- @treturn string the exact string this Node was parsed from -import Cell, RootCell from require 'alv.cell' +import Cell, RootCell, ArrayCell, StructCell from require 'alv.cell' import TemplateString from require 'alv.template_string' import Constant from require 'alv.result.const' import Dummy from require 'alv.dummy' @@ -41,6 +41,8 @@ import Tag from require 'alv.tag' { :Cell :RootCell + :ArrayCell + :StructCell :TemplateString :Constant :Dummy diff --git a/alv/builtins.moon b/alv/builtins.moon index d76effc..0d188f9 100644 --- a/alv/builtins.moon +++ b/alv/builtins.moon @@ -539,11 +539,11 @@ In case of collisions, the event that comes first in the argument list wins." @out\set input! return -array = Constant.meta +mkarray = Constant.meta meta: - name: 'array' + name: 'mkarray' summary: "Construct an array." - examples: { '(array a b c…)' } + examples: { '[a b c…]', '(mkarray a b c…)' } description: "Produces an array of values. `a`, `b`, `c`… have to be values of the same type. @@ -559,11 +559,11 @@ This is a pure op, so at most one !-stream input is allowed." args = @unwrap_all! @out\set args -struct = Constant.meta +mkstruct = Constant.meta meta: - name: 'struct' + name: 'mkstruct' summary: "Construct an struct." - examples: { '(struct key1 val1 [key2 val2…])' } + examples: { '{key1 val1 [key2 val2…]}', '(mkstruct key1 val1 [key2 val2…])' } description: "Produces a struct of values. `key1`, `key2`, … have to be constant expressions. @@ -732,7 +732,7 @@ Constant.meta '->': thread_first '->>': thread_last - :array, :struct + :mkarray, :mkstruct :loop, :recur diff --git a/alv/cell.moon b/alv/cell.moon index 7b6cf61..7f2c027 100644 --- a/alv/cell.moon +++ b/alv/cell.moon @@ -155,6 +155,10 @@ class RootCell extends Cell head: => Constant.sym 'do' tail: => @children + new: (...) => + super ... + @tag = Tag.parse '0' + stringify: => buf = '' buf ..= @white[0] @@ -172,12 +176,34 @@ class RootCell extends Cell -- -- @tparam table parts -- @treturn Cell - @parse: (...) -> - tag, children, white = parse_args (Tag.parse '0'), ... - RootCell tag, children, white + @parse: (parts) => + _, children, white = parse_args nil, parts + @@ nil, children, white + +-- @type ArrayCell +class ArrayCell extends RootCell + head: => Constant.sym 'mkarray' + tail: => @children + stringify: => '[' .. super! .. ']' + + new: (...) => + Cell.__init @, ... + assert #@children > 0, Error 'syntax', "array literal can't be empty" + +-- @type StructCell +class StructCell extends RootCell + head: => Constant.sym 'mkstruct' + tail: => @children + stringify: => '{' .. super! .. '}' + + new: (...) => + Cell.__init @, ... + assert #@children > 0, Error 'syntax', "struct literal can't be empty" + assert #@children % 2 == 0, Error 'syntax', "struct literal can't have uneven number values" { :Cell :RootCell - :TemplateString + :ArrayCell + :StructCell } diff --git a/alv/parsing.moon b/alv/parsing.moon index d5ec313..e8cc5b6 100644 --- a/alv/parsing.moon +++ b/alv/parsing.moon @@ -2,7 +2,7 @@ -- Lpeg Grammar for parsing `alive` code. -- -- @module parsing -import Cell, RootCell, Constant, TemplateString, Tag from require 'alv.ast' +import Cell, RootCell, ArrayCell, StructCell, Constant, TemplateString, Tag from require 'alv.ast' import R, S, P, V, C, Ct from require 'lpeg' -- whitespace @@ -29,26 +29,28 @@ fract = digit^1 * '/' * digit^1 float = (digit^1 * '.' * digit^0) + (digit^0 * '.' * digit^1) num = ((P '-')^-1 * (float + fract + int)) / Constant\parse 'num' -atom = num + sym + str -expr = (V 'cell') + atom - tplcont = ((P '\\"') + (P '\\\\') + (1 - (P '"') - (P '#')))^1 / 1 -tplstr = (P '#"') * ((P '##') / 1 + ('#' * expr) + tplcont)^0 * '"' / TemplateString\parse +tplstr = (P '#"') * ((P '##') / 1 + ('#' * (V 'expr')) + tplcont)^0 * '"' / TemplateString\parse -expitem = tplstr + expr +expitem = tplstr + (V 'expr') explist = Ct mspace * (expitem * (space * expitem)^0 * mspace)^-1 tag = (P '[') * (digit^1 / Tag.parse) * (P ']') cell = (P '(') * tag^-1 * explist * (P ')') / Cell.parse +array = (P '[') * explist * (P ']') / ArrayCell\parse +struct = (P '{') * explist * (P '}') / StructCell\parse + +atom = num + sym + str +expr = cell + array + struct + atom root = P { - explist / RootCell.parse - :cell + explist / RootCell\parse + :expr } cell = P { - 'cell' - :cell + cell + :expr } program = root * -1 diff --git a/docs/reference/05-1_arrays.md b/docs/reference/05-1_arrays.md index fdd26ff..19075f5 100644 --- a/docs/reference/05-1_arrays.md +++ b/docs/reference/05-1_arrays.md @@ -1,9 +1,11 @@ Arrays are composite types that contain a fixed number of values of the same -type. Arrays values can be created using the [`(array …)`][:array:] builtin, -which uses [Pure Op](04-2_pure-operators.html) semantics to construct an array -from its parameters, all of which have to be of the same type. +type. Arrays values can be created using square brackets `[1 2 3]` (which is +syntactic sugar for the [`(mkarray …)`][:mkarray:] builtin). - (trace (array 1 2 3)) +This uses [Pure Op](04-2_pure-operators.html) semantics to construct an array +from several values, all of which have to be of the same type. + + (trace [1 2 3]) ```output ``` @@ -11,4 +13,4 @@ from its parameters, all of which have to be of the same type. The type notation `num[3]` designates an array of three numbers, whereas the value notation `[1 2 3]` is used to show the array contents. -The [array-][:array-/:] module provides *Op*s for working with arrays. +The [array][:array/:] module provides *Op*s for working with arrays. diff --git a/docs/reference/05-2_structs.md b/docs/reference/05-2_structs.md index d3074c9..c9b234e 100644 --- a/docs/reference/05-2_structs.md +++ b/docs/reference/05-2_structs.md @@ -2,11 +2,13 @@ Structs are composite types that contain values of different types associated with a set of string keys. The set of keys and their corresponding value types is fixed at *runtime*. -Struct values can be created using the the [`(struct …)`][:struct:] builtin, -which uses [Pure Op](04-2_pure-operators.html) semantics to construct a struct +Struct values can be created using curly brackets `{"key" 3}` (which is +syntactic sugar for the [`(mkstruct …)`][:mkstruct:] builtin). + +This uses [Pure Op](04-2_pure-operators.html) semantics to construct a struct from its parameters. The keys have to be constants. - (trace (struct "a" 1 "b" 'hello world')) + (trace {"a" 1 "b" 'hello world'}) ```output <{a: num b: str}= {a: 1 b: "hello world"}> ``` @@ -16,4 +18,4 @@ mapping to a value of type `num` and the key `b` mapping to a value of type `str` respectively, whereas the value notation `{a: 1 b: "hello world"}` shows the struct contents. -The [struct-][:struct-/:] module provides *Op*s for working with arrays. +The [struct][:struct/:] module provides *Op*s for working with arrays. diff --git a/examples/love.alv b/examples/love.alv index 71a8287..113ea2d 100644 --- a/examples/love.alv +++ b/examples/love.alv @@ -3,17 +3,17 @@ The size changes when the left mouse button is held, and the color changes when space is pressed. ) -([1]import* love math time array-) +([1]import* love math time array) ([20]def #(cycle colors when space is pressed) fill-color ([21]switch ([22]key-presses "space") - ([23]array 0.3 0 0.9) - ([24]array 0 0.9 0.3) - ([27]array 0.9 0.3 0) - ([28]array 0.3 0.9 0) - ([30]array 0 0.3 0.9) - ([31]array 0.9 0 0.3)) + [0.3 0 0.9] + [0 0.9 0.3] + [0.9 0.3 0] + [0.3 0.9 0] + [0 0.3 0.9] + [0.9 0 0.3]) #(smooth out rgb channels individually) smooth-color ([36]map fill-color diff --git a/spec/internal/cell_spec.moon b/spec/internal/cell_spec.moon index bd97e63..c9ccb39 100644 --- a/spec/internal/cell_spec.moon +++ b/spec/internal/cell_spec.moon @@ -39,18 +39,18 @@ describe 'Cell', -> describe 'RootCell', -> test 'tag is always [0]', -> - cell = RootCell.parse {} + cell = RootCell\parse {} assert.is.equal '[0]', cell.tag\stringify! test 'head is always "do"', -> - cell = RootCell.parse {} + cell = RootCell\parse {} assert.is.equal (Constant.sym 'do'), cell\head! cell = RootCell nil, { hello_world, two_plus_two } assert.is.equal (Constant.sym 'do'), cell\head! test 'tail is all children', -> - cell = RootCell.parse {} + cell = RootCell\parse {} assert.is.same {}, cell\tail! cell = RootCell nil, { hello_world, two_plus_two } diff --git a/spec/lib/array_spec.moon b/spec/lib/array_spec.moon index b55cec6..130138c 100644 --- a/spec/lib/array_spec.moon +++ b/spec/lib/array_spec.moon @@ -2,147 +2,147 @@ import TestPilot from require 'spec.test_setup' import T, Array from require 'alv' describe "array", -> - test = TestPilot '', '(import* array-)\n' + test = TestPilot '', '(import* array)\n' svec3 = Array 3, T.str it "can contain any type", -> - COPILOT\eval_once '(array 1 2 3)' - COPILOT\eval_once '(array true false)' - COPILOT\eval_once '(array "a")' - COPILOT\eval_once '(array (array 1 2) (array 3 4))' + COPILOT\eval_once '[1 2 3]' + COPILOT\eval_once '[true false]' + COPILOT\eval_once '["a"]' + COPILOT\eval_once '[[1 2] [3 4]]' it "cannot contain mixed types", -> - err = assert.has.error -> COPILOT\eval_once '(array 1 false)' + err = assert.has.error -> COPILOT\eval_once '[1 false]' assert.matches "argument error: couldn't match arguments", err describe "(set)", -> it "can swap values", -> - rt = COPILOT\eval_once '(set (array "f" "b" "c") 0 "a")' + rt = COPILOT\eval_once '(set ["f" "b" "c"] 0 "a")' assert.is.true rt\is_const! assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result it "checks value type", -> - err = assert.has.error -> COPILOT\eval_once '(set (array 1) 0 "a")' + err = assert.has.error -> COPILOT\eval_once '(set [1] 0 "a")' 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)' + err = assert.has.error -> COPILOT\eval_once '(set [1 2] -1 0)' assert.matches "index '%-1' out of range!", err - COPILOT\eval_once '(set (array 1 2) 0 0)' + COPILOT\eval_once '(set [1 2] 0 0)' - COPILOT\eval_once '(set (array 1 2) 1 0)' + COPILOT\eval_once '(set [1 2] 1 0)' - err = assert.has.error -> COPILOT\eval_once '(set (array 1 2) 2 0)' + err = assert.has.error -> COPILOT\eval_once '(set [1 2] 2 0)' assert.matches "index '2' out of range!", err describe "(get)", -> it "can get a value", -> - rt = COPILOT\eval_once '(get (array 1 2) 0)' + rt = COPILOT\eval_once '(get [1 2] 0)' assert.is.true rt\is_const! assert.is.equal '', tostring rt.result it "checks index range", -> - err = assert.has.error -> COPILOT\eval_once '(get (array 1 2) -1)' + err = assert.has.error -> COPILOT\eval_once '(get [1 2] -1)' assert.matches "index '%-1' out of range!", err - COPILOT\eval_once '(get (array 1 2) 0)' + COPILOT\eval_once '(get [1 2] 0)' - COPILOT\eval_once '(get (array 1 2) 1)' + COPILOT\eval_once '(get [1 2] 1)' - err = assert.has.error -> COPILOT\eval_once '(get (array 1 2) 2)' + err = assert.has.error -> COPILOT\eval_once '(get [1 2] 2)' assert.matches "index '2' out of range!", err describe '(head)', -> it "can peek a value", -> - rt = COPILOT\eval_once '(head (array 1 2))' + rt = COPILOT\eval_once '(head [1 2])' assert.is.true rt\is_const! assert.is.equal '', tostring rt.result describe '(tail)', -> it "gets rest of an array", -> - rt = COPILOT\eval_once '(tail (array 1))' + rt = COPILOT\eval_once '(tail [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))' + rt = COPILOT\eval_once '(tail [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))' + rt = COPILOT\eval_once '(tail [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)' + rt = COPILOT\eval_once '(prepend [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)' + rt = COPILOT\eval_once '(prepend [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")' + rt = COPILOT\eval_once '(insert ["b" "c"] 0 "a")' assert.is.true rt\is_const! assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result - rt = COPILOT\eval_once '(insert (array "a" "c") 1 "b")' + rt = COPILOT\eval_once '(insert ["a" "c"] 1 "b")' assert.is.true rt\is_const! assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result - rt = COPILOT\eval_once '(insert (array "a" "b") 2 "c")' + rt = COPILOT\eval_once '(insert ["a" "b"] 2 "c")' assert.is.true rt\is_const! assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result it "checks index range", -> - err = assert.has.error -> COPILOT\eval_once '(insert (array 1 2) -1 0)' + err = assert.has.error -> COPILOT\eval_once '(insert [1 2] -1 0)' assert.matches "index '%-1' out of range!", err - COPILOT\eval_once '(insert (array 1 2) 0 0)' + COPILOT\eval_once '(insert [1 2] 0 0)' - COPILOT\eval_once '(insert (array 1 2) 1 0)' + COPILOT\eval_once '(insert [1 2] 1 0)' - COPILOT\eval_once '(insert (array 1 2) 2 0)' + COPILOT\eval_once '(insert [1 2] 2 0)' - err = assert.has.error -> COPILOT\eval_once '(insert (array 1 2) 3 0)' + err = assert.has.error -> COPILOT\eval_once '(insert [1 2] 3 0)' assert.matches "index '3' out of range!", err describe "(remove)", -> it "can remove a value", -> - rt = COPILOT\eval_once '(remove (array "d" "a" "b" "c") 0)' + rt = COPILOT\eval_once '(remove ["d" "a" "b" "c"] 0)' assert.is.true rt\is_const! assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result - rt = COPILOT\eval_once '(remove (array "a" "b" "c" "d") 3)' + rt = COPILOT\eval_once '(remove ["a" "b" "c" "d"] 3)' assert.is.true rt\is_const! assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result it "checks index range", -> - err = assert.has.error -> COPILOT\eval_once '(remove (array 1 2 3) -1)' + err = assert.has.error -> COPILOT\eval_once '(remove [1 2 3] -1)' assert.matches "index '%-1' out of range!", err - err = assert.has.error -> COPILOT\eval_once '(remove (array 1 2 3) 3)' + err = assert.has.error -> COPILOT\eval_once '(remove [1 2 3] 3)' assert.matches "index '3' out of range!", err it "can be concatenated with (concat)", -> - rt = COPILOT\eval_once '(concat (array "a" "b") (array "c"))' + rt = COPILOT\eval_once '(concat ["a" "b"] ["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))' + rt = COPILOT\eval_once '(size [1])' assert.is.true rt\is_const! assert.is.equal '', tostring rt.result - rt = COPILOT\eval_once '(size (array 1 2 3))' + rt = COPILOT\eval_once '(size [1 2 3])' assert.is.true rt\is_const! assert.is.equal '', tostring rt.result diff --git a/spec/lib/builtins/cond_spec.moon b/spec/lib/builtins/cond_spec.moon index 04c56ef..b075f8b 100644 --- a/spec/lib/builtins/cond_spec.moon +++ b/spec/lib/builtins/cond_spec.moon @@ -5,7 +5,7 @@ describe "if", -> COPILOT = TestPilot! it "checks truthiness", -> - for truthy in *{'true', '1', '-1', '1234', '(array 1 2 3)', '"test"', '""'} + for truthy in *{'true', '1', '-1', '1234', '[1 2 3]', '"test"', '""'} with COPILOT\eval_once "(if #{truthy} 'yes' 'no')" assert.is.true \is_const! assert.is.equal 'yes', .result! @@ -41,7 +41,7 @@ describe "if", -> it "forwards any result", -> with COPILOT\eval_once ' (import* time) - (if true (every 1 (array 1 2 3)))' + (if true (every 1 [1 2 3]))' assert.is.false \is_const! assert.is.equal '', tostring .result @@ -49,7 +49,7 @@ describe "when", -> COPILOT = TestPilot! it "checks truthiness", -> - for truthy in *{'true', '1', '-1', '1234', '(array 1 2 3)', '"test"', '""'} + for truthy in *{'true', '1', '-1', '1234', '[1 2 3]', '"test"', '""'} with COPILOT\eval_once "(when #{truthy} 'yes')" assert.is.true \is_const! assert.is.equal 'yes', .result! @@ -81,12 +81,12 @@ describe "when", -> with COPILOT\eval_once ' (import* time) (when true - (every 1 (array 1 2 3)) + (every 1 [1 2 3]) 1 2 3)' assert.is.false \is_const! assert.is.equal '', tostring .result - with COPILOT\eval_once '(when true (array 1 2 3))' + with COPILOT\eval_once '(when true [1 2 3])' assert.is.true \is_const! assert.is.equal '', tostring .result diff --git a/spec/lib/builtins/literal_spec.moon b/spec/lib/builtins/literal_spec.moon index 3db010d..61a9b90 100644 --- a/spec/lib/builtins/literal_spec.moon +++ b/spec/lib/builtins/literal_spec.moon @@ -6,8 +6,8 @@ describe "literal", -> (def str "hello" num 2 bool true - curl ([5]struct "a" 2 "b" false) - sqre ([7]array 1 2 3 4)) + curl {"a" 2 "b" false} + sqre [1 2 3 4]) (export*)' assert.is.true COPILOT.active_module.root\is_const! diff --git a/spec/lib/logic_spec.moon b/spec/lib/logic_spec.moon index 2760e54..5b71465 100644 --- a/spec/lib/logic_spec.moon +++ b/spec/lib/logic_spec.moon @@ -9,25 +9,25 @@ describe "logic", -> (expect= false (== 1 2)) (expect= false (== 1 "hello")) (expect= true (== "hello" "hello")) - (expect= true (== (array 1 2 3) (array 1 2 3))) - (expect= false (== (array 1 2 3) (array 1 2 1))) - (expect= false (== (array 1 2 3) (array 1 2))) - (expect= false (== (array 1 2 3) (array 1 2 3 4))) + (expect= true (== [1 2 3] [1 2 3])) + (expect= false (== [1 2 3] [1 2 1])) + (expect= false (== [1 2 3] [1 2])) + (expect= false (== [1 2 3] [1 2 3 4])) (expect= true (== - (struct "a" 1 "b" true "c" (array "test")) - (struct "a" 1 "b" true "c" (array "test")))) + {"a" 1 "b" true "c" ["test"]} + {"a" 1 "b" true "c" ["test"]})) (expect= false (== - (struct "a" 1 "b" false "c" (array "test")) - (struct "a" 1 "b" true "c" (array "test")))) + {"a" 1 "b" false "c" ["test"]} + {"a" 1 "b" true "c" ["test"]})) (expect= false (== - (struct "a" 1 "b" true "c" (array "test" "toast")) - (struct "a" 1 "b" true "c" (array "test")))) + {"a" 1 "b" true "c" ["test" "toast"]} + {"a" 1 "b" true "c" ["test"]})) (expect= false (== - (struct "a" 1 "b" true) - (struct "a" 1 "b" true "c" (array "test")))) + {"a" 1 "b" true} + {"a" 1 "b" true "c" ["test"]})) (expect= false (== - (struct "a" 1 "b" true) - (struct "a" 1))) + {"a" 1 "b" true} + {"a" 1})) (expect= true (== print print)) (expect= false (== print ==)) ' @@ -42,25 +42,25 @@ describe "logic", -> (expect= true (!= 1 2)) (expect= true (!= 1 "hello")) (expect= false (!= "hello" "hello")) - (expect= false (!= (array 1 2 3) (array 1 2 3))) - (expect= true (!= (array 1 2 3) (array 1 2 1))) - (expect= true (!= (array 1 2 3) (array 1 2))) - (expect= true (!= (array 1 2 3) (array 1 2 3 4))) + (expect= false (!= [1 2 3] [1 2 3])) + (expect= true (!= [1 2 3] [1 2 1])) + (expect= true (!= [1 2 3] [1 2])) + (expect= true (!= [1 2 3] [1 2 3 4])) (expect= false (!= - (struct "a" 1 "b" true "c" (array "test")) - (struct "a" 1 "b" true "c" (array "test")))) + {"a" 1 "b" true "c" ["test"]} + {"a" 1 "b" true "c" ["test"]})) (expect= true (!= - (struct "a" 1 "b" false "c" (array "test")) - (struct "a" 1 "b" true "c" (array "test")))) + {"a" 1 "b" false "c" ["test"]} + {"a" 1 "b" true "c" ["test"]})) (expect= true (!= - (struct "a" 1 "b" true "c" (array "test" "toast")) - (struct "a" 1 "b" true "c" (array "test")))) + {"a" 1 "b" true "c" ["test" "toast"]} + {"a" 1 "b" true "c" ["test"]})) (expect= true (!= - (struct "a" 1 "b" true) - (struct "a" 1 "b" true "c" (array "test")))) + {"a" 1 "b" true} + {"a" 1 "b" true "c" ["test"]})) (expect= true (!= - (struct "a" 1 "b" true) - (struct "a" 1))) + {"a" 1 "b" true} + {"a" 1})) (expect= false (!= print print)) (expect= true (!= print ==)) ' diff --git a/spec/lib/math_spec.moon b/spec/lib/math_spec.moon index 23a353b..40c7abd 100644 --- a/spec/lib/math_spec.moon +++ b/spec/lib/math_spec.moon @@ -88,85 +88,83 @@ describe "math", -> describe "add, sub, mul, div, pow, mod", -> it "handle scalar/vector", -> COPILOT\eval_once ' - (expect= (array 3 4 5) - (+ 1 (array 1 2 3) 1)) + (expect= [3 4 5] + (+ 1 [1 2 3] 1)) - (expect= (array 0 1 2) - (- (array 1 2 3) 1)) + (expect= [0 1 2] + (- [1 2 3] 1)) - (expect= (array 3 6 9) - (* 3 (array 1 2 3))) - (expect= (array 3 6 9) - (* (array 1 2 3) 3)) + (expect= [3 6 9] + (* 3 [1 2 3])) + (expect= [3 6 9] + (* [1 2 3] 3)) - (expect= (array 12 9 4) - (/ 36 (array 3 4 9))) + (expect= [12 9 4] + (/ 36 [3 4 9])) - (expect= (array 9 16 25) - (^ (array 3 4 5) 2)) - (expect= (array 1 2 4 8) - (^ 2 (array 0 1 2 3))) + (expect= [9 16 25] + (^ [3 4 5] 2)) + (expect= [1 2 4 8] + (^ 2 [0 1 2 3])) - (expect= (array 3 0 1) - (% (array 3 4 5) 4)) + (expect= [3 0 1] + (% [3 4 5] 4)) ' it "handle vector/vector and matrix/matrix", -> COPILOT\eval_once ' - (expect= (array 5 7 9) - (+ (array 1 2 3) - (array 4 5 6))) + (expect= [5 7 9] + (+ [1 2 3] + [4 5 6])) - (expect= (array (array 11 12) - (array 13 14)) + (expect= [[11 12] + [13 14]] (+ - (array (array 1 2) - (array 3 4)) + [[1 2] + [3 4]] 5 5)) - (expect= (array 2 0 -2) - (- (array 3 2 1) - (array 1 2 3))) + (expect= [2 0 -2] + (- [3 2 1] + [1 2 3])) - (expect= (array 1 -2 -3) - (- (array -1 2 3))) + (expect= [1 -2 -3] + (- [-1 2 3])) ' err = assert.has.error -> COPILOT\eval_once ' - (+ (array 1 2 3) - (array 1 2))' + (+ [1 2 3] + [1 2])' err = assert.has.error -> COPILOT\eval_once ' - (+ (array (array 1 2) (array 1 2)) - (array 1 2))' + (+ [[1 2] [1 2]] + [1 2])' err = assert.has.error -> COPILOT\eval_once ' - (- (array 1 2 3) - (array 1 2))' + (- [1 2 3] + [1 2])' err = assert.has.error -> COPILOT\eval_once ' - (- (array (array 1 2) (array 1 2)) - (array 1 2))' + (- [[1 2] [1 2]] + [1 2])' describe "mul", -> it "handles scalars and matrices", -> with COPILOT\eval_once ' (* 3 - (array - (array 1 2) - (array 4 5)))' + [[1 2] + [4 5]])' assert.is.true \is_const! assert.is.equal '', tostring .result with COPILOT\eval_once ' - (* (array - (array 1 2) - (array 4 5)) + (* [[1 2] + [4 5]] 3)' assert.is.true \is_const! assert.is.equal '', tostring .result @@ -174,90 +172,90 @@ describe "math", -> it "handles vectors and matrices", -> with COPILOT\eval_once ' (* - (array (array 1 0 0) - (array 0 1 0) - (array 0 0 1)) - (array 4 5 6))' + [[1 0 0] + [0 1 0] + [0 0 1]] + [4 5 6])' assert.is.true \is_const! assert.is.equal '', tostring .result with COPILOT\eval_once ' (* - (array (array 1 0 0) - (array 0 1 0) - (array 3 2 1)) - (array 4 5 1))' + [[1 0 0] + [0 1 0] + [3 2 1]] + [4 5 1])' assert.is.true \is_const! assert.is.equal '', tostring .result it "handles matrices", -> with COPILOT\eval_once ' (* - (array (array 1 2 3) - (array 4 5 6)) - (array (array 10 11) - (array 20 21) - (array 30 31)))' + [[1 2 3] + [4 5 6]] + [[10 11] + [20 21] + [30 31]])' assert.is.true \is_const! assert.is.equal '', tostring .result it "handles everything mixed", -> with COPILOT\eval_once ' (* - (array (array 1 2 3) - (array 4 5 6)) + [[1 2 3] + [4 5 6]] 2 - (array (array 10 11) - (array 20 21) - (array 30 31)))' + [[10 11] + [20 21] + [30 31]])' assert.is.true \is_const! assert.is.equal '', tostring .result with COPILOT\eval_once ' (* - (array (array 1 2 3) - (array 4 5 6)) + [[1 2 3] + [4 5 6]] 2 - (array (array 10 11) - (array 20 21) - (array 30 31)) - (array 4 7))' + [[10 11] + [20 21] + [30 31]] + [4 7])' assert.is.true \is_const! assert.is.equal '', tostring .result it "errors with wrong sizes (matrix and vector)", -> err = assert.has.error -> COPILOT\eval_once ' (* - (array (array 1 2 3) - (array 4 5 6)) - (array 1 2))' + [[1 2 3] + [4 5 6]] + [1 2])' -- assert.matches "", err err = assert.has.error -> COPILOT\eval_once ' (* - (array 1 2 3) - (array (array 1 2 3) - (array 4 5 6)))' + [1 2 3] + [[1 2 3] + [4 5 6]])' -- assert.matches "", err it "errors with wrong sizes (matrix)", -> err = assert.has.error -> COPILOT\eval_once ' (* - (array (array 1 2 3) - (array 4 5 6)) - (array (array 1 2) - (array 4 5)))' + [[1 2 3] + [4 5 6)] + [[1 2] + [4 5]])' -- assert.matches "", err it "min, max, clamp, huge", -> COPILOT\eval_once ' - (expect= (array 3 2 1) - (min (array 3 4 1) (array 5 2 huge))) - (expect= (array 5 huge 4) - (max (array 3 huge 4) (array 5 999 2))) + (expect= [3 2 1] + (min [3 4 1] [5 2 huge])) + (expect= [5 huge 4] + (max [3 huge 4] [5 999 2])) - (expect= (array -2 -1 0 1 3.5) - (clamp -2 3.5 (array -4 -1 0 1 4))) + (expect= [-2 -1 0 1 3.5] + (clamp -2 3.5 [-4 -1 0 1 4])) (expect= 1 (inc 0)) (expect= -1 (dec 0)) diff --git a/spec/lib/string_spec.moon b/spec/lib/string_spec.moon index 859c402..c787609 100644 --- a/spec/lib/string_spec.moon +++ b/spec/lib/string_spec.moon @@ -16,22 +16,22 @@ describe "string", -> it "stringifies arrays", -> COPILOT\eval_once ' - (expect= "[1 2 3]" (string/str (array 1 2 3))) - (expect= \'["a" "b" "c"]\' (string/str (array "a" "b" "c"))) + (expect= "[1 2 3]" (string/str [1 2 3])) + (expect= \'["a" "b" "c"]\' (string/str ["a" "b" "c"])) ' it "stringifies structs", -> COPILOT\eval_once ' (expect= \'{a: 1 b: true c: "hello"}\' - (string/str (struct "a" 1 - "b" true - "c" "hello"))) + (string/str {"a" 1 + "b" true + "c" "hello"})) ' it "stringifies deeply", -> COPILOT\eval_once ' (expect= "{a: {b: [1 2 3]}}" - (string/str (struct "a" (struct "b" (array 1 2 3))))) + (string/str {"a" {"b" [1 2 3]}})) ' it "joins multiple arguments", -> @@ -48,23 +48,23 @@ describe "string", -> describe "concat", -> it "concatenates string-arrays", -> COPILOT\eval_once ' - (expect= "hello" (string/concat (array "hello"))) - (expect= "helloworld" (string/concat (array "hello" "world"))) - (expect= "helloobeautifulworld" (string/concat (array "hello" "o" "beautiful" "world"))) + (expect= "hello" (string/concat ["hello"])) + (expect= "helloworld" (string/concat ["hello" "world"])) + (expect= "helloobeautifulworld" (string/concat ["hello" "o" "beautiful" "world"])) ' it "takes custom separator", -> COPILOT\eval_once ' - (expect= "a, b, c" (string/concat ", " (array "a" "b" "c"))) - (expect= "hello world" (string/concat " " (array "hello" "world"))) - (expect= "hello o beautiful world" (string/concat " " (array "hello" "o" "beautiful" "world"))) + (expect= "a, b, c" (string/concat ", " ["a" "b" "c"])) + (expect= "hello world" (string/concat " " ["hello" "world"])) + (expect= "hello o beautiful world" (string/concat " " ["hello" "o" "beautiful" "world"])) ' describe "join", -> it "concatenates and stringifies", -> COPILOT\eval_once ' (expect= "that is 1 beautiful tree" (string/join " " "that is" 1 "beautiful tree")) - (expect= "my favorite color is [0.9 0.2 1]" (string/join " " "my favorite color is" (array 0.9 0.2 1))) + (expect= "my favorite color is [0.9 0.2 1]" (string/join " " "my favorite color is" [0.9 0.2 1])) (expect= "i_am_snek" (string/join "_" "i" "am" "snek")) ' diff --git a/spec/lib/struct_spec.moon b/spec/lib/struct_spec.moon index 9937f99..94dcc5a 100644 --- a/spec/lib/struct_spec.moon +++ b/spec/lib/struct_spec.moon @@ -2,50 +2,50 @@ import TestPilot from require 'spec.test_setup' import T, Struct from require 'alv' describe "struct", -> - test = TestPilot '', '(import* struct-)\n' + test = TestPilot '', '(import* struct)\n' ab = Struct { a: T.num, b: T.bool } describe "(set)", -> it "can update values", -> - rt = COPILOT\eval_once '(set (struct "a" 1 "b" false) "a" 2)' + rt = COPILOT\eval_once '(set {"a" 1 "b" false} "a" 2)' assert.is.true rt\is_const! assert.is.equal ab\mk_const({ a: 2, b: false }), rt.result it "cannot add members", -> - err = assert.has.error -> COPILOT\eval_once '(set (struct "a" 1) "b" 2)' + err = assert.has.error -> COPILOT\eval_once '(set {"a" 1} "b" 2)' assert.matches "{a: num} has no 'b' key", err it "checks value type", -> - err = assert.has.error -> COPILOT\eval_once '(set (struct "a" 1) "a" "str")' + err = assert.has.error -> COPILOT\eval_once '(set {"a" 1} "a" "str")' assert.matches "expected value for key 'a' to be num, not str", err describe "(get)", -> it "can get values", -> - rt = COPILOT\eval_once '(get (struct "a" 1 "b" false) "a")' + rt = COPILOT\eval_once '(get {"a" 1 "b" false} "a")' assert.is.true rt\is_const! assert.is.equal '', tostring rt.result it "checks keys", -> - err = assert.has.error -> COPILOT\eval_once '(get (struct "a" 1) "b")' + err = assert.has.error -> COPILOT\eval_once '(get {"a" 1} "b")' assert.matches "has no 'b' key", err describe "(insert)", -> it "can add members", -> - rt = COPILOT\eval_once '(insert (struct "b" true) "a" 1)' + rt = COPILOT\eval_once '(insert {"b" true} "a" 1)' assert.is.true rt\is_const! assert.is.equal ab\mk_const({ a: 1, b: true }), rt.result it "doesn't clobber existing members", -> - err = assert.has.error -> COPILOT\eval_once '(insert (struct "a" 1) "a" 2)' + err = assert.has.error -> COPILOT\eval_once '(insert {"a" 1} "a" 2)' assert.matches "key 'a' already exists in value of type {a: num}", err describe "(remove)", -> it "can remove members", -> - rt = COPILOT\eval_once '(remove (struct "a" 1 "b" false "c" "abc") "c")' + rt = COPILOT\eval_once '(remove {"a" 1 "b" false "c" "abc"} "c")' assert.is.true rt\is_const! assert.is.equal ab\mk_const({ a: 1, b: false }), rt.result it "checks keys", -> - err = assert.has.error -> COPILOT\eval_once '(remove (struct "a" 1) "b")' + err = assert.has.error -> COPILOT\eval_once '(remove {"a" 1} "b")' assert.matches "has no 'b' key", err diff --git a/spec/lib/testing_spec.moon b/spec/lib/testing_spec.moon index 5be7848..0605c00 100644 --- a/spec/lib/testing_spec.moon +++ b/spec/lib/testing_spec.moon @@ -46,7 +46,7 @@ describe "testing", -> assert.is.true \is_const! assert.is.nil .result - with COPILOT\eval_once '(expect= (array 1 2) (array 1 2))' + with COPILOT\eval_once '(expect= [1 2] (mkarray 1 2))' assert.is.true \is_const! assert.is.nil .result @@ -58,7 +58,7 @@ describe "testing", -> assert.has.error -> COPILOT\eval_once '(expect= true false)' assert.has.error -> COPILOT\eval_once '(expect= "asdf" "bsdf")' - assert.has.error -> COPILOT\eval_once '(expect= (array 1 2) (array 1 3))' + assert.has.error -> COPILOT\eval_once '(expect= [1 2] [1 3])' it "fails different types", -> assert.has.error -> COPILOT\eval_once '(expect= true 2)' -- cgit v1.2.3