Skip to main content

bop/
parser.rs

1#[cfg(not(feature = "std"))]
2use alloc::{boxed::Box, format, string::{String, ToString}, vec::Vec};
3
4use crate::error::BopError;
5use crate::lexer::{SpannedToken, StringPart, Token};
6
7// ─── AST ───────────────────────────────────────────────────────────────────
8
9#[derive(Debug, Clone)]
10pub struct Expr {
11    pub kind: ExprKind,
12    pub line: u32,
13}
14
15#[derive(Debug, Clone)]
16pub enum ExprKind {
17    Number(f64),
18    Str(String),
19    StringInterp(Vec<StringPart>),
20    Bool(bool),
21    None,
22    Ident(String),
23    BinaryOp {
24        left: Box<Expr>,
25        op: BinOp,
26        right: Box<Expr>,
27    },
28    UnaryOp {
29        op: UnaryOp,
30        expr: Box<Expr>,
31    },
32    Call {
33        callee: Box<Expr>,
34        args: Vec<Expr>,
35    },
36    MethodCall {
37        object: Box<Expr>,
38        method: String,
39        args: Vec<Expr>,
40    },
41    Index {
42        object: Box<Expr>,
43        index: Box<Expr>,
44    },
45    Array(Vec<Expr>),
46    Dict(Vec<(String, Expr)>),
47    IfExpr {
48        condition: Box<Expr>,
49        then_expr: Box<Expr>,
50        else_expr: Box<Expr>,
51    },
52}
53
54#[derive(Debug, Clone, Copy)]
55pub enum BinOp {
56    Add,
57    Sub,
58    Mul,
59    Div,
60    Mod,
61    Eq,
62    NotEq,
63    Lt,
64    Gt,
65    LtEq,
66    GtEq,
67    And,
68    Or,
69}
70
71#[derive(Debug, Clone, Copy)]
72pub enum UnaryOp {
73    Neg,
74    Not,
75}
76
77#[derive(Debug, Clone)]
78pub struct Stmt {
79    pub kind: StmtKind,
80    pub line: u32,
81}
82
83#[derive(Debug, Clone)]
84pub enum StmtKind {
85    Let {
86        name: String,
87        value: Expr,
88    },
89    Assign {
90        target: AssignTarget,
91        op: AssignOp,
92        value: Expr,
93    },
94    If {
95        condition: Expr,
96        body: Vec<Stmt>,
97        else_ifs: Vec<(Expr, Vec<Stmt>)>,
98        else_body: Option<Vec<Stmt>>,
99    },
100    While {
101        condition: Expr,
102        body: Vec<Stmt>,
103    },
104    Repeat {
105        count: Expr,
106        body: Vec<Stmt>,
107    },
108    ForIn {
109        var: String,
110        iterable: Expr,
111        body: Vec<Stmt>,
112    },
113    FnDecl {
114        name: String,
115        params: Vec<String>,
116        body: Vec<Stmt>,
117    },
118    Return {
119        value: Option<Expr>,
120    },
121    Break,
122    Continue,
123    ExprStmt(Expr),
124}
125
126#[derive(Debug, Clone)]
127pub enum AssignTarget {
128    Variable(String),
129    Index { object: Expr, index: Expr },
130}
131
132#[derive(Debug, Clone, Copy)]
133pub enum AssignOp {
134    Eq,
135    AddEq,
136    SubEq,
137    MulEq,
138    DivEq,
139    ModEq,
140}
141
142// ─── Parser ────────────────────────────────────────────────────────────────
143
144const MAX_PARSE_DEPTH: usize = 128;
145
146pub fn parse(tokens: Vec<SpannedToken>) -> Result<Vec<Stmt>, BopError> {
147    let mut parser = Parser::new(tokens);
148    parser.parse_program()
149}
150
151struct Parser {
152    tokens: Vec<SpannedToken>,
153    pos: usize,
154    depth: usize,
155}
156
157impl Parser {
158    fn new(tokens: Vec<SpannedToken>) -> Self {
159        Self {
160            tokens,
161            pos: 0,
162            depth: 0,
163        }
164    }
165
166    fn enter(&mut self) -> Result<(), BopError> {
167        self.depth += 1;
168        if self.depth > MAX_PARSE_DEPTH {
169            Err(self.error(self.peek_line(), "Code is nested too deeply"))
170        } else {
171            Ok(())
172        }
173    }
174
175    fn leave(&mut self) {
176        self.depth -= 1;
177    }
178
179    fn peek(&self) -> &Token {
180        self.tokens
181            .get(self.pos)
182            .map(|t| &t.token)
183            .unwrap_or(&Token::Eof)
184    }
185
186    fn peek_line(&self) -> u32 {
187        self.tokens.get(self.pos).map(|t| t.line).unwrap_or(0)
188    }
189
190    fn advance(&mut self) -> &Token {
191        let tok = self
192            .tokens
193            .get(self.pos)
194            .map(|t| &t.token)
195            .unwrap_or(&Token::Eof);
196        if self.pos < self.tokens.len() {
197            self.pos += 1;
198        }
199        tok
200    }
201
202    fn is_at_end(&self) -> bool {
203        matches!(self.peek(), Token::Eof)
204    }
205
206    fn expect(&mut self, expected: &Token) -> Result<u32, BopError> {
207        let line = self.peek_line();
208        if self.peek() == expected {
209            self.advance();
210            Ok(line)
211        } else {
212            Err(self.error(
213                line,
214                format!(
215                    "Expected `{}` but found `{}`",
216                    fmt_token(expected),
217                    fmt_token(self.peek())
218                ),
219            ))
220        }
221    }
222
223    fn expect_ident(&mut self) -> Result<(String, u32), BopError> {
224        let line = self.peek_line();
225        if let Token::Ident(name) = self.peek().clone() {
226            self.advance();
227            Ok((name, line))
228        } else {
229            Err(self.error(
230                line,
231                format!("Expected a name but found `{}`", fmt_token(self.peek())),
232            ))
233        }
234    }
235
236    fn skip_semicolons(&mut self) {
237        while matches!(self.peek(), Token::Semicolon) {
238            self.advance();
239        }
240    }
241
242    fn error(&self, line: u32, message: impl Into<String>) -> BopError {
243        BopError {
244            line: Some(line),
245            column: None,
246            message: message.into(),
247            friendly_hint: None,
248        }
249    }
250
251    // ─── Program & Blocks ──────────────────────────────────────────────
252
253    fn parse_program(&mut self) -> Result<Vec<Stmt>, BopError> {
254        let mut stmts = Vec::new();
255        self.skip_semicolons();
256        while !self.is_at_end() {
257            stmts.push(self.parse_statement()?);
258            self.skip_semicolons();
259        }
260        Ok(stmts)
261    }
262
263    fn parse_block(&mut self) -> Result<Vec<Stmt>, BopError> {
264        self.enter()?;
265        self.expect(&Token::LBrace)?;
266        let mut stmts = Vec::new();
267        self.skip_semicolons();
268        while !matches!(self.peek(), Token::RBrace | Token::Eof) {
269            stmts.push(self.parse_statement()?);
270            self.skip_semicolons();
271        }
272        self.expect(&Token::RBrace)?;
273        self.leave();
274        Ok(stmts)
275    }
276
277    // ─── Statements ────────────────────────────────────────────────────
278
279    fn parse_statement(&mut self) -> Result<Stmt, BopError> {
280        let line = self.peek_line();
281        match self.peek() {
282            Token::Let => self.parse_let(),
283            Token::If => self.parse_if_stmt(),
284            Token::While => self.parse_while(),
285            Token::For => self.parse_for(),
286            Token::Repeat => self.parse_repeat(),
287            Token::Fn => self.parse_fn_decl(),
288            Token::Return => self.parse_return(),
289            Token::Break => {
290                self.advance();
291                Ok(Stmt {
292                    kind: StmtKind::Break,
293                    line,
294                })
295            }
296            Token::Continue => {
297                self.advance();
298                Ok(Stmt {
299                    kind: StmtKind::Continue,
300                    line,
301                })
302            }
303            _ => self.parse_expr_or_assign(),
304        }
305    }
306
307    fn parse_let(&mut self) -> Result<Stmt, BopError> {
308        let line = self.peek_line();
309        self.advance(); // consume 'let'
310        let (name, _) = self.expect_ident()?;
311        self.expect(&Token::Eq)?;
312        let value = self.parse_expr()?;
313        Ok(Stmt {
314            kind: StmtKind::Let { name, value },
315            line,
316        })
317    }
318
319    fn parse_if_stmt(&mut self) -> Result<Stmt, BopError> {
320        let line = self.peek_line();
321        self.advance(); // consume 'if'
322        let condition = self.parse_expr()?;
323        let body = self.parse_block()?;
324
325        let mut else_ifs = Vec::new();
326        let mut else_body = None;
327
328        while matches!(self.peek(), Token::Else) {
329            self.advance(); // consume 'else'
330            if matches!(self.peek(), Token::If) {
331                self.advance(); // consume 'if'
332                let cond = self.parse_expr()?;
333                let block = self.parse_block()?;
334                else_ifs.push((cond, block));
335            } else {
336                else_body = Some(self.parse_block()?);
337                break;
338            }
339        }
340
341        Ok(Stmt {
342            kind: StmtKind::If {
343                condition,
344                body,
345                else_ifs,
346                else_body,
347            },
348            line,
349        })
350    }
351
352    fn parse_while(&mut self) -> Result<Stmt, BopError> {
353        let line = self.peek_line();
354        self.advance(); // consume 'while'
355        let condition = self.parse_expr()?;
356        let body = self.parse_block()?;
357        Ok(Stmt {
358            kind: StmtKind::While { condition, body },
359            line,
360        })
361    }
362
363    fn parse_for(&mut self) -> Result<Stmt, BopError> {
364        let line = self.peek_line();
365        self.advance(); // consume 'for'
366        let (var, _) = self.expect_ident()?;
367        self.expect(&Token::In)?;
368        let iterable = self.parse_expr()?;
369        let body = self.parse_block()?;
370        Ok(Stmt {
371            kind: StmtKind::ForIn {
372                var,
373                iterable,
374                body,
375            },
376            line,
377        })
378    }
379
380    fn parse_repeat(&mut self) -> Result<Stmt, BopError> {
381        let line = self.peek_line();
382        self.advance(); // consume 'repeat'
383        let count = self.parse_expr()?;
384        let body = self.parse_block()?;
385        Ok(Stmt {
386            kind: StmtKind::Repeat { count, body },
387            line,
388        })
389    }
390
391    fn parse_fn_decl(&mut self) -> Result<Stmt, BopError> {
392        let line = self.peek_line();
393        self.advance(); // consume 'fn'
394        let (name, _) = self.expect_ident()?;
395        self.expect(&Token::LParen)?;
396
397        let mut params = Vec::new();
398        if !matches!(self.peek(), Token::RParen) {
399            let (p, _) = self.expect_ident()?;
400            params.push(p);
401            while matches!(self.peek(), Token::Comma) {
402                self.advance();
403                let (p, _) = self.expect_ident()?;
404                params.push(p);
405            }
406        }
407        self.expect(&Token::RParen)?;
408        let body = self.parse_block()?;
409        Ok(Stmt {
410            kind: StmtKind::FnDecl { name, params, body },
411            line,
412        })
413    }
414
415    fn parse_return(&mut self) -> Result<Stmt, BopError> {
416        let line = self.peek_line();
417        self.advance(); // consume 'return'
418        let value = if matches!(self.peek(), Token::Semicolon | Token::RBrace | Token::Eof) {
419            None
420        } else {
421            Some(self.parse_expr()?)
422        };
423        Ok(Stmt {
424            kind: StmtKind::Return { value },
425            line,
426        })
427    }
428
429    fn parse_expr_or_assign(&mut self) -> Result<Stmt, BopError> {
430        let line = self.peek_line();
431        let expr = self.parse_expr()?;
432
433        let op = match self.peek() {
434            Token::Eq => Some(AssignOp::Eq),
435            Token::PlusEq => Some(AssignOp::AddEq),
436            Token::MinusEq => Some(AssignOp::SubEq),
437            Token::StarEq => Some(AssignOp::MulEq),
438            Token::SlashEq => Some(AssignOp::DivEq),
439            Token::PercentEq => Some(AssignOp::ModEq),
440            _ => None,
441        };
442
443        if let Some(op) = op {
444            self.advance(); // consume assignment operator
445            let target = expr_to_assign_target(expr, line)?;
446            let value = self.parse_expr()?;
447            Ok(Stmt {
448                kind: StmtKind::Assign { target, op, value },
449                line,
450            })
451        } else {
452            Ok(Stmt {
453                kind: StmtKind::ExprStmt(expr),
454                line,
455            })
456        }
457    }
458
459    // ─── Expressions ───────────────────────────────────────────────────
460
461    fn parse_expr(&mut self) -> Result<Expr, BopError> {
462        self.parse_or()
463    }
464
465    fn parse_or(&mut self) -> Result<Expr, BopError> {
466        let mut left = self.parse_and()?;
467        while matches!(self.peek(), Token::PipePipe) {
468            let line = self.peek_line();
469            self.advance();
470            let right = self.parse_and()?;
471            left = Expr {
472                kind: ExprKind::BinaryOp {
473                    left: Box::new(left),
474                    op: BinOp::Or,
475                    right: Box::new(right),
476                },
477                line,
478            };
479        }
480        Ok(left)
481    }
482
483    fn parse_and(&mut self) -> Result<Expr, BopError> {
484        let mut left = self.parse_equality()?;
485        while matches!(self.peek(), Token::AmpAmp) {
486            let line = self.peek_line();
487            self.advance();
488            let right = self.parse_equality()?;
489            left = Expr {
490                kind: ExprKind::BinaryOp {
491                    left: Box::new(left),
492                    op: BinOp::And,
493                    right: Box::new(right),
494                },
495                line,
496            };
497        }
498        Ok(left)
499    }
500
501    fn parse_equality(&mut self) -> Result<Expr, BopError> {
502        let mut left = self.parse_comparison()?;
503        while matches!(self.peek(), Token::EqEq | Token::BangEq) {
504            let line = self.peek_line();
505            let op = if matches!(self.peek(), Token::EqEq) {
506                BinOp::Eq
507            } else {
508                BinOp::NotEq
509            };
510            self.advance();
511            let right = self.parse_comparison()?;
512            left = Expr {
513                kind: ExprKind::BinaryOp {
514                    left: Box::new(left),
515                    op,
516                    right: Box::new(right),
517                },
518                line,
519            };
520        }
521        Ok(left)
522    }
523
524    fn parse_comparison(&mut self) -> Result<Expr, BopError> {
525        let mut left = self.parse_addition()?;
526        while matches!(
527            self.peek(),
528            Token::Lt | Token::Gt | Token::LtEq | Token::GtEq
529        ) {
530            let line = self.peek_line();
531            let op = match self.peek() {
532                Token::Lt => BinOp::Lt,
533                Token::Gt => BinOp::Gt,
534                Token::LtEq => BinOp::LtEq,
535                _ => BinOp::GtEq,
536            };
537            self.advance();
538            let right = self.parse_addition()?;
539            left = Expr {
540                kind: ExprKind::BinaryOp {
541                    left: Box::new(left),
542                    op,
543                    right: Box::new(right),
544                },
545                line,
546            };
547        }
548        Ok(left)
549    }
550
551    fn parse_addition(&mut self) -> Result<Expr, BopError> {
552        let mut left = self.parse_multiply()?;
553        while matches!(self.peek(), Token::Plus | Token::Minus) {
554            let line = self.peek_line();
555            let op = if matches!(self.peek(), Token::Plus) {
556                BinOp::Add
557            } else {
558                BinOp::Sub
559            };
560            self.advance();
561            let right = self.parse_multiply()?;
562            left = Expr {
563                kind: ExprKind::BinaryOp {
564                    left: Box::new(left),
565                    op,
566                    right: Box::new(right),
567                },
568                line,
569            };
570        }
571        Ok(left)
572    }
573
574    fn parse_multiply(&mut self) -> Result<Expr, BopError> {
575        let mut left = self.parse_unary()?;
576        while matches!(self.peek(), Token::Star | Token::Slash | Token::Percent) {
577            let line = self.peek_line();
578            let op = match self.peek() {
579                Token::Star => BinOp::Mul,
580                Token::Slash => BinOp::Div,
581                _ => BinOp::Mod,
582            };
583            self.advance();
584            let right = self.parse_unary()?;
585            left = Expr {
586                kind: ExprKind::BinaryOp {
587                    left: Box::new(left),
588                    op,
589                    right: Box::new(right),
590                },
591                line,
592            };
593        }
594        Ok(left)
595    }
596
597    fn parse_unary(&mut self) -> Result<Expr, BopError> {
598        self.enter()?;
599        let line = self.peek_line();
600        let result = match self.peek() {
601            Token::Bang => {
602                self.advance();
603                let expr = self.parse_unary()?;
604                Ok(Expr {
605                    kind: ExprKind::UnaryOp {
606                        op: UnaryOp::Not,
607                        expr: Box::new(expr),
608                    },
609                    line,
610                })
611            }
612            Token::Minus => {
613                self.advance();
614                let expr = self.parse_unary()?;
615                Ok(Expr {
616                    kind: ExprKind::UnaryOp {
617                        op: UnaryOp::Neg,
618                        expr: Box::new(expr),
619                    },
620                    line,
621                })
622            }
623            _ => self.parse_postfix(),
624        };
625        self.leave();
626        result
627    }
628
629    fn parse_postfix(&mut self) -> Result<Expr, BopError> {
630        let mut expr = self.parse_primary()?;
631
632        loop {
633            match self.peek() {
634                Token::LParen => {
635                    let line = self.peek_line();
636                    self.advance();
637                    let args = self.parse_args()?;
638                    self.expect(&Token::RParen)?;
639                    expr = Expr {
640                        kind: ExprKind::Call {
641                            callee: Box::new(expr),
642                            args,
643                        },
644                        line,
645                    };
646                }
647                Token::LBracket => {
648                    let line = self.peek_line();
649                    self.advance();
650                    let index = self.parse_expr()?;
651                    self.expect(&Token::RBracket)?;
652                    expr = Expr {
653                        kind: ExprKind::Index {
654                            object: Box::new(expr),
655                            index: Box::new(index),
656                        },
657                        line,
658                    };
659                }
660                Token::Dot => {
661                    let line = self.peek_line();
662                    self.advance();
663                    let (method, _) = self.expect_ident()?;
664                    self.expect(&Token::LParen)?;
665                    let args = self.parse_args()?;
666                    self.expect(&Token::RParen)?;
667                    expr = Expr {
668                        kind: ExprKind::MethodCall {
669                            object: Box::new(expr),
670                            method,
671                            args,
672                        },
673                        line,
674                    };
675                }
676                _ => break,
677            }
678        }
679
680        Ok(expr)
681    }
682
683    fn parse_args(&mut self) -> Result<Vec<Expr>, BopError> {
684        let mut args = Vec::new();
685        if !matches!(self.peek(), Token::RParen) {
686            args.push(self.parse_expr()?);
687            while matches!(self.peek(), Token::Comma) {
688                self.advance();
689                args.push(self.parse_expr()?);
690            }
691        }
692        Ok(args)
693    }
694
695    fn parse_primary(&mut self) -> Result<Expr, BopError> {
696        let line = self.peek_line();
697
698        match self.peek().clone() {
699            Token::Number(n) => {
700                self.advance();
701                Ok(Expr {
702                    kind: ExprKind::Number(n),
703                    line,
704                })
705            }
706            Token::Str(s) => {
707                self.advance();
708                Ok(Expr {
709                    kind: ExprKind::Str(s),
710                    line,
711                })
712            }
713            Token::StringInterp(parts) => {
714                self.advance();
715                Ok(Expr {
716                    kind: ExprKind::StringInterp(parts),
717                    line,
718                })
719            }
720            Token::True => {
721                self.advance();
722                Ok(Expr {
723                    kind: ExprKind::Bool(true),
724                    line,
725                })
726            }
727            Token::False => {
728                self.advance();
729                Ok(Expr {
730                    kind: ExprKind::Bool(false),
731                    line,
732                })
733            }
734            Token::None => {
735                self.advance();
736                Ok(Expr {
737                    kind: ExprKind::None,
738                    line,
739                })
740            }
741            Token::Ident(name) => {
742                self.advance();
743                Ok(Expr {
744                    kind: ExprKind::Ident(name),
745                    line,
746                })
747            }
748            Token::LParen => {
749                self.enter()?;
750                self.advance();
751                let expr = self.parse_expr()?;
752                self.expect(&Token::RParen)?;
753                self.leave();
754                Ok(expr)
755            }
756            Token::LBracket => self.parse_array_literal(),
757            Token::LBrace => self.parse_dict_literal(),
758            Token::If => self.parse_if_expr(),
759            _ => Err(self.error(
760                line,
761                format!("I didn't expect `{}` here", fmt_token(self.peek())),
762            )),
763        }
764    }
765
766    fn parse_array_literal(&mut self) -> Result<Expr, BopError> {
767        let line = self.peek_line();
768        self.advance(); // consume [
769        let mut elements = Vec::new();
770        if !matches!(self.peek(), Token::RBracket) {
771            elements.push(self.parse_expr()?);
772            while matches!(self.peek(), Token::Comma) {
773                self.advance();
774                if matches!(self.peek(), Token::RBracket) {
775                    break; // trailing comma
776                }
777                elements.push(self.parse_expr()?);
778            }
779        }
780        self.expect(&Token::RBracket)?;
781        Ok(Expr {
782            kind: ExprKind::Array(elements),
783            line,
784        })
785    }
786
787    fn parse_dict_literal(&mut self) -> Result<Expr, BopError> {
788        let line = self.peek_line();
789        self.advance(); // consume {
790        let mut entries = Vec::new();
791        if !matches!(self.peek(), Token::RBrace) {
792            let key = self.expect_string_key()?;
793            self.expect(&Token::Colon)?;
794            let value = self.parse_expr()?;
795            entries.push((key, value));
796            while matches!(self.peek(), Token::Comma) {
797                self.advance();
798                if matches!(self.peek(), Token::RBrace) {
799                    break; // trailing comma
800                }
801                let key = self.expect_string_key()?;
802                self.expect(&Token::Colon)?;
803                let value = self.parse_expr()?;
804                entries.push((key, value));
805            }
806        }
807        self.expect(&Token::RBrace)?;
808        Ok(Expr {
809            kind: ExprKind::Dict(entries),
810            line,
811        })
812    }
813
814    fn expect_string_key(&mut self) -> Result<String, BopError> {
815        let line = self.peek_line();
816        match self.peek().clone() {
817            Token::Str(s) => {
818                self.advance();
819                Ok(s)
820            }
821            _ => Err(self.error(line, "Dict keys must be strings (in quotes)")),
822        }
823    }
824
825    fn parse_if_expr(&mut self) -> Result<Expr, BopError> {
826        let line = self.peek_line();
827        self.advance(); // consume 'if'
828        let condition = self.parse_expr()?;
829        self.expect(&Token::LBrace)?;
830        let then_expr = self.parse_expr()?;
831        self.expect(&Token::RBrace)?;
832        self.expect(&Token::Else)?;
833        self.expect(&Token::LBrace)?;
834        let else_expr = self.parse_expr()?;
835        self.expect(&Token::RBrace)?;
836        Ok(Expr {
837            kind: ExprKind::IfExpr {
838                condition: Box::new(condition),
839                then_expr: Box::new(then_expr),
840                else_expr: Box::new(else_expr),
841            },
842            line,
843        })
844    }
845}
846
847// ─── Instruction counting ───────────────────────────────────────────────────
848
849/// Count instructions in a list of statements (AST-based, format-independent).
850///
851/// Every `Stmt` counts as 1 instruction. Compound statements (if/while/repeat/for)
852/// recurse into their body. `FnDecl` counts as 1 but does NOT recurse into the
853/// function body — this rewards defining reusable functions.
854pub fn count_instructions(stmts: &[Stmt]) -> u32 {
855    let mut count = 0u32;
856    for stmt in stmts {
857        count += 1; // the statement itself
858        match &stmt.kind {
859            StmtKind::If {
860                body,
861                else_ifs,
862                else_body,
863                ..
864            } => {
865                count += count_instructions(body);
866                for (_, branch_body) in else_ifs {
867                    count += count_instructions(branch_body);
868                }
869                if let Some(eb) = else_body {
870                    count += count_instructions(eb);
871                }
872            }
873            StmtKind::While { body, .. }
874            | StmtKind::Repeat { body, .. }
875            | StmtKind::ForIn { body, .. } => {
876                count += count_instructions(body);
877            }
878            StmtKind::FnDecl { .. } => {
879                // Don't recurse into function body — reward reuse
880            }
881            _ => {}
882        }
883    }
884    count
885}
886
887// ─── Helpers ───────────────────────────────────────────────────────────────
888
889fn expr_to_assign_target(expr: Expr, line: u32) -> Result<AssignTarget, BopError> {
890    match expr.kind {
891        ExprKind::Ident(name) => Ok(AssignTarget::Variable(name)),
892        ExprKind::Index { object, index } => Ok(AssignTarget::Index {
893            object: *object,
894            index: *index,
895        }),
896        _ => Err(BopError {
897            line: Some(line),
898            column: None,
899            message: "You can only assign to a variable or an index (like `arr[0]`)".to_string(),
900            friendly_hint: None,
901        }),
902    }
903}
904
905pub fn fmt_token(token: &Token) -> &'static str {
906    match token {
907        Token::Number(_) => "a number",
908        Token::Str(_) | Token::StringInterp(_) => "a string",
909        Token::True => "true",
910        Token::False => "false",
911        Token::None => "none",
912        Token::Ident(_) => "a name",
913        Token::Let => "let",
914        Token::Fn => "fn",
915        Token::Return => "return",
916        Token::If => "if",
917        Token::Else => "else",
918        Token::While => "while",
919        Token::For => "for",
920        Token::In => "in",
921        Token::Repeat => "repeat",
922        Token::Break => "break",
923        Token::Continue => "continue",
924        Token::Plus => "+",
925        Token::Minus => "-",
926        Token::Star => "*",
927        Token::Slash => "/",
928        Token::Percent => "%",
929        Token::EqEq => "==",
930        Token::BangEq => "!=",
931        Token::Lt => "<",
932        Token::Gt => ">",
933        Token::LtEq => "<=",
934        Token::GtEq => ">=",
935        Token::AmpAmp => "&&",
936        Token::PipePipe => "||",
937        Token::Bang => "!",
938        Token::Eq => "=",
939        Token::PlusEq => "+=",
940        Token::MinusEq => "-=",
941        Token::StarEq => "*=",
942        Token::SlashEq => "/=",
943        Token::PercentEq => "%=",
944        Token::LParen => "(",
945        Token::RParen => ")",
946        Token::LBracket => "[",
947        Token::RBracket => "]",
948        Token::LBrace => "{",
949        Token::RBrace => "}",
950        Token::Comma => ",",
951        Token::Colon => ":",
952        Token::Dot => ".",
953        Token::Semicolon => ";",
954        Token::Newline => "newline",
955        Token::Eof => "end of code",
956    }
957}