用于注释的antlr语法,以及标签之间的纯文本
antlr grammar for comments, and plain text between tags
后续问题:
1.I 使用了规则
COMMENT : START_1_TAG START_COMMENT END_1_TAG .*? START_2_TAG END_COMMENT END_2_TAG -> skip;
使用我的词法分析器跳过任何评论。
但是,当我在标签内给出任何 space 时,我得到了不匹配的输入。
我的 Lexer 的相关部分是:
lexer grammar DemoLexer;
START_1_TAG : '<%' -> pushMode(IN_TAG);
START_2_TAG : '<<' -> pushMode(IN_TAG);
COMMENT : START_1_TAG START_COMMENT END_1_TAG .*? START_2_TAG END_COMMENT END_2_TAG -> skip;
TEXT : ( ~[<] | '<' ~[<%] )+;
mode IN_TAG;
START_COMMENT : 'startcomment' ;
END_COMMENT : 'endcomment' ;
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);
我的问题是,<%comment%>hi<%endcomment%>
被正确解析。但是,虽然我的输入是 <% comment %>
或 <% endcomment %>
,标签之间有 spaces,但 COMMENT 规则无法识别它。
它被 COMMENT 规则识别,当我将规则定义为:
COMMENT : START_1_TAG SPACE*? 'commentstart' SPACE*? END_1_TAG .*? START_1_TAG SPACE*? 'commentend' SPACE*? END_1_TAG -> skip;
带有明确的 spaces。
这是处理此问题的正确方法吗?
2.I 有一条规则,我需要标签对中的原始内容。
例如:
这里的token需要是<%startraw%>,<%Hi%>和<%endraw%>
我尝试使用文本规则,但它不起作用,因为它不包含“<%”和“<<”。
我试过了:
在我的解析器中,
rawText : RAW_TAG_START RAW_TEXT RAW_TAG_END ;
在我的词法分析器中,
RAW_TAG_START : '<%' 'startraw' '%>' -> pushMode(RAW_MODE);
RAW_TAG_END : '<%' 'endraw' '%>' -> popMode;
mode RAW_MODE;
RAW_TEXT : .*? ;
出于某种原因,当我尝试使用 intellij antlr 插件解析它时,每当我尝试匹配 rawText 规则时,它似乎会冻结并崩溃。
Is this the proper method to handle this?
不,我会说不是。在这种情况下,它不仅仅是一个普通的评论,而是一个恰好代表评论的常规标签。因此,我会将其视为任何其他标记(在解析器中定义它,而不是在词法分析器中定义)。
For some reason, when I try to parse this with the intellij antlr plugin, it seems to freeze and crash whenever I try to match the rawText rule.
这可能是因为:RAW_TEXT : .*? ;
匹配一个空字符串,并导致词法分析器产生无限量的标记。
我会这样做:
lexer grammar DemoLexer;
START_1_TAG : '<%' -> pushMode(IN_TAG);
START_2_TAG : '<<' -> pushMode(IN_TAG);
TEXT : ( ~[<] | '<' ~[<%] )+;
fragment S : [ \t\r\n];
fragment ID : [A-Za-z_][A-Za-z0-9_]*;
mode IN_TAG;
START_RAW : 'raw' S* '%>' -> pushMode(IN_RAW);
START_COMMENT : 'comment';
END_COMMENT : 'endcomment';
END_ID : 'end' ID;
START_ID : ID;
INT_NUMBER : [0-9]+;
END_1_TAG : '%>' -> popMode;
END_2_TAG : '>>' -> popMode;
SPACE : [ \t\r\n] -> channel(HIDDEN);
mode IN_RAW;
END_RAW : '<%' S* 'endraw' S* '%>' -> popMode, popMode; // pop twice: out of IN_RAW and IN_TAG!
ANY_RAW : . ; // No '+' or '*', just a single token!
一个演示解析器:
parser grammar DemoParser;
options {
tokenVocab=DemoLexer;
}
code
: codeBlock* EOF
;
codeBlock
: TEXT
| tag1Ops
| tag2Ops
;
tag1Ops
: rawTag
| commentTag
| otherTag
;
rawTag
: START_1_TAG START_RAW ANY_RAW* END_RAW
;
commentTag
: START_1_TAG START_COMMENT END_1_TAG TEXT START_1_TAG END_COMMENT END_1_TAG
;
otherTag
: START_1_TAG START_ID END_1_TAG TEXT START_1_TAG END_ID END_1_TAG
;
tag2Ops
: START_2_TAG START_ID END_2_TAG TEXT START_2_TAG END_ID END_2_TAG
;
还有一点主要 class 来测试它:
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.ParseTree;
public class Main {
public static void main(String[] args) {
String source = "aaa <% raw %> RAW <% endraw %> " +
"bbb " +
"<% foo %> FOO <% endfoo %> " +
"ccc " +
"<%comment%> COMMENT <%endcomment%> " +
"ddd";
DemoLexer lexer = new DemoLexer(CharStreams.fromString(source));
DemoParser parser = new DemoParser(new CommonTokenStream(lexer));
ParseTree tree = parser.code();
System.out.println(tree.toStringTree(parser));
}
}
这将打印:
(code
(codeBlock aaa )
(codeBlock (tag1Ops (rawTag <% raw %> R A W <% endraw %>)))
(codeBlock bbb )
(codeBlock (tag1Ops (otherTag <% foo %> FOO <% endfoo %>)))
(codeBlock ccc )
(codeBlock (tag1Ops (commentTag <% comment %> COMMENT <% endcomment %>)))
(codeBlock ddd)
<EOF>)
(为了清楚起见,我添加了一些手动换行符)
而 ANTLR IntelliJ 插件也可以应对:
编辑
如果您在词法分析器中设置 skip
ping 评论(我不会这样做),那么您可以这样做:
lexer grammar DemoLexer;
COMMENT : '<%' S* 'comment' S* '%>' .*? '<%' S* 'endcomment' S* '%>' -> skip;
START_1_TAG : '<%' -> pushMode(IN_TAG);
START_2_TAG : '<<' -> pushMode(IN_TAG);
TEXT : ( ~[<] | '<' ~[<%] )+;
编辑二
如果您测得 ANY_RAW
对性能有显着影响,您可以这样做:
mode IN_RAW;
END_RAW : '<%' S* 'endraw' S* '%>' -> popMode, popMode;
SAFE_RAW : ( ~[<] | '<' ~[<%] )+
// Fall through to match "<" from "<% ..." that are not matched by END_RAW
OTHER_RAW : . ;
后续问题:
1.I 使用了规则
COMMENT : START_1_TAG START_COMMENT END_1_TAG .*? START_2_TAG END_COMMENT END_2_TAG -> skip;
使用我的词法分析器跳过任何评论。 但是,当我在标签内给出任何 space 时,我得到了不匹配的输入。
我的 Lexer 的相关部分是:
lexer grammar DemoLexer;
START_1_TAG : '<%' -> pushMode(IN_TAG);
START_2_TAG : '<<' -> pushMode(IN_TAG);
COMMENT : START_1_TAG START_COMMENT END_1_TAG .*? START_2_TAG END_COMMENT END_2_TAG -> skip;
TEXT : ( ~[<] | '<' ~[<%] )+;
mode IN_TAG;
START_COMMENT : 'startcomment' ;
END_COMMENT : 'endcomment' ;
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);
我的问题是,<%comment%>hi<%endcomment%>
被正确解析。但是,虽然我的输入是 <% comment %>
或 <% endcomment %>
,标签之间有 spaces,但 COMMENT 规则无法识别它。
它被 COMMENT 规则识别,当我将规则定义为:
COMMENT : START_1_TAG SPACE*? 'commentstart' SPACE*? END_1_TAG .*? START_1_TAG SPACE*? 'commentend' SPACE*? END_1_TAG -> skip;
带有明确的 spaces。
这是处理此问题的正确方法吗?
2.I 有一条规则,我需要标签对中的原始内容。 例如:
这里的token需要是<%startraw%>,<%Hi%>和<%endraw%>
我尝试使用文本规则,但它不起作用,因为它不包含“<%”和“<<”。
我试过了:
在我的解析器中,
rawText : RAW_TAG_START RAW_TEXT RAW_TAG_END ;
在我的词法分析器中,
RAW_TAG_START : '<%' 'startraw' '%>' -> pushMode(RAW_MODE);
RAW_TAG_END : '<%' 'endraw' '%>' -> popMode;
mode RAW_MODE;
RAW_TEXT : .*? ;
出于某种原因,当我尝试使用 intellij antlr 插件解析它时,每当我尝试匹配 rawText 规则时,它似乎会冻结并崩溃。
Is this the proper method to handle this?
不,我会说不是。在这种情况下,它不仅仅是一个普通的评论,而是一个恰好代表评论的常规标签。因此,我会将其视为任何其他标记(在解析器中定义它,而不是在词法分析器中定义)。
For some reason, when I try to parse this with the intellij antlr plugin, it seems to freeze and crash whenever I try to match the rawText rule.
这可能是因为:RAW_TEXT : .*? ;
匹配一个空字符串,并导致词法分析器产生无限量的标记。
我会这样做:
lexer grammar DemoLexer;
START_1_TAG : '<%' -> pushMode(IN_TAG);
START_2_TAG : '<<' -> pushMode(IN_TAG);
TEXT : ( ~[<] | '<' ~[<%] )+;
fragment S : [ \t\r\n];
fragment ID : [A-Za-z_][A-Za-z0-9_]*;
mode IN_TAG;
START_RAW : 'raw' S* '%>' -> pushMode(IN_RAW);
START_COMMENT : 'comment';
END_COMMENT : 'endcomment';
END_ID : 'end' ID;
START_ID : ID;
INT_NUMBER : [0-9]+;
END_1_TAG : '%>' -> popMode;
END_2_TAG : '>>' -> popMode;
SPACE : [ \t\r\n] -> channel(HIDDEN);
mode IN_RAW;
END_RAW : '<%' S* 'endraw' S* '%>' -> popMode, popMode; // pop twice: out of IN_RAW and IN_TAG!
ANY_RAW : . ; // No '+' or '*', just a single token!
一个演示解析器:
parser grammar DemoParser;
options {
tokenVocab=DemoLexer;
}
code
: codeBlock* EOF
;
codeBlock
: TEXT
| tag1Ops
| tag2Ops
;
tag1Ops
: rawTag
| commentTag
| otherTag
;
rawTag
: START_1_TAG START_RAW ANY_RAW* END_RAW
;
commentTag
: START_1_TAG START_COMMENT END_1_TAG TEXT START_1_TAG END_COMMENT END_1_TAG
;
otherTag
: START_1_TAG START_ID END_1_TAG TEXT START_1_TAG END_ID END_1_TAG
;
tag2Ops
: START_2_TAG START_ID END_2_TAG TEXT START_2_TAG END_ID END_2_TAG
;
还有一点主要 class 来测试它:
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.ParseTree;
public class Main {
public static void main(String[] args) {
String source = "aaa <% raw %> RAW <% endraw %> " +
"bbb " +
"<% foo %> FOO <% endfoo %> " +
"ccc " +
"<%comment%> COMMENT <%endcomment%> " +
"ddd";
DemoLexer lexer = new DemoLexer(CharStreams.fromString(source));
DemoParser parser = new DemoParser(new CommonTokenStream(lexer));
ParseTree tree = parser.code();
System.out.println(tree.toStringTree(parser));
}
}
这将打印:
(code
(codeBlock aaa )
(codeBlock (tag1Ops (rawTag <% raw %> R A W <% endraw %>)))
(codeBlock bbb )
(codeBlock (tag1Ops (otherTag <% foo %> FOO <% endfoo %>)))
(codeBlock ccc )
(codeBlock (tag1Ops (commentTag <% comment %> COMMENT <% endcomment %>)))
(codeBlock ddd)
<EOF>)
(为了清楚起见,我添加了一些手动换行符)
而 ANTLR IntelliJ 插件也可以应对:
编辑
如果您在词法分析器中设置 skip
ping 评论(我不会这样做),那么您可以这样做:
lexer grammar DemoLexer;
COMMENT : '<%' S* 'comment' S* '%>' .*? '<%' S* 'endcomment' S* '%>' -> skip;
START_1_TAG : '<%' -> pushMode(IN_TAG);
START_2_TAG : '<<' -> pushMode(IN_TAG);
TEXT : ( ~[<] | '<' ~[<%] )+;
编辑二
如果您测得 ANY_RAW
对性能有显着影响,您可以这样做:
mode IN_RAW;
END_RAW : '<%' S* 'endraw' S* '%>' -> popMode, popMode;
SAFE_RAW : ( ~[<] | '<' ~[<%] )+
// Fall through to match "<" from "<% ..." that are not matched by END_RAW
OTHER_RAW : . ;