Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 78 additions & 4 deletions lib/lua/compiler/codegen.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,18 @@ 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: [],
# Reverse to maintain order
prototypes: Enum.reverse(ctx.prototypes),
upvalue_descriptors: [],
param_count: 0,
is_vararg: false,
max_registers: 0,
max_registers: func_scope.max_register,
source: source,
lines: {1, 1}
}
Expand Down Expand Up @@ -418,6 +419,79 @@ 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,
# For now, no nested-nested functions
prototypes: [],
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
Expand Down
65 changes: 65 additions & 0 deletions lib/lua/compiler/scope.ex
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,71 @@ 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
2 changes: 1 addition & 1 deletion lib/lua/vm.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading