如何定义一个符号,它是集合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定义的"排除"算子?

在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)。

这个答案比我的其他答案简洁多了。如果它确实工作,我将标记为正确。

相关内容

  • 没有找到相关文章

最新更新