1use tatara_lisp::{Atom, Sexp, Spanned, SpannedForm};
46
47use crate::lex::{lex, Span, Token, TokenKind};
48use crate::spelling::{Note, Spelling};
49
50#[derive(Clone, Debug, PartialEq)]
65pub struct ParseError {
66 pub message: String,
67 pub span: Span,
68 pub line: usize,
70 pub col: usize,
72}
73
74impl ParseError {
75 fn new(message: impl Into<String>, span: Span) -> Self {
79 Self {
80 message: message.into(),
81 span,
82 line: 0,
83 col: 0,
84 }
85 }
86
87 fn located(mut self, src: &str) -> Self {
89 let (line, col) = Span::line_col(src, self.span.start.min(src.len()));
90 self.line = line;
91 self.col = col;
92 self
93 }
94}
95
96impl std::fmt::Display for ParseError {
97 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98 write!(f, "{}:{}: {}", self.line, self.col, self.message)
99 }
100}
101
102impl std::error::Error for ParseError {}
103
104impl From<crate::lex::LexError> for ParseError {
105 fn from(e: crate::lex::LexError) -> Self {
106 Self::new(e.message, e.span)
107 }
108}
109
110thread_local! {
111 static PARSES: std::cell::Cell<u64> = const { std::cell::Cell::new(0) };
112}
113
114#[must_use]
118pub fn parses_on_this_thread() -> u64 {
119 PARSES.with(std::cell::Cell::get)
120}
121
122pub fn parse_program(src: &str) -> Result<Vec<Sexp>, ParseError> {
124 parse_program_with_depth(src, MAX_EXPR_DEPTH)
125}
126
127pub fn parse_program_tree(src: &str) -> Result<Vec<Spanned>, ParseError> {
135 parse_program_tree_with_depth(src, MAX_EXPR_DEPTH)
136}
137
138pub fn parse_program_tree_with_depth(
140 src: &str,
141 max_depth: usize,
142) -> Result<Vec<Spanned>, ParseError> {
143 PARSES.with(|n| n.set(n.get() + 1));
144 let mut p = Parser::over(
145 src,
146 lex(src).map_err(|e| ParseError::from(e).located(src))?,
147 max_depth,
148 );
149 p.program().map_err(|e| e.located(src))
150}
151
152pub fn spelling_notes(src: &str) -> Result<Vec<Note>, ParseError> {
153 let mut p = Parser::over(
154 src,
155 lex(src).map_err(|e| ParseError::from(e).located(src))?,
156 MAX_EXPR_DEPTH,
157 );
158 p.program().map_err(|e| e.located(src))?;
159 Ok(p.notes)
160}
161
162pub fn parse_program_in(src: &str, pack: &crate::yakugo::Yakugo) -> Result<Vec<Sexp>, ParseError> {
175 Ok(parse_program_tree_in(src, pack)?
176 .iter()
177 .map(Spanned::to_sexp)
178 .collect())
179}
180
181pub fn parse_program_tree_in(
197 src: &str,
198 pack: &crate::yakugo::Yakugo,
199) -> Result<Vec<Spanned>, ParseError> {
200 let toks =
201 crate::yakugo::canonical_tokens(src, pack).map_err(|e| ParseError::from(e).located(src))?;
202 PARSES.with(|n| n.set(n.get() + 1));
203 let mut p = Parser::over(src, toks, MAX_EXPR_DEPTH);
204 p.program().map_err(|e| e.located(src))
205}
206
207pub fn parse_program_with_depth(src: &str, max_depth: usize) -> Result<Vec<Sexp>, ParseError> {
216 Ok(parse_program_tree_with_depth(src, max_depth)?
217 .iter()
218 .map(Spanned::to_sexp)
219 .collect())
220}
221
222pub fn parse_program_spanned(src: &str) -> Result<Vec<(Sexp, Span)>, ParseError> {
235 parse_program_spanned_with_depth(src, MAX_EXPR_DEPTH)
236}
237
238pub fn parse_program_spanned_with_depth(
240 src: &str,
241 max_depth: usize,
242) -> Result<Vec<(Sexp, Span)>, ParseError> {
243 Ok(parse_program_tree_with_depth(src, max_depth)?
244 .iter()
245 .map(|f| (f.to_sexp(), f.span))
246 .collect())
247}
248
249pub fn comments(src: &str) -> Vec<Comment> {
257 lex(src)
258 .map(|toks| {
259 toks.iter()
260 .filter_map(|t| match &t.kind {
261 TokenKind::Comment(text) => Some(Comment {
262 text: text.clone(),
263 span: t.span,
264 own_line: line_before_is_blank(src, t.span.start),
265 }),
266 _ => None,
267 })
268 .collect()
269 })
270 .unwrap_or_default()
271}
272
273#[derive(Clone, Debug, PartialEq, Eq)]
275pub struct Comment {
276 pub text: String,
278 pub span: Span,
279 pub own_line: bool,
281}
282
283fn line_before_is_blank(src: &str, start: usize) -> bool {
284 src[..start.min(src.len())]
285 .rsplit('\n')
286 .next()
287 .is_some_and(|prefix| prefix.trim().is_empty())
288}
289
290pub fn parse_expr(src: &str) -> Result<Sexp, ParseError> {
292 let forms = parse_program(src)?;
293 match forms.len() {
294 1 => Ok(forms.into_iter().next().expect("checked len")),
295 n => Err(ParseError::new(
296 format!("expected exactly one expression, found {n}"),
297 Span::new(0, src.len()),
298 )
299 .located(src)),
300 }
301}
302
303#[derive(Clone, Copy, Debug)]
313pub struct Infix {
314 pub op: &'static str,
316 pub power: (u8, u8),
318 pub callee: &'static str,
320 pub doc: &'static str,
324}
325
326pub const INFIX: &[Infix] = &[
330 Infix {
331 op: "or",
332 power: (1, 2),
333 callee: "or",
334 doc: "Logical or: the left value if truthy, otherwise the right; the right is evaluated only when needed. `||` is read as `or` and written `or` (B0025).",
335 },
336 Infix {
337 op: "and",
338 power: (3, 4),
339 callee: "and",
340 doc: "Logical and: the right value if the left is truthy, otherwise the left; short-circuits. `&&` is read as `and` and written `and` (B0025).",
341 },
342 Infix {
353 op: "==",
354 power: (5, 6),
355 callee: "equal?",
356 doc: "Structural equality over every value (okite D0001): 4 == 4.0, lists and maps by content, nil only equals nil.",
357 },
358 Infix {
359 op: "!=",
360 power: (5, 6),
361 callee: "not=",
362 doc: "Exactly not(a == b) (okite D0002).",
363 },
364 Infix {
365 op: "<",
366 power: (5, 6),
367 callee: "<",
368 doc: "Numeric less-than. A string operand is a type error: order text with compare(a, b).",
369 },
370 Infix {
371 op: "<=",
372 power: (5, 6),
373 callee: "<=",
374 doc: "Numeric less-than-or-equal. Numbers only, like <.",
375 },
376 Infix {
377 op: ">",
378 power: (5, 6),
379 callee: ">",
380 doc: "Numeric greater-than. Numbers only, like <.",
381 },
382 Infix {
383 op: ">=",
384 power: (5, 6),
385 callee: ">=",
386 doc: "Numeric greater-than-or-equal. Numbers only, like <.",
387 },
388 Infix {
389 op: "+",
390 power: (7, 8),
391 callee: "+",
392 doc: "Numeric addition; an Int and a Float give a Float. Not string concatenation: interpolate instead.",
393 },
394 Infix {
395 op: "-",
396 power: (7, 8),
397 callee: "-",
398 doc: "Numeric subtraction. Written before an operand with nothing on its left, it is unary minus: -n.",
399 },
400 Infix {
401 op: "*",
402 power: (9, 10),
403 callee: "*",
404 doc: "Numeric multiplication.",
405 },
406 Infix {
407 op: "/",
408 power: (9, 10),
409 callee: "/",
410 doc: "Division that never truncates (okite D0008): 7 / 2 is 3.5, while 6 / 2 stays the Int 3. Integer division is floor(a / b).",
411 },
412 Infix {
413 op: "%",
414 power: (9, 10),
415 callee: "mod",
416 doc: "Euclidean remainder: never negative, so (0 - 7) % 3 is 2, not -1. A zero divisor is a runtime error.",
417 },
418];
419
420pub const LOWERED_ASSERT: &str = "blue-assert";
443
444pub const LOWERED_MAP: &str = "hash-map";
452
453pub const LOWERED_CONCAT: &str = "concat";
458
459pub const LOWERED_FIELD: &str = "blue-field";
460
461pub const LOWERED_MERGE: &str = "blue-merge";
462
463pub const LOWERED_SPLICE: &str = "blue-splice";
464
465pub const LOWERED_KWARGS: &str = "blue-kwargs";
466
467pub const LOWERED_ERROR_MATCH: &str = "blue-rescues?";
468
469pub const LOWERED_ENSURE: &str = "blue-ensure";
470
471pub const LOWERED_RERAISE: &str = "blue-reraise";
472
473pub const LOWERED_ATTEMPTED: &str = "blue-attempted";
474
475pub const LOWERED_FAILED: &str = "blue-failed";
476
477pub const LOWERED_HEADS: &[&str] = &[
478 "list", "concat", "not", "apply", "equal?", "not=", "and", "or", "mod",
479];
480
481pub const RESCUED: &str = "rescued-error";
482
483pub const SURFACE_KEYWORDS: &[&str] = &[
484 "if",
485 "begin",
486 "unless",
487 "def",
488 "defmacro",
489 "quote",
490 "unquote",
491 "unquote_splice",
492 "test",
493 "assert",
494 "fn",
495 "case",
496];
497
498pub const BLOCK_KEYWORDS: &[&str] = &[
510 "do", "end", "else", "elsif", "rescue", "ensure", "true", "false", "nil",
511];
512
513pub const OPERATOR_KEYWORDS: &[&str] = &["and", "or", "not"];
514
515pub fn is_reserved_word(name: &str) -> bool {
522 SURFACE_KEYWORDS.contains(&name)
523 || BLOCK_KEYWORDS.contains(&name)
524 || OPERATOR_KEYWORDS.contains(&name)
525}
526
527pub fn is_callable_name(name: &str) -> bool {
536 if qualified(name).is_some() {
538 return true;
539 }
540 !is_reserved_word(name)
541 && matches!(
542 lex(name).as_deref(),
543 Ok([t, e]) if matches!(&t.kind, TokenKind::Ident(n) if n == name)
544 && e.kind == TokenKind::Eof
545 )
546}
547
548pub const QUALIFIER: char = '/';
553
554pub const BUILTIN_QUALIFIER: &str = "blue";
557
558#[must_use]
560pub fn qualify(pkg: &str, name: &str) -> String {
561 format!("{pkg}{QUALIFIER}{name}")
562}
563
564#[must_use]
570pub fn qualified(sym: &str) -> Option<(&str, &str)> {
571 let (pkg, name) = sym.split_once(QUALIFIER)?;
572 let ident = |s: &str| {
573 matches!(
574 lex(s).as_deref(),
575 Ok([t, e]) if matches!(&t.kind, TokenKind::Ident(n) if n == s) && e.kind == TokenKind::Eof
576 )
577 };
578 let named = |s: &str| ident(s) || matches!(s, "true" | "false" | "nil");
579 (ident(pkg) && !is_reserved_word(pkg) && named(name)).then_some((pkg, name))
580}
581
582#[must_use]
590pub fn keyword_doc(word: &str) -> &'static str {
591 match word {
592 "if" => "`if c … elsif d … else … end`: an expression; its value is the branch taken, nil when none is. An `else` that is empty or `nil` is dropped (B0035).",
593 "unless" => "`unless c … end` is read as `if not c` and written that way (B0026).",
594 "begin" => "`begin … rescue :kind as e … rescue e … ensure … end`: run the statements; a `rescue` clause catches an error by kind, binding it with `as`, and `rescue e` catches every error; `ensure` always runs. A def body takes the same clauses without `begin`.",
595 "rescue" => "A clause of `begin` or `def`: `rescue :a, :b as e`, `rescue e` (every error, last). Inline, `x rescue fallback` is fallback when x raises; it binds loosest, below `|>`.",
596 "ensure" => "The last clause of `begin` or `def`: statements that run whether the block returned or raised; the block's value or error is kept.",
597 "and" => "`a and b`: logical and; `&&` is read as `and` and written `and` (B0025).",
598 "or" => "`a or b`: logical or; `||` is read as `or` and written `or` (B0025).",
599 "not" => "`not x`: true when x is false or nil; `!x` is read as `not x` and written `not x` (B0025). `not (a == b)` is written `a != b` (B0034).",
600 "def" => "`def name(a, b) … end`: a named function; its value is the last expression. Parameters may be typed: `def f(n: Int) -> Int`.",
601 "defmacro" => "`defmacro name(x) quote … unquote(x) … end end`: a macro, a rewrite of the unevaluated form.",
602 "quote" => "`quote … end`: the form itself as data, not its value. Used inside `defmacro`.",
603 "unquote" => "`unquote(x)`: inside `quote`, splice in the value of `x`.",
604 "unquote_splice" => "`unquote_splice(xs)`: inside `quote`, splice the items of the form `xs` in place, not the form itself.",
605 "test" => "`test \"what must hold\" … end`: a test block, run by `blue test` and ignored by `blue run`.",
606 "assert" => "`assert expr`: inside a test, fail it unless `expr` is truthy; the failure quotes the expression.",
607 "fn" => "`fn(x) x * 2 end`: an anonymous function, a value. Blue has no brace or do blocks.",
608 "case" => "`case v when 1 … when 2 … else … end`: compares with `==`; it does not destructure.",
609 "do" => "Reserved and refused: blue has no do-blocks. Pass `fn(x) … end` instead.",
610 "end" => "Closes `def`, `defmacro`, `if`, `case`, `fn`, `test` and `quote`.",
611 "else" => "The branch of `if` or `case` taken when nothing before it matched.",
612 "elsif" => "A further condition in an `if` chain; an `else` holding only an `if` is written `elsif` (B0027).",
613 "true" => "The boolean true.",
614 "false" => "The boolean false. Only `false` and `nil` are falsy; 0, \"\" and [] are truthy.",
615 "nil" => "The absent value. It is not `[]` (okite D0004), and only `nil == nil`.",
616 _ => "",
617 }
618}
619
620fn infix(op: &str) -> Option<&'static Infix> {
621 INFIX.iter().find(|i| i.op == op)
622}
623
624pub const SYMBOL_SPELLINGS: &[(&str, &str)] = &[("&&", "and"), ("||", "or"), ("!", "not")];
625
626fn word_for(op: &str) -> &str {
627 SYMBOL_SPELLINGS
628 .iter()
629 .find(|(sym, _)| *sym == op)
630 .map_or(op, |(_, word)| word)
631}
632
633fn infix_of(kind: &TokenKind) -> Option<&'static Infix> {
634 match kind {
635 TokenKind::Op(o) => infix(word_for(o)),
636 TokenKind::Ident(w) if w == "and" || w == "or" => infix(w),
637 _ => None,
638 }
639}
640
641const PIPE_POWER: (u8, u8) = (0, 1);
642const RESCUE_POWER: (u8, u8) = (0, 1);
643
644struct Parser {
645 toks: Vec<Token>,
646 pos: usize,
647 depth: usize,
655 max_depth: usize,
662 src: String,
663 comment_spans: Vec<Span>,
664 notes: Vec<Note>,
665}
666
667pub const MAX_EXPR_DEPTH: usize = 256;
674
675type Fail = Box<ParseError>;
687
688fn fail(message: impl Into<String>, span: Span) -> Fail {
689 Box::new(ParseError::new(message, span))
690}
691
692impl Parser {
693 fn over(src: &str, all: Vec<Token>, max_depth: usize) -> Self {
694 let comment_spans = all
695 .iter()
696 .filter(|t| matches!(t.kind, TokenKind::Comment(_)))
697 .map(|t| t.span)
698 .collect();
699 let toks = all
700 .into_iter()
701 .filter(|t| !matches!(t.kind, TokenKind::Comment(_)))
702 .collect();
703 Parser {
704 toks,
705 pos: 0,
706 depth: 0,
707 max_depth,
708 src: src.to_string(),
709 comment_spans,
710 notes: Vec::new(),
711 }
712 }
713
714 fn note(&mut self, spelling: Spelling, span: Span, written: &str, canonical: &str) {
715 self.notes.push(Note {
716 spelling,
717 span,
718 written: written.to_string(),
719 canonical: canonical.to_string(),
720 });
721 }
722
723 fn text(&self, span: Span) -> &str {
724 self.src.get(span.start..span.end).unwrap_or("")
725 }
726
727 fn comment_within(&self, lo: usize, hi: usize) -> bool {
728 self.comment_spans
729 .iter()
730 .any(|c| c.start >= lo && c.start < hi)
731 }
732
733 fn touches_previous(&self) -> bool {
734 let here = self.peek_span();
735 self.toks
736 .get(self.pos.wrapping_sub(1))
737 .is_some_and(|prev| prev.span.end == here.start)
738 }
739
740 fn peek(&self) -> &TokenKind {
741 &self.toks[self.pos.min(self.toks.len() - 1)].kind
742 }
743
744 fn peek_span(&self) -> Span {
745 self.toks[self.pos.min(self.toks.len() - 1)].span
746 }
747
748 fn bump(&mut self) -> TokenKind {
749 let k = self.toks[self.pos.min(self.toks.len() - 1)].kind.clone();
750 if self.pos < self.toks.len() {
751 self.pos += 1;
752 }
753 k
754 }
755
756 fn at(&self, k: &TokenKind) -> bool {
757 self.peek() == k
758 }
759
760 fn eat(&mut self, k: &TokenKind) -> bool {
761 if self.at(k) {
762 self.bump();
763 true
764 } else {
765 false
766 }
767 }
768
769 fn expect(&mut self, k: &TokenKind, what: &str) -> Result<(), Fail> {
770 if self.eat(k) {
771 Ok(())
772 } else {
773 Err(self.error(format!("expected {what}, found {}", self.peek())))
774 }
775 }
776
777 fn error(&self, message: impl Into<String>) -> Fail {
778 fail(message, self.peek_span())
779 }
780
781 fn skip_newlines(&mut self) {
783 while matches!(self.peek(), TokenKind::Newline) {
784 self.bump();
785 }
786 }
787
788 fn at_ident(&self, name: &str) -> bool {
789 matches!(self.peek(), TokenKind::Ident(n) if n == name)
790 }
791
792 fn mark(&self) -> usize {
795 self.peek_span().start
796 }
797
798 fn span_since(&self, start: usize) -> Span {
804 let end = self
805 .toks
806 .get(self.pos.saturating_sub(1))
807 .map_or(start, |t| t.span.end);
808 Span::new(start, end.max(start))
809 }
810
811 fn program(&mut self) -> Result<Vec<Spanned>, Fail> {
812 let mut out = Vec::new();
813 loop {
814 self.skip_newlines();
815 if matches!(self.peek(), TokenKind::Eof) {
816 break;
817 }
818 out.push(self.statement()?);
819 self.end_of_statement(&[])?;
820 }
821 Ok(out)
822 }
823
824 fn end_of_statement(&self, terminators: &[&str]) -> Result<(), Fail> {
842 let next = self.peek();
843 if matches!(next, TokenKind::Newline | TokenKind::Eof)
844 || terminators.iter().any(|t| self.at_ident(t))
845 {
846 return Ok(());
847 }
848 let prev = self.toks.get(self.pos.wrapping_sub(1)).map(|t| &t.kind);
849 let prev_name = match prev {
850 Some(TokenKind::Ident(n)) => Some(n.as_str()),
851 _ => None,
852 };
853 let callee = prev_name.unwrap_or("f");
854 let why = match (prev, next) {
855 (Some(TokenKind::Ident(t)), _) if t == "then" => {
856 "blue's `if` has no `then`: put the branch on the next line".to_string()
857 }
858 (_, TokenKind::Ident(d)) if d == "do" => "blue has no `do` blocks: pass a function \
859 as an argument, `f(xs, fn(x) … end)`"
860 .to_string(),
861 (_, TokenKind::LBracket) => {
862 let index = match (self.toks.get(self.pos + 1), self.toks.get(self.pos + 2)) {
863 (Some(i), Some(close)) if close.kind == TokenKind::RBracket => match &i.kind {
864 TokenKind::Int(n) => n.to_string(),
865 TokenKind::Ident(n) => n.clone(),
866 _ => "i".to_string(),
867 },
868 _ => "i".to_string(),
869 };
870 let list = prev_name.unwrap_or("xs");
871 format!(
872 "blue has no `xs[i]` indexing: write `nth({index}, {list})` \
873 (or `first({list})`, `last({list})`)"
874 )
875 }
876 (_, TokenKind::LBrace) => "blue has no brace blocks: pass a function as an \
877 argument, `map(xs, fn(x) … end)`"
878 .to_string(),
879 (
880 Some(TokenKind::Ident(_)),
881 TokenKind::Ident(_)
882 | TokenKind::Int(_)
883 | TokenKind::Float(_)
884 | TokenKind::Str(_)
885 | TokenKind::InterpolatedStr { .. }
886 | TokenKind::Sym(_)
887 | TokenKind::True
888 | TokenKind::False
889 | TokenKind::Nil,
890 ) => {
891 let arg = match next {
892 TokenKind::Ident(a) => a.clone(),
893 _ => "x".to_string(),
894 };
895 format!("a call needs parentheses: write `{callee}({arg})`")
896 }
897 _ => "put each statement on its own line".to_string(),
898 };
899 Err(self.error(format!("expected the end of the line, found {next}: {why}")))
900 }
901
902 fn statement(&mut self) -> Result<Spanned, Fail> {
915 let max = self.max_depth;
929 if self.depth >= max {
930 return Err(self.error(format!(
931 "statement nests deeper than {max}; refusing to \
932 recurse further (this is a limit, not a syntax error)"
933 )));
934 }
935 self.depth += 1;
936 let r = self.statement_inner();
937 self.depth -= 1;
938 r
939 }
940
941 fn statement_inner(&mut self) -> Result<Spanned, Fail> {
942 if let TokenKind::Ident(name) = self.peek().clone() {
943 if self.peek_at(1) == "=" && !is_reserved_word(&name) {
944 let name_span = self.peek_span();
945 self.bump(); self.bump(); self.skip_newlines();
948 let value = self.expr(0)?;
949 return Ok(list_at(
953 self.span_since(name_span.start),
954 vec![sym_at(name_span, "define"), sym_at(name_span, &name), value],
955 ));
956 }
957 }
958 let e = self.expr(0)?;
959 if let SpannedForm::List(items) = &e.form {
960 if items.len() == 3
961 && items[0].as_symbol() == Some("define")
962 && items[1].as_symbol().is_some()
963 && self.text(items[0].span) == "define"
964 {
965 self.note(Spelling::OperatorSpelling, e.span, "define(x, v)", "x = v");
966 }
967 }
968 Ok(e)
969 }
970
971 fn peek_at(&self, n: usize) -> String {
974 match self.toks.get(self.pos + n).map(|t| &t.kind) {
975 Some(TokenKind::Op(o)) => o.clone(),
976 _ => String::new(),
977 }
978 }
979
980 fn expr(&mut self, min_bp: u8) -> Result<Spanned, Fail> {
982 let max = self.max_depth;
992 if self.depth >= max {
993 return Err(self.error(format!(
994 "expression nests deeper than {max}; refusing to \
995 recurse further (this is a limit, not a syntax error)"
996 )));
997 }
998 self.depth += 1;
999 let r = self.expr_inner(min_bp);
1000 self.depth -= 1;
1001 r
1002 }
1003
1004 #[inline(never)]
1005 fn rescue_node(&self, start: usize, op_span: Span, lhs: Spanned, fallback: Spanned) -> Spanned {
1006 list_at(
1007 self.span_since(start),
1008 vec![
1009 sym_at(op_span, "try"),
1010 lhs,
1011 list_at(
1012 op_span.merge(fallback.span),
1013 vec![
1014 sym_at(op_span, "catch"),
1015 list_at(op_span, vec![sym_at(op_span, "_")]),
1016 fallback,
1017 ],
1018 ),
1019 ],
1020 )
1021 }
1022
1023 #[inline(never)]
1024 fn infix_spelling(&mut self, callee: Option<&str>, op_span: Span) {
1025 let written = self.text(op_span).to_string();
1026 match (callee, self.peek().clone()) {
1027 (None, _) => self.note(Spelling::CallForm, op_span, "|>", "a call"),
1028 (Some(c), TokenKind::Op(o)) if word_for(&o) != o => {
1029 self.note(Spelling::LogicalSymbol, op_span, &written, c);
1030 }
1031 (Some(_), TokenKind::Op(o)) if written != o => {
1032 self.note(Spelling::OperatorSpelling, op_span, &written, &o);
1033 }
1034 _ => {}
1035 }
1036 }
1037
1038 #[inline(never)]
1039 fn nil_test(
1040 &mut self,
1041 start: usize,
1042 op_span: Span,
1043 lhs: &Spanned,
1044 rhs: &Spanned,
1045 ) -> Option<Spanned> {
1046 let (subject, written) = match (&lhs.form, &rhs.form) {
1047 (_, SpannedForm::Nil) if !matches!(lhs.form, SpannedForm::Nil) => (lhs, "x == nil"),
1048 (SpannedForm::Nil, _) if !matches!(rhs.form, SpannedForm::Nil) => (rhs, "nil == x"),
1049 _ => return None,
1050 };
1051 let span = self.span_since(start);
1052 self.note(Spelling::NilTest, span, written, "x.nil?");
1053 Some(list_at(
1054 span,
1055 vec![
1056 sym_at(op_span, LOWERED_FIELD),
1057 subject.clone(),
1058 atom_at(op_span, Atom::Keyword("nil?".to_string())),
1059 ],
1060 ))
1061 }
1062
1063 #[inline(never)]
1064 fn infix_node(
1065 &mut self,
1066 start: usize,
1067 callee: Option<&str>,
1068 op_span: Span,
1069 lhs: Spanned,
1070 rhs: Spanned,
1071 ) -> Spanned {
1072 if callee == Some("-") && matches!(lhs.form, SpannedForm::Atom(Atom::Int(0))) {
1073 self.note(
1074 Spelling::OperatorSpelling,
1075 lhs.span.merge(op_span),
1076 "0 - x",
1077 "-x",
1078 );
1079 }
1080
1081 if callee == Some("equal?") {
1082 if let Some(test) = self.nil_test(start, op_span, &lhs, &rhs) {
1083 return test;
1084 }
1085 }
1086
1087 if callee.is_none() {
1088 let rhs_span = rhs.span;
1093 let threaded = match rhs.form {
1094 SpannedForm::List(mut items) if !items.is_empty() => {
1095 items.insert(1, lhs);
1096 list_at(self.span_since(start), items)
1097 }
1098 callee => list_at(
1099 self.span_since(start),
1100 vec![Spanned::new(rhs_span, callee), lhs],
1101 ),
1102 };
1103 match &threaded.form {
1104 SpannedForm::List(items)
1105 if items.len() == 2 && items[0].as_symbol() == Some("not") =>
1106 {
1107 let (head, operand) = (items[0].span, items[1].clone());
1108 self.negation(head, threaded.span, operand)
1109 }
1110 SpannedForm::List(items)
1111 if items.len() == 4
1112 && items[0].as_symbol() == Some("if")
1113 && matches!(items[3].form, SpannedForm::Nil) =>
1114 {
1115 list_at(threaded.span, items[..3].to_vec())
1116 }
1117 SpannedForm::List(items)
1118 if items.len() == 3 && items[0].as_symbol() == Some("equal?") =>
1119 {
1120 let (op, a, b) = (items[0].span, items[1].clone(), items[2].clone());
1121 match self.nil_test(start, op, &a, &b) {
1122 Some(test) => test,
1123 None => threaded,
1124 }
1125 }
1126 _ => threaded,
1127 }
1128 } else {
1129 list_at(
1135 self.span_since(start),
1136 vec![sym_at(op_span, callee.unwrap_or_default()), lhs, rhs],
1137 )
1138 }
1139 }
1140
1141 fn expr_inner(&mut self, min_bp: u8) -> Result<Spanned, Fail> {
1142 let start = self.mark();
1143 let mut lhs = self.prefix()?;
1144
1145 loop {
1146 match self.peek() {
1148 TokenKind::Dot => {
1149 let dot = self.peek_span();
1150 self.bump();
1151 lhs = self.finish_send(start, lhs, dot)?;
1152 continue;
1153 }
1154 TokenKind::LParen => {
1155 let (args, splat) = self.call_args()?;
1156 let call = self.call_node(self.span_since(start), vec![lhs], args, splat);
1157 lhs = self.call_spelling(call);
1158 continue;
1159 }
1160 _ => {}
1161 }
1162
1163 let op_span = self.peek_span();
1167 if self.at_ident("rescue") {
1168 if RESCUE_POWER.0 < min_bp {
1169 break;
1170 }
1171 self.bump();
1172 self.skip_newlines();
1173 let fallback = self.expr(RESCUE_POWER.1)?;
1174 lhs = self.rescue_node(start, op_span, lhs, fallback);
1175 continue;
1176 }
1177 let (callee, (lbp, rbp)) = match self.peek() {
1178 TokenKind::Pipe => (None, PIPE_POWER),
1179 other => match infix_of(other) {
1180 Some(i) => (Some(i.callee), i.power),
1181 None => break,
1182 },
1183 };
1184 if lbp < min_bp {
1185 break;
1186 }
1187 self.infix_spelling(callee, op_span);
1188 self.bump();
1189 self.skip_newlines();
1190 let rhs = self.expr(rbp)?;
1191 lhs = self.infix_node(start, callee, op_span, lhs, rhs);
1192 }
1193
1194 Ok(lhs)
1195 }
1196
1197 #[inline(never)]
1198 fn interpolation(span: Span, parts: &[String], exprs: &[String]) -> Result<Spanned, Fail> {
1199 let mut acc = atom_at(span, Atom::Str(parts[0].clone()));
1209 for (i, raw) in exprs.iter().enumerate() {
1210 let inner = parse_expr(raw).map_err(|e| {
1211 fail(
1212 format!("in interpolation `#{{{raw}}}`: {}", e.message),
1213 span,
1214 )
1215 })?;
1216 let inner = Spanned::from_sexp_at(&inner, span);
1217 acc = list_at(span, vec![sym_at(span, LOWERED_CONCAT), acc, inner]);
1218 acc = list_at(
1221 span,
1222 vec![
1223 sym_at(span, LOWERED_CONCAT),
1224 acc,
1225 atom_at(span, Atom::Str(parts[i + 1].clone())),
1226 ],
1227 );
1228 }
1229 Ok(acc)
1230 }
1231
1232 #[inline(never)]
1233 fn not_prefix(&mut self, span: Span) -> Result<Spanned, Fail> {
1234 if self.at(&TokenKind::LParen) && self.touches_previous() {
1235 self.note(Spelling::OperatorSpelling, span, "not(x)", "not x");
1236 let (args, splat) = self.call_args()?;
1237 if args.len() != 1 || splat.is_some() {
1238 let call = self.call_node(
1239 self.span_since(span.start),
1240 vec![sym_at(span, "not")],
1241 args,
1242 splat,
1243 );
1244 return Ok(call);
1245 }
1246 let operand = args.into_iter().next().expect("one argument");
1247 return Ok(self.negation(span, self.span_since(span.start), operand));
1248 }
1249 let rhs = self.expr(11)?;
1250 Ok(self.negation(span, self.span_since(span.start), rhs))
1251 }
1252
1253 fn prefix(&mut self) -> Result<Spanned, Fail> {
1254 if self.at(&TokenKind::LParen) {
1255 let span = self.peek_span();
1256 self.bump();
1257 if self.arrow_lambda_ahead() {
1258 return self.arrow_lambda(span);
1259 }
1260 return self.group();
1261 }
1262 self.prefix_token()
1263 }
1264
1265 fn group(&mut self) -> Result<Spanned, Fail> {
1266 self.skip_newlines();
1267 let inner = self.expr(0)?;
1268 self.skip_newlines();
1269 self.expect(&TokenKind::RParen, "`)`")?;
1270 Ok(inner)
1271 }
1272
1273 #[inline(never)]
1274 fn prefix_token(&mut self) -> Result<Spanned, Fail> {
1275 let span = self.peek_span();
1276 match self.bump() {
1277 TokenKind::Int(v) => Ok(atom_at(span, Atom::Int(v))),
1278 TokenKind::Float(v) => Ok(atom_at(span, Atom::Float(v))),
1279 TokenKind::Str(s) => Ok(atom_at(span, Atom::Str(s))),
1280
1281 TokenKind::InterpolatedStr { parts, exprs } => Self::interpolation(span, &parts, &exprs),
1292 TokenKind::Sym(s) => Ok(atom_at(span, Atom::Keyword(s))),
1293 TokenKind::True => Ok(atom_at(span, Atom::Bool(true))),
1294 TokenKind::False => Ok(atom_at(span, Atom::Bool(false))),
1295 TokenKind::Nil => Ok(Spanned::new(span, SpannedForm::Nil)),
1296
1297 TokenKind::Op(o) if o == "-" => {
1298 let rhs = self.expr(11)?; Ok(list_at(
1300 self.span_since(span.start),
1301 vec![sym_at(span, "-"), atom_at(span, Atom::Int(0)), rhs],
1302 ))
1303 }
1304 TokenKind::Op(o) if o == "!" => {
1305 let written = self.text(span).to_string();
1306 self.note(Spelling::LogicalSymbol, span, &written, "not");
1307 let rhs = self.expr(11)?;
1308 Ok(self.negation(span, self.span_since(span.start), rhs))
1309 }
1310 TokenKind::Ident(w) if w == "not" => self.not_prefix(span),
1311 TokenKind::Ident(w) if (w == "and" || w == "or") && self.at(&TokenKind::LParen) => {
1312 Ok(sym_at(span, &w))
1313 }
1314 TokenKind::Ident(w) if w == "and" || w == "or" => Err(fail(
1315 format!("`{w}` joins two expressions: `a {w} b`"),
1316 span,
1317 )),
1318
1319 TokenKind::LParen => {
1320 self.skip_newlines();
1321 let inner = self.expr(0)?;
1322 self.skip_newlines();
1323 self.expect(&TokenKind::RParen, "`)`")?;
1324 Ok(inner)
1328 }
1329
1330 TokenKind::LBracket => self.list_literal(span),
1331 TokenKind::LBrace => self.map_literal(span),
1332
1333 TokenKind::Ident(name) if self.at(&TokenKind::PathSep) => {
1334 self.qualified_name(span, &name)
1335 }
1336 TokenKind::PathSep => Err(fail(
1337 "a qualified name is `package::name`, with the package written before `::`",
1338 span,
1339 )),
1340 TokenKind::Ident(name) => match name.as_str() {
1341 "if" => self.if_form(span, false),
1342 "begin" if !(self.at(&TokenKind::LParen) && self.touches_previous()) => {
1343 self.begin_form(span)
1344 }
1345 "rescue" | "ensure" => Err(fail(
1346 format!("`{name}` belongs to a `begin … end` block or a `def` body, after the statements it protects"),
1347 span,
1348 )),
1349 "unless" => {
1350 self.note(Spelling::Unless, span, "unless", "if not");
1351 self.if_form(span, true)
1352 }
1353 "def" => self.def_form(span),
1354 "defmacro" => self.defmacro_form(span),
1355 "case" => self.case_form(span),
1356 "fn" => self.lambda_form(span),
1357 "test" => self.test_form(span),
1358 "assert" => self.assert_form(span),
1359 "quote" => self.quote_form(span),
1360 "unquote" => self.unquote_form(span, false),
1361 "unquote_splice" => self.unquote_form(span, true),
1362 "do" => Err(fail(
1363 "blue has no `do` blocks: pass a function as an argument, \
1364 `f(xs, fn(x) … end)`",
1365 span,
1366 )),
1367 "end" => Err(fail(
1368 "unexpected `end`: nothing open here for it to close",
1369 span,
1370 )),
1371 _ => Ok(sym_at(span, &name)),
1372 },
1373
1374 other => Err(fail(format!("expected an expression, found {other}"), span)),
1375 }
1376 }
1377
1378 fn qualified_name(&mut self, pkg_span: Span, pkg: &str) -> Result<Spanned, Fail> {
1388 if is_reserved_word(pkg) {
1389 return Err(fail(
1390 format!("`{pkg}` is a reserved word and cannot name a package"),
1391 pkg_span,
1392 ));
1393 }
1394 self.bump(); let name_span = self.peek_span();
1396 let name = match self.bump() {
1397 TokenKind::Ident(n) => n,
1398 TokenKind::True => "true".to_string(),
1400 TokenKind::False => "false".to_string(),
1401 TokenKind::Nil => "nil".to_string(),
1402 other => {
1403 return Err(fail(
1404 format!("expected a name after `{pkg}::`, found {other}"),
1405 name_span,
1406 ))
1407 }
1408 };
1409 if self.at(&TokenKind::PathSep) {
1410 return Err(self.error(format!(
1411 "a qualified name has one qualifier: `package::name`, and `{pkg}::{name}::…` has two"
1412 )));
1413 }
1414 Ok(sym_at(
1415 self.span_since(pkg_span.start),
1416 &qualify(pkg, &name),
1417 ))
1418 }
1419
1420 #[inline(never)]
1427 fn negation(&mut self, head: Span, whole: Span, operand: Spanned) -> Spanned {
1428 if let SpannedForm::List(items) = &operand.form {
1429 let flipped = match items.first().and_then(Spanned::as_symbol) {
1430 Some("equal?") => Some("not="),
1431 Some("not=") => Some("equal?"),
1432 _ => None,
1433 };
1434 if let (Some(to), 3) = (flipped, items.len()) {
1435 let (written, canonical) = if to == "not=" {
1436 ("not (a == b)", "a != b")
1437 } else {
1438 ("not (a != b)", "a == b")
1439 };
1440 self.note(Spelling::NegatedComparison, whole, written, canonical);
1441 let mut out = items.clone();
1442 out[0] = sym_at(items[0].span, to);
1443 if to == "equal?" {
1444 if let Some(test) =
1445 self.nil_test(whole.start, items[0].span, &items[1], &items[2])
1446 {
1447 return test;
1448 }
1449 }
1450 return list_at(whole, out);
1451 }
1452 }
1453 list_at(whole, vec![sym_at(head, "not"), operand])
1454 }
1455
1456 #[inline(never)]
1457 fn call_spelling(&mut self, call: Spanned) -> Spanned {
1458 let SpannedForm::List(items) = &call.form else {
1459 return call;
1460 };
1461 let Some(head) = items.first().and_then(Spanned::as_symbol) else {
1462 return call;
1463 };
1464 let argc = items.len() - 1;
1465 if head == "equal?" && argc == 2 {
1466 let (op_span, start) = (items[0].span, call.span.start);
1467 if let Some(test) = self.nil_test(start, op_span, &items[1], &items[2]) {
1468 return test;
1469 }
1470 }
1471 let operator = match (head, argc) {
1472 ("and" | "or" | "mod" | "equal?", 2) => Some(head),
1473 ("list", _) => Some("list"),
1474 _ => None,
1475 };
1476 if let Some(op) = operator {
1477 let canonical = match op {
1478 "list" => "[…]".to_string(),
1479 "mod" => "a % b".to_string(),
1480 "equal?" => "a == b".to_string(),
1481 other => format!("a {other} b"),
1482 };
1483 self.note(
1484 Spelling::OperatorSpelling,
1485 call.span,
1486 &format!("{head}(…)"),
1487 &canonical,
1488 );
1489 return call;
1490 }
1491 if head == "apply"
1492 && self.text(items[0].span) == "apply"
1493 && argc >= 2
1494 && items[1].as_symbol().is_some_and(is_callable_name)
1495 {
1496 self.note(Spelling::Apply, call.span, "apply(f, …, xs)", "f(…, *xs)");
1497 return call;
1498 }
1499 if head == "try" {
1500 return self.try_spelling(call);
1501 }
1502 call
1503 }
1504
1505 fn try_spelling(&mut self, call: Spanned) -> Spanned {
1506 let SpannedForm::List(items) = &call.form else {
1507 return call;
1508 };
1509 let Some((catch, body)) = items[1..].split_last() else {
1510 return call;
1511 };
1512 let Some(clause) = catch.as_list() else {
1513 return call;
1514 };
1515 let binder = match clause {
1516 [head, binding, handler @ ..]
1517 if head.as_symbol() == Some("catch") && !handler.is_empty() =>
1518 {
1519 match binding.as_list() {
1520 Some([b]) => b.as_symbol().map(|n| (n.to_string(), b.span)),
1521 _ => None,
1522 }
1523 }
1524 _ => None,
1525 };
1526 let Some((name, binder_span)) = binder else {
1527 return call;
1528 };
1529 if body.is_empty() {
1530 return call;
1531 }
1532 self.note(Spelling::TryCatch, call.span, "try(…, catch(…))", "rescue");
1533 let handler = &clause[2..];
1534 let rename =
1535 name.starts_with('_') && name != "_" && !handler.iter().any(|h| mentions(h, &name));
1536 let body = if body.len() == 1 {
1537 body[0].clone()
1538 } else {
1539 let span = cover(body, call.span);
1540 let mut list = vec![sym_at(Span::new(span.start, span.start), "begin")];
1541 list.extend(body.iter().cloned());
1542 list_at(span, list)
1543 };
1544 let handler = if handler.len() == 1 {
1545 handler[0].clone()
1546 } else {
1547 let span = cover(handler, catch.span);
1548 let mut list = vec![sym_at(Span::new(span.start, span.start), "begin")];
1549 list.extend(handler.iter().cloned());
1550 list_at(span, list)
1551 };
1552 let binder = sym_at(binder_span, if rename { "_" } else { &name });
1553 list_at(
1554 call.span,
1555 vec![
1556 items[0].clone(),
1557 body,
1558 list_at(
1559 catch.span,
1560 vec![
1561 clause[0].clone(),
1562 list_at(clause[1].span, vec![binder]),
1563 handler,
1564 ],
1565 ),
1566 ],
1567 )
1568 }
1569
1570 fn finish_send(&mut self, start: usize, recv: Spanned, dot: Span) -> Result<Spanned, Fail> {
1571 let name_span = self.peek_span();
1572 let name = match self.bump() {
1573 TokenKind::Ident(n) if is_reserved_word(&n) => {
1587 return Err(self.error(format!(
1588 "`{n}` is a reserved word and cannot be a method name — \
1589 `recv.{n}` would lower onto the `{n}` form itself"
1590 )))
1591 }
1592 TokenKind::Ident(n) => n,
1593 other => {
1594 return Err(fail(
1595 format!("expected a method name after `.`, found {other}"),
1596 name_span,
1597 ))
1598 }
1599 };
1600 if !self.at(&TokenKind::LParen) {
1601 return Ok(list_at(
1602 self.span_since(start),
1603 vec![
1604 sym_at(dot, LOWERED_FIELD),
1605 recv,
1606 atom_at(name_span, Atom::Keyword(name)),
1607 ],
1608 ));
1609 }
1610 self.note(
1611 Spelling::CallForm,
1612 dot.merge(name_span),
1613 &format!("x.{name}(…)"),
1614 &format!("{name}(x, …)"),
1615 );
1616 let (args, splat) = self.call_args()?;
1617 Ok(self.call_node(
1618 self.span_since(start),
1619 vec![sym_at(name_span, &name), recv],
1620 args,
1621 splat,
1622 ))
1623 }
1624
1625 fn call_node(
1626 &self,
1627 span: Span,
1628 head: Vec<Spanned>,
1629 args: Vec<Spanned>,
1630 splat: Option<Span>,
1631 ) -> Spanned {
1632 let mut list = Vec::with_capacity(head.len() + args.len() + 1);
1633 if let Some(at) = splat {
1634 list.push(sym_at(at, "apply"));
1635 }
1636 list.extend(head);
1637 list.extend(args);
1638 list_at(span, list)
1639 }
1640
1641 fn call_args(&mut self) -> Result<(Vec<Spanned>, Option<Span>), Fail> {
1642 self.expect(&TokenKind::LParen, "`(`")?;
1643 let mut args = Vec::new();
1644 let mut splat = None;
1645 self.skip_newlines();
1646 if self.eat(&TokenKind::RParen) {
1647 return Ok((args, splat));
1648 }
1649 loop {
1650 self.skip_newlines();
1651 if splat.is_some() {
1652 return Err(
1653 self.error("a splat `*xs` passes a list as the LAST arguments: write it last")
1654 );
1655 }
1656 if self.at_spread() || matches!(self.peek(), TokenKind::Label(_)) {
1657 let open = self.peek_span();
1658 let (segments, spread) = self.map_entries(open, &TokenKind::RParen)?;
1659 if !spread {
1660 return Err(fail(
1661 "a keyword entry in a call needs a `**` spread: pass a map, f({a: 1})",
1662 open,
1663 ));
1664 }
1665 args.push(self.merged(open, LOWERED_KWARGS, segments));
1666 return Ok((args, splat));
1667 }
1668 if matches!(self.peek(), TokenKind::Op(o) if o == "*") {
1669 splat = Some(self.peek_span());
1670 self.bump();
1671 }
1672 args.push(self.expr(0)?);
1673 self.skip_newlines();
1674 if self.eat(&TokenKind::Comma) {
1675 if self.trailing_comma(&TokenKind::RParen) {
1676 break;
1677 }
1678 continue;
1679 }
1680 self.expect(&TokenKind::RParen, "`,` or `)`")?;
1681 break;
1682 }
1683 Ok((args, splat))
1684 }
1685
1686 fn trailing_comma(&mut self, closer: &TokenKind) -> bool {
1687 let comma = self
1688 .toks
1689 .get(self.pos.wrapping_sub(1))
1690 .map_or(self.peek_span(), |t| t.span);
1691 self.skip_newlines();
1692 if self.at(closer) {
1693 self.note(Spelling::TrailingComma, comma, ",", "");
1694 self.bump();
1695 return true;
1696 }
1697 false
1698 }
1699
1700 fn list_literal(&mut self, open: Span) -> Result<Spanned, Fail> {
1701 let mut items = vec![sym_at(open, "list")];
1702 self.skip_newlines();
1703 if self.eat(&TokenKind::RBracket) {
1704 return Ok(list_at(self.span_since(open.start), items));
1705 }
1706 loop {
1707 self.skip_newlines();
1708 items.push(self.expr(0)?);
1709 self.skip_newlines();
1710 if self.eat(&TokenKind::Comma) {
1711 if self.trailing_comma(&TokenKind::RBracket) {
1712 break;
1713 }
1714 continue;
1715 }
1716 self.expect(&TokenKind::RBracket, "`,` or `]`")?;
1717 break;
1718 }
1719 Ok(list_at(self.span_since(open.start), items))
1720 }
1721
1722 fn map_literal(&mut self, open: Span) -> Result<Spanned, Fail> {
1729 self.skip_newlines();
1730 if self.eat(&TokenKind::RBrace) {
1731 return Ok(list_at(
1732 self.span_since(open.start),
1733 vec![sym_at(open, LOWERED_MAP)],
1734 ));
1735 }
1736 let (mut segments, spread) = self.map_entries(open, &TokenKind::RBrace)?;
1737 if !spread {
1738 return Ok(segments.remove(0));
1739 }
1740 Ok(self.merged(open, LOWERED_MERGE, segments))
1741 }
1742
1743 fn merged(&self, open: Span, head: &str, segments: Vec<Spanned>) -> Spanned {
1744 let mut items = vec![sym_at(open, head)];
1745 items.extend(segments);
1746 list_at(self.span_since(open.start), items)
1747 }
1748
1749 fn at_spread(&self) -> bool {
1750 matches!(self.peek(), TokenKind::Op(o) if o == "**")
1751 }
1752
1753 fn map_entries(
1754 &mut self,
1755 open: Span,
1756 closer: &TokenKind,
1757 ) -> Result<(Vec<Spanned>, bool), Fail> {
1758 let mut segments = Vec::new();
1759 let mut items = vec![sym_at(open, LOWERED_MAP)];
1760 let mut spread = false;
1761 loop {
1762 self.skip_newlines();
1763 if self.at_spread() {
1764 let star = self.peek_span();
1765 self.bump();
1766 let value = self.expr(0)?;
1767 if items.len() > 1 {
1768 let span = items[1].span.merge(items[items.len() - 1].span);
1769 segments.push(list_at(span, std::mem::take(&mut items)));
1770 items = vec![sym_at(open, LOWERED_MAP)];
1771 }
1772 segments.push(list_at(
1773 star.merge(value.span),
1774 vec![sym_at(star, LOWERED_SPLICE), value],
1775 ));
1776 spread = true;
1777 } else {
1778 self.map_entry(&mut items, closer)?;
1779 }
1780 self.skip_newlines();
1781 if self.eat(&TokenKind::Comma) {
1782 if self.trailing_comma(closer) {
1783 break;
1784 }
1785 continue;
1786 }
1787 self.expect(closer, "`,` or the end of the entries")?;
1788 break;
1789 }
1790 if items.len() > 1 || !spread {
1791 let whole = self.span_since(open.start);
1792 segments.push(list_at(whole, items));
1793 }
1794 Ok((segments, spread))
1795 }
1796
1797 fn map_entry(&mut self, items: &mut Vec<Spanned>, closer: &TokenKind) -> Result<(), Fail> {
1798 match self.peek().clone() {
1799 TokenKind::Label(name) => {
1800 let label_span = self.peek_span();
1801 self.bump();
1802 self.skip_newlines();
1803 items.push(atom_at(label_span, Atom::Keyword(name.clone())));
1804 if matches!(self.peek(), TokenKind::Comma) || self.at(closer) {
1805 items.push(sym_at(label_span, &name));
1806 } else {
1807 let value = self.expr(0)?;
1808 if matches!(&value.form, SpannedForm::Atom(Atom::Symbol(s)) if *s == name)
1809 && label_name(&name)
1810 {
1811 let written = format!("{name}: {name}");
1812 let canonical = format!("{name}:");
1813 self.note(
1814 Spelling::MapPun,
1815 label_span.merge(value.span),
1816 &written,
1817 &canonical,
1818 );
1819 }
1820 items.push(value);
1821 }
1822 }
1823 _ => {
1824 let k = self.expr(0)?;
1825 self.skip_newlines();
1826 if let SpannedForm::Atom(Atom::Keyword(name)) = &k.form {
1827 if blue_lang_name(name) {
1828 let written = format!(":{name} =>");
1829 let canonical = format!("{name}:");
1830 self.note(Spelling::KeywordRocket, k.span, &written, &canonical);
1831 }
1832 }
1833 self.expect(&TokenKind::Rocket, "`=>` in a map literal")?;
1834 self.skip_newlines();
1835 items.push(k);
1836 items.push(self.expr(0)?);
1837 }
1838 }
1839 Ok(())
1840 }
1841
1842 fn if_chain(&mut self, head: Span) -> Result<Spanned, Fail> {
1852 let cond = self.expr(0)?;
1853 let then = self.body(&["elsif", "else", "end"])?;
1854 let els = self.else_part()?;
1855 let mut out = vec![sym_at(head, "if"), cond, then];
1856 if let Some(e) = els {
1857 out.push(e);
1858 }
1859 Ok(list_at(self.span_since(head.start), out))
1860 }
1861
1862 fn else_part(&mut self) -> Result<Option<Spanned>, Fail> {
1863 if self.at_ident("elsif") {
1864 let elsif = self.peek_span();
1865 self.bump();
1866 return Ok(Some(self.if_chain(elsif)?));
1867 }
1868 if !self.at_ident("else") {
1869 self.expect_ident("end")?;
1870 return Ok(None);
1871 }
1872 let else_span = self.peek_span();
1873 self.bump();
1874 let save = self.pos;
1875 self.skip_newlines();
1876 let leads_with_if = self.at_ident("if") || self.at_ident("unless");
1877 self.pos = save;
1878 let e = self.body(&["end"])?;
1879 let end_at = self.peek_span().start;
1880 self.expect_ident("end")?;
1881 if matches!(e.form, SpannedForm::Nil) && !self.comment_within(else_span.start, end_at) {
1882 self.note(Spelling::ElseNil, else_span, "else nil", "no else");
1883 return Ok(None);
1884 }
1885 let single_if = matches!(&e.form, SpannedForm::List(items)
1886 if (items.len() == 3 || items.len() == 4) && items[0].as_symbol() == Some("if"));
1887 if leads_with_if && single_if {
1888 self.note(Spelling::ElseIf, else_span, "else if", "elsif");
1889 }
1890 Ok(Some(e))
1891 }
1892
1893 fn if_form(&mut self, head: Span, negate: bool) -> Result<Spanned, Fail> {
1894 let cond = self.expr(0)?;
1895 let cond = if negate {
1896 let inner = cond.span;
1899 self.negation(head, head.merge(inner), cond)
1900 } else {
1901 cond
1902 };
1903 let then = self.body(&["elsif", "else", "end"])?;
1916 let els = self.else_part()?;
1917 let mut out = vec![sym_at(head, "if"), cond, then];
1918 if let Some(e) = els {
1919 out.push(e);
1920 }
1921 Ok(list_at(self.span_since(head.start), out))
1922 }
1923
1924 fn case_form(&mut self, head: Span) -> Result<Spanned, Fail> {
1950 let subject = self.expr(0)?;
1951 self.skip_newlines();
1952
1953 let subject_var = "case-subject";
1956 let mut arms: Vec<Spanned> = Vec::new();
1957 let mut otherwise: Option<Spanned> = None;
1958 let mut else_kw: Option<Span> = None;
1961
1962 loop {
1963 self.skip_newlines();
1964 if self.at_ident("end") {
1965 break;
1966 }
1967 let maybe_else = self.peek_span();
1968 if self.eat_ident("else") {
1969 else_kw = Some(maybe_else);
1970 otherwise = Some(self.body(&["end"])?);
1971 continue;
1972 }
1973 let when = self.peek_span();
1974 if !self.eat_ident("when") {
1975 return Err(self.error(format!(
1976 "expected `when`, `else` or `end` in a case, found {}",
1977 self.peek()
1978 )));
1979 }
1980 self.skip_newlines();
1981 let pattern = self.expr(0)?;
1982 let body = self.body(&["when", "else", "end"])?;
1983 let test = list_at(
1987 when.merge(pattern.span),
1988 vec![sym_at(when, "equal?"), sym_at(when, subject_var), pattern],
1989 );
1990 arms.push(list_at(when.merge(body.span), vec![test, body]));
1991 }
1992 self.expect_ident("end")?;
1993
1994 if arms.is_empty() && otherwise.is_none() {
1995 return Err(self.error("a case needs at least one `when` or an `else`".to_string()));
1996 }
1997
1998 let mut cond = vec![sym_at(head, "cond")];
2003 cond.extend(arms);
2004 let else_span = else_kw.unwrap_or(head);
2011 let else_body = otherwise.unwrap_or_else(|| Spanned::new(else_span, SpannedForm::Nil));
2012 cond.push(list_at(
2013 else_span.merge(else_body.span),
2014 vec![sym_at(else_span, "else"), else_body],
2015 ));
2016
2017 let whole = self.span_since(head.start);
2018 let binding = list_at(
2019 head.merge(subject.span),
2020 vec![sym_at(head, subject_var), subject],
2021 );
2022 Ok(list_at(
2023 whole,
2024 vec![
2025 sym_at(head, "let"),
2026 list_at(binding.span, vec![binding]),
2027 list_at(whole, cond),
2028 ],
2029 ))
2030 }
2031
2032 fn arrow_lambda_ahead(&self) -> bool {
2035 let mut i = self.pos;
2036 let kind = |i: usize| self.toks.get(i).map(|t| &t.kind);
2037 if !matches!(kind(i), Some(TokenKind::RParen)) {
2038 loop {
2039 if matches!(kind(i), Some(TokenKind::Op(o)) if o == "*") {
2040 i += 1;
2041 }
2042 if !matches!(kind(i), Some(TokenKind::Ident(_))) {
2043 return false;
2044 }
2045 i += 1;
2046 match kind(i) {
2047 Some(TokenKind::Comma) => i += 1,
2048 Some(TokenKind::RParen) => break,
2049 _ => return false,
2050 }
2051 }
2052 }
2053 matches!(kind(i + 1), Some(TokenKind::Op(o)) if o == "->")
2054 }
2055
2056 fn arrow_lambda(&mut self, head: Span) -> Result<Spanned, Fail> {
2063 let mut params: Vec<Spanned> = Vec::new();
2064 while !self.at(&TokenKind::RParen) {
2065 let p_span = self.peek_span();
2066 if self.at_star() {
2067 params.extend(self.rest_parameter()?);
2068 break;
2069 }
2070 match self.bump() {
2071 TokenKind::Ident(p) => params.push(sym_at(p_span, &p)),
2072 other => {
2073 return Err(fail(
2074 format!("expected a parameter name, found {other}"),
2075 p_span,
2076 ))
2077 }
2078 }
2079 self.eat(&TokenKind::Comma);
2080 }
2081 self.bump();
2082 self.bump();
2083 self.skip_newlines();
2084 let params_span = cover(¶ms, head);
2085 let body = self.expr(0)?;
2086 Ok(list_at(
2087 self.span_since(head.start),
2088 vec![sym_at(head, "lambda"), list_at(params_span, params), body],
2089 ))
2090 }
2091
2092 fn rest_parameter(&mut self) -> Result<[Spanned; 2], Fail> {
2093 let star = self.peek_span();
2094 self.bump();
2095 let name_span = self.peek_span();
2096 match self.bump() {
2097 TokenKind::Ident(p) => {
2098 let whole = star.merge(name_span);
2099 Ok([sym_at(whole, "&rest"), sym_at(whole, &p)])
2100 }
2101 other => Err(fail(
2102 format!("expected a parameter name after `*`, found {other}"),
2103 name_span,
2104 )),
2105 }
2106 }
2107
2108 fn at_star(&self) -> bool {
2109 matches!(self.peek(), TokenKind::Op(o) if o == "*")
2110 }
2111
2112 fn rest_is_last(&mut self) -> Result<(), Fail> {
2113 self.skip_newlines();
2114 if self.at(&TokenKind::Comma) {
2115 return Err(self.error("a rest parameter `*name` comes last".to_string()));
2116 }
2117 Ok(())
2118 }
2119
2120 fn lambda_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2130 let mut params: Vec<Spanned> = Vec::new();
2131 if !self.at(&TokenKind::LParen) {
2132 self.note(Spelling::ParameterList, head, "fn", "fn()");
2133 }
2134 if self.at(&TokenKind::LParen) {
2135 self.bump();
2136 self.skip_newlines();
2137 if !self.eat(&TokenKind::RParen) {
2138 loop {
2139 self.skip_newlines();
2140 let p_span = self.peek_span();
2141 if self.at_star() {
2142 params.extend(self.rest_parameter()?);
2143 self.rest_is_last()?;
2144 self.expect(&TokenKind::RParen, "`)` after the rest parameter")?;
2145 break;
2146 }
2147 match self.bump() {
2148 TokenKind::Ident(p) => params.push(sym_at(p_span, &p)),
2149 other => {
2150 return Err(fail(
2151 format!("expected a parameter name, found {other}"),
2152 p_span,
2153 ))
2154 }
2155 }
2156 self.skip_newlines();
2157 if self.eat(&TokenKind::Comma) {
2158 if self.trailing_comma(&TokenKind::RParen) {
2159 break;
2160 }
2161 continue;
2162 }
2163 self.expect(&TokenKind::RParen, "`,` or `)`")?;
2164 break;
2165 }
2166 }
2167 }
2168 let params_span = cover(¶ms, head);
2169 let body = self.body(&["end"])?;
2170 self.expect_ident("end")?;
2171 Ok(list_at(
2172 self.span_since(head.start),
2173 vec![sym_at(head, "lambda"), list_at(params_span, params), body],
2174 ))
2175 }
2176
2177 fn test_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2183 let name_span = self.peek_span();
2184 let name = match self.bump() {
2185 TokenKind::Str(s) => s,
2186 other => {
2187 return Err(fail(
2188 format!("expected a string name after `test`, found {other}"),
2189 name_span,
2190 ))
2191 }
2192 };
2193 let body = self.body(&["end"])?;
2194 self.expect_ident("end")?;
2195 Ok(list_at(
2196 self.span_since(head.start),
2197 vec![
2198 sym_at(head, "deftest"),
2199 atom_at(name_span, Atom::Str(name)),
2200 body,
2201 ],
2202 ))
2203 }
2204
2205 fn assert_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2229 let e = self.expr(0)?;
2230 let quoted = Spanned::new(e.span, SpannedForm::Quote(Box::new(e.clone())));
2231 Ok(list_at(
2232 self.span_since(head.start),
2233 vec![sym_at(head, LOWERED_ASSERT), quoted, e],
2234 ))
2235 }
2236
2237 fn defmacro_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2246 let name_span = self.peek_span();
2247 let name = match self.bump() {
2248 TokenKind::Ident(n) => n,
2249 other => {
2250 return Err(fail(
2251 format!("expected a name after `defmacro`, found {other}"),
2252 name_span,
2253 ))
2254 }
2255 };
2256 self.not_reserved(&name, name_span, "a `defmacro` name")?;
2257 let mut params: Vec<Spanned> = Vec::new();
2258 if self.at(&TokenKind::LParen) {
2259 self.bump();
2260 self.skip_newlines();
2261 if !self.eat(&TokenKind::RParen) {
2262 loop {
2263 self.skip_newlines();
2264 let p_span = self.peek_span();
2265 match self.bump() {
2266 TokenKind::Ident(p) => params.push(sym_at(p_span, &p)),
2267 TokenKind::Label(p) => {
2268 return Err(self.error(format!(
2269 "macro parameter `{p}` cannot be typed: a macro receives \
2270 source forms, not values"
2271 )))
2272 }
2273 other => {
2274 return Err(fail(
2275 format!("expected a parameter name, found {other}"),
2276 p_span,
2277 ))
2278 }
2279 }
2280 self.skip_newlines();
2281 if self.eat(&TokenKind::Comma) {
2282 if self.trailing_comma(&TokenKind::RParen) {
2283 break;
2284 }
2285 continue;
2286 }
2287 self.expect(&TokenKind::RParen, "`,` or `)`")?;
2288 break;
2289 }
2290 }
2291 }
2292 if matches!(self.peek(), TokenKind::Op(o) if o == "->") {
2293 return Err(self.error(
2294 "a macro has no return type: it produces source forms, not values".to_string(),
2295 ));
2296 }
2297
2298 let params_span = cover(¶ms, name_span);
2299 let body = self.body(&["end"])?;
2300 self.expect_ident("end")?;
2301
2302 Ok(list_at(
2305 self.span_since(head.start),
2306 vec![
2307 sym_at(head, "defmacro"),
2308 sym_at(name_span, &name),
2309 list_at(params_span, params),
2310 body,
2311 ],
2312 ))
2313 }
2314
2315 fn quote_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2321 let body = self.body(&["end"])?;
2322 self.expect_ident("end")?;
2323 Ok(Spanned::new(
2332 self.span_since(head.start),
2333 SpannedForm::Quasiquote(Box::new(body)),
2334 ))
2335 }
2336
2337 fn unquote_form(&mut self, head: Span, splice: bool) -> Result<Spanned, Fail> {
2339 self.expect(&TokenKind::LParen, "`(` after unquote")?;
2340 self.skip_newlines();
2341 let inner = self.expr(0)?;
2342 self.skip_newlines();
2343 self.expect(&TokenKind::RParen, "`)`")?;
2344 let span = self.span_since(head.start);
2345 Ok(Spanned::new(
2346 span,
2347 if splice {
2348 SpannedForm::UnquoteSplice(Box::new(inner))
2349 } else {
2350 SpannedForm::Unquote(Box::new(inner))
2351 },
2352 ))
2353 }
2354
2355 fn protected_body(&mut self, head: Span) -> Result<Spanned, Fail> {
2356 let body = self.body(&["rescue", "ensure", "end"])?;
2357 if self.at_ident("rescue") || self.at_ident("ensure") {
2358 return self.rescue_tail(head, body);
2359 }
2360 Ok(body)
2361 }
2362
2363 fn begin_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2364 let body = self.body(&["rescue", "ensure", "end"])?;
2365 if !(self.at_ident("rescue") || self.at_ident("ensure")) {
2366 return Err(self.error(
2367 "a `begin` block protects its statements with `rescue` or `ensure`; without either, write the statements directly",
2368 ));
2369 }
2370 let tree = self.rescue_tail(head, body)?;
2371 self.expect_ident("end")?;
2372 Ok(Spanned::new(self.span_since(head.start), tree.form))
2373 }
2374
2375 fn rescue_tail(&mut self, head: Span, body: Spanned) -> Result<Spanned, Fail> {
2376 let mut clauses: Vec<Spanned> = Vec::new();
2377 let mut catch_all: Option<(Spanned, Spanned, Span)> = None;
2378 while self.at_ident("rescue") {
2379 let kw = self.peek_span();
2380 self.bump();
2381 if catch_all.is_some() {
2382 return Err(fail(
2383 "a `rescue NAME` catches every error, so it is the last `rescue`; put the kinds it would shadow above it",
2384 kw,
2385 ));
2386 }
2387 match self.peek().clone() {
2388 TokenKind::Sym(_) => {
2389 let mut kinds = Vec::new();
2390 loop {
2391 let k_span = self.peek_span();
2392 match self.bump() {
2393 TokenKind::Sym(k) => kinds.push(atom_at(k_span, Atom::Keyword(k))),
2394 other => {
2395 return Err(fail(
2396 format!("expected an error kind such as `:not_found`, found {other}"),
2397 k_span,
2398 ))
2399 }
2400 }
2401 if self.eat(&TokenKind::Comma) {
2402 self.skip_newlines();
2403 continue;
2404 }
2405 break;
2406 }
2407 let binder = if self.eat_ident("as") {
2408 let b_span = self.peek_span();
2409 match self.bump() {
2410 TokenKind::Ident(b) if !is_reserved_word(&b) => Some(sym_at(b_span, &b)),
2411 other => {
2412 return Err(fail(
2413 format!("expected the name the error is bound to after `as`, found {other}"),
2414 b_span,
2415 ))
2416 }
2417 }
2418 } else {
2419 None
2420 };
2421 let handler = self.body(&["rescue", "ensure", "end"])?;
2422 let mut test = vec![sym_at(kw, LOWERED_ERROR_MATCH), sym_at(kw, RESCUED)];
2423 test.extend(kinds);
2424 let test = list_at(cover(&test, kw), test);
2425 let handler = match binder {
2426 Some(b) => let_bound(b, handler, kw),
2427 None => handler,
2428 };
2429 clauses.push(list_at(kw.merge(handler.span), vec![test, handler]));
2430 }
2431 TokenKind::Ident(b) if !is_reserved_word(&b) => {
2432 let b_span = self.peek_span();
2433 self.bump();
2434 let handler = self.body(&["rescue", "ensure", "end"])?;
2435 catch_all = Some((sym_at(b_span, &b), handler, kw));
2436 }
2437 _ => {
2438 return Err(fail(
2439 "`rescue` names what it catches: `rescue :kind`, `rescue :a, :b as e`, or `rescue e` for every error (`rescue _` discards it)",
2440 kw,
2441 ))
2442 }
2443 }
2444 }
2445 let ensure = if self.at_ident("ensure") {
2446 self.bump();
2447 let cleanup = self.body(&["rescue", "end"])?;
2448 if self.at_ident("rescue") {
2449 return Err(self.error("`ensure` comes after every `rescue` of its block"));
2450 }
2451 Some(cleanup)
2452 } else {
2453 None
2454 };
2455 let whole = self.span_since(head.start);
2456 let protected = if clauses.is_empty() {
2457 match catch_all {
2458 None => body,
2459 Some((binder, handler, kw)) => list_at(
2460 whole,
2461 vec![
2462 sym_at(head, "try"),
2463 body,
2464 list_at(
2465 kw.merge(handler.span),
2466 vec![
2467 sym_at(kw, "catch"),
2468 list_at(binder.span, vec![binder]),
2469 handler,
2470 ],
2471 ),
2472 ],
2473 ),
2474 }
2475 } else {
2476 let first_kw = clauses[0].span;
2477 let otherwise = match catch_all {
2478 Some((binder, handler, kw)) => {
2479 let bound = let_bound(binder, handler, kw);
2480 list_at(kw.merge(bound.span), vec![sym_at(kw, "else"), bound])
2481 }
2482 None => list_at(
2483 head,
2484 vec![
2485 sym_at(head, "else"),
2486 list_at(
2487 head,
2488 vec![sym_at(head, LOWERED_RERAISE), sym_at(head, RESCUED)],
2489 ),
2490 ],
2491 ),
2492 };
2493 let mut cond = vec![sym_at(first_kw, "cond")];
2494 cond.extend(clauses);
2495 cond.push(otherwise);
2496 let cond_span = cover(&cond, first_kw);
2497 list_at(
2498 whole,
2499 vec![
2500 sym_at(head, "try"),
2501 body,
2502 list_at(
2503 cond_span,
2504 vec![
2505 sym_at(first_kw, "catch"),
2506 list_at(first_kw, vec![sym_at(first_kw, RESCUED)]),
2507 list_at(cond_span, cond),
2508 ],
2509 ),
2510 ],
2511 )
2512 };
2513 Ok(match ensure {
2514 None => protected,
2515 Some(cleanup) => {
2516 let attempted = list_at(
2517 protected.span,
2518 vec![sym_at(head, LOWERED_ATTEMPTED), protected],
2519 );
2520 let failed = list_at(
2521 head,
2522 vec![sym_at(head, LOWERED_FAILED), sym_at(head, RESCUED)],
2523 );
2524 let caught = list_at(
2525 whole,
2526 vec![
2527 sym_at(head, "try"),
2528 attempted,
2529 list_at(
2530 head,
2531 vec![
2532 sym_at(head, "catch"),
2533 list_at(head, vec![sym_at(head, RESCUED)]),
2534 failed,
2535 ],
2536 ),
2537 ],
2538 );
2539 list_at(
2540 whole,
2541 vec![sym_at(head, LOWERED_ENSURE), caught, thunk(cleanup, head)],
2542 )
2543 }
2544 })
2545 }
2546
2547 fn def_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2548 let name_span = self.peek_span();
2549 let name = match self.bump() {
2550 TokenKind::Ident(n) => n,
2551 other => {
2552 return Err(fail(
2553 format!("expected a name after `def`, found {other}"),
2554 name_span,
2555 ))
2556 }
2557 };
2558 self.not_reserved(&name, name_span, "a `def` name")?;
2559 let mut params: Vec<(String, Span, Option<Spanned>)> = Vec::new();
2560 let mut rest: Vec<Spanned> = Vec::new();
2561 if !self.at(&TokenKind::LParen) {
2562 self.note(
2563 Spelling::ParameterList,
2564 name_span,
2565 &format!("def {name}"),
2566 &format!("def {name}()"),
2567 );
2568 }
2569 if self.at(&TokenKind::LParen) {
2570 self.bump();
2571 self.skip_newlines();
2572 if !self.eat(&TokenKind::RParen) {
2573 loop {
2574 self.skip_newlines();
2575 let p_span = self.peek_span();
2576 if self.at_star() {
2577 rest = self.rest_parameter()?.to_vec();
2578 self.rest_is_last()?;
2579 self.expect(&TokenKind::RParen, "`)` after the rest parameter")?;
2580 break;
2581 }
2582 match self.bump() {
2583 TokenKind::Ident(p) => {
2585 self.not_reserved(&p, p_span, "a parameter name")?;
2586 params.push((p, p_span, None));
2587 }
2588 TokenKind::Label(p) => {
2590 self.not_reserved(&p, p_span, "a parameter name")?;
2591 self.skip_newlines();
2592 let ty = self.type_expr()?;
2593 params.push((p, p_span, Some(ty)));
2594 }
2595 other => {
2596 return Err(fail(
2597 format!("expected a parameter name, found {other}"),
2598 p_span,
2599 ))
2600 }
2601 }
2602 self.skip_newlines();
2603 if self.eat(&TokenKind::Comma) {
2604 if self.trailing_comma(&TokenKind::RParen) {
2605 break;
2606 }
2607 continue;
2608 }
2609 self.expect(&TokenKind::RParen, "`,` or `)`")?;
2610 break;
2611 }
2612 }
2613 }
2614 let sig_span = self.span_since(name_span.start);
2616
2617 let ret = if matches!(self.peek(), TokenKind::Op(o) if o == "->") {
2619 self.bump();
2620 self.skip_newlines();
2621 Some(self.type_expr()?)
2622 } else {
2623 None
2624 };
2625
2626 let begins = {
2627 let save = self.pos;
2628 self.skip_newlines();
2629 let at = self.at_ident("begin").then(|| self.peek_span());
2630 self.pos = save;
2631 at
2632 };
2633 let body = self.protected_body(head)?;
2634 if let Some(b) = begins {
2635 let protects = matches!(&body.form, SpannedForm::List(items)
2636 if matches!(items.first().and_then(Spanned::as_symbol), Some("try") | Some(LOWERED_ENSURE)));
2637 if protects && body.span.start == b.start {
2638 self.note(
2639 Spelling::TryCatch,
2640 b,
2641 "begin … end as a whole def body",
2642 "the clauses on the def itself",
2643 );
2644 }
2645 }
2646 self.expect_ident("end")?;
2647 let whole = self.span_since(head.start);
2648
2649 let annotated = ret.is_some() || params.iter().any(|(_, _, t)| t.is_some());
2650 if !annotated {
2651 let mut sig = vec![sym_at(name_span, &name)];
2652 sig.extend(params.into_iter().map(|(p, s, _)| sym_at(s, &p)));
2653 sig.extend(rest);
2654 return Ok(list_at(
2655 whole,
2656 vec![sym_at(head, "define"), list_at(sig_span, sig), body],
2657 ));
2658 }
2659
2660 let mut sig = vec![sym_at(name_span, &name)];
2668 for (p, s, t) in params {
2669 let ty = t.unwrap_or_else(|| sym_at(s, "dyn"));
2670 sig.push(list_at(s.merge(ty.span), vec![sym_at(s, &p), ty]));
2671 }
2672 sig.extend(rest);
2673 Ok(list_at(
2674 whole,
2675 vec![
2676 sym_at(head, "define-typed"),
2677 list_at(sig_span, sig),
2678 ret.unwrap_or_else(|| sym_at(head, "dyn")),
2679 body,
2680 ],
2681 ))
2682 }
2683
2684 fn type_expr(&mut self) -> Result<Spanned, Fail> {
2692 let name_span = self.peek_span();
2693 let name = match self.bump() {
2694 TokenKind::Ident(n) => n,
2695 other => {
2696 return Err(fail(
2697 format!("expected a type name, found {other}"),
2698 name_span,
2699 ))
2700 }
2701 };
2702 self.not_reserved(&name, name_span, "a type name")?;
2703 if self.at(&TokenKind::LParen) {
2704 self.bump();
2705 self.skip_newlines();
2706 let mut list = vec![sym_at(name_span, &name)];
2707 if !self.eat(&TokenKind::RParen) {
2708 loop {
2709 self.skip_newlines();
2710 list.push(self.type_expr()?);
2711 self.skip_newlines();
2712 if self.eat(&TokenKind::Comma) {
2713 if self.trailing_comma(&TokenKind::RParen) {
2714 break;
2715 }
2716 continue;
2717 }
2718 self.expect(&TokenKind::RParen, "`,` or `)`")?;
2719 break;
2720 }
2721 }
2722 return Ok(list_at(self.span_since(name_span.start), list));
2723 }
2724 Ok(sym_at(name_span, &name))
2725 }
2726
2727 fn not_reserved(&self, name: &str, at: Span, what: &str) -> Result<(), Fail> {
2730 if is_reserved_word(name) {
2731 return Err(fail(
2732 format!("`{name}` is a reserved word and cannot be {what}"),
2733 at,
2734 ));
2735 }
2736 Ok(())
2737 }
2738
2739 fn eat_ident(&mut self, name: &str) -> bool {
2741 if self.at_ident(name) {
2742 self.bump();
2743 true
2744 } else {
2745 false
2746 }
2747 }
2748
2749 fn expect_ident(&mut self, name: &str) -> Result<(), Fail> {
2750 if self.at_ident(name) {
2751 self.bump();
2752 Ok(())
2753 } else {
2754 Err(self.error(format!("expected `{name}`, found {}", self.peek())))
2755 }
2756 }
2757
2758 fn body(&mut self, terminators: &[&str]) -> Result<Spanned, Fail> {
2761 let start = self.mark();
2764 let mut forms = Vec::new();
2765 loop {
2766 self.skip_newlines();
2767 if matches!(self.peek(), TokenKind::Eof) {
2768 return Err(self.error(format!(
2769 "unterminated block: expected {} before the end of the file",
2770 one_of(terminators)
2771 )));
2772 }
2773 if terminators.iter().any(|t| self.at_ident(t)) {
2774 break;
2775 }
2776 forms.push(self.statement()?);
2777 self.end_of_statement(terminators)?;
2778 }
2779 Ok(match forms.len() {
2780 0 => Spanned::new(Span::new(start, start), SpannedForm::Nil),
2781 1 => forms.into_iter().next().expect("checked len"),
2782 _ => {
2783 let span = cover(&forms, Span::new(start, start));
2789 let head = Span::new(span.start, span.start);
2795 let mut list = vec![sym_at(head, "begin")];
2796 list.extend(forms);
2797 list_at(span, list)
2798 }
2799 })
2800 }
2801}
2802
2803fn one_of(words: &[&str]) -> String {
2806 let quoted: Vec<String> = words.iter().map(|w| format!("`{w}`")).collect();
2807 match quoted.split_last() {
2808 None => String::new(),
2809 Some((last, [])) => last.clone(),
2810 Some((last, rest)) => format!("{} or {last}", rest.join(", ")),
2811 }
2812}
2813
2814fn let_bound(binder: Spanned, body: Spanned, at: Span) -> Spanned {
2815 let span = at.merge(body.span);
2816 let pair = list_at(binder.span, vec![binder, sym_at(at, RESCUED)]);
2817 list_at(
2818 span,
2819 vec![sym_at(at, "let"), list_at(pair.span, vec![pair]), body],
2820 )
2821}
2822
2823fn thunk(body: Spanned, at: Span) -> Spanned {
2824 let span = at.merge(body.span);
2825 list_at(
2826 span,
2827 vec![sym_at(at, "lambda"), list_at(at, Vec::new()), body],
2828 )
2829}
2830
2831#[must_use]
2836pub fn label_name(name: &str) -> bool {
2837 matches!(
2838 lex(&format!("{name}:")).as_deref(),
2839 Ok([t, e]) if matches!(&t.kind, TokenKind::Label(n) if n == name) && e.kind == TokenKind::Eof
2840 )
2841}
2842
2843fn blue_lang_name(name: &str) -> bool {
2844 matches!(
2845 lex(name).as_deref(),
2846 Ok([t, e]) if matches!(&t.kind, TokenKind::Ident(n) if n == name) && e.kind == TokenKind::Eof
2847 ) && !name.ends_with(['?', '!'])
2848}
2849
2850fn mentions(form: &Spanned, name: &str) -> bool {
2851 match &form.form {
2852 SpannedForm::Atom(Atom::Symbol(s)) => s == name,
2853 SpannedForm::List(items) => items.iter().any(|i| mentions(i, name)),
2854 SpannedForm::Quote(i)
2855 | SpannedForm::Quasiquote(i)
2856 | SpannedForm::Unquote(i)
2857 | SpannedForm::UnquoteSplice(i) => mentions(i, name),
2858 _ => false,
2859 }
2860}
2861
2862fn sym_at(span: Span, s: &str) -> Spanned {
2864 Spanned::new(span, SpannedForm::Atom(Atom::Symbol(s.to_string())))
2865}
2866
2867fn atom_at(span: Span, a: Atom) -> Spanned {
2869 Spanned::new(span, SpannedForm::Atom(a))
2870}
2871
2872fn list_at(span: Span, items: Vec<Spanned>) -> Spanned {
2874 Spanned::new(span, SpannedForm::List(items))
2875}
2876
2877fn cover(items: &[Spanned], fallback: Span) -> Span {
2885 let merged = items
2886 .iter()
2887 .fold(Span::synthetic(), |acc, s| acc.merge(s.span));
2888 if merged.is_synthetic() {
2889 fallback
2890 } else {
2891 merged
2892 }
2893}
2894
2895#[cfg(test)]
2896mod tests {
2897 use super::*;
2898
2899 fn q(src: &str) -> String {
2903 parse_expr(src)
2904 .map(|s| s.to_string())
2905 .unwrap_or_else(|e| panic!("{src:?}: {e}"))
2906 }
2907
2908 #[test]
2911 fn arithmetic_respects_precedence() {
2912 assert_eq!(q("1 + 2 * 3"), "(+ 1 (* 2 3))");
2913 assert_eq!(q("(1 + 2) * 3"), "(* (+ 1 2) 3)");
2914 }
2915
2916 #[test]
2917 fn comparison_binds_looser_than_arithmetic() {
2918 assert_eq!(q("a + 1 < b"), "(< (+ a 1) b)");
2919 }
2920
2921 #[test]
2926 fn logical_operators_bind_loosest_and_lower_to_tataras_names() {
2927 assert_eq!(q("a && b || c"), "(or (and a b) c)");
2928 }
2929
2930 #[test]
2931 fn left_associativity() {
2932 assert_eq!(q("1 - 2 - 3"), "(- (- 1 2) 3)");
2933 }
2934
2935 #[test]
2936 fn a_dot_without_parentheses_reads_a_field() {
2937 assert_eq!(q("user.name"), "(blue-field user :name)");
2938 }
2939
2940 #[test]
2941 fn method_call_with_args() {
2942 assert_eq!(q("user.greet(1, 2)"), "(greet user 1 2)");
2943 }
2944
2945 #[test]
2946 fn chained_fields_read_left_to_right() {
2947 assert_eq!(q("a.b.c"), "(blue-field (blue-field a :b) :c)");
2948 assert_eq!(q("a.b(1).c"), "(blue-field (b a 1) :c)");
2949 }
2950
2951 #[test]
2952 fn plain_call() {
2953 assert_eq!(q("f(1, 2)"), "(f 1 2)");
2954 }
2955
2956 #[test]
2959 fn pipeline_threads_into_first_argument() {
2960 assert_eq!(q("x |> f"), "(f x)");
2961 assert_eq!(q("x |> f(1)"), "(f x 1)");
2962 assert_eq!(q("x |> f |> g"), "(g (f x))");
2963 }
2964
2965 #[test]
2966 fn pipeline_binds_looser_than_arithmetic() {
2967 assert_eq!(q("1 + 2 |> f"), "(f (+ 1 2))");
2968 }
2969
2970 #[test]
2976 fn label_and_rocket_produce_the_same_tree_for_a_symbol_key() {
2977 assert_eq!(q("{a: 1}"), q("{:a => 1}"));
2978 assert_eq!(q("{a: 1}"), "(hash-map :a 1)");
2979 }
2980
2981 #[test]
2984 fn a_string_key_has_no_shorthand() {
2985 assert_eq!(q(r#"{"k" => 1}"#), r#"(hash-map "k" 1)"#);
2986 }
2987
2988 #[test]
2989 fn list_literal() {
2990 assert_eq!(q("[1, 2, 3]"), "(list 1 2 3)");
2991 assert_eq!(q("[]"), "(list)");
2992 }
2993
2994 #[test]
2997 fn if_else_end() {
2998 assert_eq!(q("if a\n 1\nelse\n 2\nend"), "(if a 1 2)");
2999 }
3000
3001 #[test]
3002 fn if_without_else() {
3003 assert_eq!(q("if a\n 1\nend"), "(if a 1)");
3004 }
3005
3006 #[test]
3009 fn unless_is_a_negated_if() {
3010 assert_eq!(q("unless a\n 1\nend"), "(if (not a) 1)");
3011 }
3012
3013 #[test]
3014 fn multi_statement_body_becomes_begin() {
3015 assert_eq!(q("if a\n 1\n 2\nend"), "(if a (begin 1 2))");
3016 }
3017
3018 #[test]
3019 fn def_lowers_to_define() {
3020 assert_eq!(
3021 q("def add(a, b)\n a + b\nend"),
3022 "(define (add a b) (+ a b))"
3023 );
3024 }
3025
3026 #[test]
3027 fn def_with_no_params() {
3028 assert_eq!(q("def zero()\n 0\nend"), "(define (zero) 0)");
3029 }
3030
3031 #[test]
3034 fn literals_lower_to_atoms() {
3035 assert_eq!(q("42"), "42");
3036 assert_eq!(q("true"), "#t");
3037 assert_eq!(q(":ok"), ":ok");
3038 assert_eq!(q(r#""hi""#), r#""hi""#);
3039 }
3040
3041 #[test]
3042 fn unary_minus_and_not() {
3043 assert_eq!(q("-x"), "(- 0 x)");
3044 assert_eq!(q("!x"), "(not x)");
3045 }
3046
3047 #[test]
3050 fn a_program_is_a_sequence_of_forms() {
3051 let forms = parse_program("def f()\n 1\nend\nf()").expect("parse");
3052 assert_eq!(forms.len(), 2);
3053 assert_eq!(forms[1].to_string(), "(f)");
3054 }
3055
3056 #[test]
3057 fn unterminated_block_is_an_error_naming_what_was_expected() {
3058 let e = parse_program("if a\n 1").expect_err("must fail");
3059 assert!(e.message.contains("unterminated"), "{}", e.message);
3060 }
3061
3062 #[test]
3063 fn a_parse_error_carries_a_span_into_the_source() {
3064 let src = "1 + )";
3065 let e = parse_program(src).expect_err("must fail");
3066 assert!(e.span.start < src.len(), "span {:?} outside source", e.span);
3067 }
3068
3069 #[test]
3076 fn postfix_indexing_is_refused_naming_nth() {
3077 let e = parse_program("y = xs[0]").expect_err("must not split into two statements");
3078 assert!(e.message.contains("`nth(0, xs)`"), "{}", e.message);
3079 assert_eq!((e.line, e.col), (1, 7), "{e}");
3080 }
3081
3082 #[test]
3084 fn a_call_without_parentheses_is_refused_naming_the_call() {
3085 let e = parse_program("a = f x").expect_err("must not split into two statements");
3086 assert!(e.message.contains("`f(x)`"), "{}", e.message);
3087 assert_eq!((e.line, e.col), (1, 7), "{e}");
3088 }
3089
3090 #[test]
3092 fn two_statements_on_one_line_are_refused_in_a_body() {
3093 for src in [
3094 "def f(xs)\n xs[0]\nend",
3095 "if a\n f x\nend",
3096 "fn(x) x 1 end",
3097 "test \"t\"\n assert a b\nend",
3098 "1 2",
3099 ] {
3100 assert!(parse_program(src).is_err(), "{src:?} must not parse");
3101 }
3102 }
3103
3104 #[test]
3106 fn an_arrow_lambda_lowers_to_the_same_tree_as_fn() {
3107 for (arrow, block) in [
3108 ("(x) -> x * 2", "fn(x) x * 2 end"),
3109 ("(a, b) -> a + b", "fn(a, b) a + b end"),
3110 ("() -> 1", "fn() 1 end"),
3111 ("map(xs, (x) -> x + 1)", "map(xs, fn(x) x + 1 end)"),
3112 ] {
3113 let a = parse_program(arrow).expect(arrow);
3114 let b = parse_program(block).expect(block);
3115 assert_eq!(format!("{a:?}"), format!("{b:?}"), "{arrow}");
3116 }
3117 assert_eq!(
3118 parse_program("(x)").map(|p| p.len()).ok(),
3119 Some(1),
3120 "a parenthesised name is still a group"
3121 );
3122 }
3123
3124 #[test]
3128 fn a_statement_may_end_at_a_newline_the_end_or_a_closing_keyword() {
3129 for src in [
3130 "fn(x) x + 1 end",
3131 "map(fn(x) x end, xs)",
3132 "if a\n 1\nelse\n 2\nend",
3133 "if a 1 else 2 end",
3134 "case x\nwhen 1 :a\nwhen 2 :b\nelse :c\nend",
3135 "x = 1\ny = 2",
3136 "x = 1",
3137 ] {
3138 assert!(
3139 parse_program(src).is_ok(),
3140 "{src:?}: {:?}",
3141 parse_program(src)
3142 );
3143 }
3144 }
3145
3146 #[test]
3148 fn then_is_refused_by_name() {
3149 let e = parse_program("x = if c then 1 else 2 end").expect_err("then is not a keyword");
3150 assert!(e.message.contains("`then`"), "{}", e.message);
3151 }
3152
3153 #[test]
3155 fn a_brace_block_is_refused_naming_fn() {
3156 let e = parse_program("xs.map { |x| x }").expect_err("no brace blocks");
3157 assert!(e.message.contains("fn(x)"), "{}", e.message);
3158 }
3159
3160 #[test]
3167 fn a_qualified_name_is_one_symbol() {
3168 assert_eq!(q("retsu::first(xs)"), "(retsu/first xs)");
3169 assert_eq!(q("retsu::first"), "retsu/first");
3170 assert_eq!(q("xs |> retsu::first"), "(retsu/first xs)");
3171 assert_eq!(q("blue::length(xs)"), "(blue/length xs)");
3172 }
3173
3174 #[test]
3176 fn whitespace_around_the_qualifier_is_accepted() {
3177 assert_eq!(q("retsu :: first(xs)"), "(retsu/first xs)");
3178 }
3179
3180 #[test]
3183 fn a_reserved_word_after_the_qualifier_is_a_name() {
3184 assert_eq!(q("kueri::if(a, b)"), "(kueri/if a b)");
3185 assert_eq!(q("heni::test(x)"), "(heni/test x)");
3186 assert_eq!(q("sabi::fn"), "sabi/fn");
3187 }
3188
3189 #[test]
3190 fn a_qualified_name_has_exactly_one_qualifier() {
3191 for (src, says) in [
3192 ("::first(xs)", "`package::name`"),
3193 ("a::b::c", "one qualifier"),
3194 ("if::x", "reserved word"),
3195 ("retsu::1", "expected a name after `retsu::`"),
3196 ] {
3197 let e = parse_program(src).expect_err(src);
3198 assert!(e.message.contains(says), "{src}: {}", e.message);
3199 }
3200 }
3201
3202 #[test]
3205 fn qualified_reads_only_the_package_name_shape() {
3206 assert_eq!(qualified("retsu/first"), Some(("retsu", "first")));
3207 assert_eq!(qualified("kueri/if"), Some(("kueri", "if")));
3208 assert_eq!(qualified("nisshi/pair?"), Some(("nisshi", "pair?")));
3209 for not in ["/", "a/", "/a", "a/b/c", "if/x", "hash-map", "a"] {
3210 assert_eq!(qualified(not), None, "{not}");
3211 }
3212 assert!(is_callable_name("kueri/if"));
3213 }
3214
3215 #[test]
3220 fn a_symbol_may_end_in_a_question_or_bang() {
3221 assert_eq!(q("[:pair?, :set!]"), "(list :pair? :set!)");
3222 assert_eq!(q("use(\"n\", [:empty?])"), "(use \"n\" (list :empty?))");
3223 assert_eq!(q(":a != :b"), "(not= :a :b)");
3224 }
3225
3226 #[test]
3230 fn a_parse_error_displays_line_col_and_the_source_text() {
3231 let e = parse_program("x = 1\ny = [1, 2 y]").expect_err("must fail");
3232 assert_eq!(e.to_string(), "2:11: expected `,` or `]`, found `y`");
3233 assert_eq!(e.span, Span::new(16, 17), "the span stays, as data");
3234 }
3235
3236 #[test]
3238 fn no_error_message_names_a_rust_variant() {
3239 for src in [
3240 "f(1 2)",
3241 "{a: 1 b}",
3242 "def 1\nend",
3243 "x.1",
3244 "if a\n",
3245 "fn(1) 2 end",
3246 "[1 :b]",
3247 "1 +",
3248 "(1",
3249 ] {
3250 let e = parse_program(src).expect_err(src);
3251 let shown = e.to_string();
3252 for tell in [
3253 "Ident(", "Int(", "Op(", "Label(", "Sym(", "Str(", "LParen", "RBracket", "Eof",
3254 "Newline", "..",
3255 ] {
3256 assert!(!shown.contains(tell), "{src:?} -> {shown}");
3257 }
3258 assert!(e.line >= 1 && e.col >= 1, "{src:?} not located: {shown}");
3259 }
3260 }
3261
3262 #[test]
3264 fn a_lex_error_is_located_too() {
3265 let e = parse_program("x = 1\ny = 2; z = 3").expect_err("no semicolons");
3266 assert_eq!((e.line, e.col), (2, 6), "{e}");
3267 assert!(e.message.contains("new line"), "{e}");
3268 }
3269
3270 #[test]
3273 fn the_parser_rejects_garbage() {
3274 assert!(parse_program("def").is_err());
3275 assert!(parse_program("(1").is_err());
3276 assert!(parse_program("end").is_err());
3277 }
3278}