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:
parent
56ed6c912b
commit
e71bff9562
13 changed files with 169 additions and 58 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue