diff --git a/src/glua.gleam b/src/glua.gleam index b9608b3..fd9cdb9 100644 --- a/src/glua.gleam +++ b/src/glua.gleam @@ -69,7 +69,6 @@ pub type LuaRuntimeExceptionKind { /// let assert Error(e) = glua.eval( /// state: glua.new(), /// code: "if true end", -/// using: decode.string /// ) /// /// glua.format_error(e) @@ -80,7 +79,6 @@ pub type LuaRuntimeExceptionKind { /// let assert Error(e) = glua.eval( /// state: glua.new(), /// code: "local a = 1; local b = true; return a + b", -/// using: decode.string /// ) /// /// glua.format_error(e) @@ -91,7 +89,6 @@ pub type LuaRuntimeExceptionKind { /// let assert Error(e) = glua.get( /// state: glua.new(), /// keys: ["a_value"], -/// using: decode.string /// ) /// /// glua.format_error(e) @@ -102,7 +99,6 @@ pub type LuaRuntimeExceptionKind { /// let assert Error(e) = glua.eval_file( /// state: glua.new(), /// path: "my_lua_file.lua", -/// using: decode.string /// ) /// /// glua.format_error(e) @@ -110,11 +106,11 @@ pub type LuaRuntimeExceptionKind { /// ``` /// /// ```gleam -/// let assert Error(e) = glua.eval( +/// let assert Ok(#(state, [ref])) = glua.eval( /// state: glua.new(), /// code: "return 1 + 1", -/// using: decode.string /// ) +/// let assert Error(e) = glua.deference(state:, ref:, using: decode.string) /// /// glua.format_error(e) /// // -> "Expected String, but found Int" @@ -227,12 +223,6 @@ pub type Chunk /// Represents a value that can be passed to the Lua environment. pub type Value -/// Represents a reference to a value inside the Lua environment. -/// -/// Each one of the functions that returns values from the Lua environment has a `ref_` counterpart -/// that will return references to the values instead of decoding them. -pub type ValueRef - @external(erlang, "glua_ffi", "coerce_nil") pub fn nil() -> Value @@ -303,8 +293,9 @@ pub fn list(encoder: fn(a) -> Value, values: List(a)) -> List(Value) { /// value: userdata /// ) /// -/// let assert Ok(#(_, [result])) = glua.eval(state:, code: "return a_user", using: user_decoder) -/// assert result == User("Jhon Doe", False) +/// let assert Ok(#(state, [ref])) = glua.eval(state:, code: "return a_user") +/// glua.deference(state:, ref:, using: user_decoder) +/// // -> Ok(User("Jhon Doe", False)) /// ``` /// /// ```gleam @@ -321,7 +312,7 @@ pub fn list(encoder: fn(a) -> Value, values: List(a)) -> List(Value) { /// ) /// /// let assert Error(glua.LuaRuntimeException(glua.IllegalIndex(_), _)) = -/// glua.eval(state:, code: "return lucy.email", using: decode.string) +/// glua.eval(state:, code: "return lucy.email") /// ``` pub fn userdata(lua: Lua, v: anything) -> #(Lua, Value) { do_userdata(v, lua) |> pair.swap @@ -335,6 +326,40 @@ fn do_function( fun: fn(Lua, List(dynamic.Dynamic)) -> #(Lua, List(Value)), ) -> Value +/// Converts a reference to a Lua value into type-safe Gleam data using the provided decoder. +/// +/// ## Examples +/// +/// ```gleam +/// let assert Ok(#(state, [ref])) = glua.eval( +/// state: glua.new(), +/// code: "return 'Hello from Lua!'" +/// ) +/// glua.deference(state:, ref:, using: decode.string) +/// // -> Ok("Hello from Lua!") +/// ``` +/// +/// ```gleam +/// let assert Ok(#(state, [ref1, ref2])) = glua.eval( +/// state: glua.new(), +/// code: "return 1, true" +/// ) +/// assert glua.deference(state:, ref: ref1, using: decode.int) == Ok(1) +/// assert glua.deference(state:, ref: ref2, using: decode.bool) == Ok(True) +/// ``` +pub fn deference( + state lua: Lua, + ref ref: Value, + using decoder: decode.Decoder(a), +) -> Result(a, LuaError) { + do_deference(lua, ref) + |> decode.run(decoder) + |> result.map_error(UnexpectedResultType) +} + +@external(erlang, "glua_ffi", "deference") +fn do_deference(lua: Lua, ref: Value) -> dynamic.Dynamic + /// Creates a new Lua VM instance @external(erlang, "luerl", "init") pub fn new() -> Lua @@ -383,7 +408,6 @@ fn list_substraction(a: List(a), b: List(a)) -> List(a) /// let assert Error(glua.LuaRuntimeException(exception, _)) = glua.eval( /// state: lua, /// code: "os.execute(\"rm -f important_file\"); return 0", -/// using: decode.int /// ) /// // 'important_file' was not deleted /// assert exception == glua.ErrorCall(["os.execute is sandboxed"]) @@ -401,43 +425,30 @@ fn sandbox_fun(msg: String) -> Value /// ## Examples /// /// ```gleam -/// glua.get(state: glua.new(), keys: ["_VERSION"], using: decode.string) +/// let state = glua.new() +/// let assert Ok(ref) = glua.get(state:, keys: ["_VERSION"]) +/// glua.deference(state:, ref:, using: decode.string) /// // -> Ok("Lua 5.3") /// ``` /// /// ```gleam -/// let #(lua, encoded) = glua.new() |> glua.bool(True) -/// let assert Ok(lua) = glua.set( -/// state: lua, +/// let assert Ok(state) = glua.set( +/// state: glua.new(), /// keys: ["my_table", "my_value"], -/// value: encoded +/// value: glua.bool(True) /// ) /// -/// glua.get( -/// state: lua, -/// keys: ["my_table", "my_value"], -/// using: decode.bool -/// ) +/// let assert Ok(ref) = glua.get(state:, keys: ["my_table", "my_value"]) +/// glua.deference(state:, ref:, using: decode.bool) /// // -> Ok(True) /// ``` /// /// ```gleam -/// glua.get(state: glua.new(), keys: ["non_existent"], using: decode.string) +/// glua.get(state: glua.new(), keys: ["non_existent"]) /// // -> Error(glua.KeyNotFound(["non_existent"])) /// ``` -pub fn get( - state lua: Lua, - keys keys: List(String), - using decoder: decode.Decoder(a), -) -> Result(a, LuaError) { - use value <- result.try(do_get(lua, keys)) - - use decoded <- result.try( - decode.run(value, decoder) |> result.map_error(UnexpectedResultType), - ) - - Ok(decoded) -} +@external(erlang, "glua_ffi", "get_table_keys") +pub fn get(state lua: Lua, keys keys: List(String)) -> Result(Value, LuaError) /// Gets a private value that is not exposed to the Lua runtime. /// @@ -455,20 +466,11 @@ pub fn get_private( using decoder: decode.Decoder(a), ) -> Result(a, LuaError) { use value <- result.try(do_get_private(lua, key)) - use decoded <- result.try( - decode.run(value, decoder) |> result.map_error(UnexpectedResultType), - ) - - Ok(decoded) + decode.run(value, decoder) |> result.map_error(UnexpectedResultType) } -/// Same as `glua.get`, but returns a reference to the value instead of decoding it -pub fn ref_get( - state lua: Lua, - keys keys: List(String), -) -> Result(ValueRef, LuaError) { - do_ref_get(lua, keys) -} +@external(erlang, "glua_ffi", "get_private") +fn do_get_private(lua: Lua, key: String) -> Result(dynamic.Dynamic, LuaError) /// Sets a value in the Lua environment. /// @@ -480,31 +482,33 @@ pub fn ref_get( /// ## Examples /// /// ```gleam -/// let #(lua, encoded) = glua.new() |> glua.int(10) -/// let assert Ok(lua) = glua.set( -/// state: lua, +/// let assert Ok(state) = glua.set( +/// state: glua.new(), /// keys: ["my_number"], -/// value: encoded +/// value: glua.int(10) /// ) /// -/// glua.get(state: lua, keys: ["my_number"], using: decode.int) +/// let assert Ok(ref) = glua.get(state: lua, keys: ["my_number"]) +/// glua.deference(state:, ref:, using: decode.int) /// // -> Ok(10) /// ``` /// /// ```gleam /// let emails = ["jhondoe@example.com", "lucy@example.com"] -/// let #(lua, encoded) = glua.new() |> glua.list(glua.string, emails) -/// let assert Ok(lua) = glua.set( -/// state: lua, +/// let #(state, encoded) = glua.table( +/// glua.new(), +/// list.index_map(emails, fn(email, i) { #(glua.int(i + 1), glua.string(email)) }) +/// ) +/// let assert Ok(state) = glua.set( +/// state:, /// keys: ["info", "emails"], /// value: encoded /// ) /// -/// let assert Ok(#(_, results)) = glua.eval( -/// state: lua, -/// code: "return info.emails", -/// using: decode.string -/// ) +/// let assert Ok(#(state, [ref])) = glua.eval(state:, code: "return info.emails") +/// let assert Ok(results) = +/// glua.deference(state:, ref:, using: decode.dict(decode.int, decode.string)) +/// |> result.map(dict.values) /// /// assert results == emails /// ``` @@ -517,7 +521,7 @@ pub fn set( use acc, key <- list.try_fold(keys, #([], lua)) let #(keys, lua) = acc let keys = list.append(keys, [key]) - case do_ref_get(lua, keys) { + case get(lua, keys) { Ok(_) -> Ok(#(keys, lua)) Error(KeyNotFound(_)) -> { @@ -572,13 +576,12 @@ pub fn set_api( /// paths: my_scripts_paths /// ) /// -/// let assert Ok(#(_, [result])) = glua.eval( +/// let assert Ok(#(state, [ref])) = glua.eval( /// state:, /// code: "local my_math = require 'my_script'; return my_math.square(3)" -/// using: decode.int /// ) -/// -/// assert result = 9 +/// glua.deference(state:, ref:, using: decode.int) +/// // -> Ok(9) /// ``` pub fn set_lua_paths( state lua: Lua, @@ -588,15 +591,6 @@ pub fn set_lua_paths( set(lua, ["package", "path"], paths) } -@external(erlang, "glua_ffi", "get_table_keys_dec") -fn do_get(lua: Lua, keys: List(String)) -> Result(dynamic.Dynamic, LuaError) - -@external(erlang, "glua_ffi", "get_private") -fn do_get_private(lua: Lua, key: String) -> Result(dynamic.Dynamic, LuaError) - -@external(erlang, "glua_ffi", "get_table_keys") -fn do_ref_get(lua: Lua, keys: List(String)) -> Result(ValueRef, LuaError) - @external(erlang, "glua_ffi", "set_table_keys") fn do_set(lua: Lua, keys: List(String), val: a) -> Result(Lua, LuaError) @@ -624,107 +618,60 @@ fn do_delete_private(key: String, lua: Lua) -> Lua /// Parses a string of Lua code and returns it as a compiled chunk. /// -/// To eval the returned chunk, use `glua.eval_chunk` or `glua.ref_eval_chunk`. +/// To eval the returned chunk, use `glua.eval_chunk`. +@external(erlang, "glua_ffi", "load") pub fn load( state lua: Lua, code code: String, -) -> Result(#(Lua, Chunk), LuaError) { - do_load(lua, code) -} - -@external(erlang, "glua_ffi", "load") -fn do_load(lua: Lua, code: String) -> Result(#(Lua, Chunk), LuaError) +) -> Result(#(Lua, Chunk), LuaError) /// Parses a Lua source file and returns it as a compiled chunk. /// -/// To eval the returned chunk, use `glua.eval_chunk` or `glua.ref_eval_chunk`. +/// To eval the returned chunk, use `glua.eval_chunk`. +@external(erlang, "glua_ffi", "load_file") pub fn load_file( state lua: Lua, path path: String, -) -> Result(#(Lua, Chunk), LuaError) { - do_load_file(lua, path) -} - -@external(erlang, "glua_ffi", "load_file") -fn do_load_file(lua: Lua, path: String) -> Result(#(Lua, Chunk), LuaError) +) -> Result(#(Lua, Chunk), LuaError) /// Evaluates a string of Lua code. /// /// ## Examples /// /// ```gleam -/// let assert Ok(#(_, results)) = glua.eval( +/// let assert Ok(#(state, [ref])) = glua.eval( /// state: glua.new(), /// code: "return 1 + 2", -/// using: decode.int /// ) -/// assert results == [3] +/// glua.deference(state:, ref:, using: decode.int) +/// // -> Ok(3) /// ``` /// /// ```gleam -/// let my_decoder = decode.one_of(decode.string, or: [ -/// decode.int |> decode.map(int.to_string) -/// ]) -/// -/// let assert Ok(#(_, results)) = glua.eval( +/// let assert Ok(#(state, [ref1, ref2])) = glua.eval( /// state: glua.new(), /// code: "return 'hello, world!', 10", -/// using: my_decoder /// ) -/// assert results == ["hello, world!", "10"] +/// assert glua.deference(state:, ref: ref1, using: decode.string) == "hello, world!" +/// assert glua.deference(state:, ref: ref2, using: decode.int) == 10 /// ``` /// /// ```gleam -/// glua.eval(state: glua.new(), code: "return 1 * ", using: decode.int) +/// glua.eval(state: glua.new(), code: "return 1 * ") /// // -> Error(glua.LuaCompilerException( /// messages: ["syntax error before: ", "1"] /// )) /// ``` /// -/// ```gleam -/// glua.eval(state: glua.new(), code: "return 'Hello, world!'", using: decode.int) -/// // -> Error(glua.UnexpectedResultType( -/// [decode.DecodeError("Int", "String", [])] -/// )) -/// ``` -/// /// > **Note**: If you are evaluating the same piece of code multiple times, /// > instead of calling `glua.eval` repeatly it is recommended to first convert /// > the code to a chunk by passing it to `glua.load`, and then -/// > evaluate that chunk using `glua.eval_chunk` or `glua.ref_eval_chunk`. +/// > evaluate that chunk using `glua.eval_chunk`. +@external(erlang, "glua_ffi", "eval") pub fn eval( state lua: Lua, code code: String, - using decoder: decode.Decoder(a), -) -> Result(#(Lua, List(a)), LuaError) { - use #(lua, ret) <- result.try(do_eval(lua, code)) - use decoded <- result.try( - list.try_map(ret, decode.run(_, decoder)) - |> result.map_error(UnexpectedResultType), - ) - - Ok(#(lua, decoded)) -} - -@external(erlang, "glua_ffi", "eval_dec") -fn do_eval( - lua: Lua, - code: String, -) -> Result(#(Lua, List(dynamic.Dynamic)), LuaError) - -/// Same as `glua.eval`, but returns references to the values instead of decode them -pub fn ref_eval( - state lua: Lua, - code code: String, -) -> Result(#(Lua, List(ValueRef)), LuaError) { - do_ref_eval(lua, code) -} - -@external(erlang, "glua_ffi", "eval") -fn do_ref_eval( - lua: Lua, - code: String, -) -> Result(#(Lua, List(ValueRef)), LuaError) +) -> Result(#(Lua, List(Value)), LuaError) /// Evaluates a compiled chunk of Lua code. /// @@ -735,118 +682,56 @@ fn do_ref_eval( /// code: "return 'hello, world!'" /// ) /// -/// let assert Ok(#(_, results)) = glua.eval_chunk( +/// let assert Ok(#(state, [ref])) = glua.eval_chunk( /// state: lua, /// chunk:, -/// using: decode.string /// ) -/// -/// assert results == ["hello, world!"] +/// glua.deference(state:, ref:, using: decode.string) +/// // -> Ok("hello, world!") /// ``` +@external(erlang, "glua_ffi", "eval_chunk") pub fn eval_chunk( state lua: Lua, chunk chunk: Chunk, - using decoder: decode.Decoder(a), -) -> Result(#(Lua, List(a)), LuaError) { - use #(lua, ret) <- result.try(do_eval_chunk(lua, chunk)) - use decoded <- result.try( - list.try_map(ret, decode.run(_, decoder)) - |> result.map_error(UnexpectedResultType), - ) - - Ok(#(lua, decoded)) -} - -@external(erlang, "glua_ffi", "eval_chunk_dec") -fn do_eval_chunk( - lua: Lua, - chunk: Chunk, -) -> Result(#(Lua, List(dynamic.Dynamic)), LuaError) - -/// Same as `glua.eval_chunk`, but returns references to the values instead of decode them -pub fn ref_eval_chunk( - state lua: Lua, - chunk chunk: Chunk, -) -> Result(#(Lua, List(ValueRef)), LuaError) { - do_ref_eval_chunk(lua, chunk) -} - -@external(erlang, "glua_ffi", "eval_chunk") -fn do_ref_eval_chunk( - lua: Lua, - chunk: Chunk, -) -> Result(#(Lua, List(ValueRef)), LuaError) +) -> Result(#(Lua, List(Value)), LuaError) /// Evaluates a Lua source file. /// /// ## Examples /// ```gleam -/// let assert Ok(#(_, results)) = glua.eval_file( +/// let assert Ok(#(state, [ref])) = glua.eval_file( /// state: glua.new(), /// path: "path/to/hello.lua", -/// using: decode.string /// ) -/// -/// assert results == ["hello, world!"] +/// glua.deference(state:, ref:, using: decode.string) +/// Ok("hello, world!") /// ``` /// /// ```gleam /// glua.eval_file( /// state: glua.new(), /// path: "path/to/non/existent/file", -/// using: decode.string /// ) /// //-> Error(glua.FileNotFound(["path/to/non/existent/file"])) /// ``` +@external(erlang, "glua_ffi", "eval_file") pub fn eval_file( state lua: Lua, path path: String, - using decoder: decode.Decoder(a), -) -> Result(#(Lua, List(a)), LuaError) { - use #(lua, ret) <- result.try(do_eval_file(lua, path)) - use decoded <- result.try( - list.try_map(ret, decode.run(_, decoder)) - |> result.map_error(UnexpectedResultType), - ) - - Ok(#(lua, decoded)) -} - -@external(erlang, "glua_ffi", "eval_file_dec") -fn do_eval_file( - lua: Lua, - path: String, -) -> Result(#(Lua, List(dynamic.Dynamic)), LuaError) - -/// Same as `glua.eval_file`, but returns references to the values instead of decode them. -pub fn ref_eval_file( - state lua: Lua, - path path: String, -) -> Result(#(Lua, List(ValueRef)), LuaError) { - do_ref_eval_file(lua, path) -} - -@external(erlang, "glua_ffi", "eval_file") -fn do_ref_eval_file( - lua: Lua, - path: String, -) -> Result(#(Lua, List(ValueRef)), LuaError) +) -> Result(#(Lua, List(Value)), LuaError) /// Calls a Lua function by reference. /// /// ## Examples /// ```gleam -/// let assert Ok(#(lua, fun)) = glua.ref_eval(state: glua.new(), code: "return math.sqrt") -/// -/// let #(lua, encoded) = glua.int(lua, 81) -/// let assert Ok(#(_, [result])) = glua.call_function( -/// state: lua, +/// let assert Ok(#(state, [fun])) = glua.eval(state: glua.new(), code: "return math.sqrt") +/// let assert Ok(#(state, [ref])) = glua.call_function( +/// state:, /// ref: fun, -/// args: [encoded], -/// using: decode.int +/// args: [glua.int(81)], /// ) -/// -/// assert result == 9 +/// glua.deference(state:, ref:, using: decode.int) +/// // -> Ok(9) /// ``` /// /// ```gleam @@ -860,89 +745,42 @@ fn do_ref_eval_file( /// /// return fib /// " -/// let assert Ok(#(lua, fun)) = glua.ref_eval(state: glua.new(), code:) -/// -/// let #(lua, encoded) = glua.int(lua, 10) -/// let assert Ok(#(_, [result])) = glua.call_function( +/// let assert Ok(#(state, [fun])) = glua.eval(state: glua.new(), code:) +/// let assert Ok(#(state), [ref])) = glua.call_function( /// state: lua, /// ref: fun, -/// args: [encoded], -/// using: decode.int +/// args: [glua.int(10)], /// ) -/// -/// assert result == 55 +/// glua.deference(state:, ref:, using: decode.int) +/// // -> Ok(55) /// ``` +@external(erlang, "glua_ffi", "call_function") pub fn call_function( state lua: Lua, - ref fun: ValueRef, + ref fun: Value, args args: List(Value), - using decoder: decode.Decoder(a), -) -> Result(#(Lua, List(a)), LuaError) { - use #(lua, ret) <- result.try(do_call_function(lua, fun, args)) - use decoded <- result.try( - list.try_map(ret, decode.run(_, decoder)) - |> result.map_error(UnexpectedResultType), - ) - - Ok(#(lua, decoded)) -} - -@external(erlang, "glua_ffi", "call_function_dec") -fn do_call_function( - lua: Lua, - fun: ValueRef, - args: List(Value), -) -> Result(#(Lua, List(dynamic.Dynamic)), LuaError) - -/// Same as `glua.call_function`, but returns references to the values instead of decode them. -pub fn ref_call_function( - state lua: Lua, - ref fun: ValueRef, - args args: List(Value), -) -> Result(#(Lua, List(ValueRef)), LuaError) { - do_ref_call_function(lua, fun, args) -} - -@external(erlang, "glua_ffi", "call_function") -fn do_ref_call_function( - lua: Lua, - fun: ValueRef, - args: List(Value), -) -> Result(#(Lua, List(ValueRef)), LuaError) +) -> Result(#(Lua, List(Value)), LuaError) /// Gets a reference to the function at `keys`, then inmediatly calls it with the provided `args`. /// -/// This is a shorthand for `glua.ref_get` followed by `glua.call_function`. +/// This is a shorthand for `glua.get` followed by `glua.call_function`. /// /// ## Examples /// /// ```gleam -/// let #(lua, encoded) = glua.new() |> glua.string("hello from gleam!") -/// let assert Ok(#(_, [s])) = glua.call_function_by_name( -/// state: lua, +/// let assert Ok(#(state, [ref])) = glua.call_function_by_name( +/// state: glua.new(), /// keys: ["string", "upper"], -/// args: [encoded], -/// using: decode.string +/// args: [glua.string("hello from Gleam!")], /// ) -/// -/// assert s == "HELLO FROM GLEAM!" +/// glua.deference(state:, ref:, using: decode.string) +/// // -> Ok(HELLO FROM GLEAM!") /// ``` pub fn call_function_by_name( state lua: Lua, keys keys: List(String), args args: List(Value), - using decoder: decode.Decoder(a), -) -> Result(#(Lua, List(a)), LuaError) { - use fun <- result.try(ref_get(lua, keys)) - call_function(lua, fun, args, decoder) -} - -/// Same as `glua.call_function_by_name`, but it chains `glua.ref_get` with `glua.ref_call_function` instead of `glua.call_function` -pub fn ref_call_function_by_name( - state lua: Lua, - keys keys: List(String), - args args: List(Value), -) -> Result(#(Lua, List(ValueRef)), LuaError) { - use fun <- result.try(ref_get(lua, keys)) - ref_call_function(lua, fun, args) +) -> Result(#(Lua, List(Value)), LuaError) { + use fun <- result.try(get(lua, keys)) + call_function(lua, fun, args) } diff --git a/src/glua_ffi.erl b/src/glua_ffi.erl index 7ef48f4..ce102c1 100644 --- a/src/glua_ffi.erl +++ b/src/glua_ffi.erl @@ -1,27 +1,20 @@ -module(glua_ffi). -import(luerl_lib, [lua_error/2]). +-import(ttdict, [fold/3]). +-include_lib("luerl/include/luerl.hrl"). --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]). - -%% turn `{userdata, Data}` into `Data` to make it more easy to decode it in Gleam -maybe_process_userdata(Lst) when is_list(Lst) -> - lists:map(fun maybe_process_userdata/1, Lst); -maybe_process_userdata({userdata, Data}) -> - Data; -maybe_process_userdata(Other) -> - Other. +-export([get_stacktrace/1, deference/2, coerce/1, coerce_nil/0, wrap_fun/1, sandbox_fun/1, get_table_keys/2, + get_private/2, set_table_keys/3, load/2, load_file/2, eval/2, eval_file/2, + eval_chunk/2, call_function/3]). %% helper to convert luerl return values to a format %% that is more suitable for use in Gleam code to_gleam(Value) -> case Value of {ok, Result, LuaState} -> - Values = maybe_process_userdata(Result), - {ok, {LuaState, Values}}; + {ok, {LuaState, Result}}; {ok, _} = Result -> - maybe_process_userdata(Result); + Result; {lua_error, _, _} = Error -> {error, map_error(Error)}; {error, _, _} = Error -> @@ -30,6 +23,65 @@ to_gleam(Value) -> {error, {unknown_error, nil}} end. +deference_list(St, LuerlTerms) -> + lists:map(fun (Lt) -> deference(St, Lt) end, LuerlTerms). + +%% transforms Lua values to their corresponding Erlang representation +%% this is similar to `luerl:decode/2`, but returns values that are more decode-friendly in Gleam +deference(St, LT) -> + deference(LT, St, []). + +deference(nil, _, _) -> nil; +deference(false, _, _) -> false; +deference(true, _, _) -> true; +deference(B, _, _) when is_binary(B) -> B; +deference(N, _, _) when is_number(N) -> N; %Integers and floats +deference(#tref{}=T, St, In) -> + deference_table(T, St, In); +deference(#usdref{}=U, St, _In) -> + {#userdata{d=Data},_} = luerl_heap:get_userdata(U, St), + Data; +deference(#funref{}=Fun, _St, _In) -> + deference_fun(Fun); +deference(#erl_func{code=Fun}, _St, _In) -> + Fun; %Just the bare fun +deference(#erl_mfa{m=M, f=F}, _St, _In) -> + deference_fun(fun(Args, St0) -> M:F(nil, Args, St0) end); +deference(Lua, _, _) -> error({badarg,Lua}). %Shouldn't have anything else + +deference_table(#tref{i=N}=T, St, In0) -> + case lists:member(N, In0) of + true -> + % Been here before + error({recursive_table, T}); + false -> + % We are in this as well + In1 = [N | In0], + case luerl_heap:get_table(T, St) of + #table{a = Arr, d = Dict} -> + Fun = fun(K, V, Acc) -> + Acc#{ + deference(K, St, In1) + => deference(V, St, In1) + } + end, + M0 = ttdict:fold(Fun, #{}, Dict), + array:sparse_foldr(Fun, M0, Arr); + _Undefined -> + error(badarg) + end + end. + +deference_fun(F) when is_function(F, 2) -> + {luafun, fun(St0, Args) -> + try + {Ret, St1} = F(Args, St0), + {ok, {St1, Ret}} + catch + error:{lua_error, _, _} = Err -> {error, map_error(Err)} + end + end}. + map_error({error, Errors, _}) -> {lua_compile_failure, lists:map(fun map_compile_error/1, Errors)}; map_error({lua_error, {illegal_index, Value, Index}, State}) -> @@ -165,8 +217,7 @@ coerce_nil() -> wrap_fun(Fun) -> {erl_func, fun(Args, State) -> - Decoded = luerl:decode_list(Args, State), - {NewState, Ret} = Fun(State, Decoded), + {NewState, Ret} = Fun(State, deference_list(State, Args)), {Ret, NewState} end}. @@ -185,16 +236,6 @@ get_table_keys(Lua, Keys) -> to_gleam(Other) end. -get_table_keys_dec(Lua, Keys) -> - case luerl:get_table_keys_dec(Keys, Lua) of - {ok, nil, _} -> - {error, {key_not_found, Keys}}; - {ok, Value, _} -> - {ok, Value}; - Other -> - to_gleam(Other) - end. - set_table_keys(Lua, Keys, Value) -> to_gleam(luerl:set_table_keys(Keys, Value, Lua)). @@ -213,36 +254,16 @@ eval(Lua, Code) -> to_gleam(luerl:do( unicode:characters_to_list(Code), Lua)). -eval_dec(Lua, Code) -> - to_gleam(luerl:do_dec( - unicode:characters_to_list(Code), Lua)). - eval_chunk(Lua, Chunk) -> to_gleam(luerl:call_chunk(Chunk, Lua)). -eval_chunk_dec(Lua, Chunk) -> - call_function_dec(Lua, Chunk, []). - eval_file(Lua, Path) -> to_gleam(luerl:dofile( unicode:characters_to_list(Path), Lua)). -eval_file_dec(Lua, Path) -> - to_gleam(luerl:dofile_dec( - unicode:characters_to_list(Path), Lua)). - call_function(Lua, Fun, Args) -> to_gleam(luerl:call(Fun, Args, Lua)). -call_function_dec(Lua, Fun, Args) -> - case luerl:call(Fun, Args, Lua) of - {ok, Ret, St2} -> - Values = luerl:decode_list(Ret, St2), - {ok, {St2, Values}}; - Other -> - to_gleam(Other) - end. - get_private(Lua, Key) -> try {ok, luerl:get_private(Key, Lua)} diff --git a/test/glua_test.gleam b/test/glua_test.gleam index b3dfeb8..9faf2e6 100644 --- a/test/glua_test.gleam +++ b/test/glua_test.gleam @@ -32,15 +32,16 @@ pub fn get_table_test() { }) let assert Ok(lua) = glua.set(lua, ["cool_numbers"], cool_numbers) - let assert Ok(#(_lua, [table])) = - glua.call_function_by_name( - lua, - ["cool_numbers"], - [], - using: glua.table_decoder(decode.string, decode.int), + let assert Ok(#(lua, [ref])) = + glua.call_function_by_name(lua, ["cool_numbers"], []) + let assert Ok(table) = + glua.deference( + state: lua, + ref:, + using: decode.dict(decode.string, decode.int), ) - assert dict.from_list(table) == dict.from_list(my_table) + assert table == dict.from_list(my_table) } pub fn sandbox_test() { @@ -48,30 +49,21 @@ pub fn sandbox_test() { let args = list.map([20, 10], glua.int) let assert Error(glua.LuaRuntimeException(exception, _)) = - glua.call_function_by_name( - state: lua, - keys: ["math", "max"], - args:, - using: decode.int, - ) + glua.call_function_by_name(state: lua, keys: ["math", "max"], args:) assert exception == glua.ErrorCall("math.max is sandboxed", option.None) let assert Ok(lua) = glua.sandbox(glua.new(), ["string"]) let assert Error(glua.LuaRuntimeException(glua.IllegalIndex(index, _), _)) = - glua.eval( - state: lua, - code: "return string.upper('my_string')", - using: decode.string, - ) + glua.eval(state: lua, code: "return string.upper('my_string')") assert index == "upper" let assert Ok(lua) = glua.sandbox(glua.new(), ["os", "execute"]) let assert Error(glua.LuaRuntimeException(exception, _)) = - glua.ref_eval( + glua.eval( state: lua, code: "os.execute(\"echo 'sandbox test is failing'\"); os.exit(1)", ) @@ -81,12 +73,7 @@ pub fn sandbox_test() { let assert Ok(lua) = glua.sandbox(glua.new(), ["print"]) let arg = glua.string("sandbox test is failing") let assert Error(glua.LuaRuntimeException(exception, _)) = - glua.call_function_by_name( - state: lua, - keys: ["print"], - args: [arg], - using: decode.string, - ) + glua.call_function_by_name(state: lua, keys: ["print"], args: [arg]) assert exception == glua.ErrorCall("print is sandboxed", option.None) } @@ -95,18 +82,18 @@ pub fn new_sandboxed_test() { let assert Ok(lua) = glua.new_sandboxed([]) let assert Error(glua.LuaRuntimeException(exception, _)) = - glua.ref_eval(state: lua, code: "return load(\"return 1\")") + glua.eval(state: lua, code: "return load(\"return 1\")") assert exception == glua.ErrorCall("load is sandboxed", option.None) let arg = glua.int(1) let assert Error(glua.LuaRuntimeException(exception, _)) = - glua.ref_call_function_by_name(state: lua, keys: ["os", "exit"], args: [arg]) + glua.call_function_by_name(state: lua, keys: ["os", "exit"], args: [arg]) assert exception == glua.ErrorCall("os.exit is sandboxed", option.None) let assert Error(glua.LuaRuntimeException(glua.IllegalIndex(index, _), _)) = - glua.ref_eval(state: lua, code: "io.write('some_message')") + glua.eval(state: lua, code: "io.write('some_message')") assert index == "write" @@ -114,8 +101,8 @@ pub fn new_sandboxed_test() { let assert Ok(lua) = glua.set_lua_paths(lua, paths: ["./test/lua/?.lua"]) let code = "local s = require 'example'; return s" - let assert Ok(#(_, [result])) = - glua.eval(state: lua, code:, using: decode.string) + let assert Ok(#(state, [ref])) = glua.eval(state: lua, code:) + let assert Ok(result) = glua.deference(state:, ref:, using: decode.string) assert result == "LUA IS AN EMBEDDABLE LANGUAGE" } @@ -130,20 +117,24 @@ pub fn encoding_and_decoding_nested_tables_test() { let keys = ["my_nested_table"] let nested_table_decoder = - glua.table_decoder( + decode.dict( decode.string, - glua.table_decoder( - decode.int, - glua.table_decoder(decode.string, decode.string), - ), + decode.dict(decode.int, decode.dict(decode.string, decode.string)), ) let assert Ok(lua) = glua.set(state: lua, keys:, value: tb3) + let assert Ok(ref) = glua.get(state: lua, keys:) let assert Ok(result) = - glua.get(state: lua, keys:, using: nested_table_decoder) + glua.deference(state: lua, ref:, using: nested_table_decoder) - assert result == [#("key", [#(1, [#("deeper_key", "deeper_value")])])] + assert result + == dict.from_list([ + #( + "key", + dict.from_list([#(1, dict.from_list([#("deeper_key", "deeper_value")]))]), + ), + ]) } pub type Userdata { @@ -160,15 +151,16 @@ pub fn userdata_test() { } let assert Ok(lua) = glua.set(lua, ["my_userdata"], userdata) - let assert Ok(#(lua, [result])) = - glua.eval(lua, "return my_userdata", userdata_decoder) + let assert Ok(#(lua, [ref])) = glua.eval(lua, "return my_userdata") + let assert Ok(result) = + glua.deference(state: lua, ref:, using: userdata_decoder) assert result == Userdata("my-userdata", 1) 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) + glua.eval(lua, "return my_other_userdata.foo") assert index == "foo" } @@ -176,15 +168,16 @@ pub fn userdata_test() { pub fn get_test() { let state = glua.new() - let assert Ok(pi) = - glua.get(state: state, keys: ["math", "pi"], using: decode.float) + let assert Ok(ref) = glua.get(state: state, keys: ["math", "pi"]) + let assert Ok(pi) = glua.deference(state:, ref:, using: decode.float) assert pi >. 3.14 && pi <. 3.15 let keys = ["my_table", "my_value"] let encoded = glua.bool(True) let assert Ok(state) = glua.set(state:, keys:, value: encoded) - let assert Ok(ret) = glua.get(state:, keys:, using: decode.bool) + let assert Ok(ref) = glua.get(state:, keys:) + let assert Ok(ret) = glua.deference(state:, ref:, using: decode.bool) assert ret == True @@ -193,9 +186,9 @@ pub fn get_test() { my_value = 10 return 'ignored' " - let assert Ok(#(state, _)) = - glua.new() |> glua.eval(code:, using: decode.string) - let assert Ok(ret) = glua.get(state:, keys: ["my_value"], using: decode.int) + let assert Ok(#(state, _)) = glua.new() |> glua.eval(code:) + let assert Ok(ref) = glua.get(state:, keys: ["my_value"]) + let assert Ok(ret) = glua.deference(state:, ref:, using: decode.int) assert ret == 10 } @@ -203,14 +196,14 @@ pub fn get_test() { pub fn get_returns_proper_errors_test() { let state = glua.new() - assert glua.get(state:, keys: ["non_existent_global"], using: decode.string) + assert glua.get(state:, keys: ["non_existent_global"]) == Error(glua.KeyNotFound(["non_existent_global"])) let encoded = glua.int(10) let assert Ok(state) = glua.set(state:, keys: ["my_table", "some_value"], value: encoded) - assert glua.get(state:, keys: ["my_table", "my_val"], using: decode.int) + assert glua.get(state:, keys: ["my_table", "my_val"]) == Error(glua.KeyNotFound(["my_table", "my_val"])) } @@ -219,8 +212,8 @@ pub fn set_test() { let assert Ok(lua) = glua.set(state: glua.new(), keys: ["_VERSION"], value: encoded) - let assert Ok(result) = - glua.get(state: lua, keys: ["_VERSION"], using: decode.string) + let assert Ok(ref) = glua.get(state: lua, keys: ["_VERSION"]) + let assert Ok(result) = glua.deference(state: lua, ref:, using: decode.string) assert result == "custom version" @@ -237,16 +230,20 @@ pub fn set_test() { ) let assert Ok(lua) = glua.set(lua, keys, encoded) - assert glua.get(lua, keys, glua.table_decoder(decode.int, decode.int)) - == Ok([#(1, 4), #(2, 16), #(3, 49), #(4, 144)]) + let assert Ok(ref) = glua.get(lua, keys) + assert glua.deference( + state: lua, + ref:, + using: decode.dict(decode.int, decode.int), + ) + == Ok(dict.from_list([#(1, 4), #(2, 16), #(3, 49), #(4, 144)])) let count_odd = fn(lua: glua.Lua, args: List(dynamic.Dynamic)) { let assert [list] = args - let assert Ok(list) = - decode.run(list, glua.table_decoder(decode.int, decode.int)) + let assert Ok(list) = decode.run(list, decode.dict(decode.int, decode.int)) let count = - list.map(list, pair.second) + list.map(dict.to_list(list), pair.second) |> list.count(int.is_odd) #(lua, list.map([count], glua.int)) @@ -263,13 +260,9 @@ pub fn set_test() { }), ) - let assert Ok(#(lua, [result])) = - glua.call_function_by_name( - state: lua, - keys: ["count_odd"], - args: [arg], - using: decode.int, - ) + let assert Ok(#(lua, [ref])) = + glua.call_function_by_name(state: lua, keys: ["count_odd"], args: [arg]) + let assert Ok(result) = glua.deference(state: lua, ref:, using: decode.int) assert result == 5 @@ -297,22 +290,20 @@ pub fn set_test() { let assert Ok(lua) = glua.set(state: lua, keys: ["my_functions"], value: tbl) - let assert Ok(#(lua, [result])) = + let assert Ok(#(lua, [ref])) = glua.call_function_by_name( state: lua, keys: ["my_functions", "is_even"], args: [arg], - using: decode.bool, ) + let assert Ok(result) = glua.deference(state: lua, ref:, using: decode.bool) assert result == True - let assert Ok(#(_, [result])) = - glua.eval( - state: lua, - code: "return my_functions.is_odd(4)", - using: decode.bool, - ) + let assert Ok(#(lua, [ref])) = + glua.eval(state: lua, code: "return my_functions.is_odd(4)") + + let assert Ok(result) = glua.deference(state: lua, ref:, using: decode.bool) assert result == False } @@ -323,7 +314,8 @@ pub fn set_lua_paths_test() { let code = "local s = require 'example'; return s" - let assert Ok(#(_, [result])) = glua.eval(state:, code:, using: decode.string) + let assert Ok(#(lua, [ref])) = glua.eval(state:, code:) + let assert Ok(result) = glua.deference(state: lua, ref:, using: decode.string) assert result == "LUA IS AN EMBEDDABLE LANGUAGE" } @@ -335,7 +327,7 @@ pub fn get_private_test() { == Ok([1, 2, 3]) assert glua.new() - |> glua.get_private("non_existent", using: decode.string) + |> glua.get_private("non_existent", decode.string) == Error(glua.KeyNotFound(["non_existent"])) } @@ -353,8 +345,8 @@ pub fn delete_private_test() { pub fn load_test() { let assert Ok(#(lua, chunk)) = glua.load(state: glua.new(), code: "return 5 * 5") - let assert Ok(#(_, [result])) = - glua.eval_chunk(state: lua, chunk:, using: decode.int) + let assert Ok(#(lua, [ref])) = glua.eval_chunk(state: lua, chunk:) + let assert Ok(result) = glua.deference(state: lua, ref:, using: decode.int) assert result == 25 } @@ -362,8 +354,8 @@ pub fn load_test() { pub fn eval_load_file_test() { let assert Ok(#(lua, chunk)) = glua.load_file(state: glua.new(), path: "./test/lua/example.lua") - let assert Ok(#(_, [result])) = - glua.eval_chunk(state: lua, chunk:, using: decode.string) + let assert Ok(#(lua, [ref])) = glua.eval_chunk(state: lua, chunk:) + let assert Ok(result) = glua.deference(state: lua, ref:, using: decode.string) assert result == "LUA IS AN EMBEDDABLE LANGUAGE" @@ -373,17 +365,16 @@ pub fn eval_load_file_test() { } pub fn eval_test() { - let assert Ok(#(lua, [result])) = - glua.eval( - state: glua.new(), - code: "return 'hello, ' .. 'world!'", - using: decode.string, - ) + let assert Ok(#(lua, [ref])) = + glua.eval(state: glua.new(), code: "return 'hello, ' .. 'world!'") + let assert Ok(result) = glua.deference(state: lua, ref:, using: decode.string) assert result == "hello, world!" - let assert Ok(#(_, results)) = - glua.eval(state: lua, code: "return 2 + 2, 3 - 1", using: decode.int) + let assert Ok(#(lua, refs)) = + glua.eval(state: lua, code: "return 2 + 2, 3 - 1") + let assert Ok(results) = + list.try_map(refs, glua.deference(state: lua, ref: _, using: decode.int)) assert results == [4, 2] } @@ -391,22 +382,22 @@ pub fn eval_test() { pub fn eval_returns_proper_errors_test() { let state = glua.new() - let assert Error(e) = - glua.eval(state:, code: "if true then 1 + ", using: decode.int) + let assert Error(e) = glua.eval(state:, code: "if true then 1 + ") assert e == glua.LuaCompileFailure([ glua.LuaCompileError(1, glua.Parse, "syntax error before: 1"), ]) - let assert Error(e) = - glua.eval(state:, code: "print(\"hi)", using: decode.int) + let assert Error(e) = glua.eval(state:, code: "print(\"hi)") assert e == glua.LuaCompileFailure([ glua.LuaCompileError(1, glua.Tokenize, "syntax error near '\"'"), ]) - assert glua.eval(state:, code: "return 'Hello from Lua!'", using: decode.int) + let assert Ok(#(lua, [ref])) = + glua.eval(state:, code: "return 'Hello from Lua!'") + assert glua.deference(state: lua, ref:, using: decode.int) == Error( glua.UnexpectedResultType([decode.DecodeError("Int", "String", [])]), ) @@ -414,7 +405,7 @@ pub fn eval_returns_proper_errors_test() { let assert Error(glua.LuaRuntimeException( exception: glua.IllegalIndex(value:, index:), state: _, - )) = glua.eval(state:, code: "return a.b", using: decode.int) + )) = glua.eval(state:, code: "return a.b") assert value == "nil" assert index == "b" @@ -422,7 +413,7 @@ pub fn eval_returns_proper_errors_test() { let assert Error(glua.LuaRuntimeException( exception: glua.ErrorCall(message, level), state: _, - )) = glua.eval(state:, code: "error('error message')", using: decode.int) + )) = glua.eval(state:, code: "error('error message')") assert message == "error message" assert level == option.None @@ -430,37 +421,31 @@ pub fn eval_returns_proper_errors_test() { let assert Error(glua.LuaRuntimeException( exception: glua.ErrorCall(message, level), state: _, - )) = - glua.eval(state:, code: "error('error with level', 1)", using: decode.int) + )) = glua.eval(state:, code: "error('error with level', 1)") assert message == "error with level" assert level == option.Some(1) - let assert Error(_) = glua.eval(state:, code: "error({1})", using: decode.int) + let assert Error(_) = glua.eval(state:, code: "error({1})") let assert Error(glua.LuaRuntimeException( exception: glua.UndefinedFunction(value:), state: _, - )) = glua.eval(state:, code: "local a = 5; a()", using: decode.int) + )) = glua.eval(state:, code: "local a = 5; a()") assert value == "5" let assert Error(glua.LuaRuntimeException( exception: glua.UndefinedMethod(_, method:), state: _, - )) = - glua.eval( - state:, - code: "local i = function(x) return x end; i:call(1)", - using: decode.string, - ) + )) = glua.eval(state:, code: "local i = function(x) return x end; i:call(1)") assert method == "call" let assert Error(glua.LuaRuntimeException( exception: glua.BadArith(operator:, args:), state: _, - )) = glua.eval(state:, code: "return 10 / 0", using: decode.int) + )) = glua.eval(state:, code: "return 10 / 0") assert operator == "/" assert args == ["10", "0"] @@ -468,53 +453,40 @@ pub fn eval_returns_proper_errors_test() { let assert Error(glua.LuaRuntimeException( exception: glua.AssertError(message:), state: _, - )) = - glua.eval( - state:, - code: "assert(1 == 2, 'assertion failed')", - using: decode.int, - ) + )) = glua.eval(state:, code: "assert(1 == 2, 'assertion failed')") assert message == "assertion failed" - let assert Error(_) = - glua.eval(state:, code: "assert(false, {1})", using: decode.int) + let assert Error(_) = glua.eval(state:, code: "assert(false, {1})") } pub fn eval_file_test() { - let assert Ok(#(_, [result])) = - glua.eval_file( - state: glua.new(), - path: "./test/lua/example.lua", - using: decode.string, - ) + let assert Ok(#(state, [ref])) = + glua.eval_file(state: glua.new(), path: "./test/lua/example.lua") + let assert Ok(result) = glua.deference(state:, ref:, using: decode.string) assert result == "LUA IS AN EMBEDDABLE LANGUAGE" } pub fn call_function_test() { let assert Ok(#(lua, [fun])) = - glua.ref_eval(state: glua.new(), code: "return string.reverse") + glua.eval(state: glua.new(), code: "return string.reverse") let encoded = glua.string("auL") - let assert Ok(#(lua, [result])) = - glua.call_function( - state: lua, - ref: fun, - args: [encoded], - using: decode.string, - ) + let assert Ok(#(lua, [ref])) = + glua.call_function(state: lua, ref: fun, args: [encoded]) + let assert Ok(result) = glua.deference(state: lua, ref:, using: decode.string) assert result == "Lua" let assert Ok(#(lua, [fun])) = - glua.ref_eval(state: lua, code: "return function(a, b) return a .. b end") + glua.eval(state: lua, code: "return function(a, b) return a .. b end") let args = list.map(["Lua in ", "Gleam"], glua.string) - let assert Ok(#(_, [result])) = - glua.call_function(state: lua, ref: fun, args:, using: decode.string) + let assert Ok(#(lua, [ref])) = glua.call_function(state: lua, ref: fun, args:) + let assert Ok(result) = glua.deference(state: lua, ref:, using: decode.string) assert result == "Lua in Gleam" } @@ -523,55 +495,45 @@ pub fn call_function_returns_proper_errors_test() { let state = glua.new() let assert Ok(#(state, [ref])) = - glua.ref_eval(state:, code: "return string.upper") + glua.eval(state:, code: "return string.upper") let arg = glua.string("Hello from Gleam!") - assert glua.call_function(state:, ref:, args: [arg], using: decode.int) + let assert Ok(#(lua, [ref])) = glua.call_function(state:, ref:, args: [arg]) + assert glua.deference(state: lua, ref:, using: decode.int) == Error( glua.UnexpectedResultType([decode.DecodeError("Int", "String", [])]), ) - let assert Ok(#(lua, [ref])) = glua.ref_eval(state:, code: "return 1") + let assert Ok(#(lua, [ref])) = glua.eval(state:, code: "return 1") let assert Error(glua.LuaRuntimeException( exception: glua.UndefinedFunction(value:), state: _, - )) = glua.call_function(state: lua, ref:, args: [], using: decode.string) + )) = glua.call_function(state: lua, ref:, args: []) assert value == "1" } pub fn call_function_by_name_test() { let args = list.map([20, 10], glua.int) - let assert Ok(#(lua, [result])) = - glua.call_function_by_name( - state: glua.new(), - keys: ["math", "max"], - args:, - using: decode.int, - ) + let assert Ok(#(lua, [ref])) = + glua.call_function_by_name(state: glua.new(), keys: ["math", "max"], args:) + let assert Ok(result) = glua.deference(state: lua, ref:, using: decode.int) assert result == 20 - let assert Ok(#(lua, [result])) = - glua.call_function_by_name( - state: lua, - keys: ["math", "min"], - args:, - using: decode.int, - ) + let assert Ok(#(lua, [ref])) = + glua.call_function_by_name(state: lua, keys: ["math", "min"], args:) + let assert Ok(result) = glua.deference(state: lua, ref:, using: decode.int) assert result == 10 let arg = glua.float(10.2) - let assert Ok(#(_, [result])) = - glua.call_function_by_name( - state: lua, - keys: ["math", "type"], - args: [arg], - using: decode.optional(decode.string), - ) + let assert Ok(#(state, [ref])) = + glua.call_function_by_name(state: lua, keys: ["math", "type"], args: [arg]) + let assert Ok(result) = + glua.deference(state:, ref:, using: decode.optional(decode.string)) assert result == option.Some("float") } @@ -579,41 +541,39 @@ pub fn call_function_by_name_test() { pub fn nested_function_references_test() { let code = "return function() return math.sqrt end" - let assert Ok(#(lua, [ref])) = glua.ref_eval(state: glua.new(), code:) - let assert Ok(#(lua, [ref])) = - glua.ref_call_function(state: lua, ref:, args: []) + let assert Ok(#(lua, [ref])) = glua.eval(state: glua.new(), code:) + let assert Ok(#(lua, [ref])) = glua.call_function(state: lua, ref:, args: []) let arg = glua.int(400) - let assert Ok(#(_, [result])) = - glua.call_function(state: lua, ref:, args: [arg], using: decode.float) + let assert Ok(#(_, [ref])) = glua.call_function(state: lua, ref:, args: [arg]) + let assert Ok(result) = glua.deference(state: lua, ref:, using: decode.float) assert result == 20.0 } pub fn format_error_test() { let state = glua.new() - let assert Error(e) = glua.ref_eval(state:, code: "1 +") + let assert Error(e) = glua.eval(state:, code: "1 +") assert glua.format_error(e) == "Lua compile error: \n\nFailed to parse: error on line 1: syntax error before: 1" - let assert Error(e) = glua.ref_eval(state:, code: "assert(false)") + let assert Error(e) = glua.eval(state:, code: "assert(false)") assert glua.format_error(e) == "Lua runtime exception: Assertion failed with message: assertion failed\n\nLine 1: assert(false)" let assert Error(e) = - glua.ref_eval(state:, code: "local a = true; local b = 1 * a") + glua.eval(state:, code: "local a = true; local b = 1 * a") assert glua.format_error(e) == "Lua runtime exception: Bad arithmetic expression: 1 * true" - let assert Error(e) = - glua.get(state:, keys: ["non_existent"], using: decode.string) + let assert Error(e) = glua.get(state:, keys: ["non_existent"]) assert glua.format_error(e) == "Key \"non_existent\" not found" let assert Error(e) = glua.load_file(state:, path: "non_existent_file") assert glua.format_error(e) == "Lua source file \"non_existent_file\" not found" - let assert Error(e) = - glua.eval(state:, code: "return 1 + 1", using: decode.string) + let assert Ok(#(lua, [ref])) = glua.eval(state:, code: "return 1 + 1") + let assert Error(e) = glua.deference(state: lua, ref:, using: decode.string) assert glua.format_error(e) == "Expected String, but found Int" }