C# ANTLR:我可以要'';一个标记在一个上下文上,另一个标记在所述上下文之外?

C# ANTLR:我可以要'';一个标记在一个上下文上,另一个标记在所述上下文之外?,c#,parsing,antlr,antlr3,lexical-analysis,C#,Parsing,Antlr,Antlr3,Lexical Analysis,具体地说,我正在尝试在ANTLR中实现一个RegExp解析器 以下是我语法的相关部分: grammar JavaScriptRegExp; options { language = 'CSharp3'; } tokens { /* snip */ QUESTION = '?'; STAR = '*'; PLUS = '+'; L_CURLY = '{'; R_CURLY = '}'; COMMA = ','; } /* snip

具体地说,我正在尝试在ANTLR中实现一个RegExp解析器

以下是我语法的相关部分:

grammar JavaScriptRegExp;
options {
    language = 'CSharp3';
}

tokens {
    /* snip */
    QUESTION = '?';
    STAR = '*';
    PLUS = '+';
    L_CURLY = '{';
    R_CURLY = '}';
    COMMA = ',';
}

/* snip */

quantifier returns [Quantifier value]
    :   q=quantifierPrefix QUESTION?
        {
            var quant = $q.value;
            quant.Eager = $QUESTION == null;
            return quant;
        }
    ;

quantifierPrefix returns [Quantifier value]
    :   STAR { return new Quantifier { Min = 0 }; }
    |   PLUS { return new Quantifier { Min = 1 }; }
    |   QUESTION { return new Quantifier { Min = 0, Max = 1 }; }
    |   L_CURLY min=DEC_DIGITS (COMMA max=DEC_DIGITS?)? R_CURLY
        {
            var minValue = int.Parse($min.Text);
            if ($COMMA == null)
            {
                return new Quantifier { Min = minValue, Max = minValue };
            }
            else if ($max == null)
            {
                return new Quantifier { Min = minValue, Max = null };
            }
            else
            {
                var maxValue = int.Parse($max.Text);
                return new Quantifier { Min = minValue, Max = maxValue };
            }
        }
    ;

DEC_DIGITS
    :   ('0'..'9')+
    ;

/* snip */

CHAR
    :   ~('^' | '$' | '\\' | '.' | '*' | '+' | '?' | '(' | ')' | '[' | ']' | '{' | '}' | '|')
    ;
现在,在大括号内,我想把“,”标记为逗号,但在外面,我想把它标记为CHAR

这可能吗

这不是发生这种情况的唯一情况。我将有许多其他的例子,其中这是一个问题(十进制数字,字符类中的连字符,等等)

编辑:

我知道这被称为上下文敏感词法分析。这在ANTLR中是可能的吗

这称为上下文敏感词汇。这在ANTLR中是可能的吗

不,解析器不能“告诉”词法分析器它需要在解析过程中的某个特定时间处理不同的数字。仅在lexer中可能存在一些上下文敏感的词法分析,但是解析器不能影响lexer

但是,使用一些额外的解析器规则可以很容易地解决这个问题。例如,当匹配字符类(
[
..
]
)时,您使用的解析器规则匹配字符类中任何有效的内容:

char_class
 : LBRACK char_class_char+ RBRACK
 ;

// ...

char_class_char
 : LBRACK // the '[' is not special inside a character class!
 | LBRACE // the '{' is not special inside a character class!
 | RBRACE // the '}' is not special inside a character class!
 | PLUS   // the '+' is not special inside a character class!
 | STAR   // the '*' is not special inside a character class!
 | QMARK  // the '?' is not special inside a character class!
 | COMMA
 | DIGIT
 | OTHER
 ;
一个小演示:

grammar T;

parse
 : atom* EOF
 ;

atom
 : unit quantifier?
 ;

unit
 : char_class
 | single_char
 ;

quantifier
 : greedy (PLUS | QMARK)?
 ;

