Ошибка распознавания токена ANTLR с лексером и парсером - PullRequest
1 голос
/ 07 мая 2020

Я пишу грамматику ANTLR Lexer and Parser, которая будет анализировать текст, очень похожий на класс Java. В конце концов, он будет анализировать текст следующим образом:

reference schema:"https://schema.org/";
reference dc:"https://www.dublincore.org/";

type dc:Author {
}

Я медленно создаю лексер и парсер. Мне удалось успешно разобрать reference, но я уперся в стену при разборе type.

Перед добавлением поддержки type я мог использовать строковые литералы для пробела, двоеточия и точки с запятой в анализаторе, но после того, как я обнаружил cannot create implicit token for string literal ошибки. Я определил правило лексера для каждого из этих символов и заменил все вхождения литерала правилом. Однако это нарушило синтаксический анализ reference s.

. Я включил свой лексер и синтаксический анализатор, который успешно анализирует reference s ниже (вместе с образцом ввода и проанализированным абстрактным синтаксическим деревом) и усовершенствованными версиями, которые не работает. Я не получаю ошибок компиляции, но очень много token recognition error s (скриншот приведен ниже).

Как правильно обрабатывать синтаксический анализ?

Работает

Lexer

lexer grammar WorkingLexerGrammar;

WS: ('\t' | '\n' | '\r' )+ -> skip ;

fragment Colon : ':';
fragment SemiColon: ';';
fragment Underscores: '_'+ ;
fragment Digits: [0-9]+ ;
fragment LowercaseLetters: [a-z]+ ;
fragment UppercaseLetters: [A-Z]+ ;
fragment String: '"' .*? '"' ;
fragment Prefix: (Underscores | Digits | LowercaseLetters)+ ;

REFERENCE_KEYWORD: 'reference' ;
TYPE_KEYWORD: 'type' ;

PREFIXED_REFERENCE: ' ' -> pushMode(PrefixedReferenceMode) ;

mode PrefixedReferenceMode;
REFERENCE_PREFIX: Prefix;
REFERENCE_PREFIX_SEPARATOR: ':' -> pushMode(IriMode);
END_IRI: ';' -> popMode;

mode IriMode;
IRI: String  -> popMode;

Парсер

parser grammar WorkingParserGrammar ;

options { tokenVocab=WorkingLexerGrammar; }

document: reference* EOF ;

prefixedReference: REFERENCE_PREFIX ':' IRI;
reference: REFERENCE_KEYWORD ' ' prefixedReference ';';

Вход

reference schema:"https://schema.org/";
reference dc:"https://www.dublincore.org/";

Выход

Abstract syntax tree output

Evolved (не работает)

Lexer

lexer grammar NotWorkingLexerGrammar;

WS: ('\t' | '\n' | '\r' )+ -> skip ;

fragment Colon : ':';
fragment SemiColon: ';';
fragment Underscores: '_'+ ;
fragment Digits: [0-9]+ ;
fragment LowercaseLetters: [a-z]+ ;
fragment UppercaseLetters: [A-Z]+ ;
fragment String: '"' .*? '"' ;
fragment Prefix: (Underscores | Digits | LowercaseLetters)+ ;

COLON: Colon;
SEMICOLON: SemiColon;
SPACE: ' ';

REFERENCE_KEYWORD: 'reference' ;
TYPE_KEYWORD: 'type' ;

PREFIXED_REFERENCE: SPACE -> pushMode(PrefixedReferenceMode) ;

mode PrefixedReferenceMode;
REFERENCE_PREFIX: Prefix;
REFERENCE_PREFIX_SEPARATOR: COLON -> pushMode(IriMode);
END_IRI: SEMICOLON -> popMode;

mode IriMode;
IRI: String  -> popMode;

PREFIXED_NAME: SPACE -> pushMode(PrefixedNameMode) ;

mode PrefixedNameMode;
NAME_PREFIX: Prefix;
NAME_PREFIX_SEPARATOR: COLON -> pushMode(LocalNameMode);
END_NAME: SEMICOLON -> popMode;

mode LocalNameMode;
LOCAL_NAME: (Underscores | Digits | LowercaseLetters | UppercaseLetters)+  -> popMode;

Parser

parser grammar NotWorkingParserGrammar ;

options { tokenVocab=NotWorkingLexerGrammar; }

document: reference* type* EOF ;

prefixedReference: REFERENCE_PREFIX COLON IRI;
reference: REFERENCE_KEYWORD SPACE prefixedReference SEMICOLON;

prefixedName: NAME_PREFIX SPACE LOCAL_NAME;
type: TYPE_KEYWORD SPACE prefixedName;

Output

Abstract syntax tree not parsing correctly


После помощи Барта Кирса Я сделал два обновления грамматик лексера и синтаксического анализатора с переменным успехом.

Первое обновление

Это изменение анализирует определение типа правильно, но только если я удалю правила лексера для справки. Я думаю, что причина в том, что эти два правила одинаковы (т.е. PREFIXED_REFERENCE: SPACE -> pushMode(PrefixedReferenceMode) ; для ссылки и PREFIXED_NAME: SPACE -> pushMode(PrefixedNameMode) ; для типа) - то есть они оба совпадают по пробелу. Мое второе обновление пытается исправить это, но полные грамматики лексера и парсера приведены ниже.

Lexer

lexer grammar NotWorkingLexerGrammar;

WS: ('\t' | '\n' | '\r' )+ -> skip ;

fragment Underscores: '_'+ ;
fragment Digits: [0-9]+ ;
fragment LowercaseLetters: [a-z]+ ;
fragment UppercaseLetters: [A-Z]+ ;
fragment String: '"' .*? '"' ;
fragment Prefix: (Underscores | Digits | LowercaseLetters)+ ;

fragment COLON: ':';
fragment SEMICOLON: ';';
fragment SPACE: ' ';

fragment REFERENCE_KEYWORD: 'reference' ;
fragment TYPE_KEYWORD: 'type' ;

PREFIXED_REFERENCE: SPACE -> pushMode(PrefixedReferenceMode) ;

mode PrefixedReferenceMode;
REFERENCE_PREFIX: Prefix;
REFERENCE_PREFIX_SEPARATOR: COLON -> pushMode(IriMode);
END_IRI: SEMICOLON -> popMode;

mode IriMode;
IRI: String  -> popMode;

PREFIXED_NAME: SPACE -> pushMode(PrefixedNameMode) ;

mode PrefixedNameMode;
NAME_PREFIX: Prefix;
NAME_PREFIX_SEPARATOR: COLON -> pushMode(LocalNameMode);
END_NAME: SEMICOLON -> popMode;

mode LocalNameMode;
LOCAL_NAME: (Underscores | Digits | LowercaseLetters | UppercaseLetters)+  -> popMode;

Parser

parser grammar NotWorkingParserGrammar ;

options { tokenVocab=NotWorkingLexerGrammar; }

document: reference* type* EOF ;

prefixedReference: REFERENCE_PREFIX REFERENCE_PREFIX_SEPARATOR IRI;
reference: REFERENCE_KEYWORD PREFIXED_REFERENCE prefixedReference END_IRI;

prefixedName: NAME_PREFIX NAME_PREFIX_SEPARATOR LOCAL_NAME;
type: TYPE_KEYWORD PREFIXED_NAME prefixedName END_NAME;

Второе обновление

В попытке исправить это, я переместил ключевые слова reference и type в правила лексера для соответствующих частей, но это только анализирует тип, если я удалю все правила лексера для справки. Однако ссылки обрабатываются правильно.

Lexer

lexer grammar NotWorkingLexerGrammar;

WS: ('\t' | '\n' | '\r' )+ -> skip ;

fragment Underscores: '_'+ ;
fragment Digits: [0-9]+ ;
fragment LowercaseLetters: [a-z]+ ;
fragment UppercaseLetters: [A-Z]+ ;
fragment String: '"' .*? '"' ;
fragment Prefix: (Underscores | Digits | LowercaseLetters)+ ;

fragment COLON: ':';
fragment SEMICOLON: ';';
fragment SPACE: ' ';

fragment REFERENCE_KEYWORD: 'reference' ;
fragment TYPE_KEYWORD: 'type' ;

PREFIXED_REFERENCE: REFERENCE_KEYWORD SPACE -> pushMode(PrefixedReferenceMode) ;

