antlr3 - Генерация дерева разбора - PullRequest
3 голосов
/ 12 мая 2011

У меня проблемы с определением API antlr3, чтобы я мог сгенерировать и использовать дерево разбора в некотором коде JavaScript.Когда я открываю файл грамматики с помощью antlrWorks (их IDE), интерпретатор может показать мне дерево разбора, и это даже правильно.

У меня много трудностей с поиском ресурсов, как получитьэто дерево разбора в моем коде с использованием среды выполнения antlr3.Я возился с различными функциями в файлах времени выполнения и Parser, но безрезультатно:

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 автоматически генерирует дерево разбора из файла грамматики, я предполагаю, что могу получить доступ к этому базовому дереву разбора с помощью некоторых функций времени выполнения, о которых я, вероятно, не знаю.Я прав в этом мышлении?

1 Ответ

7 голосов
/ 12 мая 2011

HugeAntlrs писал:

Поскольку antlrWorks может отображать дерево разбора без какой-либо грамматики дерева от себя, и поскольку я прочитал, что antlr автоматически генерирует дерево разбора из файла грамматики, я предполагаю, что я могу получить доступ к этому основному дереву разбора с помощью некоторых функций времени выполнения что я, вероятно, не знаю. Я прав в этом мышлении?

Нет, это неправильно. ANTLR создает плоский одномерный поток токенов.

ANTLRWorks создает собственное дерево синтаксического анализа на лету при интерпретации некоторого источника. У вас нет доступа к этому дереву (не с Javascript или даже с Java). Вам нужно будет определить токены, которые, по вашему мнению, должны быть корнями ваших (под) деревьев и / или определить токены, которые необходимо удалить из вашего AST. Ознакомьтесь со следующими вопросами и ответами, в которых объясняется, как создать правильный AST: Как вывести AST, построенный с использованием ANTLR?

EDIT

Поскольку на SO еще нет надлежащей демонстрации JavaScript, вот короткая демонстрация.

Следующая грамматика анализирует логическое выражение со следующими операторами:

  • или
  • и
  • есть
  • не

, где not имеет наивысший приоритет.

Конечно, есть true и false, и выражения можно сгруппировать, используя круглые скобки.

файл: 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, который можно подавать на дерево-ходок ниже:

файл: 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.g и ExpWalker.g.

Теперь сгенерируйте лексер, парсер и обходчик деревьев:

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>

И вот результат:

enter image description here

...