roan_ast/parser/
mod.rs

1mod expressions;
2mod statements;
3
4use crate::{
5    lexer::token::{Token, TokenKind},
6    Ast,
7};
8use anyhow::Result;
9use roan_error::error::RoanError::ExpectedToken;
10use tracing::debug;
11
12#[derive(Debug, PartialEq, Eq)]
13pub enum ParseContext {
14    Normal,
15    IfCondition,
16    WhileCondition,
17}
18
19/// A parser that converts a list of tokens into an Abstract Syntax Tree (AST).
20///
21/// This struct takes tokens generated by the lexer and produces an AST,
22/// which represents the structure of the source code in a tree format.
23#[derive(Debug)]
24pub struct Parser {
25    /// A list of tokens to be parsed.
26    pub tokens: Vec<Token>,
27    /// The current index of the token being processed.
28    pub current: usize,
29    /// The current context stack for parsing.
30    pub context_stack: Vec<ParseContext>,
31}
32
33impl Parser {
34    /// Creates a new `Parser` instance with a set of tokens to parse.
35    ///
36    /// # Arguments
37    /// * `tokens` - The list of tokens generated by the lexer.
38    ///
39    /// # Returns
40    /// * A new `Parser` instance ready to parse the tokens.
41    pub fn new(tokens: Vec<Token>) -> Self {
42        Self {
43            tokens,
44            current: 0,
45            context_stack: vec![ParseContext::Normal],
46        }
47    }
48
49    /// Returns a reference to the current parser context.
50    ///
51    /// # Panics
52    ///
53    /// This function will panic if the `context_stack` is empty. However, this condition
54    /// should never happen because the context stack should always have at least one context.
55    pub fn current_context(&self) -> &ParseContext {
56        self.context_stack
57            .last()
58            .expect("Parser context stack should never be empty")
59    }
60
61    /// Pushes a new context onto the context stack.
62    ///
63    /// # Parameters
64    ///
65    /// - `context`: The `ParseContext` to be pushed onto the stack.
66    pub fn push_context(&mut self, context: ParseContext) {
67        debug!("Pushing context: {:?}", context);
68        self.context_stack.push(context);
69    }
70
71    /// Pops the current context off the context stack.
72    pub fn pop_context(&mut self) {
73        debug!("Popping context: {:?}", self.current_context());
74        self.context_stack.pop();
75    }
76
77    /// Checks if the current context matches a given context.
78    ///
79    /// # Parameters
80    ///
81    /// - `context`: A reference to a `ParseContext` to compare with the current context.
82    ///
83    /// # Returns
84    ///
85    /// `true` if the current context is equal to the given context, `false` otherwise.
86    pub fn is_context(&self, context: &ParseContext) -> bool {
87        debug!(
88            "Checking context: {:?} == {:?}",
89            self.current_context(),
90            context
91        );
92        self.current_context() == context
93    }
94}
95
96impl Parser {
97    /// Parses the list of tokens and constructs an AST.
98    ///
99    /// This method will continue parsing tokens until the end of the token stream
100    /// (or an EOF token) is encountered. Each token sequence is turned into a statement
101    /// and added to the AST.
102    ///
103    /// # Returns
104    /// * `Ok(Ast)` - If the parsing is successful and an AST is created.
105    /// * `Err` - If an error occurs during parsing.
106    pub fn parse(&mut self) -> Result<Ast> {
107        let mut ast = Ast::new();
108
109        // Parse tokens into statements until EOF is reached
110        while !self.is_eof() {
111            // Parse a statement, if successful, add it to the AST
112            let stmt = self.parse_stmt()?;
113
114            if let Some(stmt) = stmt {
115                ast.stmts.push(stmt);
116            }
117        }
118
119        Ok(ast)
120    }
121
122    /// Consumes the current token and advances to the next token in the stream.
123    ///
124    /// # Returns
125    /// * The previous token that was consumed.
126    pub fn consume(&mut self) -> Token {
127        if !self.is_eof() {
128            self.current += 1;
129        }
130
131        self.previous()
132    }
133
134    /// Retrieves the previous token.
135    ///
136    /// # Panics
137    /// This function will panic if there are no previous tokens.
138    pub fn previous(&self) -> Token {
139        assert!(self.current > 0);
140        self.tokens[self.current - 1].clone()
141    }
142
143    /// Peeks at the current token without consuming it.
144    ///
145    /// # Returns
146    /// * A copy of the current token.
147    pub fn peek(&self) -> Token {
148        self.tokens.get(self.current).cloned().unwrap_or_else(|| {
149            Token::new(TokenKind::EOF, self.tokens.last().unwrap().span.clone())
150        })
151    }
152
153    /// Peeks at the next token without consuming the current one.
154    ///
155    /// # Returns
156    /// * A copy of the next token.
157    pub fn peek_next(&self) -> Token {
158        self.tokens[self.current + 1].clone()
159    }
160
161    /// Checks if the current token is the end of file (EOF).
162    ///
163    /// # Returns
164    /// * `true` if the current token is EOF, otherwise `false`.
165    pub fn is_eof(&self) -> bool {
166        self.current >= self.tokens.len() || self.peek().kind == TokenKind::EOF
167    }
168
169    /// Consumes the current token if it matches the specified `TokenKind`.
170    ///
171    /// # Arguments
172    /// * `kind` - The kind of token to check.
173    pub fn possible_check(&mut self, kind: TokenKind) {
174        if self.peek().kind == kind {
175            self.consume();
176        }
177    }
178
179    /// Expects the current token to be of a specific `TokenKind` and consumes it.
180    ///
181    /// If the token matches the expected kind, it is consumed and returned.
182    /// If not, an error is raised indicating the expected token.
183    ///
184    /// # Arguments
185    /// * `kind` - The expected kind of the current token.
186    ///
187    /// # Returns
188    /// * `Ok(Token)` - The token that was consumed.
189    /// * `Err` - An error if the current token is not of the expected kind.
190    pub fn expect(&mut self, kind: TokenKind) -> Result<Token> {
191        let token = self.peek();
192
193        debug!("Expected token: {:?}, found: {:?}", kind, token.kind);
194        if token.kind == kind {
195            Ok(self.consume())
196        } else {
197            Err(ExpectedToken(
198                kind.to_string(),
199                format!("Expected token of kind: {}", kind),
200                token.span.clone(),
201            )
202            .into())
203        }
204    }
205}