Skip to main content

anillo/
parser.rs

1//! Types and implementations for parsing a pre-built Token buffer.
2
3use std::{collections::VecDeque, error::Error};
4
5use crate::{
6    error::CompilationError,
7    token::{
8        Ast, CallArg, ExternalFunctionCall, ExternalFunctionNode, FuncArg, FuncArgType, Ingot,
9        IsrNode, Ring, Token, TokenInfo,
10    },
11};
12
13/// The Anillo Parser
14///
15/// The parser lives at a slightly higher level than the lexer. At this level,
16/// We can begin to do some additional reasoning about the program as we build
17/// the AST (see `AST`).
18///
19/// The parse_* methods of this struct are each intended to be called **_only_**
20/// when its caller has determined that it must succeed (either due to the
21/// grammar mandating it, or a peek that indicated an optional token was found).
22/// When these functions fail, this thus causes the whole compilation to fail
23/// with a diagnostic.
24#[derive(Debug)]
25pub struct Parser {
26    tokens: VecDeque<TokenInfo>,
27}
28
29impl Parser {
30    pub fn new(tokens: VecDeque<TokenInfo>) -> Parser {
31        Parser { tokens }
32    }
33
34    /// Generates the AST from a `TokenInfo` buffer input
35    ///
36    /// Since we have been fed rich token info from the lexer at this point,
37    /// we can model the grammar closely here (no need to worry about whitespace!).
38    /// Every token type has a very small set of expected token types that may
39    /// appear next, which makes a combinator style (as-in what was shown in
40    /// class) of failure easy to detect.
41    pub fn run(&mut self, verbose: bool) -> Result<Ast, Box<dyn Error>> {
42        let mut ast_vec: Vec<Ingot> = Vec::new();
43
44        while let Some(token) = self.tokens.pop_front() {
45            if verbose {
46                println!(
47                    "###########################################################################"
48                );
49                println!("Consumed token: {}", token);
50                println!("Remaining tokens:");
51                dbg!(&self.tokens);
52                println!("AST in progress:");
53                dbg!(&ast_vec);
54                println!();
55                println!(
56                    "###########################################################################"
57                );
58            }
59
60            match token.borrow_token() {
61                Token::KeywordExtern => {
62                    let ext = self.parse_extern(token.line(), token.col())?;
63                    ast_vec.push(Ingot::ExternalFunction(ext));
64                }
65                Token::KeywordIsr => {
66                    let isr: IsrNode = self.parse_isr(token.line(), token.col())?;
67                    ast_vec.push(Ingot::Isr(isr));
68                }
69                other => {
70                    return Err(Box::new(CompilationError::new(
71                        token.line(),
72                        token.col(),
73                        format!("Expected the start of an Ingot, got {}", other),
74                    )));
75                }
76            }
77        }
78
79        Ok(Ast::new(ast_vec))
80    }
81
82    /// Parse info about an external function declaration
83    fn parse_extern(
84        &mut self,
85        last_line: u32,
86        last_col: u32,
87    ) -> Result<ExternalFunctionNode, CompilationError> {
88        match self.tokens.pop_front() {
89            // found something, need to see if it's a func
90            Some(func) => match func.borrow_token() {
91                // found a func name, need to see if '(' is next
92                Token::Identifier(func_name) => match self.tokens.pop_front() {
93                    // found something, need to see if it's '('
94                    Some(lparen) => match lparen.borrow_token() {
95                        Token::LeftParen => {
96                            let args = self.parse_func_args()?;
97                            let mut privilege: Option<Ring> = None;
98                            if let Some(token) = self.tokens.front()
99                                && let &Token::KeywordWithLevel = token.borrow_token()
100                            {
101                                privilege =
102                                    Some(self.parse_withlevel(lparen.line(), lparen.col())?);
103                            }
104
105                            // weird rust return expression
106                            Ok(ExternalFunctionNode::new(
107                                func_name.clone(),
108                                args,
109                                privilege,
110                            ))
111                        }
112                        other_token => Err(CompilationError::new(
113                            lparen.line(),
114                            lparen.col(),
115                            format!(
116                                "Expected '(' after extern declaration, found: {}",
117                                other_token
118                            ),
119                        )),
120                    },
121                    None => Err(CompilationError::new(
122                        func.line(),
123                        func.col(),
124                        String::from("Expected '(' after extern declaration, found EOF"),
125                    )),
126                },
127                other_token => Err(CompilationError::new(
128                    func.line(),
129                    func.col(),
130                    format!(
131                        "Expected function name after extern declaration, found: {}",
132                        other_token
133                    ),
134                )),
135            },
136            None => Err(CompilationError::new(
137                last_line,
138                last_col,
139                String::from("Expected function name after extern declaration, found EOF"),
140            )),
141        }
142    }
143
144    /// Parse info about the valid args in an external function declaration
145    fn parse_func_args(&mut self) -> Result<Vec<FuncArg>, CompilationError> {
146        #[derive(Clone, Copy)]
147        enum SeekState {
148            Start,
149            SeekArg,
150            SeekType,
151        }
152        let mut args: Vec<FuncArg> = Vec::new();
153        let mut state: SeekState = SeekState::Start;
154
155        let mut type_t: FuncArgType = FuncArgType::U8;
156
157        while let Some(token) = self.tokens.pop_front() {
158            match (token.borrow_token(), state) {
159                (Token::RightParen, SeekState::SeekType | SeekState::Start) => break,
160                (Token::RightParen, SeekState::SeekArg) => {
161                    return Err(CompilationError::new(
162                        token.line(),
163                        token.col(),
164                        String::from("Expected arg name for function argument, found ')'"),
165                    ));
166                }
167                (Token::Identifier(type_str), SeekState::SeekType | SeekState::Start) => {
168                    type_t = match type_str.as_str() {
169                        "u8" => FuncArgType::U8,
170                        "i8" => FuncArgType::I8,
171                        "u16" => FuncArgType::U16,
172                        "i16" => FuncArgType::I16,
173                        "u32" => FuncArgType::U32,
174                        "i32" => FuncArgType::I32,
175                        "u64" => FuncArgType::U64,
176                        "i64" => FuncArgType::I64,
177                        other => {
178                            return Err(CompilationError::new(
179                                token.line(),
180                                token.col(),
181                                format!("Invalid c type: {}", other),
182                            ));
183                        }
184                    };
185
186                    state = SeekState::SeekArg;
187                }
188                (Token::Comma, SeekState::Start) => {
189                    return Err(CompilationError::new(
190                        token.line(),
191                        token.col(),
192                        String::from("Expected ')' or fun arg, got ','"),
193                    ));
194                }
195                (Token::Comma, SeekState::SeekType) => continue,
196                (Token::Comma, SeekState::SeekArg) => continue,
197                (Token::Identifier(arg), SeekState::SeekArg) => {
198                    state = SeekState::SeekType;
199                    args.push(FuncArg::new(arg.clone(), type_t));
200                }
201                (unexpected, _) => {
202                    return Err(CompilationError::new(
203                        token.line(),
204                        token.col(),
205                        format!("Unexpected token while parsing func_args: {}", unexpected),
206                    ));
207                }
208            }
209        }
210
211        Ok(args)
212    }
213
214    /// Parse a found (optional) RingLevel expression.
215    ///
216    /// ExternalFunctions and ISRs that do not find 'WithLevel' won't call this,
217    /// and the AST validator will interpret the None variant of an Option<Ring>
218    /// as implicitly meaning the Super privilege.
219    fn parse_withlevel(
220        &mut self,
221        last_line: u32,
222        last_column: u32,
223    ) -> Result<Ring, CompilationError> {
224        match self.tokens.pop_front() {
225            Some(token) => match token.borrow_token() {
226                Token::KeywordWithLevel => match self.tokens.pop_front() {
227                    Some(lparen) => match lparen.borrow_token() {
228                        Token::LeftParen => match self.tokens.pop_front() {
229                            Some(priv_level) => match priv_level.borrow_token() {
230                                Token::KeywordPrivilege(level) => match self.tokens.pop_front() {
231                                    Some(token) => match token.borrow_token() {
232                                        Token::RightParen => Ok(*level),
233                                        other_token => Err(CompilationError::new(
234                                            token.line(),
235                                            token.col(),
236                                            format!(
237                                                "Expected ')' after privilege level, found {}",
238                                                other_token
239                                            ),
240                                        )),
241                                    },
242                                    None => Err(CompilationError::new(
243                                        priv_level.line(),
244                                        priv_level.col(),
245                                        String::from(
246                                            "Expected ')' after privilege level, found EOF",
247                                        ),
248                                    )),
249                                },
250                                other_token => Err(CompilationError::new(
251                                    priv_level.line(),
252                                    priv_level.col(),
253                                    format!("Expected privilege level, found: {}", other_token),
254                                )),
255                            },
256                            None => Err(CompilationError::new(
257                                lparen.line(),
258                                lparen.col(),
259                                String::from("Expected privilege level, found EOF"),
260                            )),
261                        },
262                        other_token => Err(CompilationError::new(
263                            lparen.line(),
264                            lparen.col(),
265                            format!("Expected '(' after WithLevel, found: {}", other_token),
266                        )),
267                    },
268                    None => Err(CompilationError::new(
269                        token.line(),
270                        token.col(),
271                        String::from("Expected '(' after WithLevel, found EOF"),
272                    )),
273                },
274                other_token => Err(CompilationError::new(
275                    token.line(),
276                    token.col(),
277                    format!("Expected 'WithLevel', found: {}", other_token),
278                )),
279            },
280            None => Err(CompilationError::new(
281                last_line,
282                last_column,
283                String::from("Expected 'WithLevel', found EOF"),
284            )),
285        }
286    }
287
288    /// Parse info about a defined isr
289    fn parse_isr(&mut self, last_line: u32, last_column: u32) -> Result<IsrNode, CompilationError> {
290        match self.tokens.pop_front() {
291            Some(name) => match name.borrow_token() {
292                Token::Identifier(isr_name) => match self.tokens.pop_front() {
293                    Some(lparen) => match lparen.borrow_token() {
294                        Token::LeftParen => match self.tokens.pop_front() {
295                            Some(num) => match num.borrow_token() {
296                                Token::Identifier(isr_num_str) => {
297                                    // Isr Numbers above 255 are rare to my knowledge
298                                    match isr_num_str.parse::<u8>() {
299                                        Ok(isr_num) => match self.tokens.pop_front() {
300                                            Some(rparen) => match rparen.borrow_token() {
301                                                Token::RightParen => {
302                                                    let mut privilege: Option<Ring> = None;
303                                                    if let Some(token) = self.tokens.front()
304                                                        && let Token::KeywordWithLevel =
305                                                            token.borrow_token()
306                                                    {
307                                                        privilege = Some(self.parse_withlevel(
308                                                            num.line(),
309                                                            num.col(),
310                                                        )?);
311                                                    }
312
313                                                    let calling_func =
314                                                        self.parse_isr_body(num.line(), num.col())?;
315                                                    Ok(IsrNode::new(
316                                                        isr_name.clone(),
317                                                        isr_num,
318                                                        privilege,
319                                                        calling_func,
320                                                    ))
321                                                }
322                                                other_token => Err(CompilationError::new(
323                                                    rparen.line(),
324                                                    rparen.col(),
325                                                    format!(
326                                                        "Expected closing ')', found: {}",
327                                                        other_token
328                                                    ),
329                                                )),
330                                            },
331                                            None => Err(CompilationError::new(
332                                                num.line(),
333                                                num.col(),
334                                                String::from("Expected closing ')', found EOF"),
335                                            )),
336                                        },
337                                        Err(e) => Err(CompilationError::new(
338                                            num.line(),
339                                            num.col(),
340                                            format!("Expected numeric value, failed with: {}", e),
341                                        )),
342                                    }
343                                }
344                                other_token => Err(CompilationError::new(
345                                    num.line(),
346                                    num.col(),
347                                    format!("Expected an Isr number, found: {}", other_token),
348                                )),
349                            },
350                            None => Err(CompilationError::new(
351                                lparen.line(),
352                                lparen.col(),
353                                String::from(""),
354                            )),
355                        },
356                        other_token => Err(CompilationError::new(
357                            lparen.line(),
358                            lparen.col(),
359                            format!("Expected '(' after Isr name, found: {}", other_token),
360                        )),
361                    },
362                    None => Err(CompilationError::new(
363                        name.line(),
364                        name.col(),
365                        String::from("Expected '(' after Isr name, found EOF"),
366                    )),
367                },
368                other_token => Err(CompilationError::new(
369                    name.line(),
370                    name.col(),
371                    format!("Expected ISR name, found: {}", other_token),
372                )),
373            },
374            None => Err(CompilationError::new(
375                last_line,
376                last_column,
377                String::from("Expected ISR name, found EOF"),
378            )),
379        }
380    }
381
382    /// Parse the collection of functions called from within the ISR.
383    ///
384    /// An ISR may only call functions (and those functions must be declared
385    /// with the 'extern' keyword as well). It may call 0 or 1 at the moment,
386    /// but this may be expanded in the future.
387    fn parse_isr_body(
388        &mut self,
389        last_line: u32,
390        last_column: u32,
391    ) -> Result<Option<ExternalFunctionCall>, CompilationError> {
392        match self.tokens.pop_front() {
393            Some(lbracket) => match lbracket.borrow_token() {
394                Token::LeftBracket => match self.tokens.pop_front() {
395                    Some(remaining) => match remaining.borrow_token() {
396                        Token::KeywordCall => Ok(Some(
397                            self.parse_function_call(remaining.line(), remaining.col())?,
398                        )),
399                        Token::RightBracket => Ok(None),
400                        other => Err(CompilationError::new(
401                            remaining.line(),
402                            remaining.col(),
403                            format!("Expected call expression or closing '}}', found: {}", other),
404                        )),
405                    },
406                    None => Err(CompilationError::new(
407                        lbracket.line(),
408                        lbracket.col(),
409                        String::from("Expected call expression or closing '}', found EOF"),
410                    )),
411                },
412                other_token => Err(CompilationError::new(
413                    lbracket.line(),
414                    lbracket.col(),
415                    format!("Expected opening '{{', found: {}", other_token),
416                )),
417            },
418            None => Err(CompilationError::new(
419                last_line,
420                last_column,
421                String::from("Expected opening '{', found EOF"),
422            )),
423        }
424    }
425
426    /// Parse the known-to-exist function called from within the ISR.
427    ///
428    /// This will be called from `parse_isr_body` if and only if it has peeked
429    /// ahead and determined a call exists (as calls are optional). Thus,
430    /// failures here are still universally treated as hard compilation errors.
431    fn parse_function_call(
432        &mut self,
433        last_line: u32,
434        last_col: u32,
435    ) -> Result<ExternalFunctionCall, CompilationError> {
436        #[derive(Clone, Copy)]
437        enum SeekState {
438            Start,
439            SeekArg,
440            SeekComma,
441        }
442
443        match self.tokens.pop_front() {
444            Some(func) => match func.borrow_token() {
445                Token::Identifier(func_name) => match self.tokens.pop_front() {
446                    Some(lparen) => match lparen.borrow_token() {
447                        Token::LeftParen => {
448                            let mut args: Vec<CallArg> = Vec::new();
449                            let mut state: SeekState = SeekState::Start;
450
451                            while let Some(token) = self.tokens.pop_front() {
452                                match (token.borrow_token(), state) {
453                                    (
454                                        Token::RightParen,
455                                        SeekState::Start | SeekState::SeekComma,
456                                    ) => break,
457                                    (Token::RightParen, SeekState::SeekArg) => {
458                                        return Err(CompilationError::new(
459                                            token.line(),
460                                            token.col(),
461                                            String::from("Expected argument, found ')'"),
462                                        ));
463                                    }
464                                    (Token::Identifier(arg), SeekState::SeekArg) => {
465                                        args.push(CallArg::Var(arg.clone()));
466                                        state = SeekState::SeekComma;
467                                    }
468                                    (Token::Dollar, SeekState::Start | SeekState::SeekArg) => {
469                                        args.push(CallArg::Dollar);
470                                        state = SeekState::SeekComma;
471                                    }
472                                    (unexpected, SeekState::Start | SeekState::SeekArg) => {
473                                        return Err(CompilationError::new(
474                                            token.line(),
475                                            token.col(),
476                                            format!(
477                                                "Unexpected token in call expression: {}",
478                                                unexpected
479                                            ),
480                                        ));
481                                    }
482                                    (Token::Comma, SeekState::SeekComma) => {
483                                        state = SeekState::SeekArg;
484                                    }
485                                    (unexpected, SeekState::SeekComma) => {
486                                        return Err(CompilationError::new(
487                                            token.line(),
488                                            token.col(),
489                                            format!(
490                                                "Unexpected token in call expression: {}",
491                                                unexpected
492                                            ),
493                                        ));
494                                    }
495                                }
496                            }
497
498                            match self.tokens.pop_front() {
499                                Some(rbracket) => match rbracket.borrow_token() {
500                                    Token::RightBracket => {
501                                        Ok(ExternalFunctionCall::new(func_name.clone(), args))
502                                    }
503                                    other => Err(CompilationError::new(
504                                        rbracket.line(),
505                                        rbracket.col(),
506                                        format!("Expected '}}', found: {}", other),
507                                    )),
508                                },
509                                None => Err(CompilationError::new(
510                                    func.line(),
511                                    func.col(),
512                                    String::from("Expected '}', found EOF"),
513                                )),
514                            }
515                        }
516                        other => Err(CompilationError::new(
517                            lparen.line(),
518                            lparen.col(),
519                            format!("Expected '(', found: {}", other),
520                        )),
521                    },
522                    None => Err(CompilationError::new(
523                        func.line(),
524                        func.col(),
525                        String::from("Expected '(', found EOF"),
526                    )),
527                },
528                other => Err(CompilationError::new(
529                    func.line(),
530                    func.col(),
531                    format!("Expected function name, found: {}", other),
532                )),
533            },
534            None => Err(CompilationError::new(
535                last_line,
536                last_col,
537                String::from("Expected function name, found EOF"),
538            )),
539        }
540    }
541}
542
543#[cfg(test)]
544mod tests {
545
546    #[test]
547    fn total_parse() {}
548
549    #[test]
550    fn only_externs() {}
551
552    #[test]
553    fn only_isrs() {}
554}