From 2cd910389bb79036976508a93787404e2f2b2893 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20Reuh=20Fildadut?= Date: Sat, 30 Dec 2023 23:43:05 +0100 Subject: [PATCH] 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. --- anselme/ast/Closure.lua | 35 ++++++++----------- anselme/ast/Definition.lua | 20 ++++------- anselme/ast/Environment.lua | 2 +- anselme/ast/Function.lua | 9 ++--- anselme/common/init.lua | 6 ++++ anselme/state/State.lua | 10 +----- anselme/stdlib/closure.lua | 22 +++++++----- test/results/exported variable nested.ans | 11 +++--- test/results/function exported.ans | 2 +- test/tests/exported variable nested.ans | 6 ++-- test/tests/exported variable.ans | 3 +- test/tests/function exported.ans | 5 ++- test/tests/function scope.ans | 3 +- ...amespace operator arbitrary expression.ans | 4 ++- 14 files changed, 65 insertions(+), 73 deletions(-) diff --git a/anselme/ast/Closure.lua b/anselme/ast/Closure.lua index c1539eb..15772f9 100644 --- a/anselme/ast/Closure.lua +++ b/anselme/ast/Closure.lua @@ -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 diff --git a/anselme/ast/Definition.lua b/anselme/ast/Definition.lua index 3837564..593d420 100644 --- a/anselme/ast/Definition.lua +++ b/anselme/ast/Definition.lua @@ -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() diff --git a/anselme/ast/Environment.lua b/anselme/ast/Environment.lua index 71e09c9..d039905 100644 --- a/anselme/ast/Environment.lua +++ b/anselme/ast/Environment.lua @@ -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, diff --git a/anselme/ast/Function.lua b/anselme/ast/Function.lua index d347f27..8abf701 100644 --- a/anselme/ast/Function.lua +++ b/anselme/ast/Function.lua @@ -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, } diff --git a/anselme/common/init.lua b/anselme/common/init.lua index 2590212..97eb575 100644 --- a/anselme/common/init.lua +++ b/anselme/common/init.lua @@ -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 = { diff --git a/anselme/state/State.lua b/anselme/state/State.lua index fb127f3..fe02233 100644 --- a/anselme/state/State.lua +++ b/anselme/state/State.lua @@ -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", diff --git a/anselme/stdlib/closure.lua b/anselme/stdlib/closure.lua index e6aab2f..4fe27ff 100644 --- a/anselme/stdlib/closure.lua +++ b/anselme/stdlib/closure.lua @@ -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 diff --git a/test/results/exported variable nested.ans b/test/results/exported variable nested.ans index 67821c7..bafafb6 100644 --- a/test/results/exported variable nested.ans +++ b/test/results/exported variable nested.ans @@ -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 #-- {} \ No newline at end of file diff --git a/test/results/function exported.ans b/test/results/function exported.ans index 8595571..dc8c9be 100644 --- a/test/results/function exported.ans +++ b/test/results/function exported.ans @@ -8,7 +8,7 @@ --- text --- | {}"" {}"1" {}"" | --- text --- -| {}"exported:" | +| {}"upvalue:" | --- text --- | {}"" {}"1" {}"" | --- text --- diff --git a/test/tests/exported variable nested.ans b/test/tests/exported variable nested.ans index 165e2bb..313fe7b 100644 --- a/test/tests/exported variable nested.ans +++ b/test/tests/exported variable nested.ans @@ -5,6 +5,8 @@ :@z = 12 | ko -|{f.x} +f! -|{f.y} +|{x} + +|{z} diff --git a/test/tests/exported variable.ans b/test/tests/exported variable.ans index 12b0cb0..5fd2511 100644 --- a/test/tests/exported variable.ans +++ b/test/tests/exported variable.ans @@ -1,4 +1,5 @@ :f = $ :@x = "ok" +f! -f.x \ No newline at end of file +x \ No newline at end of file diff --git a/test/tests/function exported.ans b/test/tests/function exported.ans index 4f3dbe2..f25b052 100644 --- a/test/tests/function exported.ans +++ b/test/tests/function exported.ans @@ -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! diff --git a/test/tests/function scope.ans b/test/tests/function scope.ans index bb21a8b..b0951dd 100644 --- a/test/tests/function scope.ans +++ b/test/tests/function scope.ans @@ -1,4 +1,5 @@ +:b = 5 :$ a - :@b = 5 + :b = 12 |a: {a.b} diff --git a/test/tests/namespace operator arbitrary expression.ans b/test/tests/namespace operator arbitrary expression.ans index 9b1a779..981a06b 100644 --- a/test/tests/namespace operator arbitrary expression.ans +++ b/test/tests/namespace operator arbitrary expression.ans @@ -1,5 +1,7 @@ :$ f - :@x = 5 + () + +f.:x = 5 :a = f