Anssssss писал:
Как только я пытаюсь изменить способ создания дерева, используя символ ^, я получаю исключение из формы:
При попытке сделать правило синтаксического анализатора a
корнем дерева внутри p
, например:
p : a^ b;
a : A A;
b : B B;
ANTLR не знает, какой из A
является корнем правила a
. И, конечно, не может быть двух корней.
Операторы встроенного дерева удобны в некоторых случаях, но в этом случае они не соответствуют задаче. Вы не можете назначить корень внутри производственного правила, которое может не иметь содержимого, например, ваше правило remaining_data
. В этом случае вам нужно создать «воображаемые жетоны» в разделе tokens { ... }
вашей грамматики и использовать правило перезаписи (-> ^( ... )
) для создания своего AST.
Демо
Следующая грамматика:
grammar FlexByteArray_HexGrammar;
options {
output=AST;
}
tokens {
ROOT;
ARRAY;
LENGTH;
DATA;
}
expr
: array* EOF -> ^(ROOT array*)
;
array
@init { int index = 0; }
: array_length array_data[$array_length.value] -> ^(ARRAY array_length array_data)
;
array_length returns [int value]
: a=hex_byte b=hex_byte {$value = $a.value*16*16 + $b.value;} -> ^(LENGTH hex_byte hex_byte)
;
array_data [int length]
: ({length > 0}?=> hex_byte {length--;})* {length == 0}? -> ^(DATA hex_byte*)
;
hex_byte returns [int value]
: a=HEX_DIGIT b=HEX_DIGIT {$value = Integer.parseInt($a.text+$b.text, 16);}
;
HEX_DIGIT
: '0'..'9' | 'a'..'f' | 'A'..'F'
;
проанализирует следующий вход:
0001ff0002abcd
в следующие AST:
Как вы можете видеть, используя следующий основной класс:
import org.antlr.runtime.*;
import org.antlr.runtime.tree.*;
import org.antlr.stringtemplate.*;
public class Main {
public static void main(String[] args) throws Exception {
FlexByteArray_HexGrammarLexer lexer = new FlexByteArray_HexGrammarLexer(new ANTLRStringStream("0001ff0002abcd"));
FlexByteArray_HexGrammarParser parser = new FlexByteArray_HexGrammarParser(new CommonTokenStream(lexer));
CommonTree tree = (CommonTree)parser.expr().getTree();
DOTTreeGenerator gen = new DOTTreeGenerator();
StringTemplate st = gen.toDOT(tree);
System.out.println(st);
}
}
Подробнее
EDIT
Чтобы дать краткое объяснение правила array_data
:
array_data [int length]
: ({length > 0}?=> hex_byte {length--;})* {length == 0}? -> ^(DATA hex_byte*)
;
Как вы уже упоминали в комментариях, вы можете передать один или несколько параметров правилам, добавив [TYPE IDENTIFIER]
после правила.
Первый (стробированный) семантический предикат, {length > 0}?=>
, проверяет, является ли length
больше нуля. Если это так, то парсер пытается сопоставить hex_byte
, после чего переменная length
уменьшается на единицу. Все это останавливается, когда length
равно нулю или когда анализатор не имеет больше hex_byte
для анализа, что происходит, когда EOF следующий в строке. Поскольку может анализировать меньше обязательного числа hex_byte
с, в самом конце правила есть (проверяющий) семантический предикат {length == 0}?
, который гарантирует, что правильное количество hex_byte
s были проанализированы (не больше и не меньше!).
Надеюсь, это прояснит это немного подробнее.