BAM

Introduction

BAM (the Big Article Machine) is an extremely simple templating engine. It converts a mixture of text and specially marked code logic into pure text. Similarly to other templating engines, it can be used on the Web to serve web pages, prepare reports, aid in literate programming and function as a code preprocessor. It can be used to mark up pure text, LaTeX, HTML, C code or any other text-based format. The code logic is written in Lua, a very simple language which is also used by other projects such as LuaTeX.

Download

Download BAM on GitHub

What it does

I have a series of posts that talk in detail about what you can do with BAM.

How to use

BAM can be used with either the Lua or the LuaJIT (1/2) interpreters (available through your package manager, MacPorts or LuaBinaries), version 5.1 or later.

In the most common incantation, BAM is used as following:

lua bam.lua [options] mytext

 


BAM will look for a mytext.bam template, process it and output into mytext.

The following options are available:

  • --sandbox: attempts to protect your system by sandboxing the BAM template you are processing. In effect, it throws an error if the BAM template tries to run shell command, create new files or access the filesystem. (Note that this is not a perfect sandbox, and you should still treat the paper template as running code)
  • --require filename: runs or loads the specified Lua module. You can load libraries (for instance, gsl-lua) to add new modules that can be called within your template. (short: -r)
  • --nformat format: specifies the default number format to use when a number is interpolated in the template (for instance, Pi is equal to <@ pi @>). The number format is a C-style format specifier (e.g. %.2f, %15.10e, etc.).

Reference

A template is parsed and executed top to bottom. While the large majority of code is straighforward Lua, a few extensions have been added to make the writing more convenient and expressive. BAM snippets are embedded in the text using one of the following syntaxes:

%@ code line

Specifies a new line of Lua code to be executed. This can be used to perform computations, control the logic flow of the text, launch shell commands, etc. For example:


We then ran our analysis pipeline on the newly obtained data.
%@ for dset = 1, 4 do
%@ -- run the analysis software
%@ results = exec("./analyze dataset_" .. dset)
%@
%@ if results:find("inconclusive") then
Unfortunately, our data analysis for dataset #<@ dset @> was inconclusive.
%@ else
The results of our data analysis for dataset #<@ dset @>is reproduced as follows:
%@ pn(results)
%@ end
%@ end

 

launches the “analyze” command on every dataset numbered from 1 to 4, reads its output and based on it branches the text (writing either that the analysis was inconclusive, or the results of the analysis). The p and pn functions are used to print a string, a number or a table into the output document. In this artificial example, the output would be:


The results of our data analysis for dataset #1 is reproduced as follows:
red = 0.38, green = 0.66, blue = 0.77
The results of our data analysis for dataset #2 is reproduced as follows:
red = 0.10, green = 0.24, blue = 0.29
Unfortunately, our data analysis for dataset #3 was inconclusive.
The results of our data analysis for dataset #4 is reproduced as follows:
red = 0.77, green = 0.73, blue = 0.05

 

You can use loops, iterators, if..then..elseif..else..end and any other blocks offered by Lua.

The following extensions are available:

  • an once … end block is printed only once, even if it is contained in a loop. This is useful when you have an introductory snippet that does not need to be repeated for each section.

<@ … @>

(the interpolating syntax). Substitutes the value of the variable or expression at its position in the document. For instance,


%@ for i = 1, 9 do
%@ for j = 1, 9 do
<@ i @> times <@ j @> equals <@ i * j @>.
%@ end
%@ end

outputs the multiplication table:

..snip..
8 times 7 equals 56.
8 times 8 equals 64.
8 times 9 equals 72.
9 times 1 equals 9.
9 times 2 equals 18.
9 times 3 equals 27.
..snip..

Any valid Lua expression that returns a value is legal inside <@ @>. Strings are outputed as is; numbers are formatted using the default number format (see the –nformat option); and table arrays are written out comma-separated, with the last element preceded by “and”. For example,


%@ authors = {"Frank Shu", "Vera Rubin", "Frank Drake"}
\authors{<@ authors @>}

will become

\authors{Frank Shu, Vera Rubin and Frank Drake}

The following extensions are available by default:

  • <@ phrase1 | phrase2 | phrase 3 | … @>: writes out one of the pipe-separated sentences randomly (to make it repeatable, set the random seed in the template using the randomseed() function). Example:
    
    This amplitude <@ implies | suggests | corresponds to @> a minimum mass of $\mass \sin i = <@ mp(fit.M[1]) @>$.
    

    becomes, for instance,

    This amplitude suggests a minimum mass of $\mass \sin i = 0.5 M_\earth$.
  • <@? cond1, “phrase1”, cond2, “phrase2”, …, “phraseelse” @>: loops through the conditions and prints the phrase whose corresponding condition is true. Otherwise, it will print the last item. Example:
    
    The orbit is <@? ecc < 0.1, "essentially circular", ecc < 0.2, "modestly eccentric", ecc < 0.6, "eccentric", "very eccentric" @>.
    

    For ecc = 0.5, the text would read “The orbit is modestly eccentric”.

  • <@! command @>: substitutes the output of the command. This is shorthand for <@ exec(“command”) @>.
  • <@~ filename @>: inserts the specified text file verbatim. This is shorthand for <@ readf(“filename”) @>.

Additional functions

For convenience, BAM imports all functions from the io, os and math tables into the global namespace (so you can write sin(0.1) instead of math.sin(0.1). BAM provides a small number of commodity functions as shorthands for more complicated Lua expressions.

  • exec(command): executes the command and returns its output.
  • exists(filename): checks whether the file filename exists.
  • f(format, …): alias for string.format.
  • filter(“type”, fnc): adds a new filter function fnc, whose type can be “interp” or “line”. See the Extending syntax section.
  • log(str): logs a string to the log file (extension .bamlog).
  • p(obj)/pn(obj): prints into the document the value of obj. pn adds a newline.
  • readf(filename): returns the content of the file.
  • split(str, sep): splits a string according to sep and returns the tokens into an array.
  • trim(str): trims the string.
  • writef(filename, str): writes the string to the file.

Extending syntax

You can create new extensions by creating new extension filters. These filters are functions that receive a string verbatim (which may not be a valid Lua expression) and return a new string that is a valid Lua expression, or nil if they are not going to process the string (e.g. it doesn’t match a specified pattern). Filters are defined using the filter() function.

For instance, here’s how the syntax <@~ file @> to include an entire file verbatim is defined. Using a new interp filter:


filter("interp", function(str) 
if (str:sub(1,1) == "~") then return "readf('" .. str:sub(2) .. "')" end
end)

 

which substitutes the string “~ filename” for “readf(‘filename’)”.

Bugs and limitations

  • Currently, BAM uses very naive pattern matching to extract tokens. This means that, for instance, nested <@ @> are not allowed and may confuse the parser.

License

BAM is licensed under the MIT open source license.

Copyright (c) 2011, Stefano Meschiari

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.