From 12a82ac889bf56c5c34897b9bbbb0209f99905bf Mon Sep 17 00:00:00 2001 From: Alex Kristiansen Date: Sat, 14 Oct 2023 11:08:03 -0700 Subject: [PATCH 1/2] initial functionality, no tests yet --- kirum/src/entries.rs | 1 - kirum/src/files.rs | 32 ++++++++++++++---- kirum/src/global.rs | 38 ++++++++++++++++++++++ kirum/src/main.rs | 1 + kirum/src/new.rs | 10 ++++-- libkirum/src/kirum.rs | 66 ++++++++++++++++++++++++++++++++------ libkirum/src/transforms.rs | 37 ++++++++++++++++++++- 7 files changed, 164 insertions(+), 21 deletions(-) create mode 100644 kirum/src/global.rs diff --git a/kirum/src/entries.rs b/kirum/src/entries.rs index 7b3a1ce..c079df4 100644 --- a/kirum/src/entries.rs +++ b/kirum/src/entries.rs @@ -12,7 +12,6 @@ pub struct RawTransform{ pub conditional: Option } - impl From for Transform{ fn from(value: RawTransform) -> Self { Transform { name: String::new(), lex_match: value.conditional, transforms: value.transforms} diff --git a/kirum/src/files.rs b/kirum/src/files.rs index d92f57c..79746ab 100644 --- a/kirum/src/files.rs +++ b/kirum/src/files.rs @@ -1,16 +1,17 @@ use std::{path::{PathBuf, Path}, collections::HashMap, fs::File, io::Write}; use anyhow::{Result, Context, anyhow}; -use libkirum::{kirum::{LanguageTree, Lexis}, transforms::{Transform, TransformFunc}, word::{Etymology, Edge}, lexcreate::LexPhonology}; +use libkirum::{kirum::{LanguageTree, Lexis}, transforms::{Transform, TransformFunc, GlobalTransform}, word::{Etymology, Edge}, lexcreate::LexPhonology}; use serde::Serialize; use walkdir::{WalkDir, DirEntry}; -use crate::entries::{RawTransform, RawLexicalEntry, TransformGraph, WordGraph}; +use crate::{entries::{RawTransform, RawLexicalEntry, TransformGraph, WordGraph}, global::{RawGlobalTransform, Global}}; use handlebars::Handlebars; /// contains path data for everything needed for a project pub struct Project { pub graphs: Vec, pub transforms: Vec, - pub phonetic_rules: Option> + pub phonetic_rules: Option>, + pub globals: Option } /// renders any templating code that was written into word definitions @@ -49,14 +50,25 @@ pub fn read_from_files(proj: Project) -> Result{ tree.word_creator_phonology = create_phonetics(phonetic_files)?; } - - for (lex_name, node) in &language_map{ debug!("creating node entry {}", lex_name); let node_lex: Lexis = Lexis { id: lex_name.to_string(), ..node.clone().into() }; add_single_word(&mut tree, &transform_map, &language_map, &node_lex, &node.etymology)?; } + if let Some(globals) = proj.globals { + let raw = std::fs::read_to_string(globals)?; + let global_trans: Global = serde_json::from_str(&raw)?; + if let Some(raw_trans) = global_trans.transforms { + let mut final_trans: Vec = Vec::new(); + for trans in raw_trans { + final_trans.push(trans.into()) + } + tree.global_transforms = Some(final_trans); + } + + } + Ok(tree) } @@ -164,6 +176,7 @@ pub fn handle_directory(path: &str) -> Result { let lang_graph_dir = lang_dir.join("tree"); let lang_transform_dir = lang_dir.join("etymology"); let phonetics_path = lang_dir.join("phonetics"); + let globals_file = lang_dir.join("globals.json"); debug!("using tree path: {}", lang_graph_dir.display()); let graphs: Vec = read_subdir_create_list(lang_graph_dir)?; @@ -178,11 +191,18 @@ pub fn handle_directory(path: &str) -> Result { } else { None }; + + let global_trans: Option = if globals_file.exists() { + Some(globals_file) + } else { + None + }; Ok(Project { graphs, transforms, - phonetic_rules}) + phonetic_rules, + globals: global_trans}) } fn read_subdir_create_list(path: PathBuf) -> Result>{ diff --git a/kirum/src/global.rs b/kirum/src/global.rs new file mode 100644 index 0000000..a014ded --- /dev/null +++ b/kirum/src/global.rs @@ -0,0 +1,38 @@ +use libkirum::{transforms::{TransformFunc, GlobalTransform}, matching::LexisMatch}; +use serde::{Serialize, Deserialize}; +use serde_with::skip_serializing_none; + + +#[skip_serializing_none] +#[derive(Serialize, Deserialize, Clone, Debug, Default)] +/// Defines the contents of the global.json file +pub struct Global { + /// Specifies global transforms + pub transforms: Option> +} + + +#[skip_serializing_none] +#[derive(Serialize, Deserialize, Clone, Debug, Default)] +pub struct RawGlobalTransform { + pub transforms: Vec, + pub conditional: GlobalConditionals +} + +#[skip_serializing_none] +#[derive(Serialize, Deserialize, Clone, Debug, Default)] +pub struct GlobalConditionals { + pub etymon: Option, + pub lexis: LexisMatch +} + + +impl From for GlobalTransform { + fn from(value: RawGlobalTransform) -> Self { + GlobalTransform { + lex_match: value.conditional.lexis, + etymon_match: value.conditional.etymon, + transforms: value.transforms + } + } +} \ No newline at end of file diff --git a/kirum/src/main.rs b/kirum/src/main.rs index 7387356..e121ed4 100644 --- a/kirum/src/main.rs +++ b/kirum/src/main.rs @@ -7,6 +7,7 @@ mod new; mod generate; mod ingest; mod import; +mod global; use clap::Parser; use entries::create_json_graph; diff --git a/kirum/src/new.rs b/kirum/src/new.rs index 975fc19..93209ca 100644 --- a/kirum/src/new.rs +++ b/kirum/src/new.rs @@ -1,6 +1,6 @@ use std::{path::PathBuf, io::Write, collections::HashMap, fs::{self, File}}; use libkirum::{transforms::TransformFunc, word::{Etymology, Edge}, lexcreate::LexPhonology}; -use crate::entries::{RawTransform, TransformGraph, RawLexicalEntry, Derivative, WordGraph}; +use crate::{entries::{RawTransform, TransformGraph, RawLexicalEntry, Derivative, WordGraph}, global::Global}; use anyhow::{Result, Context, anyhow}; pub fn create_project_directory(name: &str) -> Result<()>{ @@ -104,7 +104,11 @@ pub fn create_new_project(name: &str) -> Result<()> { write_json("ety", &mut ety_path, trans_data).context("error writing ety file")?; write_json("rules", &mut phonetic_path, phonetic_data).context("error writing rules file")?; - + let base_globals = Global{transforms: None}; + let globals_data = serde_json::to_string_pretty(&base_globals)?; + let mut globals_file = File::create("globals.json").context("could not create globals file")?; + write!(globals_file, "{}", globals_data).context("error writing globals file")?; + Ok(()) } @@ -115,7 +119,7 @@ fn write_json(subpath: &str, base_path: &mut PathBuf, data: String) -> Result<() .context(format!("could not create json file {} {}", subpath, base_path.display()))?; write!(phonetics_file, "{}", data) - .context("error writing phonetics file".to_string())?; + .context("error writing phonetics file")?; Ok(()) } diff --git a/libkirum/src/kirum.rs b/libkirum/src/kirum.rs index 50f5a68..8f793fb 100644 --- a/libkirum/src/kirum.rs +++ b/libkirum/src/kirum.rs @@ -2,9 +2,9 @@ use std::collections::HashMap; use crate::lemma::Lemma; use crate::lexcreate; -use crate::transforms::Transform; +use crate::transforms::{Transform, GlobalTransform}; use crate::word::{PartOfSpeech, Etymology, Edge}; -use petgraph::Direction::{Incoming, Outgoing}; +use petgraph::Direction::{Incoming, Outgoing, self}; use petgraph::dot::{Dot, Config}; use petgraph::graph::EdgeReference; use petgraph::stable_graph::NodeIndex; @@ -71,7 +71,6 @@ pub struct TreeEtymology { /// For example, if a lexis has two upstream etymons, Word A with agglutination_order=1 /// and Word B with agglutination_order=2, the lexis will by generated by agglutinating A+B pub agglutination_order: Option, - } impl TreeEtymology{ @@ -100,7 +99,12 @@ pub struct LanguageTree { /// A set of phonology rules that can be used generate new words without etymology. /// Using these rules, Kirum will randomly stitch together phonemes to create a new lexis. - pub word_creator_phonology: lexcreate::LexPhonology + pub word_creator_phonology: lexcreate::LexPhonology, + + /// An optional set of global transforms. + /// If specified, every word in the tree will be matched to the global transform list, + /// and the transform will be applied _after_ any other matching transform + pub global_transforms: Option> } impl Default for LanguageTree{ @@ -126,7 +130,9 @@ impl IntoIterator for LanguageTree { impl LanguageTree { pub fn new() -> Self { LanguageTree {graph: Graph::::new(), - word_creator_phonology: lexcreate::LexPhonology { groups: HashMap::new(), lexis_types: HashMap::new() }} + word_creator_phonology: lexcreate::LexPhonology { groups: HashMap::new(), lexis_types: HashMap::new() }, + global_transforms: None, + } } @@ -225,7 +231,7 @@ impl LanguageTree { if !updated.contains_key(&node){ - // try word creation + // try word generation from supplied phonetic rules first, before transforms if self.graph[node].word_create.is_some() && self.graph[node].word.is_none() { trace!("word_create has value, no word found, creating one..."); let word_type = self.graph[node].word_create.clone().unwrap(); @@ -252,13 +258,26 @@ impl LanguageTree { upstreams.push((order, edge.weight().intermediate_word.clone().unwrap())); } - // word has all populated upstream edges + // word has all populated upstream edges, add to tree proper if etymons_in_lex > 0 && is_ready{ changes+=1; let rendered_word = join_string_vectors(&mut upstreams); + trace!("updated node {} with word: {:?}", self.graph[node].id, rendered_word); self.graph[node].word = Some(rendered_word); updated.insert(node, true); + + // check global transforms + if let Some(gt) = &self.global_transforms { + let mut updating = self.graph[node].clone(); + for trans in gt { + // collect the upstream etymons + let etys: Vec<&Lexis> = self.graph.neighbors_directed(node, Direction::Incoming).map(|e| &self.graph[e]).collect(); + trans.transform(&mut updating, Some(etys)); + trace!("updated word {:?} with global transform ", self.graph[node].id); + } + self.graph[node] = updating; + } } // we have a lexis with no upstream edges, but contains a word. mark as updated. if self.graph[node].word.is_some() && etymons_in_lex == 0 { @@ -281,6 +300,7 @@ impl LanguageTree { self.graph[edge].apply_transforms(&mut temp_ref); //self.graph[node] = temp_ref; trace!("updated edge with word {:?}", temp_ref.word); + self.graph[edge].intermediate_word = temp_ref.word; changes+=1; } @@ -408,9 +428,11 @@ fn join_string_vectors(words: &mut [(i32, Lemma)]) -> Lemma{ mod tests { use std::collections::HashMap; - //use log::LevelFilter; - use crate::{kirum::{LanguageTree, Lexis}, transforms::{Transform, LetterArrayValues, TransformFunc, self, LetterValues}, matching::{LexisMatch, Value}, lexcreate::LexPhonology}; - //use env_logger::Builder; + + use log::LevelFilter; + use crate::{kirum::{LanguageTree, Lexis}, transforms::{Transform, LetterArrayValues, TransformFunc, self, LetterValues, GlobalTransform}, matching::{LexisMatch, Value, ValueMatch, EqualValue}, lexcreate::LexPhonology, lemma::Lemma}; + use env_logger::Builder; + fn create_basic_words() -> LanguageTree { let parent = Lexis{id: "parent".to_string(), word: Some("wrh".into()), language: "gauntlet".to_string(), lexis_type: "root".to_string(), ..Default::default()}; @@ -435,6 +457,30 @@ mod tests { tree } + #[test] + fn basic_global_transforms() { + Builder::new().filter_level(LevelFilter::Info).init(); + let mut test_tree = create_basic_words(); + let transforms = vec![GlobalTransform{ + lex_match: LexisMatch { id: None, word: None, + language: Some(Value::Match(ValueMatch::Equals(EqualValue::String("New Gauntlet".to_string())))), ..Default::default() }, + etymon_match: Some(LexisMatch { + language: Some(Value::Match(ValueMatch::Equals(EqualValue::String("gauntlet".to_string())))), ..Default::default()}), + transforms: vec![TransformFunc::Prefix { value: "ka".into() }] + }]; + + let derivative_lang = Lexis{id: "derivative_lang".to_string(), word: None, lexis_type: "word".to_string(), language: "New Gauntlet".to_string(), ..Default::default()}; + + test_tree.global_transforms = Some(transforms); + test_tree.connect_etymology_id(derivative_lang, "derivative_two".to_string(), + vec![Transform{name: "test".to_string(), lex_match: None, transforms: vec![TransformFunc::Loanword]}], None); + + test_tree.compute_lexicon(); + let test_word = test_tree.to_vec_etymons(|f| f.language == "New Gauntlet".to_string()); + assert_eq!(test_word[0].0.word.clone().unwrap(), Lemma::from("kaauwarh")) + + } + #[test] fn test_word_create() { //let log_level = LevelFilter::Trace; diff --git a/libkirum/src/transforms.rs b/libkirum/src/transforms.rs index 48b6576..1fa0703 100644 --- a/libkirum/src/transforms.rs +++ b/libkirum/src/transforms.rs @@ -1,6 +1,41 @@ use serde::{Deserialize, Serialize}; use crate::{matching::LexisMatch, kirum::Lexis, lemma::Lemma}; -use log::debug; +use log::{debug, trace}; + +/// Specifies a transform at a global level. Global transforms don't have a name, but can be matched to both the target lexis, and the etymon. +#[derive(Clone, Default, Debug)] +pub struct GlobalTransform { + /// Match statement for the word under transform + pub lex_match: LexisMatch, + /// Optional match statement for the lexis's etymon + /// If a given word has multiple upstream etymons, libkirum will look for any matching etymon. + pub etymon_match: Option, + pub transforms: Vec +} + +impl GlobalTransform { + /// Transform the given lexis, or return the original unaltered lexis if the specified lexii don't meet the match statements + pub fn transform(&self, lex: &mut Lexis, etymon: Option>) { + // check to see if the etymon should allow us to transform + let should_trans = if let Some(ety) = etymon { + if let Some(ety_match) = &self.etymon_match { + ety.iter().find(|&e| ety_match.matches(e)).is_some() + } else { + true + } + } else { + true + }; + + trace!("checking global transforms for {}", lex.id); + if self.lex_match.matches(lex) && should_trans{ + trace!("applying global transforms to {}", lex.id); + for trans in &self.transforms { + trans.transform(lex) + } + } + } +} /// Defines a series of transforms that are applied to a lexis. #[derive(Clone, Default)] From 0ef0a6bb5648f0080475401b0ed6ac7f39ac0af1 Mon Sep 17 00:00:00 2001 From: Alex Kristiansen Date: Sat, 14 Oct 2023 20:29:08 -0700 Subject: [PATCH 2/2] first pass at global transforms --- .github/workflows/rust.yml | 5 +- examples/global_transforms/etymology/ety.json | 20 ++++++ examples/global_transforms/globals.json | 31 +++++++++ .../global_transforms/phonetics/rules.json | 23 +++++++ examples/global_transforms/readme.md | 9 +++ .../tree/global_transforms.json | 39 +++++++++++ kirum/src/files.rs | 2 +- kirum/src/new.rs | 2 +- libkirum/src/kirum.rs | 65 +++++++++++++++++-- 9 files changed, 187 insertions(+), 9 deletions(-) create mode 100644 examples/global_transforms/etymology/ety.json create mode 100644 examples/global_transforms/globals.json create mode 100644 examples/global_transforms/phonetics/rules.json create mode 100644 examples/global_transforms/readme.md create mode 100644 examples/global_transforms/tree/global_transforms.json diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 330552d..e821e4b 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -49,4 +49,7 @@ jobs: - name: Ingest run: cargo run -- ingest -d examples/ingest_from_json/empty_language -o generate=word -o language="example" json examples/ingest_from_json/input.json - name: Post-ingest render - run: cargo run -- render -d examples/ingest_from_json/empty_language line \ No newline at end of file + run: cargo run -- render -d examples/ingest_from_json/empty_language line + + - name: Globals + run: cargo run -- render -d examples/global_transforms line \ No newline at end of file diff --git a/examples/global_transforms/etymology/ety.json b/examples/global_transforms/etymology/ety.json new file mode 100644 index 0000000..2d27d45 --- /dev/null +++ b/examples/global_transforms/etymology/ety.json @@ -0,0 +1,20 @@ +{ + "transforms": { + "of-from-latin": { + "transforms": [ + { + "match_replace": { + "old": "l", + "new": "t" + } + }, + { + "match_replace": { + "old": "a", + "new": "aa" + } + } + ] + } + } +} \ No newline at end of file diff --git a/examples/global_transforms/globals.json b/examples/global_transforms/globals.json new file mode 100644 index 0000000..75b209c --- /dev/null +++ b/examples/global_transforms/globals.json @@ -0,0 +1,31 @@ +{ + "transforms": [ + { + "transforms": [ + { + "match_replace": { + "old": "au", + "new": "ū" + } + } + ], + "conditional": { + "etymon": { + "language": { + "match": { + "equals": "Old Exemplum" + } + } + }, + "lexis": { + "language": { + "match": { + "equals": "New Exemplum" + } + } + } + } + } + ] +} + \ No newline at end of file diff --git a/examples/global_transforms/phonetics/rules.json b/examples/global_transforms/phonetics/rules.json new file mode 100644 index 0000000..d338d67 --- /dev/null +++ b/examples/global_transforms/phonetics/rules.json @@ -0,0 +1,23 @@ +{ + "groups": { + "C": [ + "x", + "m", + "p", + "l" + ], + "V": [ + "e", + "a" + ], + "S": [ + "VC", + "CCV" + ] + }, + "lexis_types": { + "word": [ + "SSS" + ] + } +} \ No newline at end of file diff --git a/examples/global_transforms/readme.md b/examples/global_transforms/readme.md new file mode 100644 index 0000000..2c556e6 --- /dev/null +++ b/examples/global_transforms/readme.md @@ -0,0 +1,9 @@ +# Using Global Transforms + +In addition to specifying transforms in word etymology, transforms can be specified at a global level, where they will be applied to all words that meet the given match statements. + +Unlike an etymon-level transform, global transforms can take a match statement for the `lexis`, the word targeted by the transform, as well as `etymon`, which will match the first upstream etymon of the given lexis. + +After looking at the example in `globals.json`, you can run the example file with + +`kirum render -d ./ line` \ No newline at end of file diff --git a/examples/global_transforms/tree/global_transforms.json b/examples/global_transforms/tree/global_transforms.json new file mode 100644 index 0000000..b81ac0d --- /dev/null +++ b/examples/global_transforms/tree/global_transforms.json @@ -0,0 +1,39 @@ +{ + "words": { + "latin_example": { + "type": "word", + "language": "New Exemplum", + "definition": "an instance, model, example", + "part_of_speech": "noun", + "etymology": { + "etymons": [ + { + "etymon": "old_word" + } + ] + }, + "archaic": true, + "derivatives": [ + { + "lexis": { + "language": "New Exemplum", + "definition": "model, example", + "part_of_speech": "noun", + "archaic": true + }, + "transforms": [ + "of-from-latin" + ] + } + ] + }, + "old_word": { + "word": "aushal", + "type": "word", + "language": "Old Exemplum", + "definition": "To buy, remove", + "part_of_speech": "verb", + "archaic": true + } + } +} \ No newline at end of file diff --git a/kirum/src/files.rs b/kirum/src/files.rs index 79746ab..5f1fbdb 100644 --- a/kirum/src/files.rs +++ b/kirum/src/files.rs @@ -3,7 +3,7 @@ use anyhow::{Result, Context, anyhow}; use libkirum::{kirum::{LanguageTree, Lexis}, transforms::{Transform, TransformFunc, GlobalTransform}, word::{Etymology, Edge}, lexcreate::LexPhonology}; use serde::Serialize; use walkdir::{WalkDir, DirEntry}; -use crate::{entries::{RawTransform, RawLexicalEntry, TransformGraph, WordGraph}, global::{RawGlobalTransform, Global}}; +use crate::{entries::{RawTransform, RawLexicalEntry, TransformGraph, WordGraph}, global::Global}; use handlebars::Handlebars; /// contains path data for everything needed for a project diff --git a/kirum/src/new.rs b/kirum/src/new.rs index 93209ca..6ee6e1c 100644 --- a/kirum/src/new.rs +++ b/kirum/src/new.rs @@ -106,7 +106,7 @@ pub fn create_new_project(name: &str) -> Result<()> { let base_globals = Global{transforms: None}; let globals_data = serde_json::to_string_pretty(&base_globals)?; - let mut globals_file = File::create("globals.json").context("could not create globals file")?; + let mut globals_file = File::create(base.join("globals.json")).context("could not create globals file")?; write!(globals_file, "{}", globals_data).context("error writing globals file")?; Ok(()) diff --git a/libkirum/src/kirum.rs b/libkirum/src/kirum.rs index 8f793fb..91a31b0 100644 --- a/libkirum/src/kirum.rs +++ b/libkirum/src/kirum.rs @@ -389,7 +389,7 @@ impl LanguageTree { /// Reduce the language graph to a vector of words that match the provided function. /// Returns a vector of tuples for each matching word and any associated etymological data. - pub fn to_vec_etymons(self, filter: F) -> Vec<(Lexis, Etymology)> + pub fn to_vec_etymons(&self, filter: F) -> Vec<(Lexis, Etymology)> where F: Fn(&Lexis) -> bool, { @@ -457,9 +457,7 @@ mod tests { tree } - #[test] - fn basic_global_transforms() { - Builder::new().filter_level(LevelFilter::Info).init(); + fn create_basic_with_globals() -> LanguageTree { let mut test_tree = create_basic_words(); let transforms = vec![GlobalTransform{ lex_match: LexisMatch { id: None, word: None, @@ -468,17 +466,72 @@ mod tests { language: Some(Value::Match(ValueMatch::Equals(EqualValue::String("gauntlet".to_string())))), ..Default::default()}), transforms: vec![TransformFunc::Prefix { value: "ka".into() }] }]; + test_tree.global_transforms = Some(transforms); - let derivative_lang = Lexis{id: "derivative_lang".to_string(), word: None, lexis_type: "word".to_string(), language: "New Gauntlet".to_string(), ..Default::default()}; + test_tree + + } + + #[test] + fn basic_global_transforms() { + Builder::new().filter_level(LevelFilter::Info).init(); + let mut test_tree = create_basic_with_globals(); + + let derivative_lang = Lexis{id: "derivative_lang".to_string(), + word: None, lexis_type: "word".to_string(), language: "New Gauntlet".to_string(), ..Default::default()}; + + test_tree.connect_etymology_id(derivative_lang, "derivative_two".to_string(), + vec![Transform{name: "test".to_string(), lex_match: None, transforms: vec![TransformFunc::Prefix { value: Lemma::from("sur") }]}], None); + + test_tree.compute_lexicon(); + let test_word = test_tree.to_vec_etymons(|f| f.language == "New Gauntlet".to_string()); + assert_eq!(test_word[0].0.word.clone().unwrap(), Lemma::from("kasurauwarh")) + + } + + #[test] + fn global_with_local_transform() { + let mut test_tree = create_basic_with_globals(); + + let derivative_lang = Lexis{id: "derivative_lang".to_string(), + word: None, lexis_type: "word".to_string(), language: "New Gauntlet".to_string(), ..Default::default()}; - test_tree.global_transforms = Some(transforms); test_tree.connect_etymology_id(derivative_lang, "derivative_two".to_string(), vec![Transform{name: "test".to_string(), lex_match: None, transforms: vec![TransformFunc::Loanword]}], None); test_tree.compute_lexicon(); let test_word = test_tree.to_vec_etymons(|f| f.language == "New Gauntlet".to_string()); assert_eq!(test_word[0].0.word.clone().unwrap(), Lemma::from("kaauwarh")) + } + + #[test] + fn global_with_downstream_transform(){ + let mut test_tree = create_basic_with_globals(); + let derivative_lang = Lexis{id: "derivative_lang".to_string(), word: None, + lexis_type: "word".to_string(), language: "New Gauntlet".to_string(), ..Default::default()}; + + let derivative_new_word = Lexis{ + id: "derivative_word".to_string(), + word: None, + lexis_type: "word".to_string(), + language: "New Gauntlet".to_string(), + ..Default::default() + }; + + test_tree.connect_etymology_id(derivative_lang, "derivative_two".to_string(), + vec![Transform{name: "test".to_string(), lex_match: None, transforms: vec![TransformFunc::Loanword]}], None); + + test_tree.connect_etymology_id(derivative_new_word, "derivative_lang".to_string(), + vec![Transform{name: "test_downstream".to_string(), lex_match: None, transforms: vec![TransformFunc::Postfix { value: "`sh".into() }]}], + None); + + test_tree.compute_lexicon(); + let test_words = test_tree.to_vec_etymons(|f| f.language == "New Gauntlet".to_string()); + assert_eq!(test_words.iter().find(|e| e.0.word == Some(Lemma::from("kaauwarh"))).is_some(), true); + + assert_eq!(test_words.iter().find(|e| e.0.word == Some(Lemma::from("kaauwarh`sh"))).is_some(), true); + } #[test]