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): 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))
|