1
0
Fork 0
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:
Étienne Fildadut 2023-12-30 23:43:05 +01:00
parent 0eea4b80a6
commit 2cd910389b
14 changed files with 65 additions and 73 deletions

View file

@ -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

View file

@ -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()

View file

@ -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,

View file

@ -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,
}

View file

@ -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 = {

View file

@ -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",

View file

@ -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

View file

@ -1,11 +1,14 @@
--# run #--
--- text ---
| {}"kk" |
| {}"ko" |
--- text ---
| {}"" {}"42" {}"" |
--- error ---
./anselme/stdlib/closure.lua:15: no exported variable "y" defined in closure
↳ from test/tests/exported variable nested.ans:10:4 in call: f . "y"
↳ from test/tests/exported variable nested.ans:10:1 in text interpolation: | {f . "y"} |
↳ from test/tests/exported variable nested.ans:10:1 in translatable: | {f . "y"} |
identifier "z" is undefined in branch 6843ee85-cea8-445d-10f21-9a8e54372094
↳ from test/tests/exported variable nested.ans:12:3 in identifier: z
↳ from test/tests/exported variable nested.ans:12:1 in text interpolation: | {z} |
↳ from test/tests/exported variable nested.ans:12:1 in translatable: | {z} |
↳ from ? in block: :f = ($() _)…
--# saved #--
{}

View file

@ -8,7 +8,7 @@
--- text ---
| {}"" {}"1" {}"" |
--- text ---
| {}"exported:" |
| {}"upvalue:" |
--- text ---
| {}"" {}"1" {}"" |
--- text ---

View file

@ -5,6 +5,8 @@
:@z = 12
| ko
|{f.x}
f!
|{f.y}
|{x}
|{z}

View file

@ -1,4 +1,5 @@
:f = $
:@x = "ok"
f!
f.x
x

View file

@ -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!

View file

@ -1,4 +1,5 @@
:b = 5
:$ a
:@b = 5
:b = 12
|a: {a.b}

View file

@ -1,5 +1,7 @@
:$ f
:@x = 5
()
f.:x = 5
:a = f