Currently in the traditional software development domain DSL has been more common, especially Martin Fowler's outstanding contributions. But in the front-end area is still less involved, and if the use of DSL in front-end development can also effectively reduce the number of code, improve readability . A common scenario is the building of a front-end template, which is essentially a micro-language, so you can start with a DSL perspective and use tools to quickly build a template engine for a particular front-end framework. This article will take Kissy Xtemplate as an example of how to build a front-end DSL.
Note:
This article continues to update the address:
Xtemplate at GitHub.
Xtemplate at docs.kissyui.com.
DSL is also a beginner, please errata.
First NPM installs Kissy
npm install -g kissy
Xtemplate Sample Code
this is kissy xtemplate: {{date}}
{{#if n > n*2}}
{{{no escape}}}
{{each array}}
index: {{xindex}}
count: {{xcount}}
value: {{value}}
{{set t = value*2}}
subValue:
{{#with this.subValue}}
{{subSubValue + ../t}}
{{/with}}
{{/each}}
{{else}}
{{#custom_block param}}
{{custom_tpl param2}}
{{/custom_block}}
{{/if}}
Template morphology/syntax
This step is primarily to prepare for the next step in building a custom language syntax tree, which is done in the direction of using tools to automatically generate a parser (parser), and you can skip this step if you intend to write a parser (in fact you can skip this article).
Because this article pays attention to the front-end technology, so the lexical and the grammar all uses the JSON format to describe, the lexical directly uses the regular expression, the grammar uses the distortion BNF form, for example Xtemplate's lexical grammar file
The tool uses the Kissy developed LALR Parser Builder Kison.
Lexical concerns how to parse out the most basic unit of code (keywords, strings, numbers ...) from the input code. ), such as Xtemplate's partial lexical
{
state: "t",
regexp: /^{{/,
token: "OPEN"
},
{
state: "t",
regexp: /^}}/,
token: "CLOSE"
},
{
state: "t",
regexp: /^<=/,
token: "LE"
},
{
state: "t",
regexp: /^\+/,
token: "PLUS"
},
{
state: "t",
regexp: /^[a-zA-Z0-9_$-]+/,
token: "ID"
},
Where state represents a single status, the lexical parsing process is also a state machine transformation state process.
Grammar parsing concerns and recognizes an effective program structure from the lexical unit, that is, a syntax-resolution tree, such as a partial syntax description of xtemplate:
{
symbol: "Expression",
rhs: ["ConditionalOrExpression"]
},
{
symbol: "ConditionalOrExpression",
rhs: ["ConditionalAndExpression"]
},
{
symbol: "program",
rhs: ["statements", "inverse", "statements"]
},
{
symbol: "statement",
rhs: ["openBlock", "program", "closeBlock"]
}
Which corresponds to the BNF form: symbol:: = RHS
Building Templates Abstract Syntax tree
The syntax lexical only describes how to recognize the template language. While the process of constructing the syntax tree needs to be built by the caller in the process of syntactic recognition, Kison supports the addition of action functions in each grammar rule item, and selectively constructs the special-shaped abstract syntax tree by means of the tool in the process of language recognition (traversal parsing tree).
For example, the Xtemplate tree node build process:
{
symbol: "program",
rhs: ["statements", "inverse", "statements"],
action: function () {
return new this.yy.ProgramNode(this.lexer.lineNumber, this.$1, this.$3);
}
},
{
symbol: "PrimaryExpression",
rhs: ["path"]
},
{
symbol: "RelationalExpression",
rhs: ["RelationalExpression", "LE", "AdditiveExpression"],
action: function () {
return new this.yy.RelationalExpression(this.$1, "<=", this.$3);
}
}
The most basic expression (primaryexpression) can be directly the value of the variable lexical unit, while the complex comparison expression and the entire program are built from the bottom up subtree.
Last use Kissy-kison command
kissy-kison -g parser.kison -m xtemplate/parser
You can generate a template parsing function module, roughly:
KISSY.add("xtemplate/parser", function(){
function parse(code){
// ...
}
return parse;
});
Template compilation
The final step is the template compilation process, the template code compiled into JavaScript code, fill in the data after the execution of the real rendering of HTML.
Call Parse
After you have parsed the function in the previous step, call the
parse(tempalteCode)
To get an abstract syntax tree, such as a piece of code for Xtemplate:
{{#each data}}
{{#if n === ../n2 * 5}}
{{n + 10.1}}
{{/if}}
{{/each}}
Corresponding abstract syntax tree:
Translation Code
You can then use the visitor mode to write the logic of the generated code to the visitor object, traversing the AST to convert the corresponding subtree or node into JavaScript code.
This step can continue gracefully using code templates to replace the code template's data with the corresponding JavaScript unit of the template. But in order not to torture the brain, finally relax, you can directly use the original code splicing:
visitor.tplNode=function(node){
if(node.escapeHTML){
codes.push("if("+node.id+" in data) { ret.push(KISSY.escapeHTML(data."+node.js+");) }"+
" else { KISSY.warn("not found")!; }");
}else{
}
};
But it was a torture.
Offline compilation
Most DSLs are recommended for conversion to the target language before use, and the client can compile online when the end-user is not too concerned with performance.
Xtemplate through the kissy-xtemplate command support to compile the template code offline as a template function module, so that the client can directly require the module, eliminating the client-side compilation process, while developing directly face HTML similar template code, Eliminates the tedious character of string embedding templates.
such as t-tpl.html
{{ offline }} compile
Run
kissy-xtemplate -t t-tpl.html -m tests/t -w
Can get T.js
KISSY.add("tests/t",function(){
function render(data){
}
return render;
});
One drawback of off-line compilation is that the compiled code is certainly much larger than the native template, which is also a reflection of the DSL-saving code, readable features (the code must be unreadable).
Next
There are two major problems:
Large Size
Compressed before 130k, but after gzip+compress due to the generation of more repetitive code, to 10k, but still need to optimize the build code: Reduce the template parser code. You can also optimize the size of the template into the final code, which is useful for offline compilation.
xtemplate module needs splitting
When you choose to compile offline, actually Xtemplate's compiled code can be split into two modules: Xtemplate/runtime and Xtemplate/compiler without downloading. This allows the functional infrastructure to load the template directly with Xtemplate/runtime when you choose to compile offline.
Xtemplate Document
Api
Demo
Tutorial
Recommended Books
Thanks to these authors, without these books, this task cannot be accomplished
Compilers:principles,techniques and Tools
DSL in Action
Language implementation Patterns:create Your OWN domain-specific and general programming Languages
Thanks
In the development process, refer to the following tools:
Velocity
Closure templates
Bison
Jison
Handlebar
Mustache