基于rply的摩尔质量计算器

比较复杂的式子需要手动加括号…

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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
from rply import ParserGenerator,LexerGenerator
from rply.token import BaseBox

TOKENS = {
'num':r'\d+',
'ele':'[A-Z][a-z]?',
'lp':r'\(',
'rp':r'\)',
'plus':r'\*|\.|\·|\+',
}
lg = LexerGenerator()
lg.ignore(r'\s+')
list(map(lambda token:lg.add(token,TOKENS[token]),TOKENS.keys()))
lexer = lg.build()
pg = ParserGenerator(list(TOKENS), precedence=[
('left', ['plus','num'])
])
eles:dict[str,int] = {
"H": 1,"He": 4,
"Li": 7,"Be": 9,"B": 11,"C": 12,"N": 14,"O": 16,"F": 19,"Ne": 20,
"Na": 23,"Mg": 24,"Al": 27,"Si": 28,"P": 31,"S": 32,"Cl": 35.5,"Ar": 40,
"K": 39,"Ca": 40,"Sc": 45,"Ti": 48,"V": 51,"Cr": 52,"Mn": 55,"Fe": 56,"Co": 59,"Ni": 59,"Cu": 64,"Zn": 65,"Ga": 70,"Ge": 73,"As": 75,"Se": 79,"Br": 80,"Kr": 84,
"Rb": 85, "Sr": 87, "Y": 89, "Zr": 91, "Nb": 93, "Mo": 96, "Tc": 98, "Ru": 101, "Rh": 103, "Pd": 106, "Ag": 108, "Cd": 112, "In": 115, "Sn": 119, "Sb": 122, "Te": 128, "I": 127, "Xe": 131,
"Cs": 133, "Ba": 137, "La": 139, "Ce": 140, "Pr": 141, "Nd": 144, "Pm": 145, "Sm": 150, "Eu": 152, "Gd": 157, "Tb": 159, "Dy": 162, "Ho": 165, "Er": 167, "Tm": 169, "Yb": 173, "Lu": 175, "Hf": 178, "Ta": 181, "W": 184, "Re": 186, "Os": 190, "Ir": 192, "Pt": 195, "Au": 197, "Hg": 201, "Tl": 204, "Pb": 207, "Bi": 209, "Po": 209, "At": 210, "Rn": 222,
"Fr": 223, "Ra": 226, "Ac": 227, "Th": 232, "Pa": 231, "U": 238, "Np": 239, "Pu": 243, "Am": 245, "Cm": 247, "Bk": 249, "Cf": 253, "Es": 254, "Fm": 259, "Md": 260, "No": 261, "Lr": 264, "Rf": 269, "Db": 270, "Sg": 273, "Bh": 274, "Hs": 272, "Mt": 278, "Ds": 283, "Rg": 282, "Cn": 287, "Fl": 291, "Lv": 295
}

class Element(BaseBox):
def __init__(self,eleName:str) -> None:
self.name = eleName
def eval(self):
if self.name not in eles:
raise Exception('element illegal')
return eles[self.name]

class Num(BaseBox): #化学式加数字就是个乘法
def __init__(self,expr,num) -> None:
self.expr = expr
self.num = num

def eval(self):
return self.expr.eval() * self.num

class Plus(BaseBox):
def __init__(self,left,right) -> None:
self.left = left
self.right = right

def eval(self):
return self.left.eval() + self.right.eval()

@pg.production('expr : ele')
def parseEle(p):
return Element(p[0].getstr())

@pg.production('expr : lp expr rp')
def parseP(p):
return p[1]

@pg.production('expr : num expr')
def parseNum(p):
return Num(p[1],int(p[0].getstr()))

@pg.production('expr : expr num')
def parseNum(p):
return Num(p[0],int(p[1].getstr()))

@pg.production('expr : expr expr')
def parseExpr(p):
return Plus(p[0],p[1])

@pg.production('expr : expr plus expr')
def parsePlus(p):
# print(p)
return Plus(p[0],p[2])

parser = pg.build()

def calc_mol(chem:str):
return parser.parse(lexer.lex(chem)).eval()

chems = [
'CuSO4',
'H2O',
'C10H16N2O2',
'CH4',
'Al2O3',
'Al2(SO4)3',
'CuSO4*5H2O',
]

for c in chems:
print(c,':',calc_mol(c))

输出:

1
2
3
4
5
6
7
CuSO4 : 160
H2O : 18
C10H16N2O2 : 196
CH4 : 16
Al2O3 : 102
Al2(SO4)3 : 342
CuSO4*5H2O : 250

基于rply的摩尔质量计算器
https://www.hakurei.org.cn/2023/01/31/cam-mol-calc/
作者
zjkimin
发布于
2023年2月1日
更新于
2023年2月1日
许可协议