///|
pub(all) struct MorphAnalysis {
word : String
stem : String?
prefix : String?
suffix : String?
flags : String
morphology : String
}
///|
pub(all) struct Explanation {
accepted : Bool
steps : Array[String]
suggestions : Array[Suggestion]
}
///|
pub fn analyze(
dictionary : Dictionary,
config : AffixConfig,
word : String,
) -> Array[MorphAnalysis] {
let normalized = apply_iconv(config, apply_ignore(config, word))
let dictionary = normalized_dictionary(config, dictionary)
let result = spell(dictionary, config, normalized)
let analyses : Array[MorphAnalysis] = []
if !result.accepted {
return analyses
}
match result.stem {
Some(stem) => {
let lower = language_lower(config, stem)
for entry in dictionary.entries {
if language_lower(config, entry.word) == lower {
analyses.push({
word: normalized,
stem: Some(entry.word),
prefix: result.prefix,
suffix: result.suffix,
flags: entry.flags,
morphology: entry.morph,
})
}
}
if analyses.length() == 0 {
analyses.push({
word: normalized,
stem: Some(stem),
prefix: result.prefix,
suffix: result.suffix,
flags: "",
morphology: "",
})
}
}
None =>
analyses.push({
word: normalized,
stem: Some(normalized),
prefix: None,
suffix: None,
flags: "",
morphology: "",
})
}
analyses
}
///|
pub fn stem(
dictionary : Dictionary,
config : AffixConfig,
word : String,
) -> String? {
let analyses = analyze(dictionary, config, word)
for item in analyses {
match item.stem {
Some(value) => return Some(value)
None => ()
}
}
None
}
///|
pub fn explain(
dictionary : Dictionary,
config : AffixConfig,
word : String,
suggestion_limit : Int,
) -> Explanation {
let normalized = apply_iconv(config, apply_ignore(config, word))
let dictionary = normalized_dictionary(config, dictionary)
let result = spell(dictionary, config, normalized)
let steps : Array[String] = []
steps.push("normalize: " + word + " -> " + normalized)
if result.accepted {
steps.push("lookup: accepted")
match result.stem {
Some(value) => steps.push("stem: " + value)
None => steps.push("stem: compound or phrase")
}
match result.prefix {
Some(value) => steps.push("prefix: " + value)
None => ()
}
match result.suffix {
Some(value) => steps.push("suffix: " + value)
None => ()
}
{ accepted: true, steps, suggestions: [], }
} else {
steps.push("lookup: rejected")
{
accepted: false,
steps,
suggestions: suggest(dictionary, config, normalized, suggestion_limit),
}
}
}
///|
fn json_escape(text : String) -> String {
let builder = StringBuilder()
for ch in text {
match ch {
'"' => builder.write_string("\\\"")
'\\' => builder.write_string("\\\\")
'\n' => builder.write_string("\\n")
'\r' => builder.write_string("\\r")
'\t' => builder.write_string("\\t")
_ =>
if ch.to_int() < 0x20 {
builder.write_string("\\u")
let value = ch.to_int()
let digits = "0123456789ABCDEF"
builder.write_char(digits.get_char(value / 0x1000 % 0x10).unwrap())
builder.write_char(digits.get_char(value / 0x100 % 0x10).unwrap())
builder.write_char(digits.get_char(value / 0x10 % 0x10).unwrap())
builder.write_char(digits.get_char(value % 0x10).unwrap())
} else {
builder.write_char(ch)
}
}
}
builder.to_string()
}
///|
pub fn analyses_to_json(items : Array[MorphAnalysis]) -> String {
let builder = StringBuilder()
builder.write_char('[')
for i in 0.. 0 {
builder.write_char(',')
}
let item = items[i]
builder.write_string("{\"word\":\"")
builder.write_string(json_escape(item.word))
builder.write_string("\",\"stem\":")
match item.stem {
Some(value) => builder.write_string("\"" + json_escape(value) + "\"")
None => builder.write_string("null")
}
builder.write_string(",\"prefix\":")
match item.prefix {
Some(value) => builder.write_string("\"" + json_escape(value) + "\"")
None => builder.write_string("null")
}
builder.write_string(",\"suffix\":")
match item.suffix {
Some(value) => builder.write_string("\"" + json_escape(value) + "\"")
None => builder.write_string("null")
}
builder.write_string(",\"flags\":\"" + json_escape(item.flags) + "\"")
builder.write_string(
",\"morphology\":\"" + json_escape(item.morphology) + "\"}",
)
}
builder.write_char(']')
builder.to_string()
}
///|
pub fn explanation_to_json(item : Explanation) -> String {
let builder = StringBuilder()
builder.write_string("{\"accepted\":")
builder.write_string(item.accepted.to_string())
builder.write_string(",\"steps\":[")
for i in 0.. 0 {
builder.write_char(',')
}
builder.write_string("\"" + json_escape(item.steps[i]) + "\"")
}
builder.write_string("],\"suggestions\":[")
for i in 0.. 0 {
builder.write_char(',')
}
let suggestion = item.suggestions[i]
builder.write_string(
"{\"word\":\"" +
json_escape(suggestion.word) +
"\",\"distance\":" +
suggestion.distance.to_string() +
"}",
)
}
builder.write_string("]}")
builder.to_string()
}