diff --git a/anselme/ast/Typed.lua b/anselme/ast/Typed.lua index 4652641..1ea7c72 100644 --- a/anselme/ast/Typed.lua +++ b/anselme/ast/Typed.lua @@ -28,7 +28,7 @@ Typed = ast.abstract.Runtime { return custom_format:call(state, d_args):format(state, prio, ...) end end - return ("type(%s, %s)"):format(self.type_expression:format(state, operator_priority["_,_"], ...), self.expression:format_right(state, operator_priority["_,_"], ...)) + return ("type(%s, %s)"):format(self.expression:format(state, operator_priority["_,_"], ...), self.type_expression:format_right(state, operator_priority["_,_"], ...)) end, traverse = function(self, fn, ...) diff --git a/anselme/stdlib/attached block.lua b/anselme/stdlib/attached block.lua index fa4babf..73cbc12 100644 --- a/anselme/stdlib/attached block.lua +++ b/anselme/stdlib/attached block.lua @@ -11,12 +11,17 @@ return { function(state, level, keep_return) -- level 2: env of the function that called the function that called attached block local env = calling_environment_manager:get_level(state, level:to_lua(state)+1) - local r = env:get(state, block_identifier) + local block = env:get(state, block_identifier).expression + local fn if keep_return:truthy() then - return Function:new(ParameterTuple:new(), r.expression):eval(state) + fn = Function:new(ParameterTuple:new(), block) else - return Function:with_return_boundary(ParameterTuple:new(), r.expression):eval(state) + fn = Function:with_return_boundary(ParameterTuple:new(), block) end + state.scope:push(env) + fn = fn:eval(state) -- make closure + state.scope:pop() + return fn end }, { @@ -25,12 +30,17 @@ return { -- level 2: env of the function that called the function that called attached block local env = calling_environment_manager:get_level(state, level:to_lua(state)+1) if env:defined(state, block_identifier) then - local r = env:get(state, block_identifier) + local block = env:get(state, block_identifier).expression + local fn if keep_return:truthy() then - return Function:new(ParameterTuple:new(), r.expression):eval(state) + fn = Function:new(ParameterTuple:new(), block) else - return Function:with_return_boundary(ParameterTuple:new(), r.expression):eval(state) + fn = Function:with_return_boundary(ParameterTuple:new(), block) end + state.scope:push(env) + fn = fn:eval(state) -- make closure + state.scope:pop() + return fn else return default end diff --git a/anselme/stdlib/for.lua b/anselme/stdlib/for.lua new file mode 100644 index 0000000..bd782fc --- /dev/null +++ b/anselme/stdlib/for.lua @@ -0,0 +1,50 @@ +return [[ +/* For loop */ +:@$for(symbol::symbol, var, block=attached block(keep return=true)) + :iterator = iter(var) + :value = iterator() + :name = symbol!to string + block.(symbol) = value + while($value != ()) + :r = block! + value = iterator() + block.(name) = value + r + +/* Range iterables */ +:@$range(stop) + [1, stop, 1]!type("range") +:@$range(start, stop, step=1) + [start, stop, step]!type("range") +:@$iter(range::is("range")) + :v = range!value + :start = v(1) + :stop = v(2) + :step = v(3) + :i = start + if(step > 0) + return($_) + if(i <= stop) + i += step + return(i-step) + else! + return($_) + if(i >= stop) + i += step + return(i-step) + +/* List/tuple iterables */ +:tuple or list = $(x) x!type == "tuple" | x!type == "list" +:@$iter(tuple::tuple or list) + :n = tuple!len + :i = 0 + $ + if(i < n) + i += 1 + return(tuple(i)) + +/* Table */ +:@$iter(table::table) + :s = table!to struct + iter(s) +]] diff --git a/anselme/stdlib/init.lua b/anselme/stdlib/init.lua index cf8f700..0f65395 100644 --- a/anselme/stdlib/init.lua +++ b/anselme/stdlib/init.lua @@ -26,6 +26,7 @@ return function(main_state) "boot", "number", "string", + "symbol", "text", "pair", "structures", @@ -34,6 +35,7 @@ return function(main_state) "function", "resume", "persist", + "for", "script" }) end diff --git a/anselme/stdlib/structures.lua b/anselme/stdlib/structures.lua index d8ccab2..a104d1b 100644 --- a/anselme/stdlib/structures.lua +++ b/anselme/stdlib/structures.lua @@ -1,5 +1,5 @@ local ast = require("anselme.ast") -local Nil, List, Table, Number = ast.Nil, ast.List, ast.Table, ast.Number +local Nil, List, Table, Number, LuaFunction, ParameterTuple, Boolean = ast.Nil, ast.List, ast.Table, ast.Number, ast.LuaFunction, ast.ParameterTuple, ast.Boolean return { -- tuple @@ -112,6 +112,23 @@ return { return s:get(k) end }, + { + "has", "(s::struct, key)", + function(state, s, k) + return Boolean:new(s:has(k)) + end + }, + { + "iter", "(s::struct)", + function(state, struct) + local iter = struct:iter() + return LuaFunction:new(ParameterTuple:new(), function() + local k = iter() + if k == nil then return Nil:new() + else return k end + end) + end + }, -- table { @@ -127,6 +144,12 @@ return { return Nil:new() end }, + { + "has", "(t::table, key)", + function(state, t, k) + return Boolean:new(t:has(state, k)) + end + }, { "to struct", "(t::table)", function(state, t) diff --git a/anselme/stdlib/symbol.lua b/anselme/stdlib/symbol.lua new file mode 100644 index 0000000..e5e22dd --- /dev/null +++ b/anselme/stdlib/symbol.lua @@ -0,0 +1,9 @@ +return { + + { + "to string", "(symbol::symbol)", + function(state, sym) + return sym:to_string() + end + }, +} diff --git a/test/results/constrained variable assignement.ans b/test/results/constrained variable assignement.ans index 8944dc7..87f10fc 100644 --- a/test/results/constrained variable assignement.ans +++ b/test/results/constrained variable assignement.ans @@ -1,8 +1,8 @@ --# run #-- --- text --- -| {}"" {}"type(\"kg\", 5)" {}"" | +| {}"" {}"type(5, \"kg\")" {}"" | --- text --- -| {}"" {}"type(\"kg\", 12)" {}"" | +| {}"" {}"type(12, \"kg\")" {}"" | --- error --- type check failure for weigh; 32 does not satisfy $(x) type(x) == t ↳ from test/tests/constrained variable assignement.ans:9:7 in assignment: weigh = 32 diff --git a/test/results/constrained variable definition.ans b/test/results/constrained variable definition.ans index 89ad27f..c81a028 100644 --- a/test/results/constrained variable definition.ans +++ b/test/results/constrained variable definition.ans @@ -1,6 +1,6 @@ --# run #-- --- text --- -| {}"" {}"type(\"kg\", 5)" {}"" | +| {}"" {}"type(5, \"kg\")" {}"" | --- text --- | {}"" {}"12" {}"" | --- return --- diff --git a/test/results/exported variable nested.ans b/test/results/exported variable nested.ans index 900b8f1..d355df6 100644 --- a/test/results/exported variable nested.ans +++ b/test/results/exported variable nested.ans @@ -5,7 +5,7 @@ --- text --- | {}"" {}"42" {}"" | --- error --- -identifier "z" is undefined in branch cf017f8a-7c86-4871-109af-6658231331e6 +identifier "z" is undefined in branch 7dfcaa5b-2163-4f36-116ed-c4ab6bc8d28b ↳ from test/tests/exported variable nested.ans:12:3 in identifier: z ↳ from test/tests/exported variable nested.ans:12:1 in text interpolation: | {z} | ↳ from ? in block: :f = ($() _)… diff --git a/test/results/for break.ans b/test/results/for break.ans new file mode 100644 index 0000000..1c15f56 --- /dev/null +++ b/test/results/for break.ans @@ -0,0 +1,8 @@ +--# run #-- +--- text --- +| {}"" {}"1" {}"" | +| {}"" {}"2" {}"" | +--- return --- +() +--# saved #-- +{} \ No newline at end of file diff --git a/test/results/for continue.ans b/test/results/for continue.ans new file mode 100644 index 0000000..63ad50d --- /dev/null +++ b/test/results/for continue.ans @@ -0,0 +1,9 @@ +--# run #-- +--- text --- +| {}"" {}"1" {}"" | +| {}"" {}"2" {}"" | +| {}"" {}"4" {}"" | +--- return --- +() +--# saved #-- +{} \ No newline at end of file diff --git a/test/results/for invalid iterator.ans b/test/results/for invalid iterator.ans new file mode 100644 index 0000000..23f29df --- /dev/null +++ b/test/results/for invalid iterator.ans @@ -0,0 +1,15 @@ +--# run #-- +--- error --- +can't call overload overload<($(table::($(x) )) _), ($(tuple::($(x) type(x) == "tuple" | type(x) == "list")) _), ($(range::($(x) type(x) == t)) _), ($(s::($(x) )) )>: no function match (42), possible functions were: + • (table::($(x) )): type check failure for parameter table in function (table::($(x) )) + • (tuple::($(x) type(x) == "tuple" | type(x) == "list")): type check failure for parameter tuple in function (tuple::($(x) type(x) == "tuple" | type(x) == "list")) + • (range::($(x) type(x) == t)): type check failure for parameter range in function (range::($(x) type(x) == t)) + • (s::($(x) )): type check failure for parameter s in function (s::($(x) )) + ↳ from for.ans:3:18 in call: iter(var) + ↳ from for.ans:3:12 in definition: :iterator = iter(var) + ↳ from for.ans:2:1 in block: :iterator = iter(var)… + ↳ from for.ans:2:68 in call: _ + ↳ from test/tests/for invalid iterator.ans:1:4 in call: for(:x, 42) + ↳ from ? in block: for(:x, 42)… +--# saved #-- +{} \ No newline at end of file diff --git a/test/results/for list.ans b/test/results/for list.ans new file mode 100644 index 0000000..b1af884 --- /dev/null +++ b/test/results/for list.ans @@ -0,0 +1,10 @@ +--# run #-- +--- text --- +| {}"" {}"7" {}"" | +| {}"" {}"96" {}"" | +| {}"" {}"a" {}"" | +| {}"" {}"3" {}"" | +--- return --- +() +--# saved #-- +{} \ No newline at end of file diff --git a/test/results/for range.ans b/test/results/for range.ans new file mode 100644 index 0000000..8454397 --- /dev/null +++ b/test/results/for range.ans @@ -0,0 +1,38 @@ +--# run #-- +--- text --- +| {}"-" | +| {}"" {}"1" {}"" | +| {}"" {}"2" {}"" | +| {}"" {}"3" {}"" | +| {}"" {}"4" {}"" | +| {}"" {}"5" {}"" | +--- text --- +| {}"-" | +| {}"" {}"2" {}"" | +| {}"" {}"3" {}"" | +| {}"" {}"4" {}"" | +| {}"" {}"5" {}"" | +| {}"" {}"6" {}"" | +| {}"" {}"7" {}"" | +--- text --- +| {}"-" | +| {}"" {}"2" {}"" | +| {}"" {}"5" {}"" | +--- text --- +| {}"-" | +| {}"" {}"4" {}"" | +| {}"" {}"3" {}"" | +| {}"" {}"2" {}"" | +| {}"" {}"1" {}"" | +| {}"" {}"0" {}"" | +--- text --- +| {}"-" | +--- text --- +| {}"-" | +--- text --- +| {}"-" | +| {}"" {}"4" {}"" | +--- return --- +() +--# saved #-- +{} \ No newline at end of file diff --git a/test/results/for struct.ans b/test/results/for struct.ans new file mode 100644 index 0000000..d4df120 --- /dev/null +++ b/test/results/for struct.ans @@ -0,0 +1,10 @@ +--# run #-- +--- text --- +| {}"" {}"true" | +| {}"" {}"true" | +| {}"" {}"true" | +| {}"" {}"true" | +--- return --- +() +--# saved #-- +{} \ No newline at end of file diff --git a/test/results/for table.ans b/test/results/for table.ans new file mode 100644 index 0000000..d4df120 --- /dev/null +++ b/test/results/for table.ans @@ -0,0 +1,10 @@ +--# run #-- +--- text --- +| {}"" {}"true" | +| {}"" {}"true" | +| {}"" {}"true" | +| {}"" {}"true" | +--- return --- +() +--# saved #-- +{} \ No newline at end of file diff --git a/test/results/for tuple.ans b/test/results/for tuple.ans new file mode 100644 index 0000000..d019a24 --- /dev/null +++ b/test/results/for tuple.ans @@ -0,0 +1,11 @@ +--# run #-- +--- text --- +| {}"" {}"2" {}"" | +| {}"" {}"5" {}"" | +| {}"" {}"7" {}"" | +| {}"" {}"1" {}"" | +| {}"" {}"8" {}"" | +--- return --- +() +--# saved #-- +{} \ No newline at end of file diff --git a/test/results/function custom type dispatch error.ans b/test/results/function custom type dispatch error.ans index 0f10730..69da3e9 100644 --- a/test/results/function custom type dispatch error.ans +++ b/test/results/function custom type dispatch error.ans @@ -4,7 +4,7 @@ --- text --- | {}"" {}"idk" {}" is esperanto" | --- error --- -can't call overload overload<($(name::($(x) type(x) == t)) _), ($(name::($(x) type(x) == t)) _)>: no function match (type("nope", 5)), possible functions were: +can't call overload overload<($(name::($(x) type(x) == t)) _), ($(name::($(x) type(x) == t)) _)>: no function match (type(5, "nope")), possible functions were: • (name::($(x) type(x) == t)): type check failure for parameter name in function (name::($(x) type(x) == t)) • (name::($(x) type(x) == t)): type check failure for parameter name in function (name::($(x) type(x) == t)) ↳ from test/tests/function custom type dispatch error.ans:14:2 in call: a(type(5, "nope")) diff --git a/test/results/function random.ans b/test/results/function random.ans index e23a436..15a5220 100644 --- a/test/results/function random.ans +++ b/test/results/function random.ans @@ -1,15 +1,15 @@ --# run #-- --- text --- -| {}"c" | ---- text --- -| {}"b" | ---- text --- | {}"a" | --- text --- -| {}"b" | +| {}"c" | --- text --- -| {}"b" | +| {}"a" | +--- text --- +| {}"a" | +--- text --- +| {}"c" | --- return --- () --# saved #-- -{"a.checkpoint":false, "a.run":1, "b.checkpoint":false, "b.run":3, "c.checkpoint":false, "c.run":1} \ No newline at end of file +{"a.checkpoint":false, "a.run":3, "c.checkpoint":false, "c.run":2} \ No newline at end of file diff --git a/test/results/function scope wrong.ans b/test/results/function scope wrong.ans index 2d30e80..7bfde1c 100644 --- a/test/results/function scope wrong.ans +++ b/test/results/function scope wrong.ans @@ -1,6 +1,6 @@ --# run #-- --- error --- -identifier "b" is undefined in branch cf017f8a-7c86-4871-109af-6658231331e6 +identifier "b" is undefined in branch 7dfcaa5b-2163-4f36-116ed-c4ab6bc8d28b ↳ from test/tests/function scope wrong.ans:4:7 in identifier: b ↳ from test/tests/function scope wrong.ans:4:1 in text interpolation: | a: {b} | ↳ from ? in block: :a = ($() _)… diff --git a/test/tests/for break.ans b/test/tests/for break.ans new file mode 100644 index 0000000..7b5569b --- /dev/null +++ b/test/tests/for break.ans @@ -0,0 +1,4 @@ +for(:x, range(10)) + if(x == 3) + break! + |{x} \ No newline at end of file diff --git a/test/tests/for continue.ans b/test/tests/for continue.ans new file mode 100644 index 0000000..38c530f --- /dev/null +++ b/test/tests/for continue.ans @@ -0,0 +1,4 @@ +for(:x, range(4)) + if(x == 3) + continue! + |{x} diff --git a/test/tests/for invalid iterator.ans b/test/tests/for invalid iterator.ans new file mode 100644 index 0000000..93b88f7 --- /dev/null +++ b/test/tests/for invalid iterator.ans @@ -0,0 +1,2 @@ +for(:x, 42) + |{x} diff --git a/test/tests/for list.ans b/test/tests/for list.ans new file mode 100644 index 0000000..0096d5b --- /dev/null +++ b/test/tests/for list.ans @@ -0,0 +1,4 @@ +:l = *[7,96,"a",3] + +for(:x, l) + |{x} diff --git a/test/tests/for range.ans b/test/tests/for range.ans new file mode 100644 index 0000000..940cd4f --- /dev/null +++ b/test/tests/for range.ans @@ -0,0 +1,27 @@ +|- +for(:x, range(5)) + |{x} + +|- +for(:y, range(2, 7)) + |{y} + +|- +for(:z, range(2, 7, 3)) + |{z} + +|- +for(:x, range(4, 0, -1)) + |{x} + +|- +for(:x, range(4, 8, -1)) + |{x} + +|- +for(:x, range(8, 4)) + |{x} + +|- +for(:x, range(4, 4)) + |{x} diff --git a/test/tests/for struct.ans b/test/tests/for struct.ans new file mode 100644 index 0000000..444439c --- /dev/null +++ b/test/tests/for struct.ans @@ -0,0 +1,7 @@ +:s = { "a":"b", 6:"c", 98:5, true:3 } +:t = *s + +/* struct iteration is non deterministic, too lazy to sort, so we check if we have 4 key that each appear at most once */ +for(:k, s) + |{t!has(k)}| + t(k) = () diff --git a/test/tests/for table.ans b/test/tests/for table.ans new file mode 100644 index 0000000..335b2a9 --- /dev/null +++ b/test/tests/for table.ans @@ -0,0 +1,6 @@ +:s = *{ "a":"b", 6:"c", 98:5, true:3 } + +/* struct iteration is non deterministic, too lazy to sort, so we check if we have 4 key that each appear at most once */ +for(:k, s) + |{s!has(k)}| + s(k) = () diff --git a/test/tests/for tuple.ans b/test/tests/for tuple.ans new file mode 100644 index 0000000..2ee1ffc --- /dev/null +++ b/test/tests/for tuple.ans @@ -0,0 +1,4 @@ +:t = [2,5,7,1,8] + +for(:x, t) + |{x}