Skip to main content

openscad_rs/
parser.rs

1/// Recursive-descent parser for `OpenSCAD`.
2///
3/// Consumes a token stream from the lexer and produces an AST.
4/// Uses precedence climbing for expression parsing.
5use crate::ast::{
6    Argument, BinaryOp, Expr, ExprKind, Modifiers, Parameter, SourceFile, Statement, UnaryOp,
7};
8use crate::error::{ParseError, ParseResult};
9use crate::lexer::{self, SpannedToken};
10use crate::span::Span;
11use crate::token::Token;
12
13/// Parse an `OpenSCAD` source string into an AST.
14///
15/// # Errors
16/// Returns a `ParseError` if the source contains syntax errors.
17pub fn parse(source: &str) -> ParseResult<SourceFile> {
18    let tokens = lexer::lex(source);
19    let mut parser = Parser::new(source, tokens);
20    parser.parse_file()
21}
22
23struct Parser<'src> {
24    source: &'src str,
25    tokens: Vec<SpannedToken>,
26    pos: usize,
27    depth: usize,
28}
29
30const MAX_DEPTH: usize = 256;
31
32impl<'src> Parser<'src> {
33    const fn new(source: &'src str, tokens: Vec<SpannedToken>) -> Self {
34        Self {
35            source,
36            tokens,
37            pos: 0,
38            depth: 0,
39        }
40    }
41
42    fn enter_recursion(&mut self) -> ParseResult<()> {
43        self.depth += 1;
44        if self.depth > MAX_DEPTH {
45            Err(ParseError::custom(
46                "maximum nesting depth exceeded",
47                self.peek_span(),
48            ))
49        } else {
50            Ok(())
51        }
52    }
53
54    const fn leave_recursion(&mut self) {
55        self.depth = self.depth.saturating_sub(1);
56    }
57
58    // ── Helpers ──────────────────────────────────────────────
59
60    fn peek(&self) -> Option<&Token> {
61        self.tokens.get(self.pos).map(|(t, _)| t)
62    }
63
64    fn peek_at(&self, offset: usize) -> Option<&Token> {
65        self.tokens.get(self.pos + offset).map(|(t, _)| t)
66    }
67
68    fn peek_span(&self) -> Span {
69        self.tokens.get(self.pos).map_or_else(
70            || Span::new(self.source.len(), self.source.len()),
71            |(_, s)| *s,
72        )
73    }
74
75    fn advance(&mut self) -> Option<SpannedToken> {
76        if self.pos < self.tokens.len() {
77            let tok = self.tokens[self.pos].clone();
78            self.pos += 1;
79            Some(tok)
80        } else {
81            None
82        }
83    }
84
85    const fn at_end(&self) -> bool {
86        self.pos >= self.tokens.len()
87    }
88
89    fn slice(&self, span: Span) -> &'src str {
90        &self.source[span.start..span.end]
91    }
92
93    fn expect(&mut self, expected: &Token) -> ParseResult<Span> {
94        match self.peek() {
95            Some(tok) if std::mem::discriminant(tok) == std::mem::discriminant(expected) => {
96                let (_, span) = self.advance().unwrap();
97                Ok(span)
98            }
99            Some(_) => {
100                let (tok, span) = self.tokens[self.pos].clone();
101                Err(ParseError::unexpected_token(
102                    &tok.to_string(),
103                    &expected.to_string(),
104                    span,
105                ))
106            }
107            None => Err(ParseError::unexpected_eof(
108                &expected.to_string(),
109                self.source.len(),
110            )),
111        }
112    }
113
114    fn expect_identifier(&mut self) -> ParseResult<(String, Span)> {
115        match self.peek() {
116            Some(Token::Identifier) => {
117                let (_, span) = self.advance().unwrap();
118                Ok((self.slice(span).to_string(), span))
119            }
120            // `for`, `let`, `each`, `assert`, `echo` are valid identifiers in module_id context
121            Some(Token::For | Token::Let | Token::Each | Token::Assert | Token::Echo) => {
122                let (tok, span) = self.advance().unwrap();
123                Ok((tok.to_string(), span))
124            }
125            Some(_) => {
126                let (tok, span) = self.tokens[self.pos].clone();
127                Err(ParseError::unexpected_token(
128                    &tok.to_string(),
129                    "identifier",
130                    span,
131                ))
132            }
133            None => Err(ParseError::unexpected_eof("identifier", self.source.len())),
134        }
135    }
136
137    fn eat(&mut self, expected: &Token) -> Option<Span> {
138        if self
139            .peek()
140            .is_some_and(|t| std::mem::discriminant(t) == std::mem::discriminant(expected))
141        {
142            Some(self.advance().unwrap().1)
143        } else {
144            None
145        }
146    }
147
148    // ── File-level ───────────────────────────────────────────
149
150    fn parse_file(&mut self) -> ParseResult<SourceFile> {
151        let mut statements = Vec::new();
152        while !self.at_end() {
153            statements.push(self.parse_statement()?);
154        }
155        let span = if statements.is_empty() {
156            Span::new(0, 0)
157        } else {
158            statements[0]
159                .span()
160                .merge(statements.last().unwrap().span())
161        };
162        Ok(SourceFile { statements, span })
163    }
164
165    // ── Statements ───────────────────────────────────────────
166
167    fn parse_statement(&mut self) -> ParseResult<Statement> {
168        self.enter_recursion()?;
169        let result = self.parse_statement_inner();
170        self.leave_recursion();
171        result
172    }
173
174    fn parse_statement_inner(&mut self) -> ParseResult<Statement> {
175        match self.peek() {
176            Some(Token::Semicolon) => {
177                let span = self.advance().unwrap().1;
178                Ok(Statement::Empty { span })
179            }
180            Some(Token::LBrace) => self.parse_block(),
181            Some(Token::Include) => self.parse_include(),
182            Some(Token::Use) => self.parse_use(),
183            Some(Token::Module) => self.parse_module_def(),
184            Some(Token::Function) => self.parse_function_def(),
185            Some(Token::If) => self.parse_if_else(),
186            // Check for assignment: identifier followed by `=` (but not `==`)
187            Some(Token::Identifier) if self.is_assignment_ahead() => self.parse_assignment(),
188            // Module instantiation (possibly with modifiers)
189            Some(
190                Token::Identifier
191                | Token::For
192                | Token::Let
193                | Token::Assert
194                | Token::Echo
195                | Token::Each
196                | Token::Bang
197                | Token::Hash
198                | Token::Percent
199                | Token::Star,
200            ) => self.parse_module_instantiation(),
201            Some(_) => {
202                let (tok, span) = self.tokens[self.pos].clone();
203                Err(ParseError::unexpected_token(
204                    &tok.to_string(),
205                    "statement",
206                    span,
207                ))
208            }
209            None => Err(ParseError::unexpected_eof("statement", self.source.len())),
210        }
211    }
212
213    fn is_assignment_ahead(&self) -> bool {
214        // Look for `identifier =` where `=` is not `==`
215        if self.pos + 1 < self.tokens.len() {
216            matches!(self.tokens[self.pos + 1].0, Token::Assign)
217                && (self.pos + 2 >= self.tokens.len()
218                    || !matches!(self.tokens[self.pos + 2].0, Token::Assign))
219        } else {
220            false
221        }
222    }
223
224    fn parse_block(&mut self) -> ParseResult<Statement> {
225        let start = self.expect(&Token::LBrace)?;
226        let mut body = Vec::new();
227        while self.peek() != Some(&Token::RBrace) && !self.at_end() {
228            body.push(self.parse_statement()?);
229        }
230        let end = self.expect(&Token::RBrace)?;
231        Ok(Statement::Block {
232            body,
233            span: start.merge(end),
234        })
235    }
236
237    #[allow(clippy::unnecessary_wraps)]
238    fn parse_include(&mut self) -> ParseResult<Statement> {
239        let (_, span) = self.advance().unwrap(); // consume Include token
240        let path = lexer::extract_include_path(self.slice(span)).to_string();
241        Ok(Statement::Include { path, span })
242    }
243
244    #[allow(clippy::unnecessary_wraps)]
245    fn parse_use(&mut self) -> ParseResult<Statement> {
246        let (_, span) = self.advance().unwrap(); // consume Use token
247        let path = lexer::extract_include_path(self.slice(span)).to_string();
248        Ok(Statement::Use { path, span })
249    }
250
251    fn parse_assignment(&mut self) -> ParseResult<Statement> {
252        let (name, start) = self.expect_identifier()?;
253        self.expect(&Token::Assign)?;
254        let expr = self.parse_expr()?;
255        let end = self.expect(&Token::Semicolon)?;
256        Ok(Statement::Assignment {
257            name,
258            expr,
259            span: start.merge(end),
260        })
261    }
262
263    fn parse_module_def(&mut self) -> ParseResult<Statement> {
264        let start = self.expect(&Token::Module)?;
265        let (name, _) = self.expect_identifier()?;
266        self.expect(&Token::LParen)?;
267        let params = self.parse_parameters()?;
268        self.expect(&Token::RParen)?;
269        let body = self.parse_child_body()?;
270        let span = start.merge(
271            body.last()
272                .map_or_else(|| self.peek_span(), super::ast::Statement::span),
273        );
274        Ok(Statement::ModuleDefinition {
275            name,
276            params,
277            body,
278            span: start.merge(span),
279        })
280    }
281
282    fn parse_function_def(&mut self) -> ParseResult<Statement> {
283        let start = self.expect(&Token::Function)?;
284        let (name, _) = self.expect_identifier()?;
285        self.expect(&Token::LParen)?;
286        let params = self.parse_parameters()?;
287        self.expect(&Token::RParen)?;
288        self.expect(&Token::Assign)?;
289        let body = self.parse_expr()?;
290        let end = self.expect(&Token::Semicolon)?;
291        Ok(Statement::FunctionDefinition {
292            name,
293            params,
294            body,
295            span: start.merge(end),
296        })
297    }
298
299    fn parse_if_else(&mut self) -> ParseResult<Statement> {
300        let start = self.expect(&Token::If)?;
301        self.expect(&Token::LParen)?;
302        let condition = self.parse_expr()?;
303        self.expect(&Token::RParen)?;
304        let then_body = self.parse_child_body()?;
305        let else_body = if self.eat(&Token::Else).is_some() {
306            Some(self.parse_child_body()?)
307        } else {
308            None
309        };
310        let end_span = else_body
311            .as_ref()
312            .and_then(|b| b.last().map(super::ast::Statement::span))
313            .or_else(|| then_body.last().map(super::ast::Statement::span))
314            .unwrap_or(start);
315        Ok(Statement::IfElse {
316            condition,
317            then_body,
318            else_body,
319            span: start.merge(end_span),
320        })
321    }
322
323    fn parse_module_instantiation(&mut self) -> ParseResult<Statement> {
324        let start_span = self.peek_span();
325        let mut modifiers = Modifiers::default();
326
327        // Parse modifier prefixes
328        loop {
329            match self.peek() {
330                Some(Token::Bang) => {
331                    self.advance();
332                    modifiers.root = true;
333                }
334                Some(Token::Hash) => {
335                    self.advance();
336                    modifiers.highlight = true;
337                }
338                Some(Token::Percent) => {
339                    self.advance();
340                    modifiers.background = true;
341                }
342                Some(Token::Star) if self.is_modifier_star() => {
343                    self.advance();
344                    modifiers.disable = true;
345                }
346                _ => break,
347            }
348        }
349
350        // After modifiers, we might have if/else
351        if self.peek() == Some(&Token::If) {
352            let stmt = self.parse_if_else()?;
353            // Wrap with modifiers if any were set
354            if modifiers != Modifiers::default()
355                && let Statement::IfElse {
356                    condition,
357                    then_body,
358                    else_body,
359                    span,
360                } = stmt
361            {
362                return Ok(Statement::IfElse {
363                    condition,
364                    then_body,
365                    else_body,
366                    span: start_span.merge(span),
367                });
368            }
369            return Ok(stmt);
370        }
371
372        let (name, _) = self.expect_identifier()?;
373        self.expect(&Token::LParen)?;
374        let args = self.parse_arguments()?;
375        self.expect(&Token::RParen)?;
376
377        let children = self.parse_child_body()?;
378        let end_span = children
379            .last()
380            .map_or_else(|| self.peek_span(), super::ast::Statement::span);
381
382        Ok(Statement::ModuleInstantiation {
383            name,
384            args,
385            children,
386            modifiers,
387            span: start_span.merge(end_span),
388        })
389    }
390
391    /// Check if a `*` is being used as a modifier (followed by an identifier or another modifier)
392    fn is_modifier_star(&self) -> bool {
393        if self.pos + 1 < self.tokens.len() {
394            matches!(
395                self.tokens[self.pos + 1].0,
396                Token::Identifier
397                    | Token::For
398                    | Token::Let
399                    | Token::Assert
400                    | Token::Echo
401                    | Token::Each
402                    | Token::If
403                    | Token::Bang
404                    | Token::Hash
405                    | Token::Percent
406                    | Token::Star
407            )
408        } else {
409            false
410        }
411    }
412
413    /// Parse child body: either `;` (no children), a single statement, or `{ ... }`
414    fn parse_child_body(&mut self) -> ParseResult<Vec<Statement>> {
415        match self.peek() {
416            Some(Token::Semicolon) => {
417                self.advance();
418                Ok(vec![])
419            }
420            Some(Token::LBrace) => {
421                self.advance(); // consume `{`
422                let mut body = Vec::new();
423                while self.peek() != Some(&Token::RBrace) && !self.at_end() {
424                    body.push(self.parse_statement()?);
425                }
426                self.expect(&Token::RBrace)?;
427                Ok(body)
428            }
429            _ => {
430                let stmt = self.parse_statement()?;
431                Ok(vec![stmt])
432            }
433        }
434    }
435
436    // ── Parameters & Arguments ───────────────────────────────
437
438    fn parse_parameters(&mut self) -> ParseResult<Vec<Parameter>> {
439        let mut params = Vec::new();
440        if self.peek() == Some(&Token::RParen) {
441            return Ok(params);
442        }
443        loop {
444            params.push(self.parse_parameter()?);
445            if self.eat(&Token::Comma).is_none() {
446                break;
447            }
448            // Allow trailing comma
449            if self.peek() == Some(&Token::RParen) {
450                break;
451            }
452        }
453        Ok(params)
454    }
455
456    fn parse_parameter(&mut self) -> ParseResult<Parameter> {
457        let (name, span) = self.expect_identifier()?;
458        let default = if self.eat(&Token::Assign).is_some() {
459            Some(self.parse_expr()?)
460        } else {
461            None
462        };
463        let end = default.as_ref().map_or(span, |e| e.span);
464        Ok(Parameter {
465            name,
466            default,
467            span: span.merge(end),
468        })
469    }
470
471    fn parse_arguments(&mut self) -> ParseResult<Vec<Argument>> {
472        let mut args = Vec::new();
473        if self.peek() == Some(&Token::RParen) {
474            return Ok(args);
475        }
476        loop {
477            args.push(self.parse_argument()?);
478            if self.eat(&Token::Comma).is_none() {
479                break;
480            }
481            // Allow trailing comma
482            if self.peek() == Some(&Token::RParen) {
483                break;
484            }
485        }
486        Ok(args)
487    }
488
489    fn parse_argument(&mut self) -> ParseResult<Argument> {
490        // Try named argument: `name = expr`
491        if matches!(self.peek(), Some(Token::Identifier)) && self.is_named_arg_ahead() {
492            let (name, start) = self.expect_identifier()?;
493            self.expect(&Token::Assign)?;
494            let value = self.parse_expr()?;
495            let span = start.merge(value.span);
496            return Ok(Argument {
497                name: Some(name),
498                value,
499                span,
500            });
501        }
502        let value = self.parse_expr()?;
503        let span = value.span;
504        Ok(Argument {
505            name: None,
506            value,
507            span,
508        })
509    }
510
511    fn is_named_arg_ahead(&self) -> bool {
512        if self.pos + 1 < self.tokens.len() {
513            matches!(self.tokens[self.pos + 1].0, Token::Assign)
514                && (self.pos + 2 >= self.tokens.len()
515                    || !matches!(self.tokens[self.pos + 2].0, Token::Assign))
516        } else {
517            false
518        }
519    }
520
521    // ── Expressions (precedence climbing) ────────────────────
522
523    fn parse_expr(&mut self) -> ParseResult<Expr> {
524        self.enter_recursion()?;
525        let result = self.parse_expr_inner();
526        self.leave_recursion();
527        result
528    }
529
530    fn parse_expr_inner(&mut self) -> ParseResult<Expr> {
531        // Check for special expression forms
532        match self.peek() {
533            Some(Token::Function) => return self.parse_anonymous_function(),
534            Some(Token::Let) => return self.parse_let_expr(),
535            Some(Token::Assert) => return self.parse_assert_expr(),
536            Some(Token::Echo) => return self.parse_echo_expr(),
537            _ => {}
538        }
539
540        let mut expr = self.parse_ternary()?;
541
542        // Check for ternary
543        if self.peek() == Some(&Token::Question) {
544            self.advance();
545            let then_expr = self.parse_expr()?;
546            self.expect(&Token::Colon)?;
547            let else_expr = self.parse_expr()?;
548            let span = expr.span.merge(else_expr.span);
549            expr = Expr::new(
550                ExprKind::Ternary {
551                    condition: Box::new(expr),
552                    then_expr: Box::new(then_expr),
553                    else_expr: Box::new(else_expr),
554                },
555                span,
556            );
557        }
558
559        Ok(expr)
560    }
561
562    fn parse_anonymous_function(&mut self) -> ParseResult<Expr> {
563        let start = self.expect(&Token::Function)?;
564        self.expect(&Token::LParen)?;
565        let params = self.parse_parameters()?;
566        self.expect(&Token::RParen)?;
567        let body = self.parse_expr()?;
568        let span = start.merge(body.span);
569        Ok(Expr::new(
570            ExprKind::AnonymousFunction {
571                params,
572                body: Box::new(body),
573            },
574            span,
575        ))
576    }
577
578    fn parse_let_expr(&mut self) -> ParseResult<Expr> {
579        let start = self.expect(&Token::Let)?;
580        self.expect(&Token::LParen)?;
581        let assignments = self.parse_arguments()?;
582        self.expect(&Token::RParen)?;
583        let body = self.parse_expr()?;
584        let span = start.merge(body.span);
585        Ok(Expr::new(
586            ExprKind::Let {
587                assignments,
588                body: Box::new(body),
589            },
590            span,
591        ))
592    }
593
594    fn parse_assert_expr(&mut self) -> ParseResult<Expr> {
595        let start = self.expect(&Token::Assert)?;
596        self.expect(&Token::LParen)?;
597        let args = self.parse_arguments()?;
598        self.expect(&Token::RParen)?;
599        let body = if !self.at_end()
600            && !matches!(
601                self.peek(),
602                Some(Token::Semicolon | Token::RParen | Token::RBracket | Token::Comma)
603            ) {
604            Some(Box::new(self.parse_expr()?))
605        } else {
606            None
607        };
608        let end = body.as_ref().map_or(start, |b| b.span);
609        Ok(Expr::new(ExprKind::Assert { args, body }, start.merge(end)))
610    }
611
612    fn parse_echo_expr(&mut self) -> ParseResult<Expr> {
613        let start = self.expect(&Token::Echo)?;
614        self.expect(&Token::LParen)?;
615        let args = self.parse_arguments()?;
616        self.expect(&Token::RParen)?;
617        let body = if !self.at_end()
618            && !matches!(
619                self.peek(),
620                Some(Token::Semicolon | Token::RParen | Token::RBracket | Token::Comma)
621            ) {
622            Some(Box::new(self.parse_expr()?))
623        } else {
624            None
625        };
626        let end = body.as_ref().map_or(start, |b| b.span);
627        Ok(Expr::new(ExprKind::Echo { args, body }, start.merge(end)))
628    }
629
630    // Precedence levels (lowest to highest):
631    // 1. ternary (handled in parse_expr)
632    // 2. logical or
633    // 3. logical and
634    // 4. equality
635    // 5. comparison
636    // 6. bitwise or
637    // 7. bitwise and
638    // 8. shift
639    // 9. addition
640    // 10. multiplication
641    // 11. exponent
642    // 12. unary
643    // 13. postfix (call, index, member)
644    // 14. primary
645
646    fn parse_ternary(&mut self) -> ParseResult<Expr> {
647        self.parse_logical_or()
648    }
649
650    fn parse_logical_or(&mut self) -> ParseResult<Expr> {
651        let mut left = self.parse_logical_and()?;
652        while self.peek() == Some(&Token::Or) {
653            self.advance();
654            let right = self.parse_logical_and()?;
655            let span = left.span.merge(right.span);
656            left = Expr::new(
657                ExprKind::BinaryOp {
658                    op: BinaryOp::LogicalOr,
659                    left: Box::new(left),
660                    right: Box::new(right),
661                },
662                span,
663            );
664        }
665        Ok(left)
666    }
667
668    fn parse_logical_and(&mut self) -> ParseResult<Expr> {
669        let mut left = self.parse_equality()?;
670        while self.peek() == Some(&Token::And) {
671            self.advance();
672            let right = self.parse_equality()?;
673            let span = left.span.merge(right.span);
674            left = Expr::new(
675                ExprKind::BinaryOp {
676                    op: BinaryOp::LogicalAnd,
677                    left: Box::new(left),
678                    right: Box::new(right),
679                },
680                span,
681            );
682        }
683        Ok(left)
684    }
685
686    fn parse_equality(&mut self) -> ParseResult<Expr> {
687        let mut left = self.parse_comparison()?;
688        loop {
689            let op = match self.peek() {
690                Some(Token::EqualEqual) => BinaryOp::Equal,
691                Some(Token::NotEqual) => BinaryOp::NotEqual,
692                _ => break,
693            };
694            self.advance();
695            let right = self.parse_comparison()?;
696            let span = left.span.merge(right.span);
697            left = Expr::new(
698                ExprKind::BinaryOp {
699                    op,
700                    left: Box::new(left),
701                    right: Box::new(right),
702                },
703                span,
704            );
705        }
706        Ok(left)
707    }
708
709    fn parse_comparison(&mut self) -> ParseResult<Expr> {
710        let mut left = self.parse_bitwise_or()?;
711        loop {
712            let op = match self.peek() {
713                Some(Token::Less) => BinaryOp::Less,
714                Some(Token::LessEqual) => BinaryOp::LessEqual,
715                Some(Token::Greater) => BinaryOp::Greater,
716                Some(Token::GreaterEqual) => BinaryOp::GreaterEqual,
717                _ => break,
718            };
719            self.advance();
720            let right = self.parse_bitwise_or()?;
721            let span = left.span.merge(right.span);
722            left = Expr::new(
723                ExprKind::BinaryOp {
724                    op,
725                    left: Box::new(left),
726                    right: Box::new(right),
727                },
728                span,
729            );
730        }
731        Ok(left)
732    }
733
734    fn parse_bitwise_or(&mut self) -> ParseResult<Expr> {
735        let mut left = self.parse_bitwise_and()?;
736        while self.peek() == Some(&Token::Pipe) {
737            self.advance();
738            let right = self.parse_bitwise_and()?;
739            let span = left.span.merge(right.span);
740            left = Expr::new(
741                ExprKind::BinaryOp {
742                    op: BinaryOp::BitwiseOr,
743                    left: Box::new(left),
744                    right: Box::new(right),
745                },
746                span,
747            );
748        }
749        Ok(left)
750    }
751
752    fn parse_bitwise_and(&mut self) -> ParseResult<Expr> {
753        let mut left = self.parse_shift()?;
754        while self.peek() == Some(&Token::Ampersand) {
755            self.advance();
756            let right = self.parse_shift()?;
757            let span = left.span.merge(right.span);
758            left = Expr::new(
759                ExprKind::BinaryOp {
760                    op: BinaryOp::BitwiseAnd,
761                    left: Box::new(left),
762                    right: Box::new(right),
763                },
764                span,
765            );
766        }
767        Ok(left)
768    }
769
770    fn parse_shift(&mut self) -> ParseResult<Expr> {
771        let mut left = self.parse_addition()?;
772        loop {
773            let op = match self.peek() {
774                Some(Token::ShiftLeft) => BinaryOp::ShiftLeft,
775                Some(Token::ShiftRight) => BinaryOp::ShiftRight,
776                _ => break,
777            };
778            self.advance();
779            let right = self.parse_addition()?;
780            let span = left.span.merge(right.span);
781            left = Expr::new(
782                ExprKind::BinaryOp {
783                    op,
784                    left: Box::new(left),
785                    right: Box::new(right),
786                },
787                span,
788            );
789        }
790        Ok(left)
791    }
792
793    fn parse_addition(&mut self) -> ParseResult<Expr> {
794        let mut left = self.parse_multiplication()?;
795        loop {
796            let op = match self.peek() {
797                Some(Token::Plus) => BinaryOp::Add,
798                Some(Token::Minus) => BinaryOp::Subtract,
799                _ => break,
800            };
801            self.advance();
802            let right = self.parse_multiplication()?;
803            let span = left.span.merge(right.span);
804            left = Expr::new(
805                ExprKind::BinaryOp {
806                    op,
807                    left: Box::new(left),
808                    right: Box::new(right),
809                },
810                span,
811            );
812        }
813        Ok(left)
814    }
815
816    fn parse_multiplication(&mut self) -> ParseResult<Expr> {
817        let mut left = self.parse_unary()?;
818        loop {
819            let op = match self.peek() {
820                Some(Token::Star) => BinaryOp::Multiply,
821                Some(Token::Slash) => BinaryOp::Divide,
822                Some(Token::Percent) => BinaryOp::Modulo,
823                _ => break,
824            };
825            self.advance();
826            let right = self.parse_unary()?;
827            let span = left.span.merge(right.span);
828            left = Expr::new(
829                ExprKind::BinaryOp {
830                    op,
831                    left: Box::new(left),
832                    right: Box::new(right),
833                },
834                span,
835            );
836        }
837        Ok(left)
838    }
839
840    fn parse_unary(&mut self) -> ParseResult<Expr> {
841        match self.peek() {
842            Some(Token::Minus) => {
843                let start = self.advance().unwrap().1;
844                let operand = self.parse_unary()?;
845                // Optimize: fold negative number literals
846                if let ExprKind::Number(n) = operand.kind {
847                    return Ok(Expr::new(ExprKind::Number(-n), start.merge(operand.span)));
848                }
849                let span = start.merge(operand.span);
850                Ok(Expr::new(
851                    ExprKind::UnaryOp {
852                        op: UnaryOp::Negate,
853                        operand: Box::new(operand),
854                    },
855                    span,
856                ))
857            }
858            Some(Token::Plus) => {
859                self.advance();
860                self.parse_unary()
861            }
862            Some(Token::Bang) => {
863                let start = self.advance().unwrap().1;
864                let operand = self.parse_unary()?;
865                let span = start.merge(operand.span);
866                Ok(Expr::new(
867                    ExprKind::UnaryOp {
868                        op: UnaryOp::Not,
869                        operand: Box::new(operand),
870                    },
871                    span,
872                ))
873            }
874            Some(Token::Tilde) => {
875                let start = self.advance().unwrap().1;
876                let operand = self.parse_unary()?;
877                let span = start.merge(operand.span);
878                Ok(Expr::new(
879                    ExprKind::UnaryOp {
880                        op: UnaryOp::BinaryNot,
881                        operand: Box::new(operand),
882                    },
883                    span,
884                ))
885            }
886            _ => self.parse_exponent(),
887        }
888    }
889
890    fn parse_exponent(&mut self) -> ParseResult<Expr> {
891        let left = self.parse_postfix()?;
892        if self.peek() == Some(&Token::Caret) {
893            self.advance();
894            // Right-associative: recurse into unary (not exponent)
895            let right = self.parse_unary()?;
896            let span = left.span.merge(right.span);
897            Ok(Expr::new(
898                ExprKind::BinaryOp {
899                    op: BinaryOp::Exponent,
900                    left: Box::new(left),
901                    right: Box::new(right),
902                },
903                span,
904            ))
905        } else {
906            Ok(left)
907        }
908    }
909
910    fn parse_postfix(&mut self) -> ParseResult<Expr> {
911        let mut expr = self.parse_primary()?;
912        loop {
913            match self.peek() {
914                Some(Token::LParen) => {
915                    self.advance();
916                    let args = self.parse_arguments()?;
917                    let end = self.expect(&Token::RParen)?;
918                    let span = expr.span.merge(end);
919                    expr = Expr::new(
920                        ExprKind::FunctionCall {
921                            callee: Box::new(expr),
922                            args,
923                        },
924                        span,
925                    );
926                }
927                Some(Token::LBracket) => {
928                    self.advance();
929                    let index = self.parse_expr()?;
930                    let end = self.expect(&Token::RBracket)?;
931                    let span = expr.span.merge(end);
932                    expr = Expr::new(
933                        ExprKind::Index {
934                            object: Box::new(expr),
935                            index: Box::new(index),
936                        },
937                        span,
938                    );
939                }
940                Some(Token::Dot) => {
941                    self.advance();
942                    let (member, end) = self.expect_identifier()?;
943                    let span = expr.span.merge(end);
944                    expr = Expr::new(
945                        ExprKind::MemberAccess {
946                            object: Box::new(expr),
947                            member,
948                        },
949                        span,
950                    );
951                }
952                _ => break,
953            }
954        }
955        Ok(expr)
956    }
957
958    fn parse_primary(&mut self) -> ParseResult<Expr> {
959        match self.peek() {
960            Some(Token::Number(_)) => {
961                let (tok, span) = self.advance().unwrap();
962                if let Token::Number(n) = tok {
963                    Ok(Expr::new(ExprKind::Number(n), span))
964                } else {
965                    unreachable!()
966                }
967            }
968            Some(Token::String(_)) => {
969                let (tok, span) = self.advance().unwrap();
970                if let Token::String(s) = tok {
971                    Ok(Expr::new(ExprKind::String(s), span))
972                } else {
973                    unreachable!()
974                }
975            }
976            Some(Token::True) => {
977                let span = self.advance().unwrap().1;
978                Ok(Expr::new(ExprKind::BoolTrue, span))
979            }
980            Some(Token::False) => {
981                let span = self.advance().unwrap().1;
982                Ok(Expr::new(ExprKind::BoolFalse, span))
983            }
984            Some(Token::Undef) => {
985                let span = self.advance().unwrap().1;
986                Ok(Expr::new(ExprKind::Undef, span))
987            }
988            Some(Token::Identifier) => {
989                let (_, span) = self.advance().unwrap();
990                let name = self.slice(span).to_string();
991                Ok(Expr::new(ExprKind::Identifier(name), span))
992            }
993            Some(Token::LParen) => {
994                self.advance();
995                let expr = self.parse_expr()?;
996                self.expect(&Token::RParen)?;
997                Ok(expr)
998            }
999            Some(Token::LBracket) => self.parse_vector_or_range(),
1000            Some(_) => {
1001                let (tok, span) = self.tokens[self.pos].clone();
1002                Err(ParseError::unexpected_token(
1003                    &tok.to_string(),
1004                    "expression",
1005                    span,
1006                ))
1007            }
1008            None => Err(ParseError::unexpected_eof("expression", self.source.len())),
1009        }
1010    }
1011
1012    fn parse_vector_or_range(&mut self) -> ParseResult<Expr> {
1013        let start = self.expect(&Token::LBracket)?;
1014
1015        // Empty vector
1016        if self.peek() == Some(&Token::RBracket) {
1017            let end = self.advance().unwrap().1;
1018            return Ok(Expr::new(ExprKind::Vector(vec![]), start.merge(end)));
1019        }
1020
1021        // Check for list comprehension elements at start of vector
1022        if matches!(
1023            self.peek(),
1024            Some(Token::For | Token::Let | Token::If | Token::Each)
1025        ) {
1026            let lc = self.parse_list_comprehension_element()?;
1027            let mut elements = vec![lc];
1028            while self.eat(&Token::Comma).is_some() {
1029                if self.peek() == Some(&Token::RBracket) {
1030                    break;
1031                }
1032                if matches!(
1033                    self.peek(),
1034                    Some(Token::For | Token::Let | Token::If | Token::Each)
1035                ) {
1036                    elements.push(self.parse_list_comprehension_element()?);
1037                } else {
1038                    elements.push(self.parse_expr()?);
1039                }
1040            }
1041            let end = self.expect(&Token::RBracket)?;
1042            return Ok(Expr::new(ExprKind::Vector(elements), start.merge(end)));
1043        }
1044
1045        // Parse first expression
1046        let first = self.parse_expr()?;
1047
1048        // Check for range syntax: [start : end] or [start : step : end]
1049        if self.peek() == Some(&Token::Colon) {
1050            self.advance();
1051            let second = self.parse_expr()?;
1052            if self.peek() == Some(&Token::Colon) {
1053                // [start : step : end]
1054                self.advance();
1055                let third = self.parse_expr()?;
1056                let end = self.expect(&Token::RBracket)?;
1057                return Ok(Expr::new(
1058                    ExprKind::Range {
1059                        start: Box::new(first),
1060                        step: Some(Box::new(second)),
1061                        end: Box::new(third),
1062                    },
1063                    start.merge(end),
1064                ));
1065            }
1066            // [start : end]
1067            let end = self.expect(&Token::RBracket)?;
1068            return Ok(Expr::new(
1069                ExprKind::Range {
1070                    start: Box::new(first),
1071                    step: None,
1072                    end: Box::new(second),
1073                },
1074                start.merge(end),
1075            ));
1076        }
1077
1078        // Vector: collect remaining elements
1079        let mut elements = vec![first];
1080        while self.eat(&Token::Comma).is_some() {
1081            // Trailing comma
1082            if self.peek() == Some(&Token::RBracket) {
1083                break;
1084            }
1085            // Check for list comprehension inside vector
1086            if matches!(
1087                self.peek(),
1088                Some(Token::For | Token::Let | Token::If | Token::Each)
1089            ) {
1090                elements.push(self.parse_list_comprehension_element()?);
1091            } else {
1092                elements.push(self.parse_expr()?);
1093            }
1094        }
1095        let end = self.expect(&Token::RBracket)?;
1096        Ok(Expr::new(ExprKind::Vector(elements), start.merge(end)))
1097    }
1098
1099    fn parse_list_comprehension_element(&mut self) -> ParseResult<Expr> {
1100        match self.peek() {
1101            Some(Token::For) => {
1102                let start = self.advance().unwrap().1;
1103                self.expect(&Token::LParen)?;
1104                let args = self.parse_arguments()?;
1105
1106                // Check for C-style for: `for (init ; cond ; update)`
1107                if self.peek() == Some(&Token::Semicolon) {
1108                    self.advance();
1109                    let condition = self.parse_expr()?;
1110                    self.expect(&Token::Semicolon)?;
1111                    let update = self.parse_arguments()?;
1112                    self.expect(&Token::RParen)?;
1113                    let body = self.parse_lc_body()?;
1114                    let span = start.merge(body.span);
1115                    return Ok(Expr::new(
1116                        ExprKind::LcForC {
1117                            init: args,
1118                            condition: Box::new(condition),
1119                            update,
1120                            body: Box::new(body),
1121                        },
1122                        span,
1123                    ));
1124                }
1125
1126                self.expect(&Token::RParen)?;
1127                let body = self.parse_lc_body()?;
1128                let span = start.merge(body.span);
1129                Ok(Expr::new(
1130                    ExprKind::LcFor {
1131                        assignments: args,
1132                        body: Box::new(body),
1133                    },
1134                    span,
1135                ))
1136            }
1137            Some(Token::Let) => {
1138                let start = self.advance().unwrap().1;
1139                self.expect(&Token::LParen)?;
1140                let assignments = self.parse_arguments()?;
1141                self.expect(&Token::RParen)?;
1142                let body = self.parse_lc_body()?;
1143                let span = start.merge(body.span);
1144                Ok(Expr::new(
1145                    ExprKind::LcLet {
1146                        assignments,
1147                        body: Box::new(body),
1148                    },
1149                    span,
1150                ))
1151            }
1152            Some(Token::If) => {
1153                let start = self.advance().unwrap().1;
1154                self.expect(&Token::LParen)?;
1155                let condition = self.parse_expr()?;
1156                self.expect(&Token::RParen)?;
1157                let then_expr = self.parse_lc_body()?;
1158                let else_expr = if self.eat(&Token::Else).is_some() {
1159                    Some(Box::new(self.parse_lc_body()?))
1160                } else {
1161                    None
1162                };
1163                let end = else_expr.as_ref().map_or(then_expr.span, |e| e.span);
1164                Ok(Expr::new(
1165                    ExprKind::LcIf {
1166                        condition: Box::new(condition),
1167                        then_expr: Box::new(then_expr),
1168                        else_expr,
1169                    },
1170                    start.merge(end),
1171                ))
1172            }
1173            Some(Token::Each) => {
1174                let start = self.advance().unwrap().1;
1175                let body = self.parse_lc_body()?;
1176                let span = start.merge(body.span);
1177                Ok(Expr::new(
1178                    ExprKind::LcEach {
1179                        body: Box::new(body),
1180                    },
1181                    span,
1182                ))
1183            }
1184            _ => self.parse_expr(),
1185        }
1186    }
1187
1188    /// Parse body of a list comprehension element (could be another LC element or an expression)
1189    fn parse_lc_body(&mut self) -> ParseResult<Expr> {
1190        if matches!(
1191            self.peek(),
1192            Some(Token::For | Token::Let | Token::If | Token::Each)
1193        ) {
1194            self.parse_list_comprehension_element()
1195        } else if self.peek() == Some(&Token::LParen)
1196            && matches!(
1197                self.peek_at(1),
1198                Some(Token::For | Token::Let | Token::If | Token::Each)
1199            )
1200        {
1201            // Parenthesized list comprehension: ( for(...) ... )
1202            self.advance();
1203            let inner = self.parse_list_comprehension_element()?;
1204            self.expect(&Token::RParen)?;
1205            Ok(inner)
1206        } else {
1207            self.parse_expr()
1208        }
1209    }
1210}
1211
1212#[cfg(test)]
1213mod tests {
1214    use super::*;
1215
1216    fn parse_ok(source: &str) -> SourceFile {
1217        parse(source).unwrap_or_else(|e| panic!("parse error for `{source}`: {e}"))
1218    }
1219
1220    fn parse_err(source: &str) -> ParseError {
1221        parse(source).unwrap_err()
1222    }
1223
1224    #[test]
1225    fn test_empty() {
1226        let file = parse_ok("");
1227        assert!(file.statements.is_empty());
1228    }
1229
1230    #[test]
1231    fn test_assignment() {
1232        let file = parse_ok("x = 42;");
1233        assert_eq!(file.statements.len(), 1);
1234        match &file.statements[0] {
1235            Statement::Assignment { name, expr, .. } => {
1236                assert_eq!(name, "x");
1237                assert!(
1238                    matches!(expr.kind, ExprKind::Number(n) if (n - 42.0).abs() < f64::EPSILON)
1239                );
1240            }
1241            other => panic!("expected assignment, got {other:?}"),
1242        }
1243    }
1244
1245    #[test]
1246    fn test_module_instantiation() {
1247        let file = parse_ok("cube(10);");
1248        assert_eq!(file.statements.len(), 1);
1249        match &file.statements[0] {
1250            Statement::ModuleInstantiation { name, args, .. } => {
1251                assert_eq!(name, "cube");
1252                assert_eq!(args.len(), 1);
1253            }
1254            other => panic!("expected module instantiation, got {other:?}"),
1255        }
1256    }
1257
1258    #[test]
1259    fn test_module_with_children() {
1260        let file = parse_ok("translate([1,2,3]) { cube(5); sphere(3); }");
1261        match &file.statements[0] {
1262            Statement::ModuleInstantiation { name, children, .. } => {
1263                assert_eq!(name, "translate");
1264                assert_eq!(children.len(), 2);
1265            }
1266            other => panic!("expected module instantiation, got {other:?}"),
1267        }
1268    }
1269
1270    #[test]
1271    fn test_module_definition() {
1272        let file = parse_ok("module box(size = 10, h) { cube(size); }");
1273        match &file.statements[0] {
1274            Statement::ModuleDefinition {
1275                name, params, body, ..
1276            } => {
1277                assert_eq!(name, "box");
1278                assert_eq!(params.len(), 2);
1279                assert_eq!(params[0].name, "size");
1280                assert!(params[0].default.is_some());
1281                assert_eq!(params[1].name, "h");
1282                assert!(params[1].default.is_none());
1283                assert_eq!(body.len(), 1);
1284            }
1285            other => panic!("expected module def, got {other:?}"),
1286        }
1287    }
1288
1289    #[test]
1290    fn test_function_definition() {
1291        let file = parse_ok("function add(a, b) = a + b;");
1292        match &file.statements[0] {
1293            Statement::FunctionDefinition {
1294                name, params, body, ..
1295            } => {
1296                assert_eq!(name, "add");
1297                assert_eq!(params.len(), 2);
1298                assert!(matches!(
1299                    body.kind,
1300                    ExprKind::BinaryOp {
1301                        op: BinaryOp::Add,
1302                        ..
1303                    }
1304                ));
1305            }
1306            other => panic!("expected function def, got {other:?}"),
1307        }
1308    }
1309
1310    #[test]
1311    fn test_if_else() {
1312        let file = parse_ok("if (x > 0) cube(x); else sphere(1);");
1313        match &file.statements[0] {
1314            Statement::IfElse {
1315                then_body,
1316                else_body,
1317                ..
1318            } => {
1319                assert_eq!(then_body.len(), 1);
1320                assert!(else_body.is_some());
1321                assert_eq!(else_body.as_ref().unwrap().len(), 1);
1322            }
1323            other => panic!("expected if/else, got {other:?}"),
1324        }
1325    }
1326
1327    #[test]
1328    fn test_modifiers() {
1329        let file = parse_ok("!#cube(10);");
1330        match &file.statements[0] {
1331            Statement::ModuleInstantiation { modifiers, .. } => {
1332                assert!(modifiers.root);
1333                assert!(modifiers.highlight);
1334                assert!(!modifiers.background);
1335                assert!(!modifiers.disable);
1336            }
1337            other => panic!("expected module instantiation, got {other:?}"),
1338        }
1339    }
1340
1341    #[test]
1342    fn test_vector() {
1343        let file = parse_ok("x = [1, 2, 3];");
1344        match &file.statements[0] {
1345            Statement::Assignment { expr, .. } => {
1346                assert!(matches!(expr.kind, ExprKind::Vector(ref v) if v.len() == 3));
1347            }
1348            other => panic!("expected assignment, got {other:?}"),
1349        }
1350    }
1351
1352    #[test]
1353    fn test_range() {
1354        let file = parse_ok("x = [0:10];");
1355        match &file.statements[0] {
1356            Statement::Assignment { expr, .. } => {
1357                assert!(matches!(expr.kind, ExprKind::Range { step: None, .. }));
1358            }
1359            other => panic!("expected assignment, got {other:?}"),
1360        }
1361    }
1362
1363    #[test]
1364    fn test_range_with_step() {
1365        let file = parse_ok("x = [0:2:10];");
1366        match &file.statements[0] {
1367            Statement::Assignment { expr, .. } => {
1368                assert!(matches!(expr.kind, ExprKind::Range { step: Some(_), .. }));
1369            }
1370            other => panic!("expected assignment, got {other:?}"),
1371        }
1372    }
1373
1374    #[test]
1375    fn test_include_use() {
1376        let file = parse_ok("include <lib/base.scad>\nuse <utils.scad>");
1377        assert_eq!(file.statements.len(), 2);
1378        match &file.statements[0] {
1379            Statement::Include { path, .. } => assert_eq!(path, "lib/base.scad"),
1380            other => panic!("expected include, got {other:?}"),
1381        }
1382        match &file.statements[1] {
1383            Statement::Use { path, .. } => assert_eq!(path, "utils.scad"),
1384            other => panic!("expected use, got {other:?}"),
1385        }
1386    }
1387
1388    #[test]
1389    fn test_operator_precedence() {
1390        let file = parse_ok("x = 1 + 2 * 3;");
1391        match &file.statements[0] {
1392            Statement::Assignment { expr, .. } => {
1393                // Should be Add(1, Mul(2, 3))
1394                match &expr.kind {
1395                    ExprKind::BinaryOp {
1396                        op: BinaryOp::Add,
1397                        right,
1398                        ..
1399                    } => {
1400                        assert!(matches!(
1401                            right.kind,
1402                            ExprKind::BinaryOp {
1403                                op: BinaryOp::Multiply,
1404                                ..
1405                            }
1406                        ));
1407                    }
1408                    other => panic!("expected Add, got {other:?}"),
1409                }
1410            }
1411            other => panic!("expected assignment, got {other:?}"),
1412        }
1413    }
1414
1415    #[test]
1416    fn test_ternary() {
1417        let file = parse_ok("x = a ? b : c;");
1418        match &file.statements[0] {
1419            Statement::Assignment { expr, .. } => {
1420                assert!(matches!(expr.kind, ExprKind::Ternary { .. }));
1421            }
1422            other => panic!("expected assignment, got {other:?}"),
1423        }
1424    }
1425
1426    #[test]
1427    fn test_anonymous_function() {
1428        let file = parse_ok("f = function(x) x * 2;");
1429        match &file.statements[0] {
1430            Statement::Assignment { expr, .. } => {
1431                assert!(matches!(expr.kind, ExprKind::AnonymousFunction { .. }));
1432            }
1433            other => panic!("expected assignment, got {other:?}"),
1434        }
1435    }
1436
1437    #[test]
1438    fn test_list_comprehension() {
1439        let file = parse_ok("x = [for (i = [0:10]) i * 2];");
1440        assert_eq!(file.statements.len(), 1);
1441    }
1442
1443    #[test]
1444    fn test_nested_modules() {
1445        let file = parse_ok("rotate([0, 0, 45]) translate([10, 0, 0]) cube(5);");
1446        assert_eq!(file.statements.len(), 1);
1447        match &file.statements[0] {
1448            Statement::ModuleInstantiation { name, children, .. } => {
1449                assert_eq!(name, "rotate");
1450                assert_eq!(children.len(), 1);
1451            }
1452            other => panic!("expected module instantiation, got {other:?}"),
1453        }
1454    }
1455
1456    #[test]
1457    fn test_complex_program() {
1458        let source = r"
1459            // A parametric box
1460            module rounded_box(size = [10, 10, 10], r = 1) {
1461                if (r > 0) {
1462                    translate([r, r, 0])
1463                        cube(size - [2*r, 2*r, 0]);
1464                } else {
1465                    cube(size);
1466                }
1467            }
1468            
1469            function area(w, h) = w * h;
1470            
1471            x = area(10, 20);
1472            rounded_box(size = [x, 30, 5], r = 2);
1473        ";
1474        let file = parse_ok(source);
1475        assert_eq!(file.statements.len(), 4);
1476    }
1477
1478    #[test]
1479    fn test_error_missing_semicolon() {
1480        let err = parse_err("x = 42");
1481        assert!(matches!(err, ParseError::UnexpectedEof { .. }));
1482    }
1483
1484    #[test]
1485    fn test_named_arguments() {
1486        let file = parse_ok("cube(size = 10, center = true);");
1487        match &file.statements[0] {
1488            Statement::ModuleInstantiation { args, .. } => {
1489                assert_eq!(args.len(), 2);
1490                assert_eq!(args[0].name.as_deref(), Some("size"));
1491                assert_eq!(args[1].name.as_deref(), Some("center"));
1492            }
1493            other => panic!("expected module instantiation, got {other:?}"),
1494        }
1495    }
1496
1497    #[test]
1498    fn test_member_access() {
1499        let file = parse_ok("x = v.x;");
1500        match &file.statements[0] {
1501            Statement::Assignment { expr, .. } => {
1502                assert!(
1503                    matches!(expr.kind, ExprKind::MemberAccess { ref member, .. } if member == "x")
1504                );
1505            }
1506            other => panic!("expected assignment, got {other:?}"),
1507        }
1508    }
1509
1510    #[test]
1511    fn test_index_access() {
1512        let file = parse_ok("x = v[0];");
1513        match &file.statements[0] {
1514            Statement::Assignment { expr, .. } => {
1515                assert!(matches!(expr.kind, ExprKind::Index { .. }));
1516            }
1517            other => panic!("expected assignment, got {other:?}"),
1518        }
1519    }
1520
1521    #[test]
1522    fn test_let_expression() {
1523        let file = parse_ok("x = let(a = 1, b = 2) a + b;");
1524        match &file.statements[0] {
1525            Statement::Assignment { expr, .. } => {
1526                assert!(matches!(expr.kind, ExprKind::Let { .. }));
1527            }
1528            other => panic!("expected assignment, got {other:?}"),
1529        }
1530    }
1531}