mirror of
https://github.com/Reuh/anselme.git
synced 2025-10-27 16:49:31 +00:00
Exported variables: no longer add export scope to every function, allow freely access and modifiy variable in function scope
Too many issues with predefining exported variables, and this is more flexible.
This commit is contained in:
parent
0eea4b80a6
commit
2cd910389b
14 changed files with 65 additions and 73 deletions
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
local ast = require("anselme.ast")
|
||||
local Overloadable, Runtime = ast.abstract.Overloadable, ast.abstract.Runtime
|
||||
local Definition
|
||||
|
||||
local resume_manager
|
||||
|
||||
|
|
@ -12,27 +11,14 @@ Closure = Runtime(Overloadable) {
|
|||
|
||||
func = nil, -- Function
|
||||
scope = nil, -- Environment
|
||||
exported_scope = nil, -- Environment
|
||||
|
||||
init = function(self, func, state)
|
||||
self.func = func
|
||||
|
||||
-- layer a new scope layer on top of captured/current scope
|
||||
-- to allow future define in the function (fn.:var = "foo")
|
||||
state.scope:push()
|
||||
self.scope = state.scope:capture()
|
||||
|
||||
-- layer a new export layer on top of captured/current scope
|
||||
state.scope:push_export()
|
||||
self.exported_scope = state.scope:capture()
|
||||
|
||||
-- pre-define exports
|
||||
for _, target in pairs(self:list_resume_targets()) do
|
||||
if Definition:is(target) and target.symbol.exported then
|
||||
resume_manager:push_no_continue(state, target)
|
||||
state.scope:push() -- create temp func scope, in case non-export definitions are done in the resume
|
||||
self.func.expression:eval(state)
|
||||
state.scope:pop()
|
||||
resume_manager:pop(state)
|
||||
end
|
||||
end
|
||||
|
||||
state.scope:pop()
|
||||
end,
|
||||
|
||||
|
|
@ -43,7 +29,6 @@ Closure = Runtime(Overloadable) {
|
|||
traverse = function(self, fn, ...)
|
||||
fn(self.func, ...)
|
||||
fn(self.scope, ...)
|
||||
fn(self.exported_scope, ...)
|
||||
end,
|
||||
|
||||
compatible_with_arguments = function(self, state, args)
|
||||
|
|
@ -53,15 +38,23 @@ Closure = Runtime(Overloadable) {
|
|||
return self.func.parameters:format(state)
|
||||
end,
|
||||
call_dispatched = function(self, state, args)
|
||||
state.scope:push(self.exported_scope)
|
||||
state.scope:push(self.scope)
|
||||
local exp = self.func:call_dispatched(state, args)
|
||||
state.scope:pop()
|
||||
return exp
|
||||
end,
|
||||
resume = function(self, state, target)
|
||||
if self.func.parameters.min_arity > 0 then error("can't resume function with parameters") end
|
||||
state.scope:push(self.scope)
|
||||
resume_manager:push(state, target)
|
||||
local exp = self.func:call(state, ast.ArgumentTuple:new())
|
||||
resume_manager:pop(state)
|
||||
state.scope:pop()
|
||||
return exp
|
||||
end,
|
||||
}
|
||||
|
||||
package.loaded[...] = Closure
|
||||
Definition = ast.Definition
|
||||
resume_manager = require("anselme.state.resume_manager")
|
||||
|
||||
return Closure
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ local Nil, Overloadable
|
|||
|
||||
local operator_priority = require("anselme.common").operator_priority
|
||||
|
||||
local Definition = ast.abstract.ResumeTarget {
|
||||
local Definition = ast.abstract.Node {
|
||||
type = "definition",
|
||||
|
||||
symbol = nil,
|
||||
|
|
@ -27,21 +27,13 @@ local Definition = ast.abstract.ResumeTarget {
|
|||
end,
|
||||
|
||||
_eval = function(self, state)
|
||||
if self.symbol.exported and state.scope:defined_in_current(self.symbol) then
|
||||
return Nil:new() -- export vars: can reuse existing definition
|
||||
end
|
||||
|
||||
local symbol = self.symbol:eval(state)
|
||||
if symbol.alias then
|
||||
state.scope:define_alias(symbol, self.expression)
|
||||
else
|
||||
local val = self.expression:eval(state)
|
||||
local val = self.expression:eval(state)
|
||||
|
||||
if Overloadable:issub(val) then
|
||||
state.scope:define_overloadable(symbol, val)
|
||||
else
|
||||
state.scope:define(symbol, val)
|
||||
end
|
||||
if Overloadable:issub(val) then
|
||||
state.scope:define_overloadable(symbol, val)
|
||||
else
|
||||
state.scope:define(symbol, val)
|
||||
end
|
||||
|
||||
return Nil:new()
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ local Environment = ast.abstract.Runtime {
|
|||
end
|
||||
return false
|
||||
end,
|
||||
-- return bool if variable is defined in the current environment only - won't search in parent event 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)
|
||||
return self.variables:has(state, identifier)
|
||||
end,
|
||||
|
|
|
|||
|
|
@ -13,10 +13,9 @@ Function = Overloadable {
|
|||
parameters = nil, -- ParameterTuple
|
||||
expression = nil,
|
||||
|
||||
init = function(self, parameters, expression, exports)
|
||||
init = function(self, parameters, expression)
|
||||
self.parameters = parameters
|
||||
self.expression = ReturnBoundary:new(expression)
|
||||
self.exports = exports or {}
|
||||
end,
|
||||
|
||||
_format = function(self, ...)
|
||||
|
|
@ -33,10 +32,6 @@ Function = Overloadable {
|
|||
traverse = function(self, fn, ...)
|
||||
fn(self.parameters, ...)
|
||||
fn(self.expression, ...)
|
||||
for sym, val in pairs(self.exports) do
|
||||
fn(sym, ...)
|
||||
fn(val, ...)
|
||||
end
|
||||
end,
|
||||
|
||||
compatible_with_arguments = function(self, state, args)
|
||||
|
|
@ -61,7 +56,7 @@ Function = Overloadable {
|
|||
end,
|
||||
|
||||
_eval = function(self, state)
|
||||
return Closure:new(Function:new(self.parameters:eval(state), self.expression, self.exports), state)
|
||||
return Closure:new(Function:new(self.parameters:eval(state), self.expression), state)
|
||||
end,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,12 @@ local common = {
|
|||
:gsub("N", math.random(0x8, 0xb)) -- variant 1
|
||||
:gsub("x", function() return ("%x"):format(math.random(0x0, 0xf)) end) -- random hexadecimal digit
|
||||
end,
|
||||
-- same as assert, but do not add position information
|
||||
-- useful for errors raised from anselme (don't care about Lua error position)
|
||||
assert0 = function(v, message, ...)
|
||||
if not v then error(message, 0) end
|
||||
return v, message, ...
|
||||
end,
|
||||
-- list of operators and their priority that are handled through regular function calls & can be overloaded/etc. by the user
|
||||
regular_operators = {
|
||||
prefixes = {
|
||||
|
|
|
|||
|
|
@ -10,17 +10,9 @@ local persistent_manager = require("anselme.state.persistent_manager")
|
|||
local uuid = require("anselme.common").uuid
|
||||
local parser = require("anselme.parser")
|
||||
local binser = require("anselme.lib.binser")
|
||||
local assert0 = require("anselme.common").assert0
|
||||
local anselme
|
||||
|
||||
-- same as assert, but do not add position information
|
||||
-- useful for errors raised from anselme (don't care about Lua error position)
|
||||
local function assert0(v, message, ...)
|
||||
if not v then
|
||||
error(message, 0)
|
||||
end
|
||||
return v, message, ...
|
||||
end
|
||||
|
||||
local State
|
||||
State = class {
|
||||
type = "anselme state",
|
||||
|
|
|
|||
|
|
@ -1,35 +1,41 @@
|
|||
local ast = require("anselme.ast")
|
||||
local Nil, Boolean, Definition = ast.Nil, ast.Boolean, ast.Definition
|
||||
local Nil, Boolean, Definition, Call, Function, ParameterTuple, FunctionParameter, Identifier, Overload = ast.Nil, ast.Boolean, ast.Definition, ast.Call, ast.Function, ast.ParameterTuple, ast.FunctionParameter, ast.Identifier, ast.Overload
|
||||
local assert0 = require("anselme.common").assert0
|
||||
|
||||
return {
|
||||
{
|
||||
"defined", "(c::closure, s::string)",
|
||||
function(state, c, s)
|
||||
return Boolean:new(c.exported_scope:defined_in_current_strict(state, s:to_identifier()))
|
||||
return Boolean:new(c.scope:defined_in_current_strict(state, s:to_identifier()))
|
||||
end
|
||||
},
|
||||
{
|
||||
"has upvalue", "(c::closure, s::string)",
|
||||
function(state, c, s)
|
||||
return Boolean:new(c.scope:defined(state, s:to_identifier()))
|
||||
end
|
||||
},
|
||||
{
|
||||
"_._", "(c::closure, s::string)",
|
||||
function(state, c, s)
|
||||
local identifier = s:to_identifier()
|
||||
assert(c.exported_scope:defined_in_current_strict(state, identifier), ("no exported variable %q defined in closure"):format(s.string))
|
||||
return c.exported_scope:get(state, identifier)
|
||||
assert0(c.scope:defined(state, identifier), ("no variable %q defined in closure"):format(s.string))
|
||||
return c.scope:get(state, identifier)
|
||||
end
|
||||
},
|
||||
{
|
||||
"_._", "(c::closure, s::string) = v",
|
||||
function(state, c, s, v)
|
||||
local identifier = s:to_identifier()
|
||||
assert(c.exported_scope:defined_in_current_strict(state, identifier), ("no exported variable %q defined in closure"):format(s.string))
|
||||
c.exported_scope:set(state, identifier, v)
|
||||
assert0(c.scope:defined(state, identifier), ("no variable %q defined in closure"):format(s.string))
|
||||
c.scope:set(state, identifier, v)
|
||||
return Nil:new()
|
||||
end
|
||||
},
|
||||
{
|
||||
"_._", "(c::closure, s::symbol) = v",
|
||||
function(state, c, s, v)
|
||||
assert(s.exported, "can't define a non-exported variable from the outside of the closure")
|
||||
state.scope:push(c.exported_scope)
|
||||
state.scope:push(c.scope)
|
||||
local r = Definition:new(s, v):eval(state)
|
||||
state.scope:pop()
|
||||
return r
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
--# run #--
|
||||
--- text ---
|
||||
| {}"kk" |
|
||||
| {}"ko" |
|
||||
--- text ---
|
||||
| {}"" {}"42" {}"" |
|
||||
--- error ---
|
||||
[0m[31m[0m[31m[0m[31m[0m[31m./anselme/stdlib/closure.lua:15: no exported variable "y" defined in closure[0m
|
||||
↳ from [4mtest/tests/exported variable nested.ans:10:4[0m in call: [2mf . "y"[0m[0m
|
||||
↳ from [4mtest/tests/exported variable nested.ans:10:1[0m in text interpolation: [2m| {f . "y"} |[0m[0m
|
||||
↳ from [4mtest/tests/exported variable nested.ans:10:1[0m in translatable: [2m| {f . "y"} |[0m[0m
|
||||
[0m[31m[0m[31m[0m[31m[0m[31midentifier "z" is undefined in branch 6843ee85-cea8-445d-10f21-9a8e54372094[0m
|
||||
↳ from [4mtest/tests/exported variable nested.ans:12:3[0m in identifier: [2mz[0m[0m
|
||||
↳ from [4mtest/tests/exported variable nested.ans:12:1[0m in text interpolation: [2m| {z} |[0m[0m
|
||||
↳ from [4mtest/tests/exported variable nested.ans:12:1[0m in translatable: [2m| {z} |[0m[0m
|
||||
↳ from [4m?[0m in block: [2m:f = ($() _)…[0m
|
||||
--# saved #--
|
||||
{}
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
--- text ---
|
||||
| {}"" {}"1" {}"" |
|
||||
--- text ---
|
||||
| {}"exported:" |
|
||||
| {}"upvalue:" |
|
||||
--- text ---
|
||||
| {}"" {}"1" {}"" |
|
||||
--- text ---
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@
|
|||
:@z = 12
|
||||
| ko
|
||||
|
||||
|{f.x}
|
||||
f!
|
||||
|
||||
|{f.y}
|
||||
|{x}
|
||||
|
||||
|{z}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
:f = $
|
||||
:@x = "ok"
|
||||
f!
|
||||
|
||||
f.x
|
||||
x
|
||||
|
|
@ -6,11 +6,10 @@
|
|||
a = a + 1
|
||||
|
||||
:$ g
|
||||
:@a = 1
|
||||
|
||||
|{a}
|
||||
|
||||
a = a + 1
|
||||
g.:a = 1
|
||||
|
||||
|local:
|
||||
|
||||
|
|
@ -20,7 +19,7 @@ f!
|
|||
|
||||
f!
|
||||
|
||||
|exported:
|
||||
|upvalue:
|
||||
|
||||
g!
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
:b = 5
|
||||
:$ a
|
||||
:@b = 5
|
||||
:b = 12
|
||||
|
||||
|a: {a.b}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
:$ f
|
||||
:@x = 5
|
||||
()
|
||||
|
||||
f.:x = 5
|
||||
|
||||
:a = f
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue