Проблема в том, что лексер должен быть контекстно-зависимым: все в левой части =
должно быть переменной, а справа от нее - значением.Вы можете сделать это с помощью лексических режимов ANTLR .Вы начинаете с того, что классифицируете последовательные непробелы как переменные, а когда встречаете =
, вы переходите в свой режим значений.Находясь внутри режима значений, вы выходите из этого режима всякий раз, когда сталкиваетесь с разрывом строки.
Обратите внимание, что лексические режимы работают только в грамматике лексера, а не в комбинированной грамматике, которая у вас есть сейчас.Кроме того, для подсветки синтаксиса вам, вероятно, нужен только лексер.
Вот краткая демонстрация того, как это может работать (вставьте его в файл с именем IniLexer.g4
):
lexer grammar IniLexer;
SECTION
: '[' ~[\]]+ ']'
;
COMMENT
: ';' ~[\r\n]*
;
ASSIGN
: '=' -> pushMode(VALUE_MODE)
;
KEY
: ~[ \t\r\n]+
;
SPACES
: [ \t\r\n]+ -> skip
;
UNRECOGNIZED
: .
;
mode VALUE_MODE;
VALUE_MODE_SPACES
: [ \t]+ -> skip
;
VALUE
: ~[ \t\r\n]+
;
VALUE_MODE_COMMENT
: ';' ~[\r\n]* -> type(COMMENT)
;
VALUE_MODE_NL
: [\r\n]+ -> skip, popMode
;
Еслитеперь вы запускаете следующий скрипт:
source = """
; Comment outside
[section s1]
; Comment inside
a = 1
b = 2
[section s2]
c = 3 ; Comment right side
d = e
"""
lexer = IniLexer(InputStream(source))
stream = CommonTokenStream(lexer)
stream.fill()
for token in stream.tokens[:-1]:
print("{0:<25} '{1}'".format(IniLexer.symbolicNames[token.type], token.text))
вы увидите следующий вывод:
COMMENT '; Comment outside'
SECTION '[section s1]'
COMMENT '; Comment inside'
KEY 'a'
ASSIGN '='
VALUE '1'
KEY 'b'
ASSIGN '='
VALUE '2'
SECTION '[section s2]'
KEY 'c'
ASSIGN '='
VALUE '3'
COMMENT '; Comment right side'
KEY 'd'
ASSIGN '='
VALUE 'e'
И сопровождающая грамматика синтаксического анализатора может выглядеть так:
parser grammar IniParser;
options {
tokenVocab=IniLexer;
}
sections
: section* EOF
;
section
: COMMENT
| SECTION section_atom*
;
section_atom
: COMMENT
| KEY ASSIGN VALUE
;
который будет анализировать ваш пример ввода в следующем дереве разбора: