Metalua Manual

Previous Up Next

Chapter 3  General purpose libraries and extensions

3.1  Standard library

Metalua comes with a standard library which extends Lua's one. These extended features are enabled by adding a ``require "std"'' statement in your source files (it is already required at the compile-time level, so there's no need for a ``-{ require "std" }'').

3.1.1  Base library extensions

Return the min of its arguments, according to <. There must be at least one argument.

Return the max of its arguments, according to <. There must be at least one argument.

Return the composition of all functions passed as arguments. For instance, printf could be defined as print `o` string.format1

Return all its arguments unchanged.

const (...)
Return a function which always returns ..., whatever its arguments. For instance, const(1, 2, 3)(4, 5, 6) returns 1, 2, 3.

Equivalent to print(string.format(fmt,...)).

Iterator, to be used in a for loop, which returns all the values of table.

for x in values(t) do [...] end is equivalent to for _, x in pairs(t) do [...] end.

Iterators, to be used in a for loop, which return all the keys of table.

for k in keys(t) do [...] end is equivalent to for k, _ in pairs(t) do [...] end.

FIXME: move this into metalua.compiler?

3.1.2  table extensions

Many of the extensions of table are dedicated to a more functional style programming. When compared to the functions in Haskell or ML's standard libs, these one are slightly more general, taking advantage from Lua's dynamic typing.

table.iforeach(f, ...)
table.iforeach(f, t) will evaluate f with every array-part elements of t in order.

If more than one table are passed as parameters, f will receive an element of each table at each iteration. For instance, table.iforeach (print, {1, 2, 3}, {4, 5, 6}, {7, 8, 9}) will print:
1 4 7
2 5 8
3 6 9
If the second and/or third parameters are numbers, they indicate the first and last indexes to use in the tables. First index defaults to 1, last index default to the length of the longest table. If only one number is passed, it's considered to be the first index. For instance, table.iforeach (print, 2, {1, 2, 3}, {4, 5, 6}, {7, 8, 9}) will only print:
2 5 8
3 6 9
table.imap(f, ...)
Similar to table.iforeach(), except that the results of f() calls are collected into a list and returned.

For instance, table.imap((|x,y|x+y), 2, {"foo", 1, 2, 3}, {"bar", 10, 20, 30}) will return {11, 22, 33}.

table.ifold(f, acc, ...)
Fold list elements thanks to a combining function f(), which takes two list elements and returns one result. For the first iteration, f() takes acc as its first param. For instance, the sum of list's elements can be computed by table.ifold( (|x,y| x+y), 0, list).

This function also accepts first and last indexes after acc, and more than one table argument: if there are more than one table, then more than two parameters are passed to f(). For instance, this function returns i2 max(x[i], y[i]): table.ifold( (|acc, xi, yi| acc + max (xi, yi)), 0, 2, x, y).

Take a sequence of lists, and return the list of their first elements, then their second elements, etc. For instance, table.izip ({1,2,3}, {4,5,6}) will return {{1,4}, {2,5} , {3,6}}.

table.ifilter(f, t)
Return the list of all elements of t for which f returns neither nil nor false.

Concatenate all the lists passed as arguments into a single one, then return it.

Flatten a list of lists into a list. for instance, table.iflatten{{1,2}, {3,4}} returns {1,2,3,4}.

Reverse the order of elements in t's array-part. This is done in-place: if you don't want to alter the original list, first copy it.

table.iall(f, ...)
Return true if and only if table.iforeach(f, ...) would return only non-false values.

table.iany(f, ...)
Return true if and only if table.iforeach(f, ...) would return at least one non-false value.

Does a shallow copy of the table t. This differs from table.icat(t), because the latter would only copy tha array-part, whereas the former also copies the hash-part.

Does a deep copy of t, i.e. all keys and values are recursively copied. Handles tables with shared and circular references correctly; also set the copy's metatable to the original's one.

table.range(a, b, c)
Return a list of all integers between a and b inclusive, with an increment c (which defaults to 1).

table.tostring(t, ...)
Return a string which represents t. This string is correctly indented, and handles Metalua's special syntax for ADT/AST gracefully. If "nohash" is passed as an additional argument, then only the tag and array-part of the table are displayed. If a number n is passed as extra argument, the function tries to keep the number of characters per line under n.

table.print(t, ...)
Equivalent to print(table.tostring(t, ...)).

3.1.3  string extensions

string.split(string, pattern)
Cut string into a list of substrings separated by pattern pattern, and return that list.

Alias for string.match: since it's quite common in metalua to use the pattern matching extension, which turns match into a keyword, it's practical to have another name for this function.

3.1.4  Library mlc

FIXME: move in metalua.compiler.

This library offer conversion between the different possible representations of metalua programs:
  • as source files
  • as compiled files
  • as source strings
  • as compiled chunk dumps (which are actually strings)
  • as lexeme streams
  • as AST
Hopefully, the function names are self-explanatory. Some of them are simply aliases to other standard functions such as loadstring() or string.dump(); many others are compositions of other functions. The point is that every sensible transformation from representation xxx to representation yyy should appear in this library under the name yyy_of_xxx(). This way, users don't have to wonder how to chain the appropriate functions to get the expected result.

The functions available in this module are:

FIXME: implementation sucks beyond maintainability, it should be rewritten.

3.1.5  Library walker

This library allows code-walking, i.e. applying advanced, non-local transformations on ASTs. It's powerful, but definitely not user friendly; eventuallty, it might be replaced by a Term Rewriting System, supported by its own Domain-Specific Language.

walk_stat (cfg)

walk_expr (cfg)
Same as walk_stat, except that it takes an expression AST instead of a statement AST.

walk_block (cfg)
Same as walk_stat, except that it takes a statements block AST instead of a single statement AST.

3.2  clopts: command line options parsing

This library allows to parse command line options in a generic, standard and reasonably powerful way. Using it for your programs helps ensure that they behave in an unsurprizing way to users. the metalua compiler parses its parameters with clopts. FIXME: Rest of the doc to be written

3.3  springs: separate universes for Lua

3.3.1  Origins and purpose

Springs (Serialization through Pluto for RINGS) is an extension of Lua Rings and Pluto: Lua Rings allow to create new Lua states from within Lua, but offers limited communication between them: a master universe can only send instruction to a slave universe through a ``dostring'', and the slave universe can only send back strings, integers and booleans as results. Since Pluto allows to serialize pretty much any Lua value as a string, it's used to create powerful bidirectional communications between universes. Springs is used internally by metalua to prevent different files' compile time actions to interfere with each other: each file is compiled on a fresh clean single-use slate. The underlying projects can be found on the web:
  • <>
  • <>
Notice however that the Pluto version used in metalua has significantly patched and debugged by Ivko Stanilov.

3.3.2  API

Go to Lua Rings web site for a reference on its original API. This API is extended by spring with:
  • function which creates a new universe ready for Pluto communication;
  • (:dostring() works as usual)
  • :pcall(f, arg1, ..., argn) works as standard function pcall(), except that execution occurs in the sub-state. Arguments are passed and results are returned transparently acrosse universes. Moreover, 'f' can also be a string, rather than a function. If it's a string, it must eval to a function in the substate's context. This allows to pass standard functions easily. For instance:
    r:pcall('table.concat', {'a', 'b', 'c'}, ',')
  • :call() is similar to :pcall(), except that in case of error, it actually throws the error in the sender universe's context. Therefore, it doesn't return a success status as does pcall(). For instance:
    assert('xxx' == r:call('string.rep', 'x', 3))

3.4  clist: Lists by comprehension

This extension offers improved tables-as-list syntax:
  • lists by comprehension;
  • literal lists splicing;
  • list sub-sampling.
Lists by comprehensions allow to describe a list in terms of generation loops and filtering. The loops are the two flavors of ``for'' controls, and the syntax is { <value> <loop_header> }. For instance, the list {10, 20, 30, 40, 50} can be generated as { x for x=10, 50, 10 }, or { 10*x for i=1, 5 }. The list of all keys from table t can be gathered with { k for k, v in pairs(t) }. Several loops can be nested. For instance, the list of all products of elements taken from list a and b can be computed with { i*j for _, i in ipairs(a) for _, j in ipairs(b) }.

Finally, results can be filtered out of a list. For instance, the elements of a which are multiple of 3 can be gathered with { x for _, x in ipairs(a) if x%3==0 }.

In Lua, when a list is defined and one of its elements is a multiple return function, only the first returned value is kept, except for the last element (cf. Lua manual 2.5.7). For instance, in the example below, x is set to {1, 1, 1, 2, 3}: only the last call to f() is expanded:
function f()
  return 1, 2, 3
x = { f(), f(), f() }
The extension offers a way to expand intermediate response: they ahve to be followed by .... In the example above, y = {f()..., f()..., f()...} would expand as {1, 2, 3, 1, 2, 3, 1, 2, 3}.

Comprehensions are naturally expanded, i.e. another way to write y would have been y = {i for i=1,3; i for i=1,3; i for i=1,3} (notice however that we had to separate elements with semicolons rather than commas: if we didn't, the i of the second loop would have been tekane as a third parameter to the first for loop header).

Sub-sampling is done with indexes, by using the comma to separate indices, and ... as an infix operator to denote intervals. The latter binds tighter than the former. For instance:
x = { i for i=101, 130 }
y = x[1 ... 10, 20, 25]
z = { i for i=101,110; 120; 125 }

assert (#y == #z)
for i = 1, #x do
  assert (y[i] == z[i]
Beware of a lexing issue: if you write ``[1...n]'', it will be interpreted as number 1. followed by operator ..: put a space between literal numbers and operators starting with a dot.

Notice taht there are now two substancially different operators with very similar syntaxes: the original index operator, which returns a single element, and the sub-sampling operators, which returns a list of elements. If you want to returna single element list, you can either reconstruct it from the regular index operator (y={x[i]}), or use a single element wide interval (y=x[i...i]).

FIXME: there should be x[...i] and x[i...] sub-sampling notations, but they aren't currently implemented.

Or in regular Lua syntax o(print, string.format).

Previous Up Next