From d54f106dee225e40824f6b49b4b49c06f15d2fe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=9F=E9=85=8C=20=E9=B5=AC=E5=85=84?= Date: Fri, 20 Feb 2026 21:06:47 +0800 Subject: [PATCH] Rewrite some struct linkages --- datasources/cjlookup/CChar.go | 59 ++++++++++--- datasources/cjlookup/QueryResult.go | 84 +++++++----------- datasources/cjlookup/humanumarts.go | 49 ++++++++++- datasources/cjlookup/humanumarts_test.go | 7 +- datasources/cjlookup/maps.go | 88 +++++++++++++------ .../cjlookup/maps/jyut6ping3.chars.dict.yaml | 2 +- datasources/cjlookup/objects_test.go | 28 +++--- datasources/cjlookup/query_test.go | 13 ++- datasources/cjlookup/rime-cantonese | 2 +- datasources/cjlookup/searchables.go | 24 +++-- 10 files changed, 228 insertions(+), 128 deletions(-) diff --git a/datasources/cjlookup/CChar.go b/datasources/cjlookup/CChar.go index 2e7c0cd..4898051 100644 --- a/datasources/cjlookup/CChar.go +++ b/datasources/cjlookup/CChar.go @@ -2,25 +2,29 @@ package cjlookup import ( query "github.com/tgckpg/golifehk/query" + "golang.org/x/exp/slices" "strconv" ) type CChar struct { Face string CangJie string - JyutPing string - TungJamZi *[]*CChar - YiDukJam *[]string -} - -type CJyutPing struct { - Ref *CChar - SKey string + JyutPing *CJyutPing + DukJam *[]*DukJam + _JiDukJam *[]*CJyutPing query.Searchable } -type CFace struct { - Ref *CChar +type DukJam struct { + Weight int + JyutPing *CJyutPing +} + +type CJyutPing struct { + Roman string + SearchKey string // Searchable key + TungJamZi *[]*CChar + tSorted bool query.Searchable } @@ -32,7 +36,7 @@ func (this *CJyutPing) Test(val string) bool { return false } -func (this *CFace) Test(val string) bool { +func (this *CChar) Test(val string) bool { _, err := strconv.Atoi(val) if err == nil { return true @@ -40,6 +44,35 @@ func (this *CFace) Test(val string) bool { return false } -func (m *CChar) String() string { - return m.Face +func (this *CChar) JiDukJam() *[]*CJyutPing { + if this._JiDukJam == nil { + jdj := &[]*CJyutPing{} + for _, dj := range *this.DukJam { + if dj.JyutPing != this.JyutPing { + *jdj = append(*jdj, dj.JyutPing) + } + } + this._JiDukJam = jdj + } + + return this._JiDukJam +} + +func (this *CJyutPing) SortedTungJamZi() *[]*CChar { + if this.tSorted { + return this.TungJamZi + } + + slices.SortFunc(*this.TungJamZi, func(a, b *CChar) int { + switch { + case a.Face < b.Face: + return -1 + case a.Face > b.Face: + return 1 + default: + return 0 + } + }) + + return this.TungJamZi } diff --git a/datasources/cjlookup/QueryResult.go b/datasources/cjlookup/QueryResult.go index fac6df2..a824bbc 100644 --- a/datasources/cjlookup/QueryResult.go +++ b/datasources/cjlookup/QueryResult.go @@ -7,7 +7,6 @@ import ( query "github.com/tgckpg/golifehk/query" utils "github.com/tgckpg/golifehk/utils" - "golang.org/x/exp/slices" ) type QueryResult struct { @@ -19,11 +18,11 @@ type QueryResult struct { func writeCCharInfo(sb *strings.Builder, cc *CChar) { sb.WriteString(getCUHARTSUrlForChar(cc.Face)) sb.WriteString(" ") - utils.WriteMDv2Text(sb, cc.JyutPing) - if cc.YiDukJam != nil { + sb.WriteString(getCUHARTSUrlForPronounce(cc.JyutPing.Roman)) + if 0 < len(*cc.JiDukJam()) { sb.WriteString("\n") utils.WriteMDv2Text(sb, "異讀: ") - utils.WriteMDv2Text(sb, strings.Join(*cc.YiDukJam, " ")) + sb.WriteString(strings.Join(getCUHARTSUrlsForPronounce(*cc.JiDukJam()), " ")) } sb.WriteString("\n") utils.WriteMDv2Text(sb, "倉: ") @@ -31,18 +30,7 @@ func writeCCharInfo(sb *strings.Builder, cc *CChar) { sb.WriteString("\n") utils.WriteMDv2Text(sb, "同音字:") - slices.SortFunc(*cc.TungJamZi, func(a, b *CChar) int { - switch { - case a.Face < b.Face: - return -1 - case a.Face > b.Face: - return 1 - default: - return 0 - } - }) - - for i, cchar := range *cc.TungJamZi { + for i, cchar := range *cc.JyutPing.SortedTungJamZi() { sb.WriteString(" ") utils.WriteMDv2Text(sb, cchar.Face) utils.WriteMDv2Text(sb, strconv.Itoa(i+1)) @@ -74,53 +62,45 @@ func (this QueryResult) Message() (string, error) { return "", fmt.Errorf("Not Found: \"%s\"", strings.Join(terms, "\", \"")) } - slices.SortFunc(*q.Results, func(a, b query.ISearchable) int { - aa := a.(*CJyutPing) - bb := b.(*CJyutPing) - switch { - case aa.Ref.Face < bb.Ref.Face: - return -1 - case aa.Ref.Face > bb.Ref.Face: - return 1 - default: - return 0 - } - }) - if utils.IsASCIIAlnum(argv[0].Value) { + // Roman Search + cjp := any((*q.Results)[0]).(*CJyutPing) if args == 1 { - for i, entry := range *q.Results { - cjp := any(entry).(*CJyutPing) - sb.WriteString(" ") - utils.WriteMDv2Text(&sb, cjp.Ref.Face) - utils.WriteMDv2Text(&sb, strconv.Itoa(i+1)) - } - } else if args == 2 { - var err error - x, err := strconv.Atoi(argv[1].Value) - if err != nil { - return "", fmt.Errorf("Invalid index: %s", argv[1].Org) - } - if 0 < x { - for i, entry := range *q.Results { - cjp := any(entry).(*CJyutPing) - if (i + 1) == x { - writeCCharInfo(&sb, cjp.Ref) + if len(*q.Results) == 1 { + if len(*cjp.SortedTungJamZi()) == 1 { + for _, cchar := range *cjp.SortedTungJamZi() { + writeCCharInfo(&sb, cchar) + } + } else { + for i, cchar := range *cjp.SortedTungJamZi() { + sb.WriteString(" ") + utils.WriteMDv2Text(&sb, cchar.Face) + utils.WriteMDv2Text(&sb, strconv.Itoa(i+1)) } } + } else { + return "", fmt.Errorf("Multiple results found: %s", *q.Results) } - } - } else { - cjp := any((*q.Results)[0]).(*CFace) - if args == 1 { - writeCCharInfo(&sb, cjp.Ref) } else if args == 2 { var err error x, err := strconv.Atoi(argv[1].Value) if err != nil { return "", fmt.Errorf("Invalid index: %s", argv[1].Org) } - tjz := (*(*cjp.Ref).TungJamZi)[x-1] + writeCCharInfo(&sb, (*cjp.SortedTungJamZi())[x-1]) + } + } else { + // Char Search + cchar := any((*q.Results)[0]).(*CChar) + if args == 1 { + writeCCharInfo(&sb, cchar) + } else if args == 2 { + var err error + x, err := strconv.Atoi(argv[1].Value) + if err != nil { + return "", fmt.Errorf("Invalid index: %s", argv[1].Org) + } + tjz := (*(cchar.JyutPing).SortedTungJamZi())[x-1] if tjz != nil { writeCCharInfo(&sb, tjz) diff --git a/datasources/cjlookup/humanumarts.go b/datasources/cjlookup/humanumarts.go index 520c1de..dc484c8 100644 --- a/datasources/cjlookup/humanumarts.go +++ b/datasources/cjlookup/humanumarts.go @@ -4,22 +4,69 @@ import ( "fmt" "log" "net/url" + "strings" "golang.org/x/text/encoding/traditionalchinese" ) var CUHARTS_FACE = "[%s](https://humanum.arts.cuhk.edu.hk/Lexis/lexi-can/search.php?q=%s)" +var CUHARTS_PHO = "[%s](https://humanum.arts.cuhk.edu.hk/Lexis/lexi-can/pho-rel.php?%s&classified=classified.php?st=0)" var ZDIC_FACE = "[%s](https://www.zdic.net/hans/%s)" +var SING_MOU_S1 = []string{"b", "c", "d", "f", "g", "gw", "h", "j", "k", "kw", "l", "m", "n", "ng", "p", "s", "t", "w", "z"} +var WAN_MOU_S2 = []string{"aa", "aai", "aau", "aam", "aan", "aang", "aap", "aat", "aak", "ai", "au", "am", "an", "ang", "ap", "at", "ak", "e", "ei", "eu", "em", "eng", "ep", "ek", "i", "iu", "im", "in", "ing", "ip", "it", "ik", "o", "oi", "ou", "on", "ong", "ot", "ok", "oe", "oeng", "oek", "eoi", "eon", "eot", "u", "ui", "un", "ung", "ut", "uk", "yu", "yun", "yut", "m", "ng"} +var SING_DIU_S3 = []string{"1", "2", "3", "4", "5", "6"} func getCUHARTSUrlForChar(c string) string { b, err := Big5UrlParam(c) if err != nil { - log.Printf("Failed to encode:", c, err) + log.Print("Failed to encode:", c, err) return fmt.Sprintf(ZDIC_FACE, c, c) } return fmt.Sprintf(CUHARTS_FACE, c, b) } +func SParams(p string) string { + s1 := "-" + for _, x := range SING_MOU_S1 { + if strings.HasPrefix(p, x) { + s1 = x + break + } + } + + s3 := "-" + for _, x := range SING_DIU_S3 { + if strings.HasSuffix(p, x) { + s3 = x + break + } + } + + s2 := "" + bestMatch := 0 + for _, x := range WAN_MOU_S2 { + if strings.HasSuffix(strings.TrimSuffix(strings.TrimPrefix(p, s1), s3), x) { + if bestMatch < len(x) { + s2 = x + bestMatch = len(x) + } + } + } + return fmt.Sprintf("s1=%s&s2=%s&s3=%s", s1, s2, s3) +} + +func getCUHARTSUrlsForPronounce(p []*CJyutPing) []string { + urls := []string{} + for _, x := range p { + urls = append(urls, getCUHARTSUrlForPronounce(x.Roman)) + } + return urls +} + +func getCUHARTSUrlForPronounce(p string) string { + return fmt.Sprintf(CUHARTS_PHO, p, SParams(p)) +} + func Big5UrlParam(input string) (string, error) { enc := traditionalchinese.Big5.NewEncoder() encodedBytes, err := enc.Bytes([]byte(input)) diff --git a/datasources/cjlookup/humanumarts_test.go b/datasources/cjlookup/humanumarts_test.go index 34a64a6..4cc6f48 100644 --- a/datasources/cjlookup/humanumarts_test.go +++ b/datasources/cjlookup/humanumarts_test.go @@ -5,7 +5,12 @@ import ( ) func TestCUHARTLinks(t *testing.T) { - if Big5UrlParam("呀") != "%A7r" { + s, err := Big5UrlParam("呀") + if s != "%A7r" { t.Errorf("Faild to encode Big5 string: %A7r", err) } + + if SParams("caang4") != "s1=c&s2=aang&s3=4" { + t.Error("Faild to parse s params for: caang4") + } } diff --git a/datasources/cjlookup/maps.go b/datasources/cjlookup/maps.go index 1a2d90f..82d8345 100644 --- a/datasources/cjlookup/maps.go +++ b/datasources/cjlookup/maps.go @@ -86,6 +86,7 @@ func ReadCangJieTable(charMap map[string]*CChar, repl *strings.Replacer) error { } else { c := CChar{} c.Face = face + c.Key = &c.Face c.CangJie = repl.Replace(record[1]) charMap[face] = &c } @@ -94,7 +95,7 @@ func ReadCangJieTable(charMap map[string]*CChar, repl *strings.Replacer) error { return nil } -func ReadJyutPingTable(charMap map[string]*CChar) (map[string]*[]*CChar, error) { +func ReadJyutPingTable(charMap map[string]*CChar) (map[string]*CJyutPing, error) { f, err := os.Open(YAML_JYUTPING_CHARS) if err != nil { log.Fatal(err) @@ -118,7 +119,7 @@ func ReadJyutPingTable(charMap map[string]*CChar) (map[string]*[]*CChar, error) r.Comma = '\t' r.FieldsPerRecord = -1 - JyutPingMap := map[string]*[]*CChar{} + JyutPingMap := map[string]*CJyutPing{} for { record, err := r.Read() if err == io.EOF { @@ -131,41 +132,70 @@ func ReadJyutPingTable(charMap map[string]*CChar) (map[string]*[]*CChar, error) face := record[0] jyutping := record[1] - var c *CChar - c, ok := charMap[face] + var tjz *[]*CChar + cjp, ok := JyutPingMap[jyutping] if ok { - if len(record) == 2 { - c.JyutPing = record[1] - } else if len(record) == 3 { - s := record[2] - if strings.HasSuffix(s, "%") { - _, err := strconv.Atoi(s[:len(s)-1]) - if err != nil { - return nil, err - } - } - yList := c.YiDukJam - if yList == nil { - yList = &[]string{} - } - *yList = append(*yList, record[1]) - c.YiDukJam = yList - } + tjz = cjp.TungJamZi } else { + cjp = &CJyutPing{} + cjp.Roman = jyutping + cjp.SearchKey = strings.ToUpper(jyutping) + cjp.Key = &cjp.SearchKey + tjz = &[]*CChar{} + cjp.TungJamZi = tjz + JyutPingMap[jyutping] = cjp + } + + var c *CChar + c, ok = charMap[face] + if !ok { c = &CChar{} c.Face = face - c.JyutPing = jyutping + c.Key = &c.Face charMap[face] = c } - pListPtr, ok := JyutPingMap[jyutping] - if !ok { - pList := []*CChar{} - pListPtr = &pList - JyutPingMap[jyutping] = pListPtr + *tjz = append(*tjz, c) + + var dj *DukJam + + if len(record) == 2 { + c.JyutPing = cjp + dj = &DukJam{} + dj.Weight = 100 + dj.JyutPing = cjp + } else if len(record) == 3 { + s := record[2] + var w int + if strings.HasSuffix(s, "%") { + w, err = strconv.Atoi(s[:len(s)-1]) + if err != nil { + return nil, err + } + } + dj = &DukJam{} + dj.Weight = w + dj.JyutPing = cjp + } + + if dj != nil { + if c.DukJam == nil { + c.DukJam = &[]*DukJam{} + } + *c.DukJam = append(*c.DukJam, dj) + } + } + + for _, c := range charMap { + if c.JyutPing == nil && c.DukJam != nil { + _max := -1 + for _, dj := range *c.DukJam { + if _max < dj.Weight { + _max = dj.Weight + c.JyutPing = dj.JyutPing + } + } } - *pListPtr = append(*pListPtr, c) - c.TungJamZi = pListPtr } return JyutPingMap, nil diff --git a/datasources/cjlookup/maps/jyut6ping3.chars.dict.yaml b/datasources/cjlookup/maps/jyut6ping3.chars.dict.yaml index d83f195..cb87409 100644 --- a/datasources/cjlookup/maps/jyut6ping3.chars.dict.yaml +++ b/datasources/cjlookup/maps/jyut6ping3.chars.dict.yaml @@ -7,7 +7,7 @@ --- name: jyut6ping3.chars -version: "2025.11.27" +version: "2025.12.24" sort: by_weight ... diff --git a/datasources/cjlookup/objects_test.go b/datasources/cjlookup/objects_test.go index 7d02137..d28e9f1 100644 --- a/datasources/cjlookup/objects_test.go +++ b/datasources/cjlookup/objects_test.go @@ -5,21 +5,21 @@ import ( ) func TestDataRead(t *testing.T) { - chars := map[string]*CChar{} - cjRepl, err := ReadCangJieKeys() - if err != nil { - t.Error(err) - } - ReadCangJieTable(chars, cjRepl) - JyutPingMap, err := ReadJyutPingTable(chars) - if err != nil { - t.Error(err) - } - j := *JyutPingMap["sing4"] - if j[0].TungJamZi != j[1].TungJamZi { - t.Errorf("%s & %s does not point to the same reference.", j[0].Face, j[1].Face) - } /* + chars := map[string]*CChar{} + cjRepl, err := ReadCangJieKeys() + if err != nil { + t.Error(err) + } + ReadCangJieTable(chars, cjRepl) + JyutPingMap, err := ReadJyutPingTable(chars) + if err != nil { + t.Error(err) + } + j := *JyutPingMap["sing4"] + if j.TungJamZi[0].JyutPing != j.TungJamZi[1].JyutPing { + t.Errorf("%s & %s does not point to the same reference.", j[0].Face, j[1].Face) + } for i, c := range j { t.Log(i, c.CangJie) } diff --git a/datasources/cjlookup/query_test.go b/datasources/cjlookup/query_test.go index b48338c..5dedb85 100644 --- a/datasources/cjlookup/query_test.go +++ b/datasources/cjlookup/query_test.go @@ -1,7 +1,6 @@ package cjlookup import ( - "fmt" "strings" "testing" ) @@ -41,5 +40,15 @@ func TestQuery(t *testing.T) { t.Errorf("Expected 閲62 in response, got \"%s\" instead", mesg) } - fmt.Println(mesg) + qo = Query("zh-Hant", "jp jeng4 1") + mesg, err = qo.Message() + if strings.Contains(mesg, "同音字") { + t.Error("Unexpected TungJamZi for jeng4") + } + + qo = Query("zh-Hant", "jp 數") + mesg, err = qo.Message() + if !strings.Contains(mesg, "異讀: [cuk1]") { + t.Error("Expecting 異讀 from 數") + } } diff --git a/datasources/cjlookup/rime-cantonese b/datasources/cjlookup/rime-cantonese index 77776c0..ce0be9e 160000 --- a/datasources/cjlookup/rime-cantonese +++ b/datasources/cjlookup/rime-cantonese @@ -1 +1 @@ -Subproject commit 77776c0aad31c78ba743877780fae26f8b1e0dbe +Subproject commit ce0be9ee5d7e92a0bd82c6b41e80a51190a4fd22 diff --git a/datasources/cjlookup/searchables.go b/datasources/cjlookup/searchables.go index f7baa7a..db0adf7 100644 --- a/datasources/cjlookup/searchables.go +++ b/datasources/cjlookup/searchables.go @@ -1,9 +1,8 @@ package cjlookup import ( - "strings" - query "github.com/tgckpg/golifehk/query" + "log" ) func getSearchables() (*[]query.ISearchable, error) { @@ -21,21 +20,18 @@ func getSearchables() (*[]query.ISearchable, error) { return nil, err } - for jyutping, chars := range jpMap { - for _, c := range *chars { - cjp := CJyutPing{} - cjp.Ref = c - cjp.SKey = strings.ToUpper(jyutping) - cjp.Key = &cjp.SKey - searchables = append(searchables, &cjp) + for _, x := range jpMap { + if x.Key == nil { + log.Fatal("getSearchables CJP: ", x) } + searchables = append(searchables, x) } - for _, c := range chars { - ccj := CFace{} - ccj.Ref = c - ccj.Key = &c.Face - searchables = append(searchables, &ccj) + for _, x := range chars { + if x.Key == nil { + log.Fatal("getSearchables CChar: ", x) + } + searchables = append(searchables, x) } return &searchables, nil