SyntaxTreeBuilder.java revision 56ed4167b942ec265f9cee70ac4d71d10b3835ce
150294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho/* 250294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * Copyright (C) 2010 Google Inc. 350294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * 454dcd9b6a06071f647dac967e9e267abb9410720Craig Cornelius * Licensed under the Apache License, Version 2.0 (the "License"); 550294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * you may not use this file except in compliance with the License. 650294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * You may obtain a copy of the License at 750294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * 850294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * http://www.apache.org/licenses/LICENSE-2.0 950294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * 1050294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * Unless required by applicable law or agreed to in writing, software 1150294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * distributed under the License is distributed on an "AS IS" BASIS, 1250294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1350294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * See the License for the specific language governing permissions and 1450294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * limitations under the License. 1550294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho */ 1650294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho 1750294ead5e5d23f5bbfed76e00e6b510bd41eee1clairehopackage com.google.clearsilver.jsilver.syntax; 1850294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho 1950294ead5e5d23f5bbfed76e00e6b510bd41eee1clairehoimport com.google.clearsilver.jsilver.autoescape.EscapeMode; 2050294ead5e5d23f5bbfed76e00e6b510bd41eee1clairehoimport com.google.clearsilver.jsilver.exceptions.JSilverBadSyntaxException; 2150294ead5e5d23f5bbfed76e00e6b510bd41eee1clairehoimport com.google.clearsilver.jsilver.exceptions.JSilverIOException; 2250294ead5e5d23f5bbfed76e00e6b510bd41eee1clairehoimport com.google.clearsilver.jsilver.syntax.lexer.Lexer; 2350294ead5e5d23f5bbfed76e00e6b510bd41eee1clairehoimport com.google.clearsilver.jsilver.syntax.lexer.LexerException; 2450294ead5e5d23f5bbfed76e00e6b510bd41eee1clairehoimport com.google.clearsilver.jsilver.syntax.node.Start; 2550294ead5e5d23f5bbfed76e00e6b510bd41eee1clairehoimport com.google.clearsilver.jsilver.syntax.node.Switch; 2650294ead5e5d23f5bbfed76e00e6b510bd41eee1clairehoimport com.google.clearsilver.jsilver.syntax.parser.Parser; 2750294ead5e5d23f5bbfed76e00e6b510bd41eee1clairehoimport com.google.clearsilver.jsilver.syntax.parser.ParserException; 28103e9ffba2cba345d0078eb8b8db33249f81840aCraig Cornelius 2950294ead5e5d23f5bbfed76e00e6b510bd41eee1clairehoimport java.io.IOException; 3050294ead5e5d23f5bbfed76e00e6b510bd41eee1clairehoimport java.io.PushbackReader; 3150294ead5e5d23f5bbfed76e00e6b510bd41eee1clairehoimport java.io.Reader; 3250294ead5e5d23f5bbfed76e00e6b510bd41eee1clairehoimport java.util.Arrays; 33103e9ffba2cba345d0078eb8b8db33249f81840aCraig Cornelius 3450294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho/** 3550294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * Parses a JSilver text template into an abstract syntax tree (AST). 3650294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * <p/> 3750294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * Acts as a facade around SableCC generated code. The simplest way to process the resulting tree is 3850294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * to use a visitor by extending 3950294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * {@link com.google.clearsilver.jsilver.syntax.analysis.DepthFirstAdapter} and passing it to 4050294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * {@link Start#apply(com.google.clearsilver.jsilver.syntax.node.Switch)}. 4150294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * <p/> 4250294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * <h3>Example:</h3> 4350294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * 4450294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * <pre> 4550294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * SyntaxTreeBuilder builder = new SyntaxTreeBuilder(); 4650294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * Start tree = builder.parse(myTemplate, "some-template.cs"); 4750294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * // Dump out the tree 4850294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * tree.apply(new SyntaxTreeDumper(System.out)); 4950294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * </pre> 5050294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * 5150294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho */ 5250294ead5e5d23f5bbfed76e00e6b510bd41eee1clairehopublic class SyntaxTreeBuilder { 5350294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho 5450294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho public SyntaxTreeBuilder() {} 5550294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho 5650294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho /** 5750294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * Size of buffer in PushbackReader... needs to be large enough to parse CS opening tag and push 5850294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * back if it is not valid. e.g. "<?csX" : not a tag, so pushback. 5950294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho */ 6050294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho private static final int PUSHBACK_SIZE = "<?cs ".length(); 6150294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho 6250294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho /** 6350294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * Syntax tree optimizers, declared in the order they must be applied: 6450294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * <ol> 6550294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * <li>Type resultion makes the abstract tree concrete and must come first. 6650294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * <li>Sequence optimization simplifies the tree and should come before most other optimizations. 6750294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * <li>Inline rewriting to remove data nodes from 'inline' sections. This should come before any 6850294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * optimization of variables. 6950294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * <li>Var optimization simplifies complex var expressions and must come after both type 7050294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * resolution and sequence optimization. 7150294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * </ol> 7250294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho */ 7350294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho protected final Switch typeResolver = new TypeResolver(); 7450294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho protected final Switch sequenceOptimizer = new SequenceOptimizer(); 7550294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho protected final Switch inlineRewriter = new InlineRewriter(); 7650294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho protected final Switch varOptimizer = new VarOptimizer(Arrays.asList("html", "js", "url")); 7750294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho 7850294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho /** 7950294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * Perform any additional processing on the tree. EscapeMode and templateName are required by 8050294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * AutoEscaper. 8150294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * 8250294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * @param root The AST to post process. 8350294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * @param escapeMode The escaping mode to apply to the given AST. If this is not 8450294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * EscapeMode.ESCAPE_NONE, AutoEscaper will be called on the AST. 8550294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * @param templateName The name of template being processed. Passed to AutoEscaper, which uses it 8650294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * when displaying error messages. 8750294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho */ 8850294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho protected void process(Start root, EscapeMode escapeMode, String templateName) { 8950294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho root.apply(typeResolver); 9050294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho root.apply(sequenceOptimizer); 9150294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho root.apply(inlineRewriter); 9250294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho // Temporarily disabled ('cos it doesn't quite work) 9350294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho // root.apply(varOptimizer); 9450294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho 9550294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho if (!escapeMode.equals(EscapeMode.ESCAPE_NONE)) { 9650294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho // AutoEscaper contains per-AST context like HTML parser object. 9750294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho // Therefore, instantiating a new AutoEscaper each time. 9850294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho root.apply(new AutoEscaper(escapeMode, templateName)); 9950294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho } 10050294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho } 10150294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho 10250294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho /** 10350294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * @param templateName Used for meaningful error messages. 10450294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho * @param escapeMode Run {@link AutoEscaper} on the abstract syntax tree created from template. 10550294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho */ 10650294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho public TemplateSyntaxTree parse(Reader input, String templateName, EscapeMode escapeMode) 10750294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho throws JSilverIOException, JSilverBadSyntaxException { 10850294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho try { 10950294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho PushbackReader pushbackReader = new PushbackReader(input, PUSHBACK_SIZE); 11050294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho Lexer lexer = new Lexer(pushbackReader); 11150294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho Parser parser = new Parser(lexer); 11250294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho Start root = parser.parse(); 11350294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho process(root, escapeMode, templateName); 11450294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho return new TemplateSyntaxTree(root); 11550294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho } catch (IOException exception) { 11650294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho throw new JSilverIOException(exception); 11750294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho } catch (ParserException exception) { 11850294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho throw new JSilverBadSyntaxException(exception.getMessage(), exception.getToken().getText(), 11950294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho templateName, exception.getToken().getLine(), exception.getToken().getPos(), exception); 12050294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho } catch (LexerException exception) { 12150294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho throw new JSilverBadSyntaxException(exception.getMessage(), null, templateName, 12250294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho JSilverBadSyntaxException.UNKNOWN_POSITION, JSilverBadSyntaxException.UNKNOWN_POSITION, 12350294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho exception); 12450294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho } 12550294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho } 12650294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho} 12750294ead5e5d23f5bbfed76e00e6b510bd41eee1claireho