【问题标题】:Generalizing integer calculator example from PLY to floating number将整数计算器示例从 PLY 推广到浮点数
【发布时间】:2017-11-13 09:28:12
【问题描述】:

我正在阅读第一个示例来自

https://github.com/dabeaz/ply

它是一个基本的计算器,只允许涉及'(',')','+','-','*','/'、整数和赋值的表达式(例如x=3)并抛出表达式的评估(即使它的结果不是整数,例如'3/4') .

我想允许浮点数,所以我基本上修改了示例中的代码,但它不起作用:

# -----------------------------------------------------------------------------
# calc.py
#
# A simple calculator with variables.
# -----------------------------------------------------------------------------

tokens = (
    'NAME','INTEGER', 'FLOAT',
    'PLUS','MINUS','TIMES','DIVIDE','EQUALS',
    'LPAREN','RPAREN',
    )

# Tokens

t_PLUS    = r'\+'
t_MINUS   = r'-'
t_TIMES   = r'\*'
t_DIVIDE  = r'/'
t_EQUALS  = r'='
t_LPAREN  = r'\('
t_RPAREN  = r'\)'
t_NAME    = r'[a-zA-Z_][a-zA-Z0-9_]*'

def t_INTEGER(t):
    r'\d+'
    t.value = int(t.value)
    return t

def t_FLOAT(t):
    r'/^(?!0\d)\d*(\.\d+)?$/mg'
    t.value = float(t.value)
    return t

# Ignored characters
t_ignore = " \t"

def t_newline(t):
    r'\n+'
    t.lexer.lineno += t.value.count("\n")

def t_error(t):
    print("Illegal character '%s'" % t.value[0])
    t.lexer.skip(1)

# Build the lexer
import ply.lex as lex
lex.lex()

# Precedence rules for the arithmetic operators
precedence = (
    ('left','PLUS','MINUS'),
    ('left','TIMES','DIVIDE'),
    ('right','UMINUS'),
    )

# dictionary of names (for storing variables)
names = { }

def p_statement_assign(p):
    'statement : NAME EQUALS expression'
    names[p[1]] = p[3]

def p_statement_expr(p):
    'statement : expression'
    print(p[1])

def p_expression_binop(p):
    '''expression : expression PLUS expression
                  | expression MINUS expression
                  | expression TIMES expression
                  | expression DIVIDE expression'''
    if p[2] == '+'  : p[0] = p[1] + p[3]
    elif p[2] == '-': p[0] = p[1] - p[3]
    elif p[2] == '*': p[0] = p[1] * p[3]
    elif p[2] == '/': p[0] = p[1] / p[3]

def p_expression_uminus(p):
    'expression : MINUS expression %prec UMINUS'
    p[0] = -p[2]

def p_expression_group(p):
    'expression : LPAREN expression RPAREN'
    p[0] = p[2]

def p_expression_integer(p):
    'expression : INTEGER'
    p[0] = p[1]

def p_expression_float(p):
    'expression : FLOAT'
    p[0] = p[1]

def p_expression_name(p):
    'expression : NAME'
    try:
        p[0] = names[p[1]]
    except LookupError:
        print("Undefined name '%s'" % p[1])
        p[0] = 0

def p_error(p):
    print("Syntax error at '%s'" % p.value)

import ply.yacc as yacc
yacc.yacc()

while True:
    try:
        s = input('calc > ')
    except EOFError:
        break
    yacc.parse(s)

我有错误:

calc > 3.14+1
Illegal character '.'
Syntax error at '14'

【问题讨论】:

    标签: python yacc lex ply


    【解决方案1】:

    ply 按声明顺序解析T_xxx 成员(使用模块上的反射)。这里发生的是T_INTEGER 匹配之前 T_FLOAT。所以你的浮点数的整数部分被解析,然后ply 扼流圈。

    如果您的浮点正则表达式没有关闭,那将直接起作用(在我的第一个答案中完全错过了这一点,被明显的错误顺序蒙蔽了双眼)。

    我已将其简化为 \d+\.\d+(与 1..9 不匹配,因此不是最佳选择),但您可以从类似问题中借用一个更好的:PLY lexer for numbers always returns double

    你必须得到T_FLOAT 解析之前 T_INTEGER。只需交换两个声明即可:

    def t_FLOAT(t):
        r'\d+\.\d+'
        # a better regex taking exponents into account:
        '[-+]?[0-9]+(\.([0-9]+)?([eE][-+]?[0-9]+)?|[eE][-+]?[0-9]+)'        
        t.value = float(t.value)
        return t
    
    def t_INTEGER(t):
        r'\d+'
        t.value = int(t.value)
        return t
    

    作为ply 的一般规则,对所有比其他更长/更具体的模式执行此操作以避免冲突。

    【讨论】:

    • 这样做仍然会产生同样的错误。我应该定义点令牌还是?
    • 现在我已经测试了您的代码并且它可以工作,请参阅我的编辑。
    【解决方案2】:

    您的 lex 文件中有两个问题。首先是 Jean-François 解释的令牌顺序:较长的令牌必须首先在 lex 中定义(参考 ply doc。):

    在构建主正则表达式时,按以下顺序添加规则:

    1. 所有由函数定义的标记都按照它们在词法分析器文件中出现的顺序添加
    2. 接下来添加由字符串定义的标记,方法是按照正则表达式长度递减的顺序对它们进行排序(首先添加较长的表达式)。

    但定义令牌的字符串应为re 兼容字符串。您的 FLOAT 定义在这里被严重破坏。如果我们将浮点数定义为仅由一个点以及点之前或之后的可选数字而不是单独的点组成,那么可接受的定义可能是:

    r'(\d*\.\d+)|(\d+\.\d*)'
    

    特别是,斜杠/ 不应包含在字符串中...

    【讨论】:

    • 我又一次因为正则表达式而失败了......非常感谢!
    猜你喜欢
    • 1970-01-01
    • 2018-03-22
    • 1970-01-01
    • 2016-11-06
    • 1970-01-01
    • 2015-09-09
    • 1970-01-01
    • 1970-01-01
    • 2013-06-12
    相关资源
    最近更新 更多