Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

failing to return a table #71

Open
Licenser opened this issue Nov 19, 2016 · 7 comments
Open

failing to return a table #71

Licenser opened this issue Nov 19, 2016 · 7 comments

Comments

@Licenser
Copy link
Contributor

Hi I've ran into a problem that a function seems unable to return a table.

St0 = luerl:init(), ok.
F = fun([], StIn) ->
    {Tab, StOut} = luerl_emul:alloc_table([{<<"a">>, 1}], StIn),
    {[Tab], StOut}
  end.
St1 = luerl:set_table([t], F, St0), ok.
luerl:eval("return t", St1). %%=> {ok,[{function,#Fun<luerl.2.17475489>}]}
luerl:eval("return t()", St1). %% => {error,badarg}

The function is based on the pack function form luerl_lib_table:

{Tab,St1} = luerl_emul:alloc_table(T, St0),

@Licenser
Copy link
Contributor Author

Licenser commented Nov 19, 2016

after some more research it seems this works:

St0 = luerl:init(), ok.
F = fun(_, StIn) ->
    {[[{<<"a">>, 1.0}]], StIn}
  end.
St1 = luerl:set_table([t], F, St0), ok.
luerl:eval("return t()", St1).

But I'm not sure I understand why the function alloc_table works in the lib but not in the call.

@rvirding
Copy link
Owner

rvirding commented Nov 21, 2016

It has to do with encoding and decoding data between the "external erlang" representation of lua and the internal representation of the same. So [{<<"a">>,1.0}] is the external representation of a tuple with key/value a,1.

The luerl module has two groups of functions, those ending in '1' and those that don't. Those that don't assume that the arguments are in external format and automatically encodes them, while those ending in '1' assume they are in internal format.

So when you call set_table/3 it assumes your fun works on external format so as it is called internally it first decodes the internal format, calls your function with external format then encodes the result back to internal before returning it into the lua emulator. Check luerl.erl line 292.

The problem was that luerl_emul:alloc_table/2 returns internal format and when the system tried to encode it it bombed as it wasn't valid external format.

In your second attempt you explicitly return external format which could be encoded into internal format and returned to the emulator so it worked. As example of using your original function you could have done:

16> f(S0),S0 = luerl:init(),ok.
ok            
17> f(F),F = fun ([],Sti) -> {T,Sto} = luerl_emul:alloc_table([{<<"a">>,1.0}],Sti),{[T],Sto} end.
#Fun<erl_eval.12.52032458>
18> f(S1),S1 = luerl:set_table1([<<"t">>], {function,F}, S0), ok.
ok            
19> luerl:eval("return t()", S1).  
{ok,[[{<<"a">>,1.0}]]}

Here {function,F} is the internal format was an erlang function which is right for set_table1/3.

@Licenser
Copy link
Contributor Author

Ohhh I was really wondering what the 1 functions do :) they totally baffled me that makes sense.

So to clarify if I understand it correctly (as I will document this ;). If I use set_table1, then the both the thing I set the table to needs to be an 'internal' value as well as the function I pass needs to return a internal value. If I use set_table both the parameter needs to be 'erlangish' as well as the function return needs to be 'erlangish'.

Additionally would you be opposed to a PR for renaming the 1 to something like _i (for internal) or even a full _internal or _raw or the same name in a different module? When I read the code to try to figure things out the 1 made absolutely baffled me without background information and even with 1 feels not as an intuitive suffix for "internal format".

@rvirding
Copy link
Owner

Doing something as radical as changing the main the interface module will have to wait until a major revision. I have plans to change the interface as such and I would prefer to wait until then. But if you feel upto writing something about it in the wiki I would not complain. 😄

@Licenser
Copy link
Contributor Author

Makes total sense. wiki entry incoming (I'll close this too as the question is answered) but will re-add a new issue for renaming functions if you don't mind so it's tracked.

@riccardomanfrin
Copy link

Ciao guys,

I think I'm having a similar problem (and likely a little bit of confusion)...
I'm now trying to fit the above snippet into my example, but any help is appreciated.

Here is the code showcasing my problem

defmodule Helper do
  def run(data) do
    script = "function foo(data) print(data); return helper.date(); end"
    {:ok, chunk, st} = :luerl.load(script, :luerl.init())
    st = :luerl.load_module(["helper"], __MODULE__, st)
    {_res, st} = :luerl.do(chunk, st)
    {result, _stuff} = :luerl.call_function([:foo], [data], st)
    result
  end
  def date([], st) do
    datetime =
      DateTime.utc_now()
      |> NaiveDateTime.to_erl()
      |> IO.inspect()
    ###############
    # How do I pack datetime in place of "wouldlikedatetimehere"
    {["wouldlikedatetimehere"], st}
    ###############
  end

  def install(st) do
    :luerl_emul.alloc_table(table(), st)
  end

  def table() do
    [
      {"date", {:erl_func, &date/2}}
    ]
  end
end

@riccardomanfrin
Copy link

riccardomanfrin commented Feb 10, 2022

After lots of trial and error and reading reading this other issue I reworked the example and am now able to use it:

This code runs lua code which calls other erlang/elixir code, and data structures traverses the languages to be returned. Hope it can be of some help to ppl

defmodule Helper do
  def run(data) do
    script = "
    function foo(data) 
      print(data);
      local datetime = helper.datetime();
      print(datetime.time.h)
      return datetime;
    end"
    {:ok, chunk, st} = :luerl.load(script, :luerl.init())
    st = :luerl.load_module(["helper"], __MODULE__, st)
    {_res, st} = :luerl.do(chunk, st)
    {result, _stuff} = :luerl.call_function([:foo], [data], st)
    result
  end
  def datetime([], st) do
    {{year, month, day}, {hour, min, sec}} =
      DateTime.utc_now()
      |> NaiveDateTime.to_erl()

    {res, st} =
      [date: [y: year, m: month, d: day], time: [h: hour, m: min, s: sec]]
      |> :luerl.encode(st)

    {[res], st}
  end

  def install(st) do
    :luerl_emul.alloc_table(table(), st)
  end

  def table() do
    [
      {"datetime", {:erl_func, &datetime/2}}
    ]
  end
end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants