mirror of
https://github.com/Reuh/anselme.git
synced 2025-10-27 08:39:30 +00:00
Add custom code inject at function/checkpoint start/end
This commit is contained in:
parent
5a61573cdb
commit
dfe838a769
2 changed files with 98 additions and 15 deletions
34
anselme.lua
34
anselme.lua
|
|
@ -351,6 +351,35 @@ local vm_mt = {
|
|||
self.state.builtin_aliases["🏁"] = reached
|
||||
return self
|
||||
end,
|
||||
--- set some code that will be added at the start of every function defined after this is called
|
||||
-- nil to disable
|
||||
-- can typically be used to define variables for every function like 👁️
|
||||
-- return self
|
||||
injectfunctionstart = function(self, code)
|
||||
self.state.inject.functionstart = code
|
||||
return self
|
||||
end,
|
||||
--- same as injectfunctionstart, but inject code at the start of every checkpoint
|
||||
-- nil to disable
|
||||
-- return self
|
||||
injectcheckpointstart = function(self, code)
|
||||
self.state.inject.checkpointstart = code
|
||||
return self
|
||||
end,
|
||||
--- same as injectfunctionstart, but inject code at the end of every function
|
||||
-- nil to disable
|
||||
-- return self
|
||||
injectfunctionend = function(self, code)
|
||||
self.state.inject.functionend = code
|
||||
return self
|
||||
end,
|
||||
--- same as injectfunctionend, but inject code at the end of every checkpoint
|
||||
-- nil to disable
|
||||
-- return self
|
||||
injectcheckpointend = function(self, code)
|
||||
self.state.inject.checkpointend = code
|
||||
return self
|
||||
end,
|
||||
|
||||
--- load & execute a built-in language file
|
||||
-- the language file may optionally contain the special variables:
|
||||
|
|
@ -473,6 +502,7 @@ local vm_mt = {
|
|||
local interpreter
|
||||
interpreter = {
|
||||
state = {
|
||||
inject = self.state.inject,
|
||||
feature_flags = self.state.feature_flags,
|
||||
builtin_aliases = self.state.builtin_aliases,
|
||||
aliases = setmetatable({}, { __index = self.state.aliases }),
|
||||
|
|
@ -544,6 +574,10 @@ return setmetatable(anselme, {
|
|||
__call = function()
|
||||
-- global state
|
||||
local state = {
|
||||
inject = {
|
||||
functionstart = nil, functionend = nil,
|
||||
checkpointstart = nil, checkpointend = nil
|
||||
},
|
||||
feature_flags = {
|
||||
["strip trailing spaces"] = true,
|
||||
["strip duplicate spaces"] = true
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
local format_identifier, identifier_pattern, escape, special_functions_names, pretty_signature, signature
|
||||
|
||||
-- try to define an alias using rem, the text that follows the identifier
|
||||
local parse_indented
|
||||
|
||||
--- try to define an alias using rem, the text that follows the identifier
|
||||
-- returns true, new_rem, alias_name in case of success
|
||||
-- returns true, rem in case of no alias and no error
|
||||
-- returns nil, err in case of alias and error
|
||||
|
|
@ -22,6 +24,7 @@ local function maybe_alias(rem, fqm, namespace, line, state)
|
|||
return true, rem, alias
|
||||
end
|
||||
|
||||
--- parse a single line into AST
|
||||
-- * ast: if success
|
||||
-- * nil, error: in case of error
|
||||
local function parse_line(line, state, namespace)
|
||||
|
|
@ -195,6 +198,17 @@ local function parse_line(line, state, namespace)
|
|||
else
|
||||
table.insert(line.children, 1, { content = ":🔖=()", source = line.source })
|
||||
end
|
||||
-- custom code injection
|
||||
if state.inject.functionstart then
|
||||
for i, ll in ipairs(state.inject.functionstart) do
|
||||
table.insert(line.children, 1+i, ll)
|
||||
end
|
||||
end
|
||||
if state.inject.functionend then
|
||||
for _, ll in ipairs(state.inject.functionend) do
|
||||
table.insert(line.children, ll)
|
||||
end
|
||||
end
|
||||
elseif r.type == "checkpoint" then
|
||||
-- define 🏁 variable
|
||||
local reached_alias = state.global_state.builtin_aliases["🏁"]
|
||||
|
|
@ -203,6 +217,17 @@ local function parse_line(line, state, namespace)
|
|||
else
|
||||
table.insert(line.children, 1, { content = ":🏁=0", source = line.source })
|
||||
end
|
||||
-- custom code injection
|
||||
if state.inject.checkpointstart then
|
||||
for i, ll in ipairs(state.inject.checkpointstart) do
|
||||
table.insert(line.children, 1+i, ll)
|
||||
end
|
||||
end
|
||||
if state.inject.checkpointend then
|
||||
for _, ll in ipairs(state.inject.checkpointend) do
|
||||
table.insert(line.children, ll)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- define args
|
||||
for _, param in ipairs(r.params) do
|
||||
|
|
@ -286,6 +311,7 @@ local function parse_line(line, state, namespace)
|
|||
return r
|
||||
end
|
||||
|
||||
--- parse an indented into final AST
|
||||
-- * block: in case of success
|
||||
-- * nil, err: in case of error
|
||||
local function parse_block(indented, state, namespace, parent_function)
|
||||
|
|
@ -398,28 +424,43 @@ local function parse_lines(s)
|
|||
return lines
|
||||
end
|
||||
|
||||
--- make indented from intial string
|
||||
-- * list: in case of success
|
||||
-- * nil, err: in case of error
|
||||
parse_indented = function(s, fnname, source)
|
||||
source = source or fnname
|
||||
-- parse lines
|
||||
local lines = parse_lines(s)
|
||||
local indented, e = parse_indent(lines, source)
|
||||
if not indented then return nil, e end
|
||||
-- wrap in named function if neccessary
|
||||
if fnname ~= nil and fnname ~= "" then
|
||||
if not fnname:match("^"..identifier_pattern.."$") then
|
||||
return nil, ("invalid function name %q"):format(fnname)
|
||||
end
|
||||
indented = {
|
||||
{ content = "$ "..fnname, source = ("%s:%s"):format(source, 0), children = indented },
|
||||
}
|
||||
end
|
||||
-- transform ast
|
||||
indented = transform_indented(indented)
|
||||
return indented
|
||||
end
|
||||
|
||||
--- preparse shit: create AST structure, define variables and functions, but don't parse expression or perform any type checking
|
||||
-- (wait for other files to be parsed before doing this with postparse)
|
||||
-- * block: in case of success
|
||||
-- * nil, err: in case of error
|
||||
local function parse(state, s, name, source)
|
||||
-- parse lines
|
||||
local lines = parse_lines(s)
|
||||
local indented, e = parse_indent(lines, source or name)
|
||||
-- get indented
|
||||
local indented, e = parse_indented(s, name, source)
|
||||
if not indented then return nil, e end
|
||||
-- wrap in named function if neccessary
|
||||
if name ~= "" then
|
||||
if not name:match("^"..identifier_pattern.."$") then
|
||||
return nil, ("invalid function name %q"):format(name)
|
||||
end
|
||||
indented = {
|
||||
{ content = "$ "..name, source = ("%s:%s"):format(source or name, 0), children = indented },
|
||||
}
|
||||
end
|
||||
-- transform ast
|
||||
indented = transform_indented(indented)
|
||||
-- build state proxy
|
||||
local state_proxy = {
|
||||
inject = {
|
||||
functionstart = nil, functionend = nil,
|
||||
checkpointstart = nil, checkpointend = nil
|
||||
},
|
||||
aliases = setmetatable({}, { __index = state.aliases }),
|
||||
variables = setmetatable({}, { __index = state.aliases }),
|
||||
functions = setmetatable({}, {
|
||||
|
|
@ -438,6 +479,14 @@ local function parse(state, s, name, source)
|
|||
queued_lines = {},
|
||||
global_state = state
|
||||
}
|
||||
-- parse injects
|
||||
for _, inject in ipairs{"functionstart", "functionend", "checkpointstart", "checkpointend"} do
|
||||
if state.inject[inject] then
|
||||
local inject_indented, err = parse_indented(state.inject[inject], nil, "injected "..inject)
|
||||
if not inject_indented then return nil, err end
|
||||
state_proxy.inject[inject] = inject_indented
|
||||
end
|
||||
end
|
||||
-- parse
|
||||
local root, err = parse_block(indented, state_proxy, "")
|
||||
if not root then return nil, err end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue