diff options
Diffstat (limited to 'contrib/lua-lupa/README.md')
-rw-r--r-- | contrib/lua-lupa/README.md | 179 |
1 files changed, 179 insertions, 0 deletions
diff --git a/contrib/lua-lupa/README.md b/contrib/lua-lupa/README.md new file mode 100644 index 0000000..edf6dce --- /dev/null +++ b/contrib/lua-lupa/README.md @@ -0,0 +1,179 @@ +# Lupa + +## Introduction + +Lupa is a [Jinja2][] template engine implementation written in Lua and supports +Lua syntax within tags and variables. + +Lupa was sponsored by the [Library of the University of Antwerp][]. + +[Jinja2]: http://jinja.pocoo.org +[Library of the University of Antwerp]: http://www.uantwerpen.be/ + +## Requirements + +Lupa has the following requirements: + +* [Lua][] 5.1, 5.2, or 5.3. +* The [LPeg][] library. + +[Lua]: http://www.lua.org +[LPeg]: http://www.inf.puc-rio.br/~roberto/lpeg/ + +## Download + +Download Lupa from the project’s [download page][]. + +[download page]: download + +## Installation + +Unzip Lupa and place the "lupa.lua" file in your Lua installation's +`package.path`. This location depends on your version of Lua. Typical locations +are listed below. + +* Lua 5.1: */usr/local/share/lua/5.1/* or */usr/local/share/lua/5.1/* +* Lua 5.2: */usr/local/share/lua/5.2/* or */usr/local/share/lua/5.2/* +* Lua 5.3: */usr/local/share/lua/5.3/* or */usr/local/share/lua/5.3/* + +You can also place the "lupa.lua" file wherever you'd like and add it to Lua's +`package.path` manually in your program. For example, if Lupa was placed in a +*/home/user/lua/* directory, it can be used as follows: + + package.path = package.path..';/home/user/lua/?.lua' + +## Usage + +Lupa is simply a Lua library. Its `lupa.expand()` and `lupa.expand_file()` +functions may called to process templates. For example: + + lupa = require('lupa') + lupa.expand("hello {{ s }}!", {s = "world"}) --> "hello world!" + lupa.expand("{% for i in {1, 2, 3} %}{{ i }}{% endfor %}") --> 123 + +By default, Lupa loads templates relative to the current working directory. This +can be changed by reconfiguring Lupa: + + lupa.expand_file('name') --> expands template "./name" + lupa.configure{loader = lupa.loaders.filesystem('path/to/templates')} + lupa.expand_file('name') --> expands template "path/to/templates/name" + +See Lupa's [API documentation][] for more information. + +[API documentation]: api.html + +## Syntax + +Please refer to Jinja2's extensive [template documentation][]. Any +incompatibilities are listed in the sections below. + +[template documentation]: http://jinja.pocoo.org/docs/dev/templates/ + +## Comparison with Jinja2 + +While Lua and Python (Jinja2's implementation language) share some similarities, +the languages themselves are fundamentally different. Nevertheless, a +significant effort was made to support a vast majority of Jinja2's Python-style +syntax. As a result, Lupa passes Jinja2's test suite with only a handful of +modifications. The comprehensive list of differences between Lupa and Jinja2 is +described in the following sections. + +### Fundamental Differences + +* Expressions use Lua's syntax instead of Python's, so many of Python's + syntactic constructs are not valid. However, the following constructs + *are valid*, despite being invalid in pure Lua: + + + Iterating over table literals or table variables directly in a "for" loop: + + {% for i in {1, 2, 3} %}...{% endfor %} + + + Conditional loops via an "if" expression suffix: + + {% for x in range(10) if is_odd(x) %}...{% endfor %} + + + Table unpacking for list elements when iterating through a list of lists: + + {% for a, b, c in {{1, 2, 3}, {4, 5, 6}} %}...{% endfor %} + + + Default values for macro arguments: + + {% macro m(a, b, c='c', d='d') %}...{% endmacro %} + +* Strings do not have unicode escapes nor is unicode interpreted in any way. + +### Syntactic Differences + +* Line statements are not supported due to parsing complexity. +* In `{% for ... %}` loops, the `loop.length`, `loop.revindex`, + `loop.revindex0`, and `loop.last` variables only apply to sequences, where + Lua's `'#'` operator applies. +* The `{% continue %}` and `{% break %}` loop controls are not supported due to + complexity. +* Loops may be used recursively by default, so the `recursive` loop modifier is + not supported. +* The `is` operator is not supported by Lua, so tests of the form `{{ x is y }}` + should be written `{{ is_y(x) }}` (e.g. `{{ is_number(42) }}`). +* Filters cannot occur after tokens within an expression (e.g. + `{{ "foo"|upper .. "bar"|upper }}`), but can only occur at the end of an + expression (e.g. `{{ "foo".."bar"|upper }}`). +* Blocks always have access to scoped variables, so the `scoped` block modifier + is not supported. +* Named block end tags are not supported since the parser cannot easily keep + track of that state information. +* Any `{% block ... %}` tags within a "false" block (e.g. `{% if a %}` where `a` + evaluates to `false`) are never read and stored due to the parser + implementation. +* Inline "if" expressions (e.g. `{% extends b if a else c %}`) are not + supported. Instead, use a Lua conditional expression + (e.g. `{% extends a and b or c %}`). +* Any `{% extends ... %}` tags within a sub-scope are not effective outside that + scope (e.g. `{% if a %}{% extends a %}{% else %}{% extends b %}{% endif %}`). + Instead, use a Lua conditional expression (e.g. `{% extends a or b %}`). +* Macros are simply Lua functions and have no metadata attributes. +* Macros do not have access to a `kwargs` variable since Lua does not support + keyword arguments. +* `{% from x import y %}` tags are not supported. Instead, you must use either + `{% import x %}`, which imports all globals in `x` into the current + environment, or use `{% import x as z %}`, which imports all globals in `x` + into the variable `z`. +* `{% set ... %}` does not support multiple assignment. Use `{% do ...%}` + instead. The catch is that `{% do ... %}` does not support filters. +* The `{% trans %}` and `{% endtrans %}` tags, `{% with %}` and `{% endwith %}` + tags, and `{% autoescape %}` and `{% endautoescape %}` tags are not supported + since they are outside the scope of this implementation. + +### Filter Differences + +* Only the `batch`, `groupby`, and `slice` filters return generators which + produce one item at a time when looping. All other filters that produce + iterable results generate all items at once. +* The `float` filter only works in Lua 5.3 since that version of Lua has a + distinction between floats and integers. +* The `safe` filter must appear at the end of a filter chain since its output + cannot be passed to any other filter. + +### Function Differences + +* The global `range(n)` function returns a sequence from 1 to `n`, inclusive, + since lists start at 1 in Lua. +* No `lipsum()`, `dict()`, or `joiner()` functions for the sake of simplicity. + +### API Differences + +* Lupa has a much simpler API consisting of just four functions and three + fields: + + + `lupa.expand()`: Expands a string template subject to an environment. + + `lupa.expand_file()`: Expands a file template subject to an environment. + + `lupa.configure()` Configures delimiters and template options. + + `lupa.reset()`: Resets delimiters and options to their defaults. + + `lupa.env`: The default environment for templates. + + `lupa.filters`: The set of available filters (`escape`, `join`, etc.). + + `lupa.tests`: The set of available tests (`is_odd`, `is_defined`, etc.). + +* There is no bytecode caching. +* Lupa has no extension mechanism. Instead, modify `lupa.env`, `lupa.filters`, + and `lupa.tests` directly. However, the parser cannot be extended. +* Sandboxing is not supported, although `lupa.env` is safe by default (`io`, + `os.execute`, `os.remove`, etc. are not available). |