У меня проблемы с расширенными строками ANTLR.
Моя основная проблема заключается в том, что если я хочу, чтобы augmentedStrings читались правильно, мне нужно сохранить строку в качестве правила синтаксического анализатора.
Но это приводит к тому, что строка имеет кавычку, тело, если оно есть, продолжение тела и конечную кавычку.
Это усложняет реализацию посетителя.
Когда я обмениваю строковое правило на
string: STRING; STRING: QUOTE ( ESCAPE_SEQUENCE | .)*? QUOTE;
он ломает augmentedString, который уже является «хакерским».
Ниже приведена грамматика.
Любые предложения будут полезны.
Используемая тестовая команда: antlr4-parse strings.g4 root -gui <test1.txt
и test1.txt содержит: string a = "normal string" + $"{id}";
grammar strings;
// Comments and white space
WS: [ \t\r\n]+ -> skip;
// key words
PLUS: '+';
// Symbols
QUESTION: '?';
LPAREN: '(';
RPAREN: ')';
LCURLY: '{';
RCURLY: '}';
SEMI: ';';
NEWLINE: '\n';
ASSIGN: '=';
QUOTE: '"';
DOLLAR: '$';
// Types
STRING_T: 'string';
type: STRING_T;
value:
| augmentedString
| concatanatedString
| string;
//this has to be changed to a lexer rule inorder to not have a child of every thing
string:
QUOTE ( ESCAPE_SEQUENCE | .)*? QUOTE;
augmentedString:
DOLLAR QUOTE (( ESCAPE_SEQUENCE | .)?( LCURLY expr RCURLY) | ( ESCAPE_SEQUENCE | .)( LCURLY expr RCURLY)? ) * QUOTE;
concatanatedString: (id | augmentedString | string ) (PLUS (id | augmentedString | string))*;
ESCAPE_SEQUENCE:
'\\' (('\\' | '\'' | '"' ) | UNICODE_ESCAPE);
fragment UNICODE_ESCAPE:
'u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT;
fragment HEX_DIGIT: [0-9a-fA-F];
// Identifiers
ID: [a-zA-Z_][a-zA-Z_0-9]*;
id: ID;
argument: type id; //maybe replace all with the right hand side.
field: type? id ASSIGN expr SEMI;
// Expressions
expr:
id
|value;
// Statements
statementList: statement*;
statement:
field;
// Add a start rule for testing
root: (
field
)*;
Я попытался изменить правило строки на правило синтаксического анализатора с дополнительным шагом, а также с кучей хакерских решений, таких как разделение строки на кавычку и тело строки и наличие отдельного конечного тела.
Поменяв STRING
на string
, вы также будете пропускать пробелы внутри строковых литералов из-за правила WS
.
Вы можете использовать лексические режимы ANTLR. Когда вы встречаете $"
, вы переключаетесь в специальный строковый режим и создаете токены, специфичные для интерполированной строки. Внутри этого режима вы снова переключаетесь в «режим кода», когда встречаете {
. Вы выходите из этого режима кода после обнаружения }
и снова возвращаетесь в режим по умолчанию, когда встречаете "
.
Чтобы использовать режимы, вы должны разделить грамматики лексера и парсера в отдельные файлы. Быстрая демонстрация:
// File: ModeDemoLexer.g4
lexer grammar ModeDemoLexer;
PLUS : '+';
SIMPLE_STRING : '"' ~["\r\n]* '"';
STRING_START : '$"' -> pushMode(STRING_MODE);
SPACES : S+ -> skip;
fragment S : [ \t\r\n];
mode STRING_MODE;
STRING_END : '"' -> popMode;
CODE_START : '{' -> pushMode(CODE_MODE);
STRING_ATOM : ~["{];
mode CODE_MODE;
CODE_MODE_SKIP : S+ -> skip;
ID : [a-zA-Z_] [a-zA-Z_0-9]*;
CODE_END : '}' -> popMode;
и:
// File: ModeDemoParser.g4
parser grammar ModeDemoParser;
options {
tokenVocab=ModeDemoLexer;
}
parse
: expr EOF
;
expr
: expr PLUS expr
| string
| SIMPLE_STRING
;
string
: STRING_START string_atom* STRING_END
;
string_atom
: STRING_ATOM
| CODE_START code_expr CODE_END
;
code_expr
: ID
;
Если теперь разобрать "normal string" + $"id: { id }"
, то получим следующий результат:
А без модов можно было бы сделать что-то вроде этого:
parse
: expr EOF
;
expr
: expr PLUS expr
| string
| '(' expr ')'
| SIMPLE_STRING
| ID
;
string
: STRING_START expr ( STRING_MIDDLE expr )* STRING_END
;
PLUS : '+';
SIMPLE_STRING : '"' ~["\r\n]* '"' | '$"' ~["\r\n{]* '"';
STRING_START : '$"' ~["{]* '{';
STRING_MIDDLE : '}' ~["{]* '{';
STRING_END : '}' ~["{]* '"';
SPACES : [ \t\r\n]+ -> skip;
ID : [a-zA-Z_] [a-zA-Z_0-9]*;
Анализ входных данных "normal string" + $"id: { id }, x + y = { (x + y) }"
приведет к следующему:
Спасибо вам большое, вы действительно оракул.
Лексеры Antlr полностью контекстно-свободны. Любые правила синтаксического анализатора можно переместить из синтаксического анализатора в лексер с тем ограничением, что все правила помечаются знаком
fragment
, за исключением правила лексера верхнего уровня. Вам не нужны режимы лексера. Но вам нужно будет переименовать символы синтаксического анализатора в символы лексера.