diff --git a/interpreter/expression.lua b/interpreter/expression.lua index 8497d2d..891f063 100644 --- a/interpreter/expression.lua +++ b/interpreter/expression.lua @@ -508,8 +508,8 @@ local function eval(state, exp) }) if not s then return nil, e end -- for classes: build resulting object - if fn.subtype == "class" then - local object = { + if fn.subtype == "class" and ret and ret.type == "nil" then + ret = { type = "annotated", value = { { @@ -525,9 +525,6 @@ local function eval(state, exp) } } } - if ret and ret.type == "nil" then - ret = object - end end -- pop scope if fn.scoped then diff --git a/interpreter/interpreter.lua b/interpreter/interpreter.lua index 183879b..38b87b8 100644 --- a/interpreter/interpreter.lua +++ b/interpreter/interpreter.lua @@ -12,42 +12,42 @@ run_line = function(state, line) if line.type == "condition" then line.parent_block.last_condition_success = nil local v, e = eval(state, line.expression) - if not v then return v, ("%s; at %s"):format(e, line.source) end + if not v then return nil, ("%s; at %s"):format(e, line.source) end if truthy(v) then line.parent_block.last_condition_success = true v, e = run_block(state, line.child) - if e then return v, e end + if e then return nil, e end if v then return v end end elseif line.type == "else-condition" then if not line.parent_block.last_condition_success then local v, e = eval(state, line.expression) - if not v then return v, ("%s; at %s"):format(e, line.source) end + if not v then return nil, ("%s; at %s"):format(e, line.source) end if truthy(v) then line.parent_block.last_condition_success = true v, e = run_block(state, line.child) - if e then return v, e end + if e then return nil, e end if v then return v end end end elseif line.type == "while" then line.parent_block.last_condition_success = nil local v, e = eval(state, line.expression) - if not v then return v, ("%s; at %s"):format(e, line.source) end + if not v then return nil, ("%s; at %s"):format(e, line.source) end while truthy(v) do line.parent_block.last_condition_success = true v, e = run_block(state, line.child) - if e then return v, e end + if e then return nil, e end if v then return v end -- next iteration v, e = eval(state, line.expression) - if not v then return v, ("%s; at %s"):format(e, line.source) end + if not v then return nil, ("%s; at %s"):format(e, line.source) end end elseif line.type == "choice" then local v, e = events:make_space_for(state, "choice") - if not v then return v, ("%s; in automatic event flush at %s"):format(e, line.source) end + if not v then return nil, ("%s; in automatic event flush at %s"):format(e, line.source) end v, e = eval(state, line.text) - if not v then return v, ("%s; at %s"):format(e, line.source) end + if not v then return nil, ("%s; at %s"):format(e, line.source) end local l = v.type == "list" and v.value or { v } -- convert text events to choices for _, item in ipairs(l) do @@ -78,39 +78,39 @@ run_line = function(state, line) end end local iv, ie = events:write_buffer(state, final_buffer) - if not iv then return iv, ("%s; at %s"):format(ie, line.source) end + if not iv then return nil, ("%s; at %s"):format(ie, line.source) end end end elseif line.type == "tag" then local v, e = eval(state, line.expression) - if not v then return v, ("%s; at %s"):format(e, line.source) end + if not v then return nil, ("%s; at %s"):format(e, line.source) end tags:push(state, v) v, e = run_block(state, line.child) tags:pop(state) - if e then return v, e end + if e then return nil, e end if v then return v end elseif line.type == "return" then local v, e = eval(state, line.expression) - if not v then return v, ("%s; at %s"):format(e, line.source) end + if not v then return nil, ("%s; at %s"):format(e, line.source) end local cv, ce = run_block(state, line.child) - if ce then return cv, ce end + if ce then return nil, ce end if cv then return cv end return v elseif line.type == "text" then local v, e = events:make_space_for(state, "text") -- do this before any evaluation start - if not v then return v, ("%s; in automatic event flush at %s"):format(e, line.source) end + if not v then return nil, ("%s; in automatic event flush at %s"):format(e, line.source) end v, e = eval(state, line.text) - if not v then return v, ("%s; at %s"):format(e, line.source) end + if not v then return nil, ("%s; at %s"):format(e, line.source) end local l = v.type == "list" and v.value or { v } for _, item in ipairs(l) do if item.type == "event buffer" then local iv, ie = events:write_buffer(state, item.value) - if not iv then return iv, ("%s; at %s"):format(ie, line.source) end + if not iv then return nil, ("%s; at %s"):format(ie, line.source) end end end 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 + if not v then return nil, ("%s; in event flush at %s"):format(e, line.source) end 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 @@ -146,7 +146,7 @@ run_block = function(state, block, resume_from_there, i, j) -- run line if not skip then local v, e = run_line(state, line) - if e then return v, e end + if e then return nil, e end if v then return v end end i = i + 1 @@ -199,10 +199,9 @@ run_block = function(state, block, resume_from_there, i, j) tags:pop(state) end local v, e = run_block(state, parent_line.parent_block, resume_from_there, parent_line.parent_position+1) - if e then return v, e end - if v then return v, e end + if e then return nil, e end + if v then return v end end - return nil end -- returns var in case of success @@ -217,7 +216,7 @@ local function run(state, block, resume_from_there, i, j) while parent_line and not parent_line.resumable 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 + if not v then return nil, ("%s; at %s"):format(e, parent_line.source) end table.insert(tags_to_add, v) end parent_line = parent_line.parent_block.parent_line @@ -236,7 +235,7 @@ local function run(state, block, resume_from_there, i, j) tags:trim(state, tags_len) end -- return - if e then return v, e end + if e then return nil, e end if v then return v else diff --git a/notes.txt b/notes.txt index af7a52b..41b88c6 100644 --- a/notes.txt +++ b/notes.txt @@ -85,9 +85,6 @@ TODO: fn/checkpoint/tag: maybe consider them a regular func call that takes chil TODO: perform seen/reached/etc default variable not in interpreter but using parse-time macro -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? -> no way to track anonymous fn, so no. - 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 + visualization of different branches the script can take. For one of those overarching story visualization thingy.