JavaCC定义匹配1个及以上、2个及以上数字Token时空输入可通过问题排查
问题1:顶层语法规则允许空输入
你定义的calc规则核心结构是(<NUM1>+ <NUM2>+)* <EOF>,其中外层的*量词表示匹配0次或多次括号内的组合。当匹配0次时,解析器不需要消费任何Token就能直接匹配到<EOF>,这就是空输入也能解析成功的直接原因。
如果你要求至少出现一组NUM1+NUM2+的组合,需要把外层量词改成+(匹配1次及以上)。
问题2:Token定义不符合需求
你对NUM1、NUM2的定义完全达不到你要的「1个及以上数字」「2个及以上数字」的要求:
- 你先定义了
<NUM :(["0"-"9"])+>,这个Token本身就已经匹配1个及以上的数字字符 <NUM1: <NUM> (<NUM>)*>等价于匹配1个及以上的NUM Token,实际能匹配的数字长度最少是1位,和NUM本身完全重复<NUM2: (<NUM>)+>同样是匹配1个及以上的NUM Token,最少也只需要1位数字,完全达不到「2个及以上数字」的要求
如果要实现需求,正确的Token定义应该先把基础数字单元定义成单个数字,再基于它构造其他Token:
TOKEN : { <SINGLE_NUM: ["0"-"9"]> // 先定义单个数字的基础Token | <NUM1: <SINGLE_NUM>+> // 1个及以上数字,符合需求 | <NUM2: <SINGLE_NUM> <SINGLE_NUM>+> // 2个及以上数字,符合需求 }
问题3:Token匹配顺序错误
JavaCC的词法分析器会按照Token定义的先后顺序优先匹配更早定义的Token,你当前的定义顺序是NUM1 → NUM2 → NUM,而NUM1可以匹配所有长度≥1的数字串,这就会导致所有数字输入都会被识别为NUM1,NUM2永远没有被匹配到的可能。
修正Token定义时需要注意,匹配范围更小、规则更严格的Token要放在更前面:也就是NUM2要放在NUM1前面定义,保证长度≥2的数字串会被优先识别为NUM2,剩下的单个数字才会被识别为NUM1。
修正后的参考代码
TOKEN : { <SINGLE_NUM: ["0"-"9"]> | <NUM2: <SINGLE_NUM> <SINGLE_NUM>+> // 2位及以上数字,优先级更高 | <NUM1: <SINGLE_NUM>> // 仅1位数字 } void calc(): {} { ( <NUM1>+ <NUM2>+ )+ <EOF> // 外层用+,要求至少有一组匹配,不允许空输入 }
内容的提问来源于stack exchange,提问作者Fernando99
相关产品推荐
相关产品推荐

