1
0
Fork 0
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:
Étienne Fildadut 2022-01-14 23:27:21 +01:00
parent 5a61573cdb
commit dfe838a769
2 changed files with 98 additions and 15 deletions

View file

@ -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

View file

@ -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