背景
某项目编译系统关键字, 在lex中被强制识别为某 token,
后来发现, 而此关键字 也会出现在其他语法中, 但是被lex强制识别为了 token, 导致编译失败!
如:
某系统的语法识别, 将文本中的 uint32,int32, uint64, int64, ipv4. ipv6. time, mac 等识别为数据类型,
ipv4 一开始被lex强制定义为某数据的数据解码器,
后期发现在非数据解码器的地方, 需要将 ipv4 作为一种数据类型。
结果就是, ipv4 无法出现在预期的地方, 总是被lex识别为 数据解码器。
类似的 ipv6, time 关键字也是一样的现象。
Bison 冲突
bison 发现了4处 冲突
1 2 3 4 [root@localhost src]# bison -v --report=all -d sdt_grammar.y [root@localhost src]# bison -v -d sdt_grammar.y # 或者 sdt_grammar.y: warning: 4 shift/reduce conflicts [-Wconflicts-sr] [root@localhost src]#
–report=all 可以得到更多信息
xxx.output
打开 sdt_grammar.output
这里看到了完整的 BNF 语法描述
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 Grammar 0 $accept: sdt $end 1 sdt: mode rule_header payload_matcher rule_action 2 | mode rule_header payload_matcher '$' rule_body rule_action 3 | COMMENT_LINE 4 mode: IDENTIFIER 5 payload_matcher: %empty 6 | '#' payload_and_expression 7 payload_and_expression: payload_match_expression 8 | payload_match_expression ',' payload_and_expression 9 payload_match_expression: payload_expression 10 | '!' payload_expression 11 | NUMBER ':' payload_expression 12 | NUMBER ':' '!' payload_expression 13 | IDENTIFIER '=' NUMBER 14 payload_expression: payload_expression_atomic 15 | payload_expression payload_expression_atomic 16 | payload_expression payload_expression_atomic_not 17 payload_expression_atomic_not: '!' IDENTIFIER 18 | '!' hex_payload 19 payload_expression_atomic: IDENTIFIER 20 | hex_payload 21 hex_payload: HEX_TEXT 22 rule_body: expression ';' 23 | logical_and_expression ';' 24 rule_action: %empty 25 | rule_action IDENTIFIER LP RP ';' 26 | rule_action ACTION LP rule_action_args RP ';' 27 | rule_action ACTION_survive LP action_survive_args RP ';' 28 rule_action_args: rule_action_arg 29 | rule_action_arg ',' rule_action_args 30 rule_action_arg: NUMBER 31 | FLOAT 32 | IDENTIFIER 33 | QUOTE_TEXT 34 | dyn_mask_hex 35 dyn_mask_hex: HEX_TEXT 36 action_survive_args: IDENTIFIER RELATIONAL_OPERATOR NUMBER 37 | action_survive_args ',' IDENTIFIER RELATIONAL_OPERATOR NUMBER 38 rule_header: IDENTIFIER '@' host_ip ':' port dir host_ip ':' port 39 | NUMBER '@' host_ip ':' port dir host_ip ':' port 40 | '*' '@' host_ip ':' port dir host_ip ':' port ...
状态解读:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 State 0 0 $accept: . sdt $end COMMENT_LINE shift, and go to state 1 IDENTIFIER shift, and go to state 2 sdt go to state 3 mode go to state 4 State 1 3 sdt: COMMENT_LINE . $default reduce using rule 3 (sdt) State 2 4 mode: IDENTIFIER . $default reduce using rule 4 (mode) State 3 0 $accept: sdt . $end $end shift, and go to state 5 State 4 1 sdt: mode . rule_header payload_matcher rule_action 2 | mode . rule_header payload_matcher '$' rule_body rule_action IDENTIFIER shift, and go to state 6 NUMBER shift, and go to state 7 '*' shift, and go to state 8 rule_header go to state 9
解读 State 0 :
State 0 表示当前状态没有任何输入的状态,
. 代表在当前输入的 光! 标! 位! 置!
移进指示: 如果遇到一个 COMMENT_LINE 转移到 [状态1]
移进指示: 如果遇到一个 IDENTIFIER 转移到 [状态2]
归约指示: 如果遇到一个 sdt 转移到 [状态3]
归约指示: 如果遇到一个 mode 转移到 [状态4]
解读 State 1 :
State 1 表示当前已经得到 COMMENT_LINE,
. 代表在当前输入的 光! 标! 位! 置!
归约指示: 默认 转移到 [语法规则3]
解读 State 2 :
State 2 表示当前已经得到 IDENTIFIER,
. 代表在当前输入的 光! 标! 位! 置!
归约指示: 默认 转移到 [语法规则4]
冲突
在文件的开始处, 可以看到 有4处状态存在问题
1 2 3 4 State 30 conflicts: 1 shift/reduce State 45 conflicts: 1 shift/reduce State 81 conflicts: 1 shift/reduce State 108 conflicts: 1 shift/reduce
定位到 第1处的冲突
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 State 30 9 payload_match_expression: payload_expression . [$end, IDENTIFIER, ACTION_survive, ACTION, '$', ','] 15 payload_expression: payload_expression . payload_expression_atomic 16 | payload_expression . payload_expression_atomic_not 17 payload_expression_atomic_not: . '!' IDENTIFIER 18 | . '!' hex_payload 19 payload_expression_atomic: . IDENTIFIER 20 | . hex_payload 21 hex_payload: . HEX_TEXT IDENTIFIER shift, and go to state 44 HEX_TEXT shift, and go to state 25 '!' shift, and go to state 47 IDENTIFIER [reduce using rule 9 (payload_match_expression)] $default reduce using rule 9 (payload_match_expression) payload_expression_atomic_not go to state 48 payload_expression_atomic go to state 49 hex_payload go to state 32
解读 State 30 :
State 30 表示当前已经得到 payload_expression,
.代表在当前输入的 光! 标! 位! 置!
移进指示: 如果遇到一个 IDENTIFIER 转移到 [状态44]
移进指示: 如果遇到一个 HEX_TEXT 转移到 [状态25]
移进指示: 如果遇到一个 ‘!’ 转移到 [状态47]
归约指示: 如果遇到一个 IDENTIFIER 转移到 [语法规则9]
归约指示: 默认什么也没有 转移到 [语法规则9]
归约指示: 如果遇到一个 payload_expression_atomic_not 转移到 [语法规则48]
归约指示: 如果遇到一个 payload_expression_atomic 转移到 [语法规则49]
归约指示: 如果遇到一个 hex_payload 转移到 [语法规则32]
冲突原因:
在 State 30 , 位于已经得到 payload_expression的状态,
再输入一个 IDENTIFIER 会得到 歧义.
此处的 IDENTIFIER 对应着两个 转移,
一个 是 payload_match_expression 的向下移进模式 payload_expression_atomic 内的 IDENTIFIER
1 2 3 4 5 9 payload_match_expression: payload_expression 向下 14 payload_expression: payload_expression_atomic 向下 19 payload_expression_atomic: IDENTIFIER 此处
注意: 这里是 payload_match_expression 向内展开, 最终得到依然是 payload_match_expression.
一个 是 payload_match_expression 的向上归约模式 payload_expression_atomic 内的 IDENTIFIER
1 2 3 4 5 6 7 8 9 10 11 12 1 sdt: mode rule_header payload_matcher rule_action # 向上 邻接 rule_action 5 payload_matcher: %empty # 向上 6 | '#' payload_and_expression 7 payload_and_expression: payload_match_expression # 向上 9 payload_match_expression: payload_expression # 向上 24 rule_action: %empty # rule_action内部存在 IDENTIFIER 可以匹配 -- 冲突 25 | rule_action IDENTIFIER LP RP ';'
注意: 这里是 payload_match_expression 内上收缩, 最终转换为 payload_matcher.
在 payload_matcher 后面的 rule_action,
rule_action 内展开 会出现一个需要 IDENTIFIER的模式.
冲突本质:
在 State 30 : 位于已经得到 payload_expression 的状态,
payload_expression . 输入光标处于 payload_expression 模式后面,
如果下一个 token 是 IDENTIFIER, 存在二义性, 既可以向下移进, 又可以向上规约。
bison 发出冲突警告!
IDENTIFIER 的 移入/归约冲突
解决:
IDENTIFIER 的 移进/规约 只能二选一,
要么 保留 IDENTIFIER 的 移进模式。
要么 保留 IDENTIFIER 的 规约模式。
参考2
1 2 3 4 5 6 7 8 9 10 11 12 State 13 1 sdt: MODE rule_header payload_matcher . rule_body_pattern rule_action_pattern CR 23 rule_body_pattern: . %empty [CR, 'C', 'A', 'B'] 24 | . 'C' rule_body 'C' shift, and go to state 34 'C' [reduce using rule 23 (rule_body_pattern)] $default reduce using rule 23 (rule_body_pattern) rule_body_pattern go to state 35
%empty [CR, ‘C’, ‘A’, ‘B’] 代表 后面的可选 token 是 CR, ‘C’, ‘A’, ‘B’
在 State 13 光标位于 '.'时,
当出现 ‘C’ 是应该 shift, 还是 reduce ?
参考3
1 2 3 4 5 6 7 8 9 10 11 12 State 13 1 sdt: MODE rule_header payload_matcher . rule_body_pattern rule_action_pattern 23 rule_body_pattern: . %empty [$end, 'C', 'A', 'B'] 24 | . 'C' rule_body 'C' shift, and go to state 34 'C' [reduce using rule 23 (rule_body_pattern)] $default reduce using rule 23 (rule_body_pattern) rule_body_pattern go to state 35
%empty [$end, ‘C’, ‘A’, ‘B’] 代表 后面的可选 token 是 $end, ‘C’, ‘A’, ‘B’
在 State 13 光标位于 '.'时,
当出现 ‘C’ 是应该 shift, 还是 reduce ?