diff --git a/anselme/ast/List.lua b/anselme/ast/List.lua index 315c36d..1bbe50f 100644 --- a/anselme/ast/List.lua +++ b/anselme/ast/List.lua @@ -47,6 +47,9 @@ List = ast.abstract.Runtime { iter = function(self, state) return self.branched:get(state):iter() end, + find = function(self, state, value) + return self.branched:get(state):find(value) + end, get = function(self, state, index) local list = self.branched:get(state) if index < 0 then index = #list.list + 1 + index end @@ -59,13 +62,21 @@ List = ast.abstract.Runtime { if index > #list.list+1 or index == 0 then error("list index out of bounds", 0) end list.list[index] = val end, - insert = function(self, state, val) + insert = function(self, state, position, val) local l = self:_prepare_branch(state) - table.insert(l.list, val) + if val then + table.insert(l.list, position, val) + else + table.insert(l.list, position) + end end, - remove = function(self, state) + remove = function(self, state, position) local l = self:_prepare_branch(state) - table.remove(l.list) + if position then + table.remove(l.list, position) + else + table.remove(l.list) + end end, to_tuple = function(self, state) diff --git a/anselme/ast/Tuple.lua b/anselme/ast/Tuple.lua index 0fcf905..87687b1 100644 --- a/anselme/ast/Tuple.lua +++ b/anselme/ast/Tuple.lua @@ -72,6 +72,14 @@ Tuple = ast.abstract.Node { iter = function(self) return ipairs(self.list) end, + find = function(self, value) + for i, v in self:iter() do + if v:hash() == value:hash() then + return i + end + end + return nil + end } return Tuple diff --git a/anselme/stdlib/base.lua b/anselme/stdlib/base.lua index 29b0329..81f2925 100644 --- a/anselme/stdlib/base.lua +++ b/anselme/stdlib/base.lua @@ -57,4 +57,12 @@ return { }, { "true", Boolean:new(true) }, { "false", Boolean:new(false) }, + + { + "error", "(message=\"error\")", + function(state, message) + if message.type == "string" then message = message.string end + error(message:format(state), 0) + end + } } diff --git a/anselme/stdlib/init.lua b/anselme/stdlib/init.lua index 9b3d8de..f31330c 100644 --- a/anselme/stdlib/init.lua +++ b/anselme/stdlib/init.lua @@ -26,6 +26,7 @@ return function(main_state) "number", "string", "text", + "pair", "structures", "closure", "checkpoint", diff --git a/anselme/stdlib/number.lua b/anselme/stdlib/number.lua index 41f3b44..d3b5877 100644 --- a/anselme/stdlib/number.lua +++ b/anselme/stdlib/number.lua @@ -50,4 +50,20 @@ return { { "rand", "(min::number, max::number)", function(state, min, max) return Number:new(math.random(min.number, max.number)) end }, { "rand", "(max::number)", function(state, max) return Number:new(math.random(max.number)) end }, { "rand", "()", function(state) return Number:new(math.random()) end }, + + { "floor", "(x::number)", function(state, x) return Number:new(math.floor(x.number)) end }, + { "ceil", "(x::number)", function(state, x) return Number:new(math.ceil(x.number)) end }, + { + "round", "(x::number, increment=1)", + function(state, x, increment) + local n = x.number / increment.number + if n >= 0 then + return Number:new(math.floor(n + 0.5) * increment.number) + else + return Number:new(math.ceil(n - 0.5) * increment.number) + end + end + }, + + { "pi", Number:new(math.pi) } } diff --git a/anselme/stdlib/pair.lua b/anselme/stdlib/pair.lua new file mode 100644 index 0000000..68a877b --- /dev/null +++ b/anselme/stdlib/pair.lua @@ -0,0 +1,14 @@ +return { + { + "name", "(pair::pair)", + function(state, pair) + return pair.name + end + }, + { + "value", "(pair::pair)", + function(state, pair) + return pair.value + end + }, +} diff --git a/anselme/stdlib/string.lua b/anselme/stdlib/string.lua index 1759355..b1ddb06 100644 --- a/anselme/stdlib/string.lua +++ b/anselme/stdlib/string.lua @@ -1,6 +1,13 @@ +local utf8 = utf8 or require("lua-utf8") local ast = require("anselme.ast") -local String = ast.String +local String, Number = ast.String, ast.Number return { - { "_+_", "(a::string, b::string)", function(state, a, b) return String:new(a.string .. b.string) end } + { "_+_", "(a::string, b::string)", function(state, a, b) return String:new(a.string .. b.string) end }, + { + "len", "(s::string)", + function(state, s) + return Number:new(utf8.len(s.string)) + end + } } diff --git a/anselme/stdlib/structures.lua b/anselme/stdlib/structures.lua index 882d92b..d8ccab2 100644 --- a/anselme/stdlib/structures.lua +++ b/anselme/stdlib/structures.lua @@ -21,6 +21,17 @@ return { return Number:new(l:len()) end }, + { + "find", "(l::tuple, value)", + function(state, l, v) + local i = l:find(v) + if i then + return Number:new(i) + else + return Nil:new() + end + end + }, -- list { @@ -36,17 +47,49 @@ return { return Nil:new() end }, + { + "len", "(l::list)", + function(state, l) + return Number:new(l:len(state)) + end + }, + { + "find", "(l::list, value)", + function(state, l, v) + local i = l:find(state, v) + if i then + return Number:new(i) + else + return Nil:new() + end + end + }, { "insert", "(l::list, value)", function(state, l, v) - l:insert(state, v) + l:insert(state, v) return Nil:new() end }, { - "len", "(l::list)", + "insert", "(l::list, position::number, value)", + function(state, l, position, v) + l:insert(state, position.number, v) + return Nil:new() + end + }, + { + "remove", "(l::list)", function(state, l) - return Number:new(l:len(state)) + l:remove(state) + return Nil:new() + end + }, + { + "remove", "(l::list, position::number)", + function(state, l, position) + l:remove(state, position.number) + return Nil:new() end }, { diff --git a/anselme/stdlib/text.lua b/anselme/stdlib/text.lua index f73a472..2c97f0a 100644 --- a/anselme/stdlib/text.lua +++ b/anselme/stdlib/text.lua @@ -1,5 +1,5 @@ local ast = require("anselme.ast") -local Nil, Choice, PartialScope, ArgumentTuple, Identifier = ast.Nil, ast.Choice, ast.PartialScope, ast.ArgumentTuple, ast.Identifier +local Nil, Choice, PartialScope, ArgumentTuple, Identifier, Text = ast.Nil, ast.Choice, ast.PartialScope, ast.ArgumentTuple, ast.Identifier, ast.Text local event_manager = require("anselme.state.event_manager") local translation_manager = require("anselme.state.translation_manager") @@ -8,6 +8,19 @@ local resume_manager = require("anselme.state.resume_manager") return { -- text + { + "_+_", "(a::text, b::text)", + function(state, a, b) + local r = Text:new() + for _, e in ipairs(a.list) do + r:insert(e[1], e[2]) + end + for _, e in ipairs(b.list) do + r:insert(e[1], e[2]) + end + return r + end + }, { "_!", "(txt::text)", function(state, text) diff --git a/anselme/stdlib/type_check.lua b/anselme/stdlib/type_check.lua index 32bf086..1e07655 100644 --- a/anselme/stdlib/type_check.lua +++ b/anselme/stdlib/type_check.lua @@ -11,6 +11,7 @@ return { { "text", "(x)", function(state, x) return Boolean:new(x.type == "text") end }, + { "pair", "(x)", function(state, x) return Boolean:new(x.type == "pair") end }, { "tuple", "(x)", function(state, x) return Boolean:new(x.type == "tuple") end }, { "list", "(x)", function(state, x) return Boolean:new(x.type == "list") end }, { "struct", "(x)", function(state, x) return Boolean:new(x.type == "struct") end }, diff --git a/ideas.md b/ideas.md index 307f557..877b98e 100644 --- a/ideas.md +++ b/ideas.md @@ -20,10 +20,9 @@ Do some more fancy scope work to allow the translation to access variables defin Standard library. -* Text manipulation would make sense, but that would require a full UTF-8/Unicode support library like https://github.com/starwing/luautf8. +* Text and string manipulation would make sense, but that would require a full UTF-8/Unicode support library like https://github.com/starwing/luautf8. + - retag/add tags * Something to load other files. Maybe not load it by default to let the calling game sandbox Anselme. Probably create an export scope per file to perform some nice module loading. -* Implement the useful functions from Anselme v1. -* Text manipulation: concatenation, retag/add tags * And in general, clean up everything. --- diff --git a/test/results/error.ans b/test/results/error.ans new file mode 100644 index 0000000..0bd11e4 --- /dev/null +++ b/test/results/error.ans @@ -0,0 +1,7 @@ +--# run #-- +--- error --- +nope + ↳ from test/tests/error.ans:1:6 in call: error("nope") + ↳ from ? in block: error("nope") +--# saved #-- +{} \ No newline at end of file diff --git a/test/results/floor ceil round.ans b/test/results/floor ceil round.ans new file mode 100644 index 0000000..68673c1 --- /dev/null +++ b/test/results/floor ceil round.ans @@ -0,0 +1,13 @@ +--# run #-- +--- text --- +| {}"" {}"3" {}"" | +--- text --- +| {}"" {}"4" {}"" | +--- text --- +| {}"" {}"3" {}"" | +--- text --- +| {}"" {}"3.142" {}"" | +--- return --- +() +--# saved #-- +{} \ No newline at end of file diff --git a/test/results/list find.ans b/test/results/list find.ans new file mode 100644 index 0000000..9bbe813 --- /dev/null +++ b/test/results/list find.ans @@ -0,0 +1,9 @@ +--# run #-- +--- text --- +| {}"" {}"3" {}"" | +--- text --- +| {}"" {}"3" {}"" | +--- return --- +() +--# saved #-- +{} \ No newline at end of file diff --git a/test/results/list insert.ans b/test/results/list insert.ans new file mode 100644 index 0000000..c269894 --- /dev/null +++ b/test/results/list insert.ans @@ -0,0 +1,11 @@ +--# run #-- +--- text --- +| {}"" {}"*[1, 2, 3]" {}"" | +--- text --- +| {}"" {}"*[1, 2, 3, 4]" {}"" | +--- text --- +| {}"" {}"*[1, 5, 2, 3, 4]" {}"" | +--- return --- +() +--# saved #-- +{} \ No newline at end of file diff --git a/test/results/list len.ans b/test/results/list len.ans new file mode 100644 index 0000000..ddce4e2 --- /dev/null +++ b/test/results/list len.ans @@ -0,0 +1,9 @@ +--# run #-- +--- text --- +| {}"" {}"4" {}"" | +--- text --- +| {}"" {}"4" {}"" | +--- return --- +() +--# saved #-- +{} \ No newline at end of file diff --git a/test/results/list remove.ans b/test/results/list remove.ans new file mode 100644 index 0000000..f53102f --- /dev/null +++ b/test/results/list remove.ans @@ -0,0 +1,11 @@ +--# run #-- +--- text --- +| {}"" {}"*[1, 2, 3, 4, 5]" {}"" | +--- text --- +| {}"" {}"*[1, 2, 3, 4]" {}"" | +--- text --- +| {}"" {}"*[1, 3, 4]" {}"" | +--- return --- +() +--# saved #-- +{} \ No newline at end of file diff --git a/test/results/pair name value.ans b/test/results/pair name value.ans new file mode 100644 index 0000000..3906bfa --- /dev/null +++ b/test/results/pair name value.ans @@ -0,0 +1,9 @@ +--# run #-- +--- text --- +| {}"" {}"foo" {}"" | +--- text --- +| {}"" {}"barr" {}"" | +--- return --- +() +--# saved #-- +{} \ No newline at end of file diff --git a/test/results/string len.ans b/test/results/string len.ans new file mode 100644 index 0000000..d4cd224 --- /dev/null +++ b/test/results/string len.ans @@ -0,0 +1,7 @@ +--# run #-- +--- text --- +| {}"" {}"5" {}"" | +--- return --- +() +--# saved #-- +{} \ No newline at end of file diff --git a/test/results/text concat.ans b/test/results/text concat.ans new file mode 100644 index 0000000..0f12766 --- /dev/null +++ b/test/results/text concat.ans @@ -0,0 +1,7 @@ +--# run #-- +--- text --- +| {"a":"b"}"hello" {"b":"c"}"world and " {"b":"c", "x":"y"}"friends" {"b":"c"}"" | +--- return --- +() +--# saved #-- +{} \ No newline at end of file diff --git a/test/run.lua b/test/run.lua index 764014a..c65fc1c 100644 --- a/test/run.lua +++ b/test/run.lua @@ -77,7 +77,6 @@ local function run(path, interactive) local state = anselme:new() state:load_stdlib() - state:define("error", "(message=\"error\")", function(message) error(message, 0) end) state:define("interrupt", "(code::string)", function(state, code) state:interrupt(code:to_lua(state), "interrupt") return ast.Nil:new() end, true) state:define("interrupt", "()", function(state) state:interrupt() return ast.Nil:new() end, true) state:define("wait", "(duration::number)", function(duration) coroutine.yield("wait", duration) end) diff --git a/test/tests/error.ans b/test/tests/error.ans new file mode 100644 index 0000000..622bbad --- /dev/null +++ b/test/tests/error.ans @@ -0,0 +1 @@ +error("nope") \ No newline at end of file diff --git a/test/tests/floor ceil round.ans b/test/tests/floor ceil round.ans new file mode 100644 index 0000000..9941ac1 --- /dev/null +++ b/test/tests/floor ceil round.ans @@ -0,0 +1,9 @@ +:x = pi + +|{floor(x)} + +|{ceil(x)} + +|{round(x)} + +|{round(x, 0.001)} diff --git a/test/tests/list find.ans b/test/tests/list find.ans new file mode 100644 index 0000000..baa5932 --- /dev/null +++ b/test/tests/list find.ans @@ -0,0 +1,5 @@ +:l = [5,4,2,1] + +|{l!find(2)} + +|{(*l)!find(2)} diff --git a/test/tests/list insert.ans b/test/tests/list insert.ans new file mode 100644 index 0000000..cb5b92a --- /dev/null +++ b/test/tests/list insert.ans @@ -0,0 +1,11 @@ +:l = *[1,2,3] + +|{l} + +l!insert(4) + +|{l} + +l!insert(2, 5) + +|{l} diff --git a/test/tests/list len.ans b/test/tests/list len.ans new file mode 100644 index 0000000..94002a5 --- /dev/null +++ b/test/tests/list len.ans @@ -0,0 +1,5 @@ +:l = [5,4,2,1] + +|{l!len} + +|{(*l)!len} diff --git a/test/tests/list remove.ans b/test/tests/list remove.ans new file mode 100644 index 0000000..cf1ba85 --- /dev/null +++ b/test/tests/list remove.ans @@ -0,0 +1,11 @@ +:l = *[1,2,3,4,5] + +|{l} + +l!remove() + +|{l} + +l!remove(2) + +|{l} diff --git a/test/tests/pair name value.ans b/test/tests/pair name value.ans new file mode 100644 index 0000000..35a45a3 --- /dev/null +++ b/test/tests/pair name value.ans @@ -0,0 +1,5 @@ +:a = "foo":"barr" + +|{a!name} + +|{a!value} diff --git a/test/tests/string len.ans b/test/tests/string len.ans new file mode 100644 index 0000000..011f6a5 --- /dev/null +++ b/test/tests/string len.ans @@ -0,0 +1,3 @@ +:x = "hÉllo" + +|{x!len} diff --git a/test/tests/text concat.ans b/test/tests/text concat.ans new file mode 100644 index 0000000..04b38cf --- /dev/null +++ b/test/tests/text concat.ans @@ -0,0 +1,5 @@ +:a = ("a":"b" # | hello) + +:b = ("b":"c" # | world and { "x":"y" # |friends }) + +a+b