spaik/
comp.rs

1//! SPAIK v2 Compiler
2
3use std::collections::hash_map;
4use std::{iter, mem};
5use std::sync::atomic::{Ordering, AtomicUsize};
6
7use chasm::LblMap;
8use fnv::{FnvHashSet, FnvHashMap};
9
10use crate::nkgc::{PV, SymID, Int};
11use crate::r8vm::{R8VM, ArgSpec, r8c, Func};
12use crate::chasm::{ChOp, ChASM, Lbl, self, Arg};
13use crate::error::Source;
14use crate::ast::{AST2, M, Prog, Progn, M2, ArgList2, VarDecl, Visitor, Visitable};
15use crate::r8vm::r8c::{OpName::*, Op as R8C};
16use crate::builtins::*;
17use crate::error::Result;
18
19static LAMBDA_COUNT: AtomicUsize = AtomicUsize::new(0);
20static MODULE_COUNT: AtomicUsize = AtomicUsize::new(0);
21static DEFVAR_COUNT: AtomicUsize = AtomicUsize::new(0);
22
23macro_rules! def_macros {
24    ($d:tt, $ret:expr, $self:expr) => {
25        #[allow(unused_macros)]
26        macro_rules! asm {
27            ($d ($d args:tt)*) => {{ $self.unit().op(chasm!($d ($d args)*)); }};
28        }
29
30        #[allow(unused_macros)]
31        macro_rules! opcall {
32            ($d op:ident $d ($d arg:expr),*) => {{
33                if $ret {
34                    $d ($self.push($d arg)?;)*
35                    asm!($d op);
36                    $self.env_pop(count_args!($d($d arg),*))?;
37                } else {
38                    $d ($self.compile(false, $d arg)?;)*
39                }
40            }};
41        }
42
43        #[allow(unused_macros)]
44        macro_rules! opcall_mut {
45            ($d op:ident $d ($d arg:expr),*) => {{
46                $d ($self.push($d arg)?;)*
47                asm!($d op);
48                if !$ret {
49                    asm!(POP 1);
50                }
51                $self.env_pop(count_args!($d($d arg),*))?;
52            }};
53        }
54
55        #[allow(unused_macros)]
56        macro_rules! vopcall {
57            ($d op:ident $d argv:expr) => {{
58                if $ret {
59                    let nargs = $self.pushit(($d argv).into_iter())?;
60                    asm!($d op nargs);
61                    $self.env_pop(nargs)?;
62                } else {
63                    for arg in ($d argv).into_iter() {
64                        $self.compile(false, arg)?;
65                    }
66                }
67            }};
68        }
69    };
70}
71
72pub type SourceList = Vec<(usize, Source)>;
73
74type VarIdx = u32;
75
76#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
77pub enum BoundVar {
78    Local(VarIdx),
79    Env(VarIdx),
80}
81
82#[derive(Default, Debug)]
83struct Env {
84    name: Option<SymID>,
85    vars: Vec<SymID>,
86    statics: FnvHashMap<SymID, usize>,
87}
88
89// FIXME: Statics and locals need to be merged somehow, right
90//        now a local (let) always takes precedence over a static
91//        variable (intr::define-static)
92impl Env {
93    pub fn new(name: Option<SymID>, args: Vec<SymID>) -> Env {
94        let mut env = Env {
95            name,
96            vars: args,
97            statics: FnvHashMap::default()
98        };
99        env.defvar(Builtin::IP.sym());
100        env.defvar(Builtin::Frame.sym());
101        env
102    }
103
104    pub fn len(&self) -> usize {
105        self.vars.len()
106    }
107
108    #[allow(dead_code)]
109    pub fn is_empty(&self) -> bool {
110        self.len() == 0
111    }
112
113    pub fn none(name: Option<SymID>) -> Env {
114        Env { vars: vec![], statics: FnvHashMap::default(), name }
115    }
116
117    pub fn defvar(&mut self, var: SymID) {
118        self.vars.push(var);
119    }
120
121    pub fn anon(&mut self) -> usize {
122        let pos = self.vars.len();
123        self.vars.push(Builtin::Epsilon.sym());
124        pos
125    }
126
127    pub fn defenv(&mut self, var: SymID, env_idx: usize) {
128        self.statics.insert(var, env_idx);
129    }
130
131    pub fn pop(&mut self, n: usize) {
132        let new_top = self.vars.len() - n;
133        self.vars.truncate(new_top);
134    }
135
136    pub fn get_env_idx(&self, var: SymID) -> Option<usize> {
137        self.statics.get(&var).copied()
138    }
139
140    pub fn get_idx(&self, var: SymID) -> Option<VarIdx> {
141        for (i, &v) in self.vars.iter().enumerate().rev() {
142            if var == v {
143                return Some(i as VarIdx);
144            }
145        }
146        None
147    }
148}
149
150
151#[derive(Debug)]
152pub enum Sym {
153    Id(SymID),
154    Str(String),
155}
156
157/**
158 * Compile Value into R8C code.
159 */
160#[derive(Debug)]
161pub struct R8Compiler {
162    labels: LblMap,
163    code: Vec<R8C>,
164    units: Vec<ChASM<R8C>>,
165    srctbl: SourceList,
166    estack: Vec<Env>,
167    loops: Vec<LoopCtx>,
168    fn_ctxs: Vec<FnCtx>,
169    const_offset: usize,
170    consts: Vec<PV>,
171    code_offset: usize,
172    new_fns: Vec<(Sym, ArgSpec, Vec<SymID>, usize, usize)>,
173    new_envs: Vec<(SymID, usize, usize)>,
174    env: FnvHashMap<SymID, usize>,
175    fns: FnvHashMap<SymID, Func>,
176    unlinked_fns: FnvHashMap<SymID, Vec<usize>>,
177    #[allow(dead_code)]
178    debug_mode: bool,
179}
180
181#[derive(Debug, Clone, Copy)]
182struct FnCtx {
183    start: Lbl,
184}
185
186#[derive(Debug, Clone, Copy)]
187struct LoopCtx {
188    start: Lbl,
189    epilogue: Option<Lbl>,
190    end: Lbl,
191    ret: bool,
192    height: usize,
193}
194
195type VarSet = FnvHashSet<(SymID, BoundVar)>;
196type FnSet = FnvHashMap<SymID, Func>;
197
198struct ClzScoper<'a, 'b> {
199    env: Env,
200    outside: &'a Env,
201    fns: &'b FnSet,
202    lowered: VarSet,
203}
204
205impl Visitor for ClzScoper<'_, '_> {
206    fn visit(&mut self, elem: &mut AST2) -> Result<()> {
207        match elem.kind {
208            M::SymApp(op, _) => if let Some(var) = self.outside.get_idx(op) {
209                self.lowered.insert((op, BoundVar::Local(var)));
210            }
211            M::Let(ref vars, _) => {
212                let len = vars.len();
213                vars.iter().for_each(|VarDecl(var, _, _)| self.env.defvar(*var));
214                elem.visit(self)?;
215                self.env.pop(len);
216                return Ok(());
217            }
218            M::Lambda(ArgList2(_, ref vars), _) => {
219                let len = vars.len();
220                vars.iter().for_each(|(var, _)| self.env.defvar(*var));
221                elem.visit(self)?;
222                self.env.pop(len);
223                return Ok(());
224            }
225            M::Var(var) => {
226                if self.env.get_idx(var).is_some() {
227                } else if let Some(bound) = self.outside.get_idx(var) {
228                    self.lowered.insert((var, BoundVar::Local(bound)));
229                } else if var != Builtin::Nil.sym() && !self.fns.contains_key(&var) {
230                    return err_src!(elem.src.clone(), UndefinedVariable, var);
231                }
232            }
233            _ => ()
234        }
235        elem.visit(self)
236    }
237}
238
239impl ClzScoper<'_, '_> {
240    pub fn scope<'a, 'b>(args: Vec<SymID>,
241                         outside: &Env,
242                         fns: &'b FnSet,
243                         body: impl Iterator<Item = &'a mut AST2>) -> Result<VarSet> {
244        let mut scoper = ClzScoper {
245            lowered: FnvHashSet::default(),
246            env: Env::new(None, args),
247            fns,
248            outside,
249        };
250        for part in body {
251            scoper.visit(part)?;
252        }
253        Ok(scoper.lowered)
254    }
255}
256
257fn add_unlinked(ufns: &mut FnvHashMap<SymID, Vec<usize>>, name: SymID, pos: usize) {
258    match ufns.entry(name) {
259        hash_map::Entry::Occupied(mut e) => e.get_mut().push(pos),
260        hash_map::Entry::Vacant(e) => {e.insert(vec![pos]);}
261    }
262
263}
264
265impl R8Compiler {
266    pub fn new(vm: &R8VM) -> R8Compiler {
267        let mut cc = R8Compiler {
268            const_offset: 0,
269            debug_mode: vm.get_debug_mode(),
270            fn_ctxs: Default::default(),
271            new_fns: Default::default(),
272            estack: Default::default(),
273            labels: Default::default(),
274            consts: Default::default(),
275            loops: Default::default(),
276            srctbl: Default::default(),
277            code: Default::default(),
278            units: Default::default(),
279            new_envs: Default::default(),
280            env: vm.globals.clone(),
281            unlinked_fns: Default::default(),
282            fns: {
283                let mut map: FnvHashMap<SymID, Func> = Default::default();
284                for (sym, funk) in vm.funcs.iter() {
285                    map.insert(*sym, *funk);
286                }
287                map
288            },
289            code_offset: 0,
290        };
291        cc.begin_unit();
292        let none: Option<SymID> = None;
293        cc.enter_fn(None, none.into_iter(), ArgSpec::none());
294        cc.set_offsets(vm);
295        cc
296    }
297
298    pub fn unit(&mut self) -> &mut ChASM<R8C> {
299        self.units.last_mut().expect("No unit to save asm to")
300    }
301
302    pub fn end_unit(&mut self) -> Result<usize> {
303        let len = self.code.len();
304        self.units.pop()
305                  .expect("No unit to end")
306                  .link_into(&mut self.code,
307                             len + self.code_offset,
308                             &mut self.labels)
309    }
310
311    pub fn begin_unit(&mut self) {
312        self.units.push(ChASM::new())
313    }
314
315    pub fn set_source(&mut self, src: Source) {
316        if src.line == 0 { return } // FIXME: This shouldn't be necessary
317        match self.srctbl.last() {
318            Some((_, last_src)) if *last_src == src => (),
319            _ => {
320                let idx = self.unit().len() + self.code_offset;
321                self.srctbl.push((idx, src));
322            }
323        }
324    }
325
326    pub fn enter_fn(&mut self,
327                    name: Option<SymID>,
328                    args: impl Iterator<Item = SymID>,
329                    spec: ArgSpec) {
330        let mut env = Env::none(name);
331        if spec.has_env() {
332            env.defvar(Builtin::LambdaObject.sym());
333        }
334        for arg in args {
335            env.defvar(arg);
336        }
337        env.defvar(Builtin::IP.sym());
338        env.defvar(Builtin::Frame.sym());
339        self.estack.push(env);
340        let start = self.unit().label("fn-begin");
341        self.unit().mark(start);
342        self.fn_ctxs.push(FnCtx { start });
343        if spec.is_special() {
344            self.unit().op(chasm!(SAV 2));
345            if spec.has_opt() {
346                self.unit().op(chasm!(TOP spec.nargs));
347                self.unit().op(chasm!(JV 1, spec.nopt));
348                for _ in 0..spec.nopt {
349                    self.unit().op(chasm!(NIL));
350                }
351            }
352            if spec.has_body() {
353                self.unit().op(chasm!(TOP spec.nargs + spec.nopt));
354                self.unit().op(chasm!(VLT));
355            }
356            if spec.has_env() {
357                self.unit().op(chasm!(ZXP));
358            }
359            self.unit().op(chasm!(RST));
360        }
361    }
362
363    fn leave_fn(&mut self) {
364        self.unit().op(chasm!(RET));
365        self.estack.pop();
366        self.fn_ctxs.pop();
367    }
368
369    fn with_env<T>(&mut self, f: impl Fn(&mut Env) -> T) -> Result<T> {
370        self.estack
371            .last_mut()
372            .map(f)
373            .ok_or_else(|| "No environment".into())
374    }
375
376    #[inline]
377    fn asm_op(&mut self, op: ChOp<r8c::OpName>) {
378        self.unit().op(op);
379    }
380
381    /**
382     * Compiles code that pushes a value onto the stack.
383     *
384     * # Arguments
385     *
386     * - `code` : AST root.
387     *
388     * # Returns
389     *
390     * The statically known stack-index of the value that
391     * will be pushed by `code`.
392     *
393     * # Note
394     *
395     * You need to call `env_pop` when the value is known
396     * to be removed from the stack.
397     */
398    #[inline]
399    fn push(&mut self, code: AST2) -> Result<usize> {
400        self.compile(true, code)?;
401        self.with_env(|env| env.anon())
402    }
403
404    fn pushit(&mut self, args: impl Iterator<Item = AST2>) -> Result<usize> {
405        let mut nargs = 0;
406        for arg in args {
407            nargs += 1;
408            self.push(arg)?;
409        }
410        Ok(nargs)
411    }
412
413    fn env_pop(&mut self, n: usize) -> Result<()> {
414        self.with_env(|env| env.pop(n))
415    }
416
417    /**
418     * Reduces nested applications of `not`
419     *
420     * # Arguments
421     *
422     * - `code` : The root of the AST tree to be reduced.
423     *
424     * # Examples
425     *
426     * - `(not <_>)` ➙ `(true, <_>)`
427     * - `(not (not (not <_>)))` ➙ `(true, <_>)`
428     * - `(not (not (not (not <_>))))` ➙ `(false, <_>)`
429     *
430     * Where the boolean represents whether or not the expression
431     * has been negated.
432     *
433     * # Errors
434     *
435     * Returns an `ArgError` if `not` is given fewer or more than 1 argument.
436     *
437     * # Algorithm
438     *
439     * [Video explanation.](https://youtu.be/ohDB5gbtaEQ?t=65)
440     */
441    pub fn argument_clinic(mut code: AST2) -> (bool, AST2) {
442        let mut flipped = false;
443        while let AST2 { kind: M::Not(sub), .. } = code {
444            flipped = !flipped;
445            code = *sub;
446        }
447        (flipped, code)
448    }
449
450    fn loop_ctx(&self) -> Result<&LoopCtx> {
451        self.loops
452            .last()
453            .ok_or(error!(OutsideContext,
454                          op: Builtin::Break,
455                          ctx: Builtin::Loop))
456    }
457
458    fn loop_epilogue(&self) -> Result<Lbl> {
459        self.loop_ctx().map(|ctx| ctx.epilogue.unwrap_or(ctx.start))
460    }
461
462    fn loop_end(&self) -> Result<Lbl> {
463        self.loop_ctx().map(|ctx| ctx.end)
464    }
465
466    fn loop_simple_jmp_to(&self, op: &M) -> Option<Result<Lbl>> {
467        match (op, self.loop_ctx().map(|x| x.ret).ok()) {
468            (M::Break(None), Some(false)) => Some(self.loop_end()),
469            (M::Next, Some(false)) => Some(self.loop_epilogue()),
470            _ => None
471        }
472    }
473
474    fn bt_if(&mut self, ret: bool,
475             cond: AST2, if_t: Option<AST2>, if_f: Option<AST2>
476    ) -> Result<()> {
477        let if_t = if_t.unwrap_or_else(|| AST2::nil(cond.src.clone()));
478        let (flipped, cond) = R8Compiler::argument_clinic(cond);
479        let src = cond.src.clone();
480        let ((jt, jn), cond) = match cond.kind {
481            M::Eq(u, v) => match (*u, *v) {
482                (AST2 { kind: M::Atom(PV::Int(0)), .. }, v) |
483                (v, AST2 { kind: M::Atom(PV::Int(0)), .. }) => ((JZ, JNZ), v),
484                (u, v) => ((JT, JN),
485                           AST2 { src, kind: M::Eq(Box::new(u), Box::new(v)) }),
486            },
487            _ => ((JT, JN), cond)
488        };
489        self.compile(true, cond)?;
490
491        if let Some(lbl) = self.loop_simple_jmp_to(&if_t.kind) {
492            if flipped {
493                self.unit().op(chasm!(jn lbl?));
494            } else {
495                self.unit().op(chasm!(jt lbl?));
496            }
497            if let Some(if_false) = if_f {
498                self.compile(ret, if_false)?;
499            } else if ret {
500                self.unit().op(chasm!(NIL));
501            }
502        } else {
503            let end_lbl = self.unit().label("if_end");
504            let if_false_lbl = self.unit().label("if_false");
505            let _jmp_loc = if flipped {
506                self.unit().op(chasm!(jt if_false_lbl))
507            } else {
508                self.unit().op(chasm!(jn if_false_lbl))
509            };
510            self.compile(ret, if_t)?;
511            if let Some(if_false) = if_f {
512                self.asm_op(chasm!(JMP end_lbl));
513                self.unit().mark(if_false_lbl);
514                self.compile(ret, if_false)?;
515                self.unit().mark(end_lbl);
516            } else if ret {
517                self.asm_op(chasm!(JMP end_lbl));
518                self.unit().mark(if_false_lbl);
519                self.unit().op(chasm!(NIL));
520                self.unit().mark(end_lbl);
521            } else {
522                self.unit().mark(if_false_lbl);
523            }
524        }
525        Ok(())
526    }
527
528    fn compile_seq(&mut self,
529                   ret: bool,
530                   seq: impl IntoIterator<Item = AST2>
531    ) -> Result<()> {
532        let mut seq = seq.into_iter();
533        let Some(mut last) = seq.next() else {
534            if ret { self.asm_op(chasm!(NIL)) }
535            return Ok(())
536        };
537        for nx in seq {
538            self.compile(false, last)?;
539            last = nx;
540        }
541        self.compile(ret, last)
542    }
543
544    // FIXME: This API is a bit weird.
545    /// `nargs_idx` is the index in op.1 that holds the number
546    /// of arguments. Used in instructions like VCALL, LIST,
547    /// CLZCALL, etc.
548    fn gen_call_nargs(&mut self,
549                      ret: bool,
550                      op: (r8c::OpName, &mut [chasm::Arg]),
551                      nargs_idx: usize,
552                      args: impl Iterator<Item = AST2>) -> Result<()> {
553        let nargs = self.pushit(args)?;
554        op.1[nargs_idx] = nargs.into();
555        self.unit().add(op.0, op.1);
556        self.env_pop(nargs)?;
557        if !ret {
558            self.asm_op(chasm!(POP 1));
559        }
560        Ok(())
561    }
562
563    fn asm_get_var(&mut self,
564                   var: SymID,
565                   src: &Source) -> Result<()> {
566        match self.get_var_idx(var, src)? {
567            BoundVar::Local(idx) => self.unit().op(chasm!(MOV idx)),
568            BoundVar::Env(idx) => self.unit().op(chasm!(GET idx)),
569        };
570        Ok(())
571    }
572
573    fn get_var_idx(&mut self,
574                   var: SymID,
575                   src: &Source) -> Result<BoundVar> {
576        if let Some(idx) = self.with_env(|env| env.get_idx(var))? {
577            return Ok(BoundVar::Local(idx));
578        }
579        for env in self.estack.iter().rev() {
580            if let Some(idx) = env.get_env_idx(var) {
581                return Ok(BoundVar::Env(idx as u32))
582            }
583        }
584        if let Some(idx) = self.env.get(&var) {
585            return Ok(BoundVar::Env(*idx as u32))
586        }
587        err_src!(src.clone(), UndefinedVariable, var)
588    }
589
590    fn asm_set_var_idx(&mut self, idx: &BoundVar) -> Result<()> {
591        match idx {
592            BoundVar::Local(idx) => self.unit().op(chasm!(STR *idx)),
593            BoundVar::Env(idx) => self.unit().op(chasm!(SET *idx)),
594        };
595        Ok(())
596    }
597
598    fn bt_sym_app(&mut self, ret: bool, src: Source, op: SymID, args: Vec<AST2>) -> Result<()> {
599        if let Ok(()) = self.asm_get_var(op, &src) { // Call to closure variable
600            self.with_env(|env| env.anon())?;
601            let args = args.into_iter();
602            self.gen_call_nargs(ret, (r8c::OpName::ZCALL, &mut [0.into()]),
603                                0, args)?;
604            self.env_pop(1).unwrap();
605        } else if self.with_env(|e| e.name)?.map(|s| op == s).unwrap_or_default() {
606            let start = self.fn_ctxs.last().unwrap().start;
607            self.gen_call_nargs(ret, (r8c::OpName::CALL,
608                                      &mut [Arg::AbsLbl(start), 0.into()]),
609                                1, args.into_iter())?;
610        } else { // Call to symbol (virtual call)
611            let idx = self.new_const(PV::Sym(op));
612            self.gen_call_nargs(ret, (r8c::OpName::VCALL,
613                                      &mut [idx.into(), 0.into()]),
614                                1, args.into_iter())?;
615        }
616        Ok(())
617    }
618
619    fn gapp(&mut self, ret: bool, op: AST2, args: Vec<AST2>) -> Result<()> {
620        if !matches!(op.type_of(), Builtin::Unknown | Builtin::Lambda) {
621            return Err(error!(TypeError,
622                              expect: Builtin::Lambda,
623                              got: op.type_of())
624                       .src(op.src).argn(0).bop(Builtin::Apply))
625        }
626        self.compile(true, op)?;
627        self.with_env(|env| env.anon())?; // closure
628        self.gen_call_nargs(ret, (r8c::OpName::ZCALL, &mut [0.into()]),
629                            0, args.into_iter())?;
630        self.env_pop(1)
631    }
632
633    pub fn bt_vec_set(&mut self,
634                      ret: bool,
635                      _src: Source,
636                      dst: AST2,
637                      idx: AST2,
638                      init: AST2) -> Result<()> {
639        self.compile(true, init)?;
640        if ret { self.unit().op(chasm!(DUP)); }
641        self.compile(true, dst)?;
642        self.compile(true, idx)?;
643        self.unit().op(chasm!(VSET));
644        Ok(())
645    }
646
647    pub fn bt_set(&mut self,
648                  ret: bool,
649                  src: Source,
650                  dst: SymID,
651                  init: AST2) -> Result<()> {
652        let bound = self.get_var_idx(dst, &src)?;
653        if let BoundVar::Local(idx) = bound {
654            let mut inplace_op = |op, val: Int| {
655                self.unit().add(op, &[idx.into(), val.into()]);
656                if ret { self.asm_op(chasm!(MOV idx)) }
657            };
658            match init.kind.binary() {
659                // Handle (set x (+ x 2)) => INC x, 2
660                //        (set x (+ 1 x)) => INC x, 1
661                //        (set x (- x 1)) => DEC x, 1
662                //        (set x (- 1 x)) => Not special
663                Some((M2::Add(M::Var(sym), M::Atom(PV::Int(num))), _)) |
664                Some((M2::Add(M::Atom(PV::Int(num)), M::Var(sym)), _))
665                    if *sym == dst => {
666                    inplace_op(INC, *num);
667                    return Ok(())
668                }
669                Some((M2::Sub(M::Var(sym), M::Atom(PV::Int(num))), _))
670                    if *sym == dst => {
671                    inplace_op(DEC, *num);
672                    return Ok(())
673                }
674                Some((M2::Add(M::Var(u), M::Var(v)), (src_u, src_v))) => 'out: {
675                    let (src, src_src) =
676                        if *u == dst { (*v, src_v) }
677                        else if *v == dst { (*u, src_u) }
678                        else { break 'out };
679                    if let BoundVar::Local(src_idx) = self.get_var_idx(src, src_src)? {
680                        self.unit().op(chasm!(ADS idx, src_idx));
681                        return Ok(())
682                    }
683                }
684                Some((M2::Sub(M::Var(u), M::Var(v)), (_, src_v))) if *u == dst => {
685                    if let BoundVar::Local(src_idx) = self.get_var_idx(*v, src_v)? {
686                        self.unit().op(chasm!(SUS idx, src_idx));
687                        return Ok(())
688                    }
689                }
690                _ => ()
691            }
692        }
693        self.compile(true, init)?;
694        if ret { self.asm_op(chasm!(DUP)) }
695        // NOTE!: Currently the variable index has no reason to change
696        //        between the call to get_var_idx and asm_set_var_idx.
697        //        Should that change this will become invalid:
698        self.asm_set_var_idx(&bound)
699    }
700
701    fn lambda(&mut self,
702              name: Option<SymID>,
703              spec: ArgSpec,
704              names: Vec<(SymID, Source)>,
705              prog: impl IntoIterator<Item = AST2>) -> Result<(usize, usize)> {
706        self.begin_unit();
707        self.enter_fn(name, names.into_iter().map(|(s,_)| s), spec);
708        self.compile_seq(true, prog)?;
709        self.leave_fn();
710        let pos = self.code.len() + self.code_offset;
711        let sz = self.end_unit()?;
712        Ok((pos, sz))
713    }
714
715    fn bt_lambda(&mut self,
716                 mut spec: ArgSpec,
717                 names: Vec<(SymID, Source)>,
718                 mut prog: Progn,
719                 _src: Source) -> Result<()> {
720        let mut args: Vec<_> = names.iter().map(|(s,_)| *s).collect();
721        let Some(outside) = self.estack.last() else { unimplemented!() };
722        let lowered = ClzScoper::scope(args.clone(),
723                                       outside,
724                                       &self.fns,
725                                       prog.iter_mut())?;
726        let num = LAMBDA_COUNT.fetch_add(1, Ordering::SeqCst);
727        let name = format!("<λ>-{num}");
728        let mut num = 0;
729        for (var, bound) in lowered.iter() {
730            if let BoundVar::Local(idx) = bound {
731                args.push(*var);
732                self.unit().op(chasm!(MOV *idx));
733                num += 1;
734            }
735        }
736        spec.env = num;
737        self.begin_unit();
738        self.enter_fn(None, args.iter().copied(), spec);
739        for (var, bound) in lowered.iter() {
740            if let BoundVar::Env(idx) = bound {
741                self.with_env(|env| env.defenv(*var, *idx as usize))?;
742            }
743        }
744        self.compile_seq(true, prog)?;
745        if spec.has_env() {
746            self.asm_op(chasm!(ZAV spec.sum_nargs() + 1, spec.env));
747        }
748        self.leave_fn();
749        let pos = self.code.len() + self.code_offset;
750        let sz = self.end_unit()?;
751        self.unit().op(
752            chasm!(ARGS spec.nargs, spec.nopt, spec.env, spec.rest as u8)
753        );
754        self.unit().op(chasm!(CLZ pos, num));
755        self.new_fns.push((Sym::Str(name), spec, args, pos, sz));
756        Ok(())
757    }
758
759    fn popa(&mut self, num: usize) {
760        match self.unit().last_mut() {
761            Some(ChOp { id: r8c::OpName::POPA, ref mut args }) => {
762                args[1].add_mut(num as isize)
763                       .expect("Invalid popa instruction");
764            },
765            _ => { self.unit().op(chasm!(POPA 1, num)); },
766        }
767    }
768
769    fn bt_let(&mut self, ret: bool, decls: Vec<VarDecl>, prog: Progn) -> Result<()> {
770        let len = decls.len();
771        for VarDecl(name, _src, val) in decls.into_iter() {
772            self.compile(true, *val)?;
773            self.with_env(|env| env.defvar(name))?;
774        }
775        self.compile_seq(ret, prog)?;
776        if ret {
777            self.popa(len);
778        } else {
779            self.unit().op(chasm!(POP len));
780        }
781        self.env_pop(len)
782    }
783
784    fn bt_loop(&mut self,
785               ret: bool,
786               seq: impl IntoIterator<Item = AST2>,
787               epl: Option<impl IntoIterator<Item = AST2>>) -> Result<()> {
788        let start = self.unit().label("loop_start");
789        let end = self.unit().label("loop_end");
790        let epl_lbl = if epl.is_some() { Some(self.unit().label("epilogue")) } else { None };
791        let height = self.with_env(|env| env.len())?;
792        self.loops.push(LoopCtx { start, end, epilogue: epl_lbl, ret, height });
793        self.unit().mark(start);
794        self.compile_seq(false, seq)?;
795        if let Some(epl) = epl {
796            self.unit().mark(epl_lbl.unwrap());
797            self.compile_seq(false, epl)?;
798        }
799        self.unit().op(chasm!(JMP start));
800        self.unit().mark(end);
801        self.loops.pop();
802        Ok(())
803    }
804
805    fn bt_break(&mut self, src: Source, arg: Option<Prog>) -> Result<()> {
806        let outer = self.loops
807                        .last()
808                        .copied()
809                        .ok_or(error_src!(src, OutsideContext,
810                                          op: Builtin::Break,
811                                          ctx: Builtin::Loop))?;
812        let LoopCtx { end, ret, height, .. } = outer;
813        let dist = self.with_env(|env| env.len())? - height;
814        let popa = |cc: &mut R8Compiler| if dist > 0 {
815            cc.popa(dist-1);
816        };
817        match arg {
818            Some(code) if ret => {
819                self.compile(true, *code)?;
820                popa(self);
821            }
822            None if ret => {
823                self.asm_op(chasm!(NIL));
824                popa(self);
825            }
826            _ if dist > 0 => self.asm_op(chasm!(POP dist)),
827            _ => ()
828        }
829        self.asm_op(chasm!(JMP end));
830        Ok(())
831    }
832
833    fn bt_loop_next(&mut self, src: Source) -> Result<()> {
834        let outer = self.loops
835                        .last()
836                        .copied()
837                        .ok_or(error_src!(src, OutsideContext,
838                                          op: Builtin::Next,
839                                          ctx: Builtin::Loop))?;
840        let LoopCtx { start, epilogue, height, .. } = outer;
841        let dist = self.with_env(|env| env.len())? - height;
842        self.asm_op(chasm!(POP dist));
843        if let Some(epl) = epilogue {
844            self.asm_op(chasm!(JMP epl));
845        } else {
846            self.asm_op(chasm!(JMP start));
847        }
848        Ok(())
849    }
850
851    fn binop(&mut self,
852             op: Builtin,
853             src: Source,
854             bop: ChOp<r8c::OpName>,
855             one_arg_pre: Option<ChOp<r8c::OpName>>,
856             default: Option<ChOp<r8c::OpName>>,
857             args: impl IntoIterator<Item = AST2>) -> Result<()> {
858        let mut it = args.into_iter().peekable();
859        if let Some(fst) = it.next() {
860            if let (Some(pre), true) = (one_arg_pre, it.peek().is_none()) {
861                self.unit().op(pre);
862                self.with_env(|env| env.anon())?;
863                self.compile(true, fst)?;
864                self.unit().op(bop);
865            } else {
866                self.push(fst)?;
867                for arg in it {
868                    self.compile(true, arg)?;
869                    self.unit().op(bop.clone());
870                }
871            }
872            self.env_pop(1)?;
873        } else if let Some(op) = default {
874            self.unit().op(op);
875        } else {
876            return Err(error!(ArgError,
877                              expect: ArgSpec::rest(1, 0),
878                              got_num: 0)
879                       .src(src).bop(op))
880        }
881        Ok(())
882    }
883
884    /**
885     * Builtin vector push
886     *
887     * NOTE: In Lisp, the argument order is (push <vec> <elem>), but in
888     *       the asm <vec> and <elem> are flipped, because this lets me do
889     *       just a single DUP in cases where VPUSH is expected to return
890     *       a value.
891     */
892    fn bt_vpush(&mut self, ret: bool, vec: AST2, val: AST2) -> Result<()> {
893        self.push(val)?;
894        if ret {
895            self.unit().op(chasm!(DUP));
896        }
897        self.push(vec)?;
898        self.unit().op(chasm!(VPUSH));
899        self.env_pop(2)
900    }
901
902    fn bt_next(&mut self, ret: bool, arg: AST2) -> Result<()> {
903        let AST2 { src, kind } = arg;
904        match kind {
905            M::Var(var) => {
906                let bound = self.get_var_idx(var, &src)?;
907                match bound {
908                    BoundVar::Local(idx) => self.asm_op(chasm!(NXT idx)),
909                    BoundVar::Env(idx) => {
910                        self.asm_op(chasm!(GET idx));
911                        let idx = self.with_env(|env| env.anon())?;
912                        self.asm_op(chasm!(NXT idx));
913                        self.popa(1);
914                        self.env_pop(1)?;
915                    }
916                }
917            }
918            init => {
919                self.compile(true, AST2 { kind: init, src })?;
920                let idx = self.with_env(|env| env.anon())?;
921                self.asm_op(chasm!(NXT idx));
922                self.popa(1);
923                self.env_pop(1)?;
924            }
925        };
926        if !ret {
927            self.asm_op(chasm!(POP 1));
928        }
929        Ok(())
930    }
931
932    fn bt_and(&mut self, ret: bool, args: impl IntoIterator<Item = AST2>) -> Result<()> {
933        let mut it = args.into_iter().peekable();
934        if it.peek().is_none() {
935            if ret {
936                self.unit().op(chasm!(BOOL 1));
937            }
938            return Ok(());
939        }
940        let end_l = self.unit().label("and_end");
941        let and_exit = self.unit().label("and_early_exit");
942        while let Some(part) = it.next() {
943            let (flip, part) = R8Compiler::argument_clinic(part);
944            self.compile(it.peek().is_some() || ret, part)?;
945            if flip {
946                self.unit().op(chasm!(JT and_exit));
947            } else {
948                self.unit().op(chasm!(JN and_exit));
949            }
950        }
951        self.unit().pop();
952        if ret {
953            self.unit().op(chasm!(JMP end_l));
954            self.unit().mark(and_exit);
955            self.unit().op(chasm!(NIL));
956            self.unit().mark(end_l);
957        } else {
958            self.unit().mark(and_exit);
959        }
960        Ok(())
961    }
962
963    fn bt_or(&mut self, ret: bool, args: impl IntoIterator<Item = AST2>) -> Result<()> {
964        let mut it = args.into_iter().peekable();
965        if it.peek().is_none() {
966            if ret {
967                self.unit().op(chasm!(BOOL 0));
968            }
969            return Ok(());
970        }
971        let end_l = self.unit().label("or_end");
972        while let Some(part) = it.next() {
973            let (flip, part) = R8Compiler::argument_clinic(part);
974            self.compile(it.peek().is_some() || ret, part)?;
975            if ret { self.unit().op(chasm!(DUP)); }
976            if flip {
977                self.unit().op(chasm!(JN end_l));
978            } else {
979                self.unit().op(chasm!(JT end_l));
980            }
981            if ret { self.unit().op(chasm!(POP 1)); }
982        }
983        if ret { self.unit().op(chasm!(NIL)); }
984        self.unit().mark(end_l);
985        Ok(())
986    }
987
988    pub fn new_const(&mut self, pv: PV) -> usize {
989        let idx = self.const_offset + self.consts.len();
990        self.consts.push(pv);
991        idx
992    }
993
994    fn atom(&mut self, pv: PV) -> Result<()> {
995        let op = match pv {
996            PV::Bool(true) => chasm!(BOOL 1),
997            PV::Bool(false) => chasm!(BOOL 0),
998            PV::Char(c) => chasm!(CHR c as u32),
999            PV::Int(i) => chasm!(INT i),
1000            PV::Real(r) => chasm!(FLT r.to_bits()),
1001            PV::Nil => chasm!(NIL),
1002            pv => {
1003                let idx = self.new_const(pv);
1004                if pv.bt_type_of() == Builtin::String {
1005                    chasm!(GET idx) // Strings are immutable, no need to clone
1006                } else {
1007                    chasm!(INS idx)
1008                }
1009            }
1010        };
1011        self.unit().op(op);
1012        Ok(())
1013    }
1014
1015    fn bt_append(&mut self, ret: bool, mut xs: Vec<AST2>, src: Source) -> Result<()> {
1016        if xs.iter().all(|e| matches!(e.kind, M::List(..))) {
1017            let ast = AST2 {
1018                src,
1019                kind: M::List(xs.into_iter().flat_map(|xs| if let M::List(src) = xs.kind {
1020                    src.into_iter()
1021                } else {
1022                    unreachable!()
1023                }).collect())
1024            };
1025            self.compile(ret, ast)
1026        } else {
1027            if let Some(mut i) = xs.iter().position(|m| matches!(m.kind, M::List(..))) {
1028                for j in (i+1)..xs.len() {
1029                    match xs[j].kind {
1030                        M::List(..) => {
1031                            let (pre, post) = xs.split_at_mut(j);
1032                            let M::List(dst) = &mut pre[i].kind else {
1033                                i = j;
1034                                continue
1035                            };
1036                            let M::List(src) = &mut post[0].kind else { unreachable!() };
1037                            dst.append(src);
1038                        }
1039                        _ => i = j,
1040                    }
1041                }
1042                xs.retain(|s| if let M::List(xs) = &s.kind {
1043                    !xs.is_empty()
1044                } else { true });
1045            }
1046            let nargs = xs.len();
1047            for arg in xs.into_iter() {
1048                self.compile(ret, arg)?;
1049            }
1050            if ret { self.unit().op(chasm!(APN nargs)); }
1051            Ok(())
1052        }
1053    }
1054
1055    pub fn bt1(&mut self, ret: bool, op: Builtin, arg: AST2) -> Result<()> {
1056        def_macros!($, ret, self);
1057
1058        match op {
1059            Builtin::Len => opcall!(LEN arg),
1060            x => unimplemented!("{x:?}"),
1061        }
1062
1063        Ok(())
1064    }
1065
1066    pub fn bt2(&mut self, ret: bool, op: Builtin, a0: AST2, a1: AST2) -> Result<()> {
1067        def_macros!($, ret, self);
1068
1069        match op {
1070            Builtin::Apply => opcall_mut!(APL a0, a1),
1071            Builtin::LoopWithEpilogue => self.bt_loop(ret,
1072                                                      Some(a0).into_iter(),
1073                                                      Some(Some(a1).into_iter()))?,
1074            x => unimplemented!("{x:?}"),
1075        }
1076
1077        Ok(())
1078    }
1079
1080    fn compile(&mut self, ret: bool, AST2 { kind, src }: AST2) -> Result<()> {
1081        def_macros!($, ret, self);
1082
1083        self.set_source(src.clone());
1084
1085        match kind {
1086            M::Var(var) => match self.get_var_idx(var, &src) {
1087                Ok(BoundVar::Local(idx)) => asm!(MOV idx),
1088                Ok(BoundVar::Env(idx)) => asm!(GET idx),
1089                Err(e) => match self.fns.get(&var) {
1090                    Some(funk) => {
1091                        let s = funk.args;
1092                        let pos = funk.pos;
1093                        asm!(ARGS s.nargs, s.nopt, 0, s.rest as u8);
1094                        asm!(CLZ pos, 0);
1095                    }
1096                    _ => return Err(e)
1097                }
1098            },
1099            M::If(cond, if_t, if_f) =>
1100                self.bt_if(ret, *cond, if_t.map(|v| *v), if_f.map(|v| *v))?,
1101            M::Atom(pv) => if ret { self.atom(pv)? },
1102            M::Progn(seq) => self.compile_seq(ret, seq.into_iter())?,
1103            M::SymApp(op, args) => self.bt_sym_app(ret, src, op, args)?,
1104            M::App(op, args) => self.gapp(ret, *op, args)?,
1105            M::Lambda(ArgList2(spec, names), progn) =>
1106                self.bt_lambda(spec, names, progn, src)?,
1107            M::Defvar(sym, init) => {
1108                let spec = ArgSpec::none();
1109                let (pos, sz) = self.lambda(None, spec, vec![], Some(*init))?;
1110                let num = DEFVAR_COUNT.fetch_add(1, Ordering::SeqCst);
1111                let name = format!("<δ>-{num}");
1112                self.new_fns.push((Sym::Str(name), spec, vec![], pos, sz));
1113                let idx = self.new_const(PV::Nil);
1114                self.new_envs.push((sym, idx, pos));
1115                self.env.insert(sym, idx);
1116                if ret {
1117                    asm!(GET idx);
1118                }
1119            },
1120            M::VecSet(dst, idx, init) => self.bt_vec_set(ret, src, *dst, *idx, *init)?,
1121            M::Set(dst, init) => self.bt_set(ret, src, dst, *init)?,
1122            M::Defun(name, ArgList2(spec, names), progn) => {
1123                let syms = names.iter().map(|(s,_)| *s).collect();
1124                let (pos, sz) = self.lambda(Some(name), spec, names, progn)?;
1125                self.new_fns.push((Sym::Id(name), spec, syms, pos, sz));
1126                self.fns.insert(name, Func { pos, sz, args: spec });
1127                if ret {
1128                    asm!(ARGS spec.nargs, spec.nopt, 0, spec.rest as u8);
1129                    asm!(CLZ pos, 0);
1130                }
1131            },
1132            M::Let(decls, progn) => {
1133                self.bt_let(ret, decls, progn)?
1134            },
1135            M::Loop(prog) => {
1136                let s: Option<iter::Empty<AST2>> = None;
1137                self.bt_loop(ret, prog, s)?
1138            },
1139            M::Break(arg) => self.bt_break(src, arg)?,
1140            M::Next => self.bt_loop_next(src)?,
1141            M::Throw(arg) => {
1142                self.compile(true, *arg)?;
1143                asm!(UWND);
1144            },
1145            M::Not(x) => opcall!(NOT *x),
1146            M::Gt(x, y) => opcall!(GT *x, *y),
1147            M::Gte(x, y) => opcall!(GTE *x, *y),
1148            M::Lt(x, y) => opcall!(LT *x, *y),
1149            M::Lte(x, y) => opcall!(LTE *x, *y),
1150            M::Eq(x, y) => opcall!(EQL *x, *y),
1151            M::Eqp(x, y) => opcall!(EQP *x, *y),
1152            M::Add(args) if ret => self.binop(Builtin::Add, src, chasm!(ADD),
1153                                              None, Some(chasm!(INT 0)), args)?,
1154            M::Add(args) => vopcall!(NIL args),
1155            M::Sub(args) if ret => self.binop(Builtin::Sub, src, chasm!(SUB),
1156                                              Some(chasm!(INT 0)), None, args)?,
1157            M::Sub(args) => vopcall!(NIL args),
1158            M::Mul(args) if ret => self.binop(Builtin::Mul, src, chasm!(MUL),
1159                                              None, Some(chasm!(INT 1)), args)?,
1160            M::Mul(args) => vopcall!(NIL args),
1161            M::Div(args) if ret => self.binop(Builtin::Div, src, chasm!(DIV),
1162                                              Some(chasm!(FLT 1.0_f32.to_bits())), None, args)?,
1163            M::Div(args) => vopcall!(NIL args),
1164            M::And(args) => self.bt_and(ret, args)?,
1165            M::Or(args) => self.bt_or(ret, args)?,
1166
1167            M::NextIter(it) => self.bt_next(ret, *it)?,
1168            M::Car(x) => opcall!(CAR *x),
1169            M::Cdr(x) => opcall!(CDR *x),
1170            M::Cons(x, y) => opcall!(CNS *x, *y),
1171            M::List(xs) => vopcall!(LST xs),
1172            M::Append(xs) => self.bt_append(ret, xs, src)?,
1173            M::Vector(xs) => vopcall!(VEC xs),
1174            M::Push(vec, elem) => self.bt_vpush(ret, *vec, *elem)?,
1175            M::Get(vec, idx) => opcall!(VGET *vec, *idx),
1176            M::Pop(vec) => opcall_mut!(VPOP *vec),
1177            M::CallCC(funk) => {
1178                self.compile(true, *funk)?;
1179                asm!(CCONT 0);
1180                if !ret { asm!(POP 1) }
1181            }
1182            M::Bt1(op, arg) => self.bt1(ret, op, *arg)?,
1183            M::Bt2(op, a0, a1) => self.bt2(ret, op, *a0, *a1)?,
1184            M::TailCall(_args) => todo!(),
1185        }
1186
1187        Ok(())
1188    }
1189
1190    pub fn compile_top(&mut self, code: AST2) -> Result<()> {
1191        self.compile(false, code)
1192    }
1193
1194    pub fn compile_top_tail(&mut self, code: AST2) -> Result<usize> {
1195        let num = MODULE_COUNT.fetch_add(1, Ordering::SeqCst);
1196        let name = format!("<σ>-{num}");
1197        self.compile(true, code)?;
1198        self.leave_fn();
1199        let pos = self.code.len() + self.code_offset;
1200        let sz = self.end_unit()?;
1201        self.new_fns.push((Sym::Str(name), ArgSpec::none(), vec![], pos, sz));
1202        Ok(pos)
1203    }
1204
1205    pub fn take(&mut self, vm: &mut R8VM) -> Result<()> {
1206        vm.mem.env.append(&mut self.consts);
1207        let mut ufn = mem::take(&mut self.unlinked_fns);
1208        for (i, op) in self.code.iter_mut().enumerate() {
1209            *op = match *op {
1210                R8C::VCALL(idx, nargs) => {
1211                    let sym = vm.mem.get_env(idx as usize).sym().unwrap();
1212                    match vm.get_func(sym) {
1213                        Some(funk) => {
1214                            funk.args.check(nargs).map_err(|e| e.op(sym))?;
1215                            R8C::CALL(funk.pos.try_into()?, nargs)
1216                        }
1217                        None => {
1218                            add_unlinked(&mut ufn, sym, self.code_offset + i);
1219                            *op
1220                        }
1221                    }
1222                },
1223                op => op
1224            };
1225        }
1226        self.unlinked_fns = ufn;
1227        vm.pmem.append(&mut self.code);
1228        vm.srctbl.append(&mut self.srctbl);
1229        vm.labels.extend(self.labels.drain());
1230        for (name, spec, names, pos, sz) in self.new_fns.drain(..) {
1231            let name = match name {
1232                Sym::Id(id) => id,
1233                Sym::Str(s) => vm.mem.symdb.put(s).id(),
1234            };
1235            vm.defun(name, spec, names, pos, sz);
1236        }
1237        for (name, idx, init_pos) in self.new_envs.drain(..) {
1238            vm.defvar(name, idx, init_pos)?;
1239        }
1240        self.set_offsets(vm);
1241        Ok(())
1242    }
1243
1244    pub fn set_offsets(&mut self, vm: &R8VM) {
1245        self.code_offset = vm.pmem.len();
1246        self.const_offset = vm.mem.env.len();
1247    }
1248}