ANTLR динамические области, разделяемые между лексером и парсером? - PullRequest
1 голос
/ 16 марта 2012

Я пишу небольшой язык, на котором я буду включать XML-литералы таким образом:

X ?= <element attr='blah'>text<another attr='blah /> more text</element>;

Я работаю с XML-грамматикой , предоставленной Терренсом Парром. Моя проблема возникает из-за того, что определение для PCDATA выглядит примерно так: (~'<')+, который захватывает весь другой источник в моих модулях, который находится за пределами XML-литерала .

Что я хотел бы сделать, так это установить защиту на токен TEXT, чтобы он активировался только тогда, когда мы ожидаем XML.

PCDATA : {isInXmlFragment}?=> (~'<')+;

Проблема в том, что эту переменную области видимости необходимо установить из анализатора, но использовать в лексере. Я понимаю, что могу что-то упорядочить через статическую переменную в каком-то обычно видимом классе, но это было бы сложно, если бы я хотел анализировать несколько модулей параллельно. Решение Parr имеет такую ​​защиту, но оно работает только в контексте лексера и будет работать только в чистых XML-файлах без других видов контента.

Поскольку я встраиваю этот XML в файл исходного кода, есть другой сканируемый текст, выходящий за рамки XML-частей моего лексера, но часть правила XML PCDATA соответствует всему источнику, потому что оно настолько общее.

Есть ли какой-нибудь безопасный встроенный способ, позволяющий такую ​​связь между анализатором и лексером? что-то вроде области видимости и способ ссылки на динамическую область видимости лексера из анализатора?

1 Ответ

2 голосов
/ 16 марта 2012

... способ разрешить такую ​​связь между анализатором и лексером?

Нет, не без создания собственного лексера.С лексером / парсером по умолчанию от 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>;

приведет к следующему анализу:

enter image description here

...