Skip to main content

patch_rexx/
parser.rs

1//! REXX recursive descent parser — tokens to AST.
2//!
3//! Consumes a `Vec<Token>` from the lexer and produces an `ast::Program`.
4//! REXX has no reserved words; keywords like SAY, IF, DO are just symbols
5//! recognised by context at the start of a clause.
6
7use crate::ast::{
8    AddressAction, AssignTarget, BinOp, Clause, ClauseKind, Condition, ControlledLoop, DoBlock,
9    DoKind, Expr, NumericFormSetting, NumericSetting, ParseSource, ParseTemplate, Program,
10    SignalAction, TemplateElement, UnaryOp,
11};
12use crate::error::{RexxDiagnostic, RexxError, RexxResult, SourceLoc};
13use crate::lexer::{Token, TokenKind};
14
15pub struct Parser {
16    tokens: Vec<Token>,
17    pos: usize,
18    /// Depth counter: >0 while parsing a DO header so that symbols like
19    /// TO, BY, FOR, WHILE, UNTIL are not consumed by implicit concatenation.
20    do_header_depth: usize,
21    /// Depth counter: >0 while parsing IF condition / WHEN condition so that
22    /// THEN is not consumed by implicit concatenation.
23    condition_depth: usize,
24    /// Depth counter: >0 while parsing IF THEN/ELSE clauses so that
25    /// ELSE is not consumed by implicit concatenation.
26    if_depth: usize,
27    /// Depth counter: >0 while parsing PARSE VALUE expr WITH so that
28    /// WITH is not consumed by implicit concatenation.
29    parse_value_depth: usize,
30}
31
32impl Parser {
33    pub fn new(tokens: Vec<Token>) -> Self {
34        Self {
35            tokens,
36            pos: 0,
37            do_header_depth: 0,
38            condition_depth: 0,
39            if_depth: 0,
40            parse_value_depth: 0,
41        }
42    }
43
44    pub fn parse(&mut self) -> RexxResult<Program> {
45        let mut clauses = Vec::new();
46        self.skip_terminators();
47        while !self.at_end() {
48            clauses.push(self.parse_clause()?);
49            self.skip_terminators();
50        }
51        Ok(Program { clauses })
52    }
53
54    // ── helpers ──────────────────────────────────────────────────────
55
56    fn peek(&self) -> &Token {
57        &self.tokens[self.pos]
58    }
59
60    fn peek_kind(&self) -> &TokenKind {
61        &self.tokens[self.pos].kind
62    }
63
64    fn at_end(&self) -> bool {
65        matches!(self.peek_kind(), TokenKind::Eof)
66    }
67
68    fn advance(&mut self) -> &Token {
69        let tok = &self.tokens[self.pos];
70        if !self.at_end() {
71            self.pos += 1;
72        }
73        tok
74    }
75
76    fn loc(&self) -> SourceLoc {
77        self.peek().loc.clone()
78    }
79
80    fn expect(&mut self, kind: &TokenKind) -> RexxResult<&Token> {
81        if &self.tokens[self.pos].kind == kind {
82            let tok = &self.tokens[self.pos];
83            self.pos += 1;
84            Ok(tok)
85        } else {
86            Err(RexxDiagnostic::new(RexxError::InvalidExpression)
87                .at(self.loc())
88                .with_detail(format!("expected {kind:?}, found {:?}", self.peek_kind())))
89        }
90    }
91
92    /// True if the current token is a clause terminator (`;`, `Eol`, `Eof`).
93    fn is_terminator(&self) -> bool {
94        matches!(
95            self.peek_kind(),
96            TokenKind::Semicolon | TokenKind::Eol | TokenKind::Eof
97        )
98    }
99
100    fn skip_terminators(&mut self) {
101        while matches!(self.peek_kind(), TokenKind::Semicolon | TokenKind::Eol) {
102            self.advance();
103        }
104    }
105
106    /// Case-insensitive keyword check on a Symbol token value.
107    fn check_keyword(sym: &str, keyword: &str) -> bool {
108        sym.eq_ignore_ascii_case(keyword)
109    }
110
111    /// Check if current token is a symbol matching the given keyword.
112    fn is_keyword(&self, keyword: &str) -> bool {
113        if let TokenKind::Symbol(name) = self.peek_kind() {
114            Self::check_keyword(name, keyword)
115        } else {
116            false
117        }
118    }
119
120    /// Peek ahead by `n` tokens (0 = current).
121    fn peek_at(&self, n: usize) -> &TokenKind {
122        let idx = self.pos + n;
123        if idx < self.tokens.len() {
124            &self.tokens[idx].kind
125        } else {
126            &TokenKind::Eof
127        }
128    }
129
130    /// Check if the current token is THEN (stops implicit concat in conditions).
131    fn is_then_keyword(&self) -> bool {
132        if let TokenKind::Symbol(name) = self.peek_kind() {
133            Self::check_keyword(name, "THEN")
134        } else {
135            false
136        }
137    }
138
139    /// Check if the current token is ELSE (stops implicit concat in IF clauses).
140    fn is_else_keyword(&self) -> bool {
141        if let TokenKind::Symbol(name) = self.peek_kind() {
142            Self::check_keyword(name, "ELSE")
143        } else {
144            false
145        }
146    }
147
148    /// Check if the current token is WITH (stops implicit concat in PARSE VALUE).
149    fn is_with_keyword(&self) -> bool {
150        if let TokenKind::Symbol(name) = self.peek_kind() {
151            Self::check_keyword(name, "WITH")
152        } else {
153            false
154        }
155    }
156
157    /// Check if the current token is a DO-header keyword that should stop
158    /// implicit concatenation.
159    fn is_do_header_keyword(&self) -> bool {
160        if let TokenKind::Symbol(name) = self.peek_kind() {
161            name.eq_ignore_ascii_case("TO")
162                || name.eq_ignore_ascii_case("BY")
163                || name.eq_ignore_ascii_case("FOR")
164                || name.eq_ignore_ascii_case("WHILE")
165                || name.eq_ignore_ascii_case("UNTIL")
166        } else {
167            false
168        }
169    }
170
171    // ── clause parsing ──────────────────────────────────────────────
172
173    #[allow(clippy::too_many_lines)]
174    fn parse_clause(&mut self) -> RexxResult<Clause> {
175        let loc = self.loc();
176
177        // Look at the first token
178        if let TokenKind::Symbol(ref name) = self.peek_kind().clone() {
179            // Symbol + Colon -> Label
180            if matches!(self.peek_at(1), TokenKind::Colon) {
181                let label = name.to_uppercase();
182                self.advance(); // symbol
183                self.advance(); // colon
184                return Ok(Clause {
185                    kind: ClauseKind::Label(label),
186                    loc,
187                });
188            }
189
190            // Symbol + = -> Assignment
191            if matches!(self.peek_at(1), TokenKind::Assign) {
192                return self.parse_assignment(&loc);
193            }
194
195            // SAY instruction
196            if Self::check_keyword(name, "SAY") {
197                self.advance(); // consume SAY
198                let expr = if self.is_terminator() {
199                    // SAY with no expression outputs empty line
200                    Expr::StringLit(String::new())
201                } else {
202                    self.parse_expression()?
203                };
204                return Ok(Clause {
205                    kind: ClauseKind::Say(expr),
206                    loc,
207                });
208            }
209
210            // NOP instruction
211            if Self::check_keyword(name, "NOP") {
212                self.advance();
213                return Ok(Clause {
214                    kind: ClauseKind::Nop,
215                    loc,
216                });
217            }
218
219            // IF instruction
220            if Self::check_keyword(name, "IF") {
221                return self.parse_if();
222            }
223
224            // DO instruction
225            if Self::check_keyword(name, "DO") {
226                return self.parse_do();
227            }
228
229            // SELECT instruction
230            if Self::check_keyword(name, "SELECT") {
231                return self.parse_select();
232            }
233
234            // LEAVE instruction
235            if Self::check_keyword(name, "LEAVE") {
236                return Ok(self.parse_leave());
237            }
238
239            // ITERATE instruction
240            if Self::check_keyword(name, "ITERATE") {
241                return Ok(self.parse_iterate());
242            }
243
244            // EXIT instruction
245            if Self::check_keyword(name, "EXIT") {
246                return self.parse_exit();
247            }
248
249            // RETURN instruction
250            if Self::check_keyword(name, "RETURN") {
251                return self.parse_return();
252            }
253
254            // CALL instruction
255            if Self::check_keyword(name, "CALL") {
256                return self.parse_call();
257            }
258
259            // PROCEDURE instruction
260            if Self::check_keyword(name, "PROCEDURE") {
261                return Ok(self.parse_procedure());
262            }
263
264            // PARSE instruction
265            if Self::check_keyword(name, "PARSE") {
266                return self.parse_parse();
267            }
268
269            // PULL instruction
270            if Self::check_keyword(name, "PULL") {
271                return self.parse_pull();
272            }
273
274            // ARG instruction
275            if Self::check_keyword(name, "ARG") {
276                return self.parse_arg();
277            }
278
279            // DROP instruction
280            if Self::check_keyword(name, "DROP") {
281                return Ok(self.parse_drop());
282            }
283
284            // SIGNAL instruction
285            if Self::check_keyword(name, "SIGNAL") {
286                return self.parse_signal();
287            }
288
289            // INTERPRET instruction
290            if Self::check_keyword(name, "INTERPRET") {
291                return self.parse_interpret();
292            }
293
294            // TRACE instruction
295            if Self::check_keyword(name, "TRACE") {
296                return self.parse_trace();
297            }
298
299            // NUMERIC instruction
300            if Self::check_keyword(name, "NUMERIC") {
301                return self.parse_numeric();
302            }
303
304            // PUSH instruction
305            if Self::check_keyword(name, "PUSH") {
306                return self.parse_push();
307            }
308
309            // QUEUE instruction
310            if Self::check_keyword(name, "QUEUE") {
311                return self.parse_queue();
312            }
313
314            // ADDRESS instruction
315            if Self::check_keyword(name, "ADDRESS") {
316                return self.parse_address();
317            }
318
319            // Stray END outside DO/SELECT
320            if Self::check_keyword(name, "END") {
321                return Err(RexxDiagnostic::new(RexxError::UnexpectedEnd)
322                    .at(loc)
323                    .with_detail("END without matching DO or SELECT"));
324            }
325
326            // Stray THEN/ELSE
327            if Self::check_keyword(name, "THEN") || Self::check_keyword(name, "ELSE") {
328                return Err(RexxDiagnostic::new(RexxError::UnexpectedThenElse)
329                    .at(loc)
330                    .with_detail(format!("unexpected {}", name.to_uppercase())));
331            }
332
333            // Stray WHEN/OTHERWISE
334            if Self::check_keyword(name, "WHEN") || Self::check_keyword(name, "OTHERWISE") {
335                return Err(RexxDiagnostic::new(RexxError::UnexpectedWhenOtherwise)
336                    .at(loc)
337                    .with_detail(format!("unexpected {}", name.to_uppercase())));
338            }
339        }
340
341        // Default: command clause (expression evaluated and discarded)
342        let expr = self.parse_expression()?;
343        Ok(Clause {
344            kind: ClauseKind::Command(expr),
345            loc,
346        })
347    }
348
349    fn parse_assignment(&mut self, loc: &SourceLoc) -> RexxResult<Clause> {
350        let name = if let TokenKind::Symbol(s) = self.peek_kind() {
351            s.clone()
352        } else {
353            unreachable!("parse_assignment called on non-symbol token")
354        };
355        self.advance(); // symbol
356        self.advance(); // =
357
358        let target = if name.contains('.') {
359            // Compound variable: stem.tail
360            let parts: Vec<&str> = name.splitn(2, '.').collect();
361            let stem = parts[0].to_uppercase();
362            let tail_str = parts[1];
363            let tail = parse_tail_elements(tail_str);
364            AssignTarget::Stem { stem, tail }
365        } else {
366            AssignTarget::Simple(name.to_uppercase())
367        };
368
369        let expr = self.parse_expression()?;
370        Ok(Clause {
371            kind: ClauseKind::Assignment { target, expr },
372            loc: loc.clone(),
373        })
374    }
375
376    // ── control flow parsing ────────────────────────────────────────
377
378    /// Parse: IF expr THEN clause [ELSE clause]
379    fn parse_if(&mut self) -> RexxResult<Clause> {
380        let loc = self.loc();
381        self.advance(); // consume IF
382
383        // Parse condition expression (with THEN suppression)
384        self.condition_depth += 1;
385        let condition = self.parse_expression()?;
386        self.condition_depth -= 1;
387
388        // Skip terminators before THEN
389        self.skip_terminators();
390
391        // Expect THEN keyword
392        if !self.is_keyword("THEN") {
393            return Err(RexxDiagnostic::new(RexxError::ExpectedThen)
394                .at(self.loc())
395                .with_detail("expected THEN after IF condition"));
396        }
397        self.advance(); // consume THEN
398
399        // Skip terminators after THEN
400        self.skip_terminators();
401
402        // Parse one clause for THEN branch (with ELSE suppression)
403        self.if_depth += 1;
404        let then_clause = Box::new(self.parse_clause()?);
405
406        // Check for ELSE: skip terminators and look for ELSE keyword
407        let saved_pos = self.pos;
408        self.skip_terminators();
409        let else_clause = if self.is_keyword("ELSE") {
410            self.advance(); // consume ELSE
411            self.skip_terminators();
412            let clause = self.parse_clause()?;
413            Some(Box::new(clause))
414        } else {
415            // Restore position — those terminators might be meaningful
416            self.pos = saved_pos;
417            None
418        };
419        self.if_depth -= 1;
420
421        Ok(Clause {
422            kind: ClauseKind::If {
423                condition,
424                then_clause,
425                else_clause,
426            },
427            loc,
428        })
429    }
430
431    /// Parse: DO [variant]; body; END [name]
432    fn parse_do(&mut self) -> RexxResult<Clause> {
433        let loc = self.loc();
434        self.advance(); // consume DO
435
436        // Disambiguate variant
437        // 1. DO; ... END  (simple) — next is terminator
438        // 2. DO FOREVER   — next is Symbol("FOREVER")
439        // 3. DO WHILE expr — next is Symbol("WHILE")
440        // 4. DO UNTIL expr — next is Symbol("UNTIL")
441        // 5. DO var = start [TO..BY..FOR..WHILE..UNTIL] — next is Symbol, peek(+1) is =
442        // 6. DO expr — counted loop
443
444        if self.is_terminator() {
445            // Simple DO block
446            self.skip_terminators();
447            let body = self.parse_do_body()?;
448            return Ok(Clause {
449                kind: ClauseKind::Do(Box::new(DoBlock {
450                    kind: DoKind::Simple,
451                    body,
452                    name: None,
453                })),
454                loc,
455            });
456        }
457
458        if self.is_keyword("FOREVER") {
459            self.advance(); // consume FOREVER
460            self.skip_terminators();
461            let body = self.parse_do_body()?;
462            return Ok(Clause {
463                kind: ClauseKind::Do(Box::new(DoBlock {
464                    kind: DoKind::Forever,
465                    body,
466                    name: None,
467                })),
468                loc,
469            });
470        }
471
472        if self.is_keyword("WHILE") {
473            self.advance(); // consume WHILE
474            let cond = self.parse_expression()?;
475            self.skip_terminators();
476            let body = self.parse_do_body()?;
477            return Ok(Clause {
478                kind: ClauseKind::Do(Box::new(DoBlock {
479                    kind: DoKind::While(cond),
480                    body,
481                    name: None,
482                })),
483                loc,
484            });
485        }
486
487        if self.is_keyword("UNTIL") {
488            self.advance(); // consume UNTIL
489            let cond = self.parse_expression()?;
490            self.skip_terminators();
491            let body = self.parse_do_body()?;
492            return Ok(Clause {
493                kind: ClauseKind::Do(Box::new(DoBlock {
494                    kind: DoKind::Until(cond),
495                    body,
496                    name: None,
497                })),
498                loc,
499            });
500        }
501
502        // Check for controlled loop: Symbol followed by =
503        if let TokenKind::Symbol(_) = self.peek_kind()
504            && matches!(self.peek_at(1), TokenKind::Assign)
505        {
506            return self.parse_controlled_do(loc);
507        }
508
509        // Counted DO: DO expr
510        let count_expr = self.parse_expression()?;
511        self.skip_terminators();
512        let body = self.parse_do_body()?;
513        Ok(Clause {
514            kind: ClauseKind::Do(Box::new(DoBlock {
515                kind: DoKind::Count(count_expr),
516                body,
517                name: None,
518            })),
519            loc,
520        })
521    }
522
523    /// Parse controlled DO: DO var = start [TO limit] [BY step] [FOR count] [WHILE cond] [UNTIL cond]
524    fn parse_controlled_do(&mut self, loc: SourceLoc) -> RexxResult<Clause> {
525        let var_name = if let TokenKind::Symbol(s) = self.peek_kind() {
526            s.to_uppercase()
527        } else {
528            unreachable!()
529        };
530        self.advance(); // consume variable name
531        self.advance(); // consume =
532
533        // Parse start expression with do_header_depth guard
534        self.do_header_depth += 1;
535        let start = self.parse_expression()?;
536
537        let mut to: Option<Expr> = None;
538        let mut by: Option<Expr> = None;
539        let mut r#for: Option<Expr> = None;
540        let mut while_cond: Option<Expr> = None;
541        let mut until_cond: Option<Expr> = None;
542
543        // Parse optional TO/BY/FOR/WHILE/UNTIL in any order.
544        // Duplicate keywords are rejected per REXX (Error 27).
545        loop {
546            if self.is_keyword("TO") {
547                if to.is_some() {
548                    return Err(RexxDiagnostic::new(RexxError::InvalidDoSyntax)
549                        .at(self.loc())
550                        .with_detail("duplicate TO in DO instruction"));
551                }
552                self.advance();
553                to = Some(self.parse_expression()?);
554            } else if self.is_keyword("BY") {
555                if by.is_some() {
556                    return Err(RexxDiagnostic::new(RexxError::InvalidDoSyntax)
557                        .at(self.loc())
558                        .with_detail("duplicate BY in DO instruction"));
559                }
560                self.advance();
561                by = Some(self.parse_expression()?);
562            } else if self.is_keyword("FOR") {
563                if r#for.is_some() {
564                    return Err(RexxDiagnostic::new(RexxError::InvalidDoSyntax)
565                        .at(self.loc())
566                        .with_detail("duplicate FOR in DO instruction"));
567                }
568                self.advance();
569                r#for = Some(self.parse_expression()?);
570            } else if self.is_keyword("WHILE") {
571                if while_cond.is_some() {
572                    return Err(RexxDiagnostic::new(RexxError::InvalidDoSyntax)
573                        .at(self.loc())
574                        .with_detail("duplicate WHILE in DO instruction"));
575                }
576                self.advance();
577                while_cond = Some(self.parse_expression()?);
578            } else if self.is_keyword("UNTIL") {
579                if until_cond.is_some() {
580                    return Err(RexxDiagnostic::new(RexxError::InvalidDoSyntax)
581                        .at(self.loc())
582                        .with_detail("duplicate UNTIL in DO instruction"));
583                }
584                self.advance();
585                until_cond = Some(self.parse_expression()?);
586            } else {
587                break;
588            }
589        }
590
591        self.do_header_depth -= 1;
592
593        self.skip_terminators();
594        let body = self.parse_do_body()?;
595
596        Ok(Clause {
597            kind: ClauseKind::Do(Box::new(DoBlock {
598                kind: DoKind::Controlled(Box::new(ControlledLoop {
599                    var: var_name.clone(),
600                    start,
601                    to,
602                    by,
603                    r#for,
604                    while_cond,
605                    until_cond,
606                })),
607                body,
608                name: Some(var_name),
609            })),
610            loc,
611        })
612    }
613
614    /// Parse DO body: clauses until END [name]
615    fn parse_do_body(&mut self) -> RexxResult<Vec<Clause>> {
616        let mut body = Vec::new();
617        self.skip_terminators();
618        loop {
619            if self.at_end() {
620                return Err(RexxDiagnostic::new(RexxError::IncompleteBlock)
621                    .at(self.loc())
622                    .with_detail("expected END to close DO block"));
623            }
624            if self.is_keyword("END") {
625                self.advance(); // consume END
626                // Optionally consume a symbol after END (e.g., END i)
627                if let TokenKind::Symbol(_) = self.peek_kind()
628                    && !self.is_terminator()
629                {
630                    self.advance(); // consume the name after END
631                }
632                break;
633            }
634            body.push(self.parse_clause()?);
635            self.skip_terminators();
636        }
637        Ok(body)
638    }
639
640    /// Parse: SELECT; WHEN expr THEN clause...; ... [OTHERWISE; clause...;] END
641    fn parse_select(&mut self) -> RexxResult<Clause> {
642        let loc = self.loc();
643        self.advance(); // consume SELECT
644        self.skip_terminators();
645
646        let mut when_clauses: Vec<(Expr, Vec<Clause>)> = Vec::new();
647        let mut otherwise: Option<Vec<Clause>> = None;
648
649        loop {
650            self.skip_terminators();
651
652            if self.at_end() {
653                return Err(RexxDiagnostic::new(RexxError::IncompleteBlock)
654                    .at(self.loc())
655                    .with_detail("expected END to close SELECT"));
656            }
657
658            if self.is_keyword("END") {
659                self.advance(); // consume END
660                break;
661            }
662
663            if self.is_keyword("WHEN") {
664                self.advance(); // consume WHEN
665                self.condition_depth += 1;
666                let condition = self.parse_expression()?;
667                self.condition_depth -= 1;
668                self.skip_terminators();
669
670                if !self.is_keyword("THEN") {
671                    return Err(RexxDiagnostic::new(RexxError::ExpectedThen)
672                        .at(self.loc())
673                        .with_detail("expected THEN after WHEN condition"));
674                }
675                self.advance(); // consume THEN
676                self.skip_terminators();
677
678                // Parse one or more clauses for this WHEN
679                let mut body = Vec::new();
680                loop {
681                    if self.at_end()
682                        || self.is_keyword("WHEN")
683                        || self.is_keyword("OTHERWISE")
684                        || self.is_keyword("END")
685                    {
686                        break;
687                    }
688                    body.push(self.parse_clause()?);
689                    self.skip_terminators();
690                }
691                when_clauses.push((condition, body));
692                continue;
693            }
694
695            if self.is_keyword("OTHERWISE") {
696                self.advance(); // consume OTHERWISE
697                self.skip_terminators();
698
699                let mut body = Vec::new();
700                loop {
701                    if self.at_end() || self.is_keyword("END") {
702                        break;
703                    }
704                    body.push(self.parse_clause()?);
705                    self.skip_terminators();
706                }
707                otherwise = Some(body);
708                continue;
709            }
710
711            return Err(RexxDiagnostic::new(RexxError::ExpectedWhenOtherwise)
712                .at(self.loc())
713                .with_detail("expected WHEN, OTHERWISE, or END in SELECT"));
714        }
715
716        Ok(Clause {
717            kind: ClauseKind::Select {
718                when_clauses,
719                otherwise,
720            },
721            loc,
722        })
723    }
724
725    /// Parse: LEAVE [name]
726    fn parse_leave(&mut self) -> Clause {
727        let loc = self.loc();
728        self.advance(); // consume LEAVE
729        let name = self.try_consume_symbol_name();
730        Clause {
731            kind: ClauseKind::Leave(name),
732            loc,
733        }
734    }
735
736    /// Parse: ITERATE [name]
737    fn parse_iterate(&mut self) -> Clause {
738        let loc = self.loc();
739        self.advance(); // consume ITERATE
740        let name = self.try_consume_symbol_name();
741        Clause {
742            kind: ClauseKind::Iterate(name),
743            loc,
744        }
745    }
746
747    /// Try to consume an optional symbol name (for LEAVE/ITERATE).
748    fn try_consume_symbol_name(&mut self) -> Option<String> {
749        if self.is_terminator() {
750            return None;
751        }
752        if let TokenKind::Symbol(s) = self.peek_kind() {
753            let n = s.to_uppercase();
754            self.advance();
755            Some(n)
756        } else {
757            None
758        }
759    }
760
761    /// Parse: EXIT [expr]
762    fn parse_exit(&mut self) -> RexxResult<Clause> {
763        let loc = self.loc();
764        self.advance(); // consume EXIT
765        let expr = if self.is_terminator() {
766            None
767        } else {
768            Some(self.parse_expression()?)
769        };
770        Ok(Clause {
771            kind: ClauseKind::Exit(expr),
772            loc,
773        })
774    }
775
776    /// Parse: RETURN [expr]
777    fn parse_return(&mut self) -> RexxResult<Clause> {
778        let loc = self.loc();
779        self.advance(); // consume RETURN
780        let expr = if self.is_terminator() {
781            None
782        } else {
783            Some(self.parse_expression()?)
784        };
785        Ok(Clause {
786            kind: ClauseKind::Return(expr),
787            loc,
788        })
789    }
790
791    /// Parse: CALL name [expr [, expr]...]
792    fn parse_call(&mut self) -> RexxResult<Clause> {
793        let loc = self.loc();
794        self.advance(); // consume CALL
795
796        // Read routine name
797        let name = if let TokenKind::Symbol(s) = self.peek_kind() {
798            let n = s.to_uppercase();
799            self.advance();
800            n
801        } else {
802            return Err(RexxDiagnostic::new(RexxError::ExpectedSymbol)
803                .at(self.loc())
804                .with_detail("expected routine name after CALL"));
805        };
806
807        // Parse optional comma-separated arguments
808        let mut args = Vec::new();
809        if !self.is_terminator() {
810            args.push(self.parse_expression()?);
811            while matches!(self.peek_kind(), TokenKind::Comma) {
812                self.advance(); // consume comma
813                args.push(self.parse_expression()?);
814            }
815        }
816
817        Ok(Clause {
818            kind: ClauseKind::Call { name, args },
819            loc,
820        })
821    }
822
823    /// Parse: PROCEDURE [EXPOSE name [name...]]
824    fn parse_procedure(&mut self) -> Clause {
825        let loc = self.loc();
826        self.advance(); // consume PROCEDURE
827
828        let expose = if self.is_keyword("EXPOSE") {
829            self.advance(); // consume EXPOSE
830            let mut names = Vec::new();
831            while !self.is_terminator() {
832                if let TokenKind::Symbol(s) = self.peek_kind() {
833                    names.push(s.to_uppercase());
834                    self.advance();
835                } else {
836                    break;
837                }
838            }
839            Some(names)
840        } else {
841            None
842        };
843
844        Clause {
845            kind: ClauseKind::Procedure(expose),
846            loc,
847        }
848    }
849
850    /// Parse a PARSE template: sequence of variables, dots, literal patterns,
851    /// positional patterns, variable patterns, and commas.
852    fn parse_template(&mut self) -> RexxResult<ParseTemplate> {
853        let mut elements = Vec::new();
854        while !self.is_terminator() {
855            match self.peek_kind().clone() {
856                TokenKind::Symbol(name) => {
857                    elements.push(TemplateElement::Variable(name.to_uppercase()));
858                    self.advance();
859                }
860                TokenKind::Dot => {
861                    elements.push(TemplateElement::Dot);
862                    self.advance();
863                }
864                TokenKind::StringLit(s) => {
865                    elements.push(TemplateElement::Literal(s));
866                    self.advance();
867                }
868                TokenKind::Number(n) => {
869                    elements.push(TemplateElement::AbsolutePos(Expr::Number(n)));
870                    self.advance();
871                }
872                TokenKind::Plus => {
873                    self.advance(); // consume +
874                    if let TokenKind::Number(n) = self.peek_kind().clone() {
875                        self.advance();
876                        let val: i32 = n.parse().map_err(|_| {
877                            RexxDiagnostic::new(RexxError::InvalidTemplate)
878                                .at(self.loc())
879                                .with_detail(format!("invalid relative position '+{n}'"))
880                        })?;
881                        elements.push(TemplateElement::RelativePos(val));
882                    } else {
883                        return Err(RexxDiagnostic::new(RexxError::InvalidTemplate)
884                            .at(self.loc())
885                            .with_detail("expected number after '+' in template"));
886                    }
887                }
888                TokenKind::Minus => {
889                    self.advance(); // consume -
890                    if let TokenKind::Number(n) = self.peek_kind().clone() {
891                        self.advance();
892                        let val: i32 = n.parse().map_err(|_| {
893                            RexxDiagnostic::new(RexxError::InvalidTemplate)
894                                .at(self.loc())
895                                .with_detail(format!("invalid relative position '-{n}'"))
896                        })?;
897                        elements.push(TemplateElement::RelativePos(-val));
898                    } else {
899                        return Err(RexxDiagnostic::new(RexxError::InvalidTemplate)
900                            .at(self.loc())
901                            .with_detail("expected number after '-' in template"));
902                    }
903                }
904                TokenKind::LeftParen => {
905                    self.advance(); // consume (
906                    if let TokenKind::Symbol(name) = self.peek_kind().clone() {
907                        let var_name = name.to_uppercase();
908                        self.advance(); // consume symbol
909                        let err_loc = self.loc();
910                        self.expect(&TokenKind::RightParen).map_err(|_| {
911                            RexxDiagnostic::new(RexxError::InvalidTemplate)
912                                .at(err_loc)
913                                .with_detail("expected ')' after variable pattern name")
914                        })?;
915                        elements.push(TemplateElement::VariablePattern(var_name));
916                    } else {
917                        return Err(RexxDiagnostic::new(RexxError::InvalidTemplate)
918                            .at(self.loc())
919                            .with_detail("expected symbol inside '(' ')' in template"));
920                    }
921                }
922                TokenKind::Comma => {
923                    elements.push(TemplateElement::Comma);
924                    self.advance();
925                }
926                _ => break,
927            }
928        }
929        Ok(ParseTemplate { elements })
930    }
931
932    /// Parse: PARSE [UPPER] source template
933    fn parse_parse(&mut self) -> RexxResult<Clause> {
934        let loc = self.loc();
935        self.advance(); // consume PARSE
936
937        // Check for UPPER
938        let upper = if self.is_keyword("UPPER") {
939            self.advance();
940            true
941        } else {
942            false
943        };
944
945        // Dispatch on source keyword
946        let source = if self.is_keyword("ARG") {
947            self.advance();
948            ParseSource::Arg
949        } else if self.is_keyword("PULL") {
950            self.advance();
951            ParseSource::Pull
952        } else if self.is_keyword("SOURCE") {
953            self.advance();
954            ParseSource::Source
955        } else if self.is_keyword("VERSION") {
956            self.advance();
957            ParseSource::Version
958        } else if self.is_keyword("LINEIN") {
959            self.advance();
960            ParseSource::LineIn
961        } else if self.is_keyword("VAR") {
962            self.advance();
963            if let TokenKind::Symbol(name) = self.peek_kind().clone() {
964                let var_name = name.to_uppercase();
965                self.advance();
966                ParseSource::Var(var_name)
967            } else {
968                return Err(RexxDiagnostic::new(RexxError::ExpectedSymbol)
969                    .at(self.loc())
970                    .with_detail("expected variable name after PARSE VAR"));
971            }
972        } else if self.is_keyword("VALUE") {
973            self.advance();
974            self.parse_value_depth += 1;
975            let expr = self.parse_expression();
976            self.parse_value_depth -= 1;
977            let expr = expr?;
978            // Expect WITH keyword
979            if !self.is_with_keyword() {
980                return Err(RexxDiagnostic::new(RexxError::InvalidSubKeyword)
981                    .at(self.loc())
982                    .with_detail("expected WITH after PARSE VALUE expression"));
983            }
984            self.advance(); // consume WITH
985            ParseSource::Value(expr)
986        } else {
987            return Err(RexxDiagnostic::new(RexxError::InvalidSubKeyword)
988                .at(self.loc())
989                .with_detail(
990                    "expected ARG, PULL, SOURCE, VERSION, LINEIN, VAR, or VALUE after PARSE",
991                ));
992        };
993
994        let template = if self.is_terminator() {
995            ParseTemplate { elements: vec![] }
996        } else {
997            self.parse_template()?
998        };
999
1000        Ok(Clause {
1001            kind: ClauseKind::Parse {
1002                upper,
1003                source,
1004                template,
1005            },
1006            loc,
1007        })
1008    }
1009
1010    /// Parse: PULL [template]
1011    fn parse_pull(&mut self) -> RexxResult<Clause> {
1012        let loc = self.loc();
1013        self.advance(); // consume PULL
1014
1015        let template = if self.is_terminator() {
1016            None
1017        } else {
1018            Some(self.parse_template()?)
1019        };
1020
1021        Ok(Clause {
1022            kind: ClauseKind::Pull(template),
1023            loc,
1024        })
1025    }
1026
1027    /// Parse: ARG [template]
1028    fn parse_arg(&mut self) -> RexxResult<Clause> {
1029        let loc = self.loc();
1030        self.advance(); // consume ARG
1031        let template = if self.is_terminator() {
1032            ParseTemplate { elements: vec![] }
1033        } else {
1034            self.parse_template()?
1035        };
1036        Ok(Clause {
1037            kind: ClauseKind::Arg(template),
1038            loc,
1039        })
1040    }
1041
1042    /// Parse: DROP name [name...]
1043    fn parse_drop(&mut self) -> Clause {
1044        let loc = self.loc();
1045        self.advance(); // consume DROP
1046
1047        let mut names = Vec::new();
1048        while !self.is_terminator() {
1049            if let TokenKind::Symbol(s) = self.peek_kind() {
1050                names.push(s.to_uppercase());
1051                self.advance();
1052            } else {
1053                break;
1054            }
1055        }
1056
1057        Clause {
1058            kind: ClauseKind::Drop(names),
1059            loc,
1060        }
1061    }
1062
1063    // ── SIGNAL parsing ───────────────────────────────────────────────
1064
1065    /// Parse: SIGNAL label | SIGNAL VALUE expr | SIGNAL ON condition [NAME label] | SIGNAL OFF condition
1066    fn parse_signal(&mut self) -> RexxResult<Clause> {
1067        let loc = self.loc();
1068        self.advance(); // consume SIGNAL
1069
1070        if self.is_keyword("ON") {
1071            self.advance(); // consume ON
1072            let condition = self.parse_condition()?;
1073            let name = if self.is_keyword("NAME") {
1074                self.advance(); // consume NAME
1075                if let TokenKind::Symbol(s) = self.peek_kind() {
1076                    let n = s.to_uppercase();
1077                    self.advance();
1078                    Some(n)
1079                } else {
1080                    return Err(RexxDiagnostic::new(RexxError::ExpectedSymbol)
1081                        .at(self.loc())
1082                        .with_detail("expected label name after SIGNAL ON condition NAME"));
1083                }
1084            } else {
1085                None
1086            };
1087            return Ok(Clause {
1088                kind: ClauseKind::Signal(SignalAction::On { condition, name }),
1089                loc,
1090            });
1091        }
1092
1093        if self.is_keyword("OFF") {
1094            self.advance(); // consume OFF
1095            let condition = self.parse_condition()?;
1096            return Ok(Clause {
1097                kind: ClauseKind::Signal(SignalAction::Off(condition)),
1098                loc,
1099            });
1100        }
1101
1102        if self.is_keyword("VALUE") {
1103            self.advance(); // consume VALUE
1104            let expr = self.parse_expression()?;
1105            return Ok(Clause {
1106                kind: ClauseKind::Signal(SignalAction::Value(expr)),
1107                loc,
1108            });
1109        }
1110
1111        // SIGNAL label — must be a symbol
1112        if let TokenKind::Symbol(s) = self.peek_kind() {
1113            let label = s.to_uppercase();
1114            self.advance();
1115            return Ok(Clause {
1116                kind: ClauseKind::Signal(SignalAction::Label(label)),
1117                loc,
1118            });
1119        }
1120
1121        Err(RexxDiagnostic::new(RexxError::ExpectedSymbol)
1122            .at(self.loc())
1123            .with_detail("expected label name, VALUE, ON, or OFF after SIGNAL"))
1124    }
1125
1126    /// Parse a condition name: ERROR, FAILURE, HALT, NOVALUE, NOTREADY, SYNTAX, LOSTDIGITS
1127    fn parse_condition(&mut self) -> RexxResult<Condition> {
1128        if let TokenKind::Symbol(s) = self.peek_kind() {
1129            let upper = s.to_uppercase();
1130            let condition = match upper.as_str() {
1131                "ERROR" => Condition::Error,
1132                "FAILURE" => Condition::Failure,
1133                "HALT" => Condition::Halt,
1134                "NOVALUE" => Condition::NoValue,
1135                "NOTREADY" => Condition::NotReady,
1136                "SYNTAX" => Condition::Syntax,
1137                "LOSTDIGITS" => Condition::LostDigits,
1138                _ => {
1139                    return Err(RexxDiagnostic::new(RexxError::InvalidSubKeyword)
1140                        .at(self.loc())
1141                        .with_detail(format!(
1142                            "'{upper}' is not a valid condition name; expected ERROR, FAILURE, HALT, NOVALUE, NOTREADY, SYNTAX, or LOSTDIGITS"
1143                        )));
1144                }
1145            };
1146            self.advance();
1147            Ok(condition)
1148        } else {
1149            Err(RexxDiagnostic::new(RexxError::ExpectedSymbol)
1150                .at(self.loc())
1151                .with_detail("expected condition name"))
1152        }
1153    }
1154
1155    // ── TRACE parsing ──────────────────────────────────────────────────
1156
1157    /// Parse: TRACE [setting]
1158    fn parse_trace(&mut self) -> RexxResult<Clause> {
1159        let loc = self.loc();
1160        self.advance(); // consume TRACE
1161        let expr = if self.is_terminator() {
1162            Expr::StringLit("N".to_string())
1163        } else {
1164            self.parse_expression()?
1165        };
1166        Ok(Clause {
1167            kind: ClauseKind::Trace(expr),
1168            loc,
1169        })
1170    }
1171
1172    // ── INTERPRET parsing ─────────────────────────────────────────────
1173
1174    /// Parse: INTERPRET expr
1175    fn parse_interpret(&mut self) -> RexxResult<Clause> {
1176        let loc = self.loc();
1177        self.advance(); // consume INTERPRET
1178        let expr = if self.is_terminator() {
1179            Expr::StringLit(String::new())
1180        } else {
1181            self.parse_expression()?
1182        };
1183        Ok(Clause {
1184            kind: ClauseKind::Interpret(expr),
1185            loc,
1186        })
1187    }
1188
1189    // ── ADDRESS parsing ───────────────────────────────────────────────
1190
1191    /// Parse: ADDRESS [env [command]] | ADDRESS VALUE expr | ADDRESS (swap)
1192    fn parse_address(&mut self) -> RexxResult<Clause> {
1193        let loc = self.loc();
1194        self.advance(); // consume ADDRESS
1195
1196        // Bare ADDRESS → swap default ↔ previous
1197        if self.is_terminator() {
1198            return Ok(Clause {
1199                kind: ClauseKind::Address(AddressAction::SetEnvironment(String::new())),
1200                loc,
1201            });
1202        }
1203
1204        // ADDRESS VALUE expr → dynamic environment name
1205        if self.is_keyword("VALUE") {
1206            self.advance(); // consume VALUE
1207            let expr = self.parse_expression()?;
1208            return Ok(Clause {
1209                kind: ClauseKind::Address(AddressAction::Value(expr)),
1210                loc,
1211            });
1212        }
1213
1214        // ADDRESS env [command]
1215        if let TokenKind::Symbol(name) = self.peek_kind().clone() {
1216            let env_name = name.to_uppercase();
1217            self.advance(); // consume environment name
1218
1219            if self.is_terminator() {
1220                // ADDRESS env — set default
1221                return Ok(Clause {
1222                    kind: ClauseKind::Address(AddressAction::SetEnvironment(env_name)),
1223                    loc,
1224                });
1225            }
1226
1227            // ADDRESS env command — one-shot
1228            let command = self.parse_expression()?;
1229            return Ok(Clause {
1230                kind: ClauseKind::Address(AddressAction::Temporary {
1231                    environment: env_name,
1232                    command,
1233                }),
1234                loc,
1235            });
1236        }
1237
1238        Err(RexxDiagnostic::new(RexxError::ExpectedSymbol)
1239            .at(self.loc())
1240            .with_detail("expected environment name, VALUE, or end of clause after ADDRESS"))
1241    }
1242
1243    // ── NUMERIC parsing ────────────────────────────────────────────────
1244
1245    /// Parse: NUMERIC DIGITS [expr] | NUMERIC FORM ... | NUMERIC FUZZ [expr]
1246    fn parse_numeric(&mut self) -> RexxResult<Clause> {
1247        let loc = self.loc();
1248        self.advance(); // consume NUMERIC
1249
1250        if self.is_keyword("DIGITS") {
1251            self.advance(); // consume DIGITS
1252            let expr = if self.is_terminator() {
1253                None
1254            } else {
1255                Some(self.parse_expression()?)
1256            };
1257            return Ok(Clause {
1258                kind: ClauseKind::Numeric(NumericSetting::Digits(expr)),
1259                loc,
1260            });
1261        }
1262
1263        if self.is_keyword("FORM") {
1264            self.advance(); // consume FORM
1265            let form = if self.is_keyword("SCIENTIFIC") {
1266                self.advance();
1267                NumericFormSetting::Scientific
1268            } else if self.is_keyword("ENGINEERING") {
1269                self.advance();
1270                NumericFormSetting::Engineering
1271            } else if self.is_keyword("VALUE") {
1272                self.advance();
1273                let expr = self.parse_expression()?;
1274                NumericFormSetting::Value(expr)
1275            } else if self.is_terminator() {
1276                // Bare "NUMERIC FORM" defaults to SCIENTIFIC
1277                NumericFormSetting::Scientific
1278            } else {
1279                let expr = self.parse_expression()?;
1280                NumericFormSetting::Value(expr)
1281            };
1282            return Ok(Clause {
1283                kind: ClauseKind::Numeric(NumericSetting::Form(form)),
1284                loc,
1285            });
1286        }
1287
1288        if self.is_keyword("FUZZ") {
1289            self.advance(); // consume FUZZ
1290            let expr = if self.is_terminator() {
1291                None
1292            } else {
1293                Some(self.parse_expression()?)
1294            };
1295            return Ok(Clause {
1296                kind: ClauseKind::Numeric(NumericSetting::Fuzz(expr)),
1297                loc,
1298            });
1299        }
1300
1301        Err(RexxDiagnostic::new(RexxError::InvalidSubKeyword)
1302            .at(self.loc())
1303            .with_detail("expected DIGITS, FORM, or FUZZ after NUMERIC"))
1304    }
1305
1306    // ── PUSH / QUEUE parsing ─────────────────────────────────────────────
1307
1308    /// Parse: PUSH [expr]
1309    fn parse_push(&mut self) -> RexxResult<Clause> {
1310        let loc = self.loc();
1311        self.advance(); // consume PUSH
1312        let expr = if self.is_terminator() {
1313            None
1314        } else {
1315            Some(self.parse_expression()?)
1316        };
1317        Ok(Clause {
1318            kind: ClauseKind::Push(expr),
1319            loc,
1320        })
1321    }
1322
1323    /// Parse: QUEUE [expr]
1324    fn parse_queue(&mut self) -> RexxResult<Clause> {
1325        let loc = self.loc();
1326        self.advance(); // consume QUEUE
1327        let expr = if self.is_terminator() {
1328            None
1329        } else {
1330            Some(self.parse_expression()?)
1331        };
1332        Ok(Clause {
1333            kind: ClauseKind::Queue(expr),
1334            loc,
1335        })
1336    }
1337
1338    // ── expression parsing (precedence climbing) ────────────────────
1339    //
1340    // Lowest to highest:
1341    //   1. OR / XOR    (| &&)
1342    //   2. AND         (&)
1343    //   3. comparison  (= \= > < >= <= == \== >> << >>= <<=)
1344    //   4. concat      (blank-concat, ||, abuttal)
1345    //   5. add / sub   (+ -)
1346    //   6. mul / div   (* / % //)
1347    //   7. power       (**)   — right associative
1348    //   8. unary       (+ - \)
1349    //   9. primary     (literals, symbols, parens, function calls)
1350
1351    fn parse_expression(&mut self) -> RexxResult<Expr> {
1352        self.parse_or_xor()
1353    }
1354
1355    // Level 1: OR (|) and XOR (&&)
1356    fn parse_or_xor(&mut self) -> RexxResult<Expr> {
1357        let mut left = self.parse_and()?;
1358        loop {
1359            let op = match self.peek_kind() {
1360                TokenKind::Or => BinOp::Or,
1361                TokenKind::Xor => BinOp::Xor,
1362                _ => break,
1363            };
1364            self.advance();
1365            let right = self.parse_and()?;
1366            left = Expr::BinOp {
1367                left: Box::new(left),
1368                op,
1369                right: Box::new(right),
1370            };
1371        }
1372        Ok(left)
1373    }
1374
1375    // Level 2: AND (&)
1376    fn parse_and(&mut self) -> RexxResult<Expr> {
1377        let mut left = self.parse_comparison()?;
1378        loop {
1379            if !matches!(self.peek_kind(), TokenKind::And) {
1380                break;
1381            }
1382            self.advance();
1383            let right = self.parse_comparison()?;
1384            left = Expr::BinOp {
1385                left: Box::new(left),
1386                op: BinOp::And,
1387                right: Box::new(right),
1388            };
1389        }
1390        Ok(left)
1391    }
1392
1393    // Level 3: comparison operators
1394    fn parse_comparison(&mut self) -> RexxResult<Expr> {
1395        let mut left = self.parse_concat()?;
1396        loop {
1397            let op = match self.peek_kind() {
1398                // = at expression level is comparison, not assignment
1399                TokenKind::Assign | TokenKind::Equal => BinOp::Eq,
1400                TokenKind::NotEqual => BinOp::NotEq,
1401                TokenKind::Greater => BinOp::Gt,
1402                TokenKind::Less => BinOp::Lt,
1403                TokenKind::GreaterEq => BinOp::GtEq,
1404                TokenKind::LessEq => BinOp::LtEq,
1405                TokenKind::StrictEq => BinOp::StrictEq,
1406                TokenKind::StrictNotEq => BinOp::StrictNotEq,
1407                TokenKind::StrictGt => BinOp::StrictGt,
1408                TokenKind::StrictLt => BinOp::StrictLt,
1409                TokenKind::StrictGte => BinOp::StrictGtEq,
1410                TokenKind::StrictLte => BinOp::StrictLtEq,
1411                _ => break,
1412            };
1413            self.advance();
1414            let right = self.parse_concat()?;
1415            left = Expr::BinOp {
1416                left: Box::new(left),
1417                op,
1418                right: Box::new(right),
1419            };
1420        }
1421        Ok(left)
1422    }
1423
1424    // Level 4: concatenation (||, blank-concat, abuttal)
1425    fn parse_concat(&mut self) -> RexxResult<Expr> {
1426        let mut left = self.parse_addition()?;
1427        loop {
1428            // Explicit || concat
1429            if matches!(self.peek_kind(), TokenKind::Concat) {
1430                self.advance();
1431                let right = self.parse_addition()?;
1432                left = Expr::BinOp {
1433                    left: Box::new(left),
1434                    op: BinOp::Concat,
1435                    right: Box::new(right),
1436                };
1437                continue;
1438            }
1439
1440            // When inside a DO header, suppress implicit concatenation for
1441            // DO-header keywords so they can be consumed by the header parser.
1442            if self.do_header_depth > 0 && self.is_do_header_keyword() {
1443                break;
1444            }
1445
1446            // When inside a condition (IF/WHEN), suppress implicit concatenation
1447            // for THEN so it can be consumed by the control flow parser.
1448            if self.condition_depth > 0 && self.is_then_keyword() {
1449                break;
1450            }
1451
1452            // When inside an IF, suppress implicit concatenation for ELSE
1453            // so it can be consumed by the IF parser.
1454            if self.if_depth > 0 && self.is_else_keyword() {
1455                break;
1456            }
1457
1458            // When inside PARSE VALUE, suppress implicit concatenation for WITH
1459            // so it can be consumed by the PARSE parser.
1460            if self.parse_value_depth > 0 && self.is_with_keyword() {
1461                break;
1462            }
1463
1464            // Implicit concatenation: the next token can start a term,
1465            // is not a binary operator, and there may or may not be a space.
1466            if self.can_start_term() && !self.is_binary_op() {
1467                let has_space = self.peek().space_before;
1468                let op = if has_space {
1469                    BinOp::ConcatBlank
1470                } else {
1471                    BinOp::Concat
1472                };
1473                let right = self.parse_addition()?;
1474                left = Expr::BinOp {
1475                    left: Box::new(left),
1476                    op,
1477                    right: Box::new(right),
1478                };
1479                continue;
1480            }
1481
1482            break;
1483        }
1484        Ok(left)
1485    }
1486
1487    /// True if the current token could start a primary expression term.
1488    fn can_start_term(&self) -> bool {
1489        matches!(
1490            self.peek_kind(),
1491            TokenKind::StringLit(_)
1492                | TokenKind::Number(_)
1493                | TokenKind::Symbol(_)
1494                | TokenKind::LeftParen
1495                | TokenKind::Plus
1496                | TokenKind::Minus
1497                | TokenKind::Not
1498        )
1499    }
1500
1501    /// True if the current token is a binary operator (not concat-related).
1502    fn is_binary_op(&self) -> bool {
1503        matches!(
1504            self.peek_kind(),
1505            TokenKind::Plus
1506                | TokenKind::Minus
1507                | TokenKind::Star
1508                | TokenKind::Slash
1509                | TokenKind::IntDiv
1510                | TokenKind::Remainder
1511                | TokenKind::Power
1512                | TokenKind::Assign
1513                | TokenKind::Equal
1514                | TokenKind::NotEqual
1515                | TokenKind::Greater
1516                | TokenKind::Less
1517                | TokenKind::GreaterEq
1518                | TokenKind::LessEq
1519                | TokenKind::StrictEq
1520                | TokenKind::StrictNotEq
1521                | TokenKind::StrictGt
1522                | TokenKind::StrictLt
1523                | TokenKind::StrictGte
1524                | TokenKind::StrictLte
1525                | TokenKind::And
1526                | TokenKind::Or
1527                | TokenKind::Xor
1528                | TokenKind::Concat
1529        )
1530    }
1531
1532    // Level 5: addition / subtraction
1533    fn parse_addition(&mut self) -> RexxResult<Expr> {
1534        let mut left = self.parse_multiplication()?;
1535        loop {
1536            let op = match self.peek_kind() {
1537                TokenKind::Plus => BinOp::Add,
1538                TokenKind::Minus => BinOp::Sub,
1539                _ => break,
1540            };
1541            self.advance();
1542            let right = self.parse_multiplication()?;
1543            left = Expr::BinOp {
1544                left: Box::new(left),
1545                op,
1546                right: Box::new(right),
1547            };
1548        }
1549        Ok(left)
1550    }
1551
1552    // Level 6: multiplication / division
1553    fn parse_multiplication(&mut self) -> RexxResult<Expr> {
1554        let mut left = self.parse_power()?;
1555        loop {
1556            let op = match self.peek_kind() {
1557                TokenKind::Star => BinOp::Mul,
1558                TokenKind::Slash => BinOp::Div,
1559                TokenKind::IntDiv => BinOp::IntDiv,
1560                TokenKind::Remainder => BinOp::Remainder,
1561                _ => break,
1562            };
1563            self.advance();
1564            let right = self.parse_power()?;
1565            left = Expr::BinOp {
1566                left: Box::new(left),
1567                op,
1568                right: Box::new(right),
1569            };
1570        }
1571        Ok(left)
1572    }
1573
1574    // Level 7: power (**) — right associative
1575    fn parse_power(&mut self) -> RexxResult<Expr> {
1576        let base = self.parse_unary()?;
1577        if matches!(self.peek_kind(), TokenKind::Power) {
1578            self.advance();
1579            let exp = self.parse_power()?; // right-recursive for right-assoc
1580            Ok(Expr::BinOp {
1581                left: Box::new(base),
1582                op: BinOp::Power,
1583                right: Box::new(exp),
1584            })
1585        } else {
1586            Ok(base)
1587        }
1588    }
1589
1590    // Level 8: unary prefix (+ - \)
1591    fn parse_unary(&mut self) -> RexxResult<Expr> {
1592        match self.peek_kind() {
1593            TokenKind::Plus => {
1594                self.advance();
1595                let operand = self.parse_unary()?;
1596                Ok(Expr::UnaryOp {
1597                    op: UnaryOp::Plus,
1598                    operand: Box::new(operand),
1599                })
1600            }
1601            TokenKind::Minus => {
1602                self.advance();
1603                let operand = self.parse_unary()?;
1604                Ok(Expr::UnaryOp {
1605                    op: UnaryOp::Minus,
1606                    operand: Box::new(operand),
1607                })
1608            }
1609            TokenKind::Not => {
1610                self.advance();
1611                let operand = self.parse_unary()?;
1612                Ok(Expr::UnaryOp {
1613                    op: UnaryOp::Not,
1614                    operand: Box::new(operand),
1615                })
1616            }
1617            _ => self.parse_primary(),
1618        }
1619    }
1620
1621    // Level 9: primary expressions
1622    fn parse_primary(&mut self) -> RexxResult<Expr> {
1623        match self.peek_kind().clone() {
1624            TokenKind::StringLit(s) => {
1625                self.advance();
1626                Ok(Expr::StringLit(s))
1627            }
1628            TokenKind::Number(n) => {
1629                self.advance();
1630                Ok(Expr::Number(n))
1631            }
1632            TokenKind::Symbol(name) => {
1633                self.advance();
1634                // Function call: symbol immediately followed by '(' (no space)
1635                if matches!(self.peek_kind(), TokenKind::LeftParen) && !self.peek().space_before {
1636                    return self.parse_function_call(&name);
1637                }
1638                // Compound variable: stem.tail
1639                if name.contains('.') {
1640                    let parts: Vec<&str> = name.splitn(2, '.').collect();
1641                    let stem = parts[0].to_uppercase();
1642                    if stem.is_empty() {
1643                        return Err(RexxDiagnostic::new(RexxError::InvalidExpression)
1644                            .at(self.loc())
1645                            .with_detail("compound variable stem cannot be empty"));
1646                    }
1647                    let tail_str = parts[1];
1648                    let tail = parse_tail_elements(tail_str);
1649                    return Ok(Expr::Compound { stem, tail });
1650                }
1651                Ok(Expr::Symbol(name.to_uppercase()))
1652            }
1653            TokenKind::LeftParen => {
1654                self.advance(); // (
1655                let expr = self.parse_expression()?;
1656                let err_loc = self.loc();
1657                self.expect(&TokenKind::RightParen).map_err(|_| {
1658                    RexxDiagnostic::new(RexxError::UnmatchedParen)
1659                        .at(err_loc)
1660                        .with_detail("expected closing ')'")
1661                })?;
1662                Ok(Expr::Paren(Box::new(expr)))
1663            }
1664            _ => Err(RexxDiagnostic::new(RexxError::InvalidExpression)
1665                .at(self.loc())
1666                .with_detail(format!("unexpected token {:?}", self.peek_kind()))),
1667        }
1668    }
1669
1670    fn parse_function_call(&mut self, name: &str) -> RexxResult<Expr> {
1671        self.advance(); // (
1672        let mut args = Vec::new();
1673        if !matches!(self.peek_kind(), TokenKind::RightParen) {
1674            args.push(self.parse_expression()?);
1675            while matches!(self.peek_kind(), TokenKind::Comma) {
1676                self.advance();
1677                args.push(self.parse_expression()?);
1678            }
1679        }
1680        let err_loc = self.loc();
1681        self.expect(&TokenKind::RightParen).map_err(|_| {
1682            RexxDiagnostic::new(RexxError::UnmatchedParen)
1683                .at(err_loc)
1684                .with_detail("expected ')' after function arguments")
1685        })?;
1686        Ok(Expr::FunctionCall {
1687            name: name.to_uppercase(),
1688            args,
1689        })
1690    }
1691}
1692
1693/// Parse tail elements from the string after the first dot in a compound symbol.
1694/// E.g. for `arr.i.j`, after splitting on the first dot we get `"i.j"`,
1695/// which produces `[Var("I"), Var("J")]`.
1696fn parse_tail_elements(tail: &str) -> Vec<crate::ast::TailElement> {
1697    use crate::ast::TailElement;
1698    tail.split('.')
1699        .map(|part| {
1700            if part.is_empty() || part.starts_with(|c: char| c.is_ascii_digit()) {
1701                TailElement::Const(part.to_uppercase())
1702            } else {
1703                TailElement::Var(part.to_uppercase())
1704            }
1705        })
1706        .collect()
1707}
1708
1709#[cfg(test)]
1710mod tests {
1711    use super::*;
1712    use crate::lexer::Lexer;
1713
1714    fn parse(src: &str) -> Program {
1715        let mut lexer = Lexer::new(src);
1716        let tokens = lexer.tokenize().unwrap();
1717        let mut parser = Parser::new(tokens);
1718        parser.parse().unwrap()
1719    }
1720
1721    fn parse_expr(src: &str) -> Expr {
1722        // Wrap in a command clause to extract expression
1723        let prog = parse(src);
1724        match prog.clauses.into_iter().next().unwrap().kind {
1725            ClauseKind::Command(e) | ClauseKind::Say(e) => e,
1726            other => panic!("expected expression clause, got {other:?}"),
1727        }
1728    }
1729
1730    #[test]
1731    fn parse_number_literal() {
1732        let expr = parse_expr("42");
1733        assert!(matches!(expr, Expr::Number(n) if n == "42"));
1734    }
1735
1736    #[test]
1737    fn parse_string_literal() {
1738        let expr = parse_expr("'hello'");
1739        assert!(matches!(expr, Expr::StringLit(s) if s == "hello"));
1740    }
1741
1742    #[test]
1743    fn parse_addition() {
1744        let expr = parse_expr("2 + 3");
1745        assert!(matches!(expr, Expr::BinOp { op: BinOp::Add, .. }));
1746    }
1747
1748    #[test]
1749    fn parse_precedence() {
1750        // 2 + 3 * 4 should parse as 2 + (3 * 4)
1751        let expr = parse_expr("2 + 3 * 4");
1752        match expr {
1753            Expr::BinOp {
1754                op: BinOp::Add,
1755                ref right,
1756                ..
1757            } => {
1758                assert!(matches!(**right, Expr::BinOp { op: BinOp::Mul, .. }));
1759            }
1760            _ => panic!("expected Add at top level"),
1761        }
1762    }
1763
1764    #[test]
1765    fn parse_power_right_assoc() {
1766        // 2 ** 3 ** 4 should parse as 2 ** (3 ** 4)
1767        let expr = parse_expr("2 ** 3 ** 4");
1768        match expr {
1769            Expr::BinOp {
1770                op: BinOp::Power,
1771                ref right,
1772                ..
1773            } => {
1774                assert!(matches!(
1775                    **right,
1776                    Expr::BinOp {
1777                        op: BinOp::Power,
1778                        ..
1779                    }
1780                ));
1781            }
1782            _ => panic!("expected Power at top level"),
1783        }
1784    }
1785
1786    #[test]
1787    fn parse_parens() {
1788        let expr = parse_expr("(2 + 3) * 4");
1789        assert!(matches!(expr, Expr::BinOp { op: BinOp::Mul, .. }));
1790    }
1791
1792    #[test]
1793    fn parse_unary_minus() {
1794        let expr = parse_expr("-5");
1795        assert!(matches!(
1796            expr,
1797            Expr::UnaryOp {
1798                op: UnaryOp::Minus,
1799                ..
1800            }
1801        ));
1802    }
1803
1804    #[test]
1805    fn parse_say_clause() {
1806        let prog = parse("say 'hello'");
1807        match &prog.clauses[0].kind {
1808            ClauseKind::Say(Expr::StringLit(s)) => assert_eq!(s, "hello"),
1809            other => panic!("expected Say, got {other:?}"),
1810        }
1811    }
1812
1813    #[test]
1814    fn parse_assignment_clause() {
1815        let prog = parse("x = 42");
1816        match &prog.clauses[0].kind {
1817            ClauseKind::Assignment {
1818                target: AssignTarget::Simple(name),
1819                expr: Expr::Number(n),
1820            } => {
1821                assert_eq!(name, "X");
1822                assert_eq!(n, "42");
1823            }
1824            other => panic!("expected Assignment, got {other:?}"),
1825        }
1826    }
1827
1828    #[test]
1829    fn parse_label() {
1830        let prog = parse("myLabel:");
1831        match &prog.clauses[0].kind {
1832            ClauseKind::Label(name) => assert_eq!(name, "MYLABEL"),
1833            other => panic!("expected Label, got {other:?}"),
1834        }
1835    }
1836
1837    #[test]
1838    fn parse_concat_forms() {
1839        // Blank concatenation: 'a' 'b'
1840        let expr = parse_expr("'a' 'b'");
1841        assert!(matches!(
1842            expr,
1843            Expr::BinOp {
1844                op: BinOp::ConcatBlank,
1845                ..
1846            }
1847        ));
1848    }
1849
1850    #[test]
1851    fn parse_explicit_concat() {
1852        let expr = parse_expr("'a' || 'b'");
1853        assert!(matches!(
1854            expr,
1855            Expr::BinOp {
1856                op: BinOp::Concat,
1857                ..
1858            }
1859        ));
1860    }
1861
1862    #[test]
1863    fn parse_multiple_clauses() {
1864        let prog = parse("x = 10; say x + 5");
1865        assert_eq!(prog.clauses.len(), 2);
1866        assert!(matches!(
1867            &prog.clauses[0].kind,
1868            ClauseKind::Assignment { .. }
1869        ));
1870        assert!(matches!(&prog.clauses[1].kind, ClauseKind::Say(_)));
1871    }
1872
1873    #[test]
1874    fn parse_unmatched_paren_error() {
1875        let mut lexer = Lexer::new("(2 + 3");
1876        let tokens = lexer.tokenize().unwrap();
1877        let mut parser = Parser::new(tokens);
1878        let result = parser.parse();
1879        assert!(result.is_err());
1880    }
1881
1882    #[test]
1883    fn parse_comparison() {
1884        let expr = parse_expr("3 > 2");
1885        assert!(matches!(expr, Expr::BinOp { op: BinOp::Gt, .. }));
1886    }
1887
1888    #[test]
1889    fn parse_function_call() {
1890        // Note: function call requires no space before paren
1891        let src = "length('hello')";
1892        let prog = parse(src);
1893        match &prog.clauses[0].kind {
1894            ClauseKind::Command(Expr::FunctionCall { name, args }) => {
1895                assert_eq!(name, "LENGTH");
1896                assert_eq!(args.len(), 1);
1897            }
1898            other => panic!("expected FunctionCall, got {other:?}"),
1899        }
1900    }
1901
1902    #[test]
1903    fn parse_if_then() {
1904        let prog = parse("if 1 then say 'yes'");
1905        assert!(matches!(&prog.clauses[0].kind, ClauseKind::If { .. }));
1906    }
1907
1908    #[test]
1909    fn parse_if_then_else() {
1910        let prog = parse("if 0 then say 'no'; else say 'yes'");
1911        match &prog.clauses[0].kind {
1912            ClauseKind::If { else_clause, .. } => {
1913                assert!(else_clause.is_some());
1914            }
1915            other => panic!("expected If, got {other:?}"),
1916        }
1917    }
1918
1919    #[test]
1920    fn parse_simple_do() {
1921        let prog = parse("do; say 'a'; end");
1922        match &prog.clauses[0].kind {
1923            ClauseKind::Do(block) => {
1924                assert!(matches!(block.kind, DoKind::Simple));
1925                assert_eq!(block.body.len(), 1);
1926            }
1927            other => panic!("expected Do, got {other:?}"),
1928        }
1929    }
1930
1931    #[test]
1932    fn parse_do_count() {
1933        let prog = parse("do 3; say 'x'; end");
1934        match &prog.clauses[0].kind {
1935            ClauseKind::Do(block) => {
1936                assert!(matches!(block.kind, DoKind::Count(_)));
1937            }
1938            other => panic!("expected Do Count, got {other:?}"),
1939        }
1940    }
1941
1942    #[test]
1943    fn parse_controlled_do() {
1944        let prog = parse("do i = 1 to 5; say i; end");
1945        match &prog.clauses[0].kind {
1946            ClauseKind::Do(block) => {
1947                assert!(matches!(block.kind, DoKind::Controlled(_)));
1948            }
1949            other => panic!("expected Do Controlled, got {other:?}"),
1950        }
1951    }
1952
1953    #[test]
1954    fn parse_select() {
1955        let prog = parse("select; when 1 then say 'one'; otherwise say 'other'; end");
1956        assert!(matches!(&prog.clauses[0].kind, ClauseKind::Select { .. }));
1957    }
1958
1959    #[test]
1960    fn parse_leave() {
1961        let prog = parse("do forever; leave; end");
1962        match &prog.clauses[0].kind {
1963            ClauseKind::Do(block) => {
1964                assert!(matches!(&block.body[0].kind, ClauseKind::Leave(None)));
1965            }
1966            other => panic!("expected Do, got {other:?}"),
1967        }
1968    }
1969
1970    #[test]
1971    fn parse_exit() {
1972        let prog = parse("exit");
1973        assert!(matches!(&prog.clauses[0].kind, ClauseKind::Exit(None)));
1974    }
1975
1976    #[test]
1977    fn parse_exit_with_expr() {
1978        let prog = parse("exit 0");
1979        assert!(matches!(&prog.clauses[0].kind, ClauseKind::Exit(Some(_))));
1980    }
1981}