ltp Lua template processor

Software icon

ltp is a Lua-based template processor useful for generative programming, producing static Web pages, or as a general purpose preprocessor. For example, this Web site is generated using ltp. Also, ltp has been used as the basis for dynamic Web page rendering systems, such as the renderer and relay services included in Web Wispers™. In depth documentation and examples will be provided with future releases. At the moment, additional information is available in the man page.

A Simple Example

Even though ltp can be used as a stand-alone preprocessor, it also can be embedded into another program, such as the Web Wispers™ dynamic page renderer. Here is an example of an XHTML page template written for use with ltp and Web Wispers™.

<?lua= global.page.doctype ?> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <meta http-equiv="imagetoolbar" content="no" /> <link id="DefaultCSS" rel="stylesheet" href="<?lua= global.url.css_default ?>"></link> <?lua if page.css then for i,child in ipairs(page.css) do if child.code then ?> <style type="text/css"><?lua= child.code ?></style> <?lua else ?> <link id="PageCSS<?lua= i ?>" rel="stylesheet" href="<?lua= child.url ?>"></link> <?lua end end end if page.js then for i,child in ipairs(page.js) do if child.code then ?> <script type="text/javascript"><?lua= child.code ?></script> <?lua else ?> <script type="text/javascript" src="<?lua= child.url ?>"></script> <?lua end end end ?> <?lua if page.title then ?> <title><?lua= page.title ?></title> <?lua end ?> </head> <body> <div class="content"> <?lua for i,content in ipairs(page.content.column) do ?> <div id="Column<?lua= i - 1 ?>"><?lua= content ?></div> <?lua end ?> </div> </body> </html>

Using the same separation of presentation code from application logic as ClearSilver, ltp enables you to construct pages by defining the data to be used to populate a template:

return { wspr = { template = { html = "/include/page.ltp" } }, page = { title = "Hello World", css = { { url = "/css/hello.css" }, -- Example of inline code using the template example from above. { code = [[body { min-width: 400px; }]] } }, js = { { url = "/js/hello.js" } } content = { column = { [[ <h1>Hello World Column One</h1> <p>Hello <?lua= column_one_name ?>!</p> ]], [[ <h1>Hello World Column Two</h1> <p>Hello <?lua= column_two_name ?>!</p> ]] } } } }

Notice that you can embed dynamic variables inside of dynamically generated content without requiring new syntax. ltp lets you do everything you can do with ClearSilver with respect to templating and dynamic content generation. However, it's simpler, doesn't require learning an awkward template language of limited power, and is considerably faster.

Why Yet Another Template Processor?

We wrote ltp because early versions of Web Wispers™ used ClearSilver, which we found inadequate over time. First, maintaining code that embeds ClearSilver is a major chore. ClearSilver invents a byzantine error handling API that ultimately reproduces what you get for free with C++ exceptions. The error handling system is awkward to integrate with exception-based C++ code and is prone to memory leaks if you're not very careful. The ClearSilver templating language has very limited expressive power, making it overly difficult to perform simple tasks. When you try to modularize your code via macro libraries, you discover macros of any complexity are unreadable. Also, you pay performance penalties from stripping all of the whitespace you inserted to make your macros readable.

After we converted the Web Wispers™ renderer from ClearSilver to ltp, we found our dynamic page rendering times were between 4 and 5 times faster on average. That's comparing the two systems compiled with compiler optimizations enabled. Ultimately, our decision to write ltp was not driven by performance, but instead by software maintenance requirements. There is no guarantee that if you convert your ClearSilver templates to ltp that you will see comparable performance improvements (especially given that our comparisons are limited to using embedded ClearSilver in the Web Wispers™ renderer).

However, we found ClearSilver's claim to be "extremely fast" because it is coded in C to be misleading. The ClearSilver template language has to be parsed and the resulting data structure interpreted for rendering. HDF files have to be parsed into an internal data structure. We found using the Lua language (also written in C) directly for these tasks is faster. Also, in conjunction with our embedded use in Web Wispers™, the Web Wispers™ hierarchical Properties data structure is faster than the ClearSilver HDF library (for the data set sizes we use).

Finally, the idea that using a full-featured language like Lua in a templating system makes it impossible to make a separation between presentation and application logic is misconceived. The separation between presentation and application logic is a function of system architecture, not a function of templating language. PHP, J2EE JSP, Ruby on Rails, and other frameworks make it easy for the programmer to throw in application logic along with presentation code. You can't do that without extreme contortions when using ltp and Web Wispers™ because Web Wispers™ separates all rendering into one or more distinct rendering processes. Rendering processes have no knowledge of application state. Page templates, being executed in a sandbox, have no ability to interact with application state beyond extracting the data handed to them by the renderer. Therefore, by using Lua as the basis of ltp and the Web Wispers™ rendering system, you gain the development benefits of a full-featured programming language while preserving the separation of data and function provided by template systems such as ClearSilver.