在 antlr 中获取纯文本而不是标记
Getting plain text in antlr instead of tokens
我正在尝试使用 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' ;
除了各种标签,我还需要实施一个规则来获取不在任何标签内的文本。目前的语法似乎工作正常,但由于 'text' 规则属于 Lexer 方面,输入的任何文本都被标记化,我得到一个标记列表,而不是单个字符串标记。 intellij 中的 antlr 分析器还显示了对每个标记的模糊调用。
For example, 'Hi Hello, how are you??' needs to be a single token, instead of multiple tokens, which is generated by this grammar.
我想我可能看错了角度,想知道是否有任何其他方法可以处理 'text' 规则。
首先:你有一个 WS
规则将 space 个字符放在隐藏频道上,但在语法的后面,你有一个 SPACES
规则。鉴于此 SPACES
规则位于 WS
之后且匹配完全相同,因此永远不会匹配 SPACES
规则。
For example, 'Hi Hello, how are you??' needs to be a single token, instead of multiple tokens, which is generated by this grammar.
您不能在当前设置中执行此操作。你能做的就是利用 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);
要测试这个词法分析器语法,运行 这个 class:
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
;
...
编辑
[...] but I am a bit confused on the TEXT : ( ~[<] | '<' ~[<%] )+; rule. can you elaborate what it does a bit further?
( ~[<] | '<' ~[<%] )+
的细分:
( # 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
And, can lexical modes be considered an alternative to semantic predicates?
有点。语义谓词更强大:你可以通过纯代码检查它们里面的任何你喜欢的东西。然而,一个很大的缺点是您在语法中混合了目标特定代码,而词法模式适用于所有目标。因此,经验法则是尽可能避免使用谓词。
我正在尝试使用 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' ;
除了各种标签,我还需要实施一个规则来获取不在任何标签内的文本。目前的语法似乎工作正常,但由于 'text' 规则属于 Lexer 方面,输入的任何文本都被标记化,我得到一个标记列表,而不是单个字符串标记。 intellij 中的 antlr 分析器还显示了对每个标记的模糊调用。
For example, 'Hi Hello, how are you??' needs to be a single token, instead of multiple tokens, which is generated by this grammar.
我想我可能看错了角度,想知道是否有任何其他方法可以处理 'text' 规则。
首先:你有一个 WS
规则将 space 个字符放在隐藏频道上,但在语法的后面,你有一个 SPACES
规则。鉴于此 SPACES
规则位于 WS
之后且匹配完全相同,因此永远不会匹配 SPACES
规则。
For example, 'Hi Hello, how are you??' needs to be a single token, instead of multiple tokens, which is generated by this grammar.
您不能在当前设置中执行此操作。你能做的就是利用 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);
要测试这个词法分析器语法,运行 这个 class:
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
;
...
编辑
[...] but I am a bit confused on the TEXT : ( ~[<] | '<' ~[<%] )+; rule. can you elaborate what it does a bit further?
( ~[<] | '<' ~[<%] )+
的细分:
( # 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
And, can lexical modes be considered an alternative to semantic predicates?
有点。语义谓词更强大:你可以通过纯代码检查它们里面的任何你喜欢的东西。然而,一个很大的缺点是您在语法中混合了目标特定代码,而词法模式适用于所有目标。因此,经验法则是尽可能避免使用谓词。