greedy
 : PLUS
 | STAR
 | QMARK
 | LBRACE (number (COMMA number?)?) RBRACE
 ;

char_class
 : LBRACK char_class_char+ RBRACK
 ;

number
 : DIGIT+
 ;

single_char
 : DIGIT
 | COMMA
 | RBRACE
 | RBRACK // this is only special inside a character class
 | OTHER
 ;

char_class_char
 : LBRACK
 | LBRACE
 | RBRACE
 | PLUS
 | STAR
 | QMARK
 | COMMA
 | DIGIT
 | OTHER
 ;

LBRACK : '[';
RBRACK : ']';
LBRACE : '{';
RBRACE : '}';
PLUS   : '+';
STAR   : '*';
QMARK  : '?';
COMMA  : ',';
DIGIT  : '0'..'9';
OTHER  : . ;
这将解析输入
“[+*]{5,20}?A*+”
,如下所示:

在这里可以找到更完整的PCRE语法:(可以找到语法)

编辑 我更愿意将“,”标记为花括号内的逗号,但将其标记为外部的CHAR。我现在将使用变通方法,但这可能吗

不,就像我说的:lexer不受解析器的影响。如果你想要这个,你应该选择a而不是ANTLR。对于ANTLR,词法分析和语法分析之间有一个严格的分离:对此您无能为力

但是,您可以只更改在解析器规则中匹配的标记的类型。每个解析器规则都有一个
$start
$end
标记,表示它匹配的第一个和最后一个标记。由于
char\u class\u char
(和
single\u char
)将始终匹配单个令牌,因此您可以在规则的
@after
块中更改令牌的类型,如下所示:

single_char
@after{$start.setType(CHAR);}
 : DIGIT
 | COMMA
 | RBRACE
 | RBRACK // this is only special inside a character class
 | OTHER
 ;

char_class_char
@after{$start.setType(CHAR);}
 : LBRACK
 | LBRACE
 | RBRACE
 | PLUS
 | STAR
 | QMARK
 | COMMA
 | DIGIT
 | CHAR
 ;

// ...

CHAR : . ;
导致你追求的行为(我猜)


HTH

可以使用lexer中的选通语义谓词来实现这一点。在下面的代码中,只有当isComma为true时,“”才会匹配逗号规则。否则,如果字符出现在语法中的逗号之后,它将与字符匹配。我不知道CSharp,所以我不能给出一个完整的例子

L_CURLY : '{' {setComma();};
R_CURLY : '}' {clearComma();};
COMMA : {isComma}? => ',';

显然,如果在不同的上下文中使用大括号,这可能不起作用。我建议避免以这种方式使用lexer,除非它真的会把解析器搞得一团糟。

我想诀窍就是让解析器在两个地方都接受这些标记。然而,我更希望标记在语义上是正确的。我更愿意将“,”标记为花括号内的逗号,但将其标记为外部的CHAR。我现在将使用变通方法,但这可能吗?@JohnGietzen,请参阅我的edit这是真的,但是,因为regex有很多字符,在某些上下文中需要以不同的方式处理(字符类内部的
^
、字符类内部不特殊的大多数普通元字符、
\Q
\E
内部的所有字符,以及更多……),我不建议这样做。这几乎正是我想要的。但是,我打算切换到PEG样式的解析器,而不是ANTLR。@巴特:克拉在字符类内外都很特殊。唯一真正奇怪的字符是连字符。其他一切都很好。@JohnGietzen,嗯,
[^a]
匹配除文本
'a'
以外的任何字符,而
[a^]
匹配文字
'a'
或文字
'^'
。在字符类中,连字符放在类的开头或结尾,或直接放在范围或速记字符类之后时,没有任何特殊意义:即
[-abc]
[abc-]
[d-\w]中的连字符
,…都匹配文本
“-”
@Bart:这很公平。我想我需要使用PEG或递归下降解析器。