使解析器忽略行注释,除了尾随注释

时间:2018-06-01 14:29:24

标签: bison jison

我正在使用Jison(Bison的Javascript版本,非常相似)。

目的

我想解析一个输入并获得有效的令牌(KapacitorIDENTIFIER

我的定义

  • trailing comments
    • 一个单词包含字母
  • A IDENTIFIER
    1. line comment和零个或多个字母开头的整行;或
    2. 一行,以一个或多个空格(空格,制表符)开头,后跟--和零个或多个字母
  • A --
    • trailing comment开头但跟随--的字符串 为简单起见,我们假设我有一个要解析的输入文件:

输入

IDENTIFIER

为了解析上述内容,我编写了Jison / Bison文件(见下文),但它不完整,只解析非评论内容,即:

输出(当前,错误)

I want -- not to ignore this
-- This must be ignored

但是,我希望解析器将尾随注释作为单个有效字符串,即:

输出(预期)

I
want

鉴于下面的Jison / Bisnon文件,如何按照我的意图修改它?

Jison /野牛

I
want
-- not to ignore this

1 个答案:

答案 0 :(得分:2)

在(f)lex中,我只是使用以^开头的规则强制它仅在一行的开头匹配,但是jison并没有这样解释插入符号,并且无论如何你真的不想将初始注释限制在行的精确开始,而是限制行上的第一个非空白标记。 (我不清楚你如何处理除标识符以外的令牌。我已经忽略了这个问题。请参阅注释以三个问号开头。)

一种简单的方法是使用“开始条件”(参见Jison的documentation中的示例),如下所示:(但请参阅下面的注释以获得更好的解决方案)

%lex
%s TRAILING
%%

\n                  this.begin('INITIAL')
\s                  /* skip whitespace */
<TRAILING>'--'.*    return 'IDENTIFIER'; /* or whatever */
<INITIAL>'--'.*     /* skip initial comments */
[a-zA-Z]+           this.begin('TRAILING'); return 'IDENTIFIER'
.                   /* ??? this.begin('TRAILING'); */ /* skip the others */
<<EOF>>             return 'EOF'

注意: (在我记住这个Jison启动条件后,添加了。)上述代码受(f)lex的启动条件界面的影响很大。 jison和flex(但不是lex)都允许一堆启动条件,这在上下文嵌套时很方便。如上例所示,上下文并不总是嵌套;有时在状态机中从一个切换到另一个更方便。 BEGIN(f)lex宏就是这样做的; flex还提供yy_push_stateyy_pop_state来使用开始条件堆栈。 (在flex中,您必须指定%option stack才能使其正常工作。)

Jison,出于某种原因,只有提供面向堆栈的转换,而this.begin只是this.pushStack的别名。因此,与flex界面不同,您实际上应该平衡每个this.beginthis.popStack(并且可能会将this.begin更改为this.pushStack以减少混淆。上面的代码,如上所述,做了很多无用的启动条件堆栈推送和没有弹出,因此它将不必要地使用大量内存,特别是在大输入时。

上面的例子可以被重写以更节俭地使用堆栈,但它需要复制大多数模式规则(条件INITIAL的一个版本,当找到IDENTIFIER但不在新行上弹出时推送,TRAILING的另一个版本,它不会推入IDENTIFIER但会在换行规则中弹出)。这对我来说似乎有点难看,所以我提供了以下替代实现,它使用lexer对象中的自定义字段来存储先前遇到的IDENTIFIER的行号,并且只有在它们位于同一行时才向输出添加注释:

%lex
%%

\s+             /* skip whitespace */
'--'.*          if (yylloc.first_line == this.last_id_line) return 'IDENTIFIER';
[a-zA-Z]+       this.last_id_line = yylloc.first_line; return 'IDENTIFIER'
.               /* skip the others */
<<EOF>>         return 'EOF'

必须小心地将自定义字段添加到词法分析器对象;没有文档定义保留哪些名称(或可以使用哪些名称前缀)。