1
0
Fork 0
mirror of https://github.com/Reuh/anselme.git synced 2025-10-28 00:59:31 +00:00

Translation system first draft

This commit is contained in:
Étienne Fildadut 2023-12-23 21:09:12 +01:00
parent ffadc0dd69
commit c4636343b4
15 changed files with 215 additions and 18 deletions

View file

@ -3,7 +3,8 @@ local Identifier, Quote
local attached_block_identifier, attached_block_symbol
local AttachBlock = ast.abstract.Node {
local AttachBlock
AttachBlock = ast.abstract.Node {
type = "attach block",
expression = nil,
@ -38,7 +39,18 @@ local AttachBlock = ast.abstract.Node {
state.scope:define(attached_block_symbol, Quote:new(self.block))
self.expression:prepare(state)
state.scope:pop()
end
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

View file

@ -26,11 +26,11 @@ local ResumeParentFunction = ast.abstract.Node {
end,
_eval = function(self, state)
if resumable_manager:resuming(state, self) then
if self:resuming(state) then
self.expression:eval(state)
return resumable_manager:get_data(state, self):call(state, ArgumentTuple:new())
return self:get_data(state):call(state, ArgumentTuple:new())
else
resumable_manager:set_data(state, self, resumable_manager:capture(state, 1))
self:set_data(state, resumable_manager:capture(state, 1))
return self.expression:eval(state)
end
end

View file

@ -112,7 +112,17 @@ Struct = ast.abstract.Runtime {
has = function(self, key)
local hash = key:hash()
return not not self.table[hash]
end
end,
iter = function(self)
local t, h = self.table, nil
return function()
local e
h, e = next(t, h)
if h == nil then return nil
else return e[1], e[2]
end
end
end,
}
package.loaded[...] = Struct

View file

@ -58,14 +58,8 @@ Table = ast.abstract.Runtime {
return s:has(key)
end,
iter = function(self, state)
local t, h = self.branched:get(state).table, nil
return function()
local e
h, e = next(t, h)
if h == nil then return nil
else return e[1], e[2]
end
end
local s = self.branched:get(state)
return s:iter()
end,
to_struct = function(self, state)

49
ast/Translatable.lua Normal file
View file

@ -0,0 +1,49 @@
local ast = require("ast")
local TextInterpolation, String
local operator_priority = require("common").operator_priority
local translation_manager
local Translatable = ast.abstract.Node {
type = "translatable",
format_priority = operator_priority["%_"],
expression = nil,
init = function(self, expression)
self.expression = expression
self.context = ast.Struct:new()
self.context:set(String:new("source"), String:new(self.expression.source))
if TextInterpolation:is(self.expression) then
self.format_priority = expression.format_priority
end
end,
_format = function(self, ...)
if TextInterpolation:is(self.expression) then -- wrapped in translatable by default
return self.expression:format(...)
else
return "%"..self.expression:format_right(...)
end
end,
traverse = function(self, fn, ...)
fn(self.expression, ...)
end,
_eval = function(self, state)
return translation_manager:eval(state, self.context, self)
end,
list_translatable = function(self, t)
table.insert(t, self)
end
}
package.loaded[...] = Translatable
TextInterpolation, String = ast.TextInterpolation, ast.String
translation_manager = require("state.translation_manager")
return Translatable

View file

@ -44,6 +44,9 @@ traverse = {
end,
hash = function(self, t)
table.insert(t, self:hash())
end,
list_translatable = function(self, t)
self:list_translatable(t)
end
}
@ -119,6 +122,15 @@ Node = class {
self:traverse(traverse.prepare, state)
end,
-- generate a list of translatable nodes that appear in this node
-- should only be called on non-runtime nodes
-- if a node is translatable, redefine this to add it to the table - note that it shouldn't call :traverse or :list_translatable on its children, as nested translations should not be needed
list_translatable = function(self, t)
t = t or {}
self:traverse(traverse.list_translatable, t)
return t
end,
-- same as eval, but make the evaluated expression as a resume boundary
-- i.e. if a checkpoint is defined somewhere in this eval, it will start back from this node eval when resuming
eval_resumable = function(self, state)