gimura_preprocessor_lib/
lib.rs

1use std::{collections::HashMap, fs};
2use log::*;
3
4pub mod prelude {
5    pub use super::PreprocessorOptions;
6    pub use super::Preporcessor;
7    pub use super::CodeSource;
8}
9
10#[derive(Debug, Clone)]
11pub struct PreprocessorOptions {
12    pub start_operator: String,
13    pub defines: HashMap<String, String>,
14}
15
16impl Default for PreprocessorOptions {
17    fn default() -> Self {
18        Self {
19            start_operator: "//!".to_string(),
20            defines: HashMap::new(),
21        }
22    }
23}
24
25pub struct CodeSource {
26    sources: HashMap<String, String>,
27}
28
29impl CodeSource {
30    pub fn new(sources: HashMap<String, String>) -> Self {
31        Self {
32            sources,
33        }
34    }
35
36    pub fn from_path(path: String) -> Self {
37        let mut sources = HashMap::<String, String>::new();
38
39        for entry in glob::glob(format!("{}/**/*", path).as_str()).unwrap() {
40            let entry = &entry.unwrap();
41
42            if !entry.is_file() {
43                continue;
44            }
45
46            let name = entry.file_name().unwrap().to_str().unwrap().to_string().replace("/", "__");
47            let source = fs::read_to_string(entry.as_path()).unwrap();
48
49            sources.insert(name, source);
50        }
51
52        Self {
53            sources
54        }
55    }
56
57    pub fn get_source(&self, name: String) -> &String {
58        self.sources.get(&name).unwrap()
59    }
60}
61
62#[derive(Debug)]
63pub enum Token {
64    Command(CommandType),
65    Literal(LiteralType),
66    Separator(SeparatorType),
67
68    OtherCode(String)
69}
70
71#[derive(Debug)]
72pub enum SeparatorType {
73    Colon
74}
75
76#[derive(Debug)]
77pub enum CommandType {
78    Include,
79    Define,
80    UnDefine,
81    IfDef,
82    IfNotDef,
83    Endif,
84    Error,
85    Warn
86}
87
88#[derive(Debug)]
89pub enum LiteralType {
90    NameLiteral(String),
91    StringLiteral(String),
92}
93
94
95pub struct Preporcessor {
96    sources: HashMap<String, CodeSource>,
97    preprocessor_options: PreprocessorOptions,
98
99    defines: HashMap<String, String>,
100    ifs: Vec<bool>
101}
102
103impl Preporcessor {
104    pub fn new(preprocessor_options: PreprocessorOptions) -> Self {
105        Self {
106            preprocessor_options: preprocessor_options.clone(),
107            sources: HashMap::new(),
108            defines: preprocessor_options.defines,
109            ifs: Vec::new(),
110        }
111    }
112
113    pub fn add_source(&mut self, name: String, code_source: CodeSource) {
114        self.sources.insert(name, code_source);
115    }
116
117    pub fn tokenize_line(&mut self, line: String) -> Vec<Token> {
118        let mut tokens = Vec::new();
119
120        let trimmed_line = line.trim_start().trim_end();
121        let line_words = trimmed_line.split(" ");
122
123        let mut other_code_buffer = "".to_string();
124        let mut other_code = true;
125
126        let mut str_literal_buffer = "".to_string();
127        let mut str_literal = false;
128
129        for word in line_words {
130            if word == self.preprocessor_options.start_operator {
131                other_code = false;
132                tokens.push(Token::OtherCode(other_code_buffer.clone()));
133
134                continue;
135            }
136
137            if other_code {
138                other_code_buffer += &(word.to_owned() + " ");
139                continue;
140            }
141
142            if str_literal {
143                str_literal_buffer += word;
144            }
145
146            if word.starts_with('"') {
147                str_literal_buffer = "".to_string();
148                str_literal = true;
149
150                str_literal_buffer += word;
151            }
152
153            if word.ends_with('"') {
154                str_literal = false;
155                tokens.push(Token::Literal(LiteralType::StringLiteral(str_literal_buffer.replace("\"", "").clone())));
156
157                continue;
158            }
159
160            if str_literal {
161                str_literal_buffer += " ";
162                continue;
163            }
164
165            match word {
166                "include" => tokens.push(Token::Command(CommandType::Include)),
167                "define" => tokens.push(Token::Command(CommandType::Define)),
168                "undef" => tokens.push(Token::Command(CommandType::UnDefine)),
169                "ifdef" => tokens.push(Token::Command(CommandType::IfDef)),
170                "ifndef" => tokens.push(Token::Command(CommandType::IfNotDef)),
171                "endif" => tokens.push(Token::Command(CommandType::Endif)),
172                "error" => tokens.push(Token::Command(CommandType::Error)),
173                "warn" => tokens.push(Token::Command(CommandType::Warn)),
174
175                _ => tokens.push(Token::Literal(LiteralType::NameLiteral(word.to_string())))
176            }
177        }
178
179        if other_code {
180            tokens.push(Token::OtherCode(other_code_buffer.clone()));
181        }
182
183        tokens
184    }
185
186    pub fn preprocess_line(&mut self, line: String) -> Vec<String> {
187        let mut strings = Vec::new();
188        let mut tokens = self.tokenize_line(line).into_iter().peekable();
189
190        while tokens.peek().is_some() {
191            let command = tokens.next().unwrap();
192
193            match command {
194                Token::Command(CommandType::IfDef) => {
195                    let name = tokens.next().unwrap();
196
197                    if let Token::Literal(LiteralType::NameLiteral(name)) = name {
198                        self.ifs.push(self.defines.contains_key(&name));
199                    }
200                },
201                Token::Command(CommandType::IfNotDef) => {
202                    let name = tokens.next().unwrap();
203
204                    if let Token::Literal(LiteralType::NameLiteral(name)) = name {
205                        self.ifs.push(!self.defines.contains_key(&name));
206                    }
207                },
208                Token::Command(CommandType::Endif) => {
209                    self.ifs.pop();
210                },
211
212                _ => {}
213            }
214
215            if self.ifs.len() != 0 {
216                if !self.ifs[self.ifs.len() - 1] {
217                    continue;
218                }
219            }
220
221            match command {
222                Token::Command(command) => match command {
223                    CommandType::Include => {
224                        let lib = tokens.next().unwrap();
225                        let name = tokens.next().unwrap();
226
227                        if let Token::Literal(LiteralType::StringLiteral(lib)) = lib {
228                            if let Token::Literal(LiteralType::StringLiteral(name)) = name {
229                                let source = self.preprocess(lib, name);
230
231                                strings.append(&mut source.lines().map(|x| x.to_string()).collect());
232                            } else {
233                                panic!("Unexpected token");
234                            }
235                        } else {
236                            panic!("Unexpected token");
237                        }
238                    },
239                    CommandType::Define => {
240                        let name = tokens.next().unwrap();
241                        let value = tokens.next().unwrap();
242
243                        if let Token::Literal(LiteralType::NameLiteral(name)) = name {
244                            if let Token::Literal(LiteralType::StringLiteral(value)) = value {
245                                self.defines.insert(name, value);
246                            } else {
247                                panic!("Unexpected token");
248                            }
249                        } else {
250                            panic!("Unexpected token");
251                        }
252                    },
253                    CommandType::UnDefine => {
254                        let name = tokens.next().unwrap();
255
256                        if let Token::Literal(LiteralType::NameLiteral(name)) = name {
257                            self.defines.remove(&name);
258                        } else {
259                            panic!("Unexpected token");
260                        }
261                    },
262                    CommandType::Error => {
263                        let messange = tokens.next().unwrap();
264
265                        if let Token::Literal(LiteralType::StringLiteral(messange)) = messange {
266                            error!("Error: {}", messange);
267                            panic!("Error: {}", messange);
268                        } else {
269                            panic!("Unexpected token");
270                        }
271                    },
272                    CommandType::Warn => {
273                        let messange = tokens.next().unwrap();
274
275                        if let Token::Literal(LiteralType::StringLiteral(messange)) = messange {
276                            warn!("Warning: {}", messange);
277                        } else {
278                            panic!("Unexpected token");
279                        }
280                    },
281                    _ => {},
282                },
283                Token::OtherCode(code) => strings.push(self.replace_defines(code)),
284
285                _ => panic!("Exepted command found {:?}", command)
286            }
287        }
288
289        strings
290    }
291
292    pub fn replace_defines(&self, string: String) -> String {
293        let mut string = string;
294
295        for define in self.defines.keys() {
296            let define = self.defines.get_key_value(define).unwrap();
297            string = string.replace(define.0, define.1);
298        }
299
300        string
301    }
302
303    pub fn preprocess(&mut self, lib: String, name: String) -> String {
304        let mut out_sources = Vec::<String>::new();
305        
306        let main_namespace = self.sources.get(lib.as_str()).unwrap();
307        let main_file = main_namespace.sources.get(name.as_str()).unwrap().clone();
308        let main_file_lines = main_file.lines();
309
310        for line in main_file_lines {
311            for preprocessed_line in self.preprocess_line(line.to_string()) {
312                if preprocessed_line.trim() == "" {
313                    continue;
314                }
315
316                out_sources.push(preprocessed_line);
317            }
318        }
319
320        out_sources.join("\n")
321    }
322}