1use std::collections::HashMap;
2
3use chronlang_parser::ast;
4
5#[derive(Clone, Debug, PartialEq)]
6pub struct Location {
7 pub source_name: String,
8 pub span: ast::Span,
9}
10
11#[derive(Clone, Debug, PartialEq)]
12pub struct Symbol {
13 pub name: String,
14 pub loc: Location,
15 pub value: Entity,
16 pub dependencies: Vec<String>,
17}
18
19#[derive(Clone, Debug, PartialEq)]
20pub enum Entity {
21 Language {
22 id: ast::Spanned<String>,
23 name: Option<ast::Spanned<String>>,
24 parent: Option<ast::Spanned<String>>,
25 },
26 Word {
27 gloss: ast::Spanned<String>,
28 pronunciation: ast::Spanned<Vec<String>>,
29 definitions: Vec<ast::Definition>,
30 tag: Tag,
31 },
32 Class {
33 label: ast::Spanned<String>,
34 encodes: Vec<ast::Spanned<String>>,
35 annotates: Vec<ast::Spanned<String>>,
36 phonemes: Vec<String>,
37 },
38 Phoneme {
39 class: ast::Spanned<String>,
40 label: ast::Spanned<String>,
41 traits: Vec<ast::Spanned<String>>,
42 },
43 Series {
44 label: ast::Spanned<String>,
45 series: ast::Spanned<ast::Series>,
46 },
47 Trait {
48 label: ast::Spanned<String>,
49 members: Vec<ast::TraitMember>,
50 },
51 TraitMember {
52 label: ast::Spanned<String>,
53 aliases: Vec<ast::Spanned<String>>,
54 default: bool,
55 notation: Option<ast::Spanned<String>>,
56 }
57}
58
59#[derive(Debug, PartialEq)]
60pub enum ImportError {
61 NoSuchSymbol(String),
62 FailedDependency(String),
63 NameCollision(Symbol),
64}
65
66#[derive(Debug, PartialEq)]
67pub struct Project {
68 pub milestones: Vec<i64>,
69 pub symbols: HashMap<String, Symbol>,
70 pub sound_changes: Vec<SoundChange>,
71}
72
73impl Project {
74 pub fn new() -> Self {
75 Self {
76 symbols: HashMap::new(),
77 milestones: Vec::new(),
78 sound_changes: Vec::new(),
79 }
80 }
81
82 pub fn add_symbol(&mut self, symbol: Symbol) -> Result<(), Symbol> {
83 let id = symbol.name.clone();
84 match self.symbols.get(&id) {
85 Some(clash) => Err(clash.clone()),
86 _ => {
87 self.symbols.insert(id, symbol);
88 Ok(())
89 }
90 }
91 }
92
93 pub fn add_all_symbols(&mut self, symbols: impl Iterator<Item = Symbol>) -> Result<(), Vec<Symbol>> {
94 let clashes = symbols
95 .flat_map(|sym| match self.add_symbol(sym) {
96 Ok(_) => vec![],
97 Err(symbol) => vec![symbol]
98 })
99 .collect::<Vec<_>>();
100
101 if clashes.is_empty() {
102 Ok(())
103 } else {
104 Err(clashes)
105 }
106 }
107
108 pub fn import(&mut self, names: &[&str], other: &Self) -> Result<(), Vec<ImportError>> {
109 let mut imports = HashMap::new();
110 let mut errors = Vec::new();
111 let mut deps = Vec::new();
112
113 names.iter().for_each(|name| match other.symbols.get(&name.to_string()) {
114 Some(_) => deps.push(name.to_string()),
115 None => errors.push(ImportError::NoSuchSymbol(name.to_string())),
116 });
117
118 while deps.len() > 0 {
119 let current = deps.pop().unwrap();
120 if imports.contains_key(¤t) { continue; }
121 match other.symbols.get(¤t) {
122 Some(symbol) => {
123 imports.insert(current.to_string(), symbol.clone());
124 deps.append(&mut symbol.dependencies.clone());
126 },
129 None => errors.push(ImportError::FailedDependency(current.to_string())),
130 }
131 }
132
133 let maybe_clashes = self.add_all_symbols(imports.values().map(|s| s.clone()));
136 if let Err(clashes) = maybe_clashes {
137 errors.append(&mut clashes.iter().map(|symbol| ImportError::NameCollision(symbol.clone())).collect())
138 }
139
140 if errors.len() > 0 {
141 Err(errors)
142 } else {
143 Ok(())
144 }
145 }
146
147 pub fn import_all_from(&mut self, other: &Project) -> Result<(), Vec<ImportError>> {
148 self.import(other.symbols.keys().map(|k| k.as_str()).collect::<Vec<_>>().as_slice(), other)
149 }
150}
151
152#[derive(Clone, Debug, PartialEq)]
153pub struct Tag {
154 pub language: String,
155 pub lang_set_span: ast::Span,
156 pub time: ast::Time,
157 pub time_set_span: ast::Span,
158}
159
160impl Tag {
161 pub fn new(lang: &ast::Spanned<String>, time: &ast::Spanned<ast::Time>) -> Self {
162 Self {
163 language: lang.1.clone(),
164 lang_set_span: lang.0.clone(),
165 time: time.1.clone(),
166 time_set_span: time.0.clone(),
167 }
168 }
169}
170
171#[derive(Clone, Debug, PartialEq)]
172pub struct SoundChange {
173 pub source_name: String,
174 pub source: ast::Spanned<ast::Source>,
175 pub target: ast::Spanned<ast::Target>,
176 pub environment: Option<ast::Spanned<ast::Environment>>,
177 pub description: Option<ast::Spanned<String>>,
178 pub tag: Tag,
179}