passerine/common/
lambda.rs

1use std::fmt;
2
3use crate::common::{
4    opcode::Opcode,
5    data::Data,
6    number::build_number,
7    span::Span,
8};
9
10use crate::core::ffi::FFIFunction;
11
12/// Represents a variable visible in the current scope.
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub enum Captured {
15    /// The index on the stack if the variable is local to the current scope.
16    Local(usize),
17    /// The index of the upvalue in the enclosing scope.
18    Nonlocal(usize),
19}
20
21/// Represents a single interpretable chunk of bytecode,
22/// think a function.
23#[derive(Debug, Clone, PartialEq)]
24pub struct Lambda {
25    // TODO: make this a list of variable names
26    // So structs can be made, and state preserved in the repl.
27    /// Number of variables declared in this scope.
28    pub decls: usize,
29    /// Each byte is an opcode or a number-stream.
30    pub code: Vec<u8>,
31    /// Each usize indexes the bytecode op that begins each line.
32    pub spans: Vec<(usize, Span)>,
33    /// Number-stream indexed, used to load constants.
34    pub constants: Vec<Data>,
35    /// List of positions of locals in the scope where this lambda is defined,
36    /// indexes must be gauranteed to be data on the heap.
37    pub captures: Vec<Captured>,
38    /// List of FFI functions (i.e. Rust functions)
39    /// that can be called from this function.
40    pub ffi: Vec<FFIFunction>,
41}
42
43impl Lambda {
44    /// Creates a new empty `Lambda` to be filled.
45    pub fn empty() -> Lambda {
46        Lambda {
47            decls:     0,
48            code:      vec![],
49            spans:     vec![],
50            constants: vec![],
51            captures:  vec![],
52            ffi:       vec![],
53        }
54    }
55
56    /// Emits an opcode as a byte.
57    pub fn emit(&mut self, op: Opcode) {
58        self.code.push(op as u8)
59    }
60
61    /// Emits a series of bytes.
62    pub fn emit_bytes(&mut self, bytes: &mut Vec<u8>) {
63        self.code.append(bytes)
64    }
65
66    /// Emits a span, should be called before an opcode is emmited.
67    /// This function ties opcodes to spans in source.
68    /// See index_span as well.
69    pub fn emit_span(&mut self, span: &Span) {
70        self.spans.push((self.code.len(), span.clone()))
71    }
72
73    /// Removes the last emitted byte.
74    pub fn demit(&mut self) {
75        self.code.pop();
76    }
77
78    /// Given some data, this function adds it to the constants table,
79    /// and returns the data's index.
80    /// The constants table is push only, so constants are identified by their index.
81    /// The resulting usize can be split up into a number byte stream,
82    /// and be inserted into the bytecode.
83    pub fn index_data(&mut self, data: Data) -> usize {
84        match self.constants.iter().position(|d| d == &data) {
85            Some(d) => d,
86            None => {
87                self.constants.push(data);
88                self.constants.len() - 1
89            },
90        }
91    }
92
93    /// Look up the nearest span at or before the index of a specific bytecode op.
94    pub fn index_span(&self, index: usize) -> Span {
95        let mut best = &Span::empty();
96
97        for (i, span) in self.spans.iter() {
98            if i > &index { break; }
99            best = span;
100        }
101
102        best.clone()
103    }
104
105    /// Adds a ffi function to the ffi table,
106    /// without checking for duplicates.
107    /// The `Compiler` ensures that functions are valid
108    /// and not duplicated during codegen.
109    pub fn add_ffi(&mut self, function: FFIFunction) -> usize {
110        self.ffi.push(function);
111        self.ffi.len() - 1
112    }
113}
114
115impl fmt::Display for Lambda {
116    /// Dump a human-readable breakdown of a `Lambda`'s bytecode.
117    /// Including constants, captures, and variables declared.
118    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
119        writeln!(f, "-- Dumping Constants:")?;
120        for constant in self.constants.iter() {
121            writeln!(f, "{:?}", constant)?;
122        }
123
124        // writeln!(f, "-- Dumping Spans:")?;
125        // for span in self.spans.iter() {
126        //     writeln!(f, "{:?}", span)?;
127        // }
128
129        writeln!(f, "-- Dumping Captures:")?;
130        for capture in self.captures.iter() {
131            writeln!(f, "{:?}", capture)?;
132        }
133
134        writeln!(f, "-- Dumping Variables: {}", self.decls)?;
135
136        writeln!(f, "-- Dumping Bytecode:")?;
137        writeln!(f, "Inst.   \tArgs\tValue?")?;
138        let mut index = 0;
139
140        while index < self.code.len() {
141            index += 1;
142            match Opcode::from_byte(self.code[index - 1]) {
143                Opcode::Con => {
144                    let (constant_index, consumed) = build_number(&self.code[index..]);
145                    index += consumed;
146                    writeln!(f, "Load Con\t{}\t{:?}", constant_index, self.constants[constant_index])?;
147                },
148                Opcode::NotInit => { writeln!(f, "NotInit \t\tDeclare variable")?; }
149                Opcode::Del     => { writeln!(f, "Delete  \t\t--")?; },
150                Opcode::Capture => {
151                    let (local_index, consumed) = build_number(&self.code[index..]);
152                    index += consumed;
153                    writeln!(f, "Capture \t{}\tIndexed local moved to heap", local_index)?;
154                },
155                Opcode::Save => {
156                    let (local_index, consumed) = build_number(&self.code[index..]);
157                    index += consumed;
158                    writeln!(f, "Save    \t{}\tIndexed local", local_index)?;
159                },
160                Opcode::SaveCap => {
161                    let (upvalue_index, consumed) = build_number(&self.code[index..]);
162                    index += consumed;
163                    writeln!(f, "Save Cap\t{}\tIndexed upvalue on heap", upvalue_index)?;
164                },
165                Opcode::Load => {
166                    let (local_index, consumed) = build_number(&self.code[index..]);
167                    index += consumed;
168                    writeln!(f, "Load    \t{}\tIndexed local", local_index)?;
169                },
170                Opcode::LoadCap => {
171                    let (upvalue_index, consumed) = build_number(&self.code[index..]);
172                    index += consumed;
173                    writeln!(f, "Load Cap\t{}\tIndexed upvalue on heap", upvalue_index)?;
174                },
175                Opcode::Call => { writeln!(f, "Call    \t\tRun top function using next stack value")?; }
176                Opcode::Return => {
177                    let (num_locals, consumed) = build_number(&self.code[index..]);
178                    index += consumed;
179                    writeln!(f, "Return  \t{}\tLocals on stack deleted", num_locals)?;
180                },
181                Opcode::Closure => {
182                    let (todo_index, consumed) = build_number(&self.code[index..]);
183                    index += consumed;
184                    writeln!(f, "Closure \t{}\tIndex of lambda to be wrapped", todo_index)?;
185                },
186                Opcode::Print   => { writeln!(f, "Print    \t\t--")?; },
187                Opcode::Label   => { writeln!(f, "Label    \t\t--")?; },
188                Opcode::Tuple => {
189                    let (length, consumed) = build_number(&self.code[index..]);
190                    index += consumed;
191                    writeln!(f, "Tuple   \t{}\tValues tupled together", length)?;
192                },
193                Opcode::UnLabel => { writeln!(f, "UnLabel  \t\t--")?; },
194                Opcode::UnData  => { writeln!(f, "UnData   \t\t--")?; },
195                Opcode::UnTuple => {
196                    let (item_index, consumed) = build_number(&self.code[index..]);
197                    index += consumed;
198                    writeln!(f, "UnTuple \t{}\tItem accessed", item_index)?;
199                },
200                Opcode::Copy    => { writeln!(f, "Copy     \t\t--")?; },
201                Opcode::FFICall => {
202                    let (ffi_index, consumed) = build_number(&self.code[index..]);
203                    index += consumed;
204                    writeln!(f, "Return  \t{}\tIndexed FFI function called", ffi_index)?;
205                },
206            }
207        }
208
209        Ok(())
210    }
211}