从PLY到floating numb泛化整数计算器实例

2024-05-01 21:32:08 发布

您现在位置:Python中文网/ 问答频道 /正文

我在读第一个例子

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)

我有错误:

^{pr2}$

Tags: namenamesvaluedefplusintegerstatementprint
2条回答

ply按照声明的顺序解析T_xxx成员(使用模块上的反射)。这里发生的是T_INTEGERT_FLOAT之前匹配。所以浮点的整数部分被解析,然后ply在点上阻塞。在

如果您的regex for floats没有关闭(在我的第一个回答中完全忽略了这一点,被明显的错误顺序蒙蔽了双眼),这将直接起作用。在

我已经把它简化为\d+\.\d+(这与1.或{}不匹配,所以不是最佳选择),但是您可以从类似的问题中借用一个更好的版本:PLY lexer for numbers always returns double

您必须在T_INTEGER之前对T_FLOAT进行解析。只需交换两个声明即可:

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的一般规则,对于比其他模式更长/更具体的所有模式都要这样做,以避免冲突。在

你的lex文件有两个问题。首先是令牌顺序,正如Jean-francois所解释的:较长的令牌必须首先在lex中定义(来自ply doc.):

When building the master regular expression, rules are added in the following order:

  1. All tokens defined by functions are added in the same order as they appear in the lexer file.
  2. Tokens defined by strings are added next by sorting them in order of decreasing regular expression length (longer expressions are added first).

但是定义令牌的字符串应该是re兼容的字符串。你的浮动定义在这里被严重破坏了。如果我们定义一个由一个点组成的浮点数,以及该点之前或之后的可选数字,而不是一个单独的点,则可接受的定义可以是:

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

尤其是,斜杠/不应包括在字符串中。。。在

相关问题 更多 >