mod syntax;
mod runtime;
use std::fmt;
pub use syntax::*;
pub use runtime::*;
use crate::RuleGroup;
#[derive(Debug, Clone)]
pub enum ASCAError {
WordSyn(WordSyntaxError),
RuleSyn(RuleSyntaxError),
RuleRun(RuleRuntimeError),
AliasSyn(AliasSyntaxError),
AliasRun(AliasRuntimeError),
}
impl fmt::Display for ASCAError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.get_error_message())
}
}
impl ASCAError {
pub fn get_error_message(&self) -> String {
match self {
Self::WordSyn(e) => e.to_string(),
Self::RuleSyn(e) => e.to_string(),
Self::RuleRun(e) => e.to_string(),
Self::AliasSyn(e) => e.to_string(),
Self::AliasRun(e) => e.to_string(),
}
}
pub fn format_word_error(&self) -> String {
match self {
Self::WordSyn(e) => e.format(),
_ => unreachable!()
}
}
pub fn format_rule_error(&self, s: &[RuleGroup]) -> String {
match self {
Self::RuleSyn(e) => e.format(s),
Self::RuleRun(e) => e.format(s),
_ => unreachable!(),
}
}
pub fn format_alias_error(&self, into: &[String], from: &[String]) -> String {
match self {
Self::AliasSyn(e) => e.format(into, from),
Self::AliasRun(e) => e.format(into, from),
_ => unreachable!()
}
}
}
const FEAT_VARIANTS: [&str; 184] = [
"root", "rut", "rt",
"consonantal", "consonant", "cons" , "cns",
"sonorant", "sonor", "son" , "snrt", "sn",
"syllabic", "syllab", "syll" , "syl",
"manner", "mann", "man", "mnnr" , "mnr" ,
"continuant", "contin", "cont" , "cnt",
"approximant", "approx", "appr", "app",
"lateral", "latrl", "ltrl", "lat",
"nasal", "nsl", "nas",
"delayedrelease", "delrel", "d.r.", "del.rel.", "delayed", "dl", "dlrl", "dr", "delay", "del.rel", "drel",
"strident", "strid", "stri", "stridnt",
"rhotic", "rhot", "rho", "rhtc", "rh",
"click", "clik", "clk", "clck",
"laryngeal", "laryng", "laryn", "lar",
"voice", "voi", "vce", "vc",
"spreadglottis", "spreadglot", "spread", "s.g.", "s.g", "sg",
"constrictedglottis", "constricted", "constglot", "constr", "c.g.", "c.g", "cg",
"place", "plce", "plc",
"labial", "lbl", "lab",
"labiodental", "ldental", "labiodent", "labdent", "lbdntl", "ldent", "ldl",
"round", "rund", "rnd", "rd",
"coronal", "coron", "crnl", "cor",
"anterior", "anter", "antr", "ant",
"distributed", "distrib", "dist", "dis" , "dst",
"dorsal", "drsl", "dors", "dor",
"front", "frnt", "fnt", "fro", "frt", "fr",
"back", "bck", "bk",
"high", "hgh", "hi",
"low", "lw", "lo",
"tense", "tens", "tns", "ten",
"reduced", "reduc", "redu", "rdcd", "red",
"pharyngeal", "pharyng", "pharyn", "phar", "phr",
"advancedtongueroot", "a.t.r.", "a.t.r", "a.tr", "at.r", "atr",
"retractedtongueroot", "r.t.r.", "r.t.r", "r.tr", "rt.r", "rtr",
"long", "lng",
"overlong", "overlng", "ovrlng", "olong", "vlong", "olng", "vlng",
"stress", "str",
"secondarystress", "sec.stress", "secstress", "sec.str.", "sec.str", "secstr", "sec",
"length", "len",
"anystress", "anystr", "stressany", "strany",
"allstress", "allstr", "stressall", "strall"
];
fn get_feat_closest(s: &str) -> &'static str {
let mut best_lev = usize::MAX;
let mut best_str= "";
for var in FEAT_VARIANTS {
let len = lev(s, var);
if len < best_lev {
best_str = var;
best_lev = len;
}
}
best_str
}
fn lev(a: &str, b: &str) -> usize {
let mut dist = 0;
if a == b { return dist }
let a_len = a.chars().count();
let b_len = b.chars().count();
debug_assert!(a_len > 0);
debug_assert!(b_len > 0);
let mut cache: Vec<usize> = (1..).take(a_len).collect();
for (bi, b_ch) in b.chars().enumerate() {
dist = bi;
let mut a_dist = bi;
for (ai, a_ch) in a.chars().enumerate() {
let b_dist = a_dist + (a_ch != b_ch) as usize;
a_dist = cache[ai];
dist = if a_dist > dist {
if b_dist > dist {
dist + 1
} else {
b_dist
}
} else if b_dist > a_dist {
a_dist + 1
} else {
b_dist
};
cache[ai] = dist;
}
}
dist
}