1use tatara_lisp::{Atom, Sexp, Spanned, SpannedForm};
46
47use crate::lex::{lex, Span, Token, TokenKind};
48use crate::spelling::{Note, Spelling};
49
50#[derive(Clone, Debug, PartialEq)]
65pub struct ParseError {
66 pub message: String,
67 pub span: Span,
68 pub line: usize,
70 pub col: usize,
72}
73
74impl ParseError {
75 fn new(message: impl Into<String>, span: Span) -> Self {
79 Self {
80 message: message.into(),
81 span,
82 line: 0,
83 col: 0,
84 }
85 }
86
87 fn located(mut self, src: &str) -> Self {
89 let (line, col) = Span::line_col(src, self.span.start.min(src.len()));
90 self.line = line;
91 self.col = col;
92 self
93 }
94}
95
96impl std::fmt::Display for ParseError {
97 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98 write!(f, "{}:{}: {}", self.line, self.col, self.message)
99 }
100}
101
102impl std::error::Error for ParseError {}
103
104impl From<crate::lex::LexError> for ParseError {
105 fn from(e: crate::lex::LexError) -> Self {
106 Self::new(e.message, e.span)
107 }
108}
109
110thread_local! {
111 static PARSES: std::cell::Cell<u64> = const { std::cell::Cell::new(0) };
112}
113
114#[must_use]
118pub fn parses_on_this_thread() -> u64 {
119 PARSES.with(std::cell::Cell::get)
120}
121
122pub fn parse_program(src: &str) -> Result<Vec<Sexp>, ParseError> {
124 parse_program_with_depth(src, MAX_EXPR_DEPTH)
125}
126
127pub fn parse_program_tree(src: &str) -> Result<Vec<Spanned>, ParseError> {
135 parse_program_tree_with_depth(src, MAX_EXPR_DEPTH)
136}
137
138pub fn parse_program_tree_with_depth(
140 src: &str,
141 max_depth: usize,
142) -> Result<Vec<Spanned>, ParseError> {
143 PARSES.with(|n| n.set(n.get() + 1));
144 let mut p = Parser::over(
145 src,
146 lex(src).map_err(|e| ParseError::from(e).located(src))?,
147 max_depth,
148 );
149 p.program().map_err(|e| e.located(src))
150}
151
152pub fn spelling_notes(src: &str) -> Result<Vec<Note>, ParseError> {
153 let mut p = Parser::over(
154 src,
155 lex(src).map_err(|e| ParseError::from(e).located(src))?,
156 MAX_EXPR_DEPTH,
157 );
158 p.program().map_err(|e| e.located(src))?;
159 Ok(p.notes)
160}
161
162pub fn parse_program_in(src: &str, pack: &crate::yakugo::Yakugo) -> Result<Vec<Sexp>, ParseError> {
175 Ok(parse_program_tree_in(src, pack)?
176 .iter()
177 .map(Spanned::to_sexp)
178 .collect())
179}
180
181pub fn parse_program_tree_in(
197 src: &str,
198 pack: &crate::yakugo::Yakugo,
199) -> Result<Vec<Spanned>, ParseError> {
200 let toks =
201 crate::yakugo::canonical_tokens(src, pack).map_err(|e| ParseError::from(e).located(src))?;
202 PARSES.with(|n| n.set(n.get() + 1));
203 let mut p = Parser::over(src, toks, MAX_EXPR_DEPTH);
204 p.program().map_err(|e| e.located(src))
205}
206
207pub fn parse_program_with_depth(src: &str, max_depth: usize) -> Result<Vec<Sexp>, ParseError> {
216 Ok(parse_program_tree_with_depth(src, max_depth)?
217 .iter()
218 .map(Spanned::to_sexp)
219 .collect())
220}
221
222pub fn parse_program_spanned(src: &str) -> Result<Vec<(Sexp, Span)>, ParseError> {
235 parse_program_spanned_with_depth(src, MAX_EXPR_DEPTH)
236}
237
238pub fn parse_program_spanned_with_depth(
240 src: &str,
241 max_depth: usize,
242) -> Result<Vec<(Sexp, Span)>, ParseError> {
243 Ok(parse_program_tree_with_depth(src, max_depth)?
244 .iter()
245 .map(|f| (f.to_sexp(), f.span))
246 .collect())
247}
248
249pub fn comments(src: &str) -> Vec<Comment> {
257 lex(src)
258 .map(|toks| {
259 toks.iter()
260 .filter_map(|t| match &t.kind {
261 TokenKind::Comment(text) => Some(Comment {
262 text: text.clone(),
263 span: t.span,
264 own_line: line_before_is_blank(src, t.span.start),
265 }),
266 _ => None,
267 })
268 .collect()
269 })
270 .unwrap_or_default()
271}
272
273#[derive(Clone, Debug, PartialEq, Eq)]
275pub struct Comment {
276 pub text: String,
278 pub span: Span,
279 pub own_line: bool,
281}
282
283fn line_before_is_blank(src: &str, start: usize) -> bool {
284 src[..start.min(src.len())]
285 .rsplit('\n')
286 .next()
287 .is_some_and(|prefix| prefix.trim().is_empty())
288}
289
290pub fn parse_expr(src: &str) -> Result<Sexp, ParseError> {
292 let forms = parse_program(src)?;
293 match forms.len() {
294 1 => Ok(forms.into_iter().next().expect("checked len")),
295 n => Err(ParseError::new(
296 format!("expected exactly one expression, found {n}"),
297 Span::new(0, src.len()),
298 )
299 .located(src)),
300 }
301}
302
303#[derive(Clone, Copy, Debug)]
313pub struct Infix {
314 pub op: &'static str,
316 pub power: (u8, u8),
318 pub callee: &'static str,
320 pub doc: &'static str,
324}
325
326pub const INFIX: &[Infix] = &[
330 Infix {
331 op: "or",
332 power: (1, 2),
333 callee: "or",
334 doc: "Logical or: the left value if truthy, otherwise the right; the right is evaluated only when needed. `||` is read as `or` and written `or` (B0025).",
335 },
336 Infix {
337 op: "and",
338 power: (3, 4),
339 callee: "and",
340 doc: "Logical and: the right value if the left is truthy, otherwise the left; short-circuits. `&&` is read as `and` and written `and` (B0025).",
341 },
342 Infix {
353 op: "==",
354 power: (5, 6),
355 callee: "equal?",
356 doc: "Structural equality over every value (okite D0001): 4 == 4.0, lists and maps by content, nil only equals nil.",
357 },
358 Infix {
359 op: "!=",
360 power: (5, 6),
361 callee: "not=",
362 doc: "Exactly not(a == b) (okite D0002).",
363 },
364 Infix {
365 op: "<",
366 power: (5, 6),
367 callee: "<",
368 doc: "Numeric less-than. A string operand is a type error: order text with compare(a, b).",
369 },
370 Infix {
371 op: "<=",
372 power: (5, 6),
373 callee: "<=",
374 doc: "Numeric less-than-or-equal. Numbers only, like <.",
375 },
376 Infix {
377 op: ">",
378 power: (5, 6),
379 callee: ">",
380 doc: "Numeric greater-than. Numbers only, like <.",
381 },
382 Infix {
383 op: ">=",
384 power: (5, 6),
385 callee: ">=",
386 doc: "Numeric greater-than-or-equal. Numbers only, like <.",
387 },
388 Infix {
389 op: "+",
390 power: (7, 8),
391 callee: "+",
392 doc: "Numeric addition; an Int and a Float give a Float. Not string concatenation: interpolate instead.",
393 },
394 Infix {
395 op: "-",
396 power: (7, 8),
397 callee: "-",
398 doc: "Numeric subtraction. Written before an operand with nothing on its left, it is unary minus: -n.",
399 },
400 Infix {
401 op: "*",
402 power: (9, 10),
403 callee: "*",
404 doc: "Numeric multiplication.",
405 },
406 Infix {
407 op: "/",
408 power: (9, 10),
409 callee: "/",
410 doc: "Division that never truncates (okite D0008): 7 / 2 is 3.5, while 6 / 2 stays the Int 3. Integer division is floor(a / b).",
411 },
412 Infix {
413 op: "%",
414 power: (9, 10),
415 callee: "mod",
416 doc: "Euclidean remainder: never negative, so (0 - 7) % 3 is 2, not -1. A zero divisor is a runtime error.",
417 },
418];
419
420pub const LOWERED_ASSERT: &str = "blue-assert";
443
444pub const LOWERED_MAP: &str = "hash-map";
452
453pub const LOWERED_CONCAT: &str = "concat";
458
459pub const LOWERED_FIELD: &str = "blue-field";
460
461pub const LOWERED_ERROR_MATCH: &str = "blue-rescues?";
462
463pub const LOWERED_ENSURE: &str = "blue-ensure";
464
465pub const LOWERED_RERAISE: &str = "blue-reraise";
466
467pub const LOWERED_ATTEMPTED: &str = "blue-attempted";
468
469pub const LOWERED_FAILED: &str = "blue-failed";
470
471pub const RESCUED: &str = "rescued-error";
472
473pub const SURFACE_KEYWORDS: &[&str] = &[
474 "if",
475 "begin",
476 "unless",
477 "def",
478 "defmacro",
479 "quote",
480 "unquote",
481 "unquote_splice",
482 "test",
483 "assert",
484 "fn",
485 "case",
486];
487
488pub const BLOCK_KEYWORDS: &[&str] = &[
500 "do", "end", "else", "elsif", "rescue", "ensure", "true", "false", "nil",
501];
502
503pub const OPERATOR_KEYWORDS: &[&str] = &["and", "or", "not"];
504
505pub fn is_reserved_word(name: &str) -> bool {
512 SURFACE_KEYWORDS.contains(&name)
513 || BLOCK_KEYWORDS.contains(&name)
514 || OPERATOR_KEYWORDS.contains(&name)
515}
516
517pub fn is_callable_name(name: &str) -> bool {
526 if qualified(name).is_some() {
528 return true;
529 }
530 !is_reserved_word(name)
531 && matches!(
532 lex(name).as_deref(),
533 Ok([t, e]) if matches!(&t.kind, TokenKind::Ident(n) if n == name)
534 && e.kind == TokenKind::Eof
535 )
536}
537
538pub const QUALIFIER: char = '/';
543
544pub const BUILTIN_QUALIFIER: &str = "blue";
547
548#[must_use]
550pub fn qualify(pkg: &str, name: &str) -> String {
551 format!("{pkg}{QUALIFIER}{name}")
552}
553
554#[must_use]
560pub fn qualified(sym: &str) -> Option<(&str, &str)> {
561 let (pkg, name) = sym.split_once(QUALIFIER)?;
562 let ident = |s: &str| {
563 matches!(
564 lex(s).as_deref(),
565 Ok([t, e]) if matches!(&t.kind, TokenKind::Ident(n) if n == s) && e.kind == TokenKind::Eof
566 )
567 };
568 let named = |s: &str| ident(s) || matches!(s, "true" | "false" | "nil");
569 (ident(pkg) && !is_reserved_word(pkg) && named(name)).then_some((pkg, name))
570}
571
572#[must_use]
580pub fn keyword_doc(word: &str) -> &'static str {
581 match word {
582 "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).",
583 "unless" => "`unless c … end` is read as `if not c` and written that way (B0026).",
584 "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`.",
585 "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 `|>`.",
586 "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.",
587 "and" => "`a and b`: logical and; `&&` is read as `and` and written `and` (B0025).",
588 "or" => "`a or b`: logical or; `||` is read as `or` and written `or` (B0025).",
589 "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).",
590 "def" => "`def name(a, b) … end`: a named function; its value is the last expression. Parameters may be typed: `def f(n: Int) -> Int`.",
591 "defmacro" => "`defmacro name(x) quote … unquote(x) … end end`: a macro, a rewrite of the unevaluated form.",
592 "quote" => "`quote … end`: the form itself as data, not its value. Used inside `defmacro`.",
593 "unquote" => "`unquote(x)`: inside `quote`, splice in the value of `x`.",
594 "unquote_splice" => "`unquote_splice(xs)`: inside `quote`, splice the items of the form `xs` in place, not the form itself.",
595 "test" => "`test \"what must hold\" … end`: a test block, run by `blue test` and ignored by `blue run`.",
596 "assert" => "`assert expr`: inside a test, fail it unless `expr` is truthy; the failure quotes the expression.",
597 "fn" => "`fn(x) x * 2 end`: an anonymous function, a value. Blue has no brace or do blocks.",
598 "case" => "`case v when 1 … when 2 … else … end`: compares with `==`; it does not destructure.",
599 "do" => "Reserved and refused: blue has no do-blocks. Pass `fn(x) … end` instead.",
600 "end" => "Closes `def`, `defmacro`, `if`, `case`, `fn`, `test` and `quote`.",
601 "else" => "The branch of `if` or `case` taken when nothing before it matched.",
602 "elsif" => "A further condition in an `if` chain; an `else` holding only an `if` is written `elsif` (B0027).",
603 "true" => "The boolean true.",
604 "false" => "The boolean false. Only `false` and `nil` are falsy; 0, \"\" and [] are truthy.",
605 "nil" => "The absent value. It is not `[]` (okite D0004), and only `nil == nil`.",
606 _ => "",
607 }
608}
609
610fn infix(op: &str) -> Option<&'static Infix> {
611 INFIX.iter().find(|i| i.op == op)
612}
613
614pub const SYMBOL_SPELLINGS: &[(&str, &str)] = &[("&&", "and"), ("||", "or"), ("!", "not")];
615
616fn word_for(op: &str) -> &str {
617 SYMBOL_SPELLINGS
618 .iter()
619 .find(|(sym, _)| *sym == op)
620 .map_or(op, |(_, word)| word)
621}
622
623fn infix_of(kind: &TokenKind) -> Option<&'static Infix> {
624 match kind {
625 TokenKind::Op(o) => infix(word_for(o)),
626 TokenKind::Ident(w) if w == "and" || w == "or" => infix(w),
627 _ => None,
628 }
629}
630
631const PIPE_POWER: (u8, u8) = (0, 1);
632const RESCUE_POWER: (u8, u8) = (0, 1);
633
634struct Parser {
635 toks: Vec<Token>,
636 pos: usize,
637 depth: usize,
645 max_depth: usize,
652 src: String,
653 comment_spans: Vec<Span>,
654 notes: Vec<Note>,
655}
656
657pub const MAX_EXPR_DEPTH: usize = 256;
664
665type Fail = Box<ParseError>;
677
678fn fail(message: impl Into<String>, span: Span) -> Fail {
679 Box::new(ParseError::new(message, span))
680}
681
682impl Parser {
683 fn over(src: &str, all: Vec<Token>, max_depth: usize) -> Self {
684 let comment_spans = all
685 .iter()
686 .filter(|t| matches!(t.kind, TokenKind::Comment(_)))
687 .map(|t| t.span)
688 .collect();
689 let toks = all
690 .into_iter()
691 .filter(|t| !matches!(t.kind, TokenKind::Comment(_)))
692 .collect();
693 Parser {
694 toks,
695 pos: 0,
696 depth: 0,
697 max_depth,
698 src: src.to_string(),
699 comment_spans,
700 notes: Vec::new(),
701 }
702 }
703
704 fn note(&mut self, spelling: Spelling, span: Span, written: &str, canonical: &str) {
705 self.notes.push(Note {
706 spelling,
707 span,
708 written: written.to_string(),
709 canonical: canonical.to_string(),
710 });
711 }
712
713 fn text(&self, span: Span) -> &str {
714 self.src.get(span.start..span.end).unwrap_or("")
715 }
716
717 fn comment_within(&self, lo: usize, hi: usize) -> bool {
718 self.comment_spans
719 .iter()
720 .any(|c| c.start >= lo && c.start < hi)
721 }
722
723 fn touches_previous(&self) -> bool {
724 let here = self.peek_span();
725 self.toks
726 .get(self.pos.wrapping_sub(1))
727 .is_some_and(|prev| prev.span.end == here.start)
728 }
729
730 fn peek(&self) -> &TokenKind {
731 &self.toks[self.pos.min(self.toks.len() - 1)].kind
732 }
733
734 fn peek_span(&self) -> Span {
735 self.toks[self.pos.min(self.toks.len() - 1)].span
736 }
737
738 fn bump(&mut self) -> TokenKind {
739 let k = self.toks[self.pos.min(self.toks.len() - 1)].kind.clone();
740 if self.pos < self.toks.len() {
741 self.pos += 1;
742 }
743 k
744 }
745
746 fn at(&self, k: &TokenKind) -> bool {
747 self.peek() == k
748 }
749
750 fn eat(&mut self, k: &TokenKind) -> bool {
751 if self.at(k) {
752 self.bump();
753 true
754 } else {
755 false
756 }
757 }
758
759 fn expect(&mut self, k: &TokenKind, what: &str) -> Result<(), Fail> {
760 if self.eat(k) {
761 Ok(())
762 } else {
763 Err(self.error(format!("expected {what}, found {}", self.peek())))
764 }
765 }
766
767 fn error(&self, message: impl Into<String>) -> Fail {
768 fail(message, self.peek_span())
769 }
770
771 fn skip_newlines(&mut self) {
773 while matches!(self.peek(), TokenKind::Newline) {
774 self.bump();
775 }
776 }
777
778 fn at_ident(&self, name: &str) -> bool {
779 matches!(self.peek(), TokenKind::Ident(n) if n == name)
780 }
781
782 fn mark(&self) -> usize {
785 self.peek_span().start
786 }
787
788 fn span_since(&self, start: usize) -> Span {
794 let end = self
795 .toks
796 .get(self.pos.saturating_sub(1))
797 .map_or(start, |t| t.span.end);
798 Span::new(start, end.max(start))
799 }
800
801 fn program(&mut self) -> Result<Vec<Spanned>, Fail> {
802 let mut out = Vec::new();
803 loop {
804 self.skip_newlines();
805 if matches!(self.peek(), TokenKind::Eof) {
806 break;
807 }
808 out.push(self.statement()?);
809 self.end_of_statement(&[])?;
810 }
811 Ok(out)
812 }
813
814 fn end_of_statement(&self, terminators: &[&str]) -> Result<(), Fail> {
832 let next = self.peek();
833 if matches!(next, TokenKind::Newline | TokenKind::Eof)
834 || terminators.iter().any(|t| self.at_ident(t))
835 {
836 return Ok(());
837 }
838 let prev = self.toks.get(self.pos.wrapping_sub(1)).map(|t| &t.kind);
839 let prev_name = match prev {
840 Some(TokenKind::Ident(n)) => Some(n.as_str()),
841 _ => None,
842 };
843 let callee = prev_name.unwrap_or("f");
844 let why = match (prev, next) {
845 (Some(TokenKind::Ident(t)), _) if t == "then" => {
846 "blue's `if` has no `then`: put the branch on the next line".to_string()
847 }
848 (_, TokenKind::Ident(d)) if d == "do" => "blue has no `do` blocks: pass a function \
849 as an argument, `f(xs, fn(x) … end)`"
850 .to_string(),
851 (_, TokenKind::LBracket) => {
852 let index = match (self.toks.get(self.pos + 1), self.toks.get(self.pos + 2)) {
853 (Some(i), Some(close)) if close.kind == TokenKind::RBracket => match &i.kind {
854 TokenKind::Int(n) => n.to_string(),
855 TokenKind::Ident(n) => n.clone(),
856 _ => "i".to_string(),
857 },
858 _ => "i".to_string(),
859 };
860 let list = prev_name.unwrap_or("xs");
861 format!(
862 "blue has no `xs[i]` indexing: write `nth({index}, {list})` \
863 (or `first({list})`, `last({list})`)"
864 )
865 }
866 (_, TokenKind::LBrace) => "blue has no brace blocks: pass a function as an \
867 argument, `map(xs, fn(x) … end)`"
868 .to_string(),
869 (
870 Some(TokenKind::Ident(_)),
871 TokenKind::Ident(_)
872 | TokenKind::Int(_)
873 | TokenKind::Float(_)
874 | TokenKind::Str(_)
875 | TokenKind::InterpolatedStr { .. }
876 | TokenKind::Sym(_)
877 | TokenKind::True
878 | TokenKind::False
879 | TokenKind::Nil,
880 ) => {
881 let arg = match next {
882 TokenKind::Ident(a) => a.clone(),
883 _ => "x".to_string(),
884 };
885 format!("a call needs parentheses: write `{callee}({arg})`")
886 }
887 _ => "put each statement on its own line".to_string(),
888 };
889 Err(self.error(format!("expected the end of the line, found {next}: {why}")))
890 }
891
892 fn statement(&mut self) -> Result<Spanned, Fail> {
905 let max = self.max_depth;
919 if self.depth >= max {
920 return Err(self.error(format!(
921 "statement nests deeper than {max}; refusing to \
922 recurse further (this is a limit, not a syntax error)"
923 )));
924 }
925 self.depth += 1;
926 let r = self.statement_inner();
927 self.depth -= 1;
928 r
929 }
930
931 fn statement_inner(&mut self) -> Result<Spanned, Fail> {
932 if let TokenKind::Ident(name) = self.peek().clone() {
933 if self.peek_at(1) == "=" && !is_reserved_word(&name) {
934 let name_span = self.peek_span();
935 self.bump(); self.bump(); self.skip_newlines();
938 let value = self.expr(0)?;
939 return Ok(list_at(
943 self.span_since(name_span.start),
944 vec![sym_at(name_span, "define"), sym_at(name_span, &name), value],
945 ));
946 }
947 }
948 let e = self.expr(0)?;
949 if let SpannedForm::List(items) = &e.form {
950 if items.len() == 3
951 && items[0].as_symbol() == Some("define")
952 && items[1].as_symbol().is_some()
953 && self.text(items[0].span) == "define"
954 {
955 self.note(Spelling::OperatorSpelling, e.span, "define(x, v)", "x = v");
956 }
957 }
958 Ok(e)
959 }
960
961 fn peek_at(&self, n: usize) -> String {
964 match self.toks.get(self.pos + n).map(|t| &t.kind) {
965 Some(TokenKind::Op(o)) => o.clone(),
966 _ => String::new(),
967 }
968 }
969
970 fn expr(&mut self, min_bp: u8) -> Result<Spanned, Fail> {
972 let max = self.max_depth;
982 if self.depth >= max {
983 return Err(self.error(format!(
984 "expression nests deeper than {max}; refusing to \
985 recurse further (this is a limit, not a syntax error)"
986 )));
987 }
988 self.depth += 1;
989 let r = self.expr_inner(min_bp);
990 self.depth -= 1;
991 r
992 }
993
994 #[inline(never)]
995 fn rescue_node(&self, start: usize, op_span: Span, lhs: Spanned, fallback: Spanned) -> Spanned {
996 list_at(
997 self.span_since(start),
998 vec![
999 sym_at(op_span, "try"),
1000 lhs,
1001 list_at(
1002 op_span.merge(fallback.span),
1003 vec![
1004 sym_at(op_span, "catch"),
1005 list_at(op_span, vec![sym_at(op_span, "_")]),
1006 fallback,
1007 ],
1008 ),
1009 ],
1010 )
1011 }
1012
1013 #[inline(never)]
1014 fn infix_spelling(&mut self, callee: Option<&str>, op_span: Span) {
1015 let written = self.text(op_span).to_string();
1016 match (callee, self.peek().clone()) {
1017 (None, _) => self.note(Spelling::CallForm, op_span, "|>", "a call"),
1018 (Some(c), TokenKind::Op(o)) if word_for(&o) != o => {
1019 self.note(Spelling::LogicalSymbol, op_span, &written, c);
1020 }
1021 (Some(_), TokenKind::Op(o)) if written != o => {
1022 self.note(Spelling::OperatorSpelling, op_span, &written, &o);
1023 }
1024 _ => {}
1025 }
1026 }
1027
1028 #[inline(never)]
1029 fn nil_test(&mut self, start: usize, op_span: Span, lhs: &Spanned, rhs: &Spanned) -> Option<Spanned> {
1030 let (subject, written) = match (&lhs.form, &rhs.form) {
1031 (_, SpannedForm::Nil) if !matches!(lhs.form, SpannedForm::Nil) => (lhs, "x == nil"),
1032 (SpannedForm::Nil, _) if !matches!(rhs.form, SpannedForm::Nil) => (rhs, "nil == x"),
1033 _ => return None,
1034 };
1035 let span = self.span_since(start);
1036 self.note(Spelling::NilTest, span, written, "x.nil?");
1037 Some(list_at(
1038 span,
1039 vec![
1040 sym_at(op_span, LOWERED_FIELD),
1041 subject.clone(),
1042 atom_at(op_span, Atom::Keyword("nil?".to_string())),
1043 ],
1044 ))
1045 }
1046
1047 #[inline(never)]
1048 fn infix_node(
1049 &mut self,
1050 start: usize,
1051 callee: Option<&str>,
1052 op_span: Span,
1053 lhs: Spanned,
1054 rhs: Spanned,
1055 ) -> Spanned {
1056 if callee == Some("-") && matches!(lhs.form, SpannedForm::Atom(Atom::Int(0))) {
1057 self.note(
1058 Spelling::OperatorSpelling,
1059 lhs.span.merge(op_span),
1060 "0 - x",
1061 "-x",
1062 );
1063 }
1064
1065 if callee == Some("equal?") {
1066 if let Some(test) = self.nil_test(start, op_span, &lhs, &rhs) {
1067 return test;
1068 }
1069 }
1070
1071 if callee.is_none() {
1072 let rhs_span = rhs.span;
1077 let threaded = match rhs.form {
1078 SpannedForm::List(mut items) if !items.is_empty() => {
1079 items.insert(1, lhs);
1080 list_at(self.span_since(start), items)
1081 }
1082 callee => list_at(
1083 self.span_since(start),
1084 vec![Spanned::new(rhs_span, callee), lhs],
1085 ),
1086 };
1087 match &threaded.form {
1088 SpannedForm::List(items)
1089 if items.len() == 2 && items[0].as_symbol() == Some("not") =>
1090 {
1091 let (head, operand) = (items[0].span, items[1].clone());
1092 self.negation(head, threaded.span, operand)
1093 }
1094 SpannedForm::List(items)
1095 if items.len() == 4
1096 && items[0].as_symbol() == Some("if")
1097 && matches!(items[3].form, SpannedForm::Nil) =>
1098 {
1099 list_at(threaded.span, items[..3].to_vec())
1100 }
1101 SpannedForm::List(items)
1102 if items.len() == 3 && items[0].as_symbol() == Some("equal?") =>
1103 {
1104 let (op, a, b) = (items[0].span, items[1].clone(), items[2].clone());
1105 match self.nil_test(start, op, &a, &b) {
1106 Some(test) => test,
1107 None => threaded,
1108 }
1109 }
1110 _ => threaded,
1111 }
1112 } else {
1113 list_at(
1119 self.span_since(start),
1120 vec![sym_at(op_span, callee.unwrap_or_default()), lhs, rhs],
1121 )
1122 }
1123 }
1124
1125 fn expr_inner(&mut self, min_bp: u8) -> Result<Spanned, Fail> {
1126 let start = self.mark();
1127 let mut lhs = self.prefix()?;
1128
1129 loop {
1130 match self.peek() {
1132 TokenKind::Dot => {
1133 let dot = self.peek_span();
1134 self.bump();
1135 lhs = self.finish_send(start, lhs, dot)?;
1136 continue;
1137 }
1138 TokenKind::LParen => {
1139 let (args, splat) = self.call_args()?;
1140 let call = self.call_node(self.span_since(start), vec![lhs], args, splat);
1141 lhs = self.call_spelling(call);
1142 continue;
1143 }
1144 _ => {}
1145 }
1146
1147 let op_span = self.peek_span();
1151 if self.at_ident("rescue") {
1152 if RESCUE_POWER.0 < min_bp {
1153 break;
1154 }
1155 self.bump();
1156 self.skip_newlines();
1157 let fallback = self.expr(RESCUE_POWER.1)?;
1158 lhs = self.rescue_node(start, op_span, lhs, fallback);
1159 continue;
1160 }
1161 let (callee, (lbp, rbp)) = match self.peek() {
1162 TokenKind::Pipe => (None, PIPE_POWER),
1163 other => match infix_of(other) {
1164 Some(i) => (Some(i.callee), i.power),
1165 None => break,
1166 },
1167 };
1168 if lbp < min_bp {
1169 break;
1170 }
1171 self.infix_spelling(callee, op_span);
1172 self.bump();
1173 self.skip_newlines();
1174 let rhs = self.expr(rbp)?;
1175 lhs = self.infix_node(start, callee, op_span, lhs, rhs);
1176 }
1177
1178 Ok(lhs)
1179 }
1180
1181 #[inline(never)]
1182 fn interpolation(span: Span, parts: &[String], exprs: &[String]) -> Result<Spanned, Fail> {
1183 let mut acc = atom_at(span, Atom::Str(parts[0].clone()));
1193 for (i, raw) in exprs.iter().enumerate() {
1194 let inner = parse_expr(raw).map_err(|e| {
1195 fail(
1196 format!("in interpolation `#{{{raw}}}`: {}", e.message),
1197 span,
1198 )
1199 })?;
1200 let inner = Spanned::from_sexp_at(&inner, span);
1201 acc = list_at(span, vec![sym_at(span, LOWERED_CONCAT), acc, inner]);
1202 acc = list_at(
1205 span,
1206 vec![
1207 sym_at(span, LOWERED_CONCAT),
1208 acc,
1209 atom_at(span, Atom::Str(parts[i + 1].clone())),
1210 ],
1211 );
1212 }
1213 Ok(acc)
1214 }
1215
1216 #[inline(never)]
1217 fn not_prefix(&mut self, span: Span) -> Result<Spanned, Fail> {
1218 if self.at(&TokenKind::LParen) && self.touches_previous() {
1219 self.note(Spelling::OperatorSpelling, span, "not(x)", "not x");
1220 let (args, splat) = self.call_args()?;
1221 if args.len() != 1 || splat.is_some() {
1222 let call = self.call_node(
1223 self.span_since(span.start),
1224 vec![sym_at(span, "not")],
1225 args,
1226 splat,
1227 );
1228 return Ok(call);
1229 }
1230 let operand = args.into_iter().next().expect("one argument");
1231 return Ok(self.negation(span, self.span_since(span.start), operand));
1232 }
1233 let rhs = self.expr(11)?;
1234 Ok(self.negation(span, self.span_since(span.start), rhs))
1235 }
1236
1237 fn prefix(&mut self) -> Result<Spanned, Fail> {
1238 if self.at(&TokenKind::LParen) {
1239 let span = self.peek_span();
1240 self.bump();
1241 if self.arrow_lambda_ahead() {
1242 return self.arrow_lambda(span);
1243 }
1244 return self.group();
1245 }
1246 self.prefix_token()
1247 }
1248
1249 fn group(&mut self) -> Result<Spanned, Fail> {
1250 self.skip_newlines();
1251 let inner = self.expr(0)?;
1252 self.skip_newlines();
1253 self.expect(&TokenKind::RParen, "`)`")?;
1254 Ok(inner)
1255 }
1256
1257 #[inline(never)]
1258 fn prefix_token(&mut self) -> Result<Spanned, Fail> {
1259 let span = self.peek_span();
1260 match self.bump() {
1261 TokenKind::Int(v) => Ok(atom_at(span, Atom::Int(v))),
1262 TokenKind::Float(v) => Ok(atom_at(span, Atom::Float(v))),
1263 TokenKind::Str(s) => Ok(atom_at(span, Atom::Str(s))),
1264
1265 TokenKind::InterpolatedStr { parts, exprs } => Self::interpolation(span, &parts, &exprs),
1276 TokenKind::Sym(s) => Ok(atom_at(span, Atom::Keyword(s))),
1277 TokenKind::True => Ok(atom_at(span, Atom::Bool(true))),
1278 TokenKind::False => Ok(atom_at(span, Atom::Bool(false))),
1279 TokenKind::Nil => Ok(Spanned::new(span, SpannedForm::Nil)),
1280
1281 TokenKind::Op(o) if o == "-" => {
1282 let rhs = self.expr(11)?; Ok(list_at(
1284 self.span_since(span.start),
1285 vec![sym_at(span, "-"), atom_at(span, Atom::Int(0)), rhs],
1286 ))
1287 }
1288 TokenKind::Op(o) if o == "!" => {
1289 let written = self.text(span).to_string();
1290 self.note(Spelling::LogicalSymbol, span, &written, "not");
1291 let rhs = self.expr(11)?;
1292 Ok(self.negation(span, self.span_since(span.start), rhs))
1293 }
1294 TokenKind::Ident(w) if w == "not" => self.not_prefix(span),
1295 TokenKind::Ident(w) if (w == "and" || w == "or") && self.at(&TokenKind::LParen) => {
1296 Ok(sym_at(span, &w))
1297 }
1298 TokenKind::Ident(w) if w == "and" || w == "or" => Err(fail(
1299 format!("`{w}` joins two expressions: `a {w} b`"),
1300 span,
1301 )),
1302
1303 TokenKind::LParen => {
1304 self.skip_newlines();
1305 let inner = self.expr(0)?;
1306 self.skip_newlines();
1307 self.expect(&TokenKind::RParen, "`)`")?;
1308 Ok(inner)
1312 }
1313
1314 TokenKind::LBracket => self.list_literal(span),
1315 TokenKind::LBrace => self.map_literal(span),
1316
1317 TokenKind::Ident(name) if self.at(&TokenKind::PathSep) => {
1318 self.qualified_name(span, &name)
1319 }
1320 TokenKind::PathSep => Err(fail(
1321 "a qualified name is `package::name`, with the package written before `::`",
1322 span,
1323 )),
1324 TokenKind::Ident(name) => match name.as_str() {
1325 "if" => self.if_form(span, false),
1326 "begin" if !(self.at(&TokenKind::LParen) && self.touches_previous()) => {
1327 self.begin_form(span)
1328 }
1329 "rescue" | "ensure" => Err(fail(
1330 format!("`{name}` belongs to a `begin … end` block or a `def` body, after the statements it protects"),
1331 span,
1332 )),
1333 "unless" => {
1334 self.note(Spelling::Unless, span, "unless", "if not");
1335 self.if_form(span, true)
1336 }
1337 "def" => self.def_form(span),
1338 "defmacro" => self.defmacro_form(span),
1339 "case" => self.case_form(span),
1340 "fn" => self.lambda_form(span),
1341 "test" => self.test_form(span),
1342 "assert" => self.assert_form(span),
1343 "quote" => self.quote_form(span),
1344 "unquote" => self.unquote_form(span, false),
1345 "unquote_splice" => self.unquote_form(span, true),
1346 "do" => Err(fail(
1347 "blue has no `do` blocks: pass a function as an argument, \
1348 `f(xs, fn(x) … end)`",
1349 span,
1350 )),
1351 "end" => Err(fail(
1352 "unexpected `end`: nothing open here for it to close",
1353 span,
1354 )),
1355 _ => Ok(sym_at(span, &name)),
1356 },
1357
1358 other => Err(fail(format!("expected an expression, found {other}"), span)),
1359 }
1360 }
1361
1362 fn qualified_name(&mut self, pkg_span: Span, pkg: &str) -> Result<Spanned, Fail> {
1372 if is_reserved_word(pkg) {
1373 return Err(fail(
1374 format!("`{pkg}` is a reserved word and cannot name a package"),
1375 pkg_span,
1376 ));
1377 }
1378 self.bump(); let name_span = self.peek_span();
1380 let name = match self.bump() {
1381 TokenKind::Ident(n) => n,
1382 TokenKind::True => "true".to_string(),
1384 TokenKind::False => "false".to_string(),
1385 TokenKind::Nil => "nil".to_string(),
1386 other => {
1387 return Err(fail(
1388 format!("expected a name after `{pkg}::`, found {other}"),
1389 name_span,
1390 ))
1391 }
1392 };
1393 if self.at(&TokenKind::PathSep) {
1394 return Err(self.error(format!(
1395 "a qualified name has one qualifier: `package::name`, and `{pkg}::{name}::…` has two"
1396 )));
1397 }
1398 Ok(sym_at(
1399 self.span_since(pkg_span.start),
1400 &qualify(pkg, &name),
1401 ))
1402 }
1403
1404 #[inline(never)]
1411 fn negation(&mut self, head: Span, whole: Span, operand: Spanned) -> Spanned {
1412 if let SpannedForm::List(items) = &operand.form {
1413 let flipped = match items.first().and_then(Spanned::as_symbol) {
1414 Some("equal?") => Some("not="),
1415 Some("not=") => Some("equal?"),
1416 _ => None,
1417 };
1418 if let (Some(to), 3) = (flipped, items.len()) {
1419 let (written, canonical) = if to == "not=" {
1420 ("not (a == b)", "a != b")
1421 } else {
1422 ("not (a != b)", "a == b")
1423 };
1424 self.note(Spelling::NegatedComparison, whole, written, canonical);
1425 let mut out = items.clone();
1426 out[0] = sym_at(items[0].span, to);
1427 if to == "equal?" {
1428 if let Some(test) =
1429 self.nil_test(whole.start, items[0].span, &items[1], &items[2])
1430 {
1431 return test;
1432 }
1433 }
1434 return list_at(whole, out);
1435 }
1436 }
1437 list_at(whole, vec![sym_at(head, "not"), operand])
1438 }
1439
1440 #[inline(never)]
1441 fn call_spelling(&mut self, call: Spanned) -> Spanned {
1442 let SpannedForm::List(items) = &call.form else {
1443 return call;
1444 };
1445 let Some(head) = items.first().and_then(Spanned::as_symbol) else {
1446 return call;
1447 };
1448 let argc = items.len() - 1;
1449 if head == "equal?" && argc == 2 {
1450 let (op_span, start) = (items[0].span, call.span.start);
1451 if let Some(test) = self.nil_test(start, op_span, &items[1], &items[2]) {
1452 return test;
1453 }
1454 }
1455 let operator = match (head, argc) {
1456 ("and" | "or" | "mod" | "equal?", 2) => Some(head),
1457 ("list", _) => Some("list"),
1458 _ => None,
1459 };
1460 if let Some(op) = operator {
1461 let canonical = match op {
1462 "list" => "[…]".to_string(),
1463 "mod" => "a % b".to_string(),
1464 "equal?" => "a == b".to_string(),
1465 other => format!("a {other} b"),
1466 };
1467 self.note(
1468 Spelling::OperatorSpelling,
1469 call.span,
1470 &format!("{head}(…)"),
1471 &canonical,
1472 );
1473 return call;
1474 }
1475 if head == "apply"
1476 && self.text(items[0].span) == "apply"
1477 && argc >= 2
1478 && items[1].as_symbol().is_some_and(is_callable_name)
1479 {
1480 self.note(Spelling::Apply, call.span, "apply(f, …, xs)", "f(…, *xs)");
1481 return call;
1482 }
1483 if head == "try" {
1484 return self.try_spelling(call);
1485 }
1486 call
1487 }
1488
1489 fn try_spelling(&mut self, call: Spanned) -> Spanned {
1490 let SpannedForm::List(items) = &call.form else {
1491 return call;
1492 };
1493 let Some((catch, body)) = items[1..].split_last() else {
1494 return call;
1495 };
1496 let Some(clause) = catch.as_list() else {
1497 return call;
1498 };
1499 let binder = match clause {
1500 [head, binding, handler @ ..]
1501 if head.as_symbol() == Some("catch") && !handler.is_empty() =>
1502 {
1503 match binding.as_list() {
1504 Some([b]) => b.as_symbol().map(|n| (n.to_string(), b.span)),
1505 _ => None,
1506 }
1507 }
1508 _ => None,
1509 };
1510 let Some((name, binder_span)) = binder else {
1511 return call;
1512 };
1513 if body.is_empty() {
1514 return call;
1515 }
1516 self.note(Spelling::TryCatch, call.span, "try(…, catch(…))", "rescue");
1517 let handler = &clause[2..];
1518 let rename =
1519 name.starts_with('_') && name != "_" && !handler.iter().any(|h| mentions(h, &name));
1520 let body = if body.len() == 1 {
1521 body[0].clone()
1522 } else {
1523 let span = cover(body, call.span);
1524 let mut list = vec![sym_at(Span::new(span.start, span.start), "begin")];
1525 list.extend(body.iter().cloned());
1526 list_at(span, list)
1527 };
1528 let handler = if handler.len() == 1 {
1529 handler[0].clone()
1530 } else {
1531 let span = cover(handler, catch.span);
1532 let mut list = vec![sym_at(Span::new(span.start, span.start), "begin")];
1533 list.extend(handler.iter().cloned());
1534 list_at(span, list)
1535 };
1536 let binder = sym_at(binder_span, if rename { "_" } else { &name });
1537 list_at(
1538 call.span,
1539 vec![
1540 items[0].clone(),
1541 body,
1542 list_at(
1543 catch.span,
1544 vec![
1545 clause[0].clone(),
1546 list_at(clause[1].span, vec![binder]),
1547 handler,
1548 ],
1549 ),
1550 ],
1551 )
1552 }
1553
1554 fn finish_send(&mut self, start: usize, recv: Spanned, dot: Span) -> Result<Spanned, Fail> {
1555 let name_span = self.peek_span();
1556 let name = match self.bump() {
1557 TokenKind::Ident(n) if is_reserved_word(&n) => {
1571 return Err(self.error(format!(
1572 "`{n}` is a reserved word and cannot be a method name — \
1573 `recv.{n}` would lower onto the `{n}` form itself"
1574 )))
1575 }
1576 TokenKind::Ident(n) => n,
1577 other => {
1578 return Err(fail(
1579 format!("expected a method name after `.`, found {other}"),
1580 name_span,
1581 ))
1582 }
1583 };
1584 if !self.at(&TokenKind::LParen) {
1585 return Ok(list_at(
1586 self.span_since(start),
1587 vec![
1588 sym_at(dot, LOWERED_FIELD),
1589 recv,
1590 atom_at(name_span, Atom::Keyword(name)),
1591 ],
1592 ));
1593 }
1594 self.note(
1595 Spelling::CallForm,
1596 dot.merge(name_span),
1597 &format!("x.{name}(…)"),
1598 &format!("{name}(x, …)"),
1599 );
1600 let (args, splat) = self.call_args()?;
1601 Ok(self.call_node(
1602 self.span_since(start),
1603 vec![sym_at(name_span, &name), recv],
1604 args,
1605 splat,
1606 ))
1607 }
1608
1609 fn call_node(
1610 &self,
1611 span: Span,
1612 head: Vec<Spanned>,
1613 args: Vec<Spanned>,
1614 splat: Option<Span>,
1615 ) -> Spanned {
1616 let mut list = Vec::with_capacity(head.len() + args.len() + 1);
1617 if let Some(at) = splat {
1618 list.push(sym_at(at, "apply"));
1619 }
1620 list.extend(head);
1621 list.extend(args);
1622 list_at(span, list)
1623 }
1624
1625 fn paren_args(&mut self) -> Result<Vec<Spanned>, Fail> {
1626 let (args, splat) = self.call_args()?;
1627 if let Some(at) = splat {
1628 return Err(fail(
1629 "a splat `*xs` passes a list as a call's last arguments, and this is not a call",
1630 at,
1631 ));
1632 }
1633 Ok(args)
1634 }
1635
1636 fn call_args(&mut self) -> Result<(Vec<Spanned>, Option<Span>), Fail> {
1637 self.expect(&TokenKind::LParen, "`(`")?;
1638 let mut args = Vec::new();
1639 let mut splat = None;
1640 self.skip_newlines();
1641 if self.eat(&TokenKind::RParen) {
1642 return Ok((args, splat));
1643 }
1644 loop {
1645 self.skip_newlines();
1646 if splat.is_some() {
1647 return Err(
1648 self.error("a splat `*xs` passes a list as the LAST arguments: write it last")
1649 );
1650 }
1651 if matches!(self.peek(), TokenKind::Op(o) if o == "*") {
1652 splat = Some(self.peek_span());
1653 self.bump();
1654 }
1655 args.push(self.expr(0)?);
1656 self.skip_newlines();
1657 if self.eat(&TokenKind::Comma) {
1658 if self.trailing_comma(&TokenKind::RParen) {
1659 break;
1660 }
1661 continue;
1662 }
1663 self.expect(&TokenKind::RParen, "`,` or `)`")?;
1664 break;
1665 }
1666 Ok((args, splat))
1667 }
1668
1669 fn trailing_comma(&mut self, closer: &TokenKind) -> bool {
1670 let comma = self
1671 .toks
1672 .get(self.pos.wrapping_sub(1))
1673 .map_or(self.peek_span(), |t| t.span);
1674 self.skip_newlines();
1675 if self.at(closer) {
1676 self.note(Spelling::TrailingComma, comma, ",", "");
1677 self.bump();
1678 return true;
1679 }
1680 false
1681 }
1682
1683 fn list_literal(&mut self, open: Span) -> Result<Spanned, Fail> {
1684 let mut items = vec![sym_at(open, "list")];
1685 self.skip_newlines();
1686 if self.eat(&TokenKind::RBracket) {
1687 return Ok(list_at(self.span_since(open.start), items));
1688 }
1689 loop {
1690 self.skip_newlines();
1691 items.push(self.expr(0)?);
1692 self.skip_newlines();
1693 if self.eat(&TokenKind::Comma) {
1694 if self.trailing_comma(&TokenKind::RBracket) {
1695 break;
1696 }
1697 continue;
1698 }
1699 self.expect(&TokenKind::RBracket, "`,` or `]`")?;
1700 break;
1701 }
1702 Ok(list_at(self.span_since(open.start), items))
1703 }
1704
1705 fn map_literal(&mut self, open: Span) -> Result<Spanned, Fail> {
1712 let mut items = vec![sym_at(open, LOWERED_MAP)];
1713 self.skip_newlines();
1714 if self.eat(&TokenKind::RBrace) {
1715 return Ok(list_at(self.span_since(open.start), items));
1716 }
1717 loop {
1718 self.skip_newlines();
1719 match self.peek().clone() {
1720 TokenKind::Label(name) => {
1721 let label_span = self.peek_span();
1722 self.bump();
1723 self.skip_newlines();
1724 items.push(atom_at(label_span, Atom::Keyword(name.clone())));
1725 if matches!(self.peek(), TokenKind::Comma | TokenKind::RBrace) {
1726 items.push(sym_at(label_span, &name));
1727 } else {
1728 let value = self.expr(0)?;
1729 if matches!(&value.form, SpannedForm::Atom(Atom::Symbol(s)) if *s == name)
1730 && label_name(&name)
1731 {
1732 let written = format!("{name}: {name}");
1733 let canonical = format!("{name}:");
1734 self.note(
1735 Spelling::MapPun,
1736 label_span.merge(value.span),
1737 &written,
1738 &canonical,
1739 );
1740 }
1741 items.push(value);
1742 }
1743 }
1744 _ => {
1745 let k = self.expr(0)?;
1746 self.skip_newlines();
1747 if let SpannedForm::Atom(Atom::Keyword(name)) = &k.form {
1748 if blue_lang_name(name) {
1749 let written = format!(":{name} =>");
1750 let canonical = format!("{name}:");
1751 self.note(Spelling::KeywordRocket, k.span, &written, &canonical);
1752 }
1753 }
1754 self.expect(&TokenKind::Rocket, "`=>` in a map literal")?;
1755 self.skip_newlines();
1756 items.push(k);
1757 items.push(self.expr(0)?);
1758 }
1759 }
1760 self.skip_newlines();
1761 if self.eat(&TokenKind::Comma) {
1762 if self.trailing_comma(&TokenKind::RBrace) {
1763 break;
1764 }
1765 continue;
1766 }
1767 self.expect(&TokenKind::RBrace, "`,` or `}`")?;
1768 break;
1769 }
1770 Ok(list_at(self.span_since(open.start), items))
1771 }
1772
1773 fn if_chain(&mut self, head: Span) -> Result<Spanned, Fail> {
1783 let cond = self.expr(0)?;
1784 let then = self.body(&["elsif", "else", "end"])?;
1785 let els = self.else_part()?;
1786 let mut out = vec![sym_at(head, "if"), cond, then];
1787 if let Some(e) = els {
1788 out.push(e);
1789 }
1790 Ok(list_at(self.span_since(head.start), out))
1791 }
1792
1793 fn else_part(&mut self) -> Result<Option<Spanned>, Fail> {
1794 if self.at_ident("elsif") {
1795 let elsif = self.peek_span();
1796 self.bump();
1797 return Ok(Some(self.if_chain(elsif)?));
1798 }
1799 if !self.at_ident("else") {
1800 self.expect_ident("end")?;
1801 return Ok(None);
1802 }
1803 let else_span = self.peek_span();
1804 self.bump();
1805 let save = self.pos;
1806 self.skip_newlines();
1807 let leads_with_if = self.at_ident("if") || self.at_ident("unless");
1808 self.pos = save;
1809 let e = self.body(&["end"])?;
1810 let end_at = self.peek_span().start;
1811 self.expect_ident("end")?;
1812 if matches!(e.form, SpannedForm::Nil) && !self.comment_within(else_span.start, end_at) {
1813 self.note(Spelling::ElseNil, else_span, "else nil", "no else");
1814 return Ok(None);
1815 }
1816 let single_if = matches!(&e.form, SpannedForm::List(items)
1817 if (items.len() == 3 || items.len() == 4) && items[0].as_symbol() == Some("if"));
1818 if leads_with_if && single_if {
1819 self.note(Spelling::ElseIf, else_span, "else if", "elsif");
1820 }
1821 Ok(Some(e))
1822 }
1823
1824 fn if_form(&mut self, head: Span, negate: bool) -> Result<Spanned, Fail> {
1825 let cond = self.expr(0)?;
1826 let cond = if negate {
1827 let inner = cond.span;
1830 self.negation(head, head.merge(inner), cond)
1831 } else {
1832 cond
1833 };
1834 let then = self.body(&["elsif", "else", "end"])?;
1847 let els = self.else_part()?;
1848 let mut out = vec![sym_at(head, "if"), cond, then];
1849 if let Some(e) = els {
1850 out.push(e);
1851 }
1852 Ok(list_at(self.span_since(head.start), out))
1853 }
1854
1855 fn case_form(&mut self, head: Span) -> Result<Spanned, Fail> {
1881 let subject = self.expr(0)?;
1882 self.skip_newlines();
1883
1884 let subject_var = "case-subject";
1887 let mut arms: Vec<Spanned> = Vec::new();
1888 let mut otherwise: Option<Spanned> = None;
1889 let mut else_kw: Option<Span> = None;
1892
1893 loop {
1894 self.skip_newlines();
1895 if self.at_ident("end") {
1896 break;
1897 }
1898 let maybe_else = self.peek_span();
1899 if self.eat_ident("else") {
1900 else_kw = Some(maybe_else);
1901 otherwise = Some(self.body(&["end"])?);
1902 continue;
1903 }
1904 let when = self.peek_span();
1905 if !self.eat_ident("when") {
1906 return Err(self.error(format!(
1907 "expected `when`, `else` or `end` in a case, found {}",
1908 self.peek()
1909 )));
1910 }
1911 self.skip_newlines();
1912 let pattern = self.expr(0)?;
1913 let body = self.body(&["when", "else", "end"])?;
1914 let test = list_at(
1918 when.merge(pattern.span),
1919 vec![sym_at(when, "equal?"), sym_at(when, subject_var), pattern],
1920 );
1921 arms.push(list_at(when.merge(body.span), vec![test, body]));
1922 }
1923 self.expect_ident("end")?;
1924
1925 if arms.is_empty() && otherwise.is_none() {
1926 return Err(self.error("a case needs at least one `when` or an `else`".to_string()));
1927 }
1928
1929 let mut cond = vec![sym_at(head, "cond")];
1934 cond.extend(arms);
1935 let else_span = else_kw.unwrap_or(head);
1942 let else_body = otherwise.unwrap_or_else(|| Spanned::new(else_span, SpannedForm::Nil));
1943 cond.push(list_at(
1944 else_span.merge(else_body.span),
1945 vec![sym_at(else_span, "else"), else_body],
1946 ));
1947
1948 let whole = self.span_since(head.start);
1949 let binding = list_at(
1950 head.merge(subject.span),
1951 vec![sym_at(head, subject_var), subject],
1952 );
1953 Ok(list_at(
1954 whole,
1955 vec![
1956 sym_at(head, "let"),
1957 list_at(binding.span, vec![binding]),
1958 list_at(whole, cond),
1959 ],
1960 ))
1961 }
1962
1963 fn arrow_lambda_ahead(&self) -> bool {
1966 let mut i = self.pos;
1967 let kind = |i: usize| self.toks.get(i).map(|t| &t.kind);
1968 if !matches!(kind(i), Some(TokenKind::RParen)) {
1969 loop {
1970 if !matches!(kind(i), Some(TokenKind::Ident(_))) {
1971 return false;
1972 }
1973 i += 1;
1974 match kind(i) {
1975 Some(TokenKind::Comma) => i += 1,
1976 Some(TokenKind::RParen) => break,
1977 _ => return false,
1978 }
1979 }
1980 }
1981 matches!(kind(i + 1), Some(TokenKind::Op(o)) if o == "->")
1982 }
1983
1984 fn arrow_lambda(&mut self, head: Span) -> Result<Spanned, Fail> {
1991 let mut params: Vec<Spanned> = Vec::new();
1992 while !self.at(&TokenKind::RParen) {
1993 let p_span = self.peek_span();
1994 match self.bump() {
1995 TokenKind::Ident(p) => params.push(sym_at(p_span, &p)),
1996 other => {
1997 return Err(fail(
1998 format!("expected a parameter name, found {other}"),
1999 p_span,
2000 ))
2001 }
2002 }
2003 self.eat(&TokenKind::Comma);
2004 }
2005 self.bump();
2006 self.bump();
2007 self.skip_newlines();
2008 let params_span = cover(¶ms, head);
2009 let body = self.expr(0)?;
2010 Ok(list_at(
2011 self.span_since(head.start),
2012 vec![sym_at(head, "lambda"), list_at(params_span, params), body],
2013 ))
2014 }
2015
2016 fn lambda_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2026 let mut params: Vec<Spanned> = Vec::new();
2027 if !self.at(&TokenKind::LParen) {
2028 self.note(Spelling::ParameterList, head, "fn", "fn()");
2029 }
2030 if self.at(&TokenKind::LParen) {
2031 self.bump();
2032 self.skip_newlines();
2033 if !self.eat(&TokenKind::RParen) {
2034 loop {
2035 self.skip_newlines();
2036 let p_span = self.peek_span();
2037 match self.bump() {
2038 TokenKind::Ident(p) => params.push(sym_at(p_span, &p)),
2039 other => {
2040 return Err(fail(
2041 format!("expected a parameter name, found {other}"),
2042 p_span,
2043 ))
2044 }
2045 }
2046 self.skip_newlines();
2047 if self.eat(&TokenKind::Comma) {
2048 if self.trailing_comma(&TokenKind::RParen) {
2049 break;
2050 }
2051 continue;
2052 }
2053 self.expect(&TokenKind::RParen, "`,` or `)`")?;
2054 break;
2055 }
2056 }
2057 }
2058 let params_span = cover(¶ms, head);
2059 let body = self.body(&["end"])?;
2060 self.expect_ident("end")?;
2061 Ok(list_at(
2062 self.span_since(head.start),
2063 vec![sym_at(head, "lambda"), list_at(params_span, params), body],
2064 ))
2065 }
2066
2067 fn test_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2073 let name_span = self.peek_span();
2074 let name = match self.bump() {
2075 TokenKind::Str(s) => s,
2076 other => {
2077 return Err(fail(
2078 format!("expected a string name after `test`, found {other}"),
2079 name_span,
2080 ))
2081 }
2082 };
2083 let body = self.body(&["end"])?;
2084 self.expect_ident("end")?;
2085 Ok(list_at(
2086 self.span_since(head.start),
2087 vec![
2088 sym_at(head, "deftest"),
2089 atom_at(name_span, Atom::Str(name)),
2090 body,
2091 ],
2092 ))
2093 }
2094
2095 fn assert_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2119 let e = self.expr(0)?;
2120 let quoted = Spanned::new(e.span, SpannedForm::Quote(Box::new(e.clone())));
2121 Ok(list_at(
2122 self.span_since(head.start),
2123 vec![sym_at(head, LOWERED_ASSERT), quoted, e],
2124 ))
2125 }
2126
2127 fn defmacro_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2136 let name_span = self.peek_span();
2137 let name = match self.bump() {
2138 TokenKind::Ident(n) => n,
2139 other => {
2140 return Err(fail(
2141 format!("expected a name after `defmacro`, found {other}"),
2142 name_span,
2143 ))
2144 }
2145 };
2146 let mut params: Vec<Spanned> = Vec::new();
2147 if self.at(&TokenKind::LParen) {
2148 self.bump();
2149 self.skip_newlines();
2150 if !self.eat(&TokenKind::RParen) {
2151 loop {
2152 self.skip_newlines();
2153 let p_span = self.peek_span();
2154 match self.bump() {
2155 TokenKind::Ident(p) => params.push(sym_at(p_span, &p)),
2156 TokenKind::Label(p) => {
2157 return Err(self.error(format!(
2158 "macro parameter `{p}` cannot be typed: a macro receives \
2159 source forms, not values"
2160 )))
2161 }
2162 other => {
2163 return Err(fail(
2164 format!("expected a parameter name, found {other}"),
2165 p_span,
2166 ))
2167 }
2168 }
2169 self.skip_newlines();
2170 if self.eat(&TokenKind::Comma) {
2171 if self.trailing_comma(&TokenKind::RParen) {
2172 break;
2173 }
2174 continue;
2175 }
2176 self.expect(&TokenKind::RParen, "`,` or `)`")?;
2177 break;
2178 }
2179 }
2180 }
2181 if matches!(self.peek(), TokenKind::Op(o) if o == "->") {
2182 return Err(self.error(
2183 "a macro has no return type: it produces source forms, not values".to_string(),
2184 ));
2185 }
2186
2187 let params_span = cover(¶ms, name_span);
2188 let body = self.body(&["end"])?;
2189 self.expect_ident("end")?;
2190
2191 Ok(list_at(
2194 self.span_since(head.start),
2195 vec![
2196 sym_at(head, "defmacro"),
2197 sym_at(name_span, &name),
2198 list_at(params_span, params),
2199 body,
2200 ],
2201 ))
2202 }
2203
2204 fn quote_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2210 let body = self.body(&["end"])?;
2211 self.expect_ident("end")?;
2212 Ok(Spanned::new(
2221 self.span_since(head.start),
2222 SpannedForm::Quasiquote(Box::new(body)),
2223 ))
2224 }
2225
2226 fn unquote_form(&mut self, head: Span, splice: bool) -> Result<Spanned, Fail> {
2228 self.expect(&TokenKind::LParen, "`(` after unquote")?;
2229 self.skip_newlines();
2230 let inner = self.expr(0)?;
2231 self.skip_newlines();
2232 self.expect(&TokenKind::RParen, "`)`")?;
2233 let span = self.span_since(head.start);
2234 Ok(Spanned::new(
2235 span,
2236 if splice {
2237 SpannedForm::UnquoteSplice(Box::new(inner))
2238 } else {
2239 SpannedForm::Unquote(Box::new(inner))
2240 },
2241 ))
2242 }
2243
2244 fn protected_body(&mut self, head: Span) -> Result<Spanned, Fail> {
2245 let body = self.body(&["rescue", "ensure", "end"])?;
2246 if self.at_ident("rescue") || self.at_ident("ensure") {
2247 return self.rescue_tail(head, body);
2248 }
2249 Ok(body)
2250 }
2251
2252 fn begin_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2253 let body = self.body(&["rescue", "ensure", "end"])?;
2254 if !(self.at_ident("rescue") || self.at_ident("ensure")) {
2255 return Err(self.error(
2256 "a `begin` block protects its statements with `rescue` or `ensure`; without either, write the statements directly",
2257 ));
2258 }
2259 let tree = self.rescue_tail(head, body)?;
2260 self.expect_ident("end")?;
2261 Ok(Spanned::new(self.span_since(head.start), tree.form))
2262 }
2263
2264 fn rescue_tail(&mut self, head: Span, body: Spanned) -> Result<Spanned, Fail> {
2265 let mut clauses: Vec<Spanned> = Vec::new();
2266 let mut catch_all: Option<(Spanned, Spanned, Span)> = None;
2267 while self.at_ident("rescue") {
2268 let kw = self.peek_span();
2269 self.bump();
2270 if catch_all.is_some() {
2271 return Err(fail(
2272 "a `rescue NAME` catches every error, so it is the last `rescue`; put the kinds it would shadow above it",
2273 kw,
2274 ));
2275 }
2276 match self.peek().clone() {
2277 TokenKind::Sym(_) => {
2278 let mut kinds = Vec::new();
2279 loop {
2280 let k_span = self.peek_span();
2281 match self.bump() {
2282 TokenKind::Sym(k) => kinds.push(atom_at(k_span, Atom::Keyword(k))),
2283 other => {
2284 return Err(fail(
2285 format!("expected an error kind such as `:not_found`, found {other}"),
2286 k_span,
2287 ))
2288 }
2289 }
2290 if self.eat(&TokenKind::Comma) {
2291 self.skip_newlines();
2292 continue;
2293 }
2294 break;
2295 }
2296 let binder = if self.eat_ident("as") {
2297 let b_span = self.peek_span();
2298 match self.bump() {
2299 TokenKind::Ident(b) if !is_reserved_word(&b) => Some(sym_at(b_span, &b)),
2300 other => {
2301 return Err(fail(
2302 format!("expected the name the error is bound to after `as`, found {other}"),
2303 b_span,
2304 ))
2305 }
2306 }
2307 } else {
2308 None
2309 };
2310 let handler = self.body(&["rescue", "ensure", "end"])?;
2311 let mut test = vec![sym_at(kw, LOWERED_ERROR_MATCH), sym_at(kw, RESCUED)];
2312 test.extend(kinds);
2313 let test = list_at(cover(&test, kw), test);
2314 let handler = match binder {
2315 Some(b) => let_bound(b, handler, kw),
2316 None => handler,
2317 };
2318 clauses.push(list_at(kw.merge(handler.span), vec![test, handler]));
2319 }
2320 TokenKind::Ident(b) if !is_reserved_word(&b) => {
2321 let b_span = self.peek_span();
2322 self.bump();
2323 let handler = self.body(&["rescue", "ensure", "end"])?;
2324 catch_all = Some((sym_at(b_span, &b), handler, kw));
2325 }
2326 _ => {
2327 return Err(fail(
2328 "`rescue` names what it catches: `rescue :kind`, `rescue :a, :b as e`, or `rescue e` for every error (`rescue _` discards it)",
2329 kw,
2330 ))
2331 }
2332 }
2333 }
2334 let ensure = if self.at_ident("ensure") {
2335 self.bump();
2336 let cleanup = self.body(&["rescue", "end"])?;
2337 if self.at_ident("rescue") {
2338 return Err(self.error("`ensure` comes after every `rescue` of its block"));
2339 }
2340 Some(cleanup)
2341 } else {
2342 None
2343 };
2344 let whole = self.span_since(head.start);
2345 let protected = if clauses.is_empty() {
2346 match catch_all {
2347 None => body,
2348 Some((binder, handler, kw)) => list_at(
2349 whole,
2350 vec![
2351 sym_at(head, "try"),
2352 body,
2353 list_at(
2354 kw.merge(handler.span),
2355 vec![
2356 sym_at(kw, "catch"),
2357 list_at(binder.span, vec![binder]),
2358 handler,
2359 ],
2360 ),
2361 ],
2362 ),
2363 }
2364 } else {
2365 let first_kw = clauses[0].span;
2366 let otherwise = match catch_all {
2367 Some((binder, handler, kw)) => {
2368 let bound = let_bound(binder, handler, kw);
2369 list_at(kw.merge(bound.span), vec![sym_at(kw, "else"), bound])
2370 }
2371 None => list_at(
2372 head,
2373 vec![
2374 sym_at(head, "else"),
2375 list_at(
2376 head,
2377 vec![sym_at(head, LOWERED_RERAISE), sym_at(head, RESCUED)],
2378 ),
2379 ],
2380 ),
2381 };
2382 let mut cond = vec![sym_at(first_kw, "cond")];
2383 cond.extend(clauses);
2384 cond.push(otherwise);
2385 let cond_span = cover(&cond, first_kw);
2386 list_at(
2387 whole,
2388 vec![
2389 sym_at(head, "try"),
2390 body,
2391 list_at(
2392 cond_span,
2393 vec![
2394 sym_at(first_kw, "catch"),
2395 list_at(first_kw, vec![sym_at(first_kw, RESCUED)]),
2396 list_at(cond_span, cond),
2397 ],
2398 ),
2399 ],
2400 )
2401 };
2402 Ok(match ensure {
2403 None => protected,
2404 Some(cleanup) => {
2405 let attempted = list_at(
2406 protected.span,
2407 vec![sym_at(head, LOWERED_ATTEMPTED), protected],
2408 );
2409 let failed = list_at(
2410 head,
2411 vec![sym_at(head, LOWERED_FAILED), sym_at(head, RESCUED)],
2412 );
2413 let caught = list_at(
2414 whole,
2415 vec![
2416 sym_at(head, "try"),
2417 attempted,
2418 list_at(
2419 head,
2420 vec![
2421 sym_at(head, "catch"),
2422 list_at(head, vec![sym_at(head, RESCUED)]),
2423 failed,
2424 ],
2425 ),
2426 ],
2427 );
2428 list_at(
2429 whole,
2430 vec![sym_at(head, LOWERED_ENSURE), caught, thunk(cleanup, head)],
2431 )
2432 }
2433 })
2434 }
2435
2436 fn def_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2437 let name_span = self.peek_span();
2438 let name = match self.bump() {
2439 TokenKind::Ident(n) => n,
2440 other => {
2441 return Err(fail(
2442 format!("expected a name after `def`, found {other}"),
2443 name_span,
2444 ))
2445 }
2446 };
2447 let mut params: Vec<(String, Span, Option<Spanned>)> = Vec::new();
2448 if !self.at(&TokenKind::LParen) {
2449 self.note(
2450 Spelling::ParameterList,
2451 name_span,
2452 &format!("def {name}"),
2453 &format!("def {name}()"),
2454 );
2455 }
2456 if self.at(&TokenKind::LParen) {
2457 self.bump();
2458 self.skip_newlines();
2459 if !self.eat(&TokenKind::RParen) {
2460 loop {
2461 self.skip_newlines();
2462 let p_span = self.peek_span();
2463 match self.bump() {
2464 TokenKind::Ident(p) => params.push((p, p_span, None)),
2466 TokenKind::Label(p) => {
2468 self.skip_newlines();
2469 let ty = self.type_expr()?;
2470 params.push((p, p_span, Some(ty)));
2471 }
2472 other => {
2473 return Err(fail(
2474 format!("expected a parameter name, found {other}"),
2475 p_span,
2476 ))
2477 }
2478 }
2479 self.skip_newlines();
2480 if self.eat(&TokenKind::Comma) {
2481 if self.trailing_comma(&TokenKind::RParen) {
2482 break;
2483 }
2484 continue;
2485 }
2486 self.expect(&TokenKind::RParen, "`,` or `)`")?;
2487 break;
2488 }
2489 }
2490 }
2491 let sig_span = self.span_since(name_span.start);
2493
2494 let ret = if matches!(self.peek(), TokenKind::Op(o) if o == "->") {
2496 self.bump();
2497 self.skip_newlines();
2498 Some(self.type_expr()?)
2499 } else {
2500 None
2501 };
2502
2503 let begins = {
2504 let save = self.pos;
2505 self.skip_newlines();
2506 let at = self.at_ident("begin").then(|| self.peek_span());
2507 self.pos = save;
2508 at
2509 };
2510 let body = self.protected_body(head)?;
2511 if let Some(b) = begins {
2512 let protects = matches!(&body.form, SpannedForm::List(items)
2513 if matches!(items.first().and_then(Spanned::as_symbol), Some("try") | Some(LOWERED_ENSURE)));
2514 if protects && body.span.start == b.start {
2515 self.note(
2516 Spelling::TryCatch,
2517 b,
2518 "begin … end as a whole def body",
2519 "the clauses on the def itself",
2520 );
2521 }
2522 }
2523 self.expect_ident("end")?;
2524 let whole = self.span_since(head.start);
2525
2526 let annotated = ret.is_some() || params.iter().any(|(_, _, t)| t.is_some());
2527 if !annotated {
2528 let mut sig = vec![sym_at(name_span, &name)];
2529 sig.extend(params.into_iter().map(|(p, s, _)| sym_at(s, &p)));
2530 return Ok(list_at(
2531 whole,
2532 vec![sym_at(head, "define"), list_at(sig_span, sig), body],
2533 ));
2534 }
2535
2536 let mut sig = vec![sym_at(name_span, &name)];
2544 for (p, s, t) in params {
2545 let ty = t.unwrap_or_else(|| sym_at(s, "dyn"));
2546 sig.push(list_at(s.merge(ty.span), vec![sym_at(s, &p), ty]));
2547 }
2548 Ok(list_at(
2549 whole,
2550 vec![
2551 sym_at(head, "define-typed"),
2552 list_at(sig_span, sig),
2553 ret.unwrap_or_else(|| sym_at(head, "dyn")),
2554 body,
2555 ],
2556 ))
2557 }
2558
2559 fn type_expr(&mut self) -> Result<Spanned, Fail> {
2562 let name_span = self.peek_span();
2563 let name = match self.bump() {
2564 TokenKind::Ident(n) => n,
2565 other => {
2566 return Err(fail(
2567 format!("expected a type name, found {other}"),
2568 name_span,
2569 ))
2570 }
2571 };
2572 if self.at(&TokenKind::LParen) {
2573 let args = self.paren_args()?;
2574 let mut list = vec![sym_at(name_span, &name)];
2575 list.extend(args);
2576 return Ok(list_at(self.span_since(name_span.start), list));
2577 }
2578 Ok(sym_at(name_span, &name))
2579 }
2580
2581 fn eat_ident(&mut self, name: &str) -> bool {
2583 if self.at_ident(name) {
2584 self.bump();
2585 true
2586 } else {
2587 false
2588 }
2589 }
2590
2591 fn expect_ident(&mut self, name: &str) -> Result<(), Fail> {
2592 if self.at_ident(name) {
2593 self.bump();
2594 Ok(())
2595 } else {
2596 Err(self.error(format!("expected `{name}`, found {}", self.peek())))
2597 }
2598 }
2599
2600 fn body(&mut self, terminators: &[&str]) -> Result<Spanned, Fail> {
2603 let start = self.mark();
2606 let mut forms = Vec::new();
2607 loop {
2608 self.skip_newlines();
2609 if matches!(self.peek(), TokenKind::Eof) {
2610 return Err(self.error(format!(
2611 "unterminated block: expected {} before the end of the file",
2612 one_of(terminators)
2613 )));
2614 }
2615 if terminators.iter().any(|t| self.at_ident(t)) {
2616 break;
2617 }
2618 forms.push(self.statement()?);
2619 self.end_of_statement(terminators)?;
2620 }
2621 Ok(match forms.len() {
2622 0 => Spanned::new(Span::new(start, start), SpannedForm::Nil),
2623 1 => forms.into_iter().next().expect("checked len"),
2624 _ => {
2625 let span = cover(&forms, Span::new(start, start));
2631 let head = Span::new(span.start, span.start);
2637 let mut list = vec![sym_at(head, "begin")];
2638 list.extend(forms);
2639 list_at(span, list)
2640 }
2641 })
2642 }
2643}
2644
2645fn one_of(words: &[&str]) -> String {
2648 let quoted: Vec<String> = words.iter().map(|w| format!("`{w}`")).collect();
2649 match quoted.split_last() {
2650 None => String::new(),
2651 Some((last, [])) => last.clone(),
2652 Some((last, rest)) => format!("{} or {last}", rest.join(", ")),
2653 }
2654}
2655
2656fn let_bound(binder: Spanned, body: Spanned, at: Span) -> Spanned {
2657 let span = at.merge(body.span);
2658 let pair = list_at(binder.span, vec![binder, sym_at(at, RESCUED)]);
2659 list_at(
2660 span,
2661 vec![sym_at(at, "let"), list_at(pair.span, vec![pair]), body],
2662 )
2663}
2664
2665fn thunk(body: Spanned, at: Span) -> Spanned {
2666 let span = at.merge(body.span);
2667 list_at(
2668 span,
2669 vec![sym_at(at, "lambda"), list_at(at, Vec::new()), body],
2670 )
2671}
2672
2673#[must_use]
2678pub fn label_name(name: &str) -> bool {
2679 matches!(
2680 lex(&format!("{name}:")).as_deref(),
2681 Ok([t, e]) if matches!(&t.kind, TokenKind::Label(n) if n == name) && e.kind == TokenKind::Eof
2682 )
2683}
2684
2685fn blue_lang_name(name: &str) -> bool {
2686 matches!(
2687 lex(name).as_deref(),
2688 Ok([t, e]) if matches!(&t.kind, TokenKind::Ident(n) if n == name) && e.kind == TokenKind::Eof
2689 ) && !name.ends_with(['?', '!'])
2690}
2691
2692fn mentions(form: &Spanned, name: &str) -> bool {
2693 match &form.form {
2694 SpannedForm::Atom(Atom::Symbol(s)) => s == name,
2695 SpannedForm::List(items) => items.iter().any(|i| mentions(i, name)),
2696 SpannedForm::Quote(i)
2697 | SpannedForm::Quasiquote(i)
2698 | SpannedForm::Unquote(i)
2699 | SpannedForm::UnquoteSplice(i) => mentions(i, name),
2700 _ => false,
2701 }
2702}
2703
2704fn sym_at(span: Span, s: &str) -> Spanned {
2706 Spanned::new(span, SpannedForm::Atom(Atom::Symbol(s.to_string())))
2707}
2708
2709fn atom_at(span: Span, a: Atom) -> Spanned {
2711 Spanned::new(span, SpannedForm::Atom(a))
2712}
2713
2714fn list_at(span: Span, items: Vec<Spanned>) -> Spanned {
2716 Spanned::new(span, SpannedForm::List(items))
2717}
2718
2719fn cover(items: &[Spanned], fallback: Span) -> Span {
2727 let merged = items
2728 .iter()
2729 .fold(Span::synthetic(), |acc, s| acc.merge(s.span));
2730 if merged.is_synthetic() {
2731 fallback
2732 } else {
2733 merged
2734 }
2735}
2736
2737#[cfg(test)]
2738mod tests {
2739 use super::*;
2740
2741 fn q(src: &str) -> String {
2745 parse_expr(src)
2746 .map(|s| s.to_string())
2747 .unwrap_or_else(|e| panic!("{src:?}: {e}"))
2748 }
2749
2750 #[test]
2753 fn arithmetic_respects_precedence() {
2754 assert_eq!(q("1 + 2 * 3"), "(+ 1 (* 2 3))");
2755 assert_eq!(q("(1 + 2) * 3"), "(* (+ 1 2) 3)");
2756 }
2757
2758 #[test]
2759 fn comparison_binds_looser_than_arithmetic() {
2760 assert_eq!(q("a + 1 < b"), "(< (+ a 1) b)");
2761 }
2762
2763 #[test]
2768 fn logical_operators_bind_loosest_and_lower_to_tataras_names() {
2769 assert_eq!(q("a && b || c"), "(or (and a b) c)");
2770 }
2771
2772 #[test]
2773 fn left_associativity() {
2774 assert_eq!(q("1 - 2 - 3"), "(- (- 1 2) 3)");
2775 }
2776
2777 #[test]
2778 fn a_dot_without_parentheses_reads_a_field() {
2779 assert_eq!(q("user.name"), "(blue-field user :name)");
2780 }
2781
2782 #[test]
2783 fn method_call_with_args() {
2784 assert_eq!(q("user.greet(1, 2)"), "(greet user 1 2)");
2785 }
2786
2787 #[test]
2788 fn chained_fields_read_left_to_right() {
2789 assert_eq!(q("a.b.c"), "(blue-field (blue-field a :b) :c)");
2790 assert_eq!(q("a.b(1).c"), "(blue-field (b a 1) :c)");
2791 }
2792
2793 #[test]
2794 fn plain_call() {
2795 assert_eq!(q("f(1, 2)"), "(f 1 2)");
2796 }
2797
2798 #[test]
2801 fn pipeline_threads_into_first_argument() {
2802 assert_eq!(q("x |> f"), "(f x)");
2803 assert_eq!(q("x |> f(1)"), "(f x 1)");
2804 assert_eq!(q("x |> f |> g"), "(g (f x))");
2805 }
2806
2807 #[test]
2808 fn pipeline_binds_looser_than_arithmetic() {
2809 assert_eq!(q("1 + 2 |> f"), "(f (+ 1 2))");
2810 }
2811
2812 #[test]
2818 fn label_and_rocket_produce_the_same_tree_for_a_symbol_key() {
2819 assert_eq!(q("{a: 1}"), q("{:a => 1}"));
2820 assert_eq!(q("{a: 1}"), "(hash-map :a 1)");
2821 }
2822
2823 #[test]
2826 fn a_string_key_has_no_shorthand() {
2827 assert_eq!(q(r#"{"k" => 1}"#), r#"(hash-map "k" 1)"#);
2828 }
2829
2830 #[test]
2831 fn list_literal() {
2832 assert_eq!(q("[1, 2, 3]"), "(list 1 2 3)");
2833 assert_eq!(q("[]"), "(list)");
2834 }
2835
2836 #[test]
2839 fn if_else_end() {
2840 assert_eq!(q("if a\n 1\nelse\n 2\nend"), "(if a 1 2)");
2841 }
2842
2843 #[test]
2844 fn if_without_else() {
2845 assert_eq!(q("if a\n 1\nend"), "(if a 1)");
2846 }
2847
2848 #[test]
2851 fn unless_is_a_negated_if() {
2852 assert_eq!(q("unless a\n 1\nend"), "(if (not a) 1)");
2853 }
2854
2855 #[test]
2856 fn multi_statement_body_becomes_begin() {
2857 assert_eq!(q("if a\n 1\n 2\nend"), "(if a (begin 1 2))");
2858 }
2859
2860 #[test]
2861 fn def_lowers_to_define() {
2862 assert_eq!(
2863 q("def add(a, b)\n a + b\nend"),
2864 "(define (add a b) (+ a b))"
2865 );
2866 }
2867
2868 #[test]
2869 fn def_with_no_params() {
2870 assert_eq!(q("def zero()\n 0\nend"), "(define (zero) 0)");
2871 }
2872
2873 #[test]
2876 fn literals_lower_to_atoms() {
2877 assert_eq!(q("42"), "42");
2878 assert_eq!(q("true"), "#t");
2879 assert_eq!(q(":ok"), ":ok");
2880 assert_eq!(q(r#""hi""#), r#""hi""#);
2881 }
2882
2883 #[test]
2884 fn unary_minus_and_not() {
2885 assert_eq!(q("-x"), "(- 0 x)");
2886 assert_eq!(q("!x"), "(not x)");
2887 }
2888
2889 #[test]
2892 fn a_program_is_a_sequence_of_forms() {
2893 let forms = parse_program("def f()\n 1\nend\nf()").expect("parse");
2894 assert_eq!(forms.len(), 2);
2895 assert_eq!(forms[1].to_string(), "(f)");
2896 }
2897
2898 #[test]
2899 fn unterminated_block_is_an_error_naming_what_was_expected() {
2900 let e = parse_program("if a\n 1").expect_err("must fail");
2901 assert!(e.message.contains("unterminated"), "{}", e.message);
2902 }
2903
2904 #[test]
2905 fn a_parse_error_carries_a_span_into_the_source() {
2906 let src = "1 + )";
2907 let e = parse_program(src).expect_err("must fail");
2908 assert!(e.span.start < src.len(), "span {:?} outside source", e.span);
2909 }
2910
2911 #[test]
2918 fn postfix_indexing_is_refused_naming_nth() {
2919 let e = parse_program("y = xs[0]").expect_err("must not split into two statements");
2920 assert!(e.message.contains("`nth(0, xs)`"), "{}", e.message);
2921 assert_eq!((e.line, e.col), (1, 7), "{e}");
2922 }
2923
2924 #[test]
2926 fn a_call_without_parentheses_is_refused_naming_the_call() {
2927 let e = parse_program("a = f x").expect_err("must not split into two statements");
2928 assert!(e.message.contains("`f(x)`"), "{}", e.message);
2929 assert_eq!((e.line, e.col), (1, 7), "{e}");
2930 }
2931
2932 #[test]
2934 fn two_statements_on_one_line_are_refused_in_a_body() {
2935 for src in [
2936 "def f(xs)\n xs[0]\nend",
2937 "if a\n f x\nend",
2938 "fn(x) x 1 end",
2939 "test \"t\"\n assert a b\nend",
2940 "1 2",
2941 ] {
2942 assert!(parse_program(src).is_err(), "{src:?} must not parse");
2943 }
2944 }
2945
2946 #[test]
2948 fn an_arrow_lambda_lowers_to_the_same_tree_as_fn() {
2949 for (arrow, block) in [
2950 ("(x) -> x * 2", "fn(x) x * 2 end"),
2951 ("(a, b) -> a + b", "fn(a, b) a + b end"),
2952 ("() -> 1", "fn() 1 end"),
2953 ("map(xs, (x) -> x + 1)", "map(xs, fn(x) x + 1 end)"),
2954 ] {
2955 let a = parse_program(arrow).expect(arrow);
2956 let b = parse_program(block).expect(block);
2957 assert_eq!(format!("{a:?}"), format!("{b:?}"), "{arrow}");
2958 }
2959 assert_eq!(
2960 parse_program("(x)").map(|p| p.len()).ok(),
2961 Some(1),
2962 "a parenthesised name is still a group"
2963 );
2964 }
2965
2966 #[test]
2970 fn a_statement_may_end_at_a_newline_the_end_or_a_closing_keyword() {
2971 for src in [
2972 "fn(x) x + 1 end",
2973 "map(fn(x) x end, xs)",
2974 "if a\n 1\nelse\n 2\nend",
2975 "if a 1 else 2 end",
2976 "case x\nwhen 1 :a\nwhen 2 :b\nelse :c\nend",
2977 "x = 1\ny = 2",
2978 "x = 1",
2979 ] {
2980 assert!(
2981 parse_program(src).is_ok(),
2982 "{src:?}: {:?}",
2983 parse_program(src)
2984 );
2985 }
2986 }
2987
2988 #[test]
2990 fn then_is_refused_by_name() {
2991 let e = parse_program("x = if c then 1 else 2 end").expect_err("then is not a keyword");
2992 assert!(e.message.contains("`then`"), "{}", e.message);
2993 }
2994
2995 #[test]
2997 fn a_brace_block_is_refused_naming_fn() {
2998 let e = parse_program("xs.map { |x| x }").expect_err("no brace blocks");
2999 assert!(e.message.contains("fn(x)"), "{}", e.message);
3000 }
3001
3002 #[test]
3009 fn a_qualified_name_is_one_symbol() {
3010 assert_eq!(q("retsu::first(xs)"), "(retsu/first xs)");
3011 assert_eq!(q("retsu::first"), "retsu/first");
3012 assert_eq!(q("xs |> retsu::first"), "(retsu/first xs)");
3013 assert_eq!(q("blue::length(xs)"), "(blue/length xs)");
3014 }
3015
3016 #[test]
3018 fn whitespace_around_the_qualifier_is_accepted() {
3019 assert_eq!(q("retsu :: first(xs)"), "(retsu/first xs)");
3020 }
3021
3022 #[test]
3025 fn a_reserved_word_after_the_qualifier_is_a_name() {
3026 assert_eq!(q("kueri::if(a, b)"), "(kueri/if a b)");
3027 assert_eq!(q("heni::test(x)"), "(heni/test x)");
3028 assert_eq!(q("sabi::fn"), "sabi/fn");
3029 }
3030
3031 #[test]
3032 fn a_qualified_name_has_exactly_one_qualifier() {
3033 for (src, says) in [
3034 ("::first(xs)", "`package::name`"),
3035 ("a::b::c", "one qualifier"),
3036 ("if::x", "reserved word"),
3037 ("retsu::1", "expected a name after `retsu::`"),
3038 ] {
3039 let e = parse_program(src).expect_err(src);
3040 assert!(e.message.contains(says), "{src}: {}", e.message);
3041 }
3042 }
3043
3044 #[test]
3047 fn qualified_reads_only_the_package_name_shape() {
3048 assert_eq!(qualified("retsu/first"), Some(("retsu", "first")));
3049 assert_eq!(qualified("kueri/if"), Some(("kueri", "if")));
3050 assert_eq!(qualified("nisshi/pair?"), Some(("nisshi", "pair?")));
3051 for not in ["/", "a/", "/a", "a/b/c", "if/x", "hash-map", "a"] {
3052 assert_eq!(qualified(not), None, "{not}");
3053 }
3054 assert!(is_callable_name("kueri/if"));
3055 }
3056
3057 #[test]
3062 fn a_symbol_may_end_in_a_question_or_bang() {
3063 assert_eq!(q("[:pair?, :set!]"), "(list :pair? :set!)");
3064 assert_eq!(q("use(\"n\", [:empty?])"), "(use \"n\" (list :empty?))");
3065 assert_eq!(q(":a != :b"), "(not= :a :b)");
3066 }
3067
3068 #[test]
3072 fn a_parse_error_displays_line_col_and_the_source_text() {
3073 let e = parse_program("x = 1\ny = [1, 2 y]").expect_err("must fail");
3074 assert_eq!(e.to_string(), "2:11: expected `,` or `]`, found `y`");
3075 assert_eq!(e.span, Span::new(16, 17), "the span stays, as data");
3076 }
3077
3078 #[test]
3080 fn no_error_message_names_a_rust_variant() {
3081 for src in [
3082 "f(1 2)",
3083 "{a: 1 b}",
3084 "def 1\nend",
3085 "x.1",
3086 "if a\n",
3087 "fn(1) 2 end",
3088 "[1 :b]",
3089 "1 +",
3090 "(1",
3091 ] {
3092 let e = parse_program(src).expect_err(src);
3093 let shown = e.to_string();
3094 for tell in [
3095 "Ident(", "Int(", "Op(", "Label(", "Sym(", "Str(", "LParen", "RBracket", "Eof",
3096 "Newline", "..",
3097 ] {
3098 assert!(!shown.contains(tell), "{src:?} -> {shown}");
3099 }
3100 assert!(e.line >= 1 && e.col >= 1, "{src:?} not located: {shown}");
3101 }
3102 }
3103
3104 #[test]
3106 fn a_lex_error_is_located_too() {
3107 let e = parse_program("x = 1\ny = 2; z = 3").expect_err("no semicolons");
3108 assert_eq!((e.line, e.col), (2, 6), "{e}");
3109 assert!(e.message.contains("new line"), "{e}");
3110 }
3111
3112 #[test]
3115 fn the_parser_rejects_garbage() {
3116 assert!(parse_program("def").is_err());
3117 assert!(parse_program("(1").is_err());
3118 assert!(parse_program("end").is_err());
3119 }
3120}