计算器实现

首先,将一行字符串解析成tokens。然后,按照特定的语法处理tokens。

语法

语法的定义,参考:

实现

词法解析

词法解析,使用正则表达式。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
from typing import NamedTuple
import re

class Token(NamedTuple):
type: str
value: str
pos: int

def tokenize(line:str):
"""将字符串解析成tokens"""
# 预处理
if len(line.strip().splitlines()) > 1:
raise Exception('only parse one line')
line = line.strip()

# token的定义
SPECIFICATION = [
('NUMBER', r'\d+(\.\d*)?'),
('LP',r'\('),
('RP',r'\)'),
('OP1', r'[\+\-]'),
('OP2', r'[\*/]'),
('SKIP',r'[ \t]+'),
('MISMATCH',r'.'),
]

# 解析
tok_regex = '|'.join('(?P<%s>%s)' % pair for pair in SPECIFICATION)
for mo in re.finditer(tok_regex, line):
kind = mo.lastgroup
value = mo.group()
pos = mo.start()
if kind == 'MISMATCH':
raise Exception('mismatch: %s, position:%d' % (value, pos))
if kind != 'SKIP':
yield Token(kind, value, pos)

语法处理

代码的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
class Calc:
def __init__(self) -> None:
self.tokens = []
self.current_token = None

def next_token(self):
"""一次只处理一个"""
self.current_token = next(self.tokens, None)
print(self.current_token)
return self.current_token

def factor(self):
result = None
if self.current_token!=None:
if self.current_token.type == 'NUMBER':
result = float(self.current_token.value)
self.next_token()
elif self.current_token.type == 'LP':
self.next_token()
result = self.expr()
if self.current_token is not None and self.current_token.type== 'RP':
self.next_token()
else:
raise Exception('missing ")"')
else:
raise Exception('should be number')
return result

def term(self):
x = self.factor()
result = x

if x!=None:
y = self.current_token
while y is not None and y.type == 'OP2':
self.next_token()
z = self.factor()
if z == None:
raise Exception('missing factor')
else:
if y.value == '*':
result = result * float(z)
elif y.value == '/':
result = result / float(z)
y = self.current_token

return result

def expr(self):
x = self.term()
result = x

if x!=None:
y = self.current_token
while y is not None and y.type == 'OP1':
self.next_token()
z = self.term()
if z == None:
raise Exception('missing factor')
else:
if y.value == '+':
result = result + float(z)
elif y.value == '-':
result = result - float(z)
y = self.current_token

return result

def run(self,line:str):
self.tokens = tokenize(line)
self.next_token()
result = self.expr()
return result

使用

1
2
3
line = '2*(3-2)+5*2/2+1'
calc = Calc()
print(calc.run(line))