Как сделать калькулятор со строками и числами в качестве смешанного ввода, используя парсер Python Ply
Я хотел бы попросить помощи для упражнения на калькулятор, который распознает английские слова и цифры в Python, но теперь использует PLY (Python Lex-Yacc)
Числа и операторы могут быть заданы в двух формах, записанных в виде строки с использованием английских слов: "плюс" = "+", "два" = 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] == 'плюс': p[0] = p[1] + p[3]?
Заранее спасибо.
Я добавил код, предложенный sfk, но у меня все еще есть проблема с распознаванием чисел и операторов, введенных как текст, в английских словах.
Генерация таблиц LALR ПРЕДУПРЕЖДЕНИЕ: 12 конфликтов сдвига / уменьшения Введите ваш ввод: calc > one + two Неопределенное имя 'one' Неопределенное имя 'two' P1: 0 Введите ваш ввод: calc > 1+2 P1 is: 3 3 Введите ваш ввод: calc> 1 плюс 2 Синтаксическая ошибка в "плюс" P1: 2 2
У вас есть представление о том, что я делаю неправильно?
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://stackru.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)