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                            if 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                                            if let Some(Token::Identifier(inner_ident)) = self.tokens.get(self.pos + 1) {
130                                                if inner_ident == "endif" {
131                                                    break;
132                                                }
133                                            }
134                                        }
135                                        if let Some(Token::Text(text)) = self.peek() {
136                                            else_nodes.push(Node::Text(text));
137                                            self.advance();
138                                        } else if let Some(Token::VariableStart) = self.peek() {
139                                            self.advance();
140                                            let expr = self.parse_expr()?;
141                                            self.expect(Token::VariableEnd)?;
142                                            else_nodes.push(Node::Variable(expr));
143                                        } else if let Some(Token::BlockStart) = self.peek() {
144                                            self.advance();
145                                            else_nodes.push(self.parse_block()?);
146                                        }
147                                    }
148                                    else_branch = Some(else_nodes);
149                                    continue;
150                                } else if ident == "endif" {
151                                    self.advance(); // consume BlockStart
152                                    self.advance(); // consume "endif"
153                                    self.expect(Token::BlockEnd)?;
154                                    break;
155                                }
156                            }
157                        }
158
159                        if let Some(Token::Text(text)) = self.peek() {
160                            then_branch.push(Node::Text(text));
161                            self.advance();
162                        } else if let Some(Token::VariableStart) = self.peek() {
163                            self.advance();
164                            let expr = self.parse_expr()?;
165                            self.expect(Token::VariableEnd)?;
166                            then_branch.push(Node::Variable(expr));
167                        } else if let Some(Token::BlockStart) = self.peek() {
168                            self.advance();
169                            then_branch.push(self.parse_block()?);
170                        }
171                    }
172
173                    Ok(Node::If {
174                        condition,
175                        then_branch,
176                        else_branch,
177                    })
178                }
179                "for" => {
180                    self.advance(); // consume "for"
181                    let item = if let Some(Token::Identifier(name)) = self.advance() {
182                        name
183                    } else {
184                        return Err("Expected item identifier in for loop".to_string());
185                    };
186
187                    if let Some(Token::Identifier(keyword_in)) = self.advance() {
188                        if keyword_in != "in" {
189                            return Err(format!("Expected 'in', found '{}'", keyword_in));
190                        }
191                    } else {
192                        return Err("Expected 'in' keyword in for loop".to_string());
193                    }
194
195                    let iterator = self.parse_expr()?;
196                    self.expect(Token::BlockEnd)?;
197
198                    let mut body = Vec::new();
199                    while self.pos < self.tokens.len() {
200                        if let Some(Token::BlockStart) = self.peek() {
201                            if let Some(Token::Identifier(ident)) = self.tokens.get(self.pos + 1) {
202                                if ident == "endfor" {
203                                    self.advance(); // consume BlockStart
204                                    self.advance(); // consume "endfor"
205                                    self.expect(Token::BlockEnd)?;
206                                    break;
207                                }
208                            }
209                        }
210
211                        if let Some(Token::Text(text)) = self.peek() {
212                            body.push(Node::Text(text));
213                            self.advance();
214                        } else if let Some(Token::VariableStart) = self.peek() {
215                            self.advance();
216                            let expr = self.parse_expr()?;
217                            self.expect(Token::VariableEnd)?;
218                            body.push(Node::Variable(expr));
219                        } else if let Some(Token::BlockStart) = self.peek() {
220                            self.advance();
221                            body.push(self.parse_block()?);
222                        }
223                    }
224
225                    Ok(Node::For {
226                        item,
227                        iterator,
228                        body,
229                    })
230                }
231                other => Err(format!("Unknown block keyword: {}", other)),
232            }
233        } else {
234            Err("Expected keyword at start of block statement".to_string())
235        }
236    }
237
238    fn parse_expr(&mut self) -> Result<Expr, String> {
239        let mut left = self.parse_comparison_expr()?;
240
241        while let Some(Token::Operator(op)) = self.peek() {
242            if op == "|" {
243                self.advance(); // consume "|"
244                if let Some(Token::Identifier(filter_name)) = self.advance() {
245                    let mut args = Vec::new();
246                    if let Some(Token::Operator(paren)) = self.peek() {
247                        if paren == "(" {
248                            self.advance(); // consume "("
249                            while self.pos < self.tokens.len() {
250                                if let Some(Token::Operator(close_paren)) = self.peek() {
251                                    if close_paren == ")" {
252                                        self.advance(); // consume ")"
253                                        break;
254                                    }
255                                }
256                                args.push(self.parse_expr()?);
257                                if let Some(Token::Comma) = self.peek() {
258                                    self.advance();
259                                }
260                            }
261                        }
262                    }
263                    left = Expr::Filter {
264                        expr: Box::new(left),
265                        filter_name,
266                        args,
267                    };
268                } else {
269                    return Err("Expected filter name after '|'".to_string());
270                }
271            } else {
272                break;
273            }
274        }
275
276        Ok(left)
277    }
278
279    fn parse_comparison_expr(&mut self) -> Result<Expr, String> {
280        let left = self.parse_primary_expr()?;
281
282        if let Some(Token::Operator(op)) = self.peek() {
283            if op == "==" || op == "!=" || op == "<" || op == ">" || op == "<=" || op == ">=" {
284                self.advance(); // consume operator
285                let right = self.parse_primary_expr()?;
286                return Ok(Expr::Comparison {
287                    left: Box::new(left),
288                    op,
289                    right: Box::new(right),
290                });
291            }
292        }
293
294        Ok(left)
295    }
296
297    fn parse_primary_expr(&mut self) -> Result<Expr, String> {
298        if let Some(token) = self.peek() {
299            match token {
300                Token::StringLiteral(s) => {
301                    self.advance();
302                    Ok(Expr::StringLiteral(s))
303                }
304                Token::NumberLiteral(n) => {
305                    self.advance();
306                    Ok(Expr::NumberLiteral(n))
307                }
308                Token::Identifier(s) if s == "true" => {
309                    self.advance();
310                    Ok(Expr::BooleanLiteral(true))
311                }
312                Token::Identifier(s) if s == "false" => {
313                    self.advance();
314                    Ok(Expr::BooleanLiteral(false))
315                }
316                Token::Identifier(first_name) => {
317                    self.advance();
318                    let mut path = vec![first_name];
319                    while let Some(Token::Dot) = self.peek() {
320                        self.advance(); // consume "."
321                        if let Some(Token::Identifier(field)) = self.advance() {
322                            path.push(field);
323                        } else {
324                            return Err("Expected identifier after '.'".to_string());
325                        }
326                    }
327                    Ok(Expr::Path(path))
328                }
329                other => Err(format!("Expected primary expression, found {:?}", other)),
330            }
331        } else {
332            Err("Expected primary expression, reached EOF".to_string())
333        }
334    }
335}