gimura_preprocessor_lib/
lib.rs1use 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}