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 pub(crate) 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 pub(crate) 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 parse_program_documented_with_depth(src, max_depth).map(crate::doc::strip)
144}
145
146pub fn parse_program_documented(src: &str) -> Result<Vec<Spanned>, ParseError> {
149 parse_program_documented_with_depth(src, MAX_EXPR_DEPTH)
150}
151
152pub fn parse_program_documented_with_depth(
154 src: &str,
155 max_depth: usize,
156) -> Result<Vec<Spanned>, ParseError> {
157 if let Some(hit) = RECENT.with(|r| {
158 r.borrow()
159 .iter()
160 .find(|(d, s, _)| *d == max_depth && s.as_str() == src)
161 .map(|(_, _, t)| t.clone())
162 }) {
163 return hit;
164 }
165 PARSES.with(|n| n.set(n.get() + 1));
166 let parsed = lex(src)
167 .map_err(|e| ParseError::from(e).located(src))
168 .and_then(|toks| {
169 Parser::over(src, toks, max_depth)
170 .program()
171 .map_err(|e| e.located(src))
172 });
173 RECENT.with(|r| {
174 let mut r = r.borrow_mut();
175 if r.len() == RECENT_TEXTS {
176 drop(r.remove(0));
177 }
178 r.push((max_depth, src.to_string(), parsed.clone()));
179 });
180 parsed
181}
182
183pub(crate) fn parse_snippet(src: &str) -> Result<Vec<Spanned>, ParseError> {
186 let toks = lex(src).map_err(|e| ParseError::from(e).located(src))?;
187 Parser::over(src, toks, MAX_EXPR_DEPTH)
188 .program()
189 .map(crate::doc::strip)
190 .map_err(|e| e.located(src))
191}
192
193const RECENT_TEXTS: usize = 4;
197
198type Recent = Vec<(usize, String, Result<Vec<Spanned>, ParseError>)>;
199
200thread_local! {
201 static RECENT: std::cell::RefCell<Recent> = const { std::cell::RefCell::new(Vec::new()) };
202}
203
204pub fn spelling_notes(src: &str) -> Result<Vec<Note>, ParseError> {
205 let mut p = Parser::over(
206 src,
207 lex(src).map_err(|e| ParseError::from(e).located(src))?,
208 MAX_EXPR_DEPTH,
209 );
210 p.program().map_err(|e| e.located(src))?;
211 Ok(p.notes)
212}
213
214pub fn parse_program_in(src: &str, pack: &crate::yakugo::Yakugo) -> Result<Vec<Sexp>, ParseError> {
227 Ok(parse_program_tree_in(src, pack)?
228 .iter()
229 .map(Spanned::to_sexp)
230 .collect())
231}
232
233pub fn parse_program_tree_in(
249 src: &str,
250 pack: &crate::yakugo::Yakugo,
251) -> Result<Vec<Spanned>, ParseError> {
252 let toks =
253 crate::yakugo::canonical_tokens(src, pack).map_err(|e| ParseError::from(e).located(src))?;
254 PARSES.with(|n| n.set(n.get() + 1));
255 let mut p = Parser::over(src, toks, MAX_EXPR_DEPTH);
256 p.program()
257 .map(crate::doc::strip)
258 .map_err(|e| e.located(src))
259}
260
261pub fn parse_program_with_depth(src: &str, max_depth: usize) -> Result<Vec<Sexp>, ParseError> {
270 Ok(parse_program_tree_with_depth(src, max_depth)?
271 .iter()
272 .map(Spanned::to_sexp)
273 .collect())
274}
275
276pub fn parse_program_spanned(src: &str) -> Result<Vec<(Sexp, Span)>, ParseError> {
289 parse_program_spanned_with_depth(src, MAX_EXPR_DEPTH)
290}
291
292pub fn parse_program_spanned_with_depth(
294 src: &str,
295 max_depth: usize,
296) -> Result<Vec<(Sexp, Span)>, ParseError> {
297 Ok(parse_program_tree_with_depth(src, max_depth)?
298 .iter()
299 .map(|f| (f.to_sexp(), f.span))
300 .collect())
301}
302
303pub fn comments(src: &str) -> Vec<Comment> {
311 lex(src)
312 .map(|toks| {
313 toks.iter()
314 .filter_map(|t| match &t.kind {
315 TokenKind::Comment(text) => Some(Comment {
316 text: text.clone(),
317 span: t.span,
318 own_line: line_before_is_blank(src, t.span.start),
319 }),
320 _ => None,
321 })
322 .collect()
323 })
324 .unwrap_or_default()
325}
326
327#[derive(Clone, Debug, PartialEq, Eq)]
329pub struct Comment {
330 pub text: String,
332 pub span: Span,
333 pub own_line: bool,
335}
336
337fn line_before_is_blank(src: &str, start: usize) -> bool {
338 src[..start.min(src.len())]
339 .rsplit('\n')
340 .next()
341 .is_some_and(|prefix| {
342 prefix
343 .trim_matches(|c: char| c.is_whitespace() || c == '\u{feff}')
344 .is_empty()
345 })
346}
347
348pub fn parse_expr(src: &str) -> Result<Sexp, ParseError> {
350 let forms = parse_program(src)?;
351 match forms.len() {
352 1 => Ok(forms.into_iter().next().expect("checked len")),
353 n => Err(ParseError::new(
354 format!("expected exactly one expression, found {n}"),
355 Span::new(0, src.len()),
356 )
357 .located(src)),
358 }
359}
360
361#[derive(Clone, Copy, Debug)]
371pub struct Infix {
372 pub op: &'static str,
374 pub power: (u8, u8),
376 pub callee: &'static str,
378 pub doc: &'static str,
382}
383
384pub const INFIX: &[Infix] = &[
388 Infix {
389 op: "or",
390 power: (1, 2),
391 callee: "or",
392 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).",
393 },
394 Infix {
395 op: "and",
396 power: (3, 4),
397 callee: "and",
398 doc: "Logical and: the right value if the left is truthy, otherwise the left; short-circuits. `&&` is read as `and` and written `and` (B0025).",
399 },
400 Infix {
411 op: "==",
412 power: (5, 6),
413 callee: "equal?",
414 doc: "Structural equality over every value (okite D0001): 4 == 4.0, lists and maps by content, nil only equals nil.",
415 },
416 Infix {
417 op: "!=",
418 power: (5, 6),
419 callee: "not=",
420 doc: "Exactly not(a == b) (okite D0002).",
421 },
422 Infix {
423 op: "<",
424 power: (5, 6),
425 callee: "<",
426 doc: "Numeric less-than. A string operand is a type error: order text with compare(a, b).",
427 },
428 Infix {
429 op: "<=",
430 power: (5, 6),
431 callee: "<=",
432 doc: "Numeric less-than-or-equal. Numbers only, like <.",
433 },
434 Infix {
435 op: ">",
436 power: (5, 6),
437 callee: ">",
438 doc: "Numeric greater-than. Numbers only, like <.",
439 },
440 Infix {
441 op: ">=",
442 power: (5, 6),
443 callee: ">=",
444 doc: "Numeric greater-than-or-equal. Numbers only, like <.",
445 },
446 Infix {
447 op: "+",
448 power: (7, 8),
449 callee: "+",
450 doc: "Numeric addition; an Int and a Float give a Float. Not string concatenation: interpolate instead.",
451 },
452 Infix {
453 op: "-",
454 power: (7, 8),
455 callee: "-",
456 doc: "Numeric subtraction. Written before an operand with nothing on its left, it is unary minus: -n.",
457 },
458 Infix {
459 op: "*",
460 power: (9, 10),
461 callee: "*",
462 doc: "Numeric multiplication.",
463 },
464 Infix {
465 op: "/",
466 power: (9, 10),
467 callee: "/",
468 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).",
469 },
470 Infix {
471 op: "%",
472 power: (9, 10),
473 callee: "mod",
474 doc: "Euclidean remainder: never negative, so (0 - 7) % 3 is 2, not -1. A zero divisor is a runtime error.",
475 },
476];
477
478pub const MASTER_VERBS: &[&str] = &[
496 "read", "need", "has?", "put", "update", "delete", "create", "diff", "info", "exists?",
497];
498
499pub const LOWERED_ASSERT: &str = "blue-assert";
506
507pub const LOWERED_MAP: &str = "hash-map";
515
516pub const LOWERED_CONCAT: &str = "concat";
521
522pub const OPTIONAL_MARKER: &str = "&optional";
523
524pub const LOWERED_OPTIONAL_ARITY: &str = "blue-optional-arity";
525
526pub const LOWERED_OPTIONAL_ARG: &str = "blue-optional-arg";
527
528pub const LOWERED_FIELD: &str = "blue-field";
529
530pub const LOWERED_MERGE: &str = "blue-merge";
531
532pub const LOWERED_SPLICE: &str = "blue-splice";
533
534pub const LOWERED_KWARGS: &str = "blue-kwargs";
535
536pub const LOWERED_ERROR_MATCH: &str = "blue-rescues?";
537
538pub const LOWERED_ENSURE: &str = "blue-ensure";
539
540pub const LOWERED_RERAISE: &str = "blue-reraise";
541
542pub const LOWERED_ATTEMPTED: &str = "blue-attempted";
543
544pub const LOWERED_FAILED: &str = "blue-failed";
545
546pub const LOWERED_HEADS: &[&str] = &[
547 "list", "concat", "not", "apply", "equal?", "not=", "and", "or", "mod",
548];
549
550pub const RESCUED: &str = "rescued-error";
551
552pub const SURFACE_KEYWORDS: &[&str] = &[
553 "if",
554 "begin",
555 "unless",
556 "def",
557 "defmacro",
558 "quote",
559 "unquote",
560 "unquote_splice",
561 "test",
562 "assert",
563 "fn",
564 "case",
565];
566
567pub const BLOCK_KEYWORDS: &[&str] = &[
579 "do", "end", "else", "elsif", "rescue", "ensure", "true", "false", "nil",
580];
581
582pub const OPERATOR_KEYWORDS: &[&str] = &["and", "or", "not"];
583
584pub fn is_reserved_word(name: &str) -> bool {
591 SURFACE_KEYWORDS.contains(&name)
592 || BLOCK_KEYWORDS.contains(&name)
593 || OPERATOR_KEYWORDS.contains(&name)
594}
595
596pub fn is_callable_name(name: &str) -> bool {
605 if qualified(name).is_some() {
607 return true;
608 }
609 !is_reserved_word(name)
610 && matches!(
611 lex(name).as_deref(),
612 Ok([t, e]) if matches!(&t.kind, TokenKind::Ident(n) if n == name)
613 && e.kind == TokenKind::Eof
614 )
615}
616
617pub const QUALIFIER: char = '/';
622
623pub const BUILTIN_QUALIFIER: &str = "blue";
626
627#[must_use]
629pub fn qualify(pkg: &str, name: &str) -> String {
630 format!("{pkg}{QUALIFIER}{name}")
631}
632
633#[must_use]
639pub fn qualified(sym: &str) -> Option<(&str, &str)> {
640 let (pkg, name) = sym.split_once(QUALIFIER)?;
641 let ident = |s: &str| {
642 matches!(
643 lex(s).as_deref(),
644 Ok([t, e]) if matches!(&t.kind, TokenKind::Ident(n) if n == s) && e.kind == TokenKind::Eof
645 )
646 };
647 let named = |s: &str| ident(s) || matches!(s, "true" | "false" | "nil");
648 (ident(pkg) && !is_reserved_word(pkg) && named(name)).then_some((pkg, name))
649}
650
651#[must_use]
659pub fn keyword_doc(word: &str) -> &'static str {
660 match word {
661 "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).",
662 "unless" => "`unless c … end` is read as `if not c` and written that way (B0026).",
663 "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`.",
664 "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 `|>`.",
665 "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.",
666 "and" => "`a and b`: logical and; `&&` is read as `and` and written `and` (B0025).",
667 "or" => "`a or b`: logical or; `||` is read as `or` and written `or` (B0025).",
668 "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).",
669 "def" => "`def name(a, b) … end`: a named function; its value is the last expression. Parameters may be typed: `def f(n: Int) -> Int`.",
670 "defmacro" => "`defmacro name(x) quote … unquote(x) … end end`: a macro, a rewrite of the unevaluated form.",
671 "quote" => "`quote … end`: the form itself as data, not its value. Used inside `defmacro`.",
672 "unquote" => "`unquote(x)`: inside `quote`, splice in the value of `x`.",
673 "unquote_splice" => "`unquote_splice(xs)`: inside `quote`, splice the items of the form `xs` in place, not the form itself.",
674 "test" => "`test \"what must hold\" … end`: a test block, run by `blue test` and ignored by `blue run`.",
675 "assert" => "`assert expr`: inside a test, fail it unless `expr` is truthy; the failure quotes the expression.",
676 "fn" => "`fn(x) x * 2 end`: an anonymous function, a value. Blue has no brace or do blocks.",
677 "case" => "`case v when 1 … when 2 … else … end`: compares with `==`; it does not destructure.",
678 "do" => "Reserved and refused: blue has no do-blocks. Pass `fn(x) … end` instead.",
679 "end" => "Closes `def`, `defmacro`, `if`, `case`, `fn`, `test` and `quote`.",
680 "else" => "The branch of `if` or `case` taken when nothing before it matched.",
681 "elsif" => "A further condition in an `if` chain; an `else` holding only an `if` is written `elsif` (B0027).",
682 "true" => "The boolean true.",
683 "false" => "The boolean false. Only `false` and `nil` are falsy; 0, \"\" and [] are truthy.",
684 "nil" => "The absent value. It is not `[]` (okite D0004), and only `nil == nil`.",
685 _ => "",
686 }
687}
688
689fn infix(op: &str) -> Option<&'static Infix> {
690 INFIX.iter().find(|i| i.op == op)
691}
692
693pub const SYMBOL_SPELLINGS: &[(&str, &str)] = &[("&&", "and"), ("||", "or"), ("!", "not")];
694
695fn word_for(op: &str) -> &str {
696 SYMBOL_SPELLINGS
697 .iter()
698 .find(|(sym, _)| *sym == op)
699 .map_or(op, |(_, word)| word)
700}
701
702fn infix_of(kind: &TokenKind) -> Option<&'static Infix> {
703 match kind {
704 TokenKind::Op(o) => infix(word_for(o)),
705 TokenKind::Ident(w) if w == "and" || w == "or" => infix(w),
706 _ => None,
707 }
708}
709
710const PIPE_POWER: (u8, u8) = (0, 1);
711const RESCUE_POWER: (u8, u8) = (0, 1);
712
713struct Parser {
714 toks: Vec<Token>,
715 pos: usize,
716 depth: usize,
724 max_depth: usize,
731 src: String,
732 comment_spans: Vec<Span>,
733 notes: Vec<Note>,
734}
735
736pub const MAX_EXPR_DEPTH: usize = 256;
743
744type Fail = Box<ParseError>;
756
757fn fail(message: impl Into<String>, span: Span) -> Fail {
758 Box::new(ParseError::new(message, span))
759}
760
761impl Parser {
762 fn over(src: &str, all: Vec<Token>, max_depth: usize) -> Self {
763 let comment_spans = all
764 .iter()
765 .filter(|t| matches!(t.kind, TokenKind::Comment(_)))
766 .map(|t| t.span)
767 .collect();
768 let toks = all
769 .into_iter()
770 .filter(|t| !matches!(t.kind, TokenKind::Comment(_)))
771 .collect();
772 Parser {
773 toks,
774 pos: 0,
775 depth: 0,
776 max_depth,
777 src: src.to_string(),
778 comment_spans,
779 notes: Vec::new(),
780 }
781 }
782
783 fn note(&mut self, spelling: Spelling, span: Span, written: &str, canonical: &str) {
784 self.notes.push(Note {
785 spelling,
786 span,
787 written: written.to_string(),
788 canonical: canonical.to_string(),
789 });
790 }
791
792 fn text(&self, span: Span) -> &str {
793 self.src.get(span.start..span.end).unwrap_or("")
794 }
795
796 fn comment_within(&self, lo: usize, hi: usize) -> bool {
797 self.comment_spans
798 .iter()
799 .any(|c| c.start >= lo && c.start < hi)
800 }
801
802 fn touches_previous(&self) -> bool {
803 let here = self.peek_span();
804 self.toks
805 .get(self.pos.wrapping_sub(1))
806 .is_some_and(|prev| prev.span.end == here.start)
807 }
808
809 fn peek(&self) -> &TokenKind {
810 &self.toks[self.pos.min(self.toks.len() - 1)].kind
811 }
812
813 fn peek_span(&self) -> Span {
814 self.toks[self.pos.min(self.toks.len() - 1)].span
815 }
816
817 fn bump(&mut self) -> TokenKind {
818 let k = self.toks[self.pos.min(self.toks.len() - 1)].kind.clone();
819 if self.pos < self.toks.len() {
820 self.pos += 1;
821 }
822 k
823 }
824
825 fn at(&self, k: &TokenKind) -> bool {
826 self.peek() == k
827 }
828
829 fn eat(&mut self, k: &TokenKind) -> bool {
830 if self.at(k) {
831 self.bump();
832 true
833 } else {
834 false
835 }
836 }
837
838 fn expect(&mut self, k: &TokenKind, what: &str) -> Result<(), Fail> {
839 if self.eat(k) {
840 Ok(())
841 } else {
842 Err(self.error(format!("expected {what}, found {}", self.peek())))
843 }
844 }
845
846 fn error(&self, message: impl Into<String>) -> Fail {
847 fail(message, self.peek_span())
848 }
849
850 fn skip_newlines(&mut self) {
852 while matches!(self.peek(), TokenKind::Newline) {
853 self.bump();
854 }
855 }
856
857 fn at_ident(&self, name: &str) -> bool {
858 matches!(self.peek(), TokenKind::Ident(n) if n == name)
859 }
860
861 fn mark(&self) -> usize {
864 self.peek_span().start
865 }
866
867 fn span_since(&self, start: usize) -> Span {
873 let end = self
874 .toks
875 .get(self.pos.saturating_sub(1))
876 .map_or(start, |t| t.span.end);
877 Span::new(start, end.max(start))
878 }
879
880 fn program(&mut self) -> Result<Vec<Spanned>, Fail> {
881 let mut out = Vec::new();
882 loop {
883 self.skip_newlines();
884 if matches!(self.peek(), TokenKind::Eof) {
885 break;
886 }
887 if let TokenKind::Doc { module, text } = self.peek().clone() {
888 out.push(self.documentation(module, text, out.is_empty())?);
889 continue;
890 }
891 out.push(self.statement()?);
892 self.end_of_statement(&[])?;
893 }
894 Ok(out)
895 }
896
897 fn documentation(&mut self, module: bool, text: String, first: bool) -> Result<Spanned, Fail> {
900 let span = self.peek_span();
901 self.bump();
902 let word = if module { "@moduledoc" } else { "@doc" };
903 let head = sym_at(
904 Span::new(span.start, span.start + word.len()),
905 if module {
906 crate::doc::MODULEDOC_HEAD
907 } else {
908 crate::doc::DOC_HEAD
909 },
910 );
911 let text = crate::doc::text_node(span, text);
912 self.end_of_statement(&[])?;
913 if module {
914 if !first {
915 return Err(fail(
916 "`@moduledoc` is the first form of a file: it documents the whole file",
917 span,
918 ));
919 }
920 return Ok(list_at(span, vec![head, text]));
921 }
922 self.skip_newlines();
923 if matches!(self.peek(), TokenKind::Doc { .. } | TokenKind::Eof) {
924 return Err(fail(
925 "`@doc` documents the definition below it, and none follows",
926 span,
927 ));
928 }
929 let def = self.statement()?;
930 self.end_of_statement(&[])?;
931 if !crate::doc::is_definition(&def) {
932 return Err(fail(
933 "`@doc` documents the definition below it: a `def`, a `defmacro` or a \
934 top-level `name = value`",
935 span,
936 ));
937 }
938 Ok(list_at(
939 Span::new(span.start, def.span.end),
940 vec![head, text, def],
941 ))
942 }
943
944 fn end_of_statement(&self, terminators: &[&str]) -> Result<(), Fail> {
962 let next = self.peek();
963 if matches!(next, TokenKind::Newline | TokenKind::Eof)
964 || terminators.iter().any(|t| self.at_ident(t))
965 {
966 return Ok(());
967 }
968 let prev = self.toks.get(self.pos.wrapping_sub(1)).map(|t| &t.kind);
969 let prev_name = match prev {
970 Some(TokenKind::Ident(n)) => Some(n.as_str()),
971 _ => None,
972 };
973 let callee = prev_name.unwrap_or("f");
974 let why = match (prev, next) {
975 (Some(TokenKind::Ident(t)), _) if t == "then" => {
976 "blue's `if` has no `then`: put the branch on the next line".to_string()
977 }
978 (_, TokenKind::Ident(d)) if d == "do" => "blue has no `do` blocks: pass a function \
979 as an argument, `f(xs, fn(x) … end)`"
980 .to_string(),
981 (_, TokenKind::LBracket) => {
982 let index = match (self.toks.get(self.pos + 1), self.toks.get(self.pos + 2)) {
983 (Some(i), Some(close)) if close.kind == TokenKind::RBracket => match &i.kind {
984 TokenKind::Int(n) => n.to_string(),
985 TokenKind::Ident(n) => n.clone(),
986 _ => "i".to_string(),
987 },
988 _ => "i".to_string(),
989 };
990 let list = prev_name.unwrap_or("xs");
991 format!(
992 "blue has no `xs[i]` indexing: write `nth({index}, {list})` \
993 (or `first({list})`, `last({list})`)"
994 )
995 }
996 (_, TokenKind::LBrace) => "blue has no brace blocks: pass a function as an \
997 argument, `map(xs, fn(x) … end)`"
998 .to_string(),
999 (
1000 Some(TokenKind::Ident(_)),
1001 TokenKind::Ident(_)
1002 | TokenKind::Int(_)
1003 | TokenKind::Float(_)
1004 | TokenKind::Str(_)
1005 | TokenKind::InterpolatedStr { .. }
1006 | TokenKind::Sym(_)
1007 | TokenKind::True
1008 | TokenKind::False
1009 | TokenKind::Nil,
1010 ) => {
1011 let arg = match next {
1012 TokenKind::Ident(a) => a.clone(),
1013 _ => "x".to_string(),
1014 };
1015 format!("a call needs parentheses: write `{callee}({arg})`")
1016 }
1017 _ => "put each statement on its own line".to_string(),
1018 };
1019 Err(self.error(format!("expected the end of the line, found {next}: {why}")))
1020 }
1021
1022 fn statement(&mut self) -> Result<Spanned, Fail> {
1035 let max = self.max_depth;
1049 if self.depth >= max {
1050 return Err(self.error(format!(
1051 "statement nests deeper than {max}; refusing to \
1052 recurse further (this is a limit, not a syntax error)"
1053 )));
1054 }
1055 self.depth += 1;
1056 let r = self.statement_inner();
1057 self.depth -= 1;
1058 r
1059 }
1060
1061 fn statement_inner(&mut self) -> Result<Spanned, Fail> {
1062 if let TokenKind::Ident(name) = self.peek().clone() {
1063 if self.peek_at(1) == "=" && !is_reserved_word(&name) {
1064 let name_span = self.peek_span();
1065 self.bump(); self.bump(); self.skip_newlines();
1068 let value = self.expr(0)?;
1069 return Ok(list_at(
1073 self.span_since(name_span.start),
1074 vec![sym_at(name_span, "define"), sym_at(name_span, &name), value],
1075 ));
1076 }
1077 }
1078 let e = self.expr(0)?;
1079 if let SpannedForm::List(items) = &e.form {
1080 if items.len() == 3
1081 && items[0].as_symbol() == Some("define")
1082 && items[1].as_symbol().is_some()
1083 && self.text(items[0].span) == "define"
1084 {
1085 self.note(Spelling::OperatorSpelling, e.span, "define(x, v)", "x = v");
1086 }
1087 }
1088 Ok(e)
1089 }
1090
1091 fn peek_at(&self, n: usize) -> String {
1094 match self.toks.get(self.pos + n).map(|t| &t.kind) {
1095 Some(TokenKind::Op(o)) => o.clone(),
1096 _ => String::new(),
1097 }
1098 }
1099
1100 fn expr(&mut self, min_bp: u8) -> Result<Spanned, Fail> {
1102 let max = self.max_depth;
1112 if self.depth >= max {
1113 return Err(self.error(format!(
1114 "expression nests deeper than {max}; refusing to \
1115 recurse further (this is a limit, not a syntax error)"
1116 )));
1117 }
1118 self.depth += 1;
1119 let r = self.expr_inner(min_bp);
1120 self.depth -= 1;
1121 r
1122 }
1123
1124 #[inline(never)]
1125 fn rescue_node(&self, start: usize, op_span: Span, lhs: Spanned, fallback: Spanned) -> Spanned {
1126 list_at(
1127 self.span_since(start),
1128 vec![
1129 sym_at(op_span, "try"),
1130 lhs,
1131 list_at(
1132 op_span.merge(fallback.span),
1133 vec![
1134 sym_at(op_span, "catch"),
1135 list_at(op_span, vec![sym_at(op_span, "_")]),
1136 fallback,
1137 ],
1138 ),
1139 ],
1140 )
1141 }
1142
1143 #[inline(never)]
1144 fn infix_spelling(&mut self, callee: Option<&str>, op_span: Span) {
1145 let written = self.text(op_span).to_string();
1146 match (callee, self.peek().clone()) {
1147 (None, _) => self.note(Spelling::CallForm, op_span, "|>", "a call"),
1148 (Some(c), TokenKind::Op(o)) if word_for(&o) != o => {
1149 self.note(Spelling::LogicalSymbol, op_span, &written, c);
1150 }
1151 (Some(_), TokenKind::Op(o)) if written != o => {
1152 self.note(Spelling::OperatorSpelling, op_span, &written, &o);
1153 }
1154 _ => {}
1155 }
1156 }
1157
1158 #[inline(never)]
1159 fn nil_test(
1160 &mut self,
1161 start: usize,
1162 op_span: Span,
1163 lhs: &Spanned,
1164 rhs: &Spanned,
1165 ) -> Option<Spanned> {
1166 let (subject, written) = match (&lhs.form, &rhs.form) {
1167 (_, SpannedForm::Nil) if !matches!(lhs.form, SpannedForm::Nil) => (lhs, "x == nil"),
1168 (SpannedForm::Nil, _) if !matches!(rhs.form, SpannedForm::Nil) => (rhs, "nil == x"),
1169 _ => return None,
1170 };
1171 let span = self.span_since(start);
1172 self.note(Spelling::NilTest, span, written, "x.nil?");
1173 Some(list_at(
1174 span,
1175 vec![
1176 sym_at(op_span, LOWERED_FIELD),
1177 subject.clone(),
1178 atom_at(op_span, Atom::Keyword("nil?".to_string())),
1179 ],
1180 ))
1181 }
1182
1183 #[inline(never)]
1184 fn infix_node(
1185 &mut self,
1186 start: usize,
1187 callee: Option<&str>,
1188 op_span: Span,
1189 lhs: Spanned,
1190 rhs: Spanned,
1191 ) -> Spanned {
1192 if callee == Some("-") && matches!(lhs.form, SpannedForm::Atom(Atom::Int(0))) {
1193 self.note(
1194 Spelling::OperatorSpelling,
1195 lhs.span.merge(op_span),
1196 "0 - x",
1197 "-x",
1198 );
1199 }
1200
1201 if callee == Some("equal?") {
1202 if let Some(test) = self.nil_test(start, op_span, &lhs, &rhs) {
1203 return test;
1204 }
1205 }
1206
1207 if callee.is_none() {
1208 let rhs_span = rhs.span;
1213 let threaded = match rhs.form {
1214 SpannedForm::List(mut items) if !items.is_empty() => {
1215 items.insert(1, lhs);
1216 list_at(self.span_since(start), items)
1217 }
1218 callee => list_at(
1219 self.span_since(start),
1220 vec![Spanned::new(rhs_span, callee), lhs],
1221 ),
1222 };
1223 match &threaded.form {
1224 SpannedForm::List(items)
1225 if items.len() == 2 && items[0].as_symbol() == Some("not") =>
1226 {
1227 let (head, operand) = (items[0].span, items[1].clone());
1228 self.negation(head, threaded.span, operand)
1229 }
1230 SpannedForm::List(items)
1231 if items.len() == 4
1232 && items[0].as_symbol() == Some("if")
1233 && matches!(items[3].form, SpannedForm::Nil) =>
1234 {
1235 list_at(threaded.span, items[..3].to_vec())
1236 }
1237 SpannedForm::List(items)
1238 if items.len() == 3 && items[0].as_symbol() == Some("equal?") =>
1239 {
1240 let (op, a, b) = (items[0].span, items[1].clone(), items[2].clone());
1241 match self.nil_test(start, op, &a, &b) {
1242 Some(test) => test,
1243 None => threaded,
1244 }
1245 }
1246 _ => threaded,
1247 }
1248 } else {
1249 list_at(
1255 self.span_since(start),
1256 vec![sym_at(op_span, callee.unwrap_or_default()), lhs, rhs],
1257 )
1258 }
1259 }
1260
1261 fn expr_inner(&mut self, min_bp: u8) -> Result<Spanned, Fail> {
1262 let start = self.mark();
1263 let mut lhs = self.prefix()?;
1264
1265 loop {
1266 match self.peek() {
1268 TokenKind::Dot => {
1269 let dot = self.peek_span();
1270 self.bump();
1271 lhs = self.finish_send(start, lhs, dot)?;
1272 continue;
1273 }
1274 TokenKind::LParen => {
1275 let (args, splat) = self.call_args()?;
1276 let call = self.call_node(self.span_since(start), vec![lhs], args, splat);
1277 lhs = self.call_spelling(call);
1278 continue;
1279 }
1280 _ => {}
1281 }
1282
1283 let op_span = self.peek_span();
1287 if self.at_ident("rescue") {
1288 if RESCUE_POWER.0 < min_bp {
1289 break;
1290 }
1291 self.bump();
1292 self.skip_newlines();
1293 let fallback = self.expr(RESCUE_POWER.1)?;
1294 lhs = self.rescue_node(start, op_span, lhs, fallback);
1295 continue;
1296 }
1297 let (callee, (lbp, rbp)) = match self.peek() {
1298 TokenKind::Pipe => (None, PIPE_POWER),
1299 other => match infix_of(other) {
1300 Some(i) => (Some(i.callee), i.power),
1301 None => break,
1302 },
1303 };
1304 if lbp < min_bp {
1305 break;
1306 }
1307 self.infix_spelling(callee, op_span);
1308 self.bump();
1309 self.skip_newlines();
1310 let rhs = self.expr(rbp)?;
1311 lhs = self.infix_node(start, callee, op_span, lhs, rhs);
1312 }
1313
1314 Ok(lhs)
1315 }
1316
1317 #[inline(never)]
1318 fn interpolation(span: Span, parts: &[String], exprs: &[String]) -> Result<Spanned, Fail> {
1319 let mut acc = atom_at(span, Atom::Str(parts[0].clone()));
1329 for (i, raw) in exprs.iter().enumerate() {
1330 let inner = parse_expr(raw).map_err(|e| {
1331 fail(
1332 format!("in interpolation `#{{{raw}}}`: {}", e.message),
1333 span,
1334 )
1335 })?;
1336 let inner = Spanned::from_sexp_at(&inner, span);
1337 acc = list_at(span, vec![sym_at(span, LOWERED_CONCAT), acc, inner]);
1338 acc = list_at(
1341 span,
1342 vec![
1343 sym_at(span, LOWERED_CONCAT),
1344 acc,
1345 atom_at(span, Atom::Str(parts[i + 1].clone())),
1346 ],
1347 );
1348 }
1349 Ok(acc)
1350 }
1351
1352 #[inline(never)]
1353 fn not_prefix(&mut self, span: Span) -> Result<Spanned, Fail> {
1354 if self.at(&TokenKind::LParen) && self.touches_previous() {
1355 self.note(Spelling::OperatorSpelling, span, "not(x)", "not x");
1356 let (args, splat) = self.call_args()?;
1357 if args.len() != 1 || splat.is_some() {
1358 let call = self.call_node(
1359 self.span_since(span.start),
1360 vec![sym_at(span, "not")],
1361 args,
1362 splat,
1363 );
1364 return Ok(call);
1365 }
1366 let operand = args.into_iter().next().expect("one argument");
1367 return Ok(self.negation(span, self.span_since(span.start), operand));
1368 }
1369 let rhs = self.expr(11)?;
1370 Ok(self.negation(span, self.span_since(span.start), rhs))
1371 }
1372
1373 fn prefix(&mut self) -> Result<Spanned, Fail> {
1374 if self.at(&TokenKind::LParen) {
1375 let span = self.peek_span();
1376 self.bump();
1377 if self.arrow_lambda_ahead() {
1378 return self.arrow_lambda(span);
1379 }
1380 return self.group();
1381 }
1382 self.prefix_token()
1383 }
1384
1385 fn group(&mut self) -> Result<Spanned, Fail> {
1386 self.skip_newlines();
1387 let inner = self.expr(0)?;
1388 self.skip_newlines();
1389 self.expect(&TokenKind::RParen, "`)`")?;
1390 Ok(inner)
1391 }
1392
1393 #[inline(never)]
1394 fn prefix_token(&mut self) -> Result<Spanned, Fail> {
1395 let span = self.peek_span();
1396 match self.bump() {
1397 TokenKind::Int(v) => Ok(atom_at(span, Atom::Int(v))),
1398 TokenKind::Float(v) => Ok(atom_at(span, Atom::Float(v))),
1399 TokenKind::Str(s) => Ok(atom_at(span, Atom::Str(s))),
1400 TokenKind::Doc { module, .. } => Err(fail(
1401 format!(
1402 "`{}` sits at the top level, above the definition it documents, never \
1403 inside a form",
1404 if module { "@moduledoc" } else { "@doc" }
1405 ),
1406 span,
1407 )),
1408
1409 TokenKind::InterpolatedStr { parts, exprs } => Self::interpolation(span, &parts, &exprs),
1420 TokenKind::Sym(s) => Ok(atom_at(span, Atom::Keyword(s))),
1421 TokenKind::True => Ok(atom_at(span, Atom::Bool(true))),
1422 TokenKind::False => Ok(atom_at(span, Atom::Bool(false))),
1423 TokenKind::Nil => Ok(Spanned::new(span, SpannedForm::Nil)),
1424
1425 TokenKind::Op(o) if o == "-" => {
1426 let rhs = self.expr(11)?; Ok(list_at(
1428 self.span_since(span.start),
1429 vec![sym_at(span, "-"), atom_at(span, Atom::Int(0)), rhs],
1430 ))
1431 }
1432 TokenKind::Op(o) if o == "!" => {
1433 let written = self.text(span).to_string();
1434 self.note(Spelling::LogicalSymbol, span, &written, "not");
1435 let rhs = self.expr(11)?;
1436 Ok(self.negation(span, self.span_since(span.start), rhs))
1437 }
1438 TokenKind::Ident(w) if w == "not" => self.not_prefix(span),
1439 TokenKind::Ident(w) if (w == "and" || w == "or") && self.at(&TokenKind::LParen) => {
1440 Ok(sym_at(span, &w))
1441 }
1442 TokenKind::Ident(w) if w == "and" || w == "or" => Err(fail(
1443 format!("`{w}` joins two expressions: `a {w} b`"),
1444 span,
1445 )),
1446
1447 TokenKind::LParen => {
1448 self.skip_newlines();
1449 let inner = self.expr(0)?;
1450 self.skip_newlines();
1451 self.expect(&TokenKind::RParen, "`)`")?;
1452 Ok(inner)
1456 }
1457
1458 TokenKind::LBracket => self.list_literal(span),
1459 TokenKind::LBrace => self.map_literal(span),
1460
1461 TokenKind::Ident(name) if self.at(&TokenKind::PathSep) => {
1462 self.qualified_name(span, &name)
1463 }
1464 TokenKind::PathSep => Err(fail(
1465 "a qualified name is `package::name`, with the package written before `::`",
1466 span,
1467 )),
1468 TokenKind::Ident(name) => match name.as_str() {
1469 "if" => self.if_form(span, false),
1470 "begin" if !(self.at(&TokenKind::LParen) && self.touches_previous()) => {
1471 self.begin_form(span)
1472 }
1473 "rescue" | "ensure" => Err(fail(
1474 format!("`{name}` belongs to a `begin … end` block or a `def` body, after the statements it protects"),
1475 span,
1476 )),
1477 "unless" => {
1478 self.note(Spelling::Unless, span, "unless", "if not");
1479 self.if_form(span, true)
1480 }
1481 "def" => self.def_form(span),
1482 "defmacro" => self.defmacro_form(span),
1483 "case" => self.case_form(span),
1484 "fn" => self.lambda_form(span),
1485 "test" => self.test_form(span),
1486 "assert" => self.assert_form(span),
1487 "quote" => self.quote_form(span),
1488 "unquote" => self.unquote_form(span, false),
1489 "unquote_splice" => self.unquote_form(span, true),
1490 "do" => Err(fail(
1491 "blue has no `do` blocks: pass a function as an argument, \
1492 `f(xs, fn(x) … end)`",
1493 span,
1494 )),
1495 "end" => Err(fail(
1496 "unexpected `end`: nothing open here for it to close",
1497 span,
1498 )),
1499 _ => Ok(sym_at(span, &name)),
1500 },
1501
1502 other => Err(fail(format!("expected an expression, found {other}"), span)),
1503 }
1504 }
1505
1506 fn qualified_name(&mut self, pkg_span: Span, pkg: &str) -> Result<Spanned, Fail> {
1516 if is_reserved_word(pkg) {
1517 return Err(fail(
1518 format!("`{pkg}` is a reserved word and cannot name a package"),
1519 pkg_span,
1520 ));
1521 }
1522 self.bump(); let name_span = self.peek_span();
1524 let name = match self.bump() {
1525 TokenKind::Ident(n) => n,
1526 TokenKind::True => "true".to_string(),
1528 TokenKind::False => "false".to_string(),
1529 TokenKind::Nil => "nil".to_string(),
1530 other => {
1531 return Err(fail(
1532 format!("expected a name after `{pkg}::`, found {other}"),
1533 name_span,
1534 ))
1535 }
1536 };
1537 if self.at(&TokenKind::PathSep) {
1538 return Err(self.error(format!(
1539 "a qualified name has one qualifier: `package::name`, and `{pkg}::{name}::…` has two"
1540 )));
1541 }
1542 Ok(sym_at(
1543 self.span_since(pkg_span.start),
1544 &qualify(pkg, &name),
1545 ))
1546 }
1547
1548 #[inline(never)]
1555 fn negation(&mut self, head: Span, whole: Span, operand: Spanned) -> Spanned {
1556 if let SpannedForm::List(items) = &operand.form {
1557 let flipped = match items.first().and_then(Spanned::as_symbol) {
1558 Some("equal?") => Some("not="),
1559 Some("not=") => Some("equal?"),
1560 _ => None,
1561 };
1562 if let (Some(to), 3) = (flipped, items.len()) {
1563 let (written, canonical) = if to == "not=" {
1564 ("not (a == b)", "a != b")
1565 } else {
1566 ("not (a != b)", "a == b")
1567 };
1568 self.note(Spelling::NegatedComparison, whole, written, canonical);
1569 let mut out = items.clone();
1570 out[0] = sym_at(items[0].span, to);
1571 if to == "equal?" {
1572 if let Some(test) =
1573 self.nil_test(whole.start, items[0].span, &items[1], &items[2])
1574 {
1575 return test;
1576 }
1577 }
1578 return list_at(whole, out);
1579 }
1580 }
1581 list_at(whole, vec![sym_at(head, "not"), operand])
1582 }
1583
1584 #[inline(never)]
1585 fn call_spelling(&mut self, call: Spanned) -> Spanned {
1586 let SpannedForm::List(items) = &call.form else {
1587 return call;
1588 };
1589 let Some(head) = items.first().and_then(Spanned::as_symbol) else {
1590 return call;
1591 };
1592 let argc = items.len() - 1;
1593 if head == "equal?" && argc == 2 {
1594 let (op_span, start) = (items[0].span, call.span.start);
1595 if let Some(test) = self.nil_test(start, op_span, &items[1], &items[2]) {
1596 return test;
1597 }
1598 }
1599 let operator = match (head, argc) {
1600 ("and" | "or" | "mod" | "equal?", 2) => Some(head),
1601 ("list", _) => Some("list"),
1602 _ => None,
1603 };
1604 if let Some(op) = operator {
1605 let canonical = match op {
1606 "list" => "[…]".to_string(),
1607 "mod" => "a % b".to_string(),
1608 "equal?" => "a == b".to_string(),
1609 other => format!("a {other} b"),
1610 };
1611 self.note(
1612 Spelling::OperatorSpelling,
1613 call.span,
1614 &format!("{head}(…)"),
1615 &canonical,
1616 );
1617 return call;
1618 }
1619 if head == "apply"
1620 && self.text(items[0].span) == "apply"
1621 && argc >= 2
1622 && items[1].as_symbol().is_some_and(is_callable_name)
1623 {
1624 self.note(Spelling::Apply, call.span, "apply(f, …, xs)", "f(…, *xs)");
1625 return call;
1626 }
1627 if head == "try" {
1628 return self.try_spelling(call);
1629 }
1630 call
1631 }
1632
1633 fn try_spelling(&mut self, call: Spanned) -> Spanned {
1634 let SpannedForm::List(items) = &call.form else {
1635 return call;
1636 };
1637 let Some((catch, body)) = items[1..].split_last() else {
1638 return call;
1639 };
1640 let Some(clause) = catch.as_list() else {
1641 return call;
1642 };
1643 let binder = match clause {
1644 [head, binding, handler @ ..]
1645 if head.as_symbol() == Some("catch") && !handler.is_empty() =>
1646 {
1647 match binding.as_list() {
1648 Some([b]) => b.as_symbol().map(|n| (n.to_string(), b.span)),
1649 _ => None,
1650 }
1651 }
1652 _ => None,
1653 };
1654 let Some((name, binder_span)) = binder else {
1655 return call;
1656 };
1657 if body.is_empty() {
1658 return call;
1659 }
1660 self.note(Spelling::TryCatch, call.span, "try(…, catch(…))", "rescue");
1661 let handler = &clause[2..];
1662 let rename =
1663 name.starts_with('_') && name != "_" && !handler.iter().any(|h| mentions(h, &name));
1664 let body = if body.len() == 1 {
1665 body[0].clone()
1666 } else {
1667 let span = cover(body, call.span);
1668 let mut list = vec![sym_at(Span::new(span.start, span.start), "begin")];
1669 list.extend(body.iter().cloned());
1670 list_at(span, list)
1671 };
1672 let handler = if handler.len() == 1 {
1673 handler[0].clone()
1674 } else {
1675 let span = cover(handler, catch.span);
1676 let mut list = vec![sym_at(Span::new(span.start, span.start), "begin")];
1677 list.extend(handler.iter().cloned());
1678 list_at(span, list)
1679 };
1680 let binder = sym_at(binder_span, if rename { "_" } else { &name });
1681 list_at(
1682 call.span,
1683 vec![
1684 items[0].clone(),
1685 body,
1686 list_at(
1687 catch.span,
1688 vec![
1689 clause[0].clone(),
1690 list_at(clause[1].span, vec![binder]),
1691 handler,
1692 ],
1693 ),
1694 ],
1695 )
1696 }
1697
1698 fn finish_send(&mut self, start: usize, recv: Spanned, dot: Span) -> Result<Spanned, Fail> {
1699 let name_span = self.peek_span();
1700 let name = match self.bump() {
1701 TokenKind::Ident(n) if is_reserved_word(&n) => {
1715 return Err(self.error(format!(
1716 "`{n}` is a reserved word and cannot be a method name — \
1717 `recv.{n}` would lower onto the `{n}` form itself"
1718 )))
1719 }
1720 TokenKind::Ident(n) => n,
1721 other => {
1722 return Err(fail(
1723 format!("expected a method name after `.`, found {other}"),
1724 name_span,
1725 ))
1726 }
1727 };
1728 if !self.at(&TokenKind::LParen) {
1729 return Ok(list_at(
1730 self.span_since(start),
1731 vec![
1732 sym_at(dot, LOWERED_FIELD),
1733 recv,
1734 atom_at(name_span, Atom::Keyword(name)),
1735 ],
1736 ));
1737 }
1738 self.note(
1739 Spelling::CallForm,
1740 dot.merge(name_span),
1741 &format!("x.{name}(…)"),
1742 &format!("{name}(x, …)"),
1743 );
1744 let (args, splat) = self.call_args()?;
1745 Ok(self.call_node(
1746 self.span_since(start),
1747 vec![sym_at(name_span, &name), recv],
1748 args,
1749 splat,
1750 ))
1751 }
1752
1753 fn call_node(
1754 &self,
1755 span: Span,
1756 head: Vec<Spanned>,
1757 args: Vec<Spanned>,
1758 splat: Option<Span>,
1759 ) -> Spanned {
1760 let mut list = Vec::with_capacity(head.len() + args.len() + 1);
1761 if let Some(at) = splat {
1762 list.push(sym_at(at, "apply"));
1763 }
1764 list.extend(head);
1765 list.extend(args);
1766 list_at(span, list)
1767 }
1768
1769 fn call_args(&mut self) -> Result<(Vec<Spanned>, Option<Span>), Fail> {
1770 self.expect(&TokenKind::LParen, "`(`")?;
1771 let mut args = Vec::new();
1772 let mut splat = None;
1773 self.skip_newlines();
1774 if self.eat(&TokenKind::RParen) {
1775 return Ok((args, splat));
1776 }
1777 loop {
1778 self.skip_newlines();
1779 if splat.is_some() {
1780 return Err(
1781 self.error("a splat `*xs` passes a list as the LAST arguments: write it last")
1782 );
1783 }
1784 if self.at_spread() || matches!(self.peek(), TokenKind::Label(_)) {
1785 let open = self.peek_span();
1786 let (segments, spread) = self.map_entries(open, &TokenKind::RParen)?;
1787 if !spread {
1788 return Err(fail(
1789 "a keyword entry in a call needs a `**` spread: pass a map, f({a: 1})",
1790 open,
1791 ));
1792 }
1793 args.push(self.merged(open, LOWERED_KWARGS, segments));
1794 return Ok((args, splat));
1795 }
1796 if matches!(self.peek(), TokenKind::Op(o) if o == "*") {
1797 splat = Some(self.peek_span());
1798 self.bump();
1799 }
1800 args.push(self.expr(0)?);
1801 self.skip_newlines();
1802 if self.eat(&TokenKind::Comma) {
1803 if self.trailing_comma(&TokenKind::RParen) {
1804 break;
1805 }
1806 continue;
1807 }
1808 self.expect(&TokenKind::RParen, "`,` or `)`")?;
1809 break;
1810 }
1811 Ok((args, splat))
1812 }
1813
1814 fn trailing_comma(&mut self, closer: &TokenKind) -> bool {
1815 let comma = self
1816 .toks
1817 .get(self.pos.wrapping_sub(1))
1818 .map_or(self.peek_span(), |t| t.span);
1819 self.skip_newlines();
1820 if self.at(closer) {
1821 self.note(Spelling::TrailingComma, comma, ",", "");
1822 self.bump();
1823 return true;
1824 }
1825 false
1826 }
1827
1828 fn list_literal(&mut self, open: Span) -> Result<Spanned, Fail> {
1829 let mut items = vec![sym_at(open, "list")];
1830 self.skip_newlines();
1831 if self.eat(&TokenKind::RBracket) {
1832 return Ok(list_at(self.span_since(open.start), items));
1833 }
1834 loop {
1835 self.skip_newlines();
1836 items.push(self.expr(0)?);
1837 self.skip_newlines();
1838 if self.eat(&TokenKind::Comma) {
1839 if self.trailing_comma(&TokenKind::RBracket) {
1840 break;
1841 }
1842 continue;
1843 }
1844 self.expect(&TokenKind::RBracket, "`,` or `]`")?;
1845 break;
1846 }
1847 Ok(list_at(self.span_since(open.start), items))
1848 }
1849
1850 fn map_literal(&mut self, open: Span) -> Result<Spanned, Fail> {
1857 self.skip_newlines();
1858 if self.eat(&TokenKind::RBrace) {
1859 return Ok(list_at(
1860 self.span_since(open.start),
1861 vec![sym_at(open, LOWERED_MAP)],
1862 ));
1863 }
1864 let (mut segments, spread) = self.map_entries(open, &TokenKind::RBrace)?;
1865 if !spread {
1866 return Ok(segments.remove(0));
1867 }
1868 Ok(self.merged(open, LOWERED_MERGE, segments))
1869 }
1870
1871 fn merged(&self, open: Span, head: &str, segments: Vec<Spanned>) -> Spanned {
1872 let mut items = vec![sym_at(open, head)];
1873 items.extend(segments);
1874 list_at(self.span_since(open.start), items)
1875 }
1876
1877 fn at_spread(&self) -> bool {
1878 matches!(self.peek(), TokenKind::Op(o) if o == "**")
1879 }
1880
1881 fn map_entries(
1882 &mut self,
1883 open: Span,
1884 closer: &TokenKind,
1885 ) -> Result<(Vec<Spanned>, bool), Fail> {
1886 let mut segments = Vec::new();
1887 let mut items = vec![sym_at(open, LOWERED_MAP)];
1888 let mut spread = false;
1889 loop {
1890 self.skip_newlines();
1891 if self.at_spread() {
1892 let star = self.peek_span();
1893 self.bump();
1894 let value = self.expr(0)?;
1895 if items.len() > 1 {
1896 let span = items[1].span.merge(items[items.len() - 1].span);
1897 items[0] = sym_at(items[1].span, LOWERED_MAP);
1898 segments.push(list_at(span, std::mem::take(&mut items)));
1899 items = vec![sym_at(open, LOWERED_MAP)];
1900 }
1901 segments.push(list_at(
1902 star.merge(value.span),
1903 vec![sym_at(star, LOWERED_SPLICE), value],
1904 ));
1905 spread = true;
1906 } else {
1907 self.map_entry(&mut items, closer)?;
1908 }
1909 self.skip_newlines();
1910 if self.eat(&TokenKind::Comma) {
1911 if self.trailing_comma(closer) {
1912 break;
1913 }
1914 continue;
1915 }
1916 self.expect(closer, "`,` or the end of the entries")?;
1917 break;
1918 }
1919 if items.len() > 1 || !spread {
1920 let whole = self.span_since(open.start);
1921 segments.push(list_at(whole, items));
1922 }
1923 Ok((segments, spread))
1924 }
1925
1926 fn map_entry(&mut self, items: &mut Vec<Spanned>, closer: &TokenKind) -> Result<(), Fail> {
1927 match self.peek().clone() {
1928 TokenKind::Label(name) => {
1929 let label_span = self.peek_span();
1930 self.bump();
1931 self.skip_newlines();
1932 items.push(atom_at(label_span, Atom::Keyword(name.clone())));
1933 if matches!(self.peek(), TokenKind::Comma) || self.at(closer) {
1934 items.push(sym_at(label_span, &name));
1935 } else {
1936 let value = self.expr(0)?;
1937 if matches!(&value.form, SpannedForm::Atom(Atom::Symbol(s)) if *s == name)
1938 && label_name(&name)
1939 {
1940 let written = format!("{name}: {name}");
1941 let canonical = format!("{name}:");
1942 self.note(
1943 Spelling::MapPun,
1944 label_span.merge(value.span),
1945 &written,
1946 &canonical,
1947 );
1948 }
1949 items.push(value);
1950 }
1951 }
1952 _ => {
1953 let k = self.expr(0)?;
1954 self.skip_newlines();
1955 if let SpannedForm::Atom(Atom::Keyword(name)) = &k.form {
1956 if blue_lang_name(name) {
1957 let written = format!(":{name} =>");
1958 let canonical = format!("{name}:");
1959 self.note(Spelling::KeywordRocket, k.span, &written, &canonical);
1960 }
1961 }
1962 self.expect(&TokenKind::Rocket, "`=>` in a map literal")?;
1963 self.skip_newlines();
1964 items.push(k);
1965 items.push(self.expr(0)?);
1966 }
1967 }
1968 Ok(())
1969 }
1970
1971 fn if_chain(&mut self, head: Span) -> Result<Spanned, Fail> {
1981 let cond = self.expr(0)?;
1982 let then = self.body(&["elsif", "else", "end"])?;
1983 let els = self.else_part()?;
1984 let mut out = vec![sym_at(head, "if"), cond, then];
1985 if let Some(e) = els {
1986 out.push(e);
1987 }
1988 Ok(list_at(self.span_since(head.start), out))
1989 }
1990
1991 fn else_part(&mut self) -> Result<Option<Spanned>, Fail> {
1992 if self.at_ident("elsif") {
1993 let elsif = self.peek_span();
1994 self.bump();
1995 return Ok(Some(self.if_chain(elsif)?));
1996 }
1997 if !self.at_ident("else") {
1998 self.expect_ident("end")?;
1999 return Ok(None);
2000 }
2001 let else_span = self.peek_span();
2002 self.bump();
2003 let save = self.pos;
2004 self.skip_newlines();
2005 let leads_with_if = self.at_ident("if") || self.at_ident("unless");
2006 self.pos = save;
2007 let e = self.body(&["end"])?;
2008 let end_at = self.peek_span().start;
2009 self.expect_ident("end")?;
2010 if matches!(e.form, SpannedForm::Nil) && !self.comment_within(else_span.start, end_at) {
2011 self.note(Spelling::ElseNil, else_span, "else nil", "no else");
2012 return Ok(None);
2013 }
2014 let single_if = matches!(&e.form, SpannedForm::List(items)
2015 if (items.len() == 3 || items.len() == 4) && items[0].as_symbol() == Some("if"));
2016 if leads_with_if && single_if {
2017 self.note(Spelling::ElseIf, else_span, "else if", "elsif");
2018 }
2019 Ok(Some(e))
2020 }
2021
2022 fn if_form(&mut self, head: Span, negate: bool) -> Result<Spanned, Fail> {
2023 let cond = self.expr(0)?;
2024 let cond = if negate {
2025 let inner = cond.span;
2028 self.negation(head, head.merge(inner), cond)
2029 } else {
2030 cond
2031 };
2032 let then = self.body(&["elsif", "else", "end"])?;
2045 let els = self.else_part()?;
2046 let mut out = vec![sym_at(head, "if"), cond, then];
2047 if let Some(e) = els {
2048 out.push(e);
2049 }
2050 Ok(list_at(self.span_since(head.start), out))
2051 }
2052
2053 fn case_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2079 let subject = self.expr(0)?;
2080 self.skip_newlines();
2081
2082 let subject_var = "case-subject";
2085 let mut arms: Vec<Spanned> = Vec::new();
2086 let mut otherwise: Option<Spanned> = None;
2087 let mut else_kw: Option<Span> = None;
2090
2091 loop {
2092 self.skip_newlines();
2093 if self.at_ident("end") {
2094 break;
2095 }
2096 let maybe_else = self.peek_span();
2097 if self.eat_ident("else") {
2098 else_kw = Some(maybe_else);
2099 otherwise = Some(self.body(&["end"])?);
2100 continue;
2101 }
2102 let when = self.peek_span();
2103 if !self.eat_ident("when") {
2104 return Err(self.error(format!(
2105 "expected `when`, `else` or `end` in a case, found {}",
2106 self.peek()
2107 )));
2108 }
2109 self.skip_newlines();
2110 let pattern = self.expr(0)?;
2111 let body = self.body(&["when", "else", "end"])?;
2112 let test = list_at(
2116 when.merge(pattern.span),
2117 vec![sym_at(when, "equal?"), sym_at(when, subject_var), pattern],
2118 );
2119 arms.push(list_at(when.merge(body.span), vec![test, body]));
2120 }
2121 self.expect_ident("end")?;
2122
2123 if arms.is_empty() && otherwise.is_none() {
2124 return Err(self.error("a case needs at least one `when` or an `else`".to_string()));
2125 }
2126
2127 let mut cond = vec![sym_at(head, "cond")];
2132 cond.extend(arms);
2133 let else_span = else_kw.unwrap_or(head);
2140 let else_body = otherwise.unwrap_or_else(|| Spanned::new(else_span, SpannedForm::Nil));
2141 cond.push(list_at(
2142 else_span.merge(else_body.span),
2143 vec![sym_at(else_span, "else"), else_body],
2144 ));
2145
2146 let whole = self.span_since(head.start);
2147 let binding = list_at(
2148 head.merge(subject.span),
2149 vec![sym_at(head, subject_var), subject],
2150 );
2151 Ok(list_at(
2152 whole,
2153 vec![
2154 sym_at(head, "let"),
2155 list_at(binding.span, vec![binding]),
2156 list_at(whole, cond),
2157 ],
2158 ))
2159 }
2160
2161 fn arrow_lambda_ahead(&self) -> bool {
2164 let mut i = self.pos;
2165 let kind = |i: usize| self.toks.get(i).map(|t| &t.kind);
2166 if !matches!(kind(i), Some(TokenKind::RParen)) {
2167 loop {
2168 if matches!(kind(i), Some(TokenKind::Op(o)) if o == "*") {
2169 i += 1;
2170 }
2171 if !matches!(kind(i), Some(TokenKind::Ident(_))) {
2172 return false;
2173 }
2174 i += 1;
2175 match kind(i) {
2176 Some(TokenKind::Comma) => i += 1,
2177 Some(TokenKind::RParen) => break,
2178 _ => return false,
2179 }
2180 }
2181 }
2182 matches!(kind(i + 1), Some(TokenKind::Op(o)) if o == "->")
2183 }
2184
2185 fn arrow_lambda(&mut self, head: Span) -> Result<Spanned, Fail> {
2192 let mut params: Vec<Spanned> = Vec::new();
2193 while !self.at(&TokenKind::RParen) {
2194 let p_span = self.peek_span();
2195 if self.at_star() {
2196 params.extend(self.rest_parameter()?);
2197 break;
2198 }
2199 match self.bump() {
2200 TokenKind::Ident(p) => params.push(sym_at(p_span, &p)),
2201 other => {
2202 return Err(fail(
2203 format!("expected a parameter name, found {other}"),
2204 p_span,
2205 ))
2206 }
2207 }
2208 self.eat(&TokenKind::Comma);
2209 }
2210 self.bump();
2211 self.bump();
2212 self.skip_newlines();
2213 let params_span = cover(¶ms, head);
2214 let body = self.expr(0)?;
2215 Ok(list_at(
2216 self.span_since(head.start),
2217 vec![sym_at(head, "lambda"), list_at(params_span, params), body],
2218 ))
2219 }
2220
2221 fn rest_parameter(&mut self) -> Result<[Spanned; 2], Fail> {
2222 let star = self.peek_span();
2223 self.bump();
2224 let name_span = self.peek_span();
2225 match self.bump() {
2226 TokenKind::Ident(p) => {
2227 let whole = star.merge(name_span);
2228 Ok([sym_at(whole, "&rest"), sym_at(whole, &p)])
2229 }
2230 other => Err(fail(
2231 format!("expected a parameter name after `*`, found {other}"),
2232 name_span,
2233 )),
2234 }
2235 }
2236
2237 fn at_star(&self) -> bool {
2238 matches!(self.peek(), TokenKind::Op(o) if o == "*")
2239 }
2240
2241 fn optional_mark(
2242 &self,
2243 name: String,
2244 span: Span,
2245 optional_from: &mut Option<usize>,
2246 index: usize,
2247 ) -> Result<String, Fail> {
2248 match name.strip_suffix('?').filter(|n| !n.is_empty()) {
2249 Some(bare) => {
2250 optional_from.get_or_insert(index);
2251 Ok(bare.to_string())
2252 }
2253 None if optional_from.is_some() => Err(fail(
2254 format!("the required parameter `{name}` cannot follow an optional one"),
2255 span,
2256 )),
2257 None => Ok(name),
2258 }
2259 }
2260
2261 fn rest_is_last(&mut self) -> Result<(), Fail> {
2262 self.skip_newlines();
2263 if self.at(&TokenKind::Comma) {
2264 return Err(self.error("a rest parameter `*name` comes last".to_string()));
2265 }
2266 Ok(())
2267 }
2268
2269 fn lambda_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2279 let mut params: Vec<Spanned> = Vec::new();
2280 if !self.at(&TokenKind::LParen) {
2281 self.note(Spelling::ParameterList, head, "fn", "fn()");
2282 }
2283 if self.at(&TokenKind::LParen) {
2284 self.bump();
2285 self.skip_newlines();
2286 if !self.eat(&TokenKind::RParen) {
2287 loop {
2288 self.skip_newlines();
2289 let p_span = self.peek_span();
2290 if self.at_star() {
2291 params.extend(self.rest_parameter()?);
2292 self.rest_is_last()?;
2293 self.expect(&TokenKind::RParen, "`)` after the rest parameter")?;
2294 break;
2295 }
2296 match self.bump() {
2297 TokenKind::Ident(p) => params.push(sym_at(p_span, &p)),
2298 other => {
2299 return Err(fail(
2300 format!("expected a parameter name, found {other}"),
2301 p_span,
2302 ))
2303 }
2304 }
2305 self.skip_newlines();
2306 if self.eat(&TokenKind::Comma) {
2307 if self.trailing_comma(&TokenKind::RParen) {
2308 break;
2309 }
2310 continue;
2311 }
2312 self.expect(&TokenKind::RParen, "`,` or `)`")?;
2313 break;
2314 }
2315 }
2316 }
2317 let params_span = cover(¶ms, head);
2318 let body = self.body(&["end"])?;
2319 self.expect_ident("end")?;
2320 Ok(list_at(
2321 self.span_since(head.start),
2322 vec![sym_at(head, "lambda"), list_at(params_span, params), body],
2323 ))
2324 }
2325
2326 fn test_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2332 let name_span = self.peek_span();
2333 let name = match self.bump() {
2334 TokenKind::Str(s) => s,
2335 other => {
2336 return Err(fail(
2337 format!("expected a string name after `test`, found {other}"),
2338 name_span,
2339 ))
2340 }
2341 };
2342 let body = self.body(&["end"])?;
2343 self.expect_ident("end")?;
2344 Ok(list_at(
2345 self.span_since(head.start),
2346 vec![
2347 sym_at(head, "deftest"),
2348 atom_at(name_span, Atom::Str(name)),
2349 body,
2350 ],
2351 ))
2352 }
2353
2354 fn assert_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2378 let e = self.expr(0)?;
2379 let quoted = Spanned::new(e.span, SpannedForm::Quote(Box::new(e.clone())));
2380 Ok(list_at(
2381 self.span_since(head.start),
2382 vec![sym_at(head, LOWERED_ASSERT), quoted, e],
2383 ))
2384 }
2385
2386 fn defmacro_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2395 let name_span = self.peek_span();
2396 let name = match self.bump() {
2397 TokenKind::Ident(n) => n,
2398 other => {
2399 return Err(fail(
2400 format!("expected a name after `defmacro`, found {other}"),
2401 name_span,
2402 ))
2403 }
2404 };
2405 self.not_reserved(&name, name_span, "a `defmacro` name")?;
2406 let mut params: Vec<Spanned> = Vec::new();
2407 if self.at(&TokenKind::LParen) {
2408 self.bump();
2409 self.skip_newlines();
2410 if !self.eat(&TokenKind::RParen) {
2411 loop {
2412 self.skip_newlines();
2413 let p_span = self.peek_span();
2414 match self.bump() {
2415 TokenKind::Ident(p) => params.push(sym_at(p_span, &p)),
2416 TokenKind::Label(p) => {
2417 return Err(self.error(format!(
2418 "macro parameter `{p}` cannot be typed: a macro receives \
2419 source forms, not values"
2420 )))
2421 }
2422 other => {
2423 return Err(fail(
2424 format!("expected a parameter name, found {other}"),
2425 p_span,
2426 ))
2427 }
2428 }
2429 self.skip_newlines();
2430 if self.eat(&TokenKind::Comma) {
2431 if self.trailing_comma(&TokenKind::RParen) {
2432 break;
2433 }
2434 continue;
2435 }
2436 self.expect(&TokenKind::RParen, "`,` or `)`")?;
2437 break;
2438 }
2439 }
2440 }
2441 if matches!(self.peek(), TokenKind::Op(o) if o == "->") {
2442 return Err(self.error(
2443 "a macro has no return type: it produces source forms, not values".to_string(),
2444 ));
2445 }
2446
2447 let params_span = cover(¶ms, name_span);
2448 let body = self.body(&["end"])?;
2449 self.expect_ident("end")?;
2450
2451 Ok(list_at(
2454 self.span_since(head.start),
2455 vec![
2456 sym_at(head, "defmacro"),
2457 sym_at(name_span, &name),
2458 list_at(params_span, params),
2459 body,
2460 ],
2461 ))
2462 }
2463
2464 fn quote_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2470 let body = self.body(&["end"])?;
2471 self.expect_ident("end")?;
2472 Ok(Spanned::new(
2481 self.span_since(head.start),
2482 SpannedForm::Quasiquote(Box::new(body)),
2483 ))
2484 }
2485
2486 fn unquote_form(&mut self, head: Span, splice: bool) -> Result<Spanned, Fail> {
2488 self.expect(&TokenKind::LParen, "`(` after unquote")?;
2489 self.skip_newlines();
2490 let inner = self.expr(0)?;
2491 self.skip_newlines();
2492 self.expect(&TokenKind::RParen, "`)`")?;
2493 let span = self.span_since(head.start);
2494 Ok(Spanned::new(
2495 span,
2496 if splice {
2497 SpannedForm::UnquoteSplice(Box::new(inner))
2498 } else {
2499 SpannedForm::Unquote(Box::new(inner))
2500 },
2501 ))
2502 }
2503
2504 fn protected_body(&mut self, head: Span) -> Result<Spanned, Fail> {
2505 let body = self.body(&["rescue", "ensure", "end"])?;
2506 if self.at_ident("rescue") || self.at_ident("ensure") {
2507 return self.rescue_tail(head, body);
2508 }
2509 Ok(body)
2510 }
2511
2512 fn begin_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2513 let body = self.body(&["rescue", "ensure", "end"])?;
2514 if !(self.at_ident("rescue") || self.at_ident("ensure")) {
2515 return Err(self.error(
2516 "a `begin` block protects its statements with `rescue` or `ensure`; without either, write the statements directly",
2517 ));
2518 }
2519 let tree = self.rescue_tail(head, body)?;
2520 self.expect_ident("end")?;
2521 Ok(Spanned::new(self.span_since(head.start), tree.form))
2522 }
2523
2524 fn rescue_tail(&mut self, head: Span, body: Spanned) -> Result<Spanned, Fail> {
2525 let mut clauses: Vec<Spanned> = Vec::new();
2526 let mut catch_all: Option<(Spanned, Spanned, Span)> = None;
2527 while self.at_ident("rescue") {
2528 let kw = self.peek_span();
2529 self.bump();
2530 if catch_all.is_some() {
2531 return Err(fail(
2532 "a `rescue NAME` catches every error, so it is the last `rescue`; put the kinds it would shadow above it",
2533 kw,
2534 ));
2535 }
2536 match self.peek().clone() {
2537 TokenKind::Sym(_) => {
2538 let mut kinds = Vec::new();
2539 loop {
2540 let k_span = self.peek_span();
2541 match self.bump() {
2542 TokenKind::Sym(k) => kinds.push(atom_at(k_span, Atom::Keyword(k))),
2543 other => {
2544 return Err(fail(
2545 format!("expected an error kind such as `:not_found`, found {other}"),
2546 k_span,
2547 ))
2548 }
2549 }
2550 if self.eat(&TokenKind::Comma) {
2551 self.skip_newlines();
2552 continue;
2553 }
2554 break;
2555 }
2556 let binder = if self.eat_ident("as") {
2557 let b_span = self.peek_span();
2558 match self.bump() {
2559 TokenKind::Ident(b) if !is_reserved_word(&b) => Some(sym_at(b_span, &b)),
2560 other => {
2561 return Err(fail(
2562 format!("expected the name the error is bound to after `as`, found {other}"),
2563 b_span,
2564 ))
2565 }
2566 }
2567 } else {
2568 None
2569 };
2570 let handler = self.body(&["rescue", "ensure", "end"])?;
2571 let mut test = vec![sym_at(kw, LOWERED_ERROR_MATCH), sym_at(kw, RESCUED)];
2572 test.extend(kinds);
2573 let test = list_at(cover(&test, kw), test);
2574 let handler = match binder {
2575 Some(b) => let_bound(b, handler, kw),
2576 None => handler,
2577 };
2578 clauses.push(list_at(kw.merge(handler.span), vec![test, handler]));
2579 }
2580 TokenKind::Ident(b) if !is_reserved_word(&b) => {
2581 let b_span = self.peek_span();
2582 self.bump();
2583 let handler = self.body(&["rescue", "ensure", "end"])?;
2584 catch_all = Some((sym_at(b_span, &b), handler, kw));
2585 }
2586 _ => {
2587 return Err(fail(
2588 "`rescue` names what it catches: `rescue :kind`, `rescue :a, :b as e`, or `rescue e` for every error (`rescue _` discards it)",
2589 kw,
2590 ))
2591 }
2592 }
2593 }
2594 let ensure = if self.at_ident("ensure") {
2595 self.bump();
2596 let cleanup = self.body(&["rescue", "end"])?;
2597 if self.at_ident("rescue") {
2598 return Err(self.error("`ensure` comes after every `rescue` of its block"));
2599 }
2600 Some(cleanup)
2601 } else {
2602 None
2603 };
2604 let whole = self.span_since(head.start);
2605 let protected = if clauses.is_empty() {
2606 match catch_all {
2607 None => body,
2608 Some((binder, handler, kw)) => list_at(
2609 whole,
2610 vec![
2611 sym_at(head, "try"),
2612 body,
2613 list_at(
2614 kw.merge(handler.span),
2615 vec![
2616 sym_at(kw, "catch"),
2617 list_at(binder.span, vec![binder]),
2618 handler,
2619 ],
2620 ),
2621 ],
2622 ),
2623 }
2624 } else {
2625 let first_kw = clauses[0].span;
2626 let otherwise = match catch_all {
2627 Some((binder, handler, kw)) => {
2628 let bound = let_bound(binder, handler, kw);
2629 list_at(kw.merge(bound.span), vec![sym_at(kw, "else"), bound])
2630 }
2631 None => list_at(
2632 head,
2633 vec![
2634 sym_at(head, "else"),
2635 list_at(
2636 head,
2637 vec![sym_at(head, LOWERED_RERAISE), sym_at(head, RESCUED)],
2638 ),
2639 ],
2640 ),
2641 };
2642 let mut cond = vec![sym_at(first_kw, "cond")];
2643 cond.extend(clauses);
2644 cond.push(otherwise);
2645 let cond_span = cover(&cond, first_kw);
2646 list_at(
2647 whole,
2648 vec![
2649 sym_at(head, "try"),
2650 body,
2651 list_at(
2652 cond_span,
2653 vec![
2654 sym_at(first_kw, "catch"),
2655 list_at(first_kw, vec![sym_at(first_kw, RESCUED)]),
2656 list_at(cond_span, cond),
2657 ],
2658 ),
2659 ],
2660 )
2661 };
2662 Ok(match ensure {
2663 None => protected,
2664 Some(cleanup) => {
2665 let attempted = list_at(
2666 protected.span,
2667 vec![sym_at(head, LOWERED_ATTEMPTED), protected],
2668 );
2669 let failed = list_at(
2670 head,
2671 vec![sym_at(head, LOWERED_FAILED), sym_at(head, RESCUED)],
2672 );
2673 let caught = list_at(
2674 whole,
2675 vec![
2676 sym_at(head, "try"),
2677 attempted,
2678 list_at(
2679 head,
2680 vec![
2681 sym_at(head, "catch"),
2682 list_at(head, vec![sym_at(head, RESCUED)]),
2683 failed,
2684 ],
2685 ),
2686 ],
2687 );
2688 list_at(
2689 whole,
2690 vec![sym_at(head, LOWERED_ENSURE), caught, thunk(cleanup, head)],
2691 )
2692 }
2693 })
2694 }
2695
2696 fn def_form(&mut self, head: Span) -> Result<Spanned, Fail> {
2697 let name_span = self.peek_span();
2698 let name = match self.bump() {
2699 TokenKind::Ident(n) => n,
2700 other => {
2701 return Err(fail(
2702 format!("expected a name after `def`, found {other}"),
2703 name_span,
2704 ))
2705 }
2706 };
2707 self.not_reserved(&name, name_span, "a `def` name")?;
2708 let mut params: Vec<(String, Span, Option<Spanned>)> = Vec::new();
2709 let mut optional_from: Option<usize> = None;
2710 let mut rest: Vec<Spanned> = Vec::new();
2711 if !self.at(&TokenKind::LParen) {
2712 self.note(
2713 Spelling::ParameterList,
2714 name_span,
2715 &format!("def {name}"),
2716 &format!("def {name}()"),
2717 );
2718 }
2719 if self.at(&TokenKind::LParen) {
2720 self.bump();
2721 self.skip_newlines();
2722 if !self.eat(&TokenKind::RParen) {
2723 loop {
2724 self.skip_newlines();
2725 let p_span = self.peek_span();
2726 if self.at_star() {
2727 if optional_from.is_some() {
2728 return Err(fail(
2729 "a rest parameter cannot follow an optional one",
2730 p_span,
2731 ));
2732 }
2733 rest = self.rest_parameter()?.to_vec();
2734 self.rest_is_last()?;
2735 self.expect(&TokenKind::RParen, "`)` after the rest parameter")?;
2736 break;
2737 }
2738 match self.bump() {
2739 TokenKind::Ident(p) => {
2741 self.not_reserved(&p, p_span, "a parameter name")?;
2742 let p =
2743 self.optional_mark(p, p_span, &mut optional_from, params.len())?;
2744 params.push((p, p_span, None));
2745 }
2746 TokenKind::Label(p) => {
2748 self.not_reserved(&p, p_span, "a parameter name")?;
2749 let p =
2750 self.optional_mark(p, p_span, &mut optional_from, params.len())?;
2751 self.skip_newlines();
2752 let ty = self.type_expr()?;
2753 params.push((p, p_span, Some(ty)));
2754 }
2755 other => {
2756 return Err(fail(
2757 format!("expected a parameter name, found {other}"),
2758 p_span,
2759 ))
2760 }
2761 }
2762 self.skip_newlines();
2763 if self.eat(&TokenKind::Comma) {
2764 if self.trailing_comma(&TokenKind::RParen) {
2765 break;
2766 }
2767 continue;
2768 }
2769 self.expect(&TokenKind::RParen, "`,` or `)`")?;
2770 break;
2771 }
2772 }
2773 }
2774 let sig_span = self.span_since(name_span.start);
2776
2777 let ret = if matches!(self.peek(), TokenKind::Op(o) if o == "->") {
2779 self.bump();
2780 self.skip_newlines();
2781 Some(self.type_expr()?)
2782 } else {
2783 None
2784 };
2785
2786 let begins = {
2787 let save = self.pos;
2788 self.skip_newlines();
2789 let at = self.at_ident("begin").then(|| self.peek_span());
2790 self.pos = save;
2791 at
2792 };
2793 let body = self.protected_body(head)?;
2794 if let Some(b) = begins {
2795 let protects = matches!(&body.form, SpannedForm::List(items)
2796 if matches!(items.first().and_then(Spanned::as_symbol), Some("try") | Some(LOWERED_ENSURE)));
2797 if protects && body.span.start == b.start {
2798 self.note(
2799 Spelling::TryCatch,
2800 b,
2801 "begin … end as a whole def body",
2802 "the clauses on the def itself",
2803 );
2804 }
2805 }
2806 self.expect_ident("end")?;
2807 let whole = self.span_since(head.start);
2808
2809 let annotated = ret.is_some() || params.iter().any(|(_, _, t)| t.is_some());
2810 if !annotated {
2811 let mut sig = vec![sym_at(name_span, &name)];
2812 for (i, (p, s, _)) in params.into_iter().enumerate() {
2813 if optional_from == Some(i) {
2814 sig.push(sym_at(s, OPTIONAL_MARKER));
2815 }
2816 sig.push(sym_at(s, &p));
2817 }
2818 sig.extend(rest);
2819 return Ok(list_at(
2820 whole,
2821 vec![sym_at(head, "define"), list_at(sig_span, sig), body],
2822 ));
2823 }
2824
2825 let mut sig = vec![sym_at(name_span, &name)];
2833 for (i, (p, s, t)) in params.into_iter().enumerate() {
2834 if optional_from == Some(i) {
2835 sig.push(sym_at(s, OPTIONAL_MARKER));
2836 }
2837 let ty = t.unwrap_or_else(|| sym_at(s, "dyn"));
2838 sig.push(list_at(s.merge(ty.span), vec![sym_at(s, &p), ty]));
2839 }
2840 sig.extend(rest);
2841 Ok(list_at(
2842 whole,
2843 vec![
2844 sym_at(head, "define-typed"),
2845 list_at(sig_span, sig),
2846 ret.unwrap_or_else(|| sym_at(head, "dyn")),
2847 body,
2848 ],
2849 ))
2850 }
2851
2852 fn type_expr(&mut self) -> Result<Spanned, Fail> {
2854 let name_span = self.peek_span();
2855 let name = match self.bump() {
2856 TokenKind::Ident(n) => n,
2857 other => {
2858 return Err(fail(
2859 format!("expected a type name, found {other}"),
2860 name_span,
2861 ))
2862 }
2863 };
2864 self.not_reserved(&name, name_span, "a type name")?;
2865 if self.at(&TokenKind::LParen) {
2866 self.bump();
2867 self.skip_newlines();
2868 let mut list = vec![sym_at(name_span, &name)];
2869 if !self.eat(&TokenKind::RParen) {
2870 loop {
2871 self.skip_newlines();
2872 list.push(self.type_expr()?);
2873 self.skip_newlines();
2874 if self.eat(&TokenKind::Comma) {
2875 if self.trailing_comma(&TokenKind::RParen) {
2876 break;
2877 }
2878 continue;
2879 }
2880 self.expect(&TokenKind::RParen, "`,` or `)`")?;
2881 break;
2882 }
2883 }
2884 return Ok(list_at(self.span_since(name_span.start), list));
2885 }
2886 Ok(sym_at(name_span, &name))
2887 }
2888
2889 fn not_reserved(&self, name: &str, at: Span, what: &str) -> Result<(), Fail> {
2890 if is_reserved_word(name) {
2891 return Err(fail(
2892 format!("`{name}` is a reserved word and cannot be {what}"),
2893 at,
2894 ));
2895 }
2896 Ok(())
2897 }
2898
2899 fn eat_ident(&mut self, name: &str) -> bool {
2901 if self.at_ident(name) {
2902 self.bump();
2903 true
2904 } else {
2905 false
2906 }
2907 }
2908
2909 fn expect_ident(&mut self, name: &str) -> Result<(), Fail> {
2910 if self.at_ident(name) {
2911 self.bump();
2912 Ok(())
2913 } else {
2914 Err(self.error(format!("expected `{name}`, found {}", self.peek())))
2915 }
2916 }
2917
2918 fn body(&mut self, terminators: &[&str]) -> Result<Spanned, Fail> {
2921 let start = self.mark();
2924 let mut forms = Vec::new();
2925 loop {
2926 self.skip_newlines();
2927 if matches!(self.peek(), TokenKind::Eof) {
2928 return Err(self.error(format!(
2929 "unterminated block: expected {} before the end of the file",
2930 one_of(terminators)
2931 )));
2932 }
2933 if terminators.iter().any(|t| self.at_ident(t)) {
2934 break;
2935 }
2936 forms.push(self.statement()?);
2937 self.end_of_statement(terminators)?;
2938 }
2939 Ok(match forms.len() {
2940 0 => Spanned::new(Span::new(start, start), SpannedForm::Nil),
2941 1 => forms.into_iter().next().expect("checked len"),
2942 _ => {
2943 let span = cover(&forms, Span::new(start, start));
2949 let head = Span::new(span.start, span.start);
2955 let mut list = vec![sym_at(head, "begin")];
2956 list.extend(forms);
2957 list_at(span, list)
2958 }
2959 })
2960 }
2961}
2962
2963fn one_of(words: &[&str]) -> String {
2966 let quoted: Vec<String> = words.iter().map(|w| format!("`{w}`")).collect();
2967 match quoted.split_last() {
2968 None => String::new(),
2969 Some((last, [])) => last.clone(),
2970 Some((last, rest)) => format!("{} or {last}", rest.join(", ")),
2971 }
2972}
2973
2974fn let_bound(binder: Spanned, body: Spanned, at: Span) -> Spanned {
2975 let span = at.merge(body.span);
2976 let pair = list_at(binder.span, vec![binder, sym_at(at, RESCUED)]);
2977 list_at(
2978 span,
2979 vec![sym_at(at, "let"), list_at(pair.span, vec![pair]), body],
2980 )
2981}
2982
2983fn thunk(body: Spanned, at: Span) -> Spanned {
2984 let span = at.merge(body.span);
2985 list_at(
2986 span,
2987 vec![sym_at(at, "lambda"), list_at(at, Vec::new()), body],
2988 )
2989}
2990
2991#[must_use]
2996pub fn label_name(name: &str) -> bool {
2997 matches!(
2998 lex(&format!("{name}:")).as_deref(),
2999 Ok([t, e]) if matches!(&t.kind, TokenKind::Label(n) if n == name) && e.kind == TokenKind::Eof
3000 )
3001}
3002
3003fn blue_lang_name(name: &str) -> bool {
3004 matches!(
3005 lex(name).as_deref(),
3006 Ok([t, e]) if matches!(&t.kind, TokenKind::Ident(n) if n == name) && e.kind == TokenKind::Eof
3007 ) && !name.ends_with(['?', '!'])
3008}
3009
3010fn mentions(form: &Spanned, name: &str) -> bool {
3011 match &form.form {
3012 SpannedForm::Atom(Atom::Symbol(s)) => s == name,
3013 SpannedForm::List(items) => items.iter().any(|i| mentions(i, name)),
3014 SpannedForm::Quote(i)
3015 | SpannedForm::Quasiquote(i)
3016 | SpannedForm::Unquote(i)
3017 | SpannedForm::UnquoteSplice(i) => mentions(i, name),
3018 _ => false,
3019 }
3020}
3021
3022fn sym_at(span: Span, s: &str) -> Spanned {
3024 Spanned::new(span, SpannedForm::Atom(Atom::Symbol(s.to_string())))
3025}
3026
3027fn atom_at(span: Span, a: Atom) -> Spanned {
3029 Spanned::new(span, SpannedForm::Atom(a))
3030}
3031
3032fn list_at(span: Span, items: Vec<Spanned>) -> Spanned {
3034 Spanned::new(span, SpannedForm::List(items))
3035}
3036
3037fn cover(items: &[Spanned], fallback: Span) -> Span {
3045 let merged = items
3046 .iter()
3047 .fold(Span::synthetic(), |acc, s| acc.merge(s.span));
3048 if merged.is_synthetic() {
3049 fallback
3050 } else {
3051 merged
3052 }
3053}
3054
3055#[cfg(test)]
3056mod tests {
3057 use super::*;
3058
3059 fn q(src: &str) -> String {
3063 parse_expr(src)
3064 .map(|s| s.to_string())
3065 .unwrap_or_else(|e| panic!("{src:?}: {e}"))
3066 }
3067
3068 #[test]
3069 fn a_reserved_word_is_not_a_def_name_a_parameter_or_a_type() {
3070 for src in [
3071 "def def a(x)\n x\nend",
3072 "def end()\n 1\nend",
3073 "def f(end)\n 1\nend",
3074 "def f(a: not)\n 1\nend",
3075 "def f(a) -> not a\n a\nend",
3076 "def f(a: List(1 + 2))\n a\nend",
3077 "defmacro defmacro m(a)\n quote\n a\n end\nend",
3078 ] {
3079 assert!(parse_program(src).is_err(), "{src:?} parsed");
3080 }
3081 }
3082
3083 #[test]
3084 fn a_type_application_takes_types_of_any_arity() {
3085 assert_eq!(
3086 q("def f(a: Map(Str, List(Int))) -> Unit()\n a\nend"),
3087 "(define-typed (f (a (Map Str (List Int)))) (Unit) a)"
3088 );
3089 }
3090
3091 #[test]
3094 fn arithmetic_respects_precedence() {
3095 assert_eq!(q("1 + 2 * 3"), "(+ 1 (* 2 3))");
3096 assert_eq!(q("(1 + 2) * 3"), "(* (+ 1 2) 3)");
3097 }
3098
3099 #[test]
3100 fn comparison_binds_looser_than_arithmetic() {
3101 assert_eq!(q("a + 1 < b"), "(< (+ a 1) b)");
3102 }
3103
3104 #[test]
3109 fn logical_operators_bind_loosest_and_lower_to_tataras_names() {
3110 assert_eq!(q("a && b || c"), "(or (and a b) c)");
3111 }
3112
3113 #[test]
3114 fn left_associativity() {
3115 assert_eq!(q("1 - 2 - 3"), "(- (- 1 2) 3)");
3116 }
3117
3118 #[test]
3119 fn a_dot_without_parentheses_reads_a_field() {
3120 assert_eq!(q("user.name"), "(blue-field user :name)");
3121 }
3122
3123 #[test]
3124 fn method_call_with_args() {
3125 assert_eq!(q("user.greet(1, 2)"), "(greet user 1 2)");
3126 }
3127
3128 #[test]
3129 fn chained_fields_read_left_to_right() {
3130 assert_eq!(q("a.b.c"), "(blue-field (blue-field a :b) :c)");
3131 assert_eq!(q("a.b(1).c"), "(blue-field (b a 1) :c)");
3132 }
3133
3134 #[test]
3135 fn plain_call() {
3136 assert_eq!(q("f(1, 2)"), "(f 1 2)");
3137 }
3138
3139 #[test]
3142 fn pipeline_threads_into_first_argument() {
3143 assert_eq!(q("x |> f"), "(f x)");
3144 assert_eq!(q("x |> f(1)"), "(f x 1)");
3145 assert_eq!(q("x |> f |> g"), "(g (f x))");
3146 }
3147
3148 #[test]
3149 fn pipeline_binds_looser_than_arithmetic() {
3150 assert_eq!(q("1 + 2 |> f"), "(f (+ 1 2))");
3151 }
3152
3153 #[test]
3159 fn label_and_rocket_produce_the_same_tree_for_a_symbol_key() {
3160 assert_eq!(q("{a: 1}"), q("{:a => 1}"));
3161 assert_eq!(q("{a: 1}"), "(hash-map :a 1)");
3162 }
3163
3164 #[test]
3167 fn a_string_key_has_no_shorthand() {
3168 assert_eq!(q(r#"{"k" => 1}"#), r#"(hash-map "k" 1)"#);
3169 }
3170
3171 #[test]
3172 fn list_literal() {
3173 assert_eq!(q("[1, 2, 3]"), "(list 1 2 3)");
3174 assert_eq!(q("[]"), "(list)");
3175 }
3176
3177 #[test]
3180 fn if_else_end() {
3181 assert_eq!(q("if a\n 1\nelse\n 2\nend"), "(if a 1 2)");
3182 }
3183
3184 #[test]
3185 fn if_without_else() {
3186 assert_eq!(q("if a\n 1\nend"), "(if a 1)");
3187 }
3188
3189 #[test]
3192 fn unless_is_a_negated_if() {
3193 assert_eq!(q("unless a\n 1\nend"), "(if (not a) 1)");
3194 }
3195
3196 #[test]
3197 fn multi_statement_body_becomes_begin() {
3198 assert_eq!(q("if a\n 1\n 2\nend"), "(if a (begin 1 2))");
3199 }
3200
3201 #[test]
3202 fn def_lowers_to_define() {
3203 assert_eq!(
3204 q("def add(a, b)\n a + b\nend"),
3205 "(define (add a b) (+ a b))"
3206 );
3207 }
3208
3209 #[test]
3210 fn def_with_no_params() {
3211 assert_eq!(q("def zero()\n 0\nend"), "(define (zero) 0)");
3212 }
3213
3214 fn refused(src: &str) -> String {
3215 match parse_expr(src) {
3216 Ok(s) => panic!("{src:?} should be refused, parsed to {s}"),
3217 Err(e) => e.to_string(),
3218 }
3219 }
3220
3221 #[test]
3222 fn a_question_mark_parameter_is_optional() {
3223 assert_eq!(q("def f(a, b?)\n b\nend"), "(define (f a &optional b) b)");
3224 assert_eq!(q("def f(a?, b?)\n a\nend"), "(define (f &optional a b) a)");
3225 assert_eq!(q("def f(a?)\n a\nend"), "(define (f &optional a) a)");
3226 }
3227
3228 #[test]
3229 fn an_annotated_optional_parameter_keeps_its_type_after_the_marker() {
3230 assert_eq!(
3231 q("def f(a: Int, b?: Str) -> Int\n a\nend"),
3232 "(define-typed (f (a Int) &optional (b Str)) Int a)"
3233 );
3234 assert_eq!(
3235 q("def f(a, b?: Str)\n a\nend"),
3236 "(define-typed (f (a dyn) &optional (b Str)) dyn a)"
3237 );
3238 }
3239
3240 #[test]
3241 fn a_def_without_question_marks_carries_no_marker() {
3242 assert_eq!(q("def f(a, b)\n a\nend"), "(define (f a b) a)");
3243 assert_eq!(q("def f(a, *r)\n a\nend"), "(define (f a &rest r) a)");
3244 }
3245
3246 #[test]
3247 fn a_required_parameter_cannot_follow_an_optional_one() {
3248 assert!(refused("def f(a?, b)\n a\nend").contains("`b` cannot follow an optional"));
3249 assert!(refused("def f(a, b?, c: Int)\n a\nend").contains("`c` cannot follow"));
3250 }
3251
3252 #[test]
3253 fn a_rest_parameter_cannot_follow_an_optional_one() {
3254 assert!(refused("def f(a, b?, *r)\n a\nend").contains("rest parameter cannot follow"));
3255 }
3256
3257 #[test]
3260 fn literals_lower_to_atoms() {
3261 assert_eq!(q("42"), "42");
3262 assert_eq!(q("true"), "#t");
3263 assert_eq!(q(":ok"), ":ok");
3264 assert_eq!(q(r#""hi""#), r#""hi""#);
3265 }
3266
3267 #[test]
3268 fn unary_minus_and_not() {
3269 assert_eq!(q("-x"), "(- 0 x)");
3270 assert_eq!(q("!x"), "(not x)");
3271 }
3272
3273 #[test]
3276 fn a_program_is_a_sequence_of_forms() {
3277 let forms = parse_program("def f()\n 1\nend\nf()").expect("parse");
3278 assert_eq!(forms.len(), 2);
3279 assert_eq!(forms[1].to_string(), "(f)");
3280 }
3281
3282 #[test]
3283 fn unterminated_block_is_an_error_naming_what_was_expected() {
3284 let e = parse_program("if a\n 1").expect_err("must fail");
3285 assert!(e.message.contains("unterminated"), "{}", e.message);
3286 }
3287
3288 #[test]
3289 fn a_parse_error_carries_a_span_into_the_source() {
3290 let src = "1 + )";
3291 let e = parse_program(src).expect_err("must fail");
3292 assert!(e.span.start < src.len(), "span {:?} outside source", e.span);
3293 }
3294
3295 #[test]
3302 fn postfix_indexing_is_refused_naming_nth() {
3303 let e = parse_program("y = xs[0]").expect_err("must not split into two statements");
3304 assert!(e.message.contains("`nth(0, xs)`"), "{}", e.message);
3305 assert_eq!((e.line, e.col), (1, 7), "{e}");
3306 }
3307
3308 #[test]
3310 fn a_call_without_parentheses_is_refused_naming_the_call() {
3311 let e = parse_program("a = f x").expect_err("must not split into two statements");
3312 assert!(e.message.contains("`f(x)`"), "{}", e.message);
3313 assert_eq!((e.line, e.col), (1, 7), "{e}");
3314 }
3315
3316 #[test]
3318 fn two_statements_on_one_line_are_refused_in_a_body() {
3319 for src in [
3320 "def f(xs)\n xs[0]\nend",
3321 "if a\n f x\nend",
3322 "fn(x) x 1 end",
3323 "test \"t\"\n assert a b\nend",
3324 "1 2",
3325 ] {
3326 assert!(parse_program(src).is_err(), "{src:?} must not parse");
3327 }
3328 }
3329
3330 #[test]
3332 fn an_arrow_lambda_lowers_to_the_same_tree_as_fn() {
3333 for (arrow, block) in [
3334 ("(x) -> x * 2", "fn(x) x * 2 end"),
3335 ("(a, b) -> a + b", "fn(a, b) a + b end"),
3336 ("() -> 1", "fn() 1 end"),
3337 ("map(xs, (x) -> x + 1)", "map(xs, fn(x) x + 1 end)"),
3338 ] {
3339 let a = parse_program(arrow).expect(arrow);
3340 let b = parse_program(block).expect(block);
3341 assert_eq!(format!("{a:?}"), format!("{b:?}"), "{arrow}");
3342 }
3343 assert_eq!(
3344 parse_program("(x)").map(|p| p.len()).ok(),
3345 Some(1),
3346 "a parenthesised name is still a group"
3347 );
3348 }
3349
3350 #[test]
3354 fn a_statement_may_end_at_a_newline_the_end_or_a_closing_keyword() {
3355 for src in [
3356 "fn(x) x + 1 end",
3357 "map(fn(x) x end, xs)",
3358 "if a\n 1\nelse\n 2\nend",
3359 "if a 1 else 2 end",
3360 "case x\nwhen 1 :a\nwhen 2 :b\nelse :c\nend",
3361 "x = 1\ny = 2",
3362 "x = 1",
3363 ] {
3364 assert!(
3365 parse_program(src).is_ok(),
3366 "{src:?}: {:?}",
3367 parse_program(src)
3368 );
3369 }
3370 }
3371
3372 #[test]
3374 fn then_is_refused_by_name() {
3375 let e = parse_program("x = if c then 1 else 2 end").expect_err("then is not a keyword");
3376 assert!(e.message.contains("`then`"), "{}", e.message);
3377 }
3378
3379 #[test]
3381 fn a_brace_block_is_refused_naming_fn() {
3382 let e = parse_program("xs.map { |x| x }").expect_err("no brace blocks");
3383 assert!(e.message.contains("fn(x)"), "{}", e.message);
3384 }
3385
3386 #[test]
3393 fn a_qualified_name_is_one_symbol() {
3394 assert_eq!(q("retsu::first(xs)"), "(retsu/first xs)");
3395 assert_eq!(q("retsu::first"), "retsu/first");
3396 assert_eq!(q("xs |> retsu::first"), "(retsu/first xs)");
3397 assert_eq!(q("blue::length(xs)"), "(blue/length xs)");
3398 }
3399
3400 #[test]
3402 fn whitespace_around_the_qualifier_is_accepted() {
3403 assert_eq!(q("retsu :: first(xs)"), "(retsu/first xs)");
3404 }
3405
3406 #[test]
3409 fn a_reserved_word_after_the_qualifier_is_a_name() {
3410 assert_eq!(q("kueri::if(a, b)"), "(kueri/if a b)");
3411 assert_eq!(q("heni::test(x)"), "(heni/test x)");
3412 assert_eq!(q("sabi::fn"), "sabi/fn");
3413 }
3414
3415 #[test]
3416 fn a_qualified_name_has_exactly_one_qualifier() {
3417 for (src, says) in [
3418 ("::first(xs)", "`package::name`"),
3419 ("a::b::c", "one qualifier"),
3420 ("if::x", "reserved word"),
3421 ("retsu::1", "expected a name after `retsu::`"),
3422 ] {
3423 let e = parse_program(src).expect_err(src);
3424 assert!(e.message.contains(says), "{src}: {}", e.message);
3425 }
3426 }
3427
3428 #[test]
3431 fn qualified_reads_only_the_package_name_shape() {
3432 assert_eq!(qualified("retsu/first"), Some(("retsu", "first")));
3433 assert_eq!(qualified("kueri/if"), Some(("kueri", "if")));
3434 assert_eq!(qualified("nisshi/pair?"), Some(("nisshi", "pair?")));
3435 for not in ["/", "a/", "/a", "a/b/c", "if/x", "hash-map", "a"] {
3436 assert_eq!(qualified(not), None, "{not}");
3437 }
3438 assert!(is_callable_name("kueri/if"));
3439 }
3440
3441 #[test]
3446 fn a_symbol_may_end_in_a_question_or_bang() {
3447 assert_eq!(q("[:pair?, :set!]"), "(list :pair? :set!)");
3448 assert_eq!(q("use(\"n\", [:empty?])"), "(use \"n\" (list :empty?))");
3449 assert_eq!(q(":a != :b"), "(not= :a :b)");
3450 }
3451
3452 #[test]
3456 fn a_parse_error_displays_line_col_and_the_source_text() {
3457 let e = parse_program("x = 1\ny = [1, 2 y]").expect_err("must fail");
3458 assert_eq!(e.to_string(), "2:11: expected `,` or `]`, found `y`");
3459 assert_eq!(e.span, Span::new(16, 17), "the span stays, as data");
3460 }
3461
3462 #[test]
3464 fn no_error_message_names_a_rust_variant() {
3465 for src in [
3466 "f(1 2)",
3467 "{a: 1 b}",
3468 "def 1\nend",
3469 "x.1",
3470 "if a\n",
3471 "fn(1) 2 end",
3472 "[1 :b]",
3473 "1 +",
3474 "(1",
3475 ] {
3476 let e = parse_program(src).expect_err(src);
3477 let shown = e.to_string();
3478 for tell in [
3479 "Ident(", "Int(", "Op(", "Label(", "Sym(", "Str(", "LParen", "RBracket", "Eof",
3480 "Newline", "..",
3481 ] {
3482 assert!(!shown.contains(tell), "{src:?} -> {shown}");
3483 }
3484 assert!(e.line >= 1 && e.col >= 1, "{src:?} not located: {shown}");
3485 }
3486 }
3487
3488 #[test]
3490 fn a_lex_error_is_located_too() {
3491 let e = parse_program("x = 1\ny = 2; z = 3").expect_err("no semicolons");
3492 assert_eq!((e.line, e.col), (2, 6), "{e}");
3493 assert!(e.message.contains("new line"), "{e}");
3494 }
3495
3496 #[test]
3499 fn the_parser_rejects_garbage() {
3500 assert!(parse_program("def").is_err());
3501 assert!(parse_program("(1").is_err());
3502 assert!(parse_program("end").is_err());
3503 }
3504}