yacc - 没有运算符的规则的优先级?

时间:2016-11-23 01:34:48

标签: parsing yacc ply

考虑使用yacc解析正则表达式(我实际上正在使用PLY),一些规则如下:

expr : expr expr
expr : expr '|' expr
expr : expr '*'

问题是,第一条规则(连接)必须优先于第二条规则,而不是第三条规则。

但是,连接规则中没有运算符。

在这种情况下,如何正确指定优先顺序?

谢谢!

修改

我修改了规则以避免这个问题,但我仍然很好奇这是什么问题。

以下是源代码:

tokens = ['PLEFT', 'PRIGHT', 'BAR', 'ASTERISK', 'NORMAL']

t_PLEFT = r'\('
t_PRIGHT = r'\)'
t_BAR = r'\|'
t_ASTERISK = '\*'
t_NORMAL = r'[a-zA-Z0-9]'

lex.lex()

precedence = (
  ('left', 'BAR'),
  ('left', 'CONCAT'),
  ('left', 'ASTERISK'),
)

def p_normal(p):
    '''expr : NORMAL'''
    p[0] = p[1]

def p_par(p):
    '''expr : PLEFT expr PRIGHT'''
    p[0] = p[2]

def p_or(p):
    '''expr : expr BAR expr'''
    p[0] = ('|', p[1], p[3])

def p_concat(p):
    '''expr : expr expr %prec CONCAT'''
    p[0] = ('CONCAT', p[1], p[2])

def p_repeat(p):
    '''expr : expr ASTERISK'''
    p[0] = ('*', p[1])

yacc.yacc()

'ab|cd*'的解析结果为('CONCAT', ('|', ('CONCAT', 'a', 'b'), 'c'), ('*', 'd'))

1 个答案:

答案 0 :(得分:3)

您没有义务使用优先权来消除歧义;你可以简单地写一个明确的语法:

term : CHAR | '(' expr ')'
rept : term | term '*' | term '+' | term '?'
conc : rept | conc rept
expr : conc | expr '|' conc

如果你真的想使用优先权,你可以使用"虚构"带有%prec注释的标记。有关详细信息,请参阅manual。 (此功能来自yacc,因此您也可以在任何yacc / bison文档中阅读它。)

请记住,优先级始终是生产(在解析器堆栈顶部)和前瞻令牌之间的比较。通常,生产的优先级取自生产中最后一个终端的优先级(通常每个适用的生产中只有一个终端),因此它似乎是终端之间的比较。但为了优先使用"隐形"运算符,您需要单独考虑生产优先级和先行标记优先级。

制作的优先顺序可以用虚拟的"来设置。令牌,如上所述。但是没有对应于不可见操作符的超前令牌;前瞻标记将是以下操作数中的第一个标记。换句话说,它可以是expr FIRST 集合中的任何标记,在本例中为{NORMAL, PRIGHT};必须将此集添加到优先级声明中,就像它们是连接运算符一样

precedence = (
  ('left', 'BAR'),
  ('left', 'CONCAT', 'NORMAL', 'PLEFT'),
  ('left', 'ASTERISK'),
)

一旦你这样做,你可以节省虚构的CONCAT令牌,因为你可以使用任何FIRST(expr)令牌作为代理,但这可能被认为不太可读。