From 90ebf4ca98a230eafbe5ce163e9c5bc8371dcd65 Mon Sep 17 00:00:00 2001 From: Dave Lucia Date: Sat, 7 Feb 2026 14:01:32 -0500 Subject: [PATCH 1/2] feat(vm): Basic Functions This implements basic functions without closures --- lib/lua/compiler/codegen.ex | 80 +++++++++++++++++++++++++++++++++++-- lib/lua/compiler/scope.ex | 63 +++++++++++++++++++++++++++++ lib/lua/vm.ex | 2 +- 3 files changed, 140 insertions(+), 5 deletions(-) diff --git a/lib/lua/compiler/codegen.ex b/lib/lua/compiler/codegen.ex index 57d45d2..69d8067 100644 --- a/lib/lua/compiler/codegen.ex +++ b/lib/lua/compiler/codegen.ex @@ -18,17 +18,17 @@ defmodule Lua.Compiler.Codegen do func_scope = scope_state.functions[:chunk] start_reg = func_scope.max_register - {instructions, _ctx} = - gen_block(block, %{next_reg: start_reg, source: source, scope: scope_state}) + {instructions, ctx} = + gen_block(block, %{next_reg: start_reg, source: source, scope: scope_state, prototypes: []}) # Wrap in a prototype proto = %Prototype{ instructions: instructions, - prototypes: [], + prototypes: Enum.reverse(ctx.prototypes), # Reverse to maintain order upvalue_descriptors: [], param_count: 0, is_vararg: false, - max_registers: 0, + max_registers: func_scope.max_register, source: source, lines: {1, 1} } @@ -418,6 +418,78 @@ defmodule Lua.Compiler.Codegen do end end + defp gen_expr(%Expr.Function{params: params, body: body} = func, ctx) do + # Get the function scope from the resolved AST + func_key = Map.get(ctx.scope.var_map, func) + func_scope = ctx.scope.functions[func_key] + + # Generate the function body in a fresh context + {body_instrs, _body_ctx} = + gen_block(body, %{ + next_reg: func_scope.param_count, + source: ctx.source, + scope: ctx.scope, + prototypes: [] + }) + + # Create the nested prototype + nested_proto = %Prototype{ + instructions: body_instrs, + prototypes: [], # For now, no nested-nested functions + upvalue_descriptors: [], + param_count: func_scope.param_count, + is_vararg: func_scope.is_vararg, + max_registers: func_scope.max_register, + source: ctx.source, + lines: {1, 1} + } + + # Add to prototypes list and get its index + proto_index = length(ctx.prototypes) + ctx = %{ctx | prototypes: [nested_proto | ctx.prototypes]} + + # Generate closure instruction + dest_reg = ctx.next_reg + ctx = %{ctx | next_reg: dest_reg + 1} + + {[Instruction.closure(dest_reg, proto_index)], dest_reg, ctx} + end + + defp gen_expr(%Expr.Call{func: func_expr, args: args}, ctx) do + # Allocate base register for the call + base_reg = ctx.next_reg + + # Generate code for the function expression + {func_instrs, func_reg, ctx} = gen_expr(func_expr, ctx) + + # Move function to base register if needed + move_func = + if func_reg == base_reg do + [] + else + [Instruction.move(base_reg, func_reg)] + end + + ctx = %{ctx | next_reg: base_reg + 1} + + # Generate code for arguments (they go in base+1, base+2, ...) + {arg_instrs, _arg_regs, ctx} = + Enum.reduce(args, {[], [], ctx}, fn arg, {instrs, regs, ctx} -> + {arg_instrs, arg_reg, ctx} = gen_expr(arg, ctx) + {instrs ++ arg_instrs, regs ++ [arg_reg], ctx} + end) + + # Generate call instruction + # For now, assume single return value + arg_count = length(args) + result_count = 1 + + call_instr = Instruction.call(base_reg, arg_count, result_count) + + # Result will be in base_reg + {func_instrs ++ move_func ++ arg_instrs ++ [call_instr], base_reg, ctx} + end + # Stub for other expressions defp gen_expr(_expr, ctx) do reg = ctx.next_reg diff --git a/lib/lua/compiler/scope.ex b/lib/lua/compiler/scope.ex index 42c296e..298117e 100644 --- a/lib/lua/compiler/scope.ex +++ b/lib/lua/compiler/scope.ex @@ -209,6 +209,69 @@ defmodule Lua.Compiler.Scope do resolve_expr(operand, state) end + defp resolve_expr(%Expr.Function{params: params, body: body} = func, state) do + # Create a new function scope + func_key = make_ref() + param_count = Enum.count(params, &(&1 != :vararg)) + is_vararg = :vararg in params + + # Save current scope state + saved_locals = state.locals + saved_next_reg = state.next_register + saved_function = state.current_function + + # Start fresh for the function scope + # Parameters get registers starting from 0 + {param_locals, next_param_reg} = + params + |> Enum.reject(&(&1 == :vararg)) + |> Enum.with_index() + |> Enum.reduce({%{}, 0}, fn {param, idx}, {locals, _} -> + {Map.put(locals, param, idx), idx + 1} + end) + + state = %{state | + locals: param_locals, + next_register: next_param_reg, + current_function: func_key + } + + # Create function scope entry + func_scope = %FunctionScope{ + max_register: next_param_reg, + param_count: param_count, + is_vararg: is_vararg, + upvalue_descriptors: [] + } + + state = %{state | functions: Map.put(state.functions, func_key, func_scope)} + + # Resolve the function body + state = resolve_block(body, state) + + # Update max_register based on what was used in the body + func_scope = state.functions[func_key] + func_scope = %{func_scope | max_register: max(func_scope.max_register, state.next_register)} + state = %{state | functions: Map.put(state.functions, func_key, func_scope)} + + # Store the function key in var_map for this function node + state = %{state | var_map: Map.put(state.var_map, func, func_key)} + + # Restore previous scope + %{state | + locals: saved_locals, + next_register: saved_next_reg, + current_function: saved_function + } + end + + defp resolve_expr(%Expr.Call{func: func, args: args}, state) do + # Resolve the function expression + state = resolve_expr(func, state) + # Resolve all arguments + Enum.reduce(args, state, &resolve_expr/2) + end + # For now, stub out other expression types defp resolve_expr(_expr, state), do: state end diff --git a/lib/lua/vm.ex b/lib/lua/vm.ex index b6cd061..0337958 100644 --- a/lib/lua/vm.ex +++ b/lib/lua/vm.ex @@ -21,7 +21,7 @@ defmodule Lua.VM do # Execute the prototype instructions {results, _final_regs, final_state} = - Executor.execute(proto.instructions, registers, [], state) + Executor.execute(proto.instructions, registers, [], proto, state) {:ok, results, final_state} end From dd86045e3cb93eb3c8e4dac39ed566c564f20e23 Mon Sep 17 00:00:00 2001 From: Dave Lucia Date: Sat, 7 Feb 2026 16:11:16 -0500 Subject: [PATCH 2/2] cleanup --- lib/lua/compiler/codegen.ex | 6 +- lib/lua/compiler/scope.ex | 18 ++-- lib/lua/vm/executor.ex | 188 ++++++++++++++++++++---------------- 3 files changed, 120 insertions(+), 92 deletions(-) diff --git a/lib/lua/compiler/codegen.ex b/lib/lua/compiler/codegen.ex index 69d8067..ee7df63 100644 --- a/lib/lua/compiler/codegen.ex +++ b/lib/lua/compiler/codegen.ex @@ -24,7 +24,8 @@ defmodule Lua.Compiler.Codegen do # Wrap in a prototype proto = %Prototype{ instructions: instructions, - prototypes: Enum.reverse(ctx.prototypes), # Reverse to maintain order + # Reverse to maintain order + prototypes: Enum.reverse(ctx.prototypes), upvalue_descriptors: [], param_count: 0, is_vararg: false, @@ -435,7 +436,8 @@ defmodule Lua.Compiler.Codegen do # Create the nested prototype nested_proto = %Prototype{ instructions: body_instrs, - prototypes: [], # For now, no nested-nested functions + # For now, no nested-nested functions + prototypes: [], upvalue_descriptors: [], param_count: func_scope.param_count, is_vararg: func_scope.is_vararg, diff --git a/lib/lua/compiler/scope.ex b/lib/lua/compiler/scope.ex index 298117e..8f583c6 100644 --- a/lib/lua/compiler/scope.ex +++ b/lib/lua/compiler/scope.ex @@ -230,10 +230,11 @@ defmodule Lua.Compiler.Scope do {Map.put(locals, param, idx), idx + 1} end) - state = %{state | - locals: param_locals, - next_register: next_param_reg, - current_function: func_key + state = %{ + state + | locals: param_locals, + next_register: next_param_reg, + current_function: func_key } # Create function scope entry @@ -258,10 +259,11 @@ defmodule Lua.Compiler.Scope do state = %{state | var_map: Map.put(state.var_map, func, func_key)} # Restore previous scope - %{state | - locals: saved_locals, - next_register: saved_next_reg, - current_function: saved_function + %{ + state + | locals: saved_locals, + next_register: saved_next_reg, + current_function: saved_function } end diff --git a/lib/lua/vm/executor.ex b/lib/lua/vm/executor.ex index 7dac18b..0dbc1a8 100644 --- a/lib/lua/vm/executor.ex +++ b/lib/lua/vm/executor.ex @@ -12,121 +12,145 @@ defmodule Lua.VM.Executor do Returns {results, final_registers, final_state}. """ - @spec execute([tuple()], tuple(), list(), State.t()) :: + @spec execute([tuple()], tuple(), list(), map(), State.t()) :: {list(), tuple(), State.t()} - def execute(instructions, registers, upvalues, state) do - do_execute(instructions, registers, upvalues, state) + def execute(instructions, registers, upvalues, proto, state) do + do_execute(instructions, registers, upvalues, proto, state) end # Empty instruction list - implicit return nil - defp do_execute([], regs, _upvals, state) do + defp do_execute([], regs, _upvals, _proto, state) do {[nil], regs, state} end # load_constant - defp do_execute([{:load_constant, dest, value} | rest], regs, upvals, state) do + defp do_execute([{:load_constant, dest, value} | rest], regs, upvals, proto, state) do regs = put_elem(regs, dest, value) - do_execute(rest, regs, upvals, state) + do_execute(rest, regs, upvals, proto, state) end # load_boolean - defp do_execute([{:load_boolean, dest, value} | rest], regs, upvals, state) do + defp do_execute([{:load_boolean, dest, value} | rest], regs, upvals, proto, state) do regs = put_elem(regs, dest, value) - do_execute(rest, regs, upvals, state) + do_execute(rest, regs, upvals, proto, state) end # get_global - defp do_execute([{:get_global, dest, name} | rest], regs, upvals, state) do + defp do_execute([{:get_global, dest, name} | rest], regs, upvals, proto, state) do value = Map.get(state.globals, name, nil) regs = put_elem(regs, dest, value) - do_execute(rest, regs, upvals, state) + do_execute(rest, regs, upvals, proto, state) end # set_global - defp do_execute([{:set_global, name, source} | rest], regs, upvals, state) do + defp do_execute([{:set_global, name, source} | rest], regs, upvals, proto, state) do value = elem(regs, source) state = %{state | globals: Map.put(state.globals, name, value)} - do_execute(rest, regs, upvals, state) + do_execute(rest, regs, upvals, proto, state) end # move - defp do_execute([{:move, dest, source} | rest], regs, upvals, state) do + defp do_execute([{:move, dest, source} | rest], regs, upvals, proto, state) do value = elem(regs, source) regs = put_elem(regs, dest, value) - do_execute(rest, regs, upvals, state) + do_execute(rest, regs, upvals, proto, state) end # test - conditional execution - defp do_execute([{:test, reg, then_body, else_body} | rest], regs, upvals, state) do + defp do_execute([{:test, reg, then_body, else_body} | rest], regs, upvals, proto, state) do body = if truthy?(elem(regs, reg)), do: then_body, else: else_body - do_execute(body ++ rest, regs, upvals, state) + do_execute(body ++ rest, regs, upvals, proto, state) end # test_and - short-circuit AND - defp do_execute([{:test_and, dest, source, rest_body} | rest], regs, upvals, state) do + defp do_execute([{:test_and, dest, source, rest_body} | rest], regs, upvals, proto, state) do value = elem(regs, source) if truthy?(value) do # Value is truthy, execute rest_body to compute final result - do_execute(rest_body ++ rest, regs, upvals, state) + do_execute(rest_body ++ rest, regs, upvals, proto, state) else # Value is falsy, store it in dest and continue regs = put_elem(regs, dest, value) - do_execute(rest, regs, upvals, state) + do_execute(rest, regs, upvals, proto, state) end end # test_or - short-circuit OR - defp do_execute([{:test_or, dest, source, rest_body} | rest], regs, upvals, state) do + defp do_execute([{:test_or, dest, source, rest_body} | rest], regs, upvals, proto, state) do value = elem(regs, source) if truthy?(value) do # Value is truthy, store it in dest and continue regs = put_elem(regs, dest, value) - do_execute(rest, regs, upvals, state) + do_execute(rest, regs, upvals, proto, state) else # Value is falsy, execute rest_body to compute final result - do_execute(rest_body ++ rest, regs, upvals, state) + do_execute(rest_body ++ rest, regs, upvals, proto, state) end end # while_loop - defp do_execute([{:while_loop, cond_body, test_reg, loop_body} | rest], regs, upvals, state) do + defp do_execute( + [{:while_loop, cond_body, test_reg, loop_body} | rest], + regs, + upvals, + proto, + state + ) do # Execute condition - {_results, regs, state} = do_execute(cond_body, regs, upvals, state) + {_results, regs, state} = do_execute(cond_body, regs, upvals, proto, state) # Check condition if truthy?(elem(regs, test_reg)) do # Execute body - {_results, regs, state} = do_execute(loop_body, regs, upvals, state) + {_results, regs, state} = do_execute(loop_body, regs, upvals, proto, state) # Loop again - do_execute([{:while_loop, cond_body, test_reg, loop_body} | rest], regs, upvals, state) + do_execute( + [{:while_loop, cond_body, test_reg, loop_body} | rest], + regs, + upvals, + proto, + state + ) else # Condition false, continue after loop - do_execute(rest, regs, upvals, state) + do_execute(rest, regs, upvals, proto, state) end end # repeat_loop - defp do_execute([{:repeat_loop, loop_body, cond_body, test_reg} | rest], regs, upvals, state) do + defp do_execute( + [{:repeat_loop, loop_body, cond_body, test_reg} | rest], + regs, + upvals, + proto, + state + ) do # Execute body - {_results, regs, state} = do_execute(loop_body, regs, upvals, state) + {_results, regs, state} = do_execute(loop_body, regs, upvals, proto, state) # Execute condition - {_results, regs, state} = do_execute(cond_body, regs, upvals, state) + {_results, regs, state} = do_execute(cond_body, regs, upvals, proto, state) # Check condition (repeat UNTIL condition is true) if truthy?(elem(regs, test_reg)) do # Condition true, exit loop - do_execute(rest, regs, upvals, state) + do_execute(rest, regs, upvals, proto, state) else # Condition false, loop again - do_execute([{:repeat_loop, loop_body, cond_body, test_reg} | rest], regs, upvals, state) + do_execute( + [{:repeat_loop, loop_body, cond_body, test_reg} | rest], + regs, + upvals, + proto, + state + ) end end # numeric_for - defp do_execute([{:numeric_for, base, loop_var, body} | rest], regs, upvals, state) do + defp do_execute([{:numeric_for, base, loop_var, body} | rest], regs, upvals, proto, state) do # Get internal loop state counter = elem(regs, base) limit = elem(regs, base + 1) @@ -145,22 +169,22 @@ defmodule Lua.VM.Executor do regs = put_elem(regs, loop_var, counter) # Execute body - {_results, regs, state} = do_execute(body, regs, upvals, state) + {_results, regs, state} = do_execute(body, regs, upvals, proto, state) # Increment counter new_counter = counter + step regs = put_elem(regs, base, new_counter) # Loop again - do_execute([{:numeric_for, base, loop_var, body} | rest], regs, upvals, state) + do_execute([{:numeric_for, base, loop_var, body} | rest], regs, upvals, proto, state) else # Loop finished - do_execute(rest, regs, upvals, state) + do_execute(rest, regs, upvals, proto, state) end end # return - defp do_execute([{:return, base, count} | _rest], regs, _upvals, state) do + defp do_execute([{:return, base, count} | _rest], regs, _upvals, _proto, state) do results = if count == 0 do [nil] @@ -172,136 +196,136 @@ defmodule Lua.VM.Executor do end # Arithmetic operations - defp do_execute([{:add, dest, a, b} | rest], regs, upvals, state) do + defp do_execute([{:add, dest, a, b} | rest], regs, upvals, proto, state) do result = elem(regs, a) + elem(regs, b) regs = put_elem(regs, dest, result) - do_execute(rest, regs, upvals, state) + do_execute(rest, regs, upvals, proto, state) end - defp do_execute([{:subtract, dest, a, b} | rest], regs, upvals, state) do + defp do_execute([{:subtract, dest, a, b} | rest], regs, upvals, proto, state) do result = elem(regs, a) - elem(regs, b) regs = put_elem(regs, dest, result) - do_execute(rest, regs, upvals, state) + do_execute(rest, regs, upvals, proto, state) end - defp do_execute([{:multiply, dest, a, b} | rest], regs, upvals, state) do + defp do_execute([{:multiply, dest, a, b} | rest], regs, upvals, proto, state) do result = elem(regs, a) * elem(regs, b) regs = put_elem(regs, dest, result) - do_execute(rest, regs, upvals, state) + do_execute(rest, regs, upvals, proto, state) end - defp do_execute([{:divide, dest, a, b} | rest], regs, upvals, state) do + defp do_execute([{:divide, dest, a, b} | rest], regs, upvals, proto, state) do result = elem(regs, a) / elem(regs, b) regs = put_elem(regs, dest, result) - do_execute(rest, regs, upvals, state) + do_execute(rest, regs, upvals, proto, state) end - defp do_execute([{:floor_divide, dest, a, b} | rest], regs, upvals, state) do + defp do_execute([{:floor_divide, dest, a, b} | rest], regs, upvals, proto, state) do result = div(trunc(elem(regs, a)), trunc(elem(regs, b))) regs = put_elem(regs, dest, result) - do_execute(rest, regs, upvals, state) + do_execute(rest, regs, upvals, proto, state) end - defp do_execute([{:modulo, dest, a, b} | rest], regs, upvals, state) do + defp do_execute([{:modulo, dest, a, b} | rest], regs, upvals, proto, state) do result = rem(elem(regs, a), elem(regs, b)) regs = put_elem(regs, dest, result) - do_execute(rest, regs, upvals, state) + do_execute(rest, regs, upvals, proto, state) end - defp do_execute([{:power, dest, a, b} | rest], regs, upvals, state) do + defp do_execute([{:power, dest, a, b} | rest], regs, upvals, proto, state) do result = :math.pow(elem(regs, a), elem(regs, b)) regs = put_elem(regs, dest, result) - do_execute(rest, regs, upvals, state) + do_execute(rest, regs, upvals, proto, state) end # Bitwise operations - defp do_execute([{:bitwise_and, dest, a, b} | rest], regs, upvals, state) do + defp do_execute([{:bitwise_and, dest, a, b} | rest], regs, upvals, proto, state) do result = Bitwise.band(trunc(elem(regs, a)), trunc(elem(regs, b))) regs = put_elem(regs, dest, result) - do_execute(rest, regs, upvals, state) + do_execute(rest, regs, upvals, proto, state) end - defp do_execute([{:bitwise_or, dest, a, b} | rest], regs, upvals, state) do + defp do_execute([{:bitwise_or, dest, a, b} | rest], regs, upvals, proto, state) do result = Bitwise.bor(trunc(elem(regs, a)), trunc(elem(regs, b))) regs = put_elem(regs, dest, result) - do_execute(rest, regs, upvals, state) + do_execute(rest, regs, upvals, proto, state) end - defp do_execute([{:bitwise_xor, dest, a, b} | rest], regs, upvals, state) do + defp do_execute([{:bitwise_xor, dest, a, b} | rest], regs, upvals, proto, state) do result = Bitwise.bxor(trunc(elem(regs, a)), trunc(elem(regs, b))) regs = put_elem(regs, dest, result) - do_execute(rest, regs, upvals, state) + do_execute(rest, regs, upvals, proto, state) end - defp do_execute([{:shift_left, dest, a, b} | rest], regs, upvals, state) do + defp do_execute([{:shift_left, dest, a, b} | rest], regs, upvals, proto, state) do result = Bitwise.bsl(trunc(elem(regs, a)), trunc(elem(regs, b))) regs = put_elem(regs, dest, result) - do_execute(rest, regs, upvals, state) + do_execute(rest, regs, upvals, proto, state) end - defp do_execute([{:shift_right, dest, a, b} | rest], regs, upvals, state) do + defp do_execute([{:shift_right, dest, a, b} | rest], regs, upvals, proto, state) do result = Bitwise.bsr(trunc(elem(regs, a)), trunc(elem(regs, b))) regs = put_elem(regs, dest, result) - do_execute(rest, regs, upvals, state) + do_execute(rest, regs, upvals, proto, state) end - defp do_execute([{:bitwise_not, dest, source} | rest], regs, upvals, state) do + defp do_execute([{:bitwise_not, dest, source} | rest], regs, upvals, proto, state) do result = Bitwise.bnot(trunc(elem(regs, source))) regs = put_elem(regs, dest, result) - do_execute(rest, regs, upvals, state) + do_execute(rest, regs, upvals, proto, state) end # Comparison operations - defp do_execute([{:equal, dest, a, b} | rest], regs, upvals, state) do + defp do_execute([{:equal, dest, a, b} | rest], regs, upvals, proto, state) do result = elem(regs, a) == elem(regs, b) regs = put_elem(regs, dest, result) - do_execute(rest, regs, upvals, state) + do_execute(rest, regs, upvals, proto, state) end - defp do_execute([{:less_than, dest, a, b} | rest], regs, upvals, state) do + defp do_execute([{:less_than, dest, a, b} | rest], regs, upvals, proto, state) do result = elem(regs, a) < elem(regs, b) regs = put_elem(regs, dest, result) - do_execute(rest, regs, upvals, state) + do_execute(rest, regs, upvals, proto, state) end - defp do_execute([{:less_equal, dest, a, b} | rest], regs, upvals, state) do + defp do_execute([{:less_equal, dest, a, b} | rest], regs, upvals, proto, state) do result = elem(regs, a) <= elem(regs, b) regs = put_elem(regs, dest, result) - do_execute(rest, regs, upvals, state) + do_execute(rest, regs, upvals, proto, state) end - defp do_execute([{:greater_than, dest, a, b} | rest], regs, upvals, state) do + defp do_execute([{:greater_than, dest, a, b} | rest], regs, upvals, proto, state) do result = elem(regs, a) > elem(regs, b) regs = put_elem(regs, dest, result) - do_execute(rest, regs, upvals, state) + do_execute(rest, regs, upvals, proto, state) end - defp do_execute([{:greater_equal, dest, a, b} | rest], regs, upvals, state) do + defp do_execute([{:greater_equal, dest, a, b} | rest], regs, upvals, proto, state) do result = elem(regs, a) >= elem(regs, b) regs = put_elem(regs, dest, result) - do_execute(rest, regs, upvals, state) + do_execute(rest, regs, upvals, proto, state) end - defp do_execute([{:not_equal, dest, a, b} | rest], regs, upvals, state) do + defp do_execute([{:not_equal, dest, a, b} | rest], regs, upvals, proto, state) do result = elem(regs, a) != elem(regs, b) regs = put_elem(regs, dest, result) - do_execute(rest, regs, upvals, state) + do_execute(rest, regs, upvals, proto, state) end # Unary operations - defp do_execute([{:negate, dest, source} | rest], regs, upvals, state) do + defp do_execute([{:negate, dest, source} | rest], regs, upvals, proto, state) do result = -elem(regs, source) regs = put_elem(regs, dest, result) - do_execute(rest, regs, upvals, state) + do_execute(rest, regs, upvals, proto, state) end - defp do_execute([{:not, dest, source} | rest], regs, upvals, state) do + defp do_execute([{:not, dest, source} | rest], regs, upvals, proto, state) do result = not truthy?(elem(regs, source)) regs = put_elem(regs, dest, result) - do_execute(rest, regs, upvals, state) + do_execute(rest, regs, upvals, proto, state) end - defp do_execute([{:length, dest, source} | rest], regs, upvals, state) do + defp do_execute([{:length, dest, source} | rest], regs, upvals, proto, state) do # For now, handle string length. Will add table/list length later value = elem(regs, source) @@ -313,11 +337,11 @@ defmodule Lua.VM.Executor do end regs = put_elem(regs, dest, result) - do_execute(rest, regs, upvals, state) + do_execute(rest, regs, upvals, proto, state) end # Catch-all for unimplemented instructions - defp do_execute([instr | _rest], _regs, _upvals, _state) do + defp do_execute([instr | _rest], _regs, _upvals, _proto, _state) do raise "Unimplemented instruction: #{inspect(instr)}" end