gtk_ui/
generator.rs

1use super::parser::{
2    Statement,
3    StatementValue,
4    DefinitionType,
5    Setter,
6    Definition
7};
8use super::lexer::{
9    DefinitionType as TokenDefinitionType,
10    TypeIdentifierType as TokenTypeIdentifierType,
11    Token,
12    TokenValue,
13};
14use std::fs::File;
15use std::io::Write;
16use std::collections::HashMap;
17use std::ops::Range;
18
19#[derive(Debug)]
20pub struct CachedRawDefinition {
21    props: HashMap<String, (TokenTypeIdentifierType, TokenDefinitionType)>,
22    args: Vec<(String, TokenTypeIdentifierType, TokenDefinitionType)>,
23    inherits: Vec<String>,
24    range: Range<usize>
25}
26
27#[derive(Debug)]
28pub enum CachedDefinition {
29    Raw(CachedRawDefinition),
30    Collective(String)
31}
32
33#[derive(Debug)]
34pub struct Generator { 
35    statements: Vec<Statement>,
36    definitions: HashMap<String, CachedDefinition>,
37    header: String
38} 
39
40impl Generator {
41
42    fn is_valid_type(token: &Token, expected_type: &TokenTypeIdentifierType) -> Result<bool, (String, Range<usize>)> {
43        match token.value {
44            TokenValue::Bool(_) => {
45                Ok(matches!(expected_type, TokenTypeIdentifierType::Bool))
46            },
47            TokenValue::Number(_) => {
48                Ok(matches!(expected_type, TokenTypeIdentifierType::Number))
49            },
50            TokenValue::String(_) => {
51                Ok(matches!(expected_type, TokenTypeIdentifierType::String))
52            },
53            _ => Err((format!("{} is not a primitive and therefore it's type cannot be checked", token.to_string()), token.range.clone()))
54        }
55    }
56
57    fn get_prop_from_definition(&self, definition: &CachedRawDefinition, definition_name: &String, setter: &Setter) -> Result<(TokenTypeIdentifierType, TokenDefinitionType), (String, Range<usize>)> {
58        if let Some(prop) = definition.props.get(setter.name.as_str()) {
59            Ok(prop.clone())
60        } else {
61            for definition_name in &definition.inherits {
62                if let Some(parent_definition) = self.definitions.get(definition_name) {
63                    if let CachedDefinition::Raw(parent_definition) = parent_definition {
64                        if let Ok(result) = self.get_prop_from_definition(parent_definition, definition_name, setter) {
65                            return Ok(result);
66                        }
67                    } else {
68                        return Err((format!("cannot inherit collective definition '{}'", definition_name), definition.range.clone()))
69                    }
70                } else {
71                    return Err((format!("inherited undefined definition '{}'", definition_name), definition.range.clone()))
72                }
73            }
74            return Err((format!("no such property on '{}' called '{}'", definition_name, setter.name.as_str()), setter.range.clone()));
75        }
76    }
77
78    pub fn generate_from_collective(&self, children: &Vec<Statement>) -> Result<String, (String, Range<usize>)> {
79        let mut result = String::new();
80
81        for child in children {
82            match &child.value {
83                StatementValue::Object(object) => {
84                    if let Some(definition) = self.definitions.get(&object.name) {
85                        match definition {
86                            CachedDefinition::Raw(definition) => {
87                                if definition.args.len() != object.arguments.len() {
88                                    return Err((format!("the '{}' definition expects {} args, {} given", object.name, definition.args.len(), object.arguments.len()), child.range.clone()));
89                                }
90
91                                let mut inlines: Vec<(String, String)> = Vec::new();
92                                let mut children: Vec<(String, String)> = Vec::new();
93
94                                for i in 0..definition.args.len() {
95                                    let defined_arg = &definition.args[i];
96                                    let actual_arg = &object.arguments[i];
97                                    
98                                    // Check if actual and defined are the same type and if so check if the definition specifies it as an inline or a child
99                                    if let Ok(is_valid) = Generator::is_valid_type(&actual_arg, &defined_arg.1) {
100                                        if is_valid {
101                                            match defined_arg.2 {
102                                                TokenDefinitionType::InlineArg => {
103                                                    inlines.push((defined_arg.0.clone(), actual_arg.value_to_string()));
104                                                },
105                                                TokenDefinitionType::ChildArg => {
106                                                    children.push((defined_arg.0.clone(), actual_arg.value_to_string()));
107                                                },
108                                                _ => return Err((format!("expected either an InlineArg or a ChildArg, got {}", defined_arg.2.to_string()), actual_arg.range.clone()))
109                                            }
110                                        }
111                                    }
112                                }
113
114                                for setter in &object.setters {
115                                    let defined_prop = self.get_prop_from_definition(definition, &object.name, &setter);
116                                    let actual_prop = &setter.value;
117
118                                    match defined_prop {
119                                        Ok(defined_prop) => {
120                                            // Check if actual and defined are the same type and if so check if the definition specifies it as an inline or a child
121                                            if let Ok(is_valid) = Generator::is_valid_type(&actual_prop, &defined_prop.0) {
122                                                if is_valid {
123                                                    match defined_prop.1 {
124                                                        TokenDefinitionType::InlineProp => {
125                                                            inlines.push((setter.name.clone(), actual_prop.value_to_string()));
126                                                        },
127                                                        TokenDefinitionType::ChildProp => {
128                                                            children.push((setter.name.clone(), actual_prop.value_to_string()));
129                                                        },
130                                                        _ => return Err((format!("expected either an InlineArg or a ChildArg, got {}", defined_prop.1.to_string()), actual_prop.range.clone()))
131                                                    }
132                                                }
133                                            }
134                                        },
135                                        Err(err) => return Err(err)
136                                    }
137                                }
138
139                                // Generate from the vectors of inlines and children
140
141                                result += "<object class=\"";
142                                result += object.name.as_str();
143                                result += "\"";
144
145                                for inline in &inlines {
146                                    result += " ";
147                                    result += inline.0.as_str();
148                                    result += "=\"";
149                                    result += inline.1.as_str();
150                                    result += "\"";
151                                }
152
153                                result += ">\n";
154
155                                for child in &children {
156                                    result += "<property name=\"";
157                                    result += child.0.as_str();
158                                    result += "\">";
159                                    result += child.1.as_str();
160                                    result += "</property>\n";
161                                }
162
163                                for child in &object.children {
164                                    let child = vec![child.clone()];
165                                    match self.generate_from_collective(&child) {
166                                        Ok(collective) => {
167                                            result += "<child>\n";
168                                            result += collective.as_str();
169                                            result += "</child>\n";
170                                        },
171                                        Err(err) => return Err(err)
172                                    }
173                                }
174                                result += "</object>\n"
175                            },
176                            CachedDefinition::Collective(defintion) => {
177                                result += defintion.as_str();
178                            }
179                        }
180
181                    }
182                },
183                _ => return Err((format!("found {}, expected object in collective definition", child.to_string()), child.range.clone()))
184            }
185        }
186        
187
188        Ok(result)
189    }
190
191    pub fn generate_from_raw(&self, definition: &Definition, range: Range<usize>) -> Result<CachedRawDefinition, (String, Range<usize>)> {
192        let mut props: HashMap<String, (TokenTypeIdentifierType, TokenDefinitionType)> = HashMap::new();
193        let mut args: Vec<(String, TokenTypeIdentifierType, TokenDefinitionType)> = Vec::new();
194
195        let properties = &definition.children;
196        let inherits = &definition.inherits;
197
198        for property in properties {
199            if let StatementValue::Property(property_value) = &property.value {
200                match property_value.definition_type {
201                    TokenDefinitionType::InlineProp | TokenDefinitionType::ChildProp => {
202                        props.insert(property_value.name.clone(), (property_value.internal_type.clone(), property_value.definition_type.clone()));
203                    },
204                    TokenDefinitionType::InlineArg | TokenDefinitionType::ChildArg => {
205                        args.push((property_value.name.clone(), property_value.internal_type.clone(), property_value.definition_type.clone()));
206                    },
207                    _ => return Err((format!("expected a property definition, found {}", property_value.definition_type.to_string()), property.range.clone()))
208                }
209            }
210        }
211
212        for parent_name in inherits {
213            if let Some(parent) = self.definitions.get(parent_name) {
214                if let CachedDefinition::Collective(_) = parent {
215                    return Err((format!("cannot inherit collective definition '{}'", parent_name), range))
216                }
217            } else {
218                return Err((format!("'{}' cannot inherit undefined definition '{}'", definition.name, parent_name), range));
219            }
220        }
221
222        Ok(CachedRawDefinition {
223            inherits: inherits.clone(),
224            range, props, args
225        })
226    }
227    
228    // Pubs
229    pub fn generate(&mut self) -> Result<(), (String, Range<usize>)> {
230        for statement in &self.statements {
231            match &statement.value {
232                StatementValue::Definition(definition) => {
233                    match &definition.definition_type {
234                        DefinitionType::Root(filename) => {
235                            // print!("Writing {}.ui... ", filename);
236                            let mut file_content = String::new();
237                            match self.generate_from_collective(&definition.children) {
238                                Ok(collective) => {
239                                    file_content.push_str(collective.as_str());
240                                },
241                                Err(err) => return Err(err)
242                            }
243                            file_content.push_str("</interface>");
244
245                            let mut file = File::create(format!("{}.ui", filename)).expect("failed to create output file");
246                            writeln!(file, "{}", self.header).expect("failed to write to output file");
247                            writeln!(file, "{}", file_content).expect("failed to write to output file");
248                            // println!("done!");
249                        },
250                        DefinitionType::Collective => {
251                            match self.generate_from_collective(&definition.children) {
252                                Ok(collective) => {
253                                    self.definitions.insert(definition.name.clone(), CachedDefinition::Collective(collective));
254                                },
255                                Err(err) => return Err(err)
256                            }
257                        },
258                        DefinitionType::Raw => {
259                            match self.generate_from_raw(&definition, statement.range.clone()) {
260                                Ok(raw) => {
261                                    self.definitions.insert(definition.name.clone(), CachedDefinition::Raw(raw));
262                                },
263                                Err(err) => return Err(err)
264                            }
265                        }
266                    }
267                },
268                StatementValue::Header(header) => {
269                    self.header.push_str(header);
270                    self.header.push('\n');
271                },
272                _ => return Err((format!("this should never ever ever ever ever happen. something must be wrong with the parser if this does happen"), statement.range.clone()))
273            }
274        }
275        Ok(())
276    }
277
278    pub fn new(statements: Vec<Statement>) -> Self {
279        Generator {
280            statements,
281            definitions: HashMap::new(),
282            header: String::from("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<interface>\n")
283        }
284    }
285}
286