1
0
Fork 0
mirror of https://github.com/Reuh/anselme.git synced 2025-10-27 16:49:31 +00:00

Add object type, dot operator can return function references

This commit is contained in:
Étienne Fildadut 2022-06-09 17:07:58 +09:00
parent 69b9e17020
commit 47f95fc04a
18 changed files with 599 additions and 49 deletions

View file

@ -948,7 +948,7 @@ This only works on strings:
`a # b`: evaluates b, then evaluates a whith b added to the active tags. Returns a. `a # b`: evaluates b, then evaluates a whith b added to the active tags. Returns a.
`a.b`: if a is a function reference, returns the variable named `b` in the referenced function namespace. When overloading this operator, if `b` is an identifier, the operator will interpret it as a string (and not returns the evaluated value of the variable eventually associated to the identifier). `a.b`: if a is a function reference, returns the first found variable (or reference to a subfunction) named `b` in the referenced function namespace. When overloading this operator, if `b` is an identifier, the operator will interpret it as a string (instead of returning the evaluated value of the variable eventually associated to the identifier).
`a(b)`: evaluate b (number), returns the value with this index in a (list). Use 1-based indexing. If b is a string, will search the first pair in the list with this string as its name. Operator is named `()`. `a(b)`: evaluate b (number), returns the value with this index in a (list). Use 1-based indexing. If b is a string, will search the first pair in the list with this string as its name. Operator is named `()`.

View file

