// SPDX-License-Identifier: MIT //go:build ignore // Command casing reads the JSON produced by casing_dump.mjs on stdin and // writes casing_tables.go (to the file given with -o, else on stdout). Only the code points whose JavaScript // mapping differs from the Go simple mapping (unicode.ToUpper/ToLower) are // emitted: multi-character expansions (U+00DF to "SS") and Unicode version // differences between Go and the JavaScript engine. package main import ( "encoding/json" "flag" "fmt" "go/format" "io" "os" "sort" "strconv" "unicode" ) type dump struct { Upper map[string]string `json:"upper"` Lower map[string]string `json:"lower"` } func main() { out := flag.String("o", "", "output file (default stdout)") flag.Parse() var d dump if err := json.NewDecoder(os.Stdin).Decode(&d); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } // Everything is printed to a pipe, gofmt-ed and then written, so that the // generated file is already in canonical form. r, w, err := os.Pipe() if err != nil { fatal(err) } stdout := os.Stdout os.Stdout = w done := make(chan []byte) go func() { b, _ := io.ReadAll(r) done <- b }() defer func() { _ = w.Close() os.Stdout = stdout src, err := format.Source(<-done) if err != nil { fatal(err) } if *out == "" { _, _ = stdout.Write(src) return } if err := os.WriteFile(*out, src, 0o644); err != nil { fatal(err) } }() fmt.Println("// SPDX-License-Identifier: MIT") fmt.Println() fmt.Println("// Code generated by gen/casing.go from gen/casing_dump.mjs; DO NOT EDIT.") fmt.Println() fmt.Println("package expr") fmt.Println() fmt.Println("// casingGoUnicodeVersion is the Unicode version of the Go toolchain that generated") fmt.Println("// the tables below: they are relative to its unicode.ToUpper and unicode.ToLower.") fmt.Printf("const casingGoUnicodeVersion = %q\n", unicode.Version) fmt.Println() emit("jsUpperDelta", "JavaScript String.prototype.toUpperCase", d.Upper, unicode.ToUpper) emit("jsLowerDelta", "JavaScript String.prototype.toLowerCase (without the Final_Sigma context)", d.Lower, unicode.ToLower) } func emit(name, doc string, m map[string]string, goMap func(rune) rune) { // Include code points that JavaScript maps to themselves but Go does not. type entry struct { cp rune s string } var out []entry seen := map[rune]bool{} for k, v := range m { n, _ := strconv.Atoi(k) r := rune(n) seen[r] = true if string(goMap(r)) != v { out = append(out, entry{r, v}) } } for r := rune(0); r <= unicode.MaxRune; r++ { if r >= 0xd800 && r <= 0xdfff || seen[r] { continue } if goMap(r) != r { out = append(out, entry{r, string(r)}) } } sort.Slice(out, func(i, j int) bool { return out[i].cp < out[j].cp }) fmt.Printf("// %s maps the code points whose %s result differs from the Go simple mapping.\n", name, doc) fmt.Printf("var %s = map[rune]string{\n", name) for _, e := range out { fmt.Printf("\t0x%04X: %s, // %s\n", e.cp, strconv.Quote(e.s), strconv.QuoteToGraphic(string(e.cp))) } fmt.Println("}") fmt.Println() } func fatal(err error) { fmt.Fprintln(os.Stderr, err) os.Exit(1) }