diff --git a/src/glua.gleam b/src/glua.gleam index c475e1a..b9608b3 100644 --- a/src/glua.gleam +++ b/src/glua.gleam @@ -7,6 +7,7 @@ import gleam/dynamic/decode import gleam/int import gleam/list import gleam/option +import gleam/pair import gleam/result import gleam/string @@ -247,8 +248,12 @@ pub fn int(v: Int) -> Value @external(erlang, "glua_ffi", "coerce") pub fn float(v: Float) -> Value -@external(erlang, "glua_ffi", "coerce") -pub fn table(values: List(#(Value, Value))) -> Value +pub fn table(lua: Lua, values: List(#(Value, Value))) -> #(Lua, Value) { + do_table(values, lua) |> pair.swap +} + +@external(erlang, "luerl_heap", "alloc_table") +fn do_table(values: List(#(Value, Value)), lua: Lua) -> #(Value, Lua) pub fn table_decoder( keys_decoder: decode.Decoder(a), @@ -266,8 +271,7 @@ pub fn table_decoder( pub fn function( f: fn(Lua, List(dynamic.Dynamic)) -> #(Lua, List(Value)), ) -> Value { - // we need a little wrapper for functions to satisfy luerl's order of arguments and return value type - wrap_function(f) + do_function(f) } pub fn list(encoder: fn(a) -> Value, values: List(a)) -> List(Value) { @@ -292,10 +296,11 @@ pub fn list(encoder: fn(a) -> Value, values: List(a)) -> List(Value) { /// } /// /// let state = glua.new() +/// let #(state, userdata) = glua.userdata(state, User(name: "Jhon Doe", is_admin: False)) /// let assert Ok(state) = glua.set( /// state:, /// keys: ["a_user"], -/// value: glua.userdata(User(name: "Jhon Doe", is_admin: False)) +/// value: userdata /// ) /// /// let assert Ok(#(_, [result])) = glua.eval(state:, code: "return a_user", using: user_decoder) @@ -308,20 +313,25 @@ pub fn list(encoder: fn(a) -> Value, values: List(a)) -> List(Value) { /// } /// /// let state = glua.new() +/// let #(state, userdata) = glua.userdata(state, Person(name: "Lucy", email: "lucy@example.com")) /// let assert Ok(lua) = glua.set( /// state:, /// keys: ["lucy"], -/// value: glua.userdata(Person(name: "Lucy", email: "lucy@example.com")) +/// value: userdata /// ) /// /// let assert Error(glua.LuaRuntimeException(glua.IllegalIndex(_), _)) = /// glua.eval(state:, code: "return lucy.email", using: decode.string) /// ``` -@external(erlang, "glua_ffi", "coerce_userdata") -pub fn userdata(v: anything) -> Value +pub fn userdata(lua: Lua, v: anything) -> #(Lua, Value) { + do_userdata(v, lua) |> pair.swap +} + +@external(erlang, "luerl_heap", "alloc_userdata") +fn do_userdata(v: anything, lua: Lua) -> #(Value, Lua) @external(erlang, "glua_ffi", "wrap_fun") -fn wrap_function( +fn do_function( fun: fn(Lua, List(dynamic.Dynamic)) -> #(Lua, List(Value)), ) -> Value @@ -511,7 +521,7 @@ pub fn set( Ok(_) -> Ok(#(keys, lua)) Error(KeyNotFound(_)) -> { - let #(tbl, lua) = alloc_table([], lua) + let #(tbl, lua) = do_table([], lua) do_set(lua, keys, tbl) |> result.map(fn(lua) { #(keys, lua) }) } @@ -578,9 +588,6 @@ pub fn set_lua_paths( set(lua, ["package", "path"], paths) } -@external(erlang, "luerl_emul", "alloc_table") -fn alloc_table(content: List(a), lua: Lua) -> #(a, Lua) - @external(erlang, "glua_ffi", "get_table_keys_dec") fn do_get(lua: Lua, keys: List(String)) -> Result(dynamic.Dynamic, LuaError) diff --git a/src/glua_ffi.erl b/src/glua_ffi.erl index 497a1b9..7ef48f4 100644 --- a/src/glua_ffi.erl +++ b/src/glua_ffi.erl @@ -1,7 +1,7 @@ -module(glua_ffi). -import(luerl_lib, [lua_error/2]). --export([get_stacktrace/1, coerce/1, coerce_nil/0, coerce_userdata/1, wrap_fun/1, sandbox_fun/1, get_table_keys/2, get_table_keys_dec/2, +-export([get_stacktrace/1, coerce/1, coerce_nil/0, wrap_fun/1, sandbox_fun/1, get_table_keys/2, get_table_keys_dec/2, get_private/2, set_table_keys/3, load/2, load_file/2, eval/2, eval_dec/2, eval_file/2, eval_file_dec/2, eval_chunk/2, eval_chunk_dec/2, call_function/3, call_function_dec/3]). @@ -30,33 +30,6 @@ to_gleam(Value) -> {error, {unknown_error, nil}} end. -%% helper to determine if a value is encoded or not -%% borrowed from https://github.com/tv-labs/lua/blob/main/lib/lua/util.ex#L19-L35 -is_encoded(nil) -> - true; -is_encoded(true) -> - true; -is_encoded(false) -> - true; -is_encoded(Binary) when is_binary(Binary) -> - true; -is_encoded(N) when is_number(N) -> - true; -is_encoded({tref,_}) -> - true; -is_encoded({usrdef,_}) -> - true; -is_encoded({eref,_}) -> - true; -is_encoded({funref,_,_}) -> - true; -is_encoded({erl_func,_}) -> - true; -is_encoded({erl_mfa,_,_,_}) -> - true; -is_encoded(_) -> - false. - map_error({error, Errors, _}) -> {lua_compile_failure, lists:map(fun map_compile_error/1, Errors)}; map_error({lua_error, {illegal_index, Value, Index}, State}) -> @@ -190,18 +163,17 @@ coerce(X) -> coerce_nil() -> nil. -coerce_userdata(X) -> - {userdata, X}. - wrap_fun(Fun) -> - fun(Args, State) -> + {erl_func, fun(Args, State) -> Decoded = luerl:decode_list(Args, State), {NewState, Ret} = Fun(State, Decoded), - luerl:encode_list(Ret, NewState) - end. + {Ret, NewState} + end}. sandbox_fun(Msg) -> - fun(_, State) -> {error, map_error(lua_error({error_call, [Msg]}, State))} end. + {erl_func, fun(_, State) -> + {error, map_error(lua_error({error_call, [Msg]}, State))} + end}. get_table_keys(Lua, Keys) -> case luerl:get_table_keys(Keys, Lua) of @@ -224,11 +196,7 @@ get_table_keys_dec(Lua, Keys) -> end. set_table_keys(Lua, Keys, Value) -> - SetFun = case is_encoded(Value) of - true -> fun luerl:set_table_keys/3; - false -> fun luerl:set_table_keys_dec/3 - end, - to_gleam(SetFun(Keys, Value, Lua)). + to_gleam(luerl:set_table_keys(Keys, Value, Lua)). load(Lua, Code) -> to_gleam(luerl:load( @@ -264,12 +232,10 @@ eval_file_dec(Lua, Path) -> unicode:characters_to_list(Path), Lua)). call_function(Lua, Fun, Args) -> - {EncodedArgs, State} = luerl:encode_list(Args, Lua), - to_gleam(luerl:call(Fun, EncodedArgs, State)). + to_gleam(luerl:call(Fun, Args, Lua)). call_function_dec(Lua, Fun, Args) -> - {EncodedArgs, St1} = luerl:encode_list(Args, Lua), - case luerl:call(Fun, EncodedArgs, St1) of + case luerl:call(Fun, Args, Lua) of {ok, Ret, St2} -> Values = luerl:decode_list(Ret, St2), {ok, {St2, Values}}; diff --git a/test/glua_test.gleam b/test/glua_test.gleam index a7cce4e..b3dfeb8 100644 --- a/test/glua_test.gleam +++ b/test/glua_test.gleam @@ -21,11 +21,13 @@ pub fn get_table_test() { ] let cool_numbers = glua.function(fn(lua, _params) { - let table = + let #(lua, table) = glua.table( + lua, my_table - |> list.map(fn(pair) { #(glua.string(pair.0), glua.int(pair.1)) }), + |> list.map(fn(pair) { #(glua.string(pair.0), glua.int(pair.1)) }), ) + #(lua, [table]) }) @@ -119,17 +121,11 @@ pub fn new_sandboxed_test() { } pub fn encoding_and_decoding_nested_tables_test() { - let nested_table = [ - #( - glua.string("key"), - glua.table([ - #( - glua.int(1), - glua.table([#(glua.string("deeper_key"), glua.string("deeper_value"))]), - ), - ]), - ), - ] + let lua = glua.new() + let #(lua, tb1) = + glua.table(lua, [#(glua.string("deeper_key"), glua.string("deeper_value"))]) + let #(lua, tb2) = glua.table(lua, [#(glua.int(1), tb1)]) + let #(lua, tb3) = glua.table(lua, [#(glua.string("key"), tb2)]) let keys = ["my_nested_table"] @@ -141,9 +137,8 @@ pub fn encoding_and_decoding_nested_tables_test() { glua.table_decoder(decode.string, decode.string), ), ) - let tbl = glua.table(nested_table) - let assert Ok(lua) = glua.set(state: glua.new(), keys:, value: tbl) + let assert Ok(lua) = glua.set(state: lua, keys:, value: tb3) let assert Ok(result) = glua.get(state: lua, keys:, using: nested_table_decoder) @@ -157,22 +152,21 @@ pub type Userdata { pub fn userdata_test() { let lua = glua.new() - let userdata = Userdata("my-userdata", 1) + let #(lua, userdata) = glua.userdata(lua, Userdata("my-userdata", 1)) let userdata_decoder = { use foo <- decode.field(1, decode.string) use bar <- decode.field(2, decode.int) decode.success(Userdata(foo:, bar:)) } - let assert Ok(lua) = glua.set(lua, ["my_userdata"], glua.userdata(userdata)) + let assert Ok(lua) = glua.set(lua, ["my_userdata"], userdata) let assert Ok(#(lua, [result])) = glua.eval(lua, "return my_userdata", userdata_decoder) - assert result == userdata + assert result == Userdata("my-userdata", 1) - let userdata = Userdata("other_userdata", 2) - let assert Ok(lua) = - glua.set(lua, ["my_other_userdata"], glua.userdata(userdata)) + let #(lua, userdata) = glua.userdata(lua, Userdata("other-userdata", 2)) + let assert Ok(lua) = glua.set(lua, ["my_other_userdata"], userdata) let assert Error(glua.LuaRuntimeException(glua.IllegalIndex(index, _), _)) = glua.eval(lua, "return my_other_userdata.foo", decode.string) @@ -236,8 +230,9 @@ pub fn set_test() { let keys = ["math", "squares"] - let encoded = + let #(lua, encoded) = glua.table( + lua, numbers |> list.map(fn(pair) { #(glua.int(pair.0), glua.int(pair.1)) }), ) let assert Ok(lua) = glua.set(lua, keys, encoded) @@ -260,8 +255,9 @@ pub fn set_test() { let encoded = glua.function(count_odd) let assert Ok(lua) = glua.set(glua.new(), ["count_odd"], encoded) - let arg = + let #(lua, arg) = glua.table( + lua, list.index_map(list.range(1, 10), fn(i, n) { #(glua.int(i + 1), glua.int(n)) }), @@ -277,8 +273,8 @@ pub fn set_test() { assert result == 5 - let tbl = - glua.table([ + let #(lua, tbl) = + glua.table(lua, [ #( glua.string("is_even"), glua.function(fn(lua, args) {