1. Qix
  2. tlc

Overview

HTTPS SSH
# TLC **T**iny **L**ua-equipped **C**-preprocessor is a small integrated C/C++ preprocessor that acts as a compile-time scripting interface for code generation. TLC works closely alongside the Lua scripting language and is written in Java. TLC can also be extended to implement specific Java functionality as well. ## Inclusion There are various ways to script pre-processing. Initially TLC will include *inline*, *side-by-side*, and *imported* pre-processor scripts. ### Inline Inline scripts are Lua scripts included *directly in the C++ source file*. While arguably messy, these scripts are editable directly within the code. Inline Lua is just performed using the Lua directive. If defining functions, for example, the closure will return nothing and will essentially eliminate the call from the output source (see the below note under *Lua Directive*). ### Side-by-Side Side-by-Side inclusion occurs when a `.c` or `.cpp` file has an adjacent `.c.lua` or `.cpp.lua` file, respectively. The Lua script is automatically imported into the state before the C++ source is preprocessed. ### Imported Lastly, scripts can be imported using the `@import` directive (see below). Import scripts can be found given inclusion directories passed along the command line. #### Tricky Business The import system is just a translation of built-in Lua keywords. Include directories passed along the command line are simply added to the module path (yes, `?` is still valid), and `@import` simply `require`'s the script. ## Directives There are two directive formats that are associated with TLC and activate two different portions of the project. ### Function Directive All function directives refer to a specific **Java functionality**. Some examples: ``` @import "myScript" // Invokes the registered Java handler to import a script @define var val // Invokes the registered Java handler to create a variable ``` The two directives above will invoke specific Java (*not* Lua) classes to handle the code. ### Lua Directive The Lua directive refers to specific **Lua functionality** and executes arbitrary Lua code. It works similarly to a macro, but instead is replaced by the return value of the closure. There are two forms of the Lua directive; *return* form and *closure* form. The return form, `$value`, prepends `return ` to the beginning of the statement. The closure form, `${statement}`, simply executes Lua and doesn't necessarily have to return values. `$value` is functionally equivalent to `${return value}`. > NOTE: If 0 values are returned from a closure, the directive is simply deleted from the output source. If more than one value is returned, the rest are discarded and only the first is used as a replacement. > NOTE: The *return* form allows for tricky use of meta-methods as it calls `tostring` on all values. Developers can use this to their advantage by writing meta-method replacements for tables to do some neat stuff without having to use `${return someCode()}`. > > See *More Tricky Business* for an example For instance, if we have the Lua function: ```lua function appendif(test, append, inp) if test == true then return inp .. append else return inp end end ``` And then the equivalent C++ code ```cpp #include <iostream> // Debug mode? @set debug true void output_d() { std::cout << "Debug mode is ON"; } void output() { std::cout << "Debug mode is OFF"; } int main() { ${return appendif(debug, "_d", "output")}(); // Evaluates to output_d(); return 0; } ``` The above would effectively output `Debug mode is ON`: It also recognizes Lua tables; the following would also be acceptable: ```lua -- Lua tbl = {} function tbl.appendif(...) -- ... ``` ```cpp // C++: ${return tbl.appendif(debug, "_d", "output")}(); ``` #### More Tricky Business The note above mentions that `tostring` is called on all *replacement*-type directives. This can lead to some interesting uses of meta-methods; the following is an example. Lua: ```lua mt = {} mt.__tostring = function() return "\"Woooo!\"" end woostring = {} setmetatable(woostring, mt) ``` C/C++: ```cpp #include <iostream> int main() { // Even though woostring() is a table, its // __tostring() meta-method allows it to // be used with TLC's replacement-type Lua directive. printf("The programmer says \"%s\"", $woostring); return 0; } ``` ## Processing Processing the files is defined very clearly and how you would (hope) to expect it to. ### Order and Scope Processing is handled in a **linear** fashion, and variables **do not persist between files**. Yes, TLC (as of this writing) **does not acknowledge `#include` directives**. ### Evaluation of Invocation/Replacement Directives All directives working with Lua (i.e. Replacement Directives and Invocation Directives) are **evaluated as lua** instead of parsed by Java. Invocation directives simply **evaluate** the code, whereas replacement directives **evaluate and return** the code. For replacement directives, any code that cannot be successfully parsed with a `return` pre-pended onto the raw string will need to be placed into a function using one of the placement methods listed above. ## Caveats The golden rule of TLC is that it should be run *before* the C preprocessor is ever invoked. # License / Disclaimer This code is provided 'as-is' with no warranty whatsoever. Use it at your own risk. TLC cannot be distributed or sold (including bundled with any software) neither in source or binary form. All other reproductions, communications, modifications, enhancements, hacks or uses of the code are permitted with credit back to the (this) original GitHub repository. If you do something cool with it, I ask that you show me/submit a pull request (please!). All limitations (or lackthereof) are only applicable to the fullest extent as the referenced supporting 3rd party libraries allow, respectively. The views expressed in snarky (or anything synonymous) comments cannot be used against the original Author(s).