... способ разрешить такую связь между анализатором и лексером?
Нет, не без создания собственного лексера.С лексером / парсером по умолчанию от ANTLR между ними существует строгое разделение: лексер работает независимо от парсера.
Но, насколько я вижу, вам это не нужно.Вы можете рекурсивно вызывать правила лексера.Поэтому, когда бы вы ни наткнулись на начало этого тега, <element
, вы просто смотрите в будущее и соответствуете либо />
, либо >
.Если он соответствует >
, попробуйте сопоставить символы, отличные от <
, или рекурсивно вызвать все это правило лексера.В конце, конечно, должно быть </element>
.
Быстрая демонстрация (без атрибутов, чтобы грамматика была простой):
grammar Test;
parse
: (t=. {System.out.printf("type=\%-15s text='\%s'\n", tokenNames[$t.type], $t.text);})* EOF
;
XML
: '<' Identifier ( '/>'
| '>' (~'<' | XML)* '</' Identifier '>'
)
;
Identifier
: ('a'..'z' | 'A'..'Z' | '_') ('a'..'z' | 'A'..'Z' | '_' | '0'..'9')*
;
QAssign
: '?='
;
SCol
: ';'
;
Spaces
: (' ' | '\t' | '\r' | '\n')+ {skip();}
;
Если вы теперь анализируете входные данные:
X ?= <element>text<another/>more<i><b>text</b></i></element>;
вы увидите, как на консоль выводится следующее:
type=Identifier text='X'
type=QAssign text='?='
type=XML text='<element>text<another/>more<i><b>text</b></i></element>'
type=SCol text=';'
Как видите, XML-блоки теперь обрабатываются как одиночные токены.Если вы хотите, чтобы между открывающими и закрывающими токенами находились токены PCDATA, вам нужно посчитать количество «открывающих тегов», и всякий раз, когда это число больше 0, сопоставляйте свой токен PCDATA следующим образом: ~'<'+
.При таком подходе также не требуется связь между лексером и парсером: счетчик, отслеживающий открывающие теги, будет определен в лексере.
Демонстрационная версия:
grammar Test;
@lexer::members {
private int openTags = 0;
}
parse
: any* EOF
;
any
: Identifier
| QAssign
| SCol
| xml
;
xml
: OTag (PCData | xml)* CTag
| Tag
;
PCData
: {openTags > 0}?=> ~'<'+
;
OTag
: '<' Identifier ('>' {openTags++;} | '/>' {$type=Tag;})
;
CTag
: '</' Identifier '>' {openTags--;}
;
Identifier
: ('a'..'z' | 'A'..'Z' | '_') ('a'..'z' | 'A'..'Z' | '_' | '0'..'9')*
;
QAssign
: '?='
;
SCol
: ';'
;
Spaces
: (' ' | '\t' | '\r' | '\n')+ {skip();}
;
fragment Tag : ;
Сейчассинтаксический анализ ввода, например:
X ?= <x>text<y/>more<i><b>text</b></i></x>;
приведет к следующему анализу: