moonspell

    A pure-MoonBit Hunspell-compatible spelling engine

    hunspell
    spellchecker
    nlp
    moonbit
    Download zip
    Author
    Version
    0.1.0
    License
    Apache-2.0
    Last updated
    12 hours ago
    Downloads
    1

    Dependencies

    #MoonSpell

    MoonSpell is a pure-MoonBit, Hunspell-compatible spelling engine.

    Its goal is to parse Hunspell .aff and .dic dictionaries, perform affix and compound-word checks, and generate ranked spelling suggestions without wrapping the C++ Hunspell implementation.

    #Status

    Current milestone: a runnable MoonSpell Core with .aff/.dic parsing, exact lookup, PFX/SFX chains, cross products, compound rules, dictionary word pairs, encoding support, REP/MAP/PHONE/ph: suggestions, NGRAM ranking, morphology analysis and JSON output. The compatibility target is Hunspell v1.7.3, but full compatibility is not claimed until the complete upstream conformance suite passes.

    The current official-fixture baseline is documented in COMPATIBILITY.md: Good 107/112, Wrong 93/95, suggestion Top-1 152/173.

    #Library API

    parse_aff(text) -> Result[AffixConfig, AffixParseError] parse_dic(text) -> Result[Dictionary, DicParseError] spell(dictionary, config, word) -> SpellResult suggest(dictionary, config, word, limit) -> Array[Suggestion] analyze(dictionary, config, word) -> Array[MorphAnalysis] stem(dictionary, config, word) -> String? explain(dictionary, config, word, limit) -> Explanation decode_bytes(bytes, encoding) -> String

    #CLI

    moon run cmd/main -- validate <aff> <dic> moon run cmd/main -- check <aff> <dic> <word>... moon run cmd/main -- suggest <aff> <dic> <word> --limit 10 moon run cmd/main -- stem <aff> <dic> <word> moon run cmd/main -- analyze <aff> <dic> <word> [--format json] moon run cmd/main -- explain <aff> <dic> <word> [--limit N] [--format json]

    Example with an installed en_US dictionary:

    moon run --target native cmd/main -- check /path/to/en_US.aff /path/to/en_US.dic hello exsample moon run --target native cmd/main -- suggest /path/to/en_US.aff /path/to/en_US.dic exsample --limit 5

    #Runnable example

    moon run --target native examples/basic

    The example loads an in-memory dictionary, checks an affixed word and generates suggestions.

    #Benchmark

    moon run --target native cmd/bench -- testdata/bench.aff testdata/bench.dic 1000

    The benchmark reports dictionary load time, exact-query operations and suggestion operations. For large dictionaries, suggestion benchmarking is skipped by default because candidate generation is intentionally exhaustive.

    #Development

    moon check --target all moon test moon run cmd/main -- version moon build --target native moon run --target native cmd/main -- version

    #Compatibility policy

    • Hunspell v1.7.3 is the behavioral reference.
    • Compatibility claims must be backed by official fixtures.
    • Unsupported directives must be reported explicitly, never ignored silently.
    • Suggestion compatibility is measured by candidate set and ordering.

    #License

    Apache-2.0. Third-party notices are recorded in THIRD_PARTY_NOTICES.md.

    AffixParseError

    pub suberror AffixParseError {
    InvalidAffixHeader(Int, String)
    InvalidAffixRule(Int, String)
    }

    DicParseError

    pub suberror DicParseError {
    EmptyDictionary
    InvalidHeader(String)
    }

    AffixConfig

    pub(all) struct AffixConfig {
    encoding : String
    language : String
    ignore_chars : String
    break_enabled : Bool
    break_explicit : Bool
    break_patterns : Array[String]
    flag_mode : String
    try_chars : String
    keyboard : String
    word_chars : String
    flag_aliases : Array[String]
    morph_aliases : Array[String]
    prefixes : Array[AffixRule]
    suffixes : Array[AffixRule]
    replacements : Array[ReplacementRule]
    phone_rules : Array[PhoneRule]
    iconv_rules : Array[ReplacementRule]
    oconv_rules : Array[ReplacementRule]
    maps : Array[String]
    forbidden_word : String?
    force_ucase : String?
    keep_case : String?
    need_affix : String?
    no_suggest : String?
    circumfix : String?
    max_ngram_sugs : Int
    complex_prefixes : Bool
    check_sharps : Bool
    compound_min : Int
    compound_word_max : Int
    compound_flag : String?
    compound_begin : String?
    compound_middle : String?
    compound_end : String?
    compound_permit : String?
    compound_forbid : String?
    only_in_compound : String?
    check_compound_dup : Bool
    check_compound_rep : Bool
    check_compound_case : Bool
    check_compound_triple : Bool
    simplified_triple : Bool
    compound_rules : Array[String]
    compound_patterns : Array[CompoundPattern]
    unsupported : Array[String]
    }

    AffixKind

    pub(all) enum AffixKind {
    Prefix
    Suffix
    } derive(Eq)

    AffixRule

    pub(all) struct AffixRule {
    kind : AffixKind
    flag : String
    cross_product : Bool
    strip : String
    add : String
    continuation : String
    condition : String
    }

    CompoundPattern

    pub(all) struct CompoundPattern {
    end_chars : String
    end_flag : String?
    end_zero : Bool
    begin_chars : String
    begin_flag : String?
    replacement : String?
    }

    DicEntry

    pub(all) struct DicEntry {
    word : String
    flags : String
    morph : String
    }

    Dictionary

    pub(all) struct Dictionary {
    declared_count : Int
    entries : Array[DicEntry]
    index : Map[String, Array[DicEntry]]
    }

    Dictionary::find_entries

    fn Dictionary::find_entries(self : Dictionary, word : String) -> Array[DicEntry]

    Dictionary::has_word

    fn Dictionary::has_word(self : Dictionary, word : String) -> Bool

    Explanation

    pub(all) struct Explanation {
    accepted : Bool
    steps : Array[String]
    suggestions : Array[Suggestion]
    }

    MorphAnalysis

    pub(all) struct MorphAnalysis {
    word : String
    stem : String?
    prefix : String?
    suffix : String?
    flags : String
    morphology : String
    }

    PhoneRule

    pub(all) struct PhoneRule {
    pattern : String
    replacement : String
    }

    ReplacementRule

    pub(all) struct ReplacementRule {
    from : String
    to : String
    }

    SpellResult

    pub(all) struct SpellResult {
    accepted : Bool
    stem : String?
    prefix : String?
    suffix : String?
    }

    Suggestion

    pub(all) struct Suggestion {
    word : String
    distance : Int
    }

    analyses_to_json

    fn analyses_to_json(items : Array[MorphAnalysis]) -> String

    analyze

    fn analyze(dictionary : Dictionary, config : AffixConfig, word : String) -> Array[MorphAnalysis]

    decode_bytes

    fn decode_bytes(bytes : Bytes, encoding : String) -> String

    detect_aff_encoding

    fn detect_aff_encoding(bytes : Bytes) -> String

    explain

    fn explain(dictionary : Dictionary, config : AffixConfig, word : String, suggestion_limit : Int) -> Explanation

    explanation_to_json

    fn explanation_to_json(item : Explanation) -> String

    levenshtein_distance

    fn levenshtein_distance(left : String, right : String) -> Int

    normalize_encoding

    fn normalize_encoding(name : String) -> String

    parse_aff

    fn parse_aff(text : String) -> Result[AffixConfig, AffixParseError]

    parse_dic

    fn parse_dic(text : String) -> Result[Dictionary, DicParseError]

    parse_dic_entry

    fn parse_dic_entry(line : String) -> DicEntry?

    spell

    fn spell(dictionary : Dictionary, config : AffixConfig, word : String) -> SpellResult

    stem

    fn stem(dictionary : Dictionary, config : AffixConfig, word : String) -> String?

    strip_bom

    fn strip_bom(text : String) -> String

    Remove a leading UTF-8 byte-order mark from decoded text.

    suggest

    fn suggest(dictionary : Dictionary, config : AffixConfig, word : String, limit : Int) -> Array[Suggestion]

    version

    fn version() -> String

    Return the MoonSpell library version.

    Powered by MoonBit

    Site sourceReport issuePackagesBuild queueSkillsStatistics

    © 2026 mooncakes.io