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 if parser.found_module_name {
152 return Err(Error::InvalidModule);
153 }
154
155 parser.found_module_name = true;
157 } else {
158 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 }
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 parser.context.set_current_document(Document::default());
248
249 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::StatementAssign => {
282 let span = parser.context.current_span.end + 1..tokens.span().start;
283 let option_item = source[span].trim().to_string();
284
285 parser.context.set_current_option_item(option_item);
287 }
288 Token::OptionsEnd => {
289 println!("Options Set: {:?}", parser.context.current_option_set);
290
291 }
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 parser.context.reset();
331 }
332 Token::FieldOptionsStart => {
333 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 parser.context.current_token = Some(token);
379 parser.context.current_span = tokens.span();
380 }
381 }
382
383 if !parser.found_module_name {
385 return Err(Error::InvalidModule);
386 }
387
388 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 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 }
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}