
Advanced General-Purpose Preprocessor
Direct includes from Github (versioned) and URLs
Flexible syntax (macros, loops, expressions, etc.)
Asset filters (escape, base64, custom)
Automatic un-indentation of nested blocks
Line control statements generation
Available as cli or as npm library
Extensible and embeddable via JS API
[](https://travis-ci.org/myurasov/Pre)
Syntax
Directives
Directives start with @ symbol.
@set
@set <variable:identifier> <value:expression>
@set <variable:identifier> = <value:expression>
Assigns a value of an expression to a variable.
Variables are defined in a global context.
Example:
Sets SOMEVAR to 1:
@set SOMEVAR min(1, 2, 3)
@macro
@macro <name>(<arguments>) <body> @endmacro
@endmacro can be replaced with @end.
Defines a code region that can take it's own parameters. Macros are declared in a global scope. Macro parameters are only available within the macro scope and override global variables with the same name (but do not affect them).
Macros can be used:
via
@includedirective:@include macro(a, b, c)
inline:
@{macro(a, b, c)}When macros are used inline:
- no line control statements are generated for the output inside the macro scope
- trailing newline is trimmed from macro output
Examples:
@macro some_macro(a, b, c)
Hello, @{a}!
Roses are @{b},
And violets are @{defined(c) ? c : "of undefined color"}.
@end
Then some_macro can be used as:
@include some_macro("username", "red")
which will produce:
Hello, username!
Roses are red,
And violets are of undefined color.
The same macro used inline:
[[[ @{some_macro("username", "red", "blue")} ]]]
will ouput:
[[[ Hello, username!
Roses are red,
And violets are blue. ]]]
@include
Includes local file, external source or a macro.
@include <source:expression>
Macro
@include some_macro("username", 123)
Local Files
@include "somefile.ext"
Remote Files
@include "http://example.com/file.ext"
@include "https://example.com/file.ext"
From GitHub
@include "github:<user>/<repo>/<path>[@<ref>]"
Where:
user– user/organization namerepo– repository nameref– git reference (branch name or tag, defaults to master)
Examples:
Head of the default branch
@include "github:electricimp/Promise/Promise.class.nut"
Head of the develop branch
@include "github:electricimp/Promise/Promise.class.nut@develop"
Tag v2.0.0:
@include "github:electricimp/Promise/Promise.class.nut@v2.0.0"
Authentication
When using GitHub includes, authentication is optional, however:
- with authentication GitHub API provides much higher rate limits
- to access private repositories authentication is required
Apart from GitHub username you need to provide either a personal access token or password (which is less secure and not recommended). More info on how to provide those parameters is in usage section.
@include once
@include once <source:expression>
Acts the same as @include but has no effect if source has already been included.
Macros are always included.
@{...} (inline expressions/macros)
@{<expression>}
@{macro(a, b, c)}
Inserts the value of the enclosed expression or executes a macro.
Example:
@set name "Someone"
Hello, @{name}, the result is: @{123 * 456}.
results in the following output:
Hello, Someone, the result is: 56088.
@while
While-loop. loop variable is available in @while loops.
@while <test:expression>
// 0-based iteration counter: @{loop.index}
// 1-based iteration counter: @{loop.iteration}
@endwhile
@endwhile can be replaced with @end.
@repeat
Loop that repeats a certain number of iterations. loop variable is available in @repeat loops.
@repeat <times:expression>
// 0-based iteration counter: @{loop.index}
// 1-based iteration counter: @{loop.iteration}
@endrepeat
@endrepeat can be replaced with @end.
Example:
@repeat 3
loop.iteration: @{loop.iteration}
@end
outputs:
loop.iteration: 1
loop.iteration: 2
loop.iteration: 3
@if – @elseif – @else
Conditional directive.
@if // consequent code @elseif <test:expression> // else if #1 code // ...more elseifs... @else // alternate code @endif
@endif can be replaced with @end.
Example:
@if __FILE__ == 'abc.ext' // include something @elseif __FILE__ == 'def.ext' // include something else @else // something completely different @endif
@error
@error <message:expression>
Emits an error.
Example:
@if PLATFORM == "platform1" // platform 1 code @elseif PLATFORM == "platform2" // platform 2 code @elseif PLATFORM == "platform3" // platform 3 code @else @error "Platform is " + PLATFORM + " is unsupported" @endif
Filters
"Filter" | operator allows to pass a value through any of supported functions.
@{<expression> | <filter>}
which is equivalent to:
@{<filter>(<expression>)}
Example:
// include external HTML to a string
a = "@{include('index.html')|escape}"
// include external binary file to a base64-encoded string
b = "@{include('file.bin')|base64}"
Expressions
Directives that have parameters allow usage of expression syntax.
For example:
@include <source:expression>@set <variable:identifier> <value:expression>@if <condition:expression>@elseif <condition:expression>@{<expression>}(inline expressions)
Types
The following types are supported in expressions:
- numbers (eg:
1,1E6,1e-6,1.567) - strings (eg:
"abc",'abc') nulltruefalse
Operators
Binary
|| && == != < > <= >= + - * / %
Unary
+ - !
Member Expressions
somevar.membersomevar["member"]([1, 2, 3])[1]
Conditional Expressions
test ? consequent : alternate
Variables
- Variables defined by
@setstatements are available in expressions. - Undefined variables are evaluated as
null. - Variable names can contain
$,_, latin letters and digits and can start only with a non-digit.
__LINE__
Line number (relative to the file in which this variable appears).
Example:
Hi from line @{__LINE__}!
__FILE__
Name of the file in which this variable appears.
Example:
Hi from file @{__FILE__}!
__PATH__
Absolute path (not including file name) to the file where this variable appears. Contains url for remote includes.
Example:
Hi from file @{__PATH__}!
loop
Defined inside @while and @repeat loops.
Contains information about the current loop:
loop.index– 0-indexed iteration counterloop.iteration– 1-indexed iteration counter
Example:
@set myvar = 12
@while myvar > 9
@set myvar = myvar - 1
var: @{myvar}
loop.index: @{loop.index}
@end
outputs:
myvar: 11
loop.index: 0
myvar: 10
loop.index: 1
myvar: 9
loop.index: 2
Functions
defined(<variable_name>)– returnstrueif a variable is defined,falseotherwise.include(<source>)– includes external sourceescape(<value>)– escapes special characters in string (\b,\f,\n,\r,\t,\,',")base64(<value>)– encodes value as base64min(<numbers>)max(<numbers>)abs(<number>)
Comments
Lines starting with @ followed by space or a line break are treated as comments and not added to the output.
Example:
@ something about platform #1 @set PLATFORM "platform1"
Usage
Please note that Pre requires Node.js 4.0 and above.
As npm library:
npm i --save Prethen
const Pre = require('Pre'); const pre = new Pre(); // (optional) provide GitHub credentials pre.machine.readers.github.username = "<usename>"; pre.machine.readers.github.token = "<personal access token>"; const output = pre.machine.execute(`@include "${inputFile}"`);As CLI:
Pre provides
prprcsscommand when installed globally:npm i -g Pre pre [-D<variable> <value>...] [-l] [--github-user <usename> --github-token <token>] [-l] <input_file>
where:
-l– generate line control statements-D<variable> <value>– define a variable--github-user– GitHub username--github-token– GitHub personal access token or password (not recommended)
Testing
SPEC_LOGLEVEL=<debug|info|warning|error> \
SPEC_GITHUB_USERNAME=<GitHub username> \
SPEC_GITHUB_TOKEN=<GitHub password/access token> \
npm test
License
MIT. Pre is a fork of Electric Imp's Builder licensed under MIT.
Author
Mikhail Yurasov me@yurasov.me