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}