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| {
288 prefix
289 .trim_matches(|c: char| c.is_whitespace() || c == '\u{feff}')
290 .is_empty()
291 })
292}
293
294pub fn parse_expr(src: &str) -> Result<Sexp, ParseError> {
296 let forms = parse_program(src)?;
297 match forms.len() {
298 1 => Ok(forms.into_iter().next().expect("checked len")),
299 n => Err(ParseError::new(
300 format!("expected exactly one expression, found {n}"),
301 Span::new(0, src.len()),
302 )
303 .located(src)),
304 }
305}
306
307#[derive(Clone, Copy, Debug)]
317pub struct Infix {
318 pub op: &'static str,
320 pub power: (u8, u8),
322 pub callee: &'static str,
324 pub doc: &'static str,
328}
329
330pub const INFIX: &[Infix] = &[
334 Infix {
335 op: "or",
336 power: (1, 2),
337 callee: "or",
338 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).",
339 },
340 Infix {
341 op: "and",
342 power: (3, 4),
343 callee: "and",
344 doc: "Logical and: the right value if the left is truthy, otherwise the left; short-circuits. `&&` is read as `and` and written `and` (B0025).",
345 },
346 Infix {
357 op: "==",
358 power: (5, 6),
359 callee: "equal?",
360 doc: "Structural equality over every value (okite D0001): 4 == 4.0, lists and maps by content, nil only equals nil.",
361 },
362 Infix {
363 op: "!=",
364 power: (5, 6),
365 callee: "not=",
366 doc: "Exactly not(a == b) (okite D0002).",
367 },
368 Infix {
369 op: "<",
370 power: (5, 6),
371 callee: "<",
372 doc: "Numeric less-than. A string operand is a type error: order text with compare(a, b).",
373 },
374 Infix {
375 op: "<=",
376 power: (5, 6),
377 callee: "<=",
378 doc: "Numeric less-than-or-equal. Numbers only, like <.",
379 },
380 Infix {
381 op: ">",
382 power: (5, 6),
383 callee: ">",
384 doc: "Numeric greater-than. Numbers only, like <.",
385 },
386 Infix {
387 op: ">=",
388 power: (5, 6),
389 callee: ">=",
390 doc: "Numeric greater-than-or-equal. Numbers only, like <.",
391 },
392 Infix {
393 op: "+",
394 power: (7, 8),
395 callee: "+",
396 doc: "Numeric addition; an Int and a Float give a Float. Not string concatenation: interpolate instead.",
397 },
398 Infix {
399 op: "-",
400 power: (7, 8),
401 callee: "-",
402 doc: "Numeric subtraction. Written before an operand with nothing on its left, it is unary minus: -n.",
403 },
404 Infix {
405 op: "*",
406 power: (9, 10),
407 callee: "*",
408 doc: "Numeric multiplication.",
409 },
410 Infix {
411 op: "/",
412 power: (9, 10),
413 callee: "/",
414 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).",
415 },
416 Infix {
417 op: "%",
418 power: (9, 10),
419 callee: "mod",
420 doc: "Euclidean remainder: never negative, so (0 - 7) % 3 is 2, not -1. A zero divisor is a runtime error.",
421 },
422];
423
424pub const LOWERED_ASSERT: &str = "blue-assert";
447
448pub const LOWERED_MAP: &str = "hash-map";
456
457pub const LOWERED_CONCAT: &str = "concat";
462
463pub const OPTIONAL_MARKER: &str = "&optional";
464
465pub const LOWERED_OPTIONAL_ARITY: &str = "blue-optional-arity";
466
467pub const LOWERED_OPTIONAL_ARG: &str = "blue-optional-arg";
468
469pub const LOWERED_FIELD: &str = "blue-field";
470
471pub const LOWERED_MERGE: &str = "blue-merge";
472
473pub const LOWERED_SPLICE: &str = "blue-splice";
474
475pub const LOWERED_KWARGS: &str = "blue-kwargs";
476
477pub const LOWERED_ERROR_MATCH: &str = "blue-rescues?";
478
479pub const LOWERED_ENSURE: &str = "blue-ensure";
480
481pub const LOWERED_RERAISE: &str = "blue-reraise";
482
483pub const LOWERED_ATTEMPTED: &str = "blue-attempted";
484
485pub const LOWERED_FAILED: &str = "blue-failed";
486
487pub const LOWERED_HEADS: &[&str] = &[
488 "list", "concat", "not", "apply", "equal?", "not=", "and", "or", "mod",
489];
490
491pub const RESCUED: &str = "rescued-error";
492
493pub const SURFACE_KEYWORDS: &[&str] = &[
494 "if",
495 "begin",
496 "unless",
497 "def",
498 "defmacro",
499 "quote",
500 "unquote",
501 "unquote_splice",
502 "test",
503 "assert",
504 "fn",
505 "case",
506];
507
508pub const BLOCK_KEYWORDS: &[&str] = &[
520 "do", "end", "else", "elsif", "rescue", "ensure", "true", "false", "nil",
521];
522
523pub const OPERATOR_KEYWORDS: &[&str] = &["and", "or", "not"];
524
525pub fn is_reserved_word(name: &str) -> bool {
532 SURFACE_KEYWORDS.contains(&name)
533 || BLOCK_KEYWORDS.contains(&name)
534 || OPERATOR_KEYWORDS.contains(&name)
535}
536
537pub fn is_callable_name(name: &str) -> bool {
546 if qualified(name).is_some() {
548 return true;
549 }
550 !is_reserved_word(name)
551 && matches!(
552 lex(name).as_deref(),
553 Ok([t, e]) if matches!(&t.kind, TokenKind::Ident(n) if n == name)
554 && e.kind == TokenKind::Eof
555 )
556}
557
558pub const QUALIFIER: char = '/';
563
564pub const BUILTIN_QUALIFIER: &str = "blue";
567
568#[must_use]
570pub fn qualify(pkg: &str, name: &str) -> String {
571 format!("{pkg}{QUALIFIER}{name}")
572}
573
574#[must_use]
580pub fn qualified(sym: &str) -> Option<(&str, &str)> {
581 let (pkg, name) = sym.split_once(QUALIFIER)?;
582 let ident = |s: &str| {
583 matches!(
584 lex(s).as_deref(),
585 Ok([t, e]) if matches!(&t.kind, TokenKind::Ident(n) if n == s) && e.kind == TokenKind::Eof
586 )
587 };
588 let named = |s: &str| ident(s) || matches!(s, "true" | "false" | "nil");
589 (ident(pkg) && !is_reserved_word(pkg) && named(name)).then_some((pkg, name))
590}
591
592#[must_use]
600pub fn keyword_doc(word: &str) -> &'static str {
601 match word {
602 "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).",
603 "unless" => "`unless c … end` is read as `if not c` and written that way (B0026).",
604 "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`.",
605 "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 `|>`.",
606 "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.",
607 "and" => "`a and b`: logical and; `&&` is read as `and` and written `and` (B0025).",
608 "or" => "`a or b`: logical or; `||` is read as `or` and written `or` (B0025).",
609 "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).",
610 "def" => "`def name(a, b) … end`: a named function; its value is the last expression. Parameters may be typed: `def f(n: Int) -> Int`.",
611 "defmacro" => "`defmacro name(x) quote … unquote(x) … end end`: a macro, a rewrite of the unevaluated form.",
612 "quote" => "`quote … end`: the form itself as data, not its value. Used inside `defmacro`.",
613 "unquote" => "`unquote(x)`: inside `quote`, splice in the value of `x`.",
614 "unquote_splice" => "`unquote_splice(xs)`: inside `quote`, splice the items of the form `xs` in place, not the form itself.",
615 "test" => "`test \"what must hold\" … end`: a test block, run by `blue test` and ignored by `blue run`.",
616 "assert" => "`assert expr`: inside a test, fail it unless `expr` is truthy; the failure quotes the expression.",
617 "fn" => "`fn(x) x * 2 end`: an anonymous function, a value. Blue has no brace or do blocks.",
618 "case" => "`case v when 1 … when 2 … else … end`: compares with `==`; it does not destructure.",
619 "do" => "Reserved and refused: blue has no do-blocks. Pass `fn(x) … end` instead.",
620 "end" => "Closes `def`, `defmacro`, `if`, `case`, `fn`, `test` and `quote`.",
621 "else" => "The branch of `if` or `case` taken when nothing before it matched.",
622 "elsif" => "A further condition in an `if` chain; an `else` holding only an `if` is written `elsif` (B0027).",
623 "true" => "The boolean true.",
624 "false" => "The boolean false. Only `false` and `nil` are falsy; 0, \"\" and [] are truthy.",
625 "nil" => "The absent value. It is not `[]` (okite D0004), and only `nil == nil`.",
626 _ => "",
627 }
628}
629
630fn infix(op: &str) -> Option<&'static Infix> {
631 INFIX.iter().find(|i| i.op == op)
632}
633
634pub const SYMBOL_SPELLINGS: &[(&str, &str)] = &[("&&", "and"), ("||", "or"), ("!", "not")];
635
636fn word_for(op: &str) -> &str {
637 SYMBOL_SPELLINGS
638 .iter()
639 .find(|(sym, _)| *sym == op)
640 .map_or(op, |(_, word)| word)
641}
642
643fn infix_of(kind: &TokenKind) -> Option<&'static Infix> {
644 match kind {
645 TokenKind::Op(o) => infix(word_for(o)),
646 TokenKind::Ident(w) if w == "and" || w == "or" => infix(w),
647 _ => None,
648 }
649}
650
651const PIPE_POWER: (u8, u8) = (0, 1);
652const RESCUE_POWER: (u8, u8) = (0, 1);
653
654struct Parser {
655 toks: Vec<Token>,
656 pos: usize,
657 depth: usize,
665 max_depth: usize,
672 src: String,
673 comment_spans: Vec<Span>,
674 notes: Vec<Note>,
675}
676
677pub const MAX_EXPR_DEPTH: usize = 256;
684
685type Fail = Box<ParseError>;
697
698fn fail(message: impl Into<String>, span: Span) -> Fail {
699 Box::new(ParseError::new(message, span))
700}
701
702impl Parser {
703 fn over(src: &str, all: Vec<Token>, max_depth: usize) -> Self {
704 let comment_spans = all
705 .iter()
706 .filter(|t| matches!(t.kind, TokenKind::Comment(_)))
707 .map(|t| t.span)
708 .collect();
709 let toks = all
710 .into_iter()
711 .filter(|t| !matches!(t.kind, TokenKind::Comment(_)))
712 .collect();
713 Parser {
714 toks,
715 pos: 0,
716 depth: 0,
717 max_depth,
718 src: src.to_string(),
719 comment_spans,
720 notes: Vec::new(),
721 }
722 }
723
724 fn note(&mut self, spelling: Spelling, span: Span, written: &str, canonical: &str) {
725 self.notes.push(Note {
726 spelling,
727 span,
728 written: written.to_string(),
729 canonical: canonical.to_string(),
730 });
731 }
732
733 fn text(&self, span: Span) -> &str {
734 self.src.get(span.start..span.end).unwrap_or("")
735 }
736
737 fn comment_within(&self, lo: usize, hi: usize) -> bool {
738 self.comment_spans
739 .iter()
740 .any(|c| c.start >= lo && c.start < hi)
741 }
742
743 fn touches_previous(&self) -> bool {
744 let here = self.peek_span();
745 self.toks
746 .get(self.pos.wrapping_sub(1))
747 .is_some_and(|prev| prev.span.end == here.start)
748 }
749
750 fn peek(&self) -> &TokenKind {
751 &self.toks[self.pos.min(self.toks.len() - 1)].kind
752 }
753
754 fn peek_span(&self) -> Span {
755 self.toks[self.pos.min(self.toks.len() - 1)].span
756 }
757
758 fn bump(&mut self) -> TokenKind {
759 let k = self.toks[self.pos.min(self.toks.len() - 1)].kind.clone();
760 if self.pos < self.toks.len() {
761 self.pos += 1;
762 }
763 k
764 }
765
766 fn at(&self, k: &TokenKind) -> bool {
767 self.peek() == k
768 }
769
770 fn eat(&mut self, k: &TokenKind) -> bool {
771 if self.at(k) {
772 self.bump();
773 true
774 } else {
775 false
776 }
777 }
778
779 fn expect(&mut self, k: &TokenKind, what: &str) -> Result<(), Fail> {
780 if self.eat(k) {
781 Ok(())
782 } else {
783 Err(self.error(format!("expected {what}, found {}", self.peek())))
784 }
785 }
786
787 fn error(&self, message: impl Into<String>) -> Fail {
788 fail(message, self.peek_span())
789 }
790
791 fn skip_newlines(&mut self) {
793 while matches!(self.peek(), TokenKind::Newline) {
794 self.bump();
795 }
796 }
797
798 fn at_ident(&self, name: &str) -> bool {
799 matches!(self.peek(), TokenKind::Ident(n) if n == name)
800 }
801
802 fn mark(&self) -> usize {
805 self.peek_span().start
806 }
807
808 fn span_since(&self, start: usize) -> Span {
814 let end = self
815 .toks
816 .get(self.pos.saturating_sub(1))
817 .map_or(start, |t| t.span.end);
818 Span::new(start, end.max(start))
819 }
820
821 fn program(&mut self) -> Result<Vec<Spanned>, Fail> {
822 let mut out = Vec::new();
823 loop {
824 self.skip_newlines();
825 if matches!(self.peek(), TokenKind::Eof) {
826 break;
827 }
828 out.push(self.statement()?);
829 self.end_of_statement(&[])?;
830 }
831 Ok(out)
832 }
833
834 fn end_of_statement(&self, terminators: &[&str]) -> Result<(), Fail> {
852 let next = self.peek();
853 if matches!(next, TokenKind::Newline | TokenKind::Eof)
854 || terminators.iter().any(|t| self.at_ident(t))
855 {
856 return Ok(());
857 }
858 let prev = self.toks.get(self.pos.wrapping_sub(1)).map(|t| &t.kind);
859 let prev_name = match prev {
860 Some(TokenKind::Ident(n)) => Some(n.as_str()),
861 _ => None,
862 };
863 let callee = prev_name.unwrap_or("f");
864 let why = match (prev, next) {
865 (Some(TokenKind::Ident(t)), _) if t == "then" => {
866 "blue's `if` has no `then`: put the branch on the next line".to_string()
867 }
868 (_, TokenKind::Ident(d)) if d == "do" => "blue has no `do` blocks: pass a function \
869 as an argument, `f(xs, fn(x) … end)`"
870 .to_string(),
871 (_, TokenKind::LBracket) => {
872 let index = match (self.toks.get(self.pos + 1), self.toks.get(self.pos + 2)) {
873 (Some(i), Some(close)) if close.kind == TokenKind::RBracket => match &i.kind {
874 TokenKind::Int(n) => n.to_string(),
875 TokenKind::Ident(n) => n.clone(),
876 _ => "i".to_string(),
877 },
878 _ => "i".to_string(),
879 };
880 let list = prev_name.unwrap_or("xs");
881 format!(
882 "blue has no `xs[i]` indexing: write `nth({index}, {list})` \
883 (or `first({list})`, `last({list})`)"
884 )
885 }
886 (_, TokenKind::LBrace) => "blue has no brace blocks: pass a function as an \
887 argument, `map(xs, fn(x) … end)`"
888 .to_string(),
889 (
890 Some(TokenKind::Ident(_)),
891 TokenKind::Ident(_)
892 | TokenKind::Int(_)
893 | TokenKind::Float(_)
894 | TokenKind::Str(_)
895 | TokenKind::InterpolatedStr { .. }
896 | TokenKind::Sym(_)
897 | TokenKind::True
898 | TokenKind::False
899 | TokenKind::Nil,
900 ) => {
901 let arg = match next {
902 TokenKind::Ident(a) => a.clone(),
903 _ => "x".to_string(),
904 };
905 format!("a call needs parentheses: write `{callee}({arg})`")
906 }
907 _ => "put each statement on its own line".to_string(),
908 };
909 Err(self.error(format!("expected the end of the line, found {next}: {why}")))
910 }
911
912 fn statement(&mut self) -> Result<Spanned, Fail> {
925 let max = self.max_depth;
939 if self.depth >= max {
940 return Err(self.error(format!(
941 "statement nests deeper than {max}; refusing to \
942 recurse further (this is a limit, not a syntax error)"
943 )));
944 }
945 self.depth += 1;
946 let r = self.statement_inner();
947 self.depth -= 1;
948 r
949 }
950
951 fn statement_inner(&mut self) -> Result<Spanned, Fail> {
952 if let TokenKind::Ident(name) = self.peek().clone() {
953 if self.peek_at(1) == "=" && !is_reserved_word(&name) {
954 let name_span = self.peek_span();
955 self.bump(); self.bump(); self.skip_newlines();
958 let value = self.expr(0)?;
959 return Ok(list_at(
963 self.span_since(name_span.start),
964 vec![sym_at(name_span, "define"), sym_at(name_span, &name), value],
965 ));
966 }
967 }
968 let e = self.expr(0)?;
969 if let SpannedForm::List(items) = &e.form {
970 if items.len() == 3
971 && items[0].as_symbol() == Some("define")
972 && items[1].as_symbol().is_some()
973 && self.text(items[0].span) == "define"
974 {
975 self.note(Spelling::OperatorSpelling, e.span, "define(x, v)", "x = v");
976 }
977 }
978 Ok(e)
979 }
980
981 fn peek_at(&self, n: usize) -> String {
984 match self.toks.get(self.pos + n).map(|t| &t.kind) {
985 Some(TokenKind::Op(o)) => o.clone(),
986 _ => String::new(),
987 }
988 }
989
990 fn expr(&mut self, min_bp: u8) -> Result<Spanned, Fail> {
992 let max = self.max_depth;
1002 if self.depth >= max {
1003 return Err(self.error(format!(
1004 "expression nests deeper than {max}; refusing to \
1005 recurse further (this is a limit, not a syntax error)"
1006 )));
1007 }
1008 self.depth += 1;
1009 let r = self.expr_inner(min_bp);
1010 self.depth -= 1;
1011 r
1012 }
1013
1014 #[inline(never)]
1015 fn rescue_node(&self, start: usize, op_span: Span, lhs: Spanned, fallback: Spanned) -> Spanned {
1016 list_at(
1017 self.span_since(start),
1018 vec![
1019 sym_at(op_span, "try"),
1020 lhs,
1021 list_at(
1022 op_span.merge(fallback.span),
1023 vec![
1024 sym_at(op_span, "catch"),
1025 list_at(op_span, vec![sym_at(op_span, "_")]),
1026 fallback,
1027 ],
1028 ),
1029 ],
1030 )
1031 }
1032
1033 #[inline(never)]
1034 fn infix_spelling(&mut self, callee: Option<&str>, op_span: Span) {
1035 let written = self.text(op_span).to_string();
1036 match (callee, self.peek().clone()) {
1037 (None, _) => self.note(Spelling::CallForm, op_span, "|>", "a call"),
1038 (Some(c), TokenKind::Op(o)) if word_for(&o) != o => {
1039 self.note(Spelling::LogicalSymbol, op_span, &written, c);
1040 }
1041 (Some(_), TokenKind::Op(o)) if written != o => {
1042 self.note(Spelling::OperatorSpelling, op_span, &written, &o);
1043 }
1044 _ => {}
1045 }
1046 }
1047
1048 #[inline(never)]
1049 fn nil_test(
1050 &mut self,
1051 start: usize,
1052 op_span: Span,
1053 lhs: &Spanned,
1054 rhs: &Spanned,
1055 ) -> Option<Spanned> {
1056 let (subject, written) = match (&lhs.form, &rhs.form) {
1057 (_, SpannedForm::Nil) if !matches!(lhs.form, SpannedForm::Nil) => (lhs, "x == nil"),
1058 (SpannedForm::Nil, _) if !matches!(rhs.form, SpannedForm::Nil) => (rhs, "nil == x"),
1059 _ => return None,
1060 };
1061 let span = self.span_since(start);
1062 self.note(Spelling::NilTest, span, written, "x.nil?");
1063 Some(list_at(
1064 span,
1065 vec![
1066 sym_at(op_span, LOWERED_FIELD),
1067 subject.clone(),
1068 atom_at(op_span, Atom::Keyword("nil?".to_string())),
1069 ],
1070 ))
1071 }
1072
1073 #[inline(never)]
1074 fn infix_node(
1075 &mut self,
1076 start: usize,
1077 callee: Option<&str>,
1078 op_span: Span,
1079 lhs: Spanned,
1080 rhs: Spanned,
1081 ) -> Spanned {
1082 if callee == Some("-") && matches!(lhs.form, SpannedForm::Atom(Atom::Int(0))) {
1083 self.note(
1084 Spelling::OperatorSpelling,
1085 lhs.span.merge(op_span),
1086 "0 - x",
1087 "-x",
1088 );
1089 }
1090
1091 if callee == Some("equal?") {
1092 if let Some(test) = self.nil_test(start, op_span, &lhs, &rhs) {
1093 return test;
1094 }
1095 }
1096
1097 if callee.is_none() {
1098 let rhs_span = rhs.span;
1103 let threaded = match rhs.form {
1104 SpannedForm::List(mut items) if !items.is_empty() => {
1105 items.insert(1, lhs);
1106 list_at(self.span_since(start), items)
1107 }
1108 callee => list_at(
1109 self.span_since(start),
1110 vec![Spanned::new(rhs_span, callee), lhs],
1111 ),
1112 };
1113 match &threaded.form {
1114 SpannedForm::List(items)
1115 if items.len() == 2 && items[0].as_symbol() == Some("not") =>
1116 {
1117 let (head, operand) = (items[0].span, items[1].clone());
1118 self.negation(head, threaded.span, operand)
1119 }
1120 SpannedForm::List(items)
1121 if items.len() == 4
1122 && items[0].as_symbol() == Some("if")
1123 && matches!(items[3].form, SpannedForm::Nil) =>
1124 {
1125 list_at(threaded.span, items[..3].to_vec())
1126 }
1127 SpannedForm::List(items)
1128 if items.len() == 3 && items[0].as_symbol() == Some("equal?") =>
1129 {
1130 let (op, a, b) = (items[0].span, items[1].clone(), items[2].clone());
1131 match self.nil_test(start, op, &a, &b) {
1132 Some(test) => test,
1133 None => threaded,
1134 }
1135 }
1136 _ => threaded,
1137 }
1138 } else {
1139 list_at(
1145 self.span_since(start),
1146 vec![sym_at(op_span, callee.unwrap_or_default()), lhs, rhs],
1147 )
1148 }
1149 }
1150
1151 fn expr_inner(&mut self, min_bp: u8) -> Result<Spanned, Fail> {
1152 let start = self.mark();
1153 let mut lhs = self.prefix()?;
1154
1155 loop {
1156 match self.peek() {
1158 TokenKind::Dot => {
1159 let dot = self.peek_span();
1160 self.bump();
1161 lhs = self.finish_send(start, lhs, dot)?;
1162 continue;
1163 }
1164 TokenKind::LParen => {
1165 let (args, splat) = self.call_args()?;
1166 let call = self.call_node(self.span_since(start), vec![lhs], args, splat);
1167 lhs = self.call_spelling(call);
1168 continue;
1169 }
1170 _ => {}
1171 }
1172
1173 let op_span = self.peek_span();
1177 if self.at_ident("rescue") {
1178 if RESCUE_POWER.0 < min_bp {
1179 break;
1180 }
1181 self.bump();
1182 self.skip_newlines();
1183 let fallback = self.expr(RESCUE_POWER.1)?;
1184 lhs = self.rescue_node(start, op_span, lhs, fallback);
1185 continue;
1186 }
1187 let (callee, (lbp, rbp)) = match self.peek() {
1188 TokenKind::Pipe => (None, PIPE_POWER),
1189 other => match infix_of(other) {
1190 Some(i) => (Some(i.callee), i.power),
1191 None => break,
1192 },
1193 };
1194 if lbp < min_bp {
1195 break;
1196 }
1197 self.infix_spelling(callee, op_span);
1198 self.bump();
1199 self.skip_newlines();
1200 let rhs = self.expr(rbp)?;
1201 lhs = self.infix_node(start, callee, op_span, lhs, rhs);
1202 }
1203
1204 Ok(lhs)
1205 }
1206
1207 #[inline(never)]
1208 fn interpolation(span: Span, parts: &[String], exprs: &[String]) -> Result<Spanned, Fail> {
1209 let mut acc = atom_at(span, Atom::Str(parts[0].clone()));
1219 for (i, raw) in exprs.iter().enumerate() {
1220 let inner = parse_expr(raw).map_err(|e| {
1221 fail(
1222 format!("in interpolation `#{{{raw}}}`: {}", e.message),
1223 span,
1224 )
1225 })?;
1226 let inner = Spanned::from_sexp_at(&inner, span);
1227 acc = list_at(span, vec![sym_at(span, LOWERED_CONCAT), acc, inner]);
1228 acc = list_at(
1231 span,
1232 vec![
1233 sym_at(span, LOWERED_CONCAT),
1234 acc,
1235 atom_at(span, Atom::Str(parts[i + 1].clone())),
1236 ],
1237 );
1238 }
1239 Ok(acc)
1240 }
1241
1242 #[inline(never)]
1243 fn not_prefix(&mut self, span: Span) -> Result<Spanned, Fail> {
1244 if self.at(&TokenKind::LParen) && self.touches_previous() {
1245 self.note(Spelling::OperatorSpelling, span, "not(x)", "not x");
1246 let (args, splat) = self.call_args()?;
1247 if args.len() != 1 || splat.is_some() {
1248 let call = self.call_node(
1249 self.span_since(span.start),
1250 vec![sym_at(span, "not")],
1251 args,
1252 splat,
1253 );
1254 return Ok(call);
1255 }
1256 let operand = args.into_iter().next().expect("one argument");
1257 return Ok(self.negation(span, self.span_since(span.start), operand));
1258 }
1259 let rhs = self.expr(11)?;
1260 Ok(self.negation(span, self.span_since(span.start), rhs))
1261 }
1262
1263 fn prefix(&mut self) -> Result<Spanned, Fail> {
1264 if self.at(&TokenKind::LParen) {
1265 let span = self.peek_span();
1266 self.bump();
1267 if self.arrow_lambda_ahead() {
1268 return self.arrow_lambda(span);
1269 }
1270 return self.group();
1271 }
1272 self.prefix_token()
1273 }
1274
1275 fn group(&mut self) -> Result<Spanned, Fail> {
1276 self.skip_newlines();
1277 let inner = self.expr(0)?;
1278 self.skip_newlines();
1279 self.expect(&TokenKind::RParen, "`)`")?;
1280 Ok(inner)
1281 }
1282
1283 #[inline(never)]
1284 fn prefix_token(&mut self) -> Result<Spanned, Fail> {
1285 let span = self.peek_span();
1286 match self.bump() {
1287 TokenKind::Int(v) => Ok(atom_at(span, Atom::Int(v))),
1288 TokenKind::Float(v) => Ok(atom_at(span, Atom::Float(v))),
1289 TokenKind::Str(s) => Ok(atom_at(span, Atom::Str(s))),
1290
1291 TokenKind::InterpolatedStr { parts, exprs } => Self::interpolation(span, &parts, &exprs),
1302 TokenKind::Sym(s) => Ok(atom_at(span, Atom::Keyword(s))),
1303 TokenKind::True => Ok(atom_at(span, Atom::Bool(true))),
1304 TokenKind::False => Ok(atom_at(span, Atom::Bool(false))),
1305 TokenKind::Nil => Ok(Spanned::new(span, SpannedForm::Nil)),
1306
1307 TokenKind::Op(o) if o == "-" => {
1308 let rhs = self.expr(11)?; Ok(list_at(
1310 self.span_since(span.start),
1311 vec![sym_at(span, "-"), atom_at(span, Atom::Int(0)), rhs],
1312 ))
1313 }
1314 TokenKind::Op(o) if o == "!" => {
1315 let written = self.text(span).to_string();
1316 self.note(Spelling::LogicalSymbol, span, &written, "not");
1317 let rhs = self.expr(11)?;
1318 Ok(self.negation(span, self.span_since(span.start), rhs))
1319 }
1320 TokenKind::Ident(w) if w == "not" => self.not_prefix(span),
1321 TokenKind::Ident(w) if (w == "and" || w == "or") && self.at(&TokenKind::LParen) => {
1322 Ok(sym_at(span, &w))
1323 }
1324 TokenKind::Ident(w) if w == "and" || w == "or" => Err(fail(
1325 format!("`{w}` joins two expressions: `a {w} b`"),
1326 span,
1327 )),
1328
1329 TokenKind::LParen => {
1330 self.skip_newlines();
1331 let inner = self.expr(0)?;
1332 self.skip_newlines();
1333 self.expect(&TokenKind::RParen, "`)`")?;
1334 Ok(inner)
1338 }
1339
1340 TokenKind::LBracket => self.list_literal(span),
1341 TokenKind::LBrace => self.map_literal(span),
1342
1343 TokenKind::Ident(name) if self.at(&TokenKind::PathSep) => {
1344 self.qualified_name(span, &name)
1345 }
1346 TokenKind::PathSep => Err(fail(
1347 "a qualified name is `package::name`, with the package written before `::`",
1348 span,
1349 )),
1350 TokenKind::Ident(name) => match name.as_str() {
1351 "if" => self.if_form(span, false),
1352 "begin" if !(self.at(&TokenKind::LParen) && self.touches_previous()) => {
1353 self.begin_form(span)
1354 }
1355 "rescue" | "ensure" => Err(fail(
1356 format!("`{name}` belongs to a `begin … end` block or a `def` body, after the statements it protects"),
1357 span,
1358 )),
1359 "unless" => {
1360 self.note(Spelling::Unless, span, "unless", "if not");
1361 self.if_form(span, true)
1362 }
1363 "def" => self.def_form(span),
1364 "defmacro" => self.defmacro_form(span),
1365 "case" => self.case_form(span),
1366 "fn" => self.lambda_form(span),
1367 "test" => self.test_form(span),
1368 "assert" => self.assert_form(span),
1369 "quote" => self.quote_form(span),
1370 "unquote" => self.unquote_form(span, false),
1371 "unquote_splice" => self.unquote_form(span, true),
1372 "do" => Err(fail(
1373 "blue has no `do` blocks: pass a function as an argument, \
1374 `f(xs, fn(x) … end)`",
1375 span,
1376 )),
1377 "end" => Err(fail(
1378 "unexpected `end`: nothing open here for it to close",
1379 span,
1380 )),
1381 _ => Ok(sym_at(span, &name)),
1382 },
1383
1384 other => Err(fail(format!("expected an expression, found {other}"), span)),
1385 }
1386 }
1387
1388 fn qualified_name(&mut self, pkg_span: Span, pkg: &str) -> Result<Spanned, Fail> {
1398 if is_reserved_word(pkg) {
1399 return Err(fail(
1400 format!("`{pkg}` is a reserved word and cannot name a package"),
1401 pkg_span,
1402 ));
1403 }
1404 self.bump(); let name_span = self.peek_span();
1406 let name = match self.bump() {
1407 TokenKind::Ident(n) => n,
1408 TokenKind::True => "true".to_string(),
1410 TokenKind::False => "false".to_string(),
1411 TokenKind::Nil => "nil".to_string(),
1412 other => {
1413 return Err(fail(
1414 format!("expected a name after `{pkg}::`, found {other}"),
1415 name_span,
1416 ))
1417 }
1418 };
1419 if self.at(&TokenKind::PathSep) {
1420 return Err(self.error(format!(
1421 "a qualified name has one qualifier: `package::name`, and `{pkg}::{name}::…` has two"
1422 )));
1423 }
1424 Ok(sym_at(
1425 self.span_since(pkg_span.start),
1426 &qualify(pkg, &name),
1427 ))
1428 }
1429
1430 #[inline(never)]
1437 fn negation(&mut self, head: Span, whole: Span, operand: Spanned) -> Spanned {
1438 if let SpannedForm::List(items) = &operand.form {
1439 let flipped = match items.first().and_then(Spanned::as_symbol) {
1440 Some("equal?") => Some("not="),
1441 Some("not=") => Some("equal?"),
1442 _ => None,
1443 };
1444 if let (Some(to), 3) = (flipped, items.len()) {
1445 let (written, canonical) = if to == "not=" {
1446 ("not (a == b)", "a != b")
1447 } else {
1448 ("not (a != b)", "a == b")
1449 };
1450 self.note(Spelling::NegatedComparison, whole, written, canonical);
1451 let mut out = items.clone();
1452 out[0] = sym_at(items[0].span, to);
1453 if to == "equal?" {
1454 if let Some(test) =
1455 self.nil_test(whole.start, items[0].span, &items[1], &items[2])
1456 {
1457 return test;
1458 }
1459 }
1460 return list_at(whole, out);
1461 }
1462 }
1463 list_at(whole, vec![sym_at(head, "not"), operand])
1464 }
1465
1466 #[inline(never)]
1467 fn call_spelling(&mut self, call: Spanned) -> Spanned {
1468 let SpannedForm::List(items) = &call.form else {
1469 return call;
1470 };
1471 let Some(head) = items.first().and_then(Spanned::as_symbol) else {
1472 return call;
1473 };
1474 let argc = items.len() - 1;
1475 if head == "equal?" && argc == 2 {
1476 let (op_span, start) = (items[0].span, call.span.start);
1477 if let Some(test) = self.nil_test(start, op_span, &items[1], &items[2]) {
1478 return test;
1479 }
1480 }
1481 let operator = match (head, argc) {
1482 ("and" | "or" | "mod" | "equal?", 2) => Some(head),
1483 ("list", _) => Some("list"),
1484 _ => None,
1485 };
1486 if let Some(op) = operator {
1487 let canonical = match op {
1488 "list" => "[…]".to_string(),
1489 "mod" => "a % b".to_string(),
1490 "equal?" => "a == b".to_string(),
1491 other => format!("a {other} b"),
1492 };
1493 self.note(
1494 Spelling::OperatorSpelling,
1495 call.span,
1496 &format!("{head}(…)"),
1497 &canonical,
1498 );
1499 return call;
1500 }
1501 if head == "apply"
1502 && self.text(items[0].span) == "apply"
1503 && argc >= 2
1504 && items[1].as_symbol().is_some_and(is_callable_name)
1505 {
1506 self.note(Spelling::Apply, call.span, "apply(f, …, xs)", "f(…, *xs)");
1507 return call;
1508 }
1509 if head == "try" {
1510 return self.try_spelling(call);
1511 }
1512 call
1513 }
1514
1515 fn try_spelling(&mut self, call: Spanned) -> Spanned {
1516 let SpannedForm::List(items) = &call.form else {
1517 return call;
1518 };
1519 let Some((catch, body)) = items[1..].split_last() else {
1520 return call;
1521 };
1522 let Some(clause) = catch.as_list() else {
1523 return call;
1524 };
1525 let binder = match clause {
1526 [head, binding, handler @ ..]
1527 if head.as_symbol() == Some("catch") && !handler.is_empty() =>
1528 {
1529 match binding.as_list() {
1530 Some([b]) => b.as_symbol().map(|n| (n.to_string(), b.span)),
1531 _ => None,
1532 }
1533 }
1534 _ => None,
1535 };
1536 let Some((name, binder_span)) = binder else {
1537 return call;
1538 };
1539 if body.is_empty() {
1540 return call;
1541 }
1542 self.note(Spelling::TryCatch, call.span, "try(…, catch(…))", "rescue");
1543 let handler = &clause[2..];
1544 let rename =
1545 name.starts_with('_') && name != "_" && !handler.iter().any(|h| mentions(h, &name));
1546 let body = if body.len() == 1 {
1547 body[0].clone()
1548 } else {
1549 let span = cover(body, call.span);
1550 let mut list = vec![sym_at(Span::new(span.start, span.start), "begin")];
1551 list.extend(body.iter().cloned());
1552 list_at(span, list)
1553 };
1554 let handler = if handler.len() == 1 {
1555 handler[0].clone()
1556 } else {
1557 let span = cover(handler, catch.span);
1558 let mut list = vec![sym_at(Span::new(span.start, span.start), "begin")];
1559 list.extend(handler.iter().cloned());
1560 list_at(span, list)
1561 };
1562 let binder = sym_at(binder_span, if rename { "_" } else { &name });
1563 list_at(
1564 call.span,
1565 vec![
1566 items[0].clone(),
1567 body,
1568 list_at(
1569 catch.span,
1570 vec![
1571 clause[0].clone(),
1572 list_at(clause[1].span, vec![binder]),
1573 handler,
1574 ],
1575 ),
1576 ],
1577 )
1578 }
1579
1580 fn finish_send(&mut self, start: usize, recv: Spanned, dot: Span) -> Result<Spanned, Fail> {
1581 let name_span = self.peek_span();
1582 let name = match self.bump() {
1583 TokenKind::Ident(n) if is_reserved_word(&n) => {
1597 return Err(self.error(format!(
1598 "`{n}` is a reserved word and cannot be a method name — \
1599 `recv.{n}` would lower onto the `{n}` form itself"
1600 )))
1601 }
1602 TokenKind::Ident(n) => n,
1603 other => {
1604 return Err(fail(
1605 format!("expected a method name after `.`, found {other}"),
1606 name_span,
1607 ))
1608 }
1609 };
1610 if !self.at(&TokenKind::LParen) {
1611 return Ok(list_at(
1612 self.span_since(start),
1613 vec![
1614 sym_at(dot, LOWERED_FIELD),
1615 recv,
1616 atom_at(name_span, Atom::Keyword(name)),
1617 ],
1618 ));
1619 }
1620 self.note(
1621 Spelling::CallForm,
1622 dot.merge(name_span),
1623 &format!("x.{name}(…)"),
1624 &format!("{name}(x, …)"),
1625 );
1626 let (args, splat) = self.call_args()?;
1627 Ok(self.call_node(
1628 self.span_since(start),
1629 vec![sym_at(name_span, &name), recv],
1630 args,
1631 splat,
1632 ))
1633 }
1634
1635 fn call_node(
1636 &self,
1637 span: Span,
1638 head: Vec<Spanned>,
1639 args: Vec<Spanned>,
1640 splat: Option<Span>,
1641 ) -> Spanned {
1642 let mut list = Vec::with_capacity(head.len() + args.len() + 1);
1643 if let Some(at) = splat {
1644 list.push(sym_at(at, "apply"));
1645 }
1646 list.extend(head);
1647 list.extend(args);
1648 list_at(span, list)
1649 }
1650
1651 fn call_args(&mut self) -> Result<(Vec<Spanned>, Option<Span>), Fail> {
1652 self.expect(&TokenKind::LParen, "`(`")?;
1653 let mut args = Vec::new();
1654 let mut splat = None;
1655 self.skip_newlines();
1656 if self.eat(&TokenKind::RParen) {
1657 return Ok((args, splat));
1658 }
1659 loop {
1660 self.skip_newlines();
1661 if splat.is_some() {
1662 return Err(
1663 self.error("a splat `*xs` passes a list as the LAST arguments: write it last")
1664 );
1665 }
1666 if self.at_spread() || matches!(self.peek(), TokenKind::Label(_)) {
1667 let open = self.peek_span();
1668 let (segments, spread) = self.map_entries(open, &TokenKind::RParen)?;
1669 if !spread {
1670 return Err(fail(
1671 "a keyword entry in a call needs a `**` spread: pass a map, f({a: 1})",
1672 open,
1673 ));
1674 }
1675 args.push(self.merged(open, LOWERED_KWARGS, segments));
1676 return Ok((args, splat));
1677 }
1678 if matches!(self.peek(), TokenKind::Op(o) if o == "*") {
1679 splat = Some(self.peek_span());
1680 self.bump();
1681 }
1682 args.push(self.expr(0)?);
1683 self.skip_newlines();
1684 if self.eat(&TokenKind::Comma) {
1685 if self.trailing_comma(&TokenKind::RParen) {
1686 break;
1687 }
1688 continue;
1689 }
1690 self.expect(&TokenKind::RParen, "`,` or `)`")?;
1691 break;
1692 }
1693 Ok((args, splat))
1694 }
1695
1696 fn trailing_comma(&mut self, closer: &TokenKind) -> bool {
1697 let comma = self
1698 .toks
1699 .get(self.pos.wrapping_sub(1))
1700 .map_or(self.peek_span(), |t| t.span);
1701 self.skip_newlines();
1702 if self.at(closer) {
1703 self.note(Spelling::TrailingComma, comma, ",", "");
1704 self.bump();
1705 return true;
1706 }
1707 false
1708 }
1709
1710 fn list_literal(&mut self, open: Span) -> Result<Spanned, Fail> {
1711 let mut items = vec![sym_at(open, "list")];
1712 self.skip_newlines();
1713 if self.eat(&TokenKind::RBracket) {
1714 return Ok(list_at(self.span_since(open.start), items));
1715 }
1716 loop {
1717 self.skip_newlines();
1718 items.push(self.expr(0)?);
1719 self.skip_newlines();
1720 if self.eat(&TokenKind::Comma) {
1721 if self.trailing_comma(&TokenKind::RBracket) {
1722 break;
1723 }
1724 continue;
1725 }
1726 self.expect(&TokenKind::RBracket, "`,` or `]`")?;
1727 break;
1728 }
1729 Ok(list_at(self.span_since(open.start), items))
1730 }
1731
1732 fn map_literal(&mut self, open: Span) -> Result<Spanned, Fail> {
1739 self.skip_newlines();
1740 if self.eat(&TokenKind::RBrace) {
1741 return Ok(list_at(
1742 self.span_since(open.start),
1743 vec![sym_at(open, LOWERED_MAP)],
1744 ));
1745 }
1746 let (mut segments, spread) = self.map_entries(open, &TokenKind::RBrace)?;
1747 if !spread {
1748 return Ok(segments.remove(0));
1749 }
1750 Ok(self.merged(open, LOWERED_MERGE, segments))
1751 }
1752
1753 fn merged(&self, open: Span, head: &str, segments: Vec<Spanned>) -> Spanned {
1754 let mut items = vec![sym_at(open, head)];
1755 items.extend(segments);
1756 list_at(self.span_since(open.start), items)
1757 }
1758
1759 fn at_spread(&self) -> bool {
1760 matches!(self.peek(), TokenKind::Op(o) if o == "**")
1761 }
1762
1763 fn map_entries(
1764 &mut self,
1765 open: Span,
1766 closer: &TokenKind,
1767 ) -> Result<(Vec<Spanned>, bool), Fail> {
1768 let mut segments = Vec::new();
1769 let mut items = vec![sym_at(open, LOWERED_MAP)];
1770 let mut spread = false;
1771 loop {
1772 self.skip_newlines();
1773 if self.at_spread() {
1774 let star = self.peek_span();
1775 self.bump();
1776 let value = self.expr(0)?;
1777 if items.len() > 1 {
1778 let span = items[1].span.merge(items[items.len() - 1].span);
1779 segments.push(list_at(span, std::mem::take(&mut items)));
1780 items = vec![sym_at(open, LOWERED_MAP)];
1781 }
1782 segments.push(list_at(
1783 star.merge(value.span),
1784 vec![sym_at(star, LOWERED_SPLICE), value],
1785 ));
1786 spread = true;
1787 } else {
1788 self.map_entry(&mut items, closer)?;
1789 }
1790 self.skip_newlines();
1791 if self.eat(&TokenKind::Comma) {
1792 if self.trailing_comma(closer) {
1793 break;
1794 }
1795 continue;
1796 }
1797 self.expect(closer, "`,` or the end of the entries")?;
1798 break;
1799 }
1800 if items.len() > 1 || !spread {
1801 let whole = self.span_since(open.start);
1802 segments.push(list_at(whole, items));
1803 }
1804 Ok((segments, spread))
1805 }
1806
1807 fn map_entry(&mut self, items: &mut Vec<Spanned>, closer: &TokenKind) -> Result<(), Fail> {
1808 match self.peek().clone() {
1809 TokenKind::Label(name) => {
1810 let label_span = self.peek_span();
1811 self.bump();
1812 self.skip_newlines();
1813 items.push(atom_at(label_span, Atom::Keyword(name.clone())));
1814 if matches!(self.peek(), TokenKind::Comma) || self.at(closer) {
1815 items.push(sym_at(label_span, &name));
1816 } else {
1817 let value = self.expr(0)?;
1818 if matches!(&value.form, SpannedForm::Atom(Atom::Symbol(s)) if *s == name)
1819 && label_name(&name)
1820 {
1821 let written = format!("{name}: {name}");
1822 let canonical = format!("{name}:");
1823 self.note(
1824 Spelling::MapPun,
1825 label_span.merge(value.span),
1826 &written,
1827 &canonical,
1828 );
1829 }
1830 items.push(value);
1831 }
1832 }
1833 _ => {
1834 let k = self.expr(0)?;
1835 self.skip_newlines();
1836 if let SpannedForm::Atom(Atom::Keyword(name)) = &k.form {
1837 if blue_lang_name(name) {
1838 let written = format!(":{name} =>");
1839 let canonical = format!("{name}:");
1840 self.note(Spelling::KeywordRocket, k.span, &written, &canonical);
1841 }
1842 }
1843 self.expect(&TokenKind::Rocket, "`=>` in a map literal")?;
1844 self.skip_newlines();
1845 items.push(k);
1846 items.push(self.expr(0)?);
1847 }
1848 }
1849 Ok(())
1850 }
1851
1852 fn if_chain(&mut self, head: Span) -> Result<Spanned, Fail> {
1862 let cond = self.expr(0)?;
1863 let then = self.body(&["elsif", "else", "end"])?;
1864 let els = self.else_part()?;
1865 let mut out = vec![sym_at(head, "if"), cond, then];
1866 if let Some(e) = els {
1867 out.push(e);
1868 }
1869 Ok(list_at(self.span_since(head.start), out))
1870 }
1871
1872 fn else_part(&mut self) -> Result<Option<Spanned>, Fail> {
1873 if self.at_ident("elsif") {
1874 let elsif = self.peek_span();
1875 self.bump();
1876 return Ok(Some(self.if_chain(elsif)?));
1877 }
1878 if !self.at_ident("else") {
1879 self.expect_ident("end")?;
1880 return Ok(None);
1881 }
1882 let else_span = self.peek_span();
1883 self.bump();
1884 let save = self.pos;
1885 self.skip_newlines();
1886 let leads_with_if = self.at_ident("if") || self.at_ident("unless");
1887 self.pos = save;
1888 let e = self.body(&["end"])?;
1889 let end_at = self.peek_span().start;
1890 self.expect_ident("end")?;
1891 if matches!(e.form, SpannedForm::Nil) && !self.comment_within(else_span.start, end_at) {
1892 self.note(Spelling::ElseNil, else_span, "else nil", "no else");
1893 return Ok(None);
1894 }
1895 let single_if = matches!(&e.form, SpannedForm::List(items)
1896 if (items.len() == 3 || items.len() == 4) && items[0].as_symbol() == Some("if"));
1897 if leads_with_if && single_if {
1898 self.note(Spelling::ElseIf, else_span, "else if", "elsif");
1899 }
1900 Ok(Some(e))
1901 }
1902
1903 fn if_form(&mut self, head: Span, negate: bool) -> Result<Spanned, Fail> {
1904 let cond = self.expr(0)?;
1905 let cond = if negate {
1906 let inner = cond.span;
1909 self.negation(head, head.merge(inner), cond)
1910 } else {
1911 cond
1912 };
1913 let then = self.body(&["elsif", "else", "end"])?;
1926 let els = self.else_part()?;
1927 let mut out = vec![sym_at(head, "if"), cond, then];
1928 if let Some(e) = els {
1929 out.push(e);
1930 }
1931 Ok(list_at(self.span_since(head.start), out))
1932 }
1933
1934 fn case_form(&mut self, head: Span) -> Result<Spanned, Fail> {
1960 let subject = self.expr(0)?;
1961 self.skip_newlines();
1962
1963 let subject_var = "case-subject";
1966 let mut arms: Vec<Spanned> = Vec::new();
1967 let mut otherwise: Option<Spanned> = None;
1968 let mut else_kw: Option<Span> = None;
1971
1972 loop {
1973 self.skip_newlines();
1974 if self.at_ident("end") {
1975 break;
1976 }
1977 let maybe_else = self.peek_span();
1978 if self.eat_ident("else") {
1979 else_kw = Some(maybe_else);
1980 otherwise = Some(self.body(&["end"])?);
1981 continue;
1982 }
1983 let when = self.peek_span();
1984 if !self.eat_ident("when") {
1985 return Err(self.error(format!(
1986 "expected `when`, `else` or `end` in a case, found {}",
1987 self.peek()
1988 )));
1989 }
1990 self.skip_newlines();
1991 let pattern = self.expr(0)?;
1992 let body = self.body(&["when", "else", "end"])?;
1993 let test = list_at(
1997 when.merge(pattern.span),
1998 vec![sym_at(when, "equal?"), sym_at(when, subject_var), pattern],
1999 );
2000 arms.push(list_at(when.merge(body.span), vec![test, body]));
2001 }
2002 self.expect_ident("end")?;
2003
2004 if arms.is_empty() && otherwise.is_none() {
2005 return Err(self.error("a case needs at least one `when` or an `else`".to_string()));
2006 }
2007
2008 let mut cond = vec![sym_at(head, "cond")];
2013 cond.extend(arms);
2014 let else_span = else_kw.unwrap_or(head);
2021 let else_body = otherwise.unwrap_or_else(|| Spanned::new(else_span, SpannedForm::Nil));
2022 cond.push(list_at(
2023 else_span.merge(else_body.span),
2024 vec![sym_at(else_span, "else"), else_body],
2025 ));
2026
2027 let whole = self.span_since(head.start);
2028 let binding = list_at(
2029 head.merge(subject.span),
2030 vec![sym_at(head, subject_var), subject],
2031 );
2032 Ok(list_at(
2033 whole,
2034 vec![
2035 sym_at(head, "let"),
2036 list_at(binding.span, vec![binding]),
2037 list_at(whole, cond),
2038 ],
2039 ))
2040 }
2041
2042 fn arrow_lambda_ahead(&self) -> bool {
2045 let mut i = self.pos;
2046 let kind = |i: usize| self.toks.get(i).map(|t| &t.kind);
2047 if !matches!(kind(i), Some(TokenKind::RParen)) {
2048 loop {
2049 if matches!(kind(i), Some(TokenKind::Op(o)) if o == "*") {
2050 i += 1;
2051 }
2052 if !matches!(kind(i), Some(TokenKind::Ident(_))) {
2053 return false;
2054 }
2055 i += 1;
2056 match kind(i) {
2057 Some(TokenKind::Comma) => i += 1,
2058 Some(TokenKind::RParen) => break,
2059 _ => return false,
2060 }
2061 }
2062 }
2063 matches!(kind(i + 1), Some(TokenKind::Op(o)) if o == "->")
2064 }
2065
2066 fn arrow_lambda(&mut self, head: Span) -> Result<Spanned, Fail> {
2073 let mut params: Vec<Spanned> = Vec::new();
2074 while !self.at(&TokenKind::RParen) {
2075 let p_span = self.peek_span();
2076 if self.at_star() {
2077 params.extend(self.rest_parameter()?);
2078 break;
2079 }
2080 match self.bump() {
2081 TokenKind::Ident(p) => params.push(sym_at(p_span, &p)),
2082 other => {
2083 return Err(fail(
2084 format!("expected a parameter name, found {other}"),
2085 p_span,
2086 ))
2087 }
2088 }
2089 self.eat(&TokenKind::Comma);
2090 }
2091 self.bump();
2092 self.bump();
2093 self.skip_newlines();
2094 let params_span = cover(¶ms, head);
2095 let body = self.expr(0)?;
2096 Ok(list_at(
2097 self.span_since(head.start),
2098 vec![sym_at(head, "lambda"), list_at(params_span, params), body],
2099 ))
2100 }
2101
2102 fn rest_parameter(&mut self) -> Result<[Spanned; 2], Fail> {
2103 let star = self.peek_span();
2104 self.bump();
2105 let name_span = self.peek_span();
2106 match self.bump() {
2107 TokenKind::Ident(p) => {
2108 let whole = star.merge(name_span);
2109 Ok([sym_at(whole, "&rest"), sym_at(whole, &p)])
2110 }
2111 other => Err(fail(
2112 format!("expected a parameter name after `*`, found {other}"),
2113 name_span,
2114 )),
2115 }
2116 }
2117
2118 fn at_star(&self) -> bool {
2119 matches!(self.peek(), TokenKind::Op(o) if o == "*")
2120 }
2121
2122 fn optional_mark(
2123 &self,
2124 name: String,
2125 span: Span,
2126 optional_from: &mut Option<usize>,
2127 index: usize,
2128 ) -> Result<String, Fail> {
2129 match name.strip_suffix('?').filter(|n| !n.is_empty()) {
2130 Some(bare) => {
2131 optional_from.get_or_insert(index);
2132 Ok(bare.to_string())
2133 }
2134 None if optional_from.is_some() => Err(fail(
2135 format!("the required parameter `{name}` cannot follow an optional one"),
2136 span,
2137 )),
2138 None => Ok(name),
2139 }
2140 }
2141
2142 fn rest_is_last(&mut self) -> Result<(), Fail> {
2143 self.skip_newlines();
2144 if self.at(&TokenKind::Comma) {
2145 return Err(self.error("a rest parameter `*name` comes last".to_string()));
2146 }
2147 Ok(())
2148 }
2149
2150 fn lambda_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2160 let mut params: Vec<Spanned> = Vec::new();
2161 if !self.at(&TokenKind::LParen) {
2162 self.note(Spelling::ParameterList, head, "fn", "fn()");
2163 }
2164 if self.at(&TokenKind::LParen) {
2165 self.bump();
2166 self.skip_newlines();
2167 if !self.eat(&TokenKind::RParen) {
2168 loop {
2169 self.skip_newlines();
2170 let p_span = self.peek_span();
2171 if self.at_star() {
2172 params.extend(self.rest_parameter()?);
2173 self.rest_is_last()?;
2174 self.expect(&TokenKind::RParen, "`)` after the rest parameter")?;
2175 break;
2176 }
2177 match self.bump() {
2178 TokenKind::Ident(p) => params.push(sym_at(p_span, &p)),
2179 other => {
2180 return Err(fail(
2181 format!("expected a parameter name, found {other}"),
2182 p_span,
2183 ))
2184 }
2185 }
2186 self.skip_newlines();
2187 if self.eat(&TokenKind::Comma) {
2188 if self.trailing_comma(&TokenKind::RParen) {
2189 break;
2190 }
2191 continue;
2192 }
2193 self.expect(&TokenKind::RParen, "`,` or `)`")?;
2194 break;
2195 }
2196 }
2197 }
2198 let params_span = cover(¶ms, head);
2199 let body = self.body(&["end"])?;
2200 self.expect_ident("end")?;
2201 Ok(list_at(
2202 self.span_since(head.start),
2203 vec![sym_at(head, "lambda"), list_at(params_span, params), body],
2204 ))
2205 }
2206
2207 fn test_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2213 let name_span = self.peek_span();
2214 let name = match self.bump() {
2215 TokenKind::Str(s) => s,
2216 other => {
2217 return Err(fail(
2218 format!("expected a string name after `test`, found {other}"),
2219 name_span,
2220 ))
2221 }
2222 };
2223 let body = self.body(&["end"])?;
2224 self.expect_ident("end")?;
2225 Ok(list_at(
2226 self.span_since(head.start),
2227 vec![
2228 sym_at(head, "deftest"),
2229 atom_at(name_span, Atom::Str(name)),
2230 body,
2231 ],
2232 ))
2233 }
2234
2235 fn assert_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2259 let e = self.expr(0)?;
2260 let quoted = Spanned::new(e.span, SpannedForm::Quote(Box::new(e.clone())));
2261 Ok(list_at(
2262 self.span_since(head.start),
2263 vec![sym_at(head, LOWERED_ASSERT), quoted, e],
2264 ))
2265 }
2266
2267 fn defmacro_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2276 let name_span = self.peek_span();
2277 let name = match self.bump() {
2278 TokenKind::Ident(n) => n,
2279 other => {
2280 return Err(fail(
2281 format!("expected a name after `defmacro`, found {other}"),
2282 name_span,
2283 ))
2284 }
2285 };
2286 self.not_reserved(&name, name_span, "a `defmacro` name")?;
2287 let mut params: Vec<Spanned> = Vec::new();
2288 if self.at(&TokenKind::LParen) {
2289 self.bump();
2290 self.skip_newlines();
2291 if !self.eat(&TokenKind::RParen) {
2292 loop {
2293 self.skip_newlines();
2294 let p_span = self.peek_span();
2295 match self.bump() {
2296 TokenKind::Ident(p) => params.push(sym_at(p_span, &p)),
2297 TokenKind::Label(p) => {
2298 return Err(self.error(format!(
2299 "macro parameter `{p}` cannot be typed: a macro receives \
2300 source forms, not values"
2301 )))
2302 }
2303 other => {
2304 return Err(fail(
2305 format!("expected a parameter name, found {other}"),
2306 p_span,
2307 ))
2308 }
2309 }
2310 self.skip_newlines();
2311 if self.eat(&TokenKind::Comma) {
2312 if self.trailing_comma(&TokenKind::RParen) {
2313 break;
2314 }
2315 continue;
2316 }
2317 self.expect(&TokenKind::RParen, "`,` or `)`")?;
2318 break;
2319 }
2320 }
2321 }
2322 if matches!(self.peek(), TokenKind::Op(o) if o == "->") {
2323 return Err(self.error(
2324 "a macro has no return type: it produces source forms, not values".to_string(),
2325 ));
2326 }
2327
2328 let params_span = cover(¶ms, name_span);
2329 let body = self.body(&["end"])?;
2330 self.expect_ident("end")?;
2331
2332 Ok(list_at(
2335 self.span_since(head.start),
2336 vec![
2337 sym_at(head, "defmacro"),
2338 sym_at(name_span, &name),
2339 list_at(params_span, params),
2340 body,
2341 ],
2342 ))
2343 }
2344
2345 fn quote_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2351 let body = self.body(&["end"])?;
2352 self.expect_ident("end")?;
2353 Ok(Spanned::new(
2362 self.span_since(head.start),
2363 SpannedForm::Quasiquote(Box::new(body)),
2364 ))
2365 }
2366
2367 fn unquote_form(&mut self, head: Span, splice: bool) -> Result<Spanned, Fail> {
2369 self.expect(&TokenKind::LParen, "`(` after unquote")?;
2370 self.skip_newlines();
2371 let inner = self.expr(0)?;
2372 self.skip_newlines();
2373 self.expect(&TokenKind::RParen, "`)`")?;
2374 let span = self.span_since(head.start);
2375 Ok(Spanned::new(
2376 span,
2377 if splice {
2378 SpannedForm::UnquoteSplice(Box::new(inner))
2379 } else {
2380 SpannedForm::Unquote(Box::new(inner))
2381 },
2382 ))
2383 }
2384
2385 fn protected_body(&mut self, head: Span) -> Result<Spanned, Fail> {
2386 let body = self.body(&["rescue", "ensure", "end"])?;
2387 if self.at_ident("rescue") || self.at_ident("ensure") {
2388 return self.rescue_tail(head, body);
2389 }
2390 Ok(body)
2391 }
2392
2393 fn begin_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2394 let body = self.body(&["rescue", "ensure", "end"])?;
2395 if !(self.at_ident("rescue") || self.at_ident("ensure")) {
2396 return Err(self.error(
2397 "a `begin` block protects its statements with `rescue` or `ensure`; without either, write the statements directly",
2398 ));
2399 }
2400 let tree = self.rescue_tail(head, body)?;
2401 self.expect_ident("end")?;
2402 Ok(Spanned::new(self.span_since(head.start), tree.form))
2403 }
2404
2405 fn rescue_tail(&mut self, head: Span, body: Spanned) -> Result<Spanned, Fail> {
2406 let mut clauses: Vec<Spanned> = Vec::new();
2407 let mut catch_all: Option<(Spanned, Spanned, Span)> = None;
2408 while self.at_ident("rescue") {
2409 let kw = self.peek_span();
2410 self.bump();
2411 if catch_all.is_some() {
2412 return Err(fail(
2413 "a `rescue NAME` catches every error, so it is the last `rescue`; put the kinds it would shadow above it",
2414 kw,
2415 ));
2416 }
2417 match self.peek().clone() {
2418 TokenKind::Sym(_) => {
2419 let mut kinds = Vec::new();
2420 loop {
2421 let k_span = self.peek_span();
2422 match self.bump() {
2423 TokenKind::Sym(k) => kinds.push(atom_at(k_span, Atom::Keyword(k))),
2424 other => {
2425 return Err(fail(
2426 format!("expected an error kind such as `:not_found`, found {other}"),
2427 k_span,
2428 ))
2429 }
2430 }
2431 if self.eat(&TokenKind::Comma) {
2432 self.skip_newlines();
2433 continue;
2434 }
2435 break;
2436 }
2437 let binder = if self.eat_ident("as") {
2438 let b_span = self.peek_span();
2439 match self.bump() {
2440 TokenKind::Ident(b) if !is_reserved_word(&b) => Some(sym_at(b_span, &b)),
2441 other => {
2442 return Err(fail(
2443 format!("expected the name the error is bound to after `as`, found {other}"),
2444 b_span,
2445 ))
2446 }
2447 }
2448 } else {
2449 None
2450 };
2451 let handler = self.body(&["rescue", "ensure", "end"])?;
2452 let mut test = vec![sym_at(kw, LOWERED_ERROR_MATCH), sym_at(kw, RESCUED)];
2453 test.extend(kinds);
2454 let test = list_at(cover(&test, kw), test);
2455 let handler = match binder {
2456 Some(b) => let_bound(b, handler, kw),
2457 None => handler,
2458 };
2459 clauses.push(list_at(kw.merge(handler.span), vec![test, handler]));
2460 }
2461 TokenKind::Ident(b) if !is_reserved_word(&b) => {
2462 let b_span = self.peek_span();
2463 self.bump();
2464 let handler = self.body(&["rescue", "ensure", "end"])?;
2465 catch_all = Some((sym_at(b_span, &b), handler, kw));
2466 }
2467 _ => {
2468 return Err(fail(
2469 "`rescue` names what it catches: `rescue :kind`, `rescue :a, :b as e`, or `rescue e` for every error (`rescue _` discards it)",
2470 kw,
2471 ))
2472 }
2473 }
2474 }
2475 let ensure = if self.at_ident("ensure") {
2476 self.bump();
2477 let cleanup = self.body(&["rescue", "end"])?;
2478 if self.at_ident("rescue") {
2479 return Err(self.error("`ensure` comes after every `rescue` of its block"));
2480 }
2481 Some(cleanup)
2482 } else {
2483 None
2484 };
2485 let whole = self.span_since(head.start);
2486 let protected = if clauses.is_empty() {
2487 match catch_all {
2488 None => body,
2489 Some((binder, handler, kw)) => list_at(
2490 whole,
2491 vec![
2492 sym_at(head, "try"),
2493 body,
2494 list_at(
2495 kw.merge(handler.span),
2496 vec![
2497 sym_at(kw, "catch"),
2498 list_at(binder.span, vec![binder]),
2499 handler,
2500 ],
2501 ),
2502 ],
2503 ),
2504 }
2505 } else {
2506 let first_kw = clauses[0].span;
2507 let otherwise = match catch_all {
2508 Some((binder, handler, kw)) => {
2509 let bound = let_bound(binder, handler, kw);
2510 list_at(kw.merge(bound.span), vec![sym_at(kw, "else"), bound])
2511 }
2512 None => list_at(
2513 head,
2514 vec![
2515 sym_at(head, "else"),
2516 list_at(
2517 head,
2518 vec![sym_at(head, LOWERED_RERAISE), sym_at(head, RESCUED)],
2519 ),
2520 ],
2521 ),
2522 };
2523 let mut cond = vec![sym_at(first_kw, "cond")];
2524 cond.extend(clauses);
2525 cond.push(otherwise);
2526 let cond_span = cover(&cond, first_kw);
2527 list_at(
2528 whole,
2529 vec![
2530 sym_at(head, "try"),
2531 body,
2532 list_at(
2533 cond_span,
2534 vec![
2535 sym_at(first_kw, "catch"),
2536 list_at(first_kw, vec![sym_at(first_kw, RESCUED)]),
2537 list_at(cond_span, cond),
2538 ],
2539 ),
2540 ],
2541 )
2542 };
2543 Ok(match ensure {
2544 None => protected,
2545 Some(cleanup) => {
2546 let attempted = list_at(
2547 protected.span,
2548 vec![sym_at(head, LOWERED_ATTEMPTED), protected],
2549 );
2550 let failed = list_at(
2551 head,
2552 vec![sym_at(head, LOWERED_FAILED), sym_at(head, RESCUED)],
2553 );
2554 let caught = list_at(
2555 whole,
2556 vec![
2557 sym_at(head, "try"),
2558 attempted,
2559 list_at(
2560 head,
2561 vec![
2562 sym_at(head, "catch"),
2563 list_at(head, vec![sym_at(head, RESCUED)]),
2564 failed,
2565 ],
2566 ),
2567 ],
2568 );
2569 list_at(
2570 whole,
2571 vec![sym_at(head, LOWERED_ENSURE), caught, thunk(cleanup, head)],
2572 )
2573 }
2574 })
2575 }
2576
2577 fn def_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2578 let name_span = self.peek_span();
2579 let name = match self.bump() {
2580 TokenKind::Ident(n) => n,
2581 other => {
2582 return Err(fail(
2583 format!("expected a name after `def`, found {other}"),
2584 name_span,
2585 ))
2586 }
2587 };
2588 self.not_reserved(&name, name_span, "a `def` name")?;
2589 let mut params: Vec<(String, Span, Option<Spanned>)> = Vec::new();
2590 let mut optional_from: Option<usize> = None;
2591 let mut rest: Vec<Spanned> = Vec::new();
2592 if !self.at(&TokenKind::LParen) {
2593 self.note(
2594 Spelling::ParameterList,
2595 name_span,
2596 &format!("def {name}"),
2597 &format!("def {name}()"),
2598 );
2599 }
2600 if self.at(&TokenKind::LParen) {
2601 self.bump();
2602 self.skip_newlines();
2603 if !self.eat(&TokenKind::RParen) {
2604 loop {
2605 self.skip_newlines();
2606 let p_span = self.peek_span();
2607 if self.at_star() {
2608 if optional_from.is_some() {
2609 return Err(fail(
2610 "a rest parameter cannot follow an optional one",
2611 p_span,
2612 ));
2613 }
2614 rest = self.rest_parameter()?.to_vec();
2615 self.rest_is_last()?;
2616 self.expect(&TokenKind::RParen, "`)` after the rest parameter")?;
2617 break;
2618 }
2619 match self.bump() {
2620 TokenKind::Ident(p) => {
2622 self.not_reserved(&p, p_span, "a parameter name")?;
2623 let p =
2624 self.optional_mark(p, p_span, &mut optional_from, params.len())?;
2625 params.push((p, p_span, None));
2626 }
2627 TokenKind::Label(p) => {
2629 self.not_reserved(&p, p_span, "a parameter name")?;
2630 let p =
2631 self.optional_mark(p, p_span, &mut optional_from, params.len())?;
2632 self.skip_newlines();
2633 let ty = self.type_expr()?;
2634 params.push((p, p_span, Some(ty)));
2635 }
2636 other => {
2637 return Err(fail(
2638 format!("expected a parameter name, found {other}"),
2639 p_span,
2640 ))
2641 }
2642 }
2643 self.skip_newlines();
2644 if self.eat(&TokenKind::Comma) {
2645 if self.trailing_comma(&TokenKind::RParen) {
2646 break;
2647 }
2648 continue;
2649 }
2650 self.expect(&TokenKind::RParen, "`,` or `)`")?;
2651 break;
2652 }
2653 }
2654 }
2655 let sig_span = self.span_since(name_span.start);
2657
2658 let ret = if matches!(self.peek(), TokenKind::Op(o) if o == "->") {
2660 self.bump();
2661 self.skip_newlines();
2662 Some(self.type_expr()?)
2663 } else {
2664 None
2665 };
2666
2667 let begins = {
2668 let save = self.pos;
2669 self.skip_newlines();
2670 let at = self.at_ident("begin").then(|| self.peek_span());
2671 self.pos = save;
2672 at
2673 };
2674 let body = self.protected_body(head)?;
2675 if let Some(b) = begins {
2676 let protects = matches!(&body.form, SpannedForm::List(items)
2677 if matches!(items.first().and_then(Spanned::as_symbol), Some("try") | Some(LOWERED_ENSURE)));
2678 if protects && body.span.start == b.start {
2679 self.note(
2680 Spelling::TryCatch,
2681 b,
2682 "begin … end as a whole def body",
2683 "the clauses on the def itself",
2684 );
2685 }
2686 }
2687 self.expect_ident("end")?;
2688 let whole = self.span_since(head.start);
2689
2690 let annotated = ret.is_some() || params.iter().any(|(_, _, t)| t.is_some());
2691 if !annotated {
2692 let mut sig = vec![sym_at(name_span, &name)];
2693 for (i, (p, s, _)) in params.into_iter().enumerate() {
2694 if optional_from == Some(i) {
2695 sig.push(sym_at(s, OPTIONAL_MARKER));
2696 }
2697 sig.push(sym_at(s, &p));
2698 }
2699 sig.extend(rest);
2700 return Ok(list_at(
2701 whole,
2702 vec![sym_at(head, "define"), list_at(sig_span, sig), body],
2703 ));
2704 }
2705
2706 let mut sig = vec![sym_at(name_span, &name)];
2714 for (i, (p, s, t)) in params.into_iter().enumerate() {
2715 if optional_from == Some(i) {
2716 sig.push(sym_at(s, OPTIONAL_MARKER));
2717 }
2718 let ty = t.unwrap_or_else(|| sym_at(s, "dyn"));
2719 sig.push(list_at(s.merge(ty.span), vec![sym_at(s, &p), ty]));
2720 }
2721 sig.extend(rest);
2722 Ok(list_at(
2723 whole,
2724 vec![
2725 sym_at(head, "define-typed"),
2726 list_at(sig_span, sig),
2727 ret.unwrap_or_else(|| sym_at(head, "dyn")),
2728 body,
2729 ],
2730 ))
2731 }
2732
2733 fn type_expr(&mut self) -> Result<Spanned, Fail> {
2735 let name_span = self.peek_span();
2736 let name = match self.bump() {
2737 TokenKind::Ident(n) => n,
2738 other => {
2739 return Err(fail(
2740 format!("expected a type name, found {other}"),
2741 name_span,
2742 ))
2743 }
2744 };
2745 self.not_reserved(&name, name_span, "a type name")?;
2746 if self.at(&TokenKind::LParen) {
2747 self.bump();
2748 self.skip_newlines();
2749 let mut list = vec![sym_at(name_span, &name)];
2750 if !self.eat(&TokenKind::RParen) {
2751 loop {
2752 self.skip_newlines();
2753 list.push(self.type_expr()?);
2754 self.skip_newlines();
2755 if self.eat(&TokenKind::Comma) {
2756 if self.trailing_comma(&TokenKind::RParen) {
2757 break;
2758 }
2759 continue;
2760 }
2761 self.expect(&TokenKind::RParen, "`,` or `)`")?;
2762 break;
2763 }
2764 }
2765 return Ok(list_at(self.span_since(name_span.start), list));
2766 }
2767 Ok(sym_at(name_span, &name))
2768 }
2769
2770 fn not_reserved(&self, name: &str, at: Span, what: &str) -> Result<(), Fail> {
2771 if is_reserved_word(name) {
2772 return Err(fail(
2773 format!("`{name}` is a reserved word and cannot be {what}"),
2774 at,
2775 ));
2776 }
2777 Ok(())
2778 }
2779
2780 fn eat_ident(&mut self, name: &str) -> bool {
2782 if self.at_ident(name) {
2783 self.bump();
2784 true
2785 } else {
2786 false
2787 }
2788 }
2789
2790 fn expect_ident(&mut self, name: &str) -> Result<(), Fail> {
2791 if self.at_ident(name) {
2792 self.bump();
2793 Ok(())
2794 } else {
2795 Err(self.error(format!("expected `{name}`, found {}", self.peek())))
2796 }
2797 }
2798
2799 fn body(&mut self, terminators: &[&str]) -> Result<Spanned, Fail> {
2802 let start = self.mark();
2805 let mut forms = Vec::new();
2806 loop {
2807 self.skip_newlines();
2808 if matches!(self.peek(), TokenKind::Eof) {
2809 return Err(self.error(format!(
2810 "unterminated block: expected {} before the end of the file",
2811 one_of(terminators)
2812 )));
2813 }
2814 if terminators.iter().any(|t| self.at_ident(t)) {
2815 break;
2816 }
2817 forms.push(self.statement()?);
2818 self.end_of_statement(terminators)?;
2819 }
2820 Ok(match forms.len() {
2821 0 => Spanned::new(Span::new(start, start), SpannedForm::Nil),
2822 1 => forms.into_iter().next().expect("checked len"),
2823 _ => {
2824 let span = cover(&forms, Span::new(start, start));
2830 let head = Span::new(span.start, span.start);
2836 let mut list = vec![sym_at(head, "begin")];
2837 list.extend(forms);
2838 list_at(span, list)
2839 }
2840 })
2841 }
2842}
2843
2844fn one_of(words: &[&str]) -> String {
2847 let quoted: Vec<String> = words.iter().map(|w| format!("`{w}`")).collect();
2848 match quoted.split_last() {
2849 None => String::new(),
2850 Some((last, [])) => last.clone(),
2851 Some((last, rest)) => format!("{} or {last}", rest.join(", ")),
2852 }
2853}
2854
2855fn let_bound(binder: Spanned, body: Spanned, at: Span) -> Spanned {
2856 let span = at.merge(body.span);
2857 let pair = list_at(binder.span, vec![binder, sym_at(at, RESCUED)]);
2858 list_at(
2859 span,
2860 vec![sym_at(at, "let"), list_at(pair.span, vec![pair]), body],
2861 )
2862}
2863
2864fn thunk(body: Spanned, at: Span) -> Spanned {
2865 let span = at.merge(body.span);
2866 list_at(
2867 span,
2868 vec![sym_at(at, "lambda"), list_at(at, Vec::new()), body],
2869 )
2870}
2871
2872#[must_use]
2877pub fn label_name(name: &str) -> bool {
2878 matches!(
2879 lex(&format!("{name}:")).as_deref(),
2880 Ok([t, e]) if matches!(&t.kind, TokenKind::Label(n) if n == name) && e.kind == TokenKind::Eof
2881 )
2882}
2883
2884fn blue_lang_name(name: &str) -> bool {
2885 matches!(
2886 lex(name).as_deref(),
2887 Ok([t, e]) if matches!(&t.kind, TokenKind::Ident(n) if n == name) && e.kind == TokenKind::Eof
2888 ) && !name.ends_with(['?', '!'])
2889}
2890
2891fn mentions(form: &Spanned, name: &str) -> bool {
2892 match &form.form {
2893 SpannedForm::Atom(Atom::Symbol(s)) => s == name,
2894 SpannedForm::List(items) => items.iter().any(|i| mentions(i, name)),
2895 SpannedForm::Quote(i)
2896 | SpannedForm::Quasiquote(i)
2897 | SpannedForm::Unquote(i)
2898 | SpannedForm::UnquoteSplice(i) => mentions(i, name),
2899 _ => false,
2900 }
2901}
2902
2903fn sym_at(span: Span, s: &str) -> Spanned {
2905 Spanned::new(span, SpannedForm::Atom(Atom::Symbol(s.to_string())))
2906}
2907
2908fn atom_at(span: Span, a: Atom) -> Spanned {
2910 Spanned::new(span, SpannedForm::Atom(a))
2911}
2912
2913fn list_at(span: Span, items: Vec<Spanned>) -> Spanned {
2915 Spanned::new(span, SpannedForm::List(items))
2916}
2917
2918fn cover(items: &[Spanned], fallback: Span) -> Span {
2926 let merged = items
2927 .iter()
2928 .fold(Span::synthetic(), |acc, s| acc.merge(s.span));
2929 if merged.is_synthetic() {
2930 fallback
2931 } else {
2932 merged
2933 }
2934}
2935
2936#[cfg(test)]
2937mod tests {
2938 use super::*;
2939
2940 fn q(src: &str) -> String {
2944 parse_expr(src)
2945 .map(|s| s.to_string())
2946 .unwrap_or_else(|e| panic!("{src:?}: {e}"))
2947 }
2948
2949 #[test]
2950 fn a_reserved_word_is_not_a_def_name_a_parameter_or_a_type() {
2951 for src in [
2952 "def def a(x)\n x\nend",
2953 "def end()\n 1\nend",
2954 "def f(end)\n 1\nend",
2955 "def f(a: not)\n 1\nend",
2956 "def f(a) -> not a\n a\nend",
2957 "def f(a: List(1 + 2))\n a\nend",
2958 "defmacro defmacro m(a)\n quote\n a\n end\nend",
2959 ] {
2960 assert!(parse_program(src).is_err(), "{src:?} parsed");
2961 }
2962 }
2963
2964 #[test]
2965 fn a_type_application_takes_types_of_any_arity() {
2966 assert_eq!(
2967 q("def f(a: Map(Str, List(Int))) -> Unit()\n a\nend"),
2968 "(define-typed (f (a (Map Str (List Int)))) (Unit) a)"
2969 );
2970 }
2971
2972 #[test]
2975 fn arithmetic_respects_precedence() {
2976 assert_eq!(q("1 + 2 * 3"), "(+ 1 (* 2 3))");
2977 assert_eq!(q("(1 + 2) * 3"), "(* (+ 1 2) 3)");
2978 }
2979
2980 #[test]
2981 fn comparison_binds_looser_than_arithmetic() {
2982 assert_eq!(q("a + 1 < b"), "(< (+ a 1) b)");
2983 }
2984
2985 #[test]
2990 fn logical_operators_bind_loosest_and_lower_to_tataras_names() {
2991 assert_eq!(q("a && b || c"), "(or (and a b) c)");
2992 }
2993
2994 #[test]
2995 fn left_associativity() {
2996 assert_eq!(q("1 - 2 - 3"), "(- (- 1 2) 3)");
2997 }
2998
2999 #[test]
3000 fn a_dot_without_parentheses_reads_a_field() {
3001 assert_eq!(q("user.name"), "(blue-field user :name)");
3002 }
3003
3004 #[test]
3005 fn method_call_with_args() {
3006 assert_eq!(q("user.greet(1, 2)"), "(greet user 1 2)");
3007 }
3008
3009 #[test]
3010 fn chained_fields_read_left_to_right() {
3011 assert_eq!(q("a.b.c"), "(blue-field (blue-field a :b) :c)");
3012 assert_eq!(q("a.b(1).c"), "(blue-field (b a 1) :c)");
3013 }
3014
3015 #[test]
3016 fn plain_call() {
3017 assert_eq!(q("f(1, 2)"), "(f 1 2)");
3018 }
3019
3020 #[test]
3023 fn pipeline_threads_into_first_argument() {
3024 assert_eq!(q("x |> f"), "(f x)");
3025 assert_eq!(q("x |> f(1)"), "(f x 1)");
3026 assert_eq!(q("x |> f |> g"), "(g (f x))");
3027 }
3028
3029 #[test]
3030 fn pipeline_binds_looser_than_arithmetic() {
3031 assert_eq!(q("1 + 2 |> f"), "(f (+ 1 2))");
3032 }
3033
3034 #[test]
3040 fn label_and_rocket_produce_the_same_tree_for_a_symbol_key() {
3041 assert_eq!(q("{a: 1}"), q("{:a => 1}"));
3042 assert_eq!(q("{a: 1}"), "(hash-map :a 1)");
3043 }
3044
3045 #[test]
3048 fn a_string_key_has_no_shorthand() {
3049 assert_eq!(q(r#"{"k" => 1}"#), r#"(hash-map "k" 1)"#);
3050 }
3051
3052 #[test]
3053 fn list_literal() {
3054 assert_eq!(q("[1, 2, 3]"), "(list 1 2 3)");
3055 assert_eq!(q("[]"), "(list)");
3056 }
3057
3058 #[test]
3061 fn if_else_end() {
3062 assert_eq!(q("if a\n 1\nelse\n 2\nend"), "(if a 1 2)");
3063 }
3064
3065 #[test]
3066 fn if_without_else() {
3067 assert_eq!(q("if a\n 1\nend"), "(if a 1)");
3068 }
3069
3070 #[test]
3073 fn unless_is_a_negated_if() {
3074 assert_eq!(q("unless a\n 1\nend"), "(if (not a) 1)");
3075 }
3076
3077 #[test]
3078 fn multi_statement_body_becomes_begin() {
3079 assert_eq!(q("if a\n 1\n 2\nend"), "(if a (begin 1 2))");
3080 }
3081
3082 #[test]
3083 fn def_lowers_to_define() {
3084 assert_eq!(
3085 q("def add(a, b)\n a + b\nend"),
3086 "(define (add a b) (+ a b))"
3087 );
3088 }
3089
3090 #[test]
3091 fn def_with_no_params() {
3092 assert_eq!(q("def zero()\n 0\nend"), "(define (zero) 0)");
3093 }
3094
3095 fn refused(src: &str) -> String {
3096 match parse_expr(src) {
3097 Ok(s) => panic!("{src:?} should be refused, parsed to {s}"),
3098 Err(e) => e.to_string(),
3099 }
3100 }
3101
3102 #[test]
3103 fn a_question_mark_parameter_is_optional() {
3104 assert_eq!(q("def f(a, b?)\n b\nend"), "(define (f a &optional b) b)");
3105 assert_eq!(q("def f(a?, b?)\n a\nend"), "(define (f &optional a b) a)");
3106 assert_eq!(q("def f(a?)\n a\nend"), "(define (f &optional a) a)");
3107 }
3108
3109 #[test]
3110 fn an_annotated_optional_parameter_keeps_its_type_after_the_marker() {
3111 assert_eq!(
3112 q("def f(a: Int, b?: Str) -> Int\n a\nend"),
3113 "(define-typed (f (a Int) &optional (b Str)) Int a)"
3114 );
3115 assert_eq!(
3116 q("def f(a, b?: Str)\n a\nend"),
3117 "(define-typed (f (a dyn) &optional (b Str)) dyn a)"
3118 );
3119 }
3120
3121 #[test]
3122 fn a_def_without_question_marks_carries_no_marker() {
3123 assert_eq!(q("def f(a, b)\n a\nend"), "(define (f a b) a)");
3124 assert_eq!(q("def f(a, *r)\n a\nend"), "(define (f a &rest r) a)");
3125 }
3126
3127 #[test]
3128 fn a_required_parameter_cannot_follow_an_optional_one() {
3129 assert!(refused("def f(a?, b)\n a\nend").contains("`b` cannot follow an optional"));
3130 assert!(refused("def f(a, b?, c: Int)\n a\nend").contains("`c` cannot follow"));
3131 }
3132
3133 #[test]
3134 fn a_rest_parameter_cannot_follow_an_optional_one() {
3135 assert!(refused("def f(a, b?, *r)\n a\nend").contains("rest parameter cannot follow"));
3136 }
3137
3138 #[test]
3141 fn literals_lower_to_atoms() {
3142 assert_eq!(q("42"), "42");
3143 assert_eq!(q("true"), "#t");
3144 assert_eq!(q(":ok"), ":ok");
3145 assert_eq!(q(r#""hi""#), r#""hi""#);
3146 }
3147
3148 #[test]
3149 fn unary_minus_and_not() {
3150 assert_eq!(q("-x"), "(- 0 x)");
3151 assert_eq!(q("!x"), "(not x)");
3152 }
3153
3154 #[test]
3157 fn a_program_is_a_sequence_of_forms() {
3158 let forms = parse_program("def f()\n 1\nend\nf()").expect("parse");
3159 assert_eq!(forms.len(), 2);
3160 assert_eq!(forms[1].to_string(), "(f)");
3161 }
3162
3163 #[test]
3164 fn unterminated_block_is_an_error_naming_what_was_expected() {
3165 let e = parse_program("if a\n 1").expect_err("must fail");
3166 assert!(e.message.contains("unterminated"), "{}", e.message);
3167 }
3168
3169 #[test]
3170 fn a_parse_error_carries_a_span_into_the_source() {
3171 let src = "1 + )";
3172 let e = parse_program(src).expect_err("must fail");
3173 assert!(e.span.start < src.len(), "span {:?} outside source", e.span);
3174 }
3175
3176 #[test]
3183 fn postfix_indexing_is_refused_naming_nth() {
3184 let e = parse_program("y = xs[0]").expect_err("must not split into two statements");
3185 assert!(e.message.contains("`nth(0, xs)`"), "{}", e.message);
3186 assert_eq!((e.line, e.col), (1, 7), "{e}");
3187 }
3188
3189 #[test]
3191 fn a_call_without_parentheses_is_refused_naming_the_call() {
3192 let e = parse_program("a = f x").expect_err("must not split into two statements");
3193 assert!(e.message.contains("`f(x)`"), "{}", e.message);
3194 assert_eq!((e.line, e.col), (1, 7), "{e}");
3195 }
3196
3197 #[test]
3199 fn two_statements_on_one_line_are_refused_in_a_body() {
3200 for src in [
3201 "def f(xs)\n xs[0]\nend",
3202 "if a\n f x\nend",
3203 "fn(x) x 1 end",
3204 "test \"t\"\n assert a b\nend",
3205 "1 2",
3206 ] {
3207 assert!(parse_program(src).is_err(), "{src:?} must not parse");
3208 }
3209 }
3210
3211 #[test]
3213 fn an_arrow_lambda_lowers_to_the_same_tree_as_fn() {
3214 for (arrow, block) in [
3215 ("(x) -> x * 2", "fn(x) x * 2 end"),
3216 ("(a, b) -> a + b", "fn(a, b) a + b end"),
3217 ("() -> 1", "fn() 1 end"),
3218 ("map(xs, (x) -> x + 1)", "map(xs, fn(x) x + 1 end)"),
3219 ] {
3220 let a = parse_program(arrow).expect(arrow);
3221 let b = parse_program(block).expect(block);
3222 assert_eq!(format!("{a:?}"), format!("{b:?}"), "{arrow}");
3223 }
3224 assert_eq!(
3225 parse_program("(x)").map(|p| p.len()).ok(),
3226 Some(1),
3227 "a parenthesised name is still a group"
3228 );
3229 }
3230
3231 #[test]
3235 fn a_statement_may_end_at_a_newline_the_end_or_a_closing_keyword() {
3236 for src in [
3237 "fn(x) x + 1 end",
3238 "map(fn(x) x end, xs)",
3239 "if a\n 1\nelse\n 2\nend",
3240 "if a 1 else 2 end",
3241 "case x\nwhen 1 :a\nwhen 2 :b\nelse :c\nend",
3242 "x = 1\ny = 2",
3243 "x = 1",
3244 ] {
3245 assert!(
3246 parse_program(src).is_ok(),
3247 "{src:?}: {:?}",
3248 parse_program(src)
3249 );
3250 }
3251 }
3252
3253 #[test]
3255 fn then_is_refused_by_name() {
3256 let e = parse_program("x = if c then 1 else 2 end").expect_err("then is not a keyword");
3257 assert!(e.message.contains("`then`"), "{}", e.message);
3258 }
3259
3260 #[test]
3262 fn a_brace_block_is_refused_naming_fn() {
3263 let e = parse_program("xs.map { |x| x }").expect_err("no brace blocks");
3264 assert!(e.message.contains("fn(x)"), "{}", e.message);
3265 }
3266
3267 #[test]
3274 fn a_qualified_name_is_one_symbol() {
3275 assert_eq!(q("retsu::first(xs)"), "(retsu/first xs)");
3276 assert_eq!(q("retsu::first"), "retsu/first");
3277 assert_eq!(q("xs |> retsu::first"), "(retsu/first xs)");
3278 assert_eq!(q("blue::length(xs)"), "(blue/length xs)");
3279 }
3280
3281 #[test]
3283 fn whitespace_around_the_qualifier_is_accepted() {
3284 assert_eq!(q("retsu :: first(xs)"), "(retsu/first xs)");
3285 }
3286
3287 #[test]
3290 fn a_reserved_word_after_the_qualifier_is_a_name() {
3291 assert_eq!(q("kueri::if(a, b)"), "(kueri/if a b)");
3292 assert_eq!(q("heni::test(x)"), "(heni/test x)");
3293 assert_eq!(q("sabi::fn"), "sabi/fn");
3294 }
3295
3296 #[test]
3297 fn a_qualified_name_has_exactly_one_qualifier() {
3298 for (src, says) in [
3299 ("::first(xs)", "`package::name`"),
3300 ("a::b::c", "one qualifier"),
3301 ("if::x", "reserved word"),
3302 ("retsu::1", "expected a name after `retsu::`"),
3303 ] {
3304 let e = parse_program(src).expect_err(src);
3305 assert!(e.message.contains(says), "{src}: {}", e.message);
3306 }
3307 }
3308
3309 #[test]
3312 fn qualified_reads_only_the_package_name_shape() {
3313 assert_eq!(qualified("retsu/first"), Some(("retsu", "first")));
3314 assert_eq!(qualified("kueri/if"), Some(("kueri", "if")));
3315 assert_eq!(qualified("nisshi/pair?"), Some(("nisshi", "pair?")));
3316 for not in ["/", "a/", "/a", "a/b/c", "if/x", "hash-map", "a"] {
3317 assert_eq!(qualified(not), None, "{not}");
3318 }
3319 assert!(is_callable_name("kueri/if"));
3320 }
3321
3322 #[test]
3327 fn a_symbol_may_end_in_a_question_or_bang() {
3328 assert_eq!(q("[:pair?, :set!]"), "(list :pair? :set!)");
3329 assert_eq!(q("use(\"n\", [:empty?])"), "(use \"n\" (list :empty?))");
3330 assert_eq!(q(":a != :b"), "(not= :a :b)");
3331 }
3332
3333 #[test]
3337 fn a_parse_error_displays_line_col_and_the_source_text() {
3338 let e = parse_program("x = 1\ny = [1, 2 y]").expect_err("must fail");
3339 assert_eq!(e.to_string(), "2:11: expected `,` or `]`, found `y`");
3340 assert_eq!(e.span, Span::new(16, 17), "the span stays, as data");
3341 }
3342
3343 #[test]
3345 fn no_error_message_names_a_rust_variant() {
3346 for src in [
3347 "f(1 2)",
3348 "{a: 1 b}",
3349 "def 1\nend",
3350 "x.1",
3351 "if a\n",
3352 "fn(1) 2 end",
3353 "[1 :b]",
3354 "1 +",
3355 "(1",
3356 ] {
3357 let e = parse_program(src).expect_err(src);
3358 let shown = e.to_string();
3359 for tell in [
3360 "Ident(", "Int(", "Op(", "Label(", "Sym(", "Str(", "LParen", "RBracket", "Eof",
3361 "Newline", "..",
3362 ] {
3363 assert!(!shown.contains(tell), "{src:?} -> {shown}");
3364 }
3365 assert!(e.line >= 1 && e.col >= 1, "{src:?} not located: {shown}");
3366 }
3367 }
3368
3369 #[test]
3371 fn a_lex_error_is_located_too() {
3372 let e = parse_program("x = 1\ny = 2; z = 3").expect_err("no semicolons");
3373 assert_eq!((e.line, e.col), (2, 6), "{e}");
3374 assert!(e.message.contains("new line"), "{e}");
3375 }
3376
3377 #[test]
3380 fn the_parser_rejects_garbage() {
3381 assert!(parse_program("def").is_err());
3382 assert!(parse_program("(1").is_err());
3383 assert!(parse_program("end").is_err());
3384 }
3385}