5

我正在自学在一个爱好项目中使用 JavaCC,并有一个简单的语法来编写解析器。解析器的一部分包括以下内容:

TOKEN : { < DIGIT : (["0"-"9"]) > }
TOKEN : { < INTEGER : (<DIGIT>)+ > }
TOKEN : { < INTEGER_PAIR : (<INTEGER>){2} > }
TOKEN : { < FLOAT : (<NEGATE>)? <INTEGER> | (<NEGATE>)? <INTEGER>  "." <INTEGER>  | (<NEGATE>)? <INTEGER> "." | (<NEGATE>)? "." <INTEGER> > } 
TOKEN : { < FLOAT_PAIR : (<FLOAT>){2} > }
TOKEN : { < NUMBER_PAIR : <FLOAT_PAIR> | <INTEGER_PAIR> > }
TOKEN : { < NEGATE : "-" > }

使用 JavaCC 编译时,我得到输出:

Warning: Regular Expression choice : FLOAT_PAIR can never be matched as : NUMBER_PAIR

Warning: Regular Expression choice : INTEGER_PAIR can never be matched as : NUMBER_PAIR

我确信这是一个简单的概念,但我不明白这个警告,作为解析器生成和正则表达式的新手。

这个警告是什么意思(在你可以得到的新手术语中)?

4

4 回答 4

4

我不知道JavaCC,但我是一名编译器工程师。

FLOAT_PAIR规则是模棱两可的。考虑以下文本:

0.0

FLOAT 0后面可以是FLOAT .0; 或者它可以FLOAT 0.跟随FLOAT 0; 两者都导致 FLOAT_PAIR。或者它可能是一个单一的 FLOAT 0.0

但是,更重要的是,您正在以一种永远不可能奏效的方式使用带有组合的词法分析。考虑这个数字:

12345

这可以被解析为INTEGER 12, INTEGER 345导致INTEGER_PAIR. 或者它可以被解析为INTEGER 123, INTEGER 45另一个INTEGER_PAIR. 或者它可能是INTEGER 12345另一个令牌。存在问题是因为您不需要INTEGER_PAIR(or FLOAT_PAIR) 的词法元素之间有空格。

您几乎不应该尝试在词法分析器中处理这样的对。相反,您应该将普通数字 (INTEGERFLOAT) 作为标记处理,并在解析器中处理诸如否定和配对之类的事情,其中​​空格已被处理和剥离。

(例如,您将如何处理"----42"?这是大多数编程语言中的有效表达式,可以正确计算多个否定,但不会由您的词法分析器处理。)

另外,请注意,词法分析器中的个位数整数不会匹配为INTEGER,它们会输出为DIGIT。不过,我不知道 JavaCC 为您解决这个问题的正确语法。您想要的DIGIT不是定义为令牌,而只是您可以在其他令牌的定义中使用的东西;或者,将DIGIT( [0-9]) 的定义直接嵌入到您DIGIT在规则中使用的任何位置。

于 2009-04-26T23:51:26.953 回答
0

我没有使用过 JavaCC,但 NUMBER_PAIR 可能是模棱两可的。

我认为问题归结为这样一个事实,即 FLOAT_PAIR 和 INTEGER_PAIR 可以匹配相同的确切事物,因为 FLOAT 可以匹配 INTEGER。

但这只是一个从未见过 JavaCC 语法的猜测 :)

于 2009-04-26T20:59:53.713 回答
0

这可能意味着对于每一个FLOAT_PAIR你只会得到一个FLOAT_PAIR令牌,而不是一个NUMBER_PAIR令牌。该FLOAT_PAIR规则已经匹配了所有输入,JavaCC 不会尝试查找更多匹配规则。那将是我的解释,但我不了解 JavaCC,所以请持保留态度。

也许您可以以某种方式指定这NUMBER_PAIR是主要产品,并且您不希望获得任何其他令牌作为结果。

于 2009-04-26T22:24:37.240 回答
0

感谢巴里凯利的回答,我想出的解决方案是:

    SKIP : { < #TO_SKIP : " " | "\t" > }
    TOKEN : { < #DIGIT : (["0"-"9"]) > }
    TOKEN : { < #DIGITS : (<DIGIT>)+ > }
    TOKEN : { < INTEGER : <DIGITS> > }
    TOKEN : { < INTEGER_PAIR : (<INTEGER>) (<TO_SKIP>)+ (<INTEGER>) > }
    TOKEN : { < FLOAT : (<NEGATE>)?<DIGITS>"."<DIGITS> | (<NEGATE>)?"."<DIGITS> > } 
    TOKEN : { < FLOAT_PAIR : (<FLOAT>) (<TO_SKIP>)+ (<FLOAT>) > }
    TOKEN : { < #NUMBER : <FLOAT> | <INTEGER> > }
    TOKEN : { < NUMBER_PAIR : (<NUMBER>) (<TO_SKIP>)+ (<NUMBER>) >}
    TOKEN : { < NEGATE : "-" > }

我完全忘记了包含用于分隔两个标记的空格,我还使用了“#”符号来停止匹配标记,并且仅用于其他标记的定义。以上由JavaCC编译,没有警告或错误。

然而,正如巴里所指出的,有理由反对这样做。

于 2009-04-27T20:32:19.263 回答