• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Devin-Yeung / loxide / 14567938002

21 Apr 2025 05:28AM UTC coverage: 69.794% (-8.8%) from 78.623%
14567938002

push

github

web-flow
chore(deps): Bump the dependencies group across 1 directory with 8 updates (#123)

* chore(deps): Bump the dependencies group across 1 directory with 8 updates

Bumps the dependencies group with 8 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [insta](https://github.com/mitsuhiko/insta) | `1.42.1` | `1.42.2` |
| [miette](https://github.com/zkat/miette) | `7.4.0` | `7.5.0` |
| [clap](https://github.com/clap-rs/clap) | `4.5.27` | `4.5.31` |
| [automod](https://github.com/dtolnay/automod) | `1.0.14` | `1.0.15` |
| [once_cell](https://github.com/matklad/once_cell) | `1.20.2` | `1.20.3` |
| [proc-macro2](https://github.com/dtolnay/proc-macro2) | `1.0.93` | `1.0.94` |
| [quote](https://github.com/dtolnay/quote) | `1.0.38` | `1.0.39` |
| [syn](https://github.com/dtolnay/syn) | `2.0.96` | `2.0.100` |



Updates `insta` from 1.42.1 to 1.42.2
- [Release notes](https://github.com/mitsuhiko/insta/releases)
- [Changelog](https://github.com/mitsuhiko/insta/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mitsuhiko/insta/compare/1.42.1...1.42.2)

Updates `miette` from 7.4.0 to 7.5.0
- [Release notes](https://github.com/zkat/miette/releases)
- [Changelog](https://github.com/zkat/miette/blob/main/CHANGELOG.md)
- [Commits](https://github.com/zkat/miette/commits)

Updates `clap` from 4.5.27 to 4.5.31
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.27...v4.5.31)

Updates `automod` from 1.0.14 to 1.0.15
- [Release notes](https://github.com/dtolnay/automod/releases)
- [Commits](https://github.com/dtolnay/automod/compare/1.0.14...1.0.15)

Updates `once_cell` from 1.20.2 to 1.20.3
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.20.2...v1.20.3)

Updates `proc-macro2` from 1.0.93 to 1.0.94
- [Release n... (continued)

781 of 1119 relevant lines covered (69.79%)

7109.07 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

56.04
/crates/loxide_parser/src/parser.rs
1
use crate::ast::ExprKind::Binary;
2
use crate::ast::Literal::{Boolean, Nil};
3
use crate::ast::{
4
    AssignExpr, BinaryExpr, CallExpr, ConditionStmt, Expr, ExprKind, ForStmt, FunDeclaration,
5
    Identifier, ReturnStmt, Stmt, StmtKind, UnaryExpr, UnaryOperator, WhileStmt,
6
};
7
use crate::error::SyntaxError;
8
use crate::scanner::Scanner;
9
use crate::token::{Keyword, Span, Token, TokenType};
10
use std::iter::Peekable;
11
use std::sync::Arc;
12

13
pub struct Parser<'src> {
14
    tokens: Peekable<Scanner<'src>>,
15
}
16

17
impl<'src> Parser<'src> {
18
    pub fn new(src: &'src str) -> Parser {
57✔
19
        Parser {
20
            tokens: Scanner::new(src).peekable(),
57✔
21
        }
22
    }
23

24
    pub fn parse(&mut self) -> (Vec<Stmt>, Vec<SyntaxError>) {
29✔
25
        let mut statements = Vec::<Stmt>::new();
29✔
26
        let mut errors = Vec::<SyntaxError>::new();
29✔
27
        loop {
×
28
            match self.peek_type() {
114✔
29
                Ok(TokenType::EOF) => break,
29✔
30
                Ok(_) => match self.declaration() {
85✔
31
                    Ok(stmt) => {
82✔
32
                        statements.push(stmt);
82✔
33
                        while self.consume_if(TokenType::Comment) {
82✔
34
                            continue;
×
35
                        }
36
                    }
37
                    Err(err) => {
3✔
38
                        errors.push(err);
3✔
39
                        self.synchronize();
3✔
40
                    }
41
                },
42
                Err(err) => errors.push(err),
×
43
            }
44
        }
45
        (statements, errors)
29✔
46
    }
47

48
    fn consume(&mut self, ty: TokenType) -> Result<Token, SyntaxError> {
473✔
49
        let token = self.peek_token()?;
946✔
50
        if token.ty == ty {
×
51
            Ok(self.advance()?)
473✔
52
        } else {
53
            Err(SyntaxError::UnexpectedToken {
×
54
                span: token.span,
×
55
                expected: ty.name(),
×
56
                found: token.ty.name(),
×
57
            })
58
        }
59
    }
60

61
    fn consume_identifier(&mut self) -> Result<Identifier, SyntaxError> {
98✔
62
        let token = self.consume(TokenType::Identifier)?;
196✔
63
        Ok(Identifier {
×
64
            name: token.lexeme.to_string(),
×
65
            span: token.span,
×
66
        })
67
    }
68

69
    fn consume_if(&mut self, ty: TokenType) -> bool {
292✔
70
        if self.peek_type() == Ok(ty) {
292✔
71
            self.advance().unwrap();
18✔
72
            return true;
18✔
73
        }
74
        false
274✔
75
    }
76

77
    fn peek_type(&mut self) -> Result<TokenType, SyntaxError> {
2,067✔
78
        self.skip_comments();
2,067✔
79
        match self.tokens.peek() {
2,067✔
80
            None => Ok(TokenType::EOF),
×
81
            Some(Ok(token)) => Ok(token.ty.clone()),
2,067✔
82
            Some(Err(err)) => Err(*err),
×
83
        }
84
    }
85

86
    fn skip_comments(&mut self) {
2,816✔
87
        match self.tokens.peek() {
2,816✔
88
            Some(Ok(token)) if token.ty == TokenType::Comment => {
2,850✔
89
                let _ = self.advance(); // drop it
34✔
90
                self.skip_comments();
34✔
91
            }
92
            _ => { /* Do nothing */ }
2,782✔
93
        }
94
    }
95

96
    fn peek_token(&mut self) -> Result<&Token, SyntaxError> {
715✔
97
        self.skip_comments();
715✔
98
        match self.tokens.peek() {
715✔
99
            None => Err(SyntaxError::UnexpectedEOF),
×
100
            Some(Ok(token)) => Ok(token), // never be a comment
715✔
101
            Some(Err(err)) => Err(*err),
×
102
        }
103
    }
104

105
    fn advance(&mut self) -> Result<Token, SyntaxError> {
743✔
106
        self.tokens
743✔
107
            .next()
108
            .unwrap_or(Err(SyntaxError::UnexpectedEOF))
743✔
109
    }
110

111
    /// parse declaration according to following rules:
112
    ///
113
    /// ```text
114
    /// declaration  → funDecl
115
    ///              | varDecl
116
    ///              | statement ;
117
    /// ```
118
    fn declaration(&mut self) -> Result<Stmt, SyntaxError> {
123✔
119
        match self.peek_type()? {
123✔
120
            TokenType::Keyword(Keyword::Var) => self.var_declaration(),
23✔
121
            TokenType::Keyword(Keyword::Fun) => self.func_declaration(),
6✔
122
            _ => self.statement(),
94✔
123
        }
124
    }
125

126
    /// parse function declaration according to following rules:
127
    ///
128
    /// ```text
129
    /// funDecl  → "fun" function ;
130
    /// ```
131
    fn func_declaration(&mut self) -> Result<Stmt, SyntaxError> {
6✔
132
        self.consume(TokenType::Keyword(Keyword::Fun))?;
6✔
133
        self.function()
6✔
134
    }
135

136
    /// parse function body according to following rules:
137
    ///
138
    /// ```text
139
    /// function  → IDENTIFIER "(" parameters? ")" block ;
140
    /// ```
141
    fn function(&mut self) -> Result<Stmt, SyntaxError> {
6✔
142
        let name = self.consume_identifier()?;
12✔
143
        let left_paren = self.consume(TokenType::LeftParen)?.span;
6✔
144
        let params = match self.peek_type()? {
6✔
145
            TokenType::RightParen => Vec::new(),
3✔
146
            _ => self.parameters()?,
3✔
147
        };
148
        let right_parent = self.consume(TokenType::RightParen)?.span;
6✔
149
        let (body, body_span) = self.spanned_block()?;
6✔
150

151
        Ok(Stmt {
×
152
            span: Span::new(name.span().start, body_span.end),
×
153
            kind: StmtKind::FunDeclaration(Arc::new(FunDeclaration {
×
154
                name,
×
155
                paren_token: Span::new(left_paren.start, right_parent.end),
×
156
                params,
×
157
                body,
×
158
            })),
159
        })
160
    }
161

162
    /// parse parameters according to following rules:
163
    ///
164
    /// ```text
165
    /// parameters  → IDENTIFIER ( "," IDENTIFIER )* ;
166
    /// ```
167
    ///
168
    fn parameters(&mut self) -> Result<Vec<Identifier>, SyntaxError> {
3✔
169
        let mut idents = Vec::<Identifier>::new();
3✔
170

171
        idents.push(self.consume_identifier()?);
3✔
172

173
        while self.consume_if(TokenType::Comma) {
5✔
174
            idents.push(self.consume_identifier()?);
2✔
175
        }
176

177
        Ok(idents)
3✔
178
    }
179

180
    /// parse var declaration according to following rules:
181
    ///
182
    /// ```text
183
    /// varDecl  → "var" IDENTIFIER ( "=" expression )? ";" ;
184
    /// ```
185
    fn var_declaration(&mut self) -> Result<Stmt, SyntaxError> {
23✔
186
        let var = self.consume(TokenType::Keyword(Keyword::Var))?.span;
46✔
187
        let name = self.consume_identifier()?;
23✔
188
        let mut expr: Option<Expr> = None;
×
189
        if self.peek_type() == Ok(TokenType::Equal) {
×
190
            self.consume(TokenType::Equal)?;
21✔
191
            expr = Some(self.expression()?);
42✔
192
        }
193
        let semi = self.consume(TokenType::Semicolon)?.span;
46✔
194

195
        let span = Span::new(var.start, semi.end);
×
196
        let kind = StmtKind::VarDeclaration(name, expr);
×
197
        Ok(Stmt { kind, span })
×
198
    }
199

200
    /// parse statement according to following rules:
201
    ///
202
    /// ```text
203
    /// statement  → exprStmt
204
    ///            | printStmt
205
    ///            | returnStmt
206
    ///            | ifStmt
207
    ///            | whileStmt
208
    ///            | forStmt
209
    ///            | block ;
210
    /// ```
211
    fn statement(&mut self) -> Result<Stmt, SyntaxError> {
114✔
212
        match self.peek_type()? {
114✔
213
            TokenType::Keyword(Keyword::Print) => self.print_stmt(),
42✔
214
            TokenType::LeftBrace => self.block_stmt(),
18✔
215
            TokenType::Keyword(Keyword::If) => self.if_stmt(),
5✔
216
            TokenType::Keyword(Keyword::While) => self.while_stmt(),
5✔
217
            TokenType::Keyword(Keyword::For) => self.for_stmt(),
5✔
218
            TokenType::Keyword(Keyword::Return) => self.return_stmt(),
4✔
219
            _ => self.expression_stmt(),
35✔
220
        }
221
    }
222

223
    /// parse if statement according to following rules:
224
    /// ```text
225
    /// ifStmt  → "if" "(" expression ")" statement
226
    ///           ( "else" statement )? ;
227
    /// ```
228
    ///
229
    /// note: the else is bound to the nearest if that precedes it
230
    /// for the following dangling else case
231
    /// ```text
232
    /// if (first)
233
    /// |   if (second)
234
    /// |      when_true();
235
    /// else
236
    ///     when_false();
237
    /// ```
238
    /// the actual semantic is:
239
    /// ```text
240
    /// if (first)
241
    ///     if (second)
242
    ///     |   when_true();
243
    ///     else
244
    ///         when_false();
245
    /// ```
246
    fn if_stmt(&mut self) -> Result<Stmt, SyntaxError> {
5✔
247
        let kw = self.consume(TokenType::Keyword(Keyword::If))?.span;
10✔
248
        self.consume(TokenType::LeftParen)?;
×
249
        let condition = self.expression()?;
10✔
250
        self.consume(TokenType::RightParen)?;
×
251
        let then_branch = Box::new(self.statement()?);
10✔
252
        // else statement if optional
253
        let else_branch = if self.consume_if(TokenType::Keyword(Keyword::Else)) {
5✔
254
            // the else is bound to the nearest if that precedes it
255
            Some(Box::new(self.statement()?))
2✔
256
        } else {
257
            None
3✔
258
        };
259
        // track the span
260
        let span = Span::new(
261
            kw.start,
×
262
            else_branch
×
263
                .as_ref()
×
264
                .map_or_else(|| then_branch.span.end, |stmt| stmt.span.end),
5✔
265
        );
266
        let kind = StmtKind::Condition(ConditionStmt {
×
267
            condition,
×
268
            then_branch,
×
269
            else_branch,
×
270
        });
271
        Ok(Stmt { kind, span })
×
272
    }
273

274
    /// parse while statement according to following rules:
275
    /// ```text
276
    /// whileStmt  → "while" "(" expression ")" statement ;
277
    /// ```
278
    fn while_stmt(&mut self) -> Result<Stmt, SyntaxError> {
5✔
279
        let kw = self.consume(TokenType::Keyword(Keyword::While))?.span;
10✔
280
        self.consume(TokenType::LeftParen)?;
×
281
        let condition = self.expression()?;
10✔
282
        self.consume(TokenType::RightParen)?;
×
283
        let body = Box::new(self.statement()?);
10✔
284

285
        let span = Span::new(kw.start, body.span.end);
×
286
        let kind = StmtKind::While(WhileStmt { condition, body });
×
287
        Ok(Stmt { kind, span })
×
288
    }
289

290
    /// parse for statement according to following rules:
291
    /// ```text
292
    /// forStmt  → "for"
293
    ///          "("
294
    ///             ( varDecl | exprStmt | ";" )
295
    ///               expression? ";"
296
    ///               expression?
297
    ///          ")"
298
    ///          statement ;
299
    /// ```
300
    fn for_stmt(&mut self) -> Result<Stmt, SyntaxError> {
5✔
301
        let kw = self.consume(TokenType::Keyword(Keyword::For))?.span;
10✔
302
        self.consume(TokenType::LeftParen)?;
×
303
        let initializer = match self.peek_type()? {
10✔
304
            TokenType::Semicolon => {
×
305
                self.consume(TokenType::Semicolon)?;
1✔
306
                None
1✔
307
            }
308
            _ => Some(Box::new(self.declaration()?)),
4✔
309
        };
310
        let condition = match self.peek_type()? {
5✔
311
            TokenType::Semicolon => None,
1✔
312
            _ => Some(Box::new(self.expression()?)),
4✔
313
        };
314
        self.consume(TokenType::Semicolon)?;
×
315
        let increment = match self.peek_type()? {
10✔
316
            TokenType::RightParen => None,
1✔
317
            _ => Some(Box::new(self.expression()?)),
4✔
318
        };
319
        self.consume(TokenType::RightParen)?;
×
320
        let body = Box::new(self.statement()?);
10✔
321
        // track the span
322
        let span = Span::new(kw.start, body.span.end);
×
323
        let kind = StmtKind::For(ForStmt {
×
324
            initializer,
×
325
            condition,
×
326
            increment,
×
327
            body,
×
328
        });
329

330
        Ok(Stmt { kind, span })
×
331
    }
332

333
    /// parse block statement according to following rules:
334
    /// ```text
335
    /// block  → "{" declaration* "}" ;
336
    /// ```
337
    fn block_stmt(&mut self) -> Result<Stmt, SyntaxError> {
18✔
338
        let (stmts, span) = self.spanned_block()?;
36✔
339
        Ok(Stmt {
×
340
            kind: StmtKind::Block(stmts),
×
341
            span,
×
342
        })
343
    }
344

345
    fn block(&mut self) -> Result<Vec<Stmt>, SyntaxError> {
×
346
        let (stmts, _) = self.spanned_block()?;
×
347
        Ok(stmts)
×
348
    }
349

350
    /// helper method for parsing  a block statement
351
    /// ```text
352
    /// block  → "{" declaration* "}" ;
353
    /// ```
354
    fn spanned_block(&mut self) -> Result<(Vec<Stmt>, Span), SyntaxError> {
24✔
355
        let lbrace = self.consume(TokenType::LeftBrace)?.span;
48✔
356
        let mut stmts: Vec<Stmt> = Vec::new();
×
357
        loop {
×
358
            match self.peek_type()? {
58✔
359
                TokenType::RightBrace => break,
24✔
360
                _ => {
×
361
                    stmts.push(self.declaration()?);
34✔
362
                    while self.consume_if(TokenType::Comment) {
34✔
363
                        continue;
×
364
                    }
365
                }
366
            }
367
        }
368
        let rbrace = self.consume(TokenType::RightBrace)?.span;
24✔
369
        let span = Span::new(lbrace.start, rbrace.end);
×
370
        Ok((stmts, span))
×
371
    }
372

373
    /// parse print statement according to following rules:
374
    ///
375
    /// ```text
376
    /// printStmt  → "print" expression ";" ;
377
    /// ```
378
    fn print_stmt(&mut self) -> Result<Stmt, SyntaxError> {
42✔
379
        self.consume(TokenType::Keyword(Keyword::Print))?;
42✔
380
        let expr = self.expression()?;
84✔
381
        let semi = self.consume(TokenType::Semicolon)?.span;
42✔
382
        Ok(Stmt {
×
383
            span: Span::new(expr.span.start, semi.end),
×
384
            kind: StmtKind::PrintStmt(expr),
×
385
        })
386
    }
387

388
    /// parse expression statement according to following rules:
389
    ///
390
    /// ```text
391
    /// exprStmt  → expression ";" ;
392
    /// ```
393
    fn expression_stmt(&mut self) -> Result<Stmt, SyntaxError> {
35✔
394
        let expr = self.expression()?;
70✔
395
        let semi = self.consume(TokenType::Semicolon)?.span;
32✔
396
        Ok(Stmt {
×
397
            span: Span::new(expr.span.start, semi.end),
×
398
            kind: StmtKind::Expression(expr),
×
399
        })
400
    }
401

402
    /// parse return statement according to following rules:
403
    ///
404
    /// ```text
405
    /// returnStmt  → "return" expression? ";" ;
406
    /// ```
407
    fn return_stmt(&mut self) -> Result<Stmt, SyntaxError> {
4✔
408
        let kw = self.consume(TokenType::Keyword(Keyword::Return))?.span;
8✔
409
        let value = match self.peek_type()? {
4✔
410
            TokenType::Semicolon => None,
1✔
411
            _ => Some(self.expression()?),
3✔
412
        };
413
        let semi = self.consume(TokenType::Semicolon)?.span;
4✔
414

415
        Ok(Stmt {
×
416
            span: Span::new(kw.start, semi.end),
×
417
            kind: StmtKind::ReturnStmt(ReturnStmt { value }),
×
418
        })
419
    }
420

421
    /// parse expression according to following rules:
422
    ///
423
    /// ```text
424
    /// expression  → assignment ;
425
    /// ```
426
    fn expression(&mut self) -> Result<Expr, SyntaxError> {
155✔
427
        self.assignment()
155✔
428
    }
429

430
    /// assignment  → IDENTIFIER "=" assignment
431
    ///             | equality ;
432
    fn assignment(&mut self) -> Result<Expr, SyntaxError> {
169✔
433
        let expr = self.equality()?;
338✔
434

435
        if self.consume_if(TokenType::Equal) {
×
436
            let value = self.assignment()?;
28✔
437
            let span = Span::new(expr.span.start, value.span.end);
×
438
            return match expr.kind {
×
439
                ExprKind::Variable(v) => Ok(Expr {
14✔
440
                    kind: ExprKind::Assign(AssignExpr::new(v, value)),
14✔
441
                    span,
14✔
442
                }),
443
                _ => Err(SyntaxError::InvalidAssignmentTarget(expr.span())),
×
444
            };
445
        }
446
        Ok(expr)
149✔
447
    }
448

449
    /// parse equality expression according to following rules:
450
    ///
451
    /// ```text
452
    /// equality  → comparison ( ( "!=" | "==" ) comparison )* ;
453
    /// ```
454
    fn equality(&mut self) -> Result<Expr, SyntaxError> {
171✔
455
        let mut expr = self.comparison()?;
342✔
456
        loop {
×
457
            let ops = match self.peek_type()? {
179✔
458
                TokenType::EqualEqual | TokenType::BangEqual => self.advance()?.try_into()?,
14✔
459
                _ => break,
165✔
460
            };
461
            let rhs: Expr = self.comparison()?;
7✔
462
            let span = Span::new(expr.span.start, rhs.span.end);
×
463
            expr = Expr {
×
464
                kind: Binary(BinaryExpr {
×
465
                    lhs: Box::new(expr),
×
466
                    rhs: Box::new(rhs),
×
467
                    operator: ops,
×
468
                }),
469
                span,
×
470
            }
471
        }
472
        Ok(expr)
165✔
473
    }
474

475
    /// parse comparison expression according to following rules:
476
    ///
477
    /// ```text
478
    /// comparison  → term ( ( ">" | ">=" | "<" | "<=" ) term )* ;
479
    /// ```
480
    fn comparison(&mut self) -> Result<Expr, SyntaxError> {
182✔
481
        let mut expr = self.term()?;
364✔
482
        loop {
×
483
            let ops = match self.peek_type()? {
214✔
484
                TokenType::Greater
×
485
                | TokenType::GreaterEqual
×
486
                | TokenType::Less
×
487
                | TokenType::LessEqual => self.advance()?.try_into()?,
38✔
488
                _ => break,
176✔
489
            };
490
            let rhs = self.term()?;
19✔
491
            let span = Span::new(expr.span.start, rhs.span.end);
×
492
            expr = Expr {
×
493
                kind: Binary(BinaryExpr {
×
494
                    lhs: Box::new(expr),
×
495
                    rhs: Box::new(rhs),
×
496
                    operator: ops,
×
497
                }),
498
                span,
×
499
            }
500
        }
501
        Ok(expr)
176✔
502
    }
503

504
    /// parse term expression according to following rules:
505
    ///
506
    /// ```text
507
    /// term  → factor ( ( "-" | "+" ) factor )* ;
508
    /// ```
509
    fn term(&mut self) -> Result<Expr, SyntaxError> {
203✔
510
        let mut expr = self.factor()?;
406✔
511
        loop {
×
512
            let ops = match self.peek_type()? {
241✔
513
                TokenType::Minus | TokenType::Plus => self.advance()?.try_into()?,
44✔
514
                _ => break,
197✔
515
            };
516
            let rhs = self.factor()?;
22✔
517
            let span = Span::new(expr.span.start, rhs.span.end);
×
518
            expr = Expr {
×
519
                kind: Binary(BinaryExpr {
×
520
                    lhs: Box::new(expr),
×
521
                    rhs: Box::new(rhs),
×
522
                    operator: ops,
×
523
                }),
524
                span,
×
525
            }
526
        }
527
        Ok(expr)
197✔
528
    }
529

530
    /// parse factor expression according to following rules:
531
    ///
532
    /// ```text
533
    /// factor  → unary ( ( "/" | "*" ) unary )* ;
534
    /// ```
535
    fn factor(&mut self) -> Result<Expr, SyntaxError> {
226✔
536
        let mut expr = self.unary()?;
452✔
537
        loop {
×
538
            let ops = match self.peek_type()? {
230✔
539
                TokenType::Slash | TokenType::Star => self.advance()?.try_into()?,
10✔
540
                _ => break,
220✔
541
            };
542
            let rhs = self.unary()?;
5✔
543
            let span = Span::new(expr.span.start, rhs.span.end);
×
544
            expr = Expr {
×
545
                kind: Binary(BinaryExpr {
×
546
                    lhs: Box::new(expr),
×
547
                    rhs: Box::new(rhs),
×
548
                    operator: ops,
×
549
                }),
550
                span,
×
551
            }
552
        }
553
        Ok(expr)
220✔
554
    }
555

556
    /// parse unary expression according to following rules:
557
    ///
558
    /// ```text
559
    /// unary  → ( "!" | "-" ) unary
560
    ///        | call ;
561
    /// ```
562
    fn unary(&mut self) -> Result<Expr, SyntaxError> {
247✔
563
        let expr = match self.peek_type()? {
486✔
564
            TokenType::Bang | TokenType::Minus => {
×
565
                let operator = self.advance()?;
26✔
566
                let start = operator.span.start;
×
567
                let operator: UnaryOperator = operator.try_into()?;
13✔
568
                let expr = Box::new(self.unary()?);
13✔
569

570
                Expr {
571
                    span: Span::new(start, expr.span.end),
×
572
                    kind: ExprKind::Unary(UnaryExpr { operator, expr }),
×
573
                }
574
            }
575
            _ => self.call()?,
240✔
576
        };
577
        Ok(expr)
×
578
    }
579

580
    /// parse unary expression according to following rules:
581
    ///
582
    /// ```text
583
    /// call  → primary ( "(" arguments? ")" )* ;
584
    /// ```
585
    fn call(&mut self) -> Result<Expr, SyntaxError> {
234✔
586
        let mut expr = self.primary()?;
468✔
587
        loop {
×
588
            match self.peek_type() {
238✔
589
                Ok(TokenType::LeftParen) => {
×
590
                    let lparen = self.consume(TokenType::LeftParen)?.span.start;
20✔
591
                    let args = self.arguments()?;
10✔
592
                    let rparen = self.consume(TokenType::RightParen)?.span.end;
10✔
593
                    expr = Expr {
×
594
                        span: Span::new(expr.span.start, rparen),
×
595
                        kind: ExprKind::Call(CallExpr {
×
596
                            callee: Box::new(expr),
×
597
                            paren_token: Span::new(lparen, rparen),
×
598
                            args,
×
599
                        }),
600
                    }
601
                }
602
                _ => break,
228✔
603
            }
604
        }
605
        Ok(expr)
228✔
606
    }
607

608
    /// parse arguments expression according to following rules:
609
    ///
610
    /// ```text
611
    /// arguments  → expression ( "," expression )* ;
612
    /// ```
613
    fn arguments(&mut self) -> Result<Vec<Expr>, SyntaxError> {
10✔
614
        let mut args = Vec::new();
10✔
615
        loop {
×
616
            match self.peek_type()? {
19✔
617
                TokenType::RightParen => break,
10✔
618
                TokenType::Comma => {
×
619
                    self.consume(TokenType::Comma)?;
1✔
620
                }
621
                _ => args.push(self.expression()?),
8✔
622
            }
623
        }
624
        Ok(args)
×
625
    }
626

627
    /// parse primary expression according to following rules:
628
    ///
629
    /// ```text
630
    /// primary  → NUMBER | STRING | "true" | "false" | "nil"
631
    ///          | "(" expression ")"
632
    ///          | IDENTIFIER ;
633
    /// ```
634
    fn primary(&mut self) -> Result<Expr, SyntaxError> {
242✔
635
        let token = self.peek_token()?;
484✔
636
        let expr = match &token.ty {
256✔
637
            TokenType::Keyword(Keyword::True) => {
×
638
                let span = self.advance()?.span;
20✔
639
                Expr {
640
                    kind: ExprKind::Literal(Boolean(true)),
×
641
                    span,
642
                }
643
            }
644
            TokenType::Keyword(Keyword::False) => {
×
645
                let span = self.advance()?.span;
14✔
646
                Expr {
647
                    kind: ExprKind::Literal(Boolean(false)),
×
648
                    span,
649
                }
650
            }
651
            TokenType::Keyword(Keyword::Nil) => {
×
652
                let span = self.advance()?.span;
6✔
653
                Expr {
654
                    kind: ExprKind::Literal(Nil),
×
655
                    span,
656
                }
657
            }
658
            TokenType::Literal(_) => {
×
659
                let literal = self.advance()?;
258✔
660
                let span = literal.span;
×
661
                Expr {
662
                    kind: ExprKind::Literal(literal.try_into()?),
×
663
                    span,
664
                }
665
            }
666
            TokenType::LeftParen => {
×
667
                let start = self.consume(TokenType::LeftParen)?.span.start;
46✔
668
                let expr = self.expression()?;
23✔
669
                let end = self.consume(TokenType::RightParen)?.span.end;
23✔
670
                Expr {
671
                    kind: ExprKind::Grouped(Box::new(expr)),
×
672
                    span: Span::new(start, end),
×
673
                }
674
            }
675
            TokenType::Identifier => {
×
676
                let name = self.consume_identifier()?;
128✔
677
                Expr {
678
                    span: name.span(),
×
679
                    kind: ExprKind::Variable(name),
×
680
                }
681
            }
682
            ty => {
6✔
683
                return Err(SyntaxError::UnexpectedToken {
6✔
684
                    span: token.span,
6✔
685
                    expected: "primary expression",
6✔
686
                    found: ty.name(),
6✔
687
                })
688
            }
689
        };
690
        Ok(expr)
×
691
    }
692

693
    fn synchronize(&mut self) {
3✔
694
        loop {
×
695
            match self.peek_type() {
3✔
696
                Ok(ty) => match ty {
3✔
697
                    TokenType::Semicolon => {
×
698
                        self.advance().unwrap();
3✔
699
                        while self.consume_if(TokenType::Comment) {
3✔
700
                            continue; // remove comment if necessary, exhaustively
×
701
                        }
702
                        return;
×
703
                    }
704
                    TokenType::Keyword(Keyword::Class)
×
705
                    | TokenType::Keyword(Keyword::Fun)
×
706
                    | TokenType::Keyword(Keyword::Var)
×
707
                    | TokenType::Keyword(Keyword::For)
×
708
                    | TokenType::Keyword(Keyword::If)
×
709
                    | TokenType::Keyword(Keyword::While)
×
710
                    | TokenType::Keyword(Keyword::Print)
×
711
                    | TokenType::Keyword(Keyword::Return)
×
712
                    | TokenType::EOF => return,
×
713
                    _ => {
×
714
                        self.advance().unwrap();
×
715
                    }
716
                },
717
                _ => {
718
                    unreachable!()
719
                }
720
            }
721
        }
722
    }
723
}
724

725
#[cfg(test)]
726
mod tests {
727
    use crate::parser::Parser;
728
    use crate::utils::test_utils::SPAN_FILTER;
729
    use loxide_testsuite::unittest;
730

731
    unittest!(primary, filters => vec![SPAN_FILTER], |src| {
732
        let asts = src
733
            .split('\n')
734
            .map(|line| Parser::new(line).primary())
735
            .collect::<Vec<_>>();
736
        insta::assert_debug_snapshot!(asts);
737
    });
738

739
    unittest!(unary, filters => vec![SPAN_FILTER], |src| {
740
        let asts = src
741
            .split('\n')
742
            .map(|line| Parser::new(line).unary())
743
            .collect::<Vec<_>>();
744
        insta::assert_debug_snapshot!(asts);
745
    });
746

747
    unittest!(factor, filters => vec![SPAN_FILTER], |src| {
748
        let asts = src
749
            .split('\n')
750
            .map(|line| Parser::new(line).factor())
751
            .collect::<Vec<_>>();
752
        insta::assert_debug_snapshot!(asts);
753
    });
754

755
    unittest!(term, filters => vec![SPAN_FILTER], |src| {
756
        let asts = src
757
            .split('\n')
758
            .map(|line| Parser::new(line).term())
759
            .collect::<Vec<_>>();
760
        insta::assert_debug_snapshot!(asts);
761
    });
762

763
    unittest!(comparison, filters => vec![SPAN_FILTER], |src| {
764
        let asts = src
765
            .split('\n')
766
            .map(|line| Parser::new(line).comparison())
767
            .collect::<Vec<_>>();
768
        insta::assert_debug_snapshot!(asts);
769
    });
770

771
    unittest!(equality, filters => vec![SPAN_FILTER], |src| {
772
        let asts = src
773
            .split('\n')
774
            .map(|line| Parser::new(line).equality())
775
            .collect::<Vec<_>>();
776
        insta::assert_debug_snapshot!(asts);
777
    });
778

779
    unittest!(expression, filters => vec![SPAN_FILTER], |src| {
780
        let asts = src
781
            .split('\n')
782
            .map(|line| Parser::new(line).expression())
783
            .collect::<Vec<_>>();
784
        insta::assert_debug_snapshot!(asts);
785
    });
786

787
    unittest!(single_statement, filters => vec![SPAN_FILTER], |src| {
788
        let asts = src
789
            .split('\n')
790
            .map(|line| Parser::new(line).statement())
791
            .collect::<Vec<_>>();
792
        insta::assert_debug_snapshot!(asts);
793
    });
794

795
    unittest!(many_statements, filters => vec![SPAN_FILTER], |src| {
796
        let mut parser = Parser::new(src);
797
        let results = parser.parse();
798
        insta::assert_debug_snapshot!(results);
799
    });
800

801
    unittest!(declarations, filters => vec![SPAN_FILTER], |src| {
802
        let mut parser = Parser::new(src);
803
        let results = parser.parse();
804
        insta::assert_debug_snapshot!(results);
805
    });
806

807
    unittest!(block_statement, filters => vec![SPAN_FILTER], |src| {
808
        let mut parser = Parser::new(src);
809
        let results = parser.parse();
810
        insta::assert_debug_snapshot!(results);
811
    });
812

813
    unittest!(if_statement, filters => vec![SPAN_FILTER], |src| {
814
        let mut parser = Parser::new(src);
815
        let results = parser.parse();
816
        insta::assert_debug_snapshot!(results);
817
    });
818

819
    unittest!(while_statement, filters => vec![SPAN_FILTER], |src| {
820
        let mut parser = Parser::new(src);
821
        let results = parser.parse();
822
        insta::assert_debug_snapshot!(results);
823
    });
824

825
    unittest!(for_statement, filters => vec![SPAN_FILTER], |src| {
826
        let mut parser = Parser::new(src);
827
        let results = parser.parse();
828
        insta::assert_debug_snapshot!(results);
829
    });
830

831
    unittest!(fn_call, filters => vec![SPAN_FILTER], |src| {
832
        let mut parser = Parser::new(src);
833
        let results = parser.parse();
834
        insta::assert_debug_snapshot!(results);
835
    });
836

837
    unittest!(fn_decl, filters => vec![SPAN_FILTER], |src| {
838
        let mut parser = Parser::new(src);
839
        let results = parser.parse();
840
        insta::assert_debug_snapshot!(results);
841
    });
842

843
    unittest!(return_stmt, filters => vec![SPAN_FILTER], |src| {
844
        let mut parser = Parser::new(src);
845
        let results = parser.parse();
846
        insta::assert_debug_snapshot!(results);
847
    });
848
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc