【问题标题】:antlr3 - Generating a Parse Treeantlr3 - 生成解析树
【发布时间】:2011-05-12 16:24:20
【问题描述】:

我无法弄清楚 antlr3 API,因此我可以在一些 javascript 代码中生成和使用解析树。当我使用 antlrWorks(他们的 IDE)打开语法文件时,解释器能够向我显示解析树,它甚至是正确的。

我在使用 antlr3 运行时跟踪如何在我的代码中获取此解析树的资源时遇到了很多困难。我一直在搞乱运行时和解析器文件中的各种功能,但无济于事:

var input = "(PR=5000)",
cstream = new org.antlr.runtime.ANTLRStringStream(input),
lexer = new TLexer(cstream),
tstream = new org.antlr.runtime.CommonTokenStream(lexer),
parser = new TParser(tstream);

var tree = parser.query().tree;
var nodeStream = new org.antlr.runtime.tree.CommonTreeNodeStream(tree);
nodeStream.setTokenStream(tstream);

parseTree = new org.antlr.runtime.tree.TreeParser(nodeStream);

由于 antlrWorks 可以在没有我自己的任何树语法的情况下显示解析树,并且由于我已经阅读了 antlr 自动从语法文件生成解析树,我假设我可以使用一些运行时函数访问这个基本解析树我可能不知道。我的这个想法对吗?

【问题讨论】:

    标签: antlr antlr3 antlrworks parse-tree


    【解决方案1】:

    HugeAntlrs 写道:

    由于 antlrWorks 可以在没有我自己的任何树语法的情况下显示解析树,并且由于我已经阅读了 antlr 自动从语法文件生成解析树,我假设我可以使用一些运行时函数访问这个基本解析树我可能不知道。我的这个想法对吗?

    不,这是不正确的。 ANTLR 创建一个平面的一维令牌流。

    ANTLRWorks 在解释某些源代码时会即时创建自己的解析树。您无权访问此树(不能使用 Javascript 甚至 Java)。您必须定义您认为应该是(子)树根的标记和/或定义需要从 AST 中删除的标记。查看以下问答,了解如何创建正确的 AST:How to output the AST built using ANTLR?

    编辑

    由于在 SO 上还没有合适的 JavaScript 演示,这里有一个快速演示。

    以下语法使用以下运算符解析布尔表达式:

    • 不是

    not 的优先级最高。

    当然有truefalse,表达式可以用括号分组。

    文件:Exp.g

    grammar Exp;
    
    options {
      output=AST;
      language=JavaScript;
    }
    
    parse
      :  exp EOF -> exp
      ;
    
    exp
      :  orExp
      ;
    
    orExp
      :  andExp (OR^ andExp)*
      ;
    
    andExp
      :  eqExp (AND^ eqExp)*
      ;
    
    eqExp
      :  unaryExp (IS^ unaryExp)*
      ;
    
    unaryExp
      :  NOT atom -> ^(NOT atom)
      |  atom
      ;
    
    atom
      :  TRUE
      |  FALSE
      |  '(' exp ')' -> exp
      ;
    
    OR     : 'or' ;
    AND    : 'and' ;
    IS     : 'is' ;
    NOT    : 'not' ;
    TRUE   : 'true' ;
    FALSE  : 'false' ;
    SPACE  : (' ' | '\t' | '\r' | '\n') {$channel=HIDDEN;} ;
    

    上面的语法产生了一个 AST,可以提供给下面的 tree-walker:

    文件:ExpWalker.g

    tree grammar ExpWalker;
    
    options {
      tokenVocab=Exp;
      ASTLabelType=CommonTree;
      language=JavaScript;
    }
    
    // `walk` returns a string
    walk returns [expr]
      :  exp {expr = ($exp.expr == 1) ? 'True' : 'False';}
      ;
    
    // `exp` returns either 1 (true) or 0 (false)
    exp returns [expr]
      :  ^(OR  a=exp b=exp) {expr = ($a.expr == 1 || $b.expr == 1) ? 1 : 0;}
      |  ^(AND a=exp b=exp) {expr = ($a.expr == 1 && $b.expr == 1) ? 1 : 0;}
      |  ^(IS  a=exp b=exp) {expr = ($a.expr == $b.expr) ? 1 : 0;}
      |  ^(NOT a=exp)       {expr = ($a.expr == 1) ? 0 : 1;}
      |  TRUE               {expr = 1;}
      |  FALSE              {expr = 0;}
      ;
    

    (对于 { ... } 中乱七八糟的 JavaScript 代码表示歉意:我对 JavaScript 的经验很少!)

    现在下载 ANTLR 3.3(不是早期版本!)和 JavaScript 运行时文件:

    antlr-3.3-complete.jar 重命名为antlr-3.3.jar 并解压缩antlr-javascript-runtime-3.1.zip 并将所有文件存储在与Exp.gExpWalker.g 文件相同的文件夹中。

    现在生成词法分析器、解析器和tree-walker:

    java -cp antlr-3.3.jar org.antlr.Tool Exp.g java -cp antlr-3.3.jar org.antlr.Tool ExpWalker.g

    并使用以下 html 文件对其进行测试:

    <html>
      <head>
        <script type="text/javascript" src="antlr3-all-min.js"></script>
        <script type="text/javascript" src="ExpLexer.js"></script>
        <script type="text/javascript" src="ExpParser.js"></script>
        <script type="text/javascript" src="ExpWalker.js"></script>
    
        <script type="text/javascript">
    
        function init() {
          var evalButton = document.getElementById("eval");
          evalButton.onclick = evalExpression;
        }
    
        function evalExpression() {
          document.getElementById("answer").innerHTML = "";
          var expression = document.getElementById("exp").value;
          if(expression) {
            var lexer = new ExpLexer(new org.antlr.runtime.ANTLRStringStream(expression));
            var tokens = new org.antlr.runtime.CommonTokenStream(lexer);
            var parser = new ExpParser(tokens);
            var nodes = new org.antlr.runtime.tree.CommonTreeNodeStream(parser.parse().getTree());
            nodes.setTokenStream(tokens);
            var walker = new ExpWalker(nodes);
            var value = walker.walk();
            document.getElementById("answer").innerHTML = expression + " = " + value;
          }
          else {
            document.getElementById("exp").value = "enter an expression here first"; 
          }
        }
    
        </script>
      </head>
      <body onload="init()">
        <input id="exp" type="text" size="35" />
        <button id="eval">evaluate</button>
        <div id="answer"></div>
      </body>
    </html>
    

    看看结果:

    【讨论】:

    • 非常感谢您的解释。它为我消除了antlr内部的很多误解。最终,我将尝试使用另一个解析器生成器 Jison,因为它是 nodejs 原生的。再次感谢!
    • @HugeAntlrs,Jison 看起来不错,我去看看。谢谢。您可能还想查看PEG.js
    猜你喜欢
    • 1970-01-01
    • 2012-08-03
    • 2012-02-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多