@ -577,10 +577,26 @@ local vm_mt = {
save = function(self) save = function(self)
local vars = {} local vars = {}
for k, v in pairs(self.state.variables) do for k, v in pairs(self.state.variables) do
if should_keep_variable(self.state, k) then if should_keep_variable(self.state, k, v) then
if v.type == "object" then -- filter object attributes
local attributes = {}
for kk, vv in pairs(v.value.attributes) do
if should_keep_variable(self.state, kk, vv) then
attributes[kk] = vv
end
end
vars[k] = {
type = "object",
value = {
class = v.value.class,
attributes = attributes
}
}
else
vars[k] = v vars[k] = v
end end
end end
end
return { return {
anselme = { anselme = {
versions = anselme.versions, versions = anselme.versions,

View file

@ -189,9 +189,8 @@ common = {
end, end,
--- returns true if a variable should be persisted on save --- returns true if a variable should be persisted on save
-- will exclude: undefined variables, variables in scoped functions, internal anselme variables -- will exclude: undefined variables, variables in scoped functions, internal anselme variables
should_keep_variable = function(state, name) should_keep_variable = function(state, name, value)
local v = state.variables[name] return value.type ~= "undefined argument" and value.type ~= "pending definition" and name:match("^"..identifier_pattern.."$") and not name:match("^anselme%.")
return v.type ~= "undefined argument" and v.type ~= "pending definition" and name:match("^"..identifier_pattern.."$") and not name:match("^anselme%.")
end, end,
--- check truthyness of an anselme value --- check truthyness of an anselme value
truthy = function(val) truthy = function(val)

View file

@ -208,16 +208,10 @@ local function eval(state, exp)
local tried_function_error_messages = {} local tried_function_error_messages = {}
local selected_variant = { depths = { assignment = nil }, variant = nil, args_to_set = nil } local selected_variant = { depths = { assignment = nil }, variant = nil, args_to_set = nil }
for _, fn in ipairs(variants) do for _, fn in ipairs(variants) do
-- checkpoint: no args, nothing to select on if fn.type ~= "function" then
if fn.type == "checkpoint" then return nil, ("unknown function type %q"):format(fn.type)
if not selected_variant.variant then -- functions
selected_variant.depths = {}
selected_variant.variant = fn
else else
return nil, ("checkpoint call %q is ambigous; may be at least either:\n\t%s\n\t%s"):format(exp.called_name, fn.pretty_signature, selected_variant.variant.pretty_signature)
end
-- function
elseif fn.type == "function" then
if not fn.assignment or exp.assignment then if not fn.assignment or exp.assignment then
local ok = true local ok = true
-- get and set args -- get and set args
@ -342,18 +336,20 @@ local function eval(state, exp)
end end
end end
end end
else
return nil, ("unknown function type %q"):format(fn.type)
end end
end end
-- function successfully selected: run -- function successfully selected: run
if selected_variant.variant then if selected_variant.variant then
local fn = selected_variant.variant local fn = selected_variant.variant
if fn.type == "checkpoint" then if fn.type ~= "function" then
return nil, ("unknown function type %q"):format(fn.type)
-- checkpoint: no args and resume execution
elseif fn.subtype == "checkpoint" then
local r, e = run(state, fn.child, not paren_call) local r, e = run(state, fn.child, not paren_call)
if not r then return r, e end if not r then return r, e end
return r return r
elseif fn.type == "function" then -- other functions
else
local ret local ret
-- push scope -- push scope
-- NOTE: if error happens between here and scope:pop, will leave the stack a mess -- NOTE: if error happens between here and scope:pop, will leave the stack a mess
@ -452,6 +448,28 @@ local function eval(state, exp)
type = "number", type = "number",
value = seen.value + 1 value = seen.value + 1
}) })
-- for classes: build resulting object
if fn.subtype == "class" then
local object = {
type = "type",
value = {
{
type = "object",
value = {
class = fn.name,
attributes = {}
}
},
{
type = "function reference",
value = { fn.name }
}
}
}
if ret and ret.type == "nil" then
ret = object
end
end
-- pop scope -- pop scope
if fn.scoped then if fn.scoped then
scope:pop(state, fn) scope:pop(state, fn)

View file

@ -111,7 +111,7 @@ run_line = function(state, line)
elseif line.type == "flush_events" then elseif line.type == "flush_events" then
local v, e = events:flush(state) local v, e = events:flush(state)
if not v then return v, ("%s; in event flush at %s"):format(e, line.source) end if not v then return v, ("%s; in event flush at %s"):format(e, line.source) end
elseif line.type == "checkpoint" then elseif line.type == "function" and line.subtype == "checkpoint" then
local reached, reachede = get_variable(state, line.namespace.."🏁") local reached, reachede = get_variable(state, line.namespace.."🏁")
if not reached then return nil, reachede end if not reached then return nil, reachede end
set_variable(state, line.namespace.."🏁", { set_variable(state, line.namespace.."🏁", {
@ -150,9 +150,9 @@ run_block = function(state, block, resume_from_there, i, j)
i = i + 1 i = i + 1
end end
-- if we are exiting a checkpoint block, mark it as ran and update checkpoint -- if we are exiting a checkpoint block, mark it as ran and update checkpoint
-- (when resuming from a checkpoint, execution is resumed from inside the checkpoint, the line.type=="checkpoint" check in run_line is never called) -- (when resuming from a checkpoint, execution is resumed from inside the checkpoint, the line.subtype=="checkpoint" check in run_line is never called)
-- (and we want this to be done after executing the checkpoint block anyway) -- (and we want this to be done after executing the checkpoint block anyway)
if block.parent_line and block.parent_line.type == "checkpoint" then if block.parent_line and block.parent_line.type == "function" and block.parent_line.subtype == "checkpoint" then
local parent_line = block.parent_line local parent_line = block.parent_line
local reached, reachede = get_variable(state, parent_line.namespace.."🏁") local reached, reachede = get_variable(state, parent_line.namespace.."🏁")
if not reached then return nil, reachede end if not reached then return nil, reachede end
@ -183,7 +183,7 @@ run_block = function(state, block, resume_from_there, i, j)
-- if parent is a choice, will ignore choices that belong to the same block (like the whole block was executed naturally from a higher parent) -- if parent is a choice, will ignore choices that belong to the same block (like the whole block was executed naturally from a higher parent)
-- if parent if a condition, will mark it as a success (skipping following else-conditions) (for the same reasons as for choices) -- if parent if a condition, will mark it as a success (skipping following else-conditions) (for the same reasons as for choices)
-- if parent pushed a tag, will pop it (tags from parents are added to the stack in run()) -- if parent pushed a tag, will pop it (tags from parents are added to the stack in run())
if resume_from_there and block.parent_line and block.parent_line.type ~= "function" then if resume_from_there and block.parent_line and not block.parent_line.resume_boundary then
local parent_line = block.parent_line local parent_line = block.parent_line
if parent_line.type == "choice" then if parent_line.type == "choice" then
state.interpreter.skip_choices_until_flush = true state.interpreter.skip_choices_until_flush = true
@ -209,7 +209,7 @@ local function run(state, block, resume_from_there, i, j)
local tags_to_add = {} local tags_to_add = {}
-- go up in hierarchy in ascending order until function boundary -- go up in hierarchy in ascending order until function boundary
local parent_line = block.parent_line local parent_line = block.parent_line
while parent_line and parent_line.type ~= "function" do while parent_line and not parent_line.resume_boundary do
if parent_line.type == "tag" then if parent_line.type == "tag" then
local v, e = eval(state, parent_line.expression) local v, e = eval(state, parent_line.expression)
if not v then return v, ("%s; at %s"):format(e, parent_line.source) end if not v then return v, ("%s; at %s"):format(e, parent_line.source) end

View file

@ -43,6 +43,8 @@ Reserved symbols that are still not used as a line type: `^+-=</[]*{}|\_!?.,;)"&
Broad goals and ideas that may never be implemented. Mostly used as personal post-it notes. Broad goals and ideas that may never be implemented. Mostly used as personal post-it notes.
TODO: type system is not super nice to use
TODO: distinguish between list and argument list to confuse less pairs with named arguments TODO: distinguish between list and argument list to confuse less pairs with named arguments
TODO: some sensible way to capture text event in string litterals (string interpolation)? -> meh, this means we can capture choice events, and choice events contain their code block, and we don't want to store code blocks in the save file (as code can be updated/translated/whatever) TODO: some sensible way to capture text event in string litterals (string interpolation)? -> meh, this means we can capture choice events, and choice events contain their code block, and we don't want to store code blocks in the save file (as code can be updated/translated/whatever)
@ -83,7 +85,7 @@ TODO: perform seen/reached/etc default variable not in interpreter but using par
TODO: no function call without () (reference to fn instead?). Fn could be stored in save but replaced with new versions on code reload... -> how to track these references? TODO: no function call without () (reference to fn instead?). Fn could be stored in save but replaced with new versions on code reload... -> how to track these references?
if we make fn first class this means anonymous fn, is it ok? if we make fn first class this means anonymous fn, is it ok?
TODO: make language simple enough to be able to reimplement it in, say, nim. Especially the interpreter (we could precompile a lot of stuff...) TODO: make language simple enough to be able to reimplement it in, say, nim. Especially the AST interpreter (we could precompile a lot of stuff...)
TODO: test reacheability of script paths TODO: test reacheability of script paths

View file

@ -54,24 +54,41 @@ local function parse_line(line, state, namespace, parent_function)
r.child = true r.child = true
r.text = l:match("^>%s*(.-)$") r.text = l:match("^>%s*(.-)$")
-- function & checkpoint -- function & checkpoint
elseif l:match("^%$") or l:match("") then -- § is a 2-bytes caracter, DO NOT USE LUA PATTERN OPERATORS as they operate on single bytes elseif l:match("^%$") or l:match("") or l:match("^%%") then -- § is a 2-bytes caracter, DO NOT USE LUA PATTERN OPERATORS as they operate on single bytes
r.type = l:match("^%$") and "function" or "checkpoint" r.type = "function"
r.child = true r.child = true
-- store parent function and run checkpoint when line is read -- subtype options
if r.type == "checkpoint" then local allow_params = true
r.parent_function = parent_function local allow_assign = true
local keep_in_ast = false
if l:match("^%$") then
r.subtype = "function"
r.resume_boundary = true
elseif l:match("^%%") then
r.subtype = "class"
r.resume_boundary = true
allow_params = false
allow_assign = false
elseif l:match("") then
r.subtype = "checkpoint"
allow_params = false
allow_assign = false
keep_in_ast = true
r.parent_function = parent_function -- store parent function and run checkpoint when line is read
else
error("unknown function line type")
end end
-- don't keep function node in block AST -- don't keep function node in block AST
if r.type == "function" then if not keep_in_ast then
r.remove_from_block_ast = true r.remove_from_block_ast = true
end
-- lua function -- lua function
if state.global_state.link_next_function_definition_to_lua_function then if r.subtype == "function" and state.global_state.link_next_function_definition_to_lua_function then
r.lua_function = state.global_state.link_next_function_definition_to_lua_function r.lua_function = state.global_state.link_next_function_definition_to_lua_function
state.global_state.link_next_function_definition_to_lua_function = nil state.global_state.link_next_function_definition_to_lua_function = nil
end end
end
-- get identifier -- get identifier
local lc = l:match("^%$(.-)$") or l:match("^§(.-)$") local lc = l:match("^[%$%%](.-)$") or l:match("^§(.-)$")
local identifier, rem = lc:match("^("..identifier_pattern..")(.-)$") local identifier, rem = lc:match("^("..identifier_pattern..")(.-)$")
if not identifier then if not identifier then
for _, name in ipairs(special_functions_names) do for _, name in ipairs(special_functions_names) do
@ -80,7 +97,7 @@ local function parse_line(line, state, namespace, parent_function)
end end
end end
if not identifier then if not identifier then
return nil, ("no valid identifier in checkpoint/function definition line %q; at %s"):format(lc, line.source) return nil, ("no valid identifier in function definition line %q; at %s"):format(lc, line.source)
end end
-- format identifier -- format identifier
local fqm = ("%s%s"):format(namespace, format_identifier(identifier)) local fqm = ("%s%s"):format(namespace, format_identifier(identifier))
@ -101,7 +118,7 @@ local function parse_line(line, state, namespace, parent_function)
r.name = fqm r.name = fqm
-- get params -- get params
r.params = {} r.params = {}
if r.type == "function" and rem:match("^%b()") then if allow_params and rem:match("^%b()") then
r.scoped = true r.scoped = true
local content local content
content, rem = rem:match("^(%b())%s*(.*)$") content, rem = rem:match("^(%b())%s*(.*)$")
@ -131,7 +148,7 @@ local function parse_line(line, state, namespace, parent_function)
end end
end end
-- get assignment param -- get assignment param
if r.type == "function" and rem:match("^%:%=") then if allow_assign and rem:match("^%:%=") then
local param = rem:match("^%:%=(.*)$") local param = rem:match("^%:%=(.*)$")
-- get identifier -- get identifier
local param_identifier, param_rem = param:match("^("..identifier_pattern..")(.-)$") local param_identifier, param_rem = param:match("^("..identifier_pattern..")(.-)$")
@ -153,7 +170,7 @@ local function parse_line(line, state, namespace, parent_function)
-- add parameter -- add parameter
r.assignment = { name = param_identifier, alias = param_alias, full_name = param_fqm, type_annotation = type_annotation, default = nil, vararg = nil } r.assignment = { name = param_identifier, alias = param_alias, full_name = param_fqm, type_annotation = type_annotation, default = nil, vararg = nil }
elseif rem:match("[^%s]") then elseif rem:match("[^%s]") then
return nil, ("expected end-of-line at end of checkpoint/function definition line, but got %q; at %s"):format(rem, line.source) return nil, ("expected end-of-line at end of function definition line, but got %q; at %s"):format(rem, line.source)
end end
-- calculate arity -- calculate arity
local minarity, maxarity = #r.params, #r.params local minarity, maxarity = #r.params, #r.params
@ -190,7 +207,7 @@ local function parse_line(line, state, namespace, parent_function)
else else
table.insert(line.children, 1, { content = ":👁️=0", source = line.source }) table.insert(line.children, 1, { content = ":👁️=0", source = line.source })
end end
if r.type == "function" then if r.subtype ~= "checkpoint" then
-- define 🔖 variable -- define 🔖 variable
local checkpoint_alias = state.global_state.builtin_aliases["🔖"] local checkpoint_alias = state.global_state.builtin_aliases["🔖"]
if checkpoint_alias then if checkpoint_alias then
@ -222,7 +239,7 @@ local function parse_line(line, state, namespace, parent_function)
end end
end end
end end
elseif r.type == "checkpoint" then elseif r.subtype == "checkpoint" then
-- define 🏁 variable -- define 🏁 variable
local reached_alias = state.global_state.builtin_aliases["🏁"] local reached_alias = state.global_state.builtin_aliases["🏁"]
if reached_alias then if reached_alias then
@ -372,7 +389,7 @@ local function parse_block(indented, state, namespace, parent_function)
if not ast.child then if not ast.child then
return nil, ("line %s (%s) can't have children"):format(ast.source, ast.type) return nil, ("line %s (%s) can't have children"):format(ast.source, ast.type)
else else
local r, e = parse_block(l.children, state, ast.namespace or namespace, ast.type == "function" and ast or parent_function) local r, e = parse_block(l.children, state, ast.namespace or namespace, (ast.type == "function" and ast.subtype ~= "checkpoint") and ast or parent_function)
if not r then return r, e end if not r then return r, e end
r.parent_line = ast r.parent_line = ast
ast.child = r ast.child = r

View file

@ -8,6 +8,7 @@ return [[
:pair="pair" :pair="pair"
:function reference="function reference" :function reference="function reference"
:variable reference="variable reference" :variable reference="variable reference"
:object="object"
:pi=3.1415926535898 :pi=3.1415926535898
]] ]]

View file

@ -76,28 +76,37 @@ lua_functions = {
}, },
-- namespace -- namespace
["_._(r::function reference, name::string)"] = { ["_._(r::function reference, name::string)"] = {
mode = "raw", mode = "untyped raw",
value = function(r, n) value = function(r, n)
local state = anselme.running.state local state = anselme.running.state
local rval = r.value local rval = r.value
local name = n.value local name = n.value
for _, ffqm in ipairs(rval) do for _, ffqm in ipairs(rval) do
local var, vfqm = find(state.aliases, state.interpreter.global_state.variables, ffqm..".", name) local var, vfqm = find(state.aliases, state.interpreter.global_state.variables, "", ffqm.."."..name)
if var then if var then
return get_variable(state, vfqm) return get_variable(state, vfqm)
end end
end end
for _, ffqm in ipairs(rval) do
local fn, fnfqm = find(state.aliases, state.functions, "", ffqm.."."..name)
if fn then
return {
type = "function reference",
value = { fnfqm }
}
end
end
return nil, ("can't find variable %q in function reference (searched in namespaces: %s)"):format(name, table.concat(rval, ", ")) return nil, ("can't find variable %q in function reference (searched in namespaces: %s)"):format(name, table.concat(rval, ", "))
end end
}, },
["_._(r::function reference, name::string) := v"] = { ["_._(r::function reference, name::string) := v"] = {
mode = "raw", mode = "untyped raw",
value = function(r, n, v) value = function(r, n, v)
local state = anselme.running.state local state = anselme.running.state
local rval = r.value local rval = r.value
local name = n.value local name = n.value
for _, ffqm in ipairs(rval) do for _, ffqm in ipairs(rval) do
local var, vfqm = find(state.aliases, state.interpreter.global_state.variables, ffqm..".", name) local var, vfqm = find(state.aliases, state.interpreter.global_state.variables, "", ffqm.."."..name)
if var then if var then
set_variable(state, vfqm, v) set_variable(state, vfqm, v)
return v return v
@ -106,6 +115,52 @@ lua_functions = {
return nil, ("can't find variable %q in function reference (searched in namespaces: %s)"):format(name, table.concat(rval, ", ")) return nil, ("can't find variable %q in function reference (searched in namespaces: %s)"):format(name, table.concat(rval, ", "))
end end
}, },
["_._(r::object, name::string)"] = {
mode = "untyped raw",
value = function(r, n)
local state = anselme.running.state
local obj = r.value
local name = n.value
-- attribute already present in object
local var = find(state.aliases, obj.attributes, "", obj.class.."."..name)
if var then return var end
-- search for attribute in base class
local cvar, cvfqm = find(state.aliases, state.interpreter.global_state.variables, "", obj.class.."."..name)
if cvar then return get_variable(state, cvfqm) end
-- search for method in base class
local fn, fnfqm = find(state.aliases, state.functions, "", obj.class.."."..name)
if fn then
return {
type = "function reference",
value = { fnfqm }
}
end
return nil, ("can't find attribute %q in object"):format(name)
end
},
["_._(r::object, name::string) := v"] = {
mode = "untyped raw",
value = function(r, n, v)
local state = anselme.running.state
local obj = r.value
local name = n.value
-- attribute already present in object
local var, vfqm = find(state.aliases, obj.attributes, "", obj.class.."."..name)
if var then
obj.attributes[vfqm] = v
mark_as_modified(anselme.running.state, obj.attributes)
return v
end
-- search for attribute in base class
local cvar, cvfqm = find(state.aliases, state.interpreter.global_state.variables, "", obj.class.."."..name)
if cvar then
obj.attributes[cvfqm] = v
mark_as_modified(anselme.running.state, obj.attributes)
return v
end
return nil, ("can't find attribute %q in object"):format(name)
end
},
-- index -- index
["()(l::list, i::number)"] = { ["()(l::list, i::number)"] = {
mode = "untyped raw", mode = "untyped raw",

View file

@ -166,6 +166,12 @@ types.anselme = {
end, end,
to_lua = nil to_lua = nil
}, },
object = {
format = function(val)
return ("%%%s"):format(val.class)
end,
to_lua = nil
},
-- internal types -- internal types
["event buffer"] = { ["event buffer"] = {
format = function(val) -- triggered from subtexts format = function(val) -- triggered from subtexts

View file

@ -0,0 +1,9 @@
$ f
$ a
@12
:x = &f
{x.a}
{x.a!}

View file

@ -0,0 +1,22 @@
local _={}
_[9]={}
_[8]={}
_[7]={tags=_[9],text="12"}
_[6]={tags=_[8],text="&function reference dot operator function.f.a"}
_[5]={_[7]}
_[4]={_[6]}
_[3]={"return"}
_[2]={"text",_[5]}
_[1]={"text",_[4]}
return {_[1],_[2],_[3]}
--[[
{ "text", { {
tags = {},
text = "&function reference dot operator function.f.a"
} } }
{ "text", { {
tags = {},
text = "12"
} } }
{ "return" }
]]--

View file

@ -0,0 +1,15 @@
% class
:a:b = "foo"
:c = "bar"
$ new(o::&class, x)
~ o.c := x
@o
:o = class
:p = class!new("hoho")
{o}, {p}
{o.c} == {class.c}
{p.c} != {class.c}

View file

@ -0,0 +1,57 @@
local _={}
_[23]={}
_[22]={}
_[21]={}
_[20]={}
_[19]={}
_[18]={}
_[17]={}
_[16]={}
_[15]={}
_[14]={tags=_[23],text="bar"}
_[13]={tags=_[22],text=" != "}
_[12]={tags=_[21],text="hoho"}
_[11]={tags=_[20],text="bar"}
_[10]={tags=_[19],text=" == "}
_[9]={tags=_[18],text="bar"}
_[8]={tags=_[17],text="%object constructor.class::&object constructor.class"}
_[7]={tags=_[16],text=", "}
_[6]={tags=_[15],text="%object constructor.class::&object constructor.class"}
_[5]={_[9],_[10],_[11],_[12],_[13],_[14]}
_[4]={_[6],_[7],_[8]}
_[3]={"return"}
_[2]={"text",_[5]}
_[1]={"text",_[4]}
return {_[1],_[2],_[3]}
--[[
{ "text", { {
tags = {},
text = "%object constructor.class::&object constructor.class"
}, {
tags = {},
text = ", "
}, {
tags = {},
text = "%object constructor.class::&object constructor.class"
} } }
{ "text", { {
tags = {},
text = "bar"
}, {
tags = {},
text = " == "
}, {
tags = {},
text = "bar"
}, {
tags = {},
text = "hoho"
}, {
tags = {},
text = " != "
}, {
tags = {},
text = "bar"
} } }
{ "return" }
]]--

View file

@ -0,0 +1,26 @@
% class
:a:b = "foo"
:c = "bar"
:o = class
:p = class
{o}, {p}
{o.a} == {class.a}
{o.b} == {class.b}
{p.a} == {class.a}
{p.b} == {class.b}
~ o.b := "haha"
{o.a} != {class.a}
{o.b} != {class.b}
{p.a} == {class.a}
{p.b} == {class.b}
{o.c} == {class.c}
{p.c} == {class.c}

View file

@ -0,0 +1,192 @@
local _={}
_[81]={}
_[80]={}
_[79]={}
_[78]={}
_[77]={}
_[76]={}
_[75]={}
_[74]={}
_[73]={}
_[72]={}
_[71]={}
_[70]={}
_[69]={}
_[68]={}
_[67]={}
_[66]={}
_[65]={}
_[64]={}
_[63]={}
_[62]={}
_[61]={}
_[60]={}
_[59]={}
_[58]={}
_[57]={}
_[56]={}
_[55]={}
_[54]={}
_[53]={}
_[52]={}
_[51]={}
_[50]={}
_[49]={}
_[48]={tags=_[81],text="bar"}
_[47]={tags=_[80],text=" == "}
_[46]={tags=_[79],text="bar"}
_[45]={tags=_[78],text="bar"}
_[44]={tags=_[77],text=" == "}
_[43]={tags=_[76],text="bar"}
_[42]={tags=_[75],text="foo"}
_[41]={tags=_[74],text=" == "}
_[40]={tags=_[73],text="foo"}
_[39]={tags=_[72],text="foo"}
_[38]={tags=_[71],text=" == "}
_[37]={tags=_[70],text="foo"}
_[36]={tags=_[69],text="foo"}
_[35]={tags=_[68],text=" != "}
_[34]={tags=_[67],text="haha"}
_[33]={tags=_[66],text="foo"}
_[32]={tags=_[65],text=" != "}
_[31]={tags=_[64],text="haha"}
_[30]={tags=_[63],text="foo"}
_[29]={tags=_[62],text=" == "}
_[28]={tags=_[61],text="foo"}
_[27]={tags=_[60],text="foo"}
_[26]={tags=_[59],text=" == "}
_[25]={tags=_[58],text="foo"}
_[24]={tags=_[57],text="foo"}
_[23]={tags=_[56],text=" == "}
_[22]={tags=_[55],text="foo"}
_[21]={tags=_[54],text="foo"}
_[20]={tags=_[53],text=" == "}
_[19]={tags=_[52],text="foo"}
_[18]={tags=_[51],text="%object several.class::&object several.class"}
_[17]={tags=_[50],text=", "}
_[16]={tags=_[49],text="%object several.class::&object several.class"}
_[15]={_[46],_[47],_[48]}
_[14]={_[43],_[44],_[45]}
_[13]={_[37],_[38],_[39],_[40],_[41],_[42]}
_[12]={_[31],_[32],_[33],_[34],_[35],_[36]}
_[11]={_[25],_[26],_[27],_[28],_[29],_[30]}
_[10]={_[19],_[20],_[21],_[22],_[23],_[24]}
_[9]={_[16],_[17],_[18]}
_[8]={"return"}
_[7]={"text",_[15]}
_[6]={"text",_[14]}
_[5]={"text",_[13]}
_[4]={"text",_[12]}
_[3]={"text",_[11]}
_[2]={"text",_[10]}
_[1]={"text",_[9]}
return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8]}
--[[
{ "text", { {
tags = {},
text = "%object several.class::&object several.class"
}, {
tags = {},
text = ", "
}, {
tags = {},
text = "%object several.class::&object several.class"
} } }
{ "text", { {
tags = {},
text = "foo"
}, {
tags = {},
text = " == "
}, {
tags = {},
text = "foo"
}, {
tags = {},
text = "foo"
}, {
tags = {},
text = " == "
}, {
tags = {},
text = "foo"
} } }
{ "text", { {
tags = {},
text = "foo"
}, {
tags = {},
text = " == "
}, {
tags = {},
text = "foo"
}, {
tags = {},
text = "foo"
}, {
tags = {},
text = " == "
}, {
tags = {},
text = "foo"
} } }
{ "text", { {
tags = {},
text = "haha"
}, {
tags = {},
text = " != "
}, {
tags = {},
text = "foo"
}, {
tags = {},
text = "haha"
}, {
tags = {},
text = " != "
}, {
tags = {},
text = "foo"
} } }
{ "text", { {
tags = {},
text = "foo"
}, {
tags = {},
text = " == "
}, {
tags = {},
text = "foo"
}, {
tags = {},
text = "foo"
}, {
tags = {},
text = " == "
}, {
tags = {},
text = "foo"
} } }
{ "text", { {
tags = {},
text = "bar"
}, {
tags = {},
text = " == "
}, {
tags = {},
text = "bar"
} } }
{ "text", { {
tags = {},
text = "bar"
}, {
tags = {},
text = " == "
}, {
tags = {},
text = "bar"
} } }
{ "return" }
]]--

View file

@ -0,0 +1,17 @@
% class
:a:b = "foo"
:c = "bar"
:o = class
{o}
{o.a} == {class.a}
{o.b} == {class.b}
~ o.b := "haha"
{o.a} != {class.a}
{o.b} != {class.b}
{o.c} == {class.c}

View file

@ -0,0 +1,98 @@
local _={}
_[41]={}
_[40]={}
_[39]={}
_[38]={}
_[37]={}
_[36]={}
_[35]={}
_[34]={}
_[33]={}
_[32]={}
_[31]={}
_[30]={}
_[29]={}
_[28]={}
_[27]={}
_[26]={}
_[25]={tags=_[41],text="bar"}
_[24]={tags=_[40],text=" == "}
_[23]={tags=_[39],text="bar"}
_[22]={tags=_[38],text="foo"}
_[21]={tags=_[37],text=" != "}
_[20]={tags=_[36],text="haha"}
_[19]={tags=_[35],text="foo"}
_[18]={tags=_[34],text=" != "}
_[17]={tags=_[33],text="haha"}
_[16]={tags=_[32],text="foo"}
_[15]={tags=_[31],text=" == "}
_[14]={tags=_[30],text="foo"}
_[13]={tags=_[29],text="foo"}
_[12]={tags=_[28],text=" == "}
_[11]={tags=_[27],text="foo"}
_[10]={tags=_[26],text="%object simple.class::&object simple.class"}
_[9]={_[23],_[24],_[25]}
_[8]={_[17],_[18],_[19],_[20],_[21],_[22]}
_[7]={_[11],_[12],_[13],_[14],_[15],_[16]}
_[6]={_[10]}
_[5]={"return"}
_[4]={"text",_[9]}
_[3]={"text",_[8]}
_[2]={"text",_[7]}
_[1]={"text",_[6]}
return {_[1],_[2],_[3],_[4],_[5]}
--[[
{ "text", { {
tags = {},
text = "%object simple.class::&object simple.class"
} } }
{ "text", { {
tags = {},
text = "foo"
}, {
tags = {},
text = " == "
}, {
tags = {},
text = "foo"
}, {
tags = {},
text = "foo"
}, {
tags = {},
text = " == "
}, {
tags = {},
text = "foo"
} } }
{ "text", { {
tags = {},
text = "haha"
}, {
tags = {},
text = " != "
}, {
tags = {},
text = "foo"
}, {
tags = {},
text = "haha"
}, {
tags = {},
text = " != "
}, {
tags = {},
text = "foo"
} } }
{ "text", { {
tags = {},
text = "bar"
}, {
tags = {},
text = " == "
}, {
tags = {},
text = "bar"
} } }
{ "return" }
]]--