在Mathematica中,评论以(*
开头,以*)
结尾,评论可以嵌套。我目前使用JFlex扫描评论的方法包含以下代码
%xstate IN_COMMENT
"(*" { yypushstate(IN_COMMENT); return MathematicaElementTypes.COMMENT;}
<IN_COMMENT> {
"(*" {yypushstate(IN_COMMENT); return MathematicaElementTypes.COMMENT;}
[^\*\)\(]* {return MathematicaElementTypes.COMMENT;}
"*)" {yypopstate(); return MathematicaElementTypes.COMMENT;}
[\*\)\(] {return MathematicaElementTypes.COMMENT;}
. {return MathematicaElementTypes.BAD_CHARACTER;}
}
方法yypushstate
和yypopstate
定义为
private final LinkedList<Integer> states = new LinkedList();
private void yypushstate(int state) {
states.addFirst(yystate());
yybegin(state);
}
private void yypopstate() {
final int state = states.removeFirst();
yybegin(state);
}
让我有机会跟踪我处理的评论级别。
不幸的是,这导致一个评论有几个COMMENT
个令牌,因为我必须匹配嵌套的评论开始和评论结束。
问题:JFlex可以将其API与yypushback
或advance()
等方法一起使用,在整个评论范围内只返回一个令牌,即使评论是嵌套的吗?
答案 0 :(得分:4)
似乎赏金是不必要的,因为解决方案很简单,我只是没有考虑它。让我解释。扫描简单的嵌套注释时
(* (*..*) *)
我必须跟踪,我看到多少个开放评论标记,以便我最后,在最后一个真正的结束评论中可以将整个评论作为一个标记返回。
我没有意识到的是,当JFlex匹配时,不需要告知提前到下一部分。经过仔细审查后,我发现这是explained here,但有些隐藏在我不关心的部分中:
因为我们还没有向解析器返回值,所以我们的扫描仪会立即进行。
因此,flex
文件中的规则就像这样
[^\(\*\)]+ { }
读取所有可能是评论开头/结尾的字符,并且不执行任何操作但它会前进到下一个标记。
这意味着我可以简单地执行以下操作。在YYINITIAL
状态中,我有一个匹配开头注释的规则,但它没有做任何其他操作,然后将词法分析器切换到IN_COMMENT
状态。特别是,它不会返回任何令牌:
{CommentStart} { yypushstate(IN_COMMENT);}
现在,我们处于IN_COMMENT
状态,在那里,我也是这样做的。我吃掉了所有的角色,但从未退回过令牌。当我点击一个新的开场评论时,我小心翼翼地把它推到一个堆栈但什么都不做。只有当我点击最后结束评论时,我知道我已离开IN_COMMENT
状态,这是唯一的一点,我最终会返回令牌。让我们来看看规则:
<IN_COMMENT> {
{CommentStart} { yypushstate(IN_COMMENT);}
[^\(\*\)]+ { }
{CommentEnd} { yypopstate();
if(yystate() != IN_COMMENT)
return MathematicaElementTypes.COMMENT_CONTENT;
}
[\*\)\(] { }
. { return MathematicaElementTypes.BAD_CHARACTER; }
}
那就是它。现在,无论您的注释嵌套多深,您都将获得一个包含整个注释的令牌。
现在,我感到很尴尬,我很抱歉这么简单的问题。
如果您正在做这样的事情,您必须记住,只有在您点击正确的结束时才会返回一个令牌&#34;字符&#34;。因此,你绝对应该制定一个捕获文件结尾的规则。在IDEA中,默认行为只是返回注释标记,因此您需要另一行(有用与否,我想优雅地结束):
<<EOF>> { yyclearstack(); yybegin(YYINITIAL);
return MathematicaElementTypes.COMMENT;}
答案 1 :(得分:2)
当我首先写下答案时,我甚至没有意识到现有的答案之一就是提问者本身。另一方面,我很少在相当小的SO lex社区找到赏金。因此,在我看来,有必要学习足够的Java和jflex来编写样本:
/* JFlex scanner: to recognize nested comments in Mathematica style
*/
%%
%{
/* counter for open (nested) comments */
int open = 0;
%}
%state IN_COMMENT
%%
/* any state */
"(*" { if (!open++) yybegin(IN_COMMENT); }
"*)" {
if (open) {
if (!--open) {
yybegin(YYINITIAL);
return MathematicaElementTypes.COMMENT;
}
} else {
/* or return MathematicaElementTypes.BAD_CHARACTER;
/* or: throw new Error("'*)' without '(*'!"); */
}
}
<IN_COMMENT> {
. |
\n { }
}
<<EOF>> {
if (open) {
/* This is obsolete if the scanner is instanced new for
* each invocation.
*/
open = 0; yybegin(IN_COMMENT);
/* Notify about syntax error, e.g. */
throw new Error("Premature end of file! ("
+ open + " open comments not closed.)");
}
return MathematicaElementTypes.EOF; /* just a guess */
}
可能存在拼写错误和愚蠢的错误,尽管我试图小心并尽力而为。
作为“概念证明”,我将原始实现保留在此处,这是通过flex和C / C ++完成的。
此扫描仪
printf()
)我的解决方案基本上是基于以下事实:弹性规则可能以break
或return
结束。因此,只有匹配模式的规则才能关闭最外面的注释,才会返回令牌。评论中的内容只是“记录”在缓冲区中 - 在我的例子中是std::string
。
(AFAIK,string
甚至是Java中的内置类型。因此,我决定将C和C ++混合使用,而我通常不会这样做。)
我的来源scan-nested-comments.l
:
%{
#include <cstdio>
#include <string>
// counter for open (nested) comments
static int open = 0;
// buffer for collected comments
static std::string comment;
%}
/* make never interactive (prevent usage of certain C functions) */
%option never-interactive
/* force lexer to process 8 bit ASCIIs (unsigned characters) */
%option 8bit
/* prevent usage of yywrap */
%option noyywrap
%s IN_COMMENT
%%
"(*" {
if (!open++) BEGIN(IN_COMMENT);
comment += "(*";
}
"*)" {
if (open) {
comment += "*)";
if (!--open) {
BEGIN(INITIAL);
printf("EMIT TOKEN COMMENT(lexem: '%s')\n", comment.c_str());
comment.clear();
}
} else {
printf("ERROR: '*)' without '(*'!\n");
}
}
<IN_COMMENT>{
. |
"\n" { comment += *yytext; }
}
<<EOF>> {
if (open) {
printf("ERROR: Premature end of file!\n"
"(%d open comments not closed.)\n", open);
return 1;
}
return 0;
}
%%
int main(int argc, char **argv)
{
if (argc > 1) {
yyin = fopen(argv[1], "r");
if (!yyin) {
printf("Cannot open file '%s'!\n", argv[1]);
return 1;
}
} else yyin = stdin;
return yylex();
}
我在Windows 10(64位)的cygwin中用flex和g ++编译它:
$ flex -oscan-nested-comments.cc scan-nested-comments.l ; g++ -o scan-nested-comments scan-nested-comments.cc
scan-nested-comments.cc:398:0: warning: "yywrap" redefined
^
scan-nested-comments.cc:74:0: note: this is the location of the previous definition
^
$
由%option noyywrap
显示警告。我想这并不意味着任何伤害,可以忽略。
现在,我做了一些测试:
$ cat >good-text.txt <<EOF
> Test for nested comments.
> (* a comment *)
> (* a (* nested *) comment *)
> No comment.
> (* a
> (* nested
> (* multiline *)
> *)
> comment *)
> End of file.
> EOF
$ cat good-text | ./scan-nested-comments
Test for nested comments.
EMIT TOKEN COMMENT(lexem: '(* a comment *)')
EMIT TOKEN COMMENT(lexem: '(* a (* nested *) comment *)')
No comment.
EMIT TOKEN COMMENT(lexem: '(* a
(* nested
(* multiline *)
*)
comment *)')
End of file.
$ cat >bad-text-1.txt <<EOF
> Test for wrong comment.
> (* a comment *)
> with wrong nesting *)
> End of file.
> EOF
$ cat >bad-text-1.txt | ./scan-nested-comments
Test for wrong comment.
EMIT TOKEN COMMENT(lexem: '(* a comment *)')
with wrong nesting ERROR: '*)' without '(*'!
End of file.
$ cat >bad-text-2.txt <<EOF
> Test for wrong comment.
> (* a comment
> which is not closed.
> End of file.
> EOF
$ cat >bad-text-2.txt | ./scan-nested-comments
Test for wrong comment.
ERROR: Premature end of file!
(1 open comments not closed.)
$
答案 2 :(得分:0)
Java传统注释在样本语法中用
定义TraditionalComment = "/*" [^*] ~"*/" | "/*" "*"+ "/"
我认为这个表达式也适用于Mathematica评论。