@@ -28,21 +28,75 @@ import (
2828type BuildOption int
2929
3030const (
31- KIWI_BUILD_LOAD_DEFAULT_DICT BuildOption = C .KIWI_BUILD_LOAD_DEFAULT_DICT
3231 KIWI_BUILD_INTEGRATE_ALLOMORPH BuildOption = C .KIWI_BUILD_INTEGRATE_ALLOMORPH
32+ KIWI_BUILD_LOAD_DEFAULT_DICT BuildOption = C .KIWI_BUILD_LOAD_DEFAULT_DICT
33+ KIWI_BUILD_LOAD_TYPO_DICT BuildOption = C .KIWI_BUILD_LOAD_TYPO_DICT
34+ KIWI_BUILD_LOAD_MULTI_DICT BuildOption = C .KIWI_BUILD_LOAD_MULTI_DICT
3335 KIWI_BUILD_DEFAULT BuildOption = C .KIWI_BUILD_DEFAULT
36+
37+ // Model type is a single-select field, not a bitmask.
38+ // Select exactly one of the following mutually exclusive model types:
39+ //
40+ // - MODEL_TYPE_DEFAULT: default model (0x0000)
41+ // - MODEL_TYPE_LARGEST: largest available model
42+ // - MODEL_TYPE_KNLM: KNLM model (deprecated)
43+ // - MODEL_TYPE_SBG: SBG model (deprecated)
44+ // - MODEL_TYPE_CONG: CoNg model
45+ // - MODEL_TYPE_CONG_GLOBAL: CoNg global model
46+ //
47+ // Do NOT combine these with bitwise OR.
48+ KIWI_BUILD_MODEL_TYPE_DEFAULT BuildOption = C .KIWI_BUILD_MODEL_TYPE_DEFAULT
49+ KIWI_BUILD_MODEL_TYPE_LARGEST BuildOption = C .KIWI_BUILD_MODEL_TYPE_LARGEST
50+ KIWI_BUILD_MODEL_TYPE_KNLM BuildOption = C .KIWI_BUILD_MODEL_TYPE_KNLM
51+ KIWI_BUILD_MODEL_TYPE_SBG BuildOption = C .KIWI_BUILD_MODEL_TYPE_SBG
52+ KIWI_BUILD_MODEL_TYPE_CONG BuildOption = C .KIWI_BUILD_MODEL_TYPE_CONG
53+ KIWI_BUILD_MODEL_TYPE_CONG_GLOBAL BuildOption = C .KIWI_BUILD_MODEL_TYPE_CONG_GLOBAL
3454)
3555
3656// MatchOption is a bitwise OR of the KiwiMatchOption values.
3757type MatchOption int
3858
3959const (
40- KIWI_MATCH_URL MatchOption = C .KIWI_MATCH_URL
41- KIWI_MATCH_EMAIL MatchOption = C .KIWI_MATCH_EMAIL
42- KIWI_MATCH_HASHTAG MatchOption = C .KIWI_MATCH_HASHTAG
43- KIWI_MATCH_MENTION MatchOption = C .KIWI_MATCH_MENTION
60+ KIWI_MATCH_URL MatchOption = C .KIWI_MATCH_URL
61+ KIWI_MATCH_EMAIL MatchOption = C .KIWI_MATCH_EMAIL
62+ KIWI_MATCH_HASHTAG MatchOption = C .KIWI_MATCH_HASHTAG
63+ KIWI_MATCH_MENTION MatchOption = C .KIWI_MATCH_MENTION
64+ KIWI_MATCH_SERIAL MatchOption = C .KIWI_MATCH_SERIAL
65+ KIWI_MATCH_EMOJI MatchOption = C .KIWI_MATCH_EMOJI
66+
67+ // OOV detection mode is a 2-bit field (bits 8-9), not independent flags.
68+ // Select exactly one of the following mutually exclusive modes:
69+ //
70+ // - OOV_RULE_ONLY: rule-based scoring (default, value 0 << 8)
71+ // - OOV_CHR_MODEL: character model-based scoring
72+ // - OOV_CHR_FREQ_MODEL: character + frequency model scoring
73+ // - OOV_CHR_FREQ_BRANCH_MODEL: character + frequency + branch model scoring
74+ //
75+ // Do NOT combine these with bitwise OR. KIWI_MATCH_OOV_MASK is for
76+ // internal use and should not be selected directly.
77+ KIWI_MATCH_OOV_RULE_ONLY MatchOption = C .KIWI_MATCH_OOV_RULE_ONLY
78+ KIWI_MATCH_OOV_CHR_MODEL MatchOption = C .KIWI_MATCH_OOV_CHR_MODEL
79+ KIWI_MATCH_OOV_CHR_FREQ_MODEL MatchOption = C .KIWI_MATCH_OOV_CHR_FREQ_MODEL
80+ KIWI_MATCH_OOV_CHR_FREQ_BRANCH_MODEL MatchOption = C .KIWI_MATCH_OOV_CHR_FREQ_BRANCH_MODEL
81+ KIWI_MATCH_OOV_MASK MatchOption = C .KIWI_MATCH_OOV_MASK
82+
83+ KIWI_MATCH_NORMALIZE_CODA MatchOption = C .KIWI_MATCH_NORMALIZE_CODA
84+ KIWI_MATCH_JOIN_NOUN_PREFIX MatchOption = C .KIWI_MATCH_JOIN_NOUN_PREFIX
85+ KIWI_MATCH_JOIN_NOUN_SUFFIX MatchOption = C .KIWI_MATCH_JOIN_NOUN_SUFFIX
86+ KIWI_MATCH_JOIN_VERB_SUFFIX MatchOption = C .KIWI_MATCH_JOIN_VERB_SUFFIX
87+ KIWI_MATCH_JOIN_ADJ_SUFFIX MatchOption = C .KIWI_MATCH_JOIN_ADJ_SUFFIX
88+ KIWI_MATCH_JOIN_ADV_SUFFIX MatchOption = C .KIWI_MATCH_JOIN_ADV_SUFFIX
89+ KIWI_MATCH_JOIN_V_SUFFIX MatchOption = C .KIWI_MATCH_JOIN_V_SUFFIX
90+ KIWI_MATCH_JOIN_AFFIX MatchOption = C .KIWI_MATCH_JOIN_AFFIX
91+ KIWI_MATCH_SPLIT_COMPLEX MatchOption = C .KIWI_MATCH_SPLIT_COMPLEX
92+ KIWI_MATCH_Z_CODA MatchOption = C .KIWI_MATCH_Z_CODA
93+ KIWI_MATCH_COMPATIBLE_JAMO MatchOption = C .KIWI_MATCH_COMPATIBLE_JAMO
94+ KIWI_MATCH_SPLIT_SAISIOT MatchOption = C .KIWI_MATCH_SPLIT_SAISIOT
95+ KIWI_MATCH_MERGE_SAISIOT MatchOption = C .KIWI_MATCH_MERGE_SAISIOT
96+ KIWI_MATCH_JOIN_PARTICLE_YO MatchOption = C .KIWI_MATCH_JOIN_PARTICLE_YO
97+ KIWI_MATCH_USE_OLD_SPLITTER MatchOption = C .KIWI_MATCH_USE_OLD_SPLITTER
98+
4499 KIWI_MATCH_ALL MatchOption = C .KIWI_MATCH_ALL
45- KIWI_MATCH_NORMALIZE_CODA MatchOption = C .KIWI_MATCH_NORMALIZE_CODA
46100 KIWI_MATCH_ALL_WITH_NORMALIZING MatchOption = C .KIWI_MATCH_ALL_WITH_NORMALIZING
47101)
48102
@@ -136,12 +190,80 @@ func WithTopN(n int) AnalyzeOptionFunc {
136190 }
137191}
138192
193+ // WithBlocklist sets the blocklist for Analyze.
194+ func WithBlocklist (blocklist * Morphset ) AnalyzeOptionFunc {
195+ return func (opts * AnalyzeOptions ) {
196+ opts .Blocklist = blocklist
197+ }
198+ }
199+
200+ // WithOpenEnding sets whether to keep the sentence open after the last morpheme.
201+ func WithOpenEnding (openEnding bool ) AnalyzeOptionFunc {
202+ return func (opts * AnalyzeOptions ) {
203+ opts .OpenEnding = openEnding
204+ }
205+ }
206+
207+ // WithAllowedDialects sets the allowed dialects for Analyze.
208+ func WithAllowedDialects (dialects Dialect ) AnalyzeOptionFunc {
209+ return func (opts * AnalyzeOptions ) {
210+ opts .AllowedDialects = dialects
211+ }
212+ }
213+
139214// AnalyzeOptions provides configuration for the Analyze function.
140215type AnalyzeOptions struct {
141- MatchOptions MatchOption
142- DialectCost float32
143- TypoThreshold float32
144- TopN int
216+ MatchOptions MatchOption
217+ Blocklist * Morphset
218+ OpenEnding bool
219+ AllowedDialects Dialect
220+ DialectCost float32
221+ TypoThreshold float32
222+ TopN int
223+ }
224+
225+ // Morphset represents a set of morphemes that can be used as a blocklist.
226+ type Morphset struct {
227+ handler C.kiwi_morphset_h
228+ }
229+
230+ // NewMorphset creates a new morpheme set.
231+ // The Morphset must be closed after use, before the parent Kiwi instance is closed.
232+ func (k * Kiwi ) NewMorphset () (* Morphset , error ) {
233+ h := C .kiwi_new_morphset (k .handler )
234+ if h == nil {
235+ return nil , fmt .Errorf ("failed to create morphset: %s" , KiwiError ())
236+ }
237+ return & Morphset {handler : h }, nil
238+ }
239+
240+ // Add adds a morpheme to the set.
241+ // tag is a POS tag such as "NNG". If tag is empty, all morphemes matching form are added.
242+ // Returns the number of morphemes added, or an error.
243+ func (ms * Morphset ) Add (form string , tag string ) (int , error ) {
244+ cForm := C .CString (form )
245+ defer C .free (unsafe .Pointer (cForm ))
246+
247+ var cTag * C.char
248+ if tag != "" {
249+ cTag = C .CString (tag )
250+ defer C .free (unsafe .Pointer (cTag ))
251+ }
252+
253+ result := int (C .kiwi_morphset_add (ms .handler , cForm , cTag ))
254+ if result < 0 {
255+ return 0 , fmt .Errorf ("failed to add morpheme: %s" , KiwiError ())
256+ }
257+ return result , nil
258+ }
259+
260+ // Close frees the resources allocated for the Morphset.
261+ // Must be called before the parent Kiwi instance is closed.
262+ func (ms * Morphset ) Close () {
263+ if ms .handler != nil {
264+ C .kiwi_morphset_close (ms .handler )
265+ ms .handler = nil
266+ }
145267}
146268
147269// DefaultAnalyzeOptions returns the default AnalyzeOptions recommended by Kiwi.
@@ -170,13 +292,18 @@ func KiwiClearError() {
170292}
171293
172294// Kiwi is a wrapper for the kiwi C library.
295+ //
296+ // Thread Safety: Concurrent calls to Analyze are safe.
297+ // However, SetGlobalConfig must not be called concurrently with Analyze
298+ // or other methods that read the configuration.
173299type Kiwi struct {
174- handler C.kiwi_h
300+ handler C.kiwi_h
301+ dialects Dialect
175302}
176303
177304// New returns a new Kiwi instance.
178305// Don't forget to call Close after this.
179- func New (modelPath string , opts ... Option ) * Kiwi {
306+ func New (modelPath string , opts ... Option ) ( * Kiwi , error ) {
180307 options := kiwiOptions {
181308 buildOptions : KIWI_BUILD_DEFAULT ,
182309 dialects : DialectStandard ,
@@ -186,9 +313,18 @@ func New(modelPath string, opts ...Option) *Kiwi {
186313 opt (& options )
187314 }
188315
189- return & Kiwi {
190- handler : C .kiwi_init (C .CString (modelPath ), C .int (options .numThread ), C .int (options .buildOptions ), C .int (options .dialects )),
316+ cModelPath := C .CString (modelPath )
317+ defer C .free (unsafe .Pointer (cModelPath ))
318+
319+ h := C .kiwi_init (cModelPath , C .int (options .numThread ), C .int (options .buildOptions ), C .int (options .dialects ))
320+ if h == nil {
321+ return nil , fmt .Errorf ("kiwi_init failed: %s" , KiwiError ())
191322 }
323+
324+ return & Kiwi {
325+ handler : h ,
326+ dialects : options .dialects ,
327+ }, nil
192328}
193329
194330// TokenInfo returns the token info for the given token(Str).
@@ -223,10 +359,28 @@ func (k *Kiwi) Analyze(text string, opts ...AnalyzeOptionFunc) ([]TokenResult, e
223359
224360 defer C .free (unsafe .Pointer (cText ))
225361
362+ allowedDialects := options .AllowedDialects
363+ if allowedDialects == 0 {
364+ allowedDialects = k .dialects
365+ }
366+
367+ var blocklistHandler C.kiwi_morphset_h
368+ if options .Blocklist != nil {
369+ blocklistHandler = options .Blocklist .handler
370+ }
371+
372+ openEnding := 0
373+ if options .OpenEnding {
374+ openEnding = 1
375+ }
376+
226377 cOptions := C.kiwi_analyze_option_t {
227- match_options : C .int (options .MatchOptions ),
228- dialect_cost : C .float (options .DialectCost ),
229- typo_threshold : C .float (options .TypoThreshold ),
378+ match_options : C .int (options .MatchOptions ),
379+ blocklist : blocklistHandler ,
380+ open_ending : C .int (openEnding ),
381+ allowed_dialects : C .int (allowedDialects ),
382+ dialect_cost : C .float (options .DialectCost ),
383+ typo_threshold : C .float (options .TypoThreshold ),
230384 }
231385
232386 kiwiResH := C .kiwi_analyze (k .handler , cText , C .int (options .TopN ), cOptions , pretokenized )
@@ -334,7 +488,7 @@ type KiwiBuilder struct {
334488
335489// NewBuilder returns a new KiwiBuilder instance.
336490// Don't forget to call Close after this.
337- func NewBuilder (modelPath string , opts ... Option ) * KiwiBuilder {
491+ func NewBuilder (modelPath string , opts ... Option ) ( * KiwiBuilder , error ) {
338492 options := kiwiOptions {
339493 buildOptions : KIWI_BUILD_DEFAULT ,
340494 dialects : DialectStandard ,
@@ -344,19 +498,36 @@ func NewBuilder(modelPath string, opts ...Option) *KiwiBuilder {
344498 opt (& options )
345499 }
346500
347- return & KiwiBuilder {
348- handler : C .kiwi_builder_init (C .CString (modelPath ), C .int (options .numThread ), C .int (options .buildOptions ), C .int (options .dialects )),
501+ cModelPath := C .CString (modelPath )
502+ defer C .free (unsafe .Pointer (cModelPath ))
503+
504+ h := C .kiwi_builder_init (cModelPath , C .int (options .numThread ), C .int (options .buildOptions ), C .int (options .dialects ))
505+ if h == nil {
506+ return nil , fmt .Errorf ("kiwi_builder_init failed: %s" , KiwiError ())
349507 }
508+
509+ return & KiwiBuilder {
510+ handler : h ,
511+ }, nil
350512}
351513
352514// AddWord set custom word with word, pos, score.
353515func (kb * KiwiBuilder ) AddWord (word string , pos POSType , score float32 ) int {
354- return int (C .kiwi_builder_add_word (kb .handler , C .CString (word ), C .CString (string (pos )), C .float (score )))
516+ cWord := C .CString (word )
517+ defer C .free (unsafe .Pointer (cWord ))
518+
519+ cPos := C .CString (string (pos ))
520+ defer C .free (unsafe .Pointer (cPos ))
521+
522+ return int (C .kiwi_builder_add_word (kb .handler , cWord , cPos , C .float (score )))
355523}
356524
357525// LoadDict loads user dict with dict file path.
358526func (kb * KiwiBuilder ) LoadDict (dictPath string ) int {
359- return int (C .kiwi_builder_load_dict (kb .handler , C .CString (dictPath )))
527+ cDictPath := C .CString (dictPath )
528+ defer C .free (unsafe .Pointer (cDictPath ))
529+
530+ return int (C .kiwi_builder_load_dict (kb .handler , cDictPath ))
360531}
361532
362533// Build creates kiwi instance with user word etc.
@@ -451,3 +622,98 @@ func (kb *KiwiBuilder) ExtractWords(readSeeker io.ReadSeeker, minCnt int, maxWor
451622
452623 return res , nil
453624}
625+
626+ // Config represents the configuration for Kiwi analysis.
627+ type Config struct {
628+ IntegrateAllomorph bool
629+ CutOffThreshold float32
630+ OovRuleScale float32
631+ OovRuleBias float32
632+ OovChrBias float32
633+ OovGlobalWeight float32
634+ OovLocalWeight float32
635+ OovGlobalMinFreq float32
636+ SpacePenalty float32
637+ TypoCostWeight float32
638+ MaxUnkFormSize uint32
639+ MaxUnkFormSizeFollowedByJClass uint32
640+ SpaceTolerance uint32
641+ }
642+
643+ // GetGlobalConfig returns the global configuration of the Kiwi instance.
644+ func (k * Kiwi ) GetGlobalConfig () Config {
645+ cConfig := C .kiwi_get_global_config (k .handler )
646+ return Config {
647+ IntegrateAllomorph : cConfig .integrate_allomorph != 0 ,
648+ CutOffThreshold : float32 (cConfig .cut_off_threshold ),
649+ OovRuleScale : float32 (cConfig .oov_rule_scale ),
650+ OovRuleBias : float32 (cConfig .oov_rule_bias ),
651+ OovChrBias : float32 (cConfig .oov_chr_bias ),
652+ OovGlobalWeight : float32 (cConfig .oov_global_weight ),
653+ OovLocalWeight : float32 (cConfig .oov_local_weight ),
654+ OovGlobalMinFreq : float32 (cConfig .oov_global_min_freq ),
655+ SpacePenalty : float32 (cConfig .space_penalty ),
656+ TypoCostWeight : float32 (cConfig .typo_cost_weight ),
657+ MaxUnkFormSize : uint32 (cConfig .max_unk_form_size ),
658+ MaxUnkFormSizeFollowedByJClass : uint32 (cConfig .max_unk_form_size_followed_by_j_class ),
659+ SpaceTolerance : uint32 (cConfig .space_tolerance ),
660+ }
661+ }
662+
663+ // SetGlobalConfig sets the global configuration of the Kiwi instance.
664+ func (k * Kiwi ) SetGlobalConfig (config Config ) {
665+ cConfig := C.kiwi_config_t {
666+ integrate_allomorph : boolToCUint8 (config .IntegrateAllomorph ),
667+ cut_off_threshold : C .float (config .CutOffThreshold ),
668+ oov_rule_scale : C .float (config .OovRuleScale ),
669+ oov_rule_bias : C .float (config .OovRuleBias ),
670+ oov_chr_bias : C .float (config .OovChrBias ),
671+ oov_global_weight : C .float (config .OovGlobalWeight ),
672+ oov_local_weight : C .float (config .OovLocalWeight ),
673+ oov_global_min_freq : C .float (config .OovGlobalMinFreq ),
674+ space_penalty : C .float (config .SpacePenalty ),
675+ typo_cost_weight : C .float (config .TypoCostWeight ),
676+ max_unk_form_size : C .uint (config .MaxUnkFormSize ),
677+ max_unk_form_size_followed_by_j_class : C .uint (config .MaxUnkFormSizeFollowedByJClass ),
678+ space_tolerance : C .uint (config .SpaceTolerance ),
679+ }
680+ C .kiwi_set_global_config (k .handler , cConfig )
681+ }
682+
683+ func boolToCUint8 (b bool ) C.uint8_t {
684+ if b {
685+ return 1
686+ }
687+ return 0
688+ }
689+
690+ // OptionType represents the type of option for kiwi_set_option/kiwi_get_option.
691+ type OptionType int
692+
693+ const (
694+ KIWI_NUM_THREADS OptionType = C .KIWI_NUM_THREADS
695+ )
696+
697+ // GetOptionF returns the float value of the specified option.
698+ // Note: As of Kiwi v0.23.2, there are no float options available.
699+ // This function is provided for future compatibility.
700+ func (k * Kiwi ) GetOptionF (option OptionType ) float32 {
701+ return float32 (C .kiwi_get_option_f (k .handler , C .int (option )))
702+ }
703+
704+ // SetOptionF sets the float value of the specified option.
705+ // Note: As of Kiwi v0.23.2, there are no float options available.
706+ // This function is provided for future compatibility.
707+ func (k * Kiwi ) SetOptionF (option OptionType , value float32 ) {
708+ C .kiwi_set_option_f (k .handler , C .int (option ), C .float (value ))
709+ }
710+
711+ // GetOption returns the int value of the specified option.
712+ func (k * Kiwi ) GetOption (option OptionType ) int {
713+ return int (C .kiwi_get_option (k .handler , C .int (option )))
714+ }
715+
716+ // SetOption sets the int value of the specified option.
717+ func (k * Kiwi ) SetOption (option OptionType , value int ) {
718+ C .kiwi_set_option (k .handler , C .int (option ), C .int (value ))
719+ }
0 commit comments