在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
定义的"排除"算子?
在ANTLR中没有set/range数学。您只能通过OR操作符组合多个集合/范围。一些不相交范围的典型规则如下:
fragment LETTER_WHEN_UNQUOTED:
'0'..'9'
| 'A'..'Z'
| '$'
| '_'
| 'u0080'..'uffff'
;
一种方法是对一组字符进行"计算",这样我们就可以定义只组合字符的词法规则:
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 <">>
这将迫使我进一步重构上面的权宜之计'SEPARATORS'令牌,如:
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;
也许这是编写词法分析器工作的一部分,并且无法避免,但感觉更像是用错误的方式解决问题!
(注:我不会把这个答案标记为"正确"。)
在@mike-lischke的回答的刺激下(因为LETTER_WHEN_UNQUOTED
仍然感觉不对),我寻找其他语法中引用字符串字面量的常见处理方法。在Terrence Parr自己的Java 1.6 ANTLR3语法(不适合作为text/plain
)(通过ANTLR3语法列表)中,他在词法分析器规则中找到了"匹配除'之外的任何字符"的符号操作符~
:
STRINGLITERAL
: '"'
( EscapeSequence
| ~( '\' | '"' | 'r' | 'n' )
)*
'"'
;
// Copyright (c) 2007-2008 Terence Parr and possibly Yang Jiang.
注意:上面的代码是在BSD许可证下授权的,但是我不是在BSD许可证下重新分发这个片段(因为这篇文章本身是在CC-BY-SA下)。相反,我正在在我所理解的"合理使用"条款内使用它。
所以~
给了我一个选项来表达:'所有的Unicode字符,除了集合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*/))*
;
技巧是用excluding the set I don't want
(Unicode 128+)表示including the set I do want
(Unicode 0-127)。
这个答案比我的其他答案简洁多了。如果它确实工作,我将标记为正确。