250 likes | 369 Views
StringTemplate Overview. Terence Parr. Topics. Motivation and concepts Feature summary Model-view data transfer Canonical operations StringTemplate groups and inheritance Templates and attribute expressions Scoping Experience. Why use StringTemplate for generated structured text?.
E N D
StringTemplate Overview Terence Parr
Topics • Motivation and concepts • Feature summary • Model-view data transfer • Canonical operations • StringTemplate groups and inheritance • Templates and attribute expressions • Scoping • Experience
Why use StringTemplate for generated structured text? • Most text generators are unstructured blobs of generation logic and print statements (this includes tree walking code emitters) • Template engines arose to encourage separation of logic/display (model-view separation) • Fearing weakness, engines support entanglement • Nature of problem dictates solution • Output is not random; it’s a language (format) • Separation goal requires restricted template expressions • Proper formalism is output grammar • Grammars are proper means of describing languages • Restricted templates are equivalent to CF grammars • StringTemplate embodies this philosophy
Motivation for separation • Encapsulation; view and model isolated, unentangled • Clarity; Java and output not intertwined • Division of labor; templates, model developed in parallel • Component reuse; output factored into reusable templates • Single point-of-change; avoid errors from having multiple things to find, change • Maintenance; changing template is easier, safer than changing code • Interchangeable views; retarget code generators
Rules of Separation • the view cannot modify the model (technically toString() method could erase hard drive) • cannot perform computations upon dependent data values • cannot compare dependent data values (except boolean) • cannot make type assumptions (to be practical, can assume objects have properties) • data from model cannot contain display, layout information (unenforceable)
Equivalence to CFGs • Attributes = terminals, templates = rules • Can show grammar’s derivation tree for any sentence maps to a nested template tree structure Grammar Template prog : decl func ; decl : type ID ; func : type ID “()” “{“ body “}” … prog ::= “<decl()> <func()>” decl ::= “<type> <ID> ;” func : << <type> <ID>() { <body()> } >> …
What’s a template? • A sentence or fragment conforming to an output language with embedded expressions evaluating to text at rendering-time • Differs from code that generates text; template is an exemplar not computation • Overall output: nested tree of templates • Expressions are functions of incoming attributes: name/value pairs • All attributes are available prior to rendering
Getting started • Add stringtemplate-2.2.jar and antlr-2.7.5 to your path then compile and run. Output is QUERY: SELECT name FROM User; import org.antlr.stringtemplate.*; class Simple { public static void main(String[] args) { StringTemplate query = new StringTemplate("SELECT $column$ FROM $table$;"); query.setAttribute("column", "name"); query.setAttribute("table", "User"); System.out.println("QUERY: "+query.toString()); } }
Model-view data transfer • Each template has an attribute table mapping attribute name to value • Code pushes attributes into template; template does not pull from model • Attribute value can be anything including another template or list of values • Properties of attribute are getters or fields<a.foo> tries a.getFoo() then tries a.foo • Special case: if a is of type Map<a.foo> invokes a.get(“foo”)
Feature summary • Strictly enforces separation of model & view • Side-effect free expressions; no order of evaluation • Recursion (recall output structures are nested) • Dynamic attribute scoping • “Lazy-evaluation”; attributes resolve at toString time • Template inheritance/polymorphism • Output filters; e.g., auto-indent (the default) • Attribute renderer objects;e.g., how to render Date objects • Simple: No assignments, loops, arbitrary code, … • Small: 12,000 lines including generated parsers
Canonical Operations • Attribute reference:<type> • Template references (possibly recursive):<statementList()> • Apply template to multi-valued attribute:<decls:decl()>or with closure-like blocks<decls:{d | <d.type> <d.name>;}> • Conditional include:<if(superClass)>extends <superClass><endif>
StringTemplate groups • Set of mutually-referential templates • Either directory of template files or group file format with formal arguments: /** ANTLR Error Messages -- US english */ group en_US; DIR_NOT_FOUND(arg) ::= "directory not found: <arg>" OUTPUT_DIR_IS_FILE(arg) ::= "output directory is a file: <arg>” MISSING_RULE_ARGS(file,line,col,arg) ::= "<loc(…)>missing parameter(s) on rule reference: <arg>” loc(file,line,col) ::= "<file>:<line>:<col>: "
Code generation group example group javaTemplates; method(type,name,args,body) ::= << public <type> <name>( <args:arg(); separator=“,”> ) { <statements> } >> assign(lhs,expr) ::= “<lhs> = <expr>;” if(expr,stat) ::= “if (<expr>) <stat>” call(name,args) ::= << <name>( <args; separator=“,”> ); >> Note: controller creates assign, if, call templates and sets The statements attribute of method etc…
Template Polymorphism • Output: “y=1;” not “x=1;” because template instance’s group is subGroup group sup; slist() ::= “<assign()>” assign() ::= “x=1;” group sub; assign() ::= “y=1;” Late bind sub.setSuperGroup(sup); StringTemplate st = sub.getInstanceOf(”slist"); System.out.println(st.toString()); Group determines symbol resolution
Attribute Expressions • <attribute>, <a.property>, <a.(expr)> • <multi-valued-attribute; separator=“…”> • empty attributes yield no output UNREACHABLE_ALTS(file,line,alts) ::= << <file>:<line>:unreachable: <alts; separator=","> >>
Template inclusion • <template(args)>, <(expr)(args)> • Argument lists are parameter=expr pairs method(type,name,slist) ::= << public <type> <name>() { <debug(loc={enter method <name>})> <slist> }>> catch(exceptionName,slist) ::= << catch (<exceptionName>) { <debug(loc={catch <exceptionName>})> <slist> }>> debug(loc) ::= <<System.out.println(“at location <loc>”);>>
Applying template to attribute • A crucial distinguishing feature! • Obviates the need for loops • <attributeExpr:templateExpr> • <names:bold()>, <names:bold(item=it)> • <names:(templateName)()> • <names:italics():bold()> • <vars:{v | <v.type> <v.id>;<\n>}> • <varTypes,varIDs:{t,id | <t> <id>;<\n>}>
Translation maps • Want to translate string from language x to y • Must keep literals out of code so can’t have hashtable in code with mapping • ST 2.2 feature: <typeInitMap.int> is “0” also <typeInitMap.(typeName)> typeInitMap ::= [ "int":"0", "float":"0.0", "boolean":"false", default:"null" // anything else ]
Lists • Combines attributes into list:<[userDefined,implicitlyDefined]:decl()><[boys,girls,other]:{Attendee: <it>.<\n>}> • Operators: first, rest, last • ops on empty attributes yield an empty value • rest(a) is empty and tail(a) == first(a) if a is single valued <first(numbers):{ n | int sum = <n>;}> <rest(numbers):{ n | sum += <n>;}>
Recursively walking a list • Using rest and tail recursion, print out each element of x recurse(x) ::= “<x><rest(x):recurse()>”
Attribute Scoping • Dynamically not lexically scoped • Template can see all attributes from enclosing templates unless a formal argument hides an enclosing value • Look up a in template t: • Look in t's attribute table • Look in t's arguments • Look recursively up t's enclosing template instance chain • Look recursively up t's group / supergroup chain for a map
Best practice • Design templates top-down • Construct bottom-up • model = input token streamcontroller = parserview = template engine + template files • Controller extracts data from model, gives to view • Controller maps input constructs to output constructs such as assignment to assignment • Abstract concepts represented by one or more rules in the parser grammar and one or more templates in the template file. • View says what abstract concept looks like in target
Experience with ANTLR v3 • Tree walker (controller) collects data from AST (model), pushes data into templates (view) • Lazy evaluation decouples order of computation from order of output (this is huge) • Enforced separation guarantees easy retargeting, no code duplication, … • no code in template • no output strings in code generator • Previous code generator hopelessly entangled • Group file format (output grammar) is great! “Executable document”
Simplified ANTLR v3 Template parser(name, tokens, rules, DFAs) ::= << class <name> extends Parser { <tokens:{t | public static final int <t.name>=<t.type>;}> public <name>(TokenStream input) { super(input); } <rules; separator="\n"> <DFAs:{dfa | protected DFA<dfa.decisionNum> dfa<dfa.decisionNum> = new DFA<dfa.decisionNum>(); }> <DFAs> } >>