mirror of
https://github.com/Reuh/anselme.git
synced 2025-10-27 16:49:31 +00:00
90 lines
3.1 KiB
Lua
90 lines
3.1 KiB
Lua
-- create a partial layer to define temporary variables
|
|
|
|
local ast = require("ast")
|
|
local Identifier, Quote
|
|
|
|
local attached_block_identifier, attached_block_symbol
|
|
|
|
local PartialScope
|
|
PartialScope = ast.abstract.Node {
|
|
type = "partial scope",
|
|
|
|
expression = nil,
|
|
definitions = nil, -- {[sym]=value,...} where values are already evaluated!
|
|
_identifiers = nil, -- {identifier,...} - just a cache so we don't rebuild it on every eval
|
|
|
|
init = function(self, expression)
|
|
self.expression = expression
|
|
self.definitions = {}
|
|
self._identifiers = {}
|
|
self.format_priority = self.expression.format_priority
|
|
end,
|
|
define = function(self, symbol, value) -- for construction only
|
|
assert(not self.definitions[symbol], ("%s already defined in partial layer"):format(symbol))
|
|
table.insert(self._identifiers, symbol:to_identifier())
|
|
self.definitions[symbol] = value
|
|
end,
|
|
|
|
_format = function(self, state, priority, indentation, ...)
|
|
if self.definitions[attached_block_symbol] then
|
|
local block = self.definitions[attached_block_symbol]
|
|
local exp = self.expression:format(state, priority, indentation, ...)
|
|
if exp:sub(-2) == " _" then exp = exp:sub(1, -3) end
|
|
return exp.."\n\t"..block:format(state, priority, indentation + 1, ...)
|
|
else
|
|
return self.expression:format(state, priority, indentation, ...)
|
|
end
|
|
end,
|
|
|
|
traverse = function(self, fn, ...)
|
|
fn(self.expression, ...)
|
|
for sym, val in pairs(self.definitions) do
|
|
fn(sym, ...)
|
|
fn(val, ...)
|
|
end
|
|
end,
|
|
|
|
_eval = function(self, state)
|
|
state.scope:push_partial(table.unpack(self._identifiers))
|
|
for sym, val in pairs(self.definitions) do state.scope:define(sym, val) end
|
|
local exp = self.expression:eval(state)
|
|
state.scope:pop()
|
|
|
|
return exp
|
|
end,
|
|
|
|
_prepare = function(self, state)
|
|
state.scope:push_partial(table.unpack(self._identifiers))
|
|
for sym, val in pairs(self.definitions) do state.scope:define(sym, val) end
|
|
self.expression:prepare(state)
|
|
state.scope:pop()
|
|
end,
|
|
|
|
-- class method: if the identifier is currently defined, wrap node in an PartialScope so the identifier is still defined in this node
|
|
-- used to e.g. preserve the defined _ block without the need to build a full closure
|
|
-- used e.g. for -> translation, as we want to preserve _ while still executing the translation in the Translatable scope and not restore a different scope from a closure
|
|
-- (operates on un-evaluated nodes!)
|
|
preserve = function(self, state, expression, ...)
|
|
local partial = PartialScope:new(expression)
|
|
for _, ident in ipairs{...} do
|
|
if state.scope:defined(ident) then
|
|
partial:define(state.scope:get_symbol(ident), state.scope:get(ident))
|
|
end
|
|
end
|
|
return partial
|
|
end,
|
|
-- class method: return a PartialScope that define the block identifier _ to a Quote of `block`
|
|
attach_block = function(self, expression, block)
|
|
local partial = ast.PartialScope:new(expression)
|
|
partial:define(attached_block_symbol, Quote:new(block))
|
|
return partial
|
|
end
|
|
}
|
|
|
|
package.loaded[...] = PartialScope
|
|
Identifier, Quote = ast.Identifier, ast.Quote
|
|
|
|
attached_block_identifier = Identifier:new("_")
|
|
attached_block_symbol = attached_block_identifier:to_symbol()
|
|
|
|
return PartialScope
|