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)]
132pub struct Legacy {
133 pub since: String,
134 pub kind: LegacyKind,
135 pub span: Span,
136}
137
138#[derive(Clone, Debug, PartialEq)]
140pub enum LegacyKind {
141 Prefix(String),
143 Moved(Vec<Moved>),
145}
146
147#[derive(Clone, Debug, PartialEq, Eq)]
150pub struct Moved {
151 pub old: String,
152 pub home: String,
153 pub new: String,
154}
155
156#[must_use]
160pub fn legacy_target(form: &Spanned) -> Option<Legacy> {
161 let items = form.as_list()?;
162 let [head, since, what] = items else {
163 return None;
164 };
165 if head.as_symbol() != Some(LEGACY_NAMES) {
166 return None;
167 }
168 let SpannedForm::Atom(Atom::Str(since)) = &since.form else {
169 return None;
170 };
171 let str_of = |s: &Spanned| match &s.form {
172 SpannedForm::Atom(Atom::Str(x)) => Some(x.clone()),
173 _ => None,
174 };
175 let kind = if let Some(prefix) = str_of(what) {
176 LegacyKind::Prefix(prefix)
177 } else {
178 let moves = list_items(what)?
179 .iter()
180 .map(|pair| {
181 let [old, target] = list_items(pair)? else {
182 return None;
183 };
184 let (old, target) = (str_of(old)?, str_of(target)?);
185 let (home, new) = target
186 .split_once("::")
187 .map_or((String::new(), target.clone()), |(h, n)| {
188 (h.to_string(), n.to_string())
189 });
190 Some(Moved { old, home, new })
191 })
192 .collect::<Option<Vec<_>>>()?;
193 LegacyKind::Moved(moves)
194 };
195 Some(Legacy {
196 since: since.clone(),
197 kind,
198 span: form.span,
199 })
200}
201
202fn list_items(form: &Spanned) -> Option<&[Spanned]> {
204 let items = form.as_list()?;
205 (items.first()?.as_symbol() == Some("list")).then(|| &items[1..])
206}
207
208pub const SHAPED_FORMS: &[&str] = &[
213 "lambda",
214 "let",
215 "let*",
216 "letrec",
217 "try",
218 "cond",
219 "quote",
220 "quasiquote",
221 "provide",
222 "require",
223];
224
225pub trait Scopes {
227 fn head_kind(&self, name: &str) -> Option<HeadKind>;
230 fn open(&mut self);
232 fn bind(&mut self, name: &str, span: Span, kind: BinderKind);
234 fn close(&mut self);
236 fn reference(&mut self, node: &Spanned, name: &str, opaque: bool);
239 fn call(&mut self, node: &Spanned, _whole: &Spanned, name: &str, opaque: bool) {
240 self.reference(node, name, opaque);
241 }
242 fn place(&mut self, node: &Spanned, name: &str, opaque: bool) {
243 self.reference(node, name, opaque);
244 }
245 fn head(&mut self, _node: &Spanned, _name: &str, _kind: HeadKind) {}
248 fn template_symbol(&mut self, _node: &Spanned, _name: &str) {}
251}
252
253pub fn walk_program(forms: &[Spanned], pass: &mut dyn Scopes) {
257 for form in forms {
258 walk_top(form, pass);
259 }
260}
261
262pub fn walk_top(form: &Spanned, pass: &mut dyn Scopes) {
264 Walker { pass, depth: 0 }.walk(form, false);
265}
266
267#[must_use]
277pub fn definitions_of(form: &Spanned) -> Vec<(String, Span, HeadKind)> {
278 let mut out = Vec::new();
279 definition_nodes(form, &mut |node, kind| {
280 if let Some(n) = node.as_symbol() {
281 out.push((n.to_string(), node.span, kind));
282 }
283 });
284 out
285}
286
287pub fn definition_nodes<'a>(form: &'a Spanned, out: &mut dyn FnMut(&'a Spanned, HeadKind)) {
290 let Some(items) = form.as_list() else { return };
291 let head = items.first().and_then(Spanned::as_symbol);
292 match head {
293 Some("defmacro") => {
294 if let Some(n) = items.get(1).filter(|n| n.as_symbol().is_some()) {
295 out(n, HeadKind::Macro);
296 }
297 }
298 Some("define" | "define-typed") => {
299 if let Some(n) = defined_name_node(items) {
300 out(n, HeadKind::Value);
301 }
302 if items.get(1).is_some_and(|t| t.as_symbol().is_some()) {
305 for item in &items[2..] {
306 definition_nodes(item, out);
307 }
308 }
309 }
310 Some("lambda" | "let" | "let*" | "letrec" | "deftest" | "quote" | "quasiquote") => {}
311 Some("try") => {
312 for item in &items[1..] {
314 let is_clause = item
315 .as_list()
316 .and_then(|c| c.first())
317 .and_then(Spanned::as_symbol)
318 .is_some_and(|h| h == "catch" || h == "finally");
319 if !is_clause {
320 definition_nodes(item, out);
321 }
322 }
323 }
324 _ => {
325 for item in items {
326 definition_nodes(item, out);
327 }
328 }
329 }
330}
331
332#[must_use]
333pub fn nullary_definition(form: &Spanned) -> Option<&str> {
334 let items = form.as_list()?;
335 let head = items.first()?.as_symbol()?;
336 if head != "define" && head != "define-typed" {
337 return None;
338 }
339 match items.get(1)?.as_list()? {
340 [name] => name.as_symbol(),
341 _ => None,
342 }
343}
344
345#[must_use]
347pub fn defined_name_node(items: &[Spanned]) -> Option<&Spanned> {
348 let head = items.first()?.as_symbol()?;
349 if head != "define" && head != "define-typed" {
350 return None;
351 }
352 let target = items.get(1)?;
353 if target.as_symbol().is_some() {
354 return Some(target);
355 }
356 let name = target.as_list()?.first()?;
357 name.as_symbol().map(|_| name)
358}
359
360#[must_use]
363pub fn params_of(list: &Spanned) -> Vec<(String, Span)> {
364 if let Some(n) = list.as_symbol() {
365 return vec![(n.to_string(), list.span)];
366 }
367 list.as_list()
368 .map(|ps| ps.iter().filter_map(param).collect())
369 .unwrap_or_default()
370}
371
372fn param(p: &Spanned) -> Option<(String, Span)> {
373 let n = p.as_symbol()?;
374 if n.starts_with('&') || n == "." {
375 return None;
376 }
377 Some((n.to_string(), p.span))
378}
379
380struct Walker<'p> {
381 pass: &'p mut dyn Scopes,
382 depth: usize,
384}
385
386impl Walker<'_> {
387 fn open(&mut self) {
388 self.depth += 1;
389 self.pass.open();
390 }
391
392 fn close(&mut self) {
393 self.depth -= 1;
394 self.pass.close();
395 }
396
397 fn hoist(&mut self, body: &[Spanned]) {
401 for f in body {
402 let function = f.as_list().is_some_and(|items| {
403 items.first().and_then(Spanned::as_symbol) == Some("define")
404 && items.get(1).is_some_and(|t| t.as_list().is_some())
405 });
406 let pass = &mut *self.pass;
407 definition_nodes(f, &mut |node, kind| {
408 let Some(name) = node.as_symbol() else { return };
409 let bk = match kind {
410 HeadKind::Value if !function => BinderKind::Assignment,
411 _ => BinderKind::Function,
412 };
413 pass.bind(name, node.span, bk);
414 });
415 }
416 }
417
418 fn body(&mut self, params: &[(String, Span)], forms: &[Spanned], opaque: bool) {
421 self.open();
422 for (p, s) in params {
423 self.pass.bind(p, *s, BinderKind::Parameter);
424 }
425 self.hoist(forms);
426 for f in forms {
427 self.walk(f, opaque);
428 }
429 self.close();
430 }
431
432 fn walk(&mut self, form: &Spanned, opaque: bool) {
433 match &form.form {
434 SpannedForm::Atom(Atom::Symbol(n)) => self.pass.reference(form, n, opaque),
435 SpannedForm::Quote(_) => {}
436 SpannedForm::Quasiquote(inner) => self.walk_template(inner, opaque),
437 SpannedForm::Unquote(inner) | SpannedForm::UnquoteSplice(inner) => {
438 self.walk(inner, opaque);
439 }
440 SpannedForm::List(items) if !items.is_empty() => self.walk_list(form, items, opaque),
441 _ => {}
442 }
443 }
444
445 fn walk_template(&mut self, form: &Spanned, opaque: bool) {
447 match &form.form {
448 SpannedForm::Atom(Atom::Symbol(n)) => self.pass.template_symbol(form, n),
449 SpannedForm::Unquote(inner) | SpannedForm::UnquoteSplice(inner) => {
450 self.walk(inner, opaque);
451 }
452 SpannedForm::List(items) => {
453 for i in items {
454 self.walk_template(i, opaque);
455 }
456 }
457 SpannedForm::Quasiquote(inner) => self.walk_template(inner, opaque),
458 _ => {}
459 }
460 }
461
462 fn walk_all(&mut self, items: &[Spanned], opaque: bool) {
463 for i in items {
464 self.walk(i, opaque);
465 }
466 }
467
468 fn walk_list(&mut self, form: &Spanned, items: &[Spanned], opaque: bool) {
469 let Some(head) = items[0].as_symbol() else {
470 self.walk_all(items, opaque);
471 return;
472 };
473 if head == "deftest" && self.depth == 0 {
476 if let Some(body) = items.get(2) {
477 self.body(&[], std::slice::from_ref(body), opaque);
478 }
479 return;
480 }
481 if (head == "use" || head == LEGACY_NAMES) && self.depth == 0 {
484 self.pass.head(&items[0], head, HeadKind::SpecialForm);
485 return;
486 }
487 if head == "define" || head == "define-typed" {
490 self.pass.head(&items[0], head, HeadKind::SpecialForm);
491 self.walk_define(head, items, opaque);
492 return;
493 }
494 if head == "defmacro" && self.depth == 0 {
498 self.pass.head(&items[0], head, HeadKind::SpecialForm);
499 let params = items.get(2).map(params_of).unwrap_or_default();
500 self.body(¶ms, items.get(3..).unwrap_or(&[]), opaque);
501 return;
502 }
503 match self.pass.head_kind(head) {
504 Some(HeadKind::SpecialForm) => {
505 self.pass.head(&items[0], head, HeadKind::SpecialForm);
506 self.walk_special(head, items, opaque);
507 }
508 Some(HeadKind::Macro) => {
509 self.pass.head(&items[0], head, HeadKind::Macro);
511 for i in &items[1..] {
512 self.walk(i, true);
513 }
514 }
515 Some(HeadKind::Value) | None => {
516 self.pass.call(&items[0], form, head, opaque);
517 self.walk_all(&items[1..], opaque);
518 }
519 }
520 }
521
522 fn walk_define(&mut self, head: &str, items: &[Spanned], opaque: bool) {
523 let Some(target) = items.get(1) else { return };
524 if target.as_symbol().is_some() {
525 self.walk_all(&items[2..], opaque);
527 return;
528 }
529 let Some(sig) = target.as_list() else {
530 self.walk_all(&items[1..], opaque);
531 return;
532 };
533 let params: Vec<(String, Span)> = sig
534 .iter()
535 .skip(1)
536 .filter_map(|p| {
537 if head == "define-typed" {
538 let pair = p.as_list()?;
539 let n = pair.first()?;
540 Some((n.as_symbol()?.to_string(), n.span))
541 } else {
542 param(p)
543 }
544 })
545 .collect();
546 let body_from = if head == "define-typed" { 3 } else { 2 };
548 self.body(¶ms, items.get(body_from..).unwrap_or(&[]), opaque);
549 }
550
551 fn walk_special(&mut self, head: &str, items: &[Spanned], opaque: bool) {
552 let args = &items[1..];
553 match head {
554 "quote" | "provide" | "require" => {}
555 "quasiquote" => {
556 for a in args {
557 self.walk_template(a, opaque);
558 }
559 }
560 "lambda" => {
561 let params = args.first().map(params_of).unwrap_or_default();
562 self.body(¶ms, args.get(1..).unwrap_or(&[]), opaque);
563 }
564 "cond" => {
565 for clause in args {
566 match clause.as_list() {
567 Some(parts) => {
568 let rest = match parts.first().and_then(Spanned::as_symbol) {
569 Some("else") => &parts[1..],
570 _ => parts,
571 };
572 for p in rest {
573 if p.as_symbol() != Some("=>") {
574 self.walk(p, opaque);
575 }
576 }
577 }
578 None => self.walk(clause, opaque),
579 }
580 }
581 }
582 "let" | "let*" | "letrec" => self.walk_let(head, args, opaque),
583 "set!" => match args.split_first() {
584 Some((target, rest)) if target.as_symbol().is_some() => {
585 self.pass
586 .place(target, target.as_symbol().expect("matched"), opaque);
587 self.walk_all(rest, opaque);
588 }
589 _ => self.walk_all(args, opaque),
590 },
591 "try" => {
592 for a in args {
593 let clause = a.as_list();
594 match clause.and_then(|c| c.first()).and_then(Spanned::as_symbol) {
595 Some("catch") => {
596 let c = clause.expect("matched");
597 let var = c.get(1).and_then(|v| {
598 v.as_list()
599 .and_then(|l| l.first())
600 .and_then(|s| s.as_symbol().map(|n| (n.to_string(), s.span)))
601 .or_else(|| v.as_symbol().map(|n| (n.to_string(), v.span)))
602 });
603 self.open();
604 if let Some((n, s)) = var {
605 self.pass.bind(&n, s, BinderKind::Catch);
606 }
607 self.walk_all(c.get(2..).unwrap_or(&[]), opaque);
608 self.close();
609 }
610 Some("finally") => {
611 let c = clause.expect("matched");
612 self.walk_all(&c[1..], opaque);
613 }
614 _ => self.walk(a, opaque),
615 }
616 }
617 }
618 _ => self.walk_all(args, opaque),
621 }
622 }
623
624 fn walk_let(&mut self, head: &str, args: &[Spanned], opaque: bool) {
625 let (name, rest) = match args.first() {
627 Some(n) if n.as_symbol().is_some() && head == "let" => (Some(n), &args[1..]),
628 _ => (None, args),
629 };
630 let Some(bindings) = rest.first().and_then(Spanned::as_list) else {
631 self.walk_all(rest, opaque);
632 return;
633 };
634 let pairs: Vec<(String, Span, Option<&Spanned>)> = bindings
635 .iter()
636 .filter_map(|b| match b.as_list() {
637 Some(pair) => {
638 let n = pair.first()?;
639 Some((n.as_symbol()?.to_string(), n.span, pair.get(1)))
640 }
641 None => b.as_symbol().map(|n| (n.to_string(), b.span, None)),
642 })
643 .collect();
644 let body = rest.get(1..).unwrap_or(&[]);
645 if head == "let" {
646 for (_, _, init) in &pairs {
648 if let Some(e) = init {
649 self.walk(e, opaque);
650 }
651 }
652 }
653 self.open();
654 match head {
655 "let" => {
656 if let Some(n) = name {
657 self.pass.bind(
658 n.as_symbol().expect("matched"),
659 n.span,
660 BinderKind::Function,
661 );
662 }
663 for (n, s, _) in &pairs {
664 self.pass.bind(n, *s, BinderKind::Let);
665 }
666 }
667 "let*" => {
668 for (n, s, init) in &pairs {
669 if let Some(e) = init {
670 self.walk(e, opaque);
671 }
672 self.pass.bind(n, *s, BinderKind::Let);
673 }
674 }
675 _ => {
676 for (n, s, _) in &pairs {
677 self.pass.bind(n, *s, BinderKind::Let);
678 }
679 for (_, _, init) in &pairs {
680 if let Some(e) = init {
681 self.walk(e, opaque);
682 }
683 }
684 }
685 }
686 self.hoist(body);
687 self.walk_all(body, opaque);
688 self.close();
689 }
690}