1use std::{error::Error, fmt::Display};
4
5use crate::tokenizer::{Token, TokenTag, keyword::Keyword};
6
7use super::Parser;
8
9#[derive(Debug, PartialEq, Clone)]
11pub enum Expr<'src> {
12 Print(Box<Expr<'src>>),
14 Variable(&'src str),
16 Assignment(&'src str, Box<Expr<'src>>),
18 Binary {
20 op: BinaryOp,
22 left: Box<Expr<'src>>,
24 right: Box<Expr<'src>>,
26 },
27 Unary {
29 op: UnaryOp,
31 node: Box<Expr<'src>>,
33 },
34 Grouping(Box<Expr<'src>>),
36 Literal(Literal<'src>),
38}
39
40#[derive(Debug, PartialEq, Clone, Copy)]
42pub enum Literal<'src> {
43 String(&'src str),
45 Number(f64),
47 Bool(bool),
49 Null,
51}
52
53impl Display for Literal<'_> {
54 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55 match self {
56 Self::Bool(b) => write!(f, "{b}"),
57 Self::String(s) => write!(f, "{s}"),
58 Self::Number(n) => write!(f, "{n}"),
59 Self::Null => write!(f, "null"),
60 }
61 }
62}
63
64#[derive(Debug, PartialEq, Clone, Copy)]
66pub enum UnaryOp {
67 Neg,
69 Not,
71}
72
73#[derive(Debug, PartialEq, Clone, Copy)]
75pub enum BinaryOp {
76 Add,
78 Sub,
80 Mul,
82 Div,
84 Eq,
86 Neq,
88 Gt,
90 Gte,
92 Lt,
94 Lte,
96}
97
98#[derive(Clone, PartialEq, Debug, Default)]
100pub struct ParseError {
101 pub message: String,
103 pub line: usize,
105 pub col: usize,
107 pub len: usize,
109}
110
111impl Display for ParseError {
112 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113 write!(f, "parser :(")
114 }
115}
116
117impl Error for ParseError {}
118
119impl<'src> Parser<'src> {
120 pub fn parse(&mut self) -> Result<Vec<Expr<'src>>, ParseError> {
122 let mut statements = vec![];
123
124 while self.peek() != TokenTag::EOF {
125 statements.push(self.statement()?);
126 }
127
128 Ok(statements)
129 }
130
131 pub fn statement(&mut self) -> Result<Expr<'src>, ParseError> {
133 match self.peek() {
134 TokenTag::Keyword(Keyword::Print) => {
135 self.advance();
136 self.consume_open_paren_if_necessary()?;
137 let next = self.expression()?;
138 self.consume_close_paren_if_necessary()?;
139 self.consume_end()?;
140 Ok(Expr::Print(Box::new(next)))
141 }
142 _ => {
143 let res = self.expression()?;
144 self.consume_end()?;
145 Ok(res)
146 }
147 }
148 }
149
150 fn expression(&mut self) -> Result<Expr<'src>, ParseError> {
152 match self.peek() {
153 TokenTag::Keyword(Keyword::VariableDeclaration) => {
154 self.advance();
155 if let TokenTag::Identifier(name) = self.advance() {
156 self.consume(&TokenTag::Keyword(Keyword::Equal))?;
157 let assignment = self.equality()?;
158
159 Ok(Expr::Assignment(name, Box::new(assignment)))
160 } else {
161 let Token {
162 tag,
163 line,
164 col,
165 len,
166 } = self.peek_token();
167 Err(ParseError {
168 message: format!("Expected a variable expression, found `{tag:?}`"),
169 line,
170 col,
171 len,
172 })
173 }
174 }
175
176 _ => self.equality(),
177 }
178 }
179
180 fn equality(&mut self) -> Result<Expr<'src>, ParseError> {
182 let mut expr = self.comparison()?;
183
184 while matches!(
185 self.peek(),
186 TokenTag::Keyword(Keyword::EqualEqual) | TokenTag::Keyword(Keyword::BangEqual)
187 ) {
188 let op = match self.advance() {
189 TokenTag::Keyword(Keyword::EqualEqual) => BinaryOp::Eq,
190 TokenTag::Keyword(Keyword::BangEqual) => BinaryOp::Neq,
191 _ => unreachable!(),
192 };
193
194 let right = Box::new(self.comparison()?);
195
196 expr = Expr::Binary {
197 op,
198 left: Box::new(expr),
199 right,
200 }
201 }
202
203 Ok(expr)
204 }
205
206 fn comparison(&mut self) -> Result<Expr<'src>, ParseError> {
208 let mut expr = self.term()?;
209
210 while matches!(
211 self.peek(),
212 TokenTag::Keyword(Keyword::Greater)
213 | TokenTag::Keyword(Keyword::GreaterEqual)
214 | TokenTag::Keyword(Keyword::Less)
215 | TokenTag::Keyword(Keyword::LessEqual)
216 ) {
217 let op = match self.advance() {
218 TokenTag::Keyword(Keyword::Greater) => BinaryOp::Gt,
219 TokenTag::Keyword(Keyword::GreaterEqual) => BinaryOp::Gte,
220 TokenTag::Keyword(Keyword::Less) => BinaryOp::Lt,
221 TokenTag::Keyword(Keyword::LessEqual) => BinaryOp::Lte,
222 _ => unreachable!(),
223 };
224
225 let right = Box::new(self.term()?);
226
227 expr = Expr::Binary {
228 op,
229 left: Box::new(expr),
230 right,
231 }
232 }
233
234 Ok(expr)
235 }
236
237 fn term(&mut self) -> Result<Expr<'src>, ParseError> {
239 let mut expr = self.factor()?;
240
241 while matches!(self.peek(), TokenTag::Plus | TokenTag::Minus) {
242 let op = match self.advance() {
243 TokenTag::Plus => BinaryOp::Add,
244 TokenTag::Minus => BinaryOp::Sub,
245 _ => unreachable!(),
246 };
247
248 let right = Box::new(self.factor()?);
249
250 expr = Expr::Binary {
251 op,
252 left: Box::new(expr),
253 right,
254 };
255 }
256
257 Ok(expr)
258 }
259
260 fn factor(&mut self) -> Result<Expr<'src>, ParseError> {
262 let mut expr = self.unary()?;
263
264 while matches!(self.peek(), TokenTag::Star | TokenTag::Slash) {
265 let op = match self.advance() {
266 TokenTag::Star => BinaryOp::Mul,
267 TokenTag::Slash => BinaryOp::Div,
268 _ => unreachable!(),
269 };
270
271 let right = Box::new(self.unary()?);
272
273 expr = Expr::Binary {
274 op,
275 left: Box::new(expr),
276 right,
277 };
278 }
279
280 Ok(expr)
281 }
282
283 fn unary(&mut self) -> Result<Expr<'src>, ParseError> {
285 if matches!(
286 self.peek(),
287 TokenTag::Keyword(Keyword::Bang) | TokenTag::Minus
288 ) {
289 let op = match self.advance() {
290 TokenTag::Keyword(Keyword::Bang) => UnaryOp::Not,
291 TokenTag::Minus => UnaryOp::Neg,
292 _ => unreachable!(),
293 };
294
295 let unary = self.unary()?;
296
297 Ok(Expr::Unary {
298 op,
299 node: Box::new(unary),
300 })
301 } else {
302 self.primary()
303 }
304 }
305
306 fn primary(&mut self) -> Result<Expr<'src>, ParseError> {
308 let advance = self.advance();
309 let prim = match advance {
310 TokenTag::Number(n) => Expr::Literal(Literal::Number(n)),
311 TokenTag::Keyword(Keyword::True) => Expr::Literal(Literal::Bool(true)),
312 TokenTag::Keyword(Keyword::False) => Expr::Literal(Literal::Bool(false)),
313 TokenTag::Keyword(Keyword::EmptyValue) => Expr::Literal(Literal::Null),
314 TokenTag::Identifier(ident) => Expr::Variable(ident),
315 TokenTag::String(s) => Expr::Literal(Literal::String(s)),
316 TokenTag::OpenParen => {
317 let expr = self.expression()?;
318 self.consume(&TokenTag::CloseParen)?;
319
320 Expr::Grouping(Box::new(expr))
321 }
322 _ => {
323 let Token {
324 tag,
325 line,
326 col,
327 len,
328 } = self.peek_token();
329 return Err(ParseError {
330 message: format!("Expected primary statement, found `{tag:?}`"),
331 line,
332 col,
333 len,
334 });
335 }
336 };
337
338 Ok(prim)
339 }
340}
341
342#[cfg(test)]
371mod tests {
372 use rand::SeedableRng;
373 use rand_chacha::ChaCha8Rng;
374
375 use crate::{
376 parser::{
377 Parser,
378 ast::{BinaryOp, Expr, Literal, UnaryOp},
379 },
380 tokenizer::Tokenizable,
381 };
382
383 #[test]
384 fn variable_assignment() {
385 let mut rng = ChaCha8Rng::seed_from_u64(42);
386 let stream = "$ i = 0 .".tokenize(&mut rng).expect("Valid tokenization");
387 let mut parser = Parser::from_rng(&mut rng).with_tokens(&stream);
388
389 let ast = &parser.parse().expect("Parse to AST")[0];
390
391 assert_eq!(
392 ast,
393 &Expr::Assignment("i", Box::new(Expr::Literal(Literal::Number(0.0))))
394 )
395 }
396
397 #[test]
398 fn print_statement() {
399 let mut rng = ChaCha8Rng::seed_from_u64(42);
400 let stream = "fmt.Println 42 ."
401 .tokenize(&mut rng)
402 .expect("Valid tokenization");
403 let mut parser = Parser::from_rng(&mut rng).with_tokens(&stream);
404
405 let ast = &parser.parse().expect("Parse to AST")[0];
406
407 assert_eq!(
408 ast,
409 &Expr::Print(Box::new(Expr::Literal(Literal::Number(42.0))))
410 );
411 }
412
413 #[test]
414 fn binary_operations() {
415 let mut rng = ChaCha8Rng::seed_from_u64(42);
416 let stream = "1 + 2 * 3 ."
417 .tokenize(&mut rng)
418 .expect("Valid tokenization");
419 let mut parser = Parser::from_rng(&mut rng).with_tokens(&stream);
420
421 let ast = &parser.parse().expect("Parse to AST")[0];
422
423 assert_eq!(
424 ast,
425 &Expr::Binary {
426 op: BinaryOp::Add,
427 left: Box::new(Expr::Literal(Literal::Number(1.0))),
428 right: Box::new(Expr::Binary {
429 op: BinaryOp::Mul,
430 left: Box::new(Expr::Literal(Literal::Number(2.0))),
431 right: Box::new(Expr::Literal(Literal::Number(3.0))),
432 }),
433 }
434 );
435 }
436
437 #[test]
438 fn comparison_operations() {
439 let mut rng = ChaCha8Rng::seed_from_u64(42);
440 let stream = "1 < 2 .".tokenize(&mut rng).expect("Valid tokenization");
441 let mut parser = Parser::from_rng(&mut rng).with_tokens(&stream);
442
443 let ast = &parser.parse().expect("Parse to AST")[0];
444
445 assert_eq!(
446 ast,
447 &Expr::Binary {
448 op: BinaryOp::Lt,
449 left: Box::new(Expr::Literal(Literal::Number(1.0))),
450 right: Box::new(Expr::Literal(Literal::Number(2.0))),
451 }
452 );
453 }
454
455 #[test]
456 fn equality_operations() {
457 let mut rng = ChaCha8Rng::seed_from_u64(42);
458 let stream = "1 equals 1 ."
459 .tokenize(&mut rng)
460 .expect("Valid tokenization");
461 let mut parser = Parser::from_rng(&mut rng).with_tokens(&stream);
462
463 let ast = &parser.parse().expect("Parse to AST")[0];
464
465 assert_eq!(
466 ast,
467 &Expr::Binary {
468 op: BinaryOp::Eq,
469 left: Box::new(Expr::Literal(Literal::Number(1.0))),
470 right: Box::new(Expr::Literal(Literal::Number(1.0))),
471 }
472 );
473 }
474
475 #[test]
476 fn unary_operations() {
477 let mut rng = ChaCha8Rng::seed_from_u64(42);
478 let stream = "not true .".tokenize(&mut rng).expect("Valid tokenization");
479 let mut parser = Parser::from_rng(&mut rng).with_tokens(&stream);
480
481 let ast = &parser.parse().expect("Parse to AST")[0];
482
483 assert_eq!(
484 ast,
485 &Expr::Unary {
486 op: UnaryOp::Not,
487 node: Box::new(Expr::Literal(Literal::Bool(true))),
488 }
489 );
490 }
491
492 #[test]
493 fn grouping() {
494 let mut rng = ChaCha8Rng::seed_from_u64(42);
495 let stream = "(1 + 2) * 3 ."
496 .tokenize(&mut rng)
497 .expect("Valid tokenization");
498 let mut parser = Parser::from_rng(&mut rng).with_tokens(&stream);
499
500 let ast = &parser.parse().expect("Parse to AST")[0];
501 let expected = Expr::Binary {
502 op: BinaryOp::Mul,
503 left: Box::new(Expr::Grouping(Box::new(Expr::Binary {
504 op: BinaryOp::Add,
505 left: Box::new(Expr::Literal(Literal::Number(1.0))),
506 right: Box::new(Expr::Literal(Literal::Number(2.0))),
507 }))),
508 right: Box::new(Expr::Literal(Literal::Number(3.0))),
509 };
510
511 assert_eq!(ast, &expected);
512 }
513
514 #[test]
515 fn variable_reference() {
516 let mut rng = ChaCha8Rng::seed_from_u64(42);
517 let stream = "x .".tokenize(&mut rng).expect("Valid tokenization");
518 let mut parser = Parser::from_rng(&mut rng).with_tokens(&stream);
519
520 let ast = &parser.parse().expect("Parse to AST")[0];
521
522 assert_eq!(ast, &Expr::Variable("x"));
523 }
524
525 #[test]
526 fn complex_expression() {
527 let mut rng = ChaCha8Rng::seed_from_u64(42);
528 let stream = "$ x = 5 * (3 + 2) . fmt.Println x < 10 ."
529 .tokenize(&mut rng)
530 .expect("Valid tokenization");
531 let mut parser = Parser::from_rng(&mut rng).with_tokens(&stream);
532
533 let ast = parser.parse().expect("Parse to AST");
534
535 assert_eq!(
536 ast,
537 vec![
538 Expr::Assignment(
539 "x",
540 Box::new(Expr::Binary {
541 op: BinaryOp::Mul,
542 left: Box::new(Expr::Literal(Literal::Number(5.0))),
543 right: Box::new(Expr::Grouping(Box::new(Expr::Binary {
544 op: BinaryOp::Add,
545 left: Box::new(Expr::Literal(Literal::Number(3.0))),
546 right: Box::new(Expr::Literal(Literal::Number(2.0))),
547 }))),
548 }),
549 ),
550 Expr::Print(Box::new(Expr::Binary {
551 op: BinaryOp::Lt,
552 left: Box::new(Expr::Variable("x")),
553 right: Box::new(Expr::Literal(Literal::Number(10.0))),
554 })),
555 ]
556 );
557 }
558}