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. "&lt;?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