#parsing #antlr #antlr4
#синтаксический анализ #antlr #antlr4
Вопрос:
Я новичок в Antlr, и недавно я столкнулся с одной проблемой, связанной с сопоставлением скобок. Каждый узел в дереве синтаксического анализа имеет вид (Node1,W1,W2,Node2)
, где Node1
и Node2
— два узла, а W1
и W2
— два веса между ними. Учитывая входной файл as (1,C,10,2).((2,P,2,3).(3,S,3,2))*.(2,T,2,4)
, дерево синтаксического анализа выглядит неправильно, где оператор не является родительским для этих узлов, а круглые скобки не совпадают.
Файл синтаксического анализа, который я написал, выглядит следующим образом:
grammar Semi;
prog
: expr
;
expr
: expr '*'
| expr ('.'|' ') expr
| tuple
| '(' expr ')'
;
tuple
: LP NODE W1 W2 NODE RP
;
LP : '(' ;
RP : ')' ;
W1 : [PCST0];
W2 : [0-9] ;
NODE: [0-9] ;
WS : [ trn] -> skip ; // toss out whitespace
COMMA: ',' -> skip;
Похоже expr| '(' expr ')'
, что это работает некорректно. Итак, что я должен сделать, чтобы этот анализатор определял, принадлежат ли круглые скобки узлу или нет?
Обновить:
В команде две ошибки:
line 1:1 no viable alternative at input '(1'
line 1:13 no viable alternative at input '(2'
Похоже, что лексер не обнаружил кортежи, но почему это так?
Комментарии:
1. Я подозреваю, что ваша проблема не имеет ничего общего с
'(' expr ')'
производством, а с вашими лексическими правилами. Если вы посмотрите на токены, сгенерированные для вашего ввода, вы, вероятно, обнаружите, что они не соответствуют вашим ожиданиям.2. @sepp2k да, я только что понял это, но я не знаю, как это исправить .. Я также обнаружил ошибку «нет жизнеспособной альтернативы при вводе ‘(1’ » в команде
3. Прежде всего, у вас не должно быть нескольких правил лексера, которые соответствуют одному и тому же шаблону. Это просто приводит к тому, что один из них мертв. Тогда другая проблема заключается в том, что один
0
сам по себе создастW1
токен, но, по-видимому, вы также хотите разрешить 0s как W2 или узлы. Самым простым решением для этого, вероятно, является наличие специального правила лексера только для'0'
, которое можно использовать вездеW1
,W2
где может использоваться узел или.4. @sepp2k омг большое вам спасибо! Я просто пытался использовать только один лексер для чисел, и теперь он работает отлично. У меня просто есть еще один вопрос относительно вашего ответа, если я использую запятую в качестве разделителей, можно ли избежать этой проблемы с одним 0? Похоже, это так в дереве синтаксического анализа вывода, но я не уверен.
5. Проблема с 0 заключается в том, что входные данные, такие как
(0, 0, 0, 0)
, которые должны быть действительными в соответствии с моим пониманием вашего формата, будут обозначаться какLP W1 W1 W1 W1 RP
, что не соответствует вашемуtuple
правилу. Теперь вы можете изменить кортеж, чтобы разрешитьW1
везде, но тогда(P, P, P, P)
это также будет разрешено, чего, я полагаю, вы не хотите. Поэтому я предлагаю изменить его так, чтобы он обозначался как .0 12 P
ZERO NUMBER W1_LETTER
Тогда вы могли бы определитьw2
иnode
(как правила синтаксического анализа, а не правила лексера) какZERO | NUMBER
и вы могли бы определитьw1
какZERO | W1_LETTER
.
Ответ №1:
Ваши правила W2 и NODE одинаковы, поэтому узлы, которые вы намереваетесь сделать узлом, соответствуют W2.
опция grun with -tokens: (обратите внимание, нет токенов узла)
[@0,0:0='(',<'('>,1:0]
[@1,1:1='1',<W2>,1:1]
[@2,3:3='C',<W1>,1:3]
[@3,5:6='10',<W2>,1:5]
[@4,8:8='2',<W2>,1:8]
[@5,9:9=')',<')'>,1:9]
[@6,10:10='.',<'.'>,1:10]
[@7,11:11='(',<'('>,1:11]
[@8,12:12='(',<'('>,1:12]
[@9,13:13='2',<W2>,1:13]
[@10,15:15='P',<W1>,1:15]
[@11,17:17='2',<W2>,1:17]
[@12,19:19='3',<W2>,1:19]
[@13,20:20=')',<')'>,1:20]
[@14,21:21='.',<'.'>,1:21]
[@15,22:22='(',<'('>,1:22]
[@16,23:23='3',<W2>,1:23]
[@17,25:25='S',<W1>,1:25]
[@18,27:27='3',<W2>,1:27]
[@19,29:29='2',<W2>,1:29]
[@20,30:30=')',<')'>,1:30]
[@21,31:31=')',<')'>,1:31]
[@22,32:32='*',<'*'>,1:32]
[@23,33:33='.',<'.'>,1:33]
[@24,34:34='(',<'('>,1:34]
[@25,35:35='2',<W2>,1:35]
[@26,37:37='T',<W1>,1:37]
[@27,39:39='2',<W2>,1:39]
[@28,41:41='4',<W2>,1:41]
[@29,42:42=')',<')'>,1:42]
[@30,43:42='<EOF>',<EOF>,1:43]
Если я заменю узлы в вашем правиле синтаксического анализа на W2s (извините, я понятия не имею, что это должно представлять), я получаю:
Похоже, что ваше заблуждение заключается в том, что синтаксический анализ рекурсивного спуска начинается с правила синтаксического анализатора и, когда он сталкивается с правилом лексера, пытается его сопоставить.
ANTLR работает не так. С ANTLR ваш ввод сначала проходит через лексер (он же Tokenizer) для создания потока токенов. Этот шаг абсолютно ничего не знает о ваших правилах синтаксического анализа. (Вот почему его так часто полезно использовать grun
для сброса потока токенов, это дает вам представление о том, на что действуют ваши правила синтаксического анализа (и вы можете видеть в вашем примере, что нет токенов УЗЛА, потому что все они соответствуют W2).
Кроме того, предложение… Может показаться, что запятые являются неотъемлемой частью правильного ввода (если (1C102).((2P23).(3S32))*.(2T24)
только они не считаются допустимым вводом. Исходя из этого предположения, я удалил -> skip
и добавил их в ваше правило синтаксического анализа (вот почему вы видите их в дереве синтаксического анализа). Результирующая грамматика, которую я использовал, была:
grammar Semi;
prog: expr ;
expr: expr '*' | expr ('.' | ' ') expr | tuple | LP expr RP;
tuple: LP W2 COMMA W1 COMMA W2 COMMA W2 RP;
LP: '(';
RP: ')';
W1: [PCST0];
W2: [0-9] ;
NODE: [0-9] ;
WS: [ trn] -> skip; // toss out whitespace
COMMA: ',';
Чтобы проявить немного больше свободы в вашей грамматике, я бы предположил, что ваши правила лексера должны быть ориентированы на необработанный тип. И что вы можете использовать метки, чтобы сделать различные элементы или ваш кортеж более доступными в вашем коде. Вот пример:
grammar Semi;
prog: expr ;
expr: expr '*' | expr ('.' | ' ') expr | tuple | LP expr RP;
tuple: LP nodef=INT COMMA w1=PCST0 COMMA w2=INT COMMA nodet=INT RP;
LP: '(';
RP: ')';
PCST0: [PCST0];
INT: [0-9] ;
COMMA: ',';
WS: [ trn] -> skip; // toss out whitespace
С этим изменением ваш класс контекста кортежа будет иметь средства доступа для w1, w1 и node. узел будет представлять собой массив токенов NBR, как я его определил здесь .
Комментарии:
1. Спасибо за ваше подробное объяснение, и теперь я чувствую, что это намного понятнее для меня! У меня просто есть еще один вопрос относительно вашей обновленной грамматики, потому что мне нужно получить значение w1 и w2 для разных посетителей, имеет ли смысл, если я добавлю еще один слой в грамматику? Например
tuple: LP nodef wc wd nodet RP; nodef: INT; wc: W1; wd: INT; nodet: INT;
, где INT — это числа. Поэтому в этом случае я не буду использовать здесь несколько w2.2. Хороший вопрос. использование меток в ваших правилах синтаксического анализатора — это очень простой способ получить то, что вы ищете, без другого уровня правил (уровни правил легко выходят из-под контроля и затрудняют навигацию. Я отредактировал свой ответ, чтобы показать, как вы это сделаете.