【问题标题】:ANTLR: call a rule from a different grammarANTLR:从不同的语法中调用规则
【发布时间】:2011-07-11 14:01:52
【问题描述】:

是否可以从不同的语法调用规则?
目的是在同一个文件中有两种语言,第二种语言以 (begin ...) 开头,其中 ... 是第二种语言。该语法应该调用另一个语法来解析第二种语言。

例如:


grammar A;

start_rule
    :    '(' 'begin' B.program ')' //or something like that
    ;


grammar B;

program
    :   something* EOF
    ;

something
    : ...
    ;

【问题讨论】:

    标签: antlr grammar modularity rule


    【解决方案1】:

    您的问题可以(至少)以两种方式解释:

    1. 将大型语法中的规则分离成单独的语法;
    2. 在您的“主要”语言(孤岛语法)中解析一种单独的语言。

    我假设它是第一个,在这种情况下你可以导入语法。

    选项 1 的演示:

    文件:L.g

    lexer grammar L;
    
    Digit
      :  '0'..'9'
      ;
    

    文件:子.g

    parser grammar Sub;
    
    number
      :  Digit+
      ;
    

    文件:Root.g

    grammar Root;
    
    import Sub;
    
    parse
      :  number EOF {System.out.println("Parsed: " + $number.text);}
      ;
    

    文件:Main.java

    import org.antlr.runtime.*;
    
    public class Main {
      public static void main(String[] args) throws Exception {
        L lexer = new L(new ANTLRStringStream("42"));
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        RootParser parser = new RootParser(tokens);
        parser.parse();
      }
    }
    

    运行演示:

    bart@hades:~/Programming/ANTLR/Demos/Composite$ java -cp antlr-3.3.jar org.antlr.Tool L.g
    bart@hades:~/Programming/ANTLR/Demos/Composite$ java -cp antlr-3.3.jar org.antlr.Tool Root.g 
    bart@hades:~/Programming/ANTLR/Demos/Composite$ javac -cp antlr-3.3.jar *.java
    bart@hades:~/Programming/ANTLR/Demos/Composite$ java -cp .:antlr-3.3.jar Main
    

    将打印:

    Parsed: 42
    

    到控制台。

    更多信息,请参阅:http://www.antlr.org/wiki/display/ANTLR3/Composite+Grammars

    选项 2 的演示:

    语言中的语言的一个很好的例子是正则表达式。您拥有带有元字符的“普通”正则表达式语言,但其中还有另一种:描述字符集(或字符类)的语言。

    您可以简单地将字符集视为由@987654332 组成的单个标记,而不是考虑正则表达式语法中字符集的元字符(范围-、否定^ 等) @ 然后在你的正则表达式语法中包含](可能包含\]!)。然后,当您在其中一个解析器规则中偶然发现 CharSet 标记时,您将调用 CharSet 解析器。

    文件:Regex.g

    grammar Regex;
    
    options { 
      output=AST;
    }
    
    tokens {
      REGEX;
      ATOM;
      CHARSET;
      INT;
      GROUP;
      CONTENTS;
    }
    
    @members {
      public static CommonTree ast(String source) throws RecognitionException {
        RegexLexer lexer = new RegexLexer(new ANTLRStringStream(source));
        RegexParser parser = new RegexParser(new CommonTokenStream(lexer));
        return (CommonTree)parser.parse().getTree();
      }
    }
    
    parse
      :  atom+ EOF -> ^(REGEX atom+)
      ;
    
    atom
      :  group quantifier?     -> ^(ATOM group quantifier?)
      |  EscapeSeq quantifier? -> ^(ATOM EscapeSeq quantifier?)
      |  Other quantifier?     -> ^(ATOM Other quantifier?)
      |  CharSet quantifier?   -> ^(CHARSET {CharSetParser.ast($CharSet.text)} quantifier?)
      ;
    
    group
      :  '(' atom+ ')' -> ^(GROUP atom+)
      ;
    
    quantifier
      :  '+'
      |  '*'
      ;
    
    CharSet
      :  '[' (('\\' .) | ~('\\' | ']'))+ ']'
      ;
    
    EscapeSeq
      :  '\\' .
      ;
    
    Other
      :  ~('\\' | '(' | ')' | '[' | ']' | '+' | '*')
      ;
    

    文件:CharSet.g

    grammar CharSet;
    
    options { 
      output=AST;
    }
    
    tokens {
      NORMAL_CHAR_SET;
      NEGATED_CHAR_SET;
      RANGE;
    }
    
    @members {
      public static CommonTree ast(String source) throws RecognitionException {
        CharSetLexer lexer = new CharSetLexer(new ANTLRStringStream(source));
        CharSetParser parser = new CharSetParser(new CommonTokenStream(lexer));
        return (CommonTree)parser.parse().getTree();
      }
    }
    
    parse
      :  OSqBr ( normal  -> ^(NORMAL_CHAR_SET normal)
               | negated -> ^(NEGATED_CHAR_SET negated)
               ) 
         CSqBr
      ;
    
    normal
      :  (EscapeSeq | Hyphen | Other) atom* Hyphen?
      ;
    
    negated
      :  Caret normal -> normal
      ;
    
    atom
      :  EscapeSeq
      |  Caret
      |  Other
      |  range
      ;
    
    range
      :  from=Other Hyphen to=Other -> ^(RANGE $from $to)
      ;
    
    OSqBr
          :  '['
      ;
    
    CSqBr
      :  ']'
      ;
    
    EscapeSeq
      :  '\\' .
      ;
    
    Caret
      :  '^'
      ;
    
    Hyphen
      :  '-'
      ;
    
    Other
      :  ~('-' | '\\' | '[' | ']')
      ;
    

    文件:Main.java

    import org.antlr.runtime.*;
    import org.antlr.runtime.tree.*;
    import org.antlr.stringtemplate.*;
    
    public class Main {
      public static void main(String[] args) throws Exception {
        CommonTree tree = RegexParser.ast("((xyz)*[^\\da-f])foo");
        DOTTreeGenerator gen = new DOTTreeGenerator();
        StringTemplate st = gen.toDOT(tree);
        System.out.println(st);
      }
    }
    

    如果您运行主类,您将看到正则表达式 ((xyz)*[^\\da-f])fooDOT output,它是以下树:

    神奇之处在于atom 规则中的Regex.g 语法,我通过调用CharSetParser 类中的静态ast 方法在重写规则中插入了一个树节点:

    CharSet ... -> ^(... {CharSetParser.ast($CharSet.text)} ...)
    

    请注意,在这样的重写规则中,不能是分号!所以,这是错误的:{CharSetParser.ast($CharSet.text);}

    编辑

    以下是为这两种语法创建树遍历器的方法:

    文件:RegexWalker.g

    tree grammar RegexWalker;
    
    options {
      tokenVocab=Regex;
      ASTLabelType=CommonTree;
    }
    
    walk
      :  ^(REGEX atom+) {System.out.println("REGEX: " + $start.toStringTree());}
      ;
    
    atom
      :  ^(ATOM group quantifier?)
      |  ^(ATOM EscapeSeq quantifier?)
      |  ^(ATOM Other quantifier?)
      |  ^(CHARSET t=. quantifier?) {CharSetWalker.walk($t);}
      ;
    
    group
      :  ^(GROUP atom+)
      ;
    
    quantifier
      :  '+'
      |  '*'
      ;
    

    文件:CharSetWalker.g

    tree grammar CharSetWalker;
    
    options {
      tokenVocab=CharSet;
      ASTLabelType=CommonTree;
    }
    
    @members {
      public static void walk(CommonTree tree) {
        try {
          CommonTreeNodeStream nodes = new CommonTreeNodeStream(tree);
          CharSetWalker walker = new CharSetWalker(nodes);
          walker.walk();
        } catch(Exception e) {
          e.printStackTrace();
        }
      }
    }
    
    walk
      :  ^(NORMAL_CHAR_SET normal)  {System.out.println("NORMAL_CHAR_SET: " + $start.toStringTree());}
      |  ^(NEGATED_CHAR_SET normal) {System.out.println("NEGATED_CHAR_SET: " + $start.toStringTree());}
      ;
    
    normal
      :  (EscapeSeq | Hyphen | Other) atom* Hyphen?
      ;
    
    atom
      :  EscapeSeq
      |  Caret
      |  Other
      |  range
      ;
    
    range
      :  ^(RANGE Other Other)
      ;
    

    Main.java

    import org.antlr.runtime.*;
    import org.antlr.runtime.tree.*;
    import org.antlr.stringtemplate.*;
    
    public class Main {
      public static void main(String[] args) throws Exception {
        CommonTree tree = RegexParser.ast("((xyz)*[^\\da-f])foo");
        CommonTreeNodeStream nodes = new CommonTreeNodeStream(tree);
        RegexWalker walker = new RegexWalker(nodes);
        walker.walk();
      }
    }
    

    要运行演示,请执行以下操作:

    java -cp antlr-3.3.jar org.antlr.Tool CharSet.g 
    java -cp antlr-3.3.jar org.antlr.Tool Regex.g
    java -cp antlr-3.3.jar org.antlr.Tool CharSetWalker.g
    java -cp antlr-3.3.jar org.antlr.Tool RegexWalker.g 
    javac -cp antlr-3.3.jar *.java
    java -cp .:antlr-3.3.jar Main
    

    将打印:

    NEGATED_CHAR_SET: (NEGATED_CHAR_SET \d (RANGE a f))
    REGEX: (REGEX (ATOM (GROUP (ATOM (GROUP (ATOM x) (ATOM y) (ATOM z)) *) (CHARSET (NEGATED_CHAR_SET \d (RANGE a f))))) (ATOM f) (ATOM o) (ATOM o))
    

    【讨论】:

    • 实际上是第二个问题,我将在帖子中对其进行编辑以澄清这一点。但是不能以同样的方式回答第二个问题吗?它看起来就像一个解决方案。
    • @Sebastian,您可能会使用复合语法:这完全取决于您要解析的语言(它们的词法分析器有何不同)。你当然可以试试。如果失败,请使用您尝试解析的真实示例来编辑您的问题。
    • @Sebastian,我添加了一个如何处理这种孤岛语法的示例。
    • 另一个问题:如何在树中引用 ^(ATOM {CharSetParser.ast($CharSet.text)} 量词中的其他语法的(子)树?)语法?
    • 恐怕不可能有两个单独的树行者?还是谢谢
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-06-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-06
    • 2014-08-09
    • 1970-01-01
    相关资源
    最近更新 更多