Module:Chem2: Difference between revisions
Appearance
Content deleted Content added
indent |
Update from sandbox. Supports wikilinks with numbers in, and autolinks to common element-like symbols e.g. R, X. |
||
Line 4: | Line 4: | ||
-- Elements with wiki links |
-- Elements with wiki links |
||
local am = { |
local am = { |
||
H = " |
H = "Hydrogen", |
||
He = " |
He = "Helium", |
||
Li = " |
Li = "Lithium", |
||
Be = " |
Be = "Beryllium", |
||
B = " |
B = "Boron", |
||
C = " |
C = "Carbon", |
||
N = " |
N = "Nitrogen", |
||
O = " |
O = "Oxygen", |
||
F = " |
F = "Fluorine", |
||
Ne = " |
Ne = "Neon", |
||
Na = " |
Na = "Sodium", |
||
Mg = " |
Mg = "Magnesium", |
||
Al = " |
Al = "Aluminium", |
||
Si = " |
Si = "Silicon", |
||
P = " |
P = "Phosphorus", |
||
S = " |
S = "Sulfur", |
||
Cl = " |
Cl = "Chlorine", |
||
Ar = " |
Ar = "Argon", |
||
K = " |
K = "Potassium", |
||
Ca = " |
Ca = "Calcium", |
||
Sc = " |
Sc = "Scandium", |
||
Ti = " |
Ti = "Titanium", |
||
V = " |
V = "Vanadium", |
||
Cr = " |
Cr = "Chromium", |
||
Mn = " |
Mn = "Manganese", |
||
Fe = " |
Fe = "Iron", |
||
Co = " |
Co = "Cobalt", |
||
Ni = " |
Ni = "Nickel", |
||
Cu = " |
Cu = "Copper", |
||
Zn = " |
Zn = "Zinc", |
||
Ga = " |
Ga = "Gallium", |
||
Ge = " |
Ge = "Germanium", |
||
As = " |
As = "Arsenic", |
||
Se = " |
Se = "Selenium", |
||
Br = " |
Br = "Bromine", |
||
Kr = " |
Kr = "Krypton", |
||
Rb = " |
Rb = "Rubidium", |
||
Sr = " |
Sr = "Strontium", |
||
Y = " |
Y = "Yttrium", |
||
Zr = " |
Zr = "Zirconium", |
||
Nb = " |
Nb = "Niobium", |
||
Mo = " |
Mo = "Molybdenum", |
||
Tc = " |
Tc = "Technetium", |
||
Ru = " |
Ru = "Ruthenium", |
||
Rh = " |
Rh = "Rhodium", |
||
Pd = " |
Pd = "Palladium", |
||
Ag = " |
Ag = "Silver", |
||
Cd = " |
Cd = "Cadmium", |
||
In = " |
In = "Indium", |
||
Sn = " |
Sn = "Tin", |
||
Sb = " |
Sb = "Antimony", |
||
Te = " |
Te = "Tellurium", |
||
I = " |
I = "Iodine", |
||
Xe = " |
Xe = "Xenon", |
||
Cs = " |
Cs = "Caesium", |
||
Ba = " |
Ba = "Barium", |
||
La = " |
La = "Lanthanum", |
||
Ce = " |
Ce = "Cerium", |
||
Pr = " |
Pr = "Praseodymium", |
||
Nd = " |
Nd = "Neodymium", |
||
Pm = " |
Pm = "Promethium", |
||
Sm = " |
Sm = "Samarium", |
||
Eu = " |
Eu = "Europium", |
||
Gd = " |
Gd = "Gadolinium", |
||
Tb = " |
Tb = "Terbium", |
||
Dy = " |
Dy = "Dysprosium", |
||
Ho = " |
Ho = "Holmium", |
||
Er = " |
Er = "Erbium", |
||
Tm = " |
Tm = "Thulium", |
||
Yb = " |
Yb = "Ytterbium", |
||
Lu = " |
Lu = "Lutetium", |
||
Hf = " |
Hf = "Hafnium", |
||
Ta = " |
Ta = "Tantalum", |
||
W = " |
W = "Tungsten", |
||
Re = " |
Re = "Rhenium", |
||
Os = " |
Os = "Osmium", |
||
Ir = " |
Ir = "Iridium", |
||
Pt = " |
Pt = "Platinum", |
||
Au = " |
Au = "Gold", |
||
Hg = " |
Hg = "Mercury (element)", |
||
Tl = " |
Tl = "Thallium", |
||
Pb = " |
Pb = "Lead", |
||
Bi = " |
Bi = "Bismuth", |
||
Po = " |
Po = "Polonium", |
||
At = " |
At = "Astatine", |
||
Rn = " |
Rn = "Radon", |
||
Fr = " |
Fr = "Francium", |
||
Ra = " |
Ra = "Radium", |
||
Ac = " |
Ac = "Actinium", |
||
Th = " |
Th = "Thorium", |
||
Pa = " |
Pa = "Protactinium", |
||
U = " |
U = "Uranium", |
||
Np = " |
Np = "Neptunium", |
||
Pu = " |
Pu = "Plutonium", |
||
Am = " |
Am = "Americium", |
||
Cm = " |
Cm = "Curium", |
||
Bk = " |
Bk = "Berkelium", |
||
Cf = " |
Cf = "Californium", |
||
Es = " |
Es = "Einsteinium", |
||
Fm = " |
Fm = "Fermium", |
||
Md = " |
Md = "Mendelevium", |
||
No = " |
No = "Nobelium", |
||
Lr = " |
Lr = "Lawrencium", |
||
Rf = " |
Rf = "Rutherfordium", |
||
Db = " |
Db = "Dubnium", |
||
Sg = " |
Sg = "Seaborgium", |
||
Bh = " |
Bh = "Bohrium", |
||
Hs = " |
Hs = "Hassium", |
||
Mt = " |
Mt = "Meitnerium", |
||
Ds = " |
Ds = "Darmstadtium", |
||
Rg = " |
Rg = "Roentgenium", |
||
Cp = " |
Cp = "Copernicium", |
||
Nh = " |
Nh = "Nihonium", |
||
Fl = " |
Fl = "Flerovium", |
||
Mc = " |
Mc = "Moscovium", |
||
Lv = " |
Lv = "Livermorium", |
||
Ts = " |
Ts = "Tennessine", |
||
Og = " |
Og = "Oganesson", |
||
-- Groups etc with element-like names |
|||
Bn = 'Benzyl group', |
|||
Bz = 'Benzoyl group', |
|||
D = 'Deuterium', |
|||
Et = 'Ethyl group', |
|||
Ln = 'Lanthanide', |
|||
Nu = 'Nucleophile', |
|||
Ph = 'Phenyl group', |
|||
R = 'Substituent', |
|||
T = 'Tritium', |
|||
Tf = 'Trifluoromethylsulfonyl group', |
|||
X = 'Halogen', |
|||
} |
|||
-- Groups which are redirected from their normal target if wikilinked; never |
|||
-- autolinked. |
|||
⚫ | |||
CH3 = 'Methyl group', |
|||
CO3 = 'Carbonate', |
|||
COOH = 'Carboxyl group', |
|||
ClO = 'Hypochlorite', |
|||
ClO2 = 'Chlorite', |
|||
ClO3 = 'Chlorate', |
|||
ClO4 = 'Perchlorate', |
|||
H2O = 'Water of crystallization', |
|||
H3O = 'Hydronium', |
|||
NH2 = 'Amine group', |
|||
NH4 = 'Ammonium', |
|||
NO3 = 'Nitrate', |
|||
O2 = 'Dioxygen', |
|||
OH = 'Hydroxy group', |
|||
PO3 = 'Phosphite', |
|||
PO4 = 'Phosphate', |
|||
SH = 'Thiol group', |
|||
SO3 = 'Sulfite', |
|||
SO4 = 'Sulfate', |
|||
SeH = 'Selenol group' |
|||
} |
} |
||
Line 140: | Line 178: | ||
local T_UNDERSCORE = 19 -- _{ ... } |
local T_UNDERSCORE = 19 -- _{ ... } |
||
local T_CARET = 20 -- ^{ ... } |
local T_CARET = 20 -- ^{ ... } |
||
local T_LINKOPEN = 21 -- Opening of link, always like "[[target|" even if the source wasn't |
|||
local T_NOCHANGE = 30 -- Anything else like ☃ |
local T_NOCHANGE = 30 -- Anything else like ☃ |
||
Line 158: | Line 197: | ||
function item(f) -- (iterator) returns one token (type, value) at a time from the formula 'f' |
function item(f) -- (iterator) returns one token (type, value) at a time from the formula 'f' |
||
local i = 1 |
local i = 1 |
||
⚫ | |||
return function () |
return function () |
||
local t, x = nil, nil |
local t, x = nil, nil |
||
if ( |
if (i == 1) and f:match('^[0-9]', i) then |
||
x = f:match('^[%d.]+', i); t = T_NOCHANGE; i = i + x:len(); -- matching coefficient (need a space first) |
x = f:match('^[%d.]+', i); t = T_NOCHANGE; i = i + x:len(); -- matching coefficient (need a space first) |
||
Line 177: | Line 215: | ||
if not x then x = f:match('^%(%d*[+-]%)', i); t = T_CHARGE; end -- matching (x+) (xx+), (x-) (xx-) |
if not x then x = f:match('^%(%d*[+-]%)', i); t = T_CHARGE; end -- matching (x+) (xx+), (x-) (xx-) |
||
if not x then x = f:match('^[%d.]+', i); t = T_NUM; end -- matching number |
if not x then x = f:match('^[%d.]+', i); t = T_NUM; end -- matching number |
||
if not x and (f:match('^%[%[%[[^[]', i) or f:match('^%[[^[]', i)) then |
|||
i = i + 1; return T_OPEN, '[' end -- escape [[[X or [X (relevant to auto-linking) |
|||
if not x and f:sub(i, i + 1) == '[[' then |
|||
x = f:match('^%[%[([^]|]*)', i) -- link target |
|||
local len = x:len() + 3 |
|||
x = '[[' .. (groups[x] or am[x] or x) .. '|' -- override link target for common groups |
|||
if f:sub(len + i, len + i) == ']' then |
|||
-- We're going to read the link twice, once as target and once as |
|||
-- chemical markup, e.g. [[CH3]] => "[[CH3|", "CH3]]" |
|||
⚫ | |||
else |
|||
i = i + len |
|||
end |
|||
return T_LINKOPEN, x |
|||
end |
|||
if not x then x = f:match('^[(|{|%[]', i); t = T_OPEN; end -- matching ({[ |
if not x then x = f:match('^[(|{|%[]', i); t = T_OPEN; end -- matching ({[ |
||
if not x then x = f:match('^[)|}|%]]', i); t = T_CLOSE; end |
if not x then x = f:match('^[)|}|%]]', i); t = T_CLOSE; end -- matching )}] |
||
if not x then x = f:match('^[+-]', i); t = T_PM_CHARGE; end |
if not x then x = f:match('^[+-]', i); t = T_PM_CHARGE; end -- matching + or - |
||
if not x then x = f:match('^%*[%d.]*H2O', i); t = T_WATER; end -- Crystal water |
if not x then x = f:match('^%*[%d.]*H2O', i); t = T_WATER; end -- Crystal water |
||
if not x then x = f:match('^%*[%d.]*', i); t = T_CRYSTAL; end -- Crystal |
if not x then x = f:match('^%*[%d.]*', i); t = T_CRYSTAL; end -- Crystal |
||
if not x then x = f:match('^[\\].{%d+}', i); t = T_SPECIAL2; end -- \y{x} |
if not x then x = f:match('^[\\].{%d+}', i); t = T_SPECIAL2; end -- \y{x} |
||
if not x then x = f:match('^[\\].', i); t = T_SPECIAL; end -- \x |
if not x then x = f:match('^[\\].', i); t = T_SPECIAL; end -- \x |
||
if not x then x = f:match('^_{[^}]*}', i); t = T_UNDERSCORE; end -- _{...} |
if not x then x = f:match('^_{[^}]*}', i); t = T_UNDERSCORE; end -- _{...} |
||
if not x then x = f:match('^\^{[^}]*}', i); t = T_CARET; end -- ^{...} |
if not x then x = f:match('^\^{[^}]*}', i); t = T_CARET; end -- ^{...} |
||
if not x then x = f:match('^.', i); t = T_NOCHANGE; end --the rest - one by one |
if not x then x = f:match('^.', i); t = T_NOCHANGE; end --the rest - one by one |
||
if x then i = i + x:len(); else i = i + 999; error("Invalid character in formula! : "..f) end |
if x then i = i + x:len(); else i = i + 999; error("Invalid character in formula! : "..f) end |
||
end |
end |
||
⚫ | |||
return t, x |
return t, x |
||
end |
end |
||
Line 200: | Line 253: | ||
f = string.gsub(f, "−", "-") -- replace – with - (hyphen not minus sign) |
f = string.gsub(f, "−", "-") -- replace – with - (hyphen not minus sign) |
||
⚫ | |||
local formula = '' |
local formula = '' |
||
local t, x |
local t, x |
||
Line 206: | Line 258: | ||
local link = args['link'] or "" |
local link = args['link'] or "" |
||
local auto = args['auto'] or "" |
local auto = args['auto'] or "" |
||
⚫ | |||
local _debug = false |
|||
if not (link == '') then formula = formula .. "[[" .. link .. "|"; end -- wikilink start [[link| |
if not (link == '') then formula = formula .. "[[" .. link .. "|"; end -- wikilink start [[link| |
||
for t, x in item(f) do |
for t, x in item(f) do |
||
if _debug then |
|||
formula = ("%s\n* %d %s"):format(formula, t, x) |
|||
elseif t == T_ELEM then |
|||
if (auto == '') or (not am[x]) or seen[x] then formula = formula .. x |
|||
else formula = ("%s[[%s|%s]]"):format(formula, am[x], x); seen[x] = true |
|||
end |
end |
||
elseif t == T_COEFFICIENT then formula = formula .. x |
elseif t == T_COEFFICIENT then formula = formula .. x |
||
elseif t == T_NUM then formula = formula .. su("", x); |
elseif t == T_NUM then formula = formula .. su("", x); |
||
elseif t == |
elseif t == T_LINKOPEN then formula = formula .. x; -- [[Link| |
||
elseif t == |
elseif t == T_OPEN then formula = formula .. x; -- ([{ |
||
elseif t == |
elseif t == T_CLOSE then formula = formula .. x; -- )]} |
||
elseif t == T_PM_CHARGE then formula = formula .. su(x:gsub("-", "−"), ""); |
|||
elseif t == T_SUF_CHARGE then |
elseif t == T_SUF_CHARGE then |
||
formula = formula .. su(string.gsub( |
formula = formula .. su(string.gsub(x:match("[+-]"), "-", "−"), x:match("%d+"), ""); |
||
elseif t == T_SUF_CHARGE2 then |
elseif t == T_SUF_CHARGE2 then |
||
formula = formula .. su(string.sub(string.gsub( |
formula = formula .. su(string.sub(string.gsub(x:match("%(%d*[+-]"), "-", "−"), 2, -1), x:match("%d+")) |
||
elseif t == T_CHARGE then formula = formula .. "<sup>" |
elseif t == T_CHARGE then |
||
formula = formula .. "<sup>" |
|||
if x:match("%d+") then formula = formula .. x:match("%d+"); end |
|||
formula = formula .. x:match("[%+-]"):gsub("-", "−") .. "</sup>"; |
|||
-- Cannot concatenat a nil value from x:match("%d+"); |
|||
elseif t == T_CRYSTAL then formula = formula .. DotIt() .. string.gsub( x, "*", '', 1 ); |
elseif t == T_CRYSTAL then formula = formula .. DotIt() .. string.gsub( x, "*", '', 1 ); |
||
elseif t == T_SPECIAL then |
elseif t == T_SPECIAL then |
||
parameter = |
parameter = x:sub(2, 2) -- x fra \x |
||
if parameter == "s" then formula = formula .. "−" -- single bond |
if parameter == "s" then formula = formula .. "−" -- single bond |
||
elseif parameter == "d" then formula = formula .. "=" -- double bond |
elseif parameter == "d" then formula = formula .. "=" -- double bond |
||
Line 239: | Line 299: | ||
end |
end |
||
elseif t == T_SPECIAL2 then -- \y{x} |
elseif t == T_SPECIAL2 then -- \y{x} |
||
parameter = |
parameter = x:sub(2, 2) -- y fra \y{x} |
||
if parameter == "h" then --[[Hapticity]] |
if parameter == "h" then --[[Hapticity]] |
||
if (auto == '') then formula = formula .. "η<sup>" .. |
if (auto == '') then formula = formula .. "η<sup>" .. x:match('%d+') .. "</sup>-" |
||
else |
else |
||
formula = formula .. "[[Hapticity|η<sup>" .. |
formula = formula .. "[[Hapticity|η<sup>" .. x:match('%d+') .. "</sup>]]-" |
||
end |
end |
||
elseif parameter == "m" then formula = formula .. "μ<sub>" .. |
elseif parameter == "m" then formula = formula .. "μ<sub>" .. x:match('%d+') .. "</sub>-" -- mu ([[bridging ligand]]) |
||
end |
end |
||
elseif t == T_WATER then |
elseif t == T_WATER then |
||
if |
if x:match("^%*[%d.]") then |
||
formula = formula .. DotIt() .. |
formula = formula .. DotIt() .. x:match("%f[%.%d]%d*%.?%d*%f[^%.%d%]]") .. "H<sub>2</sub>O"; |
||
else |
else |
||
formula = formula .. DotIt() .. "H<sub>2</sub>O"; |
formula = formula .. DotIt() .. "H<sub>2</sub>O"; |
||
end |
end |
||
elseif t == T_UNDERSCORE then formula = formula .. su("", |
elseif t == T_UNDERSCORE then formula = formula .. su("", x:sub(3,-2)) -- x contains _{string} |
||
elseif t == T_CARET then formula = formula .. su( |
elseif t == T_CARET then formula = formula .. su(x:sub(3,-2), "") -- x contains ^{string} |
||
elseif t == T_ARROW_R then formula = formula .. " → " |
elseif t == T_ARROW_R then formula = formula .. " → " |
||
elseif t == T_ARROW_EQ then formula = formula .. " ⇌ " |
elseif t == T_ARROW_EQ then formula = formula .. " ⇌ " |
||
elseif t == T_NOCHANGE then formula = formula .. x; -- The rest - everything which isn't captured by the regular expresions. |
elseif t == T_NOCHANGE then formula = formula .. x; -- The rest - everything which isn't captured by the regular expresions. |
||
else error('unreachable - ???') end -- in fact, unreachable |
else error('unreachable - ???') end -- in fact, unreachable |
||
Line 267: | Line 327: | ||
if args[2] or args[3] or args[4] then |
if args[2] or args[3] or args[4] then |
||
formula = formula .. require('Module:If preview')._warning{ |
formula = formula .. require('Module:If preview')._warning{ |
||
'{{chem2}} was called with multiple positional arguments. It should have just one, e.g. {{chem2|H2O}}.' |
|||
} |
} |
||
end |
end |
||
Line 276: | Line 336: | ||
local args = getArgs(frame) |
local args = getArgs(frame) |
||
return p._chem(args) |
return p._chem(args) |
||
end |
|||
-- PRIVATE function to generate documentation. |
|||
function p._autodoc(frame) |
|||
local TableTools = require('Module:TableTools') -- we don't want to load this on articles for no reason |
|||
local result = { |
|||
'===Elements and element-style symbols===\nThese may be automatically linked or used as if they were redirects.\n', |
|||
'{| class="wikitable"\n! Symbol !! Link target\n' |
|||
} |
|||
for symbol, target in TableTools.sortedPairs(am) do |
|||
result[#result + 1] = ('|-\n| %s || [[%s]]\n'):format(symbol, target) |
|||
end |
|||
result[#result + 1] = '|}\n===Groups===\nThese must be linked manually as if they were redirects.\n' |
|||
result[#result + 1] = '{| class="wikitable"\n! Symbol !! Link target\n' |
|||
for symbol, target in TableTools.sortedPairs(groups) do |
|||
result[#result + 1] = ('|-\n| %s || [[%s]]\n'):format(symbol, target) |
|||
end |
|||
result[#result + 1] = '|}' |
|||
return table.concat(result) |
|||
end |
end |
||
Revision as of 12:11, 8 January 2022
![]() | This module is rated as ready for general use. It has reached a mature form and is thought to be relatively bug-free and ready for use wherever appropriate. It is ready to mention on help pages and other Wikipedia resources as an option for new users to learn. To reduce server load and bad output, it should be improved by sandbox testing rather than repeated trial-and-error editing. |
![]() | This module is subject to page protection. It is a highly visible module in use by a very large number of pages, or is substituted very frequently. Because vandalism or mistakes would affect many pages, and even trivial editing might cause substantial load on the servers, it is protected from editing. |
![]() | This Lua module is used on approximately 8,100 pages and changes may be widely noticed. Test changes in the module's /sandbox or /testcases subpages, or in your own module sandbox. Consider discussing changes on the talk page before implementing them. |
![]() | This module depends on the following other modules: |
![]() | This module uses TemplateStyles: |
This module implements {{chem2}}. Please see its documentation for details.
local getArgs = require('Module:Arguments').getArgs
local p = {} -- module's table
-- Elements with wiki links
local am = {
H = "Hydrogen",
He = "Helium",
Li = "Lithium",
Be = "Beryllium",
B = "Boron",
C = "Carbon",
N = "Nitrogen",
O = "Oxygen",
F = "Fluorine",
Ne = "Neon",
Na = "Sodium",
Mg = "Magnesium",
Al = "Aluminium",
Si = "Silicon",
P = "Phosphorus",
S = "Sulfur",
Cl = "Chlorine",
Ar = "Argon",
K = "Potassium",
Ca = "Calcium",
Sc = "Scandium",
Ti = "Titanium",
V = "Vanadium",
Cr = "Chromium",
Mn = "Manganese",
Fe = "Iron",
Co = "Cobalt",
Ni = "Nickel",
Cu = "Copper",
Zn = "Zinc",
Ga = "Gallium",
Ge = "Germanium",
As = "Arsenic",
Se = "Selenium",
Br = "Bromine",
Kr = "Krypton",
Rb = "Rubidium",
Sr = "Strontium",
Y = "Yttrium",
Zr = "Zirconium",
Nb = "Niobium",
Mo = "Molybdenum",
Tc = "Technetium",
Ru = "Ruthenium",
Rh = "Rhodium",
Pd = "Palladium",
Ag = "Silver",
Cd = "Cadmium",
In = "Indium",
Sn = "Tin",
Sb = "Antimony",
Te = "Tellurium",
I = "Iodine",
Xe = "Xenon",
Cs = "Caesium",
Ba = "Barium",
La = "Lanthanum",
Ce = "Cerium",
Pr = "Praseodymium",
Nd = "Neodymium",
Pm = "Promethium",
Sm = "Samarium",
Eu = "Europium",
Gd = "Gadolinium",
Tb = "Terbium",
Dy = "Dysprosium",
Ho = "Holmium",
Er = "Erbium",
Tm = "Thulium",
Yb = "Ytterbium",
Lu = "Lutetium",
Hf = "Hafnium",
Ta = "Tantalum",
W = "Tungsten",
Re = "Rhenium",
Os = "Osmium",
Ir = "Iridium",
Pt = "Platinum",
Au = "Gold",
Hg = "Mercury (element)",
Tl = "Thallium",
Pb = "Lead",
Bi = "Bismuth",
Po = "Polonium",
At = "Astatine",
Rn = "Radon",
Fr = "Francium",
Ra = "Radium",
Ac = "Actinium",
Th = "Thorium",
Pa = "Protactinium",
U = "Uranium",
Np = "Neptunium",
Pu = "Plutonium",
Am = "Americium",
Cm = "Curium",
Bk = "Berkelium",
Cf = "Californium",
Es = "Einsteinium",
Fm = "Fermium",
Md = "Mendelevium",
No = "Nobelium",
Lr = "Lawrencium",
Rf = "Rutherfordium",
Db = "Dubnium",
Sg = "Seaborgium",
Bh = "Bohrium",
Hs = "Hassium",
Mt = "Meitnerium",
Ds = "Darmstadtium",
Rg = "Roentgenium",
Cp = "Copernicium",
Nh = "Nihonium",
Fl = "Flerovium",
Mc = "Moscovium",
Lv = "Livermorium",
Ts = "Tennessine",
Og = "Oganesson",
-- Groups etc with element-like names
Bn = 'Benzyl group',
Bz = 'Benzoyl group',
D = 'Deuterium',
Et = 'Ethyl group',
Ln = 'Lanthanide',
Nu = 'Nucleophile',
Ph = 'Phenyl group',
R = 'Substituent',
T = 'Tritium',
Tf = 'Trifluoromethylsulfonyl group',
X = 'Halogen',
}
-- Groups which are redirected from their normal target if wikilinked; never
-- autolinked.
local groups = {
CH3 = 'Methyl group',
CO3 = 'Carbonate',
COOH = 'Carboxyl group',
ClO = 'Hypochlorite',
ClO2 = 'Chlorite',
ClO3 = 'Chlorate',
ClO4 = 'Perchlorate',
H2O = 'Water of crystallization',
H3O = 'Hydronium',
NH2 = 'Amine group',
NH4 = 'Ammonium',
NO3 = 'Nitrate',
O2 = 'Dioxygen',
OH = 'Hydroxy group',
PO3 = 'Phosphite',
PO4 = 'Phosphate',
SH = 'Thiol group',
SO3 = 'Sulfite',
SO4 = 'Sulfate',
SeH = 'Selenol group'
}
local T_ELEM = 0 -- token types
local T_NUM = 1 -- number
local T_OPEN = 2 -- open '('
local T_CLOSE = 3 -- close ')'
local T_PM_CHARGE = 4 -- + or −
local T_WATER = 6 -- .xH2O x number
local T_CRYSTAL = 9 -- .x
local T_CHARGE = 8 -- charge (x+), (x-)
local T_SUF_CHARGE = 10 -- suffix and charge e.g. 2+ from H2+
local T_SUF_CHARGE2 = 12 -- suffix and (charge) e.g. 2(2+) from He2(2+)
local T_SPECIAL = 14 -- starting with \ e.g. \d for double bond (=)
local T_SPECIAL2 = 16 -- starting with \y{x} e.g. \i{12} for isotope with mass number 12
local T_ARROW_R = 17 -- match: ->
local T_ARROW_EQ = 18 -- match: <->
local T_UNDERSCORE = 19 -- _{ ... }
local T_CARET = 20 -- ^{ ... }
local T_LINKOPEN = 21 -- Opening of link, always like "[[target|" even if the source wasn't
local T_NOCHANGE = 30 -- Anything else like ☃
function su(up, down)
if up == "" then
return ('<sub class="template-chem2-sub">%s</sub>'):format(down)
end
if down == "" then
return ('<sup class="template-chem2-sup">%s</sup>'):format(up)
end
return ('<span class="template-chem2-su"><span>%s</span><span>%s</span></span>'):format(up, down)
end
function DotIt()
return '·'
end
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 == 1) and f:match('^[0-9]', i) then
x = f:match('^[%d.]+', i); t = T_NOCHANGE; i = i + x:len(); -- matching coefficient (need a space first)
elseif i <= f:len() then
x = f:match('^%s+[%d.]+', i); t = T_NOCHANGE; -- matching coefficient (need a space first)
if not x then x = f:match('^%s[+]', i); t = T_NOCHANGE; end -- matching + (H2O + H2O)
if not x then x = f:match('^%&%#[%w%d]+%;', i); t = T_NOCHANGE; end -- &#...;
if not x then x = f:match('^%<%-%>', i); t = T_ARROW_EQ; end -- matching <->
if not x then x = f:match('^%-%>', i); t = T_ARROW_R; end -- matching ->
if not x then x = f:match('^%u%l*', i); t = T_ELEM; end -- matching symbols like Aaaaa
if not x then x = f:match('^%d+[+-]', i); t = T_SUF_CHARGE; end -- matching x+, x-
if not x then x = f:match('^%d+%(%d*[+-]%)', i); t = T_SUF_CHARGE2; end -- matching x(y+/-), x(+/-)
if not x then x = f:match('^%(%d*[+-]%)', i); t = T_CHARGE; end -- matching (x+) (xx+), (x-) (xx-)
if not x then x = f:match('^[%d.]+', i); t = T_NUM; end -- matching number
if not x and (f:match('^%[%[%[[^[]', i) or f:match('^%[[^[]', i)) then
i = i + 1; return T_OPEN, '[' end -- escape [[[X or [X (relevant to auto-linking)
if not x and f:sub(i, i + 1) == '[[' then
x = f:match('^%[%[([^]|]*)', i) -- link target
local len = x:len() + 3
x = '[[' .. (groups[x] or am[x] or x) .. '|' -- override link target for common groups
if f:sub(len + i, len + i) == ']' then
-- We're going to read the link twice, once as target and once as
-- chemical markup, e.g. [[CH3]] => "[[CH3|", "CH3]]"
i = i + 2
else
i = i + len
end
return T_LINKOPEN, x
end
if not x then x = f:match('^[(|{|%[]', i); t = T_OPEN; end -- matching ({[
if not x then x = f:match('^[)|}|%]]', i); t = T_CLOSE; end -- matching )}]
if not x then x = f:match('^[+-]', i); t = T_PM_CHARGE; end -- matching + or -
if not x then x = f:match('^%*[%d.]*H2O', i); t = T_WATER; end -- Crystal water
if not x then x = f:match('^%*[%d.]*', i); t = T_CRYSTAL; end -- Crystal
if not x then x = f:match('^[\\].{%d+}', i); t = T_SPECIAL2; end -- \y{x}
if not x then x = f:match('^[\\].', i); t = T_SPECIAL; end -- \x
if not x then x = f:match('^_{[^}]*}', i); t = T_UNDERSCORE; end -- _{...}
if not x then x = f:match('^\^{[^}]*}', i); t = T_CARET; end -- ^{...}
if not x then x = f:match('^.', i); t = T_NOCHANGE; end --the rest - one by one
if x then i = i + x:len(); else i = i + 999; error("Invalid character in formula! : "..f) end
end
return t, x
end
end
function p._chem(args)
local f = args[1] or ''
f = string.gsub(f, "–", "-") -- replace – with - (hyphen not ndash)
f = string.gsub(f, "−", "-") -- replace – with - (hyphen not minus sign)
local formula = ''
local t, x
local link = args['link'] or ""
local auto = args['auto'] or ""
local seen = {}
local _debug = false
if not (link == '') then formula = formula .. "[[" .. link .. "|"; end -- wikilink start [[link|
for t, x in item(f) do
if _debug then
formula = ("%s\n* %d %s"):format(formula, t, x)
elseif t == T_ELEM then
if (auto == '') or (not am[x]) or seen[x] then formula = formula .. x
else formula = ("%s[[%s|%s]]"):format(formula, am[x], x); seen[x] = true
end
elseif t == T_COEFFICIENT then formula = formula .. x
elseif t == T_NUM then formula = formula .. su("", x);
elseif t == T_LINKOPEN then formula = formula .. x; -- [[Link|
elseif t == T_OPEN then formula = formula .. x; -- ([{
elseif t == T_CLOSE then formula = formula .. x; -- )]}
elseif t == T_PM_CHARGE then formula = formula .. su(x:gsub("-", "−"), "");
elseif t == T_SUF_CHARGE then
formula = formula .. su(string.gsub(x:match("[+-]"), "-", "−"), x:match("%d+"), "");
elseif t == T_SUF_CHARGE2 then
formula = formula .. su(string.sub(string.gsub(x:match("%(%d*[+-]"), "-", "−"), 2, -1), x:match("%d+"))
elseif t == T_CHARGE then
formula = formula .. "<sup>"
if x:match("%d+") then formula = formula .. x:match("%d+"); end
formula = formula .. x:match("[%+-]"):gsub("-", "−") .. "</sup>";
-- Cannot concatenat a nil value from x:match("%d+");
elseif t == T_CRYSTAL then formula = formula .. DotIt() .. string.gsub( x, "*", '', 1 );
elseif t == T_SPECIAL then
parameter = x:sub(2, 2) -- x fra \x
if parameter == "s" then formula = formula .. "−" -- single bond
elseif parameter == "d" then formula = formula .. "=" -- double bond
elseif parameter == "t" then formula = formula .. "≡" -- tripple bond
elseif parameter == "q" then formula = formula .. "≣" -- Quadruple bond
elseif parameter == "h" then formula = formula .. "η" -- η, hapticity
elseif parameter == "*" then formula = formula .. "*" -- *, normal *
elseif parameter == "-" then formula = formula .. "-" -- -
elseif parameter == "\\" then formula = formula .. "\\" -- \
elseif parameter == "\'" then formula = formula .. "'" -- html-code for '
end
elseif t == T_SPECIAL2 then -- \y{x}
parameter = x:sub(2, 2) -- y fra \y{x}
if parameter == "h" then --[[Hapticity]]
if (auto == '') then formula = formula .. "η<sup>" .. x:match('%d+') .. "</sup>-"
else
formula = formula .. "[[Hapticity|η<sup>" .. x:match('%d+') .. "</sup>]]-"
end
elseif parameter == "m" then formula = formula .. "μ<sub>" .. x:match('%d+') .. "</sub>-" -- mu ([[bridging ligand]])
end
elseif t == T_WATER then
if x:match("^%*[%d.]") then
formula = formula .. DotIt() .. x:match("%f[%.%d]%d*%.?%d*%f[^%.%d%]]") .. "H<sub>2</sub>O";
else
formula = formula .. DotIt() .. "H<sub>2</sub>O";
end
elseif t == T_UNDERSCORE then formula = formula .. su("", x:sub(3,-2)) -- x contains _{string}
elseif t == T_CARET then formula = formula .. su(x:sub(3,-2), "") -- x contains ^{string}
elseif t == T_ARROW_R then formula = formula .. " → "
elseif t == T_ARROW_EQ then formula = formula .. " ⇌ "
elseif t == T_NOCHANGE then formula = formula .. x; -- The rest - everything which isn't captured by the regular expresions.
else error('unreachable - ???') end -- in fact, unreachable
end
if not (link == nil or link == '') then formula = formula .. "]]"; end -- wikilink closing ]]
formula = mw.getCurrentFrame():preprocess('<templatestyles src="Module:Chem2/styles.css"/>') ..
'<span class="chemf nowrap">' .. formula .. '</span>'
if args[2] or args[3] or args[4] then
formula = formula .. require('Module:If preview')._warning{
'{{chem2}} was called with multiple positional arguments. It should have just one, e.g. {{chem2|H2O}}.'
}
end
return formula
end
function p.chem(frame)
local args = getArgs(frame)
return p._chem(args)
end
-- PRIVATE function to generate documentation.
function p._autodoc(frame)
local TableTools = require('Module:TableTools') -- we don't want to load this on articles for no reason
local result = {
'===Elements and element-style symbols===\nThese may be automatically linked or used as if they were redirects.\n',
'{| class="wikitable"\n! Symbol !! Link target\n'
}
for symbol, target in TableTools.sortedPairs(am) do
result[#result + 1] = ('|-\n| %s || [[%s]]\n'):format(symbol, target)
end
result[#result + 1] = '|}\n===Groups===\nThese must be linked manually as if they were redirects.\n'
result[#result + 1] = '{| class="wikitable"\n! Symbol !! Link target\n'
for symbol, target in TableTools.sortedPairs(groups) do
result[#result + 1] = ('|-\n| %s || [[%s]]\n'):format(symbol, target)
end
result[#result + 1] = '|}'
return table.concat(result)
end
return p