如何使用解析器python ply将带有字符串和数字的计算器作为混合输入

时间:2013-09-14 13:27:35

标签: python parsing calculator lex ply

我想请求练习帮助做一个能识别Python中英文单词和数字的计算器 但现在使用PLY(Python Lex-Yacc)

数字和运算符可以用英文单词写成两种形式,“plus”=“+”,“two”= 2,“一百十二”= 112等... 一个例子可能是这些条目: “二十五除以5”或 “25/5”或 “二十五除以五” 结果应该是相同的,数字5(不是字符串)。

“ - 3乘4”将给出-12

除以0将给出“错误” “34除以0”将给出“错误”

这应该适用于几个基本运算符“ - ”,“+”,“x”和“/”(减号,加号,次数和除以),如果我输入数学符号或我键入文本或混合。

以下是我的代码的一些部分:

# ------- Calculator tokenizing rules

tokens = (
    'NAME','NUMBER', 'times', 'divided_by', 'plus', 'minus'
)

literals = ['=','+','-','*','/', '(',')']

t_ignore = " \t"

t_plus    = r'\+'
t_minus   = r'-'
t_times   = r'\*'
t_divided_by  = r'/'
t_NAME    = r'[a-zA-Z_][a-zA-Z0-9_]*'

  precedence = (
    ('left','+','-'),
    ('left','plus','minus'),
    ('left','times','divided_by'),
    ('left','*','/'),
    ('right','UMINUS'),
)

此处更改了分配

def p_statement_assign(p):
    'statement : expression times divided_by plus minus  expression'
    variables[p[1]] = p[3]
    p[0] = None

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

def p_expression_binop(p):
    '''expression : expression '+' expression
                  | expression 'plus' expression
                  | expression '-' expression
                  | expression 'minus' expression
                  | expression '*' expression
                  | expression 'times' expression
                  | expression 'divided_by' expression
                  | expression '/' 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]

我的令牌定义不好吗? 我怎么知道这个号码可以用英文字母或数字来介绍? 表达式(p [2] =='+':p [0] = p [1] + p [3])必须有一个字符, 为什么无效以这种形式写p [2] =='plus':p [0] = p [1] + p [3]?

先谢谢。


我添加了sfk建议的代码,但我仍然有问题要识别以英文单词输入的数字和运算符。

生成LALR表 警告:12次转换/减少冲突  输入您的输入:calc>一+二 未定义的名称'one' 未定义的名称'两个' P1是:0  输入您的输入:calc> 1 + 2 P1是:3 3  输入您的输入:calc> 1加2 'plus'的语法错误 P1是:2 2

你对我做错了什么了解吗?

1 个答案:

答案 0 :(得分:1)

首先,为英语单词添加标记定义

t_plustext    = r'plus'

将这些新令牌添加到tokens

tokens = (
    'NAME','NUMBER', 'times', 'divided_by', 'plus', 'minus', 'plustext', ....
)

最后,以这种方式在语法中使用这些新标记:

def p_expression_binop(p):
    '''expression : expression '+' expression
                  | expression plustext expression
    '''

更新:这是语法的工作子集

#!/usr/bin/python

from __future__ import print_function

import sys
import ply.lex as lex
import ply.yacc as yacc

# ------- Calculator tokenizing rules

tokens = (
    'NUMBER', 'times', 'divided_by', 'plus', 'minus', 'plustext',
    'one', 'two', 'three',
)

literals = ['=','+','-','*','/', '(',')']

t_ignore = " \t\n"

t_plustext    = r'plus'
t_plus    = r'\+'
t_minus   = r'-'
t_times   = r'\*'
t_divided_by  = r'/'
t_one = 'one'
t_two = 'two'
t_three = 'three'

def t_NUMBER(t):
    r'\d+'
    try:
        t.value = int(t.value)
    except ValueError:
        print("Integer value too large %d", t.value)
        t.value = 0
    return t

precedence = (
    ('left','+','-','plustext'),
    ('left','times','divided_by'),
    ('left','*','/'),
)


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

def p_expression_binop(p):
    '''expression : expression '+' expression
                  | expression plustext expression
                  | expression '-' expression
                  | expression '*' expression
                  | expression '/' 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]
    elif p[2] == 'plus': p[0] = p[1] + p[3]

def p_statement_lit(p):
    '''expression : NUMBER
          | TXTNUMBER
    '''
    p[0] = p[1]

def p_txtnumber(p):
    '''TXTNUMBER : one
         | two
         | three
    '''
    p[0] = w2n(p[1])

def w2n(s):
    if s == 'one': return 1
    elif s == 'two': return 2
    elif s == 'three': return 3
    assert(False)
    # See http://stackoverflow.com/questions/493174/is-there-a-way-to-convert-number-words-to-integers-python for a complete implementation

def process(data):
    lex.lex()
        yacc.yacc()
        #yacc.parse(data, debug=1, tracking=True)
        yacc.parse(data)

if __name__ == "__main__":
        data = open(sys.argv[1]).read()
        process(data)