Freely available under the terms of the MIT license.
Loulabelle is a compiler (or transpiler), which translates programs written in Lua version 5.2 to JavaScript (ECMAScript 2015 ES6), and a runtime support library, which enables the execution of such translated programs in a JavaScript environment such as a Web browser or Node.js.
Programs compiled by Loulabelle can be used anywhere that JavaScript code can be used, for example as the scripts to control a Web page, or in a smaller role, as supporting scripts that interact with other scripts written in JavaScript.
This reference manual assumes the reader is familiar with Lua 5.2 and ECMAScript 2015 ES6. The manual is styled after the Lua reference manual, but discusses information specific to Loulabelle. As such, it happens to discuss implementation details before the larger points. Please feel free to skip ahead to section 4 and section 7 for a discussion about the interoperability of Lua and JavaScript programs.
Loulabelle is free software, and is provided as usual with no guarantees, as stated in its license. The implementation described in this manual is available at the Loulabelle official web site, www.spaceflint.com/loulabelle.
This section describes the Loulabelle implementation of the basic concepts of the Lua language.
Lua types boolean
, number
and string
are implemented as the corresponding types in JavaScript. Note that strings in JavaScript
are encoded in UNICODE, while most Lua implementations support 8-bit strings.
Lua nil
is implemented as JavaScript undefined
.
The Lua table
is implemented as the following JavaScript object.
{ luatable: true, array: [], hash: new Map(), metatable: undefined, id: undefined }
luatable
is a truthy value that is used in type checks,
array
and hash
are, respectively,
Array
and Map
JavaScript objects for the array and hash parts of the table,
metatable
is an optional reference to a metatable,
and finally, id
is only assigned if the table is passed to the tostring
function,
which uses an incrementing counter to simulate a unique address value for each table.
The Lua function
is implemented as a JavaScript
generator function,
which provides a mechanism to yield
execution, and in turn, to emulate synchronous
execution and Lua coroutines. Because generator functions are slower than normal JavaScript functions,
Loulabelle also provides the function coroutine.fastcall
to call Lua functions as normal
JavaScript functions, which are faster but cannot yield or suspend.
The Lua coroutine
is implemented as a JavaScript object,
which contains the property luacoroutine
that tracks the
state of the coroutine, and also participates in type checks.
Other properties in this object are id
which was described above,
generator
which references the coroutine function,
status
which is the status string returned by the coroutine.status
library function, stack
which is an array that contains the Lua call stack,
and protected
, which indicates execution within
a pcall
or xpcall
scope.
While the JavaScript objects representing the Lua table and coroutine have been briefly discussed, the reader is strongly advised to interact with these objects exclusively through the Loulabelle core library, which exposes a JavaScript API, discussed in section 4.
The Loulabelle library function type
behaves the same
and returns the same results as its Lua namesake.
Functions in Lua may return multiple values, and this is implemented in Loulabelle by always returning an array, even if the array has no elements (which would indicate the function did not return any result).
Loulabelle assigns a property named env
in each JavaScript function
object, and this property references the environment table for the function.
Typically, this env
property references the global environment table.
A reference to the Lua variable _ENV
(either implicitly or explictly)
in a function is translated a reference to a local variable in JavaScript (called env
)
which is assigned from the env
property of the same function. Note that as in
Lua 5.2, references to global variables, as well as to _G
(the global table),
are done through the implicit _ENV
variable.
The library function load
can bind a function to a custom environment:
local f2 = load(string.dump(f), nil, nil, custom_env_table)This works the same in Loulabelle, but please note a few points:
string.dump
merely
returns the function passed as a parameter, without any modification.
(Note that at the time that Lua code is executing in the JavaScript
environment, the function is actually a JavaScript function object.)load
does not
support compilation from source code, as that would
require bundling the compiler with the core library.After the Loulabelle compiler has been invoked on itself to produce a JavaScript version of the compiler, and that compiled version of the compiler is executing in a JavaScript environment, it can compile a function from Lua source code while specifying a custom environment:
local compiler = require "Loulabelle" local func, err = compiler("filename.lua", "source code text", { env = custom_env_table })
As with Lua 5.2 environments, changing the value of _ENV
or _G
before calling a function will not have an effect on the function,
which is bound to an environment when its containing chunk is loaded.
But note that unlike in Lua 5.2, the debug
library in Loulabelle
does not provide a mechanism to override the value of the bound _ENV
.
Loulabelle provides error handling facilities in the functions error
,
pcall
, xpcall
and debug.traceback
, all of
which work the same as in Lua 5.2.
In terms of JavaScript, error handling is implemented via JavaScript
try...catch
statements, and the error
function throws an error string,
or some other error value, depending on the parameter passed to it.
For direct invocation of a Lua function from JavaScript
code, the Loulabelle xpcall
API function
may be used instead of the unprotected call
API function, or call
can just be enclosed
in a JavaScript try...catch
block.
__gc
method is not supported:
Object finalizers are not supported and not invoked.__mode
property is not supported:
Weak tables are not supported, table entries are
garbage-collected only after all references to
them are deleted.
These facilities are not supported because JavaScript
does not provide callbacks from its garbage collector,
and because WeakMap
is not enumerable. For similar reasons, the collectgarbage
Lua library function is not provided.
As in Lua 5.2, metamethods may yield (or suspend).
Loulabelle fully supports Lua coroutines. Each
thread of execution, including the first loaded chunk,
occurs in the context of a coroutine. This coroutine
context, or coroutine object, is a JavaScript object
that is accessible in Lua as an object with type
"thread"
.
In JavaScript, the coroutine object associated with
the currently-executing thread is referenced by the
property co
in the global runtime object.
Within this coroutine object, the property
main
is set to a boolean true
for the thread of the first loaded chunk.
In Lua code, the same object reference is returned as
an opaque value by the function coroutine.running
,
which also returns the main
boolean flag
as its second result.
As well as normal coroutines, created by
coroutine.create
, and wrapped coroutines,
created by coroutine.wrap
, Loulabelle
supports two more types of coroutines.
Spawned coroutines, created by coroutine.spawn
,
are detached from the creating coroutine, and begin
execution asynchronously and independently. They are
scheduled via a call to JavaScript
setTimeout
with a zero timeout, and should not be started explicitly.
Callback coroutines, created by coroutine.jscallback
,
are normal JavaScript functions (not generator functions)
that can be passed to JavaScript code, or attached to
browser events. When the function is executed, it
creates a temporary coroutine, calls a specified Lua
function, and passes its first result on to the caller.
Coroutines in Loulabelle can yield, as in Lua, via a
call to coroutine.yield
but can also
suspend, via a call to coroutine.suspend
.
A suspended coroutine does not pass control back to
its calling coroutine. Instead it just suspends execution,
presumably while waiting for some event to occur
and cause it to resume execution. For example, the
coroutine.sleep
function suspends the
current coroutine after installing a timeout
callback which calls coroutine.resume
.
Loulabelle strives to implement the Lua language precisely according to the Lua 5.2 specification, however there are a few minor points of difference.
continue
statement.
goto
statement is limited in
Loulabelle because JavaScript itself does not support
a goto statement. While it is possible to simulate
goto statements using loops and switches, this was
deemed unnecessary. In Loulabelle, a goto statement
may target a statement ...
break
statement; orcontinue
statement.#
, when applied
to tables, may return a different result in Loulabelle.
In Loulabelle, the table length is calculated by starting
from the JavaScript length
property of the
array part of the Lua table, and repeatedly decrementing
it as long as it indexes undefined
(i.e. nil
) entries.
local a, b, c = func1(x, y, func2())multiple results returned from
func2
may be passed to func1
after the
y
parameter, and the second and third
results returned from func1
may be
assigned to b
and c
.
Loulabelle checks if the return value is a multiple-value array
using JavaScript Array.isArray
.
When mixing JavaScript and Lua objects in Lua code,
it is important to keep this multiple value rule in
mind. Avoid passing a JavaScript array as the last
parameter in a Lua function call, or as the last value
in the array part of a table constructor. Instead,
pass at least one more parameter (even nil
).
JavaScript
statement lets Loulabelle
programs directly embed JavaScript code. The syntax
of the statement is:
JavaScript("stmt_string", arg1, ...argN)
where stmt_string
is a quoted
string constant containing JavaScript code,
with these considerations:
$1
through $N
(quoted within the string) that reference
parameters arg1
through
argN
.$L
references the
global runtime object $lua
.$$
translates to
a single $
sign.{
or }
or ;
(curly braces, semicolon)."public var "
(11 characters, including the last space), the initial
word public
is dropped, and the result is
written at the very top of the compiled output.
The identifier JavaScript
is not,
strictly speaking, a reserved word. It has a special
meaning only in the form of a function call statement.
Example JavaScript
statement:
local context = {} local callback_id JavaScript("$1=window.requestAnimationFrame($2)", callback_id, (coroutine.jscallback( ::update_stack_frame:: JavaScript("console.log('in callback')") function(context, timestamp) context.timestamp = timestamp end, context))) context.callback_id = callback_idThis example illustrates a few important points.
$1=...
in the example,
where $1
references the local variable
callback_id
). However, it cannot
assign directly into tables (or global variables).coroutine.jscallback
,
to make sure that $2
is a function
reference. Without the parentheses, $2
would be a reference to an array containing that
function reference that.
JavaScript
statement can be
nested inside a function definition that is one
of the parameters to a higher-level
JavaScript
statement.JavaScript
statement does not
cause line number debug information to be recorded.
To force line number information to be written at
the top of a function, a dummy label such as
::update_stack_frame::
is needed.
Note that the name of the label is arbitrary.
To account for the dynamic types in Lua, and the
metamethod mechanism, the Loulabelle compiler has
to generate code that checks the actual types of
objects at runtime. For example, the expression
a + b
, when types of the local variables
a
and b
are not known,
is translated to the following:
v3 = (typeof v1 === 'number' && typeof v2 === 'number' ? v1 + v2 : yield*$lua.add(v1, v2, true));
On the other hand, if the compiler knows for certain
that a
and b
are numbers,
then the generated code is shorter and faster: v3=(v1+v2);
Using type annotations to tell the compiler to assume a specific type for expressions and local variables, enables the compiler to make the optimizations described above, and skip some type checks when generating code.
The type annoations are:
assume_number
- assumes a number
type. This skips type checks in arithmetic
expressions, and references the array
part in table indexing. Numeric constants,
numeric for
loop variables, and
the string length operator (#
) all
assume the number type by default, as well as
arithmetic expressions with operands that are all
known to be a number type.
assume_string
- assumes a string
type. This skips type checks in the string
concatenation operator (..
), and
references the hash
part in table
indexing. String constants assume the string
type by default, as well as concatenation expressions
with operands that are all known to be a string type.assume_function
- assumes a function
type. This skips type checks in function calls.
Note that this type is not assumed by default, not
even for local function
definitions.assume_nometa
- assumes a table that
is known to not have an associated metatable. Note
that this affects only table access, in that getting
will not invoke the __index
metamethod,
and setting will not invoke the __newindex
metamethod. Note also that this type is not assumed
by default.assume_untyped
- discards any type
annotation associated with a local variable or an
expression. This restores the variable or expression
to an 'untyped' state, and disables any subsequent
optimizations based for that variable or expression.local a = 4 -- type of a is number local b = x() -- type of b is not known local c = 4 + assume_number(b) -- type of c is number local d = y() -- tpye of d is not known assume_number(b, d) -- types of b and d are now number local e = c + b * b -- type of e is number
This section describes interoperability between the JavaScript environment, and any Lua code compiled by Loulabelle into JavaScript. It also includes a description of various Loulabelle API functions that are can be invoked from JavaScript code.
All API functions and properties are contained in the
global runtime object $lua
.
A Lua function is compiled into the following JavaScript:
$lua.func( function* func() { // JavaScript code translated from Lua source code }, $lua.env, // or func.env function(s){return eval(s)}, // or undefined 'filename.lua', // or func.file 1) // line number );
$lua.func
is a small utility function
which stores additional properties on the function
object func
which is passed as the
first parameter. The following properties are stored:
func.self
, assigned from the first
parameter, is a reference to the function itself.func.env
, assigned from the second
parameter, is a reference to the Lua environment for
the function. For a chunk function (the outermost
function in a compiled chunk), this is passed as
$lua.env
, to reference the global
environment. For functions defined within a chunk,
this is passed as func.env
, which
references the environment of the encompassing
function or chunk.func.eval
, assigned from the third
parameter, is a recompiler utility function. Functions
passed to load
and
coroutine.fastcall
need to be recompiled, and if the function references
any upvalues, it must be recompiled in the same
context or scope in which it was defined. The
recompiler utility function can provide that original
context. For functions that do not reference upvalues,
the third parameter is passed as undefined
.
(Note that Loulabelle will not be able to detect
upvalue references in functions that reference
upvalues only through JavaScript
statements.)func.file
, assigned from the fourth
parameter, is the name of the source file. For a
chunk function (the outermost function in a compiled
chunk), this is the source file name string. For
functions defined within a chunk, this is passed as
func.file
, which references the name
of the encompassing function or chunk.func.line
, assigned from the fifth
parameter, is the line number of the function
definition.func.fast
is a utility function
that is used in the fastcall
mechanism;
see coroutine.fastcall
.Note that like in Lua, a Lua source file is compiled by Loulabelle into a chunk, which is effectively a single JavaScript function that contains all the translated statements from the Lua source file:
$lua.chunk( $lua.func ( ... ) );
JavaScript scripts compiled by Loulabelle from Lua can be loaded into a JavaScript environment (such as a Web browser or Node.js) the same way that any other JavaScript script is loaded. However, the Loulabelle core library must be loaded first, to set up the global runtime object and the Lua environment.
A very simple HTML page example may look like this:
<!DOCTYPE html> <html><head> <script type="text/javascript" src="core.js"></script> <script type="text/javascript" src="main.js"></script> </head><body></body></html>
Note in this example that core.js
, the
Loulabelle core library, is loaded and executed first.
Only then, main.js
, which would be the
Loulabelle JavaScript translation of a Lua chunk,
is loaded.
It is not valid to load more than one chunk in this
way. Lua code should use the Lua require
function to load additional chunks. However, compiled
chunks can be modified to start with $lua.preload_chunk
,
instead of $lua.chunk
, and this enables
preloading chunks in advance. For more on this, refer
to the discussion in section 6.3.
All functions and properties listed below are contained
in the global runtime object $lua
.
Almost all API functions are generator functions,
which means they can be invoked using the
yield*
expression if invoked from a generator function:
JavaScript("yield*$L.tostring(123)")
If invoked from a normal function, the JavaScript
function next
should be used. For example,
var str = $lua.tostring(123).next().valueHere, the Loulabelle API generator function
tostring
is invoked (generated) with parameter 123
,
and iterated once via a call to next
.
call
call (name, func, arg1, ...argN)
Calls the Lua function func
, passing
it a variable number of parameters specified in
arg1
through argN
.
The first parameter name
is the name
of the called function, as it should appear in the
Lua call stack entry created for the function, or
if passed as undefined
, inhibits the
creation of a call stack entry.
As discussed in section 3, the last
parameter is checked to see if it is an array, using
Array.isArray
.
If the check succeeds, call
assumes
the last parameter is an array of multiple values,
and unpacks those values as additional parameters
for the function call.
When using call
to call functions with
non-Lua JavaScript objects, it is important to keep
this multiple value rule in mind. Avoid passing a
JavaScript array as the last parameter. Instead,
pass at least one more parameter (even undefined
).
The call
function returns an array that
contains the results returned from the Lua function.
error
error (msg, level)
Generates an error message. If msg
is a string, level
specifies which
call stack entry to use for decorating the error
message, where 0
means the current
stack entry, 1
(the default) means
the calling entry, and so on. A value of
-1
means to not decorate the message.
traceback
traceback (msg, level)
Generates a formatted stack trace. The parameters
are similar to the parameters for error
,
described above, except that a value of -1
for the level
parameter is not valid.
type
type (v)Returns the type name for the Lua value
v
as a string (not wrapped in an array).
Generates an error
if the value is not
a valid Lua type.
table
table (vals, n1, n2)
Creates a Lua table using the values passed in the
JavaScript array vals
. This array
specifies both key-value pairs, and sequential values.
The key-value pairs must come first, followed by
the sequential values. The array may be empty, if
an empty table should be created.
The parameter n1
, which must be an even
number, and can be zero, specifies the number of
key-value pairs in the array. The parameter
n2
specifies the number of sequential
values in the array.
Generates an error
if a key in the
key-value segment of the array is NaN
or
undefined
. Otherwise the return value
is a JavaScript object that represents a Lua table.
This returned object is not wrapped in an array.
As discussed in section 3, the last
parameter is checked to see if it is an array, using
Array.isArray
.
If the check succeeds, table
assumes
the last parameter is an array of multiple values,
and unpacks those values as additional sequential
values.
When using table
to create a table which
references non-Lua JavaScript objects, it is important
to keep this multiple value rule in mind. Avoid
passing a JavaScript array as the last element in
the vals
array. Instead, pass at least
one more parameter (even undefined
).
Note in the example below that n1
is
specified as 10
, for the first five
key-value pairs, and n2
is specified as
4
for the following four sequential values.
var t = $lua.table([ "a", 'val_a', "b", 'val_b', "c", undefined, true, 'val_true', false, 'val_false', 123, 456, 111, 789 ],10,4).next().value
len
len (v)
Returns the length of the Lua value v
,
which may be a string or a table. May invoke the
__len
metamethod for a table, and
generates an error
for an unsupported
type. Returns the length as a number, not wrapped
in an array.
get
get (tbl, key, name)
Extracts the value of the key key
from the table tbl
. The key should
be any valid value that can index into a table.
May invoke the __index
metamethod.
May generate an error
, in which case,
the name
parameter, if provided,
is used to decorate the error message. Returns
the value directly, not wrapped in an array.
set
set (tbl, key, name, val)
Sets the value of the key key
in the table tbl
to value val
.
The key should be any valid value that can index
into a table. May invoke the __newindex
metamethod. May generate an error
,
in which case, the name
parameter,
if provided, is used to decorate the error message.
This function does not return any results.
gmt
gmt (v, typeof_v, who)
If v
is a table, returns the metatable
associated with the table, without respecting the
__metatable
property. If v
is not a table, returns the shared metatable for
values of the type of v
, which may be
passed in the parameter typeof_v
, or
else will be determined from v
itself.
The parameter who
, if provided, is used
to decorate the generated error
. The
returned table reference is not wrapped in an array.
smt
smt (v, mt)
Assigns the metatable mt
for table
v
, if v
is a table, or
the shared metatable for values of the type of
v
. Does not respect the
__metatable
property. This function
does not return any results, and may generate an
error
.
tonumber
tonumber (s)
Parses the string s
as a number
according the Lua rules for a numeric constant
presented in section 3.1 of the Lua reference manual.
Returns a number, or undefined
if the
string cannot be converted to a number. The return
value is not wrapped in an array.
tostring
tostring (v)
Converts the value v
to a string, using
a __tostring
metamethod if available.
Where Lua generally uses the memory address of an
object in order to give tables and coroutines a unique
string representation, Loulabelle uses an incrementing
counter to produce a similar result.
Note that when converting a number, Lua uses
sprintf("%.14g")
, which produces results
that may differ from JavaScript conversions, whether
toString
or
toPrecision
is used.
As the Lua reference manual suggests, for complete
control over the output, use string.format
.
next
next (tbl, key)
Given a key key
in table tbl
,
returns the next key. If key
is undefined
,
returns the first key. Enumeration starts with the
array part of the table, then switches to the hash part.
The return value is not wrapped in an array.
The iterator for next
is kept as part of
the table being iterated, and will be invalidated if
next
is invoked for a key that does not
match the current key in the iterator. Avoid multiple
concurrent next
loops on the same table.
xpcall
xpcall (msgh, func, args_array)
Calls func
in a protected scope,
passing it the arguments in the JavaScript array
args_array
. The parameter
msgh
specifies the message handler
generator function to invoke in case of error,
and can be specified as $lua.dflt_msgh
if no message handler is needed, for a
pcall
-like behavior rather than
xpcall
-like behavior.
Returns an array where the first element is
a boolean
that indicates if the call
succeeded without errors. If true
,
the array also contains the zero or more results
returned from the called function. If
false
, the array contains the error
message in the second element.
cocreate
cocreate (f)
Creates a coroutine object for the function f
.
The coroutine is initially suspended and must be resumed
before it begins executing. This function returns an
array containing a single element, the coroutine object.
The coroutine object has a method resume
,
a normal function (not a generator), which accepts
a JavaScript array of arguments, and resumes execution
of the coroutine, passing it those arguments. This
method does not return a value: If the resumed
coroutine yields, it will itself invoke the
resume
method on the coroutine being
yielded to, passing it the values to yield, and then
return control.
coresume
coresume (co, arg1, ...argN)
Resumes coroutine co
, passing it a
variable number of parameters specified in
arg1
through argN
.
Returns an array containing the values yielded by
or returned from the coroutine being resumed.
cosuspend
cosuspend ()
Suspends the current coroutine, which is the coroutine
object referenced by $lua.co
.
Unlike a Lua yield, a suspended coroutine does not automatically cause execution to resume in another coroutine. The suspend mechanism is designed to accomodate the asynchronous nature of JavaScript. Lua code may schedule some asynchronous JavaScript operation, such as an XMLHttpRequest, suspend itself, and have the asynchronous callback resume it. For example, JavaScript code embedded in a Lua function:
JavaScript("var co=$L.co") JavaScript("setTimeout(co.resume,5000)") JavaScript("yield*$L.cosuspend()")
The first line saves the current coroutine, because
some other coroutine may be scheduled by the time the
setTimeout callback fires. The second line sets up
a five second timer with a callback that resumes the
current coroutine. And the third line, which can only
be executed in a generator function (such as a Lua
function), suspends the current coroutine. If writing
non-generator JavaScript code, the third line can be
rewritten as: $lua.cosuspend().next()
cowrap
cowrap (func)
Creates a function that wraps and drives a coroutine. Returns an array containing a generator function in the first element, and a coroutine object in the second element. Each invocation of the generator function causes the coroutine to resume, passing it any parameters passed to the generator function.
require_lua
require_lua (url)
In the Web browser, creates a new SCRIPT
element to asynchronously load a script. In a Web worker,
or in Node.js, loads the script synchronously, using
importScripts
or require
,
respectively.
The loaded script is expected to have been compiled by Loulabelle
such that it contains a single call to $lua.chunk
which wraps all other code in the chunk, as discussed
at the top of section 4.
Returns a (generator) function representing the loaded chunk. This returned function is not wrapped in an array.
require_js
require_js (url)
In the Web browser, creates a new SCRIPT
element to asynchronously load a script. In a Web worker,
or in Node.js, loads the script synchronously, using
importScripts
or require
,
respectively.
Unlike require_lua
, this function is
intended to load non-Lua JavaScript code. There is
no return value, and the invoking code should check
if the script loaded successfully.
require_css
require_css (url, media)
In the Web browser, creates a new LINK
element to asynchronously load a CSS file. In a Web worker,
or in Node.js, this functions does not do anything.
The media
parameter, which defaults to
the string screen
if omitted, is
assigned to the media
property of the
created element.
fastcall
fastcall (f)
This normal, non-generator JavaScript function
performs a dynamic, on-the-fly recompilation of the
Loulabelle-translated generator function passed in
f
, into a normal, non-generator
JavaScript function. The new function is stored
into a property named fast
in the
supplied function f
, and then the
new function is returned, not wrapped in an array.
This section describes parameter-checking utility functions in the Loulabelle API. These are useful when writing JavaScript code which needs to validate any parameters passed to it from Lua code.
error_arg
error_arg (num, msg)
Generates this error
:
bad argument #(num) to 'function' (msg)
error_argtype
error_argtype (got, num, exp)
Generates this error
:
bad argument #(num) to 'function' ((exp) expected, got (got))
error_argexp
error_argexp (num, check)
If num
is smaller than check
,
generates this error
:
bad argument #(num) to 'function' (value expected)
checkstring
checkstring (arg, num)
If the arg
parameter is a string, returns it.
If it is a number, converts the number to a string and returns it.
Otherwise calls error_argtype
with num
.
The return value is not wrapped in an array.
checknumber
checknumber (arg, num)
If the arg
parameter is a number, returns it.
If it is a string that can be converted using tonumber
,
converts the string to a number and returns it.
Otherwise calls error_argtype
with num
.
The return value is not wrapped in an array.
checktable
checktable (arg, num)
Returns nothing if arg
is a Lua table.
Otherwise calls error_argtype
with num
.
checkfunction
checkfunction (arg, num)
Returns nothing if arg
is a Lua function.
Otherwise calls error_argtype
with num
.
checkcoroutine
checkcoroutine (arg, num)
Returns nothing if arg
is a Lua coroutine.
Otherwise calls error_argtype
with num
.
Not implemented: collectgarbage
, dofile
, loadfile
.
load (func, source, mode, env)
Clones the Lua generator function passed in
func
as a new generator function which
will use the environment table passed in the
parameter env
, which can be passed as
_ENV
or _G
for the
default environment.
Ignores parameters source
and mode
.
See also section 2.2.
The function is recompiled by calling JavaScript
toString()
on the input function, and compiling the resulting source code
using the recompiler utility function assigned to
the eval
property of the original function,
as discussed at the top of section 4.
(But note that if the function references upvalues only
through JavaScript
statements, the eval
property will not
be set, and the converted function will not compile
correctly.)
next (table [, index])
The iterator used to track calls to this function is associated with the table being iterator. Concurrent iteration over different indexes will impact performance by forcing the iterator to be repeatedly invalidated. Note that functionality will not be impacted in any way. See also the next API function in section 4.8. For example, try to avoid code like this:
k1 = next(t) -- iterator advances to second element while k1 do k2 = next(t) -- iterator restarts at first element while k2 do k2 = next(t, k2) end k1 = next(t, k1) -- iterator restarts, positions to k1 end
In this example, the outer and inner loop share
the same internal iterator, which has to be
repeatedly invalidated, restated and repositioned.
Prefer to use the functions pairs
and
ipairs
, which use dedicated iterators,
and are not subject to this performance issue.
print (···)
Similar to Lua print, uses tostring
on all input parameters to construct an output string.
See there for considerations about number conversion.
If the global printwriter
is a Lua function,
it is called with this function with the output string.
Otherwise, the default behavior is to call JavaScript
console.log
with the output string.
Coroutines in Loulabelle can suspend/resume in
addition to a normal Lua resume/yield combination.
This is discussed in more detail in section 2.6
and in the cosuspend
API function.
coroutine.spawn (f, arg1, ...argN)
setTimeout
with a zero timeout. Returns the new coroutine.
Note that JavaScript is a single-threaded environment, and only one thread of JavaScript executes at the same time. (For sake of simplicity, we ignore worker threads.)
This example illustrates cooperating multithreading using asynchronous coroutines:
local main = coroutine.running() local f = function(three, four, six) print (three) coroutine.resume(main) print (four) coroutine.suspend() print (six) end print '#1' local co = coroutine.spawn(f, '#3', '#4', '#6') print '#2' coroutine.suspend() print '#5' coroutine.resume(co)
The output is #1 #2 #3 #4 #5 #6
. Note
that spawn
or resume
do
not immediately cause another thread to run. The
currently-running thread (whichever one it is) must
suspend itself before any other thread can run.
coroutine.suspend ()
Suspends the current coroutine. Unlike a Lua yield,
a suspended coroutine does not automatically cause
execution to resume in another coroutine.
The suspend mechanism is designed to accomodate the
asynchronous nature of JavaScript. Lua code may
schedule some asynchronous JavaScript operation,
such as an XMLHttpRequest, suspend itself, and have
the asynchronous callback resume it. See also the
cosuspend
API function.
Note that some care should be taken when one coroutine may be resumed by more than one callback at the same time. For example, if the same suspended coroutine can be resumed either by a button click or an XMLHttpRequest callback, the coroutine may need some way to identify which callback caused it to resume.
coroutine.sleep (ms)
Suspends execution of the current coroutine for a duration
of ms
milliseconds, using the JavaScript
setTimeout
function.
coroutine.status (co)
The only difference from the standard Lua function
of the same name is that this function may also
return the status asyncwait
which
indicates the coroutine has suspended itself using
coroutine.suspend
or cosuspend
.
Note that a status of suspended
, as in
Lua, indicates the coroutine has not started running
yet, or has called yield
.
coroutine.wrap (f)
coroutine.jscallback (func, arg1, ...argN)
JavaScript callbacks are typically normal functions,
whereas Loulabelle-translated Lua functions are
generator functions. The jscallback
function makes it simple to connect Lua functions to
JavaScript callbacks, as shown in the example in section 3.6.
The returned function is normal JavaScript function
(not generator functions) which acts as the callback
wrapper. When this wrapper is called, it creates a
temporary coroutine with the call stack at the point
of the call to jscallback
. Then the Lua
function is called in the context of the temporary
coroutine. The Lua function receives the parameters
passed to jscallback
as well as any
parameters passed in the invocation of the
callback wrapper.
The first result from the Lua function is returned to the caller of the callback wrapper. Note that if the Lua function yields or suspends, instead of returning, this also returns control to the callback wrapper, but in this case, nothing is returned to the caller of the callback wrapper.
Note that the temporary coroutine created for
the duration of the callback is not subject to a
pcall
or xpcall
protected
scope that was established during the creation of
the callback, i.e. when jscallback
was called.
coroutine.jsconvert (jsobj, luatbl)
This function converts between Lua tables and JavaScript objects and vice versa. It is not directly related to coroutine management.
If jsobj
is nil
, the
function recursively traverses the Lua table in
luatbl
and constructs a JavaScript
object that contains all boolean
,
number
and string
values in the table, and any tables referenced by it.
Values in any other type are ignored.
If jsobj
is not nil
, the
function recursively traverses the JavaScript object
and populates the Lua table luatbl
with the properties from the JavaScript object.
This function may make non-Lua JavaScript objects
accessible to the Lua caller of this function.
Be advised that using Lua functions such as
tostring
or type
on
non-Lua objects may generate various forms of the
error: unexpected type in getmetatable
.
coroutine.mutex ()
Returns a mutex table object which can be used to synchronize between threads. The object provides the following methods:
lock
- suspends the caller
(using coroutine.suspend
)
until the mutex can be acquired, then returns true
.trylock
- if the mutex is not
acquired, acquires it and return true
,
otherwise returns false
without suspending.unlock
- releases an acquired mutex,
and resumes the first thread that tried to acquire the
mutex while it was locked.All methods expect a single parameter, the mutex table object, and can be invoked using the Lua colon syntax for method calls.
coroutine.fastcall (func, arg1, ...argN)
Performs a dynamic, on-the-fly recompilation of the
Lua function func
, into a normal,
non-generator JavaScript function. In many
JavaScript engines, JavaScript generator functions
do not enjoy the same JavaScript JIT compiler
optimizations that are available to non-generator
functions. The fastcall
mechanism
makes it possible to optimize specific Lua
functions that require higher performance.
The converted, "fast" version of the function is
cached for future invocations, by storing it in the
fast
property of the function object.
Any function called by func
will also
be fastcall
-converted and cached: This
is done by converting any call to function
fn
with a call to function
fn.fast
instead. As discussed at the
top of section 4, this fast
property is initially a utility function that
fastcall
-converts the function, and
overwrites the fast
property with the
converted function.
Note that functions converted by fastcall
cannot use any asynchronous features that depend on
generators, such as coroutines and require
.
The fastcall
recompiler works by
calling JavaScript toString()
on the input function.
Then, under the assumption that the function was
created by Loulabelle, rewrites yield*
expressions as direct function calls. Finally,
the converted function is recompiled using the
recompiler utility function assigned to the
eval
property of the original function,
as discussed at the top of section 4.
(But note that if the function references upvalues only
through JavaScript
statements, the eval
property will not
be set, and the converted function will not compile
correctly.)
As in Lua, the require
function is
provided in the global environment. The
package
table provides only two
fields: package.loaded
which caches
(or can be used to preload) modules, and
package.Loulabelle
which is the
Loulabelle version number and set to 5203
.
require (modname)
Invokes require_lua
to load a script file from a relative URL that is
composed of modname
concatenated with
the suffix .js
.
The script is loaded and executed, and per the
discussion in section 4 and in
require_lua
,
the script is expected to contain a single call
to $lua.chunk
, which makes the loaded
chunk accessible to Loulabelle.
The loaded chunk is then called, and its result is
recorded in the package.loaded
table,
and returned to the caller.
A chunk may be preloaded in advance of a call to
require
, by replacing the call to
$lua.chunk
with a call to
$lua.preload_chunk
.
At the top of the compiled chunk, instead of:
$lua.chunk( $lua.func ( ... ) );
Change to:
$lua.preload_chunk('new_chunk_name', $lua.func ( ... ) );
Preloading can also be used to bundle several chunks
into one file, and change chunk names.
The Loulabelle compiler itself makes use of this
technique; see the build.sh
example
in the Loulabelle compiler
folder.
Strings in Loulabelle are JavaScript strings, encoded in UNICODE, while most Lua implementations support 8-bit strings.
string.format
in Loulabelle supports
only 20 digits of precision in the various floating
point number formats, as opposed to 99 digits in Lua.
Patterns and Regular Expressions
Loulabelle does not support Lua patterns. Instead,
the full JavaScript regular expression engine is
available for use anywhere that a Lua pattern can
be used. To use a regular expression string, wrap
it in a call to string.regex
. For example:
local pattern = string.regex("([A-Z])([a-z]*)") local iterator = string.gmatch("Quick Brown Fox", pattern) while true do local cap1, cap2 = iterator() if cap1 then print (cap1, cap2) else break end end
If you limit the use of patterns and regular expressions to only those features common to both, then the following polyfill can be used in programs that have to run under Lua as well as Loulabelle.
string.regex = string.regex or function(s) return s end
string.dump (f)
Returns the input function f
. This is
merely intended to maintain compatibility with Lua
code that uses the result of string.dump
in a call to load
,
as in the example in section 2.2.
string.regex (s)
Records the regular expression s
in the
table of known regular expressions, for use by one
of the pattern functions: string.find
,
string.match
, string.gmatch
,
and string.gsub
.
string.index (haystack, needle, init)
Searches for the string needle
in string haystack
, optionally starting
at position init
(default is 1).
Returns the index of occurence, or nil
if not found.
Unlike the init
parameter to
string.find
, negative positions
do not count back from the end of the string.
The search is done using the JavaScript function
indexOf
.
string.rindex (haystack, needle, init)
Searches backwards for the string needle
in string haystack
, optionally starting
at position init
(default is #haystack
)
and going back towards the beginning of the string.
Returns the index of occurence, or nil
if not found.
Unlike the init
parameter to
string.find
, negative positions
do not count back from the end of the string.
The search is done using the JavaScript function
lastIndexOf
.
string.startswith (string, prefix, index)
Checks if string
, starting at position
index
(default to 1), starts with the
sub-string #prefix
.
The check is done using the JavaScript function
startsWith
.
string.endswith (string, suffix, length)
Checks if string
,
of length length
(default to #string
),
ends with the sub-string #suffix
.
The check is done using the JavaScript function
endsWith
.
string.trim (s)
Returns the string s
with whitespace
removed from both ends of the string.
Uses JavaScript function
trim
.
string.ltrim (s)
Returns the string s
with whitespace
removed from the left end of the string.
Uses JavaScript function
trimLeft
.
string.rtrim (s)
Returns the string s
with whitespace
removed from the right end of the string.
Uses JavaScript function
trimRight
.
os
library,
and even those provide limited functionality.
os.clock ()
Returns a time value measured in seconds. Note that
unlike the os.clock
function in Lua,
this is not CPU time. Uses
performance.now()
in the browser, and process.hrtime
in Node.js.
os.date (format, time)
The only formats supported are "*t"
to
return the results in a table (in which the field
isdst
is always false
),
or "%c"
(the default) to return a full
date string.
The format
string may optionally start
with '!'
to select UTC time rather than
local time.
os.time ([table])
If the field isdst
in the table is
true
, the time zone difference from UTC
is added to the result. This is not necessarily
desired or correct.
os.setlocale (locale, category)
Sets the current locale for the string collator,
which is initially set to a default JavaScript
Intl.Collator
object. Therefore category
can only
be specified as "all"
or "collate"
,
if not omitted. The locale
is passed
to the Intl.Collator
constructor.
This function does not return the current locale.
debug
library:
debug.getmetatable
,
debug.setmetatable
,
and debug.traceback
.
compiler
folder.
To invoke the compiler, you may need to set the
LUA_PATH
environment variable to include
the compiler folder.
export LOULABELLE=~/Loulabelle/compiler export LUA_PATH=$LOULABELLE/?.lua lua $LOULABELLE/main.lua [options] inputfileThe options are:
-h
: display usage-o
: optimize function calls and turn off call stack-G
: disallow assignment to globals without table reference-J
: disallow JavaScript statements-A
: disallow assume keywords-L name
: set object name for JavaScript (default $lua)-n name
: set chunk name (default same as inputfile argument)
The source Lua program is read from inputfile
if specified, otherwise from standard input. The compiled
JavaScript program is written to standard output.
build.sh
shell script in the
compiler
folder combines the various
modules that make up the compiler into a single
Loulabelle.js
file. This compiled
compiler can be preloaded into a web application:
<!DOCTYPE html> <html><head> <script type="text/javascript" src="core.js"></script> <script type="text/javascript" src="Loulabelle.js"></script> <script type="text/javascript" src="webapp.js"></script> </head><body></body></html>
In this example, webapp.js
is the
compiled form of webapp.lua
, which might
contain the following Lua code:
local Loulabelle = require "Loulabelle" local chunk_text = "print (_VERSION)" local chunk_name = 'test.lua' local func, err = Loulabelle(chunk_name, chunk_text, { env = _G, debug=true }) if not err then func() endThe third parameter to the compiler is a table of options:
version
:
If this field is true, the compiler immediately
returns with a version string and version number,
e.g. return "1.0", 1.0
jsobject
:
The name of the global runtime object. The default
name is $lua
. All compiled Lua code in
the same JavaScript environment should use the
same name.
debug
:
If this field is false, "release" or optimized
output is selected rather than "debug" output:
The generated code is slightly faster, does not
include Lua call stack and line number information,
and is minimized.
JavaScript
:
If this field is false, the compiler will not
recognize the JavaScript
statement.
globals
:
If this field is false, the compiler will detect
assignments to global variables, and raise an error.
assumes
:
If this field is false, the compiler will not
recognize the assume_...
keywords, in either statement or expression form.
env
:
If this field is omitted, the compiler returns a
string containing JavaScript source code. If this
field is specified, the compiler returns a JavaScript
function that was compiled from the source code string,
and had its Lua environment set to the value specified
in this field, which should be a Lua table.