1use std::collections::BTreeMap;
26use std::sync::Arc;
27
28use tatara_lisp::{Atom, Span, Spanned, SpannedForm};
29use tatara_lisp_eval::value::Closure;
30use tatara_lisp_eval::vm::run::CompiledClosure;
31use tatara_lisp_eval::Value;
32
33use crate::mailbox::copy_data;
34
35#[derive(Clone, Debug, Default)]
42pub struct Code {
43 pub defs: Vec<Spanned>,
45 pub bindings: Vec<(Arc<str>, Value)>,
47 pub start: Option<Spanned>,
49}
50
51impl Code {
52 #[must_use]
54 pub fn program(&self) -> Vec<Spanned> {
55 let mut forms = self.defs.clone();
56 forms.extend(self.start.clone());
57 forms
58 }
59
60 pub fn bind(&mut self, name: &str, value: Value) {
62 match self.bindings.iter_mut().find(|(n, _)| &**n == name) {
63 Some(slot) => slot.1 = value,
64 None => self.bindings.push((Arc::from(name), value)),
65 }
66 }
67}
68
69#[derive(Clone, Debug)]
71pub enum Entry {
72 Named(String),
74 Lambda(Spanned),
76 Builtin(String),
78}
79
80#[derive(Clone)]
82enum Function {
83 Source(Arc<Closure>),
84 Builtin(Arc<str>),
85}
86
87fn function_of(v: &Value) -> Option<Function> {
88 match v {
89 Value::Closure(c) => Some(Function::Source(c.clone())),
90 Value::NativeFn(n) => Some(Function::Builtin(n.name.clone())),
91 Value::Foreign(any) => any.clone().downcast::<CompiledClosure>().ok().map(|cc| {
92 let lifted = cc.lift_to_closure();
95 Function::Source(Arc::new(Closure {
96 params: lifted.params.clone(),
97 rest: lifted.rest.clone(),
98 body: lifted.body.clone(),
99 captured_env: lifted.captured_env.clone(),
100 source: lifted.source,
101 name: cc.body.name.clone(),
102 }))
103 }),
104 _ => None,
105 }
106}
107
108#[must_use]
111pub fn name_of(v: &Value) -> Option<String> {
112 match function_of(v)? {
113 Function::Source(c) => c.name.as_deref().map(str::to_string),
114 Function::Builtin(n) => Some(n.to_string()),
115 }
116}
117
118#[must_use]
120pub fn is_function(v: &Value) -> bool {
121 function_of(v).is_some()
122}
123
124fn sym(name: &str) -> Spanned {
125 Spanned::new(
126 Span::synthetic(),
127 SpannedForm::Atom(Atom::Symbol(name.to_string())),
128 )
129}
130
131fn list(items: Vec<Spanned>) -> Spanned {
132 Spanned::new(Span::synthetic(), SpannedForm::List(items))
133}
134
135fn params_of(c: &Closure) -> Spanned {
137 let mut ps: Vec<Spanned> = c.params.iter().map(|p| sym(p)).collect();
138 if let Some(r) = &c.rest {
139 ps.push(sym("&rest"));
140 ps.push(sym(r));
141 }
142 list(ps)
143}
144
145fn lambda_of(c: &Closure) -> Spanned {
147 let mut items = vec![sym("lambda"), params_of(c)];
148 items.extend(c.body.iter().cloned());
149 Spanned::new(c.source, SpannedForm::List(items))
150}
151
152fn define_of(name: &str, c: &Closure) -> Spanned {
154 let mut head = vec![sym(name)];
155 if let SpannedForm::List(ps) = params_of(c).form {
156 head.extend(ps);
157 }
158 let mut items = vec![sym("define"), list(head)];
159 items.extend(c.body.iter().cloned());
160 Spanned::new(c.source, SpannedForm::List(items))
161}
162
163#[must_use]
165pub fn define_value(name: &str, value: Spanned) -> Spanned {
166 list(vec![sym("define"), sym(name), value])
167}
168
169#[must_use]
171pub fn call(head: Spanned, args: &[&str]) -> Spanned {
172 let mut items = vec![head];
173 items.extend(args.iter().map(|a| sym(a)));
174 list(items)
175}
176
177#[must_use]
179pub fn symbol(name: &str) -> Spanned {
180 sym(name)
181}
182
183fn same(a: &Value, b: &Value) -> bool {
185 match (a, b) {
186 (Value::Closure(x), Value::Closure(y)) => Arc::ptr_eq(x, y),
187 (Value::Foreign(x), Value::Foreign(y)) => Arc::ptr_eq(x, y),
188 (Value::List(x), Value::List(y)) => Arc::ptr_eq(x, y),
189 (Value::Map(x), Value::Map(y)) => Arc::ptr_eq(x, y),
190 (Value::NativeFn(x), Value::NativeFn(y)) => x.name == y.name,
191 _ => crate::pattern::data_eq(a, b),
192 }
193}
194
195struct Walk {
197 defs: Vec<Spanned>,
198 seen: BTreeMap<String, Value>,
201 bindings: Vec<(Arc<str>, Value)>,
202}
203
204impl Walk {
205 fn new() -> Self {
206 Self {
207 defs: Vec::new(),
208 seen: BTreeMap::new(),
209 bindings: Vec::new(),
210 }
211 }
212
213 fn reach(&mut self, c: &Closure) -> Result<(), String> {
215 let free = blue_lang_waku::free_names(&[lambda_of(c)], &|_| false);
216 for name in free {
217 let Some(v) = c.captured_env.lookup(&name) else {
218 continue;
221 };
222 self.bring(&name, &v)?;
223 }
224 Ok(())
225 }
226
227 fn bring(&mut self, name: &str, v: &Value) -> Result<(), String> {
228 if let Some(prev) = self.seen.get(name) {
229 if same(prev, v) {
230 return Ok(());
231 }
232 return Err(format!(
233 "spawn reaches two different values named `{name}`; rename one"
234 ));
235 }
236 self.seen.insert(name.to_string(), v.clone());
237 match function_of(v) {
238 Some(Function::Builtin(_)) => Ok(()),
241 Some(Function::Source(k)) => {
242 self.defs.push(define_of(name, &k));
243 self.reach(&k)
244 }
245 None => {
246 let copy = copy_data(v).map_err(|kind| {
247 format!(
248 "spawn copies the values a process reaches, and `{name}` is a {kind}, \
249 which is not data; create it inside the process"
250 )
251 })?;
252 self.bindings.push((Arc::from(name), copy));
253 Ok(())
254 }
255 }
256 }
257}
258
259pub fn spawn_code(f: &Value, args: &[Value]) -> Result<(Code, Entry), String> {
265 let function =
266 function_of(f).ok_or_else(|| format!("spawn takes a function, got {}", f.type_name()))?;
267 let mut walk = Walk::new();
268 let mut arg_names = Vec::with_capacity(args.len());
269 for (i, a) in args.iter().enumerate() {
270 let name = format!("%a{i}");
271 let copy = copy_data(a).map_err(|kind| {
272 format!(
273 "spawn copies its arguments, and argument {} is a {kind}, which is not data",
274 i + 1
275 )
276 })?;
277 walk.bindings.push((Arc::from(name.as_str()), copy));
278 arg_names.push(name);
279 }
280 let names: Vec<&str> = arg_names.iter().map(String::as_str).collect();
281 let (entry, start) = match function {
282 Function::Builtin(n) => (Entry::Builtin(n.to_string()), call(sym(&n), &names)),
283 Function::Source(c) => {
284 let named = c.name.as_deref().filter(|n| {
287 c.captured_env
288 .lookup(n)
289 .is_some_and(|v| name_of(&v).as_deref() == Some(*n))
290 });
291 match named {
292 Some(n) => {
293 let global = c
294 .captured_env
295 .lookup(n)
296 .unwrap_or(Value::Closure(c.clone()));
297 walk.bring(n, &global)?;
298 (Entry::Named(n.to_string()), call(sym(n), &names))
299 }
300 None => {
301 walk.reach(&c)?;
302 let lambda = lambda_of(&c);
303 (Entry::Lambda(lambda.clone()), call(lambda, &names))
304 }
305 }
306 }
307 };
308 Ok((
309 Code {
310 defs: walk.defs,
311 bindings: walk.bindings,
312 start: Some(start),
313 },
314 entry,
315 ))
316}
317
318pub fn function_code(f: &Value, as_name: &str) -> Result<Code, String> {
325 let function =
326 function_of(f).ok_or_else(|| format!("expected a function, got {}", f.type_name()))?;
327 let mut walk = Walk::new();
328 match function {
329 Function::Builtin(n) => {
330 walk.defs.push(define_value(as_name, sym(&n)));
331 }
332 Function::Source(c) => {
333 let named = c.name.as_deref().filter(|n| {
334 c.captured_env
335 .lookup(n)
336 .is_some_and(|v| name_of(&v).as_deref() == Some(*n))
337 });
338 match named {
339 Some(n) => {
340 let global = c
341 .captured_env
342 .lookup(n)
343 .unwrap_or(Value::Closure(c.clone()));
344 walk.bring(n, &global)?;
345 if n != as_name {
346 walk.defs.push(define_value(as_name, sym(n)));
347 }
348 }
349 None => {
350 walk.reach(&c)?;
351 walk.defs.push(define_value(as_name, lambda_of(&c)));
352 }
353 }
354 }
355 }
356 Ok(Code {
357 defs: walk.defs,
358 bindings: walk.bindings,
359 start: None,
360 })
361}