typo-dub.lua /size: 35 Kb    last modification: 2021-10-28 13:50
1
if
not
modules
then
modules
=
{
}
end
modules
[
'
typo-dub
'
]
=
{
2
version
=
1
.
001
,
3
comment
=
"
companion to typo-dir.mkiv
"
,
4
author
=
"
Hans Hagen, PRAGMA-ADE, Hasselt NL
"
,
5
copyright
=
"
PRAGMA ADE / ConTeXt Development Team
"
,
6
license
=
"
see context related readme files
"
,
7
comment
=
"
Unicode bidi (sort of) variant b
"
,
8
}
9 10
-- This is a follow up on typo-uba which itself is a follow up on t-bidi by Khaled Hosny which
11
-- in turn is based on minibidi.c from Arabeyes. This is a further optimizations, as well as
12
-- an update on some recent unicode bidi developments. There is (and will) also be more control
13
-- added. As a consequence this module is somewhat slower than its precursor which itself is
14
-- slower than the one-pass bidi handler. This is also a playground and I might add some plugin
15
-- support.
16 17
-- todo (cf html):
18
--
19
-- normal The element does not offer a additional level of embedding with respect to the bidirectional algorithm. For inline elements implicit reordering works across element boundaries.
20
-- embed If the element is inline, this value opens an additional level of embedding with respect to the bidirectional algorithm. The direction of this embedding level is given by the direction property.
21
-- bidi-override For inline elements this creates an override. For block container elements this creates an override for inline-level descendants not within another block container element. This means that inside the element, reordering is strictly in sequence according to the direction property; the implicit part of the bidirectional algorithm is ignored.
22
-- isolate This keyword indicates that the element's container directionality should be calculated without considering the content of this element. The element is therefore isolated from its siblings. When applying its bidirectional-resolution algorithm, its container element treats it as one or several U+FFFC Object Replacement Character, i.e. like an image.
23
-- isolate-override This keyword applies the isolation behavior of the isolate keyword to the surrounding content and the override behavior o f the bidi-override keyword to the inner content.
24
-- plaintext This keyword makes the elements directionality calculated without considering its parent bidirectional state or the value of the direction property. The directionality is calculated using the P2 and P3 rules of the Unicode Bidirectional Algorithm.
25
-- This value allows to display data which has already formatted using a tool following the Unicode Bidirectional Algorithm.
26
--
27
-- todo: check for introduced errors
28
-- todo: reuse list, we have size, so we can just change values (and auto allocate when not there)
29
-- todo: reuse the stack
30
-- todo: no need for a max check
31
-- todo: collapse bound similar ranges (not ok yet)
32
-- todo: combine some sweeps
33
-- todo: removing is not needed when we inject at the same spot (only chnage the dir property)
34
-- todo: isolated runs (isolating runs are similar to bidi=local in the basic analyzer)
35 36
-- todo: check unicode addenda (from the draft):
37
--
38
-- Added support for canonical equivalents in BD16.
39
-- Changed logic in N0 to not check forwards for context in the case of enclosed text opposite the embedding direction.
40
-- Major extension of the algorithm to allow for the implementation of directional isolates and the introduction of new isolate-related values to the Bidi_Class property.
41
-- Adds BD8, BD9, BD10, BD11, BD12, BD13, BD14, BD15, and BD16, Sections 2.4 and 2.5, and Rules X5a, X5b, X5c and X6a.
42
-- Extensively revises Section 3.3.2, Explicit Levels and Directions and its existing X rules to formalize the algorithm for matching a PDF with the embedding or override initiator whose scope it terminates.
43
-- Moves Rules X9 and X10 into a separate new Section 3.3.3, Preparations for Implicit Processing.
44
-- Modifies Rule X10 to make the isolating run sequence the unit to which subsequent rules are applied.
45
-- Modifies Rule W1 to change an NSM preceded by an isolate initiator or PDI into ON.
46
-- Adds Rule N0 and makes other changes to Section 3.3.5, Resolving Neutral and Isolate Formatting Types to resolve bracket pairs to the same level.
47 48
local
insert
,
remove
,
unpack
,
concat
=
table
.
insert
,
table
.
remove
,
table
.
unpack
,
table
.
concat
49
local
utfchar
=
utf
.
char
50
local
setmetatable
=
setmetatable
51
local
formatters
=
string
.
formatters
52 53
local
directiondata
=
characters
.
directions
54
local
mirrordata
=
characters
.
mirrors
55
local
textclassdata
=
characters
.
textclasses
56 57
local
nuts
=
nodes
.
nuts
58 59
local
getnext
=
nuts
.
getnext
60
local
getid
=
nuts
.
getid
61
local
getsubtype
=
nuts
.
getsubtype
62
local
getlist
=
nuts
.
getlist
63
local
getchar
=
nuts
.
getchar
64
local
getattr
=
nuts
.
getattr
65
local
getprop
=
nuts
.
getprop
66
local
getdirection
=
nuts
.
getdirection
67 68
local
setprop
=
nuts
.
setprop
69
local
setchar
=
nuts
.
setchar
70
local
setdirection
=
nuts
.
setdirection
71
local
setattrlist
=
nuts
.
setattrlist
72 73
local
remove_node
=
nuts
.
remove
74
local
insertnodeafter
=
nuts
.
insertafter
75
local
insertnodebefore
=
nuts
.
insertbefore
76 77
local
startofpar
=
nuts
.
startofpar
78 79
local
nodepool
=
nuts
.
pool
80
local
new_direction
=
nodepool
.
direction
81 82
local
nodecodes
=
nodes
.
nodecodes
83
local
gluecodes
=
nodes
.
gluecodes
84 85
local
glyph_code
=
nodecodes
.
glyph
86
local
glue_code
=
nodecodes
.
glue
87
local
hlist_code
=
nodecodes
.
hlist
88
local
vlist_code
=
nodecodes
.
vlist
89
local
math_code
=
nodecodes
.
math
90
local
dir_code
=
nodecodes
.
dir
91
local
par_code
=
nodecodes
.
par
92 93
local
parfillskip_code
=
gluecodes
.
parfillskip
94 95
local
dirvalues
=
nodes
.
dirvalues
96
local
lefttoright_code
=
dirvalues
.
lefttoright
97
local
righttoleft_code
=
dirvalues
.
righttoleft
98 99
local
maximum_stack
=
0xFF
100 101
local
a_directions
=
attributes
.
private
(
'
directions
'
)
102 103
local
directions
=
typesetters
.
directions
104
local
setcolor
=
directions
.
setcolor
105
local
getfences
=
directions
.
getfences
106 107
local
remove_controls
=
true
directives
.
register
(
"
typesetters.directions.removecontrols
"
,
function
(
v
)
remove_controls
=
v
end
)
108
----- analyze_fences = true directives.register("typesetters.directions.analyzefences", function(v) analyze_fences = v end)
109 110
local
report_directions
=
logs
.
reporter
(
"
typesetting
"
,
"
directions three
"
)
111 112
local
trace_directions
=
false
trackers
.
register
(
"
typesetters.directions
"
,
function
(
v
)
trace_directions
=
v
end
)
113
local
trace_details
=
false
trackers
.
register
(
"
typesetters.directions.details
"
,
function
(
v
)
trace_details
=
v
end
)
114
local
trace_list
=
false
trackers
.
register
(
"
typesetters.directions.list
"
,
function
(
v
)
trace_list
=
v
end
)
115 116
-- strong (old):
117
--
118
-- l : left to right
119
-- r : right to left
120
-- lro : left to right override
121
-- rlo : left to left override
122
-- lre : left to right embedding
123
-- rle : left to left embedding
124
-- al : right to legt arabic (esp punctuation issues)
125
--
126
-- weak:
127
--
128
-- en : english number
129
-- es : english number separator
130
-- et : english number terminator
131
-- an : arabic number
132
-- cs : common number separator
133
-- nsm : nonspacing mark
134
-- bn : boundary neutral
135
--
136
-- neutral:
137
--
138
-- b : paragraph separator
139
-- s : segment separator
140
-- ws : whitespace
141
-- on : other neutrals
142
--
143
-- interesting: this is indeed better (and more what we expect i.e. we already use this split
144
-- in the old original (also these isolates)
145
--
146
-- strong (new):
147
--
148
-- l : left to right
149
-- r : right to left
150
-- al : right to left arabic (esp punctuation issues)
151
--
152
-- explicit: (new)
153
--
154
-- lro : left to right override
155
-- rlo : left to left override
156
-- lre : left to right embedding
157
-- rle : left to left embedding
158
-- pdf : pop dir format
159
-- lri : left to right isolate
160
-- rli : left to left isolate
161
-- fsi : first string isolate
162
-- pdi : pop directional isolate
163 164
local
whitespace
=
{
165
lre
=
true
,
166
rle
=
true
,
167
lro
=
true
,
168
rlo
=
true
,
169
pdf
=
true
,
170
bn
=
true
,
171
ws
=
true
,
172
}
173 174
local
b_s_ws_on
=
{
175
b
=
true
,
176
s
=
true
,
177
ws
=
true
,
178
on
=
true
179
}
180 181
-- tracing
182 183
local
function
show_list
(
list
,
size
,
what
)
184
local
what
=
what
or
"
direction
"
185
local
joiner
=
utfchar
(
0x200C
)
186
local
result
=
{
}
187
for
i
=
1
,
size
do
188
local
entry
=
list
[
i
]
189
local
character
=
entry
.
char
190
local
direction
=
entry
[
what
]
191
if
character
=
=
0xFFFC
then
192
local
first
=
entry
.
id
193
local
last
=
entry
.
last
194
local
skip
=
entry
.
skip
195
if
last
then
196
result
[
i
]
=
formatters
[
"
%-3s:%s %s..%s (%i)
"
]
(
direction
,
joiner
,
nodecodes
[
first
]
,
nodecodes
[
last
]
,
skip
or
0
)
197
else
198
result
[
i
]
=
formatters
[
"
%-3s:%s %s (%i)
"
]
(
direction
,
joiner
,
nodecodes
[
first
]
,
skip
or
0
)
199
end
200
elseif
character
>
=
0x202A
and
character
<
=
0x202C
then
201
result
[
i
]
=
formatters
[
"
%-3s:%s %U
"
]
(
direction
,
joiner
,
character
)
202
else
203
result
[
i
]
=
formatters
[
"
%-3s:%s %c %U
"
]
(
direction
,
joiner
,
character
,
character
)
204
end
205
end
206
return
concat
(
result
,
joiner
.
.
"
|
"
.
.
joiner
)
207
end
208 209
-- preparation
210 211
local
function
show_done
(
list
,
size
)
212
local
joiner
=
utfchar
(
0x200C
)
213
local
result
=
{
}
214
local
format
=
formatters
[
"
<%s>
"
]
215
for
i
=
1
,
size
do
216
local
entry
=
list
[
i
]
217
local
character
=
entry
.
char
218
local
begindir
=
entry
.
begindir
219
local
enddir
=
entry
.
enddir
220
if
begindir
then
221
result
[
#
result
+
1
]
=
format
(
begindir
)
222
end
223
if
entry
.
remove
then
224
-- continue
225
elseif
character
=
=
0xFFFC
then
226
result
[
#
result
+
1
]
=
format
(
"
?
"
)
227
elseif
character
=
=
0x0020
then
228
result
[
#
result
+
1
]
=
format
(
"
"
)
229
elseif
character
>
=
0x202A
and
character
<
=
0x202C
then
230
result
[
#
result
+
1
]
=
format
(
entry
.
original
)
231
else
232
result
[
#
result
+
1
]
=
utfchar
(
character
)
233
end
234
if
enddir
then
235
result
[
#
result
+
1
]
=
format
(
enddir
)
236
end
237
end
238
return
concat
(
result
,
joiner
)
239
end
240 241
-- keeping the list and overwriting doesn't save much runtime, only a few percent
242
-- char is only used for mirror, so in fact we can as well only store it for
243
-- glyphs only
244 245
local
function
build_list
(
head
)
246
-- P1
247
local
current
=
head
248
local
list
=
{
}
249
local
size
=
0
250
while
current
do
251
size
=
size
+
1
252
local
id
=
getid
(
current
)
253
if
getprop
(
current
,
"
directions
"
)
then
254
local
skip
=
0
255
local
last
=
id
256
current
=
getnext
(
current
)
257
while
current
do
258
local
id
=
getid
(
current
)
259
if
getprop
(
current
,
"
directions
"
)
then
260
skip
=
skip
+
1
261
last
=
id
262
current
=
getnext
(
current
)
263
else
264
break
265
end
266
end
267
if
id
=
=
last
then
-- the start id
268
list
[
size
]
=
{
char
=
0xFFFC
,
direction
=
"
on
"
,
original
=
"
on
"
,
level
=
0
,
skip
=
skip
,
id
=
id
}
269
else
270
list
[
size
]
=
{
char
=
0xFFFC
,
direction
=
"
on
"
,
original
=
"
on
"
,
level
=
0
,
skip
=
skip
,
id
=
id
,
last
=
last
}
271
end
272
elseif
id
=
=
glyph_code
then
273
local
chr
=
getchar
(
current
)
274
local
dir
=
directiondata
[
chr
]
275
list
[
size
]
=
{
char
=
chr
,
direction
=
dir
,
original
=
dir
,
level
=
0
}
276
current
=
getnext
(
current
)
277
elseif
id
=
=
glue_code
then
-- and how about kern
278
list
[
size
]
=
{
char
=
0x0020
,
direction
=
"
ws
"
,
original
=
"
ws
"
,
level
=
0
}
279
current
=
getnext
(
current
)
280
elseif
id
=
=
dir_code
then
281
local
direction
,
pop
=
getdirection
(
current
)
282
if
direction
=
=
lefttoright_code
then
283
if
pop
then
284
list
[
size
]
=
{
char
=
0x202C
,
direction
=
"
pdf
"
,
original
=
"
pdf
"
,
level
=
0
}
285
else
286
list
[
size
]
=
{
char
=
0x202A
,
direction
=
"
lre
"
,
original
=
"
lre
"
,
level
=
0
}
287
end
288
elseif
direction
=
=
righttoleft_code
then
289
if
pop
then
290
list
[
size
]
=
{
char
=
0x202C
,
direction
=
"
pdf
"
,
original
=
"
pdf
"
,
level
=
0
}
291
else
292
list
[
size
]
=
{
char
=
0x202B
,
direction
=
"
rle
"
,
original
=
"
rle
"
,
level
=
0
}
293
end
294
else
295
list
[
size
]
=
{
char
=
0xFFFC
,
direction
=
"
on
"
,
original
=
"
on
"
,
level
=
0
,
id
=
id
}
-- object replacement character
296
end
297
current
=
getnext
(
current
)
298
elseif
id
=
=
math_code
then
299
local
skip
=
0
300
current
=
getnext
(
current
)
301
while
getid
(
current
)
~
=
math_code
do
302
skip
=
skip
+
1
303
current
=
getnext
(
current
)
304
end
305
skip
=
skip
+
1
306
current
=
getnext
(
current
)
307
list
[
size
]
=
{
char
=
0xFFFC
,
direction
=
"
on
"
,
original
=
"
on
"
,
level
=
0
,
skip
=
skip
,
id
=
id
}
308
else
309
local
skip
=
0
310
local
last
=
id
311
current
=
getnext
(
current
)
312
while
n
do
313
local
id
=
getid
(
current
)
314
if
id
~
=
glyph_code
and
id
~
=
glue_code
and
id
~
=
dir_code
then
315
skip
=
skip
+
1
316
last
=
id
317
current
=
getnext
(
current
)
318
else
319
break
320
end
321
end
322
if
id
=
=
last
then
-- the start id
323
list
[
size
]
=
{
char
=
0xFFFC
,
direction
=
"
on
"
,
original
=
"
on
"
,
level
=
0
,
skip
=
skip
,
id
=
id
}
324
else
325
list
[
size
]
=
{
char
=
0xFFFC
,
direction
=
"
on
"
,
original
=
"
on
"
,
level
=
0
,
skip
=
skip
,
id
=
id
,
last
=
last
}
326
end
327
end
328
end
329
return
list
,
size
330
end
331 332
-- new
333 334
-- we could support ( ] and [ ) and such ...
335 336
-- ש ) ל ( א 0-0
337
-- ש ( ל ] א 0-0
338
-- ש ( ל ) א 2-4
339
-- ש ( ל [ א ) כ ] 2-6
340
-- ש ( ל ] א ) כ 2-6
341
-- ש ( ל ) א ) כ 2-4
342
-- ש ( ל ( א ) כ 4-6
343
-- ש ( ל ( א ) כ ) 2-8,4-6
344
-- ש ( ל [ א ] כ ) 2-8,4-6
345 346
local
function
resolve_fences
(
list
,
size
,
start
,
limit
)
347
-- N0: funny effects, not always better, so it's an option
348
local
stack
=
{
}
349
local
nofstack
=
0
350
for
i
=
start
,
limit
do
351
local
entry
=
list
[
i
]
352
if
entry
.
direction
=
=
"
on
"
then
353
local
char
=
entry
.
char
354
local
mirror
=
mirrordata
[
char
]
355
if
mirror
then
356
local
class
=
textclassdata
[
char
]
357
entry
.
mirror
=
mirror
358
entry
.
class
=
class
359
if
class
=
=
"
open
"
then
360
nofstack
=
nofstack
+
1
361
stack
[
nofstack
]
=
{
mirror
,
i
,
false
}
362
elseif
nofstack
=
=
0
then
363
-- skip
364
elseif
class
=
=
"
close
"
then
365
while
nofstack
>
0
do
366
local
stacktop
=
stack
[
nofstack
]
367
if
stacktop
[
1
]
=
=
char
then
368
local
open
=
stacktop
[
2
]
369
local
close
=
i
370
list
[
open
]
.
paired
=
close
371
list
[
close
]
.
paired
=
open
372
break
373
else
374
-- do we mirror or not
375
end
376
nofstack
=
nofstack
-
1
377
end
378
end
379
end
380
end
381
end
382
end
383 384
-- local function test_fences(str)
385
-- local list = { }
386
-- for s in string.gmatch(str,".") do
387
-- local b = utf.byte(s)
388
-- list[#list+1] = { c = s, char = b, direction = directiondata[b] }
389
-- end
390
-- resolve_fences(list,#list,1,#size)
391
-- inspect(list)
392
-- end
393
--
394
-- test_fences("a(b)c(d)e(f(g)h)i")
395
-- test_fences("a(b[c)d]")
396 397
-- the action
398 399
local
function
get_baselevel
(
head
,
list
,
size
,
direction
)
400
if
direction
=
=
lefttoright_code
or
direction
=
=
righttoleft_code
then
401
return
direction
,
true
402
elseif
getid
(
head
)
=
=
par_code
and
startofpar
(
head
)
then
403
direction
=
getdirection
(
head
)
404
if
direction
=
=
lefttoright_code
or
direction
=
=
righttoleft_code
then
405
return
direction
,
true
406
end
407
end
408
-- P2, P3
409
for
i
=
1
,
size
do
410
local
entry
=
list
[
i
]
411
local
direction
=
entry
.
direction
412
if
direction
=
=
"
r
"
or
direction
=
=
"
al
"
then
-- and an ?
413
return
righttoleft_code
,
true
414
elseif
direction
=
=
"
l
"
then
415
return
lefttoright_code
,
true
416
end
417
end
418
return
lefttoright_code
,
false
419
end
420 421
local
function
resolve_explicit
(
list
,
size
,
baselevel
)
422
-- X1
423
local
level
=
baselevel
424
local
override
=
"
on
"
425
local
stack
=
{
}
426
local
nofstack
=
0
427
for
i
=
1
,
size
do
428
local
entry
=
list
[
i
]
429
local
direction
=
entry
.
direction
430
-- X2
431
if
direction
=
=
"
rle
"
then
432
if
nofstack
<
maximum_stack
then
433
nofstack
=
nofstack
+
1
434
stack
[
nofstack
]
=
{
level
,
override
}
435
level
=
level
+
(
level
%
2
=
=
1
and
2
or
1
)
-- least_greater_odd(level)
436
override
=
"
on
"
437
entry
.
level
=
level
438
entry
.
direction
=
"
bn
"
439
entry
.
remove
=
true
440
elseif
trace_directions
then
441
report_directions
(
"
stack overflow at position %a with direction %a
"
,
i
,
direction
)
442
end
443
-- X3
444
elseif
direction
=
=
"
lre
"
then
445
if
nofstack
<
maximum_stack
then
446
nofstack
=
nofstack
+
1
447
stack
[
nofstack
]
=
{
level
,
override
}
448
level
=
level
+
(
level
%
2
=
=
1
and
1
or
2
)
-- least_greater_even(level)
449
override
=
"
on
"
450
entry
.
level
=
level
451
entry
.
direction
=
"
bn
"
452
entry
.
remove
=
true
453
elseif
trace_directions
then
454
report_directions
(
"
stack overflow at position %a with direction %a
"
,
i
,
direction
)
455
end
456
-- X4
457
elseif
direction
=
=
"
rlo
"
then
458
if
nofstack
<
maximum_stack
then
459
nofstack
=
nofstack
+
1
460
stack
[
nofstack
]
=
{
level
,
override
}
461
level
=
level
+
(
level
%
2
=
=
1
and
2
or
1
)
-- least_greater_odd(level)
462
override
=
"
r
"
463
entry
.
level
=
level
464
entry
.
direction
=
"
bn
"
465
entry
.
remove
=
true
466
elseif
trace_directions
then
467
report_directions
(
"
stack overflow at position %a with direction %a
"
,
i
,
direction
)
468
end
469
-- X5
470
elseif
direction
=
=
"
lro
"
then
471
if
nofstack
<
maximum_stack
then
472
nofstack
=
nofstack
+
1
473
stack
[
nofstack
]
=
{
level
,
override
}
474
level
=
level
+
(
level
%
2
=
=
1
and
1
or
2
)
-- least_greater_even(level)
475
override
=
"
l
"
476
entry
.
level
=
level
477
entry
.
direction
=
"
bn
"
478
entry
.
remove
=
true
479
elseif
trace_directions
then
480
report_directions
(
"
stack overflow at position %a with direction %a
"
,
i
,
direction
)
481
end
482
-- X7
483
elseif
direction
=
=
"
pdf
"
then
484
if
nofstack
>
0
then
485
local
stacktop
=
stack
[
nofstack
]
486
level
=
stacktop
[
1
]
487
override
=
stacktop
[
2
]
488
nofstack
=
nofstack
-
1
489
entry
.
level
=
level
490
entry
.
direction
=
"
bn
"
491
entry
.
remove
=
true
492
elseif
trace_directions
then
493
report_directions
(
"
stack underflow at position %a with direction %a
"
,
494
i
,
direction
)
495
end
496
-- X6
497
else
498
entry
.
level
=
level
499
if
override
~
=
"
on
"
then
500
entry
.
direction
=
override
501
end
502
end
503
end
504
-- X8 (reset states and overrides after paragraph)
505
end
506 507
local
function
resolve_weak
(
list
,
size
,
start
,
limit
,
orderbefore
,
orderafter
)
508
-- W1: non spacing marks get the direction of the previous character
509
for
i
=
start
,
limit
do
510
local
entry
=
list
[
i
]
511
if
entry
.
direction
=
=
"
nsm
"
then
512
if
i
=
=
start
then
513
entry
.
direction
=
orderbefore
514
else
515
entry
.
direction
=
list
[
i
-1
]
.
direction
516
end
517
end
518
end
519
-- W2: mess with numbers and arabic
520
for
i
=
start
,
limit
do
521
local
entry
=
list
[
i
]
522
if
entry
.
direction
=
=
"
en
"
then
523
for
j
=
i
-1
,
start
,
-1
do
524
local
prev
=
list
[
j
]
525
local
direction
=
prev
.
direction
526
if
direction
=
=
"
al
"
then
527
entry
.
direction
=
"
an
"
528
break
529
elseif
direction
=
=
"
r
"
or
direction
=
=
"
l
"
then
530
break
531
end
532
end
533
end
534
end
535
-- W3
536
for
i
=
start
,
limit
do
537
local
entry
=
list
[
i
]
538
if
entry
.
direction
=
=
"
al
"
then
539
entry
.
direction
=
"
r
"
540
end
541
end
542
-- W4: make separators number
543
if
true
then
544
for
i
=
start
+
1
,
limit
-1
do
545
local
entry
=
list
[
i
]
546
local
direction
=
entry
.
direction
547
if
direction
=
=
"
es
"
then
548
if
list
[
i
-1
]
.
direction
=
=
"
en
"
and
list
[
i
+
1
]
.
direction
=
=
"
en
"
then
549
entry
.
direction
=
"
en
"
550
end
551
elseif
direction
=
=
"
cs
"
then
552
local
prevdirection
=
list
[
i
-1
]
.
direction
553
if
prevdirection
=
=
"
en
"
then
554
if
list
[
i
+
1
]
.
direction
=
=
"
en
"
then
555
entry
.
direction
=
"
en
"
556
end
557
elseif
prevdirection
=
=
"
an
"
and
list
[
i
+
1
]
.
direction
=
=
"
an
"
then
558
entry
.
direction
=
"
an
"
559
end
560
end
561
end
562
else
-- probably more efficient
563
local
runner
=
start
+
2
564
if
runner
<
=
limit
then
565
local
before
=
list
[
start
]
566
local
entry
=
list
[
start
+
1
]
567
local
after
=
list
[
runner
]
568
while
after
do
569
local
direction
=
entry
.
direction
570
if
direction
=
=
"
es
"
then
571
if
before
.
direction
=
=
"
en
"
and
after
.
direction
=
=
"
en
"
then
572
entry
.
direction
=
"
en
"
573
end
574
elseif
direction
=
=
"
cs
"
then
575
local
prevdirection
=
before
.
direction
576
if
prevdirection
=
=
"
en
"
then
577
if
after
.
direction
=
=
"
en
"
then
578
entry
.
direction
=
"
en
"
579
end
580
elseif
prevdirection
=
=
"
an
"
and
after
.
direction
=
=
"
an
"
then
581
entry
.
direction
=
"
an
"
582
end
583
end
584
before
=
current
585
current
=
after
586
after
=
list
[
runner
]
587
end
588
end
589
end
590
-- W5
591
local
i
=
start
592
while
i
<
=
limit
do
593
if
list
[
i
]
.
direction
=
=
"
et
"
then
594
local
runstart
=
i
595
local
runlimit
=
runstart
596
for
i
=
runstart
,
limit
do
597
if
list
[
i
]
.
direction
=
=
"
et
"
then
598
runlimit
=
i
599
else
600
break
601
end
602
end
603
local
rundirection
=
runstart
=
=
start
and
sor
or
list
[
runstart
-1
]
.
direction
604
if
rundirection
~
=
"
en
"
then
605
rundirection
=
runlimit
=
=
limit
and
orderafter
or
list
[
runlimit
+
1
]
.
direction
606
end
607
if
rundirection
=
=
"
en
"
then
608
for
j
=
runstart
,
runlimit
do
609
list
[
j
]
.
direction
=
"
en
"
610
end
611
end
612
i
=
runlimit
613
end
614
i
=
i
+
1
615
end
616
-- W6
617
for
i
=
start
,
limit
do
618
local
entry
=
list
[
i
]
619
local
direction
=
entry
.
direction
620
if
direction
=
=
"
es
"
or
direction
=
=
"
et
"
or
direction
=
=
"
cs
"
then
621
entry
.
direction
=
"
on
"
622
end
623
end
624
-- W7
625
for
i
=
start
,
limit
do
626
local
entry
=
list
[
i
]
627
if
entry
.
direction
=
=
"
en
"
then
628
local
prev_strong
=
orderbefore
629
for
j
=
i
-1
,
start
,
-1
do
630
local
direction
=
list
[
j
]
.
direction
631
if
direction
=
=
"
l
"
or
direction
=
=
"
r
"
then
632
prev_strong
=
direction
633
break
634
end
635
end
636
if
prev_strong
=
=
"
l
"
then
637
entry
.
direction
=
"
l
"
638
end
639
end
640
end
641
end
642 643
local
function
resolve_neutral
(
list
,
size
,
start
,
limit
,
orderbefore
,
orderafter
)
644
-- N1, N2
645
for
i
=
start
,
limit
do
646
local
entry
=
list
[
i
]
647
if
b_s_ws_on
[
entry
.
direction
]
then
648
-- this needs checking
649
local
leading_direction
,
trailing_direction
,
resolved_direction
650
local
runstart
=
i
651
local
runlimit
=
runstart
652
-- for j=runstart,limit do
653
for
j
=
runstart
+
1
,
limit
do
654
if
b_s_ws_on
[
list
[
j
]
.
direction
]
then
655
-- runstart = j
656
runlimit
=
j
657
else
658
break
659
end
660
end
661
if
runstart
=
=
start
then
662
leading_direction
=
orderbefore
663
else
664
leading_direction
=
list
[
runstart
-1
]
.
direction
665
if
leading_direction
=
=
"
en
"
or
leading_direction
=
=
"
an
"
then
666
leading_direction
=
"
r
"
667
end
668
end
669
if
runlimit
=
=
limit
then
670
trailing_direction
=
orderafter
671
else
672
trailing_direction
=
list
[
runlimit
+
1
]
.
direction
673
if
trailing_direction
=
=
"
en
"
or
trailing_direction
=
=
"
an
"
then
674
trailing_direction
=
"
r
"
675
end
676
end
677
if
leading_direction
=
=
trailing_direction
then
678
-- N1
679
resolved_direction
=
leading_direction
680
else
681
-- N2 / does the weird period
682
resolved_direction
=
entry
.
level
%
2
=
=
1
and
"
r
"
or
"
l
"
683
end
684
for
j
=
runstart
,
runlimit
do
685
list
[
j
]
.
direction
=
resolved_direction
686
end
687
i
=
runlimit
688
end
689
i
=
i
+
1
690
end
691
end
692 693
local
function
resolve_implicit
(
list
,
size
,
start
,
limit
,
orderbefore
,
orderafter
,
baselevel
)
694
for
i
=
start
,
limit
do
695
local
entry
=
list
[
i
]
696
local
level
=
entry
.
level
697
local
direction
=
entry
.
direction
698
if
level
%
2
~
=
1
then
-- even
699
-- I1
700
if
direction
=
=
"
r
"
then
701
entry
.
level
=
level
+
1
702
elseif
direction
=
=
"
an
"
or
direction
=
=
"
en
"
then
703
entry
.
level
=
level
+
2
704
end
705
else
706
-- I2
707
if
direction
=
=
"
l
"
or
direction
=
=
"
en
"
or
direction
=
=
"
an
"
then
708
entry
.
level
=
level
+
1
709
end
710
end
711
end
712
end
713 714
local
function
resolve_levels
(
list
,
size
,
baselevel
,
analyze_fences
)
715
-- X10
716
local
start
=
1
717
while
start
<
size
do
718
local
level
=
list
[
start
]
.
level
719
local
limit
=
start
+
1
720
while
limit
<
size
and
list
[
limit
]
.
level
=
=
level
do
721
limit
=
limit
+
1
722
end
723
local
prev_level
=
start
=
=
1
and
baselevel
or
list
[
start
-1
]
.
level
724
local
next_level
=
limit
=
=
size
and
baselevel
or
list
[
limit
+
1
]
.
level
725
local
orderbefore
=
(
level
>
prev_level
and
level
or
prev_level
)
%
2
=
=
1
and
"
r
"
or
"
l
"
726
local
orderafter
=
(
level
>
next_level
and
level
or
next_level
)
%
2
=
=
1
and
"
r
"
or
"
l
"
727
-- W1 .. W7
728
resolve_weak
(
list
,
size
,
start
,
limit
,
orderbefore
,
orderafter
)
729
-- N0
730
if
analyze_fences
then
731
resolve_fences
(
list
,
size
,
start
,
limit
)
732
end
733
-- N1 .. N2
734
resolve_neutral
(
list
,
size
,
start
,
limit
,
orderbefore
,
orderafter
)
735
-- I1 .. I2
736
resolve_implicit
(
list
,
size
,
start
,
limit
,
orderbefore
,
orderafter
,
baselevel
)
737
start
=
limit
738
end
739
-- L1
740
for
i
=
1
,
size
do
741
local
entry
=
list
[
i
]
742
local
direction
=
entry
.
original
743
-- (1)
744
if
direction
=
=
"
s
"
or
direction
=
=
"
b
"
then
745
entry
.
level
=
baselevel
746
-- (2)
747
for
j
=
i
-1
,
1
,
-1
do
748
local
entry
=
list
[
j
]
749
if
whitespace
[
entry
.
original
]
then
750
entry
.
level
=
baselevel
751
else
752
break
753
end
754
end
755
end
756
end
757
-- (3)
758
for
i
=
size
,
1
,
-1
do
759
local
entry
=
list
[
i
]
760
if
whitespace
[
entry
.
original
]
then
761
entry
.
level
=
baselevel
762
else
763
break
764
end
765
end
766
-- L4
767
if
analyze_fences
then
768
for
i
=
1
,
size
do
769
local
entry
=
list
[
i
]
770
if
entry
.
level
%
2
=
=
1
then
-- odd(entry.level)
771
if
entry
.
mirror
and
not
entry
.
paired
then
772
entry
.
mirror
=
false
773
end
774
-- okay
775
elseif
entry
.
mirror
then
776
entry
.
mirror
=
false
777
end
778
end
779
else
780
for
i
=
1
,
size
do
781
local
entry
=
list
[
i
]
782
if
entry
.
level
%
2
=
=
1
then
-- odd(entry.level)
783
local
mirror
=
mirrordata
[
entry
.
char
]
784
if
mirror
then
785
entry
.
mirror
=
mirror
786
end
787
end
788
end
789
end
790
end
791 792
local
function
insert_dir_points
(
list
,
size
)
793
-- L2, but no actual reversion is done, we simply annotate where
794
-- begindir/endddir node will be inserted.
795
local
maxlevel
=
0
796
local
toggle
=
true
797
for
i
=
1
,
size
do
798
local
level
=
list
[
i
]
.
level
799
if
level
>
maxlevel
then
800
maxlevel
=
level
801
end
802
end
803
for
level
=
0
,
maxlevel
do
804
local
started
-- = false
805
local
begindir
-- = nil
806
local
enddir
-- = nil
807
local
prev
-- = nil
808
if
toggle
then
809
begindir
=
lefttoright_code
810
enddir
=
lefttoright_code
811
toggle
=
false
812
else
813
begindir
=
righttoleft_code
814
enddir
=
righttoleft_code
815
toggle
=
true
816
end
817
for
i
=
1
,
size
do
818
local
entry
=
list
[
i
]
819
if
entry
.
level
>
=
level
then
820
if
not
started
then
821
entry
.
begindir
=
begindir
822
started
=
true
823
end
824
else
825
if
started
then
826
prev
.
enddir
=
enddir
827
started
=
false
828
end
829
end
830
prev
=
entry
831
end
832
end
833
-- make sure to close the run at end of line
834
local
last
=
list
[
size
]
835
if
not
last
.
enddir
then
836
local
s
=
{
}
837
local
n
=
0
838
for
i
=
1
,
size
do
839
local
entry
=
list
[
i
]
840
local
e
=
entry
.
enddir
841
local
b
=
entry
.
begindir
842
if
e
then
843
n
=
n
-
1
844
end
845
if
b
then
846
n
=
n
+
1
847
s
[
n
]
=
b
848
end
849
end
850
if
n
>
0
then
851
if
trace_list
and
n
>
1
then
852
report_directions
(
"
unbalanced list
"
)
853
end
854
last
.
enddir
=
s
[
n
]
=
=
righttoleft_code
and
righttoleft_code
or
lefttoright_code
855
end
856
end
857
end
858 859
local
function
apply_to_list
(
list
,
size
,
head
,
pardir
)
860
local
index
=
1
861
local
current
=
head
862
if
trace_list
then
863
report_directions
(
"
start run
"
)
864
end
865
while
current
do
866
if
index
>
size
then
867
report_directions
(
"
fatal error, size mismatch
"
)
868
break
869
end
870
local
id
=
getid
(
current
)
871
local
entry
=
list
[
index
]
872
local
begindir
=
entry
.
begindir
873
local
enddir
=
entry
.
enddir
874
setprop
(
current
,
"
directions
"
,
true
)
875
if
id
=
=
glyph_code
then
876
local
mirror
=
entry
.
mirror
877
if
mirror
then
878
setchar
(
current
,
mirror
)
879
end
880
if
trace_directions
then
881
local
direction
=
entry
.
direction
882
if
trace_list
then
883
local
original
=
entry
.
original
884
local
char
=
entry
.
char
885
local
level
=
entry
.
level
886
if
direction
=
=
original
then
887
report_directions
(
"
%2i : %C : %s
"
,
level
,
char
,
direction
)
888
else
889
report_directions
(
"
%2i : %C : %s -> %s
"
,
level
,
char
,
original
,
direction
)
890
end
891
end
892
setcolor
(
current
,
direction
,
false
,
mirror
)
893
end
894
elseif
id
=
=
hlist_code
or
id
=
=
vlist_code
then
895
setdirection
(
current
,
pardir
)
-- is this really needed?
896
elseif
id
=
=
glue_code
then
897
if
enddir
and
getsubtype
(
current
)
=
=
parfillskip_code
then
898
-- insert the last enddir before \parfillskip glue
899
local
d
=
new_direction
(
enddir
,
true
)
900
-- setprop(d,"directions",true)
901
-- setattrlist(d,current)
902
head
=
insertnodebefore
(
head
,
current
,
d
)
903
enddir
=
false
904
end
905
elseif
begindir
then
906
if
id
=
=
par_code
and
startofpar
(
current
)
then
907
-- par should always be the 1st node
908
local
d
=
new_direction
(
begindir
)
909
-- setprop(d,"directions",true)
910
-- setattrlist(d,current)
911
head
,
current
=
insertnodeafter
(
head
,
current
,
d
)
912
begindir
=
nil
913
end
914
end
915
if
begindir
then
916
local
d
=
new_direction
(
begindir
)
917
-- setprop(d,"directions",true)
918
-- setattrlist(d,current)
919
head
=
insertnodebefore
(
head
,
current
,
d
)
920
end
921
local
skip
=
entry
.
skip
922
if
skip
and
skip
>
0
then
923
for
i
=
1
,
skip
do
924
current
=
getnext
(
current
)
925
setprop
(
current
,
"
directions
"
,
true
)
926
end
927
end
928
if
enddir
then
929
local
d
=
new_direction
(
enddir
,
true
)
930
-- setprop(d,"directions",true)
931
-- setattrlist(d,current)
932
head
,
current
=
insertnodeafter
(
head
,
current
,
d
)
933
end
934
if
not
entry
.
remove
then
935
current
=
getnext
(
current
)
936
elseif
remove_controls
then
937
-- X9
938
head
,
current
=
remove_node
(
head
,
current
,
true
)
939
else
940
current
=
getnext
(
current
)
941
end
942
index
=
index
+
1
943
end
944
if
trace_list
then
945
report_directions
(
"
stop run
"
)
946
end
947
return
head
948
end
949 950
local
function
process
(
head
,
direction
,
only_one
,
where
)
951
-- for the moment a whole paragraph property
952
local
attr
=
getattr
(
head
,
a_directions
)
953
local
analyze_fences
=
getfences
(
attr
)
954
--
955
local
list
,
size
=
build_list
(
head
)
956
local
baselevel
,
dirfound
=
get_baselevel
(
head
,
list
,
size
,
direction
)
957
if
not
dirfound
and
trace_details
then
958
report_directions
(
"
no initial direction found, gambling
"
)
959
end
960
if
trace_details
then
961
report_directions
(
"
before : %s
"
,
show_list
(
list
,
size
,
"
original
"
)
)
962
end
963
resolve_explicit
(
list
,
size
,
baselevel
)
964
resolve_levels
(
list
,
size
,
baselevel
,
analyze_fences
)
965
insert_dir_points
(
list
,
size
)
966
if
trace_details
then
967
report_directions
(
"
after : %s
"
,
show_list
(
list
,
size
,
"
direction
"
)
)
968
report_directions
(
"
result : %s
"
,
show_done
(
list
,
size
)
)
969
end
970
return
apply_to_list
(
list
,
size
,
head
,
baselevel
)
971
end
972 973
directions
.
installhandler
(
interfaces
.
variables
.
two
,
process
)
974