chem-str.lua /size: 32 Kb    last modification: 2020-07-01 14:35
1
if
not
modules
then
modules
=
{
}
end
modules
[
'
chem-str
'
]
=
{
2
version
=
1
.
001
,
3
comment
=
"
companion to chem-str.mkiv
"
,
4
author
=
"
Hans Hagen and Alan Braslau
"
,
5
copyright
=
"
PRAGMA ADE / ConTeXt Development Team
"
,
6
license
=
"
see context related readme files
"
7
}
8 9
-- The original \PPCHTEX\ code was written in pure \TEX\, although later we made
10
-- the move from \PICTEX\ to \METAPOST\. The current implementation is a mix between
11
-- \TEX\, \LUA\ and \METAPOST. Although the first objective is to get a compatible
12
-- but better implementation, later versions might provide more.
13
--
14
-- Well, the later version has arrived as Alan took it upon him to make the code
15
-- deviate even further from the original implementation. The original (early \MKII)
16
-- variant operated within the boundaries of \PICTEX\ and as it supported MetaPost as
17
-- alternative output. As a consequence it still used a stepwise graphic construction
18
-- approach. As we used \TEX\ for parsing, the syntax was more rigid than it is now.
19
-- This new variant uses a more mathematical and metapostisch approach. In the process
20
-- more rendering variants have been added and alignment has been automated. As a result
21
-- the current user interface is slightly different from the old one but hopefully users
22
-- will like the added value.
23 24
-- directive_strictorder: one might set this to off when associated texts are disordered too
25 26
local
trace_structure
=
false
trackers
.
register
(
"
chemistry.structure
"
,
function
(
v
)
trace_structure
=
v
end
)
27
local
trace_metapost
=
false
trackers
.
register
(
"
chemistry.metapost
"
,
function
(
v
)
trace_metapost
=
v
end
)
28
local
trace_boundingbox
=
false
trackers
.
register
(
"
chemistry.boundingbox
"
,
function
(
v
)
trace_boundingbox
=
v
end
)
29
local
trace_textstack
=
false
trackers
.
register
(
"
chemistry.textstack
"
,
function
(
v
)
trace_textstack
=
v
end
)
30
local
directive_strictorder
=
true
directives
.
register
(
"
chemistry.strictorder
"
,
function
(
v
)
directive_strictorder
=
v
end
)
31
local
directive_strictindex
=
false
directives
.
register
(
"
chemistry.strictindex
"
,
function
(
v
)
directive_strictindex
=
v
end
)
32 33
local
report_chemistry
=
logs
.
reporter
(
"
chemistry
"
)
34 35
local
tonumber
=
tonumber
36
local
format
,
gmatch
,
match
,
lower
,
gsub
=
string
.
format
,
string
.
gmatch
,
string
.
match
,
string
.
lower
,
string
.
gsub
37
local
concat
,
insert
,
remove
,
unique
,
sorted
=
table
.
concat
,
table
.
insert
,
table
.
remove
,
table
.
unique
,
table
.
sorted
38
local
processor_tostring
=
typesetters
and
typesetters
.
processors
.
tostring
39
local
settings_to_array
=
utilities
.
parsers
.
settings_to_array
40
local
settings_to_array_with_repeat
=
utilities
.
parsers
.
settings_to_array_with_repeat
41 42
local
lpegmatch
=
lpeg
.
match
43
local
P
,
R
,
S
,
C
,
Cs
,
Ct
,
Cc
,
Cmt
=
lpeg
.
P
,
lpeg
.
R
,
lpeg
.
S
,
lpeg
.
C
,
lpeg
.
Cs
,
lpeg
.
Ct
,
lpeg
.
Cc
,
lpeg
.
Cmt
44 45
local
variables
=
interfaces
and
interfaces
.
variables
46
local
commands
=
commands
47
local
context
=
context
48
local
implement
=
interfaces
.
implement
49 50
local
formatters
=
string
.
formatters
51 52
local
v_default
=
variables
.
default
53
local
v_small
=
variables
.
small
54
local
v_medium
=
variables
.
medium
55
local
v_big
=
variables
.
big
56
local
v_normal
=
variables
.
normal
57
local
v_fit
=
variables
.
fit
58
local
v_on
=
variables
.
on
59
local
v_none
=
variables
.
none
60 61
local
topoints
=
number
.
topoints
62
local
todimen
=
string
.
todimen
63 64
local
trialtypesetting
=
context
.
trialtypesetting
65 66
chemistry
=
chemistry
or
{
}
67
local
chemistry
=
chemistry
68 69
chemistry
.
instance
=
"
chemistry
"
70
chemistry
.
format
=
"
metafun
"
71
chemistry
.
method
=
"
double
"
72 73
local
nofstructures
=
0
74 75
local
common_keys
=
{
76
b
=
"
line
"
,
77
r
=
"
line
"
,
78
sb
=
"
line
"
,
79
sr
=
"
line
"
,
80
rd
=
"
line
"
,
81
rh
=
"
line
"
,
82
rb
=
"
line
"
,
83
rbd
=
"
line
"
,
84
cc
=
"
line
"
,
85
ccd
=
"
line
"
,
86
line
=
"
line
"
,
87
dash
=
"
line
"
,
88
arrow
=
"
line
"
,
89
c
=
"
fixed
"
,
90
cd
=
"
fixed
"
,
91
z
=
"
text
"
,
92
zt
=
"
text
"
,
93
zlt
=
"
text
"
,
94
zrt
=
"
text
"
,
95
rz
=
"
text
"
,
96
rt
=
"
text
"
,
97
lrt
=
"
text
"
,
98
rrt
=
"
text
"
,
99
label
=
"
text
"
,
100
zln
=
"
number
"
,
101
zrn
=
"
number
"
,
102
rn
=
"
number
"
,
103
lrn
=
"
number
"
,
104
rrn
=
"
number
"
,
105
zn
=
"
number
"
,
106
number
=
"
number
"
,
107
mov
=
"
transform
"
,
108
mark
=
"
transform
"
,
109
move
=
"
transform
"
,
110
diff
=
"
transform
"
,
111
off
=
"
transform
"
,
112
adj
=
"
transform
"
,
113
sub
=
"
transform
"
,
114
}
115 116
local
front_keys
=
{
117
bb
=
"
line
"
,
118
eb
=
"
line
"
,
119
rr
=
"
line
"
,
120
lr
=
"
line
"
,
121
lsr
=
"
line
"
,
122
rsr
=
"
line
"
,
123
lrd
=
"
line
"
,
124
rrd
=
"
line
"
,
125
lrh
=
"
line
"
,
126
rrh
=
"
line
"
,
127
lrbd
=
"
line
"
,
128
rrbd
=
"
line
"
,
129
lrb
=
"
line
"
,
130
rrb
=
"
line
"
,
131
lrz
=
"
text
"
,
132
rrz
=
"
text
"
,
133
lsub
=
"
transform
"
,
134
rsub
=
"
transform
"
,
135
}
136 137
local
one_keys
=
{
138
db
=
"
line
"
,
139
tb
=
"
line
"
,
140
bb
=
"
line
"
,
141
dr
=
"
line
"
,
142
hb
=
"
line
"
,
143
bd
=
"
line
"
,
144
bw
=
"
line
"
,
145
oe
=
"
line
"
,
146
sd
=
"
line
"
,
147
rdb
=
"
line
"
,
148
ldb
=
"
line
"
,
149
ldd
=
"
line
"
,
150
rdd
=
"
line
"
,
151
ep
=
"
line
"
,
152
es
=
"
line
"
,
153
ed
=
"
line
"
,
154
et
=
"
line
"
,
155
au
=
"
line
"
,
156
ad
=
"
line
"
,
157
cz
=
"
text
"
,
158
rot
=
"
transform
"
,
159
dir
=
"
transform
"
,
160
rm
=
"
transform
"
,
161
mir
=
"
transform
"
,
162
}
163 164
local
ring_keys
=
{
165
db
=
"
line
"
,
166
hb
=
"
line
"
,
167
br
=
"
line
"
,
168
lr
=
"
line
"
,
169
rr
=
"
line
"
,
170
lsr
=
"
line
"
,
171
rsr
=
"
line
"
,
172
lrd
=
"
line
"
,
173
rrd
=
"
line
"
,
174
lrb
=
"
line
"
,
175
rrb
=
"
line
"
,
176
lrh
=
"
line
"
,
177
rrh
=
"
line
"
,
178
lrbd
=
"
line
"
,
179
rrbd
=
"
line
"
,
180
dr
=
"
line
"
,
181
eb
=
"
line
"
,
182
er
=
"
line
"
,
183
ed
=
"
line
"
,
184
au
=
"
line
"
,
185
ad
=
"
line
"
,
186
s
=
"
line
"
,
187
ss
=
"
line
"
,
188
mid
=
"
line
"
,
189
mids
=
"
line
"
,
190
midz
=
"
text
"
,
191
lrz
=
"
text
"
,
192
rrz
=
"
text
"
,
193
crz
=
"
text
"
,
194
rot
=
"
transform
"
,
195
mir
=
"
transform
"
,
196
adj
=
"
transform
"
,
197
lsub
=
"
transform
"
,
198
rsub
=
"
transform
"
,
199
rm
=
"
transform
"
,
200
}
201 202
-- table.setmetatableindex(front_keys,common_keys)
203
-- table.setmetatableindex(one_keys,common_keys)
204
-- table.setmetatableindex(ring_keys,common_keys)
205 206
-- or (faster but not needed here):
207 208
front_keys
=
table
.
merged
(
front_keys
,
common_keys
)
209
one_keys
=
table
.
merged
(
one_keys
,
common_keys
)
210
ring_keys
=
table
.
merged
(
ring_keys
,
common_keys
)
211 212
local
syntax
=
{
213
carbon
=
{
max
=
4
,
keys
=
one_keys
,
}
,
214
alkyl
=
{
max
=
4
,
keys
=
one_keys
,
}
,
215
newmanstagger
=
{
max
=
6
,
keys
=
one_keys
,
}
,
216
newmaneclipsed
=
{
max
=
6
,
keys
=
one_keys
,
}
,
217
one
=
{
max
=
8
,
keys
=
one_keys
,
}
,
218
three
=
{
max
=
3
,
keys
=
ring_keys
,
}
,
219
four
=
{
max
=
4
,
keys
=
ring_keys
,
}
,
220
five
=
{
max
=
5
,
keys
=
ring_keys
,
}
,
221
six
=
{
max
=
6
,
keys
=
ring_keys
,
}
,
222
seven
=
{
max
=
7
,
keys
=
ring_keys
,
}
,
223
eight
=
{
max
=
8
,
keys
=
ring_keys
,
}
,
224
nine
=
{
max
=
9
,
keys
=
ring_keys
,
}
,
225
fivefront
=
{
max
=
5
,
keys
=
front_keys
,
}
,
226
sixfront
=
{
max
=
6
,
keys
=
front_keys
,
}
,
227
chair
=
{
max
=
6
,
keys
=
front_keys
,
}
,
228
boat
=
{
max
=
6
,
keys
=
front_keys
,
}
,
229
pb
=
{
direct
=
'
chem_pb;
'
}
,
230
pe
=
{
direct
=
'
chem_pe;
'
}
,
231
save
=
{
direct
=
'
chem_save;
'
}
,
232
restore
=
{
direct
=
'
chem_restore;
'
}
,
233
chem
=
{
direct
=
formatters
[
'
chem_symbol("\\chemicaltext{%s}");
'
]
,
arguments
=
1
}
,
234
space
=
{
direct
=
'
chem_symbol("\\chemicalsymbol[space]");
'
}
,
235
plus
=
{
direct
=
'
chem_symbol("\\chemicalsymbol[plus]");
'
}
,
236
minus
=
{
direct
=
'
chem_symbol("\\chemicalsymbol[minus]");
'
}
,
237
equals
=
{
direct
=
'
chem_symbol("\\chemicalsymbol[equals]");
'
}
,
238
gives
=
{
direct
=
formatters
[
'
chem_symbol("\\chemicalsymbol[gives]{%s}{%s}");
'
]
,
arguments
=
2
}
,
239
equilibrium
=
{
direct
=
formatters
[
'
chem_symbol("\\chemicalsymbol[equilibrium]{%s}{%s}");
'
]
,
arguments
=
2
}
,
240
mesomeric
=
{
direct
=
formatters
[
'
chem_symbol("\\chemicalsymbol[mesomeric]{%s}{%s}");
'
]
,
arguments
=
2
}
,
241
opencomplex
=
{
direct
=
'
chem_symbol("\\chemicalsymbol[opencomplex]");
'
}
,
242
closecomplex
=
{
direct
=
'
chem_symbol("\\chemicalsymbol[closecomplex]");
'
}
,
243
reset
=
{
direct
=
'
chem_reset;
'
}
,
244
mp
=
{
direct
=
formatters
[
'
%s
'
]
,
arguments
=
1
}
,
-- backdoor MP code - dangerous!
245
}
246 247
chemistry
.
definitions
=
chemistry
.
definitions
or
{
}
248
local
definitions
=
chemistry
.
definitions
249 250
storage
.
register
(
"
chemistry/definitions
"
,
definitions
,
"
chemistry.definitions
"
)
251 252
function
chemistry
.
undefine
(
name
)
253
definitions
[
lower
(
name
)
]
=
nil
254
end
255 256
function
chemistry
.
define
(
name
,
spec
,
text
)
257
name
=
lower
(
name
)
258
local
dn
=
definitions
[
name
]
259
if
not
dn
then
260
dn
=
{
}
261
definitions
[
name
]
=
dn
262
end
263
dn
[
#
dn
+
1
]
=
{
264
spec
=
settings_to_array_with_repeat
(
spec
,
true
)
,
265
text
=
settings_to_array_with_repeat
(
text
,
true
)
,
266
}
267
end
268 269
local
metacode
,
variant
,
keys
,
max
,
txt
,
pstack
,
sstack
,
align
270
local
molecule
=
chemistry
.
molecule
-- or use lpegmatch(chemistry.moleculeparser,...)
271 272
local
function
fetch
(
txt
)
273
local
st
=
stack
[
txt
]
274
local
t
=
st
.
text
[
st
.
n
]
275
while
not
t
and
txt
>
1
do
276
txt
=
txt
-
1
277
st
=
stack
[
txt
]
278
t
=
st
.
text
[
st
.
n
]
279
end
280
if
t
then
281
if
trace_textstack
then
282
report_chemistry
(
"
fetching from stack %a, slot %a, data %a
"
,
txt
,
st
.
n
,
t
)
283
end
284
st
.
n
=
st
.
n
+
1
285
end
286
return
txt
,
t
287
end
288 289
local
remapper
=
{
290
[
"
+
"
]
=
"
p
"
,
291
[
"
-
"
]
=
"
m
"
,
292
[
"
--
"
]
=
"
mm
"
,
293
[
"
++
"
]
=
"
pp
"
,
294
}
295 296
local
dchrs
=
R
(
"
09
"
)
297
local
sign
=
S
(
"
+-
"
)
298
local
digit
=
dchrs
/
tonumber
299
local
amount
=
(
sign
^
-1
*
(
dchrs
^
0
*
P
(
'
.
'
)
)
^
-1
*
dchrs
^
1
)
/
tonumber
300
local
single
=
digit
301
local
range
=
digit
*
P
(
"
..
"
)
*
digit
302
local
set
=
Ct
(
digit
^
2
)
303
local
colon
=
P
(
"
:
"
)
304
local
equal
=
P
(
"
=
"
)
305
local
other
=
1
-
digit
-
colon
-
equal
306
local
remapped
=
(
sign
*
sign
+
sign
)
/
remapper
307
local
operation
=
Cs
(
other
^
1
)
308
local
special
=
(
colon
*
C
(
other
^
1
)
)
+
Cc
(
"
"
)
309
local
text
=
(
equal
*
C
(
P
(
1
)
^
0
)
)
+
Cc
(
false
)
310 311
local
pattern
=
312
(
amount
+
Cc
(
1
)
)
313
*
(
remapped
+
Cc
(
"
"
)
)
314
*
Cs
(
operation
/
lower
)
315
*
Cs
(
special
/
lower
)
*
(
316
range
*
Cc
(
false
)
*
text
+
317
Cc
(
false
)
*
Cc
(
false
)
*
set
*
text
+
318
single
*
Cc
(
false
)
*
Cc
(
false
)
*
text
+
319
Cc
(
false
)
*
Cc
(
false
)
*
Cc
(
false
)
*
text
320
)
321 322
-- local n, operation, index, upto, set, text = lpegmatch(pattern,"RZ1357")
323 324
-- print(lpegmatch(pattern,"RZ=x")) -- 1 RZ false false false x
325
-- print(lpegmatch(pattern,"RZ1=x")) -- 1 RZ 1 false false x
326
-- print(lpegmatch(pattern,"RZ1..3=x")) -- 1 RZ 1 3 false x
327
-- print(lpegmatch(pattern,"RZ13=x")) -- 1 RZ false false table x
328 329
local
f_initialize
=
'
if unknown context_chem : input mp-chem.mpiv ; fi ;
'
330
local
f_start_structure
=
formatters
[
'
chem_start_structure(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%q);
'
]
331
local
f_set_trace_bounds
=
formatters
[
'
chem_trace_boundingbox := %l ;
'
]
332
local
f_stop_structure
=
'
chem_stop_structure;
'
333
local
f_start_component
=
'
chem_start_component;
'
334
local
f_stop_component
=
'
chem_stop_component;
'
335
local
f_line
=
formatters
[
'
chem_%s%s(%s,%s,%s,%s,%q);
'
]
336
local
f_set
=
formatters
[
'
chem_set(%s);
'
]
337
local
f_number
=
formatters
[
'
chem_%s%s(%s,%s,"\\chemicaltext{%s}");
'
]
338
local
f_text
=
f_number
339
local
f_empty_normal
=
formatters
[
'
chem_%s(%s,%s,"");
'
]
340
local
f_empty_center
=
formatters
[
'
chem_c%s(%s,%s,"");
'
]
341
local
f_transform
=
formatters
[
'
chem_%s(%s,%s,%s);
'
]
342
local
f_fixed
=
formatters
[
'
chem_%s(%s,%s,%q);
'
]
343 344
local
function
process
(
level
,
spec
,
text
,
n
,
rulethickness
,
rulecolor
,
offset
,
default_variant
)
345
insert
(
stack
,
{
spec
=
spec
,
text
=
text
,
n
=
n
}
)
346
local
txt
=
#
stack
347
local
m
=
#
metacode
348
local
saved_rulethickness
=
rulethickness
349
local
saved_rulecolor
=
rulecolor
350
local
saved_align
=
align
351
local
current_variant
=
default_variant
or
"
six
"
352
for
i
=
1
,
#
spec
do
353
local
step
=
spec
[
i
]
354
local
s
=
lower
(
step
)
355
local
n
=
current_variant
.
.
"
:
"
.
.
s
356
local
d
=
definitions
[
n
]
357
if
not
d
then
358
n
=
s
359
d
=
definitions
[
n
]
360
end
361
if
d
then
362
if
trace_structure
then
363
report_chemistry
(
"
level %a, step %a, definition %a, snippets %a
"
,
level
,
step
,
n
,
#
d
)
364
end
365
for
i
=
1
,
#
d
do
366
local
di
=
d
[
i
]
367
current_variant
=
process
(
level
+
1
,
di
.
spec
,
di
.
text
,
1
,
rulethickness
,
rulecolor
,
offset
,
current_variant
)
-- offset?
368
end
369
else
370
local
factor
,
osign
,
operation
,
special
,
index
,
upto
,
set
,
text
=
lpegmatch
(
pattern
,
step
)
371
if
trace_structure
then
372
local
set
=
set
and
concat
(
set
,
"
"
)
or
"
-
"
373
report_chemistry
(
"
level %a, step %a, factor %a, osign %a, operation %a, special %a, index %a, upto %a, set %a, text %a
"
,
374
level
,
step
,
factor
,
osign
,
operation
,
special
,
index
,
upto
,
set
,
text
)
375
end
376
if
operation
=
=
"
rulecolor
"
then
377
local
t
=
text
378
if
not
t
then
379
txt
,
t
=
fetch
(
txt
)
380
end
381
if
t
=
=
v_default
or
t
=
=
v_normal
or
t
=
=
"
"
then
382
rulecolor
=
saved_rulecolor
383
elseif
t
then
384
rulecolor
=
t
385
end
386
elseif
operation
=
=
"
rulethickness
"
then
387
local
t
=
text
388
if
not
t
then
389
txt
,
t
=
fetch
(
txt
)
390
end
391
if
t
=
=
v_default
or
t
=
=
v_normal
or
t
=
=
t_medium
or
t
=
=
"
"
then
392
rulethickness
=
saved_rulethickness
393
elseif
t
=
=
v_small
then
394
rulethickness
=
topoints
(
1
/
1
.
2
*
todimen
(
saved_rulethickness
)
)
395
elseif
t
=
=
v_big
then
396
rulethickness
=
topoints
(
1
.
2
*
todimen
(
saved_rulethickness
)
)
397
elseif
t
then
398
-- rulethickness = topoints(todimen(t)) -- mp can't handle sp
399
rulethickness
=
topoints
(
tonumber
(
t
)
*
todimen
(
saved_rulethickness
)
)
400
end
401
elseif
operation
=
=
"
symalign
"
then
402
local
t
=
text
403
if
not
t
then
404
txt
,
t
=
fetch
(
txt
)
405
end
406
if
t
=
=
v_default
or
t
=
=
v_normal
then
407
align
=
saved_align
408
elseif
t
and
t
~
=
"
"
then
409
align
=
"
.
"
.
.
t
410
end
411
elseif
operation
=
=
"
pb
"
then
412
insert
(
pstack
,
variant
)
413
m
=
m
+
1
;
metacode
[
m
]
=
syntax
.
pb
.
direct
414
if
keys
[
special
]
=
=
"
text
"
and
index
then
415
if
keys
[
"
c
"
.
.
special
]
=
=
"
text
"
then
-- can be option: auto ...
416
m
=
m
+
1
;
metacode
[
m
]
=
f_empty_center
(
special
,
variant
,
index
)
417
else
418
m
=
m
+
1
;
metacode
[
m
]
=
f_empty_normal
(
special
,
variant
,
index
)
419
end
420
end
421
elseif
operation
=
=
"
pe
"
then
422
variant
=
remove
(
pstack
)
423
local
ss
=
syntax
[
variant
]
424
keys
,
max
=
ss
.
keys
,
ss
.
max
425
m
=
m
+
1
;
metacode
[
m
]
=
syntax
.
pe
.
direct
426
m
=
m
+
1
;
metacode
[
m
]
=
f_set
(
variant
)
427
current_variant
=
variant
428
elseif
operation
=
=
"
save
"
then
429
insert
(
sstack
,
variant
)
430
m
=
m
+
1
;
metacode
[
m
]
=
syntax
.
save
.
direct
431
elseif
operation
=
=
"
restore
"
then
432
if
#
sstack
>
0
then
433
variant
=
remove
(
sstack
)
434
else
435
report_chemistry
(
"
restore without save
"
)
436
end
437
local
ss
=
syntax
[
variant
]
438
keys
,
max
=
ss
.
keys
,
ss
.
max
439
m
=
m
+
1
;
metacode
[
m
]
=
syntax
.
restore
.
direct
440
m
=
m
+
1
;
metacode
[
m
]
=
f_set
(
variant
)
441
current_variant
=
variant
442
elseif
operation
then
443
local
ss
=
syntax
[
operation
]
444
local
what
=
keys
[
operation
]
445
local
ns
=
0
446
if
set
then
447
local
sv
=
syntax
[
current_variant
]
448
local
ms
=
sv
and
sv
.
max
449
set
=
unique
(
set
)
450
ns
=
#
set
451
if
directive_strictorder
then
452
if
what
=
=
"
line
"
then
453
set
=
sorted
(
set
)
454
end
455
if
directive_strictindex
and
ms
then
456
for
i
=
ns
,
1
,
-1
do
457
local
si
=
set
[
i
]
458
if
si
>
ms
then
459
report_chemistry
(
"
level %a, operation %a, max nofsteps %a, ignoring %a
"
,
level
,
operation
,
ms
,
si
)
460
set
[
i
]
=
nil
461
ns
=
ns
-
1
462
else
463
break
464
end
465
end
466
end
467
else
468
if
directive_strictindex
and
ms
then
469
local
t
,
nt
=
{
}
,
0
470
for
i
=
1
,
ns
do
471
local
si
=
set
[
i
]
472
if
si
>
ms
then
473
report_chemistry
(
"
level %a, operation %a, max nofsteps %a, ignoring %a
"
,
level
,
operation
,
ms
,
si
)
474
set
[
i
]
=
nil
475
else
476
nt
=
nt
+
1
477
t
[
nt
]
=
si
478
end
479
end
480
ns
=
nt
481
set
=
t
482
end
483
end
484
end
485
if
ss
then
486
local
ds
=
ss
.
direct
487
if
ds
then
488
local
sa
=
ss
.
arguments
489
if
sa
=
=
1
then
490
local
one
;
txt
,
one
=
fetch
(
txt
)
491
m
=
m
+
1
;
metacode
[
m
]
=
ds
(
one
or
"
"
)
492
elseif
sa
=
=
2
then
493
local
one
;
txt
,
one
=
fetch
(
txt
)
494
local
two
;
txt
,
two
=
fetch
(
txt
)
495
m
=
m
+
1
;
metacode
[
m
]
=
ds
(
one
or
"
"
,
two
or
"
"
)
496
else
497
m
=
m
+
1
;
metacode
[
m
]
=
ds
498
end
499
elseif
ss
.
keys
then
500
variant
,
keys
,
max
=
s
,
ss
.
keys
,
ss
.
max
501
m
=
m
+
1
;
metacode
[
m
]
=
f_set
(
variant
)
502
current_variant
=
variant
503
end
504
elseif
what
=
=
"
line
"
then
505
local
s
=
osign
506
if
s
~
=
"
"
then
507
s
=
"
.
"
.
.
s
508
end
509
if
set
then
510
-- condense consecutive numbers in a set to a range
511
local
sf
,
st
=
set
[
1
]
512
for
i
=
1
,
ns
do
513
if
i
>
1
and
set
[
i
]
~
=
set
[
i
-1
]
+
1
then
514
m
=
m
+
1
;
metacode
[
m
]
=
f_line
(
operation
,
s
,
variant
,
sf
,
st
,
rulethickness
,
rulecolor
)
515
sf
=
set
[
i
]
516
end
517
st
=
set
[
i
]
518
end
519
m
=
m
+
1
;
metacode
[
m
]
=
f_line
(
operation
,
s
,
variant
,
sf
,
st
,
rulethickness
,
rulecolor
)
520
elseif
upto
then
521
m
=
m
+
1
;
metacode
[
m
]
=
f_line
(
operation
,
s
,
variant
,
index
,
upto
,
rulethickness
,
rulecolor
)
522
elseif
index
then
523
m
=
m
+
1
;
metacode
[
m
]
=
f_line
(
operation
,
s
,
variant
,
index
,
index
,
rulethickness
,
rulecolor
)
524
else
525
m
=
m
+
1
;
metacode
[
m
]
=
f_line
(
operation
,
s
,
variant
,
1
,
max
,
rulethickness
,
rulecolor
)
526
end
527
elseif
what
=
=
"
number
"
then
528
if
set
then
529
for
i
=
1
,
ns
do
530
local
si
=
set
[
i
]
531
m
=
m
+
1
;
metacode
[
m
]
=
f_number
(
operation
,
align
,
variant
,
si
,
si
)
532
end
533
elseif
upto
then
534
for
i
=
index
,
upto
do
535
local
si
=
set
[
i
]
536
m
=
m
+
1
;
metacode
[
m
]
=
f_number
(
operation
,
align
,
variant
,
si
,
si
)
537
end
538
elseif
index
then
539
m
=
m
+
1
;
metacode
[
m
]
=
f_number
(
operation
,
align
,
variant
,
index
,
index
)
540
else
541
for
i
=
1
,
max
do
542
m
=
m
+
1
;
metacode
[
m
]
=
f_number
(
operation
,
align
,
variant
,
i
,
i
)
543
end
544
end
545
elseif
what
=
=
"
text
"
then
546
if
set
then
547
for
i
=
1
,
ns
do
548
local
si
=
set
[
i
]
549
local
t
=
text
550
if
not
t
then
txt
,
t
=
fetch
(
txt
)
end
551
if
t
then
552
t
=
molecule
(
processor_tostring
(
t
)
)
553
-- local p, t = processors.split(t)
554
-- m = m + 1 ; metacode[m] = f_text(operation,p or align,variant,si,t)
555
m
=
m
+
1
;
metacode
[
m
]
=
f_text
(
operation
,
align
,
variant
,
si
,
t
)
556
end
557
end
558
elseif
upto
then
559
for
i
=
index
,
upto
do
560
local
t
=
text
561
if
not
t
then
txt
,
t
=
fetch
(
txt
)
end
562
if
t
then
563
t
=
molecule
(
processor_tostring
(
t
)
)
564
m
=
m
+
1
;
metacode
[
m
]
=
f_text
(
operation
,
align
,
variant
,
i
,
t
)
565
end
566
end
567
elseif
index
=
=
0
then
568
local
t
=
text
569
if
not
t
then
txt
,
t
=
fetch
(
txt
)
end
570
if
t
then
571
t
=
molecule
(
processor_tostring
(
t
)
)
572
m
=
m
+
1
;
metacode
[
m
]
=
f_text
(
operation
,
align
,
variant
,
index
,
t
)
573
end
574
elseif
index
then
575
local
t
=
text
576
if
not
t
then
txt
,
t
=
fetch
(
txt
)
end
577
if
t
then
578
t
=
molecule
(
processor_tostring
(
t
)
)
579
m
=
m
+
1
;
metacode
[
m
]
=
f_text
(
operation
,
align
,
variant
,
index
,
t
)
580
end
581
else
582
for
i
=
1
,
max
do
583
local
t
=
text
584
if
not
t
then
txt
,
t
=
fetch
(
txt
)
end
585
if
t
then
586
t
=
molecule
(
processor_tostring
(
t
)
)
587
m
=
m
+
1
;
metacode
[
m
]
=
f_text
(
operation
,
align
,
variant
,
i
,
t
)
588
end
589
end
590
end
591
elseif
what
=
=
"
transform
"
then
592
if
osign
=
=
"
m
"
then
593
factor
=
-
factor
594
end
595
if
set
then
596
for
i
=
1
,
ns
do
597
local
si
=
set
[
i
]
598
m
=
m
+
1
;
metacode
[
m
]
=
f_transform
(
operation
,
variant
,
si
,
factor
)
599
end
600
elseif
upto
then
601
for
i
=
index
,
upto
do
602
m
=
m
+
1
;
metacode
[
m
]
=
f_transform
(
operation
,
variant
,
i
,
factor
)
603
end
604
else
605
m
=
m
+
1
;
metacode
[
m
]
=
f_transform
(
operation
,
variant
,
index
or
1
,
factor
)
606
end
607
elseif
what
=
=
"
fixed
"
then
608
m
=
m
+
1
;
metacode
[
m
]
=
f_fixed
(
operation
,
variant
,
rulethickness
,
rulecolor
)
609
elseif
trace_structure
then
610
report_chemistry
(
"
level %a, ignoring undefined operation %s
"
,
level
,
operation
)
611
end
612
end
613
end
614
end
615
remove
(
stack
)
616
return
current_variant
617
end
618 619
-- the size related values are somewhat special but we want to be
620
-- compatible
621
--
622
-- rulethickness in points
623 624
local
function
checked
(
d
,
bondlength
,
unit
,
scale
)
625
if
d
=
=
v_none
then
626
return
0
627
end
628
local
n
=
tonumber
(
d
)
629
if
not
n
then
630
-- assume dimen
631
elseif
n
>
=
10
or
n
<
=
-10
then
632
return
bondlength
*
unit
*
n
/
1000
633
else
634
return
bondlength
*
unit
*
n
635
end
636
local
n
=
todimen
(
d
)
637
if
n
then
638
return
scale
*
n
639
else
640
return
v_fit
641
end
642
end
643 644
local
function
calculated
(
height
,
bottom
,
top
,
bondlength
,
unit
,
scale
)
645
local
scaled
=
0
646
if
height
=
=
v_none
then
647
-- this always wins
648
height
=
"
0pt
"
649
bottom
=
"
0pt
"
650
top
=
"
0pt
"
651
elseif
height
=
=
v_fit
then
652
height
=
"
true
"
653
bottom
=
bottom
=
=
v_fit
and
"
true
"
or
topoints
(
checked
(
bottom
,
bondlength
,
unit
,
scale
)
)
654
top
=
top
=
=
v_fit
and
"
true
"
or
topoints
(
checked
(
top
,
bondlength
,
unit
,
scale
)
)
655
else
656
height
=
checked
(
height
,
bondlength
,
unit
,
scale
)
657
if
bottom
=
=
v_fit
then
658
if
top
=
=
v_fit
then
659
bottom
=
height
/
2
660
top
=
bottom
661
else
662
top
=
checked
(
top
,
bondlength
,
unit
,
scale
)
663
bottom
=
height
-
top
664
end
665
elseif
top
=
=
v_fit
then
666
bottom
=
checked
(
bottom
,
bondlength
,
unit
,
scale
)
667
top
=
height
-
bottom
668
else
669
bottom
=
checked
(
bottom
,
bondlength
,
unit
,
scale
)
670
top
=
checked
(
top
,
bondlength
,
unit
,
scale
)
671
local
ratio
=
height
/
(
bottom
+
top
)
672
bottom
=
bottom
*
ratio
673
top
=
top
*
ratio
674
end
675
scaled
=
height
676
top
=
topoints
(
top
)
677
bottom
=
topoints
(
bottom
)
678
height
=
topoints
(
height
)
679
end
680
return
height
,
bottom
,
top
,
scaled
681
end
682 683
function
chemistry
.
start
(
settings
)
684
--
685
local
width
=
settings
.
width
or
v_fit
686
local
height
=
settings
.
height
or
v_fit
687
local
unit
=
settings
.
unit
or
655360
688
local
bondlength
=
settings
.
factor
or
3
689
local
rulethickness
=
settings
.
rulethickness
or
65536
690
local
rulecolor
=
settings
.
rulecolor
or
"
black
"
691
local
axiscolor
=
settings
.
framecolor
or
"
black
"
692
local
scale
=
settings
.
scale
or
"
normal
"
693
local
rotation
=
settings
.
rotation
or
0
694
local
offset
=
settings
.
offset
or
0
695
local
left
=
settings
.
left
or
v_fit
696
local
right
=
settings
.
right
or
v_fit
697
local
top
=
settings
.
top
or
v_fit
698
local
bottom
=
settings
.
bottom
or
v_fit
699
--
700
align
=
settings
.
symalign
or
"
auto
"
701
if
trace_structure
then
702
report_chemistry
(
"
unit %p, bondlength %s, symalign %s
"
,
unit
,
bondlength
,
align
)
703
end
704
if
align
~
=
"
"
then
705
align
=
"
.
"
.
.
align
706
end
707
if
trace_structure
then
708
report_chemistry
(
"
%s scale %a, rotation %a, width %s, height %s, left %s, right %s, top %s, bottom %s
"
,
"
asked
"
,
scale
,
rotation
,
width
,
height
,
left
,
right
,
top
,
bottom
)
709
end
710
if
scale
=
=
v_small
then
711
scale
=
1
/
1
.
2
712
elseif
scale
=
=
v_normal
or
scale
=
=
v_medium
or
scale
=
=
0
then
713
scale
=
1
714
elseif
scale
=
=
v_big
then
715
scale
=
1
.
2
716
else
717
scale
=
tonumber
(
scale
)
718
if
not
scale
or
scale
=
=
0
then
719
scale
=
1
720
elseif
scale
>
=
10
then
721
scale
=
scale
/
1000
722
elseif
scale
<
.
01
then
723
scale
=
.
01
724
end
725
end
726
--
727
unit
=
scale
*
unit
728
--
729
local
sp_width
=
0
730
local
sp_height
=
0
731
--
732
width
,
left
,
right
,
sp_width
=
calculated
(
width
,
left
,
right
,
bondlength
,
unit
,
scale
)
733
height
,
bottom
,
top
,
sp_height
=
calculated
(
height
,
bottom
,
top
,
bondlength
,
unit
,
scale
)
734
--
735
if
width
~
=
"
true
"
and
height
~
=
"
true
"
and
trialtypesetting
(
)
then
736
if
trace_structure
then
737
report_chemistry
(
"
skipping trial run
"
)
738
end
739
context
.
rule
(
sp_width
,
sp_height
,
0
)
-- maybe depth
740
return
741
end
742
--
743
nofstructures
=
nofstructures
+
1
744
--
745
rotation
=
tonumber
(
rotation
)
or
0
746
--
747
metacode
=
{
}
748
--
749
if
trace_structure
then
750
report_chemistry
(
"
%s scale %a, rotation %a, width %s, height %s, left %s, right %s, top %s, bottom %s
"
,
"
used
"
,
scale
,
rotation
,
width
,
height
,
left
,
right
,
top
,
bottom
)
751
end
752
metacode
[
#
metacode
+
1
]
=
f_start_structure
(
753
nofstructures
,
754
left
,
right
,
top
,
bottom
,
755
rotation
,
topoints
(
unit
)
,
bondlength
,
scale
,
topoints
(
offset
)
,
756
tostring
(
settings
.
axis
=
=
v_on
)
,
topoints
(
rulethickness
)
,
axiscolor
757
)
758
metacode
[
#
metacode
+
1
]
=
f_set_trace_bounds
(
trace_boundingbox
)
;
759
--
760
variant
,
keys
,
stack
,
pstack
,
sstack
=
"
one
"
,
{
}
,
{
}
,
{
}
,
{
}
761
end
762 763
function
chemistry
.
stop
(
)
764
if
metacode
then
765
metacode
[
#
metacode
+
1
]
=
f_stop_structure
766
local
mpcode
=
concat
(
metacode
,
"
\n
"
)
767
if
trace_metapost
then
768
report_chemistry
(
"
metapost code:\n%s
"
,
mpcode
)
769
end
770
metapost
.
graphic
{
771
instance
=
chemistry
.
instance
,
772
format
=
chemistry
.
format
,
773
method
=
chemistry
.
method
,
774
data
=
mpcode
,
775
definitions
=
f_initialize
,
776
}
777
metacode
=
nil
778
end
779
end
780 781
function
chemistry
.
component
(
spec
,
text
,
rulethickness
,
rulecolor
)
782
if
metacode
then
783
local
spec
=
settings_to_array_with_repeat
(
spec
,
true
)
-- no lower?
784
local
text
=
settings_to_array_with_repeat
(
text
,
true
)
785
metacode
[
#
metacode
+
1
]
=
f_start_component
786
process
(
1
,
spec
,
text
,
1
,
rulethickness
,
rulecolor
)
787
metacode
[
#
metacode
+
1
]
=
f_stop_component
788
end
789
end
790 791
statistics
.
register
(
"
chemical formulas
"
,
function
(
)
792
if
nofstructures
>
0
then
793
return
format
(
"
%s chemical structure formulas
"
,
nofstructures
)
-- no timing needed, part of metapost
794
end
795
end
)
796 797
-- interfaces
798 799
implement
{
800
name
=
"
undefinechemical
"
,
801
actions
=
chemistry
.
undefine
,
802
arguments
=
"
string
"
803
}
804 805
implement
{
806
name
=
"
definechemical
"
,
807
actions
=
chemistry
.
define
,
808
arguments
=
"
3 strings
"
,
809
}
810 811
implement
{
812
name
=
"
startchemical
"
,
813
actions
=
chemistry
.
start
,
814
arguments
=
{
815
{
816
{
"
width
"
}
,
817
{
"
height
"
}
,
818
{
"
left
"
}
,
819
{
"
right
"
}
,
820
{
"
top
"
}
,
821
{
"
bottom
"
}
,
822
{
"
scale
"
}
,
823
{
"
rotation
"
}
,
824
{
"
symalign
"
}
,
825
{
"
axis
"
}
,
826
{
"
framecolor
"
}
,
827
{
"
rulethickness
"
}
,
828
{
"
offset
"
}
,
829
{
"
unit
"
}
,
830
{
"
factor
"
}
831
}
832
}
833
}
834 835
implement
{
836
name
=
"
stopchemical
"
,
837
actions
=
chemistry
.
stop
,
838
}
839 840
implement
{
841
name
=
"
chemicalcomponent
"
,
842
actions
=
chemistry
.
component
,
843
arguments
=
"
4 strings
"
,
844
}
845 846
-- todo: top / bottom
847
-- note that "<->" here differs from ppchtex
848 849
local
inline
=
{
850
[
"
single
"
]
=
"
\\chemicalsinglebond
"
,
[
"
-
"
]
=
"
\\chemicalsinglebond
"
,
851
[
"
double
"
]
=
"
\\chemicaldoublebond
"
,
[
"
--
"
]
=
"
\\chemicaldoublebond
"
,
852
[
"
=
"
]
=
"
\\chemicaldoublebond
"
,
853
[
"
triple
"
]
=
"
\\chemicaltriplebond
"
,
[
"
---
"
]
=
"
\\chemicaltriplebond
"
,
854
[
"
"
]
=
"
\\chemicaltriplebond
"
,
855
[
"
gives
"
]
=
"
\\chemicalgives
"
,
[
"
->
"
]
=
"
\\chemicalgives
"
,
856
[
"
equilibrium
"
]
=
"
\\chemicalequilibrium
"
,
[
"
<-->
"
]
=
"
\\chemicalequilibrium
"
,
857
[
"
<=>
"
]
=
"
\\chemicalequilibrium
"
,
858
[
"
mesomeric
"
]
=
"
\\chemicalmesomeric
"
,
[
"
<>
"
]
=
"
\\chemicalmesomeric
"
,
859
[
"
<->
"
]
=
"
\\chemicalmesomeric
"
,
860
[
"
plus
"
]
=
"
\\chemicalplus
"
,
[
"
+
"
]
=
"
\\chemicalplus
"
,
861
[
"
minus
"
]
=
"
\\chemicalminus
"
,
862
[
"
equals
"
]
=
"
\\chemicalequals
"
,
863
[
"
space
"
]
=
"
\\chemicalspace
"
,
864
}
865 866
local
ctx_chemicalinline
=
context
.
chemicalinline
867 868
function
chemistry
.
inlinechemical
(
spec
)
869
local
spec
=
settings_to_array_with_repeat
(
spec
,
true
)
870
for
i
=
1
,
#
spec
do
871
local
s
=
spec
[
i
]
872
local
inl
=
inline
[
lower
(
s
)
]
873
if
inl
then
874
context
(
inl
)
-- could be a fast context.sprint
875
else
876
ctx_chemicalinline
(
molecule
(
s
)
)
877
end
878
end
879
end
880 881
implement
{
882
name
=
"
inlinechemical
"
,
883
actions
=
chemistry
.
inlinechemical
,
884
arguments
=
"
string
"
885
}
886