首页 文章

如何在ANTLR中实现将两个节点合二为一的解析器规则?

提问于
浏览
2

The second alternative ((1-9)(0-9)) of the following parser rule results in two nodes in the abstract syntax tree.

oneToHundred
  : ('1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9')
  | ('1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9')('0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9')
  | '100'
  ;

side node: "Lexing"数字到数字标记的数字不受影响) . )

所以对于15我得到两个节点一个和五个而不是十五个但我想把它作为一个节点所代表的一个数字 .

我不能用令牌级别的词法分析器来做这个,因为取决于上下文,例如15可能意味着两个非常不同的东西:"one-symbol and a five-symbol"(绝对应该是两个节点)或"fifteen"和according to this post上下文敏感性应留给解析器 .


(编辑澄清:)

上下文敏感的示例:

输入应该分开/由分号分隔

Input:
11;2102;34%;P11o

this would be split into four parts and 
11 - would not be a number but one '1'-symbol and another '1'-symbol
2102 - would not be a number but: '2'-symbol '1'-symbol '0'-symbol '2'-symbol 
34% - now here 34 would be the number thirtyfour
P11o: 'P'-symbol '1'-symbol '1'-symbol 'o'-symbol

在这四个块中,34%将被解析器规则识别为百分比块,其他块将被识别为符号块 . 所以AST看起来应该是这样的:

SYMBOL
  1
  1
SYMBOL
  2
  1
  0
  2
PERCENT
  34
SYMBOL
  P
  1
  1
  o

目标是C#:

options {
  language=CSharp3;
  output=AST;
}

I'm an Antlr-noob, so is there a good way to merge these two nodes with the parser or am I better of adding an imaginary token and concatenating the two digits "manually" in C# after parsing?

1 回答

  • 1

    你的解析器规则:

    oneToHundred
     : ('1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9')
     | ('1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9')('0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9')
     | '100'
     ;
    

    在幕后隐式创建以下标记:

    D_1_9 : ('1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9');
    D_0_9 : ('0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9');
    D_100 : '100';
    

    (不是那些规则名称,而是它们匹配的内容 are 已创建)

    因此,如果您的词法分析器将获得输入 "11" ,则会创建两个 D_1_9 标记,并且 oneToHundred 规则中的第二个替代项将无法匹配(此替代方案需要两个标记: D_1_9 D_0_9 ) .

    您必须意识到词法分析器独立于解析器运行 . 它没有't matter what type of token the parser 1574550 of the lexer: the lexer has it'自己的规则优先级,导致 '1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9' 永远不会被 D_0_9 规则匹配(因为它出现在 D_1_9 规则之后) .


    编辑

    让我们调用你的输入 11;2102;34%;P11o ,每个 atoms (其中 atom 是字母或数字)的四个单位可能以 '%' 结尾:

    unit
      :  atoms '%'?
      ;
    

    如果它以 '%' 结尾,您只需使用重写规则创建一个以 PERCENT 为根的树,否则只需创建一个以 SYMBOL 为根的树:

    unit
      :  (atoms -> ^(/* SYMBOL */)) ('%' -> ^( /* PERCENT */))?
      ;
    

    一个工作演示:

    grammar T;
    
    options {
      output=AST;
      ASTLabelType=CommonTree;
    }
    
    tokens {
      ROOT;
      SYMBOL;
      PERCENT;
      NUMBER;
    }
    
    parse
      :  unit (';' unit)* EOF -> ^(ROOT unit+)
      ;
    
    unit
      :  (atoms -> ^(SYMBOL atoms)) 
         ('%' -> ^(PERCENT {new CommonTree(new CommonToken(NUMBER, $atoms.text))}))?
      ;
    
    atoms
      :  atom+
      ;
    
    atom
      :  Letter
      |  Digit
      ;
    
    Digit  : '0'..'9';
    Letter : 'a'..'z' | 'A'..'Z';
    

    您可以使用以下类测试解析器:

    import org.antlr.runtime.*;
    import org.antlr.runtime.tree.*;
    import org.antlr.stringtemplate.*;
    
    public class Main {
      public static void main(String[] args) throws Exception {
        TLexer lexer = new TLexer(new ANTLRStringStream("11;2102;34%;P11o"));
        TParser parser = new TParser(new CommonTokenStream(lexer));
        CommonTree tree = (CommonTree)parser.parse().getTree();
        DOTTreeGenerator gen = new DOTTreeGenerator();
        StringTemplate st = gen.toDOT(tree);
        System.out.println(st);
      }
    }
    

    这将产生对应于以下AST的DOT输出:

    enter image description here

    在上图中,所有叶子的类型均为 LetterDigit ,但 "34" 除外,其类型为 NUMBER .

相关问题