代码之家  ›  专栏  ›  技术社区  ›  Richard Walton

产生(可能的)疯狂

  •  1
  • Richard Walton  · 技术社区  · 14 年前

    以下是否可能?我想“反转”给antlr的输入,并使每个令牌成为前一个令牌的子令牌。

    因此,对于输入(假设每个标记由“.”字符分隔):

    Stack.Overflow.Horse
    

    我希望我的语法生成以下AST:

    Horse
      |---Overflow
             |---Stack
    

    到目前为止,我已经成功地反转了节点,但我无法使它们成为彼此的子节点:

    function
     : ID PERIOD function
       -> function ID
     | ID
     ;
    
    ID  : 'a'..'z'*
        ;
    
    1 回复  |  直到 14 年前
        1
  •  3
  •   Bart Kiers    14 年前

    我认为没有一个简单的方法可以做到这一点。你可以这样做:

    函数
    :id周期函数
    ->^(函数ID)
    我的身份证
    ;
    < /代码> 
    
    

    但这只会使最后一个节点成为根节点,并使所有其他节点成为其子节点。例如,以下来源:

    a.b.c.d.e
    < /代码> 
    
    

    将生成以下树:

    e
    //\\
    DC-B-A
    < /代码> 
    
    

    我看不到简单的修复方法,因为当您第一次分析a.b.c.d.e时,,awill b e thei dan db.c.d.ethe recursive call tofunction:。

    a.b.c.d.e
    + + -+
    γ
    |`------>函数
    γ
    `----------->ID
    < /代码> 
    
    

    从而导致b.c.d.ea作为其子级。然后当b变为id时,它也会作为孩子添加到a旁边。在您的情况下,应将a作为儿童删除,然后添加到b的儿童列表中。但是,在Anltr中,这是不可能的(至少在语法中不是很干净)。


    编辑

    好吧,作为一个工作,我有一些优雅的想法,但这并不是我希望的那样。因此,作为一个不那么优雅的解决方案,您可以将重写规则中的lastnode匹配为根:

    函数
    :(id'')*last=id->^($last)
    ;
    < /代码> 
    
    

    然后在alist中收集所有可能的前面节点(children)。

    函数
    :(children+=id'')*last=id->^($last)
    ;
    < /代码> 
    
    

    并在解析器中使用自定义成员方法“inject”这些childreninto the root of your tree(going from right to left in yourlist!)

    函数
    :(children+=id'')*last=id反向($children,(commontree)$last.tree);->^($last)
    ;
    < /代码> 
    
    

    一个小演示:

    grammar reversetree;
    
    选项{
    输出=AST;
    }
    
    令牌{
    根;
    }
    
    @成员{
    私有void reverse(列表节点,commontree根){
    if(nodes==null)返回;
    对于(int i=nodes.size()-1;i>=0;i--){
    commonTree temp=(commonTree)节点。get(i);
    root.addchild(温度);
    根=温度;
    }
    }
    }
    
    解析
    :function+eof->^(根函数+)
    ;
    
    功能
    :(children+=id'')*last=id反向($children,(commontree)$last.tree);->^($last)
    ;
    
    身份证件
    身份证
    ;
    
    身份证件
    :('A'..'Z''A'..'Z')+
    ;
    
    空间
    :''skip();
    ;
    < /代码> 
    
    

    还有一点测试课:

    import org.antlr.runtime.*;
    导入org.antlr.runtime.tree.*;
    导入org.antlr.StringTemplate.*;
    
    公务舱主{
    公共静态void main(string[]args)引发异常{
    antlrstringstream in=新的antlrstringstream(“a.b.c.d.e stack.overflow.horse singlenode”);
    reverseTreeLexer lexer=新的reverseTreeLexer(in);
    commontokenstream tokens=新的commontokenstream(lexer);
    ReversetTreeParser Parser=新的ReversetTreeParser(标记);
    reverseTreeParser.parse_返回返回值=parser.parse();
    commonTree树=(commonTree)返回值。gettree();
    dotTreegenerator gen=new dotTreegenerator();
    stringtemplate st=gen.todot(树);
    系统.out.println(st);
    }
    }
    < /代码> 
    
    

    它将生成一个ast,看起来像:

    对于输入字符串:

    “a.b.c.d.e stack.overflow.horse singlenode”
    < /代码> 
    按如下方式制定规则:

    function
     : ID PERIOD function
       -> ^(function ID)
     | ID
     ;
    

    但这只会使最后一个节点成为根节点,并使所有其他节点成为其子节点。例如,以下来源:

    a.b.c.d.e
    

    将导致以下树:

        e
     / / \ \
    d c   b a
    

    我看不出一个简单的修复方法,自从你第一次分析a.b.c.d.e,a将是IDb.c.d.e对的递归调用function:

    a.b.c.d.e
    | +-----+
    |    |
    |    `-----> function
    |
    `----------> ID
    

    结果是B.C.D.E将有作为它的孩子。那时b成为身份证件,它也作为子级添加到. 在你的情况下,应作为子级删除,然后添加到孩子们。但是,在Anltr中,这是不可能的(至少,在语法中不是以一种干净的方式)。


    编辑

    好吧,作为一个工作,我有一些优雅的想法,但这并不是我希望的那样。因此,作为一个不那么优雅的解决方案,您可以将last节点作为重写规则的根:

    function
      :  (id '.')* last=id -> ^($last)
      ;
    

    然后收集前面所有可能的节点(children在AList使用+=操作员:

    function
      :  (children+=id '.')* last=id -> ^($last)
      ;
    

    并使用解析器中的自定义成员方法“注入”这些儿童在你的树根(从右到左!):

    function
      :  (children+=id '.')* last=id {reverse($children, (CommonTree)$last.tree);} -> ^($last)
      ;
    

    一个小演示:

    grammar ReverseTree;
    
    options {
      output=AST;
    }
    
    tokens {
      ROOT;
    }
    
    @members {
      private void reverse(List nodes, CommonTree root) {
        if(nodes == null) return;
        for(int i = nodes.size()-1; i >= 0; i--) {
          CommonTree temp = (CommonTree)nodes.get(i);
          root.addChild(temp);
          root = temp;
        }
      }
    }
    
    parse
      :  function+ EOF -> ^(ROOT function+)
      ;
    
    function
      :  (children+=id '.')* last=id {reverse($children, (CommonTree)$last.tree);} -> ^($last)
      ;
    
    id 
      :  ID
      ;
    
    ID
      :  ('a'..'z' | 'A'..'Z')+
      ;
    
    Space
      :  ' ' {skip();}
      ;
    

    还有一点测试课:

    import org.antlr.runtime.*;
    import org.antlr.runtime.tree.*;
    import org.antlr.stringtemplate.*;
    
    public class Main {
        public static void main(String[] args) throws Exception {
            ANTLRStringStream in = new ANTLRStringStream("a.b.c.d.e    Stack.Overflow.Horse    singleNode");
            ReverseTreeLexer lexer = new ReverseTreeLexer(in);
            CommonTokenStream tokens = new CommonTokenStream(lexer);
            ReverseTreeParser parser = new ReverseTreeParser(tokens);
            ReverseTreeParser.parse_return returnValue = parser.parse();
            CommonTree tree = (CommonTree)returnValue.getTree();
            DOTTreeGenerator gen = new DOTTreeGenerator();
            StringTemplate st = gen.toDOT(tree);
            System.out.println(st);
        }
    }
    

    它将生成一个AST,如下所示:

    alt text

    对于输入字符串:

    "a.b.c.d.e    Stack.Overflow.Horse    singleNode"