Module:MolarMass

-- Computes the mollar mass from a simple chemical formula --   like H2O, NH3, CuSO4, Si(OH)4, 2H2O -- local c = {} -- module's table local am = {} -- Atomic Mass table (extracted from .svg) am.H=1.00794; am.He=4.002602; am.Li=6.941; am.Be=9.012182; am.B=10.811; am.C=12.0107; am.N=14.00674; am.O=15.9994; am.F=18.9984032; am.Ne=20.1797; am.Na=22.98976928; am.Mg=24.3050; am.Al=26.9815386; am.Si=28.0855; am.P=30.973762; am.S=32.066; am.Cl=35.4527; am.Ar=39.948; am.K=39.0983; am.Ca=40.078; am.Sc=44.955912; am.Ti=47.867; am.V=50.9415; am.Cr=51.9961; am.Mn=54.938045; am.Fe=55.845; am.Co=58.933195; am.Ni=58.6934; am.Cu=63.546; am.Zn=65.39; am.Ga=69.723; am.Ge=72.61; am.As=74.92160; am.Se=78.96; am.Br=79.904; am.Kr=83.80; am.Rb=85.4678; am.Sr=87.62; am.Y=88.90585; am.Zr=91.224; am.Nb=92.90638; am.Mo=95.94; am.Tc=97.9072; am.Ru=101.07; am.Rh=102.90550; am.Pd=106.42; am.Ag=107.8682; am.Cd=112.411; am.In=114.818; am.Sn=118.710; am.Sb=121.760; am.Te=127.60; am.I=126.90447; am.Xe=131.29; am.Cs=132.9054519; am.Ba=137.327;      am.Hf=178.49; am.Ta=180.94788; am.W=183.84; am.Re=186.207; am.Os=190.23; am.Ir=192.217; am.Pt=195.084; am.Au=196.966569; am.Hg=200.59; am.Tl=204.3833; am.Pb=207.2; am.Bi=208.98040; am.Po=208.9824; am.At=209.9871; am.Rn=222.0176; am.La=138.90547; am.Ce=140.116; am.Pr=140.90765; am.Nd=144.242; am.Pm=144.9127; am.Sm=150.36; am.Eu=151.964; am.Gd=157.25; am.Tb=158.92535; am.Dy=162.500; am.Ho=164.93032; am.Er=167.259; am.Tm=168.93421; am.Yb=173.04; am.Lu=174.967; am.Fr=223.0197; am.Ra=226.0254;        am.Rf=263.1125; am.Db=262.1144; am.Sg=266.1219; am.Bh=264.1247; am.Hs=269.1341; am.Mt=268.1388; am.Ds=272.1463; am.Rg=272.1535; am.Cn=277.0; am.Uut=284.0; am.Fl=289.0; am.Uup=288.0; am.Lv=292.0; am.Uus=292.0; am.Uuo=101.3 am.Ac=227.0277; am.Th=232.03806; am.Pa=231.03588; am.U=238.02891; am.Np=237.0482; am.Pu=244.0642; am.Am=243.0614; am.Cm=247.0703; am.Bk=247.0703; am.Cf=251.0796; am.Es=252.0830; am.Fm=257.0951; am.Md=258.0984; am.No=259.1011; am.Lr=262.110; local T_ELEM = 0 -- token types local T_NUM = 1 local T_O = 2   -- open '(' local T_C = 3    -- close ')' local T_MIDDOT = 4 -- hydration delimiter local T_SPACE = 5 -- whitespace local T_WATER = 6   -- crystallization water '•xH2O' function item(f) -- (iterator) returns one token (type, value) at a time from the formula 'f'  local i = 1 return function local t, x = nil, nil if i <= f:len then x = f:match('^%u%l*', i); t = T_ELEM; -- matching elem (C, O, Ba, Na,...) if not x then x = f:match('^[%d.]+', i); t = T_NUM; end -- matching number if not x then x = f:match('^%(', i); t = T_O; end   -- matching '(' if not x then x = f:match('^%)', i); t = T_C; end   -- matching ')' if not x then x = f:match('^•[%d.]*H2O', i); t = T_WATER; end -- matching '•xH2O' x number, optional if not x then x = f:match('^•', i); t = T_MIDDOT; end   -- matching '•' if not x then x = f:match('^%s+', i); t = T_SPACE; end   -- matching whitespace if x then i = i + x:len; else error("Invalid character in formula beginning at '"..f:sub(i).."'") end end return t, x  end end

function c.mm(frame) -- molar mass of the formula 'f'  local f = frame.args[1] local sum, cur = {0}, {0} -- stacks to handle '' ; 'cur' awaits to be multiplied (or not) local t, x  for t, x in item(f) do 	if     t == T_ELEM then if not am[x] then error("Unknown element : "..x) end sum[#sum] = sum[#sum] + cur[#cur]; cur[#cur] = am[x] elseif t == T_NUM then sum[#sum] = sum[#sum] + cur[#cur] * tonumber(x); cur[#cur] = 0 elseif t == T_O   then sum[#sum] = sum[#sum] + cur[#cur]; cur[#cur] = 0;sum[#sum+1] = 0; cur[#cur+1] = 0 -- push elseif t == T_C   then if #sum < 2 then error("Too many ')' in "..f) end				sum[#sum] = sum[#sum] + cur[#cur]; cur[#cur-1] = sum[#sum]; sum[#sum], cur[#cur] = nil, nil -- pop	elseif t == T_WATER then         	if string.match(x, '•%d+') then                	sum[#sum] = sum[#sum] + ((2*am.H + am.O) * tonumber(string.match(x, '[%d.]+')))                else                        -- sum[#sum] = sum[#sum] + (2*am.H + am.O)                end	elseif t == T_MIDDOT then error("Hydration syntax (•) not yet supported")	elseif t ~= T_SPACE then error('???') end -- ignore whitespace   end   if #sum > 1 then error("Too many '(' in "..f) end

-- check leading number (2XyZ) if string.match(f, '^[%d.]+') then return (sum[1] + cur[1]) * tonumber(string.match(f, '^[%d.]+')) else return sum[1] + cur[1] end end -- tests -   c.frame = {}    c.frame.args = {} function pm(f)    c.frame.args[1] = f    print('The molar mass of '..f..' is '..c.mm(c.frame))    end pm("NaCl") pm("NaOH") pm("CaCO3") pm("H2SO4") pm("C10H8") pm("CO2") pm("Mo") pm("HCl") pm("Si(OH)4") pm("CuSO4(H20)5") --- -- return c -- exports c.mm