mirror of
https://github.com/Reuh/anselme.git
synced 2025-10-27 16:49:31 +00:00
Replace AttachBlock with more generic PartialScope
This commit is contained in:
parent
9b7d1e436e
commit
404e7dd56e
10 changed files with 124 additions and 70 deletions
|
|
@ -1,64 +0,0 @@
|
||||||
local ast = require("ast")
|
|
||||||
local Identifier, Quote
|
|
||||||
|
|
||||||
local attached_block_identifier, attached_block_symbol
|
|
||||||
|
|
||||||
local AttachBlock
|
|
||||||
AttachBlock = ast.abstract.Node {
|
|
||||||
type = "attach block",
|
|
||||||
|
|
||||||
expression = nil,
|
|
||||||
block = nil,
|
|
||||||
|
|
||||||
init = function(self, expression, block)
|
|
||||||
self.expression = expression
|
|
||||||
self.block = block
|
|
||||||
self.format_priority = self.expression.format_priority
|
|
||||||
end,
|
|
||||||
|
|
||||||
_format = function(self, state, priority, indentation, ...)
|
|
||||||
local exp = self.expression:format(state, priority, indentation, ...)
|
|
||||||
if exp:sub(-2) == " _" then exp = exp:sub(1, -3) end
|
|
||||||
return exp.."\n\t"..self.block:format(state, priority, indentation + 1, ...)
|
|
||||||
end,
|
|
||||||
|
|
||||||
traverse = function(self, fn, ...)
|
|
||||||
fn(self.expression, ...)
|
|
||||||
fn(self.block, ...)
|
|
||||||
end,
|
|
||||||
|
|
||||||
_eval = function(self, state)
|
|
||||||
state.scope:push_partial(attached_block_identifier)
|
|
||||||
state.scope:define(attached_block_symbol, Quote:new(self.block)) -- _ is always wrapped in a Call when it appears
|
|
||||||
local exp = self.expression:eval(state)
|
|
||||||
state.scope:pop()
|
|
||||||
|
|
||||||
return exp
|
|
||||||
end,
|
|
||||||
|
|
||||||
_prepare = function(self, state)
|
|
||||||
state.scope:push_partial(attached_block_identifier)
|
|
||||||
state.scope:define(attached_block_symbol, Quote:new(self.block))
|
|
||||||
self.expression:prepare(state)
|
|
||||||
state.scope:pop()
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- class method: if the block identifier is defined in the current scope, wrap node in an AttachBlock so the block is still defined in this node
|
|
||||||
-- used to 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, node)
|
|
||||||
if state.scope:defined_in_current(attached_block_symbol) then
|
|
||||||
return AttachBlock:new(node, state.scope:get(attached_block_identifier).expression) -- unwrap Quote as that will be rewrap on eval
|
|
||||||
end
|
|
||||||
return node
|
|
||||||
end,
|
|
||||||
}
|
|
||||||
|
|
||||||
package.loaded[...] = AttachBlock
|
|
||||||
Identifier, Quote = ast.Identifier, ast.Quote
|
|
||||||
|
|
||||||
attached_block_identifier = Identifier:new("_")
|
|
||||||
attached_block_symbol = attached_block_identifier:to_symbol()
|
|
||||||
|
|
||||||
return AttachBlock
|
|
||||||
|
|
@ -22,6 +22,9 @@ local VariableMetadata = ast.abstract.Runtime {
|
||||||
return self.branched:get(state)
|
return self.branched:get(state)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
get_symbol = function(self)
|
||||||
|
return self.symbol
|
||||||
|
end,
|
||||||
set = function(self, state, value)
|
set = function(self, state, value)
|
||||||
if self.symbol.constant then
|
if self.symbol.constant then
|
||||||
error(("trying to change the value of constant %s"):format(self.symbol.string), 0)
|
error(("trying to change the value of constant %s"):format(self.symbol.string), 0)
|
||||||
|
|
@ -180,6 +183,10 @@ local Environment = ast.abstract.Runtime {
|
||||||
get = function(self, state, identifier)
|
get = function(self, state, identifier)
|
||||||
return self:_get_variable(state, identifier):get(state)
|
return self:_get_variable(state, identifier):get(state)
|
||||||
end,
|
end,
|
||||||
|
-- get the symbol that was used to define the variable in current or parent environment
|
||||||
|
get_symbol = function(self, state, identifier)
|
||||||
|
return self:_get_variable(state, identifier):get_symbol()
|
||||||
|
end,
|
||||||
-- set variable value in current or parent environment
|
-- set variable value in current or parent environment
|
||||||
set = function(self, state, identifier, val)
|
set = function(self, state, identifier, val)
|
||||||
return self:_get_variable(state, identifier):set(state, val)
|
return self:_get_variable(state, identifier):set(state, val)
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,10 @@ Function = Overloadable {
|
||||||
traverse = function(self, fn, ...)
|
traverse = function(self, fn, ...)
|
||||||
fn(self.parameters, ...)
|
fn(self.parameters, ...)
|
||||||
fn(self.expression, ...)
|
fn(self.expression, ...)
|
||||||
|
for sym, val in pairs(self.exports) do
|
||||||
|
fn(sym, ...)
|
||||||
|
fn(val, ...)
|
||||||
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
compatible_with_arguments = function(self, state, args)
|
compatible_with_arguments = function(self, state, args)
|
||||||
|
|
|
||||||
90
ast/PartialScope.lua
Normal file
90
ast/PartialScope.lua
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
-- 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
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
local tree_to_block
|
local tree_to_block
|
||||||
|
|
||||||
local ast = require("ast")
|
local ast = require("ast")
|
||||||
local Block, Flush, AttachBlock
|
local Block, Flush, PartialScope
|
||||||
|
|
||||||
local expression_to_ast = require("parser.expression.to_ast")
|
local expression_to_ast = require("parser.expression.to_ast")
|
||||||
|
|
||||||
|
|
@ -26,7 +26,7 @@ end
|
||||||
local function line_to_expression(content, tree)
|
local function line_to_expression(content, tree)
|
||||||
if #tree > 0 then
|
if #tree > 0 then
|
||||||
local child_block = tree_to_block(tree)
|
local child_block = tree_to_block(tree)
|
||||||
return AttachBlock:new(expect_end_block(expression_to_ast(tree.source:clone(), content.." _", " _$")), child_block):set_source(tree.source)
|
return PartialScope:attach_block(expect_end_block(expression_to_ast(tree.source:clone(), content.." _", " _$")), child_block):set_source(tree.source)
|
||||||
else
|
else
|
||||||
return expect_end(expression_to_ast(tree.source:clone(), content, nil, nil, nil, Flush:new())):set_source(tree.source)
|
return expect_end(expression_to_ast(tree.source:clone(), content, nil, nil, nil, Flush:new())):set_source(tree.source)
|
||||||
end
|
end
|
||||||
|
|
@ -47,6 +47,6 @@ tree_to_block = function(tree)
|
||||||
end
|
end
|
||||||
|
|
||||||
package.loaded[...] = tree_to_block
|
package.loaded[...] = tree_to_block
|
||||||
Block, Flush, AttachBlock = ast.Block, ast.Flush, ast.AttachBlock
|
Block, Flush, PartialScope = ast.Block, ast.Flush, ast.PartialScope
|
||||||
|
|
||||||
return tree_to_block
|
return tree_to_block
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,7 @@ local ScopeStack = class {
|
||||||
defined_in_current = function(self, symbol) return self.current:defined_in_current(self.state, symbol) end,
|
defined_in_current = function(self, symbol) return self.current:defined_in_current(self.state, symbol) end,
|
||||||
set = function(self, identifier, exp) self.current:set(self.state, identifier, exp) end,
|
set = function(self, identifier, exp) self.current:set(self.state, identifier, exp) end,
|
||||||
get = function(self, identifier) return self.current:get(self.state, identifier) end,
|
get = function(self, identifier) return self.current:get(self.state, identifier) end,
|
||||||
|
get_symbol = function(self, identifier) return self.current:get_symbol(self.state, identifier) end,
|
||||||
depth = function(self) return self.current:depth() end,
|
depth = function(self) return self.current:depth() end,
|
||||||
|
|
||||||
-- push new scope
|
-- push new scope
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
local ast = require("ast")
|
local ast = require("ast")
|
||||||
local Nil, Choice, AttachBlock, ArgumentTuple = ast.Nil, ast.Choice, ast.AttachBlock, ast.ArgumentTuple
|
local Nil, Choice, PartialScope, ArgumentTuple = ast.Nil, ast.Choice, ast.PartialScope, ast.ArgumentTuple
|
||||||
|
|
||||||
local event_manager = require("state.event_manager")
|
local event_manager = require("state.event_manager")
|
||||||
local translation_manager = require("state.translation_manager")
|
local translation_manager = require("state.translation_manager")
|
||||||
|
|
@ -33,7 +33,8 @@ return {
|
||||||
{
|
{
|
||||||
"_->_", "(original::is(\"quote\"), translated::is(\"quote\"))",
|
"_->_", "(original::is(\"quote\"), translated::is(\"quote\"))",
|
||||||
function(state, original, translated)
|
function(state, original, translated)
|
||||||
translation_manager:set(state, tag_manager:get(state), original.expression, AttachBlock:preserve(state, translated.expression))
|
local exp = PartialScope:preserve(state, translated.expression, ast.Identifier:new("_"))
|
||||||
|
translation_manager:set(state, tag_manager:get(state), original.expression, exp)
|
||||||
return Nil:new()
|
return Nil:new()
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
--- error ---
|
--- error ---
|
||||||
[0m[31m[0m[31m[0m[31mcan't add an overload variant to non-overloadable variable a defined in the same scope[0m
|
[0m[31m[0m[31m[0m[31mcan't add an overload variant to non-overloadable variable a defined in the same scope[0m
|
||||||
↳ from [4mtest/tests/define override variable.ans:3:1[0m in definition: [2m:a = ($() _)[0m[0m
|
↳ from [4mtest/tests/define override variable.ans:3:1[0m in definition: [2m:a = ($() _)[0m[0m
|
||||||
↳ from [4mtest/tests/define override variable.ans:3:1[0m in attach block: [2m:a = ($() _)…[0m[0m
|
↳ from [4mtest/tests/define override variable.ans:3:1[0m in partial scope: [2m:a = ($() _)…[0m[0m
|
||||||
↳ from [4m?[0m in block: [2m:a = 2…[0m
|
↳ from [4m?[0m in block: [2m:a = 2…[0m
|
||||||
--# saved #--
|
--# saved #--
|
||||||
{}
|
{}
|
||||||
9
test/results/translate text attachblock.ans
Normal file
9
test/results/translate text attachblock.ans
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
--# run #--
|
||||||
|
--- text ---
|
||||||
|
| {}"Hello"|
|
||||||
|
--- text ---
|
||||||
|
| {}"Bonjour"|
|
||||||
|
--- return ---
|
||||||
|
()
|
||||||
|
--# saved #--
|
||||||
|
{}
|
||||||
6
test/tests/translate text attachblock.ans
Normal file
6
test/tests/translate text attachblock.ans
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
| Hello
|
||||||
|
|
||||||
|
| Hello| ->
|
||||||
|
| Bonjour
|
||||||
|
|
||||||
|
| Hello
|
||||||
Loading…
Add table
Add a link
Reference in a new issue