heraclitus_compiler/compiling/parser/
syntax_module.rs

1use crate::compiling::failing::failure::Failure;
2use colored::Colorize;
3
4use super::Metadata;
5
6#[macro_export]
7/// This macro is a syntax sugar for the name method
8/// All this does is setting the new name without all the syntax clutter.
9/// # Example
10/// ```
11/// # use heraclitus_compiler::prelude::*;
12/// # struct MySyntax;
13/// impl SyntaxModule<DefaultMetadata> for MySyntax {
14///     syntax_name!("MySyntax");
15/// #   fn new() -> Self { Self {} }
16/// #   fn parse(&mut self, meta: &mut DefaultMetadata) -> SyntaxResult { Ok(()) }
17///     // ...
18/// }
19/// ```
20macro_rules! syntax_name {
21    ($expr:expr) => {
22        fn name() -> &'static str {
23            $expr
24        }
25    };
26}
27
28/// Result that should be returned in the parsing phase
29pub type SyntaxResult = Result<(), Failure>;
30
31/// Trait for parsing
32///
33/// Trait that should be implemented in order to parse tokens with heraklit
34/// ```
35/// # use heraclitus_compiler::prelude::*;
36/// struct MySyntax {
37///     name: String
38///     // ... (you decide what you need to store)
39/// }
40///
41/// impl SyntaxModule<DefaultMetadata> for MySyntax {
42///     syntax_name!("MySyntax");
43///
44///     fn new() -> MySyntax {
45///         MySyntax {
46///             name: format!(""),
47///             // Default initialization
48///         }
49///     }
50///
51///     // Here you can parse the actual code
52///     fn parse(&mut self, meta: &mut DefaultMetadata) -> SyntaxResult {
53///         token(meta, "var")?;
54///         self.name = variable(meta, vec!['_'])?;
55///         Ok(())
56///     }
57/// }
58/// ```
59pub trait SyntaxModule<M: Metadata> {
60    /// Create a new default implementation of syntax module
61    fn new() -> Self;
62    /// Name of this module
63    fn name() -> &'static str;
64    /// Parse and create AST
65    ///
66    /// This method is fundamental in creating a functional AST node that can determine
67    /// if tokens provided by metadata can be consumed to create this particular AST node.
68    fn parse(&mut self, meta: &mut M) -> SyntaxResult;
69    /// Do not implement this function as this is a predefined function for debugging
70    fn parse_debug(&mut self, meta: &mut M) -> SyntaxResult {
71        match meta.get_debug() {
72            Some(debug) => {
73                let padding = "  ".repeat(debug);
74                println!("{padding}[Entered] {}", Self::name());
75                meta.set_debug(debug + 1);
76                let time = std::time::Instant::now();
77                let result = self.parse(meta);
78                match result {
79                    Ok(()) => println!("{padding}{} {} ({}ms)", "[Left]".green(), Self::name(), time.elapsed().as_millis()),
80                    Err(_) => println!("{padding}{} {} ({}ms)", "[Failed]".red(), Self::name(), time.elapsed().as_millis())
81                }
82                meta.set_debug(debug);
83                result
84            }
85            None => {
86                meta.set_debug(0);
87                self.parse_debug(meta)
88            }
89        }
90    }
91}
92
93#[cfg(test)]
94mod test {
95    use super::*;
96    use crate::compiling::parser::pattern::*;
97    use crate::compiling::parser::preset::*;
98    use crate::compiling::{ Token, DefaultMetadata, Metadata };
99
100    struct Expression {}
101    impl SyntaxModule<DefaultMetadata> for Expression {
102        syntax_name!("Expression");
103
104        fn new() -> Self {
105            Expression { }
106        }
107        fn parse(&mut self, meta: &mut DefaultMetadata) -> SyntaxResult {
108            token(meta, "let")?;
109            Ok(())
110        }
111    }
112
113    #[test]
114    fn test_token_match() {
115        let mut exp = Expression {};
116        let dataset1 = vec![
117            Token {
118                word: format!("let"),
119                pos: (0, 0),
120                start: 0
121            }
122        ];
123        let dataset2 = vec![
124            Token {
125                word: format!("tell"),
126                pos: (0, 0),
127                start: 0
128            }
129        ];
130        let path = Some(format!("path/to/file"));
131        let result1 = exp.parse(&mut DefaultMetadata::new(dataset1, path.clone(), None));
132        let result2 = exp.parse(&mut DefaultMetadata::new(dataset2, path.clone(), None));
133        assert!(result1.is_ok());
134        assert!(result2.is_err());
135    }
136
137    struct Preset {}
138    impl SyntaxModule<DefaultMetadata> for Preset {
139        syntax_name!("Preset");
140        fn new() -> Self {
141            Preset {  }
142        }
143        fn parse(&mut self, meta: &mut DefaultMetadata) -> SyntaxResult {
144            variable(meta, vec!['_'])?;
145            numeric(meta, vec![])?;
146            number(meta, vec![])?;
147            integer(meta, vec![])?;
148            float(meta, vec![])?;
149            Ok(())
150        }
151    }
152
153    #[test]
154    fn test_preset_match() {
155        let mut exp = Preset {};
156        let dataset = vec![
157            // Variable
158            Token { word: format!("_text"), pos: (0, 0), start: 0 },
159            // Numeric
160            Token { word: format!("12321"), pos: (0, 0), start: 0 },
161            // Number
162            Token { word: format!("-123.12"), pos: (0, 0), start: 0 },
163            // Integer
164            Token { word: format!("-12"), pos: (0, 0), start: 0 },
165            // Float
166            Token { word: format!("-.681"), pos: (0, 0), start: 0 }
167        ];
168        let path = Some(format!("path/to/file"));
169        let result = exp.parse(&mut DefaultMetadata::new(dataset, path, None));
170        assert!(result.is_ok());
171    }
172
173    struct PatternModule {}
174    impl SyntaxModule<DefaultMetadata> for PatternModule {
175        syntax_name!("Pattern Module");
176        fn new() -> Self {
177            PatternModule {  }
178        }
179        #[allow(unused_must_use)]
180        fn parse(&mut self, meta: &mut DefaultMetadata) -> SyntaxResult {
181            // Any
182            if let Ok(_) = token(meta, "apple") {}
183            else if let Ok(_) = token(meta, "orange") {}
184            else if let Ok(_) = token(meta, "banana") {}
185            else {
186                if let Err(details) = token(meta, "banana") {
187                    return Err(details);
188                }
189            }
190            // Optional
191            token(meta, "optional");
192            // Syntax
193            syntax(meta, &mut Expression::new())?;
194            // Repeat
195            loop {
196                if let Err(_) = token(meta, "test") {
197                    break;
198                }
199                if let Err(_) = token(meta, ",") {
200                    break;
201                }
202            }
203            // End token
204            token(meta, "end");
205            Ok(())
206        }
207    }
208
209    #[test]
210    fn rest_match() {
211        let mut exp = PatternModule {};
212        // Everything should pass
213        let dataset1 = vec![
214            Token { word: format!("orange"), pos: (0, 0), start: 0 },
215            Token { word: format!("optional"), pos: (0, 0), start: 0 },
216            Token { word: format!("let"), pos: (0, 0), start: 0 },
217            Token { word: format!("this"), pos: (0, 0), start: 0 },
218            Token { word: format!(","), pos: (0, 0), start: 0 },
219            Token { word: format!("this"), pos: (0, 0), start: 0 },
220            Token { word: format!("end"), pos: (0, 0), start: 0 }
221        ];
222        // Token should fail
223        let dataset2 = vec![
224            Token { word: format!("kiwi"), pos: (0, 0), start: 0 },
225            Token { word: format!("optional"), pos: (0, 0), start: 0 },
226            Token { word: format!("let"), pos: (0, 0), start: 0 },
227            Token { word: format!("this"), pos: (0, 0), start: 0 },
228            Token { word: format!(","), pos: (0, 0), start: 0 },
229            Token { word: format!("this"), pos: (0, 0), start: 0 },
230            Token { word: format!("end"), pos: (0, 0), start: 0 }
231        ];
232        // Syntax should fail
233        let dataset3 = vec![
234            Token { word: format!("orange"), pos: (0, 0), start: 0 },
235            Token { word: format!("tell"), pos: (0, 0), start: 0 },
236            Token { word: format!("this"), pos: (0, 0), start: 0 },
237            Token { word: format!(","), pos: (0, 0), start: 0 },
238            Token { word: format!("this"), pos: (0, 0), start: 0 },
239            Token { word: format!("end"), pos: (0, 0), start: 0 }
240        ];
241        // Token should fail because of repeat matching (this , this) ,
242        let dataset4 = vec![
243            Token { word: format!("orange"), pos: (0, 0), start: 0 },
244            Token { word: format!("tell"), pos: (0, 0), start: 0 },
245            Token { word: format!("this"), pos: (0, 0), start: 0 },
246            Token { word: format!(","), pos: (0, 0), start: 0 },
247            Token { word: format!("this"), pos: (0, 0), start: 0 },
248            Token { word: format!("this"), pos: (0, 0), start: 0 },
249            Token { word: format!("end"), pos: (0, 0), start: 0 }
250        ];
251        let path = Some(format!("path/to/file"));
252        let result1 = exp.parse(&mut DefaultMetadata::new(dataset1, path.clone(), None));
253        let result2 = exp.parse(&mut DefaultMetadata::new(dataset2, path.clone(), None));
254        let result3 = exp.parse(&mut DefaultMetadata::new(dataset3, path.clone(), None));
255        let result4 = exp.parse(&mut DefaultMetadata::new(dataset4, path.clone(), None));
256        assert!(result1.is_ok());
257        assert!(result2.is_err());
258        assert!(result3.is_err());
259        assert!(result4.is_err());
260    }
261
262}