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