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

Replace persistent variable system

Previous system linked the variable name with the saved value, meaning the variable could not be renamed or moved outside the global scope.

Instead we propose to store all persistent values in a global table, identifying each by a key. To still allow nice manipulation with identifiers, the alias syntax replace the persistent syntax for symbols - an aliases symbol will act as if a function call was used in place of the identifier when it appear.
This commit is contained in:
Étienne Fildadut 2023-12-27 21:25:14 +01:00
parent 56ed6c912b
commit e71bff9562
13 changed files with 169 additions and 58 deletions

View file

@ -98,6 +98,21 @@ ArgumentTuple = ast.abstract.Node {
end
return r
end,
-- recreate new argumenttuple with an assignment argument added
with_assignment = function(self, assignment)
local r = ArgumentTuple:new()
for i=1, self.arity do
if self.positional[i] then
r:add_positional(self.positional[i])
elseif self.named[i] then
r:add_named(Identifier:new(self.named[i]), self.named[self.named[i]])
else
r:add_assignment(self.assignment) -- welp it'll error below anyway
end
end
r:add_assignment(assignment)
return r
end,
-- return specificity (>=0), secondary specificity (>=0)
-- return false, failure message

View file

@ -30,12 +30,16 @@ local Definition = ast.abstract.Node {
end
local symbol = self.symbol:eval(state)
local val = self.expression:eval(state)
if Overloadable:issub(val) then
state.scope:define_overloadable(symbol, val)
if symbol.alias then
state.scope:define_alias(symbol, self.expression)
else
state.scope:define(symbol, val)
local val = self.expression:eval(state)
if Overloadable:issub(val) then
state.scope:define_overloadable(symbol, val)
else
state.scope:define(symbol, val)
end
end
return Nil:new()
@ -46,7 +50,9 @@ local Definition = ast.abstract.Node {
symbol:prepare(state)
val:prepare(state)
if Overloadable:issub(val) then
if self.symbol.alias then
state.scope:define(symbol:with{ alias = false }, val) -- disable alias to avoid call in Identifier:_prepare
elseif Overloadable:issub(val) then
state.scope:define_overloadable(symbol, val)
else
state.scope:define(symbol, val)

View file

@ -16,7 +16,11 @@ local VariableMetadata = ast.abstract.Runtime {
self.branched = Branched:new(state, value)
end,
get = function(self, state)
return self.branched:get(state)
if self.symbol.alias then
return self.branched:get(state):call(state, ArgumentTuple:new())
else
return self.branched:get(state)
end
end,
set = function(self, state, value)
assert(not self.symbol.constant, ("trying to change the value of constant %s"):format(self.symbol.string))
@ -24,7 +28,13 @@ local VariableMetadata = ast.abstract.Runtime {
local r = self.symbol.type_check:call(state, ArgumentTuple:new(value))
if not r:truthy() then error(("type check failure for %s; %s does not satisfy %s"):format(self.symbol.string, value, self.symbol.type_check)) end
end
self.branched:set(state, value)
if self.symbol.alias then
local assign_args = ArgumentTuple:new()
assign_args:add_assignment(value)
self.branched:get(state):call(state, assign_args)
else
self.branched:set(state, value)
end
end,
_format = function(self, ...)
@ -108,6 +118,19 @@ local Environment = ast.abstract.Runtime {
self:define(state, symbol, exp)
end
end,
define_alias = function(self, state, symbol, call)
assert(symbol.alias, "symbol is not an alias")
assert(call.type == "call", "alias expression must be a call")
local get = ast.Function:new(ast.ParameterTuple:new(), call):eval(state)
local set_param = ast.ParameterTuple:new()
set_param:insert_assignment(ast.FunctionParameter:new(ast.Identifier:new("value")))
local assign_expr = ast.Call:new(call.func, call.arguments:with_assignment(ast.Identifier:new("value")))
local set = ast.Function:new(set_param, assign_expr):eval(state)
self:define(state, symbol, ast.Overload:new(get, set))
end,
-- returns bool if variable defined in current or parent environment
defined = function(self, state, identifier)
@ -156,17 +179,6 @@ local Environment = ast.abstract.Runtime {
return self:_get_variable(state, identifier):set(state, val)
end,
-- returns a list {[symbol]=val,...} of all persistent variables in the current strict layer
list_persistent = function(self, state)
assert(self.export, "not an export scope layer")
local r = {}
for _, vm in self.variables:iter(state) do
if vm.symbol.persistent then
r[vm.symbol] = vm:get(state)
end
end
return r
end,
-- returns a list {[symbol]=val,...} of all exported variables in the current strict layer
list_exported = function(self, state)
assert(self.export, "not an export scope layer")

View file

@ -11,8 +11,8 @@ Symbol = ast.abstract.Node {
constant = nil, -- bool
type_check = nil, -- exp
alias = nil, -- bool
exported = nil, -- bool
persistent = nil, -- bool, imply exported
confined_to_branch = nil, -- bool
@ -20,23 +20,29 @@ Symbol = ast.abstract.Node {
modifiers = modifiers or {}
self.string = str
self.constant = modifiers.constant
self.persistent = modifiers.persistent
self.type_check = modifiers.type_check
self.alias = modifiers.alias
self.confined_to_branch = modifiers.confined_to_branch
self.exported = modifiers.exported or modifiers.persistent
self.exported = modifiers.exported
if self.type_check then
self.format_priority = operator_priority["_::_"]
end
end,
_eval = function(self, state)
return Symbol:new(self.string, {
constant = self.constant,
persistent = self.persistent,
type_check = self.type_check and self.type_check:eval(state),
confined_to_branch = self.confined_to_branch,
exported = self.exported
})
return self:with {
type_check = self.type_check and self.type_check:eval(state)
}
end,
with = function(self, modifiers)
modifiers = modifiers or {}
for _, k in ipairs{"constant", "type_check", "alias", "exported", "confined_to_branch"} do
if modifiers[k] == nil then
modifiers[k] = self[k]
end
end
return Symbol:new(self.string, modifiers)
end,
_hash = function(self)
@ -48,7 +54,7 @@ Symbol = ast.abstract.Node {
if self.constant then
s = s .. ":"
end
if self.persistent then
if self.alias then
s = s .. "&"
end
if self.exported then

View file

@ -85,6 +85,7 @@ Node = class {
local s, r = pcall(self._eval, self, state)
if s then
r._evaluated = true
r:set_source(self.source)
return r
else
error(format_error(state, self, r), 0)