1use std::borrow::Borrow;
2use std::collections::HashMap;
3use std::collections::HashSet;
4use std::rc::Rc;
5
6use crate::classic::klvm::__type_compatibility__::bi_one;
7use crate::compiler::comptypes::{
8 list_to_cons, ArgsAndTail, Binding, BindingPattern, BodyForm, CompileErr, CompileForm,
9 CompilerOpts, ConstantKind, DefconstData, DefmacData, DefunData, HelperForm, IncludeDesc,
10 LetData, LetFormInlineHint, LetFormKind, ModAccum,
11};
12use crate::compiler::lambda::handle_lambda;
13use crate::compiler::preprocessor::preprocess;
14use crate::compiler::rename::rename_children_compileform;
15use crate::compiler::sexp::{decode_string, enlist, SExp};
16use crate::compiler::srcloc::Srcloc;
17use crate::util::u8_from_number;
18
19pub fn collect_used_names_sexp(body: Rc<SExp>) -> Vec<Vec<u8>> {
20 match body.borrow() {
21 SExp::Atom(_, name) => vec![name.to_vec()],
22 SExp::Cons(_, head, tail) => {
23 let mut head_collected = collect_used_names_sexp(head.clone());
24 let mut tail_collected = collect_used_names_sexp(tail.clone());
25 head_collected.append(&mut tail_collected);
26 head_collected
27 }
28 _ => vec![],
29 }
30}
31
32fn collect_used_names_binding(body: &Binding) -> Vec<Vec<u8>> {
33 collect_used_names_bodyform(body.body.borrow())
34}
35
36pub fn collect_used_names_bodyform(body: &BodyForm) -> Vec<Vec<u8>> {
37 match body {
38 BodyForm::Let(_, letdata) => {
39 let mut result = Vec::new();
40 for b in letdata.bindings.iter() {
41 let mut new_binding_names = collect_used_names_binding(b);
42 result.append(&mut new_binding_names);
43 }
44
45 let mut body_names = collect_used_names_bodyform(letdata.body.borrow());
46 result.append(&mut body_names);
47 result
48 }
49 BodyForm::Quoted(_) => vec![],
50 BodyForm::Value(atom) => match atom {
51 SExp::Atom(_l, v) => vec![v.to_vec()],
52 SExp::Cons(_l, f, r) => {
53 let mut first_names = collect_used_names_sexp(f.clone());
54 let mut rest_names = collect_used_names_sexp(r.clone());
55 first_names.append(&mut rest_names);
56 first_names
57 }
58 _ => Vec::new(),
59 },
60 BodyForm::Call(_l, vs, tail) => {
61 let mut result = Vec::new();
62 for a in vs {
63 let mut argnames = collect_used_names_bodyform(a);
64 result.append(&mut argnames);
65 }
66 if let Some(t) = tail {
67 let mut tail_names = collect_used_names_bodyform(t);
68 result.append(&mut tail_names);
69 }
70 result
71 }
72 BodyForm::Mod(_, _) => vec![],
73 BodyForm::Lambda(ldata) => {
74 let mut capture_names = collect_used_names_bodyform(ldata.captures.borrow());
75 capture_names.append(&mut collect_used_names_bodyform(ldata.body.borrow()));
76 capture_names
77 }
78 }
79}
80
81fn collect_used_names_helperform(body: &HelperForm) -> Vec<Vec<u8>> {
82 match body {
83 HelperForm::Defconstant(defc) => collect_used_names_bodyform(defc.body.borrow()),
84 HelperForm::Defmacro(mac) => {
85 let mut res = collect_used_names_compileform(mac.program.borrow());
86 let mut all_token_res = collect_used_names_sexp(mac.program.to_sexp());
88 res.append(&mut all_token_res);
89 res
90 }
91 HelperForm::Defun(_, defun) => collect_used_names_bodyform(&defun.body),
92 }
93}
94
95fn collect_used_names_compileform(body: &CompileForm) -> Vec<Vec<u8>> {
96 let mut result = Vec::new();
97 for h in body.helpers.iter() {
98 let mut helper_list = collect_used_names_helperform(h);
99 result.append(&mut helper_list);
100 }
101
102 let mut ex_list = collect_used_names_bodyform(body.exp.borrow());
103 result.append(&mut ex_list);
104 result
105}
106
107fn calculate_live_helpers(
108 last_names: &HashSet<Vec<u8>>,
109 names: &HashSet<Vec<u8>>,
110 helper_map: &HashMap<Vec<u8>, HelperForm>,
111) -> HashSet<Vec<u8>> {
112 if last_names.len() == names.len() {
113 names.clone()
114 } else {
115 let new_names: HashSet<Vec<u8>> =
116 names.difference(last_names).map(|x| x.to_vec()).collect();
117 let mut needed_helpers: HashSet<Vec<u8>> = names.clone();
118
119 for name in new_names {
120 if let Some(new_helper) = helper_map.get(&name) {
121 let even_newer_names: HashSet<Vec<u8>> = collect_used_names_helperform(new_helper)
122 .iter()
123 .map(|x| x.to_vec())
124 .collect();
125 needed_helpers = needed_helpers
126 .union(&even_newer_names)
127 .map(|x| x.to_vec())
128 .collect();
129 }
130 }
131
132 calculate_live_helpers(names, &needed_helpers, helper_map)
133 }
134}
135
136fn qq_to_expression(opts: Rc<dyn CompilerOpts>, body: Rc<SExp>) -> Result<BodyForm, CompileErr> {
137 let body_copy: &SExp = body.borrow();
138
139 match body.borrow() {
140 SExp::Cons(l, f, r) => {
141 let op = match f.borrow() {
142 SExp::Atom(_, o) => o.clone(),
143 SExp::QuotedString(_, _, s) => s.clone(),
144 SExp::Integer(_, i) => u8_from_number(i.clone()),
145 _ => Vec::new(),
146 };
147
148 if op.len() == 1 && (op[0] == b'q' || op[0] == 1) {
149 return Ok(BodyForm::Quoted(body_copy.clone()));
150 } else if let Some(list) = r.proper_list() {
151 if op == b"quote" {
152 if list.len() != 1 {
153 return Err(CompileErr(l.clone(), format!("bad form {body}")));
154 }
155
156 return Ok(BodyForm::Quoted(list[0].clone()));
157 } else if op == b"unquote" {
158 if list.len() != 1 {
159 return Err(CompileErr(l.clone(), format!("bad form {body}")));
160 }
161
162 return compile_bodyform(opts.clone(), Rc::new(list[0].clone()));
163 }
164 }
165
166 qq_to_expression_list(opts, body.clone())
167 }
168 _ => Ok(BodyForm::Quoted(body_copy.clone())),
169 }
170}
171
172fn qq_to_expression_list(
173 opts: Rc<dyn CompilerOpts>,
174 body: Rc<SExp>,
175) -> Result<BodyForm, CompileErr> {
176 match body.borrow() {
177 SExp::Cons(l, f, r) => {
178 m! {
179 f_qq <- qq_to_expression(opts.clone(), f.clone());
180 r_qq <- qq_to_expression_list(opts, r.clone());
181 Ok(BodyForm::Call(l.clone(), vec!(
182 Rc::new(BodyForm::Value(
183 SExp::Atom(l.clone(), "c".as_bytes().to_vec())
184 )),
185 Rc::new(f_qq),
186 Rc::new(r_qq)
187 ), None))
188 }
189 }
190 SExp::Nil(l) => Ok(BodyForm::Quoted(SExp::Nil(l.clone()))),
191 _ => Err(CompileErr(
192 body.loc(),
193 format!("Bad list tail in qq {body}"),
194 )),
195 }
196}
197
198fn args_to_expression_list(
199 opts: Rc<dyn CompilerOpts>,
200 body: Rc<SExp>,
201) -> Result<ArgsAndTail, CompileErr> {
202 if body.nilp() {
203 Ok(ArgsAndTail::default())
204 } else {
205 match body.borrow() {
206 SExp::Cons(_l, first, rest) => {
207 if let SExp::Atom(_fl, fname) = first.borrow() {
208 if fname == b"&rest" {
209 let args_no_tail = args_to_expression_list(opts, rest.clone())?;
217
218 if args_no_tail.tail.is_some() {
219 return Err(CompileErr(
220 rest.loc(),
221 "only one use of &rest is allowed".to_string(),
222 ));
223 }
224
225 if args_no_tail.args.len() != 1 {
226 return Err(CompileErr(
227 body.loc(),
228 "&rest specified with bad tail".to_string(),
229 ));
230 }
231
232 return Ok(ArgsAndTail {
234 args: vec![],
235 tail: Some(args_no_tail.args[0].clone()),
236 });
237 }
238 }
239 let mut result_list = Vec::new();
240 let f_compiled = compile_bodyform(opts.clone(), first.clone())?;
241 result_list.push(Rc::new(f_compiled));
242 let mut args_and_tail = args_to_expression_list(opts, rest.clone())?;
243 result_list.append(&mut args_and_tail.args);
244 Ok(ArgsAndTail {
245 args: result_list,
246 tail: args_and_tail.tail,
247 })
248 }
249 _ => Err(CompileErr(body.loc(), format!("Bad arg list tail {body}"))),
250 }
251 }
252}
253
254fn make_let_bindings(
255 opts: Rc<dyn CompilerOpts>,
256 body: Rc<SExp>,
257) -> Result<Vec<Rc<Binding>>, CompileErr> {
258 let err = Err(CompileErr(body.loc(), format!("Bad binding tail {body:?}")));
259 let do_atomize = if !opts.dialect().strict {
260 |a: &SExp| -> SExp { a.atomize() }
261 } else {
262 |a: &SExp| -> SExp { a.clone() }
263 };
264 match body.borrow() {
265 SExp::Nil(_) => Ok(vec![]),
266 SExp::Cons(_, head, tl) => head
267 .proper_list()
268 .filter(|x| x.len() == 2)
269 .map(|x| match (do_atomize(&x[0]), &x[1]) {
270 (SExp::Atom(l, name), expr) => {
271 let compiled_body = compile_bodyform(opts.clone(), Rc::new(expr.clone()))?;
272 let mut result = Vec::new();
273 let mut rest_bindings = make_let_bindings(opts, tl.clone())?;
274 result.push(Rc::new(Binding {
275 loc: l.clone(),
276 nl: l,
277 pattern: BindingPattern::Name(name.to_vec()),
278 body: Rc::new(compiled_body),
279 }));
280 result.append(&mut rest_bindings);
281 Ok(result)
282 }
283 _ => err.clone(),
284 })
285 .unwrap_or_else(|| err.clone()),
286 _ => err,
287 }
288}
289
290pub fn make_provides_set(provides_set: &mut HashSet<Vec<u8>>, body_sexp: Rc<SExp>) {
292 match body_sexp.atomize() {
293 SExp::Cons(_, a, b) => {
294 make_provides_set(provides_set, a);
295 make_provides_set(provides_set, b);
296 }
297 SExp::Atom(_, name) => {
298 provides_set.insert(name);
299 }
300 _ => {}
301 }
302}
303
304fn handle_assign_form(
305 opts: Rc<dyn CompilerOpts>,
306 l: Srcloc,
307 v: &[SExp],
308 inline_hint: Option<LetFormInlineHint>,
309) -> Result<BodyForm, CompileErr> {
310 if v.len() % 2 == 0 {
311 return Err(CompileErr(
312 l,
313 "assign form should be in pairs of pattern value followed by an expression".to_string(),
314 ));
315 }
316
317 let mut bindings = Vec::new();
318 let mut check_duplicates = HashSet::new();
319
320 for idx in (0..(v.len() - 1) / 2).map(|idx| idx * 2) {
321 let destructure_pattern = Rc::new(v[idx].clone());
322 let binding_body = compile_bodyform(opts.clone(), Rc::new(v[idx + 1].clone()))?;
323
324 let mut this_provides = HashSet::new();
327 make_provides_set(&mut this_provides, destructure_pattern.clone());
328
329 for item in this_provides.iter() {
330 if check_duplicates.contains(item) {
331 return Err(CompileErr(
332 destructure_pattern.loc(),
333 format!("Duplicate binding {}", decode_string(item)),
334 ));
335 }
336 check_duplicates.insert(item.clone());
337 }
338
339 bindings.push(Rc::new(Binding {
340 loc: v[idx].loc().ext(&v[idx + 1].loc()),
341 nl: destructure_pattern.loc(),
342 pattern: BindingPattern::Complex(destructure_pattern),
343 body: Rc::new(binding_body),
344 }));
345 }
346
347 let compiled_body = compile_bodyform(opts.clone(), Rc::new(v[v.len() - 1].clone()))?;
348 if bindings.is_empty() {
350 return Ok(compiled_body);
351 }
352
353 Ok(BodyForm::Let(
356 LetFormKind::Assign,
357 Box::new(LetData {
358 loc: l.clone(),
359 kw: Some(l),
360 bindings,
361 inline_hint,
362 body: Rc::new(compiled_body),
363 }),
364 ))
365}
366
367pub fn compile_bodyform(
368 opts: Rc<dyn CompilerOpts>,
369 body: Rc<SExp>,
370) -> Result<BodyForm, CompileErr> {
371 match body.borrow() {
372 SExp::Cons(l, op, tail) => {
373 let application = || {
374 args_to_expression_list(opts.clone(), tail.clone()).and_then(|atail| {
375 compile_bodyform(opts.clone(), op.clone()).map(|func| {
376 let mut result_call = vec![Rc::new(func)];
377 let mut args_clone = atail.args.to_vec();
378 let ending = if atail.args.is_empty() {
382 l.ending()
383 } else {
384 atail.args[atail.args.len() - 1].loc().ending()
385 };
386 result_call.append(&mut args_clone);
387 BodyForm::Call(l.ext(&ending), result_call, atail.tail)
388 })
389 })
390 };
391
392 let finish_err = |site| {
393 Err(CompileErr(
394 l.clone(),
395 format!("{site}: bad argument list for form {body}"),
396 ))
397 };
398
399 match op.borrow() {
400 SExp::Atom(l, atom_name) => {
401 if *atom_name == b"q" || (atom_name.len() == 1 && atom_name[0] == 1) {
402 let tail_copy: &SExp = tail.borrow();
403 return Ok(BodyForm::Quoted(tail_copy.clone()));
404 }
405
406 let assign_lambda = *atom_name == "assign-lambda".as_bytes().to_vec();
407 let assign_inline = *atom_name == "assign-inline".as_bytes().to_vec();
408
409 match tail.proper_list() {
410 Some(v) => {
411 if *atom_name == b"let" || *atom_name == b"let*" {
412 if v.len() != 2 {
413 return finish_err("let");
414 }
415
416 let kind = if *atom_name == b"let" {
417 LetFormKind::Parallel
418 } else {
419 LetFormKind::Sequential
420 };
421
422 let bindings = v[0].clone();
423 let body = v[1].clone();
424
425 let let_bindings =
426 make_let_bindings(opts.clone(), Rc::new(bindings))?;
427 let compiled_body = compile_bodyform(opts, Rc::new(body))?;
428 Ok(BodyForm::Let(
429 kind,
430 Box::new(LetData {
431 loc: l.clone(),
432 kw: Some(l.clone()),
433 bindings: let_bindings,
434 inline_hint: None,
435 body: Rc::new(compiled_body),
436 }),
437 ))
438 } else if assign_lambda
439 || assign_inline
440 || *atom_name == "assign".as_bytes().to_vec()
441 {
442 handle_assign_form(
443 opts.clone(),
444 l.clone(),
445 &v,
446 if assign_lambda {
447 Some(LetFormInlineHint::NonInline(l.clone()))
448 } else if assign_inline {
449 Some(LetFormInlineHint::Inline(l.clone()))
450 } else {
451 Some(LetFormInlineHint::NoChoice)
452 },
453 )
454 } else if *atom_name == "quote".as_bytes().to_vec() {
455 if v.len() != 1 {
456 return finish_err("quote");
457 }
458
459 let quote_body = v[0].clone();
460
461 Ok(BodyForm::Quoted(quote_body))
462 } else if *atom_name == b"qq" {
463 if v.len() != 1 {
464 return finish_err("qq");
465 }
466
467 let quote_body = v[0].clone();
468
469 qq_to_expression(opts, Rc::new(quote_body))
470 } else if *atom_name == b"mod" {
471 let subparse = frontend(opts, &[body.clone()])?;
472 Ok(BodyForm::Mod(op.loc(), subparse))
473 } else if *atom_name == b"lambda" {
474 handle_lambda(opts, body.loc(), Some(l.clone()), &v)
475 } else {
476 application()
477 }
478 }
479 None => finish_err("tail_proper"),
480 }
481 }
482 SExp::Integer(il, i) => compile_bodyform(
483 opts,
484 Rc::new(SExp::Cons(
485 il.clone(),
486 Rc::new(SExp::Atom(il.clone(), u8_from_number(i.clone()))),
487 tail.clone(),
488 )),
489 ),
490 SExp::QuotedString(_, _, _) => {
491 let body_copy: &SExp = body.borrow();
492 Ok(BodyForm::Value(body_copy.clone()))
493 }
494 SExp::Nil(_l) => {
495 let body_copy: &SExp = body.borrow();
496 Ok(BodyForm::Quoted(body_copy.clone()))
497 }
498 SExp::Cons(_, _, _) => finish_err("bad cons"),
499 }
500 }
501 _ => {
502 let body_copy: &SExp = body.borrow();
503 Ok(BodyForm::Value(body_copy.clone()))
504 }
505 }
506}
507
508fn compile_defconst(
510 opts: Rc<dyn CompilerOpts>,
511 l: Srcloc,
512 nl: Srcloc,
513 kl: Option<Srcloc>,
514 name: Vec<u8>,
515 body: Rc<SExp>,
516) -> Result<HelperForm, CompileErr> {
517 let bf = compile_bodyform(opts.clone(), body)?;
518 Ok(HelperForm::Defconstant(DefconstData {
519 kw: kl,
520 nl,
521 loc: l,
522 kind: ConstantKind::Complex,
523 name: name.to_vec(),
524 body: Rc::new(bf),
525 tabled: opts.frontend_opt() || opts.dialect().stepping.unwrap_or(0) > 22,
526 }))
527}
528
529fn compile_defconstant(
530 opts: Rc<dyn CompilerOpts>,
531 l: Srcloc,
532 nl: Srcloc,
533 kl: Option<Srcloc>,
534 name: Vec<u8>,
535 body: Rc<SExp>,
536) -> Result<HelperForm, CompileErr> {
537 let body_borrowed: &SExp = body.borrow();
538 if let SExp::Cons(_, _, _) = body_borrowed {
539 Ok(HelperForm::Defconstant(DefconstData {
540 loc: l,
541 nl,
542 kw: kl,
543 kind: ConstantKind::Simple,
544 name: name.to_vec(),
545 body: Rc::new(BodyForm::Value(body_borrowed.clone())),
546 tabled: false,
547 }))
548 } else {
549 compile_bodyform(opts, body).map(|bf| {
550 HelperForm::Defconstant(DefconstData {
551 loc: l,
552 nl,
553 kw: kl,
554 kind: ConstantKind::Simple,
555 name: name.to_vec(),
556 body: Rc::new(bf),
557 tabled: false,
558 })
559 })
560 }
561}
562
563fn location_span(l_: Srcloc, lst_: Rc<SExp>) -> Srcloc {
564 let mut l = l_;
565 let mut lst = lst_;
566 while let SExp::Cons(_, a, b) = lst.borrow() {
567 l = location_span(l.clone(), a.clone()).ext(&b.loc());
568 lst = b.clone();
569 }
570 l
571}
572
573pub struct CompileDefun {
574 pub l: Srcloc,
575 pub nl: Srcloc,
576 pub kwl: Option<Srcloc>,
577 pub inline: bool,
578 pub name: Vec<u8>,
579 pub args: Rc<SExp>,
580 pub body: Rc<SExp>,
581}
582
583fn compile_defun(opts: Rc<dyn CompilerOpts>, data: CompileDefun) -> Result<HelperForm, CompileErr> {
584 let mut take_form = data.body.clone();
585
586 if let SExp::Cons(_, f, _r) = data.body.borrow() {
587 take_form = f.clone();
588 }
589 compile_bodyform(opts, take_form).map(|bf| {
590 HelperForm::Defun(
591 data.inline,
592 Box::new(DefunData {
593 loc: data.l,
594 nl: data.nl,
595 kw: data.kwl,
596 name: data.name,
597 args: data.args.clone(),
598 orig_args: data.args,
599 body: Rc::new(bf),
600 synthetic: None,
601 }),
602 )
603 })
604}
605
606fn compile_defmacro(
607 opts: Rc<dyn CompilerOpts>,
608 l: Srcloc,
609 nl: Srcloc,
610 kwl: Option<Srcloc>,
611 name: Vec<u8>,
612 args: Rc<SExp>,
613 body: Rc<SExp>,
614) -> Result<HelperForm, CompileErr> {
615 let program = SExp::Cons(
616 l.clone(),
617 Rc::new(SExp::Atom(l.clone(), b"mod".to_vec())),
618 Rc::new(SExp::Cons(l.clone(), args.clone(), body)),
619 );
620 let new_opts = opts.set_stdenv(false);
621 frontend(new_opts, &[Rc::new(program)]).map(|p| {
622 HelperForm::Defmacro(DefmacData {
623 loc: l,
624 nl,
625 kw: kwl,
626 name,
627 args: args.clone(),
628 program: Rc::new(p),
629 advanced: false,
630 })
631 })
632}
633
634struct OpName4Match {
635 opl: Srcloc,
636 op_name: Vec<u8>,
637 nl: Srcloc,
638 name: Vec<u8>,
639 args: Rc<SExp>,
640 body: Rc<SExp>,
641}
642
643#[allow(clippy::type_complexity)]
644fn match_op_name_4(pl: &[SExp]) -> Option<OpName4Match> {
645 if pl.is_empty() {
646 return None;
647 }
648
649 match &pl[0] {
650 SExp::Atom(l, op_name) => {
651 if pl.len() < 3 {
652 return Some(OpName4Match {
653 opl: l.clone(),
654 op_name: op_name.clone(),
655 nl: l.clone(),
656 name: Vec::new(),
657 args: Rc::new(SExp::Nil(l.clone())),
658 body: Rc::new(SExp::Nil(l.clone())),
659 });
660 }
661
662 match &pl[1] {
663 SExp::Atom(ll, name) => {
664 let mut tail_list = Vec::new();
665 for elt in pl.iter().skip(3) {
666 tail_list.push(Rc::new(elt.clone()));
667 }
668 Some(OpName4Match {
669 opl: l.clone(),
670 op_name: op_name.clone(),
671 nl: ll.clone(),
672 name: name.clone(),
673 args: Rc::new(pl[2].clone()),
674 body: Rc::new(enlist(l.clone(), &tail_list)),
675 })
676 }
677 _ => Some(OpName4Match {
678 opl: l.clone(),
679 op_name: op_name.clone(),
680 nl: pl[1].loc(),
681 name: Vec::new(),
682 args: Rc::new(SExp::Nil(l.clone())),
683 body: Rc::new(SExp::Nil(l.clone())),
684 }),
685 }
686 }
687 _ => None,
688 }
689}
690
691pub fn compile_helperform(
692 opts: Rc<dyn CompilerOpts>,
693 body: Rc<SExp>,
694) -> Result<Option<HelperForm>, CompileErr> {
695 let l = location_span(body.loc(), body.clone());
696
697 if let Some(matched) = body.proper_list().and_then(|pl| match_op_name_4(&pl)) {
698 if matched.op_name == b"defconstant" {
699 compile_defconstant(
700 opts,
701 l,
702 matched.nl,
703 Some(matched.opl),
704 matched.name.to_vec(),
705 matched.args,
706 )
707 .map(Some)
708 } else if matched.op_name == b"defconst" {
709 compile_defconst(
710 opts,
711 l,
712 matched.nl,
713 Some(matched.opl),
714 matched.name.to_vec(),
715 matched.args,
716 )
717 .map(Some)
718 } else if matched.op_name == b"defmacro" || matched.op_name == b"defmac" {
719 compile_defmacro(
720 opts,
721 l,
722 matched.nl,
723 Some(matched.opl),
724 matched.name.to_vec(),
725 matched.args,
726 matched.body,
727 )
728 .map(Some)
729 } else if matched.op_name == b"defun" {
730 compile_defun(
731 opts,
732 CompileDefun {
733 l,
734 nl: matched.nl,
735 kwl: Some(matched.opl),
736 inline: false,
737 name: matched.name.to_vec(),
738 args: matched.args,
739 body: matched.body,
740 },
741 )
742 .map(Some)
743 } else if matched.op_name == b"defun-inline" {
744 compile_defun(
745 opts,
746 CompileDefun {
747 l,
748 nl: matched.nl,
749 kwl: Some(matched.opl),
750 inline: true,
751 name: matched.name.to_vec(),
752 args: matched.args,
753 body: matched.body,
754 },
755 )
756 .map(Some)
757 } else {
758 Err(CompileErr(
759 matched.body.loc(),
760 "unknown keyword in helper".to_string(),
761 ))
762 }
763 } else {
764 Err(CompileErr(
765 body.loc(),
766 "Helper wasn't in the proper form".to_string(),
767 ))
768 }
769}
770
771fn compile_mod_(
772 mc: &ModAccum,
773 opts: Rc<dyn CompilerOpts>,
774 args: Rc<SExp>,
775 content: Rc<SExp>,
776) -> Result<ModAccum, CompileErr> {
777 match content.borrow() {
778 SExp::Nil(l) => Err(CompileErr(
779 l.clone(),
780 "no expression at end of mod".to_string(),
781 )),
782 SExp::Cons(l, body, tail) => match tail.borrow() {
783 SExp::Nil(_) => match mc.exp_form {
784 Some(_) => Err(CompileErr(l.clone(), "too many expressions".to_string())),
785 _ => Ok(mc.set_final(&CompileForm {
786 loc: mc.loc.clone(),
787 include_forms: mc.includes.clone(),
788 args,
789 helpers: mc.helpers.clone(),
790 exp: Rc::new(compile_bodyform(opts.clone(), body.clone())?),
791 })),
792 },
793 _ => {
794 let helper = compile_helperform(opts.clone(), body.clone())?;
795 match helper {
796 None => Err(CompileErr(
797 l.clone(),
798 "only the last form can be an exprssion in mod".to_string(),
799 )),
800 Some(form) => match mc.exp_form {
801 None => compile_mod_(&mc.add_helper(form), opts, args, tail.clone()),
802 Some(_) => Err(CompileErr(l.clone(), "too many expressions".to_string())),
803 },
804 }
805 }
806 },
807 _ => Err(CompileErr(
808 content.loc(),
809 format!("inappropriate sexp {content}"),
810 )),
811 }
812}
813
814fn frontend_step_finish(
815 opts: Rc<dyn CompilerOpts>,
816 includes: &mut Vec<IncludeDesc>,
817 pre_forms: &[Rc<SExp>],
818) -> Result<ModAccum, CompileErr> {
819 let loc = pre_forms[0].loc();
820 frontend_start(
821 opts.clone(),
822 includes,
823 &[Rc::new(SExp::Cons(
824 loc.clone(),
825 Rc::new(SExp::Atom(loc.clone(), "mod".as_bytes().to_vec())),
826 Rc::new(SExp::Cons(
827 loc.clone(),
828 Rc::new(SExp::Nil(loc.clone())),
829 Rc::new(list_to_cons(loc, pre_forms)),
830 )),
831 ))],
832 )
833}
834
835fn frontend_start(
836 opts: Rc<dyn CompilerOpts>,
837 includes: &mut Vec<IncludeDesc>,
838 pre_forms: &[Rc<SExp>],
839) -> Result<ModAccum, CompileErr> {
840 if pre_forms.is_empty() {
841 Err(CompileErr(
842 Srcloc::start(&opts.filename()),
843 "empty source file not allowed".to_string(),
844 ))
845 } else {
846 let l = pre_forms[0].loc();
847 pre_forms[0]
848 .proper_list()
849 .map(|x| {
850 if x.is_empty() {
851 return frontend_step_finish(opts.clone(), includes, pre_forms);
852 }
853
854 if let SExp::Atom(_, mod_atom) = &x[0] {
855 if pre_forms.len() > 1 {
856 return Err(CompileErr(
857 pre_forms[0].loc(),
858 "one toplevel mod form allowed".to_string(),
859 ));
860 }
861
862 if *mod_atom == b"mod" {
863 let args = Rc::new(x[1].clone());
864 let body_vec: Vec<Rc<SExp>> =
865 x.iter().skip(2).map(|s| Rc::new(s.clone())).collect();
866 let body = Rc::new(enlist(pre_forms[0].loc(), &body_vec));
867
868 let ls = preprocess(opts.clone(), includes, body)?;
869 return compile_mod_(
870 &ModAccum::new(l.clone()),
871 opts.clone(),
872 args,
873 Rc::new(list_to_cons(l, &ls)),
874 );
875 }
876 }
877
878 frontend_step_finish(opts.clone(), includes, pre_forms)
879 })
880 .unwrap_or_else(|| frontend_step_finish(opts, includes, pre_forms))
881 }
882}
883
884pub fn compute_live_helpers(
887 opts: Rc<dyn CompilerOpts>,
888 helper_list: &[HelperForm],
889 main_exp: Rc<BodyForm>,
890) -> Vec<HelperForm> {
891 let expr_names: HashSet<Vec<u8>> = collect_used_names_bodyform(main_exp.borrow())
892 .iter()
893 .map(|x| x.to_vec())
894 .collect();
895
896 let mut helper_map = HashMap::new();
897
898 for h in helper_list.iter() {
899 helper_map.insert(h.name().clone(), h.clone());
900 }
901
902 let helper_names = calculate_live_helpers(&HashSet::new(), &expr_names, &helper_map);
903
904 helper_list
905 .iter()
906 .filter(|h| !opts.frontend_check_live() || helper_names.contains(h.name()))
907 .cloned()
908 .collect()
909}
910
911pub fn frontend(
925 opts: Rc<dyn CompilerOpts>,
926 pre_forms: &[Rc<SExp>],
927) -> Result<CompileForm, CompileErr> {
928 let mut includes = Vec::new();
929 let started = frontend_start(opts.clone(), &mut includes, pre_forms)?;
930
931 for i in includes.iter() {
932 started.add_include(i.clone());
933 }
934
935 let compiled: Result<CompileForm, CompileErr> = match started.exp_form {
936 None => Err(CompileErr(
937 started.loc,
938 "mod must end on an expression".to_string(),
939 )),
940 Some(v) => {
941 let compiled_val: &CompileForm = &v;
942 Ok(compiled_val.clone())
943 }
944 };
945
946 let our_mod = rename_children_compileform(&compiled?)?;
947
948 let expr_names: HashSet<Vec<u8>> = collect_used_names_bodyform(our_mod.exp.borrow())
949 .iter()
950 .map(|x| x.to_vec())
951 .collect();
952
953 let helper_list = our_mod.helpers.iter().map(|h| (h.name(), h));
954 let mut helper_map = HashMap::new();
955
956 for hpair in helper_list {
957 helper_map.insert(hpair.0.clone(), hpair.1.clone());
958 }
959
960 let helper_names = calculate_live_helpers(&HashSet::new(), &expr_names, &helper_map);
961
962 let mut live_helpers = Vec::new();
963 for h in our_mod.helpers {
964 if !opts.frontend_check_live() || helper_names.contains(h.name()) {
965 live_helpers.push(h);
966 }
967 }
968
969 Ok(CompileForm {
970 loc: our_mod.loc.clone(),
971 include_forms: includes.to_vec(),
972 args: our_mod.args.clone(),
973 helpers: live_helpers,
974 exp: our_mod.exp.clone(),
975 })
976}
977
978fn is_quote_op(sexp: Rc<SExp>) -> bool {
979 match sexp.borrow() {
980 SExp::Atom(_, name) => name.len() == 1 && name[0] as char == 'q',
981 SExp::Integer(_, v) => v == &bi_one(),
982 _ => false,
983 }
984}
985
986fn from_klvm_args(args: Rc<SExp>) -> Rc<SExp> {
987 match args.borrow() {
988 SExp::Cons(l, arg, rest) => {
989 let new_arg = from_klvm(arg.clone());
990 let new_rest = from_klvm_args(rest.clone());
991 Rc::new(SExp::Cons(l.clone(), new_arg, new_rest))
992 }
993 _ => {
994 from_klvm(args.clone())
996 }
997 }
998}
999
1000pub fn from_klvm(sexp: Rc<SExp>) -> Rc<SExp> {
1009 match sexp.borrow() {
1010 SExp::Atom(l, _name) => {
1011 from_klvm(Rc::new(SExp::Integer(l.clone(), sexp.to_bigint().unwrap())))
1013 }
1014 SExp::QuotedString(l, _, _v) => {
1015 from_klvm(Rc::new(SExp::Integer(l.clone(), sexp.to_bigint().unwrap())))
1018 }
1019 SExp::Integer(l, _n) => {
1020 Rc::new(SExp::Cons(
1023 l.clone(),
1024 Rc::new(SExp::atom_from_string(l.clone(), "@")),
1025 Rc::new(SExp::Cons(
1026 l.clone(),
1027 sexp.clone(),
1028 Rc::new(SExp::Nil(l.clone())),
1029 )),
1030 ))
1031 }
1032 SExp::Nil(_l) => {
1033 sexp.clone()
1035 }
1036 SExp::Cons(l, op, args) => {
1037 if is_quote_op(op.clone()) {
1039 Rc::new(SExp::Cons(
1040 l.clone(),
1041 Rc::new(SExp::atom_from_string(l.clone(), "q")),
1042 args.clone(),
1043 ))
1044 } else {
1045 let new_args = from_klvm_args(args.clone());
1046 Rc::new(SExp::Cons(l.clone(), op.clone(), new_args))
1047 }
1048 }
1049 }
1050}