1use tatara_lisp::{Atom, Span, Spanned, SpannedForm};
26
27#[derive(Clone, Copy, Debug, PartialEq, Eq)]
29pub enum HeadKind {
30 SpecialForm,
31 Macro,
32 Value,
33}
34
35#[derive(Clone, Copy, Debug, PartialEq, Eq)]
37pub enum BinderKind {
38 Parameter,
39 Assignment,
41 Function,
43 Let,
44 Catch,
45}
46
47#[derive(Clone, Debug, PartialEq)]
54pub struct Import {
55 pub package: String,
56 pub names: Vec<(String, Span)>,
59 pub listed: bool,
61 pub span: Span,
63 pub package_span: Span,
65}
66
67#[must_use]
76pub fn use_target(form: &Spanned) -> Option<Import> {
77 let items = form.as_list()?;
78 let (head, arg, list) = match items {
79 [head, arg] => (head, arg, None),
80 [head, arg, list] => (head, arg, Some(list)),
81 _ => return None,
82 };
83 let (SpannedForm::Atom(Atom::Symbol(s)), SpannedForm::Atom(Atom::Str(name))) =
84 (&head.form, &arg.form)
85 else {
86 return None;
87 };
88 if s != "use" {
89 return None;
90 }
91 let mut names = Vec::new();
92 if let Some(list) = list {
93 let parts = list.as_list()?;
94 if parts.first().and_then(Spanned::as_symbol) != Some("list") {
95 return None;
96 }
97 for p in &parts[1..] {
98 match &p.form {
99 SpannedForm::Atom(Atom::Keyword(k)) => names.push((k.clone(), p.span)),
100 _ => return None,
101 }
102 }
103 }
104 Some(Import {
105 package: name.clone(),
106 names,
107 listed: list.is_some(),
108 span: form.span,
109 package_span: arg.span,
110 })
111}
112
113pub const LEGACY_NAMES: &str = "legacy_names";
115
116#[derive(Clone, Debug, PartialEq)]
136pub struct Legacy {
137 pub since: String,
138 pub kind: LegacyKind,
139 pub span: Span,
140}
141
142#[derive(Clone, Debug, PartialEq)]
144pub enum LegacyKind {
145 Prefix(String),
147 Moved(Vec<Moved>),
149 Flat(Vec<(String, Span)>),
150}
151
152#[derive(Clone, Debug, PartialEq, Eq)]
160pub struct Moved {
161 pub old: String,
162 pub home: String,
163 pub new: String,
164 pub append: Option<String>,
165}
166
167#[must_use]
173pub fn legacy_target(form: &Spanned) -> Option<Legacy> {
174 let items = form.as_list()?;
175 let [head, since, what] = items else {
176 return None;
177 };
178 if head.as_symbol() != Some(LEGACY_NAMES) {
179 return None;
180 }
181 let SpannedForm::Atom(Atom::Str(since)) = &since.form else {
182 return None;
183 };
184 let str_of = |s: &Spanned| match &s.form {
185 SpannedForm::Atom(Atom::Str(x)) => Some(x.clone()),
186 _ => None,
187 };
188 let keyword_of = |s: &Spanned| match &s.form {
189 SpannedForm::Atom(Atom::Keyword(k)) => Some((k.clone(), s.span)),
190 _ => None,
191 };
192 let kind = if let Some(prefix) = str_of(what) {
193 LegacyKind::Prefix(prefix)
194 } else if let Some(flat) = list_items(what)
195 .filter(|items| !items.is_empty())
196 .and_then(|items| items.iter().map(keyword_of).collect::<Option<Vec<_>>>())
197 {
198 LegacyKind::Flat(flat)
199 } else {
200 let moves = list_items(what)?
201 .iter()
202 .map(|pair| {
203 let (old, target, append) = match list_items(pair)? {
204 [old, target] => (old, target, None),
205 [old, target, append] => (old, target, Some(str_of(append)?)),
206 _ => return None,
207 };
208 let (old, target) = (str_of(old)?, str_of(target)?);
209 let (home, new) = target
210 .split_once("::")
211 .map_or((String::new(), target.clone()), |(h, n)| {
212 (h.to_string(), n.to_string())
213 });
214 Some(Moved {
215 old,
216 home,
217 new,
218 append,
219 })
220 })
221 .collect::<Option<Vec<_>>>()?;
222 LegacyKind::Moved(moves)
223 };
224 Some(Legacy {
225 since: since.clone(),
226 kind,
227 span: form.span,
228 })
229}
230
231fn list_items(form: &Spanned) -> Option<&[Spanned]> {
233 let items = form.as_list()?;
234 (items.first()?.as_symbol() == Some("list")).then(|| &items[1..])
235}
236
237pub const SHAPED_FORMS: &[&str] = &[
242 "lambda",
243 "let",
244 "let*",
245 "letrec",
246 "try",
247 "cond",
248 "quote",
249 "quasiquote",
250 "provide",
251 "require",
252];
253
254pub trait Scopes {
256 fn head_kind(&self, name: &str) -> Option<HeadKind>;
259 fn open(&mut self);
261 fn bind(&mut self, name: &str, span: Span, kind: BinderKind);
263 fn close(&mut self);
265 fn reference(&mut self, node: &Spanned, name: &str, opaque: bool);
268 fn call(&mut self, node: &Spanned, _whole: &Spanned, name: &str, opaque: bool) {
269 self.reference(node, name, opaque);
270 }
271 fn place(&mut self, node: &Spanned, name: &str, opaque: bool) {
272 self.reference(node, name, opaque);
273 }
274 fn target(&mut self, node: &Spanned, name: &str, opaque: bool) {
277 self.place(node, name, opaque);
278 }
279 fn head(&mut self, _node: &Spanned, _name: &str, _kind: HeadKind) {}
282 fn template_symbol(&mut self, _node: &Spanned, _name: &str) {}
285}
286
287pub fn walk_program(forms: &[Spanned], pass: &mut dyn Scopes) {
291 for form in forms {
292 walk_top(form, pass);
293 }
294}
295
296pub fn walk_top(form: &Spanned, pass: &mut dyn Scopes) {
298 Walker { pass, depth: 0 }.walk(form, false);
299}
300
301#[must_use]
311pub fn definitions_of(form: &Spanned) -> Vec<(String, Span, HeadKind)> {
312 let mut out = Vec::new();
313 definition_nodes(form, &mut |node, kind| {
314 if let Some(n) = node.as_symbol() {
315 out.push((n.to_string(), node.span, kind));
316 }
317 });
318 out
319}
320
321pub fn definition_nodes<'a>(form: &'a Spanned, out: &mut dyn FnMut(&'a Spanned, HeadKind)) {
324 let Some(items) = form.as_list() else { return };
325 let head = items.first().and_then(Spanned::as_symbol);
326 match head {
327 Some("defmacro") => {
328 if let Some(n) = items.get(1).filter(|n| n.as_symbol().is_some()) {
329 out(n, HeadKind::Macro);
330 }
331 }
332 Some("define" | "define-typed") => {
333 if let Some(n) = defined_name_node(items) {
334 out(n, HeadKind::Value);
335 }
336 if items.get(1).is_some_and(|t| t.as_symbol().is_some()) {
339 for item in &items[2..] {
340 definition_nodes(item, out);
341 }
342 }
343 }
344 Some("lambda" | "let" | "let*" | "letrec" | "deftest" | "quote" | "quasiquote") => {}
345 Some("try") => {
346 for item in &items[1..] {
348 let is_clause = item
349 .as_list()
350 .and_then(|c| c.first())
351 .and_then(Spanned::as_symbol)
352 .is_some_and(|h| h == "catch" || h == "finally");
353 if !is_clause {
354 definition_nodes(item, out);
355 }
356 }
357 }
358 _ => {
359 for item in items {
360 definition_nodes(item, out);
361 }
362 }
363 }
364}
365
366#[must_use]
367pub fn nullary_definition(form: &Spanned) -> Option<&str> {
368 let items = form.as_list()?;
369 let head = items.first()?.as_symbol()?;
370 if head != "define" && head != "define-typed" {
371 return None;
372 }
373 match items.get(1)?.as_list()? {
374 [name] => name.as_symbol(),
375 _ => None,
376 }
377}
378
379#[must_use]
381pub fn defined_name_node(items: &[Spanned]) -> Option<&Spanned> {
382 let head = items.first()?.as_symbol()?;
383 if head != "define" && head != "define-typed" {
384 return None;
385 }
386 let target = items.get(1)?;
387 if target.as_symbol().is_some() {
388 return Some(target);
389 }
390 let name = target.as_list()?.first()?;
391 name.as_symbol().map(|_| name)
392}
393
394#[must_use]
397pub fn params_of(list: &Spanned) -> Vec<(String, Span)> {
398 if let Some(n) = list.as_symbol() {
399 return vec![(n.to_string(), list.span)];
400 }
401 list.as_list()
402 .map(|ps| ps.iter().filter_map(param).collect())
403 .unwrap_or_default()
404}
405
406fn param(p: &Spanned) -> Option<(String, Span)> {
407 let n = p.as_symbol()?;
408 if n.starts_with('&') || n == "." {
409 return None;
410 }
411 Some((n.to_string(), p.span))
412}
413
414struct Walker<'p> {
415 pass: &'p mut dyn Scopes,
416 depth: usize,
418}
419
420impl Walker<'_> {
421 fn open(&mut self) {
422 self.depth += 1;
423 self.pass.open();
424 }
425
426 fn close(&mut self) {
427 self.depth -= 1;
428 self.pass.close();
429 }
430
431 fn hoist(&mut self, body: &[Spanned]) {
435 for f in body {
436 let function = f.as_list().is_some_and(|items| {
437 items.first().and_then(Spanned::as_symbol) == Some("define")
438 && items.get(1).is_some_and(|t| t.as_list().is_some())
439 });
440 let pass = &mut *self.pass;
441 definition_nodes(f, &mut |node, kind| {
442 let Some(name) = node.as_symbol() else { return };
443 let bk = match kind {
444 HeadKind::Value if !function => BinderKind::Assignment,
445 _ => BinderKind::Function,
446 };
447 pass.bind(name, node.span, bk);
448 });
449 }
450 }
451
452 fn body(&mut self, params: &[(String, Span)], forms: &[Spanned], opaque: bool) {
455 self.open();
456 for (p, s) in params {
457 self.pass.bind(p, *s, BinderKind::Parameter);
458 }
459 self.hoist(forms);
460 for f in forms {
461 self.walk(f, opaque);
462 }
463 self.close();
464 }
465
466 fn walk(&mut self, form: &Spanned, opaque: bool) {
467 match &form.form {
468 SpannedForm::Atom(Atom::Symbol(n)) => self.pass.reference(form, n, opaque),
469 SpannedForm::Quote(_) => {}
470 SpannedForm::Quasiquote(inner) => self.walk_template(inner, opaque),
471 SpannedForm::Unquote(inner) | SpannedForm::UnquoteSplice(inner) => {
472 self.walk(inner, opaque);
473 }
474 SpannedForm::List(items) if !items.is_empty() => self.walk_list(form, items, opaque),
475 _ => {}
476 }
477 }
478
479 fn walk_template(&mut self, form: &Spanned, opaque: bool) {
481 match &form.form {
482 SpannedForm::Atom(Atom::Symbol(n)) => self.pass.template_symbol(form, n),
483 SpannedForm::Unquote(inner) | SpannedForm::UnquoteSplice(inner) => {
484 self.walk(inner, opaque);
485 }
486 SpannedForm::List(items) => {
487 for i in items {
488 self.walk_template(i, opaque);
489 }
490 }
491 SpannedForm::Quasiquote(inner) => self.walk_template(inner, opaque),
492 _ => {}
493 }
494 }
495
496 fn walk_test_meta(&mut self, meta: &Spanned, opaque: bool) {
500 if let Some(head) = meta.as_list().and_then(|items| items.first()) {
501 self.pass
502 .head(head, crate::parse::LOWERED_TEST_META, HeadKind::SpecialForm);
503 }
504 let target = crate::test_form::test_meta(meta).and_then(|m| m.target);
505 if let Some(t) = target {
506 if let Some(name) = t.as_symbol() {
507 self.pass.target(t, name, opaque);
508 }
509 }
510 }
511
512 fn walk_all(&mut self, items: &[Spanned], opaque: bool) {
513 for i in items {
514 self.walk(i, opaque);
515 }
516 }
517
518 fn walk_list(&mut self, form: &Spanned, items: &[Spanned], opaque: bool) {
519 let Some(head) = items[0].as_symbol() else {
520 self.walk_all(items, opaque);
521 return;
522 };
523 if head == "deftest" && self.depth == 0 {
526 match crate::test_form::test_parts(form) {
527 Some(parts) => {
528 if let Some(meta) = parts.meta {
529 self.walk_test_meta(meta, opaque);
530 }
531 self.body(&[], std::slice::from_ref(parts.body), opaque);
532 }
533 None => {
534 if let Some(body) = items.get(2) {
535 self.body(&[], std::slice::from_ref(body), opaque);
536 }
537 }
538 }
539 return;
540 }
541 if (head == "use" || head == LEGACY_NAMES) && self.depth == 0 {
544 self.pass.head(&items[0], head, HeadKind::SpecialForm);
545 return;
546 }
547 if head == "define" || head == "define-typed" {
550 self.pass.head(&items[0], head, HeadKind::SpecialForm);
551 self.walk_define(head, items, opaque);
552 return;
553 }
554 if head == "defmacro" && self.depth == 0 {
558 self.pass.head(&items[0], head, HeadKind::SpecialForm);
559 let params = items.get(2).map(params_of).unwrap_or_default();
560 self.body(¶ms, items.get(3..).unwrap_or(&[]), opaque);
561 return;
562 }
563 match self.pass.head_kind(head) {
564 Some(HeadKind::SpecialForm) => {
565 self.pass.head(&items[0], head, HeadKind::SpecialForm);
566 self.walk_special(head, items, opaque);
567 }
568 Some(HeadKind::Macro) => {
569 self.pass.head(&items[0], head, HeadKind::Macro);
571 for i in &items[1..] {
572 self.walk(i, true);
573 }
574 }
575 Some(HeadKind::Value) | None => {
576 self.pass.call(&items[0], form, head, opaque);
577 self.walk_all(&items[1..], opaque);
578 }
579 }
580 }
581
582 fn walk_define(&mut self, head: &str, items: &[Spanned], opaque: bool) {
583 let Some(target) = items.get(1) else { return };
584 if target.as_symbol().is_some() {
585 self.walk_all(&items[2..], opaque);
587 return;
588 }
589 let Some(sig) = target.as_list() else {
590 self.walk_all(&items[1..], opaque);
591 return;
592 };
593 let params: Vec<(String, Span)> = sig
594 .iter()
595 .skip(1)
596 .filter_map(|p| {
597 if head == "define-typed" && p.as_list().is_some() {
598 let n = p.as_list()?.first()?;
599 Some((n.as_symbol()?.to_string(), n.span))
600 } else {
601 param(p)
602 }
603 })
604 .collect();
605 let body_from = if head == "define-typed" { 3 } else { 2 };
607 self.body(¶ms, items.get(body_from..).unwrap_or(&[]), opaque);
608 }
609
610 fn walk_special(&mut self, head: &str, items: &[Spanned], opaque: bool) {
611 let args = &items[1..];
612 match head {
613 "quote" | "provide" | "require" => {}
614 "quasiquote" => {
615 for a in args {
616 self.walk_template(a, opaque);
617 }
618 }
619 "lambda" => {
620 let params = args.first().map(params_of).unwrap_or_default();
621 self.body(¶ms, args.get(1..).unwrap_or(&[]), opaque);
622 }
623 "cond" => {
624 for clause in args {
625 match clause.as_list() {
626 Some(parts) => {
627 let rest = match parts.first().and_then(Spanned::as_symbol) {
628 Some("else") => &parts[1..],
629 _ => parts,
630 };
631 for p in rest {
632 if p.as_symbol() != Some("=>") {
633 self.walk(p, opaque);
634 }
635 }
636 }
637 None => self.walk(clause, opaque),
638 }
639 }
640 }
641 "let" | "let*" | "letrec" => self.walk_let(head, args, opaque),
642 "set!" => match args.split_first() {
643 Some((target, rest)) if target.as_symbol().is_some() => {
644 self.pass
645 .place(target, target.as_symbol().expect("matched"), opaque);
646 self.walk_all(rest, opaque);
647 }
648 _ => self.walk_all(args, opaque),
649 },
650 "try" => {
651 for a in args {
652 let clause = a.as_list();
653 match clause.and_then(|c| c.first()).and_then(Spanned::as_symbol) {
654 Some("catch") => {
655 let c = clause.expect("matched");
656 let var = c.get(1).and_then(|v| {
657 v.as_list()
658 .and_then(|l| l.first())
659 .and_then(|s| s.as_symbol().map(|n| (n.to_string(), s.span)))
660 .or_else(|| v.as_symbol().map(|n| (n.to_string(), v.span)))
661 });
662 self.open();
663 if let Some((n, s)) = var {
664 self.pass.bind(&n, s, BinderKind::Catch);
665 }
666 self.walk_all(c.get(2..).unwrap_or(&[]), opaque);
667 self.close();
668 }
669 Some("finally") => {
670 let c = clause.expect("matched");
671 self.walk_all(&c[1..], opaque);
672 }
673 _ => self.walk(a, opaque),
674 }
675 }
676 }
677 _ => self.walk_all(args, opaque),
680 }
681 }
682
683 fn walk_let(&mut self, head: &str, args: &[Spanned], opaque: bool) {
684 let (name, rest) = match args.first() {
686 Some(n) if n.as_symbol().is_some() && head == "let" => (Some(n), &args[1..]),
687 _ => (None, args),
688 };
689 let Some(bindings) = rest.first().and_then(Spanned::as_list) else {
690 self.walk_all(rest, opaque);
691 return;
692 };
693 let pairs: Vec<(String, Span, Option<&Spanned>)> = bindings
694 .iter()
695 .filter_map(|b| match b.as_list() {
696 Some(pair) => {
697 let n = pair.first()?;
698 Some((n.as_symbol()?.to_string(), n.span, pair.get(1)))
699 }
700 None => b.as_symbol().map(|n| (n.to_string(), b.span, None)),
701 })
702 .collect();
703 let body = rest.get(1..).unwrap_or(&[]);
704 if head == "let" {
705 for (_, _, init) in &pairs {
707 if let Some(e) = init {
708 self.walk(e, opaque);
709 }
710 }
711 }
712 self.open();
713 match head {
714 "let" => {
715 if let Some(n) = name {
716 self.pass.bind(
717 n.as_symbol().expect("matched"),
718 n.span,
719 BinderKind::Function,
720 );
721 }
722 for (n, s, _) in &pairs {
723 self.pass.bind(n, *s, BinderKind::Let);
724 }
725 }
726 "let*" => {
727 for (n, s, init) in &pairs {
728 if let Some(e) = init {
729 self.walk(e, opaque);
730 }
731 self.pass.bind(n, *s, BinderKind::Let);
732 }
733 }
734 _ => {
735 for (n, s, _) in &pairs {
736 self.pass.bind(n, *s, BinderKind::Let);
737 }
738 for (_, _, init) in &pairs {
739 if let Some(e) = init {
740 self.walk(e, opaque);
741 }
742 }
743 }
744 }
745 self.hoist(body);
746 self.walk_all(body, opaque);
747 self.close();
748 }
749}