From 47f95fc04af5554d9717038d7e06f470b6b2eb97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20Reuh=20Fildadut?= Date: Thu, 9 Jun 2022 17:07:58 +0900 Subject: [PATCH] Add object type, dot operator can return function references --- LANGUAGE.md | 2 +- anselme.lua | 20 +- interpreter/common.lua | 5 +- interpreter/expression.lua | 46 +++-- interpreter/interpreter.lua | 10 +- notes.txt | 4 +- parser/preparser.lua | 55 +++-- stdlib/bootscript.lua | 1 + stdlib/functions.lua | 63 +++++- stdlib/types.lua | 6 + ...nction reference dot operator function.ans | 9 + ...nction reference dot operator function.lua | 22 ++ test/tests/object constructor.ans | 15 ++ test/tests/object constructor.lua | 57 ++++++ test/tests/object several.ans | 26 +++ test/tests/object several.lua | 192 ++++++++++++++++++ test/tests/object simple.ans | 17 ++ test/tests/object simple.lua | 98 +++++++++ 18 files changed, 599 insertions(+), 49 deletions(-) create mode 100644 test/tests/function reference dot operator function.ans create mode 100644 test/tests/function reference dot operator function.lua create mode 100644 test/tests/object constructor.ans create mode 100644 test/tests/object constructor.lua create mode 100644 test/tests/object several.ans create mode 100644 test/tests/object several.lua create mode 100644 test/tests/object simple.ans create mode 100644 test/tests/object simple.lua diff --git a/LANGUAGE.md b/LANGUAGE.md index aced0f5..6dc1142 100644 --- a/LANGUAGE.md +++ b/LANGUAGE.md @@ -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`: 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 `()`. diff --git a/anselme.lua b/anselme.lua index c4aab25..5da99d8 100644 --- a/anselme.lua +++ b/anselme.lua @@ -577,8 +577,24 @@ local vm_mt = { save = function(self) local vars = {} for k, v in pairs(self.state.variables) do - if should_keep_variable(self.state, k) then - vars[k] = v + 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 + end end end return { diff --git a/interpreter/common.lua b/interpreter/common.lua index d7a37bd..96de990 100644 --- a/interpreter/common.lua +++ b/interpreter/common.lua @@ -189,9 +189,8 @@ common = { end, --- returns true if a variable should be persisted on save -- will exclude: undefined variables, variables in scoped functions, internal anselme variables - should_keep_variable = function(state, name) - local v = state.variables[name] - return v.type ~= "undefined argument" and v.type ~= "pending definition" and name:match("^"..identifier_pattern.."$") and not name:match("^anselme%.") + should_keep_variable = function(state, name, value) + return value.type ~= "undefined argument" and value.type ~= "pending definition" and name:match("^"..identifier_pattern.."$") and not name:match("^anselme%.") end, --- check truthyness of an anselme value truthy = function(val) diff --git a/interpreter/expression.lua b/interpreter/expression.lua index dec1b05..1d56edf 100644 --- a/interpreter/expression.lua +++ b/interpreter/expression.lua @@ -208,16 +208,10 @@ local function eval(state, exp) local tried_function_error_messages = {} local selected_variant = { depths = { assignment = nil }, variant = nil, args_to_set = nil } for _, fn in ipairs(variants) do - -- checkpoint: no args, nothing to select on - if fn.type == "checkpoint" then - if not selected_variant.variant then - selected_variant.depths = {} - selected_variant.variant = fn - 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 fn.type ~= "function" then + return nil, ("unknown function type %q"):format(fn.type) + -- functions + else if not fn.assignment or exp.assignment then local ok = true -- get and set args @@ -342,18 +336,20 @@ local function eval(state, exp) end end end - else - return nil, ("unknown function type %q"):format(fn.type) end end -- function successfully selected: run if selected_variant.variant then 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) if not r then return r, e end return r - elseif fn.type == "function" then + -- other functions + else local ret -- push scope -- 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", 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 if fn.scoped then scope:pop(state, fn) diff --git a/interpreter/interpreter.lua b/interpreter/interpreter.lua index a7b44b8..4ea82d6 100644 --- a/interpreter/interpreter.lua +++ b/interpreter/interpreter.lua @@ -111,7 +111,7 @@ run_line = function(state, line) elseif line.type == "flush_events" then local v, e = events:flush(state) 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.."๐Ÿ") if not reached then return nil, reachede end set_variable(state, line.namespace.."๐Ÿ", { @@ -150,9 +150,9 @@ run_block = function(state, block, resume_from_there, i, j) i = i + 1 end -- 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) - 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 reached, reachede = get_variable(state, parent_line.namespace.."๐Ÿ") 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 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 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 if parent_line.type == "choice" then 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 = {} -- go up in hierarchy in ascending order until function boundary 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 local v, e = eval(state, parent_line.expression) if not v then return v, ("%s; at %s"):format(e, parent_line.source) end diff --git a/notes.txt b/notes.txt index 148113e..a5f7436 100644 --- a/notes.txt +++ b/notes.txt @@ -43,6 +43,8 @@ Reserved symbols that are still not used as a line type: `^+-= 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? 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 diff --git a/parser/preparser.lua b/parser/preparser.lua index d63a529..34d3b1e 100644 --- a/parser/preparser.lua +++ b/parser/preparser.lua @@ -54,24 +54,41 @@ local function parse_line(line, state, namespace, parent_function) r.child = true r.text = l:match("^>%s*(.-)$") -- 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 - r.type = l:match("^%$") and "function" or "checkpoint" + 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 = "function" r.child = true - -- store parent function and run checkpoint when line is read - if r.type == "checkpoint" then - r.parent_function = parent_function + -- subtype options + local allow_params = true + 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 -- 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 - -- lua function - if state.global_state.link_next_function_definition_to_lua_function then - r.lua_function = state.global_state.link_next_function_definition_to_lua_function - state.global_state.link_next_function_definition_to_lua_function = nil - end + end + -- lua function + 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 + state.global_state.link_next_function_definition_to_lua_function = nil end -- get identifier - local lc = l:match("^%$(.-)$") or l:match("^ยง(.-)$") + local lc = l:match("^[%$%%](.-)$") or l:match("^ยง(.-)$") local identifier, rem = lc:match("^("..identifier_pattern..")(.-)$") if not identifier then for _, name in ipairs(special_functions_names) do @@ -80,7 +97,7 @@ local function parse_line(line, state, namespace, parent_function) end end 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 -- format 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 -- get params r.params = {} - if r.type == "function" and rem:match("^%b()") then + if allow_params and rem:match("^%b()") then r.scoped = true local content content, rem = rem:match("^(%b())%s*(.*)$") @@ -131,7 +148,7 @@ local function parse_line(line, state, namespace, parent_function) end end -- get assignment param - if r.type == "function" and rem:match("^%:%=") then + if allow_assign and rem:match("^%:%=") then local param = rem:match("^%:%=(.*)$") -- get identifier local param_identifier, param_rem = param:match("^("..identifier_pattern..")(.-)$") @@ -153,7 +170,7 @@ local function parse_line(line, state, namespace, parent_function) -- add parameter 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 - 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 -- calculate arity local minarity, maxarity = #r.params, #r.params @@ -190,7 +207,7 @@ local function parse_line(line, state, namespace, parent_function) else table.insert(line.children, 1, { content = ":๐Ÿ‘๏ธ=0", source = line.source }) end - if r.type == "function" then + if r.subtype ~= "checkpoint" then -- define ๐Ÿ”– variable local checkpoint_alias = state.global_state.builtin_aliases["๐Ÿ”–"] if checkpoint_alias then @@ -222,7 +239,7 @@ local function parse_line(line, state, namespace, parent_function) end end end - elseif r.type == "checkpoint" then + elseif r.subtype == "checkpoint" then -- define ๐Ÿ variable local reached_alias = state.global_state.builtin_aliases["๐Ÿ"] if reached_alias then @@ -372,7 +389,7 @@ local function parse_block(indented, state, namespace, parent_function) if not ast.child then return nil, ("line %s (%s) can't have children"):format(ast.source, ast.type) 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 r.parent_line = ast ast.child = r diff --git a/stdlib/bootscript.lua b/stdlib/bootscript.lua index 9a5de04..7756b9e 100644 --- a/stdlib/bootscript.lua +++ b/stdlib/bootscript.lua @@ -8,6 +8,7 @@ return [[ :pair="pair" :function reference="function reference" :variable reference="variable reference" +:object="object" :pi=3.1415926535898 ]] diff --git a/stdlib/functions.lua b/stdlib/functions.lua index aee5fda..323e35e 100644 --- a/stdlib/functions.lua +++ b/stdlib/functions.lua @@ -76,28 +76,37 @@ lua_functions = { }, -- namespace ["_._(r::function reference, name::string)"] = { - mode = "raw", + mode = "untyped raw", value = function(r, n) local state = anselme.running.state local rval = r.value local name = n.value 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 return get_variable(state, vfqm) 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, ", ")) end }, ["_._(r::function reference, name::string) := v"] = { - mode = "raw", + mode = "untyped raw", value = function(r, n, v) local state = anselme.running.state local rval = r.value local name = n.value 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 set_variable(state, vfqm, 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, ", ")) 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 ["()(l::list, i::number)"] = { mode = "untyped raw", diff --git a/stdlib/types.lua b/stdlib/types.lua index 03249c6..28b6ff5 100644 --- a/stdlib/types.lua +++ b/stdlib/types.lua @@ -166,6 +166,12 @@ types.anselme = { end, to_lua = nil }, + object = { + format = function(val) + return ("%%%s"):format(val.class) + end, + to_lua = nil + }, -- internal types ["event buffer"] = { format = function(val) -- triggered from subtexts diff --git a/test/tests/function reference dot operator function.ans b/test/tests/function reference dot operator function.ans new file mode 100644 index 0000000..37b1578 --- /dev/null +++ b/test/tests/function reference dot operator function.ans @@ -0,0 +1,9 @@ +$ f + $ a + @12 + +:x = &f + +{x.a} + +{x.a!} diff --git a/test/tests/function reference dot operator function.lua b/test/tests/function reference dot operator function.lua new file mode 100644 index 0000000..22c93c5 --- /dev/null +++ b/test/tests/function reference dot operator function.lua @@ -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" } +]]-- \ No newline at end of file diff --git a/test/tests/object constructor.ans b/test/tests/object constructor.ans new file mode 100644 index 0000000..4de82c5 --- /dev/null +++ b/test/tests/object constructor.ans @@ -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} diff --git a/test/tests/object constructor.lua b/test/tests/object constructor.lua new file mode 100644 index 0000000..66030b1 --- /dev/null +++ b/test/tests/object constructor.lua @@ -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" } +]]-- \ No newline at end of file diff --git a/test/tests/object several.ans b/test/tests/object several.ans new file mode 100644 index 0000000..1c9fc94 --- /dev/null +++ b/test/tests/object several.ans @@ -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} diff --git a/test/tests/object several.lua b/test/tests/object several.lua new file mode 100644 index 0000000..bbe8ba5 --- /dev/null +++ b/test/tests/object several.lua @@ -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" } +]]-- \ No newline at end of file diff --git a/test/tests/object simple.ans b/test/tests/object simple.ans new file mode 100644 index 0000000..aae5277 --- /dev/null +++ b/test/tests/object simple.ans @@ -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} diff --git a/test/tests/object simple.lua b/test/tests/object simple.lua new file mode 100644 index 0000000..7da5fb8 --- /dev/null +++ b/test/tests/object simple.lua @@ -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" } +]]-- \ No newline at end of file