mirror of
https://github.com/Reuh/anselme.git
synced 2025-10-27 16:49:31 +00:00
Add anonymous functions
This commit is contained in:
parent
2c6d66c222
commit
5e441886c0
15 changed files with 352 additions and 110 deletions
|
|
@ -945,7 +945,7 @@ From lowest to highest priority:
|
||||||
```
|
```
|
||||||
_;_ _;
|
_;_ _;
|
||||||
_:=_ _+=_ _-=_ _//=_ _/=_ _*=_ _%=_ _^=_
|
_:=_ _+=_ _-=_ _//=_ _/=_ _*=_ _%=_ _^=_
|
||||||
_,_
|
_,_ $_
|
||||||
_~?_ _~_ _#_
|
_~?_ _~_ _#_
|
||||||
_=_
|
_=_
|
||||||
_|_ _&_
|
_|_ _&_
|
||||||
|
|
@ -1030,6 +1030,10 @@ This only works on strings:
|
||||||
|
|
||||||
`a!fn(args)`: call the function or function reference with the variable as first argument. Parantheses are optional.
|
`a!fn(args)`: call the function or function reference with the variable as first argument. Parantheses are optional.
|
||||||
|
|
||||||
|
`$x`: returns a reference to an anonymous function that returns the expression `x`. Note that the returned reference *can not* be persisted.
|
||||||
|
|
||||||
|
`$(parameters)x`: returns a reference to an anonymous function that returns the expression `x` and takes some parameters (same syntax as function definition lines). Note that the returned reference *can not* be persisted.
|
||||||
|
|
||||||
##### Variable references
|
##### Variable references
|
||||||
|
|
||||||
`&var`: returns a variable reference to the given variable. If it is already a reference, returns the same reference.
|
`&var`: returns a variable reference to the given variable. If it is already a reference, returns the same reference.
|
||||||
|
|
|
||||||
23
anselme.lua
23
anselme.lua
|
|
@ -82,7 +82,8 @@ local merge_state = require(anselme_root.."interpreter.common").merge_state
|
||||||
local stdfuncs = require(anselme_root.."stdlib.functions")
|
local stdfuncs = require(anselme_root.."stdlib.functions")
|
||||||
local bootscript = require(anselme_root.."stdlib.bootscript")
|
local bootscript = require(anselme_root.."stdlib.bootscript")
|
||||||
local copy = require(anselme_root.."common").copy
|
local copy = require(anselme_root.."common").copy
|
||||||
local should_keep_variable = require(anselme_root.."interpreter.common").should_keep_variable
|
local should_be_persisted = require(anselme_root.."interpreter.common").should_be_persisted
|
||||||
|
local check_persistable = require(anselme_root.."interpreter.common").check_persistable
|
||||||
|
|
||||||
-- wrappers for love.filesystem / luafilesystem
|
-- wrappers for love.filesystem / luafilesystem
|
||||||
local function list_directory(path)
|
local function list_directory(path)
|
||||||
|
|
@ -168,7 +169,7 @@ local interpreter_methods = {
|
||||||
local line = self.state.interpreter.running_line
|
local line = self.state.interpreter.running_line
|
||||||
local namespace = self:current_namespace()
|
local namespace = self:current_namespace()
|
||||||
-- replace state with interrupted state
|
-- replace state with interrupted state
|
||||||
local exp, err = expression(expr, self.state.interpreter.global_state, namespace or "")
|
local exp, err = expression(expr, self.state.interpreter.global_state, namespace or "", "interpreter:interrupt")
|
||||||
if not exp then return "error", ("%s; during interrupt %q at %s"):format(err, expr, line and line.source or "unknown") end
|
if not exp then return "error", ("%s; during interrupt %q at %s"):format(err, expr, line and line.source or "unknown") end
|
||||||
local r, e = self.vm:run(exp)
|
local r, e = self.vm:run(exp)
|
||||||
if not r then return "error", e end
|
if not r then return "error", e end
|
||||||
|
|
@ -235,7 +236,7 @@ local interpreter_methods = {
|
||||||
end
|
end
|
||||||
-- parse
|
-- parse
|
||||||
local err
|
local err
|
||||||
if type(expr) ~= "table" then expr, err = expression(tostring(expr), self.state.interpreter.global_state, namespace or "") end
|
if type(expr) ~= "table" then expr, err = expression(tostring(expr), self.state.interpreter.global_state, namespace or "", "interpreter:run") end
|
||||||
if not expr then coroutine.yield("error", err) end
|
if not expr then coroutine.yield("error", err) end
|
||||||
-- run
|
-- run
|
||||||
local r, e
|
local r, e
|
||||||
|
|
@ -267,7 +268,7 @@ local interpreter_methods = {
|
||||||
end
|
end
|
||||||
-- parse
|
-- parse
|
||||||
local err
|
local err
|
||||||
if type(expr) ~= "table" then expr, err = expression(tostring(expr), self.state.interpreter.global_state, namespace or "") end
|
if type(expr) ~= "table" then expr, err = expression(tostring(expr), self.state.interpreter.global_state, namespace or "", "interpreter:eval") end
|
||||||
if not expr then return nil, err end
|
if not expr then return nil, err end
|
||||||
-- run
|
-- run
|
||||||
local co = coroutine.create(function()
|
local co = coroutine.create(function()
|
||||||
|
|
@ -586,11 +587,15 @@ local vm_mt = {
|
||||||
--- Save script state.
|
--- Save script state.
|
||||||
-- See `vm:load`.
|
-- See `vm:load`.
|
||||||
--
|
--
|
||||||
-- Returns save data.
|
-- Returns save data in case of success.
|
||||||
|
--
|
||||||
|
-- Returns nil, error message in case of error.
|
||||||
save = function(self)
|
save = function(self)
|
||||||
local vars = {}
|
local vars = {}
|
||||||
for k, v in pairs(self.state.variables) do
|
for k, v in pairs(self.state.variables) do
|
||||||
if should_keep_variable(self.state, k, v) then
|
if should_be_persisted(self.state, k, v) then
|
||||||
|
local s, e = check_persistable(v)
|
||||||
|
if not s then return nil, ("%s; while saving variable %s"):format(e, k) end
|
||||||
vars[k] = v
|
vars[k] = v
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -608,11 +613,11 @@ local vm_mt = {
|
||||||
--
|
--
|
||||||
-- Returns self in case of success.
|
-- Returns self in case of success.
|
||||||
--
|
--
|
||||||
-- Returns nil, error message in case of error
|
-- Returns nil, error message in case of error.
|
||||||
postload = function(self)
|
postload = function(self)
|
||||||
if #self.state.queued_lines > 0 then
|
if #self.state.queued_lines > 0 then
|
||||||
local r, e = postparse(self.state)
|
local r, e = postparse(self.state)
|
||||||
if not r then return r, e end
|
if not r then return nil, e end
|
||||||
end
|
end
|
||||||
return self
|
return self
|
||||||
end,
|
end,
|
||||||
|
|
@ -653,7 +658,7 @@ local vm_mt = {
|
||||||
if not s then return s, e end
|
if not s then return s, e end
|
||||||
--
|
--
|
||||||
local err
|
local err
|
||||||
if type(expr) ~= "table" then expr, err = expression(tostring(expr), self.state, namespace or "") end
|
if type(expr) ~= "table" then expr, err = expression(tostring(expr), self.state, namespace or "", "vm:run") end
|
||||||
if not expr then return expr, err end
|
if not expr then return expr, err end
|
||||||
-- interpreter state
|
-- interpreter state
|
||||||
local interpreter
|
local interpreter
|
||||||
|
|
|
||||||
|
|
@ -266,7 +266,9 @@ Save/load script state
|
||||||
Save script state.
|
Save script state.
|
||||||
See `vm:load`.
|
See `vm:load`.
|
||||||
|
|
||||||
Returns save data.
|
Returns save data in case of success.
|
||||||
|
|
||||||
|
Returns nil, error message in case of error.
|
||||||
|
|
||||||
### vm:postload ()
|
### vm:postload ()
|
||||||
|
|
||||||
|
|
@ -275,7 +277,7 @@ Perform parsing that needs to be done after loading code.
|
||||||
|
|
||||||
Returns self in case of success.
|
Returns self in case of success.
|
||||||
|
|
||||||
Returns nil, error message in case of error
|
Returns nil, error message in case of error.
|
||||||
|
|
||||||
### vm:enable (...)
|
### vm:enable (...)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ local copy
|
||||||
|
|
||||||
local function random_identifier()
|
local function random_identifier()
|
||||||
local r = ""
|
local r = ""
|
||||||
for _=1, 16 do -- that's live 10^31 possibilities, ought to be enough for anyone
|
for _=1, 16 do -- that's like 10^31 possibilities, ought to be enough for anyone
|
||||||
r = r .. string.char(math.random(32, 126))
|
r = r .. string.char(math.random(32, 126))
|
||||||
end
|
end
|
||||||
return r
|
return r
|
||||||
|
|
@ -94,12 +94,40 @@ common = {
|
||||||
end,
|
end,
|
||||||
--- mark a value as constant, recursively affecting all the potentially mutable subvalues
|
--- mark a value as constant, recursively affecting all the potentially mutable subvalues
|
||||||
mark_constant = function(v)
|
mark_constant = function(v)
|
||||||
if atypes[v.type] and atypes[v.type].mark_constant then
|
return assert(common.traverse(v, function(v)
|
||||||
atypes[v.type].mark_constant(v)
|
|
||||||
if v.hash_id then v.hash_id = nil end -- no longer need to compare by id
|
if v.hash_id then v.hash_id = nil end -- no longer need to compare by id
|
||||||
else
|
end, "mark_constant"))
|
||||||
error(("don't know how to mark type %s as constant"):format(v.type))
|
end,
|
||||||
|
-- traverse v and all the subvalues it contains
|
||||||
|
-- callback(v) is called on every value after traversing its subvalues
|
||||||
|
-- if pertype_callback is given, will then call the associated callback(v) in the type table for each value
|
||||||
|
-- both those callbacks can either returns nil (success) or nil, err (error)
|
||||||
|
-- returns true
|
||||||
|
-- return nil, error
|
||||||
|
traverse = function(v, callback, pertype_callback)
|
||||||
|
if atypes[v.type] and atypes[v.type].traverse then
|
||||||
|
local r, e = atypes[v.type].traverse(v.value, callback, pertype_callback)
|
||||||
|
if not r then return nil, e end
|
||||||
|
r, e = callback(v)
|
||||||
|
if e then return nil, e end
|
||||||
|
if pertype_callback and atypes[v.type][pertype_callback] then
|
||||||
|
r, e = atypes[v.type][pertype_callback](v)
|
||||||
|
if e then return nil, e end
|
||||||
end
|
end
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
error(("don't know how to traverse type %s"):format(v.type))
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
--- checks if the value can be persisted
|
||||||
|
-- returns true
|
||||||
|
-- returns nil, persist illegal message
|
||||||
|
check_persistable = function(v)
|
||||||
|
return common.traverse(v, function(v)
|
||||||
|
if v.nonpersistent then
|
||||||
|
return nil, ("can't put a non persistable %s into a persistent variable"):format(v.type)
|
||||||
|
end
|
||||||
|
end)
|
||||||
end,
|
end,
|
||||||
--- returns a variable's value, evaluating a pending expression if neccessary
|
--- returns a variable's value, evaluating a pending expression if neccessary
|
||||||
-- if you're sure the variable has already been evaluated, use state.variables[fqm] directly
|
-- if you're sure the variable has already been evaluated, use state.variables[fqm] directly
|
||||||
|
|
@ -138,6 +166,13 @@ common = {
|
||||||
return nil, ("%s; while assigning value to variable %q"):format(e, name)
|
return nil, ("%s; while assigning value to variable %q"):format(e, name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
-- check persistence
|
||||||
|
if state.variable_metadata[name].persistent then
|
||||||
|
local s, e = common.check_persistable(val)
|
||||||
|
if not s then
|
||||||
|
return nil, ("%s; while assigning value to variable %q"):format(e, name)
|
||||||
|
end
|
||||||
|
end
|
||||||
-- check constraint
|
-- check constraint
|
||||||
local s, e = common.check_constraint(state, name, val)
|
local s, e = common.check_constraint(state, name, val)
|
||||||
if not s then
|
if not s then
|
||||||
|
|
@ -220,7 +255,8 @@ common = {
|
||||||
--- returns true if a variable should be persisted on save
|
--- returns true if a variable should be persisted on save
|
||||||
-- will exclude: variable that have not been evaluated yet and non-persistent variable
|
-- will exclude: variable that have not been evaluated yet and non-persistent variable
|
||||||
-- this will by consequence excludes variable in scoped variables (can be neither persistent not evaluated into global state), constants (can not be persistent), internal anselme variables (not marked persistent), etc.
|
-- this will by consequence excludes variable in scoped variables (can be neither persistent not evaluated into global state), constants (can not be persistent), internal anselme variables (not marked persistent), etc.
|
||||||
should_keep_variable = function(state, name, value)
|
-- You may want to check afterwards with check_persistable to check if the value can actually be persisted.
|
||||||
|
should_be_persisted = function(state, name, value)
|
||||||
return value.type ~= "pending definition" and state.variable_metadata[name].persistent
|
return value.type ~= "pending definition" and state.variable_metadata[name].persistent
|
||||||
end,
|
end,
|
||||||
--- check truthyness of an anselme value
|
--- check truthyness of an anselme value
|
||||||
|
|
|
||||||
|
|
@ -495,7 +495,7 @@ local function eval(state, exp)
|
||||||
ret, e = run(state, fn.child)
|
ret, e = run(state, fn.child)
|
||||||
-- resume at last checkpoint
|
-- resume at last checkpoint
|
||||||
else
|
else
|
||||||
local expr, err = expression(checkpoint.value[1], state, fn.namespace)
|
local expr, err = expression(checkpoint.value[1], state, fn.namespace, "resume from checkpoint")
|
||||||
if not expr then return nil, err end
|
if not expr then return nil, err end
|
||||||
ret, e = eval(state, expr)
|
ret, e = eval(state, expr)
|
||||||
end
|
end
|
||||||
|
|
@ -567,6 +567,11 @@ local function eval(state, exp)
|
||||||
type = "event buffer",
|
type = "event buffer",
|
||||||
value = l
|
value = l
|
||||||
}
|
}
|
||||||
|
elseif exp.type == "nonpersistent" then
|
||||||
|
local v, e = eval(state, exp.expression)
|
||||||
|
if not v then return nil, e end
|
||||||
|
v.nonpersistent = true
|
||||||
|
return v
|
||||||
-- pass the value along (internal type, used for variable reference implicit calls)
|
-- pass the value along (internal type, used for variable reference implicit calls)
|
||||||
elseif exp.type == "value passthrough" then
|
elseif exp.type == "value passthrough" then
|
||||||
return exp.value
|
return exp.value
|
||||||
|
|
|
||||||
|
|
@ -108,3 +108,7 @@ Disadvantages:
|
||||||
TODO: write a translation guide/simplify translation process
|
TODO: write a translation guide/simplify translation process
|
||||||
|
|
||||||
TODO: make injection nicer. Some decorator-like syntax? to select specific functions to inject to
|
TODO: make injection nicer. Some decorator-like syntax? to select specific functions to inject to
|
||||||
|
|
||||||
|
TODO: allow multiple aliases for a single identifier?
|
||||||
|
|
||||||
|
TODO: closures. Especially for when returning a subfunction from a scoped variable.
|
||||||
|
|
|
||||||
|
|
@ -187,7 +187,7 @@ common = {
|
||||||
end
|
end
|
||||||
-- expr
|
-- expr
|
||||||
if r:match("^{") then
|
if r:match("^{") then
|
||||||
local exp, rem = expression(r:gsub("^{", ""), state, namespace)
|
local exp, rem = expression(r:gsub("^{", ""), state, namespace, "interpolated expression")
|
||||||
if not exp then return nil, rem end
|
if not exp then return nil, rem end
|
||||||
if not rem:match("^%s*}") then return nil, ("expected closing } at end of expression before %q"):format(rem) end
|
if not rem:match("^%s*}") then return nil, ("expected closing } at end of expression before %q"):format(rem) end
|
||||||
-- wrap in format() call
|
-- wrap in format() call
|
||||||
|
|
@ -213,7 +213,7 @@ common = {
|
||||||
end
|
end
|
||||||
-- binop expression at the end of the text
|
-- binop expression at the end of the text
|
||||||
elseif allow_binops and r:match(("^[%s]"):format(allow_binops)) then
|
elseif allow_binops and r:match(("^[%s]"):format(allow_binops)) then
|
||||||
local exp, rem = expression(r, state, namespace, nil, text_exp)
|
local exp, rem = expression(r, state, namespace, "text binop suffix", nil, text_exp)
|
||||||
if not exp then return nil, rem end
|
if not exp then return nil, rem end
|
||||||
return exp, rem
|
return exp, rem
|
||||||
elseif r == "" then
|
elseif r == "" then
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
local identifier_pattern, format_identifier, find, escape, find_function, parse_text, find_all, split, find_function_from_list
|
local identifier_pattern, format_identifier, find, escape, find_function, parse_text, find_all, split, find_function_from_list, preparse
|
||||||
|
|
||||||
--- binop priority
|
--- binop priority
|
||||||
local binops_prio = {
|
local binops_prio = {
|
||||||
|
|
@ -26,7 +26,7 @@ local implicit_multiply_priority = 9.5 -- just above / so 1/2x gives 1/(2x)
|
||||||
local prefix_unops_prio = {
|
local prefix_unops_prio = {
|
||||||
[1] = {},
|
[1] = {},
|
||||||
[2] = {},
|
[2] = {},
|
||||||
[3] = {},
|
[3] = { "$" },
|
||||||
[4] = {},
|
[4] = {},
|
||||||
[5] = {},
|
[5] = {},
|
||||||
[6] = {},
|
[6] = {},
|
||||||
|
|
@ -81,11 +81,22 @@ local function get_text_in_litteral(s, start_pos)
|
||||||
end
|
end
|
||||||
return d, r
|
return d, r
|
||||||
end
|
end
|
||||||
|
local function random_identifier_alpha()
|
||||||
|
local r = ""
|
||||||
|
for _=1, 18 do -- that's live 10^30 possibilities, ought to be enough for anyone
|
||||||
|
if math.random(1, 2) == 1 then
|
||||||
|
r = r .. string.char(math.random(65, 90))
|
||||||
|
else
|
||||||
|
r = r .. string.char(math.random(97, 122))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
|
||||||
--- parse an expression
|
--- parse an expression
|
||||||
-- return expr, remaining if success
|
-- return expr, remaining if success
|
||||||
-- returns nil, err if error
|
-- returns nil, err if error
|
||||||
local function expression(s, state, namespace, current_priority, operating_on)
|
local function expression(s, state, namespace, source, current_priority, operating_on)
|
||||||
s = s:match("^%s*(.*)$")
|
s = s:match("^%s*(.*)$")
|
||||||
current_priority = current_priority or 0
|
current_priority = current_priority or 0
|
||||||
if not operating_on then
|
if not operating_on then
|
||||||
|
|
@ -95,7 +106,7 @@ local function expression(s, state, namespace, current_priority, operating_on)
|
||||||
if not d then
|
if not d then
|
||||||
d, r = s:match("^(%d+)(.*)$")
|
d, r = s:match("^(%d+)(.*)$")
|
||||||
end
|
end
|
||||||
return expression(r, state, namespace, current_priority, {
|
return expression(r, state, namespace, source, current_priority, {
|
||||||
type = "number",
|
type = "number",
|
||||||
value = tonumber(d)
|
value = tonumber(d)
|
||||||
})
|
})
|
||||||
|
|
@ -104,13 +115,13 @@ local function expression(s, state, namespace, current_priority, operating_on)
|
||||||
local d, r = get_text_in_litteral(s)
|
local d, r = get_text_in_litteral(s)
|
||||||
local l, e = parse_text(d, state, namespace, "string") -- parse interpolated expressions
|
local l, e = parse_text(d, state, namespace, "string") -- parse interpolated expressions
|
||||||
if not l then return l, e end
|
if not l then return l, e end
|
||||||
return expression(r, state, namespace, current_priority, l)
|
return expression(r, state, namespace, source, current_priority, l)
|
||||||
-- text buffer
|
-- text buffer
|
||||||
elseif s:match("^%%%[") then
|
elseif s:match("^%%%[") then
|
||||||
local text = s:match("^%%(.*)$")
|
local text = s:match("^%%(.*)$")
|
||||||
local v, r = parse_text(text, state, namespace, "text", "#~", true)
|
local v, r = parse_text(text, state, namespace, "text", "#~", true)
|
||||||
if not v then return nil, r end
|
if not v then return nil, r end
|
||||||
return expression(r, state, namespace, current_priority, {
|
return expression(r, state, namespace, source, current_priority, {
|
||||||
type = "text buffer",
|
type = "text buffer",
|
||||||
text = v
|
text = v
|
||||||
})
|
})
|
||||||
|
|
@ -121,13 +132,13 @@ local function expression(s, state, namespace, current_priority, operating_on)
|
||||||
local exp
|
local exp
|
||||||
if content:match("[^%s]") then
|
if content:match("[^%s]") then
|
||||||
local r_paren
|
local r_paren
|
||||||
exp, r_paren = expression(content, state, namespace)
|
exp, r_paren = expression(content, state, namespace, source)
|
||||||
if not exp then return nil, "invalid expression inside parentheses: "..r_paren end
|
if not exp then return nil, "invalid expression inside parentheses: "..r_paren end
|
||||||
if r_paren:match("[^%s]") then return nil, ("unexpected %q at end of parenthesis expression"):format(r_paren) end
|
if r_paren:match("[^%s]") then return nil, ("unexpected %q at end of parenthesis expression"):format(r_paren) end
|
||||||
else
|
else
|
||||||
exp = { type = "nil", value = nil }
|
exp = { type = "nil", value = nil }
|
||||||
end
|
end
|
||||||
return expression(r, state, namespace, current_priority, {
|
return expression(r, state, namespace, source, current_priority, {
|
||||||
type = "parentheses",
|
type = "parentheses",
|
||||||
expression = exp
|
expression = exp
|
||||||
})
|
})
|
||||||
|
|
@ -138,11 +149,11 @@ local function expression(s, state, namespace, current_priority, operating_on)
|
||||||
local exp
|
local exp
|
||||||
if content:match("[^%s]") then
|
if content:match("[^%s]") then
|
||||||
local r_paren
|
local r_paren
|
||||||
exp, r_paren = expression(content, state, namespace)
|
exp, r_paren = expression(content, state, namespace, source)
|
||||||
if not exp then return nil, "invalid expression inside list parentheses: "..r_paren end
|
if not exp then return nil, "invalid expression inside list parentheses: "..r_paren end
|
||||||
if r_paren:match("[^%s]") then return nil, ("unexpected %q at end of list parenthesis expression"):format(r_paren) end
|
if r_paren:match("[^%s]") then return nil, ("unexpected %q at end of list parenthesis expression"):format(r_paren) end
|
||||||
end
|
end
|
||||||
return expression(r, state, namespace, current_priority, {
|
return expression(r, state, namespace, source, current_priority, {
|
||||||
type = "list brackets",
|
type = "list brackets",
|
||||||
expression = exp
|
expression = exp
|
||||||
})
|
})
|
||||||
|
|
@ -153,11 +164,11 @@ local function expression(s, state, namespace, current_priority, operating_on)
|
||||||
local exp
|
local exp
|
||||||
if content:match("[^%s]") then
|
if content:match("[^%s]") then
|
||||||
local r_paren
|
local r_paren
|
||||||
exp, r_paren = expression(content, state, namespace)
|
exp, r_paren = expression(content, state, namespace, source)
|
||||||
if not exp then return nil, "invalid expression inside map parentheses: "..r_paren end
|
if not exp then return nil, "invalid expression inside map parentheses: "..r_paren end
|
||||||
if r_paren:match("[^%s]") then return nil, ("unexpected %q at end of map parenthesis expression"):format(r_paren) end
|
if r_paren:match("[^%s]") then return nil, ("unexpected %q at end of map parenthesis expression"):format(r_paren) end
|
||||||
end
|
end
|
||||||
return expression(r, state, namespace, current_priority, {
|
return expression(r, state, namespace, source, current_priority, {
|
||||||
type = "map brackets",
|
type = "map brackets",
|
||||||
expression = exp
|
expression = exp
|
||||||
})
|
})
|
||||||
|
|
@ -168,7 +179,7 @@ local function expression(s, state, namespace, current_priority, operating_on)
|
||||||
-- string:value pair shorthand using =
|
-- string:value pair shorthand using =
|
||||||
if r:match("^=[^=]") and pair_priority > current_priority then
|
if r:match("^=[^=]") and pair_priority > current_priority then
|
||||||
local val
|
local val
|
||||||
val, r = expression(r:match("^=(.*)$"), state, namespace, pair_priority)
|
val, r = expression(r:match("^=(.*)$"), state, namespace, source, pair_priority)
|
||||||
if not val then return val, r end
|
if not val then return val, r end
|
||||||
local args = {
|
local args = {
|
||||||
type = "list",
|
type = "list",
|
||||||
|
|
@ -181,7 +192,7 @@ local function expression(s, state, namespace, current_priority, operating_on)
|
||||||
-- find compatible variant
|
-- find compatible variant
|
||||||
local variant, err = find_function(state, namespace, "_=_", args, true)
|
local variant, err = find_function(state, namespace, "_=_", args, true)
|
||||||
if not variant then return variant, err end
|
if not variant then return variant, err end
|
||||||
return expression(r, state, namespace, current_priority, variant)
|
return expression(r, state, namespace, source, current_priority, variant)
|
||||||
end
|
end
|
||||||
-- variables
|
-- variables
|
||||||
-- if name isn't a valid variable, suffix call: detect if a prefix is valid variable, suffix _._ call is handled in the binop section below
|
-- if name isn't a valid variable, suffix call: detect if a prefix is valid variable, suffix _._ call is handled in the binop section below
|
||||||
|
|
@ -193,7 +204,7 @@ local function expression(s, state, namespace, current_priority, operating_on)
|
||||||
if i < #nl then
|
if i < #nl then
|
||||||
r = "."..table.concat(nl, ".", i+1, #nl)..r
|
r = "."..table.concat(nl, ".", i+1, #nl)..r
|
||||||
end
|
end
|
||||||
return expression(r, state, namespace, current_priority, {
|
return expression(r, state, namespace, source, current_priority, {
|
||||||
type = "variable",
|
type = "variable",
|
||||||
name = vfqm
|
name = vfqm
|
||||||
})
|
})
|
||||||
|
|
@ -207,14 +218,14 @@ local function expression(s, state, namespace, current_priority, operating_on)
|
||||||
if i < #nl then
|
if i < #nl then
|
||||||
r = "."..table.concat(nl, ".", i+1, #nl)..r
|
r = "."..table.concat(nl, ".", i+1, #nl)..r
|
||||||
end
|
end
|
||||||
return expression(r, state, namespace, current_priority, {
|
return expression(r, state, namespace, source, current_priority, {
|
||||||
type = "potential function",
|
type = "potential function",
|
||||||
called_name = name,
|
called_name = name,
|
||||||
names = lfnqm
|
names = lfnqm
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return nil, ("can't find function or variable named %q"):format(name)
|
return nil, ("can't find function or variable named %q in namespace %q"):format(name, namespace)
|
||||||
end
|
end
|
||||||
-- prefix unops
|
-- prefix unops
|
||||||
for prio, oplist in ipairs(prefix_unops_prio) do
|
for prio, oplist in ipairs(prefix_unops_prio) do
|
||||||
|
|
@ -224,15 +235,15 @@ local function expression(s, state, namespace, current_priority, operating_on)
|
||||||
local sright = s:match("^"..escaped.."(.*)$")
|
local sright = s:match("^"..escaped.."(.*)$")
|
||||||
-- function and variable reference
|
-- function and variable reference
|
||||||
if op == "&" then
|
if op == "&" then
|
||||||
local right, r = expression(sright, state, namespace, prio)
|
local right, r = expression(sright, state, namespace, source, prio)
|
||||||
if not right then return nil, ("invalid expression after unop %q: %s"):format(op, r) end
|
if not right then return nil, ("invalid expression after unop %q: %s"):format(op, r) end
|
||||||
if right.type == "potential function" then
|
if right.type == "potential function" then
|
||||||
return expression(r, state, namespace, current_priority, {
|
return expression(r, state, namespace, source, current_priority, {
|
||||||
type = "function reference",
|
type = "function reference",
|
||||||
names = right.names
|
names = right.names
|
||||||
})
|
})
|
||||||
elseif right.type == "variable" then
|
elseif right.type == "variable" then
|
||||||
return expression(r, state, namespace, current_priority, {
|
return expression(r, state, namespace, source, current_priority, {
|
||||||
type = "variable reference",
|
type = "variable reference",
|
||||||
name = right.name,
|
name = right.name,
|
||||||
expression = right
|
expression = right
|
||||||
|
|
@ -241,16 +252,45 @@ local function expression(s, state, namespace, current_priority, operating_on)
|
||||||
-- find variant
|
-- find variant
|
||||||
local variant, err = find_function(state, namespace, op.."_", right, true)
|
local variant, err = find_function(state, namespace, op.."_", right, true)
|
||||||
if not variant then return variant, err end
|
if not variant then return variant, err end
|
||||||
return expression(r, state, namespace, current_priority, variant)
|
return expression(r, state, namespace, source, current_priority, variant)
|
||||||
end
|
end
|
||||||
|
-- anonymous function
|
||||||
|
elseif op == "$" then
|
||||||
|
-- get eventual arguments
|
||||||
|
local params = "()"
|
||||||
|
if sright:match("^%b()") then
|
||||||
|
params, sright = sright:match("^(%b())(.*)$")
|
||||||
|
end
|
||||||
|
-- define function
|
||||||
|
local fn_name = ("%s🥸%s"):format(namespace, random_identifier_alpha())
|
||||||
|
local s, e = preparse(state, (":$%s%s\n\t@%s"):format(fn_name, params, fn_name), "", source)
|
||||||
|
if not s then return nil, e end
|
||||||
|
local fn = state.functions[fn_name][1]
|
||||||
|
-- parse return expression
|
||||||
|
local right, r = expression(sright, state, fn.namespace, source, prio)
|
||||||
|
if not right then return nil, ("invalid expression after unop %q: %s"):format(op, r) end
|
||||||
|
-- put expression in return line
|
||||||
|
for _, c in ipairs(fn.child) do
|
||||||
|
if c.type == "return" and c.expression == fn_name then
|
||||||
|
c.expression = right
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- return reference to created function
|
||||||
|
return expression(r, state, namespace, source, current_priority, {
|
||||||
|
type = "nonpersistent",
|
||||||
|
expression = {
|
||||||
|
type = "function reference",
|
||||||
|
names = { fn_name }
|
||||||
|
}
|
||||||
|
})
|
||||||
-- normal prefix unop
|
-- normal prefix unop
|
||||||
else
|
else
|
||||||
local right, r = expression(sright, state, namespace, prio)
|
local right, r = expression(sright, state, namespace, source, prio)
|
||||||
if not right then return nil, ("invalid expression after unop %q: %s"):format(op, r) end
|
if not right then return nil, ("invalid expression after unop %q: %s"):format(op, r) end
|
||||||
-- find variant
|
-- find variant
|
||||||
local variant, err = find_function(state, namespace, op.."_", right, true)
|
local variant, err = find_function(state, namespace, op.."_", right, true)
|
||||||
if not variant then return variant, err end
|
if not variant then return variant, err end
|
||||||
return expression(r, state, namespace, current_priority, variant)
|
return expression(r, state, namespace, source, current_priority, variant)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -275,7 +315,7 @@ local function expression(s, state, namespace, current_priority, operating_on)
|
||||||
-- get arguments
|
-- get arguments
|
||||||
if content:match("[^%s]") then
|
if content:match("[^%s]") then
|
||||||
local err
|
local err
|
||||||
args, err = expression(content, state, namespace)
|
args, err = expression(content, state, namespace, source)
|
||||||
if not args then return args, err end
|
if not args then return args, err end
|
||||||
if err:match("[^%s]") then return nil, ("unexpected %q at end of argument list"):format(err) end
|
if err:match("[^%s]") then return nil, ("unexpected %q at end of argument list"):format(err) end
|
||||||
end
|
end
|
||||||
|
|
@ -285,12 +325,12 @@ local function expression(s, state, namespace, current_priority, operating_on)
|
||||||
-- find compatible variant
|
-- find compatible variant
|
||||||
local variant, err = find_function_from_list(state, namespace, operating_on.called_name, operating_on.names, args, paren_call, implicit_call)
|
local variant, err = find_function_from_list(state, namespace, operating_on.called_name, operating_on.names, args, paren_call, implicit_call)
|
||||||
if not variant then return variant, err end
|
if not variant then return variant, err end
|
||||||
return expression(r, state, namespace, current_priority, variant)
|
return expression(r, state, namespace, source, current_priority, variant)
|
||||||
-- implicit call on variable reference. Might be canceled afterwards due to finding a higher priority operator.
|
-- implicit call on variable reference. Might be canceled afterwards due to finding a higher priority operator.
|
||||||
elseif operating_on.type == "variable" or (operating_on.type == "function call" and operating_on.called_name == "_._") then
|
elseif operating_on.type == "variable" or (operating_on.type == "function call" and operating_on.called_name == "_._") then
|
||||||
local implicit_call_variant, err = find_function(state, namespace, "_!", { type = "value passthrough" }, false, true)
|
local implicit_call_variant, err = find_function(state, namespace, "_!", { type = "value passthrough" }, false, true)
|
||||||
if not implicit_call_variant then return implicit_call_variant, err end
|
if not implicit_call_variant then return implicit_call_variant, err end
|
||||||
return expression(s, state, namespace, current_priority, {
|
return expression(s, state, namespace, source, current_priority, {
|
||||||
type = "implicit call if reference",
|
type = "implicit call if reference",
|
||||||
variant = implicit_call_variant,
|
variant = implicit_call_variant,
|
||||||
expression = operating_on
|
expression = operating_on
|
||||||
|
|
@ -323,7 +363,7 @@ local function expression(s, state, namespace, current_priority, operating_on)
|
||||||
-- get arguments
|
-- get arguments
|
||||||
if content:match("[^%s]") then
|
if content:match("[^%s]") then
|
||||||
local err
|
local err
|
||||||
args, err = expression(content, state, namespace)
|
args, err = expression(content, state, namespace, source)
|
||||||
if not args then return args, err end
|
if not args then return args, err end
|
||||||
if err:match("[^%s]") then return nil, ("unexpected %q at end of argument map"):format(err) end
|
if err:match("[^%s]") then return nil, ("unexpected %q at end of argument map"):format(err) end
|
||||||
end
|
end
|
||||||
|
|
@ -353,7 +393,7 @@ local function expression(s, state, namespace, current_priority, operating_on)
|
||||||
-- find compatible variant
|
-- find compatible variant
|
||||||
local variant, err = find_function(state, namespace, name, args, paren_call)
|
local variant, err = find_function(state, namespace, name, args, paren_call)
|
||||||
if not variant then return variant, err end
|
if not variant then return variant, err end
|
||||||
return expression(r, state, namespace, current_priority, variant)
|
return expression(r, state, namespace, source, current_priority, variant)
|
||||||
-- namespace
|
-- namespace
|
||||||
elseif op == "." and sright:match("^"..identifier_pattern) then
|
elseif op == "." and sright:match("^"..identifier_pattern) then
|
||||||
local name, r = sright:match("^("..identifier_pattern..")(.-)$")
|
local name, r = sright:match("^("..identifier_pattern..")(.-)$")
|
||||||
|
|
@ -366,14 +406,14 @@ local function expression(s, state, namespace, current_priority, operating_on)
|
||||||
}
|
}
|
||||||
local variant, err = find_function(state, namespace, "_._", args, true)
|
local variant, err = find_function(state, namespace, "_._", args, true)
|
||||||
if not variant then return variant, err end
|
if not variant then return variant, err end
|
||||||
return expression(r, state, namespace, current_priority, variant)
|
return expression(r, state, namespace, source, current_priority, variant)
|
||||||
-- other binops
|
-- other binops
|
||||||
else
|
else
|
||||||
local right, r = expression(sright, state, namespace, prio)
|
local right, r = expression(sright, state, namespace, source, prio)
|
||||||
if right then
|
if right then
|
||||||
-- list constructor (can't do this through a function call since we need to build a list for its arguments)
|
-- list constructor (can't do this through a function call since we need to build a list for its arguments)
|
||||||
if op == "," then
|
if op == "," then
|
||||||
return expression(r, state, namespace, current_priority, {
|
return expression(r, state, namespace, source, current_priority, {
|
||||||
type = "list",
|
type = "list",
|
||||||
left = operating_on,
|
left = operating_on,
|
||||||
right = right
|
right = right
|
||||||
|
|
@ -408,18 +448,18 @@ local function expression(s, state, namespace, current_priority, operating_on)
|
||||||
end
|
end
|
||||||
-- rewrite function to perform assignment
|
-- rewrite function to perform assignment
|
||||||
operating_on.assignment = right
|
operating_on.assignment = right
|
||||||
return expression(r, state, namespace, current_priority, operating_on)
|
return expression(r, state, namespace, source, current_priority, operating_on)
|
||||||
elseif operating_on.type ~= "variable" then
|
elseif operating_on.type ~= "variable" then
|
||||||
return nil, ("trying to perform assignment on a %s expression"):format(operating_on.type)
|
return nil, ("trying to perform assignment on a %s expression"):format(operating_on.type)
|
||||||
end
|
end
|
||||||
-- assign to a variable
|
-- assign to a variable
|
||||||
return expression(r, state, namespace, current_priority, {
|
return expression(r, state, namespace, source, current_priority, {
|
||||||
type = ":=",
|
type = ":=",
|
||||||
left = operating_on,
|
left = operating_on,
|
||||||
right = right
|
right = right
|
||||||
})
|
})
|
||||||
elseif op == "&" or op == "|" or op == "~?" or op == "~" or op == "#" then
|
elseif op == "&" or op == "|" or op == "~?" or op == "~" or op == "#" then
|
||||||
return expression(r, state, namespace, current_priority, {
|
return expression(r, state, namespace, source, current_priority, {
|
||||||
type = op,
|
type = op,
|
||||||
left = operating_on,
|
left = operating_on,
|
||||||
right = right
|
right = right
|
||||||
|
|
@ -434,7 +474,7 @@ local function expression(s, state, namespace, current_priority, operating_on)
|
||||||
}
|
}
|
||||||
local variant, err = find_function(state, namespace, "_"..op.."_", args, true)
|
local variant, err = find_function(state, namespace, "_"..op.."_", args, true)
|
||||||
if not variant then return variant, err end
|
if not variant then return variant, err end
|
||||||
return expression(r, state, namespace, current_priority, variant)
|
return expression(r, state, namespace, source, current_priority, variant)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -458,12 +498,12 @@ local function expression(s, state, namespace, current_priority, operating_on)
|
||||||
-- remove ! after a previously-assumed implicit function call
|
-- remove ! after a previously-assumed implicit function call
|
||||||
if op == "!" and operating_on.type == "function call" and operating_on.implicit_call then
|
if op == "!" and operating_on.type == "function call" and operating_on.implicit_call then
|
||||||
operating_on.implicit_call = false
|
operating_on.implicit_call = false
|
||||||
return expression(r, state, namespace, current_priority, operating_on)
|
return expression(r, state, namespace, source, current_priority, operating_on)
|
||||||
-- normal suffix unop
|
-- normal suffix unop
|
||||||
else
|
else
|
||||||
local variant, err = find_function(state, namespace, "_"..op, operating_on, true)
|
local variant, err = find_function(state, namespace, "_"..op, operating_on, true)
|
||||||
if not variant then return variant, err end
|
if not variant then return variant, err end
|
||||||
return expression(r, state, namespace, current_priority, variant)
|
return expression(r, state, namespace, source, current_priority, variant)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -479,19 +519,19 @@ local function expression(s, state, namespace, current_priority, operating_on)
|
||||||
content = content:gsub("^%(", ""):gsub("%)$", "")
|
content = content:gsub("^%(", ""):gsub("%)$", "")
|
||||||
-- get arguments
|
-- get arguments
|
||||||
if content:match("[^%s]") then
|
if content:match("[^%s]") then
|
||||||
local right, r_paren = expression(content, state, namespace)
|
local right, r_paren = expression(content, state, namespace, source)
|
||||||
if not right then return right, r_paren end
|
if not right then return right, r_paren end
|
||||||
if r_paren:match("[^%s]") then return nil, ("unexpected %q at end of index/call expression"):format(r_paren) end
|
if r_paren:match("[^%s]") then return nil, ("unexpected %q at end of index/call expression"):format(r_paren) end
|
||||||
args = { type = "list", left = args, right = right }
|
args = { type = "list", left = args, right = right }
|
||||||
end
|
end
|
||||||
local variant, err = find_function(state, namespace, "()", args, true)
|
local variant, err = find_function(state, namespace, "()", args, true)
|
||||||
if not variant then return variant, err end
|
if not variant then return variant, err end
|
||||||
return expression(r, state, namespace, current_priority, variant)
|
return expression(r, state, namespace, source, current_priority, variant)
|
||||||
end
|
end
|
||||||
-- implicit multiplication
|
-- implicit multiplication
|
||||||
if implicit_multiply_priority > current_priority then
|
if implicit_multiply_priority > current_priority then
|
||||||
if s:match("^"..identifier_pattern) then
|
if s:match("^"..identifier_pattern) then
|
||||||
local right, r = expression(s, state, namespace, implicit_multiply_priority)
|
local right, r = expression(s, state, namespace, source, implicit_multiply_priority)
|
||||||
if right then
|
if right then
|
||||||
local args = {
|
local args = {
|
||||||
type = "list",
|
type = "list",
|
||||||
|
|
@ -500,7 +540,7 @@ local function expression(s, state, namespace, current_priority, operating_on)
|
||||||
}
|
}
|
||||||
local variant, err = find_function(state, namespace, "_*_", args, true)
|
local variant, err = find_function(state, namespace, "_*_", args, true)
|
||||||
if not variant then return variant, err end
|
if not variant then return variant, err end
|
||||||
return expression(r, state, namespace, current_priority, variant)
|
return expression(r, state, namespace, source, current_priority, variant)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -513,4 +553,6 @@ package.loaded[...] = expression
|
||||||
local common = require((...):gsub("expression$", "common"))
|
local common = require((...):gsub("expression$", "common"))
|
||||||
identifier_pattern, format_identifier, find, escape, find_function, parse_text, find_all, split, find_function_from_list = common.identifier_pattern, common.format_identifier, common.find, common.escape, common.find_function, common.parse_text, common.find_all, common.split, common.find_function_from_list
|
identifier_pattern, format_identifier, find, escape, find_function, parse_text, find_all, split, find_function_from_list = common.identifier_pattern, common.format_identifier, common.find, common.escape, common.find_function, common.parse_text, common.find_all, common.split, common.find_function_from_list
|
||||||
|
|
||||||
|
preparse = require((...):gsub("expression$", "preparser"))
|
||||||
|
|
||||||
return expression
|
return expression
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ local function parse(state)
|
||||||
for _, param in ipairs(line.params) do
|
for _, param in ipairs(line.params) do
|
||||||
-- get type constraints
|
-- get type constraints
|
||||||
if param.type_constraint then
|
if param.type_constraint then
|
||||||
local type_exp, rem = expression(param.type_constraint, state, namespace)
|
local type_exp, rem = expression(param.type_constraint, state, namespace, line.source)
|
||||||
if not type_exp then return nil, ("in type constraint, %s; at %s"):format(rem, line.source) end
|
if not type_exp then return nil, ("in type constraint, %s; at %s"):format(rem, line.source) end
|
||||||
if rem:match("[^%s]") then
|
if rem:match("[^%s]") then
|
||||||
return nil, ("unexpected characters after parameter %q: %q; at %s"):format(param.full_name, rem, line.source)
|
return nil, ("unexpected characters after parameter %q: %q; at %s"):format(param.full_name, rem, line.source)
|
||||||
|
|
@ -22,7 +22,7 @@ local function parse(state)
|
||||||
end
|
end
|
||||||
-- get default value
|
-- get default value
|
||||||
if param.default then
|
if param.default then
|
||||||
local default_exp, rem = expression(param.default, state, namespace)
|
local default_exp, rem = expression(param.default, state, namespace, line.source)
|
||||||
if not default_exp then return nil, ("in default value, %s; at %s"):format(rem, line.source) end
|
if not default_exp then return nil, ("in default value, %s; at %s"):format(rem, line.source) end
|
||||||
if rem:match("[^%s]") then
|
if rem:match("[^%s]") then
|
||||||
return nil, ("unexpected characters after parameter %q: %q; at %s"):format(param.full_name, rem, line.source)
|
return nil, ("unexpected characters after parameter %q: %q; at %s"):format(param.full_name, rem, line.source)
|
||||||
|
|
@ -36,7 +36,7 @@ local function parse(state)
|
||||||
end
|
end
|
||||||
-- assignment argument
|
-- assignment argument
|
||||||
if line.assignment and line.assignment.type_constraint then
|
if line.assignment and line.assignment.type_constraint then
|
||||||
local type_exp, rem = expression(line.assignment.type_constraint, state, namespace)
|
local type_exp, rem = expression(line.assignment.type_constraint, state, namespace, line.source)
|
||||||
if not type_exp then return nil, ("in type constraint, %s; at %s"):format(rem, line.source) end
|
if not type_exp then return nil, ("in type constraint, %s; at %s"):format(rem, line.source) end
|
||||||
if rem:match("[^%s]") then
|
if rem:match("[^%s]") then
|
||||||
return nil, ("unexpected characters after parameter %q: %q; at %s"):format(line.assignment.full_name, rem, line.source)
|
return nil, ("unexpected characters after parameter %q: %q; at %s"):format(line.assignment.full_name, rem, line.source)
|
||||||
|
|
@ -66,8 +66,8 @@ local function parse(state)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- expressions
|
-- expressions
|
||||||
if line.expression then
|
if line.expression and type(line.expression) == "string" then
|
||||||
local exp, rem = expression(line.expression, state, namespace)
|
local exp, rem = expression(line.expression, state, namespace, line.source)
|
||||||
if not exp then return nil, ("%s; at %s"):format(rem, line.source) end
|
if not exp then return nil, ("%s; at %s"):format(rem, line.source) end
|
||||||
if rem:match("[^%s]") then return nil, ("expected end of expression before %q; at %s"):format(rem, line.source) end
|
if rem:match("[^%s]") then return nil, ("expected end of expression before %q; at %s"):format(rem, line.source) end
|
||||||
line.expression = exp
|
line.expression = exp
|
||||||
|
|
@ -76,7 +76,7 @@ local function parse(state)
|
||||||
state.variables[line.name].value.expression = line.expression
|
state.variables[line.name].value.expression = line.expression
|
||||||
-- parse constraints
|
-- parse constraints
|
||||||
if line.constraint then
|
if line.constraint then
|
||||||
local type_exp, rem2 = expression(line.constraint, state, namespace)
|
local type_exp, rem2 = expression(line.constraint, state, namespace, line.source)
|
||||||
if not type_exp then return nil, ("in type constraint, %s; at %s"):format(rem2, line.source) end
|
if not type_exp then return nil, ("in type constraint, %s; at %s"):format(rem2, line.source) end
|
||||||
if rem2:match("[^%s]") then
|
if rem2:match("[^%s]") then
|
||||||
return nil, ("unexpected characters after variable %q: %q; at %s"):format(line.name, rem2, line.source)
|
return nil, ("unexpected characters after variable %q: %q; at %s"):format(line.name, rem2, line.source)
|
||||||
|
|
@ -92,9 +92,13 @@ local function parse(state)
|
||||||
if err:match("[^%s]") then return nil, ("expected end of expression in end-of-text expression before %q"):format(err) end
|
if err:match("[^%s]") then return nil, ("expected end of expression in end-of-text expression before %q"):format(err) end
|
||||||
line.text = txt
|
line.text = txt
|
||||||
end
|
end
|
||||||
state.queued_lines[i] = nil
|
table.remove(state.queued_lines, i)
|
||||||
end
|
end
|
||||||
|
if #state.queued_lines > 0 then -- lines were added during post-parsing, process these
|
||||||
|
return parse(state)
|
||||||
|
else
|
||||||
return true
|
return true
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
package.loaded[...] = parse
|
package.loaded[...] = parse
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
local format, to_lua, from_lua, events, anselme, escape, hash, mark_constant, update_hashes, get_variable, find_function_variant_from_fqm, post_process_text
|
local format, to_lua, from_lua, events, anselme, escape, hash, update_hashes, get_variable, find_function_variant_from_fqm, post_process_text, traverse
|
||||||
|
|
||||||
local types = {}
|
local types = {}
|
||||||
types.lua = {
|
types.lua = {
|
||||||
|
|
@ -88,7 +88,7 @@ types.anselme = {
|
||||||
hash = function()
|
hash = function()
|
||||||
return "nil()"
|
return "nil()"
|
||||||
end,
|
end,
|
||||||
mark_constant = function() end,
|
traverse = function() return true end,
|
||||||
},
|
},
|
||||||
number = {
|
number = {
|
||||||
format = function(val)
|
format = function(val)
|
||||||
|
|
@ -100,7 +100,7 @@ types.anselme = {
|
||||||
hash = function(val)
|
hash = function(val)
|
||||||
return ("n(%s)"):format(val)
|
return ("n(%s)"):format(val)
|
||||||
end,
|
end,
|
||||||
mark_constant = function() end,
|
traverse = function() return true end,
|
||||||
},
|
},
|
||||||
string = {
|
string = {
|
||||||
format = function(val)
|
format = function(val)
|
||||||
|
|
@ -112,7 +112,7 @@ types.anselme = {
|
||||||
hash = function(val)
|
hash = function(val)
|
||||||
return ("s(%s)"):format(val)
|
return ("s(%s)"):format(val)
|
||||||
end,
|
end,
|
||||||
mark_constant = function() end,
|
traverse = function() return true end,
|
||||||
},
|
},
|
||||||
pair = {
|
pair = {
|
||||||
format = function(val)
|
format = function(val)
|
||||||
|
|
@ -136,9 +136,12 @@ types.anselme = {
|
||||||
if not v then return v, ve end
|
if not v then return v, ve end
|
||||||
return ("p(%s=%s)"):format(k, v)
|
return ("p(%s=%s)"):format(k, v)
|
||||||
end,
|
end,
|
||||||
mark_constant = function(v)
|
traverse = function(val, callback, pertype_callback)
|
||||||
mark_constant(v.value[1])
|
local k, ke = traverse(val[1], callback, pertype_callback)
|
||||||
mark_constant(v.value[2])
|
if not k then return k, ke end
|
||||||
|
local v, ve = traverse(val[2], callback, pertype_callback)
|
||||||
|
if not v then return v, ve end
|
||||||
|
return true
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
annotated = {
|
annotated = {
|
||||||
|
|
@ -161,9 +164,12 @@ types.anselme = {
|
||||||
if not v then return v, ve end
|
if not v then return v, ve end
|
||||||
return ("a(%s::%s)"):format(k, v)
|
return ("a(%s::%s)"):format(k, v)
|
||||||
end,
|
end,
|
||||||
mark_constant = function(v)
|
traverse = function(val, callback, pertype_callback)
|
||||||
mark_constant(v.value[1])
|
local k, ke = traverse(val[1], callback, pertype_callback)
|
||||||
mark_constant(v.value[2])
|
if not k then return k, ke end
|
||||||
|
local v, ve = traverse(val[2], callback, pertype_callback)
|
||||||
|
if not v then return v, ve end
|
||||||
|
return true
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
list = {
|
list = {
|
||||||
|
|
@ -195,12 +201,16 @@ types.anselme = {
|
||||||
end
|
end
|
||||||
return ("l(%s)"):format(table.concat(l, ","))
|
return ("l(%s)"):format(table.concat(l, ","))
|
||||||
end,
|
end,
|
||||||
|
traverse = function(val, callback, pertype_callback)
|
||||||
|
for _, item in ipairs(val) do
|
||||||
|
local s, e = traverse(item, callback, pertype_callback)
|
||||||
|
if not s then return s, e end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end,
|
||||||
mark_constant = function(v)
|
mark_constant = function(v)
|
||||||
v.constant = true
|
v.constant = true
|
||||||
for _, item in ipairs(v.value) do
|
end,
|
||||||
mark_constant(item)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
},
|
},
|
||||||
map = {
|
map = {
|
||||||
mutable = true,
|
mutable = true,
|
||||||
|
|
@ -239,12 +249,17 @@ types.anselme = {
|
||||||
table.sort(l)
|
table.sort(l)
|
||||||
return ("m(%s)"):format(table.concat(l, ","))
|
return ("m(%s)"):format(table.concat(l, ","))
|
||||||
end,
|
end,
|
||||||
|
traverse = function(val, callback, pertype_callback)
|
||||||
|
for _, v in pairs(val) do
|
||||||
|
local ks, ke = traverse(v[1], callback, pertype_callback)
|
||||||
|
if not ks then return ks, ke end
|
||||||
|
local vs, ve = traverse(v[2], callback, pertype_callback)
|
||||||
|
if not vs then return vs, ve end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end,
|
||||||
mark_constant = function(v)
|
mark_constant = function(v)
|
||||||
v.constant = true
|
v.constant = true
|
||||||
for _, val in pairs(v.value) do
|
|
||||||
mark_constant(val[1])
|
|
||||||
mark_constant(val[2])
|
|
||||||
end
|
|
||||||
update_hashes(v)
|
update_hashes(v)
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
|
|
@ -296,6 +311,13 @@ types.anselme = {
|
||||||
table.sort(attributes)
|
table.sort(attributes)
|
||||||
return ("%%(%s;%s)"):format(val.class, table.concat(attributes, ","))
|
return ("%%(%s;%s)"):format(val.class, table.concat(attributes, ","))
|
||||||
end,
|
end,
|
||||||
|
traverse = function(v, callback, pertype_callback)
|
||||||
|
for _, attrib in pairs(v.attributes) do
|
||||||
|
local s, e = traverse(attrib, callback, pertype_callback)
|
||||||
|
if not s then return s, e end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end,
|
||||||
mark_constant = function(v)
|
mark_constant = function(v)
|
||||||
v.constant = true
|
v.constant = true
|
||||||
end,
|
end,
|
||||||
|
|
@ -312,8 +334,7 @@ types.anselme = {
|
||||||
hash = function(val)
|
hash = function(val)
|
||||||
return ("&f(%s)"):format(table.concat(val, ", "))
|
return ("&f(%s)"):format(table.concat(val, ", "))
|
||||||
end,
|
end,
|
||||||
mark_constant = function() end,
|
traverse = function() return true end,
|
||||||
|
|
||||||
},
|
},
|
||||||
["variable reference"] = {
|
["variable reference"] = {
|
||||||
format = function(val)
|
format = function(val)
|
||||||
|
|
@ -323,9 +344,9 @@ types.anselme = {
|
||||||
hash = function(val)
|
hash = function(val)
|
||||||
return ("&v(%s)"):format(val)
|
return ("&v(%s)"):format(val)
|
||||||
end,
|
end,
|
||||||
mark_constant = function() end,
|
traverse = function() return true end,
|
||||||
},
|
},
|
||||||
-- event buffer: can only be used outside of Anselme internal for text & flush events (through text buffers)
|
-- event buffer: can only be used outside of Anselme internals for text & flush events (through text buffers)
|
||||||
["event buffer"] = {
|
["event buffer"] = {
|
||||||
format = function(val) -- triggered from subtexts
|
format = function(val) -- triggered from subtexts
|
||||||
local v, e = events:write_buffer(anselme.running.state, val)
|
local v, e = events:write_buffer(anselme.running.state, val)
|
||||||
|
|
@ -365,13 +386,25 @@ types.anselme = {
|
||||||
end
|
end
|
||||||
return ("eb(%s)"):format(table.concat(l, ","))
|
return ("eb(%s)"):format(table.concat(l, ","))
|
||||||
end,
|
end,
|
||||||
mark_constant = function() end,
|
traverse = function(val, callback, pertype_callback)
|
||||||
|
for _, event in ipairs(val) do
|
||||||
|
if event.type == "text" then
|
||||||
|
for _, t in ipairs(event.value) do
|
||||||
|
local s, e = traverse(t.tags, callback, pertype_callback)
|
||||||
|
if not s then return s, e end
|
||||||
|
end
|
||||||
|
elseif event.type ~= "flush" then
|
||||||
|
return nil, ("event %q in event buffer cannot be traversed"):format(event.type)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
package.loaded[...] = types
|
package.loaded[...] = types
|
||||||
local common = require((...):gsub("stdlib%.types$", "interpreter.common"))
|
local common = require((...):gsub("stdlib%.types$", "interpreter.common"))
|
||||||
format, to_lua, from_lua, events, hash, mark_constant, update_hashes, get_variable, post_process_text = common.format, common.to_lua, common.from_lua, common.events, common.hash, common.mark_constant, common.update_hashes, common.get_variable, common.post_process_text
|
format, to_lua, from_lua, events, hash, update_hashes, get_variable, post_process_text, traverse = common.format, common.to_lua, common.from_lua, common.events, common.hash, common.update_hashes, common.get_variable, common.post_process_text, common.traverse
|
||||||
anselme = require((...):gsub("stdlib%.types$", "anselme"))
|
anselme = require((...):gsub("stdlib%.types$", "anselme"))
|
||||||
local pcommon = require((...):gsub("stdlib%.types$", "parser.common"))
|
local pcommon = require((...):gsub("stdlib%.types$", "parser.common"))
|
||||||
escape, find_function_variant_from_fqm = pcommon.escape, pcommon.find_function_variant_from_fqm
|
escape, find_function_variant_from_fqm = pcommon.escape, pcommon.find_function_variant_from_fqm
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,12 @@ if args.script or args.game then
|
||||||
print("error", err)
|
print("error", err)
|
||||||
end
|
end
|
||||||
if args.save then
|
if args.save then
|
||||||
print(inspect(vm:save()))
|
local s, e = vm:save()
|
||||||
|
if s then
|
||||||
|
print(inspect(s))
|
||||||
|
else
|
||||||
|
print(("Error while saving: %s"):format(e))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- test mode
|
-- test mode
|
||||||
|
|
@ -147,7 +152,7 @@ else
|
||||||
-- simple random to get the same result across lua versions
|
-- simple random to get the same result across lua versions
|
||||||
local prev = 0
|
local prev = 0
|
||||||
local function badrandom(a, b)
|
local function badrandom(a, b)
|
||||||
prev = (42424242424242 * prev + 242) % 2^32
|
prev = (15485863 * prev + 11) % 2038074743
|
||||||
return a + prev % (b-a+1)
|
return a + prev % (b-a+1)
|
||||||
end
|
end
|
||||||
function math.random(a, b)
|
function math.random(a, b)
|
||||||
|
|
|
||||||
21
test/tests/anonymous function.ans
Normal file
21
test/tests/anonymous function.ans
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
:f = $(x)x*x
|
||||||
|
|
||||||
|
:$g(x)
|
||||||
|
@x*x
|
||||||
|
|
||||||
|
{f(5)} = {g(5)}
|
||||||
|
|
||||||
|
{f(2)} = {g(2)}
|
||||||
|
|
||||||
|
:y = 5
|
||||||
|
:h = $(x)x*x+y
|
||||||
|
|
||||||
|
{h(3)} == 14
|
||||||
|
|
||||||
|
~ y := 7
|
||||||
|
|
||||||
|
{h(5)} == 32
|
||||||
|
|
||||||
|
:i = $y*y
|
||||||
|
|
||||||
|
{i} == 49
|
||||||
81
test/tests/anonymous function.lua
Normal file
81
test/tests/anonymous function.lua
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
local _={}
|
||||||
|
_[35]={}
|
||||||
|
_[34]={}
|
||||||
|
_[33]={}
|
||||||
|
_[32]={}
|
||||||
|
_[31]={}
|
||||||
|
_[30]={}
|
||||||
|
_[29]={}
|
||||||
|
_[28]={}
|
||||||
|
_[27]={}
|
||||||
|
_[26]={}
|
||||||
|
_[25]={}
|
||||||
|
_[24]={}
|
||||||
|
_[23]={tags=_[35],text=" == 49"}
|
||||||
|
_[22]={tags=_[34],text="49"}
|
||||||
|
_[21]={tags=_[33],text=" == 32"}
|
||||||
|
_[20]={tags=_[32],text="32"}
|
||||||
|
_[19]={tags=_[31],text=" == 14"}
|
||||||
|
_[18]={tags=_[30],text="14"}
|
||||||
|
_[17]={tags=_[29],text="4"}
|
||||||
|
_[16]={tags=_[28],text=" = "}
|
||||||
|
_[15]={tags=_[27],text="4"}
|
||||||
|
_[14]={tags=_[26],text="25"}
|
||||||
|
_[13]={tags=_[25],text=" = "}
|
||||||
|
_[12]={tags=_[24],text="25"}
|
||||||
|
_[11]={_[22],_[23]}
|
||||||
|
_[10]={_[20],_[21]}
|
||||||
|
_[9]={_[18],_[19]}
|
||||||
|
_[8]={_[15],_[16],_[17]}
|
||||||
|
_[7]={_[12],_[13],_[14]}
|
||||||
|
_[6]={"return"}
|
||||||
|
_[5]={"text",_[11]}
|
||||||
|
_[4]={"text",_[10]}
|
||||||
|
_[3]={"text",_[9]}
|
||||||
|
_[2]={"text",_[8]}
|
||||||
|
_[1]={"text",_[7]}
|
||||||
|
return {_[1],_[2],_[3],_[4],_[5],_[6]}
|
||||||
|
--[[
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "25"
|
||||||
|
}, {
|
||||||
|
tags = {},
|
||||||
|
text = " = "
|
||||||
|
}, {
|
||||||
|
tags = {},
|
||||||
|
text = "25"
|
||||||
|
} } }
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "4"
|
||||||
|
}, {
|
||||||
|
tags = {},
|
||||||
|
text = " = "
|
||||||
|
}, {
|
||||||
|
tags = {},
|
||||||
|
text = "4"
|
||||||
|
} } }
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "14"
|
||||||
|
}, {
|
||||||
|
tags = {},
|
||||||
|
text = " == 14"
|
||||||
|
} } }
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "32"
|
||||||
|
}, {
|
||||||
|
tags = {},
|
||||||
|
text = " == 32"
|
||||||
|
} } }
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "49"
|
||||||
|
}, {
|
||||||
|
tags = {},
|
||||||
|
text = " == 49"
|
||||||
|
} } }
|
||||||
|
{ "return" }
|
||||||
|
]]--
|
||||||
|
|
@ -4,11 +4,11 @@ _[20]={}
|
||||||
_[19]={}
|
_[19]={}
|
||||||
_[18]={}
|
_[18]={}
|
||||||
_[17]={}
|
_[17]={}
|
||||||
_[16]={text="a",tags=_[21]}
|
_[16]={tags=_[21],text="c"}
|
||||||
_[15]={text="b",tags=_[20]}
|
_[15]={tags=_[20],text="a"}
|
||||||
_[14]={text="b",tags=_[19]}
|
_[14]={tags=_[19],text="c"}
|
||||||
_[13]={text="c",tags=_[18]}
|
_[13]={tags=_[18],text="b"}
|
||||||
_[12]={text="c",tags=_[17]}
|
_[12]={tags=_[17],text="c"}
|
||||||
_[11]={_[16]}
|
_[11]={_[16]}
|
||||||
_[10]={_[15]}
|
_[10]={_[15]}
|
||||||
_[9]={_[14]}
|
_[9]={_[14]}
|
||||||
|
|
@ -26,21 +26,21 @@ return {_[1],_[2],_[3],_[4],_[5],_[6]}
|
||||||
tags = {},
|
tags = {},
|
||||||
text = "c"
|
text = "c"
|
||||||
} } }
|
} } }
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "b"
|
||||||
|
} } }
|
||||||
{ "text", { {
|
{ "text", { {
|
||||||
tags = {},
|
tags = {},
|
||||||
text = "c"
|
text = "c"
|
||||||
} } }
|
} } }
|
||||||
{ "text", { {
|
|
||||||
tags = {},
|
|
||||||
text = "b"
|
|
||||||
} } }
|
|
||||||
{ "text", { {
|
|
||||||
tags = {},
|
|
||||||
text = "b"
|
|
||||||
} } }
|
|
||||||
{ "text", { {
|
{ "text", { {
|
||||||
tags = {},
|
tags = {},
|
||||||
text = "a"
|
text = "a"
|
||||||
} } }
|
} } }
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "c"
|
||||||
|
} } }
|
||||||
{ "return" }
|
{ "return" }
|
||||||
]]--
|
]]--
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
local _={}
|
local _={}
|
||||||
_[1]={"error","can't find function or variable named \"b\"; at test/tests/function scope wrong.ans:4"}
|
_[1]={"error","can't find function or variable named \"b\" in namespace \"function scope wrong.\"; at test/tests/function scope wrong.ans:4"}
|
||||||
return {_[1]}
|
return {_[1]}
|
||||||
--[[
|
--[[
|
||||||
{ "error", "can't find function or variable named \"b\"; at test/tests/function scope wrong.ans:4" }
|
{ "error", "can't find function or variable named \"b\" in namespace \"function scope wrong.\"; at test/tests/function scope wrong.ans:4" }
|
||||||
]]--
|
]]--
|
||||||
Loading…
Add table
Add a link
Reference in a new issue