7

在 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;}
}

其中方法yypushstateyypopstate定义为

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 与诸如yypushbackor之类的方法一起使用,advance()以在整个评论范围内准确返回一个标记,即使评论是嵌套的?

4

3 回答 3

6

似乎不需要赏金,因为解决方案非常简单,我只是没有考虑它。让我解释。扫描简单的嵌套评论时

(* (*..*) *)

我必须跟踪我看到了多少个开放评论标记,以便我最终在最后一个真正的结束评论上可以将整个评论作为一个标记返回。

我没有意识到的是,当 JFlex 匹配某些东西时,不需要告诉它前进到下一部分。经过仔细审查后,我看到这在此处进行了解释,但有些隐藏在我不关心的部分中:

因为我们还没有向解析器返回值,所以我们的扫描器立即继续。

因此,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; }
}

而已。现在,无论您的评论嵌套多深,您总是会得到一个包含整个评论的标记。

现在,我很尴尬,我很抱歉这么简单的问题。

最后说明

如果你正在做这样的事情,你必须记住,当你点击正确的结束“字符”时,你只会返回一个标记。因此,您绝对应该制定一个捕捉文件结尾的规则。在 IDEA 中,默认行为是只返回注释标记,所以你需要另一行(有用与否,我想优雅地结束):

    <<EOF>>  { yyclearstack(); yybegin(YYINITIAL);
               return MathematicaElementTypes.COMMENT;}
于 2017-05-13T05:00:11.607 回答
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我的解决方案本质上是基于 flex 规则可能以or结尾的事实return。因此,只有在模式的规则与关闭最外层的注释匹配后,才会返回令牌。评论中的内容只是简单地“记录”在缓冲区中——在我的例子中是一个std::string. (AFAIKstring甚至是 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.)

$
于 2017-05-17T12:52:23.077 回答
-1

Java 传统注释在示例语法中定义为

TraditionalComment   = "/*" [^*] ~"*/" | "/*" "*"+ "/"

我想这个表达式也应该适用于 Mathematica 评论。

于 2014-09-16T18:57:06.677 回答