1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
|
# Line breaking (UAX #14, Unicode 16.0): each boundary of a line of code points decided by the first rule, in the
# standard's order, that matches it (as Vega's text/linebreak.c). The properties come in as data, ranges of code points
# loaded once: each its class after LB1, whether an initial or a final quotation mark (Pi, Pf), East Asian (width F, W
# or H), and pictographic and unassigned (Extended_Pictographic and Cn). A line's boundaries, the ends included, are 0
# (none), 1 (allowed) or 2 (mandatory).
record Range {
start = 0
class = 0
pi = false
pf = false
east = false
pictographic = false
}
record Point {
code = 0
}
record Text { # each position's: its code, class, range, and what the rules look back at
codes: list of number
cls: list of number
eff: list of number # its unit's class: a mark takes its base's (LB9), a lone mark is AL (LB10)
joined: list of bool # a mark LB9 joins to the character before
start: list of number # where its unit starts
nonspace: list of number # where the last unit up to it that is not a space starts, or -1
numeric: list of bool # its unit ends NU (NU | SY | IS)*
ri: list of number # how many regional indicators end at its unit
at: list of number # its range
}
state ranges: list of Range # by start, from 0
state breaks: bytes # the last line's boundaries
view boundaries = breaks
input load = message { rows: list of Range }
input line = message { points: list of Point }
fn BK = 0; fn CR = 1; fn LF = 2; fn NL = 3; fn SP = 4; fn ZW = 5; fn ZWJ = 6; fn CM = 7; fn WJ = 8; fn GL = 9
fn BA = 10; fn BB = 11; fn B2 = 12; fn HY = 13; fn CB = 14; fn CL = 15; fn CP = 16; fn EX = 17; fn IN = 18; fn NS = 19
fn OP = 20; fn QU = 21; fn IS = 22; fn NU = 23; fn PO = 24; fn PR = 25; fn SY = 26; fn AK = 27; fn AL = 28; fn AP = 29
fn AS = 30; fn EB = 31; fn EM = 32; fn H2 = 33; fn H3 = 34; fn HL = 35; fn ID = 36; fn JL = 37; fn JV = 38; fn JT = 39
fn RI = 40; fn VF = 41; fn VI = 42
fn NONE = 0; fn ALLOWED = 1; fn MANDATORY = 2
fn end(c) = c == BK || c == CR || c == LF || c == NL
on load {
for r in load.rows { ranges.append(Range { ..r }) }
}
# The range holding code point c (ranges by start: the last whose start is c or before).
fn range(c) {
let low, high = 0, ranges.count
while high - low > 1 {
let middle = (low + high) // 2
if ranges[middle].start <= c { low = middle } else { high = middle }
}
return low
}
fn next_unit(t, i) {
let j = i + 1
while j < t.codes.count && t.joined[j] { j = j + 1 }
return j
}
fn aksara(t, u) = t.eff[u] == AK || t.eff[u] == AS || t.codes[u] == 0x25CC
fn pi(t, k) = ranges[t.at[k]].pi
fn pf(t, k) = ranges[t.at[k]].pf
fn east(t, k) = ranges[t.at[k]].east
fn decide(t, i) {
let n = t.codes.count
let a, b, A, B = t.cls[i - 1], t.cls[i], t.eff[i - 1], t.eff[i]
let unit, before = t.start[i - 1], t.nonspace[i - 1]
let base = t.codes[unit]
if a == BK { return MANDATORY } # LB4
if a == CR && b == LF { return NONE } # LB5
if a == CR || a == LF || a == NL { return MANDATORY }
if end(b) { return NONE } # LB6
if b == SP || b == ZW { return NONE } # LB7
if before >= 0 && t.eff[before] == ZW { return ALLOWED } # LB8
if a == ZWJ { return NONE } # LB8a
if t.joined[i] { return NONE } # LB9
if A == WJ || B == WJ { return NONE } # LB11
if A == GL { return NONE } # LB12
if B == GL && A != SP && A != BA && A != HY { return NONE } # LB12a
if B == CL || B == CP || B == EX || B == SY { return NONE } # LB13
if before >= 0 && t.eff[before] == OP { return NONE } # LB14
if before >= 0 && t.eff[before] == QU && pi(t, before) { # LB15a
if before == 0 { return NONE }
let q = t.eff[before - 1]
if end(q) || q == OP || q == QU || q == GL || q == SP || q == ZW { return NONE }
}
if B == QU && pf(t, i) { # LB15b
let j = next_unit(t, i)
if j == n { return NONE }
let e = t.eff[j]
if e == SP || e == GL || e == WJ || e == CL || e == QU || e == CP || e == EX || e == IS || e == SY || end(e) || e == ZW {
return NONE
}
}
if A == SP && B == IS { # LB15c
let j = next_unit(t, i)
if j < n && t.eff[j] == NU { return ALLOWED }
}
if B == IS { return NONE } # LB15d
if B == NS && before >= 0 && (t.eff[before] == CL || t.eff[before] == CP) { return NONE } # LB16
if B == B2 && before >= 0 && t.eff[before] == B2 { return NONE } # LB17
if A == SP { return ALLOWED } # LB18
if B == QU && !pi(t, i) { return NONE } # LB19
if A == QU && !pf(t, unit) { return NONE }
if B == QU { # LB19a
let j = next_unit(t, i)
if !east(t, unit) || j == n || !east(t, j) { return NONE }
}
if A == QU && (!east(t, i) || unit == 0 || !east(t, t.start[unit - 1])) { return NONE }
if B == CB || A == CB { return ALLOWED } # LB20
if (A == HY || base == 0x2010) && B == AL { # LB20a
let w = if unit > 0 { t.eff[unit - 1] } else { SP }
if end(w) || w == SP || w == ZW || w == CB || w == GL { return NONE }
}
if B == BA || B == HY || B == NS || A == BB { return NONE } # LB21
if (A == HY || (A == BA && !east(t, unit))) && unit > 0 && t.eff[unit - 1] == HL && B != HL { return NONE } # LB21a
if A == SY && B == HL { return NONE } # LB21b
if B == IN { return NONE } # LB22
if ((A == AL || A == HL) && B == NU) || (A == NU && (B == AL || B == HL)) { return NONE } # LB23
if (A == PR && (B == ID || B == EB || B == EM)) || ((A == ID || A == EB || A == EM) && B == PO) { return NONE } # LB23a
if ((A == PR || A == PO) && (B == AL || B == HL)) || ((A == AL || A == HL) && (B == PR || B == PO)) { return NONE } # LB24
if (B == PO || B == PR) && (t.numeric[i - 1] || ((A == CL || A == CP) && unit > 0 && t.numeric[unit - 1])) { return NONE } # LB25
if A == PO || A == PR {
if B == NU { return NONE }
if B == OP {
let j = next_unit(t, i)
if j < n && t.eff[j] == IS { j = next_unit(t, j) }
if j < n && t.eff[j] == NU { return NONE }
}
}
if (A == HY || A == IS || t.numeric[i - 1]) && B == NU { return NONE }
if A == JL && (B == JL || B == JV || B == H2 || B == H3) { return NONE } # LB26
if ((A == JV || A == H2) && (B == JV || B == JT)) || ((A == JT || A == H3) && B == JT) { return NONE }
let jamo = A == JL || A == JV || A == JT || A == H2 || A == H3 # LB27
if (jamo && B == PO) || (A == PR && (B == JL || B == JV || B == JT || B == H2 || B == H3)) { return NONE }
if (A == AL || A == HL) && (B == AL || B == HL) { return NONE } # LB28
if A == AP && aksara(t, i) { return NONE } # LB28a
if aksara(t, unit) && (B == VF || B == VI) { return NONE }
if A == VI && unit > 0 && aksara(t, t.start[unit - 1]) && (B == AK || t.codes[i] == 0x25CC) { return NONE }
if aksara(t, unit) && aksara(t, i) {
let j = next_unit(t, i)
if j < n && t.eff[j] == VF { return NONE }
}
if A == IS && (B == AL || B == HL) { return NONE } # LB29
if (A == AL || A == HL || A == NU) && B == OP && !east(t, i) { return NONE } # LB30
if A == CP && !east(t, unit) && (B == AL || B == HL || B == NU) { return NONE }
if A == RI && B == RI && t.ri[i - 1] % 2 == 1 { return NONE } # LB30a
if B == EM && (A == EB || ranges[t.at[unit]].pictographic) { return NONE } # LB30b
return ALLOWED # LB31
}
on line {
let codes, cls, eff, start, nonspace, ri, at: list of number
let joined, numeric: list of bool
for p in line.points {
let i = codes.count
let r = range(p.code)
let c = ranges[r].class
let mark = c == CM || c == ZWJ
let previous = if i > 0 { eff[i - 1] } else { SP }
let joins = mark && i > 0 && !end(previous) && previous != SP && previous != ZW # LB9, LB10
let e = if joins { previous } else if mark { AL } else { c }
codes.append(p.code)
cls.append(c)
at.append(r)
joined.append(joins)
eff.append(e)
start.append(if joins { start[i - 1] } else { i })
nonspace.append(if e != SP { start[i] } else if i > 0 { nonspace[i - 1] } else { -1 })
numeric.append(if joins { numeric[i - 1] } else { e == NU || ((e == SY || e == IS) && i > 0 && numeric[i - 1]) })
ri.append(if joins { ri[i - 1] } else if e == RI { (if i > 0 { ri[i - 1] } else { 0 }) + 1 } else { 0 })
}
let t = Text { codes: codes, cls: cls, eff: eff, joined: joined, start: start, nonspace: nonspace, numeric: numeric, ri: ri,
at: at }
let out: list of i64 = [0] # LB2
for i in 1 .. codes.count { out.append(i64(decide(t, i))) }
if codes.count > 0 { out.append(2) } # LB3
breaks = bytes(out)
}
|