解决PLY中嵌套函数调用的语法移位/归约冲突

1 投票
1 回答
28 浏览
提问于 2025-04-12 09:33

我正在用PLY创建一个解释器的语法。目前,我在尝试将嵌入式函数调用作为表达式来实现,但我遇到了一个问题,就是不太清楚以下规则的冲突出在哪里。

情况是这样的,在这个语言中,有一些针对向量类型对象的“原生”操作,还有一些针对某些数据类型的函数。例如,对于向量,有pop()、push()、join()等函数。

比如说:

vect1.pop()
vect1.join()

所以我有以下规则:

expresion :     ID PUNTO POP PAREN_APERTURA PAREN_CIERRE
              | ID PUNTO INDEX_OF PAREN_APERTURA expresion PAREN_CIERRE
              | ID PUNTO JOIN PAREN_APERTURA PAREN_CIERRE

还有一些函数适用于特定类型的表达式,比如toString()。

例如:

console.log(12.5.toString())
var num1 : number = 20;
console.log(num1.toString())

因此,我添加了以下规则:

expresion : expresion PUNTO funcion_emb PAREN_APERTURA PAREN_CIERRE

funcion_emb     : TO_STRING
                | TO_UPPER_CASE
                | TO_LOWER_CASE

此外,还有其他函数用于转换值,比如:

parseInt("12")
parseFloat("24.5")

对应的规则如下:

expresion:      PARSE_INT PAREN_APERTURA expresion PAREN_CIERRE
              | PARSE_FLOAT PAREN_APERTURA expresion PAREN_CIERRE
              | TYPE_OF expresion

问题是,它产生了一个移位/归约冲突的警告,但并没有告诉我具体是哪个规则引起的。

看着语法,可能是以下规则引起了冲突:

expresion:      expresion PUNTO funcion_emb PAREN_APERTURA PAREN_CIERRE
              | ID PUNTO POP PAREN_APERTURA PAREN_CIERRE
              | ID PUNTO INDEX_OF PAREN_APERTURA expresion PAREN_CIERRE
              | ID PUNTO JOIN PAREN_APERTURA PAREN_CIERRE
              | PARSE_INT PAREN_APERTURA expresion PAREN_CIERRE
              | PARSE_FLOAT PAREN_APERTURA expresion PAREN_CIERRE
              | TYPE_OF expresion

这里是表达式和优先级的对应规则:

precedence = (
    ('left', 'OR'),
    ('left', 'AND'),
    ('left', 'IGUAL', 'DIFERENTE'),
    ('left', 'MENOR_QUE', 'MAYOR_QUE', 'MENOR_IGUAL', 'MAYOR_IGUAL'),
    ('left', 'MAS', 'MENOS'),
    ('left', 'MULTI', 'DIV', 'MOD'),
    ('right', 'NOT', 'UMENOS'),
    ('nonassoc', 'TYPE_OF'),    
)

expresion :     expresion MAS expresion
              | expresion MENOS expresion
              | expresion MULTI expresion
              | expresion DIV expresion
              | expresion MOD expresion
              | expresion IGUAL expresion
              | expresion DIFERENTE expresion
              | expresion MENOR_QUE expresion
              | expresion MAYOR_QUE expresion
              | expresion MENOR_IGUAL expresion
              | expresion MAYOR_IGUAL expresion
              | expresion AND expresion
              | expresion OR expresion
              | PAREN_APERTURA expresion PAREN_CIERRE
              | MENOS expresion %prec UMENOS
              | NOT expresion              
              | expresion PUNTO funcion_emb PAREN_APERTURA PAREN_CIERRE
              | ID PUNTO POP PAREN_APERTURA PAREN_CIERRE
              | ID PUNTO INDEX_OF PAREN_APERTURA expresion PAREN_CIERRE
              | ID PUNTO JOIN PAREN_APERTURA PAREN_CIERRE
              | PARSE_INT PAREN_APERTURA expresion PAREN_CIERRE
              | PARSE_FLOAT PAREN_APERTURA expresion PAREN_CIERRE
              | TYPE_OF expresion
              | literal
              | ID 


