Best Gauge code snippet using lang.Write
gen.go
Source:gen.go
...265 macro string266 prefix []string267}268type builder struct {269 w *gen.CodeWriter270 hw io.Writer // MultiWriter for w and w.Hash271 data *cldr.CLDR272 supp *cldr.SupplementalData273 // indices274 locale stringSet // common locales275 lang stringSet // canonical language ids (2 or 3 letter ISO codes) with data276 langNoIndex stringSet // 3-letter ISO codes with no associated data277 script stringSet // 4-letter ISO codes278 region stringSet // 2-letter ISO or 3-digit UN M49 codes279 variant stringSet // 4-8-alphanumeric variant code.280 // Region codes that are groups with their corresponding group IDs.281 groups map[int]index282 // langInfo283 registry map[string]*ianaEntry284}285type index uint286func newBuilder(w *gen.CodeWriter) *builder {287 r := gen.OpenCLDRCoreZip()288 defer r.Close()289 d := &cldr.Decoder{}290 data, err := d.DecodeZip(r)291 failOnError(err)292 b := builder{293 w: w,294 hw: io.MultiWriter(w, w.Hash),295 data: data,296 supp: data.Supplemental(),297 }298 b.parseRegistry()299 return &b300}301func (b *builder) parseRegistry() {302 r := gen.OpenIANAFile("assignments/language-subtag-registry")303 defer r.Close()304 b.registry = make(map[string]*ianaEntry)305 scan := bufio.NewScanner(r)306 scan.Split(bufio.ScanWords)307 var record *ianaEntry308 for more := scan.Scan(); more; {309 key := scan.Text()310 more = scan.Scan()311 value := scan.Text()312 switch key {313 case "Type:":314 record = &ianaEntry{typ: value}315 case "Subtag:", "Tag:":316 if s := strings.SplitN(value, "..", 2); len(s) > 1 {317 for a := s[0]; a <= s[1]; a = inc(a) {318 b.addToRegistry(a, record)319 }320 } else {321 b.addToRegistry(value, record)322 }323 case "Suppress-Script:":324 record.suppressScript = value325 case "Added:":326 record.added = value327 case "Deprecated:":328 record.deprecated = value329 case "Macrolanguage:":330 record.macro = value331 case "Preferred-Value:":332 record.preferred = value333 case "Prefix:":334 record.prefix = append(record.prefix, value)335 case "Scope:":336 record.scope = value337 case "Description:":338 buf := []byte(value)339 for more = scan.Scan(); more; more = scan.Scan() {340 b := scan.Bytes()341 if b[0] == '%' || b[len(b)-1] == ':' {342 break343 }344 buf = append(buf, ' ')345 buf = append(buf, b...)346 }347 record.description = append(record.description, string(buf))348 continue349 default:350 continue351 }352 more = scan.Scan()353 }354 if scan.Err() != nil {355 log.Panic(scan.Err())356 }357}358func (b *builder) addToRegistry(key string, entry *ianaEntry) {359 if info, ok := b.registry[key]; ok {360 if info.typ != "language" || entry.typ != "extlang" {361 log.Fatalf("parseRegistry: tag %q already exists", key)362 }363 } else {364 b.registry[key] = entry365 }366}367var commentIndex = make(map[string]string)368func init() {369 for _, s := range comment {370 key := strings.TrimSpace(strings.SplitN(s, " ", 2)[0])371 commentIndex[key] = s372 }373}374func (b *builder) comment(name string) {375 if s := commentIndex[name]; len(s) > 0 {376 b.w.WriteComment(s)377 } else {378 fmt.Fprintln(b.w)379 }380}381func (b *builder) pf(f string, x ...interface{}) {382 fmt.Fprintf(b.hw, f, x...)383 fmt.Fprint(b.hw, "\n")384}385func (b *builder) p(x ...interface{}) {386 fmt.Fprintln(b.hw, x...)387}388func (b *builder) addSize(s int) {389 b.w.Size += s390 b.pf("// Size: %d bytes", s)391}392func (b *builder) writeConst(name string, x interface{}) {393 b.comment(name)394 b.w.WriteConst(name, x)395}396// writeConsts computes f(v) for all v in values and writes the results397// as constants named _v to a single constant block.398func (b *builder) writeConsts(f func(string) int, values ...string) {399 b.pf("const (")400 for _, v := range values {401 b.pf("\t_%s = %v", v, f(v))402 }403 b.pf(")")404}405// writeType writes the type of the given value, which must be a struct.406func (b *builder) writeType(value interface{}) {407 b.comment(reflect.TypeOf(value).Name())408 b.w.WriteType(value)409}410func (b *builder) writeSlice(name string, ss interface{}) {411 b.writeSliceAddSize(name, 0, ss)412}413func (b *builder) writeSliceAddSize(name string, extraSize int, ss interface{}) {414 b.comment(name)415 b.w.Size += extraSize416 v := reflect.ValueOf(ss)417 t := v.Type().Elem()418 b.pf("// Size: %d bytes, %d elements", v.Len()*int(t.Size())+extraSize, v.Len())419 fmt.Fprintf(b.w, "var %s = ", name)420 b.w.WriteArray(ss)421 b.p()422}423type FromTo struct {424 From, To uint16425}426func (b *builder) writeSortedMap(name string, ss *stringSet, index func(s string) uint16) {427 ss.sortFunc(func(a, b string) bool {428 return index(a) < index(b)429 })430 m := []FromTo{}431 for _, s := range ss.s {432 m = append(m, FromTo{index(s), index(ss.update[s])})433 }434 b.writeSlice(name, m)435}436const base = 'z' - 'a' + 1437func strToInt(s string) uint {438 v := uint(0)439 for i := 0; i < len(s); i++ {440 v *= base441 v += uint(s[i] - 'a')442 }443 return v444}445// converts the given integer to the original ASCII string passed to strToInt.446// len(s) must match the number of characters obtained.447func intToStr(v uint, s []byte) {448 for i := len(s) - 1; i >= 0; i-- {449 s[i] = byte(v%base) + 'a'450 v /= base451 }452}453func (b *builder) writeBitVector(name string, ss []string) {454 vec := make([]uint8, int(math.Ceil(math.Pow(base, float64(len(ss[0])))/8)))455 for _, s := range ss {456 v := strToInt(s)457 vec[v/8] |= 1 << (v % 8)458 }459 b.writeSlice(name, vec)460}461// TODO: convert this type into a list or two-stage trie.462func (b *builder) writeMapFunc(name string, m map[string]string, f func(string) uint16) {463 b.comment(name)464 v := reflect.ValueOf(m)465 sz := v.Len() * (2 + int(v.Type().Key().Size()))466 for _, k := range m {467 sz += len(k)468 }469 b.addSize(sz)470 keys := []string{}471 b.pf(`var %s = map[string]uint16{`, name)472 for k := range m {473 keys = append(keys, k)474 }475 sort.Strings(keys)476 for _, k := range keys {477 b.pf("\t%q: %v,", k, f(m[k]))478 }479 b.p("}")480}481func (b *builder) writeMap(name string, m interface{}) {482 b.comment(name)483 v := reflect.ValueOf(m)484 sz := v.Len() * (2 + int(v.Type().Key().Size()) + int(v.Type().Elem().Size()))485 b.addSize(sz)486 f := strings.FieldsFunc(fmt.Sprintf("%#v", m), func(r rune) bool {487 return strings.IndexRune("{}, ", r) != -1488 })489 sort.Strings(f[1:])490 b.pf(`var %s = %s{`, name, f[0])491 for _, kv := range f[1:] {492 b.pf("\t%s,", kv)493 }494 b.p("}")495}496func (b *builder) langIndex(s string) uint16 {497 if s == "und" {498 return 0499 }500 if i, ok := b.lang.find(s); ok {501 return uint16(i)502 }503 return uint16(strToInt(s)) + uint16(len(b.lang.s))504}505// inc advances the string to its lexicographical successor.506func inc(s string) string {507 const maxTagLength = 4508 var buf [maxTagLength]byte509 intToStr(strToInt(strings.ToLower(s))+1, buf[:len(s)])510 for i := 0; i < len(s); i++ {511 if s[i] <= 'Z' {512 buf[i] -= 'a' - 'A'513 }514 }515 return string(buf[:len(s)])516}517func (b *builder) parseIndices() {518 meta := b.supp.Metadata519 for k, v := range b.registry {520 var ss *stringSet521 switch v.typ {522 case "language":523 if len(k) == 2 || v.suppressScript != "" || v.scope == "special" {524 b.lang.add(k)525 continue526 } else {527 ss = &b.langNoIndex528 }529 case "region":530 ss = &b.region531 case "script":532 ss = &b.script533 case "variant":534 ss = &b.variant535 default:536 continue537 }538 ss.add(k)539 }540 // Include any language for which there is data.541 for _, lang := range b.data.Locales() {542 if x := b.data.RawLDML(lang); false ||543 x.LocaleDisplayNames != nil ||544 x.Characters != nil ||545 x.Delimiters != nil ||546 x.Measurement != nil ||547 x.Dates != nil ||548 x.Numbers != nil ||549 x.Units != nil ||550 x.ListPatterns != nil ||551 x.Collations != nil ||552 x.Segmentations != nil ||553 x.Rbnf != nil ||554 x.Annotations != nil ||555 x.Metadata != nil {556 from := strings.Split(lang, "_")557 if lang := from[0]; lang != "root" {558 b.lang.add(lang)559 }560 }561 }562 // Include locales for plural rules, which uses a different structure.563 for _, plurals := range b.data.Supplemental().Plurals {564 for _, rules := range plurals.PluralRules {565 for _, lang := range strings.Split(rules.Locales, " ") {566 if lang = strings.Split(lang, "_")[0]; lang != "root" {567 b.lang.add(lang)568 }569 }570 }571 }572 // Include languages in likely subtags.573 for _, m := range b.supp.LikelySubtags.LikelySubtag {574 from := strings.Split(m.From, "_")575 b.lang.add(from[0])576 }577 // Include ISO-639 alpha-3 bibliographic entries.578 for _, a := range meta.Alias.LanguageAlias {579 if a.Reason == "bibliographic" {580 b.langNoIndex.add(a.Type)581 }582 }583 // Include regions in territoryAlias (not all are in the IANA registry!)584 for _, reg := range b.supp.Metadata.Alias.TerritoryAlias {585 if len(reg.Type) == 2 {586 b.region.add(reg.Type)587 }588 }589 for _, s := range b.lang.s {590 if len(s) == 3 {591 b.langNoIndex.remove(s)592 }593 }594 b.writeConst("NumLanguages", len(b.lang.slice())+len(b.langNoIndex.slice()))595 b.writeConst("NumScripts", len(b.script.slice()))596 b.writeConst("NumRegions", len(b.region.slice()))597 // Add dummy codes at the start of each list to represent "unspecified".598 b.lang.add("---")599 b.script.add("----")600 b.region.add("---")601 // common locales602 b.locale.parse(meta.DefaultContent.Locales)603}604// TODO: region inclusion data will probably not be use used in future matchers.605func (b *builder) computeRegionGroups() {606 b.groups = make(map[int]index)607 // Create group indices.608 for i := 1; b.region.s[i][0] < 'A'; i++ { // Base M49 indices on regionID.609 b.groups[i] = index(len(b.groups))610 }611 for _, g := range b.supp.TerritoryContainment.Group {612 // Skip UN and EURO zone as they are flattening the containment613 // relationship.614 if g.Type == "EZ" || g.Type == "UN" {615 continue616 }617 group := b.region.index(g.Type)618 if _, ok := b.groups[group]; !ok {619 b.groups[group] = index(len(b.groups))620 }621 }622 if len(b.groups) > 64 {623 log.Fatalf("only 64 groups supported, found %d", len(b.groups))624 }625 b.writeConst("nRegionGroups", len(b.groups))626}627var langConsts = []string{628 "af", "am", "ar", "az", "bg", "bn", "ca", "cs", "da", "de", "el", "en", "es",629 "et", "fa", "fi", "fil", "fr", "gu", "he", "hi", "hr", "hu", "hy", "id", "is",630 "it", "ja", "ka", "kk", "km", "kn", "ko", "ky", "lo", "lt", "lv", "mk", "ml",631 "mn", "mo", "mr", "ms", "mul", "my", "nb", "ne", "nl", "no", "pa", "pl", "pt",632 "ro", "ru", "sh", "si", "sk", "sl", "sq", "sr", "sv", "sw", "ta", "te", "th",633 "tl", "tn", "tr", "uk", "ur", "uz", "vi", "zh", "zu",634 // constants for grandfathered tags (if not already defined)635 "jbo", "ami", "bnn", "hak", "tlh", "lb", "nv", "pwn", "tao", "tay", "tsu",636 "nn", "sfb", "vgt", "sgg", "cmn", "nan", "hsn",637}638// writeLanguage generates all tables needed for language canonicalization.639func (b *builder) writeLanguage() {640 meta := b.supp.Metadata641 b.writeConst("nonCanonicalUnd", b.lang.index("und"))642 b.writeConsts(func(s string) int { return int(b.langIndex(s)) }, langConsts...)643 b.writeConst("langPrivateStart", b.langIndex("qaa"))644 b.writeConst("langPrivateEnd", b.langIndex("qtz"))645 // Get language codes that need to be mapped (overlong 3-letter codes,646 // deprecated 2-letter codes, legacy and grandfathered tags.)647 langAliasMap := stringSet{}648 aliasTypeMap := map[string]AliasType{}649 // altLangISO3 get the alternative ISO3 names that need to be mapped.650 altLangISO3 := stringSet{}651 // Add dummy start to avoid the use of index 0.652 altLangISO3.add("---")653 altLangISO3.updateLater("---", "aa")654 lang := b.lang.clone()655 for _, a := range meta.Alias.LanguageAlias {656 if a.Replacement == "" {657 a.Replacement = "und"658 }659 // TODO: support mapping to tags660 repl := strings.SplitN(a.Replacement, "_", 2)[0]661 if a.Reason == "overlong" {662 if len(a.Replacement) == 2 && len(a.Type) == 3 {663 lang.updateLater(a.Replacement, a.Type)664 }665 } else if len(a.Type) <= 3 {666 switch a.Reason {667 case "macrolanguage":668 aliasTypeMap[a.Type] = Macro669 case "deprecated":670 // handled elsewhere671 continue672 case "bibliographic", "legacy":673 if a.Type == "no" {674 continue675 }676 aliasTypeMap[a.Type] = Legacy677 default:678 log.Fatalf("new %s alias: %s", a.Reason, a.Type)679 }680 langAliasMap.add(a.Type)681 langAliasMap.updateLater(a.Type, repl)682 }683 }684 // Manually add the mapping of "nb" (Norwegian) to its macro language.685 // This can be removed if CLDR adopts this change.686 langAliasMap.add("nb")687 langAliasMap.updateLater("nb", "no")688 aliasTypeMap["nb"] = Macro689 for k, v := range b.registry {690 // Also add deprecated values for 3-letter ISO codes, which CLDR omits.691 if v.typ == "language" && v.deprecated != "" && v.preferred != "" {692 langAliasMap.add(k)693 langAliasMap.updateLater(k, v.preferred)694 aliasTypeMap[k] = Deprecated695 }696 }697 // Fix CLDR mappings.698 lang.updateLater("tl", "tgl")699 lang.updateLater("sh", "hbs")700 lang.updateLater("mo", "mol")701 lang.updateLater("no", "nor")702 lang.updateLater("tw", "twi")703 lang.updateLater("nb", "nob")704 lang.updateLater("ak", "aka")705 lang.updateLater("bh", "bih")706 // Ensure that each 2-letter code is matched with a 3-letter code.707 for _, v := range lang.s[1:] {708 s, ok := lang.update[v]709 if !ok {710 if s, ok = lang.update[langAliasMap.update[v]]; !ok {711 continue712 }713 lang.update[v] = s714 }715 if v[0] != s[0] {716 altLangISO3.add(s)717 altLangISO3.updateLater(s, v)718 }719 }720 // Complete canonicalized language tags.721 lang.freeze()722 for i, v := range lang.s {723 // We can avoid these manual entries by using the IANA registry directly.724 // Seems easier to update the list manually, as changes are rare.725 // The panic in this loop will trigger if we miss an entry.726 add := ""727 if s, ok := lang.update[v]; ok {728 if s[0] == v[0] {729 add = s[1:]730 } else {731 add = string([]byte{0, byte(altLangISO3.index(s))})732 }733 } else if len(v) == 3 {734 add = "\x00"735 } else {736 log.Panicf("no data for long form of %q", v)737 }738 lang.s[i] += add739 }740 b.writeConst("lang", tag.Index(lang.join()))741 b.writeConst("langNoIndexOffset", len(b.lang.s))742 // space of all valid 3-letter language identifiers.743 b.writeBitVector("langNoIndex", b.langNoIndex.slice())744 altLangIndex := []uint16{}745 for i, s := range altLangISO3.slice() {746 altLangISO3.s[i] += string([]byte{byte(len(altLangIndex))})747 if i > 0 {748 idx := b.lang.index(altLangISO3.update[s])749 altLangIndex = append(altLangIndex, uint16(idx))750 }751 }752 b.writeConst("altLangISO3", tag.Index(altLangISO3.join()))753 b.writeSlice("altLangIndex", altLangIndex)754 b.writeSortedMap("AliasMap", &langAliasMap, b.langIndex)755 types := make([]AliasType, len(langAliasMap.s))756 for i, s := range langAliasMap.s {757 types[i] = aliasTypeMap[s]758 }759 b.writeSlice("AliasTypes", types)760}761var scriptConsts = []string{762 "Latn", "Hani", "Hans", "Hant", "Qaaa", "Qaai", "Qabx", "Zinh", "Zyyy",763 "Zzzz",764}765func (b *builder) writeScript() {766 b.writeConsts(b.script.index, scriptConsts...)767 b.writeConst("script", tag.Index(b.script.join()))768 supp := make([]uint8, len(b.lang.slice()))769 for i, v := range b.lang.slice()[1:] {770 if sc := b.registry[v].suppressScript; sc != "" {771 supp[i+1] = uint8(b.script.index(sc))772 }773 }774 b.writeSlice("suppressScript", supp)775 // There is only one deprecated script in CLDR. This value is hard-coded.776 // We check here if the code must be updated.777 for _, a := range b.supp.Metadata.Alias.ScriptAlias {778 if a.Type != "Qaai" {779 log.Panicf("unexpected deprecated stript %q", a.Type)780 }781 }782}783func parseM49(s string) int16 {784 if len(s) == 0 {785 return 0786 }787 v, err := strconv.ParseUint(s, 10, 10)788 failOnError(err)789 return int16(v)790}791var regionConsts = []string{792 "001", "419", "BR", "CA", "ES", "GB", "MD", "PT", "UK", "US",793 "ZZ", "XA", "XC", "XK", // Unofficial tag for Kosovo.794}795func (b *builder) writeRegion() {796 b.writeConsts(b.region.index, regionConsts...)797 isoOffset := b.region.index("AA")798 m49map := make([]int16, len(b.region.slice()))799 fromM49map := make(map[int16]int)800 altRegionISO3 := ""801 altRegionIDs := []uint16{}802 b.writeConst("isoRegionOffset", isoOffset)803 // 2-letter region lookup and mapping to numeric codes.804 regionISO := b.region.clone()805 regionISO.s = regionISO.s[isoOffset:]806 regionISO.sorted = false807 regionTypes := make([]byte, len(b.region.s))808 // Is the region valid BCP 47?809 for s, e := range b.registry {810 if len(s) == 2 && s == strings.ToUpper(s) {811 i := b.region.index(s)812 for _, d := range e.description {813 if strings.Contains(d, "Private use") {814 regionTypes[i] = iso3166UserAssigned815 }816 }817 regionTypes[i] |= bcp47Region818 }819 }820 // Is the region a valid ccTLD?821 r := gen.OpenIANAFile("domains/root/db")822 defer r.Close()823 buf, err := ioutil.ReadAll(r)824 failOnError(err)825 re := regexp.MustCompile(`"/domains/root/db/([a-z]{2}).html"`)826 for _, m := range re.FindAllSubmatch(buf, -1) {827 i := b.region.index(strings.ToUpper(string(m[1])))828 regionTypes[i] |= ccTLD829 }830 b.writeSlice("regionTypes", regionTypes)831 iso3Set := make(map[string]int)832 update := func(iso2, iso3 string) {833 i := regionISO.index(iso2)834 if j, ok := iso3Set[iso3]; !ok && iso3[0] == iso2[0] {835 regionISO.s[i] += iso3[1:]836 iso3Set[iso3] = -1837 } else {838 if ok && j >= 0 {839 regionISO.s[i] += string([]byte{0, byte(j)})840 } else {841 iso3Set[iso3] = len(altRegionISO3)842 regionISO.s[i] += string([]byte{0, byte(len(altRegionISO3))})843 altRegionISO3 += iso3844 altRegionIDs = append(altRegionIDs, uint16(isoOffset+i))845 }846 }847 }848 for _, tc := range b.supp.CodeMappings.TerritoryCodes {849 i := regionISO.index(tc.Type) + isoOffset850 if d := m49map[i]; d != 0 {851 log.Panicf("%s found as a duplicate UN.M49 code of %03d", tc.Numeric, d)852 }853 m49 := parseM49(tc.Numeric)854 m49map[i] = m49855 if r := fromM49map[m49]; r == 0 {856 fromM49map[m49] = i857 } else if r != i {858 dep := b.registry[regionISO.s[r-isoOffset]].deprecated859 if t := b.registry[tc.Type]; t != nil && dep != "" && (t.deprecated == "" || t.deprecated > dep) {860 fromM49map[m49] = i861 }862 }863 }864 for _, ta := range b.supp.Metadata.Alias.TerritoryAlias {865 if len(ta.Type) == 3 && ta.Type[0] <= '9' && len(ta.Replacement) == 2 {866 from := parseM49(ta.Type)867 if r := fromM49map[from]; r == 0 {868 fromM49map[from] = regionISO.index(ta.Replacement) + isoOffset869 }870 }871 }872 for _, tc := range b.supp.CodeMappings.TerritoryCodes {873 if len(tc.Alpha3) == 3 {874 update(tc.Type, tc.Alpha3)875 }876 }877 // This entries are not included in territoryCodes. Mostly 3-letter variants878 // of deleted codes and an entry for QU.879 for _, m := range []struct{ iso2, iso3 string }{880 {"CT", "CTE"},881 {"DY", "DHY"},882 {"HV", "HVO"},883 {"JT", "JTN"},884 {"MI", "MID"},885 {"NH", "NHB"},886 {"NQ", "ATN"},887 {"PC", "PCI"},888 {"PU", "PUS"},889 {"PZ", "PCZ"},890 {"RH", "RHO"},891 {"VD", "VDR"},892 {"WK", "WAK"},893 // These three-letter codes are used for others as well.894 {"FQ", "ATF"},895 } {896 update(m.iso2, m.iso3)897 }898 for i, s := range regionISO.s {899 if len(s) != 4 {900 regionISO.s[i] = s + " "901 }902 }903 b.writeConst("regionISO", tag.Index(regionISO.join()))904 b.writeConst("altRegionISO3", altRegionISO3)905 b.writeSlice("altRegionIDs", altRegionIDs)906 // Create list of deprecated regions.907 // TODO: consider inserting SF -> FI. Not included by CLDR, but is the only908 // Transitionally-reserved mapping not included.909 regionOldMap := stringSet{}910 // Include regions in territoryAlias (not all are in the IANA registry!)911 for _, reg := range b.supp.Metadata.Alias.TerritoryAlias {912 if len(reg.Type) == 2 && reg.Reason == "deprecated" && len(reg.Replacement) == 2 {913 regionOldMap.add(reg.Type)914 regionOldMap.updateLater(reg.Type, reg.Replacement)915 i, _ := regionISO.find(reg.Type)916 j, _ := regionISO.find(reg.Replacement)917 if k := m49map[i+isoOffset]; k == 0 {918 m49map[i+isoOffset] = m49map[j+isoOffset]919 }920 }921 }922 b.writeSortedMap("regionOldMap", ®ionOldMap, func(s string) uint16 {923 return uint16(b.region.index(s))924 })925 // 3-digit region lookup, groupings.926 for i := 1; i < isoOffset; i++ {927 m := parseM49(b.region.s[i])928 m49map[i] = m929 fromM49map[m] = i930 }931 b.writeSlice("m49", m49map)932 const (933 searchBits = 7934 regionBits = 9935 )936 if len(m49map) >= 1<<regionBits {937 log.Fatalf("Maximum number of regions exceeded: %d > %d", len(m49map), 1<<regionBits)938 }939 m49Index := [9]int16{}940 fromM49 := []uint16{}941 m49 := []int{}942 for k, _ := range fromM49map {943 m49 = append(m49, int(k))944 }945 sort.Ints(m49)946 for _, k := range m49[1:] {947 val := (k & (1<<searchBits - 1)) << regionBits948 fromM49 = append(fromM49, uint16(val|fromM49map[int16(k)]))949 m49Index[1:][k>>searchBits] = int16(len(fromM49))950 }951 b.writeSlice("m49Index", m49Index)952 b.writeSlice("fromM49", fromM49)953}954const (955 // TODO: put these lists in regionTypes as user data? Could be used for956 // various optimizations and refinements and could be exposed in the API.957 iso3166Except = "AC CP DG EA EU FX IC SU TA UK"958 iso3166Trans = "AN BU CS NT TP YU ZR" // SF is not in our set of Regions.959 // DY and RH are actually not deleted, but indeterminately reserved.960 iso3166DelCLDR = "CT DD DY FQ HV JT MI NH NQ PC PU PZ RH VD WK YD"961)962const (963 iso3166UserAssigned = 1 << iota964 ccTLD965 bcp47Region966)967func find(list []string, s string) int {968 for i, t := range list {969 if t == s {970 return i971 }972 }973 return -1974}975// writeVariants generates per-variant information and creates a map from variant976// name to index value. We assign index values such that sorting multiple977// variants by index value will result in the correct order.978// There are two types of variants: specialized and general. Specialized variants979// are only applicable to certain language or language-script pairs. Generalized980// variants apply to any language. Generalized variants always sort after981// specialized variants. We will therefore always assign a higher index value982// to a generalized variant than any other variant. Generalized variants are983// sorted alphabetically among themselves.984// Specialized variants may also sort after other specialized variants. Such985// variants will be ordered after any of the variants they may follow.986// We assume that if a variant x is followed by a variant y, then for any prefix987// p of x, p-x is a prefix of y. This allows us to order tags based on the988// maximum of the length of any of its prefixes.989// TODO: it is possible to define a set of Prefix values on variants such that990// a total order cannot be defined to the point that this algorithm breaks.991// In other words, we cannot guarantee the same order of variants for the992// future using the same algorithm or for non-compliant combinations of993// variants. For this reason, consider using simple alphabetic sorting994// of variants and ignore Prefix restrictions altogether.995func (b *builder) writeVariant() {996 generalized := stringSet{}997 specialized := stringSet{}998 specializedExtend := stringSet{}999 // Collate the variants by type and check assumptions.1000 for _, v := range b.variant.slice() {1001 e := b.registry[v]1002 if len(e.prefix) == 0 {1003 generalized.add(v)1004 continue1005 }1006 c := strings.Split(e.prefix[0], "-")1007 hasScriptOrRegion := false1008 if len(c) > 1 {1009 _, hasScriptOrRegion = b.script.find(c[1])1010 if !hasScriptOrRegion {1011 _, hasScriptOrRegion = b.region.find(c[1])1012 }1013 }1014 if len(c) == 1 || len(c) == 2 && hasScriptOrRegion {1015 // Variant is preceded by a language.1016 specialized.add(v)1017 continue1018 }1019 // Variant is preceded by another variant.1020 specializedExtend.add(v)1021 prefix := c[0] + "-"1022 if hasScriptOrRegion {1023 prefix += c[1]1024 }1025 for _, p := range e.prefix {1026 // Verify that the prefix minus the last element is a prefix of the1027 // predecessor element.1028 i := strings.LastIndex(p, "-")1029 pred := b.registry[p[i+1:]]1030 if find(pred.prefix, p[:i]) < 0 {1031 log.Fatalf("prefix %q for variant %q not consistent with predecessor spec", p, v)1032 }1033 // The sorting used below does not work in the general case. It works1034 // if we assume that variants that may be followed by others only have1035 // prefixes of the same length. Verify this.1036 count := strings.Count(p[:i], "-")1037 for _, q := range pred.prefix {1038 if c := strings.Count(q, "-"); c != count {1039 log.Fatalf("variant %q preceding %q has a prefix %q of size %d; want %d", p[i+1:], v, q, c, count)1040 }1041 }1042 if !strings.HasPrefix(p, prefix) {1043 log.Fatalf("prefix %q of variant %q should start with %q", p, v, prefix)1044 }1045 }1046 }1047 // Sort extended variants.1048 a := specializedExtend.s1049 less := func(v, w string) bool {1050 // Sort by the maximum number of elements.1051 maxCount := func(s string) (max int) {1052 for _, p := range b.registry[s].prefix {1053 if c := strings.Count(p, "-"); c > max {1054 max = c1055 }1056 }1057 return1058 }1059 if cv, cw := maxCount(v), maxCount(w); cv != cw {1060 return cv < cw1061 }1062 // Sort by name as tie breaker.1063 return v < w1064 }1065 sort.Sort(funcSorter{less, sort.StringSlice(a)})1066 specializedExtend.frozen = true1067 // Create index from variant name to index.1068 variantIndex := make(map[string]uint8)1069 add := func(s []string) {1070 for _, v := range s {1071 variantIndex[v] = uint8(len(variantIndex))1072 }1073 }1074 add(specialized.slice())1075 add(specializedExtend.s)1076 numSpecialized := len(variantIndex)1077 add(generalized.slice())1078 if n := len(variantIndex); n > 255 {1079 log.Fatalf("maximum number of variants exceeded: was %d; want <= 255", n)1080 }1081 b.writeMap("variantIndex", variantIndex)1082 b.writeConst("variantNumSpecialized", numSpecialized)1083}1084func (b *builder) writeLanguageInfo() {1085}1086// writeLikelyData writes tables that are used both for finding parent relations and for1087// language matching. Each entry contains additional bits to indicate the status of the1088// data to know when it cannot be used for parent relations.1089func (b *builder) writeLikelyData() {1090 const (1091 isList = 1 << iota1092 scriptInFrom1093 regionInFrom1094 )1095 type ( // generated types1096 likelyScriptRegion struct {1097 region uint161098 script uint81099 flags uint81100 }1101 likelyLangScript struct {1102 lang uint161103 script uint81104 flags uint81105 }1106 likelyLangRegion struct {1107 lang uint161108 region uint161109 }1110 // likelyTag is used for getting likely tags for group regions, where1111 // the likely region might be a region contained in the group.1112 likelyTag struct {1113 lang uint161114 region uint161115 script uint81116 }1117 )1118 var ( // generated variables1119 likelyRegionGroup = make([]likelyTag, len(b.groups))1120 likelyLang = make([]likelyScriptRegion, len(b.lang.s))1121 likelyRegion = make([]likelyLangScript, len(b.region.s))1122 likelyScript = make([]likelyLangRegion, len(b.script.s))1123 likelyLangList = []likelyScriptRegion{}1124 likelyRegionList = []likelyLangScript{}1125 )1126 type fromTo struct {1127 from, to []string1128 }1129 langToOther := map[int][]fromTo{}1130 regionToOther := map[int][]fromTo{}1131 for _, m := range b.supp.LikelySubtags.LikelySubtag {1132 from := strings.Split(m.From, "_")1133 to := strings.Split(m.To, "_")1134 if len(to) != 3 {1135 log.Fatalf("invalid number of subtags in %q: found %d, want 3", m.To, len(to))1136 }1137 if len(from) > 3 {1138 log.Fatalf("invalid number of subtags: found %d, want 1-3", len(from))1139 }1140 if from[0] != to[0] && from[0] != "und" {1141 log.Fatalf("unexpected language change in expansion: %s -> %s", from, to)1142 }1143 if len(from) == 3 {1144 if from[2] != to[2] {1145 log.Fatalf("unexpected region change in expansion: %s -> %s", from, to)1146 }1147 if from[0] != "und" {1148 log.Fatalf("unexpected fully specified from tag: %s -> %s", from, to)1149 }1150 }1151 if len(from) == 1 || from[0] != "und" {1152 id := 01153 if from[0] != "und" {1154 id = b.lang.index(from[0])1155 }1156 langToOther[id] = append(langToOther[id], fromTo{from, to})1157 } else if len(from) == 2 && len(from[1]) == 4 {1158 sid := b.script.index(from[1])1159 likelyScript[sid].lang = uint16(b.langIndex(to[0]))1160 likelyScript[sid].region = uint16(b.region.index(to[2]))1161 } else {1162 r := b.region.index(from[len(from)-1])1163 if id, ok := b.groups[r]; ok {1164 if from[0] != "und" {1165 log.Fatalf("region changed unexpectedly: %s -> %s", from, to)1166 }1167 likelyRegionGroup[id].lang = uint16(b.langIndex(to[0]))1168 likelyRegionGroup[id].script = uint8(b.script.index(to[1]))1169 likelyRegionGroup[id].region = uint16(b.region.index(to[2]))1170 } else {1171 regionToOther[r] = append(regionToOther[r], fromTo{from, to})1172 }1173 }1174 }1175 b.writeType(likelyLangRegion{})1176 b.writeSlice("likelyScript", likelyScript)1177 for id := range b.lang.s {1178 list := langToOther[id]1179 if len(list) == 1 {1180 likelyLang[id].region = uint16(b.region.index(list[0].to[2]))1181 likelyLang[id].script = uint8(b.script.index(list[0].to[1]))1182 } else if len(list) > 1 {1183 likelyLang[id].flags = isList1184 likelyLang[id].region = uint16(len(likelyLangList))1185 likelyLang[id].script = uint8(len(list))1186 for _, x := range list {1187 flags := uint8(0)1188 if len(x.from) > 1 {1189 if x.from[1] == x.to[2] {1190 flags = regionInFrom1191 } else {1192 flags = scriptInFrom1193 }1194 }1195 likelyLangList = append(likelyLangList, likelyScriptRegion{1196 region: uint16(b.region.index(x.to[2])),1197 script: uint8(b.script.index(x.to[1])),1198 flags: flags,1199 })1200 }1201 }1202 }1203 // TODO: merge suppressScript data with this table.1204 b.writeType(likelyScriptRegion{})1205 b.writeSlice("likelyLang", likelyLang)1206 b.writeSlice("likelyLangList", likelyLangList)1207 for id := range b.region.s {1208 list := regionToOther[id]1209 if len(list) == 1 {1210 likelyRegion[id].lang = uint16(b.langIndex(list[0].to[0]))1211 likelyRegion[id].script = uint8(b.script.index(list[0].to[1]))1212 if len(list[0].from) > 2 {1213 likelyRegion[id].flags = scriptInFrom1214 }1215 } else if len(list) > 1 {1216 likelyRegion[id].flags = isList1217 likelyRegion[id].lang = uint16(len(likelyRegionList))1218 likelyRegion[id].script = uint8(len(list))1219 for i, x := range list {1220 if len(x.from) == 2 && i != 0 || i > 0 && len(x.from) != 3 {1221 log.Fatalf("unspecified script must be first in list: %v at %d", x.from, i)1222 }1223 x := likelyLangScript{1224 lang: uint16(b.langIndex(x.to[0])),1225 script: uint8(b.script.index(x.to[1])),1226 }1227 if len(list[0].from) > 2 {1228 x.flags = scriptInFrom1229 }1230 likelyRegionList = append(likelyRegionList, x)1231 }1232 }1233 }1234 b.writeType(likelyLangScript{})1235 b.writeSlice("likelyRegion", likelyRegion)1236 b.writeSlice("likelyRegionList", likelyRegionList)1237 b.writeType(likelyTag{})1238 b.writeSlice("likelyRegionGroup", likelyRegionGroup)1239}1240func (b *builder) writeRegionInclusionData() {1241 var (1242 // mm holds for each group the set of groups with a distance of 1.1243 mm = make(map[int][]index)1244 // containment holds for each group the transitive closure of1245 // containment of other groups.1246 containment = make(map[index][]index)1247 )1248 for _, g := range b.supp.TerritoryContainment.Group {1249 // Skip UN and EURO zone as they are flattening the containment1250 // relationship.1251 if g.Type == "EZ" || g.Type == "UN" {1252 continue1253 }1254 group := b.region.index(g.Type)1255 groupIdx := b.groups[group]1256 for _, mem := range strings.Split(g.Contains, " ") {1257 r := b.region.index(mem)1258 mm[r] = append(mm[r], groupIdx)1259 if g, ok := b.groups[r]; ok {1260 mm[group] = append(mm[group], g)1261 containment[groupIdx] = append(containment[groupIdx], g)1262 }1263 }1264 }1265 regionContainment := make([]uint64, len(b.groups))1266 for _, g := range b.groups {1267 l := containment[g]1268 // Compute the transitive closure of containment.1269 for i := 0; i < len(l); i++ {1270 l = append(l, containment[l[i]]...)1271 }1272 // Compute the bitmask.1273 regionContainment[g] = 1 << g1274 for _, v := range l {1275 regionContainment[g] |= 1 << v1276 }1277 }1278 b.writeSlice("regionContainment", regionContainment)1279 regionInclusion := make([]uint8, len(b.region.s))1280 bvs := make(map[uint64]index)1281 // Make the first bitvector positions correspond with the groups.1282 for r, i := range b.groups {1283 bv := uint64(1 << i)1284 for _, g := range mm[r] {1285 bv |= 1 << g1286 }1287 bvs[bv] = i1288 regionInclusion[r] = uint8(bvs[bv])1289 }1290 for r := 1; r < len(b.region.s); r++ {1291 if _, ok := b.groups[r]; !ok {1292 bv := uint64(0)1293 for _, g := range mm[r] {1294 bv |= 1 << g1295 }1296 if bv == 0 {1297 // Pick the world for unspecified regions.1298 bv = 1 << b.groups[b.region.index("001")]1299 }1300 if _, ok := bvs[bv]; !ok {1301 bvs[bv] = index(len(bvs))1302 }1303 regionInclusion[r] = uint8(bvs[bv])1304 }1305 }1306 b.writeSlice("regionInclusion", regionInclusion)1307 regionInclusionBits := make([]uint64, len(bvs))1308 for k, v := range bvs {1309 regionInclusionBits[v] = uint64(k)1310 }1311 // Add bit vectors for increasingly large distances until a fixed point is reached.1312 regionInclusionNext := []uint8{}1313 for i := 0; i < len(regionInclusionBits); i++ {1314 bits := regionInclusionBits[i]1315 next := bits1316 for i := uint(0); i < uint(len(b.groups)); i++ {1317 if bits&(1<<i) != 0 {1318 next |= regionInclusionBits[i]1319 }1320 }1321 if _, ok := bvs[next]; !ok {1322 bvs[next] = index(len(bvs))1323 regionInclusionBits = append(regionInclusionBits, next)1324 }1325 regionInclusionNext = append(regionInclusionNext, uint8(bvs[next]))1326 }1327 b.writeSlice("regionInclusionBits", regionInclusionBits)1328 b.writeSlice("regionInclusionNext", regionInclusionNext)1329}1330type parentRel struct {1331 lang uint161332 script uint81333 maxScript uint81334 toRegion uint161335 fromRegion []uint161336}1337func (b *builder) writeParents() {1338 b.writeType(parentRel{})1339 parents := []parentRel{}1340 // Construct parent overrides.1341 n := 01342 for _, p := range b.data.Supplemental().ParentLocales.ParentLocale {1343 // Skipping non-standard scripts to root is implemented using addTags.1344 if p.Parent == "root" {1345 continue1346 }1347 sub := strings.Split(p.Parent, "_")1348 parent := parentRel{lang: b.langIndex(sub[0])}1349 if len(sub) == 2 {1350 // TODO: check that all undefined scripts are indeed Latn in these1351 // cases.1352 parent.maxScript = uint8(b.script.index("Latn"))1353 parent.toRegion = uint16(b.region.index(sub[1]))1354 } else {1355 parent.script = uint8(b.script.index(sub[1]))1356 parent.maxScript = parent.script1357 parent.toRegion = uint16(b.region.index(sub[2]))1358 }1359 for _, c := range strings.Split(p.Locales, " ") {1360 region := b.region.index(c[strings.LastIndex(c, "_")+1:])1361 parent.fromRegion = append(parent.fromRegion, uint16(region))1362 }1363 parents = append(parents, parent)1364 n += len(parent.fromRegion)1365 }1366 b.writeSliceAddSize("parents", n*2, parents)1367}1368func main() {1369 gen.Init()1370 gen.Repackage("gen_common.go", "common.go", "language")1371 w := gen.NewCodeWriter()1372 defer w.WriteGoFile("tables.go", "language")1373 fmt.Fprintln(w, `import "golang.org/x/text/internal/tag"`)1374 b := newBuilder(w)1375 gen.WriteCLDRVersion(w)1376 b.parseIndices()1377 b.writeType(FromTo{})1378 b.writeLanguage()1379 b.writeScript()1380 b.writeRegion()1381 b.writeVariant()1382 // TODO: b.writeLocale()1383 b.computeRegionGroups()1384 b.writeLikelyData()1385 b.writeRegionInclusionData()1386 b.writeParents()1387}...
Write
Using AI Code Generation
1lang.Write("Hello World")2lang.Write("Hello World")3lang.Write("Hello World")4lang.Write("Hello World")5lang.Write("Hello World")6lang.Write("Hello World")7lang.Write("Hello World")8lang.Write("Hello World")9lang.Write("Hello World")10lang.Write("Hello World")11lang.Write("Hello World")12lang.Write("Hello World")13lang.Write("Hello World")14lang.Write("Hello World")15lang.Write("Hello World")16lang.Write("Hello World")17lang.Write("Hello World")18lang.Write("Hello World")19lang.Write("Hello World")20lang.Write("Hello World")21lang.Write("Hello World")22lang.Write("Hello World")
Write
Using AI Code Generation
1import (2func main() {3 fmt.Println("Hello World")4}5import (6func main() {7 fmt.Println("Hello World")8}9import (10func main() {11 fmt.Println("Hello World")12}13import (14func main() {15 fmt.Println("Hello World")16}17import (18func main() {19 fmt.Println("Hello World")20}
Write
Using AI Code Generation
1import (2func main() {3 fmt.Println("Hello World")4}5import (6func main() {7 fmt.Println("Hello World")8}9import (10func main() {11 fmt.Println("Hello World")12}13import (14func main() {15 fmt.Println("Hello World")16}17import (18func main() {19 fmt.Println("Hello World")20}21import (22func main() {23 fmt.Println("Hello World")24}25import (26func main() {27 fmt.Println("Hello World")28}29import (30func main() {31 fmt.Println("Hello World")32}33import (34func main() {35 fmt.Println("Hello World")36}37import (38func main() {39 fmt.Println("Hello World")40}41import (42func main() {43 fmt.Println("Hello World")44}45import (46func main() {47 fmt.Println("Hello World")48}49import (50func main() {51 fmt.Println("Hello World")52}53import (54func main() {55 fmt.Println("Hello
Write
Using AI Code Generation
1import "fmt"2func main() {3 fmt.Println("Hello, World!")4}5import "fmt"6func main() {7 fmt.Println("Hello, World!")8 fmt.Println("Hello, World!")9 fmt.Println("Hello, World!")10}11import "fmt"12func main() {13 fmt.Println("Hell
Write
Using AI Code Generation
1func main() {2 lang.Write("Hello World")3}4import "fmt"5func main() {6 fmt.Println("Hello World")7}
Write
Using AI Code Generation
1import (2func main() {3 fmt.Println("Hello World")4 file, err := os.OpenFile("test.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)5 if err != nil {6 fmt.Println("Error in opening file")7 }8 defer file.Close()9 file.WriteString("Hello World")10 file.WriteString("Hello World")
Write
Using AI Code Generation
1import (2func main() {3 l = eng.New()4 l.Write("Hello World!")5 l = french.New()6 l.Write("Hello World!")7 l = german.New()8 l.Write("Hello World!")9 l = italian.New()10 l.Write("Hello World!")11 l = spanish.New()12 l.Write("Hello World!")13 fmt.Println("Done!")14}
Write
Using AI Code Generation
1import (2func main() {3 l := lang.New("Go")4 l.Write([]byte("Hello World5}6import "fmt"7type Lang struct {8}9func New(name string) *Lang {10 return &Lang{name}11}12func (l *Lang) Write(data []byte) (int, error) {13 return fmt.Printf("%s: %s", l.name, data)14}
Write
Using AI Code Generation
1import (2func main() {3 lang.Write()4}5func Read() {6 reader := bufio.NewReader(os.Stdin)7 fmt.Print("Enter text: ")8 text, _ := reader.ReadString('9 fmt.Println(text)10}11import (12func main() {13 lang.Read()14}15func Add(a int, b int) {16 fmt.Println(a + b)17}18import (19func main() {20 lang.Add(2, 3)21}22func Sub(a int, b int) {23 fmt.Println(a - b)24}25import (26func main() {27 lang.Sub(5, 3)28}
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!