1pub mod ast;
4
5use crate::error::Diagnostic;
6use crate::lexer::{Token, TokenKind};
7use crate::span::Span;
8use ast::*;
9
10const TERNARY_LBP: u8 = 2;
12const UNARY_RBP: u8 = 15;
13
14fn infix_bp(kind: &TokenKind<'_>) -> Option<(u8, u8, BinOp)> {
15 use TokenKind::*;
16 Some(match kind {
17 Or => (3, 4, BinOp::Or),
18 And => (5, 6, BinOp::And),
19 EqEq => (7, 8, BinOp::Eq),
20 NotEq => (7, 8, BinOp::Ne),
21 Lt => (9, 10, BinOp::Lt),
22 Gt => (9, 10, BinOp::Gt),
23 LtEq => (9, 10, BinOp::Le),
24 GtEq => (9, 10, BinOp::Ge),
25 Plus => (11, 12, BinOp::Add),
26 Minus => (11, 12, BinOp::Sub),
27 Star => (13, 14, BinOp::Mul),
28 Slash => (13, 14, BinOp::Div),
29 Percent => (13, 14, BinOp::Rem),
30 _ => return None,
31 })
32}
33
34const MAX_PARSE_DEPTH: u32 = 256;
38
39pub struct Parser<'a> {
40 toks: Vec<Token<'a>>,
41 pos: usize,
42 diags: Vec<Diagnostic>,
43 depth: u32,
44}
45
46impl<'a> Parser<'a> {
47 pub fn new(toks: Vec<Token<'a>>) -> Self {
48 debug_assert!(matches!(toks.last().map(|t| &t.kind), Some(TokenKind::Eof)));
49 Parser {
50 toks,
51 pos: 0,
52 diags: Vec::new(),
53 depth: 0,
54 }
55 }
56
57 fn enter(&mut self) -> Result<(), Diagnostic> {
59 self.depth += 1;
60 if self.depth > MAX_PARSE_DEPTH {
61 self.depth -= 1;
62 return Err(self.err(
63 "E0208",
64 "nesting too deep",
65 "simplify or split this expression/block",
66 ));
67 }
68 Ok(())
69 }
70
71 fn leave(&mut self) {
72 self.depth -= 1;
73 }
74
75 fn peek(&self) -> &TokenKind<'a> {
78 &self.toks[self.pos].kind
79 }
80
81 fn peek_at(&self, off: usize) -> &TokenKind<'a> {
82 self.toks
83 .get(self.pos + off)
84 .map(|t| &t.kind)
85 .unwrap_or(&TokenKind::Eof)
86 }
87
88 fn span(&self) -> Span {
89 self.toks[self.pos].span
90 }
91
92 fn prev_end_span(&self) -> Span {
93 self.toks[self.pos.saturating_sub(1)].span
94 }
95
96 fn bump(&mut self) {
97 if !matches!(self.peek(), TokenKind::Eof) {
98 self.pos += 1;
99 }
100 }
101
102 fn eat(&mut self, kind: &TokenKind<'_>) -> bool {
103 if self.peek() == kind {
104 self.bump();
105 true
106 } else {
107 false
108 }
109 }
110
111 fn join(&self, from: Span) -> Span {
112 Span {
113 source: from.source,
114 start: from.start,
115 end: self.prev_end_span().end,
116 }
117 }
118
119 fn err(
120 &self,
121 code: &'static str,
122 msg: impl Into<String>,
123 label: impl Into<String>,
124 ) -> Diagnostic {
125 Diagnostic::error(code, msg, self.span(), label)
126 }
127
128 fn expect(&mut self, kind: &TokenKind<'_>, what: &str) -> Result<(), Diagnostic> {
129 if self.eat(kind) {
130 Ok(())
131 } else {
132 Err(self.err(
133 "E0200",
134 format!("expected {what}"),
135 format!("found {:?}", self.peek()),
136 ))
137 }
138 }
139
140 fn expect_ident(&mut self, what: &str) -> Result<(&'a str, Span), Diagnostic> {
141 if let TokenKind::Ident(s) = self.peek() {
142 let (s, sp) = (*s, self.span());
143 self.bump();
144 Ok((s, sp))
145 } else {
146 Err(self.err(
147 "E0200",
148 format!("expected {what}"),
149 format!("found {:?}", self.peek()),
150 ))
151 }
152 }
153
154 fn expect_key(&mut self) -> Result<(&'a str, Span), Diagnostic> {
156 match self.peek() {
157 TokenKind::Ident(s) | TokenKind::Str(s) => {
158 let (s, sp) = (*s, self.span());
159 self.bump();
160 Ok((s, sp))
161 }
162 _ => Err(self.err(
163 "E0200",
164 "expected property key",
165 format!("found {:?}", self.peek()),
166 )),
167 }
168 }
169
170 fn skip_newlines(&mut self) {
171 while matches!(self.peek(), TokenKind::Newline) {
172 self.bump();
173 }
174 }
175
176 fn eat_separator(&mut self) -> Result<(), Diagnostic> {
178 match self.peek() {
179 TokenKind::Newline => {
180 self.skip_newlines();
181 Ok(())
182 }
183 TokenKind::End | TokenKind::Eof => Ok(()),
184 _ => Err(self.err(
185 "E0205",
186 "expected end of statement",
187 "statements are separated by newlines",
188 )),
189 }
190 }
191
192 fn recover(&mut self) {
194 while !matches!(
195 self.peek(),
196 TokenKind::Newline | TokenKind::End | TokenKind::Eof
197 ) {
198 self.bump();
199 }
200 self.skip_newlines();
201 }
202
203 pub fn parse_expression(mut self) -> Result<Expr<'a>, Diagnostic> {
205 let e = self.parse_expr(0)?;
206 self.skip_newlines();
207 if !matches!(self.peek(), TokenKind::Eof) {
208 return Err(self.err(
209 "E0204",
210 "unexpected trailing tokens after expression",
211 "expected end of expression",
212 ));
213 }
214 Ok(e)
215 }
216
217 #[cfg(all(not(target_family = "wasm"), debug_assertions))]
235 pub fn parse_module(self) -> Result<Module<'a>, Vec<Diagnostic>> {
236 std::thread::scope(|s| {
237 std::thread::Builder::new()
238 .stack_size(64 * 1024 * 1024)
239 .spawn_scoped(s, move || self.parse_module_impl())
240 .expect("spawn parser thread")
241 .join()
242 .expect("parser thread panicked")
243 })
244 }
245
246 #[cfg(any(target_family = "wasm", not(debug_assertions)))]
248 pub fn parse_module(self) -> Result<Module<'a>, Vec<Diagnostic>> {
249 self.parse_module_impl()
250 }
251
252 fn parse_module_impl(mut self) -> Result<Module<'a>, Vec<Diagnostic>> {
253 let start = self.span();
254 let mut imports = Vec::new();
255 let mut stmts = Vec::new();
256 self.skip_newlines();
257 while matches!(self.peek(), TokenKind::Import) {
258 match self.parse_import() {
259 Ok(i) => {
260 imports.push(i);
261 if let Err(d) = self.eat_separator() {
262 self.diags.push(d);
263 self.recover();
264 }
265 }
266 Err(d) => {
267 self.diags.push(d);
268 self.recover();
269 }
270 }
271 }
272 while !matches!(self.peek(), TokenKind::Eof) {
273 let before = self.pos;
274 match self.parse_stmt().and_then(|s| {
275 self.eat_separator()?;
276 Ok(s)
277 }) {
278 Ok(s) => stmts.push(s),
279 Err(d) => {
280 self.diags.push(d);
281 self.recover();
282 if self.pos == before {
286 self.bump();
287 }
288 }
289 }
290 }
291 if self.diags.is_empty() {
292 Ok(Module {
293 imports,
294 stmts,
295 span: self.join(start),
296 })
297 } else {
298 Err(self.diags)
299 }
300 }
301
302 fn parse_import(&mut self) -> Result<Import<'a>, Diagnostic> {
303 let start = self.span();
304 self.bump(); let source = match self.peek() {
306 TokenKind::ImportPath { path, version } => {
307 let s = ImportSource::Registry { path, version };
308 self.bump();
309 s
310 }
311 TokenKind::Str(p) => {
312 let s = ImportSource::File(p);
313 self.bump();
314 s
315 }
316 _ => {
317 return Err(self.err(
318 "E0200",
319 "expected import path or \"file.aura\"",
320 "invalid import source",
321 ))
322 }
323 };
324 self.expect(&TokenKind::As, "`as` after import source")?;
325 let (alias, _) = self.expect_ident("import alias")?;
326 Ok(Import {
327 source,
328 alias,
329 span: self.join(start),
330 })
331 }
332
333 fn parse_stmt(&mut self) -> Result<Stmt<'a>, Diagnostic> {
336 self.enter()?;
337 let r = self.parse_stmt_impl();
338 self.leave();
339 r
340 }
341
342 fn parse_stmt_impl(&mut self) -> Result<Stmt<'a>, Diagnostic> {
343 match self.peek() {
344 TokenKind::Type => self.parse_type_decl(false),
345 TokenKind::Enum => self.parse_enum_decl(false),
346 TokenKind::Def => self.parse_func_decl(false),
347 TokenKind::Pub => {
349 self.bump();
350 match self.peek() {
351 TokenKind::Def => self.parse_func_decl(true),
352 TokenKind::Type => self.parse_type_decl(true),
353 TokenKind::Enum => self.parse_enum_decl(true),
354 _ => Err(self.err(
355 "E0206",
356 "`pub` is only allowed before `def`, `type` or `enum`",
357 "properties are exported by default; `=` bindings are always private",
358 )),
359 }
360 }
361 TokenKind::Domain => Ok(Stmt::Block(self.parse_block(BlockKind::Domain)?)),
362 TokenKind::Assert => self.parse_assert(),
363 TokenKind::Shadow => {
364 let start = self.span();
365 self.bump();
366 let (name, _) = self.expect_ident("variable name after `shadow`")?;
367 self.expect(&TokenKind::Assign, "`=` in shadow assignment")?;
368 let value = self.parse_expr(0)?;
369 Ok(Stmt::Assign {
370 name,
371 shadow: true,
372 value,
373 span: self.join(start),
374 })
375 }
376 TokenKind::Str(_) if matches!(self.peek_at(1), TokenKind::Colon) => {
378 let start = self.span();
379 let (key, _) = self.expect_key()?;
380 self.bump(); let value = self.parse_property_value()?;
382 Ok(Stmt::Property {
383 key,
384 value,
385 span: self.join(start),
386 })
387 }
388 TokenKind::Ident(_) => {
389 let start = self.span();
390 match (self.peek_at(1), self.peek_at(2)) {
391 (TokenKind::Assign, _) => {
392 let (name, _) = self.expect_ident("name")?;
393 self.bump(); let value = self.parse_expr(0)?;
395 Ok(Stmt::Assign {
396 name,
397 shadow: false,
398 value,
399 span: self.join(start),
400 })
401 }
402 (TokenKind::Colon, _) => {
403 let (key, _) = self.expect_ident("key")?;
404 self.bump(); let value = self.parse_property_value()?;
406 Ok(Stmt::Property {
407 key,
408 value,
409 span: self.join(start),
410 })
411 }
412 (TokenKind::Ident(_), TokenKind::Colon) => {
414 let mut d = self.err(
415 "E0201",
416 "inline blocks were removed in Aura v1.2",
417 "this looks like an inline block",
418 );
419 if let TokenKind::Ident(name) = self.peek() {
420 d.help = Some(format!(
421 "write it as an object block:\n{name}:\n key: value\nend"
422 ));
423 }
424 Err(d)
425 }
426 _ => Ok(Stmt::Expr(self.parse_expr(0)?)),
427 }
428 }
429 _ => Ok(Stmt::Expr(self.parse_expr(0)?)),
430 }
431 }
432
433 fn parse_property_value(&mut self) -> Result<Expr<'a>, Diagnostic> {
435 if matches!(self.peek(), TokenKind::Newline) {
436 self.bump();
437 Ok(Expr::ObjectLiteral(self.parse_object_body()?))
438 } else {
439 self.parse_expr(0)
440 }
441 }
442
443 fn parse_stmt_body(&mut self, what: &'static str) -> Result<Vec<Stmt<'a>>, Diagnostic> {
448 self.enter()?;
449 let r = self.parse_stmt_body_impl(what);
450 self.leave();
451 r
452 }
453
454 fn parse_stmt_body_impl(&mut self, what: &'static str) -> Result<Vec<Stmt<'a>>, Diagnostic> {
455 let mut body = Vec::new();
456 loop {
457 self.skip_newlines();
458 if self.eat(&TokenKind::End) {
459 return Ok(body);
460 }
461 if matches!(self.peek(), TokenKind::Eof) {
462 return Err(self.err(
463 "E0203",
464 "missing `end`",
465 match what {
466 "block" => "block is not closed",
467 "function" => "function body is not closed",
468 _ => "lambda body is not closed",
469 },
470 ));
471 }
472 let stmt = self.parse_stmt()?;
473 self.eat_separator()?;
474 body.push(stmt);
475 }
476 }
477
478 fn parse_object_body(&mut self) -> Result<ObjectBody<'a>, Diagnostic> {
479 self.enter()?;
480 let r = self.parse_object_body_impl();
481 self.leave();
482 r
483 }
484
485 fn parse_object_body_impl(&mut self) -> Result<ObjectBody<'a>, Diagnostic> {
486 let mut props = Vec::new();
487 loop {
488 self.skip_newlines();
489 if self.eat(&TokenKind::End) {
490 return Ok(ObjectBody { props });
491 }
492 if matches!(self.peek(), TokenKind::Eof) {
493 return Err(self.err("E0203", "missing `end`", "object block is not closed"));
494 }
495 let (key, kspan) = self.expect_key()?;
496 self.expect(&TokenKind::Colon, "`:` after property key")?;
497 let value = self.parse_property_value()?;
498 props.push((key, value, kspan));
499 if !matches!(self.peek(), TokenKind::Newline | TokenKind::End) {
500 return Err(self.err(
501 "E0205",
502 "expected end of property",
503 "properties are separated by newlines",
504 ));
505 }
506 }
507 }
508
509 fn parse_enum_decl(&mut self, public: bool) -> Result<Stmt<'a>, Diagnostic> {
511 let start = self.span();
512 self.bump(); let (name, _) = self.expect_ident("enum name")?;
514 self.expect(&TokenKind::Newline, "newline after enum name")?;
515 let mut members: Vec<&'a str> = Vec::new();
516 loop {
517 self.skip_newlines();
518 if self.eat(&TokenKind::End) {
519 break;
520 }
521 let TokenKind::Str(m) = self.peek() else {
522 return Err(self.err(
523 "E0211",
524 "expected an enum member",
525 "members are quoted strings, one per line",
526 ));
527 };
528 if members.contains(m) {
529 return Err(self.err(
530 "E0212",
531 format!("duplicate enum member '{m}'"),
532 "each member must be listed once",
533 ));
534 }
535 members.push(m);
536 self.bump();
537 if !matches!(self.peek(), TokenKind::Newline | TokenKind::End) {
538 return Err(self.err(
539 "E0205",
540 "expected end of enum member",
541 "members are separated by newlines",
542 ));
543 }
544 }
545 if members.is_empty() {
546 return Err(self.err(
547 "E0213",
548 "enum has no members",
549 "an empty enum could never be satisfied",
550 ));
551 }
552 Ok(Stmt::EnumDecl(EnumDeclaration {
553 name,
554 members,
555 public,
556 span: self.join(start),
557 }))
558 }
559
560 fn parse_type_decl(&mut self, public: bool) -> Result<Stmt<'a>, Diagnostic> {
561 let start = self.span();
562 self.bump(); let (name, _) = self.expect_ident("schema name")?;
564 self.expect(&TokenKind::Newline, "newline after schema name")?;
565 let mut fields = Vec::new();
566 loop {
567 self.skip_newlines();
568 if self.eat(&TokenKind::End) {
569 break;
570 }
571 let (field, _) = self.expect_ident("field name")?;
572 self.expect(&TokenKind::Colon, "`:` after field name")?;
573 let (ty, _) = self.expect_ident("field type")?;
574 let ty = match ty {
575 "String" => TypeName::String,
576 "Int" => TypeName::Int,
577 "Float" => TypeName::Float,
578 "Bool" => TypeName::Bool,
579 "List" => TypeName::List,
580 "Object" => TypeName::Object,
581 other => TypeName::Custom(other),
582 };
583 let default = if self.eat(&TokenKind::Assign) {
585 Some(self.parse_expr(0)?)
586 } else {
587 None
588 };
589 fields.push(SchemaField {
590 name: field,
591 ty,
592 default,
593 });
594 }
595 Ok(Stmt::TypeDecl(SchemaDeclaration {
596 name,
597 fields,
598 public,
599 span: self.join(start),
600 }))
601 }
602
603 fn parse_func_decl(&mut self, public: bool) -> Result<Stmt<'a>, Diagnostic> {
604 let start = self.span();
605 self.bump(); let (name, _) = self.expect_ident("function name")?;
607 self.expect(&TokenKind::LParen, "`(` after function name")?;
608 let params = self.parse_param_list()?;
609 self.expect(&TokenKind::Newline, "newline after function signature")?;
610 let body = self.parse_stmt_body("function")?;
611 Ok(Stmt::FuncDecl {
612 name,
613 params,
614 body,
615 public,
616 span: self.join(start),
617 })
618 }
619
620 fn parse_param_list(&mut self) -> Result<Vec<&'a str>, Diagnostic> {
621 let mut params = Vec::new();
622 if !self.eat(&TokenKind::RParen) {
623 loop {
624 let (p, _) = self.expect_ident("parameter name")?;
625 params.push(p);
626 if !self.eat(&TokenKind::Comma) {
627 break;
628 }
629 }
630 self.expect(&TokenKind::RParen, "`)` after parameters")?;
631 }
632 Ok(params)
633 }
634
635 fn parse_assert(&mut self) -> Result<Stmt<'a>, Diagnostic> {
636 let start = self.span();
637 self.bump(); let cond = self.parse_expr(0)?;
639 let message = if self.eat(&TokenKind::Comma) {
640 Some(self.parse_expr(0)?)
641 } else {
642 None
643 };
644 Ok(Stmt::Assert {
645 cond,
646 message,
647 span: self.join(start),
648 })
649 }
650
651 fn parse_block(&mut self, kind: BlockKind) -> Result<BlockDeclaration<'a>, Diagnostic> {
652 let start = self.span();
653 self.bump(); let label = self.parse_expr(0)?;
655 self.expect(&TokenKind::Newline, "newline after block label")?;
656 let body = self.parse_stmt_body("block")?;
657 Ok(BlockDeclaration {
658 kind,
659 label,
660 body,
661 span: self.join(start),
662 })
663 }
664
665 fn parse_expr(&mut self, min_bp: u8) -> Result<Expr<'a>, Diagnostic> {
668 self.enter()?;
669 let r = self.parse_expr_impl(min_bp);
670 self.leave();
671 r
672 }
673
674 fn parse_expr_impl(&mut self, min_bp: u8) -> Result<Expr<'a>, Diagnostic> {
675 let start = self.span();
676 let mut lhs = self.parse_prefix()?;
677 loop {
678 match self.peek() {
679 TokenKind::Dot => {
680 lhs = self.parse_postfix_dot(lhs, start)?;
681 }
682 TokenKind::LParen => {
683 let args = self.parse_args()?;
684 lhs = Expr::Call {
685 callee: Box::new(lhs),
686 args,
687 span: self.join(start),
688 };
689 }
690 TokenKind::LBracket => {
692 self.bump();
693 if matches!(self.peek(), TokenKind::Str(_))
695 && matches!(self.peek_at(1), TokenKind::RBracket)
696 {
697 let mut d = self.err(
698 "E0318",
699 "bracket access on objects is not supported",
700 "string key in brackets",
701 );
702 if let TokenKind::Str(k) = self.peek() {
703 d.help = Some(format!("use dot access instead: `.\"{k}\"`"));
704 }
705 return Err(d);
706 }
707 let key = self.parse_expr(0)?;
708 self.expect(&TokenKind::RBracket, "closing `]` in index")?;
709 lhs = Expr::Index {
710 recv: Box::new(lhs),
711 key: Box::new(key),
712 bracket: true,
713 span: self.join(start),
714 };
715 }
716 TokenKind::Question if TERNARY_LBP >= min_bp => {
717 self.bump();
718 let then = self.parse_expr(0)?;
719 self.expect(&TokenKind::Colon, "`:` in ternary expression")?;
720 let otherwise = self.parse_expr(TERNARY_LBP - 1)?; lhs = Expr::Ternary {
722 cond: Box::new(lhs),
723 then: Box::new(then),
724 otherwise: Box::new(otherwise),
725 span: self.join(start),
726 };
727 }
728 k => {
729 let Some((lbp, rbp, op)) = infix_bp(k) else {
730 break;
731 };
732 if lbp < min_bp {
733 break;
734 }
735 self.bump();
736 let rhs = self.parse_expr(rbp)?;
737 lhs = Expr::Binary {
738 op,
739 lhs: Box::new(lhs),
740 rhs: Box::new(rhs),
741 span: self.join(start),
742 };
743 }
744 }
745 }
746 Ok(lhs)
747 }
748
749 fn parse_prefix(&mut self) -> Result<Expr<'a>, Diagnostic> {
750 let sp = self.span();
751 match self.peek() {
752 TokenKind::Int(n) => {
753 let n = *n;
754 self.bump();
755 Ok(Expr::Literal(LitValue::Int(n), sp))
756 }
757 TokenKind::Float(n) => {
758 let n = *n;
759 self.bump();
760 Ok(Expr::Literal(LitValue::Float(n), sp))
761 }
762 TokenKind::Str(s) => {
763 let s = *s;
764 self.bump();
765 Ok(Expr::Literal(LitValue::Str(s), sp))
766 }
767 TokenKind::InterpStr(parts) => {
768 let parts = parts.clone();
769 self.bump();
770 Ok(Expr::Literal(LitValue::InterpStr(parts), sp))
771 }
772 TokenKind::True => {
773 self.bump();
774 Ok(Expr::Literal(LitValue::Bool(true), sp))
775 }
776 TokenKind::False => {
777 self.bump();
778 Ok(Expr::Literal(LitValue::Bool(false), sp))
779 }
780 TokenKind::Null => {
781 self.bump();
782 Ok(Expr::Literal(LitValue::Null, sp))
783 }
784 TokenKind::Ident(s) => {
785 let s = *s;
786 self.bump();
787 Ok(Expr::Variable(s, sp))
788 }
789 TokenKind::Minus => {
790 self.bump();
791 let rhs = self.parse_expr(UNARY_RBP)?;
792 Ok(Expr::Unary {
793 op: UnaryOp::Neg,
794 rhs: Box::new(rhs),
795 span: self.join(sp),
796 })
797 }
798 TokenKind::Not => {
799 self.bump();
800 let rhs = self.parse_expr(UNARY_RBP)?;
801 Ok(Expr::Unary {
802 op: UnaryOp::Not,
803 rhs: Box::new(rhs),
804 span: self.join(sp),
805 })
806 }
807 TokenKind::LParen => {
808 if self.lambda_ahead() {
809 self.parse_lambda()
810 } else {
811 self.bump();
812 let e = self.parse_expr(0)?;
813 self.expect(&TokenKind::RParen, "closing `)`")?;
814 Ok(e)
815 }
816 }
817 TokenKind::LBracket => self.parse_list(),
818 TokenKind::New => {
819 self.bump();
820 let (first, _) = self.expect_ident("schema name after `new`")?;
821 let (schema, schema_alias) = if self.eat(&TokenKind::Dot) {
823 let (name, _) = self.expect_ident("schema name after module alias")?;
824 (name, Some(first))
825 } else {
826 (first, None)
827 };
828 self.expect(&TokenKind::Newline, "newline after `new SchemaName`")?;
829 let body = self.parse_object_body()?;
830 Ok(Expr::SchemaInstance {
831 schema,
832 schema_alias,
833 body,
834 span: self.join(sp),
835 })
836 }
837 TokenKind::Cond => self.parse_cond(),
838 _ => Err(self.err(
839 "E0204",
840 "expected expression",
841 format!("found {:?}", self.peek()),
842 )),
843 }
844 }
845
846 fn lambda_ahead(&self) -> bool {
848 let mut i = self.pos + 1;
849 loop {
850 match self.toks.get(i).map(|t| &t.kind) {
851 Some(TokenKind::RParen) => {
852 return matches!(
853 self.toks.get(i + 1).map(|t| &t.kind),
854 Some(TokenKind::Arrow)
855 )
856 }
857 Some(TokenKind::Ident(_)) => match self.toks.get(i + 1).map(|t| &t.kind) {
858 Some(TokenKind::Comma) => i += 2,
859 Some(TokenKind::RParen) => {
860 return matches!(
861 self.toks.get(i + 2).map(|t| &t.kind),
862 Some(TokenKind::Arrow)
863 )
864 }
865 _ => return false,
866 },
867 _ => return false,
868 }
869 }
870 }
871
872 fn parse_lambda(&mut self) -> Result<Expr<'a>, Diagnostic> {
874 let start = self.span();
875 self.bump(); let params = self.parse_param_list()?;
877 self.expect(&TokenKind::Arrow, "`->` in lambda")?;
878 let stmt_body = matches!(
880 (self.peek(), self.peek_at(1)),
881 (TokenKind::Ident(_) | TokenKind::Str(_), TokenKind::Colon)
882 | (TokenKind::Ident(_), TokenKind::Assign)
883 | (
884 TokenKind::Shadow
885 | TokenKind::Assert
886 | TokenKind::Def
887 | TokenKind::Type
888 | TokenKind::Pub
889 | TokenKind::Domain,
890 _
891 )
892 );
893 let body = if stmt_body {
894 LambdaBody::Block(self.parse_stmt_body("lambda")?)
895 } else {
896 let e = self.parse_expr(0)?;
897 self.skip_newlines();
898 self.expect(&TokenKind::End, "`end` closing lambda body")?;
899 LambdaBody::Expr(Box::new(e))
900 };
901 Ok(Expr::Lambda {
902 params,
903 body,
904 span: self.join(start),
905 })
906 }
907
908 fn parse_list(&mut self) -> Result<Expr<'a>, Diagnostic> {
909 let start = self.span();
910 self.bump(); let mut items = Vec::new();
912 loop {
913 while matches!(self.peek(), TokenKind::Newline | TokenKind::Comma) {
914 self.bump();
915 }
916 if self.eat(&TokenKind::RBracket) {
917 return Ok(Expr::ListLiteral(items, self.join(start)));
918 }
919 if matches!(self.peek(), TokenKind::Eof) {
920 return Err(self.err("E0203", "missing `]`", "list is not closed"));
921 }
922 items.push(self.parse_expr(0)?);
923 if !matches!(
924 self.peek(),
925 TokenKind::Newline | TokenKind::Comma | TokenKind::RBracket
926 ) {
927 return Err(self.err(
928 "E0205",
929 "expected list separator",
930 "elements are separated by newlines or commas",
931 ));
932 }
933 }
934 }
935
936 fn parse_cond(&mut self) -> Result<Expr<'a>, Diagnostic> {
938 let start = self.span();
939 self.bump(); self.expect(&TokenKind::Newline, "newline after `cond`")?;
941 let mut arms = Vec::new();
942 loop {
943 self.skip_newlines();
944 if matches!(self.peek(), TokenKind::Else) {
945 break;
946 }
947 if matches!(self.peek(), TokenKind::End | TokenKind::Eof) {
948 return Err(self.err(
949 "E0207",
950 "`cond` requires an `else` arm",
951 "add `else -> ...` before `end`",
952 ));
953 }
954 let condition = self.parse_expr(0)?;
955 self.expect(&TokenKind::Arrow, "`->` after a cond condition")?;
956 let value = self.parse_expr(0)?;
957 arms.push((condition, value));
958 self.eat_separator()?;
959 }
960 self.bump(); self.expect(&TokenKind::Arrow, "`->` after `else`")?;
962 let otherwise = self.parse_expr(0)?;
963 self.skip_newlines();
964 self.expect(&TokenKind::End, "`end` to close `cond`")?;
965 Ok(Expr::Cond {
966 arms,
967 otherwise: Box::new(otherwise),
968 span: self.join(start),
969 })
970 }
971
972 fn parse_args(&mut self) -> Result<Vec<Expr<'a>>, Diagnostic> {
973 self.bump(); let mut args = Vec::new();
975 if !self.eat(&TokenKind::RParen) {
976 loop {
977 args.push(self.parse_expr(0)?);
978 if !self.eat(&TokenKind::Comma) {
979 break;
980 }
981 }
982 self.expect(&TokenKind::RParen, "`)` after arguments")?;
983 }
984 Ok(args)
985 }
986
987 fn parse_postfix_dot(&mut self, recv: Expr<'a>, start: Span) -> Result<Expr<'a>, Diagnostic> {
990 self.bump(); match self.peek() {
993 TokenKind::Str(key) => {
994 let key = *key;
995 self.bump();
996 return Ok(Expr::FieldAccess {
997 recv: Box::new(recv),
998 field: key,
999 span: self.join(start),
1000 });
1001 }
1002 TokenKind::InterpStr(parts) => {
1003 let parts = parts.clone();
1004 let ksp = self.span();
1005 self.bump();
1006 let key = Expr::Literal(LitValue::InterpStr(parts), ksp);
1007 return Ok(Expr::Index {
1008 recv: Box::new(recv),
1009 key: Box::new(key),
1010 bracket: false,
1011 span: self.join(start),
1012 });
1013 }
1014 _ => {}
1015 }
1016 let (name, _) = self.expect_ident("field or method name after `.`")?;
1017 if matches!(self.peek(), TokenKind::LParen) {
1018 let (args, lambda) = if self.lambda_ahead() {
1019 (Vec::new(), Some(Box::new(self.parse_lambda()?)))
1020 } else {
1021 let args = self.parse_args()?;
1022 let lambda = if matches!(self.peek(), TokenKind::LParen) && self.lambda_ahead() {
1023 Some(Box::new(self.parse_lambda()?))
1024 } else {
1025 None
1026 };
1027 (args, lambda)
1028 };
1029 Ok(Expr::MethodCall {
1030 recv: Box::new(recv),
1031 method: name,
1032 args,
1033 lambda,
1034 span: self.join(start),
1035 })
1036 } else {
1037 Ok(Expr::FieldAccess {
1038 recv: Box::new(recv),
1039 field: name,
1040 span: self.join(start),
1041 })
1042 }
1043 }
1044}
1045
1046#[cfg(test)]
1047mod tests {
1048 use super::*;
1049 use crate::lexer::Lexer;
1050
1051 fn module(src: &str) -> Module<'_> {
1052 let toks = Lexer::new(src, 0).tokenize().expect("lex ok");
1053 Parser::new(toks).parse_module().expect("parse ok")
1054 }
1055
1056 fn expr(src: &str) -> Expr<'_> {
1057 let m = module(src);
1058 match m.stmts.into_iter().next().expect("one stmt") {
1059 Stmt::Expr(e) => e,
1060 other => panic!("expected expr stmt, got {other:?}"),
1061 }
1062 }
1063
1064 fn first_err(src: &str) -> &'static str {
1065 let toks = Lexer::new(src, 0).tokenize().expect("lex ok");
1066 Parser::new(toks)
1067 .parse_module()
1068 .expect_err("parse must fail")[0]
1069 .code
1070 }
1071
1072 #[test]
1073 fn deep_nesting_is_e0208_not_a_crash() {
1074 let parens = format!("x: {}1{}", "(".repeat(2000), ")".repeat(2000));
1079 assert_eq!(first_err(&parens), "E0208");
1080 let brackets = format!("x: {}", "[".repeat(2000));
1081 assert_eq!(first_err(&brackets), "E0208");
1082 let blocks = format!(
1083 "{}\n{}",
1084 (0..2000)
1085 .map(|i| format!("domain \"d{i}\""))
1086 .collect::<Vec<_>>()
1087 .join("\n"),
1088 "end\n".repeat(2000)
1089 );
1090 assert_eq!(first_err(&blocks), "E0208");
1091 let ok = format!("x: {}1{}", "(".repeat(10), ")".repeat(10));
1093 module(&ok);
1094 }
1095
1096 #[test]
1097 fn enum_parses_d18() {
1098 let m = module(
1099 "enum Tier
1100 \"frontend\"
1101 \"backend\"
1102end
1103",
1104 );
1105 let Stmt::EnumDecl(e) = &m.stmts[0] else {
1106 panic!("expected an enum declaration")
1107 };
1108 assert_eq!(e.name, "Tier");
1109 assert_eq!(e.members, vec!["frontend", "backend"]);
1110 assert!(!e.public);
1111 }
1112
1113 #[test]
1114 fn enum_errors_are_specific() {
1115 assert_eq!(
1116 first_err(
1117 "enum T
1118 bare
1119end
1120"
1121 ),
1122 "E0211"
1123 ); assert_eq!(
1125 first_err(
1126 "enum T
1127 \"a\"
1128 \"a\"
1129end
1130"
1131 ),
1132 "E0212"
1133 ); assert_eq!(
1135 first_err(
1136 "enum T
1137end
1138"
1139 ),
1140 "E0213"
1141 ); }
1143
1144 #[test]
1145 fn cond_parses_d14() {
1146 let m = module("t: cond\n a == 1 -> \"x\"\n else -> \"y\"\nend");
1147 let Stmt::Property {
1148 value: Expr::Cond { arms, .. },
1149 ..
1150 } = &m.stmts[0]
1151 else {
1152 panic!("expected cond property, got {:?}", m.stmts[0])
1153 };
1154 assert_eq!(arms.len(), 1);
1155 assert_eq!(first_err("x: cond\n true -> 1\nend"), "E0207");
1157 }
1158
1159 #[test]
1160 fn mul_binds_tighter_than_add() {
1161 let Expr::Binary {
1162 op: BinOp::Add,
1163 rhs,
1164 ..
1165 } = expr("1 + 2 * 3")
1166 else {
1167 panic!()
1168 };
1169 assert!(matches!(*rhs, Expr::Binary { op: BinOp::Mul, .. }));
1170 }
1171
1172 #[test]
1173 fn comparison_vs_logic_precedence() {
1174 let Expr::Binary {
1176 op: BinOp::And,
1177 lhs,
1178 rhs,
1179 ..
1180 } = expr("a == b && c < d")
1181 else {
1182 panic!()
1183 };
1184 assert!(matches!(*lhs, Expr::Binary { op: BinOp::Eq, .. }));
1185 assert!(matches!(*rhs, Expr::Binary { op: BinOp::Lt, .. }));
1186 }
1187
1188 #[test]
1189 fn ternary_is_right_associative() {
1190 let Expr::Ternary { otherwise, .. } = expr("a ? b : c ? d : e") else {
1192 panic!()
1193 };
1194 assert!(matches!(*otherwise, Expr::Ternary { .. }));
1195 }
1196
1197 #[test]
1198 fn method_chains_are_left_associative() {
1199 let Expr::MethodCall {
1201 method: "uniq",
1202 recv,
1203 ..
1204 } = expr("xs.compact().uniq()")
1205 else {
1206 panic!()
1207 };
1208 assert!(matches!(
1209 *recv,
1210 Expr::MethodCall {
1211 method: "compact",
1212 ..
1213 }
1214 ));
1215 }
1216
1217 #[test]
1218 fn field_access_chain() {
1219 let Expr::FieldAccess {
1220 field: "version",
1221 recv,
1222 ..
1223 } = expr("cargo_data.package.version")
1224 else {
1225 panic!()
1226 };
1227 assert!(matches!(
1228 *recv,
1229 Expr::FieldAccess {
1230 field: "package",
1231 ..
1232 }
1233 ));
1234 }
1235
1236 #[test]
1237 fn trailing_lambda_on_map() {
1238 let src = "xs.map (name, index) -> name end";
1239 let Expr::MethodCall {
1240 method: "map",
1241 args,
1242 lambda: Some(l),
1243 ..
1244 } = expr(src)
1245 else {
1246 panic!()
1247 };
1248 assert!(args.is_empty());
1249 let Expr::Lambda { params, .. } = *l else {
1250 panic!()
1251 };
1252 assert_eq!(params, vec!["name", "index"]);
1253 }
1254
1255 #[test]
1256 fn list_with_unary_minus_element_d2() {
1257 let Expr::ListLiteral(items, _) = expr("[a\n-b]") else {
1258 panic!()
1259 };
1260 assert_eq!(items.len(), 2);
1261 assert!(matches!(
1262 items[1],
1263 Expr::Unary {
1264 op: UnaryOp::Neg,
1265 ..
1266 }
1267 ));
1268 }
1269
1270 #[test]
1271 fn inline_block_is_e0201() {
1272 assert_eq!(first_err("metrics port: 9090 path: \"/metrics\""), "E0201");
1273 }
1274
1275 #[test]
1276 fn shadow_assignment_d7() {
1277 let m = module("shadow x = 1");
1278 assert!(matches!(
1279 m.stmts[0],
1280 Stmt::Assign {
1281 name: "x",
1282 shadow: true,
1283 ..
1284 }
1285 ));
1286 }
1287
1288 #[test]
1289 fn new_schema_instance_d4() {
1290 let m = module("m = new ServiceMeta\n name: \"auth\"\n port: 8001\nend");
1291 let Stmt::Assign {
1292 value:
1293 Expr::SchemaInstance {
1294 schema: "ServiceMeta",
1295 body,
1296 ..
1297 },
1298 ..
1299 } = &m.stmts[0]
1300 else {
1301 panic!()
1302 };
1303 assert_eq!(body.props.len(), 2);
1304 }
1305
1306 #[test]
1307 fn assert_with_message_d5() {
1308 let m = module("assert xs.len() >= 1, \"too few\"");
1309 assert!(matches!(
1310 m.stmts[0],
1311 Stmt::Assert {
1312 message: Some(_),
1313 ..
1314 }
1315 ));
1316 }
1317
1318 #[test]
1319 fn nested_object_blocks() {
1320 let m = module("domain \"d\"\n security:\n tls: true\n certs:\n path: \"/x\"\n end\n end\nend");
1321 let Stmt::Block(b) = &m.stmts[0] else {
1322 panic!()
1323 };
1324 let Stmt::Property {
1325 key: "security",
1326 value: Expr::ObjectLiteral(body),
1327 ..
1328 } = &b.body[0]
1329 else {
1330 panic!()
1331 };
1332 assert!(matches!(
1333 body.props[1],
1334 ("certs", Expr::ObjectLiteral(_), _)
1335 ));
1336 }
1337
1338 #[test]
1339 fn dot_string_field_access_d11() {
1340 let Expr::FieldAccess {
1342 field: "port",
1343 recv,
1344 ..
1345 } = expr("a.\"eu west\".port")
1346 else {
1347 panic!()
1348 };
1349 assert!(matches!(
1350 *recv,
1351 Expr::FieldAccess {
1352 field: "eu west",
1353 ..
1354 }
1355 ));
1356 }
1357
1358 #[test]
1359 fn dynamic_key_desugars_to_index_d11() {
1360 let Expr::Index {
1361 bracket: false,
1362 key,
1363 ..
1364 } = expr("a.\"#{r}\"")
1365 else {
1366 panic!()
1367 };
1368 assert!(matches!(*key, Expr::Literal(LitValue::InterpStr(_), _)));
1369 }
1370
1371 #[test]
1372 fn list_indexing_d11() {
1373 let Expr::Index {
1375 bracket: true,
1376 recv,
1377 ..
1378 } = expr("xs[0][1]")
1379 else {
1380 panic!()
1381 };
1382 assert!(matches!(*recv, Expr::Index { bracket: true, .. }));
1383 }
1384
1385 #[test]
1386 fn bracket_string_key_is_e0318() {
1387 assert_eq!(first_err("x = a[\"key\"]"), "E0318");
1388 }
1389
1390 #[test]
1391 fn string_property_keys_d11() {
1392 let m = module("domain \"d\"\n \"app.kubernetes.io/name\": \"auth\"\nend");
1393 let Stmt::Block(b) = &m.stmts[0] else {
1394 panic!()
1395 };
1396 assert!(matches!(
1397 b.body[0],
1398 Stmt::Property {
1399 key: "app.kubernetes.io/name",
1400 ..
1401 }
1402 ));
1403 }
1404
1405 #[test]
1406 fn pub_def_and_type_d12() {
1407 let m = module("pub def f(x)\n a: x\nend\npub type T\n a: Int\nend\ndef g()\n b: 1\nend\nassert g().b == 1 && f == f && T == T");
1408 assert!(matches!(m.stmts[0], Stmt::FuncDecl { public: true, .. }));
1409 assert!(matches!(&m.stmts[1], Stmt::TypeDecl(s) if s.public));
1410 assert!(matches!(m.stmts[2], Stmt::FuncDecl { public: false, .. }));
1411 assert_eq!(first_err("pub x = 1"), "E0206");
1413 }
1414
1415 #[test]
1416 fn new_with_module_alias_d12() {
1417 let m = module("m: new pkg.Meta\n a: 1\nend");
1418 let Stmt::Property {
1419 value:
1420 Expr::SchemaInstance {
1421 schema: "Meta",
1422 schema_alias: Some("pkg"),
1423 ..
1424 },
1425 ..
1426 } = &m.stmts[0]
1427 else {
1428 panic!()
1429 };
1430 }
1431
1432 #[test]
1433 fn missing_end_is_e0203() {
1434 assert_eq!(first_err("domain \"d\"\n x = 1\n"), "E0203");
1435 }
1436}