PLY yacc 解析器:如何处理未知值,如虚数?
PLY yacc parser : how can I handle unknown value, like imaginary numbers?
我正在使用 PLY:lex 和 yacc 来创建一种 'bc' 命令:可以解析但也可以分配变量。
所以我可以解决这样的问题:
1 + 3.12 - 4 = ?
或
5 * 3 = ?
或
(1 + 3) * 7 /2 = ?
还有类似的东西,知道 'abc' = 10:
(abc + 3 * abc) * 2 = ?
但我真的不知道应该如何处理未知值或复数,例如虚数。
我该如何处理:
2 * 2 * i + 3 - 1
应该等于 4i + 2
我想不出一个解析规则来处理虚数。有帮助吗?
我的代码:
1.主要代码:
from global_variables import tokens
from lexer import lexer
from parser import parser
while True:
s = input('> ')
if s:
# lexer.input(s)
# while True:
# tok = lexer.token()
# if not tok:
# break
# print(tok)
parser.parse(s)
2。词法分析器:
from global_variables import tokens
import ply.lex as lex
t_PLUS = r'\+'
t_MINUS = r'\-'
t_TIMES = r'\*'
t_DIVIDE = r'\/'
t_MODULO = r'\%'
t_EQUALS = r'\='
t_LPAREN = r'\('
t_RPAREN = r'\)'
t_POWER = r'\^'
t_QUESTION = r'\?'
t_NAME = r'[a-zA-Z]{2,}|[a-hj-zA-HJ-Z]' # all words (only letters) except the word 'i' alone
t_IMAGINE = r'i' # the word 'i' alone
t_COMMAND = r'![\x00-\x7F]*' # all unicode characters after '!'
def t_NUMBER(t):
r'\d+(\.\d+)?'
try:
t.value = int(t.value)
except:
t.value = float(t.value)
return t
t_ignore = " \t"
def t_error(t):
print("Illegal character '%s'" % t.value[0])
t.lexer.skip(1)
lexer = lex.lex()
3。解析器:
from global_variables import tokens
from global_variables import variables
from global_variables import prRed
from global_variables import prGreen
from global_variables import prLightPurple
import ply.yacc as yacc
precedence = (
('left','PLUS','MINUS'),
('left','TIMES','DIVIDE'),
('right','UMINUS'),
)
def p_statement_assign(t):
'statement : NAME EQUALS expression'
variables[t[1].lower()] = t[3]
print(t[3])
def p_statement_expr(t):
'''statement : expression
| expression EQUALS QUESTION'''
print(t[1])
def p_expression_binop(t):
'''expression : expression PLUS expression
| expression MINUS expression
| expression TIMES expression
| expression DIVIDE expression
| expression POWER expression
| expression MODULO expression'''
if t[2] == '+': t[0] = t[1] + t[3]
elif t[2] == '-': t[0] = t[1] - t[3]
elif t[2] == '*': t[0] = t[1] * t[3]
elif t[2] == '%': t[0] = t[1] % t[3]
elif t[2] == '^': t[0] = t[1] ** t[3]
elif t[2] == '/': t[0] = float(t[1]) / float(t[3])
if t[0] % 1 == 0:
t[0] = int(t[0])
else:
t[0] = float(t[0])
def p_expression_uminus(t):
'expression : MINUS expression %prec UMINUS'
t[0] = -t[2]
def p_expression_group(t):
'expression : LPAREN expression RPAREN'
t[0] = t[2]
def p_expression_number(t):
'''expression : NUMBER
| IMAGINE'''
t[0] = t[1]
def p_expression_name(t):
'''expression : NAME
| NAME EQUALS QUESTION'''
try:
t[0] = variables[t[1].lower()]
except LookupError:
prRed("Undefined name '%s'" % t[1])
t[0] = 0
def p_execute_command(t):
'statement : COMMAND'
letter = t[1].split('!')[1]
if letter == 'h':
prGreen("Help:")
print(" - !p = print all variables")
print(" - !q = quit the computor")
elif letter == 'p':
if variables:
prGreen("Variables:")
for key,value in variables.items():
print(" {} = {}".format(key, value))
else:
prRed("Variables:")
print(" There are no variables")
elif letter == 'q':
prGreen("Bye bye!")
exit()
else:
print("Type '!h' for help.")
def p_error(t):
if t:
print("Syntax error at '%s'" % t.value)
else:
print("Syntax error!")
parser = yacc.yacc()
4.一些全局变量:
tokens = (
'NAME',
'NUMBER',
'PLUS',
'MINUS',
'TIMES',
'DIVIDE',
'MODULO',
'EQUALS',
'LPAREN',
'RPAREN',
'POWER',
'QUESTION',
'IMAGINE',
'COMMAND',
)
variables = {}
def prRed(skk): print("3[91m{}3[00m" .format(skk))
def prGreen(skk): print("3[92m{}3[00m" .format(skk))
def prLightPurple(skk): print("3[94m{}3[00m" .format(skk))
i
在这里只是一个保留名称,因此您可以在词法分析器中识别它(与识别任何其他关键字的方式相同)。如果您确实希望 2i
有效,那么您需要在词法分析器中做更多的工作,但应该清楚需要什么。 (或者,您可以将其放入解析器中,这样 2 i
也将有效,但我不得不说这对我来说看起来很奇怪。)
所以你的问题确实不是解析问题。您只需要一个复数数据类型,Python 方便 already has. (Also see this brief explanation。)
Ply 不对语义值设置任何限制。如果你想使用复数,就使用它们。 (但请注意,Python 使用 j
而不是 i
来表示 jmagjnary 数字。这并不意味着你也必须使用它们,但是如果你想使用 i
,你必须进行显式的数字到字符串的转换。)
我正在使用 PLY:lex 和 yacc 来创建一种 'bc' 命令:可以解析但也可以分配变量。
所以我可以解决这样的问题:
1 + 3.12 - 4 = ?
或
5 * 3 = ?
或
(1 + 3) * 7 /2 = ?
还有类似的东西,知道 'abc' = 10:
(abc + 3 * abc) * 2 = ?
但我真的不知道应该如何处理未知值或复数,例如虚数。
我该如何处理:
2 * 2 * i + 3 - 1
应该等于 4i + 2
我想不出一个解析规则来处理虚数。有帮助吗?
我的代码:
1.主要代码:
from global_variables import tokens
from lexer import lexer
from parser import parser
while True:
s = input('> ')
if s:
# lexer.input(s)
# while True:
# tok = lexer.token()
# if not tok:
# break
# print(tok)
parser.parse(s)
2。词法分析器:
from global_variables import tokens
import ply.lex as lex
t_PLUS = r'\+'
t_MINUS = r'\-'
t_TIMES = r'\*'
t_DIVIDE = r'\/'
t_MODULO = r'\%'
t_EQUALS = r'\='
t_LPAREN = r'\('
t_RPAREN = r'\)'
t_POWER = r'\^'
t_QUESTION = r'\?'
t_NAME = r'[a-zA-Z]{2,}|[a-hj-zA-HJ-Z]' # all words (only letters) except the word 'i' alone
t_IMAGINE = r'i' # the word 'i' alone
t_COMMAND = r'![\x00-\x7F]*' # all unicode characters after '!'
def t_NUMBER(t):
r'\d+(\.\d+)?'
try:
t.value = int(t.value)
except:
t.value = float(t.value)
return t
t_ignore = " \t"
def t_error(t):
print("Illegal character '%s'" % t.value[0])
t.lexer.skip(1)
lexer = lex.lex()
3。解析器:
from global_variables import tokens
from global_variables import variables
from global_variables import prRed
from global_variables import prGreen
from global_variables import prLightPurple
import ply.yacc as yacc
precedence = (
('left','PLUS','MINUS'),
('left','TIMES','DIVIDE'),
('right','UMINUS'),
)
def p_statement_assign(t):
'statement : NAME EQUALS expression'
variables[t[1].lower()] = t[3]
print(t[3])
def p_statement_expr(t):
'''statement : expression
| expression EQUALS QUESTION'''
print(t[1])
def p_expression_binop(t):
'''expression : expression PLUS expression
| expression MINUS expression
| expression TIMES expression
| expression DIVIDE expression
| expression POWER expression
| expression MODULO expression'''
if t[2] == '+': t[0] = t[1] + t[3]
elif t[2] == '-': t[0] = t[1] - t[3]
elif t[2] == '*': t[0] = t[1] * t[3]
elif t[2] == '%': t[0] = t[1] % t[3]
elif t[2] == '^': t[0] = t[1] ** t[3]
elif t[2] == '/': t[0] = float(t[1]) / float(t[3])
if t[0] % 1 == 0:
t[0] = int(t[0])
else:
t[0] = float(t[0])
def p_expression_uminus(t):
'expression : MINUS expression %prec UMINUS'
t[0] = -t[2]
def p_expression_group(t):
'expression : LPAREN expression RPAREN'
t[0] = t[2]
def p_expression_number(t):
'''expression : NUMBER
| IMAGINE'''
t[0] = t[1]
def p_expression_name(t):
'''expression : NAME
| NAME EQUALS QUESTION'''
try:
t[0] = variables[t[1].lower()]
except LookupError:
prRed("Undefined name '%s'" % t[1])
t[0] = 0
def p_execute_command(t):
'statement : COMMAND'
letter = t[1].split('!')[1]
if letter == 'h':
prGreen("Help:")
print(" - !p = print all variables")
print(" - !q = quit the computor")
elif letter == 'p':
if variables:
prGreen("Variables:")
for key,value in variables.items():
print(" {} = {}".format(key, value))
else:
prRed("Variables:")
print(" There are no variables")
elif letter == 'q':
prGreen("Bye bye!")
exit()
else:
print("Type '!h' for help.")
def p_error(t):
if t:
print("Syntax error at '%s'" % t.value)
else:
print("Syntax error!")
parser = yacc.yacc()
4.一些全局变量:
tokens = (
'NAME',
'NUMBER',
'PLUS',
'MINUS',
'TIMES',
'DIVIDE',
'MODULO',
'EQUALS',
'LPAREN',
'RPAREN',
'POWER',
'QUESTION',
'IMAGINE',
'COMMAND',
)
variables = {}
def prRed(skk): print("3[91m{}3[00m" .format(skk))
def prGreen(skk): print("3[92m{}3[00m" .format(skk))
def prLightPurple(skk): print("3[94m{}3[00m" .format(skk))
i
在这里只是一个保留名称,因此您可以在词法分析器中识别它(与识别任何其他关键字的方式相同)。如果您确实希望 2i
有效,那么您需要在词法分析器中做更多的工作,但应该清楚需要什么。 (或者,您可以将其放入解析器中,这样 2 i
也将有效,但我不得不说这对我来说看起来很奇怪。)
所以你的问题确实不是解析问题。您只需要一个复数数据类型,Python 方便 already has. (Also see this brief explanation。)
Ply 不对语义值设置任何限制。如果你想使用复数,就使用它们。 (但请注意,Python 使用 j
而不是 i
来表示 jmagjnary 数字。这并不意味着你也必须使用它们,但是如果你想使用 i
,你必须进行显式的数字到字符串的转换。)