制作解析器以忽略行注释,尾随注释除外

  • 本文关键字:注释 bison jison
  • 更新时间 :
  • 英文 :


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

目的

我想解析输入并获取有效令牌(IDENTIFIERtrailing comments(

我的定义

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

输入

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

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

输出(电流,错误(

I
want

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

输出(预期(

I
want
-- not to ignore this

鉴于Jison/Bisnon文件如下,我该如何按预期修改它?

吉森/野牛

%lex
%%
s+         /* skip whitespace */
'--'.*      /* skip comment */
[a-zA-Z]+   return 'IDENTIFIER'
<<EOF>>     return 'EOF'
.           /* skip the others */
/lex
%start expressions
%%
expressions
: expressions EOF
| expressions expression
| expression
{;}
;
expression
: 'IDENTIFIER'
{console.log($1); $$ = $1;}
;

在 (f(lex 中,我只是使用以^开头的规则来强制它只在一行的开头匹配,但 jison 不会以这种方式解释插入符号,无论如何,您都不想将初始注释限制为行的精确开头,而是限制为行上的第一个非空格标记。(我不清楚您希望如何处理标识符以外的令牌。我忽略了这个问题。请参阅以三个问号开头的注释。

一种简单的方法是使用"启动条件"(参见 Jison 文档中的示例(,如下所示:(但请参阅下面的注释以获得更好的解决方案(

%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.popStack来平衡每个this.begin(并且可能会将this.begin更改为this.pushStack以减少混淆(。上面的代码,正如所写的,做了许多无用的启动条件堆栈推送,没有弹出,所以它会不必要地使用大量内存,特别是在大输入上。

上面的示例可以重写以更节俭地使用堆栈,但它需要复制大多数模式规则(一个用于条件INITIAL的版本,当找到 IDENTIFIER 但不在换行符上弹出时推送,另一个版本用于不推送 IDENTIFIER 但会在换行符规则中弹出的TRAILING(。这对我来说似乎有点难看,所以我提供了以下替代实现,它使用词法分析器对象中的自定义字段来存储以前遇到的 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'

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

相关内容

  • 没有找到相关文章

最新更新