mirror of
https://github.com/Reuh/anselme.git
synced 2025-10-28 09:09: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 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 {
|
local VariableMetadata = ast.abstract.Runtime {
|
||||||
type = "variable metadata",
|
type = "variable metadata",
|
||||||
|
|
@ -22,6 +22,10 @@ local VariableMetadata = ast.abstract.Runtime {
|
||||||
return v
|
return v
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
undefined = function(self, state)
|
||||||
|
local v = self.branched:get(state)
|
||||||
|
return Undefined:is(v)
|
||||||
|
end,
|
||||||
get_symbol = function(self)
|
get_symbol = function(self)
|
||||||
return self.symbol
|
return self.symbol
|
||||||
end,
|
end,
|
||||||
|
|
@ -68,21 +72,16 @@ local Environment = ast.abstract.Runtime {
|
||||||
variables = nil, -- Table of { {identifier} = variable metadata, ... }
|
variables = nil, -- Table of { {identifier} = variable metadata, ... }
|
||||||
|
|
||||||
partial = nil, -- { [name string] = true, ... }
|
partial = nil, -- { [name string] = true, ... }
|
||||||
undefine = nil, -- { [name string] = true, ... } - variable present here will not be looked up in parent env
|
|
||||||
export = nil, -- bool
|
export = nil, -- bool
|
||||||
|
|
||||||
_lookup_cache = nil, -- { [name string] = variable metadata, ... }
|
_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)
|
init = function(self, state, parent, partial_names, is_export)
|
||||||
self.variables = Table:new(state)
|
self.variables = Table:new(state)
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.partial = partial_names
|
self.partial = partial_names
|
||||||
self.export = is_export
|
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 = {}
|
||||||
self._lookup_cache_current = {}
|
self._lookup_cache_current = {}
|
||||||
end,
|
end,
|
||||||
|
|
@ -107,14 +106,10 @@ local Environment = ast.abstract.Runtime {
|
||||||
or (self.export ~= symbol.exported) then
|
or (self.export ~= symbol.exported) then
|
||||||
return self.parent:define(state, symbol, exp)
|
return self.parent:define(state, symbol, exp)
|
||||||
end
|
end
|
||||||
if symbol.undefine then
|
local variable = VariableMetadata:new(state, symbol, exp)
|
||||||
self.undefine[name] = true
|
self.variables:set(state, symbol:to_identifier(), variable)
|
||||||
else
|
self._lookup_cache[name] = variable
|
||||||
local variable = VariableMetadata:new(state, symbol, exp)
|
self._lookup_cache_current[name] = variable
|
||||||
self.variables:set(state, symbol:to_identifier(), variable)
|
|
||||||
self._lookup_cache[name] = variable
|
|
||||||
self._lookup_cache_current[name] = true
|
|
||||||
end
|
|
||||||
end,
|
end,
|
||||||
-- define or redefine new overloadable variable in current environment, inheriting existing overload variants from (parent) scopes
|
-- define or redefine new overloadable variable in current environment, inheriting existing overload variants from (parent) scopes
|
||||||
define_overloadable = function(self, state, symbol, exp)
|
define_overloadable = function(self, state, symbol, exp)
|
||||||
|
|
@ -152,11 +147,34 @@ local Environment = ast.abstract.Runtime {
|
||||||
if _cache[name] == nil then
|
if _cache[name] == nil then
|
||||||
if self.variables:has(state, identifier) then
|
if self.variables:has(state, identifier) then
|
||||||
_cache[name] = self.variables:get(state, identifier)
|
_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)
|
_cache[name] = self.parent:_lookup(state, identifier)
|
||||||
end
|
end
|
||||||
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,
|
end,
|
||||||
|
|
||||||
-- returns bool if variable defined in current or parent environment
|
-- 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
|
-- 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.)
|
-- (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)
|
defined_in_current = function(self, state, symbol)
|
||||||
local name = symbol.string
|
return self:_lookup_in_current(state, symbol) ~= nil
|
||||||
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]
|
|
||||||
end,
|
end,
|
||||||
-- return bool if variable is defined in the current environment only - won't search in parent env for exported & partial names
|
-- 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)
|
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,
|
end,
|
||||||
|
|
||||||
-- get variable in current or parent scope, with metadata
|
-- get variable in current or parent scope, with metadata
|
||||||
|
|
@ -236,6 +243,6 @@ local Environment = ast.abstract.Runtime {
|
||||||
}
|
}
|
||||||
|
|
||||||
package.loaded[...] = Environment
|
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
|
return Environment
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
-- create a partial layer to define temporary variables
|
-- create a partial layer to define temporary variables
|
||||||
|
|
||||||
local ast = require("anselme.ast")
|
local ast = require("anselme.ast")
|
||||||
local Identifier, Quote, Nil
|
local Identifier, Quote, Undefined
|
||||||
|
|
||||||
local attached_block_identifier, attached_block_symbol
|
local attached_block_identifier, attached_block_symbol
|
||||||
local unpack = table.unpack or unpack
|
local unpack = table.unpack or unpack
|
||||||
|
|
@ -74,14 +74,14 @@ PartialScope = ast.abstract.Node {
|
||||||
attach_block = function(self, expression, block)
|
attach_block = function(self, expression, block)
|
||||||
local partial = PartialScope:new(expression)
|
local partial = PartialScope:new(expression)
|
||||||
local unpartial = PartialScope:new(block)
|
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))
|
partial:define(attached_block_symbol, Quote:new(unpartial))
|
||||||
return partial
|
return partial
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
package.loaded[...] = PartialScope
|
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_identifier = Identifier:new("_")
|
||||||
attached_block_symbol = attached_block_identifier:to_symbol()
|
attached_block_symbol = attached_block_identifier:to_symbol()
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ Symbol = ast.abstract.Node {
|
||||||
exported = nil, -- bool
|
exported = nil, -- bool
|
||||||
|
|
||||||
confined_to_branch = nil, -- bool
|
confined_to_branch = nil, -- bool
|
||||||
undefine = nil, -- bool
|
|
||||||
|
|
||||||
init = function(self, str, modifiers)
|
init = function(self, str, modifiers)
|
||||||
modifiers = modifiers or {}
|
modifiers = modifiers or {}
|
||||||
|
|
@ -25,7 +24,6 @@ Symbol = ast.abstract.Node {
|
||||||
self.alias = modifiers.alias
|
self.alias = modifiers.alias
|
||||||
self.confined_to_branch = modifiers.confined_to_branch
|
self.confined_to_branch = modifiers.confined_to_branch
|
||||||
self.exported = modifiers.exported
|
self.exported = modifiers.exported
|
||||||
self.undefine = modifiers.undefine
|
|
||||||
end,
|
end,
|
||||||
|
|
||||||
_eval = function(self, state)
|
_eval = function(self, state)
|
||||||
|
|
@ -36,7 +34,7 @@ Symbol = ast.abstract.Node {
|
||||||
|
|
||||||
with = function(self, modifiers)
|
with = function(self, modifiers)
|
||||||
modifiers = modifiers or {}
|
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
|
if modifiers[k] == nil then
|
||||||
modifiers[k] = self[k]
|
modifiers[k] = self[k]
|
||||||
end
|
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