package lang import "strings" // Kind är vad ett textsegment är. type Kind int // Segmenttyper. const ( Plain Kind = iota // allt som inte är japanska: siffror, pilar, latin, mellanslag Word // ett ord ur ordboken Unknown // japanska tecken som inte täcks av ordboken ) // Segment är en bit av en skylttext. type Segment struct { Text string Kind Kind Entry *Entry // satt när Kind == Word } // Tokenize delar upp en text i ordboksord (längsta matchning först), okänd // japanska och övrig text. Sammanhängande okänd japanska slås ihop till ett // segment så att tester kan peka ut vad som saknas i ordboken. func (d *Dictionary) Tokenize(s string) []Segment { runes := []rune(s) var out []Segment flush := func(kind Kind, text string) { if text == "" { return } if n := len(out); n > 0 && out[n-1].Kind == kind && kind != Word { out[n-1].Text += text return } out = append(out, Segment{Text: text, Kind: kind}) } for i := 0; i < len(runes); { r := runes[i] if !IsKana(r) && !IsKanji(r) { flush(Plain, string(r)) i++ continue } if w := d.longestMatch(runes[i:]); w != "" { out = append(out, Segment{Text: w, Kind: Word, Entry: d.entries[w]}) i += len([]rune(w)) continue } flush(Unknown, string(r)) i++ } return out } func (d *Dictionary) longestMatch(rest []rune) string { for _, w := range d.words { // längsta först wr := []rune(w) if len(wr) > len(rest) { continue } match := true for k, c := range wr { if rest[k] != c { match = false break } } if match { return w } } return "" } // Unknown returnerar den japanska i texten som ordboken inte täcker. func (d *Dictionary) Unknown(s string) []string { var out []string for _, seg := range d.Tokenize(s) { if seg.Kind == Unknown { out = append(out, strings.TrimSpace(seg.Text)) } } return out } // WordsIn returnerar ordboksorden i texten i ordning (med dubbletter). func (d *Dictionary) WordsIn(s string) []string { var out []string for _, seg := range d.Tokenize(s) { if seg.Kind == Word { out = append(out, seg.Text) } } return out }