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 MASTER_VERBS: &[&str] = &[
442 "read", "need", "has?", "put", "update", "delete", "create", "diff", "info", "exists?",
443];
444
445pub const LOWERED_ASSERT: &str = "blue-assert";
452
453pub const LOWERED_MAP: &str = "hash-map";
461
462pub const LOWERED_CONCAT: &str = "concat";
467
468pub const OPTIONAL_MARKER: &str = "&optional";
469
470pub const LOWERED_OPTIONAL_ARITY: &str = "blue-optional-arity";
471
472pub const LOWERED_OPTIONAL_ARG: &str = "blue-optional-arg";
473
474pub const LOWERED_FIELD: &str = "blue-field";
475
476pub const LOWERED_MERGE: &str = "blue-merge";
477
478pub const LOWERED_SPLICE: &str = "blue-splice";
479
480pub const LOWERED_KWARGS: &str = "blue-kwargs";
481
482pub const LOWERED_ERROR_MATCH: &str = "blue-rescues?";
483
484pub const LOWERED_ENSURE: &str = "blue-ensure";
485
486pub const LOWERED_RERAISE: &str = "blue-reraise";
487
488pub const LOWERED_ATTEMPTED: &str = "blue-attempted";
489
490pub const LOWERED_FAILED: &str = "blue-failed";
491
492pub const LOWERED_HEADS: &[&str] = &[
493 "list", "concat", "not", "apply", "equal?", "not=", "and", "or", "mod",
494];
495
496pub const RESCUED: &str = "rescued-error";
497
498pub const SURFACE_KEYWORDS: &[&str] = &[
499 "if",
500 "begin",
501 "unless",
502 "def",
503 "defmacro",
504 "quote",
505 "unquote",
506 "unquote_splice",
507 "test",
508 "assert",
509 "fn",
510 "case",
511];
512
513pub const BLOCK_KEYWORDS: &[&str] = &[
525 "do", "end", "else", "elsif", "rescue", "ensure", "true", "false", "nil",
526];
527
528pub const OPERATOR_KEYWORDS: &[&str] = &["and", "or", "not"];
529
530pub fn is_reserved_word(name: &str) -> bool {
537 SURFACE_KEYWORDS.contains(&name)
538 || BLOCK_KEYWORDS.contains(&name)
539 || OPERATOR_KEYWORDS.contains(&name)
540}
541
542pub fn is_callable_name(name: &str) -> bool {
551 if qualified(name).is_some() {
553 return true;
554 }
555 !is_reserved_word(name)
556 && matches!(
557 lex(name).as_deref(),
558 Ok([t, e]) if matches!(&t.kind, TokenKind::Ident(n) if n == name)
559 && e.kind == TokenKind::Eof
560 )
561}
562
563pub const QUALIFIER: char = '/';
568
569pub const BUILTIN_QUALIFIER: &str = "blue";
572
573#[must_use]
575pub fn qualify(pkg: &str, name: &str) -> String {
576 format!("{pkg}{QUALIFIER}{name}")
577}
578
579#[must_use]
585pub fn qualified(sym: &str) -> Option<(&str, &str)> {
586 let (pkg, name) = sym.split_once(QUALIFIER)?;
587 let ident = |s: &str| {
588 matches!(
589 lex(s).as_deref(),
590 Ok([t, e]) if matches!(&t.kind, TokenKind::Ident(n) if n == s) && e.kind == TokenKind::Eof
591 )
592 };
593 let named = |s: &str| ident(s) || matches!(s, "true" | "false" | "nil");
594 (ident(pkg) && !is_reserved_word(pkg) && named(name)).then_some((pkg, name))
595}
596
597#[must_use]
605pub fn keyword_doc(word: &str) -> &'static str {
606 match word {
607 "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).",
608 "unless" => "`unless c … end` is read as `if not c` and written that way (B0026).",
609 "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`.",
610 "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 `|>`.",
611 "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.",
612 "and" => "`a and b`: logical and; `&&` is read as `and` and written `and` (B0025).",
613 "or" => "`a or b`: logical or; `||` is read as `or` and written `or` (B0025).",
614 "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).",
615 "def" => "`def name(a, b) … end`: a named function; its value is the last expression. Parameters may be typed: `def f(n: Int) -> Int`.",
616 "defmacro" => "`defmacro name(x) quote … unquote(x) … end end`: a macro, a rewrite of the unevaluated form.",
617 "quote" => "`quote … end`: the form itself as data, not its value. Used inside `defmacro`.",
618 "unquote" => "`unquote(x)`: inside `quote`, splice in the value of `x`.",
619 "unquote_splice" => "`unquote_splice(xs)`: inside `quote`, splice the items of the form `xs` in place, not the form itself.",
620 "test" => "`test \"what must hold\" … end`: a test block, run by `blue test` and ignored by `blue run`.",
621 "assert" => "`assert expr`: inside a test, fail it unless `expr` is truthy; the failure quotes the expression.",
622 "fn" => "`fn(x) x * 2 end`: an anonymous function, a value. Blue has no brace or do blocks.",
623 "case" => "`case v when 1 … when 2 … else … end`: compares with `==`; it does not destructure.",
624 "do" => "Reserved and refused: blue has no do-blocks. Pass `fn(x) … end` instead.",
625 "end" => "Closes `def`, `defmacro`, `if`, `case`, `fn`, `test` and `quote`.",
626 "else" => "The branch of `if` or `case` taken when nothing before it matched.",
627 "elsif" => "A further condition in an `if` chain; an `else` holding only an `if` is written `elsif` (B0027).",
628 "true" => "The boolean true.",
629 "false" => "The boolean false. Only `false` and `nil` are falsy; 0, \"\" and [] are truthy.",
630 "nil" => "The absent value. It is not `[]` (okite D0004), and only `nil == nil`.",
631 _ => "",
632 }
633}
634
635fn infix(op: &str) -> Option<&'static Infix> {
636 INFIX.iter().find(|i| i.op == op)
637}
638
639pub const SYMBOL_SPELLINGS: &[(&str, &str)] = &[("&&", "and"), ("||", "or"), ("!", "not")];
640
641fn word_for(op: &str) -> &str {
642 SYMBOL_SPELLINGS
643 .iter()
644 .find(|(sym, _)| *sym == op)
645 .map_or(op, |(_, word)| word)
646}
647
648fn infix_of(kind: &TokenKind) -> Option<&'static Infix> {
649 match kind {
650 TokenKind::Op(o) => infix(word_for(o)),
651 TokenKind::Ident(w) if w == "and" || w == "or" => infix(w),
652 _ => None,
653 }
654}
655
656const PIPE_POWER: (u8, u8) = (0, 1);
657const RESCUE_POWER: (u8, u8) = (0, 1);
658
659struct Parser {
660 toks: Vec<Token>,
661 pos: usize,
662 depth: usize,
670 max_depth: usize,
677 src: String,
678 comment_spans: Vec<Span>,
679 notes: Vec<Note>,
680}
681
682pub const MAX_EXPR_DEPTH: usize = 256;
689
690type Fail = Box<ParseError>;
702
703fn fail(message: impl Into<String>, span: Span) -> Fail {
704 Box::new(ParseError::new(message, span))
705}
706
707impl Parser {
708 fn over(src: &str, all: Vec<Token>, max_depth: usize) -> Self {
709 let comment_spans = all
710 .iter()
711 .filter(|t| matches!(t.kind, TokenKind::Comment(_)))
712 .map(|t| t.span)
713 .collect();
714 let toks = all
715 .into_iter()
716 .filter(|t| !matches!(t.kind, TokenKind::Comment(_)))
717 .collect();
718 Parser {
719 toks,
720 pos: 0,
721 depth: 0,
722 max_depth,
723 src: src.to_string(),
724 comment_spans,
725 notes: Vec::new(),
726 }
727 }
728
729 fn note(&mut self, spelling: Spelling, span: Span, written: &str, canonical: &str) {
730 self.notes.push(Note {
731 spelling,
732 span,
733 written: written.to_string(),
734 canonical: canonical.to_string(),
735 });
736 }
737
738 fn text(&self, span: Span) -> &str {
739 self.src.get(span.start..span.end).unwrap_or("")
740 }
741
742 fn comment_within(&self, lo: usize, hi: usize) -> bool {
743 self.comment_spans
744 .iter()
745 .any(|c| c.start >= lo && c.start < hi)
746 }
747
748 fn touches_previous(&self) -> bool {
749 let here = self.peek_span();
750 self.toks
751 .get(self.pos.wrapping_sub(1))
752 .is_some_and(|prev| prev.span.end == here.start)
753 }
754
755 fn peek(&self) -> &TokenKind {
756 &self.toks[self.pos.min(self.toks.len() - 1)].kind
757 }
758
759 fn peek_span(&self) -> Span {
760 self.toks[self.pos.min(self.toks.len() - 1)].span
761 }
762
763 fn bump(&mut self) -> TokenKind {
764 let k = self.toks[self.pos.min(self.toks.len() - 1)].kind.clone();
765 if self.pos < self.toks.len() {
766 self.pos += 1;
767 }
768 k
769 }
770
771 fn at(&self, k: &TokenKind) -> bool {
772 self.peek() == k
773 }
774
775 fn eat(&mut self, k: &TokenKind) -> bool {
776 if self.at(k) {
777 self.bump();
778 true
779 } else {
780 false
781 }
782 }
783
784 fn expect(&mut self, k: &TokenKind, what: &str) -> Result<(), Fail> {
785 if self.eat(k) {
786 Ok(())
787 } else {
788 Err(self.error(format!("expected {what}, found {}", self.peek())))
789 }
790 }
791
792 fn error(&self, message: impl Into<String>) -> Fail {
793 fail(message, self.peek_span())
794 }
795
796 fn skip_newlines(&mut self) {
798 while matches!(self.peek(), TokenKind::Newline) {
799 self.bump();
800 }
801 }
802
803 fn at_ident(&self, name: &str) -> bool {
804 matches!(self.peek(), TokenKind::Ident(n) if n == name)
805 }
806
807 fn mark(&self) -> usize {
810 self.peek_span().start
811 }
812
813 fn span_since(&self, start: usize) -> Span {
819 let end = self
820 .toks
821 .get(self.pos.saturating_sub(1))
822 .map_or(start, |t| t.span.end);
823 Span::new(start, end.max(start))
824 }
825
826 fn program(&mut self) -> Result<Vec<Spanned>, Fail> {
827 let mut out = Vec::new();
828 loop {
829 self.skip_newlines();
830 if matches!(self.peek(), TokenKind::Eof) {
831 break;
832 }
833 out.push(self.statement()?);
834 self.end_of_statement(&[])?;
835 }
836 Ok(out)
837 }
838
839 fn end_of_statement(&self, terminators: &[&str]) -> Result<(), Fail> {
857 let next = self.peek();
858 if matches!(next, TokenKind::Newline | TokenKind::Eof)
859 || terminators.iter().any(|t| self.at_ident(t))
860 {
861 return Ok(());
862 }
863 let prev = self.toks.get(self.pos.wrapping_sub(1)).map(|t| &t.kind);
864 let prev_name = match prev {
865 Some(TokenKind::Ident(n)) => Some(n.as_str()),
866 _ => None,
867 };
868 let callee = prev_name.unwrap_or("f");
869 let why = match (prev, next) {
870 (Some(TokenKind::Ident(t)), _) if t == "then" => {
871 "blue's `if` has no `then`: put the branch on the next line".to_string()
872 }
873 (_, TokenKind::Ident(d)) if d == "do" => "blue has no `do` blocks: pass a function \
874 as an argument, `f(xs, fn(x) … end)`"
875 .to_string(),
876 (_, TokenKind::LBracket) => {
877 let index = match (self.toks.get(self.pos + 1), self.toks.get(self.pos + 2)) {
878 (Some(i), Some(close)) if close.kind == TokenKind::RBracket => match &i.kind {
879 TokenKind::Int(n) => n.to_string(),
880 TokenKind::Ident(n) => n.clone(),
881 _ => "i".to_string(),
882 },
883 _ => "i".to_string(),
884 };
885 let list = prev_name.unwrap_or("xs");
886 format!(
887 "blue has no `xs[i]` indexing: write `nth({index}, {list})` \
888 (or `first({list})`, `last({list})`)"
889 )
890 }
891 (_, TokenKind::LBrace) => "blue has no brace blocks: pass a function as an \
892 argument, `map(xs, fn(x) … end)`"
893 .to_string(),
894 (
895 Some(TokenKind::Ident(_)),
896 TokenKind::Ident(_)
897 | TokenKind::Int(_)
898 | TokenKind::Float(_)
899 | TokenKind::Str(_)
900 | TokenKind::InterpolatedStr { .. }
901 | TokenKind::Sym(_)
902 | TokenKind::True
903 | TokenKind::False
904 | TokenKind::Nil,
905 ) => {
906 let arg = match next {
907 TokenKind::Ident(a) => a.clone(),
908 _ => "x".to_string(),
909 };
910 format!("a call needs parentheses: write `{callee}({arg})`")
911 }
912 _ => "put each statement on its own line".to_string(),
913 };
914 Err(self.error(format!("expected the end of the line, found {next}: {why}")))
915 }
916
917 fn statement(&mut self) -> Result<Spanned, Fail> {
930 let max = self.max_depth;
944 if self.depth >= max {
945 return Err(self.error(format!(
946 "statement nests deeper than {max}; refusing to \
947 recurse further (this is a limit, not a syntax error)"
948 )));
949 }
950 self.depth += 1;
951 let r = self.statement_inner();
952 self.depth -= 1;
953 r
954 }
955
956 fn statement_inner(&mut self) -> Result<Spanned, Fail> {
957 if let TokenKind::Ident(name) = self.peek().clone() {
958 if self.peek_at(1) == "=" && !is_reserved_word(&name) {
959 let name_span = self.peek_span();
960 self.bump(); self.bump(); self.skip_newlines();
963 let value = self.expr(0)?;
964 return Ok(list_at(
968 self.span_since(name_span.start),
969 vec![sym_at(name_span, "define"), sym_at(name_span, &name), value],
970 ));
971 }
972 }
973 let e = self.expr(0)?;
974 if let SpannedForm::List(items) = &e.form {
975 if items.len() == 3
976 && items[0].as_symbol() == Some("define")
977 && items[1].as_symbol().is_some()
978 && self.text(items[0].span) == "define"
979 {
980 self.note(Spelling::OperatorSpelling, e.span, "define(x, v)", "x = v");
981 }
982 }
983 Ok(e)
984 }
985
986 fn peek_at(&self, n: usize) -> String {
989 match self.toks.get(self.pos + n).map(|t| &t.kind) {
990 Some(TokenKind::Op(o)) => o.clone(),
991 _ => String::new(),
992 }
993 }
994
995 fn expr(&mut self, min_bp: u8) -> Result<Spanned, Fail> {
997 let max = self.max_depth;
1007 if self.depth >= max {
1008 return Err(self.error(format!(
1009 "expression nests deeper than {max}; refusing to \
1010 recurse further (this is a limit, not a syntax error)"
1011 )));
1012 }
1013 self.depth += 1;
1014 let r = self.expr_inner(min_bp);
1015 self.depth -= 1;
1016 r
1017 }
1018
1019 #[inline(never)]
1020 fn rescue_node(&self, start: usize, op_span: Span, lhs: Spanned, fallback: Spanned) -> Spanned {
1021 list_at(
1022 self.span_since(start),
1023 vec![
1024 sym_at(op_span, "try"),
1025 lhs,
1026 list_at(
1027 op_span.merge(fallback.span),
1028 vec![
1029 sym_at(op_span, "catch"),
1030 list_at(op_span, vec![sym_at(op_span, "_")]),
1031 fallback,
1032 ],
1033 ),
1034 ],
1035 )
1036 }
1037
1038 #[inline(never)]
1039 fn infix_spelling(&mut self, callee: Option<&str>, op_span: Span) {
1040 let written = self.text(op_span).to_string();
1041 match (callee, self.peek().clone()) {
1042 (None, _) => self.note(Spelling::CallForm, op_span, "|>", "a call"),
1043 (Some(c), TokenKind::Op(o)) if word_for(&o) != o => {
1044 self.note(Spelling::LogicalSymbol, op_span, &written, c);
1045 }
1046 (Some(_), TokenKind::Op(o)) if written != o => {
1047 self.note(Spelling::OperatorSpelling, op_span, &written, &o);
1048 }
1049 _ => {}
1050 }
1051 }
1052
1053 #[inline(never)]
1054 fn nil_test(
1055 &mut self,
1056 start: usize,
1057 op_span: Span,
1058 lhs: &Spanned,
1059 rhs: &Spanned,
1060 ) -> Option<Spanned> {
1061 let (subject, written) = match (&lhs.form, &rhs.form) {
1062 (_, SpannedForm::Nil) if !matches!(lhs.form, SpannedForm::Nil) => (lhs, "x == nil"),
1063 (SpannedForm::Nil, _) if !matches!(rhs.form, SpannedForm::Nil) => (rhs, "nil == x"),
1064 _ => return None,
1065 };
1066 let span = self.span_since(start);
1067 self.note(Spelling::NilTest, span, written, "x.nil?");
1068 Some(list_at(
1069 span,
1070 vec![
1071 sym_at(op_span, LOWERED_FIELD),
1072 subject.clone(),
1073 atom_at(op_span, Atom::Keyword("nil?".to_string())),
1074 ],
1075 ))
1076 }
1077
1078 #[inline(never)]
1079 fn infix_node(
1080 &mut self,
1081 start: usize,
1082 callee: Option<&str>,
1083 op_span: Span,
1084 lhs: Spanned,
1085 rhs: Spanned,
1086 ) -> Spanned {
1087 if callee == Some("-") && matches!(lhs.form, SpannedForm::Atom(Atom::Int(0))) {
1088 self.note(
1089 Spelling::OperatorSpelling,
1090 lhs.span.merge(op_span),
1091 "0 - x",
1092 "-x",
1093 );
1094 }
1095
1096 if callee == Some("equal?") {
1097 if let Some(test) = self.nil_test(start, op_span, &lhs, &rhs) {
1098 return test;
1099 }
1100 }
1101
1102 if callee.is_none() {
1103 let rhs_span = rhs.span;
1108 let threaded = match rhs.form {
1109 SpannedForm::List(mut items) if !items.is_empty() => {
1110 items.insert(1, lhs);
1111 list_at(self.span_since(start), items)
1112 }
1113 callee => list_at(
1114 self.span_since(start),
1115 vec![Spanned::new(rhs_span, callee), lhs],
1116 ),
1117 };
1118 match &threaded.form {
1119 SpannedForm::List(items)
1120 if items.len() == 2 && items[0].as_symbol() == Some("not") =>
1121 {
1122 let (head, operand) = (items[0].span, items[1].clone());
1123 self.negation(head, threaded.span, operand)
1124 }
1125 SpannedForm::List(items)
1126 if items.len() == 4
1127 && items[0].as_symbol() == Some("if")
1128 && matches!(items[3].form, SpannedForm::Nil) =>
1129 {
1130 list_at(threaded.span, items[..3].to_vec())
1131 }
1132 SpannedForm::List(items)
1133 if items.len() == 3 && items[0].as_symbol() == Some("equal?") =>
1134 {
1135 let (op, a, b) = (items[0].span, items[1].clone(), items[2].clone());
1136 match self.nil_test(start, op, &a, &b) {
1137 Some(test) => test,
1138 None => threaded,
1139 }
1140 }
1141 _ => threaded,
1142 }
1143 } else {
1144 list_at(
1150 self.span_since(start),
1151 vec![sym_at(op_span, callee.unwrap_or_default()), lhs, rhs],
1152 )
1153 }
1154 }
1155
1156 fn expr_inner(&mut self, min_bp: u8) -> Result<Spanned, Fail> {
1157 let start = self.mark();
1158 let mut lhs = self.prefix()?;
1159
1160 loop {
1161 match self.peek() {
1163 TokenKind::Dot => {
1164 let dot = self.peek_span();
1165 self.bump();
1166 lhs = self.finish_send(start, lhs, dot)?;
1167 continue;
1168 }
1169 TokenKind::LParen => {
1170 let (args, splat) = self.call_args()?;
1171 let call = self.call_node(self.span_since(start), vec![lhs], args, splat);
1172 lhs = self.call_spelling(call);
1173 continue;
1174 }
1175 _ => {}
1176 }
1177
1178 let op_span = self.peek_span();
1182 if self.at_ident("rescue") {
1183 if RESCUE_POWER.0 < min_bp {
1184 break;
1185 }
1186 self.bump();
1187 self.skip_newlines();
1188 let fallback = self.expr(RESCUE_POWER.1)?;
1189 lhs = self.rescue_node(start, op_span, lhs, fallback);
1190 continue;
1191 }
1192 let (callee, (lbp, rbp)) = match self.peek() {
1193 TokenKind::Pipe => (None, PIPE_POWER),
1194 other => match infix_of(other) {
1195 Some(i) => (Some(i.callee), i.power),
1196 None => break,
1197 },
1198 };
1199 if lbp < min_bp {
1200 break;
1201 }
1202 self.infix_spelling(callee, op_span);
1203 self.bump();
1204 self.skip_newlines();
1205 let rhs = self.expr(rbp)?;
1206 lhs = self.infix_node(start, callee, op_span, lhs, rhs);
1207 }
1208
1209 Ok(lhs)
1210 }
1211
1212 #[inline(never)]
1213 fn interpolation(span: Span, parts: &[String], exprs: &[String]) -> Result<Spanned, Fail> {
1214 let mut acc = atom_at(span, Atom::Str(parts[0].clone()));
1224 for (i, raw) in exprs.iter().enumerate() {
1225 let inner = parse_expr(raw).map_err(|e| {
1226 fail(
1227 format!("in interpolation `#{{{raw}}}`: {}", e.message),
1228 span,
1229 )
1230 })?;
1231 let inner = Spanned::from_sexp_at(&inner, span);
1232 acc = list_at(span, vec![sym_at(span, LOWERED_CONCAT), acc, inner]);
1233 acc = list_at(
1236 span,
1237 vec![
1238 sym_at(span, LOWERED_CONCAT),
1239 acc,
1240 atom_at(span, Atom::Str(parts[i + 1].clone())),
1241 ],
1242 );
1243 }
1244 Ok(acc)
1245 }
1246
1247 #[inline(never)]
1248 fn not_prefix(&mut self, span: Span) -> Result<Spanned, Fail> {
1249 if self.at(&TokenKind::LParen) && self.touches_previous() {
1250 self.note(Spelling::OperatorSpelling, span, "not(x)", "not x");
1251 let (args, splat) = self.call_args()?;
1252 if args.len() != 1 || splat.is_some() {
1253 let call = self.call_node(
1254 self.span_since(span.start),
1255 vec![sym_at(span, "not")],
1256 args,
1257 splat,
1258 );
1259 return Ok(call);
1260 }
1261 let operand = args.into_iter().next().expect("one argument");
1262 return Ok(self.negation(span, self.span_since(span.start), operand));
1263 }
1264 let rhs = self.expr(11)?;
1265 Ok(self.negation(span, self.span_since(span.start), rhs))
1266 }
1267
1268 fn prefix(&mut self) -> Result<Spanned, Fail> {
1269 if self.at(&TokenKind::LParen) {
1270 let span = self.peek_span();
1271 self.bump();
1272 if self.arrow_lambda_ahead() {
1273 return self.arrow_lambda(span);
1274 }
1275 return self.group();
1276 }
1277 self.prefix_token()
1278 }
1279
1280 fn group(&mut self) -> Result<Spanned, Fail> {
1281 self.skip_newlines();
1282 let inner = self.expr(0)?;
1283 self.skip_newlines();
1284 self.expect(&TokenKind::RParen, "`)`")?;
1285 Ok(inner)
1286 }
1287
1288 #[inline(never)]
1289 fn prefix_token(&mut self) -> Result<Spanned, Fail> {
1290 let span = self.peek_span();
1291 match self.bump() {
1292 TokenKind::Int(v) => Ok(atom_at(span, Atom::Int(v))),
1293 TokenKind::Float(v) => Ok(atom_at(span, Atom::Float(v))),
1294 TokenKind::Str(s) => Ok(atom_at(span, Atom::Str(s))),
1295
1296 TokenKind::InterpolatedStr { parts, exprs } => Self::interpolation(span, &parts, &exprs),
1307 TokenKind::Sym(s) => Ok(atom_at(span, Atom::Keyword(s))),
1308 TokenKind::True => Ok(atom_at(span, Atom::Bool(true))),
1309 TokenKind::False => Ok(atom_at(span, Atom::Bool(false))),
1310 TokenKind::Nil => Ok(Spanned::new(span, SpannedForm::Nil)),
1311
1312 TokenKind::Op(o) if o == "-" => {
1313 let rhs = self.expr(11)?; Ok(list_at(
1315 self.span_since(span.start),
1316 vec![sym_at(span, "-"), atom_at(span, Atom::Int(0)), rhs],
1317 ))
1318 }
1319 TokenKind::Op(o) if o == "!" => {
1320 let written = self.text(span).to_string();
1321 self.note(Spelling::LogicalSymbol, span, &written, "not");
1322 let rhs = self.expr(11)?;
1323 Ok(self.negation(span, self.span_since(span.start), rhs))
1324 }
1325 TokenKind::Ident(w) if w == "not" => self.not_prefix(span),
1326 TokenKind::Ident(w) if (w == "and" || w == "or") && self.at(&TokenKind::LParen) => {
1327 Ok(sym_at(span, &w))
1328 }
1329 TokenKind::Ident(w) if w == "and" || w == "or" => Err(fail(
1330 format!("`{w}` joins two expressions: `a {w} b`"),
1331 span,
1332 )),
1333
1334 TokenKind::LParen => {
1335 self.skip_newlines();
1336 let inner = self.expr(0)?;
1337 self.skip_newlines();
1338 self.expect(&TokenKind::RParen, "`)`")?;
1339 Ok(inner)
1343 }
1344
1345 TokenKind::LBracket => self.list_literal(span),
1346 TokenKind::LBrace => self.map_literal(span),
1347
1348 TokenKind::Ident(name) if self.at(&TokenKind::PathSep) => {
1349 self.qualified_name(span, &name)
1350 }
1351 TokenKind::PathSep => Err(fail(
1352 "a qualified name is `package::name`, with the package written before `::`",
1353 span,
1354 )),
1355 TokenKind::Ident(name) => match name.as_str() {
1356 "if" => self.if_form(span, false),
1357 "begin" if !(self.at(&TokenKind::LParen) && self.touches_previous()) => {
1358 self.begin_form(span)
1359 }
1360 "rescue" | "ensure" => Err(fail(
1361 format!("`{name}` belongs to a `begin … end` block or a `def` body, after the statements it protects"),
1362 span,
1363 )),
1364 "unless" => {
1365 self.note(Spelling::Unless, span, "unless", "if not");
1366 self.if_form(span, true)
1367 }
1368 "def" => self.def_form(span),
1369 "defmacro" => self.defmacro_form(span),
1370 "case" => self.case_form(span),
1371 "fn" => self.lambda_form(span),
1372 "test" => self.test_form(span),
1373 "assert" => self.assert_form(span),
1374 "quote" => self.quote_form(span),
1375 "unquote" => self.unquote_form(span, false),
1376 "unquote_splice" => self.unquote_form(span, true),
1377 "do" => Err(fail(
1378 "blue has no `do` blocks: pass a function as an argument, \
1379 `f(xs, fn(x) … end)`",
1380 span,
1381 )),
1382 "end" => Err(fail(
1383 "unexpected `end`: nothing open here for it to close",
1384 span,
1385 )),
1386 _ => Ok(sym_at(span, &name)),
1387 },
1388
1389 other => Err(fail(format!("expected an expression, found {other}"), span)),
1390 }
1391 }
1392
1393 fn qualified_name(&mut self, pkg_span: Span, pkg: &str) -> Result<Spanned, Fail> {
1403 if is_reserved_word(pkg) {
1404 return Err(fail(
1405 format!("`{pkg}` is a reserved word and cannot name a package"),
1406 pkg_span,
1407 ));
1408 }
1409 self.bump(); let name_span = self.peek_span();
1411 let name = match self.bump() {
1412 TokenKind::Ident(n) => n,
1413 TokenKind::True => "true".to_string(),
1415 TokenKind::False => "false".to_string(),
1416 TokenKind::Nil => "nil".to_string(),
1417 other => {
1418 return Err(fail(
1419 format!("expected a name after `{pkg}::`, found {other}"),
1420 name_span,
1421 ))
1422 }
1423 };
1424 if self.at(&TokenKind::PathSep) {
1425 return Err(self.error(format!(
1426 "a qualified name has one qualifier: `package::name`, and `{pkg}::{name}::…` has two"
1427 )));
1428 }
1429 Ok(sym_at(
1430 self.span_since(pkg_span.start),
1431 &qualify(pkg, &name),
1432 ))
1433 }
1434
1435 #[inline(never)]
1442 fn negation(&mut self, head: Span, whole: Span, operand: Spanned) -> Spanned {
1443 if let SpannedForm::List(items) = &operand.form {
1444 let flipped = match items.first().and_then(Spanned::as_symbol) {
1445 Some("equal?") => Some("not="),
1446 Some("not=") => Some("equal?"),
1447 _ => None,
1448 };
1449 if let (Some(to), 3) = (flipped, items.len()) {
1450 let (written, canonical) = if to == "not=" {
1451 ("not (a == b)", "a != b")
1452 } else {
1453 ("not (a != b)", "a == b")
1454 };
1455 self.note(Spelling::NegatedComparison, whole, written, canonical);
1456 let mut out = items.clone();
1457 out[0] = sym_at(items[0].span, to);
1458 if to == "equal?" {
1459 if let Some(test) =
1460 self.nil_test(whole.start, items[0].span, &items[1], &items[2])
1461 {
1462 return test;
1463 }
1464 }
1465 return list_at(whole, out);
1466 }
1467 }
1468 list_at(whole, vec![sym_at(head, "not"), operand])
1469 }
1470
1471 #[inline(never)]
1472 fn call_spelling(&mut self, call: Spanned) -> Spanned {
1473 let SpannedForm::List(items) = &call.form else {
1474 return call;
1475 };
1476 let Some(head) = items.first().and_then(Spanned::as_symbol) else {
1477 return call;
1478 };
1479 let argc = items.len() - 1;
1480 if head == "equal?" && argc == 2 {
1481 let (op_span, start) = (items[0].span, call.span.start);
1482 if let Some(test) = self.nil_test(start, op_span, &items[1], &items[2]) {
1483 return test;
1484 }
1485 }
1486 let operator = match (head, argc) {
1487 ("and" | "or" | "mod" | "equal?", 2) => Some(head),
1488 ("list", _) => Some("list"),
1489 _ => None,
1490 };
1491 if let Some(op) = operator {
1492 let canonical = match op {
1493 "list" => "[…]".to_string(),
1494 "mod" => "a % b".to_string(),
1495 "equal?" => "a == b".to_string(),
1496 other => format!("a {other} b"),
1497 };
1498 self.note(
1499 Spelling::OperatorSpelling,
1500 call.span,
1501 &format!("{head}(…)"),
1502 &canonical,
1503 );
1504 return call;
1505 }
1506 if head == "apply"
1507 && self.text(items[0].span) == "apply"
1508 && argc >= 2
1509 && items[1].as_symbol().is_some_and(is_callable_name)
1510 {
1511 self.note(Spelling::Apply, call.span, "apply(f, …, xs)", "f(…, *xs)");
1512 return call;
1513 }
1514 if head == "try" {
1515 return self.try_spelling(call);
1516 }
1517 call
1518 }
1519
1520 fn try_spelling(&mut self, call: Spanned) -> Spanned {
1521 let SpannedForm::List(items) = &call.form else {
1522 return call;
1523 };
1524 let Some((catch, body)) = items[1..].split_last() else {
1525 return call;
1526 };
1527 let Some(clause) = catch.as_list() else {
1528 return call;
1529 };
1530 let binder = match clause {
1531 [head, binding, handler @ ..]
1532 if head.as_symbol() == Some("catch") && !handler.is_empty() =>
1533 {
1534 match binding.as_list() {
1535 Some([b]) => b.as_symbol().map(|n| (n.to_string(), b.span)),
1536 _ => None,
1537 }
1538 }
1539 _ => None,
1540 };
1541 let Some((name, binder_span)) = binder else {
1542 return call;
1543 };
1544 if body.is_empty() {
1545 return call;
1546 }
1547 self.note(Spelling::TryCatch, call.span, "try(…, catch(…))", "rescue");
1548 let handler = &clause[2..];
1549 let rename =
1550 name.starts_with('_') && name != "_" && !handler.iter().any(|h| mentions(h, &name));
1551 let body = if body.len() == 1 {
1552 body[0].clone()
1553 } else {
1554 let span = cover(body, call.span);
1555 let mut list = vec![sym_at(Span::new(span.start, span.start), "begin")];
1556 list.extend(body.iter().cloned());
1557 list_at(span, list)
1558 };
1559 let handler = if handler.len() == 1 {
1560 handler[0].clone()
1561 } else {
1562 let span = cover(handler, catch.span);
1563 let mut list = vec![sym_at(Span::new(span.start, span.start), "begin")];
1564 list.extend(handler.iter().cloned());
1565 list_at(span, list)
1566 };
1567 let binder = sym_at(binder_span, if rename { "_" } else { &name });
1568 list_at(
1569 call.span,
1570 vec![
1571 items[0].clone(),
1572 body,
1573 list_at(
1574 catch.span,
1575 vec![
1576 clause[0].clone(),
1577 list_at(clause[1].span, vec![binder]),
1578 handler,
1579 ],
1580 ),
1581 ],
1582 )
1583 }
1584
1585 fn finish_send(&mut self, start: usize, recv: Spanned, dot: Span) -> Result<Spanned, Fail> {
1586 let name_span = self.peek_span();
1587 let name = match self.bump() {
1588 TokenKind::Ident(n) if is_reserved_word(&n) => {
1602 return Err(self.error(format!(
1603 "`{n}` is a reserved word and cannot be a method name — \
1604 `recv.{n}` would lower onto the `{n}` form itself"
1605 )))
1606 }
1607 TokenKind::Ident(n) => n,
1608 other => {
1609 return Err(fail(
1610 format!("expected a method name after `.`, found {other}"),
1611 name_span,
1612 ))
1613 }
1614 };
1615 if !self.at(&TokenKind::LParen) {
1616 return Ok(list_at(
1617 self.span_since(start),
1618 vec![
1619 sym_at(dot, LOWERED_FIELD),
1620 recv,
1621 atom_at(name_span, Atom::Keyword(name)),
1622 ],
1623 ));
1624 }
1625 self.note(
1626 Spelling::CallForm,
1627 dot.merge(name_span),
1628 &format!("x.{name}(…)"),
1629 &format!("{name}(x, …)"),
1630 );
1631 let (args, splat) = self.call_args()?;
1632 Ok(self.call_node(
1633 self.span_since(start),
1634 vec![sym_at(name_span, &name), recv],
1635 args,
1636 splat,
1637 ))
1638 }
1639
1640 fn call_node(
1641 &self,
1642 span: Span,
1643 head: Vec<Spanned>,
1644 args: Vec<Spanned>,
1645 splat: Option<Span>,
1646 ) -> Spanned {
1647 let mut list = Vec::with_capacity(head.len() + args.len() + 1);
1648 if let Some(at) = splat {
1649 list.push(sym_at(at, "apply"));
1650 }
1651 list.extend(head);
1652 list.extend(args);
1653 list_at(span, list)
1654 }
1655
1656 fn call_args(&mut self) -> Result<(Vec<Spanned>, Option<Span>), Fail> {
1657 self.expect(&TokenKind::LParen, "`(`")?;
1658 let mut args = Vec::new();
1659 let mut splat = None;
1660 self.skip_newlines();
1661 if self.eat(&TokenKind::RParen) {
1662 return Ok((args, splat));
1663 }
1664 loop {
1665 self.skip_newlines();
1666 if splat.is_some() {
1667 return Err(
1668 self.error("a splat `*xs` passes a list as the LAST arguments: write it last")
1669 );
1670 }
1671 if self.at_spread() || matches!(self.peek(), TokenKind::Label(_)) {
1672 let open = self.peek_span();
1673 let (segments, spread) = self.map_entries(open, &TokenKind::RParen)?;
1674 if !spread {
1675 return Err(fail(
1676 "a keyword entry in a call needs a `**` spread: pass a map, f({a: 1})",
1677 open,
1678 ));
1679 }
1680 args.push(self.merged(open, LOWERED_KWARGS, segments));
1681 return Ok((args, splat));
1682 }
1683 if matches!(self.peek(), TokenKind::Op(o) if o == "*") {
1684 splat = Some(self.peek_span());
1685 self.bump();
1686 }
1687 args.push(self.expr(0)?);
1688 self.skip_newlines();
1689 if self.eat(&TokenKind::Comma) {
1690 if self.trailing_comma(&TokenKind::RParen) {
1691 break;
1692 }
1693 continue;
1694 }
1695 self.expect(&TokenKind::RParen, "`,` or `)`")?;
1696 break;
1697 }
1698 Ok((args, splat))
1699 }
1700
1701 fn trailing_comma(&mut self, closer: &TokenKind) -> bool {
1702 let comma = self
1703 .toks
1704 .get(self.pos.wrapping_sub(1))
1705 .map_or(self.peek_span(), |t| t.span);
1706 self.skip_newlines();
1707 if self.at(closer) {
1708 self.note(Spelling::TrailingComma, comma, ",", "");
1709 self.bump();
1710 return true;
1711 }
1712 false
1713 }
1714
1715 fn list_literal(&mut self, open: Span) -> Result<Spanned, Fail> {
1716 let mut items = vec![sym_at(open, "list")];
1717 self.skip_newlines();
1718 if self.eat(&TokenKind::RBracket) {
1719 return Ok(list_at(self.span_since(open.start), items));
1720 }
1721 loop {
1722 self.skip_newlines();
1723 items.push(self.expr(0)?);
1724 self.skip_newlines();
1725 if self.eat(&TokenKind::Comma) {
1726 if self.trailing_comma(&TokenKind::RBracket) {
1727 break;
1728 }
1729 continue;
1730 }
1731 self.expect(&TokenKind::RBracket, "`,` or `]`")?;
1732 break;
1733 }
1734 Ok(list_at(self.span_since(open.start), items))
1735 }
1736
1737 fn map_literal(&mut self, open: Span) -> Result<Spanned, Fail> {
1744 self.skip_newlines();
1745 if self.eat(&TokenKind::RBrace) {
1746 return Ok(list_at(
1747 self.span_since(open.start),
1748 vec![sym_at(open, LOWERED_MAP)],
1749 ));
1750 }
1751 let (mut segments, spread) = self.map_entries(open, &TokenKind::RBrace)?;
1752 if !spread {
1753 return Ok(segments.remove(0));
1754 }
1755 Ok(self.merged(open, LOWERED_MERGE, segments))
1756 }
1757
1758 fn merged(&self, open: Span, head: &str, segments: Vec<Spanned>) -> Spanned {
1759 let mut items = vec![sym_at(open, head)];
1760 items.extend(segments);
1761 list_at(self.span_since(open.start), items)
1762 }
1763
1764 fn at_spread(&self) -> bool {
1765 matches!(self.peek(), TokenKind::Op(o) if o == "**")
1766 }
1767
1768 fn map_entries(
1769 &mut self,
1770 open: Span,
1771 closer: &TokenKind,
1772 ) -> Result<(Vec<Spanned>, bool), Fail> {
1773 let mut segments = Vec::new();
1774 let mut items = vec![sym_at(open, LOWERED_MAP)];
1775 let mut spread = false;
1776 loop {
1777 self.skip_newlines();
1778 if self.at_spread() {
1779 let star = self.peek_span();
1780 self.bump();
1781 let value = self.expr(0)?;
1782 if items.len() > 1 {
1783 let span = items[1].span.merge(items[items.len() - 1].span);
1784 items[0] = sym_at(items[1].span, LOWERED_MAP);
1785 segments.push(list_at(span, std::mem::take(&mut items)));
1786 items = vec![sym_at(open, LOWERED_MAP)];
1787 }
1788 segments.push(list_at(
1789 star.merge(value.span),
1790 vec![sym_at(star, LOWERED_SPLICE), value],
1791 ));
1792 spread = true;
1793 } else {
1794 self.map_entry(&mut items, closer)?;
1795 }
1796 self.skip_newlines();
1797 if self.eat(&TokenKind::Comma) {
1798 if self.trailing_comma(closer) {
1799 break;
1800 }
1801 continue;
1802 }
1803 self.expect(closer, "`,` or the end of the entries")?;
1804 break;
1805 }
1806 if items.len() > 1 || !spread {
1807 let whole = self.span_since(open.start);
1808 segments.push(list_at(whole, items));
1809 }
1810 Ok((segments, spread))
1811 }
1812
1813 fn map_entry(&mut self, items: &mut Vec<Spanned>, closer: &TokenKind) -> Result<(), Fail> {
1814 match self.peek().clone() {
1815 TokenKind::Label(name) => {
1816 let label_span = self.peek_span();
1817 self.bump();
1818 self.skip_newlines();
1819 items.push(atom_at(label_span, Atom::Keyword(name.clone())));
1820 if matches!(self.peek(), TokenKind::Comma) || self.at(closer) {
1821 items.push(sym_at(label_span, &name));
1822 } else {
1823 let value = self.expr(0)?;
1824 if matches!(&value.form, SpannedForm::Atom(Atom::Symbol(s)) if *s == name)
1825 && label_name(&name)
1826 {
1827 let written = format!("{name}: {name}");
1828 let canonical = format!("{name}:");
1829 self.note(
1830 Spelling::MapPun,
1831 label_span.merge(value.span),
1832 &written,
1833 &canonical,
1834 );
1835 }
1836 items.push(value);
1837 }
1838 }
1839 _ => {
1840 let k = self.expr(0)?;
1841 self.skip_newlines();
1842 if let SpannedForm::Atom(Atom::Keyword(name)) = &k.form {
1843 if blue_lang_name(name) {
1844 let written = format!(":{name} =>");
1845 let canonical = format!("{name}:");
1846 self.note(Spelling::KeywordRocket, k.span, &written, &canonical);
1847 }
1848 }
1849 self.expect(&TokenKind::Rocket, "`=>` in a map literal")?;
1850 self.skip_newlines();
1851 items.push(k);
1852 items.push(self.expr(0)?);
1853 }
1854 }
1855 Ok(())
1856 }
1857
1858 fn if_chain(&mut self, head: Span) -> Result<Spanned, Fail> {
1868 let cond = self.expr(0)?;
1869 let then = self.body(&["elsif", "else", "end"])?;
1870 let els = self.else_part()?;
1871 let mut out = vec![sym_at(head, "if"), cond, then];
1872 if let Some(e) = els {
1873 out.push(e);
1874 }
1875 Ok(list_at(self.span_since(head.start), out))
1876 }
1877
1878 fn else_part(&mut self) -> Result<Option<Spanned>, Fail> {
1879 if self.at_ident("elsif") {
1880 let elsif = self.peek_span();
1881 self.bump();
1882 return Ok(Some(self.if_chain(elsif)?));
1883 }
1884 if !self.at_ident("else") {
1885 self.expect_ident("end")?;
1886 return Ok(None);
1887 }
1888 let else_span = self.peek_span();
1889 self.bump();
1890 let save = self.pos;
1891 self.skip_newlines();
1892 let leads_with_if = self.at_ident("if") || self.at_ident("unless");
1893 self.pos = save;
1894 let e = self.body(&["end"])?;
1895 let end_at = self.peek_span().start;
1896 self.expect_ident("end")?;
1897 if matches!(e.form, SpannedForm::Nil) && !self.comment_within(else_span.start, end_at) {
1898 self.note(Spelling::ElseNil, else_span, "else nil", "no else");
1899 return Ok(None);
1900 }
1901 let single_if = matches!(&e.form, SpannedForm::List(items)
1902 if (items.len() == 3 || items.len() == 4) && items[0].as_symbol() == Some("if"));
1903 if leads_with_if && single_if {
1904 self.note(Spelling::ElseIf, else_span, "else if", "elsif");
1905 }
1906 Ok(Some(e))
1907 }
1908
1909 fn if_form(&mut self, head: Span, negate: bool) -> Result<Spanned, Fail> {
1910 let cond = self.expr(0)?;
1911 let cond = if negate {
1912 let inner = cond.span;
1915 self.negation(head, head.merge(inner), cond)
1916 } else {
1917 cond
1918 };
1919 let then = self.body(&["elsif", "else", "end"])?;
1932 let els = self.else_part()?;
1933 let mut out = vec![sym_at(head, "if"), cond, then];
1934 if let Some(e) = els {
1935 out.push(e);
1936 }
1937 Ok(list_at(self.span_since(head.start), out))
1938 }
1939
1940 fn case_form(&mut self, head: Span) -> Result<Spanned, Fail> {
1966 let subject = self.expr(0)?;
1967 self.skip_newlines();
1968
1969 let subject_var = "case-subject";
1972 let mut arms: Vec<Spanned> = Vec::new();
1973 let mut otherwise: Option<Spanned> = None;
1974 let mut else_kw: Option<Span> = None;
1977
1978 loop {
1979 self.skip_newlines();
1980 if self.at_ident("end") {
1981 break;
1982 }
1983 let maybe_else = self.peek_span();
1984 if self.eat_ident("else") {
1985 else_kw = Some(maybe_else);
1986 otherwise = Some(self.body(&["end"])?);
1987 continue;
1988 }
1989 let when = self.peek_span();
1990 if !self.eat_ident("when") {
1991 return Err(self.error(format!(
1992 "expected `when`, `else` or `end` in a case, found {}",
1993 self.peek()
1994 )));
1995 }
1996 self.skip_newlines();
1997 let pattern = self.expr(0)?;
1998 let body = self.body(&["when", "else", "end"])?;
1999 let test = list_at(
2003 when.merge(pattern.span),
2004 vec![sym_at(when, "equal?"), sym_at(when, subject_var), pattern],
2005 );
2006 arms.push(list_at(when.merge(body.span), vec![test, body]));
2007 }
2008 self.expect_ident("end")?;
2009
2010 if arms.is_empty() && otherwise.is_none() {
2011 return Err(self.error("a case needs at least one `when` or an `else`".to_string()));
2012 }
2013
2014 let mut cond = vec![sym_at(head, "cond")];
2019 cond.extend(arms);
2020 let else_span = else_kw.unwrap_or(head);
2027 let else_body = otherwise.unwrap_or_else(|| Spanned::new(else_span, SpannedForm::Nil));
2028 cond.push(list_at(
2029 else_span.merge(else_body.span),
2030 vec![sym_at(else_span, "else"), else_body],
2031 ));
2032
2033 let whole = self.span_since(head.start);
2034 let binding = list_at(
2035 head.merge(subject.span),
2036 vec![sym_at(head, subject_var), subject],
2037 );
2038 Ok(list_at(
2039 whole,
2040 vec![
2041 sym_at(head, "let"),
2042 list_at(binding.span, vec![binding]),
2043 list_at(whole, cond),
2044 ],
2045 ))
2046 }
2047
2048 fn arrow_lambda_ahead(&self) -> bool {
2051 let mut i = self.pos;
2052 let kind = |i: usize| self.toks.get(i).map(|t| &t.kind);
2053 if !matches!(kind(i), Some(TokenKind::RParen)) {
2054 loop {
2055 if matches!(kind(i), Some(TokenKind::Op(o)) if o == "*") {
2056 i += 1;
2057 }
2058 if !matches!(kind(i), Some(TokenKind::Ident(_))) {
2059 return false;
2060 }
2061 i += 1;
2062 match kind(i) {
2063 Some(TokenKind::Comma) => i += 1,
2064 Some(TokenKind::RParen) => break,
2065 _ => return false,
2066 }
2067 }
2068 }
2069 matches!(kind(i + 1), Some(TokenKind::Op(o)) if o == "->")
2070 }
2071
2072 fn arrow_lambda(&mut self, head: Span) -> Result<Spanned, Fail> {
2079 let mut params: Vec<Spanned> = Vec::new();
2080 while !self.at(&TokenKind::RParen) {
2081 let p_span = self.peek_span();
2082 if self.at_star() {
2083 params.extend(self.rest_parameter()?);
2084 break;
2085 }
2086 match self.bump() {
2087 TokenKind::Ident(p) => params.push(sym_at(p_span, &p)),
2088 other => {
2089 return Err(fail(
2090 format!("expected a parameter name, found {other}"),
2091 p_span,
2092 ))
2093 }
2094 }
2095 self.eat(&TokenKind::Comma);
2096 }
2097 self.bump();
2098 self.bump();
2099 self.skip_newlines();
2100 let params_span = cover(¶ms, head);
2101 let body = self.expr(0)?;
2102 Ok(list_at(
2103 self.span_since(head.start),
2104 vec![sym_at(head, "lambda"), list_at(params_span, params), body],
2105 ))
2106 }
2107
2108 fn rest_parameter(&mut self) -> Result<[Spanned; 2], Fail> {
2109 let star = self.peek_span();
2110 self.bump();
2111 let name_span = self.peek_span();
2112 match self.bump() {
2113 TokenKind::Ident(p) => {
2114 let whole = star.merge(name_span);
2115 Ok([sym_at(whole, "&rest"), sym_at(whole, &p)])
2116 }
2117 other => Err(fail(
2118 format!("expected a parameter name after `*`, found {other}"),
2119 name_span,
2120 )),
2121 }
2122 }
2123
2124 fn at_star(&self) -> bool {
2125 matches!(self.peek(), TokenKind::Op(o) if o == "*")
2126 }
2127
2128 fn optional_mark(
2129 &self,
2130 name: String,
2131 span: Span,
2132 optional_from: &mut Option<usize>,
2133 index: usize,
2134 ) -> Result<String, Fail> {
2135 match name.strip_suffix('?').filter(|n| !n.is_empty()) {
2136 Some(bare) => {
2137 optional_from.get_or_insert(index);
2138 Ok(bare.to_string())
2139 }
2140 None if optional_from.is_some() => Err(fail(
2141 format!("the required parameter `{name}` cannot follow an optional one"),
2142 span,
2143 )),
2144 None => Ok(name),
2145 }
2146 }
2147
2148 fn rest_is_last(&mut self) -> Result<(), Fail> {
2149 self.skip_newlines();
2150 if self.at(&TokenKind::Comma) {
2151 return Err(self.error("a rest parameter `*name` comes last".to_string()));
2152 }
2153 Ok(())
2154 }
2155
2156 fn lambda_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2166 let mut params: Vec<Spanned> = Vec::new();
2167 if !self.at(&TokenKind::LParen) {
2168 self.note(Spelling::ParameterList, head, "fn", "fn()");
2169 }
2170 if self.at(&TokenKind::LParen) {
2171 self.bump();
2172 self.skip_newlines();
2173 if !self.eat(&TokenKind::RParen) {
2174 loop {
2175 self.skip_newlines();
2176 let p_span = self.peek_span();
2177 if self.at_star() {
2178 params.extend(self.rest_parameter()?);
2179 self.rest_is_last()?;
2180 self.expect(&TokenKind::RParen, "`)` after the rest parameter")?;
2181 break;
2182 }
2183 match self.bump() {
2184 TokenKind::Ident(p) => params.push(sym_at(p_span, &p)),
2185 other => {
2186 return Err(fail(
2187 format!("expected a parameter name, found {other}"),
2188 p_span,
2189 ))
2190 }
2191 }
2192 self.skip_newlines();
2193 if self.eat(&TokenKind::Comma) {
2194 if self.trailing_comma(&TokenKind::RParen) {
2195 break;
2196 }
2197 continue;
2198 }
2199 self.expect(&TokenKind::RParen, "`,` or `)`")?;
2200 break;
2201 }
2202 }
2203 }
2204 let params_span = cover(¶ms, head);
2205 let body = self.body(&["end"])?;
2206 self.expect_ident("end")?;
2207 Ok(list_at(
2208 self.span_since(head.start),
2209 vec![sym_at(head, "lambda"), list_at(params_span, params), body],
2210 ))
2211 }
2212
2213 fn test_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2219 let name_span = self.peek_span();
2220 let name = match self.bump() {
2221 TokenKind::Str(s) => s,
2222 other => {
2223 return Err(fail(
2224 format!("expected a string name after `test`, found {other}"),
2225 name_span,
2226 ))
2227 }
2228 };
2229 let body = self.body(&["end"])?;
2230 self.expect_ident("end")?;
2231 Ok(list_at(
2232 self.span_since(head.start),
2233 vec![
2234 sym_at(head, "deftest"),
2235 atom_at(name_span, Atom::Str(name)),
2236 body,
2237 ],
2238 ))
2239 }
2240
2241 fn assert_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2265 let e = self.expr(0)?;
2266 let quoted = Spanned::new(e.span, SpannedForm::Quote(Box::new(e.clone())));
2267 Ok(list_at(
2268 self.span_since(head.start),
2269 vec![sym_at(head, LOWERED_ASSERT), quoted, e],
2270 ))
2271 }
2272
2273 fn defmacro_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2282 let name_span = self.peek_span();
2283 let name = match self.bump() {
2284 TokenKind::Ident(n) => n,
2285 other => {
2286 return Err(fail(
2287 format!("expected a name after `defmacro`, found {other}"),
2288 name_span,
2289 ))
2290 }
2291 };
2292 self.not_reserved(&name, name_span, "a `defmacro` name")?;
2293 let mut params: Vec<Spanned> = Vec::new();
2294 if self.at(&TokenKind::LParen) {
2295 self.bump();
2296 self.skip_newlines();
2297 if !self.eat(&TokenKind::RParen) {
2298 loop {
2299 self.skip_newlines();
2300 let p_span = self.peek_span();
2301 match self.bump() {
2302 TokenKind::Ident(p) => params.push(sym_at(p_span, &p)),
2303 TokenKind::Label(p) => {
2304 return Err(self.error(format!(
2305 "macro parameter `{p}` cannot be typed: a macro receives \
2306 source forms, not values"
2307 )))
2308 }
2309 other => {
2310 return Err(fail(
2311 format!("expected a parameter name, found {other}"),
2312 p_span,
2313 ))
2314 }
2315 }
2316 self.skip_newlines();
2317 if self.eat(&TokenKind::Comma) {
2318 if self.trailing_comma(&TokenKind::RParen) {
2319 break;
2320 }
2321 continue;
2322 }
2323 self.expect(&TokenKind::RParen, "`,` or `)`")?;
2324 break;
2325 }
2326 }
2327 }
2328 if matches!(self.peek(), TokenKind::Op(o) if o == "->") {
2329 return Err(self.error(
2330 "a macro has no return type: it produces source forms, not values".to_string(),
2331 ));
2332 }
2333
2334 let params_span = cover(¶ms, name_span);
2335 let body = self.body(&["end"])?;
2336 self.expect_ident("end")?;
2337
2338 Ok(list_at(
2341 self.span_since(head.start),
2342 vec![
2343 sym_at(head, "defmacro"),
2344 sym_at(name_span, &name),
2345 list_at(params_span, params),
2346 body,
2347 ],
2348 ))
2349 }
2350
2351 fn quote_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2357 let body = self.body(&["end"])?;
2358 self.expect_ident("end")?;
2359 Ok(Spanned::new(
2368 self.span_since(head.start),
2369 SpannedForm::Quasiquote(Box::new(body)),
2370 ))
2371 }
2372
2373 fn unquote_form(&mut self, head: Span, splice: bool) -> Result<Spanned, Fail> {
2375 self.expect(&TokenKind::LParen, "`(` after unquote")?;
2376 self.skip_newlines();
2377 let inner = self.expr(0)?;
2378 self.skip_newlines();
2379 self.expect(&TokenKind::RParen, "`)`")?;
2380 let span = self.span_since(head.start);
2381 Ok(Spanned::new(
2382 span,
2383 if splice {
2384 SpannedForm::UnquoteSplice(Box::new(inner))
2385 } else {
2386 SpannedForm::Unquote(Box::new(inner))
2387 },
2388 ))
2389 }
2390
2391 fn protected_body(&mut self, head: Span) -> Result<Spanned, Fail> {
2392 let body = self.body(&["rescue", "ensure", "end"])?;
2393 if self.at_ident("rescue") || self.at_ident("ensure") {
2394 return self.rescue_tail(head, body);
2395 }
2396 Ok(body)
2397 }
2398
2399 fn begin_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2400 let body = self.body(&["rescue", "ensure", "end"])?;
2401 if !(self.at_ident("rescue") || self.at_ident("ensure")) {
2402 return Err(self.error(
2403 "a `begin` block protects its statements with `rescue` or `ensure`; without either, write the statements directly",
2404 ));
2405 }
2406 let tree = self.rescue_tail(head, body)?;
2407 self.expect_ident("end")?;
2408 Ok(Spanned::new(self.span_since(head.start), tree.form))
2409 }
2410
2411 fn rescue_tail(&mut self, head: Span, body: Spanned) -> Result<Spanned, Fail> {
2412 let mut clauses: Vec<Spanned> = Vec::new();
2413 let mut catch_all: Option<(Spanned, Spanned, Span)> = None;
2414 while self.at_ident("rescue") {
2415 let kw = self.peek_span();
2416 self.bump();
2417 if catch_all.is_some() {
2418 return Err(fail(
2419 "a `rescue NAME` catches every error, so it is the last `rescue`; put the kinds it would shadow above it",
2420 kw,
2421 ));
2422 }
2423 match self.peek().clone() {
2424 TokenKind::Sym(_) => {
2425 let mut kinds = Vec::new();
2426 loop {
2427 let k_span = self.peek_span();
2428 match self.bump() {
2429 TokenKind::Sym(k) => kinds.push(atom_at(k_span, Atom::Keyword(k))),
2430 other => {
2431 return Err(fail(
2432 format!("expected an error kind such as `:not_found`, found {other}"),
2433 k_span,
2434 ))
2435 }
2436 }
2437 if self.eat(&TokenKind::Comma) {
2438 self.skip_newlines();
2439 continue;
2440 }
2441 break;
2442 }
2443 let binder = if self.eat_ident("as") {
2444 let b_span = self.peek_span();
2445 match self.bump() {
2446 TokenKind::Ident(b) if !is_reserved_word(&b) => Some(sym_at(b_span, &b)),
2447 other => {
2448 return Err(fail(
2449 format!("expected the name the error is bound to after `as`, found {other}"),
2450 b_span,
2451 ))
2452 }
2453 }
2454 } else {
2455 None
2456 };
2457 let handler = self.body(&["rescue", "ensure", "end"])?;
2458 let mut test = vec![sym_at(kw, LOWERED_ERROR_MATCH), sym_at(kw, RESCUED)];
2459 test.extend(kinds);
2460 let test = list_at(cover(&test, kw), test);
2461 let handler = match binder {
2462 Some(b) => let_bound(b, handler, kw),
2463 None => handler,
2464 };
2465 clauses.push(list_at(kw.merge(handler.span), vec![test, handler]));
2466 }
2467 TokenKind::Ident(b) if !is_reserved_word(&b) => {
2468 let b_span = self.peek_span();
2469 self.bump();
2470 let handler = self.body(&["rescue", "ensure", "end"])?;
2471 catch_all = Some((sym_at(b_span, &b), handler, kw));
2472 }
2473 _ => {
2474 return Err(fail(
2475 "`rescue` names what it catches: `rescue :kind`, `rescue :a, :b as e`, or `rescue e` for every error (`rescue _` discards it)",
2476 kw,
2477 ))
2478 }
2479 }
2480 }
2481 let ensure = if self.at_ident("ensure") {
2482 self.bump();
2483 let cleanup = self.body(&["rescue", "end"])?;
2484 if self.at_ident("rescue") {
2485 return Err(self.error("`ensure` comes after every `rescue` of its block"));
2486 }
2487 Some(cleanup)
2488 } else {
2489 None
2490 };
2491 let whole = self.span_since(head.start);
2492 let protected = if clauses.is_empty() {
2493 match catch_all {
2494 None => body,
2495 Some((binder, handler, kw)) => list_at(
2496 whole,
2497 vec![
2498 sym_at(head, "try"),
2499 body,
2500 list_at(
2501 kw.merge(handler.span),
2502 vec![
2503 sym_at(kw, "catch"),
2504 list_at(binder.span, vec![binder]),
2505 handler,
2506 ],
2507 ),
2508 ],
2509 ),
2510 }
2511 } else {
2512 let first_kw = clauses[0].span;
2513 let otherwise = match catch_all {
2514 Some((binder, handler, kw)) => {
2515 let bound = let_bound(binder, handler, kw);
2516 list_at(kw.merge(bound.span), vec![sym_at(kw, "else"), bound])
2517 }
2518 None => list_at(
2519 head,
2520 vec![
2521 sym_at(head, "else"),
2522 list_at(
2523 head,
2524 vec![sym_at(head, LOWERED_RERAISE), sym_at(head, RESCUED)],
2525 ),
2526 ],
2527 ),
2528 };
2529 let mut cond = vec![sym_at(first_kw, "cond")];
2530 cond.extend(clauses);
2531 cond.push(otherwise);
2532 let cond_span = cover(&cond, first_kw);
2533 list_at(
2534 whole,
2535 vec![
2536 sym_at(head, "try"),
2537 body,
2538 list_at(
2539 cond_span,
2540 vec![
2541 sym_at(first_kw, "catch"),
2542 list_at(first_kw, vec![sym_at(first_kw, RESCUED)]),
2543 list_at(cond_span, cond),
2544 ],
2545 ),
2546 ],
2547 )
2548 };
2549 Ok(match ensure {
2550 None => protected,
2551 Some(cleanup) => {
2552 let attempted = list_at(
2553 protected.span,
2554 vec![sym_at(head, LOWERED_ATTEMPTED), protected],
2555 );
2556 let failed = list_at(
2557 head,
2558 vec![sym_at(head, LOWERED_FAILED), sym_at(head, RESCUED)],
2559 );
2560 let caught = list_at(
2561 whole,
2562 vec![
2563 sym_at(head, "try"),
2564 attempted,
2565 list_at(
2566 head,
2567 vec![
2568 sym_at(head, "catch"),
2569 list_at(head, vec![sym_at(head, RESCUED)]),
2570 failed,
2571 ],
2572 ),
2573 ],
2574 );
2575 list_at(
2576 whole,
2577 vec![sym_at(head, LOWERED_ENSURE), caught, thunk(cleanup, head)],
2578 )
2579 }
2580 })
2581 }
2582
2583 fn def_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2584 let name_span = self.peek_span();
2585 let name = match self.bump() {
2586 TokenKind::Ident(n) => n,
2587 other => {
2588 return Err(fail(
2589 format!("expected a name after `def`, found {other}"),
2590 name_span,
2591 ))
2592 }
2593 };
2594 self.not_reserved(&name, name_span, "a `def` name")?;
2595 let mut params: Vec<(String, Span, Option<Spanned>)> = Vec::new();
2596 let mut optional_from: Option<usize> = None;
2597 let mut rest: Vec<Spanned> = Vec::new();
2598 if !self.at(&TokenKind::LParen) {
2599 self.note(
2600 Spelling::ParameterList,
2601 name_span,
2602 &format!("def {name}"),
2603 &format!("def {name}()"),
2604 );
2605 }
2606 if self.at(&TokenKind::LParen) {
2607 self.bump();
2608 self.skip_newlines();
2609 if !self.eat(&TokenKind::RParen) {
2610 loop {
2611 self.skip_newlines();
2612 let p_span = self.peek_span();
2613 if self.at_star() {
2614 if optional_from.is_some() {
2615 return Err(fail(
2616 "a rest parameter cannot follow an optional one",
2617 p_span,
2618 ));
2619 }
2620 rest = self.rest_parameter()?.to_vec();
2621 self.rest_is_last()?;
2622 self.expect(&TokenKind::RParen, "`)` after the rest parameter")?;
2623 break;
2624 }
2625 match self.bump() {
2626 TokenKind::Ident(p) => {
2628 self.not_reserved(&p, p_span, "a parameter name")?;
2629 let p =
2630 self.optional_mark(p, p_span, &mut optional_from, params.len())?;
2631 params.push((p, p_span, None));
2632 }
2633 TokenKind::Label(p) => {
2635 self.not_reserved(&p, p_span, "a parameter name")?;
2636 let p =
2637 self.optional_mark(p, p_span, &mut optional_from, params.len())?;
2638 self.skip_newlines();
2639 let ty = self.type_expr()?;
2640 params.push((p, p_span, Some(ty)));
2641 }
2642 other => {
2643 return Err(fail(
2644 format!("expected a parameter name, found {other}"),
2645 p_span,
2646 ))
2647 }
2648 }
2649 self.skip_newlines();
2650 if self.eat(&TokenKind::Comma) {
2651 if self.trailing_comma(&TokenKind::RParen) {
2652 break;
2653 }
2654 continue;
2655 }
2656 self.expect(&TokenKind::RParen, "`,` or `)`")?;
2657 break;
2658 }
2659 }
2660 }
2661 let sig_span = self.span_since(name_span.start);
2663
2664 let ret = if matches!(self.peek(), TokenKind::Op(o) if o == "->") {
2666 self.bump();
2667 self.skip_newlines();
2668 Some(self.type_expr()?)
2669 } else {
2670 None
2671 };
2672
2673 let begins = {
2674 let save = self.pos;
2675 self.skip_newlines();
2676 let at = self.at_ident("begin").then(|| self.peek_span());
2677 self.pos = save;
2678 at
2679 };
2680 let body = self.protected_body(head)?;
2681 if let Some(b) = begins {
2682 let protects = matches!(&body.form, SpannedForm::List(items)
2683 if matches!(items.first().and_then(Spanned::as_symbol), Some("try") | Some(LOWERED_ENSURE)));
2684 if protects && body.span.start == b.start {
2685 self.note(
2686 Spelling::TryCatch,
2687 b,
2688 "begin … end as a whole def body",
2689 "the clauses on the def itself",
2690 );
2691 }
2692 }
2693 self.expect_ident("end")?;
2694 let whole = self.span_since(head.start);
2695
2696 let annotated = ret.is_some() || params.iter().any(|(_, _, t)| t.is_some());
2697 if !annotated {
2698 let mut sig = vec![sym_at(name_span, &name)];
2699 for (i, (p, s, _)) in params.into_iter().enumerate() {
2700 if optional_from == Some(i) {
2701 sig.push(sym_at(s, OPTIONAL_MARKER));
2702 }
2703 sig.push(sym_at(s, &p));
2704 }
2705 sig.extend(rest);
2706 return Ok(list_at(
2707 whole,
2708 vec![sym_at(head, "define"), list_at(sig_span, sig), body],
2709 ));
2710 }
2711
2712 let mut sig = vec![sym_at(name_span, &name)];
2720 for (i, (p, s, t)) in params.into_iter().enumerate() {
2721 if optional_from == Some(i) {
2722 sig.push(sym_at(s, OPTIONAL_MARKER));
2723 }
2724 let ty = t.unwrap_or_else(|| sym_at(s, "dyn"));
2725 sig.push(list_at(s.merge(ty.span), vec![sym_at(s, &p), ty]));
2726 }
2727 sig.extend(rest);
2728 Ok(list_at(
2729 whole,
2730 vec![
2731 sym_at(head, "define-typed"),
2732 list_at(sig_span, sig),
2733 ret.unwrap_or_else(|| sym_at(head, "dyn")),
2734 body,
2735 ],
2736 ))
2737 }
2738
2739 fn type_expr(&mut self) -> Result<Spanned, Fail> {
2741 let name_span = self.peek_span();
2742 let name = match self.bump() {
2743 TokenKind::Ident(n) => n,
2744 other => {
2745 return Err(fail(
2746 format!("expected a type name, found {other}"),
2747 name_span,
2748 ))
2749 }
2750 };
2751 self.not_reserved(&name, name_span, "a type name")?;
2752 if self.at(&TokenKind::LParen) {
2753 self.bump();
2754 self.skip_newlines();
2755 let mut list = vec![sym_at(name_span, &name)];
2756 if !self.eat(&TokenKind::RParen) {
2757 loop {
2758 self.skip_newlines();
2759 list.push(self.type_expr()?);
2760 self.skip_newlines();
2761 if self.eat(&TokenKind::Comma) {
2762 if self.trailing_comma(&TokenKind::RParen) {
2763 break;
2764 }
2765 continue;
2766 }
2767 self.expect(&TokenKind::RParen, "`,` or `)`")?;
2768 break;
2769 }
2770 }
2771 return Ok(list_at(self.span_since(name_span.start), list));
2772 }
2773 Ok(sym_at(name_span, &name))
2774 }
2775
2776 fn not_reserved(&self, name: &str, at: Span, what: &str) -> Result<(), Fail> {
2777 if is_reserved_word(name) {
2778 return Err(fail(
2779 format!("`{name}` is a reserved word and cannot be {what}"),
2780 at,
2781 ));
2782 }
2783 Ok(())
2784 }
2785
2786 fn eat_ident(&mut self, name: &str) -> bool {
2788 if self.at_ident(name) {
2789 self.bump();
2790 true
2791 } else {
2792 false
2793 }
2794 }
2795
2796 fn expect_ident(&mut self, name: &str) -> Result<(), Fail> {
2797 if self.at_ident(name) {
2798 self.bump();
2799 Ok(())
2800 } else {
2801 Err(self.error(format!("expected `{name}`, found {}", self.peek())))
2802 }
2803 }
2804
2805 fn body(&mut self, terminators: &[&str]) -> Result<Spanned, Fail> {
2808 let start = self.mark();
2811 let mut forms = Vec::new();
2812 loop {
2813 self.skip_newlines();
2814 if matches!(self.peek(), TokenKind::Eof) {
2815 return Err(self.error(format!(
2816 "unterminated block: expected {} before the end of the file",
2817 one_of(terminators)
2818 )));
2819 }
2820 if terminators.iter().any(|t| self.at_ident(t)) {
2821 break;
2822 }
2823 forms.push(self.statement()?);
2824 self.end_of_statement(terminators)?;
2825 }
2826 Ok(match forms.len() {
2827 0 => Spanned::new(Span::new(start, start), SpannedForm::Nil),
2828 1 => forms.into_iter().next().expect("checked len"),
2829 _ => {
2830 let span = cover(&forms, Span::new(start, start));
2836 let head = Span::new(span.start, span.start);
2842 let mut list = vec![sym_at(head, "begin")];
2843 list.extend(forms);
2844 list_at(span, list)
2845 }
2846 })
2847 }
2848}
2849
2850fn one_of(words: &[&str]) -> String {
2853 let quoted: Vec<String> = words.iter().map(|w| format!("`{w}`")).collect();
2854 match quoted.split_last() {
2855 None => String::new(),
2856 Some((last, [])) => last.clone(),
2857 Some((last, rest)) => format!("{} or {last}", rest.join(", ")),
2858 }
2859}
2860
2861fn let_bound(binder: Spanned, body: Spanned, at: Span) -> Spanned {
2862 let span = at.merge(body.span);
2863 let pair = list_at(binder.span, vec![binder, sym_at(at, RESCUED)]);
2864 list_at(
2865 span,
2866 vec![sym_at(at, "let"), list_at(pair.span, vec![pair]), body],
2867 )
2868}
2869
2870fn thunk(body: Spanned, at: Span) -> Spanned {
2871 let span = at.merge(body.span);
2872 list_at(
2873 span,
2874 vec![sym_at(at, "lambda"), list_at(at, Vec::new()), body],
2875 )
2876}
2877
2878#[must_use]
2883pub fn label_name(name: &str) -> bool {
2884 matches!(
2885 lex(&format!("{name}:")).as_deref(),
2886 Ok([t, e]) if matches!(&t.kind, TokenKind::Label(n) if n == name) && e.kind == TokenKind::Eof
2887 )
2888}
2889
2890fn blue_lang_name(name: &str) -> bool {
2891 matches!(
2892 lex(name).as_deref(),
2893 Ok([t, e]) if matches!(&t.kind, TokenKind::Ident(n) if n == name) && e.kind == TokenKind::Eof
2894 ) && !name.ends_with(['?', '!'])
2895}
2896
2897fn mentions(form: &Spanned, name: &str) -> bool {
2898 match &form.form {
2899 SpannedForm::Atom(Atom::Symbol(s)) => s == name,
2900 SpannedForm::List(items) => items.iter().any(|i| mentions(i, name)),
2901 SpannedForm::Quote(i)
2902 | SpannedForm::Quasiquote(i)
2903 | SpannedForm::Unquote(i)
2904 | SpannedForm::UnquoteSplice(i) => mentions(i, name),
2905 _ => false,
2906 }
2907}
2908
2909fn sym_at(span: Span, s: &str) -> Spanned {
2911 Spanned::new(span, SpannedForm::Atom(Atom::Symbol(s.to_string())))
2912}
2913
2914fn atom_at(span: Span, a: Atom) -> Spanned {
2916 Spanned::new(span, SpannedForm::Atom(a))
2917}
2918
2919fn list_at(span: Span, items: Vec<Spanned>) -> Spanned {
2921 Spanned::new(span, SpannedForm::List(items))
2922}
2923
2924fn cover(items: &[Spanned], fallback: Span) -> Span {
2932 let merged = items
2933 .iter()
2934 .fold(Span::synthetic(), |acc, s| acc.merge(s.span));
2935 if merged.is_synthetic() {
2936 fallback
2937 } else {
2938 merged
2939 }
2940}
2941
2942#[cfg(test)]
2943mod tests {
2944 use super::*;
2945
2946 fn q(src: &str) -> String {
2950 parse_expr(src)
2951 .map(|s| s.to_string())
2952 .unwrap_or_else(|e| panic!("{src:?}: {e}"))
2953 }
2954
2955 #[test]
2956 fn a_reserved_word_is_not_a_def_name_a_parameter_or_a_type() {
2957 for src in [
2958 "def def a(x)\n x\nend",
2959 "def end()\n 1\nend",
2960 "def f(end)\n 1\nend",
2961 "def f(a: not)\n 1\nend",
2962 "def f(a) -> not a\n a\nend",
2963 "def f(a: List(1 + 2))\n a\nend",
2964 "defmacro defmacro m(a)\n quote\n a\n end\nend",
2965 ] {
2966 assert!(parse_program(src).is_err(), "{src:?} parsed");
2967 }
2968 }
2969
2970 #[test]
2971 fn a_type_application_takes_types_of_any_arity() {
2972 assert_eq!(
2973 q("def f(a: Map(Str, List(Int))) -> Unit()\n a\nend"),
2974 "(define-typed (f (a (Map Str (List Int)))) (Unit) a)"
2975 );
2976 }
2977
2978 #[test]
2981 fn arithmetic_respects_precedence() {
2982 assert_eq!(q("1 + 2 * 3"), "(+ 1 (* 2 3))");
2983 assert_eq!(q("(1 + 2) * 3"), "(* (+ 1 2) 3)");
2984 }
2985
2986 #[test]
2987 fn comparison_binds_looser_than_arithmetic() {
2988 assert_eq!(q("a + 1 < b"), "(< (+ a 1) b)");
2989 }
2990
2991 #[test]
2996 fn logical_operators_bind_loosest_and_lower_to_tataras_names() {
2997 assert_eq!(q("a && b || c"), "(or (and a b) c)");
2998 }
2999
3000 #[test]
3001 fn left_associativity() {
3002 assert_eq!(q("1 - 2 - 3"), "(- (- 1 2) 3)");
3003 }
3004
3005 #[test]
3006 fn a_dot_without_parentheses_reads_a_field() {
3007 assert_eq!(q("user.name"), "(blue-field user :name)");
3008 }
3009
3010 #[test]
3011 fn method_call_with_args() {
3012 assert_eq!(q("user.greet(1, 2)"), "(greet user 1 2)");
3013 }
3014
3015 #[test]
3016 fn chained_fields_read_left_to_right() {
3017 assert_eq!(q("a.b.c"), "(blue-field (blue-field a :b) :c)");
3018 assert_eq!(q("a.b(1).c"), "(blue-field (b a 1) :c)");
3019 }
3020
3021 #[test]
3022 fn plain_call() {
3023 assert_eq!(q("f(1, 2)"), "(f 1 2)");
3024 }
3025
3026 #[test]
3029 fn pipeline_threads_into_first_argument() {
3030 assert_eq!(q("x |> f"), "(f x)");
3031 assert_eq!(q("x |> f(1)"), "(f x 1)");
3032 assert_eq!(q("x |> f |> g"), "(g (f x))");
3033 }
3034
3035 #[test]
3036 fn pipeline_binds_looser_than_arithmetic() {
3037 assert_eq!(q("1 + 2 |> f"), "(f (+ 1 2))");
3038 }
3039
3040 #[test]
3046 fn label_and_rocket_produce_the_same_tree_for_a_symbol_key() {
3047 assert_eq!(q("{a: 1}"), q("{:a => 1}"));
3048 assert_eq!(q("{a: 1}"), "(hash-map :a 1)");
3049 }
3050
3051 #[test]
3054 fn a_string_key_has_no_shorthand() {
3055 assert_eq!(q(r#"{"k" => 1}"#), r#"(hash-map "k" 1)"#);
3056 }
3057
3058 #[test]
3059 fn list_literal() {
3060 assert_eq!(q("[1, 2, 3]"), "(list 1 2 3)");
3061 assert_eq!(q("[]"), "(list)");
3062 }
3063
3064 #[test]
3067 fn if_else_end() {
3068 assert_eq!(q("if a\n 1\nelse\n 2\nend"), "(if a 1 2)");
3069 }
3070
3071 #[test]
3072 fn if_without_else() {
3073 assert_eq!(q("if a\n 1\nend"), "(if a 1)");
3074 }
3075
3076 #[test]
3079 fn unless_is_a_negated_if() {
3080 assert_eq!(q("unless a\n 1\nend"), "(if (not a) 1)");
3081 }
3082
3083 #[test]
3084 fn multi_statement_body_becomes_begin() {
3085 assert_eq!(q("if a\n 1\n 2\nend"), "(if a (begin 1 2))");
3086 }
3087
3088 #[test]
3089 fn def_lowers_to_define() {
3090 assert_eq!(
3091 q("def add(a, b)\n a + b\nend"),
3092 "(define (add a b) (+ a b))"
3093 );
3094 }
3095
3096 #[test]
3097 fn def_with_no_params() {
3098 assert_eq!(q("def zero()\n 0\nend"), "(define (zero) 0)");
3099 }
3100
3101 fn refused(src: &str) -> String {
3102 match parse_expr(src) {
3103 Ok(s) => panic!("{src:?} should be refused, parsed to {s}"),
3104 Err(e) => e.to_string(),
3105 }
3106 }
3107
3108 #[test]
3109 fn a_question_mark_parameter_is_optional() {
3110 assert_eq!(q("def f(a, b?)\n b\nend"), "(define (f a &optional b) b)");
3111 assert_eq!(q("def f(a?, b?)\n a\nend"), "(define (f &optional a b) a)");
3112 assert_eq!(q("def f(a?)\n a\nend"), "(define (f &optional a) a)");
3113 }
3114
3115 #[test]
3116 fn an_annotated_optional_parameter_keeps_its_type_after_the_marker() {
3117 assert_eq!(
3118 q("def f(a: Int, b?: Str) -> Int\n a\nend"),
3119 "(define-typed (f (a Int) &optional (b Str)) Int a)"
3120 );
3121 assert_eq!(
3122 q("def f(a, b?: Str)\n a\nend"),
3123 "(define-typed (f (a dyn) &optional (b Str)) dyn a)"
3124 );
3125 }
3126
3127 #[test]
3128 fn a_def_without_question_marks_carries_no_marker() {
3129 assert_eq!(q("def f(a, b)\n a\nend"), "(define (f a b) a)");
3130 assert_eq!(q("def f(a, *r)\n a\nend"), "(define (f a &rest r) a)");
3131 }
3132
3133 #[test]
3134 fn a_required_parameter_cannot_follow_an_optional_one() {
3135 assert!(refused("def f(a?, b)\n a\nend").contains("`b` cannot follow an optional"));
3136 assert!(refused("def f(a, b?, c: Int)\n a\nend").contains("`c` cannot follow"));
3137 }
3138
3139 #[test]
3140 fn a_rest_parameter_cannot_follow_an_optional_one() {
3141 assert!(refused("def f(a, b?, *r)\n a\nend").contains("rest parameter cannot follow"));
3142 }
3143
3144 #[test]
3147 fn literals_lower_to_atoms() {
3148 assert_eq!(q("42"), "42");
3149 assert_eq!(q("true"), "#t");
3150 assert_eq!(q(":ok"), ":ok");
3151 assert_eq!(q(r#""hi""#), r#""hi""#);
3152 }
3153
3154 #[test]
3155 fn unary_minus_and_not() {
3156 assert_eq!(q("-x"), "(- 0 x)");
3157 assert_eq!(q("!x"), "(not x)");
3158 }
3159
3160 #[test]
3163 fn a_program_is_a_sequence_of_forms() {
3164 let forms = parse_program("def f()\n 1\nend\nf()").expect("parse");
3165 assert_eq!(forms.len(), 2);
3166 assert_eq!(forms[1].to_string(), "(f)");
3167 }
3168
3169 #[test]
3170 fn unterminated_block_is_an_error_naming_what_was_expected() {
3171 let e = parse_program("if a\n 1").expect_err("must fail");
3172 assert!(e.message.contains("unterminated"), "{}", e.message);
3173 }
3174
3175 #[test]
3176 fn a_parse_error_carries_a_span_into_the_source() {
3177 let src = "1 + )";
3178 let e = parse_program(src).expect_err("must fail");
3179 assert!(e.span.start < src.len(), "span {:?} outside source", e.span);
3180 }
3181
3182 #[test]
3189 fn postfix_indexing_is_refused_naming_nth() {
3190 let e = parse_program("y = xs[0]").expect_err("must not split into two statements");
3191 assert!(e.message.contains("`nth(0, xs)`"), "{}", e.message);
3192 assert_eq!((e.line, e.col), (1, 7), "{e}");
3193 }
3194
3195 #[test]
3197 fn a_call_without_parentheses_is_refused_naming_the_call() {
3198 let e = parse_program("a = f x").expect_err("must not split into two statements");
3199 assert!(e.message.contains("`f(x)`"), "{}", e.message);
3200 assert_eq!((e.line, e.col), (1, 7), "{e}");
3201 }
3202
3203 #[test]
3205 fn two_statements_on_one_line_are_refused_in_a_body() {
3206 for src in [
3207 "def f(xs)\n xs[0]\nend",
3208 "if a\n f x\nend",
3209 "fn(x) x 1 end",
3210 "test \"t\"\n assert a b\nend",
3211 "1 2",
3212 ] {
3213 assert!(parse_program(src).is_err(), "{src:?} must not parse");
3214 }
3215 }
3216
3217 #[test]
3219 fn an_arrow_lambda_lowers_to_the_same_tree_as_fn() {
3220 for (arrow, block) in [
3221 ("(x) -> x * 2", "fn(x) x * 2 end"),
3222 ("(a, b) -> a + b", "fn(a, b) a + b end"),
3223 ("() -> 1", "fn() 1 end"),
3224 ("map(xs, (x) -> x + 1)", "map(xs, fn(x) x + 1 end)"),
3225 ] {
3226 let a = parse_program(arrow).expect(arrow);
3227 let b = parse_program(block).expect(block);
3228 assert_eq!(format!("{a:?}"), format!("{b:?}"), "{arrow}");
3229 }
3230 assert_eq!(
3231 parse_program("(x)").map(|p| p.len()).ok(),
3232 Some(1),
3233 "a parenthesised name is still a group"
3234 );
3235 }
3236
3237 #[test]
3241 fn a_statement_may_end_at_a_newline_the_end_or_a_closing_keyword() {
3242 for src in [
3243 "fn(x) x + 1 end",
3244 "map(fn(x) x end, xs)",
3245 "if a\n 1\nelse\n 2\nend",
3246 "if a 1 else 2 end",
3247 "case x\nwhen 1 :a\nwhen 2 :b\nelse :c\nend",
3248 "x = 1\ny = 2",
3249 "x = 1",
3250 ] {
3251 assert!(
3252 parse_program(src).is_ok(),
3253 "{src:?}: {:?}",
3254 parse_program(src)
3255 );
3256 }
3257 }
3258
3259 #[test]
3261 fn then_is_refused_by_name() {
3262 let e = parse_program("x = if c then 1 else 2 end").expect_err("then is not a keyword");
3263 assert!(e.message.contains("`then`"), "{}", e.message);
3264 }
3265
3266 #[test]
3268 fn a_brace_block_is_refused_naming_fn() {
3269 let e = parse_program("xs.map { |x| x }").expect_err("no brace blocks");
3270 assert!(e.message.contains("fn(x)"), "{}", e.message);
3271 }
3272
3273 #[test]
3280 fn a_qualified_name_is_one_symbol() {
3281 assert_eq!(q("retsu::first(xs)"), "(retsu/first xs)");
3282 assert_eq!(q("retsu::first"), "retsu/first");
3283 assert_eq!(q("xs |> retsu::first"), "(retsu/first xs)");
3284 assert_eq!(q("blue::length(xs)"), "(blue/length xs)");
3285 }
3286
3287 #[test]
3289 fn whitespace_around_the_qualifier_is_accepted() {
3290 assert_eq!(q("retsu :: first(xs)"), "(retsu/first xs)");
3291 }
3292
3293 #[test]
3296 fn a_reserved_word_after_the_qualifier_is_a_name() {
3297 assert_eq!(q("kueri::if(a, b)"), "(kueri/if a b)");
3298 assert_eq!(q("heni::test(x)"), "(heni/test x)");
3299 assert_eq!(q("sabi::fn"), "sabi/fn");
3300 }
3301
3302 #[test]
3303 fn a_qualified_name_has_exactly_one_qualifier() {
3304 for (src, says) in [
3305 ("::first(xs)", "`package::name`"),
3306 ("a::b::c", "one qualifier"),
3307 ("if::x", "reserved word"),
3308 ("retsu::1", "expected a name after `retsu::`"),
3309 ] {
3310 let e = parse_program(src).expect_err(src);
3311 assert!(e.message.contains(says), "{src}: {}", e.message);
3312 }
3313 }
3314
3315 #[test]
3318 fn qualified_reads_only_the_package_name_shape() {
3319 assert_eq!(qualified("retsu/first"), Some(("retsu", "first")));
3320 assert_eq!(qualified("kueri/if"), Some(("kueri", "if")));
3321 assert_eq!(qualified("nisshi/pair?"), Some(("nisshi", "pair?")));
3322 for not in ["/", "a/", "/a", "a/b/c", "if/x", "hash-map", "a"] {
3323 assert_eq!(qualified(not), None, "{not}");
3324 }
3325 assert!(is_callable_name("kueri/if"));
3326 }
3327
3328 #[test]
3333 fn a_symbol_may_end_in_a_question_or_bang() {
3334 assert_eq!(q("[:pair?, :set!]"), "(list :pair? :set!)");
3335 assert_eq!(q("use(\"n\", [:empty?])"), "(use \"n\" (list :empty?))");
3336 assert_eq!(q(":a != :b"), "(not= :a :b)");
3337 }
3338
3339 #[test]
3343 fn a_parse_error_displays_line_col_and_the_source_text() {
3344 let e = parse_program("x = 1\ny = [1, 2 y]").expect_err("must fail");
3345 assert_eq!(e.to_string(), "2:11: expected `,` or `]`, found `y`");
3346 assert_eq!(e.span, Span::new(16, 17), "the span stays, as data");
3347 }
3348
3349 #[test]
3351 fn no_error_message_names_a_rust_variant() {
3352 for src in [
3353 "f(1 2)",
3354 "{a: 1 b}",
3355 "def 1\nend",
3356 "x.1",
3357 "if a\n",
3358 "fn(1) 2 end",
3359 "[1 :b]",
3360 "1 +",
3361 "(1",
3362 ] {
3363 let e = parse_program(src).expect_err(src);
3364 let shown = e.to_string();
3365 for tell in [
3366 "Ident(", "Int(", "Op(", "Label(", "Sym(", "Str(", "LParen", "RBracket", "Eof",
3367 "Newline", "..",
3368 ] {
3369 assert!(!shown.contains(tell), "{src:?} -> {shown}");
3370 }
3371 assert!(e.line >= 1 && e.col >= 1, "{src:?} not located: {shown}");
3372 }
3373 }
3374
3375 #[test]
3377 fn a_lex_error_is_located_too() {
3378 let e = parse_program("x = 1\ny = 2; z = 3").expect_err("no semicolons");
3379 assert_eq!((e.line, e.col), (2, 6), "{e}");
3380 assert!(e.message.contains("new line"), "{e}");
3381 }
3382
3383 #[test]
3386 fn the_parser_rejects_garbage() {
3387 assert!(parse_program("def").is_err());
3388 assert!(parse_program("(1").is_err());
3389 assert!(parse_program("end").is_err());
3390 }
3391}