首先:你有一个 WS 规则,将空格字符放在隐藏通道上,但在语法的后面,你有一个 SPACES 规则。鉴于此 SPACES 规则位于 WS 之后并且完全匹配,SPACES 规则将永远不会匹配。
例如,“嗨,你好,你好吗??”需要是单个token,而不是多个token,由这个语法生成。
您无法在当前设置中执行此操作。你可以做的是利用lexical modes。快速演示:
// Must be in a separate file called DemoLexer.g4
lexer grammar DemoLexer;
START_1_TAG : '<%' -> pushMode(IN_TAG);
START_2_TAG : '<<' -> pushMode(IN_TAG);
TEXT : ( ~[<] | '<' ~[<%] )+;
mode IN_TAG;
ID : [A-Za-z_][A-Za-z0-9_]*;
INT_NUMBER : [0-9]+;
END_1_TAG : '%>' -> popMode;
END_2_TAG : '>>' -> popMode;
SPACE : [ \t\r\n] -> channel(HIDDEN);
要测试这个词法分析器,运行这个类:
import org.antlr.v4.runtime.*;
public class Main {
public static void main(String[] args) {
String source = "<%FOO%>FOO BAR<<123>>456 mu!";
DemoLexer lexer = new DemoLexer(CharStreams.fromString(source));
CommonTokenStream tokenStream = new CommonTokenStream(lexer);
tokenStream.fill();
for (Token t : tokenStream.getTokens()) {
System.out.printf("%-20s %s\n", DemoLexer.VOCABULARY.getSymbolicName(t.getType()), t.getText());
}
}
}
将打印:
START_1_TAG <%
ID FOO
END_1_TAG %>
TEXT FOO BAR
START_2_TAG <<
INT_NUMBER 123
END_2_TAG >>
TEXT 456 mu!
EOF <EOF>
在单独的解析器语法中使用您的词法分析器语法,如下所示:
// Must be in a separate file called DemoParser.g4
parser grammar DemoParser;
options {
tokenVocab=DemoLexer;
}
code
: codeBlock* EOF
;
...
编辑
[...] 但我对 TEXT 有点困惑: ( ~[ 规则。你能详细说明它的作用吗?
( ~[<] | '<' ~[<%] )+的细分:
( # start group
~[<] # match any char other than '<'
| # OR
'<' ~[<%] # match a '<' followed by any char other than '<' and '%'
)+ # end group, and repeat it once or more
而且,词汇模式可以被视为语义谓词的替代品吗?
有点。语义谓词更强大:您可以通过纯代码检查您喜欢的任何内容。但是,一个很大的缺点是您在语法中混合了目标特定的代码,而词法模式适用于所有目标。因此,经验法则是尽可能避免使用谓词。