mirror of
https://github.com/Reuh/anselme.git
synced 2025-10-27 16:49:31 +00:00
Rework variable undefinition
This commit is contained in:
parent
93dadb3b5d
commit
3d10b1b15c
4 changed files with 55 additions and 37 deletions
|
|
@ -2,7 +2,7 @@ local ast = require("anselme.ast")
|
|||
|
||||
local operator_priority = require("anselme.common").operator_priority
|
||||
|
||||
local Branched, ArgumentTuple, Overload, Overloadable, Table
|
||||
local Branched, ArgumentTuple, Overload, Overloadable, Table, Undefined
|
||||
|
||||
local VariableMetadata = ast.abstract.Runtime {
|
||||
type = "variable metadata",
|
||||
|
|
@ -22,6 +22,10 @@ local VariableMetadata = ast.abstract.Runtime {
|
|||
return v
|
||||
end
|
||||
end,
|
||||
undefined = function(self, state)
|
||||
local v = self.branched:get(state)
|
||||
return Undefined:is(v)
|
||||
end,
|
||||
get_symbol = function(self)
|
||||
return self.symbol
|
||||
end,
|
||||
|
|
@ -68,21 +72,16 @@ local Environment = ast.abstract.Runtime {
|
|||
variables = nil, -- Table of { {identifier} = variable metadata, ... }
|
||||
|
||||
partial = nil, -- { [name string] = true, ... }
|
||||
undefine = nil, -- { [name string] = true, ... } - variable present here will not be looked up in parent env
|
||||
export = nil, -- bool
|
||||
|
||||
_lookup_cache = nil, -- { [name string] = variable metadata, ... }
|
||||
_lookup_cache_current = nil, -- { [name string] = true, ... }
|
||||
_lookup_cache_current = nil, -- { [name string] = variable metadata, ... }
|
||||
|
||||
init = function(self, state, parent, partial_names, is_export)
|
||||
self.variables = Table:new(state)
|
||||
self.parent = parent
|
||||
self.partial = partial_names
|
||||
self.export = is_export
|
||||
self.undefine = {}
|
||||
-- TODO: something defined in a parent scope after caching here will not be reflected
|
||||
-- 99% of the time a scope is not reused after popping it off the stack, except when captured in closures
|
||||
-- so we might want to make accessing variable defined after environment capture illegal or precache all upvalues
|
||||
self._lookup_cache = {}
|
||||
self._lookup_cache_current = {}
|
||||
end,
|
||||
|
|
@ -107,14 +106,10 @@ local Environment = ast.abstract.Runtime {
|
|||
or (self.export ~= symbol.exported) then
|
||||
return self.parent:define(state, symbol, exp)
|
||||
end
|
||||
if symbol.undefine then
|
||||
self.undefine[name] = true
|
||||
else
|
||||
local variable = VariableMetadata:new(state, symbol, exp)
|
||||
self.variables:set(state, symbol:to_identifier(), variable)
|
||||
self._lookup_cache[name] = variable
|
||||
self._lookup_cache_current[name] = true
|
||||
end
|
||||
local variable = VariableMetadata:new(state, symbol, exp)
|
||||
self.variables:set(state, symbol:to_identifier(), variable)
|
||||
self._lookup_cache[name] = variable
|
||||
self._lookup_cache_current[name] = variable
|
||||
end,
|
||||
-- define or redefine new overloadable variable in current environment, inheriting existing overload variants from (parent) scopes
|
||||
define_overloadable = function(self, state, symbol, exp)
|
||||
|
|
@ -152,11 +147,34 @@ local Environment = ast.abstract.Runtime {
|
|||
if _cache[name] == nil then
|
||||
if self.variables:has(state, identifier) then
|
||||
_cache[name] = self.variables:get(state, identifier)
|
||||
elseif self.parent and not self.undefine[identifier.name] then
|
||||
elseif self.parent then
|
||||
_cache[name] = self.parent:_lookup(state, identifier)
|
||||
end
|
||||
end
|
||||
return _cache[name]
|
||||
local var = _cache[name]
|
||||
if var and not var:undefined(state) then
|
||||
return var
|
||||
end
|
||||
return nil
|
||||
end,
|
||||
_lookup_in_current = function(self, state, symbol)
|
||||
local name = symbol.string
|
||||
local _cache = self._lookup_cache_current
|
||||
if _cache[name] == nil then
|
||||
local identifier = symbol:to_identifier()
|
||||
if self.variables:has(state, identifier) then
|
||||
_cache[name] = self.variables:get(state, identifier)
|
||||
elseif self.parent then
|
||||
if (self.partial and not self.partial[name]) or (self.export ~= symbol.exported) then
|
||||
_cache[name] = self.parent:_lookup_in_current(state, symbol)
|
||||
end
|
||||
end
|
||||
end
|
||||
local var = _cache[name]
|
||||
if var and not var:undefined(state) then
|
||||
return var
|
||||
end
|
||||
return nil
|
||||
end,
|
||||
|
||||
-- returns bool if variable defined in current or parent environment
|
||||
|
|
@ -166,22 +184,11 @@ local Environment = ast.abstract.Runtime {
|
|||
-- returns bool if variable defined in current environment layer
|
||||
-- (note: by current layer, we mean the closest one where the variable is able to exist - if it is exported, the closest export layer, etc.)
|
||||
defined_in_current = function(self, state, symbol)
|
||||
local name = symbol.string
|
||||
local _cache = self._lookup_cache_current
|
||||
if _cache[name] == nil then
|
||||
if self.variables:has(state, symbol:to_identifier()) then
|
||||
_cache[name] = true
|
||||
elseif self.parent and not self.undefine[name] then
|
||||
if (self.partial and not self.partial[name]) or (self.export ~= symbol.exported) then
|
||||
_cache[name] = self.parent:defined_in_current(state, symbol)
|
||||
end
|
||||
end
|
||||
end
|
||||
return _cache[name]
|
||||
return self:_lookup_in_current(state, symbol) ~= nil
|
||||
end,
|
||||
-- return bool if variable is defined in the current environment only - won't search in parent env for exported & partial names
|
||||
defined_in_current_strict = function(self, state, identifier)
|
||||
return self.variables:has(state, identifier)
|
||||
return self.variables:has(state, identifier) and not self.variables:get(state, identifier):undefined(state)
|
||||
end,
|
||||
|
||||
-- get variable in current or parent scope, with metadata
|
||||
|
|
@ -236,6 +243,6 @@ local Environment = ast.abstract.Runtime {
|
|||
}
|
||||
|
||||
package.loaded[...] = Environment
|
||||
Branched, ArgumentTuple, Overload, Overloadable, Table = ast.Branched, ast.ArgumentTuple, ast.Overload, ast.abstract.Overloadable, ast.Table
|
||||
Branched, ArgumentTuple, Overload, Overloadable, Table, Undefined = ast.Branched, ast.ArgumentTuple, ast.Overload, ast.abstract.Overloadable, ast.Table, ast.Undefined
|
||||
|
||||
return Environment
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
-- create a partial layer to define temporary variables
|
||||
|
||||
local ast = require("anselme.ast")
|
||||
local Identifier, Quote, Nil
|
||||
local Identifier, Quote, Undefined
|
||||
|
||||
local attached_block_identifier, attached_block_symbol
|
||||
local unpack = table.unpack or unpack
|
||||
|
|
@ -74,14 +74,14 @@ PartialScope = ast.abstract.Node {
|
|||
attach_block = function(self, expression, block)
|
||||
local partial = PartialScope:new(expression)
|
||||
local unpartial = PartialScope:new(block)
|
||||
unpartial:define(attached_block_symbol:with{undefine=true}, Nil:new())
|
||||
unpartial:define(attached_block_symbol, Undefined:new())
|
||||
partial:define(attached_block_symbol, Quote:new(unpartial))
|
||||
return partial
|
||||
end
|
||||
}
|
||||
|
||||
package.loaded[...] = PartialScope
|
||||
Identifier, Quote, Nil = ast.Identifier, ast.Quote, ast.Nil
|
||||
Identifier, Quote, Undefined = ast.Identifier, ast.Quote, ast.Undefined
|
||||
|
||||
attached_block_identifier = Identifier:new("_")
|
||||
attached_block_symbol = attached_block_identifier:to_symbol()
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ Symbol = ast.abstract.Node {
|
|||
exported = nil, -- bool
|
||||
|
||||
confined_to_branch = nil, -- bool
|
||||
undefine = nil, -- bool
|
||||
|
||||
init = function(self, str, modifiers)
|
||||
modifiers = modifiers or {}
|
||||
|
|
@ -25,7 +24,6 @@ Symbol = ast.abstract.Node {
|
|||
self.alias = modifiers.alias
|
||||
self.confined_to_branch = modifiers.confined_to_branch
|
||||
self.exported = modifiers.exported
|
||||
self.undefine = modifiers.undefine
|
||||
end,
|
||||
|
||||
_eval = function(self, state)
|
||||
|
|
@ -36,7 +34,7 @@ Symbol = ast.abstract.Node {
|
|||
|
||||
with = function(self, modifiers)
|
||||
modifiers = modifiers or {}
|
||||
for _, k in ipairs{"constant", "type_check", "alias", "exported", "confined_to_branch", "undefine"} do
|
||||
for _, k in ipairs{"constant", "type_check", "alias", "exported", "confined_to_branch"} do
|
||||
if modifiers[k] == nil then
|
||||
modifiers[k] = self[k]
|
||||
end
|
||||
|
|
|
|||
13
anselme/ast/Undefined.lua
Normal file
13
anselme/ast/Undefined.lua
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
local ast = require("anselme.ast")
|
||||
|
||||
return ast.abstract.Runtime {
|
||||
type = "undefined",
|
||||
|
||||
init = function(self) end,
|
||||
|
||||
_format = function(self)
|
||||
return "<undefined>"
|
||||
end,
|
||||
|
||||
truthy = function(self) return false end
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue