Skip to main content

konjure_lang/
parser.rs

1//! Bounded lexer and parser for Konjure source modules.
2//!
3//! The parser deliberately keeps syntax errors portable: every error is tied to
4//! a byte span in the named input module and malformed input never panics.
5use crate::ast::*;
6use std::collections::BTreeMap;
7
8const MAX_SOURCE_BYTES: usize = 1 << 20;
9const MAX_TOKENS: usize = 100_000;
10const MAX_NESTING: usize = 64;
11
12/// A source token intended for editors and other language tooling.
13#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
14pub struct Token {
15    /// Lexical category: keyword, identifier, number, string, punctuation, or comment.
16    pub kind: String,
17    /// Exact source spelling, including delimiters for string literals.
18    pub text: String,
19    /// Token source location using UTF-8 byte offsets.
20    pub span: Span,
21}
22
23#[derive(Clone, Debug)]
24struct LexToken {
25    token: Token,
26    value: Option<String>,
27}
28
29/// Tokenizes source without discarding comments.  Spans are UTF-8 byte offsets.
30pub fn tokenize(source: &str, module: &str) -> Result<Vec<Token>, Vec<Diagnostic>> {
31    lex(source, module).map(|tokens| tokens.into_iter().map(|token| token.token).collect())
32}
33
34/// Parses a complete source module into its top-level items.
35pub fn parse(source: &str, module: &str) -> Result<Vec<Item>, Vec<Diagnostic>> {
36    if source.len() > MAX_SOURCE_BYTES {
37        return Err(vec![Diagnostic::new(
38            "E_LIMIT",
39            "source exceeds the 1 MiB limit",
40            Span::new(module, 0, source.len()),
41        )]);
42    }
43    let tokens = lex(source, module)?
44        .into_iter()
45        .filter(|token| token.token.kind != "comment")
46        .collect();
47    let mut parser = Parser {
48        tokens,
49        at: 0,
50        module: module.into(),
51        source_len: source.len(),
52        nesting: 0,
53    };
54    parser.module_items()
55}
56
57fn lex(source: &str, module: &str) -> Result<Vec<LexToken>, Vec<Diagnostic>> {
58    if source.len() > MAX_SOURCE_BYTES {
59        return Err(vec![Diagnostic::new(
60            "E_LIMIT",
61            "source exceeds the 1 MiB limit",
62            Span::new(module, 0, source.len()),
63        )]);
64    }
65    let mut tokens = Vec::new();
66    let mut at = 0;
67    while at < source.len() {
68        let start = at;
69        let ch = source[at..].chars().next().expect("valid UTF-8 boundary");
70        if ch.is_whitespace() {
71            at += ch.len_utf8();
72            continue;
73        }
74        let (kind, text_end, value) = if source[at..].starts_with("//") {
75            let end = source[at..]
76                .find('\n')
77                .map_or(source.len(), |offset| at + offset);
78            ("comment", end, None)
79        } else if ch == '"' {
80            at += 1;
81            let mut value = String::new();
82            let mut closed = false;
83            while at < source.len() {
84                let current = source[at..].chars().next().expect("valid UTF-8 boundary");
85                if current == '"' {
86                    at += 1;
87                    closed = true;
88                    break;
89                }
90                if current == '\\' {
91                    let escape_at = at;
92                    at += 1;
93                    let Some(escaped) = source[at..].chars().next() else {
94                        return Err(vec![diagnostic(
95                            module,
96                            "E_LEX",
97                            "unterminated string",
98                            start,
99                            source.len(),
100                        )]);
101                    };
102                    match escaped {
103                        'n' => value.push('\n'),
104                        'r' => value.push('\r'),
105                        't' => value.push('\t'),
106                        '"' => value.push('"'),
107                        '\\' => value.push('\\'),
108                        _ => {
109                            return Err(vec![diagnostic(
110                                module,
111                                "E_LEX",
112                                "unsupported string escape",
113                                escape_at,
114                                at + escaped.len_utf8(),
115                            )]);
116                        }
117                    };
118                    at += escaped.len_utf8();
119                } else {
120                    value.push(current);
121                    at += current.len_utf8();
122                }
123            }
124            if !closed {
125                return Err(vec![diagnostic(
126                    module,
127                    "E_LEX",
128                    "unterminated string",
129                    start,
130                    source.len(),
131                )]);
132            }
133            ("string", at, Some(value))
134        } else if ch.is_ascii_digit() {
135            at += 1;
136            while source.as_bytes().get(at).is_some_and(u8::is_ascii_digit) {
137                at += 1;
138            }
139            if source.as_bytes().get(at) == Some(&b'.')
140                && source
141                    .as_bytes()
142                    .get(at + 1)
143                    .is_some_and(u8::is_ascii_digit)
144            {
145                at += 1;
146                while source.as_bytes().get(at).is_some_and(u8::is_ascii_digit) {
147                    at += 1;
148                }
149            }
150            ("number", at, None)
151        } else if ch.is_ascii_alphabetic() || ch == '_' {
152            at += ch.len_utf8();
153            while let Some(next) = source[at..].chars().next() {
154                if next.is_ascii_alphanumeric() || next == '_' {
155                    at += next.len_utf8();
156                } else {
157                    break;
158                }
159            }
160            let word = &source[start..at];
161            (
162                if is_keyword(word) {
163                    "keyword"
164                } else {
165                    "identifier"
166                },
167                at,
168                None,
169            )
170        } else if source[at..].starts_with("&&") || source[at..].starts_with("||") {
171            let legacy = &source[at..at + 2];
172            let replacement = if legacy == "&&" { "and" } else { "or" };
173            return Err(vec![diagnostic(
174                module,
175                "E_PARSE",
176                &format!("`{legacy}` is no longer supported; use `{replacement}`"),
177                start,
178                start + legacy.len(),
179            )]);
180        } else if let Some(punctuation) = punctuation_at(source, at) {
181            at += punctuation.len();
182            ("punctuation", at, None)
183        } else {
184            return Err(vec![diagnostic(
185                module,
186                "E_LEX",
187                "unexpected character",
188                start,
189                start + ch.len_utf8(),
190            )]);
191        };
192        let text = source[start..text_end].to_owned();
193        tokens.push(LexToken {
194            token: Token {
195                kind: kind.into(),
196                text,
197                span: Span::new(module, start, text_end),
198            },
199            value,
200        });
201        if tokens.len() > MAX_TOKENS {
202            return Err(vec![diagnostic(
203                module,
204                "E_LIMIT",
205                "token count exceeds the 100000 limit",
206                start,
207                text_end,
208            )]);
209        }
210        at = text_end;
211    }
212    Ok(tokens)
213}
214
215fn punctuation_at(source: &str, at: usize) -> Option<&'static str> {
216    for punctuation in [
217        "...", "->", "=>", "==", "!=", "<=", ">=", "{", "}", "(", ")", "[", "]", ",", ";", ":",
218        ".", "=", "+", "-", "*", "/", "%", "@", "<", ">", "!", "?",
219    ] {
220        if source[at..].starts_with(punctuation) {
221            return Some(punctuation);
222        }
223    }
224    None
225}
226
227fn is_keyword(word: &str) -> bool {
228    matches!(
229        word,
230        "class"
231            | "type"
232            | "enum"
233            | "match"
234            | "func"
235            | "trait"
236            | "impl"
237            | "for"
238            | "system"
239            | "of"
240            | "import"
241            | "export"
242            | "let"
243            | "mut"
244            | "if"
245            | "else"
246            | "while"
247            | "in"
248            | "ret"
249            | "return"
250            | "break"
251            | "continue"
252            | "true"
253            | "false"
254            | "and"
255            | "or"
256    )
257}
258
259fn diagnostic(module: &str, code: &str, message: &str, start: usize, end: usize) -> Diagnostic {
260    Diagnostic::new(code, message, Span::new(module, start, end))
261}
262
263struct Parser {
264    tokens: Vec<LexToken>,
265    at: usize,
266    module: String,
267    source_len: usize,
268    nesting: usize,
269}
270
271impl Parser {
272    fn module_items(&mut self) -> Result<Vec<Item>, Vec<Diagnostic>> {
273        let mut items = Vec::new();
274        while !self.eof() {
275            let item = match self.text() {
276                Some("type") => Item::Class(self.class()?),
277                Some("class") => {
278                    return self
279                        .error("`class` has been renamed to `type`; migrate this declaration");
280                }
281                Some("enum") => Item::Enum(self.enum_item()?),
282                Some("func") => Item::Function(self.function(false)?),
283                Some("trait") => Item::Trait(self.trait_item()?),
284                Some("impl") => Item::Implementation(self.implementation()?),
285                Some("system") => Item::System(self.system()?),
286                Some("import") => self.import()?,
287                Some("export") => self.export()?,
288                _ => Item::Statement(self.statement()?),
289            };
290            items.push(item);
291            self.optional(";");
292        }
293        Ok(items)
294    }
295
296    fn class(&mut self) -> Result<Class, Vec<Diagnostic>> {
297        let start = self.expect("type")?.span.start;
298        let (name, _) = self.identifier()?;
299        self.expect("{")?;
300        let mut fields = Vec::new();
301        let mut methods = Vec::new();
302        while !self.check("}") {
303            if self.eof() {
304                return self.error("expected `}` to close class");
305            }
306            if self.check("func") {
307                methods.push(self.function(false)?);
308                self.separator();
309                continue;
310            }
311            let (field_name, field_span) = self.identifier()?;
312            self.expect(":")?;
313            let ty = self.ty()?;
314            let default = if self.optional("=") {
315                Some(self.expression(0)?)
316            } else {
317                None
318            };
319            let end = default
320                .as_ref()
321                .map_or(field_span.end, |expr| expr.span.end);
322            fields.push(Field {
323                name: field_name,
324                ty,
325                default,
326                span: Span::new(&self.module, field_span.start, end),
327            });
328            self.separator();
329        }
330        let end = self.expect("}")?.span.end;
331        Ok(Class {
332            name,
333            fields,
334            methods,
335            span: Span::new(&self.module, start, end),
336        })
337    }
338
339    fn enum_item(&mut self) -> Result<Enum, Vec<Diagnostic>> {
340        let start = self.expect("enum")?.span.start;
341        let (name, _) = self.identifier()?;
342        self.expect("{")?;
343        let mut cases = Vec::new();
344        while !self.check("}") {
345            if self.eof() {
346                return self.error("expected `}` to close enum");
347            }
348            let (case_name, case_span) = self.identifier()?;
349            let mut payload = Vec::new();
350            let end = if self.optional("(") {
351                while !self.check(")") {
352                    payload.push(self.ty()?);
353                    if !self.optional(",") {
354                        break;
355                    }
356                }
357                self.expect(")")?.span.end
358            } else {
359                case_span.end
360            };
361            cases.push(EnumCase {
362                name: case_name,
363                payload,
364                span: Span::new(&self.module, case_span.start, end),
365            });
366            self.separator();
367        }
368        let end = self.expect("}")?.span.end;
369        Ok(Enum {
370            name,
371            cases,
372            span: Span::new(&self.module, start, end),
373        })
374    }
375
376    fn function(&mut self, signature_only: bool) -> Result<Function, Vec<Diagnostic>> {
377        let start = self.expect("func")?.span.start;
378        let (name, _) = self.identifier()?;
379        self.expect("(")?;
380        let mut parameters = Vec::new();
381        while !self.check(")") {
382            let (parameter_name, parameter_span) = self.identifier()?;
383            self.expect(":")?;
384            let ty = self.ty()?;
385            parameters.push(Parameter {
386                name: parameter_name,
387                ty,
388                span: parameter_span,
389            });
390            if !self.optional(",") {
391                break;
392            }
393        }
394        self.expect(")")?;
395        self.expect("->")?;
396        let returns = self.ty()?;
397        let body = if signature_only {
398            if !self.optional(";") {
399                return self.error("trait methods require a signature ending in `;`; default method bodies are not supported");
400            }
401            Vec::new()
402        } else {
403            if self.check(";") {
404                return self
405                    .error("functions require a body; only trait method signatures end in `;`");
406            }
407            self.block()?
408        };
409        let end = body
410            .last()
411            .map_or_else(|| self.previous_end(), |statement| statement.span.end);
412        Ok(Function {
413            name,
414            parameters,
415            returns,
416            body,
417            span: Span::new(&self.module, start, end),
418        })
419    }
420
421    fn trait_item(&mut self) -> Result<Trait, Vec<Diagnostic>> {
422        let start = self.expect("trait")?.span.start;
423        let (name, _) = self.identifier()?;
424        self.expect("{")?;
425        let mut methods = Vec::new();
426        while !self.check("}") {
427            if self.eof() {
428                return self.error("expected `}` to close trait");
429            }
430            if !self.check("func") {
431                return self.error("expected trait method signature");
432            }
433            methods.push(self.function(true)?);
434            self.separator();
435        }
436        let end = self.expect("}")?.span.end;
437        Ok(Trait {
438            name,
439            methods,
440            span: Span::new(&self.module, start, end),
441        })
442    }
443
444    fn implementation(&mut self) -> Result<Implementation, Vec<Diagnostic>> {
445        let start = self.expect("impl")?.span.start;
446        let (first, _) = self.qualified_identifier()?;
447        let (trait_name, class) = if self.optional("for") {
448            let (class, _) = self.qualified_identifier()?;
449            (Some(first), class)
450        } else {
451            (None, first)
452        };
453        self.expect("{")?;
454        let mut methods = Vec::new();
455        while !self.check("}") {
456            if self.eof() {
457                return self.error("expected `}` to close impl");
458            }
459            if !self.check("func") {
460                return self.error("expected implementation method");
461            }
462            methods.push(self.function(false)?);
463            self.separator();
464        }
465        let end = self.expect("}")?.span.end;
466        Ok(Implementation {
467            trait_name,
468            class,
469            methods,
470            span: Span::new(&self.module, start, end),
471        })
472    }
473
474    fn system(&mut self) -> Result<System, Vec<Diagnostic>> {
475        let start = self.expect("system")?.span.start;
476        let (name, _) = self.identifier()?;
477        self.expect("of")?;
478        let mut bindings = Vec::new();
479        if self.optional("(") {
480            while !self.check(")") {
481                let (binding_name, span) = self.identifier()?;
482                self.expect(":")?;
483                let ty = self.ty()?;
484                bindings.push(Parameter {
485                    name: binding_name,
486                    ty,
487                    span,
488                });
489                if !self.optional(",") {
490                    break;
491                }
492            }
493            self.expect(")")?;
494        } else {
495            let (component, span) = self.qualified_identifier()?;
496            bindings.push(Parameter {
497                name: "self".into(),
498                ty: Type::named(component),
499                span,
500            });
501        }
502        self.expect("{")?;
503        let mut callbacks = Vec::new();
504        while !self.check("}") {
505            if !self.check("func") {
506                return self.error(
507                    "systems declare lifecycle functions: func frame(dt: Number) -> Unit { ... }",
508                );
509            }
510            callbacks.push(self.function(false)?);
511            self.separator();
512        }
513        let end = self.expect("}")?.span.end;
514        Ok(System {
515            name,
516            bindings,
517            callbacks,
518            span: Span::new(&self.module, start, end),
519        })
520    }
521
522    fn import(&mut self) -> Result<Item, Vec<Diagnostic>> {
523        let start = self.expect("import")?.span.start;
524        let (mut module, _) = self.identifier()?;
525        while self.optional(".") {
526            let (part, _) = self.identifier()?;
527            module.push('.');
528            module.push_str(&part);
529        }
530        let end = self.previous_end();
531        Ok(Item::Import {
532            module,
533            span: Span::new(&self.module, start, end),
534        })
535    }
536    fn export(&mut self) -> Result<Item, Vec<Diagnostic>> {
537        let start = self.expect("export")?.span.start;
538        let (name, span) = self.identifier()?;
539        Ok(Item::Export {
540            name,
541            span: Span::new(&self.module, start, span.end),
542        })
543    }
544
545    fn block(&mut self) -> Result<Vec<Stmt>, Vec<Diagnostic>> {
546        self.enter()?;
547        self.expect("{")?;
548        let mut body = Vec::new();
549        while !self.check("}") {
550            if self.eof() {
551                self.leave();
552                return self.error("expected `}` to close block");
553            }
554            body.push(self.statement()?);
555            self.optional(";");
556        }
557        self.expect("}")?;
558        self.leave();
559        Ok(body)
560    }
561
562    fn statement(&mut self) -> Result<Stmt, Vec<Diagnostic>> {
563        self.enter()?;
564        let result = self.statement_inner();
565        self.leave();
566        result
567    }
568    fn statement_inner(&mut self) -> Result<Stmt, Vec<Diagnostic>> {
569        let start = self.current_start();
570        match self.text() {
571            Some("let") => {
572                self.advance();
573                let mutable = self.optional("mut");
574                let (name, _) = self.identifier()?;
575                let ty = if self.optional(":") {
576                    Some(self.ty()?)
577                } else {
578                    None
579                };
580                self.expect("=")?;
581                let value = self.expression(0)?;
582                let end = value.span.end;
583                Ok(Stmt {
584                    kind: StmtKind::Let {
585                        name,
586                        ty,
587                        mutable,
588                        value,
589                    },
590                    span: Span::new(&self.module, start, end),
591                })
592            }
593            Some("ret") | Some("return") => {
594                self.advance();
595                let value = if self.check(";") || self.check("}") || self.eof() {
596                    None
597                } else {
598                    Some(self.expression(0)?)
599                };
600                let end = value
601                    .as_ref()
602                    .map_or_else(|| self.previous_end(), |expr| expr.span.end);
603                Ok(Stmt {
604                    kind: StmtKind::Return(value),
605                    span: Span::new(&self.module, start, end),
606                })
607            }
608            Some("if") => {
609                self.advance();
610                let condition = self.expression(0)?;
611                let then_body = self.block()?;
612                let else_body = if self.optional("else") {
613                    if self.check("if") {
614                        vec![self.statement()?]
615                    } else {
616                        self.block()?
617                    }
618                } else {
619                    Vec::new()
620                };
621                Ok(Stmt {
622                    kind: StmtKind::If {
623                        condition,
624                        then_body,
625                        else_body,
626                    },
627                    span: Span::new(&self.module, start, self.previous_end()),
628                })
629            }
630            Some("while") => {
631                self.advance();
632                let condition = self.expression(0)?;
633                let body = self.block()?;
634                Ok(Stmt {
635                    kind: StmtKind::While { condition, body },
636                    span: Span::new(&self.module, start, self.previous_end()),
637                })
638            }
639            Some("for") => {
640                self.advance();
641                let (name, _) = self.identifier()?;
642                self.expect("in")?;
643                let collection = self.expression(0)?;
644                let body = self.block()?;
645                Ok(Stmt {
646                    kind: StmtKind::For {
647                        name,
648                        collection,
649                        body,
650                    },
651                    span: Span::new(&self.module, start, self.previous_end()),
652                })
653            }
654            Some("break") => {
655                let end = self.advance().token.span.end;
656                Ok(Stmt {
657                    kind: StmtKind::Break,
658                    span: Span::new(&self.module, start, end),
659                })
660            }
661            Some("continue") => {
662                let end = self.advance().token.span.end;
663                Ok(Stmt {
664                    kind: StmtKind::Continue,
665                    span: Span::new(&self.module, start, end),
666                })
667            }
668            _ => {
669                let target = self.expression(0)?;
670                if self.optional("=") {
671                    let value = self.expression(0)?;
672                    let end = value.span.end;
673                    Ok(Stmt {
674                        kind: StmtKind::Assign { target, value },
675                        span: Span::new(&self.module, start, end),
676                    })
677                } else {
678                    let end = target.span.end;
679                    Ok(Stmt {
680                        kind: StmtKind::Expr(target),
681                        span: Span::new(&self.module, start, end),
682                    })
683                }
684            }
685        }
686    }
687
688    fn ty(&mut self) -> Result<Type, Vec<Diagnostic>> {
689        self.enter()?;
690        if self.optional("func") {
691            self.expect("(")?;
692            let mut parameters = Vec::new();
693            while !self.check(")") {
694                parameters.push(self.ty()?);
695                if !self.optional(",") {
696                    break;
697                }
698            }
699            self.expect(")")?;
700            self.expect("->")?;
701            let returns = self.ty()?;
702            self.leave();
703            return Ok(Type::function(parameters, returns));
704        }
705        let (name, _) = self.qualified_identifier()?;
706        if self.optional("[") {
707            let mut arguments = Vec::new();
708            while !self.check("]") {
709                arguments.push(self.ty()?);
710                if !self.optional(",") {
711                    break;
712                }
713            }
714            self.expect("]")?;
715            self.leave();
716            Ok(match name.as_str() {
717                "List" if arguments.len() == 1 => Type::List(Box::new(arguments.remove(0))),
718                "ComponentReference" if arguments.len() == 1 => {
719                    Type::ComponentReference(Box::new(arguments.remove(0)))
720                }
721                "Tensor" if arguments.len() == 1 => Type::Tensor(Box::new(arguments.remove(0))),
722                _ => Type::Applied { name, arguments },
723            })
724        } else {
725            self.leave();
726            Ok(Type::named(name))
727        }
728    }
729
730    fn expression(&mut self, min_binding_power: u8) -> Result<Expr, Vec<Diagnostic>> {
731        self.enter()?;
732        let mut left = self.prefix()?;
733        let mut chain_length = 0;
734        loop {
735            chain_length += 1;
736            if chain_length > MAX_NESTING {
737                return self.error("expression operator chain exceeds nesting limit");
738            }
739            if let Some((left_power, right_power)) = self.infix_binding_power() {
740                if left_power < min_binding_power {
741                    break;
742                }
743                let op = self.advance().token.text;
744                let right = self.expression(right_power)?;
745                let span = Span::new(&self.module, left.span.start, right.span.end);
746                left = Expr {
747                    kind: ExprKind::Binary {
748                        op,
749                        left: Box::new(left),
750                        right: Box::new(right),
751                    },
752                    span,
753                };
754            } else {
755                break;
756            }
757        }
758        self.leave();
759        Ok(left)
760    }
761
762    fn prefix(&mut self) -> Result<Expr, Vec<Diagnostic>> {
763        let token = self.current().cloned().ok_or_else(|| {
764            vec![diagnostic(
765                &self.module,
766                "E_PARSE",
767                "expected expression",
768                self.source_len,
769                self.source_len,
770            )]
771        })?;
772        match token.token.kind.as_str() {
773            "number" => {
774                self.advance();
775                self.postfix(Expr {
776                    kind: ExprKind::Number(token.token.text),
777                    span: token.token.span,
778                })
779            }
780            "string" => {
781                self.advance();
782                self.postfix(Expr {
783                    kind: ExprKind::Text(token.value.unwrap_or_default()),
784                    span: token.token.span,
785                })
786            }
787            "identifier" | "keyword"
788                if token.token.text == "true" || token.token.text == "false" =>
789            {
790                self.advance();
791                self.postfix(Expr {
792                    kind: ExprKind::Bool(token.token.text == "true"),
793                    span: token.token.span,
794                })
795            }
796            "identifier" => {
797                self.advance();
798                let mut expr = Expr {
799                    kind: ExprKind::Name(token.token.text.clone()),
800                    span: token.token.span.clone(),
801                };
802                if is_uppercase_name(&token.token.text) && self.check("{") {
803                    expr = self.record(token.token.text, expr.span.start)?;
804                }
805                self.postfix(expr)
806            }
807            "keyword" if token.token.text == "match" => self.match_expression(),
808            "punctuation" if token.token.text == "-" || token.token.text == "!" => {
809                self.advance();
810                let value = if token.token.text == "-"
811                    && self
812                        .current()
813                        .is_some_and(|next| next.token.kind == "number")
814                {
815                    let number = self.advance().token;
816                    Expr {
817                        kind: ExprKind::Number(number.text),
818                        span: number.span,
819                    }
820                } else {
821                    self.expression(13)?
822                };
823                let span = Span::new(&self.module, token.token.span.start, value.span.end);
824                self.postfix(Expr {
825                    kind: ExprKind::Unary {
826                        op: token.token.text,
827                        value: Box::new(value),
828                    },
829                    span,
830                })
831            }
832            "punctuation" if token.token.text == "(" => {
833                self.advance();
834                if self.optional(")") {
835                    self.postfix(Expr {
836                        kind: ExprKind::Unit,
837                        span: Span::new(&self.module, token.token.span.start, self.previous_end()),
838                    })
839                } else {
840                    let target = self.expression(0)?;
841                    let expr = if self.optional("=") {
842                        let value = self.expression(0)?;
843                        Expr {
844                            span: Span::new(&self.module, target.span.start, value.span.end),
845                            kind: ExprKind::Assign {
846                                target: Box::new(target),
847                                value: Box::new(value),
848                            },
849                        }
850                    } else {
851                        target
852                    };
853                    self.expect(")")?;
854                    self.postfix(expr)
855                }
856            }
857            "punctuation" if token.token.text == "[" => self.list(),
858            _ => self.error("expected expression"),
859        }
860    }
861
862    fn match_expression(&mut self) -> Result<Expr, Vec<Diagnostic>> {
863        let start = self.expect("match")?.span.start;
864        let value = self.expression(0)?;
865        self.expect("{")?;
866        let mut arms = Vec::new();
867        while !self.check("}") {
868            if self.eof() {
869                return self.error("expected `}` to close match");
870            }
871            let pattern_start = self.current_start();
872            let pattern = self.pattern()?;
873            self.expect("=>")?;
874            let arm_value = self.expression(0)?;
875            let arm_span = Span::new(&self.module, pattern_start, arm_value.span.end);
876            arms.push(MatchArm {
877                pattern,
878                value: arm_value,
879                span: arm_span,
880            });
881            if !self.optional(",") {
882                break;
883            }
884        }
885        let end = self.expect("}")?.span.end;
886        self.postfix(Expr {
887            kind: ExprKind::Match {
888                value: Box::new(value),
889                arms,
890            },
891            span: Span::new(&self.module, start, end),
892        })
893    }
894
895    fn pattern(&mut self) -> Result<Pattern, Vec<Diagnostic>> {
896        let token = self.current().cloned().ok_or_else(|| {
897            vec![diagnostic(
898                &self.module,
899                "E_PARSE",
900                "expected pattern",
901                self.source_len,
902                self.source_len,
903            )]
904        })?;
905        match token.token.kind.as_str() {
906            "number" => {
907                self.advance();
908                Ok(Pattern::Literal(Box::new(Expr {
909                    kind: ExprKind::Number(token.token.text),
910                    span: token.token.span,
911                })))
912            }
913            "string" => {
914                self.advance();
915                Ok(Pattern::Literal(Box::new(Expr {
916                    kind: ExprKind::Text(token.value.unwrap_or_default()),
917                    span: token.token.span,
918                })))
919            }
920            "keyword" if token.token.text == "true" || token.token.text == "false" => {
921                self.advance();
922                Ok(Pattern::Literal(Box::new(Expr {
923                    kind: ExprKind::Bool(token.token.text == "true"),
924                    span: token.token.span,
925                })))
926            }
927            "punctuation" if token.token.text == "(" => {
928                let start = self.advance().token.span.start;
929                self.expect(")")?;
930                Ok(Pattern::Literal(Box::new(Expr {
931                    kind: ExprKind::Unit,
932                    span: Span::new(&self.module, start, self.previous_end()),
933                })))
934            }
935            "identifier" => {
936                let (name, _) = self.qualified_identifier()?;
937                if name == "_" {
938                    return Ok(Pattern::Wildcard);
939                }
940                if self.check("{") {
941                    return self.record_pattern(name);
942                }
943                if self.optional("(") {
944                    let mut payload = Vec::new();
945                    while !self.check(")") {
946                        payload.push(self.pattern()?);
947                        if !self.optional(",") {
948                            break;
949                        }
950                    }
951                    self.expect(")")?;
952                    return Ok(Pattern::Case { name, payload });
953                }
954                if name.contains('.') || matches!(name.as_str(), "None" | "Some" | "Ok" | "Err") {
955                    Ok(Pattern::Case {
956                        name,
957                        payload: Vec::new(),
958                    })
959                } else {
960                    Ok(Pattern::Binding(name))
961                }
962            }
963            _ => self.error("expected pattern"),
964        }
965    }
966
967    fn record_pattern(&mut self, class: String) -> Result<Pattern, Vec<Diagnostic>> {
968        self.expect("{")?;
969        let mut fields = BTreeMap::new();
970        while !self.check("}") {
971            if self.eof() {
972                return self.error("expected `}` to close record pattern");
973            }
974            let (name, _) = self.identifier()?;
975            let pattern = if self.optional(":") {
976                self.pattern()?
977            } else {
978                Pattern::Binding(name.clone())
979            };
980            if fields.insert(name.clone(), pattern).is_some() {
981                return self.error(&format!("duplicate record pattern field `{name}`"));
982            }
983            if !self.optional(",") {
984                break;
985            }
986        }
987        self.expect("}")?;
988        Ok(Pattern::Record { class, fields })
989    }
990
991    fn typed_intrinsic_ahead(&self, expr: &Expr) -> bool {
992        let Some(name) = expression_path(expr) else {
993            return false;
994        };
995        if !matches!(
996            name.strip_prefix("builtin.").unwrap_or(&name),
997            "get" | "has" | "query" | "bind" | "tensor"
998        ) || !self.check("[")
999        {
1000            return false;
1001        }
1002        let mut depth = 1usize;
1003        // Speculate only over type-shaped tokens. `get < 1` remains an ordinary
1004        // comparison when a lexical value happens to use an intrinsic's name.
1005        for (offset, token) in self.tokens.iter().skip(self.at + 1).enumerate() {
1006            if offset > MAX_NESTING * 4 {
1007                return false;
1008            }
1009            match token.token.text.as_str() {
1010                "[" => {
1011                    depth += 1;
1012                    if depth > MAX_NESTING {
1013                        return false;
1014                    }
1015                }
1016                "]" => {
1017                    depth -= 1;
1018                    if depth == 0 {
1019                        return self.tokens.get(self.at + offset + 2).is_none_or(|token| {
1020                            matches!(
1021                                token.token.text.as_str(),
1022                                "(" | ";"
1023                                    | ")"
1024                                    | ","
1025                                    | "]"
1026                                    | "}"
1027                                    | "."
1028                                    | "=="
1029                                    | "!="
1030                                    | "["
1031                                    | "<="
1032                                    | ">"
1033                                    | ">="
1034                                    | "+"
1035                                    | "-"
1036                                    | "*"
1037                                    | "/"
1038                                    | "%"
1039                                    | "@"
1040                                    | "and"
1041                                    | "or"
1042                            )
1043                        });
1044                    }
1045                }
1046                "." => (),
1047                _ if token.token.kind == "identifier" => (),
1048                _ => return false,
1049            }
1050        }
1051        false
1052    }
1053
1054    fn typed_literal_ahead(&self, expr: &Expr) -> bool {
1055        let numeric = matches!(&expr.kind, ExprKind::Number(_))
1056            || matches!(&expr.kind, ExprKind::Unary { op, value }
1057                if op == "-" && matches!(value.kind, ExprKind::Number(_)));
1058        numeric
1059            && self.check(":")
1060            && self.tokens.get(self.at + 1).is_some_and(|token| {
1061                token.token.kind == "identifier"
1062                    && crate::data::named_dtype(&token.token.text).is_some()
1063            })
1064    }
1065
1066    fn postfix(&mut self, mut expr: Expr) -> Result<Expr, Vec<Diagnostic>> {
1067        let mut chain_length = 0;
1068        loop {
1069            chain_length += 1;
1070            if chain_length > MAX_NESTING {
1071                return self.error("expression access chain exceeds nesting limit");
1072            }
1073            if self.typed_intrinsic_ahead(&expr) {
1074                self.expect("[")?;
1075                let ty = self.ty()?;
1076                self.expect("]")?;
1077                let name = expression_path(&expr).expect("typed intrinsic name checked above");
1078                if self.optional("(") {
1079                    let mut arguments = Vec::new();
1080                    while !self.check(")") {
1081                        arguments.push(self.expression(0)?);
1082                        if !self.optional(",") {
1083                            break;
1084                        }
1085                    }
1086                    let end = self.expect(")")?.span.end;
1087                    expr = Expr {
1088                        span: Span::new(&self.module, expr.span.start, end),
1089                        kind: ExprKind::TypedCall {
1090                            name,
1091                            ty,
1092                            arguments,
1093                        },
1094                    };
1095                } else {
1096                    expr = Expr {
1097                        span: Span::new(&self.module, expr.span.start, self.previous_end()),
1098                        kind: ExprKind::TypedFunction { name, ty },
1099                    };
1100                }
1101            } else if self.typed_literal_ahead(&expr) {
1102                let start = expr.span.start;
1103                self.expect(":")?;
1104                let ty = self.ty()?;
1105                let end = self.previous_end();
1106                expr = Expr {
1107                    kind: ExprKind::TypedLiteral {
1108                        value: Box::new(expr),
1109                        ty,
1110                    },
1111                    span: Span::new(&self.module, start, end),
1112                };
1113            } else if self.optional("(") {
1114                let mut arguments = Vec::new();
1115                while !self.check(")") {
1116                    arguments.push(self.expression(0)?);
1117                    if !self.optional(",") {
1118                        break;
1119                    }
1120                }
1121                let end = self.expect(")")?.span.end;
1122                let start = expr.span.start;
1123                expr = Expr {
1124                    kind: ExprKind::Call {
1125                        callee: Box::new(expr),
1126                        arguments,
1127                    },
1128                    span: Span::new(&self.module, start, end),
1129                };
1130            } else if self.optional(".") {
1131                let (field, field_span) = self.identifier()?;
1132                let start = expr.span.start;
1133                if field == "convert" && self.optional("[") {
1134                    let ty = self.ty()?;
1135                    let end = self.expect("]")?.span.end;
1136                    expr = Expr {
1137                        kind: ExprKind::TypedMethod {
1138                            object: Box::new(expr),
1139                            method: field,
1140                            ty,
1141                        },
1142                        span: Span::new(&self.module, start, end),
1143                    };
1144                    continue;
1145                }
1146                // A qualified class path is the one ambiguous `name.name {` form.
1147                // Restrict it to a plain leading name, leaving ordinary field access
1148                // such as `object.field` and native `builtin.sin()` unchanged.
1149                if is_uppercase_name(&field) && self.check("{") {
1150                    if let Some(prefix) = expression_path(&expr) {
1151                        expr = self.record(format!("{prefix}.{field}"), start)?;
1152                    } else {
1153                        expr = Expr {
1154                            kind: ExprKind::Field {
1155                                object: Box::new(expr),
1156                                field,
1157                            },
1158                            span: Span::new(&self.module, start, field_span.end),
1159                        };
1160                    }
1161                } else {
1162                    expr = Expr {
1163                        kind: ExprKind::Field {
1164                            object: Box::new(expr),
1165                            field,
1166                        },
1167                        span: Span::new(&self.module, start, field_span.end),
1168                    };
1169                }
1170            } else if self.optional("[") {
1171                let indices = self.indices()?;
1172                let end = self.expect("]")?.span.end;
1173                let start = expr.span.start;
1174                expr = Expr {
1175                    kind: ExprKind::Index {
1176                        object: Box::new(expr),
1177                        indices,
1178                    },
1179                    span: Span::new(&self.module, start, end),
1180                };
1181            } else if self.optional("?") {
1182                let start = expr.span.start;
1183                expr = Expr {
1184                    kind: ExprKind::Propagate(Box::new(expr)),
1185                    span: Span::new(&self.module, start, self.previous_end()),
1186                };
1187            } else {
1188                break;
1189            }
1190        }
1191        Ok(expr)
1192    }
1193
1194    fn indices(&mut self) -> Result<Vec<IndexExpr>, Vec<Diagnostic>> {
1195        let mut indices = Vec::new();
1196        while !self.check("]") {
1197            if self.eof() {
1198                return self.error("expected `]` to close index");
1199            }
1200            indices.push(self.index_expr()?);
1201            if !self.optional(",") {
1202                break;
1203            }
1204        }
1205        if indices.is_empty() {
1206            return self.error("index requires at least one selector");
1207        }
1208        Ok(indices)
1209    }
1210
1211    fn index_expr(&mut self) -> Result<IndexExpr, Vec<Diagnostic>> {
1212        if self.optional("...") {
1213            return Ok(IndexExpr::Ellipsis);
1214        }
1215        if matches!(self.text(), Some("None") | Some("newaxis")) {
1216            self.advance();
1217            return Ok(IndexExpr::NewAxis);
1218        }
1219        let start = if self.check(":") {
1220            None
1221        } else {
1222            Some(self.expression(0)?)
1223        };
1224        if !self.optional(":") {
1225            return Ok(IndexExpr::Index(
1226                start.expect("index expression is present"),
1227            ));
1228        }
1229        let stop = if self.check(":") || self.check(",") || self.check("]") {
1230            None
1231        } else {
1232            Some(self.expression(0)?)
1233        };
1234        let step = if self.optional(":") {
1235            if self.check(",") || self.check("]") {
1236                None
1237            } else {
1238                Some(self.expression(0)?)
1239            }
1240        } else {
1241            None
1242        };
1243        Ok(IndexExpr::Slice {
1244            start: start.map(Box::new),
1245            stop: stop.map(Box::new),
1246            step: step.map(Box::new),
1247        })
1248    }
1249
1250    fn list(&mut self) -> Result<Expr, Vec<Diagnostic>> {
1251        let start = self.expect("[")?.span.start;
1252        let mut items = Vec::new();
1253        while !self.check("]") {
1254            if self.eof() {
1255                return self.error("expected `]` to close list");
1256            }
1257            items.push(self.expression(0)?);
1258            if !self.optional(",") {
1259                break;
1260            }
1261        }
1262        let end = self.expect("]")?.span.end;
1263        let expr = if self.optional(":") {
1264            let dtype = self.ty()?;
1265            Expr {
1266                kind: ExprKind::TensorLiteral {
1267                    values: items,
1268                    dtype,
1269                },
1270                span: Span::new(&self.module, start, self.previous_end()),
1271            }
1272        } else {
1273            Expr {
1274                kind: ExprKind::List(items),
1275                span: Span::new(&self.module, start, end),
1276            }
1277        };
1278        self.postfix(expr)
1279    }
1280
1281    fn record(&mut self, class: String, start: usize) -> Result<Expr, Vec<Diagnostic>> {
1282        self.expect("{")?;
1283        let mut fields = BTreeMap::new();
1284        while !self.check("}") {
1285            if self.eof() {
1286                return self.error("expected `}` to close constructor");
1287            }
1288            let (name, _) = self.identifier()?;
1289            self.expect(":")?;
1290            let value = self.expression(0)?;
1291            if fields.insert(name.clone(), value).is_some() {
1292                return self.error(&format!("duplicate constructor field `{name}`"));
1293            }
1294            if !self.optional(",") {
1295                break;
1296            }
1297        }
1298        let end = self.expect("}")?.span.end;
1299        Ok(Expr {
1300            kind: ExprKind::Record { class, fields },
1301            span: Span::new(&self.module, start, end),
1302        })
1303    }
1304
1305    fn infix_binding_power(&self) -> Option<(u8, u8)> {
1306        Some(match self.text()? {
1307            "or" => (1, 2),
1308            "and" => (3, 4),
1309            "==" | "!=" => (5, 6),
1310            "<" | "<=" | ">" | ">=" => (7, 8),
1311            "+" | "-" => (9, 10),
1312            "*" | "/" | "%" | "@" => (11, 12),
1313            _ => return None,
1314        })
1315    }
1316    fn separator(&mut self) {
1317        if !self.optional(";") {
1318            self.optional(",");
1319        }
1320    }
1321    fn enter(&mut self) -> Result<(), Vec<Diagnostic>> {
1322        self.nesting += 1;
1323        if self.nesting > MAX_NESTING {
1324            self.nesting -= 1;
1325            self.error("nesting exceeds the 64 level limit")
1326        } else {
1327            Ok(())
1328        }
1329    }
1330    fn leave(&mut self) {
1331        self.nesting = self.nesting.saturating_sub(1);
1332    }
1333    fn qualified_identifier(&mut self) -> Result<(String, Span), Vec<Diagnostic>> {
1334        let (mut name, mut span) = self.identifier()?;
1335        while self.optional(".") {
1336            let (part, next) = self.identifier()?;
1337            name.push('.');
1338            name.push_str(&part);
1339            span.end = next.end;
1340        }
1341        Ok((name, span))
1342    }
1343    fn identifier(&mut self) -> Result<(String, Span), Vec<Diagnostic>> {
1344        let token = self.current().cloned().ok_or_else(|| {
1345            vec![diagnostic(
1346                &self.module,
1347                "E_PARSE",
1348                "expected identifier",
1349                self.source_len,
1350                self.source_len,
1351            )]
1352        })?;
1353        if token.token.kind == "identifier" {
1354            self.advance();
1355            Ok((token.token.text, token.token.span))
1356        } else {
1357            self.error("expected identifier")
1358        }
1359    }
1360    fn expect(&mut self, expected: &str) -> Result<Token, Vec<Diagnostic>> {
1361        if self.check(expected) {
1362            Ok(self.advance().token)
1363        } else {
1364            self.error(&format!("expected `{expected}`"))
1365        }
1366    }
1367    fn optional(&mut self, text: &str) -> bool {
1368        if self.check(text) {
1369            self.advance();
1370            true
1371        } else {
1372            false
1373        }
1374    }
1375    fn check(&self, text: &str) -> bool {
1376        self.text() == Some(text)
1377    }
1378    fn text(&self) -> Option<&str> {
1379        self.current().map(|token| token.token.text.as_str())
1380    }
1381    fn current(&self) -> Option<&LexToken> {
1382        self.tokens.get(self.at)
1383    }
1384    fn current_start(&self) -> usize {
1385        self.current()
1386            .map_or(self.source_len, |token| token.token.span.start)
1387    }
1388    fn previous_end(&self) -> usize {
1389        self.tokens
1390            .get(self.at.saturating_sub(1))
1391            .map_or(self.source_len, |token| token.token.span.end)
1392    }
1393    fn advance(&mut self) -> LexToken {
1394        let token = self.tokens[self.at].clone();
1395        self.at += 1;
1396        token
1397    }
1398    fn eof(&self) -> bool {
1399        self.at >= self.tokens.len()
1400    }
1401    fn error<T>(&self, message: &str) -> Result<T, Vec<Diagnostic>> {
1402        let span = self.current().map_or_else(
1403            || Span::new(&self.module, self.source_len, self.source_len),
1404            |token| token.token.span.clone(),
1405        );
1406        Err(vec![Diagnostic::new("E_PARSE", message, span)])
1407    }
1408}
1409
1410fn is_uppercase_name(name: &str) -> bool {
1411    name.as_bytes().first().is_some_and(u8::is_ascii_uppercase)
1412}
1413
1414fn expression_path(expr: &Expr) -> Option<String> {
1415    match &expr.kind {
1416        ExprKind::Name(n) => Some(n.clone()),
1417        ExprKind::Field { object, field } => {
1418            Some(format!("{}.{}", expression_path(object)?, field))
1419        }
1420        _ => None,
1421    }
1422}
1423
1424#[cfg(test)]
1425mod tests {
1426    use super::parse;
1427    use crate::ast::{ExprKind, IndexExpr, Item, StmtKind, Type};
1428
1429    fn let_value(source: &str) -> crate::ast::Expr {
1430        let items = parse(source, "parser-test").expect("source parses");
1431        let Item::Statement(statement) = items.into_iter().next().expect("one item") else {
1432            panic!("expected statement");
1433        };
1434        let StmtKind::Let { value, .. } = statement.kind else {
1435            panic!("expected let statement");
1436        };
1437        value
1438    }
1439
1440    #[test]
1441    fn parses_square_generics_and_typed_intrinsics_without_consuming_indexes() {
1442        let items = parse(
1443            "type Holder { values: List[Tensor[f64]]; ref: ComponentReference[Str]; } let getter = get[Holder]; let element = values[0];",
1444            "parser-test",
1445        )
1446        .expect("source parses");
1447        let Item::Class(class) = &items[0] else {
1448            panic!("expected class");
1449        };
1450        assert_eq!(
1451            class.fields[0].ty,
1452            Type::List(Box::new(Type::Tensor(Box::new(Type::named("f64")))))
1453        );
1454        assert_eq!(
1455            class.fields[1].ty,
1456            Type::ComponentReference(Box::new(Type::named("Str")))
1457        );
1458        let Item::Statement(statement) = &items[1] else {
1459            panic!("expected typed intrinsic binding");
1460        };
1461        assert!(matches!(
1462            statement.kind,
1463            StmtKind::Let {
1464                value: crate::ast::Expr {
1465                    kind: ExprKind::TypedFunction { ref name, .. },
1466                    ..
1467                },
1468                ..
1469            } if name == "get"
1470        ));
1471        let Item::Statement(statement) = &items[2] else {
1472            panic!("expected indexed binding");
1473        };
1474        assert!(matches!(
1475            statement.kind,
1476            StmtKind::Let {
1477                value: crate::ast::Expr { kind: ExprKind::Index { ref indices, .. }, .. },
1478                ..
1479            } if matches!(indices.as_slice(), [IndexExpr::Index(_)])
1480        ));
1481    }
1482
1483    #[test]
1484    fn preserves_raw_numbers_matrix_multiply_and_numpy_selectors() {
1485        let value = let_value(
1486            "let result = tensor[184467440737095516160000, :, -2::-1, None, ...] @ other;",
1487        );
1488        let ExprKind::Binary { op, left, .. } = value.kind else {
1489            panic!("expected binary expression");
1490        };
1491        assert_eq!(op, "@");
1492        let ExprKind::Index { indices, .. } = left.kind else {
1493            panic!("expected index expression");
1494        };
1495        assert!(matches!(
1496            &indices[0],
1497            IndexExpr::Index(crate::ast::Expr { kind: ExprKind::Number(raw), .. })
1498                if raw == "184467440737095516160000"
1499        ));
1500        assert!(matches!(
1501            &indices[1],
1502            IndexExpr::Slice {
1503                start: None,
1504                stop: None,
1505                step: None
1506            }
1507        ));
1508        assert!(matches!(
1509            &indices[2],
1510            IndexExpr::Slice {
1511                start: Some(_),
1512                stop: None,
1513                step: Some(_)
1514            }
1515        ));
1516        assert!(matches!(&indices[3], IndexExpr::NewAxis));
1517        assert!(matches!(&indices[4], IndexExpr::Ellipsis));
1518    }
1519
1520    #[test]
1521    fn rejects_angle_bracket_generics() {
1522        assert!(parse("let values: List<Number> = [];", "parser-test").is_err());
1523    }
1524
1525    #[test]
1526    fn parses_algebraic_types_matches_propagation_and_tensors() {
1527        let items = parse(
1528            "type Point { x: f32, y: f32 } enum State { Idle, Moving(f32) } let result: Res[f32, Str] = match state { State.Idle => 0, State.Moving(speed) => speed, }; let point = match value { Point { x, y: _ } => x, }; let matrix = [[1, 2], [3, 4]]:f32; let propagated = result?;",
1529            "parser-test",
1530        )
1531        .expect("source parses");
1532        assert!(matches!(items[0], Item::Class(_)));
1533        assert!(matches!(items[1], Item::Enum(_)));
1534        let Item::Statement(statement) = &items[2] else {
1535            panic!("expected match binding");
1536        };
1537        assert!(matches!(
1538            statement.kind,
1539            StmtKind::Let {
1540                ty: Some(Type::Applied { ref name, ref arguments }),
1541                value: crate::ast::Expr { kind: ExprKind::Match { ref arms, .. }, .. },
1542                ..
1543            } if name == "Res" && arguments.len() == 2 && arms.len() == 2
1544        ));
1545        let Item::Statement(statement) = &items[4] else {
1546            panic!("expected tensor binding");
1547        };
1548        assert!(matches!(
1549            statement.kind,
1550            StmtKind::Let {
1551                value: crate::ast::Expr { kind: ExprKind::TensorLiteral { ref values, ref dtype }, .. },
1552                ..
1553            } if values.len() == 2 && *dtype == Type::named("f32")
1554        ));
1555        let Item::Statement(statement) = &items[5] else {
1556            panic!("expected propagation binding");
1557        };
1558        assert!(matches!(
1559            statement.kind,
1560            StmtKind::Let {
1561                value: crate::ast::Expr {
1562                    kind: ExprKind::Propagate(_),
1563                    ..
1564                },
1565                ..
1566            }
1567        ));
1568
1569        let method = let_value("let converted = (a + b).convert[f32](matrix);");
1570        assert!(matches!(
1571            method.kind,
1572            ExprKind::Call { ref callee, .. }
1573                if matches!(callee.kind, ExprKind::TypedMethod { ref method, ref ty, .. }
1574                    if method == "convert" && *ty == Type::named("f32"))
1575        ));
1576    }
1577
1578    #[test]
1579    fn rejects_legacy_class_declarations_with_migration_message() {
1580        let error = parse("class Point { x: f32 }", "parser-test").expect_err("class is retired");
1581        assert!(error[0].message.contains("renamed to `type`"));
1582    }
1583
1584    #[test]
1585    fn parses_parenthesized_assignment_as_a_result_expression() {
1586        let value = let_value("let outcome = (matrix[:, 1:] = values)?;");
1587        let ExprKind::Propagate(assign) = value.kind else {
1588            panic!("expected propagated assignment");
1589        };
1590        assert!(matches!(assign.kind, ExprKind::Assign { .. }));
1591    }
1592
1593    #[test]
1594    fn parses_typed_numeric_literals_without_consuming_slice_colons() {
1595        let positive = let_value("let value = 1:f32;");
1596        assert!(matches!(
1597            positive.kind,
1598            ExprKind::TypedLiteral { ref ty, .. } if *ty == Type::named("f32")
1599        ));
1600        let negative = let_value("let value = -128:i8;");
1601        assert!(matches!(
1602            negative.kind,
1603            ExprKind::TypedLiteral { ref value, ref ty }
1604                if *ty == Type::named("i8")
1605                    && matches!(value.kind, ExprKind::Unary { ref op, .. } if op == "-")
1606        ));
1607        let indexed = let_value("let value = rows[0:2];");
1608        assert!(matches!(indexed.kind, ExprKind::Index { .. }));
1609    }
1610}