diff --git a/doc/luerl.txt b/doc/luerl.txt index f35ad11..c515a4f 100644 --- a/doc/luerl.txt +++ b/doc/luerl.txt @@ -1,137 +1,167 @@ -luerl(3) luerl(3) +luerl(3) Library Functions Manual luerl(3) +Name + luerl - The basic interface to the Luerl system - -Interface functions +Interface functions - New Version The Lua State parameter is the state of a Lua VM instance. It must be created with the luerl:init() call and be carried from one call to the next. - As it is possible in Lua to create self‐referencing data structures, + As it is possible in Lua to create self-referencing data structures, indeed the standard libraries have many instances of this, then using the functions which decode their return values will generate an error when they would cause an infinite loop during the decoding. An simple example is the top level table which contains a key _G which references - the top‐level table. + the top-level table. - Note that Lua Chunks (see definition below) can travel between differ‐ - ent States. They are precompiled bits of code, independent of State. - That you can ‘carry around’ this is no unique to Luerl but a low‐level - implementation detail of the standard Lua language (https://lua.org), - for more on chunks read (https://www.lua.org/manual/5.3/manu‐ - al.html#3.3.2) the official Lua 5.3 reference manual - (https://www.lua.org/manual/5.3/manual.html). + Note that Lua Chunks (see definition below) can travel between differ‐ + ent States. They are precompiled bits of code, independent of State. + That you can ‘carry around’ this is no unique to Luerl but a low-level + implementation detail of the standard Lua language ⟨https://lua.org⟩, + for more on chunks read ⟨https://www.lua.org/manual/5.3/man‐ + ual.html#3.3.2⟩ the official Lua 5.3 reference manual + ⟨https://www.lua.org/manual/5.3/manual.html⟩. Spec Definitions Binary means an Erlang binary string. Chunks means a portion of precompiled bytecode. State means a Lua State, this is a Lua VM instance. Path means a file system path and file name. - KeyPath means an Erlang list of atoms representing nested names, + KeyPath means an Erlang list of atoms representing nested names, e.g. [table,pack] for table.pack. - Keys means Lua table keys, the keys of a key‐value structure. - - Functions - eval and do functions differ only in what they return. The do func‐ - tions return results and a new Lua State, the eval functions return a - tuple starting on ‘ok’ or ‘error’, then the result, or cause of error. - - do ‐‐> {Result, State} - - eval ‐‐> {ok, Result} | {error, Reason} + Keys means Lua table keys, the keys of a key-value structure. - luerl:eval(String|Binary|Form, State) ‐> {ok, Result} | {error, Reason, - StackTrace}. - Evaluate a Lua expression passed in as a string or binary, and return - its result. + CompileOptions means a list of compiler options. Currently supported + options are ‘return’, which returns the errors and warnings, and ‘re‐ + port’ which will log the errors and warnings. - luerl:evalfile(Path, State) ‐> {ok, Result} | {error, Reason, StackTrace}. - Load and execute a file, and return the result. + LuaCallReturn = {ok, Result, State} | {lua_error, Error, State} + This is the return value from evaluating a Lua call. - luerl:do(String|Binary|Form, State) ‐> {Result, NewState}. - Evaluate a Lua expression and return its result, and the new Lua State. + Functions + luerl:init() -> State + Get a new Lua State = a fresh Lua VM instance. - luerl:dofile(Path, State) ‐> {Result, NewState}. - Load and execute the Lua code in the file and return its result, and - the new Lua State. Equivalent to doing luerl:do(“return dofile(‘File‐ - Name’)”). + luerl:gc(State) -> State + Runs the garbage collector on a state and returns the new state. - luerl:load(String|Binary[, CompileOptions], State) ‐> {ok,Function,New‐ - State} | {error, Reason}. - Parse a Lua chunk as string or binary, and return a compiled chunk + luerl:load(String|Binary[, CompileOptions], State) -> {ok, Function, State} + | CompileError + Parse a Lua chunk as string or binary, and return a compiled chunk (‘form’). - luerl:loadfile(FileName[, CompileOptions], State) ‐> {ok,Function,NewState} - | {error, Reason}. + luerl:loadfile(FileName[, CompileOptions], State) -> {ok, Function, State} + | CompileError Parse a Lua file, and return a compiled chunk (‘form’). - luerl:path_loadfile([Path, ], FileName[, CompileOptions], State) ‐> - {ok,Function,FullName,State} | {error, Reason}. - Search Path until the file FileName is found. Parse the file and re‐ + luerl:path_loadfile([Path, ], FileName[, CompileOptions], State) -> + {ok,Function,FullName,State} | {error, Reason} + Search Path until the file FileName is found. Parse the file and re‐ turn a compiled chunk (‘form’). If Path is not given then the path de‐ fined in the environment variable LUA_LOAD_PATH is used. - luerl:load_module(KeyPath, ErlangModule, State) ‐> State. - Load ErlangModule and install its table at KeyPath which is encoded. - - luerl:load_module1(KeyPath, ErlangModule, State) ‐> State. - Load ErlangModule and install its table at KeyPath which is NOT encoded + luerl:load_module(KeyPath, ErlangModule, State) -> State + Load ErlangModule and install its table at KeyPath which is NOT en‐ + coded. - luerl:init() ‐> State. - Get a new Lua State = a fresh Lua VM instance. + luerl:load_module_dec(EncodedKeyPath, ErlangModule, State) -> State + Load ErlangModule and install its table at KeyPath which is encoded. - luerl:call(Form, Args, State) ‐> {Result,State} - luerl:call_chunk(Form, Args, State) ‐> {Result,State} - Call a compiled chunk or function. Use the call_chunk, call has been + luerl:do(String|Binary|Form, State) -> {ok, Result, NewState} | {lua_error, + Error, State} | CompileError + Evaluate a Lua expression and return its result which is NOT decoded, + and the new Lua State. + + luerl:do_dec(String|Binary|Form, State) -> {ok, Result, NewState} | + {lua_error, Error, State} | CompileError + Evaluate a Lua expression and return its result which is automatically + decoded, and the new Lua State. + + luerl:dofile(Path, State) -> {ok, Result, NewState} | {lua_error, Error, + State} | CompileError + Load and execute the Lua code in the file and return its result which + is NOT decoded, and the new Lua State. Equivalent to doing + luerl:do(“return dofile(‘FileName’)”). + + luerl:dofile_dec(Path[, State]) -> {ok, Result, NewState} | {lua_error, Er‐ + ror, State} | CompileError + Load and execute the Lua code in the file and return its result which + is automatically decoded, and the new Lua State. + + luerl:call(FuncRef, ArgRefs, State) -> {ok, Result, State} + luerl:call_chunk(FuncRef, ArgRefs, State) -> {ok, Result, State} | {lua_er‐ + ror, Error, State} + Call a compiled chunk or function. Use the call_chunk, call has been kept for backwards compatibility. - luerl:call_function(KeyPath, Args, State) ‐> {Result,NewState} - Call a function already defined in the state. KeyPath is a list of - names to the function. KeyPath, Args and Result are automatically en‐ + luerl:call_function(FuncRef | FuncPath, ArgRefs, State] -> {ok, Result, + State} | {lua_error, Error, State} + Call a function already defined in the state. Result is NOT decoded. + + luerl:call_function_dec(KeyPath, Args, State) -> {ok, Result, State} | + {lua_error, Error, State} + Call a function already defined in the state. KeyPath is a list of + keys to the function. KeyPath, Args and Result are automatically en‐ coded/decoded. - luerl:call_function1(KeyPath, Args, State) ‐> {Result,NewState} - Call a function already defined in the state. KeyPath is a list of - keys to the function. KeyPath, Args and Result are NOT encoded/decod‐ - ed. + luerl:call_method(ObjRef, Method, ArgRefs, State) -> {ok, Result, State} | + {lua_error, Error, State} + Call a method already defined in the state. - luerl:call_method(MethPath, Args, State) ‐> {Result,NewState}. - Call a method already defined in the state. MethPath is a list of - names to the method. MethPath, Args and Result are automatically en‐ + luerl:call_method_dec(KeyPath, Method, Args, State) -> {ok, Result, State} + | {lua_error, Error, State} + Call a method already defined in the state. KeyPath is a list of keys + to the method. KeyPath, Method, Args and Result are automatically en‐ coded/decoded. - luerl:call_method1(MethPath, Args, State) ‐> {Result,NewState} - Call a method already defined in the state. MethPath is a list of keys - to the method. Keys, Args and Result are NOT encoded/decoded. + luerl:get_table_keys(KeyPath, State) -> {ok, Result, State} | {lua_error, + Error, State} + Gets a value inside the Lua state. KeyPath and Result are NOT en‐ + coded/decoded. - luerl:stop(State) ‐> GCedState. - Garbage collects the state and (todo:) does away with it. + luerl:get_table_keys_dec(KeyPath, State) -> {ok, Result, State} | {lua_er‐ + ror, Error, State} + Gets a value inside the Lua state. KeyPath is automatically encoded + and Result is decoded. - luerl:gc(State) ‐> State. - Runs the garbage collector on a state and returns the new state. + luerl:set_table_keys(KeyPath, Value, State) -> {ok,State} | {lua_error, Er‐ + ror, State} + Sets a value inside the Lua state. KeyPath and Value are NOT encoded. - luerl:set_table(KeyPath, Value, State) ‐> State. - Sets a value inside the Lua state. Value is automatically encoded. + luerl:set_table_keys_dec(KeyPath, Value, State) -> {ok, Result, State} | + {lua_error, Error, State} + Sets a value inside the Lua state. KeyPath and Value are automatically + encoded and Result is decoded. - luerl:set_table1(KeyPath, Value, State) ‐> State. - Sets a value inside the Lua state. KeyPath and Value are NOT encoded. + luerl:get_table_key(Table, Key, State) -> {ok, Result, State} | {lua_error, + Error, State} + Gets the value of a key in a table. Table and Key are NOT encoded and + Result is NOT decoded. - luerl:get_table(KeyPath, State) ‐> {Result,State}. - Gets a value inside the Lua state. KeyPath and Result are automatical‐ - ly encoded. + luerl:set_table_key(Table, Key, Value, State) -> {ok, State} | {lua_error, + Error, State} + Sets the value of a key in a table. Table, Key and Value are NOT en‐ + coded. - luerl:get_table1(KeyPath, State) ‐> {Result,State}. - Gets a value inside the Lua state. KeyPath and Result are NOT encod‐ - ed/decoded. + luerl:get_stacktrace(State) -> [{FuncName,{file,FileName},{line,Line}}] + Return a stack trace of the current call stack in the state. - You can use this function to expose an function to the Lua code by us‐ - ing this interface: fun(Args, State) ‐> {Results, State} + luerl:encode(Term, State) -> {LuerlTerm,State} + Encode the Erlang representation of a term into Luerl form updating the + state when necessary. - Args and Results must be a list of Luerl compatible Erlang values. + luerl:encode_list([Term], State) -> {[LuerlTerm],State} + Encode a list of Erlang term representations into a list of Luerl forms + updating the state when necessary. -AUTHORS - Jean Chassoul, Robert Virding. + luerl:decode(LuerlTerm, State) -> Term + Decode a term in the Luerl form into its Erlang representation. + luerl:decode_list([LuerlTerm], State) -> [Term] + Decode a list of Luerl terms into a list of Erlang representations. +AUTHORS + Jean Chassoul, Robert Virding. - 2018‐2023 luerl(3) + 2018-2024 luerl(3) diff --git a/doc/luerl_old.txt b/doc/luerl_old.txt new file mode 100644 index 0000000..2dcf422 --- /dev/null +++ b/doc/luerl_old.txt @@ -0,0 +1,137 @@ +luerl_old(3) Library Functions Manual luerl_old(3) + +Name + luerl - The old original interface to the Luerl system + +Interface functions + The Lua State parameter is the state of a Lua VM instance. It must be + created with the luerl:init() call and be carried from one call to the + next. + + As it is possible in Lua to create self-referencing data structures, + indeed the standard libraries have many instances of this, then using + the functions which decode their return values will generate an error + when they would cause an infinite loop during the decoding. An simple + example is the top level table which contains a key _G which references + the top-level table. + + Note that Lua Chunks (see definition below) can travel between differ‐ + ent States. They are precompiled bits of code, independent of State. + That you can ‘carry around’ this is no unique to Luerl but a low-level + implementation detail of the standard Lua language ⟨https://lua.org⟩, + for more on chunks read ⟨https://www.lua.org/manual/5.3/man‐ + ual.html#3.3.2⟩ the official Lua 5.3 reference manual + ⟨https://www.lua.org/manual/5.3/manual.html⟩. + + Spec Definitions + Binary means an Erlang binary string. + Chunks means a portion of precompiled bytecode. + State means a Lua State, this is a Lua VM instance. + Path means a file system path and file name. + KeyPath means an Erlang list of atoms representing nested names, + e.g. [table,pack] for table.pack. + Keys means Lua table keys, the keys of a key-value structure. + + Functions + eval and do functions differ only in what they return. The do func‐ + tions return results and a new Lua State, the eval functions return a + tuple starting on ‘ok’ or ‘error’, then the result, or cause of error. + + do --> {Result, State} + + eval --> {ok, Result} | {error, Reason} + + luerl:eval(String|Binary|Form, State) -> {ok, Result} | {error, Reason, + StackTrace}. + Evaluate a Lua expression passed in as a string or binary, and return + its result. + + luerl:evalfile(Path, State) -> {ok, Result} | {error, Reason, StackTrace}. + + Load and execute a file, and return the result. + + luerl:do(String|Binary|Form, State) -> {Result, NewState}. + Evaluate a Lua expression and return its result, and the new Lua State. + + luerl:dofile(Path, State) -> {Result, NewState}. + Load and execute the Lua code in the file and return its result, and + the new Lua State. Equivalent to doing luerl:do(“return dofile(‘File‐ + Name’)”). + + luerl:load(String|Binary[, CompileOptions], State) -> {ok,Function,New‐ + State} | {error, Reason}. + Parse a Lua chunk as string or binary, and return a compiled chunk + (‘form’). + + luerl:loadfile(FileName[, CompileOptions], State) -> {ok,Function,NewState} + | {error, Reason}. + Parse a Lua file, and return a compiled chunk (‘form’). + + luerl:path_loadfile([Path, ], FileName[, CompileOptions], State) -> + {ok,Function,FullName,State} | {error, Reason}. + Search Path until the file FileName is found. Parse the file and re‐ + turn a compiled chunk (‘form’). If Path is not given then the path de‐ + fined in the environment variable LUA_LOAD_PATH is used. + + luerl:load_module(KeyPath, ErlangModule, State) -> State. + Load ErlangModule and install its table at KeyPath which is encoded. + + luerl:load_module1(KeyPath, ErlangModule, State) -> State. + Load ErlangModule and install its table at KeyPath which is NOT encoded + + luerl:init() -> State. + Get a new Lua State = a fresh Lua VM instance. + + luerl:call(Form, Args, State) -> {Result,State} + luerl:call_chunk(Form, Args, State) -> {Result,State} + Call a compiled chunk or function. Use the call_chunk, call has been + kept for backwards compatibility. + + luerl:call_function(KeyPath, Args, State) -> {Result,NewState} + Call a function already defined in the state. KeyPath is a list of + names to the function. KeyPath, Args and Result are automatically en‐ + coded/decoded. + + luerl:call_function1(KeyPath, Args, State) -> {Result,NewState} + Call a function already defined in the state. KeyPath is a list of + keys to the function. KeyPath, Args and Result are NOT encoded/de‐ + coded. + + luerl:call_method(MethPath, Args, State) -> {Result,NewState}. + Call a method already defined in the state. MethPath is a list of + names to the method. MethPath, Args and Result are automatically en‐ + coded/decoded. + + luerl:call_method1(MethPath, Args, State) -> {Result,NewState} + Call a method already defined in the state. MethPath is a list of keys + to the method. Keys, Args and Result are NOT encoded/decoded. + + luerl:stop(State) -> GCedState. + Garbage collects the state and (todo:) does away with it. + + luerl:gc(State) -> State. + Runs the garbage collector on a state and returns the new state. + + luerl:set_table(KeyPath, Value, State) -> State. + Sets a value inside the Lua state. Value is automatically encoded. + + luerl:set_table1(KeyPath, Value, State) -> State. + Sets a value inside the Lua state. KeyPath and Value are NOT encoded. + + luerl:get_table(KeyPath, State) -> {Result,State}. + Gets a value inside the Lua state. KeyPath and Result are automati‐ + cally encoded. + + luerl:get_table1(KeyPath, State) -> {Result,State}. + Gets a value inside the Lua state. KeyPath and Result are NOT en‐ + coded/decoded. + + You can use this function to expose an function to the Lua code by us‐ + ing this interface: fun(Args, State) -> {Results, State} + + Args and Results must be a list of Luerl compatible Erlang values. + +AUTHORS + Jean Chassoul, Robert Virding. + + 2018-2024 luerl_old(3) diff --git a/doc/man/luerl.3 b/doc/man/luerl.3 index 3ecb3d2..cc0121c 100644 --- a/doc/man/luerl.3 +++ b/doc/man/luerl.3 @@ -1,44 +1,38 @@ -.\" Automatically generated by Pandoc 3.1.6.1 +.\" Automatically generated by Pandoc 3.5 .\" -.\" Define V font for inline verbatim, using C font in formats -.\" that render this, and otherwise B font. -.ie "\f[CB]x\f[]"x" \{\ -. ftr V B -. ftr VI BI -. ftr VB B -. ftr VBI BI -.\} -.el \{\ -. ftr V CR -. ftr VI CI -. ftr VB CB -. ftr VBI CBI -.\} -.TH "luerl" "3" "2018-2023" "" "" -.hy -.SH Interface functions -.PP +.TH "luerl" "3" "2018\-2024" "" +.SH Name +luerl \- The basic interface to the Luerl system +.SH Interface functions \- New Version The \f[B]Lua State\f[R] parameter is the state of a Lua VM instance. It must be created with the \f[B]luerl:init()\f[R] call and be carried from one call to the next. .PP -As it is possible in Lua to create self-referencing data structures, +As it is possible in Lua to create self\-referencing data structures, indeed the standard libraries have many instances of this, then using the functions which decode their return values will generate an error when they would cause an infinite loop during the decoding. An simple example is the top level table which contains a key -\f[B]\f[VB]_G\f[B]\f[R] which references the top-level table. +\f[B]\f[CB]_G\f[B]\f[R] which references the top\-level table. .PP Note that Lua \f[B]Chunks\f[R] (see definition below) can travel between different States. They are precompiled bits of code, independent of State. -That you can `carry around' this is no unique to Luerl but a low-level -implementation detail of the standard Lua language (https://lua.org), -for more on chunks -read (https://www.lua.org/manual/5.3/manual.html#3.3.2) the official Lua -5.3 reference manual (https://www.lua.org/manual/5.3/manual.html). +That you can `carry around' this is no unique to Luerl but a low\-level +implementation detail of the standard Lua \c +.UR https://lua.org +language +.UE \c +, for more on chunks \c +.UR https://www.lua.org/manual/5.3/manual.html#3.3.2 +read +.UE \c +\ the official Lua 5.3 \c +.UR https://www.lua.org/manual/5.3/manual.html +reference manual +.UE \c +\&. .SS Spec Definitions -.PP \f[B]Binary\f[R] means an Erlang binary string. .PD 0 .P @@ -60,117 +54,105 @@ nested names, e.g.\ [table,pack] for table.pack. .PD 0 .P .PD -\f[B]Keys\f[R] means Lua table keys, the keys of a key-value structure. -.SS Functions -.PP -\f[B]eval\f[R] and \f[B]do\f[R] functions differ only in what they -return. -The \f[B]do\f[R] functions return results and a new Lua State, the -\f[B]eval\f[R] functions return a tuple starting on `ok' or `error', -then the result, or cause of error. -.IP -.nf -\f[C] -do --> {Result, State} - -eval --> {ok, Result} | {error, Reason} -\f[R] -.fi -.SS luerl:eval(String|Binary|Form, State) -> {ok, Result} | {error, Reason, StackTrace}. -.PP -Evaluate a Lua expression passed in as a string or binary, and return -its result. -.SS luerl:evalfile(Path, State) -> {ok, Result} | {error, Reason, StackTrace}. -.PP -Load and execute a file, and return the result. -.SS luerl:do(String|Binary|Form, State) -> {Result, NewState}. +\f[B]Keys\f[R] means Lua table keys, the keys of a key\-value structure. .PP -Evaluate a Lua expression and return its result, and the new Lua State. -.SS luerl:dofile(Path, State) -> {Result, NewState}. -.PP -Load and execute the Lua code in the file and return its result, and the -new Lua State. -Equivalent to doing luerl:do(\[lq]return dofile(`FileName')\[rq]). -.SS luerl:load(String|Binary[, CompileOptions], State) -> {ok,Function,NewState} | {error, Reason}. +\f[B]CompileOptions\f[R] means a list of compiler options. +Currently supported options are `return', which returns the errors and +warnings, and `report' which will log the errors and warnings. .PP +\f[B]LuaCallReturn = {ok, Result, State} | {lua_error, Error, +State}\f[R] +.PD 0 +.P +.PD +This is the return value from evaluating a Lua call. +.SS Functions +.SS luerl:init() \-> State +Get a new Lua State = a fresh Lua VM instance. +.SS luerl:gc(State) \-> State +Runs the garbage collector on a state and returns the new state. +.SS luerl:load(String|Binary[, CompileOptions], State) \-> {ok, Function, State} | CompileError Parse a Lua chunk as string or binary, and return a compiled chunk (`form'). -.SS luerl:loadfile(FileName[, CompileOptions], State) -> {ok,Function,NewState} | {error, Reason}. -.PP +.SS luerl:loadfile(FileName[, CompileOptions], State) \-> {ok, Function, State} | CompileError Parse a Lua file, and return a compiled chunk (`form'). -.SS luerl:path_loadfile([Path, ], FileName[, CompileOptions], State) -> {ok,Function,FullName,State} | {error, Reason}. -.PP +.SS luerl:path_loadfile([Path, ], FileName[, CompileOptions], State) \-> {ok,Function,FullName,State} | {error, Reason} Search Path until the file FileName is found. Parse the file and return a compiled chunk (`form'). If Path is not given then the path defined in the environment variable LUA_LOAD_PATH is used. -.SS luerl:load_module(KeyPath, ErlangModule, State) -> State. -.PP -Load \f[V]ErlangModule\f[R] and install its table at \f[V]KeyPath\f[R] +.SS luerl:load_module(KeyPath, ErlangModule, State) \-> State +Load \f[CR]ErlangModule\f[R] and install its table at \f[CR]KeyPath\f[R] +which is \f[B]NOT\f[R] encoded. +.SS luerl:load_module_dec(EncodedKeyPath, ErlangModule, State) \-> State +Load \f[CR]ErlangModule\f[R] and install its table at \f[CR]KeyPath\f[R] which is encoded. -.SS luerl:load_module1(KeyPath, ErlangModule, State) -> State. -.PP -Load \f[V]ErlangModule\f[R] and install its table at \f[V]KeyPath\f[R] -which is \f[B]NOT\f[R] encoded -.SS luerl:init() -> State. -.PP -Get a new Lua State = a fresh Lua VM instance. -.SS luerl:call(Form, Args, State) -> {Result,State} -.SS luerl:call_chunk(Form, Args, State) -> {Result,State} -.PP +.SS luerl:do(String|Binary|Form, State) \-> {ok, Result, NewState} | {lua_error, Error, State} | CompileError +Evaluate a Lua expression and return its result which is \f[B]NOT\f[R] +decoded, and the new Lua State. +.SS luerl:do_dec(String|Binary|Form, State) \-> {ok, Result, NewState} | {lua_error, Error, State} | CompileError +Evaluate a Lua expression and return its result which is automatically +decoded, and the new Lua State. +.SS luerl:dofile(Path, State) \-> {ok, Result, NewState} | {lua_error, Error, State} | CompileError +Load and execute the Lua code in the file and return its result which is +\f[B]NOT\f[R] decoded, and the new Lua State. +Equivalent to doing luerl:do(\[lq]return dofile(`FileName')\[rq]). +.SS luerl:dofile_dec(Path[, State]) \-> {ok, Result, NewState} | {lua_error, Error, State} | CompileError +Load and execute the Lua code in the file and return its result which is +automatically decoded, and the new Lua State. +.SS luerl:call(FuncRef, ArgRefs, State) \-> {ok, Result, State} +.SS luerl:call_chunk(FuncRef, ArgRefs, State) \-> {ok, Result, State} | {lua_error, Error, State} Call a compiled chunk or function. Use the call_chunk, call has been kept for backwards compatibility. -.SS luerl:call_function(KeyPath, Args, State) -> {Result,NewState} -.PP +.SS luerl:call_function(FuncRef | FuncPath, ArgRefs, State] \-> {ok, Result, State} | {lua_error, Error, State} Call a function already defined in the state. -\f[V]KeyPath\f[R] is a list of names to the function. -\f[V]KeyPath\f[R], \f[V]Args\f[R] and \f[V]Result\f[R] are automatically -encoded/decoded. -.SS luerl:call_function1(KeyPath, Args, State) -> {Result,NewState} -.PP +\f[CR]Result\f[R] is \f[B]NOT\f[R] decoded. +.SS luerl:call_function_dec(KeyPath, Args, State) \-> {ok, Result, State} | {lua_error, Error, State} Call a function already defined in the state. -\f[V]KeyPath\f[R] is a list of keys to the function. -\f[V]KeyPath\f[R], \f[V]Args\f[R] and \f[V]Result\f[R] are \f[B]NOT\f[R] -encoded/decoded. -.SS luerl:call_method(MethPath, Args, State) -> {Result,NewState}. -.PP -Call a method already defined in the state. -\f[V]MethPath\f[R] is a list of names to the method. -\f[V]MethPath\f[R], \f[V]Args\f[R] and \f[V]Result\f[R] are +\f[CR]KeyPath\f[R] is a list of keys to the function. +\f[CR]KeyPath\f[R], \f[CR]Args\f[R] and \f[CR]Result\f[R] are automatically encoded/decoded. -.SS luerl:call_method1(MethPath, Args, State) -> {Result,NewState} -.PP +.SS luerl:call_method(ObjRef, Method, ArgRefs, State) \-> {ok, Result, State} | {lua_error, Error, State} +Call a method already defined in the state. +.SS luerl:call_method_dec(KeyPath, Method, Args, State) \-> {ok, Result, State} | {lua_error, Error, State} Call a method already defined in the state. -\f[V]MethPath\f[R] is a list of keys to the method. -\f[V]Keys\f[R], \f[V]Args\f[R] and \f[V]Result\f[R] are \f[B]NOT\f[R] +\f[CR]KeyPath\f[R] is a list of keys to the method. +\f[CR]KeyPath\f[R], \f[CR]Method\f[R], \f[CR]Args\f[R] and +\f[CR]Result\f[R] are automatically encoded/decoded. +.SS luerl:get_table_keys(KeyPath, State) \-> {ok, Result, State} | {lua_error, Error, State} +Gets a value inside the Lua state. +\f[CR]KeyPath\f[R] and \f[CR]Result\f[R] are \f[B]NOT\f[R] encoded/decoded. -.SS luerl:stop(State) -> GCedState. -.PP -Garbage collects the state and (todo:) does away with it. -.SS luerl:gc(State) -> State. -.PP -Runs the garbage collector on a state and returns the new state. -.SS luerl:set_table(KeyPath, Value, State) -> State. -.PP +.SS luerl:get_table_keys_dec(KeyPath, State) \-> {ok, Result, State} | {lua_error, Error, State} +Gets a value inside the Lua state. +\f[CR]KeyPath\f[R] is automatically encoded and \f[CR]Result\f[R] is +decoded. +.SS luerl:set_table_keys(KeyPath, Value, State) \-> {ok,State} | {lua_error, Error, State} Sets a value inside the Lua state. -Value is automatically encoded. -.SS luerl:set_table1(KeyPath, Value, State) -> State. -.PP +\f[CR]KeyPath\f[R] and \f[CR]Value\f[R] are \f[B]NOT\f[R] encoded. +.SS luerl:set_table_keys_dec(KeyPath, Value, State) \-> {ok, Result, State} | {lua_error, Error, State} Sets a value inside the Lua state. -\f[V]KeyPath\f[R] and \f[V]Value\f[R] are \f[B]NOT\f[R] encoded. -.SS luerl:get_table(KeyPath, State) -> {Result,State}. -.PP -Gets a value inside the Lua state. -\f[V]KeyPath\f[R] and \f[V]Result\f[R] are automatically encoded. -.SS luerl:get_table1(KeyPath, State) -> {Result,State}. -.PP -Gets a value inside the Lua state. -\f[V]KeyPath\f[R] and \f[V]Result\f[R] are \f[B]NOT\f[R] -encoded/decoded. -.PP -You can use this function to expose an function to the Lua code by using -this interface: \f[V]fun(Args, State) -> {Results, State}\f[R] -.PP -Args and Results must be a list of Luerl compatible Erlang values. +\f[CR]KeyPath\f[R] and \f[CR]Value\f[R] are automatically encoded and +\f[CR]Result\f[R] is decoded. +.SS luerl:get_table_key(Table, Key, State) \-> {ok, Result, State} | {lua_error, Error, State} +Gets the value of a key in a table. +\f[CR]Table\f[R] and \f[CR]Key\f[R] are \f[B]NOT\f[R] encoded and +\f[CR]Result\f[R] is \f[B]NOT\f[R] decoded. +.SS luerl:set_table_key(Table, Key, Value, State) \-> {ok, State} | {lua_error, Error, State} +Sets the value of a key in a table. +\f[CR]Table\f[R], \f[CR]Key\f[R] and \f[CR]Value\f[R] are \f[B]NOT\f[R] +encoded. +.SS luerl:get_stacktrace(State) \-> [{FuncName,{file,FileName},{line,Line}}] +Return a stack trace of the current call stack in the state. +.SS luerl:encode(Term, State) \-> {LuerlTerm,State} +Encode the Erlang representation of a term into Luerl form updating the +state when necessary. +.SS luerl:encode_list([Term], State) \-> {[LuerlTerm],State} +Encode a list of Erlang term representations into a list of Luerl forms +updating the state when necessary. +.SS luerl:decode(LuerlTerm, State) \-> Term +Decode a term in the Luerl form into its Erlang representation. +.SS luerl:decode_list([LuerlTerm], State) \-> [Term] +Decode a list of Luerl terms into a list of Erlang representations. .SH AUTHORS Jean Chassoul, Robert Virding. diff --git a/doc/man/luerl_old.3 b/doc/man/luerl_old.3 new file mode 100644 index 0000000..a3e4e04 --- /dev/null +++ b/doc/man/luerl_old.3 @@ -0,0 +1,146 @@ +.\" Automatically generated by Pandoc 3.5 +.\" +.TH "luerl_old" "3" "2018\-2024" "" +.SH Name +luerl \- The old original interface to the Luerl system +.SH Interface functions +The \f[B]Lua State\f[R] parameter is the state of a Lua VM instance. +It must be created with the \f[B]luerl:init()\f[R] call and be carried +from one call to the next. +.PP +As it is possible in Lua to create self\-referencing data structures, +indeed the standard libraries have many instances of this, then using +the functions which decode their return values will generate an error +when they would cause an infinite loop during the decoding. +An simple example is the top level table which contains a key +\f[B]\f[CB]_G\f[B]\f[R] which references the top\-level table. +.PP +Note that Lua \f[B]Chunks\f[R] (see definition below) can travel between +different States. +They are precompiled bits of code, independent of State. +That you can `carry around' this is no unique to Luerl but a low\-level +implementation detail of the standard Lua \c +.UR https://lua.org +language +.UE \c +, for more on chunks \c +.UR https://www.lua.org/manual/5.3/manual.html#3.3.2 +read +.UE \c +\ the official Lua 5.3 \c +.UR https://www.lua.org/manual/5.3/manual.html +reference manual +.UE \c +\&. +.SS Spec Definitions +\f[B]Binary\f[R] means an Erlang binary string. +.PD 0 +.P +.PD +\f[B]Chunks\f[R] means a portion of precompiled bytecode. +.PD 0 +.P +.PD +\f[B]State\f[R] means a Lua State, this \f[I]is\f[R] a Lua VM instance. +.PD 0 +.P +.PD +\f[B]Path\f[R] means a file system path and file name. +.PD 0 +.P +.PD +\f[B]KeyPath\f[R] means an Erlang list of \f[B]atoms\f[R] representing +nested names, e.g.\ [table,pack] for table.pack. +.PD 0 +.P +.PD +\f[B]Keys\f[R] means Lua table keys, the keys of a key\-value structure. +.SS Functions +\f[B]eval\f[R] and \f[B]do\f[R] functions differ only in what they +return. +The \f[B]do\f[R] functions return results and a new Lua State, the +\f[B]eval\f[R] functions return a tuple starting on `ok' or `error', +then the result, or cause of error. +.IP +.EX +do \-\-> {Result, State} + +eval \-\-> {ok, Result} | {error, Reason} +.EE +.SS luerl:eval(String|Binary|Form, State) \-> {ok, Result} | {error, Reason, StackTrace}. +Evaluate a Lua expression passed in as a string or binary, and return +its result. +.SS luerl:evalfile(Path, State) \-> {ok, Result} | {error, Reason, StackTrace}. +Load and execute a file, and return the result. +.SS luerl:do(String|Binary|Form, State) \-> {Result, NewState}. +Evaluate a Lua expression and return its result, and the new Lua State. +.SS luerl:dofile(Path, State) \-> {Result, NewState}. +Load and execute the Lua code in the file and return its result, and the +new Lua State. +Equivalent to doing luerl:do(\[lq]return dofile(`FileName')\[rq]). +.SS luerl:load(String|Binary[, CompileOptions], State) \-> {ok,Function,NewState} | {error, Reason}. +Parse a Lua chunk as string or binary, and return a compiled chunk +(`form'). +.SS luerl:loadfile(FileName[, CompileOptions], State) \-> {ok,Function,NewState} | {error, Reason}. +Parse a Lua file, and return a compiled chunk (`form'). +.SS luerl:path_loadfile([Path, ], FileName[, CompileOptions], State) \-> {ok,Function,FullName,State} | {error, Reason}. +Search Path until the file FileName is found. +Parse the file and return a compiled chunk (`form'). +If Path is not given then the path defined in the environment variable +LUA_LOAD_PATH is used. +.SS luerl:load_module(KeyPath, ErlangModule, State) \-> State. +Load \f[CR]ErlangModule\f[R] and install its table at \f[CR]KeyPath\f[R] +which is encoded. +.SS luerl:load_module1(KeyPath, ErlangModule, State) \-> State. +Load \f[CR]ErlangModule\f[R] and install its table at \f[CR]KeyPath\f[R] +which is \f[B]NOT\f[R] encoded +.SS luerl:init() \-> State. +Get a new Lua State = a fresh Lua VM instance. +.SS luerl:call(Form, Args, State) \-> {Result,State} +.SS luerl:call_chunk(Form, Args, State) \-> {Result,State} +Call a compiled chunk or function. +Use the call_chunk, call has been kept for backwards compatibility. +.SS luerl:call_function(KeyPath, Args, State) \-> {Result,NewState} +Call a function already defined in the state. +\f[CR]KeyPath\f[R] is a list of names to the function. +\f[CR]KeyPath\f[R], \f[CR]Args\f[R] and \f[CR]Result\f[R] are +automatically encoded/decoded. +.SS luerl:call_function1(KeyPath, Args, State) \-> {Result,NewState} +Call a function already defined in the state. +\f[CR]KeyPath\f[R] is a list of keys to the function. +\f[CR]KeyPath\f[R], \f[CR]Args\f[R] and \f[CR]Result\f[R] are +\f[B]NOT\f[R] encoded/decoded. +.SS luerl:call_method(MethPath, Args, State) \-> {Result,NewState}. +Call a method already defined in the state. +\f[CR]MethPath\f[R] is a list of names to the method. +\f[CR]MethPath\f[R], \f[CR]Args\f[R] and \f[CR]Result\f[R] are +automatically encoded/decoded. +.SS luerl:call_method1(MethPath, Args, State) \-> {Result,NewState} +Call a method already defined in the state. +\f[CR]MethPath\f[R] is a list of keys to the method. +\f[CR]Keys\f[R], \f[CR]Args\f[R] and \f[CR]Result\f[R] are \f[B]NOT\f[R] +encoded/decoded. +.SS luerl:stop(State) \-> GCedState. +Garbage collects the state and (todo:) does away with it. +.SS luerl:gc(State) \-> State. +Runs the garbage collector on a state and returns the new state. +.SS luerl:set_table(KeyPath, Value, State) \-> State. +Sets a value inside the Lua state. +Value is automatically encoded. +.SS luerl:set_table1(KeyPath, Value, State) \-> State. +Sets a value inside the Lua state. +\f[CR]KeyPath\f[R] and \f[CR]Value\f[R] are \f[B]NOT\f[R] encoded. +.SS luerl:get_table(KeyPath, State) \-> {Result,State}. +Gets a value inside the Lua state. +\f[CR]KeyPath\f[R] and \f[CR]Result\f[R] are automatically encoded. +.SS luerl:get_table1(KeyPath, State) \-> {Result,State}. +Gets a value inside the Lua state. +\f[CR]KeyPath\f[R] and \f[CR]Result\f[R] are \f[B]NOT\f[R] +encoded/decoded. +.PP +You can use this function to expose an function to the Lua code by using +this interface: \f[CR]fun(Args, State) \-> {Results, State}\f[R] +.PP +Args and Results must be a list of Luerl compatible Erlang values. +.SH AUTHORS +Jean Chassoul, Robert Virding. diff --git a/doc/pdf/luerl.pdf b/doc/pdf/luerl.pdf index c98516f..c64fc3e 100644 Binary files a/doc/pdf/luerl.pdf and b/doc/pdf/luerl.pdf differ diff --git a/doc/pdf/luerl_old.pdf b/doc/pdf/luerl_old.pdf new file mode 100644 index 0000000..3ec0659 Binary files /dev/null and b/doc/pdf/luerl_old.pdf differ diff --git a/doc/src/luerl.3.md b/doc/src/luerl.3.md index f5319e2..d07ca8f 100644 --- a/doc/src/luerl.3.md +++ b/doc/src/luerl.3.md @@ -1,97 +1,115 @@ % luerl(3) % Jean Chassoul, Robert Virding -% 2018-2023 +% 2018-2024 # Name -luerl - Basic interface to the Luerl system +luerl - The basic interface to the Luerl system -# Interface functions -The **Lua State** parameter is the state of a Lua VM instance. It must be created with the **luerl:init()** call and be carried from one call to the next. +# Interface functions - New Version +The **Lua State** parameter is the state of a Lua VM instance. It must be created with the **luerl:init()** call and be carried from one call to the next. As it is possible in Lua to create self-referencing data structures, indeed the standard libraries have many instances of this, then using the functions which decode their return values will generate an error when they would cause an infinite loop during the decoding. An simple example is the top level table which contains a key **`_G`** which references the top-level table. Note that Lua **Chunks** (see definition below) can travel between different States. They are precompiled bits of code, independent of State. That you can 'carry around' this is no unique to Luerl but a low-level implementation detail of the standard Lua [language](https://lua.org), for more on chunks [read](https://www.lua.org/manual/5.3/manual.html#3.3.2) the official Lua 5.3 [reference manual](https://www.lua.org/manual/5.3/manual.html). ## Spec Definitions -**Binary** means an Erlang binary string. -**Chunks** means a portion of precompiled bytecode. -**State** means a Lua State, this *is* a Lua VM instance. -**Path** means a file system path and file name. -**KeyPath** means an Erlang list of **atoms** representing nested names, e.g. [table,pack] for table.pack. -**Keys** means Lua table keys, the keys of a key-value structure. -## Functions -**eval** and **do** functions differ only in what they return. The **do** functions return results and a new Lua State, the **eval** functions return a tuple starting on 'ok' or 'error', then the result, or cause of error. +**Binary** means an Erlang binary string. +**Chunks** means a portion of precompiled bytecode. +**State** means a Lua State, this *is* a Lua VM instance. +**Path** means a file system path and file name. +**KeyPath** means an Erlang list of **atoms** representing nested names, e.g. [table,pack] for table.pack. +**Keys** means Lua table keys, the keys of a key-value structure. - do --> {Result, State} +**CompileOptions** means a list of compiler options. Currently supported options are 'return', which returns the errors and warnings, and 'report' which will log the errors and warnings. - eval --> {ok, Result} | {error, Reason} -#### luerl:eval(String|Binary|Form, State) -> {ok, Result} | {error, Reason, StackTrace}. - Evaluate a Lua expression passed in as a string or binary, and return its result. +**LuaCallReturn = {ok, Result, State} | {lua_error, Error, State}** +This is the return value from evaluating a Lua call. -#### luerl:evalfile(Path, State) -> {ok, Result} | {error, Reason, StackTrace}. - Load and execute a file, and return the result. +## Functions -#### luerl:do(String|Binary|Form, State) -> {Result, NewState}. - Evaluate a Lua expression and return its result, and the new Lua State. +#### luerl:init() -> State + Get a new Lua State = a fresh Lua VM instance. -#### luerl:dofile(Path, State) -> {Result, NewState}. - Load and execute the Lua code in the file and return its result, and the new Lua State. Equivalent to doing luerl:do("return dofile('FileName')"). +#### luerl:gc(State) -> State + Runs the garbage collector on a state and returns the new state. -#### luerl:load(String|Binary[, CompileOptions], State) -> {ok,Function,NewState} | {error, Reason}. +#### luerl:load(String|Binary[, CompileOptions], State) -> {ok, Function, State} | CompileError Parse a Lua chunk as string or binary, and return a compiled chunk ('form'). -#### luerl:loadfile(FileName[, CompileOptions], State) -> {ok,Function,NewState} | {error, Reason}. +#### luerl:loadfile(FileName[, CompileOptions], State) -> {ok, Function, State} | CompileError Parse a Lua file, and return a compiled chunk ('form'). -#### luerl:path_loadfile([Path, ], FileName[, CompileOptions], State) -> {ok,Function,FullName,State} | {error, Reason}. +#### luerl:path_loadfile([Path, ], FileName[, CompileOptions], State) -> {ok,Function,FullName,State} | {error, Reason} Search Path until the file FileName is found. Parse the file and return a compiled chunk ('form'). If Path is not given then the path defined in the environment variable LUA_LOAD_PATH is used. -#### luerl:load_module(KeyPath, ErlangModule, State) -> State. +#### luerl:load_module(KeyPath, ErlangModule, State) -> State + Load `ErlangModule` and install its table at `KeyPath` which is **NOT** encoded. + +#### luerl:load_module_dec(EncodedKeyPath, ErlangModule, State) -> State Load `ErlangModule` and install its table at `KeyPath` which is encoded. -#### luerl:load_module1(KeyPath, ErlangModule, State) -> State. - Load `ErlangModule` and install its table at `KeyPath` which is **NOT** encoded +#### luerl:do(String|Binary|Form, State) -> {ok, Result, NewState} | {lua_error, Error, State} | CompileError + Evaluate a Lua expression and return its result which is **NOT** decoded, and the new Lua State. -#### luerl:init() -> State. - Get a new Lua State = a fresh Lua VM instance. +#### luerl:do_dec(String|Binary|Form, State) -> {ok, Result, NewState} | {lua_error, Error, State} | CompileError + Evaluate a Lua expression and return its result which is automatically decoded, and the new Lua State. -#### luerl:call(Form, Args, State) -> {Result,State} -#### luerl:call_chunk(Form, Args, State) -> {Result,State} -Call a compiled chunk or function. Use the call_chunk, call has been kept for backwards compatibility. +#### luerl:dofile(Path, State) -> {ok, Result, NewState} | {lua_error, Error, State} | CompileError + Load and execute the Lua code in the file and return its result which is **NOT** decoded, and the new Lua State. Equivalent to doing luerl:do("return dofile('FileName')"). -#### luerl:call_function(KeyPath, Args, State) -> {Result,NewState} -Call a function already defined in the state. `KeyPath` is a list of names to the function. `KeyPath`, `Args` and `Result` are automatically encoded/decoded. +#### luerl:dofile_dec(Path[, State]) -> {ok, Result, NewState} | {lua_error, Error, State} | CompileError + Load and execute the Lua code in the file and return its result which is automatically decoded, and the new Lua State. -#### luerl:call_function1(KeyPath, Args, State) -> {Result,NewState} -Call a function already defined in the state. `KeyPath` is a list of keys to the function. `KeyPath`, `Args` and `Result` are **NOT** encoded/decoded. +#### luerl:call(FuncRef, ArgRefs, State) -> {ok, Result, State} -#### luerl:call_method(MethPath, Args, State) -> {Result,NewState}. -Call a method already defined in the state. `MethPath` is a list of names to the method. `MethPath`, `Args` and `Result` are automatically encoded/decoded. +#### luerl:call_chunk(FuncRef, ArgRefs, State) -> {ok, Result, State} | {lua_error, Error, State} +Call a compiled chunk or function. Use the call_chunk, call has been kept for backwards compatibility. -#### luerl:call_method1(MethPath, Args, State) -> {Result,NewState} -Call a method already defined in the state. `MethPath` is a list of keys to the method. `Keys`, `Args` and `Result` are **NOT** encoded/decoded. +#### luerl:call_function(FuncRef | FuncPath, ArgRefs, State] -> {ok, Result, State} | {lua_error, Error, State} +Call a function already defined in the state. `Result` is **NOT** decoded. -#### luerl:stop(State) -> GCedState. - Garbage collects the state and (todo:) does away with it. +#### luerl:call_function_dec(KeyPath, Args, State) -> {ok, Result, State} | {lua_error, Error, State} +Call a function already defined in the state. `KeyPath` is a list of keys to the function. `KeyPath`, `Args` and `Result` are automatically encoded/decoded. -#### luerl:gc(State) -> State. - Runs the garbage collector on a state and returns the new state. +#### luerl:call_method(ObjRef, Method, ArgRefs, State) -> {ok, Result, State} | {lua_error, Error, State} +Call a method already defined in the state. + +#### luerl:call_method_dec(KeyPath, Method, Args, State) -> {ok, Result, State} | {lua_error, Error, State} +Call a method already defined in the state. `KeyPath` is a list of keys to the method. `KeyPath`, `Method`, `Args` and `Result` are automatically encoded/decoded. + +#### luerl:get_table_keys(KeyPath, State) -> {ok, Result, State} | {lua_error, Error, State} + Gets a value inside the Lua state. `KeyPath` and `Result` are **NOT** encoded/decoded. -#### luerl:set_table(KeyPath, Value, State) -> State. - Sets a value inside the Lua state. Value is automatically encoded. +#### luerl:get_table_keys_dec(KeyPath, State) -> {ok, Result, State} | {lua_error, Error, State} + Gets a value inside the Lua state. `KeyPath` is automatically encoded and `Result` is decoded. -#### luerl:set_table1(KeyPath, Value, State) -> State. +#### luerl:set_table_keys(KeyPath, Value, State) -> {ok,State} | {lua_error, Error, State} Sets a value inside the Lua state. `KeyPath` and `Value` are **NOT** encoded. -#### luerl:get_table(KeyPath, State) -> {Result,State}. - Gets a value inside the Lua state. `KeyPath` and `Result` are automatically encoded. +#### luerl:set_table_keys_dec(KeyPath, Value, State) -> {ok, Result, State} | {lua_error, Error, State} + Sets a value inside the Lua state. `KeyPath` and `Value` are automatically encoded and `Result` is decoded. -#### luerl:get_table1(KeyPath, State) -> {Result,State}. - Gets a value inside the Lua state. `KeyPath` and `Result` are **NOT** encoded/decoded. +#### luerl:get_table_key(Table, Key, State) -> {ok, Result, State} | {lua_error, Error, State} + Gets the value of a key in a table. `Table` and `Key` are **NOT** encoded and `Result` is **NOT** decoded. + +#### luerl:set_table_key(Table, Key, Value, State) -> {ok, State} | {lua_error, Error, State} + Sets the value of a key in a table. `Table`, `Key` and `Value` are **NOT** encoded. + +#### luerl:get_stacktrace(State) -> [{FuncName,{file,FileName},{line,Line}}] +Return a stack trace of the current call stack in the state. + +#### luerl:encode(Term, State) -> {LuerlTerm,State} +Encode the Erlang representation of a term into Luerl form updating +the state when necessary. + +#### luerl:encode_list([Term], State) -> {[LuerlTerm],State} +Encode a list of Erlang term representations into a list of Luerl +forms updating the state when necessary. - You can use this function to expose an function to the Lua code by using this interface: - `fun(Args, State) -> {Results, State}` +#### luerl:decode(LuerlTerm, State) -> Term +Decode a term in the Luerl form into its Erlang representation. - Args and Results must be a list of Luerl compatible Erlang values. +#### luerl:decode_list([LuerlTerm], State) -> [Term] +Decode a list of Luerl terms into a list of Erlang representations. diff --git a/doc/src/luerl_old.3.md b/doc/src/luerl_old.3.md new file mode 100644 index 0000000..345ec36 --- /dev/null +++ b/doc/src/luerl_old.3.md @@ -0,0 +1,97 @@ +% luerl_old(3) +% Jean Chassoul, Robert Virding +% 2018-2024 + +# Name +luerl - The old original interface to the Luerl system + +# Interface functions +The **Lua State** parameter is the state of a Lua VM instance. It must be created with the **luerl:init()** call and be carried from one call to the next. + +As it is possible in Lua to create self-referencing data structures, indeed the standard libraries have many instances of this, then using the functions which decode their return values will generate an error when they would cause an infinite loop during the decoding. An simple example is the top level table which contains a key **`_G`** which references the top-level table. + +Note that Lua **Chunks** (see definition below) can travel between different States. They are precompiled bits of code, independent of State. That you can 'carry around' this is no unique to Luerl but a low-level implementation detail of the standard Lua [language](https://lua.org), for more on chunks [read](https://www.lua.org/manual/5.3/manual.html#3.3.2) the official Lua 5.3 [reference manual](https://www.lua.org/manual/5.3/manual.html). + +## Spec Definitions +**Binary** means an Erlang binary string. +**Chunks** means a portion of precompiled bytecode. +**State** means a Lua State, this *is* a Lua VM instance. +**Path** means a file system path and file name. +**KeyPath** means an Erlang list of **atoms** representing nested names, e.g. [table,pack] for table.pack. +**Keys** means Lua table keys, the keys of a key-value structure. + +## Functions +**eval** and **do** functions differ only in what they return. The **do** functions return results and a new Lua State, the **eval** functions return a tuple starting on 'ok' or 'error', then the result, or cause of error. + + do --> {Result, State} + + eval --> {ok, Result} | {error, Reason} + +#### luerl:eval(String|Binary|Form, State) -> {ok, Result} | {error, Reason, StackTrace}. + Evaluate a Lua expression passed in as a string or binary, and return its result. + +#### luerl:evalfile(Path, State) -> {ok, Result} | {error, Reason, StackTrace}. + Load and execute a file, and return the result. + +#### luerl:do(String|Binary|Form, State) -> {Result, NewState}. + Evaluate a Lua expression and return its result, and the new Lua State. + +#### luerl:dofile(Path, State) -> {Result, NewState}. + Load and execute the Lua code in the file and return its result, and the new Lua State. Equivalent to doing luerl:do("return dofile('FileName')"). + +#### luerl:load(String|Binary[, CompileOptions], State) -> {ok,Function,NewState} | {error, Reason}. + Parse a Lua chunk as string or binary, and return a compiled chunk ('form'). + +#### luerl:loadfile(FileName[, CompileOptions], State) -> {ok,Function,NewState} | {error, Reason}. + Parse a Lua file, and return a compiled chunk ('form'). + +#### luerl:path_loadfile([Path, ], FileName[, CompileOptions], State) -> {ok,Function,FullName,State} | {error, Reason}. + Search Path until the file FileName is found. Parse the file and return a compiled chunk ('form'). If Path is not given then the path defined in the environment variable LUA_LOAD_PATH is used. + +#### luerl:load_module(KeyPath, ErlangModule, State) -> State. + Load `ErlangModule` and install its table at `KeyPath` which is encoded. + +#### luerl:load_module1(KeyPath, ErlangModule, State) -> State. + Load `ErlangModule` and install its table at `KeyPath` which is **NOT** encoded + +#### luerl:init() -> State. + Get a new Lua State = a fresh Lua VM instance. + +#### luerl:call(Form, Args, State) -> {Result,State} +#### luerl:call_chunk(Form, Args, State) -> {Result,State} +Call a compiled chunk or function. Use the call_chunk, call has been kept for backwards compatibility. + +#### luerl:call_function(KeyPath, Args, State) -> {Result,NewState} +Call a function already defined in the state. `KeyPath` is a list of names to the function. `KeyPath`, `Args` and `Result` are automatically encoded/decoded. + +#### luerl:call_function1(KeyPath, Args, State) -> {Result,NewState} +Call a function already defined in the state. `KeyPath` is a list of keys to the function. `KeyPath`, `Args` and `Result` are **NOT** encoded/decoded. + +#### luerl:call_method(MethPath, Args, State) -> {Result,NewState}. +Call a method already defined in the state. `MethPath` is a list of names to the method. `MethPath`, `Args` and `Result` are automatically encoded/decoded. + +#### luerl:call_method1(MethPath, Args, State) -> {Result,NewState} +Call a method already defined in the state. `MethPath` is a list of keys to the method. `Keys`, `Args` and `Result` are **NOT** encoded/decoded. + +#### luerl:stop(State) -> GCedState. + Garbage collects the state and (todo:) does away with it. + +#### luerl:gc(State) -> State. + Runs the garbage collector on a state and returns the new state. + +#### luerl:set_table(KeyPath, Value, State) -> State. + Sets a value inside the Lua state. Value is automatically encoded. + +#### luerl:set_table1(KeyPath, Value, State) -> State. + Sets a value inside the Lua state. `KeyPath` and `Value` are **NOT** encoded. + +#### luerl:get_table(KeyPath, State) -> {Result,State}. + Gets a value inside the Lua state. `KeyPath` and `Result` are automatically encoded. + +#### luerl:get_table1(KeyPath, State) -> {Result,State}. + Gets a value inside the Lua state. `KeyPath` and `Result` are **NOT** encoded/decoded. + + You can use this function to expose an function to the Lua code by using this interface: + `fun(Args, State) -> {Results, State}` + + Args and Results must be a list of Luerl compatible Erlang values. diff --git a/src/luerl.erl b/src/luerl.erl index cd270ae..a90dfc1 100644 --- a/src/luerl.erl +++ b/src/luerl.erl @@ -1,4 +1,4 @@ -%% Copyright (c) 2013-2020 Robert Virding +%% Copyright (c) 2020-2024 Robert Virding %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -12,94 +12,94 @@ %% See the License for the specific language governing permissions and %% limitations under the License. -%% File : luerl.erl -%% Authors : Robert Virding, Henning Diedrich -%% Purpose : The original basic LUA 5.2 interface. +%% File : luerl_new.erl +%% Authors : Robert Virding +%% Purpose : The new basic LUA 5.3 interface. -module(luerl). -include("luerl.hrl"). --export([eval/2,evalfile/2, - do/2,dofile/2, - load/2,load/3, - loadfile/2,loadfile/3, - path_loadfile/2,path_loadfile/3,path_loadfile/4, - load_module/3,load_module1/3, - call/3,call_chunk/3, - call_function/3,call_function1/3,function_list/2, - call_method/3,call_method1/3,method_list/2, - get_table/2,get_table1/2,set_table/3,set_table1/3,set_table1/4, - init/0,stop/1,gc/1, - set_trace_func/2,clear_trace_func/1, - set_trace_data/2,get_trace_data/1, - get_stacktrace/1, - externalize/1,internalize/1 - ]). +%% Basic user API to luerl. +-export([init/0,gc/1, + load/2,load/3,loadfile/2,loadfile/3, + path_loadfile/2,path_loadfile/3,path_loadfile/4, + load_module/3,load_module_dec/3, + do/2,do_dec/2,do/3,do_dec/3, + dofile/2,dofile/3,dofile_dec/2,dofile_dec/3, + call/3,call_chunk/2,call_chunk/3, + call_function/3,call_function_enc/3,call_function_dec/3, + call_method/4,call_method_dec/4, + get_table_keys/2,get_table_keys_dec/2, + set_table_keys/3,set_table_keys_dec/3, + get_table_key/3,set_table_key/4, + get_stacktrace/1 + ]). + +%% Tracing. +-export([set_trace_func/2,clear_trace_func/1, + set_trace_data/2,get_trace_data/1]). %% Encoding and decoding. -export([encode/2,encode_list/2,decode/2,decode_list/2]). -%% luerl:eval(String|Binary|Form, State) -> Result. +%% Helping with storing VM state +-export([externalize/1,internalize/1]). -eval(Chunk, St0) -> - try do(Chunk, St0) of - {Ret,St1} -> {ok, decode_list(Ret, St1)} - catch - ?CATCH(_C, E, S) - {error, E, S} %{error, {E, R}} ? <- todo: decide - end. +%% init() -> State. -%% luerl:evalfile(Path, State) -> {ok, Result} | {error,Reason}. +init() -> + luerl_emul:init(). -evalfile(Path, St0) -> - try dofile(Path, St0) of - {Ret,St1} -> {ok, decode_list(Ret, St1)} - catch - ?CATCH(_C, E, S) - {error, E, S} %{error, {E, R}} ? <- todo: decide - end. +%% gc(State) -> State. +gc(St) -> + luerl_heap:gc(St). + +%% set_trace_func(TraceFunction, State) -> State. +%% clear_trace_func(State) -> State. +%% get_trace_data(State) -> TraceData. +%% set_trace_data(TraceData, State) -> State. +%% Set the trace function and access the trace data. -%% luerl:do(String|Binary|Form, State) -> {Result, NewState} +set_trace_func(Tfunc, St) -> + St#luerl{trace_func=Tfunc}. -do(S, St0) when is_binary(S); is_list(S) -> - {ok,Func,St1} = load(S, St0), - luerl_emul:call(Func, St1); -do(Func, St) -> - luerl_emul:call(Func, St). +clear_trace_func(St) -> + St#luerl{trace_func=none}. -%% luerl:dofile(Path, State) -> {Result, NewState}. +get_trace_data(St) -> + St#luerl.trace_data. -dofile(Path, St0) -> - {ok,Func,St1} = loadfile(Path, St0), - luerl_emul:call(Func, St1). +set_trace_data(Tdata, St) -> + St#luerl{trace_data=Tdata}. -%% load(String|Binary, State) -> {ok,Function,NewState}. -%% load(String|Binary, Options, State) -> {ok,Function,NewState}. +%% load(String|Binary, State) -> {ok,FuncRef,NewState}. +%% load(String|Binary, Options, State) -> {ok,FuncRef,NewState}. load(Bin, St) -> load(Bin, [return], St). load(Bin, Opts, St) when is_binary(Bin) -> - load(binary_to_list(Bin), Opts, St); -load(Str, Opts, St0) when is_list(Str) -> + load(binary_to_list(Bin), Opts, St); +load(Str, Opts, St0) -> case luerl_comp:string(Str, Opts) of - {ok,Chunk} -> - {Func,St1} = luerl_emul:load_chunk(Chunk, St0), - {ok,Func,St1}; - {error,_,_}=E -> E + {ok,Chunk} -> + {FunRef,St1} = luerl_emul:load_chunk(Chunk, St0), + {ok,FunRef,St1}; + Error -> %Compile error + Error end. -%% loadfile(FileName, State) -> {ok,Function,NewState}. -%% loadfile(FileName, Options, State) -> {ok,Function,NewState}. +%% loadfile(FileName, State) -> {ok,FuncRef,NewState}. +%% loadfile(FileName, Options, State) -> {ok,FuncRef,NewState}. loadfile(Name, St) -> loadfile(Name, [return], St). loadfile(Name, Opts, St0) -> case luerl_comp:file(Name, Opts) of - {ok,Chunk} -> - {Func,St1} = luerl_emul:load_chunk(Chunk, St0), - {ok,Func,St1}; - {error,_,_}=E -> E + {ok,Chunk} -> + {Func,St1} = luerl_emul:load_chunk(Chunk, St0), + {ok,Func,St1}; + Error -> Error end. %% path_loadfile(FileName, State) -> {ok,Function,FullName,State}. @@ -112,15 +112,15 @@ loadfile(Name, Opts, St0) -> path_loadfile(Name, St) -> Path = case os:getenv("LUA_LOAD_PATH") of - false -> []; %You get what you asked for - Env -> - %% Get path separator depending on os type. - Sep = case os:type() of - {win32,_} -> ";"; - _ -> ":" %Unix - end, - string:tokens(Env, Sep) %Split into path list - end, + false -> []; %You get what you asked for + Env -> + %% Get path separator depending on os type. + Sep = case os:type() of + {win32,_} -> ";"; %Windows + _ -> ":" %Unix + end, + string:tokens(Env, Sep) %Split into path list + end, path_loadfile(Path, Name, [return], St). path_loadfile(Dirs, Name, St) -> @@ -129,161 +129,211 @@ path_loadfile(Dirs, Name, St) -> path_loadfile([Dir|Dirs], Name, Opts, St0) -> Full = filename:join(Dir, Name), case loadfile(Full, Opts, St0) of - {ok,Func,St1} -> - {ok,Func,Full,St1}; - {error,[{_,_,enoent}],_} -> %Couldn't find the file - path_loadfile(Dirs, Name, St0); - Error -> Error + {ok,Func,St1} -> + {ok,Func,Full,St1}; + {error,[{_,_,enoent}],_} -> %Couldn't find the file + path_loadfile(Dirs, Name, St0); + Error -> Error end; path_loadfile([], _, _, _) -> {error,[{none,file,enoent}],[]}. -%% load_module(TablePath, ModuleName, State) -> State. -%% load_module1(LuaTablePath, ModuleName, State) -> State. +%% load_module(LuaTablePath, ModuleName, State) -> State. %% Load module and add module table to the path. -load_module(Fp, Mod, St0) when is_list(Fp) -> - {Lfp,St1} = encode_list(Fp, St0), - load_module1(Lfp, Mod, St1); -load_module(_, _,_) -> error(badarg). - -load_module1(Lfp, Mod, St0) -> +load_module([_|_] = Lfp, Mod, St0) -> {Tab,St1} = Mod:install(St0), - luerl_emul:set_table_keys(Lfp, Tab, St1). + luerl_emul:set_table_keys(Lfp, Tab, St1); +load_module(_, _, _) -> + error(badarg). -%% init() -> State. -init() -> luerl_emul:init(). - -%% call(Chunk, Args, State) -> {Result,State} - -call(C, As, St) -> call_chunk(C, As, St). - -call_chunk(C, As, St0) -> - {Las,St1} = encode_list(As, St0), - {Lrs,St2} = luerl_emul:call(C, Las, St1), - Rs = decode_list(Lrs, St2), - {Rs,St2}. - -%% call_function(TablePath, Args, State) -> {Result,State}. -%% call_function1(LuaTablePath | Func, LuaArgs, State) -> {LuaResult,State}. - -call_function(Fp, As, St0) -> - %% Encode the input arguments. - %% file:write_file("/Users/rv/lt", io_lib:format("cfp ~p\n", [Fp]), [append]), - {Lfp,St1} = encode_list(Fp, St0), - {Las,St2} = encode_list(As, St1), - %% Find the function definition and call function. - %% file:write_file("/Users/rv/lt", io_lib:format("cfa ~p\n", [{Lfp,Las}]), [append]), - {Lrs,St3} = call_function1(Lfp, Las, St2), - %% file:write_file("/Users/rv/lt", io_lib:format("cfl ~p\n", [Lrs]), [append]), - Rs = decode_list(Lrs, St3), - %% file:write_file("/Users/rv/lt", io_lib:format("cfr ~p\n", [Rs]), [append]), - {Rs,St3}. - -call_function1(Lfp, Las, St0) when is_list(Lfp) -> - {F,St1} = luerl_emul:get_table_keys(Lfp, St0), - %% file:write_file("/Users/rv/lt", io_lib:format("cf1 ~p\n", [F]), [append]), - luerl_emul:functioncall(F, Las, St1); -call_function1(F, Las, St) -> - luerl_emul:functioncall(F, Las, St). - -%% function_list(Keys, State) -> {V,State}. -%% Go down a list of keys and return final value. - -function_list(Ks, St) -> luerl_emul:get_table_keys(Ks, St). - -%% call_method(FuncPath, Args, State) -> {Result,State}. -%% call_method1(FuncPath | FuncPath, Args, State) -> {Result,State}. - -call_method(Fp, As, St0) -> - %% Encode the input arguments. - {Lfp,St1} = encode_list(Fp, St0), - {Las,St2} = encode_list(As, St1), - %% Find the object and method definition and call method. - {O,M,St3} = method_list(Lfp, St2), - {Lrs,St4} = luerl_emul:functioncall(M, [O|Las], St3), - Rs = decode_list(Lrs, St4), - {Rs,St4}. - -call_method1(Fp, Las, St0) -> - %% Find the object and method definition and call method. - {O,M,St1} = method_list(Fp, St0), - luerl_emul:functioncall(M, [O|Las], St1). - -method_list([G|Ks], St0) -> - {First,St1} = luerl_emul:get_global_key(G, St0), - method_list(First, Ks, St1). - -method_list(Tab, [K], St0) -> - {Func,St1} = luerl_emul:get_table_key(Tab, K, St0), - {Tab,Func,St1}; -method_list(Tab, [K|Ks], St0) -> - {Next,St1} = luerl_emul:get_table_key(Tab, K, St0), - method_list(Next, Ks, St1); -method_list(_, _, _) -> error(badarg). - -%% get_table(TablePath, State) -> {Result, State}. -%% Go down a list of keys and return decoded final value. - -get_table(Fp, St0) when is_list(Fp) -> - {Lfp,St1} = encode_list(Fp, St0), - {V,St2} = luerl_emul:get_table_keys(Lfp, St1), - Vd = decode(V, St2), - {Vd,St2}; -get_table(_,_) -> error(badarg). - -%% get_table1(LuaTablePath, State) -> {LuaResult, State}. - -get_table1(Fp, St) when is_list(Fp) -> - luerl_emul:get_table_keys(Fp, St); -get_table1(_,_) -> error(badarg). - -%% set_table(TablePath, Value, State) -> State. -%% Go down a list of keys and set final key to Value. - -set_table(Fp, V, St0) when is_list(Fp) -> - {Lfp,St1} = encode_list(Fp, St0), - {Lv, St2} = encode(V, St1), - set_table1(Lfp, Lv, St2); -set_table(_,_,_) -> error(badarg). - -%% set_table1(LuaTablePath, Value, State) -> State. -%% Must explicitly read table key to get - -set_table1(Lfp, Lv, St) -> - luerl_emul:set_table_keys(Lfp, Lv, St). - -%% set_table1(Table, Key, Value, State) -> State. -%% Must explicitly read table key to get - -set_table1(Tab, Key, Lv, St) -> - luerl_emul:set_table_key(Tab, Key, Lv, St). - -%% stop(State) -> GCedState. -stop(St) -> - luerl_heap:gc(St). +%% load_module_dec(DecodedTablePath, ModuleName, State) -> State. +%% Load module and add module table to the path. -%% gc(State) -> State. -gc(St) -> - luerl_heap:gc(St). +load_module_dec([_|_] = Dfp, Mod, St0) -> + {Efp,St1} = encode_list(Dfp, St0), + load_module(Efp, Mod, St1); +load_module_dec(_, _, _) -> + error(badarg). -%% set_trace_func(TraceFunction, State) -> State. -%% clear_trace_func(State) -> State. -%% get_trace_data(State) -> TraceData. -%% set_trace_data(TraceData, State) -> State. -%% Set the trace function and access the trace data. +%% luerl:do(String|Binary|Form, State) -> +%% luerl:do(String|Binary|Form, CompileOptions, State) -> +%% {ok,Result,NewState} | {lua_error,Error,State}. -set_trace_func(Tfunc, St) -> - St#luerl{trace_func=Tfunc}. +do(S, St) -> do(S, [return], St). -clear_trace_func(St) -> - St#luerl{trace_func=none}. +do(S, Opts, St0) -> + case load(S, Opts, St0) of + {ok,Func,St1} -> + call_function(Func, [], St1); + Error -> Error + end. -get_trace_data(St) -> - St#luerl.trace_data. +do_dec(S, St) -> + do_dec(S, [return], St). -set_trace_data(Tdata, St) -> - St#luerl{trace_data=Tdata}. +do_dec(S, Opts, St0) -> + case do(S, Opts, St0) of + {ok,Eret,St1} -> + {ok,decode_list(Eret, St1),St1}; + Error -> Error + end. + +%% luerl:dofile(FileName, State) -> +%% luerl:dofile(FileName, CompileOptions, State) -> +%% {ok,Result,NewState} | {lua_error,Error,State}. + +dofile(File, St) -> dofile(File, [], St). + +dofile(File, Opts, St0) -> + case loadfile(File, Opts, St0) of + {ok,Func,St1} -> + call_function(Func, [], St1); + Error -> Error + end. + +dofile_dec(File, St) -> + dofile_dec(File, [], St). + +dofile_dec(File, Opts, St0) -> + case dofile(File, Opts, St0) of + {ok,Eret,St1} -> + {ok,decode_list(Eret, St1),St1}; + Error -> Error + end. + +%% call(FuncRef, Args, State) -> +%% call_chunk(FuncRef, State) -> +%% call_chunk(FuncRef, Args, State) -> +%% {ok,Return,State} | {lua_error,Error,State}. + +call(C, As, St) -> + call_function(C, As, St). + +call_chunk(C, St) -> + call_chunk(C, [], St). + +call_chunk(C, As, St) -> + call_function(C, As, St). + +%% call_function(LuaFuncRef | LuaTablePath, Args, State) -> +%% {ok,LuaReturn,State} | {lua_error,Error,State}. + +call_function(Epath, Args, St0) when is_list(Epath) -> + {ok,Efunc,St1} = get_table_keys(Epath, St0), + call_function(Efunc, Args, St1); +call_function(Func, Args, St0) -> + try + {Ret,St1} = luerl_emul:functioncall(Func, Args, St0), + {ok,Ret,St1} + catch + error:{lua_error,_E,_St} = LuaErr -> + LuaErr + end. + +%% call_function_enc(DecodedFuncRef, Args, State) -> +%% {ok,LuaReturn,State} | {lua_error,Error,State}. + +call_function_enc(Dtpath, Dargs, St0) -> + {Epath,St1} = encode_list(Dtpath, St0), + {Eargs,St2} = encode_list(Dargs, St1), + call_function(Epath, Eargs, St2). + +%% call_function_dec(DecodedFuncRef, Args, State) -> +%% {ok,DecodedReturn,State} | {lua_error,Error,State}. + +call_function_dec(Dtpath, Dargs, St0) -> + case call_function_enc(Dtpath, Dargs, St0) of + {ok,Eret,St1} -> + {ok,decode_list(Eret, St1),St1}; + LuaError -> LuaError + end. + +%% call_method(LuaObject, Method, Args, State) -> +%% {ok,Return,State} | {lua_error,Error,State}. + +call_method(Obj, Meth, Args, St0) -> + try + {Ret,St1} = luerl_emul:methodcall(Obj, Meth, Args, St0), + {ok,Ret,St1} + catch + error:{lua_error,_E,_St} = LuaErr -> + LuaErr + end. + +%% call_method_dec(DecodedObject, Method, Args, State) -> +%% {ok,DecodedReturn,State} | {lua_error,Error,State}. + +call_method_dec(Dobj, Dmeth, Dargs, St0) -> + {ok,Eobj,St1} = get_table_keys_dec(Dobj, St0), + {Emeth,St2} = encode(Dmeth, St1), + {Eargs,St3} = encode_list(Dargs, St2), + case call_method(Eobj, Emeth, Eargs, St3) of + {ok,Eret,St4} -> + {ok,decode_list(Eret, St4),St4}; + LuaError -> LuaError + end. + +%% get_table_keys(Keys, State) -> +%% get_table_keys_dec(DecodedKeys, State) -> +%% {ok,Return,State} | {lua_error,Error,State}. +%% set_table_keys(Keys, Val, State) -> +%% set_table_keys_dec(DecodedKeys, DecodedVal, State) -> +%% {ok,State} | {lua_error,Error,State}. + +get_table_keys(Keys, St0) -> + try + {Eret,St1} = luerl_emul:get_table_keys(Keys, St0), + {ok,Eret,St1} + catch + error:{lua_error,_E,_St} = LuaErr -> + LuaErr + end. + +get_table_keys_dec(Dkeys, St0) -> + {Ekeys,St1} = encode_list(Dkeys, St0), + case get_table_keys(Ekeys, St1) of + {ok,Eret,St2} -> + {ok,decode(Eret, St2),St2}; + LuaError -> LuaError + end. + +set_table_keys(Keys, Val, St0) -> + try + St1 = luerl_emul:set_table_keys(Keys, Val, St0), + {ok,St1} + catch + error:{lua_error,_E,_St} = LuaErr -> + LuaErr + end. + +set_table_keys_dec(Dkeys, Dval, St0) -> + {Ekeys,St1} = encode_list(Dkeys, St0), + {Eval,St2} = encode(Dval, St1), + set_table_keys(Ekeys, Eval, St2). + +%% get_table_key(Tab, Key, State) -> +%% {ok,Value,State} | {lua_error,Error,State}. +%% set_table_key(Tab, Key, Value, State) -> +%% {ok,State} | {lua_error,Error,State}. + +get_table_key(Tab, Key, St0) -> + try + {Eret,St1} = luerl_emul:get_table_key(Tab, Key, St0), + {ok,Eret,St1} + catch + error:{lua_error,_E,_St} = LuaErr -> + LuaErr + end. + +set_table_key(Tab, Key, Val, St0) -> + try + St1 = luerl_emul:set_table_key(Tab, Key, Val, St0), + {ok,St1} + catch + error:{lua_error,_E,_St} = LuaErr -> + LuaErr + end. %% get_stacktrace(State) -> [{FuncName,[{file,FileName},{line,Line}]}]. @@ -366,11 +416,14 @@ encode(L, St0) when is_list(L) -> {T,St2}; %No more to do for now encode(F, St) when is_function(F, 2) -> F1 = fun(Args, State) -> F(Args, State) end, + %% io:format("enc ~p\n", [#erl_func{code=F1}]), {#erl_func{code=F1}, St}; encode(F, St) when is_function(F, 1) -> F1 = fun(Args, State) -> Res = F(Args), {Res,State} end, + %% io:format("enc ~p\n", [#erl_func{code=F1}]), {#erl_func{code=F1}, St}; encode({M,F,A}, St) when is_atom(M) and is_atom(F) -> + %% io:format("enc ~p\n", [#erl_mfa{m=M,f=F,a=A}]), {#erl_mfa{m=M,f=F,a=A}, St}; encode({userdata,Data}, St) -> luerl_heap:alloc_userdata(Data, St); diff --git a/src/luerl_emul.erl b/src/luerl_emul.erl index 6407a17..6e26f6b 100644 --- a/src/luerl_emul.erl +++ b/src/luerl_emul.erl @@ -1,4 +1,4 @@ -%% Copyright (c) 2013-2023 Robert Virding +%% Copyright (c) 2013-2024 Robert Virding %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/src/luerl_old.erl b/src/luerl_old.erl index b978067..f3e90d3 100644 --- a/src/luerl_old.erl +++ b/src/luerl_old.erl @@ -1,4 +1,4 @@ -%% Copyright (c) 2013-2021 Robert Virding +%% Copyright (c) 2013-2024 Robert Virding %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -14,18 +14,432 @@ %% File : luerl_old.erl %% Authors : Robert Virding, Henning Diedrich -%% Purpose : The old basic LUA 5.2 interface. - -%% This module is just an interface to the older luerl.erl which might -%% make it easier to migrate the module names if we decide to do so in -%% the future. +%% Purpose : The original basic LUA 5.2 interface. -module(luerl_old). --export(['$handle_undefined_function'/2]). +-include("luerl.hrl"). + +-export([eval/2,evalfile/2, + do/2,dofile/2, + load/2,load/3, + loadfile/2,loadfile/3, + path_loadfile/2,path_loadfile/3,path_loadfile/4, + load_module/3,load_module1/3, + call/3,call_chunk/3, + call_function/3,call_function1/3,function_list/2, + call_method/3,call_method1/3,method_list/2, + get_table/2,get_table1/2,set_table/3,set_table1/3,set_table1/4, + init/0,stop/1,gc/1, + set_trace_func/2,clear_trace_func/1, + set_trace_data/2,get_trace_data/1, + get_stacktrace/1, + externalize/1,internalize/1 + ]). + +%% Encoding and decoding. +-export([encode/2,encode_list/2,decode/2,decode_list/2]). + +%% luerl_old:eval(String|Binary|Form, State) -> Result. + +eval(Chunk, St0) -> + try do(Chunk, St0) of + {Ret,St1} -> {ok, decode_list(Ret, St1)} + catch + ?CATCH(_C, E, S) + {error, E, S} %{error, {E, R}} ? <- todo: decide + end. + +%% luerl_old:evalfile(Path, State) -> {ok, Result} | {error,Reason}. + +evalfile(Path, St0) -> + try dofile(Path, St0) of + {Ret,St1} -> {ok, decode_list(Ret, St1)} + catch + ?CATCH(_C, E, S) + {error, E, S} %{error, {E, R}} ? <- todo: decide + end. + +%% luerl_old:do(String|Binary|Form, State) -> {Result, NewState} + +do(S, St0) when is_binary(S); is_list(S) -> + {ok,Func,St1} = load(S, St0), + luerl_emul:call(Func, St1); +do(Func, St) -> + luerl_emul:call(Func, St). + +%% luerl_old:dofile(Path, State) -> {Result, NewState}. + +dofile(Path, St0) -> + {ok,Func,St1} = loadfile(Path, St0), + luerl_emul:call(Func, St1). + +%% load(String|Binary, State) -> {ok,Function,NewState}. +%% load(String|Binary, Options, State) -> {ok,Function,NewState}. + +load(Bin, St) -> load(Bin, [return], St). + +load(Bin, Opts, St) when is_binary(Bin) -> + load(binary_to_list(Bin), Opts, St); +load(Str, Opts, St0) when is_list(Str) -> + case luerl_comp:string(Str, Opts) of + {ok,Chunk} -> + {Func,St1} = luerl_emul:load_chunk(Chunk, St0), + {ok,Func,St1}; + {error,_,_}=E -> E + end. + +%% loadfile(FileName, State) -> {ok,Function,NewState}. +%% loadfile(FileName, Options, State) -> {ok,Function,NewState}. + +loadfile(Name, St) -> loadfile(Name, [return], St). + +loadfile(Name, Opts, St0) -> + case luerl_comp:file(Name, Opts) of + {ok,Chunk} -> + {Func,St1} = luerl_emul:load_chunk(Chunk, St0), + {ok,Func,St1}; + {error,_,_}=E -> E + end. + +%% path_loadfile(FileName, State) -> {ok,Function,FullName,State}. +%% path_loadfile(Path, FileName, State) -> {ok,Function,FullName,State}. +%% path_loadfile(Path, FileName, Options, State) -> +%% {ok,Function,FullName,State}. +%% When no path is given we use the value of LUA_LOAD_PATH. +%% We manually step down the path to get the correct handling of +%% filenames by the compiler. + +path_loadfile(Name, St) -> + Path = case os:getenv("LUA_LOAD_PATH") of + false -> []; %You get what you asked for + Env -> + %% Get path separator depending on os type. + Sep = case os:type() of + {win32,_} -> ";"; + _ -> ":" %Unix + end, + string:tokens(Env, Sep) %Split into path list + end, + path_loadfile(Path, Name, [return], St). + +path_loadfile(Dirs, Name, St) -> + path_loadfile(Dirs, Name, [return], St). + +path_loadfile([Dir|Dirs], Name, Opts, St0) -> + Full = filename:join(Dir, Name), + case loadfile(Full, Opts, St0) of + {ok,Func,St1} -> + {ok,Func,Full,St1}; + {error,[{_,_,enoent}],_} -> %Couldn't find the file + path_loadfile(Dirs, Name, St0); + Error -> Error + end; +path_loadfile([], _, _, _) -> + {error,[{none,file,enoent}],[]}. + +%% load_module(TablePath, ModuleName, State) -> State. +%% load_module1(LuaTablePath, ModuleName, State) -> State. +%% Load module and add module table to the path. + +load_module(Fp, Mod, St0) when is_list(Fp) -> + {Lfp,St1} = encode_list(Fp, St0), + load_module1(Lfp, Mod, St1); +load_module(_, _,_) -> error(badarg). + +load_module1(Lfp, Mod, St0) -> + {Tab,St1} = Mod:install(St0), + luerl_emul:set_table_keys(Lfp, Tab, St1). + +%% init() -> State. +init() -> luerl_emul:init(). + +%% call(Chunk, Args, State) -> {Result,State} + +call(C, As, St) -> call_chunk(C, As, St). + +call_chunk(C, As, St0) -> + {Las,St1} = encode_list(As, St0), + {Lrs,St2} = luerl_emul:call(C, Las, St1), + Rs = decode_list(Lrs, St2), + {Rs,St2}. + +%% call_function(TablePath, Args, State) -> {Result,State}. +%% call_function1(LuaTablePath | Func, LuaArgs, State) -> {LuaResult,State}. + +call_function(Fp, As, St0) -> + %% Encode the input arguments. + {Lfp,St1} = encode_list(Fp, St0), + {Las,St2} = encode_list(As, St1), + %% Find the function definition and call function. + {Lrs,St3} = call_function1(Lfp, Las, St2), + Rs = decode_list(Lrs, St3), + {Rs,St3}. + +call_function1(Lfp, Las, St0) when is_list(Lfp) -> + {F,St1} = luerl_emul:get_table_keys(Lfp, St0), + luerl_emul:functioncall(F, Las, St1); +call_function1(F, Las, St) -> + luerl_emul:functioncall(F, Las, St). + +%% function_list(Keys, State) -> {V,State}. +%% Go down a list of keys and return final value. + +function_list(Ks, St) -> luerl_emul:get_table_keys(Ks, St). + +%% call_method(FuncPath, Args, State) -> {Result,State}. +%% call_method1(FuncPath | FuncPath, Args, State) -> {Result,State}. + +call_method(Fp, As, St0) -> + %% Encode the input arguments. + {Lfp,St1} = encode_list(Fp, St0), + {Las,St2} = encode_list(As, St1), + %% Find the object and method definition and call method. + {O,M,St3} = method_list(Lfp, St2), + {Lrs,St4} = luerl_emul:functioncall(M, [O|Las], St3), + Rs = decode_list(Lrs, St4), + {Rs,St4}. + +call_method1(Fp, Las, St0) -> + %% Find the object and method definition and call method. + {O,M,St1} = method_list(Fp, St0), + luerl_emul:functioncall(M, [O|Las], St1). + +method_list([G|Ks], St0) -> + {First,St1} = luerl_emul:get_global_key(G, St0), + method_list(First, Ks, St1). + +method_list(Tab, [K], St0) -> + {Func,St1} = luerl_emul:get_table_key(Tab, K, St0), + {Tab,Func,St1}; +method_list(Tab, [K|Ks], St0) -> + {Next,St1} = luerl_emul:get_table_key(Tab, K, St0), + method_list(Next, Ks, St1); +method_list(_, _, _) -> error(badarg). + +%% get_table(TablePath, State) -> {Result, State}. +%% Go down a list of keys and return decoded final value. + +get_table(Fp, St0) when is_list(Fp) -> + {Lfp,St1} = encode_list(Fp, St0), + {V,St2} = luerl_emul:get_table_keys(Lfp, St1), + Vd = decode(V, St2), + {Vd,St2}; +get_table(_,_) -> error(badarg). + +%% get_table1(LuaTablePath, State) -> {LuaResult, State}. + +get_table1(Fp, St) when is_list(Fp) -> + luerl_emul:get_table_keys(Fp, St); +get_table1(_,_) -> error(badarg). + +%% set_table(TablePath, Value, State) -> State. +%% Go down a list of keys and set final key to Value. + +set_table(Fp, V, St0) when is_list(Fp) -> + {Lfp,St1} = encode_list(Fp, St0), + {Lv, St2} = encode(V, St1), + set_table1(Lfp, Lv, St2); +set_table(_,_,_) -> error(badarg). + +%% set_table1(LuaTablePath, Value, State) -> State. +%% Must explicitly read table key to get + +set_table1(Lfp, Lv, St) -> + luerl_emul:set_table_keys(Lfp, Lv, St). + +%% set_table1(Table, Key, Value, State) -> State. +%% Must explicitly read table key to get + +set_table1(Tab, Key, Lv, St) -> + luerl_emul:set_table_key(Tab, Key, Lv, St). + +%% stop(State) -> GCedState. +stop(St) -> + luerl_heap:gc(St). + +%% gc(State) -> State. +gc(St) -> + luerl_heap:gc(St). + +%% set_trace_func(TraceFunction, State) -> State. +%% clear_trace_func(State) -> State. +%% get_trace_data(State) -> TraceData. +%% set_trace_data(TraceData, State) -> State. +%% Set the trace function and access the trace data. + +set_trace_func(Tfunc, St) -> + St#luerl{trace_func=Tfunc}. + +clear_trace_func(St) -> + St#luerl{trace_func=none}. + +get_trace_data(St) -> + St#luerl.trace_data. + +set_trace_data(Tdata, St) -> + St#luerl{trace_data=Tdata}. + +%% get_stacktrace(State) -> [{FuncName,[{file,FileName},{line,Line}]}]. + +get_stacktrace(#luerl{cs=Stack}=St) -> + Fun = fun (Frame, Acc) -> do_stackframe(Frame, Acc, St) end, + {_,Trace} = lists:foldl(Fun, {1,[]}, Stack), + lists:reverse(Trace). + +do_stackframe(#call_frame{func=Funref,args=Args}, {Line,Trace}, St) -> + case Funref of + #funref{} -> + {Func,_} = luerl_heap:get_funcdef(Funref, St), + Anno = Func#lua_func.anno, + Name = case luerl_anno:get(name, Anno) of + undefined -> <<"-no-name-">>; + N -> N + end, + File = luerl_anno:get(file, Anno), + {Line,[{Name,Args,[{file,File},{line,Line}]} | Trace]}; + #erl_func{code=Fun} -> + {module,Module} = erlang:fun_info(Fun, module), + {name,Name} = erlang:fun_info(Fun, name), + FileName = get_filename(Module), + {Line,[{{Module,Name},Args,[{file,FileName}]} | Trace]}; + #erl_mfa{m=M,f=F,a=A} -> + FileName = get_filename(M), + %% {Line,[{{M,F},{A,Args},[{file,FileName}]} | Trace]}; + %% {Line,[{{M,F},[A | Args],[{file,FileName}]} | Trace]}; + {Line,[{{M,F,A},Args,[{file,FileName}]} | Trace]}; + Other -> + {Line,[{Other,Args,[{file,<<"-no-file-">>},{line,Line}]} | Trace]} + end; +do_stackframe(#current_line{line=Line}, {_,Trace}, _St) -> + {Line,Trace}; +do_stackframe(#loop_frame{}, Acc, _St) -> %Ignore these + Acc. + +get_filename(Mod) -> + Comp = erlang:get_module_info(Mod, compile), + case lists:keyfind(source, 1, Comp) of + {source,FileName} -> + BaseName = filename:basename(FileName), + list_to_binary(BaseName); + false -> %The compiler doesn't know + <<"-no-file-">> + end. + +%% Define IS_MAP/1 macro for is_map/1 bif. +-ifdef(HAS_MAPS). +-define(IS_MAP(T), is_map(T)). +-else. +-define(IS_MAP(T), false). +-endif. + +%% encode_list([Term], State) -> {[LuerlTerm],State}. +%% encode(Term, State) -> {LuerlTerm,State}. + +encode_list(Ts, St) -> + lists:mapfoldl(fun encode/2, St, Ts). + +encode(nil, St) -> {nil,St}; +encode(false, St) -> {false,St}; +encode(true, St) -> {true,St}; +encode(B, St) when is_binary(B) -> {B,St}; +encode(A, St) when is_atom(A) -> {atom_to_binary(A, utf8),St}; +encode(N, St) when is_number(N) -> {N,St}; %Integers and floats +encode(F, St) when ?IS_MAP(F) -> encode(maps:to_list(F), St); +encode(L, St0) when is_list(L) -> + {Es,{_,St1}} = lists:mapfoldl(fun ({K0,V0}, {I,S0}) -> + {K1,S1} = encode(K0, S0), + {V1,S2} = encode(V0, S1), + {{K1,V1},{I,S2}}; + (V0, {I,S0}) -> + {V1,S1} = encode(V0, S0), + {{I,V1},{I+1,S1}} + end, {1,St0}, L), + {T,St2} = luerl_heap:alloc_table(Es, St1), + {T,St2}; %No more to do for now +encode(F, St) when is_function(F, 2) -> + F1 = fun(Args, State) -> + Args1 = decode_list(Args, State), + {Res, State1} = F(Args1, State), + encode_list(Res, State1) + end, + {#erl_func{code=F1}, St}; +encode(F, St) when is_function(F, 1) -> + F1 = fun(Args, State) -> + Args1 = decode_list(Args, State), + Res = F(Args1), + encode_list(Res, State) + end, + {#erl_func{code=F1}, St}; +encode({M,F,A}, St) when is_atom(M) and is_atom(F) -> + {#erl_mfa{m=M,f=F,a=A}, St}; +encode({userdata,Data}, St) -> + luerl_heap:alloc_userdata(Data, St); +% Table refs should not be re-encoded +encode(#tref{}=T, St) -> + case luerl_heap:chk_table(T, St) of + ok -> {T, St}; + error -> error(badarg) + end; +encode(Term, _) -> error({badarg,Term}). %Can't encode anything else + +%% decode_list([LuerlTerm], State) -> [Term]. +%% decode(LuerlTerm, State) -> Term. +%% In decode we track of which tables we have seen to detect +%% recursive references and generate an error when that occurs. + +decode_list(Lts, St) -> + lists:map(fun (Lt) -> decode(Lt, St) end, Lts). + +decode(LT, St) -> + decode(LT, St, []). + +decode(nil, _, _) -> nil; +decode(false, _, _) -> false; +decode(true, _, _) -> true; +decode(B, _, _) when is_binary(B) -> B; +decode(N, _, _) when is_number(N) -> N; %Integers and floats +decode(#tref{}=T, St, In) -> + decode_table(T, St, In); +decode(#usdref{}=U, St, _) -> + decode_userdata(U, St); +decode(#funref{}=Fun, State, _) -> + F = fun(Args) -> + {Args1, State1} = encode_list(Args, State), + {Ret, State2} = luerl_emul:functioncall(Fun, Args1, State1), + decode_list(Ret, State2) + end, + F; %Just a bare fun +decode(#erl_func{code=Fun}, _, _) -> Fun; +decode(#erl_mfa{m=M,f=F,a=A}, _, _) -> {M,F,A}; +decode(Lua, _, _) -> error({badarg,Lua}). %Shouldn't have anything else + +decode_table(#tref{i=N}=T, St, In0) -> + case lists:member(N, In0) of + true -> error({recursive_table,T}); %Been here before + false -> + In1 = [N|In0], %We are in this as well + case luerl_heap:get_table(T, St) of + #table{a=Arr,d=Dict} -> + Fun = fun (K, V, Acc) -> + [{decode(K, St, In1),decode(V, St, In1)}|Acc] + end, + Ts = ttdict:fold(Fun, [], Dict), + array:sparse_foldr(Fun, Ts, Arr); + _Undefined -> error(badarg) + end + end. + +decode_userdata(U, St) -> + {#userdata{d=Data},_} = luerl_heap:get_userdata(U, St), + {userdata,Data}. + +%% Externalize and Internalize ensure that the VM state passed in +%% can be stored externally or can be recreated from external storage. +%% Currently very simple: only random state needs special treatment. -%% '$handle_undefined_function'(Func, Args) -%% We just pass the buck and call the old luerl module. +externalize(S) -> + luerl_lib_math:externalize(S). -'$handle_undefined_function'(Func, Args) -> - apply(luerl, Func, Args). +internalize(S) -> + luerl_lib_math:internalize(S). diff --git a/test/lib_os_SUITE.erl b/test/lib_os_SUITE.erl index 0647fbb..ef0417d 100644 --- a/test/lib_os_SUITE.erl +++ b/test/lib_os_SUITE.erl @@ -83,7 +83,7 @@ os_date_table(_) -> os_date_integrated(_) -> State = luerl:init(), Chunk = <<"return os.date('noformat'), os.date(), os.date('%c', 1683371767)">>, - {[NoFormat, _, FromTimeStamp], _State1} = luerl:do(Chunk, State), + {ok, [NoFormat, _, FromTimeStamp], _State1} = luerl:do(Chunk, State), ?assertEqual(<<"noformat">>, NoFormat), %% Date is "Sat May 6 13:16:07 2023", %% Just check year to avoid test flakiness @@ -92,6 +92,6 @@ os_date_integrated(_) -> os_date_integrated_table(_) -> State = luerl:init(), Chunk = <<"return os.date('*t').year">>, - {[Result], _State1} = luerl:do(Chunk, State), + {ok, [Result], _State1} = luerl:do(Chunk, State), {{Year, _, _}, _} = calendar:local_time(), ?assertEqual(Year, Result). diff --git a/test/luerl_funcall_tests.erl b/test/luerl_funcall_tests.erl index 0f237fd..c6ef699 100644 --- a/test/luerl_funcall_tests.erl +++ b/test/luerl_funcall_tests.erl @@ -28,13 +28,13 @@ external_fun_test() -> [A] = luerl:decode_list(Args, S), luerl:encode_list([A + 2, [A + 3, A + 4]], S) end, - State1 = luerl:set_table([<<"testFun">>], F, State), + {ok, State1} = luerl:set_table_keys_dec([<<"testFun">>], F, State), Chunk = <<"function test(i)\n" " local a, b = testFun(i)\n" " return (a == i + 2), (b[1] == i + 3), (b[2] == i + 4)\n" "end">>, - {_, State2} = luerl:do(Chunk, State1), - {Res, _State3} = luerl:call_function([test], [2], State2), + {ok, _, State2} = luerl:do(Chunk, State1), + {ok, Res, _State3} = luerl:call_function_dec([test], [2], State2), [BoolVal, BoolVal2, BoolVal3] = Res = [true,true,true], ?assertEqual(true, BoolVal), ?assertEqual(true, BoolVal2), @@ -42,8 +42,9 @@ external_fun_test() -> return_lib_function_test() -> State = luerl:init(), - {_, State1} = luerl:do(<<"function test()\n return string.find end\n">>, State), - {[{M,F,A}], _State2} = luerl:call_function([test], [1], State1), + {ok, _, State1} = + luerl:do(<<"function test()\n return string.find end\n">>, State), + {ok, [{M,F,A}], _State2} = luerl:call_function_dec([test], [1], State1), {Res, _State3} = apply(M, F, [A, [<<"barfooblafasel">>, <<"foo">>], State1]), ?assertEqual([4, 6], Res). @@ -55,9 +56,9 @@ define_fun_in_lua_test() -> " return i + incby\n" " end\n" "end\n">>, - {_, State1} = luerl:do(Chunk, State), - {[Fun2], State2} = luerl:call_function([mkadder], [1], State1), - {[Fun3], State3} = luerl:call_function([mkadder], [2], State1), + {ok, _, State1} = luerl:do(Chunk, State), + {ok, [Fun2], State2} = luerl:call_function_dec([mkadder], [1], State1), + {ok, [Fun3], State3} = luerl:call_function_dec([mkadder], [2], State1), %% Should really decode the return value, but it is only a number. ?assertMatch({[5], _}, Fun2([4], State2)), @@ -75,18 +76,25 @@ define_fun2_in_lua_test() -> " return list\n" " end\n" "end\n">>, - {_, State1} = luerl:do(Chunk, State), - {[Fun2], State2} = luerl:call_function1([<<"mklist">>], [5], State1), + {ok, _, State1} = luerl:do(Chunk, State), - %% Must decode the return values as they are tables. - {Res21,State21} = luerl:call_function1(Fun2, [4], State2), + %% Build a luerl function and safely call it. + {Emklist, St2} = luerl:encode_list([mklist], State1), + {ok, [Efunc5], St3} = luerl:call_function(Emklist, [5], St2), + {ok, Res20, St4} = luerl:call_function(Efunc5, [4], St3), + ?assertMatch([[{1,4}, {2,4}, {3,4}, {4,4}, {5,4}]], + luerl:decode_list(Res20, St4)), + + %% Build an Erlang fun and just unsafely call it. + {ok, [Fun2], State2} = luerl:call_function_dec([mklist], [5], State1), + {Res21,State21} = Fun2([4], State2), ?assertMatch([[{1,4}, {2,4}, {3,4}, {4,4}, {5,4}]], luerl:decode_list(Res21, State21)), - {Res22,State22} = luerl:call_function1(Fun2, [4.0], State2), + {Res22,State22} = Fun2([4.0], State2), ?assertMatch([[{1,4.0}, {2,4.0}, {3,4.0}, {4,4.0}, {5,4.0}]], luerl:decode_list(Res22, State22)), - {[Fun3], State3} = luerl:call_function([<<"mklist">>], [10], State1), + {ok, [Fun3], State3} = luerl:call_function_dec([mklist], [10], State1), {Res3, State31} = Fun3([4], State3), ?assertMatch([[{1,4}, {2,4}, {3,4}, {4,4}, {5,4}, {6,4}, {7,4}, {8,4}, {9,4}, {10,4}]], @@ -100,6 +108,6 @@ newindex_metamethod_test() -> "end})\n\n" "m[123] = 456\n" "return t[123], m[123]">>, - {[TVal, MVal], _State1} = luerl:do(Chunk, State), + {ok, [TVal, MVal], _State1} = luerl:do_dec(Chunk, State), ?assertEqual(456, TVal), ?assertEqual(nil, MVal). diff --git a/test/luerl_old_funcall_tests.erl b/test/luerl_old_funcall_tests.erl new file mode 100644 index 0000000..2be2c24 --- /dev/null +++ b/test/luerl_old_funcall_tests.erl @@ -0,0 +1,108 @@ +%%% @author Hans-Christian Esperer +%%% @copyright (C) 2015, Hans-Christian Esperer +%%% Licensed under the Apache License, Version 2.0 (the "License"); +%%% you may not use this file except in compliance with the License. +%%% You may obtain a copy of the License at +%%% +%%% http://www.apache.org/licenses/LICENSE-2.0 +%%% +%%% Unless required by applicable law or agreed to in writing, +%%% software distributed under the License is distributed on an "AS +%%% IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +%%% express or implied. See the License for the specific language +%%% governing permissions and limitations under the License. +%%% +%%% @doc +%% +%%% @end +%%% Created : 11 Jan 2015 by Hans-Christian Esperer + +-module(luerl_old_funcall_tests). + +-include_lib("eunit/include/eunit.hrl"). + +external_fun_test() -> + State = luerl_old:init(), + F = fun([A], S) -> + {[A + 2, [A + 3, A + 4]], S} + end, + State1 = luerl_old:set_table([<<"testFun">>], F, State), + {_, State2} = luerl_old:do(<<"function test(i)\n local a, b = testFun(i)\n return (a == i + 2), (b[1] == i + 3), (b[2] == i + 4) end">>, State1), + {Res, _State3} = luerl_old:call_function([test], [2], State2), + [BoolVal, BoolVal2, BoolVal3] = Res, + ?assertEqual(true, BoolVal), + ?assertEqual(true, BoolVal2), + ?assertEqual(true, BoolVal3). + +external_nostate_fun_test() -> + State = luerl_old:init(), + F = fun([A]) -> + [A + 2, [A + 3, A + 4]] + end, + State1 = luerl_old:set_table([<<"testFun">>], F, State), + Chunk = <<"function test(i)\n" + " local a, b = testFun(i)\n" + " return (a == i + 2), (b[1] == i + 3), (b[2] == i + 4)\n" + "end">>, + {_, State2} = luerl_old:do(Chunk, State1), + {Res, _State3} = luerl_old:call_function([test], [2], State2), + [BoolVal, BoolVal2, BoolVal3] = Res, + ?assertEqual(true, BoolVal), + ?assertEqual(true, BoolVal2), + ?assertEqual(true, BoolVal3). + +return_lib_function_test() -> + State = luerl_old:init(), + {_, State1} = luerl_old:do(<<"function test()\n return string.find end\n">>, State), + {[{M,F,A}], _State2} = luerl_old:call_function([test], [1], State1), + {Res, _State3} = apply(M, F, [A, [<<"barfooblafasel">>, <<"foo">>], State1]), + ?assertEqual([4, 6], Res). + +define_fun_in_lua_test() -> + State = luerl_old:init(), + Chunk = <<"function mkadder(incby)\n" + " return function(i)\n" + " print(\"Call into Luerl!\")\n" + " return i + incby\n" + " end\n" + "end\n">>, + {_, State1} = luerl_old:do(Chunk, State), + {[Fun], _State2} = luerl_old:call_function([mkadder], [1], State1), + {[Fun2], _State3} = luerl_old:call_function([mkadder], [2], State1), + ?assertEqual([5], Fun([4])), + ?assertEqual([5.0], Fun([4.0])), + ?assertEqual([6], Fun2([4])). + +define_fun2_in_lua_test() -> + State = luerl_old:init(), + Chunk = <<"function mklist(numentries)\n" + " return function(entryval)\n" + " local list = {}\n" + " for i = 1,numentries do\n" + " list[i] = entryval\n" + " end\n" + " return list\n" + " end\n" + "end\n">>, + {_, State1} = luerl_old:do(Chunk, State), + {[Fun], _State2} = luerl_old:call_function([mklist], [5], State1), + {[Fun2], _State3} = luerl_old:call_function([mklist], [10], State1), + ?assertEqual([[{1,4}, {2,4}, {3,4}, {4,4}, {5,4}]], + Fun([4])), + ?assertEqual([[{1,4.0}, {2,4.0}, {3,4.0}, {4,4.0}, {5,4.0}]], + Fun([4.0])), + ?assertEqual([[{1,4}, {2,4}, {3,4}, {4,4}, {5,4}, + {6,4}, {7,4}, {8,4}, {9,4}, {10,4}]], + Fun2([4])). + +newindex_metamethod_test() -> + State = luerl_old:init(), + Chunk = <<"local t = {}\n" + "local m = setmetatable({}, {__newindex = function (tab, key, value)\n" + "t[key] = value\n" + "end})\n\n" + "m[123] = 456\n" + "return t[123], m[123]">>, + {[TVal, MVal], _State1} = luerl_old:do(Chunk, State), + ?assertEqual(456, TVal), + ?assertEqual(nil, MVal). diff --git a/test/luerl_old_tests.erl b/test/luerl_old_tests.erl new file mode 100644 index 0000000..a918538 --- /dev/null +++ b/test/luerl_old_tests.erl @@ -0,0 +1,44 @@ +%% Copyright (C) 2024 Robert Virding +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. + +-module(luerl_old_tests). + +-include_lib("eunit/include/eunit.hrl"). + +encode_test() -> + State = luerl_old:init(), + ?assertMatch({nil, _State}, luerl_old:encode(nil, State)), + ?assertMatch({false, _State}, luerl_old:encode(false, State)), + ?assertMatch({true, _State}, luerl_old:encode(true, State)), + ?assertMatch({<<"binary">>, _State}, luerl_old:encode(<<"binary">>, State)), + ?assertMatch({<<"atom">>, _State}, luerl_old:encode(atom, State)), + ?assertMatch({5, _State}, luerl_old:encode(5, State)), + ?assertMatch({{tref, _}, _State}, luerl_old:encode(#{a => 1, b => 2}, State)), + ?assertMatch({{tref, _}, _State}, luerl_old:encode([{a,1},{b,2}], State)). + +encode_error_test() -> + State = luerl_old:init(), + ?assertException(error, {badarg, _}, luerl_old:encode({a,1}, State)). + +encode_table_test() -> + {Table, State} = luerl_old:encode(#{a => 1}, luerl_new:init()), + State1 = luerl_old:set_table1([<<"foo">>], Table, State), + ?assertMatch({Table, _State2}, + luerl_old:get_table1([<<"foo">>], State1)), + ?assertMatch({tref, _}, Table). + +invalid_value_test() -> + State = luerl_old:init(), + ?assertException(error, {badarg, {invalid, value}}, + luerl_old:encode({invalid, value}, State)). diff --git a/test/luerl_return_SUITE.erl b/test/luerl_return_SUITE.erl index 07cb30c..1bd41a8 100644 --- a/test/luerl_return_SUITE.erl +++ b/test/luerl_return_SUITE.erl @@ -53,8 +53,8 @@ check_unicode(Config) -> check_unicode_call_fun(<<9810/utf8>>, 3, check_aquarius, St). check_unicode_call_fun(Input, Length, LuaFun, St) -> - {[Input, Input, true, Length, Length], _} = - luerl:call_function([LuaFun], [Input], St). + {ok, [Input, Input, true, Length, Length], _} = + luerl:call_function_dec([LuaFun], [Input], St). table_tests(Config) -> run_and_check(Config, "table_indexed_table.lua", [111, 222, 333]). @@ -66,6 +66,6 @@ run_tests(Config, Tests) -> run_and_check(Config, Script, Expected) -> DataDir = ?config(data_dir, Config), ScriptFile = DataDir ++ Script, - {Result, St} = luerl:dofile(ScriptFile, luerl:init()), + {ok, Result, St} = luerl:dofile(ScriptFile, luerl:init()), Expected = Result, St. diff --git a/test/luerl_tests.erl b/test/luerl_tests.erl index f26d075..26de63e 100644 --- a/test/luerl_tests.erl +++ b/test/luerl_tests.erl @@ -1,3 +1,17 @@ +%% Copyright (C) 2024 Robert Virding +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. + -module(luerl_tests). -include_lib("eunit/include/eunit.hrl"). @@ -18,10 +32,10 @@ encode_error_test() -> ?assertException(error, {badarg, _}, luerl:encode({a,1}, State)). encode_table_test() -> - {Table, State} = luerl:encode(#{a => 1}, luerl_new:init()), - State1 = luerl:set_table1([<<"foo">>], Table, State), - ?assertMatch({Table, _State2}, - luerl:get_table1([<<"foo">>], State1)), + {Table, State} = luerl:encode(#{a => 1}, luerl:init()), + {ok, State1} = luerl:set_table_keys([<<"foo">>], Table, State), + ?assertMatch({ok, Table, _State2}, + luerl:get_table_keys([<<"foo">>], State1)), ?assertMatch({tref, _}, Table). invalid_value_test() -> diff --git a/test/luerl_time_SUITE.erl b/test/luerl_time_SUITE.erl index 40c4449..582a83d 100644 --- a/test/luerl_time_SUITE.erl +++ b/test/luerl_time_SUITE.erl @@ -34,8 +34,12 @@ os_date(_Config) -> ?assertNotMatch({badrpc, _}, LusakaLocalTime), ?assertNotMatch({badrpc, _}, LondonLocalTime), ?assert(LusakaLocalTime =/= LondonLocalTime), - ?assertEqual({ok,[<<"Sat May 6 13:16:07 2023">>]}, rpc:call(LusakaNode, luerl, eval, ["return os.date('%c', 1683371767)", luerl:init()])), - ?assertEqual({ok,[<<"Sat May 6 12:16:07 2023">>]}, rpc:call(LondonNode, luerl, eval, ["return os.date('%c', 1683371767)", luerl:init()])), + ?assertMatch({ok,[<<"Sat May 6 13:16:07 2023">>], _St1}, + rpc:call(LusakaNode, luerl, do_dec, + ["return os.date('%c', 1683371767)", luerl:init()])), + ?assertMatch({ok,[<<"Sat May 6 12:16:07 2023">>], _St2}, + rpc:call(LondonNode, luerl, do_dec, + ["return os.date('%c', 1683371767)", luerl:init()])), ok. windows_tests() -> @@ -50,5 +54,5 @@ set_path(Node) -> ok; Err = {error, _} -> throw({badpath, Path, Err}) - end || Path <- code:get_path(), filelib:is_dir(Path)], - ok. \ No newline at end of file + end || Path <- code:get_path(), filelib:is_dir(Path)], + ok.