今年过年回家本来要好好放松一下的,但是闲了几天发现很无聊,于是乎想起来了以前想学的东西,但是没来得及学的,那就是解释器,但是我没把电脑带回来,大家一定想不到,我这篇博客竟然是在树莓派中写的,哈哈。废话不多说,现在开始。
首先给出这次的代码:https://download.csdn.net/download/shixiongtao/12116774。大家自己首先下载运行一下看一下效果。
虽然功能非常简单,但是基本上体现了一个完整的过程。
PLY是lex和yacc的纯python实现,需要用到两个包 ply.lex和ply.yacc,ply.lex是把程序分割为一个个的标记(token),ply.yacc则是负责根据token生成语法,并执行。
###################################################
tokens = ['ID', 'FLOAT', 'INT']
literals = ['=', '+', '-', '*', '/', '(', ')']
t_ignore = " \t"
t_ID = r'[a-zA-Z_][a-zA-Z_0-9]*'
def t_FLOAT(t):
r'\d+\.\d+'
t.value = float(t.value)
print('t_FLOAT', t.value)
return t
def t_INT(t):
r'\d+'
t.value = int(t.value)
print('t_INT', t.value)
return 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()
###################################################
# dictionary of ids
ids = {}
# Parsing rules
precedence = (
('left', '+', '-'),
('left', '*', '/'),
('right', 'UMINUS'),
)
def p_statement_assign(p):
'statement : ID "=" expression'
ids[p[1]] = p[3]
for i in range(len(p)):
print('p_statement_assign', 'pos', i, 'value', p[i])
def p_statement_expr(p):
'statement : expression'
print(p[1])
for i in range(len(p)):
print('p_statement_expr', 'pos', i, 'value', p[i])
def p_expression_plus(p):
'''expression : expression '+' expression'''
p[0] = p[1] + p[3]
for i in range(len(p)):
print('p_expression_plus', 'pos', i, 'value', p[i])
def p_expression_minus(p):
'''expression : expression '-' expression'''
p[0] = p[1] - p[3]
for i in range(len(p)):
print('p_expression_minus', 'pos', i, 'value', p[i])
def p_expression_times(p):
'''expression : expression '*' expression'''
p[0] = p[1] * p[3]
for i in range(len(p)):
print('p_expression_times', 'pos', i, 'value', p[i])
def p_expression_div(p):
'''expression : expression '/' expression'''
p[0] = p[1] / p[3]
for i in range(len(p)):
print('p_expression_div', 'pos', i, 'value', p[i])
def p_expression_uminus(p):
"expression : '-' expression %prec UMINUS"
p[0] = -p[2]
for i in range(len(p)):
print('p_expression_uminus', 'pos', i, 'value', p[i])
def p_expression_group(p):
"expression : '(' expression ')'"
p[0] = p[2]
for i in range(len(p)):
print('p_expression_group', 'pos', i, 'value', p[i])
def p_expression_float(p):
"expression : FLOAT"
p[0] = p[1]
for i in range(len(p)):
print('p_expression_float', 'pos', i, 'value', p[i])
def p_expression_int(p):
"expression : INT"
p[0] = p[1]
for i in range(len(p)):
print('p_expression_int', 'pos', i, 'value', p[i])
def p_expression_id(p):
"expression : ID"
try:
p[0] = ids[p[1]]
except LookupError:
print("Undefined name '%s'" % p[1])
p[0] = 0
for i in range(len(p)):
print('p_expression_id', 'pos', i, 'value', p[i])
def p_error(p):
if p:
print("Syntax error at '%s'" % p.value)
else:
print("Syntax error at EOF")
###################################################
import ply.yacc as yacc
yacc.yacc()
while True:
try:
s = input('calc > ')
except EOFError:
break
if not s:
continue
yacc.parse(s)
print('ids', ids)
咱们开始一行一行的解析。
tokens = ['ID', 'FLOAT', 'INT']
在ply.lex中会默认读取我们声明的tokens变量,在这个变量中存储的是可能的标记种类。
比如a = 1+1.1,在这段程序中包含了三种标记,a(ID),1(INT),1.1(FLOAT)。
literals = ['=', '+', '-', '*', '/', '(', ')']
这一行生命的是字面量,同样的ply.lex会默认读取我们声明的literals变量,其实这也算是token只不过这个token特殊的地方在于,这个token的type和value是同一个值。
t_ignore = " \t"
这一行代表省略所有的空格。
t_ID = r'[a-zA-Z_][a-zA-Z_0-9]*'
这一行给出具体的ID这个token的正则表达式,以a-z,A-Z,和下划线为开头,以a-z,A-Z,0-9和下划线,重复任意次。
def t_FLOAT(t):
r'\d+\.\d+'
t.value = float(t.value)
print('t_FLOAT', t.value)
return t
前面的t_ID是给出ID这个token的正则表达式,ply.lex的token对应的正则表达式也可以以函数定义,这里给出了FLOAT的正则表达式定义,r'\d+\.\d+',整数出现多次,小数点,整数出现多次。在进入到这个函数的时候,首先t就是匹配到的token,t.value就是具体匹配到的字符串,这里需要把字符串转换成float数字,t.value = float(t.value),之后再返回t。
def t_INT(t):
r'\d+'
t.value = int(t.value)
print('t_INT', t.value)
return t
这个是对应INT这个token处理的函数。
def t_newline(t):
r'\n+'
t.lexer.lineno += t.value.count("\n")
这个是识别行号的处理函数,当识别到一个新行的时候,通过t.lexer.lineno来累加行号。
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()
最终导入ply.lex包,这个包就会自动读取上面写的规则。需要注意的是,上面这个规则定义的顺序决定了规则匹配的优先级,先定义的规则,在匹配的时候具有较高的优先级,具体来说,t_FLOAT的优先级必须比t_INT的优先级要高,如果弄反了,那么一个浮点数就会解析为两个整数,和一个错误。
上面就是ply.lex部分,下面开始ply.yacc部分。
ids = {}
首先定义变量存储的字典。
precedence = (
('left', '+', '-'),
('left', '*', '/'),
('right', 'UMINUS'),
)
这里声明了操作符的优先级。
def p_statement_assign(p):
'statement : ID "=" expression'
ids[p[1]] = p[3]
for i in range(len(p)):
print('p_statement_assign', 'pos', i, 'value', p[i])
这里给出第一个模式,将一个表达式的值赋值给一个ID。
这里通过ids这个字典,将所有的变量名和变量值对应存储起来。最好的for循环是为了打印p中的内存,是为了学习使用,并不是必须的,下面的都是这样。
def p_statement_expr(p):
'statement : expression'
print(p[1])
for i in range(len(p)):
print('p_statement_expr', 'pos', i, 'value', p[i])
第二个模式,当单独输入一个变量时,直接打印这个变量的值。
def p_expression_plus(p):
'''expression : expression '+' expression'''
p[0] = p[1] + p[3]
for i in range(len(p)):
print('p_expression_plus', 'pos', i, 'value', p[i])
def p_expression_minus(p):
'''expression : expression '-' expression'''
p[0] = p[1] - p[3]
for i in range(len(p)):
print('p_expression_minus', 'pos', i, 'value', p[i])
def p_expression_times(p):
'''expression : expression '*' expression'''
p[0] = p[1] * p[3]
for i in range(len(p)):
print('p_expression_times', 'pos', i, 'value', p[i])
def p_expression_div(p):
'''expression : expression '/' expression'''
p[0] = p[1] / p[3]
for i in range(len(p)):
print('p_expression_div', 'pos', i, 'value', p[i])
这四个模式是四则运算。
def p_expression_uminus(p):
"expression : '-' expression %prec UMINUS"
p[0] = -p[2]
for i in range(len(p)):
print('p_expression_uminus', 'pos', i, 'value', p[i])
这个模式是对变量取负数。
def p_expression_group(p):
"expression : '(' expression ')'"
p[0] = p[2]
for i in range(len(p)):
print('p_expression_group', 'pos', i, 'value', p[i])
这个是括号对应的模式。
def p_expression_float(p):
"expression : FLOAT"
p[0] = p[1]
for i in range(len(p)):
print('p_expression_float', 'pos', i, 'value', p[i])
def p_expression_int(p):
"expression : INT"
p[0] = p[1]
for i in range(len(p)):
print('p_expression_int', 'pos', i, 'value', p[i])
def p_expression_id(p):
"expression : ID"
try:
p[0] = ids[p[1]]
except LookupError:
print("Undefined name '%s'" % p[1])
p[0] = 0
for i in range(len(p)):
print('p_expression_id', 'pos', i, 'value', p[i])
浮点数,整数,ID的模式。
def p_error(p):
if p:
print("Syntax error at '%s'" % p.value)
else:
print("Syntax error at EOF")
最后是错误处理。
最后就是通过输入字符串,并解释字符串,输出中间详细信息,和最终的结果。
import ply.yacc as yacc
yacc.yacc()
while True:
try:
s = input('calc > ')
except EOFError:
break
if not s:
continue
yacc.parse(s)
print('ids', ids)
最后给出一个运行实例。
>>> ================================ RESTART ================================
>>>
calc > a = 1
t_INT 1
p_expression_int pos 0 value 1
p_expression_int pos 1 value 1
p_statement_assign pos 0 value None
p_statement_assign pos 1 value a
p_statement_assign pos 2 value =
p_statement_assign pos 3 value 1
ids {'a': 1}
calc > b = 2
t_INT 2
p_expression_int pos 0 value 2
p_expression_int pos 1 value 2
p_statement_assign pos 0 value None
p_statement_assign pos 1 value b
p_statement_assign pos 2 value =
p_statement_assign pos 3 value 2
ids {'b': 2, 'a': 1}
calc > c = a + b + (3 * 4 / 5) - 6
p_expression_id pos 0 value 1
p_expression_id pos 1 value a
p_expression_id pos 0 value 2
p_expression_id pos 1 value b
p_expression_plus pos 0 value 3
p_expression_plus pos 1 value 1
p_expression_plus pos 2 value +
p_expression_plus pos 3 value 2
t_INT 3
p_expression_int pos 0 value 3
p_expression_int pos 1 value 3
t_INT 4
p_expression_int pos 0 value 4
p_expression_int pos 1 value 4
p_expression_times pos 0 value 12
p_expression_times pos 1 value 3
p_expression_times pos 2 value *
p_expression_times pos 3 value 4
t_INT 5
p_expression_int pos 0 value 5
p_expression_int pos 1 value 5
p_expression_div pos 0 value 2.4
p_expression_div pos 1 value 12
p_expression_div pos 2 value /
p_expression_div pos 3 value 5
p_expression_group pos 0 value 2.4
p_expression_group pos 1 value (
p_expression_group pos 2 value 2.4
p_expression_group pos 3 value )
p_expression_plus pos 0 value 5.4
p_expression_plus pos 1 value 3
p_expression_plus pos 2 value +
p_expression_plus pos 3 value 2.4
t_INT 6
p_expression_int pos 0 value 6
p_expression_int pos 1 value 6
p_expression_minus pos 0 value -0.5999999999999996
p_expression_minus pos 1 value 5.4
p_expression_minus pos 2 value -
p_expression_minus pos 3 value 6
p_statement_assign pos 0 value None
p_statement_assign pos 1 value c
p_statement_assign pos 2 value =
p_statement_assign pos 3 value -0.5999999999999996
ids {'c': -0.5999999999999996, 'b': 2, 'a': 1}
calc >
到这块,这段简单的程序就能支持简单的四则运算了,下面接着要学的就是支持if,else,for循环了。
来源:CSDN
作者:夏特曼-S
链接:https://blog.csdn.net/shixiongtao/article/details/104059437