Jison 解析器在第一条规则后停止
Jison parser stops after first rule
我有一个简单的文件格式,我想用 jison 解析器生成器解析它。该文件可以包含任意顺序和数量的多个表达式。这是解析器的 jison 文件:
/* lexical grammar */
%lex
%%
\s+ /* skip whitespace */
\"(\.|[^"])*\" return 'STRING'
File\s*Version\s*\: return 'FILEVERSION'
[0-9]+("."[0-9]+)?\b return 'NUMBER'
<<EOF>> return 'EOF'
. return 'INVALID'
/lex
%start expressions
%% /* language grammar */
expressions
: EOF
| e expressions EOF
;
e
: STRING
| FILEID
;
FILEID
: FILEVERSION NUMBER { return + ; }
;
为简单起见,我将文件缩短为仅包含字符串和文件 ID 表达式。
我的问题是,生成的解析器似乎只能识别一个或两个完整的表达式,如果第二个表达式仅包含一个标记(如字符串)。例如:
File Version: 1.0
将被解析,或者
File Version: 1.0
"My String"
也会被解析,但是对于
File Version: 1.0
"My String"
"Not parsed string"
不会解析最后一个字符串。
我已经用 jison debugger and on the jison page 本身尝试了这段代码,但两个页面都显示了相同的结果。
我对这个问题的建议是:
- 一些词法分析器错误(正则表达式)
- 一些语法错误(左右递归)
- 解析器中缺少某些操作(类似于 { $$ = $1;} )
- 其他一些 bison/jison 我缺少的魔法
我不是那个 ebnf-parser-guru,所以请让您的回答尽可能简单。
眼前的问题是您 return
来自 FILEID
制作。 return
returns,因此解析以 returned 值终止。通常,语义规则应该通过分配给变量 $$
来提供它们的结果。 (对于右侧只有一个符号的 "unit rules" 来说,这不是必需的;在执行操作之前,解析器会执行 $$ =
,所以如果这是你想要的,你可以只留下按照你的两个 FILEID
规则执行。)
此外,您的 expressions
产品不会对 </code> 做任何事情,所以即使您解决了第一个问题,您仍然只会在结果中看到一个 <code>e
.
您的 expressions
生成也不正确,因为除了基本情况中的 EOF 之外,每个 e
还需要一个 EOF
标记。考虑作品的运作方式:
expressions -> e expressions EOF
-> e e expressions EOF EOF
-> e e e expressions EOF EOF EOF
-> e e e EOF EOF EOF EOF
就我个人而言,我建议使用左递归而不是右递归。像 jison 这样的自下而上的解析器更喜欢左递归,它通常会导致更自然的语义规则,就像在这种情况下一样。
最后,当你真正到达输入的末尾时,你需要return最终值。在jison中,这通常需要一个显式的开始规则,其语义动作是return
.
所以,考虑到所有这些,让我们试试这个:(我更改了一些非终端的名称,并小写了 FILEID
因为通常使用小写来表示非终端-终端和终端的大写)
%start prog
%%
prog : exprs EOF { return ; }
;
exprs : { $$ = []; }
| exprs expr { $$.push(); }
;
expr : file_id
| STRING
;
file_id: FILEVERSION NUMBER { $$ = + ; }
;
关于匹配字符串的正则表达式的注意事项:
\"(\.|[^"])*\" return 'STRING'
虽然它显然适用于 javascript(主要是;见下文),但它会在 flex(或 Posix 兼容的正则表达式库)中出现错误。它主要适用于 javascript,因为 javascript 正则表达式交替运算符 |
是 有序的 ;如果第一个选项匹配,则永远不会尝试第二个选项,除非模式的其余部分不匹配(在这种情况下,将触发错误)。
但在 (f)lex 中,交替运算符会注意到所有匹配的备选方案,并最终选择最长的可能匹配项。结果是匹配"\"..."
时,flex会匹配token直到第三个引用,通过使用[^"]
匹配第一个\ 然后 \.
来匹配 \"。这样它就可以继续寻找收盘价。
编写正则表达式很容易,以便它可以使用任何一种语义,我强烈建议您这样做,以防您想要迁移到不同的解析器生成器,只需确保 \ 与 [^"]
不匹配:
\"(\.|[^\"])*\" return 'STRING'
此更改还将修复细微错误,即使在 javascript 中,如果 "\"
是输入中的最后一个字符串,它也会被视为有效的字符串标记。在这种情况下,javascript 将首先使用 \.
来匹配 \",但是一旦匹配,它就不会找到任何收盘价。然后它会回溯并尝试与 [^"]
匹配,这将匹配 \,然后将报价识别为收盘价。
我有一个简单的文件格式,我想用 jison 解析器生成器解析它。该文件可以包含任意顺序和数量的多个表达式。这是解析器的 jison 文件:
/* lexical grammar */
%lex
%%
\s+ /* skip whitespace */
\"(\.|[^"])*\" return 'STRING'
File\s*Version\s*\: return 'FILEVERSION'
[0-9]+("."[0-9]+)?\b return 'NUMBER'
<<EOF>> return 'EOF'
. return 'INVALID'
/lex
%start expressions
%% /* language grammar */
expressions
: EOF
| e expressions EOF
;
e
: STRING
| FILEID
;
FILEID
: FILEVERSION NUMBER { return + ; }
;
为简单起见,我将文件缩短为仅包含字符串和文件 ID 表达式。
我的问题是,生成的解析器似乎只能识别一个或两个完整的表达式,如果第二个表达式仅包含一个标记(如字符串)。例如:
File Version: 1.0
将被解析,或者
File Version: 1.0 "My String"
也会被解析,但是对于
File Version: 1.0 "My String" "Not parsed string"
不会解析最后一个字符串。
我已经用 jison debugger and on the jison page 本身尝试了这段代码,但两个页面都显示了相同的结果。
我对这个问题的建议是:
- 一些词法分析器错误(正则表达式)
- 一些语法错误(左右递归)
- 解析器中缺少某些操作(类似于 { $$ = $1;} )
- 其他一些 bison/jison 我缺少的魔法
我不是那个 ebnf-parser-guru,所以请让您的回答尽可能简单。
眼前的问题是您 return
来自 FILEID
制作。 return
returns,因此解析以 returned 值终止。通常,语义规则应该通过分配给变量 $$
来提供它们的结果。 (对于右侧只有一个符号的 "unit rules" 来说,这不是必需的;在执行操作之前,解析器会执行 $$ =
,所以如果这是你想要的,你可以只留下按照你的两个 FILEID
规则执行。)
此外,您的 expressions
产品不会对 </code> 做任何事情,所以即使您解决了第一个问题,您仍然只会在结果中看到一个 <code>e
.
您的 expressions
生成也不正确,因为除了基本情况中的 EOF 之外,每个 e
还需要一个 EOF
标记。考虑作品的运作方式:
expressions -> e expressions EOF
-> e e expressions EOF EOF
-> e e e expressions EOF EOF EOF
-> e e e EOF EOF EOF EOF
就我个人而言,我建议使用左递归而不是右递归。像 jison 这样的自下而上的解析器更喜欢左递归,它通常会导致更自然的语义规则,就像在这种情况下一样。
最后,当你真正到达输入的末尾时,你需要return最终值。在jison中,这通常需要一个显式的开始规则,其语义动作是return
.
所以,考虑到所有这些,让我们试试这个:(我更改了一些非终端的名称,并小写了 FILEID
因为通常使用小写来表示非终端-终端和终端的大写)
%start prog
%%
prog : exprs EOF { return ; }
;
exprs : { $$ = []; }
| exprs expr { $$.push(); }
;
expr : file_id
| STRING
;
file_id: FILEVERSION NUMBER { $$ = + ; }
;
关于匹配字符串的正则表达式的注意事项:
\"(\.|[^"])*\" return 'STRING'
虽然它显然适用于 javascript(主要是;见下文),但它会在 flex(或 Posix 兼容的正则表达式库)中出现错误。它主要适用于 javascript,因为 javascript 正则表达式交替运算符 |
是 有序的 ;如果第一个选项匹配,则永远不会尝试第二个选项,除非模式的其余部分不匹配(在这种情况下,将触发错误)。
但在 (f)lex 中,交替运算符会注意到所有匹配的备选方案,并最终选择最长的可能匹配项。结果是匹配"\"..."
时,flex会匹配token直到第三个引用,通过使用[^"]
匹配第一个\ 然后 \.
来匹配 \"。这样它就可以继续寻找收盘价。
编写正则表达式很容易,以便它可以使用任何一种语义,我强烈建议您这样做,以防您想要迁移到不同的解析器生成器,只需确保 \ 与 [^"]
不匹配:
\"(\.|[^\"])*\" return 'STRING'
此更改还将修复细微错误,即使在 javascript 中,如果 "\"
是输入中的最后一个字符串,它也会被视为有效的字符串标记。在这种情况下,javascript 将首先使用 \.
来匹配 \",但是一旦匹配,它就不会找到任何收盘价。然后它会回溯并尝试与 [^"]
匹配,这将匹配 \,然后将报价识别为收盘价。