funcion_emb : TO_STRING
                | TO_UPPER_CASE
                | TO_LOWER_CASE

literal : LIT_NUMBER
            | LIT_FLOAT
            | LIT_CHAR
            | LIT_BOOLEAN
            | LIT_STRING

有什么建议吗?

更新:

确实,由于ID PUNTO的产生规则存在冲突。现在,我正在添加新的产生规则来访问对象属性。

expresion : acceso_atrib

acceso_atrib : acceso_atrib PUNTO expresion

acceso_atrib : expresion PUNTO expresion

而且,我在语法中已经指定了POINT运算符的优先级和结合性。

precedence = (
    ('left', 'OR'),
    ('left', 'AND'),
    ('left', 'IGUAL', 'DIFERENTE'),
    ('left', 'MENOR_QUE', 'MAYOR_QUE', 'MENOR_IGUAL', 'MAYOR_IGUAL'),
    ('left', 'MAS', 'MENOS'),
    ('left', 'MULTI', 'DIV', 'MOD'),
    ('right', 'NOT', 'UMENOS'),
    ('nonassoc', 'TYPE_OF'),
    ('left', 'PUNTO'), # ADDED
)

然而,在以下规则中仍然发生了冲突:

WARNING: shift/reduce conflict for PUNTO in state 43 resolved as shift


state 43

    (74) expresion -> acceso_atrib .
    (75) acceso_atrib -> acceso_atrib . PUNTO expresion

  ! shift/reduce conflict for PUNTO resolved as shift
    MAS             reduce using rule 74 (expresion -> acceso_atrib .)
    MENOS           reduce using rule 74 (expresion -> acceso_atrib .)
    MULTI           reduce using rule 74 (expresion -> acceso_atrib .)
    DIV             reduce using rule 74 (expresion -> acceso_atrib .)
    MOD             reduce using rule 74 (expresion -> acceso_atrib .)
    IGUAL           reduce using rule 74 (expresion -> acceso_atrib .)
    DIFERENTE       reduce using rule 74 (expresion -> acceso_atrib .)
    MENOR_QUE       reduce using rule 74 (expresion -> acceso_atrib .)
    MAYOR_QUE       reduce using rule 74 (expresion -> acceso_atrib .)
    MENOR_IGUAL     reduce using rule 74 (expresion -> acceso_atrib .)
    MAYOR_IGUAL     reduce using rule 74 (expresion -> acceso_atrib .)
    AND             reduce using rule 74 (expresion -> acceso_atrib .)
    OR              reduce using rule 74 (expresion -> acceso_atrib .)
    PUNTO_COMA      reduce using rule 74 (expresion -> acceso_atrib .)
    PAREN_CIERRE    reduce using rule 74 (expresion -> acceso_atrib .)
    COMA            reduce using rule 74 (expresion -> acceso_atrib .)
    CORCHETE_CIERRE reduce using rule 74 (expresion -> acceso_atrib .)
    DOS_PUNTOS      reduce using rule 74 (expresion -> acceso_atrib .)
    PUNTO           shift and go to state 87

  ! PUNTO           [ reduce using rule 74 (expresion -> acceso_atrib .) ]

1 个回答

2

调试这些问题的最好方法是调用 yacc.yacc(debug=True),然后查看生成的文件 parser.out

    (18) expresion -> ID . PUNTO POP ( )
    (19) expresion -> ID . PUNTO INDEX_OF ( expresion )
    (20) expresion -> ID . PUNTO JOIN ( )
    (25) expresion -> ID .

你已经读取了一个 ID,接下来准备读取一个句点(.)。但是语法不确定是应该直接处理这个句点,还是把这个 ID 转换成一个表达式。之所以会有这种不确定,是因为你的语法中有 expression . function_emb ( ) 这样的结构。

解决办法是,ID PUNTO ... 这种情况在你的语法中是绝对不应该出现的。所有这些情况都应该是 expression PUNTO ...,因为 pop() 等操作可以在任何表达式上调用。

撰写回答