我是ANTLR的新手,正在使用ANTLR3进行解析器工作,但是在以下情况下遇到了麻烦。在我们分析的文本中,可能存在^-字符出现的多种情况。但是,在一种特殊情况下,“ ^”后面紧跟一个字符。这发生在字符串中:
在第一种情况下'^ M'是字符串的一部分,其中^ M表示13十六进制,但在第二种情况下不是;那里是一个指针指示器。第二种情况是在语法规则中捕获的(在多个规则中使用^-字符)。
如果我使用以下标记解决它,它将失败,因为'^ MyValue'在'^ M'和'yValue'中被标记。但是,我希望仅在^之后有一个字符时才使用令牌ControlChar。否则,应将其忽略而不是标记化,以便可以在语法中使用它。
Pointer : '^'
;
QuotedString : '\'' ('\'\'' | ~('\''))* '\''
;
TkIdentifier : (Alpha | '_') (Alpha | Digit | '_')*
;
ControlString : Controlchar (Controlchar)*
;
fragment
Controlchar : '#' Digitseq
| '#' '$' Hexdigitseq
| '^' Alpha
;
fragment
Alpha : 'a'..'z'
| 'A'..'Z'
;
fragment
Digit : '0'..'9'
;
所以,我的问题是。如何指示ANTLR '^' Alpha
仅在此字符后紧跟一个Alpha时才匹配,否则在文本中保留'^'并将其标记为TkIdentifier令牌吗?
例如,词法分析器应创建以下标记:
^Foo -> Pointer TkIdentifier
^F oo -> ControlChar TkIdentifier
^ F oo -> Pointer TkIdentifier TkIdentifier
Foo^M -> TkIdentifier ControlChar
Foo ^ M -> TkIdentifier Pointer TkIdentifier
Foo ^M -> TkIdentifier ControlChar
Foo^ M -> TkIdentifier Pointer TkIdentifier
'Text'^M -> QuotedString ControlChar
'Text' ^M -> QuotedString ControlChar
'Text' ^ M -> QuotedString Pointer TkIdentifier
^M'Text' -> ControlChar QuotedString
^M 'Text' -> ControlChar QuotedString
^ M'Text' -> Pointer TkIdentifier QuotedString
在这种情况下,您必须在语法的a predicate中使用目标特定的代码。
您需要做的是:每当词法分析器偶然发现^
时,它都必须在流中向前看2个字符。如果这两个字符是一个单词和一个非单词,它将创建一个Controlchar
令牌,其中包括Alpha
之后的^
。如果不是,请从Pointer
创建一个^
。
对于以Java为目标语言的ANTLR3,可能看起来像这样:
// Cannot be a fragment now, because we're changing the `type` in certain cases. And because it is
// no fragment any more, it has to come before the `ControlString` rule.
Controlchar
: '^' ( // Execute the predicate, which looks ahead 2 chars and passes if
// these 2 chars are a word and a non-word
{((char)input.LA(1) + "" + (char)input.LA(2)).matches("\\w\\W")}?=>
// If the predicate is true, match a single `Alpha`
Alpha
| // If the predicate failed, change the type of this token to a `Pointer`
{$type=Pointer;}
)
| '#' Digitseq
| '#' '$' Hexdigitseq
;
ControlString
: Controlchar+
;
我进行了快速测试:
import org.antlr.runtime.*;
public class Main {
public static void main(String[] args) throws Exception {
String[] tests = {
"^Foo",
"^F oo",
"^ F oo",
"Foo^M",
"Foo ^ M",
"Foo ^M",
"Foo^ M",
"'Text'^M",
"'Text' ^M",
"'Text' ^ M",
"^M'Text'",
"^M 'Text'",
"^ M'Text'",
"^Q^E^D"
};
for (String test : tests) {
TLexer lexer = new TLexer(new ANTLRStringStream(test));
CommonTokenStream tokenStream = new CommonTokenStream(lexer);
tokenStream.fill();
System.out.printf("\ntest: %-15s tokens: ", test);
for (Token t : tokenStream.getTokens()) {
if (t.getType() != -1) {
System.out.printf(" %s", TParser.tokenNames[t.getType()]);
}
}
}
}
}
其中印刷:
test: ^Foo tokens: Pointer TkIdentifier
test: ^F oo tokens: Controlchar TkIdentifier
test: ^ F oo tokens: Pointer TkIdentifier TkIdentifier
test: Foo^M tokens: TkIdentifier Controlchar
test: Foo ^ M tokens: TkIdentifier Pointer TkIdentifier
test: Foo ^M tokens: TkIdentifier Controlchar
test: Foo^ M tokens: TkIdentifier Pointer TkIdentifier
test: 'Text'^M tokens: QuotedString Controlchar
test: 'Text' ^M tokens: QuotedString Controlchar
test: 'Text' ^ M tokens: QuotedString Pointer TkIdentifier
test: ^M'Text' tokens: Controlchar QuotedString
test: ^M 'Text' tokens: Controlchar QuotedString
test: ^ M'Text' tokens: Pointer TkIdentifier QuotedString
test: ^Q^E^D tokens: ControlString
请注意,您还可以通过移动lexer::members
部分中的嵌入式代码来使语法保持一点(清晰):
// Place this in the top of your grammar definition
@lexer::members {
private boolean isControlchar() {
// TODO
// - check if there are actually 2 chars ahead and not an EOF
// - perhaps something else than a regex match here
return ((char)input.LA(1) + "" + (char)input.LA(2)).matches("\\w\\W");
}
}
...
Controlchar
: '^' ( {isControlchar()}?=> Alpha
| {$type=Pointer;}
)
| '#' Digitseq
| '#' '$' Hexdigitseq
;