我正在尝试使用antlr创建解析器。我的语法如下。
code : codeBlock* EOF;
codeBlock
: text
| tag1Ops
| tag2Ops
;
tag1Ops: START_1_TAG ID END_2_TAG ;
tag2Ops: START_2_TAG ID END_2_TAG ;
text: ~(START_1_TAG|START_2_TAG)+;
START_1_TAG : '<%' ;
END_1_TAG : '%>' ;
START_2_TAG : '<<';
END_2_TAG : '>>' ;
ID : [A-Za-z_][A-Za-z0-9_]*;
INT_NUMBER: [0-9]+;
WS : ( ' ' | '\n' | '\r' | '\t')+ -> channel(HIDDEN);
SPACES: SPACE+;
ANY_CHAR : .;
fragment SPACE : ' ' | '\r' | '\n' | '\t' ;
除了各种标签之外,我还需要实现一个规则来获取不在任何标签内的文本。对于当前的语法来说,事情似乎工作得很好,但是因为&#39;文本&#39;规则属于Lexer方面,输入的任何文本都是标记化的,我得到一个令牌列表,而不是单个字符串令牌。 intellij中的antlr分析器还显示每个令牌的模糊调用。
例如,&#39;你好,你好吗?&#39;需要是一个令牌,而不是由这个语法生成的多个令牌。
我想我可能会看错了角度,并且想知道是否还有其他办法来处理这些文字&#39;规则。
答案 0 :(得分:1)
首先:你有一个WS
规则将空格字符放在隐藏的频道上,但在语法的后面,你有一个SPACES
规则。鉴于此SPACES
规则位于WS
之后且匹配完全相同,SPACES
规则将永远不会匹配。
例如,&#39;你好,你好吗?&#39;需要是一个令牌,而不是由这个语法生成的多个令牌。
您无法在当前设置中执行此操作。你可以做的是利用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感到有点困惑:(〜[&lt;] |&#39;&lt;&#;;#&lt;%]} +; 规则。你能详细说明它的作用吗?
明细( ~[<] | '<' ~[<%] )+
:
( # 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
而且,词汇模式可以被视为语义谓词的替代吗?
排序。语义谓词更强大:你可以通过简单的代码检查你喜欢的内容。但是,一个很大的缺点是你在语法中混合了特定于目标的代码,而词汇模式则适用于所有目标。因此,经验法则是尽可能避免使用谓词。