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
40 changes: 40 additions & 0 deletions lib/lua/vm/stdlib.ex
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ defmodule Lua.VM.Stdlib do
|> State.register_function("setmetatable", &lua_setmetatable/2)
|> State.register_function("getmetatable", &lua_getmetatable/2)
|> State.register_function("select", &lua_select/2)
|> State.register_function("load", &lua_load/2)
|> State.register_function("require", &lua_require/2)
|> install_package_table()
|> Lua.VM.Stdlib.String.install()
Expand Down Expand Up @@ -332,6 +333,45 @@ defmodule Lua.VM.Stdlib do
raise ArgumentError.value_expected("select", 1)
end

# load(chunk, chunkname, mode, env) — loads a Lua chunk
# chunk can be a string
# Returns compiled function or (nil, error message)
defp lua_load([chunk | _rest], state) when is_binary(chunk) do
case Lua.Parser.parse(chunk) do
{:ok, ast} ->
case Lua.Compiler.compile(ast) do
{:ok, prototype} ->
# Create a closure from the compiled prototype
closure = {:lua_closure, prototype, %{}}
{[closure], state}

{:error, reason} ->
error_msg = format_compile_error(reason)
{[nil, error_msg], state}
end

{:error, reason} ->
error_msg = format_parse_error(reason)
{[nil, error_msg], state}
end
end

defp lua_load([non_string | _], state) when not is_binary(non_string) do
# For now, only support string chunks
# TODO: Support function chunks and reader functions
{[nil, "load only supports string chunks currently"], state}
end

defp lua_load([], _state) do
raise ArgumentError.value_expected("load", 1)
end

defp format_parse_error(error) when is_binary(error), do: error
defp format_parse_error(error), do: inspect(error)

defp format_compile_error(error) when is_binary(error), do: error
defp format_compile_error(error), do: inspect(error)

# setmetatable(table, metatable) — sets the metatable for a table
defp lua_setmetatable([{:tref, _} = tref, metatable], state) do
# Validate metatable is nil or a table
Expand Down
45 changes: 45 additions & 0 deletions test/lua_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -1557,6 +1557,51 @@ defmodule LuaTest do
end
end

describe "load function" do
setup do
%{lua: Lua.new(sandboxed: [])}
end

test "load compiles and returns a function", %{lua: lua} do
code = """
f = load("return 1 + 2")
return f()
"""

assert {[3], _} = Lua.eval!(lua, code)
end

test "load with syntax error returns nil", %{lua: lua} do
# Note: Multi-assignment and table constructors don't capture multiple return values yet
# So we just test that load returns nil on error
code = """
f = load("return 1 +")
return f == nil
"""

assert {[true], _} = Lua.eval!(lua, code)
end

test "loaded function can access upvalues", %{lua: lua} do
code = """
x = 10
f = load("return x + 5")
return f()
"""

assert {[15], _} = Lua.eval!(lua, code)
end

test "load can compile complex code", %{lua: lua} do
code = """
f = load("function add(a, b) return a + b end; return add(3, 4)")
return f()
"""

assert {[7], _} = Lua.eval!(lua, code)
end
end

defp test_file(name) do
Path.join(["test", "fixtures", name])
end
Expand Down
Loading