mode PrefixedReferenceMode;
REFERENCE_PREFIX: Prefix;
REFERENCE_PREFIX_SEPARATOR: COLON -> pushMode(IriMode);
END_IRI: SEMICOLON -> popMode;

mode IriMode;
IRI: String  -> popMode;

TYPE_DEFINITION: TYPE_KEYWORD SPACE -> pushMode(PrefixedNameMode) ;

mode PrefixedNameMode;
NAME_PREFIX: Prefix;
NAME_PREFIX_SEPARATOR: COLON -> pushMode(LocalNameMode);
END_NAME: SEMICOLON -> popMode;

mode LocalNameMode;
LOCAL_NAME: (Underscores | Digits | LowercaseLetters | UppercaseLetters)+  -> popMode;

Parser

parser grammar NotWorkingParserGrammar ;

options { tokenVocab=NotWorkingLexerGrammar; }

document: reference* type* EOF ;

prefixedReference: REFERENCE_PREFIX REFERENCE_PREFIX_SEPARATOR IRI;
reference: PREFIXED_REFERENCE prefixedReference END_IRI;

prefixedName: NAME_PREFIX NAME_PREFIX_SEPARATOR LOCAL_NAME;
type: TYPE_DEFINITION prefixedName END_NAME;

Output

Для следующего ввода:

reference schema:"https://schema.org/";
reference dc:"https://www.dublincore.org/";

type dc:Author;

Это результат:

line 4:0 token recognition error at: 't'
line 4:1 token recognition error at: 'y'
line 4:2 token recognition error at: 'p'
line 4:3 token recognition error at: 'e'
line 4:4 token recognition error at: ' '
line 4:5 token recognition error at: 'd'
line 4:6 token recognition error at: 'c'
line 4:7 token recognition error at: ':'
line 4:8 token recognition error at: 'A'
line 4:9 token recognition error at: 'u'
line 4:10 token recognition error at: 't'
line 4:11 token recognition error at: 'h'
line 4:12 token recognition error at: 'o'
line 4:13 token recognition error at: 'r;'

Мое рассуждение в пользу использования режимов состоит в том, чтобы ограничить объем правил. Это язык, который я контролирую, но предпочел бы не менять его кардинально. Язык - это гораздо больше, чем я показал здесь, и у нас уже есть грамматика (в настоящее время комбинированная грамматика), но она довольно хрупкая. Я попытался внести изменения, чтобы запретить использование прописных букв в префиксах, но разрешить их в локальном имени, но это нарастало, и начали применяться другие правила. Исследования показали, что режимы - это подход к решению этой ситуации, но я не очень знаком с ANTLR, поэтому, возможно, неправильно его понял.

Abstract syntax tree for second update

1 Ответ

1 голос
/ 08 мая 2020

При обнаружении ошибок / предупреждений, подобных этим:

line 4:0 token recognition error at: 't'
line 4:1 token recognition error at: 'y'
line 4:2 token recognition error at: 'p'
line 4:3 token recognition error at: 'e'
...

, это означает, что лексер не может создать токен для ввода (type ... в данном случае). В вашем случае это означает, что лексер не может создать токен из ввода в режиме, в котором он сейчас находится.

Я попытался внести изменения, чтобы запретить использование прописных символов в префиксах, но разрешить их в локальное имя, но это нарастало снежно, и начали применяться другие правила

Есть два варианта решения таких вещей:

  1. просто синтаксический анализ префиксов, как и любой обычный идентификатор (в верхнем или нижнем регистре ) и после синтаксического анализа просмотрите сгенерированное дерево синтаксического анализа и убедитесь, что идентификаторы префиксов действительно имеют нижний регистр, используя посетителя или слушателя ANTLR (см.: https://github.com/antlr/antlr4/blob/master/doc/listeners.md)
  2. сделайте различие в ваш лексер между идентификаторами в нижнем и верхнем регистрах и используйте их соответствующим образом в правилах синтаксического анализатора, может сработать что-то вроде этого: подробнее) языки, встроенные друг в друга. Например разбор HTML файлов: есть контент (текст) и теги с атрибутами. Насколько я понимаю, вы используете его не так, как предполагалось, ИМО.
...