klvm_tools_rs/compiler/
frontend.rs

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            // Ensure any other names mentioned in qq blocks are included.
87            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                        // Rest is a list containing one item that becomes the
210                        // tail.
211                        //
212                        // Downstream, we'll use the tail instead of a nil as the
213                        // final element of a call form.  In the inline case, this
214                        // means that argument references will be generated into
215                        // the runtime tail expression when appropriate.
216                        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 a tail.
233                        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
290// Make a set of names in this sexp.
291pub 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        // Ensure bindings aren't duplicated as we won't be able to
325        // guarantee their order during toposort.
326        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    // We don't need to do much if there were no bindings.
349    if bindings.is_empty() {
350        return Ok(compiled_body);
351    }
352
353    // Return a precise representation of this assign while storing up the work
354    // we did breaking it down.
355    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                        // Ensure that the full extent of the call expression
379                        // in the source file becomes the Srcloc given to the
380                        // call itself.
381                        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
508// More modern constant definition that interprets code ala constexpr.
509fn 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
884/// Given the available helper list and the main expression, compute the list of
885/// reachable helpers.
886pub 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
911/// Entrypoint for compilation.  This yields a CompileForm which represents a full
912/// program.
913///
914/// Given a CompilerOpts specifying the global options for the compilation, return
915/// a representation of the parsed program.  Desugaring is not done in this step
916/// so this is a close representation of the user's input, containing location
917/// references etc.
918///
919/// pre_forms is a list of forms, because most SExp readers, including parse_sexp
920/// parse a list of complete forms from a source text.  It is possible for frontend
921/// to use a list of forms, but it is most often used with a single list in
922/// chiklisp.  Usually pre_forms will contain a slice containing one list or
923/// mod form.
924pub 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            // Treat tail of proper application list as expression.
995            from_klvm(args.clone())
996        }
997    }
998}
999
1000// Form proper frontend code from KLVM.
1001// The languages are related but not identical:
1002// - Left env references refer to functions from the env.
1003// - Right env references refer to user arguments.
1004// We can introduce defconstant helpers that allow us to keep track of what's
1005// being called via 'a' and use that information.
1006// Bare numbers in operator position are only prims.
1007// Bare numbers in argument position are references, rewrite as (@ ..)
1008pub fn from_klvm(sexp: Rc<SExp>) -> Rc<SExp> {
1009    match sexp.borrow() {
1010        SExp::Atom(l, _name) => {
1011            // An atom encountered as an expression is treated as a path.
1012            from_klvm(Rc::new(SExp::Integer(l.clone(), sexp.to_bigint().unwrap())))
1013        }
1014        SExp::QuotedString(l, _, _v) => {
1015            // A string is treated as a number.
1016            // An atom encountered as an expression is treated as a path.
1017            from_klvm(Rc::new(SExp::Integer(l.clone(), sexp.to_bigint().unwrap())))
1018        }
1019        SExp::Integer(l, _n) => {
1020            // A number is treated as a reference in expression position.
1021            // Results in (@ n).
1022            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            // Nil represents nil in both systems.
1034            sexp.clone()
1035        }
1036        SExp::Cons(l, op, args) => {
1037            // This expression represents applying some primitive.
1038            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}