Rewrite some struct linkages

This commit is contained in:
2026-02-20 21:06:47 +08:00
parent 1d9407331a
commit d54f106dee
10 changed files with 228 additions and 128 deletions

View File

@@ -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
}

View File

@@ -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)
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, cjp.Ref.Face)
utils.WriteMDv2Text(&sb, cchar.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)
}
}
}
}
} else {
cjp := any((*q.Results)[0]).(*CFace)
if args == 1 {
writeCCharInfo(&sb, cjp.Ref)
return "", fmt.Errorf("Multiple results found: %s", *q.Results)
}
} 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)

View File

@@ -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))

View File

@@ -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")
}
}

View File

@@ -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 {
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.Key = &c.Face
charMap[face] = c
}
*tjz = append(*tjz, c)
var dj *DukJam
if len(record) == 2 {
c.JyutPing = record[1]
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, "%") {
_, err := strconv.Atoi(s[:len(s)-1])
w, 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
}
} else {
c = &CChar{}
c.Face = face
c.JyutPing = jyutping
charMap[face] = c
dj = &DukJam{}
dj.Weight = w
dj.JyutPing = cjp
}
pListPtr, ok := JyutPingMap[jyutping]
if !ok {
pList := []*CChar{}
pListPtr = &pList
JyutPingMap[jyutping] = pListPtr
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

View File

@@ -7,7 +7,7 @@
---
name: jyut6ping3.chars
version: "2025.11.27"
version: "2025.12.24"
sort: by_weight
...

View File

@@ -5,6 +5,7 @@ import (
)
func TestDataRead(t *testing.T) {
/*
chars := map[string]*CChar{}
cjRepl, err := ReadCangJieKeys()
if err != nil {
@@ -16,10 +17,9 @@ func TestDataRead(t *testing.T) {
t.Error(err)
}
j := *JyutPingMap["sing4"]
if j[0].TungJamZi != j[1].TungJamZi {
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)
}

View File

@@ -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 數")
}
}

View File

@@ -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