Как определить токен, который является всеми этими символами в наборе A, кроме символов в подмножестве B?
В RFC2616 (HTTP / 1.1) определение "токена" в разделе "2.2 Основные правила" дано как:
token = 1*<any CHAR except CTLs or separators>
Из этого раздела у меня есть следующие фрагменты, и теперь я хочу определить "TOKEN":
lexer grammar AcceptEncoding;
TOKEN: /* (CHAR excluding (CTRL | SEPARATORS)) */
fragment CHAR: [\u0000-\u007f];
fragment CTRL: [\u0000-\u001f] | \u007f;
fragment SEPARATORS: [()<>@,;:\\;"/\[\]?={|}] | SP | HT;
fragment SP: ' ';
fragment HT: '\t';
Как мне приблизить мой гипотетический оператор "исключения" для определения TOKEN
?
3 ответа
В ANTLR нет математики для набора / диапазона. Вы можете комбинировать только несколько наборов / диапазонов с помощью оператора ИЛИ. Типичное правило для ряда непересекающихся диапазонов выглядит так:
fragment LETTER_WHEN_UNQUOTED:
'0'..'9'
| 'A'..'Z'
| '$'
| '_'
| '\u0080'..'\uffff'
;
Подстегнутый ответ от @mike-lischke (потому что LETTER_WHEN_UNQUOTED
все еще чувствовал себя не так), я охотился за общепринятым подходом к цитируемым строковым литералам в других грамматиках. В собственной грамматике Java 1.6 ANTLR3 Терренса Парра (не правильно text/plain
) (через ANTLR3 Grammar List), он достигает "соответствует любому символу, кроме" тильд-оператора ~
в правиле лексера:
STRINGLITERAL
: '"'
( EscapeSequence
| ~( '\\' | '"' | '\r' | '\n' )
)*
'"'
;
// Copyright (c) 2007-2008 Terence Parr and possibly Yang Jiang.
ПРИМЕЧАНИЕ: приведенный выше код лицензирован под лицензией BSD, но я не перераспределяю этот фрагмент под лицензией BSD (так как этот пост находится под CC-BY-SA). Вместо этого я использую его в терминах "добросовестного использования", как я их понимаю.
Итак ~
дает мне возможность выразить: "все эти символы в Юникоде, кроме символов в наборе B". "Раздражает, я не могу выбрать набор, который исключен из", подумал я. Но потом понял
TOOHIGH: [\u007f-\uffff];
TOKEN: (~( TOOHIGH | SP | HT | CTRL | SEPARATORS ))+
... все должно быть в порядке. Хотя на практике ANTLR4 не "любит" лексерные под-правила, появляющиеся в "множествах", а обрабатывает только наборы литералов, так что в конечном итоге это становится:
TOKEN:
/* this is given in '2.2 Basic Rules' as:
*
* token = 1*<any CHAR except CTLs or separators>
*
* which I am reducing down to:
* any character in ASCII 0-127 but _excluding_
* CTRL (0-31,127)
* SEPARATORS
* space (32)
* and tab (9) (which is a CTRL character anyhow)
*/
( ~( [\u0000-\u001f] | '\u007f' /*CTRL,HT*/ | [()<>@,;:\\;"/\[\]?={|}] /*SEPARATORS*/ | '\u0020' /*SP*/ | [\u0080-\uffff] /*NON_ASCII*/))*
;
Трюк выражал including the set I do want
(Unicode 0-127) с точки зрения excluding the set I don't want
(Unicode 128+).
Это гораздо более кратко, чем мой другой ответ. Если это действительно работает, я отмечу это как правильное.
Один из подходов состоит в том, чтобы "сделать математику" для набора символов, чтобы мы могли определить лексические правила, которые когда-либо объединяют символы:
lexer grammar RFC2616;
TOKEN: (DIGIT | UPALPHA | LOALPHA | NON_SEPARATORS)+
/*
* split up ASCII 0-127 into 'atoms' of
* relevance per '2.2 Basic Rules'. Regions
* not requiring to be referenced are not
* given a name.
*/
// [\u0000-\u0008]; /* (control chars) */
fragment HT: '\u0009'; /* (tab) */
fragment LF: '\u0010'; /* (LF) */
// [\u0011-\u0012]; /* (control chars) */
fragment CR: '\u0013'; /* (CR)
// [\u0014-\u001f]; /* (control chars) */
fragment SP: '\u0020'; /* (space) */
// [\u0021-\u02f]; /* !"#$%'()*+,-./ */
fragment DIGIT: [u0030-\u0039]; /* 01234556789 */
// [\u003a-\u0040]; /* :;<=>@ */
fragment UPALPHA: [\u0041-\u005a]; /* ABCDEFGHIGJLMNOPQRSTUVWXYZ */
// [\u005b-\u0060]; /* [\]^_` */
fragment LOALPHA: [\u0061-\u0071]; /* abcdefghijklmnopqrstuvwxyz */
// [\u007b-\u007e]; /* {|}~ */
// '\u007f'; /* (del) */
/*
* Considering 'all relevant gaps' and the characters we
* cannot use per RFC 2616 Section 2.2 Basic Rules definition
* of 'separators', what does that leave us with?
* (manually determined)
*/
fragment SEPARATORS: [()<>@,;:\\;"/\[\]?={|}];
fragment NON_SEPARATORS: [!#$%&'*+-.^_`~*];
Я не считаю этот подход особенно удовлетворительным. Другое правило в RFC 2616 хочет быть определено как:
TEXT: <any OCTET except CTLs, but including LWS>
qdtext = <any TEXT except <">>
Это заставит меня продолжить рефакторинг моего подходящего токена "СЕПАРАТОРЫ" выше, например:
fragment QUOT: '"';
fragment SEPARATORS_OTHER_THAN_QUOT: [()<>@,;:\\;/\[\]?={|}];
fragment SEPARATORS: SEPARATORS_OTHER_THAN_QUOT | QUOT;
fragment LWS: SP | HT;
TEXT: DIGIT | UPALPHA | LOALPHA | LWS | SEPARATORS | NON_SEPARATORS;
QDTEXT: DIGIT | UPALPHA | LOALPHA | LWS | SEPARATORS_OTHER_THAN_QUOT | NON_SEPARATORS;
Возможно, это часть работы по написанию лексера, и ее нельзя избежать, но это больше похоже на решение проблемы неправильным путем!
(NB: я не буду отмечать этот ответ как "правильный".)