Skip to main content

rustbasic_core/template/
parser.rs

1use super::lexer::Token;
2
3#[derive(Debug, Clone, PartialEq)]
4pub enum Expr {
5    Path(Vec<String>),
6    StringLiteral(String),
7    NumberLiteral(f64),
8    BooleanLiteral(bool),
9    Comparison {
10        left: Box<Expr>,
11        op: String,
12        right: Box<Expr>,
13    },
14    Filter {
15        expr: Box<Expr>,
16        filter_name: String,
17        args: Vec<Expr>,
18    },
19}
20
21#[derive(Debug, Clone, PartialEq)]
22pub enum Node {
23    Text(String),
24    Variable(Expr),
25    If {
26        condition: Expr,
27        then_branch: Vec<Node>,
28        else_branch: Option<Vec<Node>>,
29    },
30    For {
31        item: String,
32        iterator: Expr,
33        body: Vec<Node>,
34    },
35}
36
37pub struct Parser {
38    tokens: Vec<Token>,
39    pos: usize,
40}
41
42impl Parser {
43    pub fn new(tokens: Vec<Token>) -> Self {
44        Self { tokens, pos: 0 }
45    }
46
47    fn peek(&self) -> Option<Token> {
48        if self.pos < self.tokens.len() {
49            Some(self.tokens[self.pos].clone())
50        } else {
51            None
52        }
53    }
54
55    fn advance(&mut self) -> Option<Token> {
56        if self.pos < self.tokens.len() {
57            let t = &self.tokens[self.pos];
58            self.pos += 1;
59            Some(t.clone())
60        } else {
61            None
62        }
63    }
64
65    fn expect(&mut self, token: Token) -> Result<(), String> {
66        if let Some(t) = self.peek() {
67            if t == token {
68                self.advance();
69                Ok(())
70            } else {
71                Err(format!("Expected token {:?}, found {:?}", token, t))
72            }
73        } else {
74            Err(format!("Expected token {:?}, reached EOF", token))
75        }
76    }
77
78    pub fn parse(&mut self) -> Result<Vec<Node>, String> {
79        let mut nodes = Vec::new();
80        while self.pos < self.tokens.len() {
81            if let Some(token) = self.peek() {
82                match token {
83                    Token::Text(text) => {
84                        nodes.push(Node::Text(text));
85                        self.advance();
86                    }
87                    Token::VariableStart => {
88                        self.advance(); // consume {{
89                        let expr = self.parse_expr()?;
90                        self.expect(Token::VariableEnd)?;
91                        nodes.push(Node::Variable(expr));
92                    }
93                    Token::BlockStart => {
94                        self.advance(); // consume {%
95                        let node = self.parse_block()?;
96                        nodes.push(node);
97                    }
98                    _ => {
99                        return Err(format!("Unexpected token in template body: {:?}", token));
100                    }
101                }
102            }
103        }
104        Ok(nodes)
105    }
106
107    fn parse_block(&mut self) -> Result<Node, String> {
108        if let Some(Token::Identifier(keyword)) = self.peek() {
109            match keyword.as_str() {
110                "if" => {
111                    self.advance(); // consume "if"
112                    let condition = self.parse_expr()?;
113                    self.expect(Token::BlockEnd)?;
114
115                    let mut then_branch = Vec::new();
116                    let mut else_branch = None;
117
118                    while self.pos < self.tokens.len() {
119                        if let Some(Token::BlockStart) = self.peek()
120                            && let Some(Token::Identifier(ident)) = self.tokens.get(self.pos + 1) {
121                                if ident == "else" {
122                                    self.advance(); // consume BlockStart
123                                    self.advance(); // consume "else"
124                                    self.expect(Token::BlockEnd)?;
125
126                                    let mut else_nodes = Vec::new();
127                                    while self.pos < self.tokens.len() {
128                                        if let Some(Token::BlockStart) = self.peek()
129                                            && let Some(Token::Identifier(inner_ident)) = self.tokens.get(self.pos + 1)
130                                                && inner_ident == "endif" {
131                                                    break;
132                                                }
133                                        if let Some(Token::Text(text)) = self.peek() {
134                                            else_nodes.push(Node::Text(text));
135                                            self.advance();
136                                        } else if let Some(Token::VariableStart) = self.peek() {
137                                            self.advance();
138                                            let expr = self.parse_expr()?;
139                                            self.expect(Token::VariableEnd)?;
140                                            else_nodes.push(Node::Variable(expr));
141                                        } else if let Some(Token::BlockStart) = self.peek() {
142                                            self.advance();
143                                            else_nodes.push(self.parse_block()?);
144                                        }
145                                    }
146                                    else_branch = Some(else_nodes);
147                                    continue;
148                                } else if ident == "endif" {
149                                    self.advance(); // consume BlockStart
150                                    self.advance(); // consume "endif"
151                                    self.expect(Token::BlockEnd)?;
152                                    break;
153                                }
154                            }
155
156                        if let Some(Token::Text(text)) = self.peek() {
157                            then_branch.push(Node::Text(text));
158                            self.advance();
159                        } else if let Some(Token::VariableStart) = self.peek() {
160                            self.advance();
161                            let expr = self.parse_expr()?;
162                            self.expect(Token::VariableEnd)?;
163                            then_branch.push(Node::Variable(expr));
164                        } else if let Some(Token::BlockStart) = self.peek() {
165                            self.advance();
166                            then_branch.push(self.parse_block()?);
167                        }
168                    }
169
170                    Ok(Node::If {
171                        condition,
172                        then_branch,
173                        else_branch,
174                    })
175                }
176                "for" => {
177                    self.advance(); // consume "for"
178                    let item = if let Some(Token::Identifier(name)) = self.advance() {
179                        name
180                    } else {
181                        return Err("Expected item identifier in for loop".to_string());
182                    };
183
184                    if let Some(Token::Identifier(keyword_in)) = self.advance() {
185                        if keyword_in != "in" {
186                            return Err(format!("Expected 'in', found '{}'", keyword_in));
187                        }
188                    } else {
189                        return Err("Expected 'in' keyword in for loop".to_string());
190                    }
191
192                    let iterator = self.parse_expr()?;
193                    self.expect(Token::BlockEnd)?;
194
195                    let mut body = Vec::new();
196                    while self.pos < self.tokens.len() {
197                        if let Some(Token::BlockStart) = self.peek()
198                            && let Some(Token::Identifier(ident)) = self.tokens.get(self.pos + 1)
199                                && ident == "endfor" {
200                                    self.advance(); // consume BlockStart
201                                    self.advance(); // consume "endfor"
202                                    self.expect(Token::BlockEnd)?;
203                                    break;
204                                }
205
206                        if let Some(Token::Text(text)) = self.peek() {
207                            body.push(Node::Text(text));
208                            self.advance();
209                        } else if let Some(Token::VariableStart) = self.peek() {
210                            self.advance();
211                            let expr = self.parse_expr()?;
212                            self.expect(Token::VariableEnd)?;
213                            body.push(Node::Variable(expr));
214                        } else if let Some(Token::BlockStart) = self.peek() {
215                            self.advance();
216                            body.push(self.parse_block()?);
217                        }
218                    }
219
220                    Ok(Node::For {
221                        item,
222                        iterator,
223                        body,
224                    })
225                }
226                other => Err(format!("Unknown block keyword: {}", other)),
227            }
228        } else {
229            Err("Expected keyword at start of block statement".to_string())
230        }
231    }
232
233    fn parse_expr(&mut self) -> Result<Expr, String> {
234        let mut left = self.parse_comparison_expr()?;
235
236        while let Some(Token::Operator(op)) = self.peek() {
237            if op == "|" {
238                self.advance(); // consume "|"
239                if let Some(Token::Identifier(filter_name)) = self.advance() {
240                    let mut args = Vec::new();
241                    if let Some(Token::Operator(paren)) = self.peek()
242                        && paren == "(" {
243                            self.advance(); // consume "("
244                            while self.pos < self.tokens.len() {
245                                if let Some(Token::Operator(close_paren)) = self.peek()
246                                    && close_paren == ")" {
247                                        self.advance(); // consume ")"
248                                        break;
249                                    }
250                                args.push(self.parse_expr()?);
251                                if let Some(Token::Comma) = self.peek() {
252                                    self.advance();
253                                }
254                            }
255                        }
256                    left = Expr::Filter {
257                        expr: Box::new(left),
258                        filter_name,
259                        args,
260                    };
261                } else {
262                    return Err("Expected filter name after '|'".to_string());
263                }
264            } else {
265                break;
266            }
267        }
268
269        Ok(left)
270    }
271
272    fn parse_comparison_expr(&mut self) -> Result<Expr, String> {
273        let left = self.parse_primary_expr()?;
274
275        if let Some(Token::Operator(op)) = self.peek()
276            && (op == "==" || op == "!=" || op == "<" || op == ">" || op == "<=" || op == ">=") {
277                self.advance(); // consume operator
278                let right = self.parse_primary_expr()?;
279                return Ok(Expr::Comparison {
280                    left: Box::new(left),
281                    op,
282                    right: Box::new(right),
283                });
284            }
285
286        Ok(left)
287    }
288
289    fn parse_primary_expr(&mut self) -> Result<Expr, String> {
290        if let Some(token) = self.peek() {
291            match token {
292                Token::StringLiteral(s) => {
293                    self.advance();
294                    Ok(Expr::StringLiteral(s))
295                }
296                Token::NumberLiteral(n) => {
297                    self.advance();
298                    Ok(Expr::NumberLiteral(n))
299                }
300                Token::Identifier(s) if s == "true" => {
301                    self.advance();
302                    Ok(Expr::BooleanLiteral(true))
303                }
304                Token::Identifier(s) if s == "false" => {
305                    self.advance();
306                    Ok(Expr::BooleanLiteral(false))
307                }
308                Token::Identifier(first_name) => {
309                    self.advance();
310                    let mut path = vec![first_name];
311                    while let Some(Token::Dot) = self.peek() {
312                        self.advance(); // consume "."
313                        if let Some(Token::Identifier(field)) = self.advance() {
314                            path.push(field);
315                        } else {
316                            return Err("Expected identifier after '.'".to_string());
317                        }
318                    }
319                    Ok(Expr::Path(path))
320                }
321                other => Err(format!("Expected primary expression, found {:?}", other)),
322            }
323        } else {
324            Err("Expected primary expression, reached EOF".to_string())
325        }
326    }
327}