The template engine believes that everyone is often used, but the implementation of the principle estimates that not many people know (if you say is not replace it, then I have no words ...) )。
Let's take a look at what this simpletemplate wants to achieve:
- is a C # side of the template engine
- The normal variables (i, j, index, username) can be placed in the template as a direct variable name.
- A compound variable can be placed in a template (user. FirstName, user. LastName this variable with the object prefix)
The final client code is invoked in the following way:
Static voidMain (string[] args) { stringTemplate =@"Your Name: @{name}your Age: @{age}"; Dictionary<string,Object> CTX =Newdictionary<string,Object>(); ctx["name"] ="McKay"; ctx[" Age"] ="you guessed"; Console.WriteLine (Stparser.generatestringview (template, ctx)); Console.readkey (); }
You see, the focus is on @{xxxx}.
Everyone also do not spray me, said with regular, replace on the fix, look at the back first, carefully sprayed regret.
I choose to use ANTLR to do this template engine, because although now it seems that the template engine is very simple, but does not mean that later do not expand Ah, and later to join the general programming syntax of if/else/for, so in order to extend the use of syntax parser.
Know Yacc/flex can also go to see, but there is no C # plug-in, and ANTLR just have this plug-in, it is used.
Let's start by writing the grammar rules:
Parse:expression*;expression:stringtext| simple_variable| complex_variable;
Parse
The rule represents the start rule, which can be named by itself, as long as it is lowercase.
Content is only one line, expression*, representing 0 or unlimited expression rules
Expression
See, the first one is the colon ":", followed by or number "|", and finally the number ";"
Representing expression can be one of three rules: Stringtext, Simple_variable, complex_variable, representing ordinary string literals, simple variables, compound variable rules
Let's take a look at the definition of a normal text string
Stringtext:placeholderchar (Placeholderchar) *| newlines;placeholderchar:char| ': ' | space| number| Dot| ' \ ' | ' ' | ' < ' | ' > ' | ' _ ' | ' + ' | '-' | ' * ' | '/'; newlines:newline newline*; NEWLINE: ' \ r '? ' \ n '; Number: ' 0 ' ... ' 9 '; CHAR: ' A ' ... ' Z ' | ' A '.. ' Z '; SPACE: ';D ot: '. ';
Stringtext
The rules of the second choice
The first line represents a string of at least one placeholder character (followed by the * number, which means no limit to the number of characters)
Newlines, look at the back of the definition also uses the * number, representing a carriage return, or multiple carriage return rule matching
placeholder, you see the Placeholderchar rule, you know what characters are allowed for the placeholder
Note: Uppercase rules are not rules, but tokens
Let's take a look at the definition of simple variable rules
Simple_variable:v_start simple_variable_inner v_end;simple_variable_inner:identity;identity: (UNDERLINE| CHAR) (underline| char| Number) *; V_start: ' @{'; V_end: '} '; Number: ' 0 ' ... ' 9 '; CHAR: ' A ' ... ' Z ' | ' A '.. ' Z '; Underline: ' _ ';
Simple_variable
Define a V_start token for the beginning, also defined must be v_end as the end, the characters are @{and}, hehe, the middle is the variable name
This variable name is actually the definition of the identity rule, which means that the first character must be preceded by an underscore or an English letter, subsequent characters are optional, and some words must be underlined, English letters, numbers
And look at the rules for compound variables.
Complex_variable:v_start complex_variable_inner v_end;complex_variable_inner:identity DOT identity;identity: ( underline| CHAR) (underline| char| Number) *;D ot: '. ';
Tell me about the Complex_variable_inner rules here.
Because it is to match the obj.property format, so with a dot number dot,obj and the property of the rule match is actually the identity of the rule matching
Let's look at the effect of the above rules, ANTLR parse tree:
It's still more handsome.
The question below is how to use the C # project.
How to apply to C # projects
First, create a new project, then search for "ANTLR" in NuGet, locate ANTLR4, and then install
Then create a new arbitrary file, and then rename it to a G4 file, such as SIMPLETEMPLATE.G4, and then set the next G4 file generation method, such as
Thus, when we build, ANTLR will generate the corresponding C # code according to the rules definition of the G4 file.
And then say how the contents of the G4 file are copied (the original parse tree is visible in eclipse, so the original G4 definition is done there)
First, the top grammar xxxxx; The xxxxx here must be the same as the file name.
Second, the rule that follows the CompileUnit must exist, representing the default rule
Second, if the compile time is always an error (but the eclipse is normal), then you want to modify the next vs Environment of the G4 file encoding, as follows:
You also have to copy the contents of the G4 file in Eclipse into the new G4 file, and don't forget.
Next will enter the C # coding level, hehe, is not a bit impatient, ' (*∩_∩*)
The hook function is just one, very simple, basically a copy:
Public Static classStparser { Public Static stringGeneratestringview (string Template, dictionary<string,Object> variables) {Antlr4.Runtime.AntlrInputStream input=NewAntlr4.Runtime.AntlrInputStream (template); Templatelexer lexer=Newtemplatelexer (input); Antlr4.Runtime.UnbufferedTokenStream Tokens=NewAntlr4.Runtime.UnbufferedTokenStream (lexer); TemplateParser Parser=NewTemplateParser (tokens); varTree =Parser.parse (); Simpletemplatevisitor Visitor=New simpletemplatevisitor(variables); stringresult=Visitor. Visit (tree); returnresult; } }
Template is a text that is passed in
Variables is a collection of variables passed in.
This code is the ANTLR engine automatically generated class, in addition to Simpletemplatevisitor is custom (otherwise I replace the string ah)
Let's take a look at this class, which is a function overload of the visitxxxx rule, and the required custom logic is rewritten inside.
classSimpletemplatevisitor:g4. templatebasevisitor<string> { Privatedictionary<string,Object>CTX; PublicSimpletemplatevisitor (dictionary<string,Object>CTX) { This. CTX =CTX; } Public Override stringVisitparse (G4. Templateparser.parsecontext context) {StringBuilder SB=NewStringBuilder (); foreach(varExpinchcontext.expression ()) sb. Append (Visitexpression (exp)); returnsb. ToString (); } Public Override stringVisitnewlines (G4. Templateparser.newlinescontext context) {returncontext. GetText (); } Public Override stringVisitstringtext (G4. Templateparser.stringtextcontext context) {returncontext. GetText (); } Public Override stringvisitsimple_variable (G4. Templateparser.simple_variablecontext context) {returnVisitsimple_variable_inner (Context.simple_variable_inner ()); } Public Override stringvisitcomplex_variable (G4.TemplateParser.Complex_variableContext context) {returnVisitcomplex_variable_inner (Context.complex_variable_inner ()); } Public Override stringVisitsimple_variable_inner (G4. Templateparser.simple_variable_innercontext context) {stringVar_name =context.identity (). GetText (); if(!CTX. ContainsKey (var_name))Throw NewNullReferenceException (var_name); returnconvert.tostring (Ctx[var_name]); } Public Override stringVisitcomplex_variable_inner (G4.TemplateParser.Complex_variable_innerContext context) {stringVar_name = Context.identity () [0]. GetText (); if(!CTX. ContainsKey (var_name))Throw NewNullReferenceException (var_name); stringPropertyName = Context.identity () [1]. GetText (); Objectobj =Ctx[var_name]; Type T=obj. GetType (); PropertyInfo PropertyInfo=T.getproperty (PropertyName); varValue = Propertyinfo.getvalue (obj,NULL); stringString_value =convert.tostring (value); returnstring_value; } }
The CTX passed in the constructor is the set of variables we want to replace.
Light look at these functions will be dizzy, you have to combine eclipse in the analytic tree hierarchy to see at the same time, to clearly know the upper and lower relations, and then set above this visit class to understand, hehe, slowly toss to see it.
Wait here for a few weeks ...
The above is just a substitution variable, we do a loop, for example:
Your name: @{user.name}your Age: @{user.age}123@{repeat 5}testing@{end repeat}-------------------- -@{repeat Count}testing@{end Repeat}
Look, support the cyclic repeat syntax
Repeat can support fixed numbers, simple variables, or compound variables, and you should be able to draw regular shapes in your mind.
Students who are interested in in-depth can try to implement If/else grammar on their own.
The code has been uploaded to GitHub, url:https://github.com/daibinhua888/simpletemplate/.
Simpletemplate Template engine Development