#!/usr/bin/python # TYPE:sciencemath # DESC:Chemical formula parser, finds mass of formulas and amount of moles/atoms in the formula. print "Content-type: text/html\n\n" import sys,cgi,re _elements = open('../python/elements.dat').read().strip() _html = open('../moles/calc.html').read().strip() def print_it(error = ''): if error == '': global answers print re.sub("\$ANSWERS", answers, _html) else: error = ''+error+'' print re.sub("\$ANSWERS", error, _html) sys.exit() class Element: def __init__(self, symbol, name, mass): self.symbol, self.name, self.mass = symbol, name, mass def get_mass(self): return self.mass def add_amounts(self, amount, result): result[self.symbol] = result.get(self.symbol, 0) + amount class Formula: def __init__(self, *seq): self.seq = list(seq) self.count = 1 def append(self, newthing): self.seq.append(newthing) def get_mass(self): sum = 0.0 for thing in self.seq: sum = sum + thing.get_mass() return sum * self.count def set_count(self, newcount): self.count = newcount def add_amounts(self, amount, result): amount = amount * self.count for thing in self.seq: thing.add_amounts(amount, result) def show_amounts(self, moles): result = {} self.add_amounts(1, result) items = result.items() items.sort() stringy = "" totalatoms = 0 for sym, count in items: stringy += "%s %s of %s (%s) atoms
" % (signif(count * moles), add_s('mole', count), ELEMENTS[sym].name, sym) totalatoms += count * moles return stringy + '%s %s of atoms all together

' % (signif(totalatoms), add_s('mole', totalatoms)) def show_atoms(self, moles): result = {} self.add_amounts(1, result) items = result.items() items.sort() stringy = "" totalatoms = 0 for sym, count in items: stringy += "%s atoms of %s
" % (signif(count * NA * moles), sym) totalatoms += count * NA * moles return stringy + '%s total atoms
' % signif(totalatoms) def __len__(self): return len(self.seq) class Parser: def __init__(self, input): self.input = input + "" self.place = 0 def get_part(self): global ptype, pvalue self.lastplace = self.place m = _lexer(self.input, self.place) if m is None: print_it("Error in input") self.place = m.end() pvalue = m.group() if pvalue == '(': ptype = LPAR elif pvalue == ')': ptype = RPAR elif pvalue == '': ptype = EOS elif pvalue[0] in NUMBERS: ptype, pvalue = NUM, int(pvalue) else: ptype = NAME def add_s(string, count): if count not in [1,'1','1.0']: return string+'s' else: return string def make_dict(orig): dict = {} for line in orig.split('\n'): symbol, name, mass = eval(line) dict[symbol] = Element(symbol, name, mass) return dict def parse(s): global pars, ptype, pvalue pars = Parser(s) pars.get_part() seq = parse_formula() return seq def parse_formula(): global pars, ptype, pvalue seq = Formula() while ptype in (LPAR, NAME): if ptype == LPAR: pars.get_part() newpart = parse_formula() if ptype != RPAR: print_it("Parenthesis failure") pars.get_part() else: if ELEMENTS.has_key(pvalue): newpart = Formula(ELEMENTS[pvalue]) else: print_it("Element not found!") pars.get_part() if ptype == NUM: newpart.set_count(pvalue) pars.get_part() seq.append(newpart) return seq from math import floor,log10 def signif(x): n = 0.3 d=int(10*n) return str(round(x,d-1-int(floor(log10(abs(x)))))) ELEMENTS = make_dict(_elements) del _elements NA = 6.02e+23 NAME, NUM, LPAR, RPAR, EOS = range(5) NUMBERS = '0123456789' _lexer = re.compile("[A-Z][a-z]*|\d+|[()]|").match DATA = cgi.FieldStorage() answers = "" if not DATA.has_key('formula') and not DATA.has_key('todo') and not DATA.has_key('with'): print_it() elif DATA.has_key('formula') and DATA.has_key('todo') and DATA.has_key('with'): pass else: print_it("Fill in all fields.") formula = DATA['formula'].value todo = DATA['todo'].value with = DATA['with'].value seq = parse(formula) if seq and todo == 'mass': moles = float(eval(with)) mass = seq.get_mass() * moles answers += "Molar mass: %s %s" % (signif(mass), add_s('gram', mass) ) answers += "
" answers += seq.show_amounts(moles) answers += seq.show_atoms(moles) answers += "
" answers += "%s molecules" % (signif(moles * NA)) elif seq and todo == 'moles': mass = float(eval(with)) moles = mass/seq.get_mass() answers += "There are %s %s of %s in %dg" % (signif(moles), add_s('mole', moles), formula, mass) answers += "
" answers += seq.show_amounts(moles) answers += seq.show_atoms(moles) answers += "
" answers += "%s molecules" % (signif(moles * NA)) print_it()