【问题标题】:javacc' LOOKAHEAD( AllSymbols() ) AllSymbols() not chosen, sole to be parsed correctlyjavacc' LOOKAHEAD( AllSymbols() ) AllSymbols() 未选择,唯一要正确解析
【发布时间】:2023-03-26 03:27:01
【问题描述】:

语法如下:

Phi ::= Phi_sub ( ("&&" | "||") Phi_sub )*
Phi_sub ::= "(" Phi ")" | ...

Psi ::= Psi_sub ( ("&&" | "||") Psi_sub )*
Psi_sub ::= "(" Psi ")" | ...

Xi ::= LOOKAHEAD( Phi ) Phi | LOOKAHEAD( Psi ) Psi

如您所见,Xi 产生式通常需要无限前瞻,因为解析器需要区分以下情况:

((Phi_sub && Phi_sub) || Phi_sub)((Psi_sub && Psi_sub) || Psi_sub)

即任意数量的前缀(

我认为,像上面那样进行前瞻会起作用,但事实并非如此。例如,选择 Phi,即使 Xi 不展开为 Phi,而是展开为 Psi。这可以很容易地在某个流 S 上通过在解析后立即使用调试器调用 Phi 来检查,在 Xi 中,选择 Phi,并且即将调用 Phi。在这种情况下,调试器显示了对 Psi 的适当扩展,而允许解析器随意调用 Phi 会导致解析异常。

另一种测试方法是交换 Phi 和 Psi:

Xi ::= LOOKAHEAD( Psi ) Psi | LOOKAHEAD( Phi ) Phi

这将使解析器正确解析特定的 S,因此似乎只是选择了 Xi 中的第一个分支,无论它是否有效。

我想我的一些基本假设是错误的,但不知道会是什么。如果没有其他因素(例如被忽略的内部前瞻),上述方法是否应该正常工作?

【问题讨论】:

    标签: javacc lookahead


    【解决方案1】:

    你的假设没有错。您正在尝试做的事情应该有效。它应该因为你认为它应该起作用的原因而起作用。


    这是一个用 JavaCC 编写的完整示例。

    void Start() : {} { Xi() <EOF> }
    
    void Xi() : {} {
        LOOKAHEAD( Phi() ) Phi() { System.out.println( "Phi" ) ; }
    |   LOOKAHEAD( Psi() ) Psi() { System.out.println( "Psi" ) ; }
    }
    
    void Phi() : {} { Phi_sub() ( ("&&" | "||") Phi_sub() )*}
    
    void Phi_sub() : {} { "(" Phi() ")" | "Phi_sub" }
    
    void Psi() : {} { Psi_sub() ( ("&&" | "||") Psi_sub() )* }
    
    void Psi_sub() : {} { "(" Psi() ")" | "Psi_sub" }
    

    这是一些示例输出:

    Input is : <<Phi_sub>>
    Phi
    Input is : <<Psi_sub>>
    Psi
    Input is : <<((Phi_sub && Phi_sub) || Phi_sub)>>
    Phi
    Input is : <<((Psi_sub && Psi_sub) || Psi_sub)>>
    Psi
    

    您遇到的问题在于问题中未显示的内容。


    顺便说一句,在每个备选方案之前都放置一个前瞻规范是个坏主意。

    void X() : {} { LOOKAHEAD(Y()) Y() | LOOKAHEAD(Z()) Z() }
    

    大致相当于

    void X() : {} { LOOKAHEAD(Y()) Y() | LOOKAHEAD(Z()) Z() | fail with a stupid error message }
    

    例如,这里是上述语法的另一次运行

    Input is : <<((Psi_sub && Psi_sub) || Phi_sub)>>
    NOK.
    Encountered "" at line 1, column 1.
    Was expecting one of:
    

    在所有前瞻都失败后,解析器只剩下一组空的期望!

    如果你把Xi改成

    void Xi() : {} {
        LOOKAHEAD( Phi() ) Phi() { System.out.println( "Phi" ) ; }
    |   Psi() { System.out.println( "Psi" ) ; }
    }
    

    你会得到一个稍微好一点的错误信息

    Input is : <<((Psi_sub && Psi_sub) || Phi_sub)>>
    NOK.
    Encountered " "Phi_sub" "Phi_sub "" at line 1, column 26.
    Was expecting one of:
        "(" ...
        "Psi_sub" ...
    

    您还可以制作自定义错误消息

    void Xi() : {} {
        LOOKAHEAD( Phi() ) Phi() { System.out.println( "Phi" ) ; }
    |   LOOKAHEAD( Psi() ) Psi() { System.out.println( "Psi" ) ; }
    |   { throw new ParseException( "Expected either a Phi or a Psi at line "
                                   + getToken(1).beginLine
                                   + ", column " + getToken(1).beginColumn + "." ) ; 
        }
    }
    

    【讨论】:

    • 谢谢,所以现在我知道,问题很可能是被忽略的内部前瞻。我将 (i) 尝试将 Phi 和 Psi 连接到一个产生式中,然后在语义上检查 Phi 是否是 Phi 并且 Psi 是 Psi,或者 (ii) 在另一个解析器生成器中重写语法。
    • 句法前瞻规范在句法前瞻期间被忽略。请参阅常见问题解答中的讨论。这是我一直不太理解的原因。如果您将一个完整的最小失败示例作为新问题发布,那么有人可能会弄清楚。
    猜你喜欢
    • 2018-12-17
    • 1970-01-01
    • 1970-01-01
    • 2017-05-12
    • 1970-01-01
    • 2012-01-28
    • 1970-01-01
    • 2023-04-03
    • 1970-01-01
    相关资源
    最近更新 更多