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 infix_node(
1030 &mut self,
1031 start: usize,
1032 callee: Option<&str>,
1033 op_span: Span,
1034 lhs: Spanned,
1035 rhs: Spanned,
1036 ) -> Spanned {
1037 if callee == Some("-") && matches!(lhs.form, SpannedForm::Atom(Atom::Int(0))) {
1038 self.note(
1039 Spelling::OperatorSpelling,
1040 lhs.span.merge(op_span),
1041 "0 - x",
1042 "-x",
1043 );
1044 }
1045
1046 if callee.is_none() {
1047 let rhs_span = rhs.span;
1052 let threaded = match rhs.form {
1053 SpannedForm::List(mut items) if !items.is_empty() => {
1054 items.insert(1, lhs);
1055 list_at(self.span_since(start), items)
1056 }
1057 callee => list_at(
1058 self.span_since(start),
1059 vec![Spanned::new(rhs_span, callee), lhs],
1060 ),
1061 };
1062 match &threaded.form {
1063 SpannedForm::List(items)
1064 if items.len() == 2 && items[0].as_symbol() == Some("not") =>
1065 {
1066 let (head, operand) = (items[0].span, items[1].clone());
1067 self.negation(head, threaded.span, operand)
1068 }
1069 SpannedForm::List(items)
1070 if items.len() == 4
1071 && items[0].as_symbol() == Some("if")
1072 && matches!(items[3].form, SpannedForm::Nil) =>
1073 {
1074 list_at(threaded.span, items[..3].to_vec())
1075 }
1076 _ => threaded,
1077 }
1078 } else {
1079 list_at(
1085 self.span_since(start),
1086 vec![sym_at(op_span, callee.unwrap_or_default()), lhs, rhs],
1087 )
1088 }
1089 }
1090
1091 fn expr_inner(&mut self, min_bp: u8) -> Result<Spanned, Fail> {
1092 let start = self.mark();
1093 let mut lhs = self.prefix()?;
1094
1095 loop {
1096 match self.peek() {
1098 TokenKind::Dot => {
1099 let dot = self.peek_span();
1100 self.bump();
1101 lhs = self.finish_send(start, lhs, dot)?;
1102 continue;
1103 }
1104 TokenKind::LParen => {
1105 let (args, splat) = self.call_args()?;
1106 let call = self.call_node(self.span_since(start), vec![lhs], args, splat);
1107 lhs = self.call_spelling(call);
1108 continue;
1109 }
1110 _ => {}
1111 }
1112
1113 let op_span = self.peek_span();
1117 if self.at_ident("rescue") {
1118 if RESCUE_POWER.0 < min_bp {
1119 break;
1120 }
1121 self.bump();
1122 self.skip_newlines();
1123 let fallback = self.expr(RESCUE_POWER.1)?;
1124 lhs = self.rescue_node(start, op_span, lhs, fallback);
1125 continue;
1126 }
1127 let (callee, (lbp, rbp)) = match self.peek() {
1128 TokenKind::Pipe => (None, PIPE_POWER),
1129 other => match infix_of(other) {
1130 Some(i) => (Some(i.callee), i.power),
1131 None => break,
1132 },
1133 };
1134 if lbp < min_bp {
1135 break;
1136 }
1137 self.infix_spelling(callee, op_span);
1138 self.bump();
1139 self.skip_newlines();
1140 let rhs = self.expr(rbp)?;
1141 lhs = self.infix_node(start, callee, op_span, lhs, rhs);
1142 }
1143
1144 Ok(lhs)
1145 }
1146
1147 #[inline(never)]
1148 fn interpolation(span: Span, parts: &[String], exprs: &[String]) -> Result<Spanned, Fail> {
1149 let mut acc = atom_at(span, Atom::Str(parts[0].clone()));
1159 for (i, raw) in exprs.iter().enumerate() {
1160 let inner = parse_expr(raw).map_err(|e| {
1161 fail(
1162 format!("in interpolation `#{{{raw}}}`: {}", e.message),
1163 span,
1164 )
1165 })?;
1166 let inner = Spanned::from_sexp_at(&inner, span);
1167 acc = list_at(span, vec![sym_at(span, LOWERED_CONCAT), acc, inner]);
1168 acc = list_at(
1171 span,
1172 vec![
1173 sym_at(span, LOWERED_CONCAT),
1174 acc,
1175 atom_at(span, Atom::Str(parts[i + 1].clone())),
1176 ],
1177 );
1178 }
1179 Ok(acc)
1180 }
1181
1182 #[inline(never)]
1183 fn not_prefix(&mut self, span: Span) -> Result<Spanned, Fail> {
1184 if self.at(&TokenKind::LParen) && self.touches_previous() {
1185 self.note(Spelling::OperatorSpelling, span, "not(x)", "not x");
1186 let (args, splat) = self.call_args()?;
1187 if args.len() != 1 || splat.is_some() {
1188 let call = self.call_node(
1189 self.span_since(span.start),
1190 vec![sym_at(span, "not")],
1191 args,
1192 splat,
1193 );
1194 return Ok(call);
1195 }
1196 let operand = args.into_iter().next().expect("one argument");
1197 return Ok(self.negation(span, self.span_since(span.start), operand));
1198 }
1199 let rhs = self.expr(11)?;
1200 Ok(self.negation(span, self.span_since(span.start), rhs))
1201 }
1202
1203 fn prefix(&mut self) -> Result<Spanned, Fail> {
1204 if self.at(&TokenKind::LParen) {
1205 let span = self.peek_span();
1206 self.bump();
1207 if self.arrow_lambda_ahead() {
1208 return self.arrow_lambda(span);
1209 }
1210 return self.group();
1211 }
1212 self.prefix_token()
1213 }
1214
1215 fn group(&mut self) -> Result<Spanned, Fail> {
1216 self.skip_newlines();
1217 let inner = self.expr(0)?;
1218 self.skip_newlines();
1219 self.expect(&TokenKind::RParen, "`)`")?;
1220 Ok(inner)
1221 }
1222
1223 #[inline(never)]
1224 fn prefix_token(&mut self) -> Result<Spanned, Fail> {
1225 let span = self.peek_span();
1226 match self.bump() {
1227 TokenKind::Int(v) => Ok(atom_at(span, Atom::Int(v))),
1228 TokenKind::Float(v) => Ok(atom_at(span, Atom::Float(v))),
1229 TokenKind::Str(s) => Ok(atom_at(span, Atom::Str(s))),
1230
1231 TokenKind::InterpolatedStr { parts, exprs } => Self::interpolation(span, &parts, &exprs),
1242 TokenKind::Sym(s) => Ok(atom_at(span, Atom::Keyword(s))),
1243 TokenKind::True => Ok(atom_at(span, Atom::Bool(true))),
1244 TokenKind::False => Ok(atom_at(span, Atom::Bool(false))),
1245 TokenKind::Nil => Ok(Spanned::new(span, SpannedForm::Nil)),
1246
1247 TokenKind::Op(o) if o == "-" => {
1248 let rhs = self.expr(11)?; Ok(list_at(
1250 self.span_since(span.start),
1251 vec![sym_at(span, "-"), atom_at(span, Atom::Int(0)), rhs],
1252 ))
1253 }
1254 TokenKind::Op(o) if o == "!" => {
1255 let written = self.text(span).to_string();
1256 self.note(Spelling::LogicalSymbol, span, &written, "not");
1257 let rhs = self.expr(11)?;
1258 Ok(self.negation(span, self.span_since(span.start), rhs))
1259 }
1260 TokenKind::Ident(w) if w == "not" => self.not_prefix(span),
1261 TokenKind::Ident(w) if (w == "and" || w == "or") && self.at(&TokenKind::LParen) => {
1262 Ok(sym_at(span, &w))
1263 }
1264 TokenKind::Ident(w) if w == "and" || w == "or" => Err(fail(
1265 format!("`{w}` joins two expressions: `a {w} b`"),
1266 span,
1267 )),
1268
1269 TokenKind::LParen => {
1270 self.skip_newlines();
1271 let inner = self.expr(0)?;
1272 self.skip_newlines();
1273 self.expect(&TokenKind::RParen, "`)`")?;
1274 Ok(inner)
1278 }
1279
1280 TokenKind::LBracket => self.list_literal(span),
1281 TokenKind::LBrace => self.map_literal(span),
1282
1283 TokenKind::Ident(name) if self.at(&TokenKind::PathSep) => {
1284 self.qualified_name(span, &name)
1285 }
1286 TokenKind::PathSep => Err(fail(
1287 "a qualified name is `package::name`, with the package written before `::`",
1288 span,
1289 )),
1290 TokenKind::Ident(name) => match name.as_str() {
1291 "if" => self.if_form(span, false),
1292 "begin" if !(self.at(&TokenKind::LParen) && self.touches_previous()) => {
1293 self.begin_form(span)
1294 }
1295 "rescue" | "ensure" => Err(fail(
1296 format!("`{name}` belongs to a `begin … end` block or a `def` body, after the statements it protects"),
1297 span,
1298 )),
1299 "unless" => {
1300 self.note(Spelling::Unless, span, "unless", "if not");
1301 self.if_form(span, true)
1302 }
1303 "def" => self.def_form(span),
1304 "defmacro" => self.defmacro_form(span),
1305 "case" => self.case_form(span),
1306 "fn" => self.lambda_form(span),
1307 "test" => self.test_form(span),
1308 "assert" => self.assert_form(span),
1309 "quote" => self.quote_form(span),
1310 "unquote" => self.unquote_form(span, false),
1311 "unquote_splice" => self.unquote_form(span, true),
1312 "do" => Err(fail(
1313 "blue has no `do` blocks: pass a function as an argument, \
1314 `f(xs, fn(x) … end)`",
1315 span,
1316 )),
1317 "end" => Err(fail(
1318 "unexpected `end`: nothing open here for it to close",
1319 span,
1320 )),
1321 _ => Ok(sym_at(span, &name)),
1322 },
1323
1324 other => Err(fail(format!("expected an expression, found {other}"), span)),
1325 }
1326 }
1327
1328 fn qualified_name(&mut self, pkg_span: Span, pkg: &str) -> Result<Spanned, Fail> {
1338 if is_reserved_word(pkg) {
1339 return Err(fail(
1340 format!("`{pkg}` is a reserved word and cannot name a package"),
1341 pkg_span,
1342 ));
1343 }
1344 self.bump(); let name_span = self.peek_span();
1346 let name = match self.bump() {
1347 TokenKind::Ident(n) => n,
1348 TokenKind::True => "true".to_string(),
1350 TokenKind::False => "false".to_string(),
1351 TokenKind::Nil => "nil".to_string(),
1352 other => {
1353 return Err(fail(
1354 format!("expected a name after `{pkg}::`, found {other}"),
1355 name_span,
1356 ))
1357 }
1358 };
1359 if self.at(&TokenKind::PathSep) {
1360 return Err(self.error(format!(
1361 "a qualified name has one qualifier: `package::name`, and `{pkg}::{name}::…` has two"
1362 )));
1363 }
1364 Ok(sym_at(
1365 self.span_since(pkg_span.start),
1366 &qualify(pkg, &name),
1367 ))
1368 }
1369
1370 #[inline(never)]
1377 fn negation(&mut self, head: Span, whole: Span, operand: Spanned) -> Spanned {
1378 if let SpannedForm::List(items) = &operand.form {
1379 let flipped = match items.first().and_then(Spanned::as_symbol) {
1380 Some("equal?") => Some("not="),
1381 Some("not=") => Some("equal?"),
1382 _ => None,
1383 };
1384 if let (Some(to), 3) = (flipped, items.len()) {
1385 let (written, canonical) = if to == "not=" {
1386 ("not (a == b)", "a != b")
1387 } else {
1388 ("not (a != b)", "a == b")
1389 };
1390 self.note(Spelling::NegatedComparison, whole, written, canonical);
1391 let mut out = items.clone();
1392 out[0] = sym_at(items[0].span, to);
1393 return list_at(whole, out);
1394 }
1395 }
1396 list_at(whole, vec![sym_at(head, "not"), operand])
1397 }
1398
1399 #[inline(never)]
1400 fn call_spelling(&mut self, call: Spanned) -> Spanned {
1401 let SpannedForm::List(items) = &call.form else {
1402 return call;
1403 };
1404 let Some(head) = items.first().and_then(Spanned::as_symbol) else {
1405 return call;
1406 };
1407 let argc = items.len() - 1;
1408 let operator = match (head, argc) {
1409 ("and" | "or" | "mod" | "equal?", 2) => Some(head),
1410 ("list", _) => Some("list"),
1411 _ => None,
1412 };
1413 if let Some(op) = operator {
1414 let canonical = match op {
1415 "list" => "[…]".to_string(),
1416 "mod" => "a % b".to_string(),
1417 "equal?" => "a == b".to_string(),
1418 other => format!("a {other} b"),
1419 };
1420 self.note(
1421 Spelling::OperatorSpelling,
1422 call.span,
1423 &format!("{head}(…)"),
1424 &canonical,
1425 );
1426 return call;
1427 }
1428 if head == "apply"
1429 && self.text(items[0].span) == "apply"
1430 && argc >= 2
1431 && items[1].as_symbol().is_some_and(is_callable_name)
1432 {
1433 self.note(Spelling::Apply, call.span, "apply(f, …, xs)", "f(…, *xs)");
1434 return call;
1435 }
1436 if head == "try" {
1437 return self.try_spelling(call);
1438 }
1439 call
1440 }
1441
1442 fn try_spelling(&mut self, call: Spanned) -> Spanned {
1443 let SpannedForm::List(items) = &call.form else {
1444 return call;
1445 };
1446 let Some((catch, body)) = items[1..].split_last() else {
1447 return call;
1448 };
1449 let Some(clause) = catch.as_list() else {
1450 return call;
1451 };
1452 let binder = match clause {
1453 [head, binding, handler @ ..]
1454 if head.as_symbol() == Some("catch") && !handler.is_empty() =>
1455 {
1456 match binding.as_list() {
1457 Some([b]) => b.as_symbol().map(|n| (n.to_string(), b.span)),
1458 _ => None,
1459 }
1460 }
1461 _ => None,
1462 };
1463 let Some((name, binder_span)) = binder else {
1464 return call;
1465 };
1466 if body.is_empty() {
1467 return call;
1468 }
1469 self.note(Spelling::TryCatch, call.span, "try(…, catch(…))", "rescue");
1470 let handler = &clause[2..];
1471 let rename =
1472 name.starts_with('_') && name != "_" && !handler.iter().any(|h| mentions(h, &name));
1473 let body = if body.len() == 1 {
1474 body[0].clone()
1475 } else {
1476 let span = cover(body, call.span);
1477 let mut list = vec![sym_at(Span::new(span.start, span.start), "begin")];
1478 list.extend(body.iter().cloned());
1479 list_at(span, list)
1480 };
1481 let handler = if handler.len() == 1 {
1482 handler[0].clone()
1483 } else {
1484 let span = cover(handler, catch.span);
1485 let mut list = vec![sym_at(Span::new(span.start, span.start), "begin")];
1486 list.extend(handler.iter().cloned());
1487 list_at(span, list)
1488 };
1489 let binder = sym_at(binder_span, if rename { "_" } else { &name });
1490 list_at(
1491 call.span,
1492 vec![
1493 items[0].clone(),
1494 body,
1495 list_at(
1496 catch.span,
1497 vec![
1498 clause[0].clone(),
1499 list_at(clause[1].span, vec![binder]),
1500 handler,
1501 ],
1502 ),
1503 ],
1504 )
1505 }
1506
1507 fn finish_send(&mut self, start: usize, recv: Spanned, dot: Span) -> Result<Spanned, Fail> {
1508 let name_span = self.peek_span();
1509 let name = match self.bump() {
1510 TokenKind::Ident(n) if is_reserved_word(&n) => {
1524 return Err(self.error(format!(
1525 "`{n}` is a reserved word and cannot be a method name — \
1526 `recv.{n}` would lower onto the `{n}` form itself"
1527 )))
1528 }
1529 TokenKind::Ident(n) => n,
1530 other => {
1531 return Err(fail(
1532 format!("expected a method name after `.`, found {other}"),
1533 name_span,
1534 ))
1535 }
1536 };
1537 if !self.at(&TokenKind::LParen) {
1538 return Ok(list_at(
1539 self.span_since(start),
1540 vec![
1541 sym_at(dot, LOWERED_FIELD),
1542 recv,
1543 atom_at(name_span, Atom::Keyword(name)),
1544 ],
1545 ));
1546 }
1547 self.note(
1548 Spelling::CallForm,
1549 dot.merge(name_span),
1550 &format!("x.{name}(…)"),
1551 &format!("{name}(x, …)"),
1552 );
1553 let (args, splat) = self.call_args()?;
1554 Ok(self.call_node(
1555 self.span_since(start),
1556 vec![sym_at(name_span, &name), recv],
1557 args,
1558 splat,
1559 ))
1560 }
1561
1562 fn call_node(
1563 &self,
1564 span: Span,
1565 head: Vec<Spanned>,
1566 args: Vec<Spanned>,
1567 splat: Option<Span>,
1568 ) -> Spanned {
1569 let mut list = Vec::with_capacity(head.len() + args.len() + 1);
1570 if let Some(at) = splat {
1571 list.push(sym_at(at, "apply"));
1572 }
1573 list.extend(head);
1574 list.extend(args);
1575 list_at(span, list)
1576 }
1577
1578 fn paren_args(&mut self) -> Result<Vec<Spanned>, Fail> {
1579 let (args, splat) = self.call_args()?;
1580 if let Some(at) = splat {
1581 return Err(fail(
1582 "a splat `*xs` passes a list as a call's last arguments, and this is not a call",
1583 at,
1584 ));
1585 }
1586 Ok(args)
1587 }
1588
1589 fn call_args(&mut self) -> Result<(Vec<Spanned>, Option<Span>), Fail> {
1590 self.expect(&TokenKind::LParen, "`(`")?;
1591 let mut args = Vec::new();
1592 let mut splat = None;
1593 self.skip_newlines();
1594 if self.eat(&TokenKind::RParen) {
1595 return Ok((args, splat));
1596 }
1597 loop {
1598 self.skip_newlines();
1599 if splat.is_some() {
1600 return Err(
1601 self.error("a splat `*xs` passes a list as the LAST arguments: write it last")
1602 );
1603 }
1604 if matches!(self.peek(), TokenKind::Op(o) if o == "*") {
1605 splat = Some(self.peek_span());
1606 self.bump();
1607 }
1608 args.push(self.expr(0)?);
1609 self.skip_newlines();
1610 if self.eat(&TokenKind::Comma) {
1611 if self.trailing_comma(&TokenKind::RParen) {
1612 break;
1613 }
1614 continue;
1615 }
1616 self.expect(&TokenKind::RParen, "`,` or `)`")?;
1617 break;
1618 }
1619 Ok((args, splat))
1620 }
1621
1622 fn trailing_comma(&mut self, closer: &TokenKind) -> bool {
1623 let comma = self
1624 .toks
1625 .get(self.pos.wrapping_sub(1))
1626 .map_or(self.peek_span(), |t| t.span);
1627 self.skip_newlines();
1628 if self.at(closer) {
1629 self.note(Spelling::TrailingComma, comma, ",", "");
1630 self.bump();
1631 return true;
1632 }
1633 false
1634 }
1635
1636 fn list_literal(&mut self, open: Span) -> Result<Spanned, Fail> {
1637 let mut items = vec![sym_at(open, "list")];
1638 self.skip_newlines();
1639 if self.eat(&TokenKind::RBracket) {
1640 return Ok(list_at(self.span_since(open.start), items));
1641 }
1642 loop {
1643 self.skip_newlines();
1644 items.push(self.expr(0)?);
1645 self.skip_newlines();
1646 if self.eat(&TokenKind::Comma) {
1647 if self.trailing_comma(&TokenKind::RBracket) {
1648 break;
1649 }
1650 continue;
1651 }
1652 self.expect(&TokenKind::RBracket, "`,` or `]`")?;
1653 break;
1654 }
1655 Ok(list_at(self.span_since(open.start), items))
1656 }
1657
1658 fn map_literal(&mut self, open: Span) -> Result<Spanned, Fail> {
1665 let mut items = vec![sym_at(open, LOWERED_MAP)];
1666 self.skip_newlines();
1667 if self.eat(&TokenKind::RBrace) {
1668 return Ok(list_at(self.span_since(open.start), items));
1669 }
1670 loop {
1671 self.skip_newlines();
1672 match self.peek().clone() {
1673 TokenKind::Label(name) => {
1674 let label_span = self.peek_span();
1675 self.bump();
1676 self.skip_newlines();
1677 items.push(atom_at(label_span, Atom::Keyword(name.clone())));
1678 if matches!(self.peek(), TokenKind::Comma | TokenKind::RBrace) {
1679 items.push(sym_at(label_span, &name));
1680 } else {
1681 let value = self.expr(0)?;
1682 if matches!(&value.form, SpannedForm::Atom(Atom::Symbol(s)) if *s == name)
1683 && label_name(&name)
1684 {
1685 let written = format!("{name}: {name}");
1686 let canonical = format!("{name}:");
1687 self.note(Spelling::MapPun, label_span.merge(value.span), &written, &canonical);
1688 }
1689 items.push(value);
1690 }
1691 }
1692 _ => {
1693 let k = self.expr(0)?;
1694 self.skip_newlines();
1695 if let SpannedForm::Atom(Atom::Keyword(name)) = &k.form {
1696 if blue_lang_name(name) {
1697 let written = format!(":{name} =>");
1698 let canonical = format!("{name}:");
1699 self.note(Spelling::KeywordRocket, k.span, &written, &canonical);
1700 }
1701 }
1702 self.expect(&TokenKind::Rocket, "`=>` in a map literal")?;
1703 self.skip_newlines();
1704 items.push(k);
1705 items.push(self.expr(0)?);
1706 }
1707 }
1708 self.skip_newlines();
1709 if self.eat(&TokenKind::Comma) {
1710 if self.trailing_comma(&TokenKind::RBrace) {
1711 break;
1712 }
1713 continue;
1714 }
1715 self.expect(&TokenKind::RBrace, "`,` or `}`")?;
1716 break;
1717 }
1718 Ok(list_at(self.span_since(open.start), items))
1719 }
1720
1721 fn if_chain(&mut self, head: Span) -> Result<Spanned, Fail> {
1731 let cond = self.expr(0)?;
1732 let then = self.body(&["elsif", "else", "end"])?;
1733 let els = self.else_part()?;
1734 let mut out = vec![sym_at(head, "if"), cond, then];
1735 if let Some(e) = els {
1736 out.push(e);
1737 }
1738 Ok(list_at(self.span_since(head.start), out))
1739 }
1740
1741 fn else_part(&mut self) -> Result<Option<Spanned>, Fail> {
1742 if self.at_ident("elsif") {
1743 let elsif = self.peek_span();
1744 self.bump();
1745 return Ok(Some(self.if_chain(elsif)?));
1746 }
1747 if !self.at_ident("else") {
1748 self.expect_ident("end")?;
1749 return Ok(None);
1750 }
1751 let else_span = self.peek_span();
1752 self.bump();
1753 let save = self.pos;
1754 self.skip_newlines();
1755 let leads_with_if = self.at_ident("if") || self.at_ident("unless");
1756 self.pos = save;
1757 let e = self.body(&["end"])?;
1758 let end_at = self.peek_span().start;
1759 self.expect_ident("end")?;
1760 if matches!(e.form, SpannedForm::Nil) && !self.comment_within(else_span.start, end_at) {
1761 self.note(Spelling::ElseNil, else_span, "else nil", "no else");
1762 return Ok(None);
1763 }
1764 let single_if = matches!(&e.form, SpannedForm::List(items)
1765 if (items.len() == 3 || items.len() == 4) && items[0].as_symbol() == Some("if"));
1766 if leads_with_if && single_if {
1767 self.note(Spelling::ElseIf, else_span, "else if", "elsif");
1768 }
1769 Ok(Some(e))
1770 }
1771
1772 fn if_form(&mut self, head: Span, negate: bool) -> Result<Spanned, Fail> {
1773 let cond = self.expr(0)?;
1774 let cond = if negate {
1775 let inner = cond.span;
1778 self.negation(head, head.merge(inner), cond)
1779 } else {
1780 cond
1781 };
1782 let then = self.body(&["elsif", "else", "end"])?;
1795 let els = self.else_part()?;
1796 let mut out = vec![sym_at(head, "if"), cond, then];
1797 if let Some(e) = els {
1798 out.push(e);
1799 }
1800 Ok(list_at(self.span_since(head.start), out))
1801 }
1802
1803 fn case_form(&mut self, head: Span) -> Result<Spanned, Fail> {
1829 let subject = self.expr(0)?;
1830 self.skip_newlines();
1831
1832 let subject_var = "case-subject";
1835 let mut arms: Vec<Spanned> = Vec::new();
1836 let mut otherwise: Option<Spanned> = None;
1837 let mut else_kw: Option<Span> = None;
1840
1841 loop {
1842 self.skip_newlines();
1843 if self.at_ident("end") {
1844 break;
1845 }
1846 let maybe_else = self.peek_span();
1847 if self.eat_ident("else") {
1848 else_kw = Some(maybe_else);
1849 otherwise = Some(self.body(&["end"])?);
1850 continue;
1851 }
1852 let when = self.peek_span();
1853 if !self.eat_ident("when") {
1854 return Err(self.error(format!(
1855 "expected `when`, `else` or `end` in a case, found {}",
1856 self.peek()
1857 )));
1858 }
1859 self.skip_newlines();
1860 let pattern = self.expr(0)?;
1861 let body = self.body(&["when", "else", "end"])?;
1862 let test = list_at(
1866 when.merge(pattern.span),
1867 vec![sym_at(when, "equal?"), sym_at(when, subject_var), pattern],
1868 );
1869 arms.push(list_at(when.merge(body.span), vec![test, body]));
1870 }
1871 self.expect_ident("end")?;
1872
1873 if arms.is_empty() && otherwise.is_none() {
1874 return Err(self.error("a case needs at least one `when` or an `else`".to_string()));
1875 }
1876
1877 let mut cond = vec![sym_at(head, "cond")];
1882 cond.extend(arms);
1883 let else_span = else_kw.unwrap_or(head);
1890 let else_body = otherwise.unwrap_or_else(|| Spanned::new(else_span, SpannedForm::Nil));
1891 cond.push(list_at(
1892 else_span.merge(else_body.span),
1893 vec![sym_at(else_span, "else"), else_body],
1894 ));
1895
1896 let whole = self.span_since(head.start);
1897 let binding = list_at(
1898 head.merge(subject.span),
1899 vec![sym_at(head, subject_var), subject],
1900 );
1901 Ok(list_at(
1902 whole,
1903 vec![
1904 sym_at(head, "let"),
1905 list_at(binding.span, vec![binding]),
1906 list_at(whole, cond),
1907 ],
1908 ))
1909 }
1910
1911 fn arrow_lambda_ahead(&self) -> bool {
1914 let mut i = self.pos;
1915 let kind = |i: usize| self.toks.get(i).map(|t| &t.kind);
1916 if !matches!(kind(i), Some(TokenKind::RParen)) {
1917 loop {
1918 if !matches!(kind(i), Some(TokenKind::Ident(_))) {
1919 return false;
1920 }
1921 i += 1;
1922 match kind(i) {
1923 Some(TokenKind::Comma) => i += 1,
1924 Some(TokenKind::RParen) => break,
1925 _ => return false,
1926 }
1927 }
1928 }
1929 matches!(kind(i + 1), Some(TokenKind::Op(o)) if o == "->")
1930 }
1931
1932 fn arrow_lambda(&mut self, head: Span) -> Result<Spanned, Fail> {
1939 let mut params: Vec<Spanned> = Vec::new();
1940 while !self.at(&TokenKind::RParen) {
1941 let p_span = self.peek_span();
1942 match self.bump() {
1943 TokenKind::Ident(p) => params.push(sym_at(p_span, &p)),
1944 other => {
1945 return Err(fail(
1946 format!("expected a parameter name, found {other}"),
1947 p_span,
1948 ))
1949 }
1950 }
1951 self.eat(&TokenKind::Comma);
1952 }
1953 self.bump();
1954 self.bump();
1955 self.skip_newlines();
1956 let params_span = cover(¶ms, head);
1957 let body = self.expr(0)?;
1958 Ok(list_at(
1959 self.span_since(head.start),
1960 vec![sym_at(head, "lambda"), list_at(params_span, params), body],
1961 ))
1962 }
1963
1964 fn lambda_form(&mut self, head: Span) -> Result<Spanned, Fail> {
1974 let mut params: Vec<Spanned> = Vec::new();
1975 if !self.at(&TokenKind::LParen) {
1976 self.note(Spelling::ParameterList, head, "fn", "fn()");
1977 }
1978 if self.at(&TokenKind::LParen) {
1979 self.bump();
1980 self.skip_newlines();
1981 if !self.eat(&TokenKind::RParen) {
1982 loop {
1983 self.skip_newlines();
1984 let p_span = self.peek_span();
1985 match self.bump() {
1986 TokenKind::Ident(p) => params.push(sym_at(p_span, &p)),
1987 other => {
1988 return Err(fail(
1989 format!("expected a parameter name, found {other}"),
1990 p_span,
1991 ))
1992 }
1993 }
1994 self.skip_newlines();
1995 if self.eat(&TokenKind::Comma) {
1996 if self.trailing_comma(&TokenKind::RParen) {
1997 break;
1998 }
1999 continue;
2000 }
2001 self.expect(&TokenKind::RParen, "`,` or `)`")?;
2002 break;
2003 }
2004 }
2005 }
2006 let params_span = cover(¶ms, head);
2007 let body = self.body(&["end"])?;
2008 self.expect_ident("end")?;
2009 Ok(list_at(
2010 self.span_since(head.start),
2011 vec![sym_at(head, "lambda"), list_at(params_span, params), body],
2012 ))
2013 }
2014
2015 fn test_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2021 let name_span = self.peek_span();
2022 let name = match self.bump() {
2023 TokenKind::Str(s) => s,
2024 other => {
2025 return Err(fail(
2026 format!("expected a string name after `test`, found {other}"),
2027 name_span,
2028 ))
2029 }
2030 };
2031 let body = self.body(&["end"])?;
2032 self.expect_ident("end")?;
2033 Ok(list_at(
2034 self.span_since(head.start),
2035 vec![
2036 sym_at(head, "deftest"),
2037 atom_at(name_span, Atom::Str(name)),
2038 body,
2039 ],
2040 ))
2041 }
2042
2043 fn assert_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2067 let e = self.expr(0)?;
2068 let quoted = Spanned::new(e.span, SpannedForm::Quote(Box::new(e.clone())));
2069 Ok(list_at(
2070 self.span_since(head.start),
2071 vec![sym_at(head, LOWERED_ASSERT), quoted, e],
2072 ))
2073 }
2074
2075 fn defmacro_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2084 let name_span = self.peek_span();
2085 let name = match self.bump() {
2086 TokenKind::Ident(n) => n,
2087 other => {
2088 return Err(fail(
2089 format!("expected a name after `defmacro`, found {other}"),
2090 name_span,
2091 ))
2092 }
2093 };
2094 let mut params: Vec<Spanned> = Vec::new();
2095 if self.at(&TokenKind::LParen) {
2096 self.bump();
2097 self.skip_newlines();
2098 if !self.eat(&TokenKind::RParen) {
2099 loop {
2100 self.skip_newlines();
2101 let p_span = self.peek_span();
2102 match self.bump() {
2103 TokenKind::Ident(p) => params.push(sym_at(p_span, &p)),
2104 TokenKind::Label(p) => {
2105 return Err(self.error(format!(
2106 "macro parameter `{p}` cannot be typed: a macro receives \
2107 source forms, not values"
2108 )))
2109 }
2110 other => {
2111 return Err(fail(
2112 format!("expected a parameter name, found {other}"),
2113 p_span,
2114 ))
2115 }
2116 }
2117 self.skip_newlines();
2118 if self.eat(&TokenKind::Comma) {
2119 if self.trailing_comma(&TokenKind::RParen) {
2120 break;
2121 }
2122 continue;
2123 }
2124 self.expect(&TokenKind::RParen, "`,` or `)`")?;
2125 break;
2126 }
2127 }
2128 }
2129 if matches!(self.peek(), TokenKind::Op(o) if o == "->") {
2130 return Err(self.error(
2131 "a macro has no return type: it produces source forms, not values".to_string(),
2132 ));
2133 }
2134
2135 let params_span = cover(¶ms, name_span);
2136 let body = self.body(&["end"])?;
2137 self.expect_ident("end")?;
2138
2139 Ok(list_at(
2142 self.span_since(head.start),
2143 vec![
2144 sym_at(head, "defmacro"),
2145 sym_at(name_span, &name),
2146 list_at(params_span, params),
2147 body,
2148 ],
2149 ))
2150 }
2151
2152 fn quote_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2158 let body = self.body(&["end"])?;
2159 self.expect_ident("end")?;
2160 Ok(Spanned::new(
2169 self.span_since(head.start),
2170 SpannedForm::Quasiquote(Box::new(body)),
2171 ))
2172 }
2173
2174 fn unquote_form(&mut self, head: Span, splice: bool) -> Result<Spanned, Fail> {
2176 self.expect(&TokenKind::LParen, "`(` after unquote")?;
2177 self.skip_newlines();
2178 let inner = self.expr(0)?;
2179 self.skip_newlines();
2180 self.expect(&TokenKind::RParen, "`)`")?;
2181 let span = self.span_since(head.start);
2182 Ok(Spanned::new(
2183 span,
2184 if splice {
2185 SpannedForm::UnquoteSplice(Box::new(inner))
2186 } else {
2187 SpannedForm::Unquote(Box::new(inner))
2188 },
2189 ))
2190 }
2191
2192 fn protected_body(&mut self, head: Span) -> Result<Spanned, Fail> {
2193 let body = self.body(&["rescue", "ensure", "end"])?;
2194 if self.at_ident("rescue") || self.at_ident("ensure") {
2195 return self.rescue_tail(head, body);
2196 }
2197 Ok(body)
2198 }
2199
2200 fn begin_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2201 let body = self.body(&["rescue", "ensure", "end"])?;
2202 if !(self.at_ident("rescue") || self.at_ident("ensure")) {
2203 return Err(self.error(
2204 "a `begin` block protects its statements with `rescue` or `ensure`; without either, write the statements directly",
2205 ));
2206 }
2207 let tree = self.rescue_tail(head, body)?;
2208 self.expect_ident("end")?;
2209 Ok(Spanned::new(self.span_since(head.start), tree.form))
2210 }
2211
2212 fn rescue_tail(&mut self, head: Span, body: Spanned) -> Result<Spanned, Fail> {
2213 let mut clauses: Vec<Spanned> = Vec::new();
2214 let mut catch_all: Option<(Spanned, Spanned, Span)> = None;
2215 while self.at_ident("rescue") {
2216 let kw = self.peek_span();
2217 self.bump();
2218 if catch_all.is_some() {
2219 return Err(fail(
2220 "a `rescue NAME` catches every error, so it is the last `rescue`; put the kinds it would shadow above it",
2221 kw,
2222 ));
2223 }
2224 match self.peek().clone() {
2225 TokenKind::Sym(_) => {
2226 let mut kinds = Vec::new();
2227 loop {
2228 let k_span = self.peek_span();
2229 match self.bump() {
2230 TokenKind::Sym(k) => kinds.push(atom_at(k_span, Atom::Keyword(k))),
2231 other => {
2232 return Err(fail(
2233 format!("expected an error kind such as `:not_found`, found {other}"),
2234 k_span,
2235 ))
2236 }
2237 }
2238 if self.eat(&TokenKind::Comma) {
2239 self.skip_newlines();
2240 continue;
2241 }
2242 break;
2243 }
2244 let binder = if self.eat_ident("as") {
2245 let b_span = self.peek_span();
2246 match self.bump() {
2247 TokenKind::Ident(b) if !is_reserved_word(&b) => Some(sym_at(b_span, &b)),
2248 other => {
2249 return Err(fail(
2250 format!("expected the name the error is bound to after `as`, found {other}"),
2251 b_span,
2252 ))
2253 }
2254 }
2255 } else {
2256 None
2257 };
2258 let handler = self.body(&["rescue", "ensure", "end"])?;
2259 let mut test = vec![sym_at(kw, LOWERED_ERROR_MATCH), sym_at(kw, RESCUED)];
2260 test.extend(kinds);
2261 let test = list_at(cover(&test, kw), test);
2262 let handler = match binder {
2263 Some(b) => let_bound(b, handler, kw),
2264 None => handler,
2265 };
2266 clauses.push(list_at(kw.merge(handler.span), vec![test, handler]));
2267 }
2268 TokenKind::Ident(b) if !is_reserved_word(&b) => {
2269 let b_span = self.peek_span();
2270 self.bump();
2271 let handler = self.body(&["rescue", "ensure", "end"])?;
2272 catch_all = Some((sym_at(b_span, &b), handler, kw));
2273 }
2274 _ => {
2275 return Err(fail(
2276 "`rescue` names what it catches: `rescue :kind`, `rescue :a, :b as e`, or `rescue e` for every error (`rescue _` discards it)",
2277 kw,
2278 ))
2279 }
2280 }
2281 }
2282 let ensure = if self.at_ident("ensure") {
2283 self.bump();
2284 let cleanup = self.body(&["rescue", "end"])?;
2285 if self.at_ident("rescue") {
2286 return Err(self.error("`ensure` comes after every `rescue` of its block"));
2287 }
2288 Some(cleanup)
2289 } else {
2290 None
2291 };
2292 let whole = self.span_since(head.start);
2293 let protected = if clauses.is_empty() {
2294 match catch_all {
2295 None => body,
2296 Some((binder, handler, kw)) => list_at(
2297 whole,
2298 vec![
2299 sym_at(head, "try"),
2300 body,
2301 list_at(
2302 kw.merge(handler.span),
2303 vec![
2304 sym_at(kw, "catch"),
2305 list_at(binder.span, vec![binder]),
2306 handler,
2307 ],
2308 ),
2309 ],
2310 ),
2311 }
2312 } else {
2313 let first_kw = clauses[0].span;
2314 let otherwise = match catch_all {
2315 Some((binder, handler, kw)) => {
2316 let bound = let_bound(binder, handler, kw);
2317 list_at(kw.merge(bound.span), vec![sym_at(kw, "else"), bound])
2318 }
2319 None => list_at(
2320 head,
2321 vec![
2322 sym_at(head, "else"),
2323 list_at(
2324 head,
2325 vec![sym_at(head, LOWERED_RERAISE), sym_at(head, RESCUED)],
2326 ),
2327 ],
2328 ),
2329 };
2330 let mut cond = vec![sym_at(first_kw, "cond")];
2331 cond.extend(clauses);
2332 cond.push(otherwise);
2333 let cond_span = cover(&cond, first_kw);
2334 list_at(
2335 whole,
2336 vec![
2337 sym_at(head, "try"),
2338 body,
2339 list_at(
2340 cond_span,
2341 vec![
2342 sym_at(first_kw, "catch"),
2343 list_at(first_kw, vec![sym_at(first_kw, RESCUED)]),
2344 list_at(cond_span, cond),
2345 ],
2346 ),
2347 ],
2348 )
2349 };
2350 Ok(match ensure {
2351 None => protected,
2352 Some(cleanup) => {
2353 let attempted = list_at(
2354 protected.span,
2355 vec![sym_at(head, LOWERED_ATTEMPTED), protected],
2356 );
2357 let failed = list_at(
2358 head,
2359 vec![sym_at(head, LOWERED_FAILED), sym_at(head, RESCUED)],
2360 );
2361 let caught = list_at(
2362 whole,
2363 vec![
2364 sym_at(head, "try"),
2365 attempted,
2366 list_at(
2367 head,
2368 vec![
2369 sym_at(head, "catch"),
2370 list_at(head, vec![sym_at(head, RESCUED)]),
2371 failed,
2372 ],
2373 ),
2374 ],
2375 );
2376 list_at(
2377 whole,
2378 vec![sym_at(head, LOWERED_ENSURE), caught, thunk(cleanup, head)],
2379 )
2380 }
2381 })
2382 }
2383
2384 fn def_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2385 let name_span = self.peek_span();
2386 let name = match self.bump() {
2387 TokenKind::Ident(n) => n,
2388 other => {
2389 return Err(fail(
2390 format!("expected a name after `def`, found {other}"),
2391 name_span,
2392 ))
2393 }
2394 };
2395 let mut params: Vec<(String, Span, Option<Spanned>)> = Vec::new();
2396 if !self.at(&TokenKind::LParen) {
2397 self.note(
2398 Spelling::ParameterList,
2399 name_span,
2400 &format!("def {name}"),
2401 &format!("def {name}()"),
2402 );
2403 }
2404 if self.at(&TokenKind::LParen) {
2405 self.bump();
2406 self.skip_newlines();
2407 if !self.eat(&TokenKind::RParen) {
2408 loop {
2409 self.skip_newlines();
2410 let p_span = self.peek_span();
2411 match self.bump() {
2412 TokenKind::Ident(p) => params.push((p, p_span, None)),
2414 TokenKind::Label(p) => {
2416 self.skip_newlines();
2417 let ty = self.type_expr()?;
2418 params.push((p, p_span, Some(ty)));
2419 }
2420 other => {
2421 return Err(fail(
2422 format!("expected a parameter name, found {other}"),
2423 p_span,
2424 ))
2425 }
2426 }
2427 self.skip_newlines();
2428 if self.eat(&TokenKind::Comma) {
2429 if self.trailing_comma(&TokenKind::RParen) {
2430 break;
2431 }
2432 continue;
2433 }
2434 self.expect(&TokenKind::RParen, "`,` or `)`")?;
2435 break;
2436 }
2437 }
2438 }
2439 let sig_span = self.span_since(name_span.start);
2441
2442 let ret = if matches!(self.peek(), TokenKind::Op(o) if o == "->") {
2444 self.bump();
2445 self.skip_newlines();
2446 Some(self.type_expr()?)
2447 } else {
2448 None
2449 };
2450
2451 let begins = {
2452 let save = self.pos;
2453 self.skip_newlines();
2454 let at = self.at_ident("begin").then(|| self.peek_span());
2455 self.pos = save;
2456 at
2457 };
2458 let body = self.protected_body(head)?;
2459 if let Some(b) = begins {
2460 let protects = matches!(&body.form, SpannedForm::List(items)
2461 if matches!(items.first().and_then(Spanned::as_symbol), Some("try") | Some(LOWERED_ENSURE)));
2462 if protects && body.span.start == b.start {
2463 self.note(
2464 Spelling::TryCatch,
2465 b,
2466 "begin … end as a whole def body",
2467 "the clauses on the def itself",
2468 );
2469 }
2470 }
2471 self.expect_ident("end")?;
2472 let whole = self.span_since(head.start);
2473
2474 let annotated = ret.is_some() || params.iter().any(|(_, _, t)| t.is_some());
2475 if !annotated {
2476 let mut sig = vec![sym_at(name_span, &name)];
2477 sig.extend(params.into_iter().map(|(p, s, _)| sym_at(s, &p)));
2478 return Ok(list_at(
2479 whole,
2480 vec![sym_at(head, "define"), list_at(sig_span, sig), body],
2481 ));
2482 }
2483
2484 let mut sig = vec![sym_at(name_span, &name)];
2492 for (p, s, t) in params {
2493 let ty = t.unwrap_or_else(|| sym_at(s, "dyn"));
2494 sig.push(list_at(s.merge(ty.span), vec![sym_at(s, &p), ty]));
2495 }
2496 Ok(list_at(
2497 whole,
2498 vec![
2499 sym_at(head, "define-typed"),
2500 list_at(sig_span, sig),
2501 ret.unwrap_or_else(|| sym_at(head, "dyn")),
2502 body,
2503 ],
2504 ))
2505 }
2506
2507 fn type_expr(&mut self) -> Result<Spanned, Fail> {
2510 let name_span = self.peek_span();
2511 let name = match self.bump() {
2512 TokenKind::Ident(n) => n,
2513 other => {
2514 return Err(fail(
2515 format!("expected a type name, found {other}"),
2516 name_span,
2517 ))
2518 }
2519 };
2520 if self.at(&TokenKind::LParen) {
2521 let args = self.paren_args()?;
2522 let mut list = vec![sym_at(name_span, &name)];
2523 list.extend(args);
2524 return Ok(list_at(self.span_since(name_span.start), list));
2525 }
2526 Ok(sym_at(name_span, &name))
2527 }
2528
2529 fn eat_ident(&mut self, name: &str) -> bool {
2531 if self.at_ident(name) {
2532 self.bump();
2533 true
2534 } else {
2535 false
2536 }
2537 }
2538
2539 fn expect_ident(&mut self, name: &str) -> Result<(), Fail> {
2540 if self.at_ident(name) {
2541 self.bump();
2542 Ok(())
2543 } else {
2544 Err(self.error(format!("expected `{name}`, found {}", self.peek())))
2545 }
2546 }
2547
2548 fn body(&mut self, terminators: &[&str]) -> Result<Spanned, Fail> {
2551 let start = self.mark();
2554 let mut forms = Vec::new();
2555 loop {
2556 self.skip_newlines();
2557 if matches!(self.peek(), TokenKind::Eof) {
2558 return Err(self.error(format!(
2559 "unterminated block: expected {} before the end of the file",
2560 one_of(terminators)
2561 )));
2562 }
2563 if terminators.iter().any(|t| self.at_ident(t)) {
2564 break;
2565 }
2566 forms.push(self.statement()?);
2567 self.end_of_statement(terminators)?;
2568 }
2569 Ok(match forms.len() {
2570 0 => Spanned::new(Span::new(start, start), SpannedForm::Nil),
2571 1 => forms.into_iter().next().expect("checked len"),
2572 _ => {
2573 let span = cover(&forms, Span::new(start, start));
2579 let head = Span::new(span.start, span.start);
2585 let mut list = vec![sym_at(head, "begin")];
2586 list.extend(forms);
2587 list_at(span, list)
2588 }
2589 })
2590 }
2591}
2592
2593fn one_of(words: &[&str]) -> String {
2596 let quoted: Vec<String> = words.iter().map(|w| format!("`{w}`")).collect();
2597 match quoted.split_last() {
2598 None => String::new(),
2599 Some((last, [])) => last.clone(),
2600 Some((last, rest)) => format!("{} or {last}", rest.join(", ")),
2601 }
2602}
2603
2604fn let_bound(binder: Spanned, body: Spanned, at: Span) -> Spanned {
2605 let span = at.merge(body.span);
2606 let pair = list_at(binder.span, vec![binder, sym_at(at, RESCUED)]);
2607 list_at(
2608 span,
2609 vec![sym_at(at, "let"), list_at(pair.span, vec![pair]), body],
2610 )
2611}
2612
2613fn thunk(body: Spanned, at: Span) -> Spanned {
2614 let span = at.merge(body.span);
2615 list_at(
2616 span,
2617 vec![sym_at(at, "lambda"), list_at(at, Vec::new()), body],
2618 )
2619}
2620
2621#[must_use]
2626pub fn label_name(name: &str) -> bool {
2627 matches!(
2628 lex(&format!("{name}:")).as_deref(),
2629 Ok([t, e]) if matches!(&t.kind, TokenKind::Label(n) if n == name) && e.kind == TokenKind::Eof
2630 )
2631}
2632
2633fn blue_lang_name(name: &str) -> bool {
2634 matches!(
2635 lex(name).as_deref(),
2636 Ok([t, e]) if matches!(&t.kind, TokenKind::Ident(n) if n == name) && e.kind == TokenKind::Eof
2637 ) && !name.ends_with(['?', '!'])
2638}
2639
2640fn mentions(form: &Spanned, name: &str) -> bool {
2641 match &form.form {
2642 SpannedForm::Atom(Atom::Symbol(s)) => s == name,
2643 SpannedForm::List(items) => items.iter().any(|i| mentions(i, name)),
2644 SpannedForm::Quote(i)
2645 | SpannedForm::Quasiquote(i)
2646 | SpannedForm::Unquote(i)
2647 | SpannedForm::UnquoteSplice(i) => mentions(i, name),
2648 _ => false,
2649 }
2650}
2651
2652fn sym_at(span: Span, s: &str) -> Spanned {
2654 Spanned::new(span, SpannedForm::Atom(Atom::Symbol(s.to_string())))
2655}
2656
2657fn atom_at(span: Span, a: Atom) -> Spanned {
2659 Spanned::new(span, SpannedForm::Atom(a))
2660}
2661
2662fn list_at(span: Span, items: Vec<Spanned>) -> Spanned {
2664 Spanned::new(span, SpannedForm::List(items))
2665}
2666
2667fn cover(items: &[Spanned], fallback: Span) -> Span {
2675 let merged = items
2676 .iter()
2677 .fold(Span::synthetic(), |acc, s| acc.merge(s.span));
2678 if merged.is_synthetic() {
2679 fallback
2680 } else {
2681 merged
2682 }
2683}
2684
2685#[cfg(test)]
2686mod tests {
2687 use super::*;
2688
2689 fn q(src: &str) -> String {
2693 parse_expr(src)
2694 .map(|s| s.to_string())
2695 .unwrap_or_else(|e| panic!("{src:?}: {e}"))
2696 }
2697
2698 #[test]
2701 fn arithmetic_respects_precedence() {
2702 assert_eq!(q("1 + 2 * 3"), "(+ 1 (* 2 3))");
2703 assert_eq!(q("(1 + 2) * 3"), "(* (+ 1 2) 3)");
2704 }
2705
2706 #[test]
2707 fn comparison_binds_looser_than_arithmetic() {
2708 assert_eq!(q("a + 1 < b"), "(< (+ a 1) b)");
2709 }
2710
2711 #[test]
2716 fn logical_operators_bind_loosest_and_lower_to_tataras_names() {
2717 assert_eq!(q("a && b || c"), "(or (and a b) c)");
2718 }
2719
2720 #[test]
2721 fn left_associativity() {
2722 assert_eq!(q("1 - 2 - 3"), "(- (- 1 2) 3)");
2723 }
2724
2725 #[test]
2726 fn a_dot_without_parentheses_reads_a_field() {
2727 assert_eq!(q("user.name"), "(blue-field user :name)");
2728 }
2729
2730 #[test]
2731 fn method_call_with_args() {
2732 assert_eq!(q("user.greet(1, 2)"), "(greet user 1 2)");
2733 }
2734
2735 #[test]
2736 fn chained_fields_read_left_to_right() {
2737 assert_eq!(q("a.b.c"), "(blue-field (blue-field a :b) :c)");
2738 assert_eq!(q("a.b(1).c"), "(blue-field (b a 1) :c)");
2739 }
2740
2741 #[test]
2742 fn plain_call() {
2743 assert_eq!(q("f(1, 2)"), "(f 1 2)");
2744 }
2745
2746 #[test]
2749 fn pipeline_threads_into_first_argument() {
2750 assert_eq!(q("x |> f"), "(f x)");
2751 assert_eq!(q("x |> f(1)"), "(f x 1)");
2752 assert_eq!(q("x |> f |> g"), "(g (f x))");
2753 }
2754
2755 #[test]
2756 fn pipeline_binds_looser_than_arithmetic() {
2757 assert_eq!(q("1 + 2 |> f"), "(f (+ 1 2))");
2758 }
2759
2760 #[test]
2766 fn label_and_rocket_produce_the_same_tree_for_a_symbol_key() {
2767 assert_eq!(q("{a: 1}"), q("{:a => 1}"));
2768 assert_eq!(q("{a: 1}"), "(hash-map :a 1)");
2769 }
2770
2771 #[test]
2774 fn a_string_key_has_no_shorthand() {
2775 assert_eq!(q(r#"{"k" => 1}"#), r#"(hash-map "k" 1)"#);
2776 }
2777
2778 #[test]
2779 fn list_literal() {
2780 assert_eq!(q("[1, 2, 3]"), "(list 1 2 3)");
2781 assert_eq!(q("[]"), "(list)");
2782 }
2783
2784 #[test]
2787 fn if_else_end() {
2788 assert_eq!(q("if a\n 1\nelse\n 2\nend"), "(if a 1 2)");
2789 }
2790
2791 #[test]
2792 fn if_without_else() {
2793 assert_eq!(q("if a\n 1\nend"), "(if a 1)");
2794 }
2795
2796 #[test]
2799 fn unless_is_a_negated_if() {
2800 assert_eq!(q("unless a\n 1\nend"), "(if (not a) 1)");
2801 }
2802
2803 #[test]
2804 fn multi_statement_body_becomes_begin() {
2805 assert_eq!(q("if a\n 1\n 2\nend"), "(if a (begin 1 2))");
2806 }
2807
2808 #[test]
2809 fn def_lowers_to_define() {
2810 assert_eq!(
2811 q("def add(a, b)\n a + b\nend"),
2812 "(define (add a b) (+ a b))"
2813 );
2814 }
2815
2816 #[test]
2817 fn def_with_no_params() {
2818 assert_eq!(q("def zero()\n 0\nend"), "(define (zero) 0)");
2819 }
2820
2821 #[test]
2824 fn literals_lower_to_atoms() {
2825 assert_eq!(q("42"), "42");
2826 assert_eq!(q("true"), "#t");
2827 assert_eq!(q(":ok"), ":ok");
2828 assert_eq!(q(r#""hi""#), r#""hi""#);
2829 }
2830
2831 #[test]
2832 fn unary_minus_and_not() {
2833 assert_eq!(q("-x"), "(- 0 x)");
2834 assert_eq!(q("!x"), "(not x)");
2835 }
2836
2837 #[test]
2840 fn a_program_is_a_sequence_of_forms() {
2841 let forms = parse_program("def f()\n 1\nend\nf()").expect("parse");
2842 assert_eq!(forms.len(), 2);
2843 assert_eq!(forms[1].to_string(), "(f)");
2844 }
2845
2846 #[test]
2847 fn unterminated_block_is_an_error_naming_what_was_expected() {
2848 let e = parse_program("if a\n 1").expect_err("must fail");
2849 assert!(e.message.contains("unterminated"), "{}", e.message);
2850 }
2851
2852 #[test]
2853 fn a_parse_error_carries_a_span_into_the_source() {
2854 let src = "1 + )";
2855 let e = parse_program(src).expect_err("must fail");
2856 assert!(e.span.start < src.len(), "span {:?} outside source", e.span);
2857 }
2858
2859 #[test]
2866 fn postfix_indexing_is_refused_naming_nth() {
2867 let e = parse_program("y = xs[0]").expect_err("must not split into two statements");
2868 assert!(e.message.contains("`nth(0, xs)`"), "{}", e.message);
2869 assert_eq!((e.line, e.col), (1, 7), "{e}");
2870 }
2871
2872 #[test]
2874 fn a_call_without_parentheses_is_refused_naming_the_call() {
2875 let e = parse_program("a = f x").expect_err("must not split into two statements");
2876 assert!(e.message.contains("`f(x)`"), "{}", e.message);
2877 assert_eq!((e.line, e.col), (1, 7), "{e}");
2878 }
2879
2880 #[test]
2882 fn two_statements_on_one_line_are_refused_in_a_body() {
2883 for src in [
2884 "def f(xs)\n xs[0]\nend",
2885 "if a\n f x\nend",
2886 "fn(x) x 1 end",
2887 "test \"t\"\n assert a b\nend",
2888 "1 2",
2889 ] {
2890 assert!(parse_program(src).is_err(), "{src:?} must not parse");
2891 }
2892 }
2893
2894 #[test]
2896 fn an_arrow_lambda_lowers_to_the_same_tree_as_fn() {
2897 for (arrow, block) in [
2898 ("(x) -> x * 2", "fn(x) x * 2 end"),
2899 ("(a, b) -> a + b", "fn(a, b) a + b end"),
2900 ("() -> 1", "fn() 1 end"),
2901 ("map(xs, (x) -> x + 1)", "map(xs, fn(x) x + 1 end)"),
2902 ] {
2903 let a = parse_program(arrow).expect(arrow);
2904 let b = parse_program(block).expect(block);
2905 assert_eq!(format!("{a:?}"), format!("{b:?}"), "{arrow}");
2906 }
2907 assert_eq!(
2908 parse_program("(x)").map(|p| p.len()).ok(),
2909 Some(1),
2910 "a parenthesised name is still a group"
2911 );
2912 }
2913
2914 #[test]
2918 fn a_statement_may_end_at_a_newline_the_end_or_a_closing_keyword() {
2919 for src in [
2920 "fn(x) x + 1 end",
2921 "map(fn(x) x end, xs)",
2922 "if a\n 1\nelse\n 2\nend",
2923 "if a 1 else 2 end",
2924 "case x\nwhen 1 :a\nwhen 2 :b\nelse :c\nend",
2925 "x = 1\ny = 2",
2926 "x = 1",
2927 ] {
2928 assert!(
2929 parse_program(src).is_ok(),
2930 "{src:?}: {:?}",
2931 parse_program(src)
2932 );
2933 }
2934 }
2935
2936 #[test]
2938 fn then_is_refused_by_name() {
2939 let e = parse_program("x = if c then 1 else 2 end").expect_err("then is not a keyword");
2940 assert!(e.message.contains("`then`"), "{}", e.message);
2941 }
2942
2943 #[test]
2945 fn a_brace_block_is_refused_naming_fn() {
2946 let e = parse_program("xs.map { |x| x }").expect_err("no brace blocks");
2947 assert!(e.message.contains("fn(x)"), "{}", e.message);
2948 }
2949
2950 #[test]
2957 fn a_qualified_name_is_one_symbol() {
2958 assert_eq!(q("retsu::first(xs)"), "(retsu/first xs)");
2959 assert_eq!(q("retsu::first"), "retsu/first");
2960 assert_eq!(q("xs |> retsu::first"), "(retsu/first xs)");
2961 assert_eq!(q("blue::length(xs)"), "(blue/length xs)");
2962 }
2963
2964 #[test]
2966 fn whitespace_around_the_qualifier_is_accepted() {
2967 assert_eq!(q("retsu :: first(xs)"), "(retsu/first xs)");
2968 }
2969
2970 #[test]
2973 fn a_reserved_word_after_the_qualifier_is_a_name() {
2974 assert_eq!(q("kueri::if(a, b)"), "(kueri/if a b)");
2975 assert_eq!(q("heni::test(x)"), "(heni/test x)");
2976 assert_eq!(q("sabi::fn"), "sabi/fn");
2977 }
2978
2979 #[test]
2980 fn a_qualified_name_has_exactly_one_qualifier() {
2981 for (src, says) in [
2982 ("::first(xs)", "`package::name`"),
2983 ("a::b::c", "one qualifier"),
2984 ("if::x", "reserved word"),
2985 ("retsu::1", "expected a name after `retsu::`"),
2986 ] {
2987 let e = parse_program(src).expect_err(src);
2988 assert!(e.message.contains(says), "{src}: {}", e.message);
2989 }
2990 }
2991
2992 #[test]
2995 fn qualified_reads_only_the_package_name_shape() {
2996 assert_eq!(qualified("retsu/first"), Some(("retsu", "first")));
2997 assert_eq!(qualified("kueri/if"), Some(("kueri", "if")));
2998 assert_eq!(qualified("nisshi/pair?"), Some(("nisshi", "pair?")));
2999 for not in ["/", "a/", "/a", "a/b/c", "if/x", "hash-map", "a"] {
3000 assert_eq!(qualified(not), None, "{not}");
3001 }
3002 assert!(is_callable_name("kueri/if"));
3003 }
3004
3005 #[test]
3010 fn a_symbol_may_end_in_a_question_or_bang() {
3011 assert_eq!(q("[:pair?, :set!]"), "(list :pair? :set!)");
3012 assert_eq!(q("use(\"n\", [:empty?])"), "(use \"n\" (list :empty?))");
3013 assert_eq!(q(":a != :b"), "(not= :a :b)");
3014 }
3015
3016 #[test]
3020 fn a_parse_error_displays_line_col_and_the_source_text() {
3021 let e = parse_program("x = 1\ny = [1, 2 y]").expect_err("must fail");
3022 assert_eq!(e.to_string(), "2:11: expected `,` or `]`, found `y`");
3023 assert_eq!(e.span, Span::new(16, 17), "the span stays, as data");
3024 }
3025
3026 #[test]
3028 fn no_error_message_names_a_rust_variant() {
3029 for src in [
3030 "f(1 2)",
3031 "{a: 1 b}",
3032 "def 1\nend",
3033 "x.1",
3034 "if a\n",
3035 "fn(1) 2 end",
3036 "[1 :b]",
3037 "1 +",
3038 "(1",
3039 ] {
3040 let e = parse_program(src).expect_err(src);
3041 let shown = e.to_string();
3042 for tell in [
3043 "Ident(", "Int(", "Op(", "Label(", "Sym(", "Str(", "LParen", "RBracket", "Eof",
3044 "Newline", "..",
3045 ] {
3046 assert!(!shown.contains(tell), "{src:?} -> {shown}");
3047 }
3048 assert!(e.line >= 1 && e.col >= 1, "{src:?} not located: {shown}");
3049 }
3050 }
3051
3052 #[test]
3054 fn a_lex_error_is_located_too() {
3055 let e = parse_program("x = 1\ny = 2; z = 3").expect_err("no semicolons");
3056 assert_eq!((e.line, e.col), (2, 6), "{e}");
3057 assert!(e.message.contains("new line"), "{e}");
3058 }
3059
3060 #[test]
3063 fn the_parser_rejects_garbage() {
3064 assert!(parse_program("def").is_err());
3065 assert!(parse_program("(1").is_err());
3066 assert!(parse_program("end").is_err());
3067 }
3068}