1
2
3
4
5
6
7 package main
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 import (
58 "bufio"
59 "bytes"
60 "flag"
61 "fmt"
62 "log"
63 "strconv"
64 "strings"
65
66 "golang.org/x/text/internal/gen"
67 "golang.org/x/text/internal/language"
68 "golang.org/x/text/internal/language/compact"
69 "golang.org/x/text/unicode/cldr"
70 )
71
72 var (
73 test = flag.Bool("test", false,
74 "test existing tables; can be used to compare web data with package data.")
75 outputFile = flag.String("output", "tables.go", "output file")
76 outputTestFile = flag.String("testoutput", "data_test.go", "output file")
77
78 draft = flag.String("draft",
79 "contributed",
80 `Minimal draft requirements (approved, contributed, provisional, unconfirmed).`)
81 )
82
83 func main() {
84 gen.Init()
85
86 const pkg = "plural"
87
88 gen.Repackage("gen_common.go", "common.go", pkg)
89
90 r := gen.OpenCLDRCoreZip()
91 defer r.Close()
92
93 d := &cldr.Decoder{}
94 d.SetDirFilter("supplemental", "main")
95 d.SetSectionFilter("numbers", "plurals")
96 data, err := d.DecodeZip(r)
97 if err != nil {
98 log.Fatalf("DecodeZip: %v", err)
99 }
100
101 w := gen.NewCodeWriter()
102 defer w.WriteGoFile(*outputFile, pkg)
103
104 gen.WriteCLDRVersion(w)
105
106 genPlurals(w, data)
107
108 w = gen.NewCodeWriter()
109 defer w.WriteGoFile(*outputTestFile, pkg)
110
111 genPluralsTests(w, data)
112 }
113
114 type pluralTest struct {
115 locales string
116 form int
117 integer []string
118 decimal []string
119 }
120
121 func genPluralsTests(w *gen.CodeWriter, data *cldr.CLDR) {
122 w.WriteType(pluralTest{})
123
124 for _, plurals := range data.Supplemental().Plurals {
125 if plurals.Type == "" {
126
127 continue
128 }
129 tests := []pluralTest{}
130
131 for _, pRules := range plurals.PluralRules {
132 for _, rule := range pRules.PluralRule {
133 test := pluralTest{
134 locales: pRules.Locales,
135 form: int(countMap[rule.Count]),
136 }
137 scan := bufio.NewScanner(strings.NewReader(rule.Data()))
138 scan.Split(splitTokens)
139 var p *[]string
140 for scan.Scan() {
141 switch t := scan.Text(); t {
142 case "@integer":
143 p = &test.integer
144 case "@decimal":
145 p = &test.decimal
146 case ",", "…":
147 default:
148 if p != nil {
149 *p = append(*p, t)
150 }
151 }
152 }
153 tests = append(tests, test)
154 }
155 }
156 w.WriteVar(plurals.Type+"Tests", tests)
157 }
158 }
159
160 func genPlurals(w *gen.CodeWriter, data *cldr.CLDR) {
161 for _, plurals := range data.Supplemental().Plurals {
162 if plurals.Type == "" {
163 continue
164 }
165
166
167
168
169
170
171 setMap := map[[numN]bool]int{
172
173 [numN]bool{1: true}: 1,
174 [numN]bool{2: true}: 2,
175 [numN]bool{0: true}: 3,
176 }
177
178
179
180
181 inclusionMasks := [numN]uint64{
182
183 0: 1 << 3,
184 1: 1 << 1,
185 2: 1 << 2,
186 }
187
188
189 var all [numN]bool
190 for i := range all {
191
192 inclusionMasks[i] |= 1 << 0
193
194 all[i] = true
195 }
196
197 setMap[all] = 0
198
199 rules := []pluralCheck{}
200 index := []byte{0}
201 langMap := map[compact.ID]byte{0: 0}
202
203 for _, pRules := range plurals.PluralRules {
204
205 var conds []orCondition
206 for _, rule := range pRules.PluralRule {
207 form := countMap[rule.Count]
208 conds = parsePluralCondition(conds, rule.Data(), form)
209 }
210
211 for _, c := range conds {
212
213
214 empty := true
215 for _, b := range c.used {
216 empty = empty && !b
217 }
218 if empty {
219 rules = append(rules, pluralCheck{
220 cat: byte(opMod<<opShift) | byte(c.form),
221 setID: 0,
222 })
223 continue
224 }
225
226 for i, set := range c.set {
227 if !c.used[i] {
228 continue
229 }
230 index, ok := setMap[set]
231 if !ok {
232 index = len(setMap)
233 setMap[set] = index
234 for i := range inclusionMasks {
235 if set[i] {
236 inclusionMasks[i] |= 1 << uint64(index)
237 }
238 }
239 }
240 rules = append(rules, pluralCheck{
241 cat: byte(i<<opShift | andNext),
242 setID: byte(index),
243 })
244 }
245
246 rules[len(rules)-1].cat &^= formMask
247 rules[len(rules)-1].cat |= byte(c.form)
248 }
249
250 for _, loc := range strings.Split(pRules.Locales, " ") {
251 if strings.TrimSpace(loc) == "" {
252 continue
253 }
254 lang, ok := compact.FromTag(language.MustParse(loc))
255 if !ok {
256 log.Printf("No compact index for locale %q", loc)
257 }
258 langMap[lang] = byte(len(index) - 1)
259 }
260 index = append(index, byte(len(rules)))
261 }
262 w.WriteVar(plurals.Type+"Rules", rules)
263 w.WriteVar(plurals.Type+"Index", index)
264
265 langToIndex := make([]byte, compact.NumCompactTags)
266 for i := range langToIndex {
267 for p := compact.ID(i); ; p = p.Parent() {
268 if x, ok := langMap[p]; ok {
269 langToIndex[i] = x
270 break
271 }
272 }
273 }
274
275
276 for i, v := range langToIndex {
277 if v == 0 {
278 id, _ := compact.FromTag(language.Tag{
279 LangID: compact.ID(i).Tag().LangID,
280 })
281 if p := langToIndex[id]; p != 0 {
282 langToIndex[i] = p
283 }
284 }
285 }
286 w.WriteVar(plurals.Type+"LangToIndex", langToIndex)
287
288
289
290 w.WriteVar(plurals.Type+"InclusionMasks", inclusionMasks[:])
291
292 if len(rules) > 0xFF {
293 log.Fatalf("Too many entries for rules: %#x", len(rules))
294 }
295 if len(index) > 0xFF {
296 log.Fatalf("Too many entries for index: %#x", len(index))
297 }
298 if len(setMap) > 64 {
299 log.Fatalf("Too many entries for setMap: %d", len(setMap))
300 }
301 w.WriteComment(
302 "Slots used for %s: %X of 0xFF rules; %X of 0xFF indexes; %d of 64 sets",
303 plurals.Type, len(rules), len(index), len(setMap))
304
305 fmt.Fprint(w, "\n\n")
306 }
307 }
308
309 type orCondition struct {
310 original string
311
312 form Form
313 used [32]bool
314 set [32][numN]bool
315 }
316
317 func (o *orCondition) add(op opID, mod int, v []int) (ok bool) {
318 ok = true
319 for _, x := range v {
320 if x >= maxMod {
321 ok = false
322 break
323 }
324 }
325 for i := 0; i < numN; i++ {
326 m := i
327 if mod != 0 {
328 m = i % mod
329 }
330 if !intIn(m, v) {
331 o.set[op][i] = false
332 }
333 }
334 if ok {
335 o.used[op] = true
336 }
337 return ok
338 }
339
340 func intIn(x int, a []int) bool {
341 for _, y := range a {
342 if x == y {
343 return true
344 }
345 }
346 return false
347 }
348
349 var operandIndex = map[string]opID{
350 "i": opI,
351 "n": opN,
352 "f": opF,
353 "v": opV,
354 "w": opW,
355 }
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374 func parsePluralCondition(conds []orCondition, s string, f Form) []orCondition {
375 scan := bufio.NewScanner(strings.NewReader(s))
376 scan.Split(splitTokens)
377 for {
378 cond := orCondition{original: s, form: f}
379
380
381 for i := range cond.set {
382 for j := range cond.set[i] {
383 cond.set[i][j] = true
384 }
385 }
386 andLoop:
387 for {
388 var token string
389 scan.Scan()
390 switch class := scan.Text(); class {
391 case "t":
392 class = "w"
393 fallthrough
394 case "n", "i", "f", "v", "w":
395 op := scanToken(scan)
396 opCode := operandIndex[class]
397 mod := 0
398 if op == "%" {
399 opCode |= opMod
400
401 switch v := scanUint(scan); v {
402 case 10, 100:
403 mod = v
404 case 1000:
405
406
407
408
409
410
411 cond.used[opAzerbaijan00s] = true
412 return append(conds, cond)
413
414 case 1000000:
415 cond.used[opBretonM] = true
416 return append(conds, cond)
417
418 default:
419 log.Fatalf("Modulo value not supported %d", v)
420 }
421 op = scanToken(scan)
422 }
423 if op != "=" && op != "!=" {
424 log.Fatalf("Unexpected op %q", op)
425 }
426 if op == "!=" {
427 opCode |= opNotEqual
428 }
429 a := []int{}
430 v := scanUint(scan)
431 if class == "w" && v != 0 {
432 log.Fatalf("Must compare against zero for operand type %q", class)
433 }
434 token = scanToken(scan)
435 for {
436 switch token {
437 case "..":
438 end := scanUint(scan)
439 for ; v <= end; v++ {
440 a = append(a, v)
441 }
442 token = scanToken(scan)
443 default:
444 a = append(a, v)
445 }
446 if token != "," {
447 break
448 }
449 v = scanUint(scan)
450 token = scanToken(scan)
451 }
452 if !cond.add(opCode, mod, a) {
453
454
455 cond.set[opItalian800] = cond.set[opN]
456 cond.used[opItalian800] = true
457 }
458
459 case "@integer", "@decimal":
460 return conds
461 default:
462 log.Fatalf("Unexpected operand class %q (%s)", class, s)
463 }
464 switch token {
465 case "or":
466 conds = append(conds, cond)
467 break andLoop
468 case "@integer", "@decimal":
469
470 if err := scan.Err(); err != nil {
471 log.Fatal(err)
472 }
473 return append(conds, cond)
474 case "and":
475
476 default:
477 log.Fatalf("Unexpected token %q", token)
478 }
479 }
480 }
481 }
482
483 func scanToken(scan *bufio.Scanner) string {
484 scan.Scan()
485 return scan.Text()
486 }
487
488 func scanUint(scan *bufio.Scanner) int {
489 scan.Scan()
490 val, err := strconv.ParseUint(scan.Text(), 10, 32)
491 if err != nil {
492 log.Fatal(err)
493 }
494 return int(val)
495 }
496
497
498 func splitTokens(data []byte, atEOF bool) (advance int, token []byte, err error) {
499 condTokens := [][]byte{
500 []byte(".."),
501 []byte(","),
502 []byte("!="),
503 []byte("="),
504 }
505 advance, token, err = bufio.ScanWords(data, atEOF)
506 for _, t := range condTokens {
507 if len(t) >= len(token) {
508 continue
509 }
510 switch p := bytes.Index(token, t); {
511 case p == -1:
512 case p == 0:
513 advance = len(t)
514 token = token[:len(t)]
515 return advance - len(token) + len(t), token[:len(t)], err
516 case p < advance:
517
518 if t[0] == '=' && token[p-1] == '!' {
519 continue
520 }
521 advance = p
522 token = token[:p]
523 }
524 }
525 return advance, token, err
526 }
527
View as plain text