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)]
128pub struct Legacy {
129 pub since: String,
130 pub prefix: String,
131 pub span: Span,
132}
133
134#[must_use]
137pub fn legacy_target(form: &Spanned) -> Option<Legacy> {
138 let items = form.as_list()?;
139 let [head, since, prefix] = items else {
140 return None;
141 };
142 if head.as_symbol() != Some(LEGACY_NAMES) {
143 return None;
144 }
145 let (SpannedForm::Atom(Atom::Str(since)), SpannedForm::Atom(Atom::Str(prefix))) =
146 (&since.form, &prefix.form)
147 else {
148 return None;
149 };
150 Some(Legacy {
151 since: since.clone(),
152 prefix: prefix.clone(),
153 span: form.span,
154 })
155}
156
157pub const SHAPED_FORMS: &[&str] = &[
162 "lambda",
163 "let",
164 "let*",
165 "letrec",
166 "try",
167 "cond",
168 "quote",
169 "quasiquote",
170 "provide",
171 "require",
172];
173
174pub trait Scopes {
176 fn head_kind(&self, name: &str) -> Option<HeadKind>;
179 fn open(&mut self);
181 fn bind(&mut self, name: &str, span: Span, kind: BinderKind);
183 fn close(&mut self);
185 fn reference(&mut self, node: &Spanned, name: &str, opaque: bool);
188 fn head(&mut self, _node: &Spanned, _name: &str, _kind: HeadKind) {}
191 fn template_symbol(&mut self, _node: &Spanned, _name: &str) {}
194}
195
196pub fn walk_program(forms: &[Spanned], pass: &mut dyn Scopes) {
200 for form in forms {
201 walk_top(form, pass);
202 }
203}
204
205pub fn walk_top(form: &Spanned, pass: &mut dyn Scopes) {
207 Walker { pass, depth: 0 }.walk(form, false);
208}
209
210#[must_use]
220pub fn definitions_of(form: &Spanned) -> Vec<(String, Span, HeadKind)> {
221 let mut out = Vec::new();
222 definition_nodes(form, &mut |node, kind| {
223 if let Some(n) = node.as_symbol() {
224 out.push((n.to_string(), node.span, kind));
225 }
226 });
227 out
228}
229
230pub fn definition_nodes<'a>(form: &'a Spanned, out: &mut dyn FnMut(&'a Spanned, HeadKind)) {
233 let Some(items) = form.as_list() else { return };
234 let head = items.first().and_then(Spanned::as_symbol);
235 match head {
236 Some("defmacro") => {
237 if let Some(n) = items.get(1).filter(|n| n.as_symbol().is_some()) {
238 out(n, HeadKind::Macro);
239 }
240 }
241 Some("define" | "define-typed") => {
242 if let Some(n) = defined_name_node(items) {
243 out(n, HeadKind::Value);
244 }
245 if items.get(1).is_some_and(|t| t.as_symbol().is_some()) {
248 for item in &items[2..] {
249 definition_nodes(item, out);
250 }
251 }
252 }
253 Some("lambda" | "let" | "let*" | "letrec" | "deftest" | "quote" | "quasiquote") => {}
254 Some("try") => {
255 for item in &items[1..] {
257 let is_clause = item
258 .as_list()
259 .and_then(|c| c.first())
260 .and_then(Spanned::as_symbol)
261 .is_some_and(|h| h == "catch" || h == "finally");
262 if !is_clause {
263 definition_nodes(item, out);
264 }
265 }
266 }
267 _ => {
268 for item in items {
269 definition_nodes(item, out);
270 }
271 }
272 }
273}
274
275#[must_use]
277pub fn defined_name_node(items: &[Spanned]) -> Option<&Spanned> {
278 let head = items.first()?.as_symbol()?;
279 if head != "define" && head != "define-typed" {
280 return None;
281 }
282 let target = items.get(1)?;
283 if target.as_symbol().is_some() {
284 return Some(target);
285 }
286 let name = target.as_list()?.first()?;
287 name.as_symbol().map(|_| name)
288}
289
290#[must_use]
293pub fn params_of(list: &Spanned) -> Vec<(String, Span)> {
294 if let Some(n) = list.as_symbol() {
295 return vec![(n.to_string(), list.span)];
296 }
297 list.as_list()
298 .map(|ps| ps.iter().filter_map(param).collect())
299 .unwrap_or_default()
300}
301
302fn param(p: &Spanned) -> Option<(String, Span)> {
303 let n = p.as_symbol()?;
304 if n.starts_with('&') || n == "." {
305 return None;
306 }
307 Some((n.to_string(), p.span))
308}
309
310struct Walker<'p> {
311 pass: &'p mut dyn Scopes,
312 depth: usize,
314}
315
316impl Walker<'_> {
317 fn open(&mut self) {
318 self.depth += 1;
319 self.pass.open();
320 }
321
322 fn close(&mut self) {
323 self.depth -= 1;
324 self.pass.close();
325 }
326
327 fn hoist(&mut self, body: &[Spanned]) {
331 for f in body {
332 let function = f.as_list().is_some_and(|items| {
333 items.first().and_then(Spanned::as_symbol) == Some("define")
334 && items.get(1).is_some_and(|t| t.as_list().is_some())
335 });
336 let pass = &mut *self.pass;
337 definition_nodes(f, &mut |node, kind| {
338 let Some(name) = node.as_symbol() else { return };
339 let bk = match kind {
340 HeadKind::Value if !function => BinderKind::Assignment,
341 _ => BinderKind::Function,
342 };
343 pass.bind(name, node.span, bk);
344 });
345 }
346 }
347
348 fn body(&mut self, params: &[(String, Span)], forms: &[Spanned], opaque: bool) {
351 self.open();
352 for (p, s) in params {
353 self.pass.bind(p, *s, BinderKind::Parameter);
354 }
355 self.hoist(forms);
356 for f in forms {
357 self.walk(f, opaque);
358 }
359 self.close();
360 }
361
362 fn walk(&mut self, form: &Spanned, opaque: bool) {
363 match &form.form {
364 SpannedForm::Atom(Atom::Symbol(n)) => self.pass.reference(form, n, opaque),
365 SpannedForm::Quote(_) => {}
366 SpannedForm::Quasiquote(inner) => self.walk_template(inner, opaque),
367 SpannedForm::Unquote(inner) | SpannedForm::UnquoteSplice(inner) => {
368 self.walk(inner, opaque);
369 }
370 SpannedForm::List(items) if !items.is_empty() => self.walk_list(items, opaque),
371 _ => {}
372 }
373 }
374
375 fn walk_template(&mut self, form: &Spanned, opaque: bool) {
377 match &form.form {
378 SpannedForm::Atom(Atom::Symbol(n)) => self.pass.template_symbol(form, n),
379 SpannedForm::Unquote(inner) | SpannedForm::UnquoteSplice(inner) => {
380 self.walk(inner, opaque);
381 }
382 SpannedForm::List(items) => {
383 for i in items {
384 self.walk_template(i, opaque);
385 }
386 }
387 SpannedForm::Quasiquote(inner) => self.walk_template(inner, opaque),
388 _ => {}
389 }
390 }
391
392 fn walk_all(&mut self, items: &[Spanned], opaque: bool) {
393 for i in items {
394 self.walk(i, opaque);
395 }
396 }
397
398 fn walk_list(&mut self, items: &[Spanned], opaque: bool) {
399 let Some(head) = items[0].as_symbol() else {
400 self.walk_all(items, opaque);
401 return;
402 };
403 if head == "deftest" && self.depth == 0 {
406 if let Some(body) = items.get(2) {
407 self.body(&[], std::slice::from_ref(body), opaque);
408 }
409 return;
410 }
411 if (head == "use" || head == LEGACY_NAMES) && self.depth == 0 {
414 self.pass.head(&items[0], head, HeadKind::SpecialForm);
415 return;
416 }
417 if head == "define" || head == "define-typed" {
420 self.pass.head(&items[0], head, HeadKind::SpecialForm);
421 self.walk_define(head, items, opaque);
422 return;
423 }
424 if head == "defmacro" && self.depth == 0 {
428 self.pass.head(&items[0], head, HeadKind::SpecialForm);
429 let params = items.get(2).map(params_of).unwrap_or_default();
430 self.body(¶ms, items.get(3..).unwrap_or(&[]), opaque);
431 return;
432 }
433 match self.pass.head_kind(head) {
434 Some(HeadKind::SpecialForm) => {
435 self.pass.head(&items[0], head, HeadKind::SpecialForm);
436 self.walk_special(head, items, opaque);
437 }
438 Some(HeadKind::Macro) => {
439 self.pass.head(&items[0], head, HeadKind::Macro);
441 for i in &items[1..] {
442 self.walk(i, true);
443 }
444 }
445 Some(HeadKind::Value) | None => {
446 self.walk(&items[0], opaque);
447 self.walk_all(&items[1..], opaque);
448 }
449 }
450 }
451
452 fn walk_define(&mut self, head: &str, items: &[Spanned], opaque: bool) {
453 let Some(target) = items.get(1) else { return };
454 if target.as_symbol().is_some() {
455 self.walk_all(&items[2..], opaque);
457 return;
458 }
459 let Some(sig) = target.as_list() else {
460 self.walk_all(&items[1..], opaque);
461 return;
462 };
463 let params: Vec<(String, Span)> = sig
464 .iter()
465 .skip(1)
466 .filter_map(|p| {
467 if head == "define-typed" {
468 let pair = p.as_list()?;
469 let n = pair.first()?;
470 Some((n.as_symbol()?.to_string(), n.span))
471 } else {
472 param(p)
473 }
474 })
475 .collect();
476 let body_from = if head == "define-typed" { 3 } else { 2 };
478 self.body(¶ms, items.get(body_from..).unwrap_or(&[]), opaque);
479 }
480
481 fn walk_special(&mut self, head: &str, items: &[Spanned], opaque: bool) {
482 let args = &items[1..];
483 match head {
484 "quote" | "provide" | "require" => {}
485 "quasiquote" => {
486 for a in args {
487 self.walk_template(a, opaque);
488 }
489 }
490 "lambda" => {
491 let params = args.first().map(params_of).unwrap_or_default();
492 self.body(¶ms, args.get(1..).unwrap_or(&[]), opaque);
493 }
494 "cond" => {
495 for clause in args {
496 match clause.as_list() {
497 Some(parts) => {
498 let rest = match parts.first().and_then(Spanned::as_symbol) {
499 Some("else") => &parts[1..],
500 _ => parts,
501 };
502 for p in rest {
503 if p.as_symbol() != Some("=>") {
504 self.walk(p, opaque);
505 }
506 }
507 }
508 None => self.walk(clause, opaque),
509 }
510 }
511 }
512 "let" | "let*" | "letrec" => self.walk_let(head, args, opaque),
513 "try" => {
514 for a in args {
515 let clause = a.as_list();
516 match clause.and_then(|c| c.first()).and_then(Spanned::as_symbol) {
517 Some("catch") => {
518 let c = clause.expect("matched");
519 let var = c.get(1).and_then(|v| {
520 v.as_list()
521 .and_then(|l| l.first())
522 .and_then(|s| s.as_symbol().map(|n| (n.to_string(), s.span)))
523 .or_else(|| v.as_symbol().map(|n| (n.to_string(), v.span)))
524 });
525 self.open();
526 if let Some((n, s)) = var {
527 self.pass.bind(&n, s, BinderKind::Catch);
528 }
529 self.walk_all(c.get(2..).unwrap_or(&[]), opaque);
530 self.close();
531 }
532 Some("finally") => {
533 let c = clause.expect("matched");
534 self.walk_all(&c[1..], opaque);
535 }
536 _ => self.walk(a, opaque),
537 }
538 }
539 }
540 _ => self.walk_all(args, opaque),
543 }
544 }
545
546 fn walk_let(&mut self, head: &str, args: &[Spanned], opaque: bool) {
547 let (name, rest) = match args.first() {
549 Some(n) if n.as_symbol().is_some() && head == "let" => (Some(n), &args[1..]),
550 _ => (None, args),
551 };
552 let Some(bindings) = rest.first().and_then(Spanned::as_list) else {
553 self.walk_all(rest, opaque);
554 return;
555 };
556 let pairs: Vec<(String, Span, Option<&Spanned>)> = bindings
557 .iter()
558 .filter_map(|b| match b.as_list() {
559 Some(pair) => {
560 let n = pair.first()?;
561 Some((n.as_symbol()?.to_string(), n.span, pair.get(1)))
562 }
563 None => b.as_symbol().map(|n| (n.to_string(), b.span, None)),
564 })
565 .collect();
566 let body = rest.get(1..).unwrap_or(&[]);
567 if head == "let" {
568 for (_, _, init) in &pairs {
570 if let Some(e) = init {
571 self.walk(e, opaque);
572 }
573 }
574 }
575 self.open();
576 match head {
577 "let" => {
578 if let Some(n) = name {
579 self.pass.bind(
580 n.as_symbol().expect("matched"),
581 n.span,
582 BinderKind::Function,
583 );
584 }
585 for (n, s, _) in &pairs {
586 self.pass.bind(n, *s, BinderKind::Let);
587 }
588 }
589 "let*" => {
590 for (n, s, init) in &pairs {
591 if let Some(e) = init {
592 self.walk(e, opaque);
593 }
594 self.pass.bind(n, *s, BinderKind::Let);
595 }
596 }
597 _ => {
598 for (n, s, _) in &pairs {
599 self.pass.bind(n, *s, BinderKind::Let);
600 }
601 for (_, _, init) in &pairs {
602 if let Some(e) = init {
603 self.walk(e, opaque);
604 }
605 }
606 }
607 }
608 self.hoist(body);
609 self.walk_all(body, opaque);
610 self.close();
611 }
612}