使解析器忽略行注释,尾随注释除外
Making a parser to ignore a line comment, except a trailing comment
我正在使用 Jison(Javascript 版本的 Bison,非常相似)。
Objective
我想解析输入并获取有效标记(IDENTIFIER
和 trailing comments
)
我的定义
IDENTIFIER
- 一个单词包含字母表
- 一个
line comment
- 以
--
和零个或多个字母开头的整行;或
- 一整行,以一个或多个空格(空格、制表符)开头,后跟
--
和零个或多个字母
- 一个
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 文件如下,我该如何修改它?
Jison/Bison
%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(); $$ = ;}
;
在 (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_state
和 yy_pop_state
来使用开始条件栈。 (在 flex 中,您必须指定 %option stack
才能工作。)
Jison,出于某种原因,只提供了面向堆栈的转换,而this.begin
只是this.pushStack
的别名。因此,与 flex 界面不同的是,您确实应该平衡每个 this.begin
和 this.popStack
(并且可能将 this.begin
更改为 this.pushStack
以减少混淆)。上面的代码,正如所写的那样,做了很多无用的开始条件堆栈推送并且没有弹出,所以它会不必要地使用大量内存,特别是在大输入时。
可以重写上面的示例以更节俭地使用堆栈,但它需要复制大部分模式规则(条件 INITIAL
的一个版本在找到 IDENTIFIER 时推送但不弹出在换行符上,TRAILING
的另一个版本不推入 IDENTIFIER 但确实弹出换行符规则)。这对我来说似乎有点难看,所以我提供了以下替代实现,它使用词法分析器对象中的自定义字段来存储先前遇到的 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'
向词法分析器对象添加自定义字段必须小心;没有文档定义保留哪些名称(或可以使用哪些名称前缀)。
我正在使用 Jison(Javascript 版本的 Bison,非常相似)。
Objective
我想解析输入并获取有效标记(IDENTIFIER
和 trailing comments
)
我的定义
IDENTIFIER
- 一个单词包含字母表
- 一个
line comment
- 以
--
和零个或多个字母开头的整行;或 - 一整行,以一个或多个空格(空格、制表符)开头,后跟
--
和零个或多个字母
- 以
- 一个
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 文件如下,我该如何修改它?
Jison/Bison
%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(); $$ = ;}
;
在 (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_state
和 yy_pop_state
来使用开始条件栈。 (在 flex 中,您必须指定 %option stack
才能工作。)
Jison,出于某种原因,只提供了面向堆栈的转换,而this.begin
只是this.pushStack
的别名。因此,与 flex 界面不同的是,您确实应该平衡每个 this.begin
和 this.popStack
(并且可能将 this.begin
更改为 this.pushStack
以减少混淆)。上面的代码,正如所写的那样,做了很多无用的开始条件堆栈推送并且没有弹出,所以它会不必要地使用大量内存,特别是在大输入时。
可以重写上面的示例以更节俭地使用堆栈,但它需要复制大部分模式规则(条件 INITIAL
的一个版本在找到 IDENTIFIER 时推送但不弹出在换行符上,TRAILING
的另一个版本不推入 IDENTIFIER 但确实弹出换行符规则)。这对我来说似乎有点难看,所以我提供了以下替代实现,它使用词法分析器对象中的自定义字段来存储先前遇到的 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'
向词法分析器对象添加自定义字段必须小心;没有文档定义保留哪些名称(或可以使用哪些名称前缀)。