1mod trie;
2pub mod word;
3pub mod rule;
4pub mod error;
5mod alias;
6
7use serde::Deserialize;
8use std::collections::HashMap;
9use lazy_static::lazy_static;
10use wasm_bindgen::prelude::*;
11
12use alias :: Transformation;
13use trie :: *;
14use word :: { DiaMods, Diacritic, * };
15use error :: { ASCAError, * };
16use rule :: { trace::Change, BinMod, ModKind, Rule, RuleGroup };
17
18const CARDINALS_FILE: &str = include_str!("cardinals.json");
19const DIACRITIC_FILE: &str = include_str!("diacritics.json");
20lazy_static! {
21 static ref CARDINALS_MAP: HashMap<String, Segment> = serde_json::from_str(CARDINALS_FILE).unwrap();
22 static ref DIACRITS: Vec<Diacritic> = {
23 #[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Hash)]
26 pub enum DiaFeatType {
27 Root, Manner, Laryngeal, Place, Labial, Coronal, Dorsal, Pharyngeal,
28 Consonantal, Sonorant, Syllabic,
29 Continuant, Approximant, Lateral, Nasal, DelayedRelease, Strident, Rhotic, Click,
30 Voice, SpreadGlottis, ConstrGlottis,
31 Labiodental, Round,
32 Anterior, Distributed,
33 Front, Back, High, Low, Tense, Reduced,
34 AdvancedTongueRoot, RetractedTongueRoot,
35 }
36
37 #[derive(Deserialize)]
38 struct DT {
39 pub name: String,
40 pub diacrit: char,
41 pub prereqs: Option<HashMap<DiaFeatType, bool>>,
42 pub payload: Option<HashMap<DiaFeatType, bool>>,
43 }
44
45 impl DT {
46 pub fn hm_to_mod(&self, hm: &Option<HashMap<DiaFeatType, bool>>) -> DiaMods {
47 let mut args = DiaMods::new();
48 let Some(s) = hm else {return args};
50 for (key, value) in s.iter() {
51 let x = *key as usize;
52 match value {
53 true =>{
54 if x > 7 { args.feats[x - 8] = Some(ModKind::Binary(BinMod::Positive)) }
55 else { args.nodes[x] = Some(ModKind::Binary(BinMod::Positive)) };
56 },
57 false => {
58 if x > 7 { args.feats[x - 8] = Some(ModKind::Binary(BinMod::Negative)) }
59 else { args.nodes[x] = Some(ModKind::Binary(BinMod::Negative)) };
60 }
61 }
62 }
63 args
64 }
65
66 pub fn to_diacritic(&self) -> Diacritic {
67 Diacritic {
68 name: self.name.clone(),
69 diacrit: self.diacrit,
70 prereqs: self.hm_to_mod(&self.prereqs),
71 payload: self.hm_to_mod(&self.payload)
72 }
73 }
74 }
75
76 let dt: Vec<DT> = serde_json::from_str(DIACRITIC_FILE).unwrap();
77
78 dt.iter().map(|x| x.to_diacritic()).collect()
79 };
80 static ref CARDINALS_VEC: Vec<String> = CARDINALS_MAP.keys().cloned().collect();
81 static ref CARDINALS_TRIE: Trie = {
82 let mut m = Trie::new();
83 CARDINALS_MAP.keys().for_each(|k| m.insert(k.as_str()));
84 m
85 };
86}
87
88fn apply_rule_groups(rules: &[Vec<Rule>], phrases: &[Phrase]) -> Result<Vec<Phrase>, ASCAError> {
89 let mut transformed_phrases: Vec<Phrase> = Vec::with_capacity(phrases.len());
90
91 for phrase in phrases {
92 let mut transformed_phrase = Phrase::with_capacity(phrase.len());
93 for word in phrase.iter() {
94 let mut res_word = word.clone();
95 for rule_group in rules {
96 for rule in rule_group {
97 res_word = rule.apply(res_word)?;
98 }
99 }
100 transformed_phrase.push(res_word);
101 }
102 transformed_phrases.push(transformed_phrase);
103 }
104
105 Ok(transformed_phrases)
106}
107
108fn apply_rules_trace(rules: &[Vec<Rule>], phrase: &Phrase) -> Result<Vec<Change>, ASCAError> {
109 let mut changes: Vec<Change> = Vec::new();
110
111 let mut res_phrase = phrase.clone();
112 for (i, rule_group) in rules.iter().enumerate() {
113 let res_step = res_phrase.clone();
114 for (j, _) in phrase.iter().enumerate() {
115 for rule in rule_group {
116 res_phrase[j] = rule.apply(res_phrase[j].clone())?;
117 }
118 }
119 if res_phrase != res_step {
120 changes.push(Change { rule_index: i, after: res_phrase.clone() });
121 }
122 }
123
124 Ok(changes)
125}
126
127fn phrases_to_string(phrases: Vec<Phrase>, alias_from: Vec<Transformation>) -> (Vec<String>, Vec<String>) {
128 let mut res = Vec::with_capacity(phrases.len());
129 let mut unknowns = Vec::new();
130
131 for phrase in phrases {
132 let mut phr_res = String::new();
133 for word in phrase.iter() {
134 let (w, u) = word.render(&alias_from);
135 phr_res.push(' ');
136 phr_res.push_str(&w);
137 unknowns.extend(u);
138 }
139 res.push(phr_res.trim().to_string());
140 }
141
142 (res, unknowns)
143}
144
145fn input_phrases_to_string(phrases: &[Phrase]) -> Vec<String> {
146 let mut res = Vec::with_capacity(phrases.len());
147
148 for phrase in phrases {
149 let mut phr_res = String::new();
150 for word in phrase.iter() {
151 let (w, _) = word.render(&[]);
152 phr_res.push(' ');
153 phr_res.push_str(&w);
154 }
155 res.push(phr_res.trim().to_string());
156 }
157
158 res
159}
160
161fn parse_phrases(unparsed_phrases: &[String], alias_into: &[Transformation]) -> Result<Vec<Phrase>, ASCAError> {
162 unparsed_phrases.iter().map(|phrase| -> Result<Phrase, ASCAError> {
163 phrase.split(' ')
164 .map(|w| Word::new(w, alias_into))
165 .collect()
166 }).collect()
167}
168
169fn parse_rule_groups(unparsed_rule_groups: &[RuleGroup]) -> Result<Vec<Vec<Rule>>, RuleSyntaxError> {
170 let mut rule_groups = Vec::with_capacity(unparsed_rule_groups.len());
171
172 for (rgi, rg) in unparsed_rule_groups.iter().enumerate() {
173 let mut rule_group = Vec::with_capacity(rg.rule.len());
174 for (ri, r) in rg.rule.iter().enumerate() {
175 if let Some(asdf) = rule::Parser::new(rule::Lexer::new(&r.chars().collect::<Vec<_>>(), rgi, ri).get_line()?, rgi, ri).parse()? {
176 rule_group.push(asdf);
177 }
178 }
179 rule_groups.push(rule_group);
180 }
181
182 Ok(rule_groups)
183}
184
185pub fn run_unparsed(unparsed_rules: &[RuleGroup], unparsed_phrases: &[String], alias_into: &[String], alias_from: &[String]) -> Result<Vec<String>, ASCAError> {
186 let phrases = parse_phrases(unparsed_phrases, &alias::parse_into(alias_into)?)?;
187 let rules = parse_rule_groups(unparsed_rules)?;
188 let res = apply_rule_groups(&rules, &phrases)?;
189
190 let (res, _) = phrases_to_string(res, alias::parse_from(alias_from)?);
191
192 Ok(res)
193}
194
195#[allow(clippy::type_complexity)]
196pub fn run_unparsed_debug(unparsed_rules: &[RuleGroup], unparsed_phrases: &[String], alias_into: &[String], alias_from: &[String]) -> Result<(Vec<String>, Vec<String>, Vec<String>), ASCAError> {
197 let phrases = parse_phrases(unparsed_phrases, &alias::parse_into(alias_into)?)?;
198 let rules = parse_rule_groups(unparsed_rules)?;
199 let res = apply_rule_groups(&rules, &phrases)?;
200
201 let (output, unknowns) = phrases_to_string(res, alias::parse_from(alias_from)?);
202
203 Ok((input_phrases_to_string(&phrases), output, unknowns))
204}
205
206#[doc(hidden)]
209#[wasm_bindgen]
210pub struct WasmResult {
211 input: Vec<String>, output: Vec<String>,
213 unknowns: Vec<String>, trace_rules: Vec<usize>, was_ok: bool }
217
218#[wasm_bindgen]
219impl WasmResult {
220 pub fn get_input(&self) -> Vec<String> {
221 self.input.clone()
222 }
223
224 pub fn get_output(&self) -> Vec<String> {
225 self.output.clone()
226 }
227
228 pub fn get_unknowns(&self) -> Vec<String> {
229 self.unknowns.clone()
230 }
231
232 pub fn get_traces(&self) -> Vec<usize> {
233 self.trace_rules.clone()
234 }
235
236 pub fn was_ok(&self) -> bool {
237 self.was_ok
238 }
239}
240
241
242#[wasm_bindgen]
243pub fn run_wasm(val: JsValue, unparsed_phrases: Vec<String>, unparsed_into: Vec<String>, unparsed_from: Vec<String>, trace: Option<usize>) -> WasmResult {
244 let unparsed_rules: Vec<RuleGroup> = serde_wasm_bindgen::from_value(val).expect("Rules are in valid JSObject format");
245
246 match trace {
247 Some(t) => parse_result_web(run_trace_wasm(&unparsed_rules, &unparsed_phrases, &unparsed_into, t), &unparsed_rules, &unparsed_into, &unparsed_from, &unparsed_phrases),
248 None => match run_unparsed_debug(&unparsed_rules, &unparsed_phrases, &unparsed_into, &unparsed_from) {
249 Ok((inp, res, unk)) => parse_result_web(Ok((inp, res, unk, vec![])), &unparsed_rules, &unparsed_into, &unparsed_from, &unparsed_phrases),
250 Err(e) => parse_result_web(Err(e), &unparsed_rules, &unparsed_into, &unparsed_from, &unparsed_phrases),
251 }
252 }
253}
254
255fn get_trace_phrase(unparsed_phrases: &[String], alias_into: &[String], trace_index: usize) -> Result<Option<Phrase>, ASCAError> {
256 match unparsed_phrases.get(trace_index) {
257 Some(phrase) => Ok(Some(Phrase::try_from(phrase, alias_into)?)),
258 None => Ok(None),
259 }
260}
261
262#[allow(clippy::type_complexity)]
263fn run_trace_wasm(unparsed_rules: &[RuleGroup], unparsed_phrase: &[String], alias_into: &[String], trace_index: usize) -> Result<(Vec<String>, Vec<String>, Vec<String>, Vec<usize>), ASCAError> {
264 let phrase = get_trace_phrase(unparsed_phrase, alias_into, trace_index)?.unwrap_or_default();
265 let rules = parse_rule_groups(unparsed_rules)?;
266 let res = apply_rules_trace(&rules, &phrase)?;
267
268 let (out, unk, trc) = rule::trace::to_string_wasm(&phrase, res, unparsed_rules);
269
270 Ok((input_phrases_to_string(&[phrase]), out, unk, trc))
271}
272
273#[allow(clippy::type_complexity)]
274fn parse_result_web(result: Result<(Vec<String>, Vec<String>, Vec<String>, Vec<usize>), ASCAError>, rules: &[RuleGroup], unparsed_into: &[String], unparsed_from: &[String], unparsed_phrases: &[String]) -> WasmResult {
275 match result {
276 Ok((input, output, unknowns, trace_rules)) => {
277 WasmResult { input, output, unknowns, trace_rules, was_ok: true}
278 },
279 Err(err) => {
280 let output = match err {
281 ASCAError::WordSyn(e) => e.format(),
282 ASCAError::AliasSyn(e) => e.format(unparsed_into, unparsed_from),
283 ASCAError::AliasRun(e) => e.format(unparsed_into, unparsed_from),
284 ASCAError::RuleSyn(e) => e.format(rules),
285 ASCAError::RuleRun(e) => e.format(rules),
286 };
287 WasmResult { input: unparsed_phrases.to_vec(), output: vec![output], unknowns: vec![], trace_rules: vec![], was_ok: false }
288 },
289 }
290}