cao_lang/
compiler.rs

1//! The compiler module that transforms [CaoProgram] into bytecode.
2//!
3mod card;
4mod compilation_error;
5mod compile_options;
6mod function;
7mod module;
8
9mod function_ir;
10#[cfg(test)]
11mod tests;
12
13use crate::{
14    bytecode::{encode_str, write_to_vec},
15    collections::{handle_table::Handle, hash_map::CaoHashMap},
16    compiled_program::{CaoCompiledProgram, Label},
17    prelude::Trace,
18    Instruction, VariableId,
19};
20use core::slice;
21use std::borrow::Cow;
22use std::convert::TryFrom;
23use std::fmt::Debug;
24use std::mem;
25use std::{convert::TryInto, str::FromStr};
26
27pub use card::*;
28pub use compilation_error::*;
29pub use compile_options::*;
30pub use function::*;
31pub use module::*;
32
33use self::function_ir::FunctionIr;
34
35pub type CompilationResult<T> = Result<T, CompilationError>;
36
37/// Intermediate representation of a Cao-Lang program.
38///
39/// Execution will begin with the first Function
40pub(crate) type FunctionSlice<'a> = &'a [FunctionIr];
41pub(crate) type NameSpace = smallvec::SmallVec<[Box<str>; 1]>;
42pub(crate) type ImportsIr = std::collections::HashMap<String, String>;
43pub(crate) type Locals<'a> = arrayvec::ArrayVec<Local<'a>, 255>;
44type Upvalues = arrayvec::ArrayVec<Upvalue, 255>;
45
46pub struct Compiler<'a> {
47    options: CompileOptions,
48    program: CaoCompiledProgram,
49    next_var: VariableId,
50
51    /// maps functions to their metadata
52    jump_table: CaoHashMap<String, FunctionMeta>,
53
54    current_namespace: Cow<'a, NameSpace>,
55    current_imports: Cow<'a, ImportsIr>,
56    locals: Vec<Locals<'a>>,
57    upvalues: Vec<Upvalues>,
58    scope_depth: Vec<i32>,
59    current_index: CardIndex,
60    function_id: usize,
61}
62
63#[derive(Debug, Clone, Copy)]
64struct FunctionMeta {
65    pub hash_key: Handle,
66    /// number of arguments
67    pub arity: u32,
68}
69
70/// local variables during compilation
71#[derive(Debug)]
72pub(crate) struct Local<'a> {
73    pub name: &'a str,
74    pub depth: i32,
75    pub captured: bool,
76}
77
78pub fn compile(
79    compilation_unit: CaoProgram,
80    compile_options: impl Into<Option<CompileOptions>>,
81) -> CompilationResult<CaoCompiledProgram> {
82    let options = compile_options.into().unwrap_or_default();
83    let compilation_unit = compilation_unit
84        .into_ir_stream(options.recursion_limit)
85        .map_err(|err| CompilationError::with_loc(err, Trace::default()))?;
86
87    let mut compiler = Compiler::new();
88    compiler.compile(&compilation_unit, options)
89}
90
91impl<'a> Default for Compiler<'a> {
92    fn default() -> Self {
93        Self::new()
94    }
95}
96
97enum Variable {
98    Global,
99    Local(usize),
100    /// Upvalues are captured variables from enclosing scopes
101    Upvalue(usize),
102}
103
104#[derive(Default, Clone, Copy, Debug)]
105struct Upvalue {
106    is_local: bool,
107    index: u8,
108}
109
110impl<'a> Compiler<'a> {
111    pub fn new() -> Self {
112        Compiler {
113            options: Default::default(),
114            program: CaoCompiledProgram::default(),
115            next_var: VariableId(0),
116            jump_table: Default::default(),
117            current_namespace: Default::default(),
118            locals: vec![Default::default()],
119            upvalues: vec![Default::default()],
120            scope_depth: vec![0],
121            current_index: CardIndex::default(),
122            current_imports: Default::default(),
123            function_id: 0,
124        }
125    }
126
127    fn trace(&self) -> Trace {
128        Trace {
129            namespace: self.current_namespace.clone().into_owned(),
130            index: self.current_index.clone(),
131        }
132    }
133
134    pub fn compile(
135        &mut self,
136        compilation_unit: FunctionSlice<'a>,
137        compile_options: CompileOptions,
138    ) -> CompilationResult<CaoCompiledProgram> {
139        self.options = compile_options;
140        if compilation_unit.is_empty() {
141            return Err(CompilationError::with_loc(
142                CompilationErrorPayload::EmptyProgram,
143                self.trace(),
144            ));
145        }
146        self.program = CaoCompiledProgram::default();
147        self.next_var = VariableId(0);
148        self.compile_stage_1(compilation_unit)?;
149        self.compile_stage_2(compilation_unit)?;
150
151        self.current_imports = Default::default();
152        // the last instruction is a trap for native to cao-lang function calls
153        self.push_instruction(Instruction::Exit);
154        Ok(mem::take(&mut self.program))
155    }
156
157    fn error(&self, pl: CompilationErrorPayload) -> CompilationError {
158        CompilationError::with_loc(pl, self.trace())
159    }
160
161    /// build the jump table and consume the function names
162    fn compile_stage_1(&mut self, compilation_unit: FunctionSlice) -> CompilationResult<()> {
163        let mut num_cards = 0usize;
164        for n in compilation_unit.iter() {
165            num_cards += n.cards.len();
166            self.add_function(n)?;
167        }
168
169        self.program.labels.0.reserve(num_cards).expect("reserve");
170        Ok(())
171    }
172
173    fn add_function(&mut self, n: &FunctionIr) -> CompilationResult<()> {
174        let metadata = FunctionMeta {
175            hash_key: n.handle,
176            arity: n.arguments.len() as u32,
177        };
178        if self.jump_table.contains(n.name.as_ref()) {
179            return Err(self.error(CompilationErrorPayload::DuplicateName(n.name.to_string())));
180        }
181        self.jump_table.insert(n.full_name(), metadata).unwrap();
182        Ok(())
183    }
184
185    /// consume function cards and build the bytecode
186    fn compile_stage_2(&mut self, compilation_unit: FunctionSlice<'a>) -> CompilationResult<()> {
187        let mut functions = compilation_unit.iter();
188
189        if let Some(main_function) = functions.next() {
190            let il = main_function.function_index;
191            let len: u32 = match main_function.cards.len().try_into() {
192                Ok(i) => i,
193                Err(_) => return Err(self.error(CompilationErrorPayload::TooManyCards(il))),
194            };
195            self.current_index = CardIndex::new(il, 0);
196            self.scope_begin();
197            self.process_function(main_function)?;
198            self.current_index = CardIndex {
199                function: il,
200                card_index: FunctionCardIndex {
201                    indices: smallvec::smallvec![len],
202                },
203            };
204            self.scope_end();
205            // insert explicit exit after the first function
206            self.process_card(&Card::Abort)?;
207        }
208
209        for function in functions {
210            let il = function.function_index;
211            self.current_index = CardIndex::function(il);
212            let nodeid_handle = function.handle;
213            let handle = u32::try_from(self.program.bytecode.len())
214                .expect("bytecode length to fit into 32 bits");
215            self.program
216                .labels
217                .0
218                .insert(nodeid_handle, Label::new(handle))
219                .unwrap();
220
221            self.scope_begin();
222            self.process_function(function)?;
223            self.scope_end();
224            self.push_instruction(Instruction::ScalarNil);
225            self.emit_return()?;
226        }
227
228        Ok(())
229    }
230
231    /// begin nested compile sequence
232    fn compile_begin(&mut self) {
233        self.function_id += 1;
234        self.locals.push(Default::default());
235        self.upvalues.push(Default::default());
236        self.scope_depth.push(0);
237    }
238
239    /// end nested compile sequence
240    fn compile_end(&mut self) {
241        self.function_id -= 1;
242        self.locals.pop();
243        self.upvalues.pop();
244        self.scope_depth.pop();
245    }
246
247    fn scope_begin(&mut self) {
248        *self.scope_depth.last_mut().unwrap() += 1;
249    }
250
251    fn scope_depth(&self) -> i32 {
252        *self.scope_depth.last().unwrap()
253    }
254
255    fn scope_depth_mut(&mut self) -> &mut i32 {
256        self.scope_depth.last_mut().unwrap()
257    }
258
259    fn scope_end(&mut self) {
260        *self.scope_depth_mut() -= 1;
261        let scope_depth = self.scope_depth();
262        let locals = &mut self.locals[self.function_id];
263        while locals
264            .last()
265            .map(|l| l.depth > scope_depth)
266            .unwrap_or(false)
267        {
268            let var = locals.pop().unwrap();
269            if var.captured {
270                self.program.bytecode.push(Instruction::CloseUpvalue as u8);
271            } else {
272                self.program.bytecode.push(Instruction::Pop as u8);
273            }
274        }
275    }
276
277    /// add a local variable
278    ///
279    /// return its index
280    fn add_local(&mut self, name: &'a str) -> CompilationResult<u32> {
281        self.validate_var_name(name)?;
282        self.add_local_unchecked(name)
283    }
284
285    fn add_upvalue(
286        &mut self,
287        index: u8,
288        is_local: bool,
289        function_id: usize,
290    ) -> CompilationResult<usize> {
291        let upvalues = &mut self.upvalues[function_id];
292        for (i, val) in upvalues.iter().enumerate() {
293            if val.index == index && val.is_local == is_local {
294                // do not add duplicates
295                return Ok(i);
296            }
297        }
298        let i = upvalues.len();
299        upvalues.push(Upvalue { is_local, index });
300        Ok(i)
301    }
302
303    fn add_local_unchecked(&mut self, name: &'a str) -> CompilationResult<u32> {
304        let depth = self.scope_depth();
305        let locals = self.locals.last_mut().unwrap();
306        let result = locals.len();
307        locals
308            .try_push(Local {
309                name,
310                depth,
311                captured: false,
312            })
313            .map_err(|_| self.error(CompilationErrorPayload::TooManyLocals))?;
314        Ok(result as u32)
315    }
316
317    fn process_function(
318        &mut self,
319        FunctionIr {
320            arguments,
321            cards,
322            namespace,
323            imports,
324            ..
325        }: &'a FunctionIr,
326    ) -> CompilationResult<()> {
327        self.current_namespace = Cow::Borrowed(namespace);
328        self.current_imports = Cow::Borrowed(imports);
329
330        // at runtime: pop arguments reverse order as the variables were declared
331        for param in arguments.iter().rev() {
332            self.add_local(param.as_str())?;
333        }
334        for (ic, card) in cards.iter().enumerate() {
335            // valid indices always have 1 subindex, so replace that
336            self.current_index.pop_subindex();
337            self.current_index.push_subindex(ic as u32);
338            self.process_card(card)?;
339        }
340        Ok(())
341    }
342
343    fn emit_return(&mut self) -> CompilationResult<()> {
344        self.push_instruction(Instruction::Return);
345        Ok(())
346    }
347
348    /// encodes the then block (card), and conditionally jumps over it using the `skip_instr`
349    fn encode_if_then(
350        &mut self,
351        skip_instr: Instruction,
352        then: impl FnOnce(&mut Self) -> CompilationResult<()>,
353    ) -> CompilationResult<()> {
354        type Pos = i32;
355        debug_assert!(
356            matches!(
357                skip_instr,
358                Instruction::GotoIfTrue | Instruction::GotoIfFalse
359            ),
360            "invalid skip instruction"
361        );
362        self.push_instruction(skip_instr);
363        let idx = self.program.bytecode.len();
364        write_to_vec(0 as Pos, &mut self.program.bytecode);
365
366        // write the `then` block
367        then(self)?;
368
369        unsafe {
370            let ptr = self.program.bytecode.as_mut_ptr().add(idx) as *mut Pos;
371            std::ptr::write_unaligned(ptr, self.program.bytecode.len() as Pos);
372        };
373        Ok(())
374    }
375
376    fn encode_jump(&mut self, function: &str) -> CompilationResult<()> {
377        let to = self.resolve_function(&self.jump_table, function)?;
378        write_to_vec(to.hash_key, &mut self.program.bytecode);
379        write_to_vec(to.arity, &mut self.program.bytecode);
380        Ok(())
381    }
382
383    // take jump_table by param because of lifetimes
384    fn resolve_function<'b>(
385        &self,
386        jump_table: &'b CaoHashMap<String, FunctionMeta>,
387        function: &str,
388    ) -> CompilationResult<&'b FunctionMeta> {
389        // attempt to look up the function by name
390        let mut to = jump_table.get(function);
391        if to.is_none() {
392            // attempt to look up the function in the current namespace
393
394            // current_namespace.join('.').push('.').push_str(function.0)
395            let name = self
396                .current_namespace
397                .iter()
398                .flat_map(|x| [x.as_ref(), "."])
399                .chain(std::iter::once(function))
400                .collect::<String>();
401
402            to = jump_table.get(&name);
403        }
404        if to.is_none() {
405            // attempt to look up function in the imports
406            if let Some((_, alias)) = self
407                .current_imports
408                .iter()
409                .find(|(import, _)| *import == function)
410            {
411                let (super_depth, suffix) = super_depth(alias);
412                let name = self
413                    .current_namespace
414                    .iter()
415                    .take(self.current_namespace.len() - super_depth)
416                    .flat_map(|x| [x.as_ref(), "."])
417                    .chain(std::iter::once(suffix.unwrap_or(alias)))
418                    .collect::<String>();
419
420                to = jump_table.get(&name);
421            }
422        }
423        if to.is_none() {
424            // attempt to look up by imported module
425            if let Some((prefix, suffix)) = function.split_once('.') {
426                if let Some((_, alias)) = self
427                    .current_imports
428                    .iter()
429                    .find(|(import, _)| *import == prefix)
430                {
431                    // namespace.alias.suffix
432                    let (super_depth, s) = super_depth(alias);
433
434                    let name = self
435                        .current_namespace
436                        .iter()
437                        .take(self.current_namespace.len() - super_depth)
438                        .flat_map(|x| [x.as_ref(), "."])
439                        .chain([alias, ".", s.unwrap_or(suffix)].iter().copied())
440                        .collect::<String>();
441
442                    to = jump_table.get(&name);
443                }
444            }
445        }
446        to.ok_or_else(|| {
447            self.error(CompilationErrorPayload::InvalidJump {
448                dst: function.to_string(),
449                msg: None,
450            })
451        })
452    }
453
454    fn push_str(&mut self, data: &str) {
455        let handle = self.program.data.len() as u32;
456        write_to_vec(handle, &mut self.program.bytecode);
457        encode_str(data, &mut self.program.data);
458    }
459
460    /// function_id = index in the *offset array
461    fn resolve_upvalue(&mut self, name: &str, function_id: usize) -> CompilationResult<Variable> {
462        if function_id == 0 {
463            return Ok(Variable::Global);
464        }
465
466        let locals = &mut self.locals[function_id - 1];
467        // try to find in the locals of the parent function
468        for (i, local) in locals.iter_mut().enumerate() {
469            if local.name == name {
470                local.captured = true;
471                return self
472                    .add_upvalue(i as u8, true, function_id)
473                    .map(Variable::Upvalue);
474            }
475        }
476        if function_id == 0 {
477            Ok(Variable::Global)
478        } else {
479            let val = self.resolve_upvalue(name, function_id - 1)?;
480            if let Variable::Upvalue(i) = &val {
481                // if the variable is an upvalue in the enclosing function add a non-local upvalue to
482                // this function
483                let i = self.add_upvalue(*i as u8, false, function_id)?;
484                return Ok(Variable::Upvalue(i));
485            }
486            Ok(val)
487        }
488    }
489
490    fn resolve_var(&mut self, name: &str) -> CompilationResult<Variable> {
491        self.validate_var_name(name)?;
492
493        for (i, local) in self.locals[self.function_id].iter_mut().enumerate().rev() {
494            if local.name == name {
495                return Ok(Variable::Local(i));
496            }
497        }
498        self.resolve_upvalue(name, self.function_id)
499    }
500
501    fn process_card(&mut self, card: &'a Card) -> CompilationResult<()> {
502        let card_byte_index = u32::try_from(self.program.bytecode.len())
503            .expect("Expected bytecode length to fit into 32 bits");
504        let nodeid_hash = self.current_index.as_handle();
505        self.program
506            .labels
507            .0
508            .insert(nodeid_hash, Label::new(card_byte_index))
509            .unwrap();
510
511        match card {
512            Card::CompositeCard(comp) => {
513                for (i, card) in comp.cards.iter().enumerate() {
514                    self.current_index.push_subindex(i as u32);
515                    self.process_card(card)?;
516                    self.current_index.pop_subindex()
517                }
518            }
519            Card::ForEach(fe) => {
520                let ForEach {
521                    i,
522                    k,
523                    v,
524                    iterable: variable,
525                    body,
526                } = fe.as_ref();
527                self.current_index.push_subindex(0);
528                self.process_card(variable)?;
529                self.current_index.pop_subindex();
530
531                self.scope_begin();
532                let loop_var = self.add_local_unchecked("")?;
533                let loop_item = self.add_local_unchecked("")?;
534                // ForEach instruction will push these values on the stack
535                let v_index = self.add_local_unchecked("")?;
536                let k_index = self.add_local_unchecked("")?;
537                let i_index = self.add_local_unchecked("")?;
538                self.push_instruction(Instruction::BeginForEach);
539                write_to_vec(loop_var, &mut self.program.bytecode);
540                write_to_vec(loop_item, &mut self.program.bytecode);
541                write_to_vec(i_index, &mut self.program.bytecode);
542                write_to_vec(k_index, &mut self.program.bytecode);
543                write_to_vec(v_index, &mut self.program.bytecode);
544
545                let block_begin = self.program.bytecode.len() as i32;
546                self.push_instruction(Instruction::ForEach);
547                write_to_vec(loop_var, &mut self.program.bytecode);
548                write_to_vec(loop_item, &mut self.program.bytecode);
549                write_to_vec(i_index, &mut self.program.bytecode);
550                write_to_vec(k_index, &mut self.program.bytecode);
551                write_to_vec(v_index, &mut self.program.bytecode);
552                self.encode_if_then(Instruction::GotoIfFalse, |c| {
553                    c.scope_begin();
554                    if let Some(v) = v {
555                        let v = c.add_local(v)?;
556                        c.read_local_var(v_index);
557                        c.write_local_var(v);
558                    }
559                    if let Some(k) = k {
560                        let k = c.add_local(k)?;
561                        c.read_local_var(k_index);
562                        c.write_local_var(k);
563                    }
564                    if let Some(i) = i {
565                        let i = c.add_local(i)?;
566                        c.read_local_var(i_index);
567                        c.write_local_var(i);
568                    }
569                    c.current_index.push_subindex(1);
570                    c.process_card(body)?;
571                    c.current_index.pop_subindex();
572                    c.scope_end();
573                    // return to the foreach instruction
574                    c.push_instruction(Instruction::Goto);
575                    write_to_vec(block_begin, &mut c.program.bytecode);
576                    Ok(())
577                })?;
578                self.scope_end();
579            }
580            Card::While(children) => {
581                let [condition, body] = &**children;
582                let block_begin = self.program.bytecode.len() as i32;
583                self.current_index.push_subindex(0);
584                self.process_card(condition)?;
585                self.current_index.pop_subindex();
586                self.current_index.push_subindex(1);
587                // if false jump over the body block
588                self.encode_if_then(Instruction::GotoIfFalse, |c| {
589                    // if true execute body and jump to block_begin
590                    c.process_card(body)?;
591                    c.push_instruction(Instruction::Goto);
592                    write_to_vec(block_begin, &mut c.program.bytecode);
593                    Ok(())
594                })?;
595                self.current_index.pop_subindex();
596            }
597            Card::Repeat(rep) => {
598                self.current_index.push_subindex(0);
599                self.compile_subexpr(slice::from_ref(&rep.n))?;
600                self.current_index.pop_subindex();
601                let i = &rep.i;
602                let repeat = &rep.body;
603                self.scope_begin();
604                let loop_n_index = self.add_local_unchecked("")?;
605                let loop_counter_index = self.add_local_unchecked("")?;
606                self.write_local_var(loop_n_index);
607                // init counter to 0
608                self.process_card(&Card::ScalarInt(0))?;
609                self.write_local_var(loop_counter_index);
610
611                let block_begin = self.program.bytecode.len() as i32;
612                // loop condition
613                self.read_local_var(loop_counter_index);
614                self.read_local_var(loop_n_index);
615                self.push_instruction(Instruction::Less);
616                // loop body
617                self.encode_if_then(Instruction::GotoIfFalse, |c| {
618                    c.scope_begin();
619                    if let Some(var) = i {
620                        let i_index = c.add_local(var)?;
621                        c.read_local_var(loop_counter_index);
622                        c.write_local_var(i_index);
623                    }
624                    c.current_index.push_subindex(1);
625                    c.process_card(repeat)?;
626                    c.current_index.pop_subindex();
627                    c.scope_end();
628                    // i = i + 1
629                    c.process_card(&Card::ScalarInt(1))?;
630                    c.read_local_var(loop_counter_index);
631                    c.push_instruction(Instruction::Add);
632                    c.write_local_var(loop_counter_index);
633                    // return to the repeat instruction
634                    c.push_instruction(Instruction::Goto);
635                    write_to_vec(block_begin, &mut c.program.bytecode);
636                    Ok(())
637                })?;
638                self.scope_end();
639            }
640            Card::ReadVar(variable) => {
641                self.read_var_card(variable)?;
642            }
643            Card::SetVar(var) => {
644                self.compile_subexpr(slice::from_ref(&var.value))?;
645                let var = var.name.as_str();
646                match var.rsplit_once('.') {
647                    Some((read_props, set_prop)) => {
648                        self.read_var_card(read_props)?;
649                        self.push_instruction(Instruction::StringLiteral);
650                        self.push_str(set_prop);
651                        self.push_instruction(Instruction::SetProperty);
652                    }
653                    None => {
654                        let index = match self.resolve_var(var)? {
655                            Variable::Local(i) => i as u32,
656                            Variable::Global => self.add_local(var)?,
657                            Variable::Upvalue(i) => {
658                                self.write_upvalue(i as u32);
659                                return Ok(());
660                            }
661                        };
662
663                        self.write_local_var(index);
664                    }
665                }
666            }
667            Card::SetGlobalVar(var) => {
668                self.compile_subexpr(slice::from_ref(&var.value))?;
669                self.push_instruction(Instruction::SetGlobalVar);
670                let variable = var.name.as_str();
671                let next_var = &mut self.next_var;
672                if variable.is_empty() {
673                    return Err(self.error(CompilationErrorPayload::EmptyVariable));
674                }
675                let varhash = Handle::from_bytes(variable.as_bytes());
676
677                let id = self
678                    .program
679                    .variables
680                    .ids
681                    .entry(varhash)
682                    .or_insert_with(move || {
683                        let id = *next_var;
684                        *next_var = VariableId(id.0 + 1);
685                        id
686                    });
687                self.program
688                    .variables
689                    .names
690                    .entry(Handle::from_u32(id.0))
691                    .or_insert_with(move || variable.to_string());
692                write_to_vec(*id, &mut self.program.bytecode);
693            }
694            Card::IfElse(children) => {
695                let [condition, then_card, else_card] = &**children;
696                self.compile_subexpr(slice::from_ref(condition))?;
697
698                let mut idx = 0;
699                self.current_index.push_subindex(1);
700                self.encode_if_then(Instruction::GotoIfFalse, |c| {
701                    c.process_card(then_card)?;
702                    // jump over the `else` branch
703                    c.push_instruction(Instruction::Goto);
704                    idx = c.program.bytecode.len();
705                    write_to_vec(0xEEFi32, &mut c.program.bytecode);
706                    Ok(())
707                })?;
708                self.current_index.pop_subindex();
709                self.current_index.push_subindex(2);
710                self.process_card(else_card)?;
711                self.current_index.pop_subindex();
712                unsafe {
713                    let ptr = self.program.bytecode.as_mut_ptr().add(idx) as *mut i32;
714                    std::ptr::write_unaligned(ptr, self.program.bytecode.len() as i32);
715                }
716            }
717            Card::IfFalse(jmp) => {
718                let [cond, body] = &**jmp;
719                self.compile_subexpr(slice::from_ref(cond))?;
720                self.current_index.push_subindex(1);
721                self.encode_if_then(Instruction::GotoIfTrue, |c| c.process_card(body))?;
722                self.current_index.pop_subindex();
723            }
724            Card::IfTrue(jmp) => {
725                let [cond, body] = &**jmp;
726                self.compile_subexpr(slice::from_ref(cond))?;
727                self.current_index.push_subindex(1);
728                self.encode_if_then(Instruction::GotoIfFalse, |c| c.process_card(body))?;
729                self.current_index.pop_subindex();
730            }
731            Card::Call(jmp) => {
732                self.compile_subexpr(&jmp.args.0)?;
733                self.push_instruction(Instruction::FunctionPointer);
734                self.encode_jump(jmp.function_name.as_str())?;
735                self.push_instruction(Instruction::CallFunction);
736            }
737            Card::StringLiteral(c) => {
738                self.push_instruction(Instruction::StringLiteral);
739                self.push_str(c.as_str())
740            }
741            Card::CallNative(c) => {
742                self.compile_subexpr(&c.args.0)?;
743                let name = &c.name;
744                let key = Handle::from_str(name.as_str()).unwrap();
745                self.push_instruction(Instruction::CallNative);
746                write_to_vec(key, &mut self.program.bytecode);
747            }
748            Card::ScalarInt(s) => {
749                self.push_instruction(Instruction::ScalarInt);
750                write_to_vec(*s, &mut self.program.bytecode);
751            }
752            Card::ScalarFloat(s) => {
753                self.push_instruction(Instruction::ScalarFloat);
754                write_to_vec(*s, &mut self.program.bytecode);
755            }
756            Card::Function(fname) => {
757                self.push_instruction(Instruction::FunctionPointer);
758                self.encode_jump(fname)?;
759            }
760            Card::Closure(embedded_function) => {
761                // jump over the inner function
762                // yes, this is cursed
763                // no, I don't care anymore
764                self.push_instruction(Instruction::Goto);
765                let goto_index = self.program.bytecode.len();
766                write_to_vec(0xEEFi32, &mut self.program.bytecode);
767
768                self.compile_begin();
769                const CLOSURE_MASK: u64 = 0xEFEFEFEF;
770                let function_handle =
771                    self.current_index.as_handle() + Handle::from_u64(CLOSURE_MASK);
772                let arity = embedded_function.arguments.len() as u32;
773                let handle = u32::try_from(self.program.bytecode.len())
774                    .expect("bytecode length to fit into 32 bits");
775                self.program
776                    .labels
777                    .0
778                    .insert(function_handle, Label::new(handle))
779                    .unwrap();
780
781                // process the embedded function inline
782                self.scope_begin();
783                // TODO: dedupe these loops w/ process_function?
784                // at runtime: pop arguments reverse order as the variables were declared
785                for param in embedded_function.arguments.iter().rev() {
786                    self.add_local(param.as_str())?;
787                }
788                self.compile_subexpr(&embedded_function.cards)?;
789                self.scope_end();
790                self.push_instruction(Instruction::ScalarNil);
791                self.emit_return()?;
792
793                // finish the goto that jumps over the inner function
794                unsafe {
795                    let ptr = self.program.bytecode.as_mut_ptr().add(goto_index) as *mut i32;
796                    std::ptr::write_unaligned(ptr, self.program.bytecode.len() as i32);
797                }
798
799                // finally, push the closure instruction
800                // the goto instruction will jump here
801                self.push_instruction(Instruction::Closure);
802                write_to_vec(function_handle, &mut self.program.bytecode);
803                write_to_vec(arity, &mut self.program.bytecode);
804                let upvalues = std::mem::take(&mut self.upvalues[self.function_id]);
805                for upvalue in upvalues {
806                    self.push_instruction(Instruction::CopyLast);
807                    self.push_instruction(Instruction::RegisterUpvalue);
808                    write_to_vec(upvalue.index, &mut self.program.bytecode);
809                    write_to_vec(upvalue.is_local as u8, &mut self.program.bytecode);
810                }
811                self.compile_end();
812            }
813            Card::NativeFunction(fname) => {
814                self.push_instruction(Instruction::NativeFunctionPointer);
815                self.push_str(fname.as_str());
816            }
817            Card::Array(expressions) => {
818                // create a table, then for each sub-card: insert the subcard and append it to the
819                // result
820                // finally: ensure the result is on the stack
821                self.push_instruction(Instruction::InitTable);
822                let table_var = self.add_local_unchecked("")?;
823                self.write_local_var(table_var);
824                for (i, card) in expressions.iter().enumerate() {
825                    // push nil, so if the card results in no output,
826                    // we append nil to the table
827                    self.push_instruction(Instruction::ScalarNil);
828                    self.current_index.push_subindex(i as u32);
829                    self.process_card(card)?;
830                    self.current_index.pop_subindex();
831                    self.read_local_var(table_var);
832                    self.push_instruction(Instruction::AppendTable);
833                }
834                // push the table to the stack
835                self.read_local_var(table_var);
836            }
837            Card::Len(expr) => {
838                self.compile_subexpr(slice::from_ref(expr.card.as_ref()))?;
839                self.push_instruction(Instruction::Len);
840            }
841            Card::Return(expr) => {
842                self.compile_subexpr(slice::from_ref(expr.card.as_ref()))?;
843                self.emit_return()?;
844            }
845            Card::Not(expr) => {
846                self.compile_subexpr(slice::from_ref(expr.card.as_ref()))?;
847                self.push_instruction(Instruction::Not);
848            }
849            Card::Get(expr) => {
850                self.compile_subexpr(expr.as_ref())?;
851                self.push_instruction(Instruction::NthRow);
852            }
853            Card::And(expr) => {
854                self.compile_subexpr(expr.as_ref())?;
855                self.push_instruction(Instruction::And);
856            }
857            Card::Or(expr) => {
858                self.compile_subexpr(expr.as_ref())?;
859                self.push_instruction(Instruction::Or);
860            }
861            Card::Xor(expr) => {
862                self.compile_subexpr(expr.as_ref())?;
863                self.push_instruction(Instruction::Xor);
864            }
865            Card::Equals(expr) => {
866                self.compile_subexpr(expr.as_ref())?;
867                self.push_instruction(Instruction::Equals);
868            }
869            Card::Less(expr) => {
870                self.compile_subexpr(expr.as_ref())?;
871                self.push_instruction(Instruction::Less);
872            }
873            Card::LessOrEq(expr) => {
874                self.compile_subexpr(expr.as_ref())?;
875                self.push_instruction(Instruction::LessOrEq);
876            }
877            Card::NotEquals(expr) => {
878                self.compile_subexpr(expr.as_ref())?;
879                self.push_instruction(Instruction::NotEquals);
880            }
881            Card::Add(expr) => {
882                self.compile_subexpr(expr.as_ref())?;
883                self.push_instruction(Instruction::Add);
884            }
885            Card::Sub(expr) => {
886                self.compile_subexpr(expr.as_ref())?;
887                self.push_instruction(Instruction::Sub);
888            }
889            Card::Mul(expr) => {
890                self.compile_subexpr(expr.as_ref())?;
891                self.push_instruction(Instruction::Mul);
892            }
893            Card::Div(expr) => {
894                self.compile_subexpr(expr.as_ref())?;
895                self.push_instruction(Instruction::Div);
896            }
897            Card::GetProperty(expr) => {
898                self.compile_subexpr(expr.as_ref())?;
899                self.push_instruction(Instruction::GetProperty);
900            }
901            Card::SetProperty(expr) => {
902                self.compile_subexpr(expr.as_ref())?;
903                self.push_instruction(Instruction::SetProperty);
904            }
905            Card::AppendTable(expr) => {
906                self.compile_subexpr(expr.as_ref())?;
907                self.push_instruction(Instruction::AppendTable);
908            }
909            Card::PopTable(expr) => {
910                self.compile_subexpr(slice::from_ref(expr.card.as_ref()))?;
911                self.push_instruction(Instruction::PopTable);
912            }
913            Card::DynamicCall(jump) => {
914                self.compile_subexpr(jump.args.0.as_slice())?;
915                self.current_index.push_subindex(jump.args.0.len() as u32);
916                self.process_card(&jump.function)?;
917                self.current_index.pop_subindex();
918                self.push_instruction(Instruction::CallFunction);
919            }
920            Card::ScalarNil => {
921                self.push_instruction(Instruction::ScalarNil);
922            }
923            Card::Abort => {
924                self.push_instruction(Instruction::Exit);
925            }
926            Card::CreateTable => {
927                self.push_instruction(Instruction::InitTable);
928            }
929            Card::Comment(_) => {
930                // noop
931            }
932        }
933        Ok(())
934    }
935
936    fn read_var_card(&mut self, mut variable: &str) -> CompilationResult<()> {
937        // sub properties on a table
938        let props = match variable.split_once('.') {
939            Some((v, props)) => {
940                variable = v;
941                props
942            }
943            None => "",
944        };
945        let scope = self.resolve_var(variable)?;
946        match scope {
947            Variable::Local(index) => {
948                self.read_local_var(index as u32);
949            }
950            Variable::Upvalue(index) => {
951                self.read_upvalue(index as u32);
952            }
953            Variable::Global => {
954                let next_var = &mut self.next_var;
955                let varhash = Handle::from_bytes(variable.as_bytes());
956                let id = *self
957                    .program
958                    .variables
959                    .ids
960                    .entry(varhash)
961                    .or_insert_with(move || {
962                        let id = *next_var;
963                        *next_var = VariableId(id.0 + 1);
964                        id
965                    });
966                self.program
967                    .variables
968                    .names
969                    .entry(Handle::from_u32(id.0))
970                    .or_insert_with(|| variable.to_string());
971                self.push_instruction(Instruction::ReadGlobalVar);
972                write_to_vec(id, &mut self.program.bytecode);
973            }
974        }
975        // handle props
976        for prop in props.split('.').filter(|p| !p.is_empty()) {
977            self.push_instruction(Instruction::StringLiteral);
978            self.push_str(prop);
979            self.push_instruction(Instruction::GetProperty);
980        }
981        Ok(())
982    }
983
984    fn read_local_var(&mut self, index: u32) {
985        self.push_instruction(Instruction::ReadLocalVar);
986        write_to_vec(index, &mut self.program.bytecode);
987    }
988
989    fn write_local_var(&mut self, index: u32) {
990        self.push_instruction(Instruction::SetLocalVar);
991        write_to_vec(index, &mut self.program.bytecode);
992    }
993
994    fn read_upvalue(&mut self, index: u32) {
995        self.push_instruction(Instruction::ReadUpvalue);
996        write_to_vec(index, &mut self.program.bytecode);
997    }
998
999    fn write_upvalue(&mut self, index: u32) {
1000        self.push_instruction(Instruction::SetUpvalue);
1001        write_to_vec(index, &mut self.program.bytecode);
1002    }
1003
1004    fn validate_var_name(&self, name: &str) -> CompilationResult<()> {
1005        if name.is_empty() {
1006            return Err(self.error(CompilationErrorPayload::EmptyVariable));
1007        }
1008        Ok(())
1009    }
1010
1011    fn push_instruction(&mut self, instruction: Instruction) {
1012        self.program
1013            .trace
1014            .insert(self.program.bytecode.len() as u32, self.trace())
1015            .unwrap();
1016        self.program.bytecode.push(instruction as u8);
1017    }
1018
1019    fn compile_subexpr(&mut self, cards: &'a [Card]) -> CompilationResult<()> {
1020        for (i, card) in cards.iter().enumerate() {
1021            self.current_index.push_subindex(i as u32);
1022            self.process_card(card)?;
1023            self.current_index.pop_subindex();
1024        }
1025        Ok(())
1026    }
1027}
1028
1029fn super_depth(import: &str) -> (usize, Option<&str>) {
1030    let mut super_pog = import.split_once("super.");
1031    let mut super_cnt = 0;
1032    let mut suffix = None;
1033    while let Some((_sup_pre, sup_post)) = super_pog {
1034        super_cnt += 1;
1035        super_pog = sup_post.split_once("super.");
1036        suffix = Some(sup_post);
1037    }
1038
1039    (super_cnt, suffix)
1040}