B3.Syntax Summary
B3.Syntax Summary
In scenarios where templates are used, it is particularly desirable that the template syntax does not destroy the syntax of the target file and that the two coexist without interference.
For example, when generating java with velocity, I hope the template can be checked and highlighted in both velocity and java syntax. when generating html, the template should be able to preview directly in the browser without breaking the html syntax.
template syntax- syntax of template engine, such as FreeMarker, Velocity.target syntax- aka.native syntax, syntax of the target file, such as java, html.meepo syntax- simple markup directives that use the comments of the target syntax to translate the template.- All text order is left-to-right, right-to-left language is not supported.
The directive is made by comment of the native syntax and the replacement is processed line by line to output the underlay template (backend).
The processing of the native syntax is divided into two processes, parse and merge, and the lookup in parsing depends on regular expressions. When merging, except for RNA, it is output directly, and the effectiveness is equal to StringBuilder.append.
There is no complex flow control and execute function in RNA, so parsing once and the subsequent merging is very efficient.
- Without
RNA, equivalent to a static string for idempotent operations, merge only once, used directly afterwards RNArelies on anexecution engine, equivalent toMap+StringBuilderexcept for dynamic languagejavaexecution engine, dynamically compiled to bytecode, equal to native java class after first compilationStringBuilderpre-calculates the length to avoid expansion copying in the process
B3.1.Template Features
There are some basic concepts and conventions in Meepo syntax, as follows,
blank- one0x20or0x09, i.e. space or\talphanum-[a-zA-z0-9], letters and numbers, case-sensitivenative comment- comments of native language, e.g.,//,/*and*/directive- special annotation in Meepo, such as a prefixDNA- Defined Native AnnotationRNA- Runnable Native Annotation? +*-[0,1],[1,∞),[0,∞), respectivelydirective line- directive line is only parsed byMeepoand invisible in mergeline- refers to[^\n]\nor[^\n]$format
For simplicity, the native comment + blank before the directive is omitted in the following,
HI-MEEPOHi! Meepo, definenative commentfor subsequent parsingDNA:SETSet replacement, define a substitution and its scopeDNA:ENDEnd effect, stop acting when or whereDNA:BKBEffect Immunity, the region beforeENDis not parsedDNA:RAWNative raw string, after execution, output the raw string behind itDNA:SONSub-template, used as a component to import other templates during parsingRNA:PUTPut variable, execute theexecute bodyand store the result in the environmentRNA:USEUse variable, use the built-in orPUTed variable in environmentRNA:RUNExecute every time, executesfunction bodyevery time, such as counterRNA:WHENConditional execution, combined into if-elseif-else logic blocksRNA:EACHLoop execution, loop in an array or collectionRNA:ELSEElse condition, the else branch forWHENandEACHRNA:DONEStop executing, ends the scope ofWHENandEACH
Always use \n as line terminators to break line, even if \r\n in window is processed as \n. In the single line comment style that ends with\n, the\n is considered as a syntax terminator, and will not be output in the merge.
Some RegRxp background knowledge is required, as Java's RegExp is used to parse the template.
- In searching,
.^$?+*{}()[]\|is always misused, no need to escape some chars in[] - In replacement,
\is mainly used as aback reference, and some of the()combinations have special meanings.
The regular compile options are UNIX_LINES and MULTILINE, set by the embedded flag,
(?idmsuxU-idmsuxU)Nothing, but turns matchflagson/off(?idmsux-idmsux:X)X, as a non-capturing group with the givenflagson/off- i Pattern.CASE_INSENSITIVE Not case-sensitive, default off
- d Pattern.UNIX_LINES Only
\nasline terminators, default on - m Pattern.MULTILINE
^and$will matchline terminators, default on - s Pattern.DOTTAL
.matchline terminators, default off - u Pattern.UNICODE_CASE Unicode-aware case folding, default off
- x Pattern.COMMENTS whitespace and comments in pattern, default off
- U Pattern.UNICODE_CHARACTER_CLASS, default off
B3.2.HI-MEEPO Hi! Meepo
Syntax: comment head blank+ HI-MEEPO !? blank+ comment tail?
Define comment and identify this file as the native file that Meepo will parse.
comment head- The first part of a single or multi comment, the previous non-blankstringHI-MEEPO- The strange name (hi meepo) is to avoid repetition and escape!- The!preserves theblankbefore and after the directive, explained latercomment tail- The end of multi-line comment, the latter non-blankstring
Hi! Meepo must be on a separate line, and using blank can improve the readability. Similar to using DELIMITER in sql to define the terminator, for example,
- java -
// HI-MEEPO,//is comment - java -
/* HI-MEEPO */,/*and*/are comment - sql -
-- HI-MEEPO,--is comment - bash -
# HI-MEEPO,#is comment - html -
<!-- HI-MEEPO -->,<!--and-->are comment
Note: that the comment head can have repeated letters, and Meepo only handles the repetition of the same character, for example,
/*-/***** DNA:RAW, invalid, nothing to do//-////// DNA:RAW, valid, handle the repeats#-##### DNA:RAW, valid, handle the repeats
For the following text (DNA and RNA) parsing, there are 2 types of parsing, line parsing and block parsing, rules as follows,
HI-MEEPOis always a line parser and must occupy a separate line.single line commentstyle Meepo is parsed line by line.Multi-line commentstyle Meepo is read across lines and parsed by block.- Parsing is not always done line by line, so
^and$are not always at the beginning and end of a line in the regular matching.
HI-MEEPO! and HI-MEEPO handle the first blank of the directive line according to the following rules,
- Without
!and the whole line directive will ignore its line in the output, i.e. the first\nafter the directive line. - With
!, only the directive between the head and tail of the Meepo is processed, and the blanks before and after are preserved. DNA:RAWis special, the!has no effect on it, it keeps its outside and removes the first blank inside it.- In
@<!--_DNA:RAW_SUPER_-->@,@and_denote reserved and removed blanks, respectively.
In subsequent examples, // HI-MEEPO is used as an example, but omitted.
B3.3.Factory DNA, Static Replacement
DNA is like a factory director that defines replacement directives for efficient static text replacement at parse time.
B3.4.DNA:SET Setting Replacement
Syntax: DNA:SET blank+ delim find delim repl delim effect?
Replaces the pattern matching string within a specified scope or times.
delim-delimiter, the 1st non-(blank,!,alphanum) 1-2byte char, such as/find- RexExp pattern (withoutdelim), refer tobackreferenceif grouping existsfindis empty, thisSETis ignored.repl-replace, string (withoutdelim), refer tobackreferenceif grouping existsreplis empty, it means delete, i.e. replace with emptyeffect- The number of times to take effect, i.e. when/where to stop, notblank
The backreference refers to the case where there is () in find or the \1 inrepl. This affects the delimiter of the find/replace string, and also avoid writing complex expressions with the following convention rules.
If there is no group in
find, use group(0), i.e., match all.If
findhas group, take the first(, i.e. group(1) content.If
((A)(B(C)), count(in the order from left to right.- group(1) - ((A)(B(C)))
- group(2) - (A)
- group(3) - (B(C))
- group(4) - (C)
times, values separated by,or closed range with-, e.g.1-3,15.named, infinite times that can be terminated byEND.
// DNA:SET /false/{{user.male}}/
var isMale = false;
/* Replace only false in above line with the variable user.male. the output is :
var isMale = {{user.male}};
*/B3.5.DNA:END End Effect
Syntax: DNA:END (blank+ effect)+
End the scope of effects created by directives, such as the named scope of SET.
// DNA:SET /1010100/{{id}}/id
// DNA:SET /"(VAR)"/{{desc}}/1
// DNA:SET /VAR/{{info}}/2
SUPER(1010100, "ConstantEnumTemplate", "VAR", "VAR")
// DNA:END id
/* Defines named id, desc's group(1), info's 2 matches. the output is :
SUPER({{id}}, "ConstantEnumTemplate", "{{desc}}", "{{info}}")
*/B3.6.DNA:BKB Effect Immunity
Syntax: DNA:BKB blank+ effect
Define a named global effect immunity scope that can be ended by END, and the text and directive within it will not be processed.
- text - any text that is not Meepo directive
- directive - treated as text except for the END which corresponds to the current BKB.
- There can only be one BKB currently at a time.
// DNA:BKB KING
// DNA:SET /"(VAR)"/desc/1
SUPER(1010100, "ConstantEnumTemplate", "VAR", "VAR")
// DNA:END KING
/* ignore the SET directive, the output is :
// DNA:SET /"(VAR)"/desc/1
SUPER(1010100, "ConstantEnumTemplate", "VAR", "VAR")
*/B3.7.DNA:RAW Native Raw String
Syntax: DNA:RAW blank+ raw string
Define a template with comment syntax to compensate for cases where the native syntax is not supported. The use of single-line comment is simple and clear. and the use of multi-line comment to keep only the content between head and tail.
The effect is to remove the comment head, DNA:RAW, comment tail and the blank in them.
/* The following 2 lines have the same output i.e. `// DNA:RAW ` */
SUPER(1010100, "ConstantEnumTemplate", "VAR", "VAR")
// DNA:RAW SUPER(1010100, "ConstantEnumTemplate", "VAR", "VAR")B3.8.DNA:SON Sub-Template
Syntax: DNA:SON blank+ path
Reads the resource in UTF8 from path and expands its contents at the current position, and then parse them all. The path can contain a protocol, classpath by default, only the following protocols are supported.
http://,https://, read by GET requestfile://,/, read from file systemclasspath:, read from the classloader, Note no//here- Starting with
/or.means the relative path to the host template, but is not recommended.
Note: the son templates must include HI-MEEPO separately, which is a static parsing in the separate context and later merged into the current parent template.
B3.9.Workshop RNA, Dynamic Execution
RNA is like a workshop manager, defining execution directives, calling the execution engine at merge time, and using its result as a replacement.
- An
execution enginecan executefunc(function body) of multipletypes, where one type is referred to as anengine. - The name of
enginesmust bealphanum, case sensitive, e.g.js. - Engine whose name endwith
!such asjs!, will ignore errors ,continue execution, returnsnull. - If the execution returns
null,empty stringis used to merge the template.
The default engine in RNA is map. Users can register other engines with RnaManager, described later.
map-sessionlevel, usefuncas key, go value fromenvironment, if not, return the key.raw-nothinglevel, return thefuncdirectly as a string without expanding the escapes.
Meepo uses multi-line block parsing for multi-line comments, so the func naturally supports multi-line to improve readability.
B3.A.RNA:PUT Put Variable
Syntax: RNA:PUT blank+ engine? delim var delim func delim
Specify the engine to execute the func and store the function or execution result in the environment (refers map engine) for other RNA to use.
environmentis the Meepo context and some scripting engine's contextenginesee engine description for detailsdelimis the same asSET.varis the variable stored into the context, not the native literal.funcis executed by a specific engine, such as spring, then it can be executed a SpEL.- if
varorfuncis empty, nothing is executed.
// DNA:PUT os/who/basename $(pwd)/
/* Take the output of `basename $(pwd)` and store it in context with `who` as the key */B3.B.RNA:USE Use Variable
Syntax: RNA:USE blank+ delim find delim var delim effect?
RNA version of SET, the difference is the var value is get from the map engine, not the literal replacement of the underlying template. The rules for getting variable(e.g., navigation class objects, pipeline handling functions) are described in the map engine section.
Automatic multi-paragraph indentation support is performed when merging vars, depending on the type of var value satisfying the follows,
find stringstarts with indented whitespace.var valueis an Array or Collection and its size is greater than 1.
Indenting more than 2 elements will align with the 1st element. sometimes lines may be unintelligent and look bad. eg.
- Indented object, no
\nending, no line break, like zebra stripes - Unindented object, contains
\n, with line break, look interlaced
// DNA:USE /meepo/user.home/
var userHome = "meepo";
/* get System.getProperty("user.home"). the output is:
var userHome = "/home/trydofor";
*/B3.C.RNA:RUN Execute Every Time
Syntax: RNA:RUN blank+ engine? delim find delim func delim effect?
combine PUT and USE, support indentation, the difference are,
find, empty means execute only, no replacefunc, use execution result directly, no store tovar- execute every time, eg. counter function, increment on each call, no cache.
// DNA:RUN os/rand/echo $RANDOM/1-3
var userName = "meepo-rand";
var userPass = "rand-rand";
/* get random number each time and print. the output is:
var userName = "meepo-12599";
var userPass = "16345-31415";
*/B3.D.RNA:WHEN Conditional Execution
Syntax: RNA:WHEN blank+ engine? delim bool delim func delim group
Multiple WHEN can be combined into if-else if-else logic blocks.
bool- Must bey|yes|n|no|not, indicatingtrueorfalsefunc- Evaluate the execution result to boolgroup- Must bealphanum, groupELSEandDONEin the logic scope
Evaluate the following cases get false, evaluate not to false get true
- boolean
false - object
null - number
NaNor between positive/negative0.000000001(9 digits) emptystring, zero size of Array/Collection/Map
<!-- RNA:WHEN /yes/it.rem0/bg -->
<li value="code">rem0-name</li>
<!-- RNA:WHEN /not/it.rem1/bg -->
<li value="code">rem2-name</li>
<!-- RNA:ELSE bg -->
<li value="code">rem1-name</li>
<!-- RNA:DONE bg -->Equivalent to the if(a){}else if(!b){}else{} in following js pseudocode
if (it.rem0){
console.log('<li value="code">rem0-name</li>')
} else if (!it.rem1){
console.log('<li value="code">rem2-name</li>')
} else {
console.log('<li value="code">rem1-name</li>')
}B3.E.RNA:EACH Loop Execution
Syntax: RNA:EACH blank+ engine? delim step delim func delim group
Using group to refer to the current element in the loop. if the group is named it, then it.x represents the x attribute of the element.
step- must be-?[0-9]+, the order and step, negative isreverseloopingfunc- engine execution result, must be array/collection, otherwise equal toRNA:PUTgroup- Must bealphanum, groupELSEandDONEin the logic scope, and refer to current element
Depending on the data type, different loop processing is performed, null or empty is skipped and can be executed within ELSE.
- Array - Class.isArray()
Collection<E>- instance of Collection- Other types, do not loop
- Reverse-order loop, not (RandomAccess or ReverseIterator) are converted to toArray
In the loop, the following built-in properties indicate the current status, given the group named it, then
it- The current element reference, should avoid the name conflict to pollute the context- to reference the
xattribute of the current element useit.x it._count- Built-in variable, current loop counter, 1-base, 0 means not loopedit._total- Built-in variable, total number of elements in thegroupit._first- Built-in variable, whether the first elementit._last- Built-in variable, whether the last element- These built-in variables are not removed at the end of the loop and can be used outside the loop.
Since Meepo is a professional non-professional template engine, this for-each is very low-level,
- Limited support for object navigation, separated by
., see themapengine for details. - The element in the loop supports only
Map<String,? >and JavaBean's Getter. - No scope isolation, the
groupname can override the variables within the context.
<!-- RNA:EACH map/2/items/it -->
<!-- RNA:USE /name/it.name/* -->
<li value="code">rem0-name</li>
<!-- RNA:ELSE it -->
<li>no item</li>
<!-- RNA:DONE it -->
<!-- RNA:USE /total/it._total/ -->
<!-- RNA:USE /count/it._count/ -->
<div>result=count/total</div>Equivalent to the for(;;) or for-in loop logic of the following js pseudo-code, depending on the collection type and the step positive or negative.
let step=2 // Loop steps, negative numbers in reverse order, must not 0
let index=0 // Temporary variables in the process
let it = null, count=0, total=items.length; // built-in variable
for(it in items){
if(index++ % step !== 0) continue // Control step
count++
console.log('<li value="code">rem0-'+it.name+'</li>')
}
if(count === 0){
console.log('<li>no item</li>')
}
console.log('<div>result='+count+'/'+total+'</div>')B3.F.RNA:ELSE Else Condition
Syntax: RNA:ELSE blank+ group
group by group and handle else branch on WHEN or EACH with the same group.
- If
WHEN, it means none of theWHENis executed - If
EACH, it means the loop body is never executed (e.g. no elements) EACH-ELSEhas the same semantics aspebble, is different from python'sfor-else
B3.G.RNA:DONE Stop Executing
Syntax: RNA:DONE (blank+ group)+
group by group to stop executing of one or more WHEN and EACH with the same group.
B3.H.Placeholder Template
Simplified template to perform only expression-level replacement or function handling, rather than the full Meepo template syntax. For example, placeholders in configuration files usually require simple replacement or string conversion.
To use it, just customize the boundary around the variable, {{ and }} by default.
Define escaping char can escape the boundary, default is empty, no escaping. The escaping has the following characteristics, using \ for example,
- Available only for
boundary, e.g.\{{and\}}to{{and}} - Escape itself before the boundary, such as
\\{{var}}to\+varvalue - Placeholder, paired from left to right, use the closest pairs, non-matching boundary is treated as plain text.
- Other cases are invalid, such as
\n - Nested Placeholders is not supported
- Variable names cannot contain spaces, or they will be resolved as a function
B3.I.Limitation of Using RNA
Since the template generates a syntax tree during static parsing, the conditionals and loop execution in RNA do not recognize the runtime structure.
The following template intent is, to use the first truthy value between GitHash and ModTime by pattern hash. Intuitively, this format is more consistent with procedure-oriented conditional assignment, but causes a crossover scope error during syntax checking.
// HI-MEEPO
// RNA:WHEN /yes/GitHash/bg
// RNA:USE /hash/GitHash/
// RNA:ELSE bg
// RNA:USE /hash/ModTime/
// RNA:DONE bg
project hashThe crossover error occurs because the pattern hash has are two RNA:USE using it, In static parsing, the runtime value is not available, so it can not determine the correct the syntax tree to belong to, change it as follows
// HI-MEEPO
// RNA:USE /hash/GitHash/
// RNA:USE /time/ModTime/
// RNA:WHEN /yes/GitHash/bg
project hash
// RNA:ELSE bg
project time
// RNA:DONE bgOr Use the fun:see function to help
// HI-MEEPO
// RNA:USE /hash/fun:see GitHash ModTime/
project hash