antlr3 - Создание дерева синтаксического анализа

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


person HugeAntlrs    schedule 12.05.2011    source источник


Ответы (1)


HugeAntlrs написал:

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

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

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

РЕДАКТИРОВАТЬ

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

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

  • or
  • и
  • is
  • нет

где 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>

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

введите описание изображения здесь

person Bart Kiers    schedule 12.05.2011
comment
Большое спасибо за объяснение. Это помогло мне прояснить множество недоразумений внутри antlr. В конце концов, я собираюсь попробовать поиграть с другим генератором парсеров, Jison, потому что он встроен в nodejs. Еще раз спасибо! - person HugeAntlrs; 13.05.2011
comment
@HugeAntlrs, Джисон неплохо выглядит, я посмотрю. Спасибо. Вы также можете попробовать PEG.js. - person Bart Kiers; 14.05.2011