mirror of
https://github.com/Reuh/anselme.git
synced 2025-10-27 16:49:31 +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
|
self.state.builtin_aliases["🏁"] = reached
|
||||||
return self
|
return self
|
||||||
end,
|
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
|
--- load & execute a built-in language file
|
||||||
-- the language file may optionally contain the special variables:
|
-- the language file may optionally contain the special variables:
|
||||||
|
|
@ -473,6 +502,7 @@ local vm_mt = {
|
||||||
local interpreter
|
local interpreter
|
||||||
interpreter = {
|
interpreter = {
|
||||||
state = {
|
state = {
|
||||||
|
inject = self.state.inject,
|
||||||
feature_flags = self.state.feature_flags,
|
feature_flags = self.state.feature_flags,
|
||||||
builtin_aliases = self.state.builtin_aliases,
|
builtin_aliases = self.state.builtin_aliases,
|
||||||
aliases = setmetatable({}, { __index = self.state.aliases }),
|
aliases = setmetatable({}, { __index = self.state.aliases }),
|
||||||
|
|
@ -544,6 +574,10 @@ return setmetatable(anselme, {
|
||||||
__call = function()
|
__call = function()
|
||||||
-- global state
|
-- global state
|
||||||
local state = {
|
local state = {
|
||||||
|
inject = {
|
||||||
|
functionstart = nil, functionend = nil,
|
||||||
|
checkpointstart = nil, checkpointend = nil
|
||||||
|
},
|
||||||
feature_flags = {
|
feature_flags = {
|
||||||
["strip trailing spaces"] = true,
|
["strip trailing spaces"] = true,
|
||||||
["strip duplicate spaces"] = true
|
["strip duplicate spaces"] = true
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
local format_identifier, identifier_pattern, escape, special_functions_names, pretty_signature, signature
|
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, new_rem, alias_name in case of success
|
||||||
-- returns true, rem in case of no alias and no error
|
-- returns true, rem in case of no alias and no error
|
||||||
-- returns nil, err in case of alias and 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
|
return true, rem, alias
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- parse a single line into AST
|
||||||
-- * ast: if success
|
-- * ast: if success
|
||||||
-- * nil, error: in case of error
|
-- * nil, error: in case of error
|
||||||
local function parse_line(line, state, namespace)
|
local function parse_line(line, state, namespace)
|
||||||
|
|
@ -195,6 +198,17 @@ local function parse_line(line, state, namespace)
|
||||||
else
|
else
|
||||||
table.insert(line.children, 1, { content = ":🔖=()", source = line.source })
|
table.insert(line.children, 1, { content = ":🔖=()", source = line.source })
|
||||||
end
|
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
|
elseif r.type == "checkpoint" then
|
||||||
-- define 🏁 variable
|
-- define 🏁 variable
|
||||||
local reached_alias = state.global_state.builtin_aliases["🏁"]
|
local reached_alias = state.global_state.builtin_aliases["🏁"]
|
||||||
|
|
@ -203,6 +217,17 @@ local function parse_line(line, state, namespace)
|
||||||
else
|
else
|
||||||
table.insert(line.children, 1, { content = ":🏁=0", source = line.source })
|
table.insert(line.children, 1, { content = ":🏁=0", source = line.source })
|
||||||
end
|
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
|
end
|
||||||
-- define args
|
-- define args
|
||||||
for _, param in ipairs(r.params) do
|
for _, param in ipairs(r.params) do
|
||||||
|
|
@ -286,6 +311,7 @@ local function parse_line(line, state, namespace)
|
||||||
return r
|
return r
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- parse an indented into final AST
|
||||||
-- * block: in case of success
|
-- * block: in case of success
|
||||||
-- * nil, err: in case of error
|
-- * nil, err: in case of error
|
||||||
local function parse_block(indented, state, namespace, parent_function)
|
local function parse_block(indented, state, namespace, parent_function)
|
||||||
|
|
@ -398,28 +424,43 @@ local function parse_lines(s)
|
||||||
return lines
|
return lines
|
||||||
end
|
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
|
--- 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)
|
-- (wait for other files to be parsed before doing this with postparse)
|
||||||
-- * block: in case of success
|
-- * block: in case of success
|
||||||
-- * nil, err: in case of error
|
-- * nil, err: in case of error
|
||||||
local function parse(state, s, name, source)
|
local function parse(state, s, name, source)
|
||||||
-- parse lines
|
-- get indented
|
||||||
local lines = parse_lines(s)
|
local indented, e = parse_indented(s, name, source)
|
||||||
local indented, e = parse_indent(lines, source or name)
|
|
||||||
if not indented then return nil, e end
|
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
|
-- build state proxy
|
||||||
local state_proxy = {
|
local state_proxy = {
|
||||||
|
inject = {
|
||||||
|
functionstart = nil, functionend = nil,
|
||||||
|
checkpointstart = nil, checkpointend = nil
|
||||||
|
},
|
||||||
aliases = setmetatable({}, { __index = state.aliases }),
|
aliases = setmetatable({}, { __index = state.aliases }),
|
||||||
variables = setmetatable({}, { __index = state.aliases }),
|
variables = setmetatable({}, { __index = state.aliases }),
|
||||||
functions = setmetatable({}, {
|
functions = setmetatable({}, {
|
||||||
|
|
@ -438,6 +479,14 @@ local function parse(state, s, name, source)
|
||||||
queued_lines = {},
|
queued_lines = {},
|
||||||
global_state = state
|
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
|
-- parse
|
||||||
local root, err = parse_block(indented, state_proxy, "")
|
local root, err = parse_block(indented, state_proxy, "")
|
||||||
if not root then return nil, err end
|
if not root then return nil, err end
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue