解析模板语言

时间:2011-09-11 20:34:34

标签: antlr antlr3

我正在尝试解析模板化语言,而且我无法正确解析标签之间可能出现的任意html。到目前为止,我所拥有的是什么建议?有效输入的一个例子是

{foo}{#bar}blah blah blah{zed}{/bar}{>foo2}{#bar2}This Should Be Parsed as a Buffer.{/bar2}

语法是:

grammar g;

options {
  language=Java;
  output=AST;
  ASTLabelType=CommonTree;
}

/* LEXER RULES */
tokens {

}

LD  :    '{';
RD  :    '}';
LOOP    :    '#';  
END_LOOP:   '/';
PARTIAL :   '>';
fragment DIGIT  : '0'..'9';
fragment LETTER : ('a'..'z' | 'A'..'Z');
IDENT : (LETTER | '_') (LETTER | '_' | DIGIT)*;
BUFFER options {greedy=false;} : ~(LD | RD)+ ;

/* PARSER RULES */
start   : body EOF
;

body    : (tag | loop | partial | BUFFER)*
;

tag     : LD! IDENT^ RD!
;

loop    : LD! LOOP^ IDENT RD!
  body
  LD! END_LOOP! IDENT RD!
;

 partial : LD! PARTIAL^ IDENT RD!
;

buffer  : BUFFER 
;

1 个答案:

答案 0 :(得分:3)

您的词法分析器独立于解析器进行标记。如果您的解析器尝试匹配BUFFER令牌,则词法分析器不会考虑此信息。在您的情况下输入如:"blah blah blah",词法分析器会创建3个IDENT令牌,而不是单个BUFFER令牌。

你需要“告诉”你的词法分析器,当你在标签里面时(即你遇到LD标签),应该创建一个IDENT标记,当你是在标记之外(即您遇到RD标记),应创建BUFFER标记而不是IDENT标记。

为了实现这一点,您需要:

  1. 在词法分析器中创建一个boolean标志,用于跟踪您在标签内部或外部的事实。这可以在语法的@lexer::members { ... }部分内完成;
  2. 在词法分析器创建LD - 或RD - 标记后,
  3. 从(1)翻转boolean标记。这可以在词法分析器规则的@after{ ... }部分中完成;
  4. 在词法分析器中创建BUFFER标记之前,检查您是否在标记之外。这可以通过在词法分析器规则的开头使用 semantic predicate 来完成。
  5. 一个简短的演示:

    grammar g;
    
    options { 
      output=AST;
      ASTLabelType=CommonTree; 
    }
    
    @lexer::members {
      private boolean insideTag = false;
    }
    
    start   
      :  body EOF -> body
      ;
    
    body
      :  (tag | loop | partial | BUFFER)*
      ;
    
    tag
      :  LD IDENT RD -> IDENT
      ;
    
    loop    
      :  LD LOOP IDENT RD body LD END_LOOP IDENT RD -> ^(LOOP body IDENT IDENT)
      ;
    
    partial 
      :  LD PARTIAL IDENT RD -> ^(PARTIAL IDENT)
      ;
    
    LD @after{insideTag=true;}  : '{';
    RD @after{insideTag=false;} : '}';
    
    LOOP     : '#';  
    END_LOOP : '/';
    PARTIAL  : '>';
    SPACE    : (' ' | '\t' | '\r' | '\n') {$channel=HIDDEN;};
    IDENT    :  (LETTER | '_') (LETTER | '_' | DIGIT)*;
    BUFFER   : {!insideTag}?=> ~(LD | RD)+;
    
    fragment DIGIT  : '0'..'9';
    fragment LETTER : ('a'..'z' | 'A'..'Z');
    

    (请注意,您可能希望丢弃标记之间的空格,因此我添加了SPACE规则并丢弃了这些空格)

    使用以下类测试它:

    import org.antlr.runtime.*;
    import org.antlr.runtime.tree.*;
    import org.antlr.stringtemplate.*;
    
    public class Main {
      public static void main(String[] args) throws Exception {
        String src = "{foo}{#bar}blah blah blah{zed}{/bar}{>foo2}{#bar2}" + 
                     "This Should Be Parsed as a Buffer.{/bar2}";
        gLexer lexer = new gLexer(new ANTLRStringStream(src));
        gParser parser = new gParser(new CommonTokenStream(lexer));
        CommonTree tree = (CommonTree)parser.start().getTree();
        DOTTreeGenerator gen = new DOTTreeGenerator();
        StringTemplate st = gen.toDOT(tree);
        System.out.println(st);
      }
    }
    

    并且在运行主类之后:

    * nix中/ MacOS的

    java -cp antlr-3.3.jar org.antlr.Tool g.g 
    javac -cp antlr-3.3.jar *.java
    java -cp .:antlr-3.3.jar Main
    

    java -cp antlr-3.3.jar org.antlr.Tool g.g 
    javac -cp antlr-3.3.jar *.java
    java -cp .;antlr-3.3.jar Main
    

    你会看到一些DOT源被打印到控制台,它对应于以下AST:

    enter image description here

    (使用graphviz-dev.appspot.com创建的图片)