# molmassa.py - molaire massa uit een formule, ook met haakjes en kristalwater
ATOOMMASSA = {"H": 1.008, "C": 12.011, "N": 14.007, "O": 15.999, "Na": 22.990, "Mg": 24.305,
              "P": 30.974, "S": 32.06, "Cl": 35.45, "K": 39.098, "Ca": 40.078, "Fe": 55.845, "Cu": 63.546}


def lees_getal(tekst, i):
    """Leest een getal vanaf positie i. Staat er geen getal, dan is het 1."""
    j = i
    while j < len(tekst) and tekst[j].isdigit():
        j += 1
    getal = int(tekst[i:j]) if j > i else 1
    return getal, j


def tel_atomen(formule):
    """Ca(NO3)2 -> {'Ca': 1, 'N': 2, 'O': 6}"""
    stapel = [{}]                             # een woordenboek per niveau van haakjes
    i = 0
    while i < len(formule):
        teken = formule[i]
        if teken == "(":
            stapel.append({})
            i += 1
        elif teken == ")":
            getal, i = lees_getal(formule, i + 1)
            groep = stapel.pop()
            for el, aantal in groep.items():
                stapel[-1][el] = stapel[-1].get(el, 0) + aantal * getal
        else:                                 # element: een hoofdletter, soms gevolgd door een kleine letter
            el = teken
            i += 1
            if i < len(formule) and formule[i].islower():
                el += formule[i]
                i += 1
            getal, i = lees_getal(formule, i)
            stapel[-1][el] = stapel[-1].get(el, 0) + getal
    return stapel[0]


def molaire_massa(formule):
    """Molaire massa in g/mol. Kristalwater mag met een sterretje: MgSO4*7H2O."""
    totaal = 0.0
    for stuk in formule.replace("·", "*").split("*"):
        factor, i = lees_getal(stuk, 0)            # de 7 in 7H2O
        for el, aantal in tel_atomen(stuk[i:]).items():
            totaal += factor * aantal * ATOOMMASSA[el]
    return totaal


if __name__ == "__main__":
    for f in ["H2O", "CO2", "C6H12O6", "C8H18", "NaHCO3", "Ca(NO3)2", "Ca(NO3)2*4H2O",
              "MgSO4*7H2O", "KH2PO4", "(NH4)2SO4"]:
        print(f"{f:15s} {molaire_massa(f):8.2f} g/mol")
