Я столкнулся с тем же недоумением, когда впервые попробовал это сделать. Это довольно очевидный вопрос / вопрос, который делает его более странным, так как в учебниках его не рассматривают явно и прямо.
Выход из недоумения, которое я обнаружил, - это ключевое слово «возврат»:
token returns [TreeNode value]
: WORD { $value = new TreeNode( "word", $WORD.Text ); }
| INT { $value = new TreeNode( "int", $INT.Text ); }
;
WORD: ('a'..'z'|'A'..'Z')+;
INT : ('0'..'9')+;
TreeNode - это класс, который я создал. Сложно было понять, как сделать это с помощью последовательности, скажем, нескольких токенов. Решение, которое я придумал, было рекурсией:
expr returns [Accumulator value]
: a=token (WS+ b=expr)?
{
if( b != null )
{
$value = new Accumulator( "expr", a.value, b.value );
} else
{
$value = new Accumulator( "expr", a.value );
}
}
;
Я создал класс, в котором есть два разных конструктора. Один конструктор инкапсулирует один токен, а другой - один токен и другой экземпляр Accumulator. Обратите внимание, что само правило определяется рекурсивно и что b.value
является экземпляром Accumulator. Зачем? Поскольку b является expr, а определение expr имеет returns [Accumulator value]
.
Конечным итоговым деревом является отдельный экземпляр Accumulator, который сгруппировал все токены. Чтобы действительно использовать это дерево, нужно выполнить некоторую настройку и затем вызвать метод с тем же именем, что и правило, относительно которого вы анализируете свой контент:
Antlr.Runtime.ANTLRStringStream stringstream = new Antlr.Runtime.ANTLRStringStream( script );
TokenLexer lexer = new TokenLexer( stringstream );
Antlr.Runtime.CommonTokenStream tokenstream = new Antlr.Runtime.CommonTokenStream( lexer );
TokenParser parser = new TokenParser( tokenstream );
Accumulator grandtree = parser.expr().value;
Надеюсь, что это помогает людям, которые сталкиваются с этим недоумением.
Обновление
Существует более простой способ сбора элементов в списки благодаря тому, что система позволяет перемежать код целевого языка в местах, которые кажутся произвольными шаблонами. Идиома:
sequence returns [String k]
: (e=atom { $k = $e.k; })
(e=atom { $k += ", " + $e.k; })*
{ $k = "sequence (" + $k + ")"; } ;
Строка k
инициализируется значением k первого атома, а последующие атомы получают значения от +=
до k
. Фрагмент $e.k
ссылается на правило atom returns [String k]
, определенное в другом месте. Если такого правила нет, вы можете использовать свойство text
(т. Е. $e.text
, которое есть у токенов. Я не уверен, есть ли у свойства без токенов это свойство. Если нет, вы можете просто сделать:
nonToken returns [String whatever] : e=TOKEN { $whatever = $e.text; } ;
Который вы бы затем использовали в более высоких правилах, например,
e=nonToken { System.out.println($e.whatever); }