ANTLR3 Гетеро узлы не создаются
Я пытаюсь создать гетерогенное дерево на основе примера, представленного здесь: http://www.antlr.org/wiki/display/ANTLR3/Tree+construction
Я создал файл грамматики следующим образом:
grammar T;
options {
language=CSharp3;
ASTLabelType=CommonTree;
output=AST;
TokenLabelType=CommonToken;
k=3;
}
tokens {
ROOT;
UNARY_MIN;
}
@lexer::header
{
using System;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using ANTLRSandbox.Criteria;
}
@parser::header
{
using System;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using ANTLRSandbox.Criteria;
}
@parser::namespace { ANTLRSandbox }
@lexer::namespace { ANTLRSandbox }
public
parse
: exp EOF -> ^(ROOT<RootNode> exp)
;
exp
: addExp
;
addExp
: mulExp (('+'<PlusNode> | '-'<MinusNode>)^ mulExp)*
;
mulExp
: unaryExp (('*' | '/')^ unaryExp)*
;
unaryExp
: '-' atom -> ^(UNARY_MIN atom)
| atom
;
atom
: Number
| '(' exp ')' -> exp
;
Number
: ('0'..'9')+ ('.' ('0'..'9')+)?
;
Space
: (' ' | '\t' | '\r' | '\n'){Skip();}
;
И классы узлов выглядят так:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using Antlr.Runtime;
using Antlr.Runtime.Tree;
namespace ANTLRSandbox.Criteria
{
public class RootNode : CommonTree
{
public RootNode(int ttype) { }
public RootNode(int ttype, IToken t) { }
public RootNode(IToken t) { }
}
}
Классы PlusNode
а также MinusNode
идентичны с RootNode
поэтому я не буду публиковать их здесь.
И вот как я создаю фактическое дерево:
string s = "(12.5 + 56 / -7) * 0.5";
ANTLRStringStream Input = new ANTLRStringStream(s);
TLexer Lexer = new TLexer(Input);
CommonTokenStream Tokens = new CommonTokenStream(Lexer);
TParser Parser = new TParser(Tokens);
TParser.parse_return ParseReturn = Parser.parse();
CommonTree Tree = (CommonTree)ParseReturn.Tree;
Код выполняется без ошибок, но когда я "слежу" за Tree
объект, все его узлы CommonTree
тип и все контрольные точки, которые я поместил в PlusNode
, MinusNode
, RootNode
конструкторы отсутствуют.
Я ознакомился с примером, представленным на вики-странице ANTLR3, и не смог найти ни одного примера в Интернете. Я знаю, что они собираются отказаться от этого подхода в какой-то момент (об этом я узнал в предварительных заметках ANTLR3), но эта реализация мне подходит (мне нужно создавать разные типы объектов на основе грамматического контекста).
Итак... какие-нибудь намеки? Я что-то пропустил? Какой-нибудь параметр / флаг, чтобы поместить его в файл определения грамматики?
Спасибо! D.
2 ответа
Мне никогда не удавалось получить этих операторов < ... >
работать при использовании встроенных операторов дерева (^
для корней и !
за опущенные правила). Все, что я могу порекомендовать, это использовать правила перезаписи (... -> ^(...)
) справа от ваших правил синтаксического анализатора, а затем определить только пользовательский узел, <NodeName>
в правиле перезаписи, а не с обеих сторон (!), как упоминалось в вики: я подозреваю, что информация вики немного устарела. Я знаю, что такие правила выражений гораздо более читабельны с помощью встроенных операторов, чем с правилами перезаписи...
Я не слишком хорошо владею C#, поэтому вот демонстрация Java:
Tg
grammar T;
options {
ASTLabelType=CommonTree;
output=AST;
}
tokens {
ROOT;
UNARY_MIN;
}
@members {
public static class RootNode extends CommonTree {
public RootNode(Token t) { token=t; }
public RootNode(int ttype) { super(new CommonToken(ttype, "ROOT")); }
public RootNode(RootNode node) { super(node); }
public Tree dupNode() { return new RootNode(this); }
public String toString() { return "RootNode=" + token.getText(); }
}
public static class MinusNode extends CommonTree {
public MinusNode(Token t) { token=t; }
public MinusNode(MinusNode node) { super(node); }
public Tree dupNode() { return new MinusNode(this); }
public String toString() { return "MinusNode=" + token.getText(); }
}
public class PlusNode extends CommonTree {
public PlusNode(Token t) { token=t; }
public PlusNode(PlusNode node) { super(node); }
public Tree dupNode() { return new PlusNode(this); }
public String toString() { return "PlusNode=" + token.getText(); }
}
}
parse
: exp EOF -> ^(ROOT<RootNode> exp)
;
exp
: addExp
;
addExp
: (mulExp -> mulExp) ( '+' m=mulExp -> ^('+'<PlusNode> $m $addExp)
| '-' m=mulExp -> ^('-'<MinusNode> $m $addExp)
)*
;
mulExp
: unaryExp (('*' | '/')^ unaryExp)*
;
unaryExp
: '-' atom -> ^(UNARY_MIN atom)
| atom
;
atom
: Number
| '(' exp ')' -> exp
;
Number
: ('0'..'9')+ ('.' ('0'..'9')+)?
;
Space
: (' ' | '\t' | '\r' | '\n') {skip();}
;
Main.java
import org.antlr.runtime.*;
import org.antlr.runtime.tree.*;
import org.antlr.stringtemplate.*;
public class Main {
private static void traverse(CommonTree tree, int indent) {
if(tree == null) return;
for(int i = 0; i < indent; i++) System.out.print(" ");
System.out.println(tree.getClass().getName() + " -> " + tree.getText());
for(int i = 0; i < tree.getChildCount(); i++) {
traverse((CommonTree)tree.getChild(i), indent + 1);
}
}
public static void main(String[] args) throws Exception {
TLexer lexer = new TLexer(new ANTLRStringStream("1 + 2 - 3"));
TParser parser = new TParser(new CommonTokenStream(lexer));
CommonTree tree = (CommonTree)parser.parse().getTree();
traverse(tree, 0);
}
}
Запуск демо:
java -cp antlr-3.3.jar org.antlr.Tool T.g
javac -cp antlr-3.3.jar *.java
java -cp .:antlr-3.3.jar Main
напечатает:
TParser$RootNode -> ROOT
TParser$MinusNode -> -
org.antlr.runtime.tree.CommonTree -> 3
TParser$PlusNode -> +
org.antlr.runtime.tree.CommonTree -> 2
org.antlr.runtime.tree.CommonTree -> 1
Я только что получил ответ, который работает от основного участника цели CSharp3. По сути, при указании типа узла, вы должны явно использовать node=
; Вы не можете полагаться на неявное поведение, как задокументировано. Например, вам нужно изменить это:
parse
: exp EOF -> ^(ROOT<RootNode> exp)
;
...к этому:
parse
: exp EOF -> ^(ROOT<node=RootNode> exp)
;
В моей собственной грамматике, как только я внес это изменение в свои правила перезаписи, синтаксический анализатор, наконец, вывел гетерогенные узлы.