docbuf_core/
parser.rs

1use std::path::Path;
2
3use crate::{
4    document::*,
5    error::Error,
6    lexer::{Logos, Token},
7    Pragma,
8};
9
10#[derive(Debug, Clone)]
11pub enum OptionSet {
12    Document(DocumentOptions),
13    Field(FieldOptions),
14    Enum(EnumOptions),
15    None,
16}
17
18#[derive(Debug, Clone)]
19pub struct ParserContext {
20    pub current_span: std::ops::Range<usize>,
21    pub current_token: Option<Token>,
22    pub current_document: Option<Document>,
23    pub current_option_item: Option<String>,
24    pub current_comments: Option<String>,
25    pub current_option_set: OptionSet,
26    pub current_field_name: Option<String>,
27}
28
29impl ParserContext {
30    pub fn new() -> ParserContext {
31        ParserContext {
32            current_span: 0..0,
33            current_token: None,
34            current_document: None,
35            current_option_item: None,
36            current_comments: None,
37            current_option_set: OptionSet::None,
38            current_field_name: None,
39        }
40    }
41
42    pub fn set_current_token(&mut self, token: Token) {
43        self.current_token = Some(token);
44    }
45
46    pub fn set_current_span(&mut self, span: std::ops::Range<usize>) {
47        self.current_span = span;
48    }
49
50    pub fn set_current_document(&mut self, document: Document) {
51        self.current_document = Some(document);
52    }
53
54    pub fn set_current_option_item(&mut self, option_item: String) {
55        self.current_option_item = Some(option_item);
56    }
57
58    pub fn set_current_comments(&mut self, comments: String) {
59        if let Some(current_comments) = &mut self.current_comments {
60            current_comments.push_str(&comments);
61        } else {
62            self.current_comments = Some(comments);
63        }
64    }
65
66    pub fn set_current_option_set(&mut self, option_set: OptionSet) {
67        self.current_option_set = option_set;
68    }
69
70    pub fn set_current_field_name(&mut self, field_name: String) {
71        self.current_field_name = Some(field_name);
72    }
73
74    pub fn reset_current_option_item(&mut self) {
75        self.current_option_item = None;
76    }
77
78    pub fn reset_current_option_set(&mut self) {
79        self.current_option_set = OptionSet::None;
80    }
81
82    pub fn reset_current_field_name(&mut self) {
83        self.current_field_name = None;
84    }
85
86    pub fn reset_current_comments(&mut self) {
87        self.current_comments = None;
88    }
89
90    pub fn reset_current_document(&mut self) {
91        self.current_document = None;
92    }
93
94    pub fn reset(&mut self) {
95        self.reset_current_option_item();
96        self.reset_current_option_set();
97        self.reset_current_field_name();
98        self.reset_current_comments();
99        self.reset_current_document();
100    }
101}
102
103#[derive(Debug, Clone)]
104pub struct Parser {
105    pub found_module_name: bool,
106    pub context: ParserContext,
107    pub pragma: Pragma,
108    pub imports: Vec<Parser>,
109    pub module_name: String,
110    pub documents: DocumentMap,
111    pub enumerates: EnumMap,
112}
113
114impl Parser {
115    pub fn new() -> Parser {
116        Parser {
117            found_module_name: false,
118            context: ParserContext::new(),
119            pragma: Pragma::V1,
120            imports: Vec::new(),
121            module_name: String::new(),
122            documents: DocumentMap::default(),
123            enumerates: EnumMap::default(),
124        }
125    }
126
127    pub fn from_str(source: &str) -> Result<Self, Error> {
128        let mut parser = Parser::new();
129
130        let mut tokens = Token::lexer(source);
131
132        parser.context.current_token = tokens
133            .next()
134            .transpose()
135            .map_err(|_| Error::Token(String::from("No Token Found.")))?;
136
137        parser.context.current_span = tokens.span();
138
139        while let Some(token) = tokens.next() {
140            if let Ok(token) = token {
141                println!("Parsing Current Token: {:?}", token);
142
143                match token {
144                    Token::Pragma => {}
145                    Token::Module => {
146                        let span = tokens.span();
147                        let is_mod_keyword = source[span.start..span.end + 1].ends_with(" ");
148
149                        if is_mod_keyword {
150                            // Do not allow multiple module names
151                            if parser.found_module_name {
152                                return Err(Error::InvalidModule);
153                            }
154
155                            // Mark the module found
156                            parser.found_module_name = true;
157                        } else {
158                            // Exit early if the module name is not found
159                            continue;
160                        }
161                    }
162                    Token::Import => {
163                        println!("Import: {:?}", tokens.slice());
164                    }
165                    Token::StatementEnd => {
166                        match parser.context.current_token {
167                            Some(Token::Pragma) => {
168                                let span = parser.context.current_span.end + 1..tokens.span().start;
169                                parser.pragma = Self::pragma(&source[span])?;
170                            }
171                            Some(Token::Module) => {
172                                println!("Module: {:?}", tokens.slice());
173                                let span = parser.context.current_span.end + 1..tokens.span().start;
174                                parser.module_name = source[span].trim().to_string();
175                            }
176                            Some(Token::Import) => {
177                                let span = parser.context.current_span.end + 1..tokens.span().start;
178                                let import = source[span].trim().to_string();
179                                println!("Import: {:?}", import);
180                                // let import = Self::search_imports(source, Path::new("."))?;
181                                // parser.imports = import;
182                            }
183                            Some(Token::StatementAssign) => {
184                                if let Some(option_item) = &parser.context.current_option_item {
185                                    let span = parser.context.current_span.end..tokens.span().start;
186                                    let option_value = source[span].trim().to_string();
187
188                                    println!("Option Item: {:?}", option_item);
189
190                                    let value = option_value.trim().replace('\"', "");
191
192                                    println!("Option Value: {:?}", value);
193
194                                    match &mut parser.context.current_option_set {
195                                        OptionSet::Document(options) => {
196                                            match option_item.as_str() {
197                                                "name" => {
198                                                    options.name = Some(value);
199                                                }
200                                                "root" => {
201                                                    options.root = Some(value == "true");
202                                                }
203                                                _ => {}
204                                            }
205
206                                            if let Some(document) =
207                                                &mut parser.context.current_document
208                                            {
209                                                document.options = options.clone();
210                                            }
211                                        }
212                                        OptionSet::Field(options) => match option_item.as_str() {
213                                            "min_length" => options.min_length = value.parse().ok(),
214                                            "max_length" => options.max_length = value.parse().ok(),
215                                            "min_value" => {
216                                                options.min_value =
217                                                    Some(FieldValue::Raw(value.clone()))
218                                            }
219                                            "max_value" => {
220                                                options.max_value =
221                                                    Some(FieldValue::Raw(value.clone()))
222                                            }
223                                            "regex" => options.regex = Some(value),
224                                            "default" => {
225                                                options.default =
226                                                    Some(FieldValue::Raw(value.clone()))
227                                            }
228                                            "required" => {
229                                                options.required = Some(value == "true");
230                                            }
231                                            "name" => {
232                                                options.name = Some(value);
233                                            }
234                                            _ => {}
235                                        },
236                                        _ => {}
237                                    }
238                                }
239                            }
240                            _ => {}
241                        }
242
243                        println!("Parser: {:?}", parser);
244                    }
245                    Token::DocumentOptionsStart => {
246                        // Set the current document to defaults
247                        parser.context.set_current_document(Document::default());
248
249                        // Set the current option set to document options
250                        parser.context.set_current_option_set(OptionSet::Document(
251                            DocumentOptions::default(),
252                        ));
253                    }
254                    Token::CommentLine => {}
255                    Token::NewLine => match parser.context.current_token {
256                        Some(Token::CommentLine) => {
257                            let span = parser.context.current_span.end + 1..tokens.span().start;
258                            let comment = source[span].trim().to_string();
259
260                            parser.context.set_current_comments(comment);
261                        }
262                        _ => {}
263                    },
264                    // Token::Name => {
265                    //     let span = current_span.end + 1..tokens.span().start;
266                    //     let name = source[span].trim().to_string();
267
268                    //     println!("Name: {:?}", name);
269
270                    //     match current_token {
271                    //         Some(Token::Document) => {
272                    //             if let Some(document) = &mut current_document {
273                    //                 document.name = name;
274                    //             }
275
276                    //             println!("Current Document: {:?}", current_document);
277                    //         }
278                    //         _ => {}
279                    //     }
280                    // }
281                    Token::StatementAssign => {
282                        let span = parser.context.current_span.end + 1..tokens.span().start;
283                        let option_item = source[span].trim().to_string();
284
285                        // Set the current option to be used by the next statement
286                        parser.context.set_current_option_item(option_item);
287                    }
288                    Token::OptionsEnd => {
289                        println!("Options Set: {:?}", parser.context.current_option_set);
290
291                        // Reset the current option item
292                        // current_option_item = None;
293                        // current_option_set = OptionSet::None;
294
295                        // unimplemented!("Options End")
296
297                        // println!("Current Document: {:?}", current_document);
298
299                        // if let Some(document) = &mut current_document {
300                        //     let span = current_span.start..tokens.span().end;
301                        //     let options = Parser::document_options(&source[span])?;
302                        //     document.options = options;
303                        // }
304                    }
305                    Token::Document => {
306                        println!("Current Document: {:?}", parser.context.current_document);
307                    }
308                    Token::SectionStart => {
309                        let span = parser.context.current_span.end + 1..tokens.span().start - 1;
310
311                        match parser.context.current_token {
312                            Some(Token::Document | Token::OptionsEnd) => {
313                                let document_name = source[span].trim().to_string();
314                                println!("Document Name: {:?}", document_name);
315                                if let Some(document) = &mut parser.context.current_document {
316                                    document.name = document_name;
317                                }
318                            }
319                            _ => {}
320                        }
321                    }
322                    Token::SectionEnd => {
323                        if let Some(document) = &parser.context.current_document {
324                            parser
325                                .documents
326                                .insert(document.name.clone(), document.clone());
327                        }
328
329                        // Reset the context, preparing for the next section.
330                        parser.context.reset();
331                    }
332                    Token::FieldOptionsStart => {
333                        // Set the current option set to the field options
334                        parser
335                            .context
336                            .set_current_option_set(OptionSet::Field(FieldOptions::default()));
337                    }
338                    Token::FieldDelimiter => match parser.context.current_token {
339                        Some(Token::OptionsEnd | Token::FieldEnd | Token::NewLine) => {
340                            let span = parser.context.current_span.end + 1..tokens.span().start;
341                            let field_name = source[span].trim().to_string();
342                            parser.context.set_current_field_name(field_name);
343                        }
344                        _ => {}
345                    },
346                    Token::FieldEnd => {
347                        if let Some(field_name) = &parser.context.current_field_name {
348                            let span = parser.context.current_span.end + 1..tokens.span().start;
349                            let field_value = source[span].trim().to_string();
350
351                            println!("Field Name: {:?}", field_name);
352                            println!("Field Value: {:?}", field_value);
353
354                            if let Some(document) = &mut parser.context.current_document {
355                                let options = match &mut parser.context.current_option_set {
356                                    OptionSet::Field(options) => options.clone(),
357                                    _ => FieldOptions::default(),
358                                };
359
360                                document.fields.insert(field_name.clone(), options);
361
362                                println!("Current Document: {:?}", document);
363                            }
364                        }
365
366                        parser.context.reset_current_field_name();
367                        parser.context.reset_current_option_set();
368                    }
369                    Token::EnumOptionsStart => {
370                        parser
371                            .context
372                            .set_current_option_set(OptionSet::Enum(EnumOptions::default()));
373                    }
374                    _ => {}
375                }
376
377                // Update the current token and span
378                parser.context.current_token = Some(token);
379                parser.context.current_span = tokens.span();
380            }
381        }
382
383        // Throw an error if the module name does not exist.
384        if !parser.found_module_name {
385            return Err(Error::InvalidModule);
386        }
387
388        // println!("Tokens: {:?}", tokens);
389        // let pragma = Self::pragma(source)?;
390        // println!("Pragma: {:?}", pragma);
391        // let document_options = Self::document_options(source)?;
392        // println!("Document Options: {:?}", document_options);
393        // // let documents = Self::documents(&file)?;
394        // // println!("Documents: {:?}", documents);
395        // parser.pragma = pragma;
396
397        Ok(parser)
398    }
399
400    pub fn from_file(file: &Path) -> Result<Self, Error> {
401        let parent_dir = file.parent().ok_or(Error::InvalidPath)?;
402
403        println!("Parent Dir: {:?}", parent_dir);
404
405        let file = std::fs::read_to_string(file)?;
406
407        let mut parser = Self::from_str(&file)?;
408
409        let imports = Self::search_imports(&file, parent_dir)?;
410
411        parser.imports = imports;
412
413        Ok(parser)
414    }
415
416    fn pragma(source: &str) -> Result<Pragma, Error> {
417        println!("Source: {:?}", source);
418
419        match source.trim() {
420            "v1" => Ok(Pragma::V1),
421            _ => Err(Error::MissingPragma),
422        }
423    }
424
425    fn search_imports(source: &str, dir: &Path) -> Result<Vec<Parser>, Error> {
426        // Search for the import statement, `import /path/to/file;` syntax
427        let re = regex::Regex::new(r#"import (?P<file>.*);"#).unwrap();
428
429        let files = re
430            .captures_iter(source)
431            .map(|cap| cap["file"].trim().replace('\"', ""))
432            .collect::<Vec<String>>();
433
434        let files = files
435            .iter()
436            .map(|file| {
437                let file = dir.join(file);
438                println!("File: {:?}", file);
439                Self::from_file(&file).unwrap()
440            })
441            .collect::<Vec<Parser>>();
442
443        Ok(files)
444    }
445
446    // fn documents(source: &str) -> Result<(), Error> {
447    //     // Search for the document type, `Document MyDocumentType { ... }` syntax
448
449    //     let re = regex::Regex::new(r#"Document (?<name>\w+) {(?<fields>\w+)}"#).unwrap();
450
451    //     for cap in re.captures_iter(source) {
452    //         let name = &cap["name"];
453    //         let fields = &cap["fields"];
454
455    //         println!("Name: {}", name);
456    //         println!("Fields: {}", fields);
457    //     }
458
459    //     Ok(())
460    // }
461}
462
463#[cfg(test)]
464mod tests {
465    use std::path::Path;
466
467    use super::*;
468
469    #[test]
470    fn test_parser() -> Result<(), Box<dyn std::error::Error>> {
471        let file = Path::new("../examples/example.docbuf");
472
473        Parser::from_file(&file)?;
474
475        Ok(())
476    }
477}