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 head(&mut self, _node: &Spanned, _name: &str, _kind: HeadKind) {}
242 fn template_symbol(&mut self, _node: &Spanned, _name: &str) {}
245}
246
247pub fn walk_program(forms: &[Spanned], pass: &mut dyn Scopes) {
251 for form in forms {
252 walk_top(form, pass);
253 }
254}
255
256pub fn walk_top(form: &Spanned, pass: &mut dyn Scopes) {
258 Walker { pass, depth: 0 }.walk(form, false);
259}
260
261#[must_use]
271pub fn definitions_of(form: &Spanned) -> Vec<(String, Span, HeadKind)> {
272 let mut out = Vec::new();
273 definition_nodes(form, &mut |node, kind| {
274 if let Some(n) = node.as_symbol() {
275 out.push((n.to_string(), node.span, kind));
276 }
277 });
278 out
279}
280
281pub fn definition_nodes<'a>(form: &'a Spanned, out: &mut dyn FnMut(&'a Spanned, HeadKind)) {
284 let Some(items) = form.as_list() else { return };
285 let head = items.first().and_then(Spanned::as_symbol);
286 match head {
287 Some("defmacro") => {
288 if let Some(n) = items.get(1).filter(|n| n.as_symbol().is_some()) {
289 out(n, HeadKind::Macro);
290 }
291 }
292 Some("define" | "define-typed") => {
293 if let Some(n) = defined_name_node(items) {
294 out(n, HeadKind::Value);
295 }
296 if items.get(1).is_some_and(|t| t.as_symbol().is_some()) {
299 for item in &items[2..] {
300 definition_nodes(item, out);
301 }
302 }
303 }
304 Some("lambda" | "let" | "let*" | "letrec" | "deftest" | "quote" | "quasiquote") => {}
305 Some("try") => {
306 for item in &items[1..] {
308 let is_clause = item
309 .as_list()
310 .and_then(|c| c.first())
311 .and_then(Spanned::as_symbol)
312 .is_some_and(|h| h == "catch" || h == "finally");
313 if !is_clause {
314 definition_nodes(item, out);
315 }
316 }
317 }
318 _ => {
319 for item in items {
320 definition_nodes(item, out);
321 }
322 }
323 }
324}
325
326#[must_use]
328pub fn defined_name_node(items: &[Spanned]) -> Option<&Spanned> {
329 let head = items.first()?.as_symbol()?;
330 if head != "define" && head != "define-typed" {
331 return None;
332 }
333 let target = items.get(1)?;
334 if target.as_symbol().is_some() {
335 return Some(target);
336 }
337 let name = target.as_list()?.first()?;
338 name.as_symbol().map(|_| name)
339}
340
341#[must_use]
344pub fn params_of(list: &Spanned) -> Vec<(String, Span)> {
345 if let Some(n) = list.as_symbol() {
346 return vec![(n.to_string(), list.span)];
347 }
348 list.as_list()
349 .map(|ps| ps.iter().filter_map(param).collect())
350 .unwrap_or_default()
351}
352
353fn param(p: &Spanned) -> Option<(String, Span)> {
354 let n = p.as_symbol()?;
355 if n.starts_with('&') || n == "." {
356 return None;
357 }
358 Some((n.to_string(), p.span))
359}
360
361struct Walker<'p> {
362 pass: &'p mut dyn Scopes,
363 depth: usize,
365}
366
367impl Walker<'_> {
368 fn open(&mut self) {
369 self.depth += 1;
370 self.pass.open();
371 }
372
373 fn close(&mut self) {
374 self.depth -= 1;
375 self.pass.close();
376 }
377
378 fn hoist(&mut self, body: &[Spanned]) {
382 for f in body {
383 let function = f.as_list().is_some_and(|items| {
384 items.first().and_then(Spanned::as_symbol) == Some("define")
385 && items.get(1).is_some_and(|t| t.as_list().is_some())
386 });
387 let pass = &mut *self.pass;
388 definition_nodes(f, &mut |node, kind| {
389 let Some(name) = node.as_symbol() else { return };
390 let bk = match kind {
391 HeadKind::Value if !function => BinderKind::Assignment,
392 _ => BinderKind::Function,
393 };
394 pass.bind(name, node.span, bk);
395 });
396 }
397 }
398
399 fn body(&mut self, params: &[(String, Span)], forms: &[Spanned], opaque: bool) {
402 self.open();
403 for (p, s) in params {
404 self.pass.bind(p, *s, BinderKind::Parameter);
405 }
406 self.hoist(forms);
407 for f in forms {
408 self.walk(f, opaque);
409 }
410 self.close();
411 }
412
413 fn walk(&mut self, form: &Spanned, opaque: bool) {
414 match &form.form {
415 SpannedForm::Atom(Atom::Symbol(n)) => self.pass.reference(form, n, opaque),
416 SpannedForm::Quote(_) => {}
417 SpannedForm::Quasiquote(inner) => self.walk_template(inner, opaque),
418 SpannedForm::Unquote(inner) | SpannedForm::UnquoteSplice(inner) => {
419 self.walk(inner, opaque);
420 }
421 SpannedForm::List(items) if !items.is_empty() => self.walk_list(items, opaque),
422 _ => {}
423 }
424 }
425
426 fn walk_template(&mut self, form: &Spanned, opaque: bool) {
428 match &form.form {
429 SpannedForm::Atom(Atom::Symbol(n)) => self.pass.template_symbol(form, n),
430 SpannedForm::Unquote(inner) | SpannedForm::UnquoteSplice(inner) => {
431 self.walk(inner, opaque);
432 }
433 SpannedForm::List(items) => {
434 for i in items {
435 self.walk_template(i, opaque);
436 }
437 }
438 SpannedForm::Quasiquote(inner) => self.walk_template(inner, opaque),
439 _ => {}
440 }
441 }
442
443 fn walk_all(&mut self, items: &[Spanned], opaque: bool) {
444 for i in items {
445 self.walk(i, opaque);
446 }
447 }
448
449 fn walk_list(&mut self, items: &[Spanned], opaque: bool) {
450 let Some(head) = items[0].as_symbol() else {
451 self.walk_all(items, opaque);
452 return;
453 };
454 if head == "deftest" && self.depth == 0 {
457 if let Some(body) = items.get(2) {
458 self.body(&[], std::slice::from_ref(body), opaque);
459 }
460 return;
461 }
462 if (head == "use" || head == LEGACY_NAMES) && self.depth == 0 {
465 self.pass.head(&items[0], head, HeadKind::SpecialForm);
466 return;
467 }
468 if head == "define" || head == "define-typed" {
471 self.pass.head(&items[0], head, HeadKind::SpecialForm);
472 self.walk_define(head, items, opaque);
473 return;
474 }
475 if head == "defmacro" && self.depth == 0 {
479 self.pass.head(&items[0], head, HeadKind::SpecialForm);
480 let params = items.get(2).map(params_of).unwrap_or_default();
481 self.body(¶ms, items.get(3..).unwrap_or(&[]), opaque);
482 return;
483 }
484 match self.pass.head_kind(head) {
485 Some(HeadKind::SpecialForm) => {
486 self.pass.head(&items[0], head, HeadKind::SpecialForm);
487 self.walk_special(head, items, opaque);
488 }
489 Some(HeadKind::Macro) => {
490 self.pass.head(&items[0], head, HeadKind::Macro);
492 for i in &items[1..] {
493 self.walk(i, true);
494 }
495 }
496 Some(HeadKind::Value) | None => {
497 self.walk(&items[0], opaque);
498 self.walk_all(&items[1..], opaque);
499 }
500 }
501 }
502
503 fn walk_define(&mut self, head: &str, items: &[Spanned], opaque: bool) {
504 let Some(target) = items.get(1) else { return };
505 if target.as_symbol().is_some() {
506 self.walk_all(&items[2..], opaque);
508 return;
509 }
510 let Some(sig) = target.as_list() else {
511 self.walk_all(&items[1..], opaque);
512 return;
513 };
514 let params: Vec<(String, Span)> = sig
515 .iter()
516 .skip(1)
517 .filter_map(|p| {
518 if head == "define-typed" {
519 let pair = p.as_list()?;
520 let n = pair.first()?;
521 Some((n.as_symbol()?.to_string(), n.span))
522 } else {
523 param(p)
524 }
525 })
526 .collect();
527 let body_from = if head == "define-typed" { 3 } else { 2 };
529 self.body(¶ms, items.get(body_from..).unwrap_or(&[]), opaque);
530 }
531
532 fn walk_special(&mut self, head: &str, items: &[Spanned], opaque: bool) {
533 let args = &items[1..];
534 match head {
535 "quote" | "provide" | "require" => {}
536 "quasiquote" => {
537 for a in args {
538 self.walk_template(a, opaque);
539 }
540 }
541 "lambda" => {
542 let params = args.first().map(params_of).unwrap_or_default();
543 self.body(¶ms, args.get(1..).unwrap_or(&[]), opaque);
544 }
545 "cond" => {
546 for clause in args {
547 match clause.as_list() {
548 Some(parts) => {
549 let rest = match parts.first().and_then(Spanned::as_symbol) {
550 Some("else") => &parts[1..],
551 _ => parts,
552 };
553 for p in rest {
554 if p.as_symbol() != Some("=>") {
555 self.walk(p, opaque);
556 }
557 }
558 }
559 None => self.walk(clause, opaque),
560 }
561 }
562 }
563 "let" | "let*" | "letrec" => self.walk_let(head, args, opaque),
564 "try" => {
565 for a in args {
566 let clause = a.as_list();
567 match clause.and_then(|c| c.first()).and_then(Spanned::as_symbol) {
568 Some("catch") => {
569 let c = clause.expect("matched");
570 let var = c.get(1).and_then(|v| {
571 v.as_list()
572 .and_then(|l| l.first())
573 .and_then(|s| s.as_symbol().map(|n| (n.to_string(), s.span)))
574 .or_else(|| v.as_symbol().map(|n| (n.to_string(), v.span)))
575 });
576 self.open();
577 if let Some((n, s)) = var {
578 self.pass.bind(&n, s, BinderKind::Catch);
579 }
580 self.walk_all(c.get(2..).unwrap_or(&[]), opaque);
581 self.close();
582 }
583 Some("finally") => {
584 let c = clause.expect("matched");
585 self.walk_all(&c[1..], opaque);
586 }
587 _ => self.walk(a, opaque),
588 }
589 }
590 }
591 _ => self.walk_all(args, opaque),
594 }
595 }
596
597 fn walk_let(&mut self, head: &str, args: &[Spanned], opaque: bool) {
598 let (name, rest) = match args.first() {
600 Some(n) if n.as_symbol().is_some() && head == "let" => (Some(n), &args[1..]),
601 _ => (None, args),
602 };
603 let Some(bindings) = rest.first().and_then(Spanned::as_list) else {
604 self.walk_all(rest, opaque);
605 return;
606 };
607 let pairs: Vec<(String, Span, Option<&Spanned>)> = bindings
608 .iter()
609 .filter_map(|b| match b.as_list() {
610 Some(pair) => {
611 let n = pair.first()?;
612 Some((n.as_symbol()?.to_string(), n.span, pair.get(1)))
613 }
614 None => b.as_symbol().map(|n| (n.to_string(), b.span, None)),
615 })
616 .collect();
617 let body = rest.get(1..).unwrap_or(&[]);
618 if head == "let" {
619 for (_, _, init) in &pairs {
621 if let Some(e) = init {
622 self.walk(e, opaque);
623 }
624 }
625 }
626 self.open();
627 match head {
628 "let" => {
629 if let Some(n) = name {
630 self.pass.bind(
631 n.as_symbol().expect("matched"),
632 n.span,
633 BinderKind::Function,
634 );
635 }
636 for (n, s, _) in &pairs {
637 self.pass.bind(n, *s, BinderKind::Let);
638 }
639 }
640 "let*" => {
641 for (n, s, init) in &pairs {
642 if let Some(e) = init {
643 self.walk(e, opaque);
644 }
645 self.pass.bind(n, *s, BinderKind::Let);
646 }
647 }
648 _ => {
649 for (n, s, _) in &pairs {
650 self.pass.bind(n, *s, BinderKind::Let);
651 }
652 for (_, _, init) in &pairs {
653 if let Some(e) = init {
654 self.walk(e, opaque);
655 }
656 }
657 }
658 }
659 self.hoist(body);
660 self.walk_all(body, opaque);
661 self.close();
662 }
663}