1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
use std::fmt;

use crate::common::{
    opcode::Opcode,
    data::Data,
    number::build_number,
    span::Span,
};

use crate::core::ffi::FFIFunction;

/// Represents a variable visible in the current scope.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Captured {
    /// The index on the stack if the variable is local to the current scope.
    Local(usize),
    /// The index of the upvalue in the enclosing scope.
    Nonlocal(usize),
}

/// Represents a single interpretable chunk of bytecode,
/// think a function.
#[derive(Debug, Clone, PartialEq)]
pub struct Lambda {
    // TODO: make this a list of variable names
    // So structs can be made, and state preserved in the repl.
    /// Number of variables declared in this scope.
    pub decls: usize,
    /// Each byte is an opcode or a number-stream.
    pub code: Vec<u8>,
    /// Each usize indexes the bytecode op that begins each line.
    pub spans: Vec<(usize, Span)>,
    /// Number-stream indexed, used to load constants.
    pub constants: Vec<Data>,
    /// List of positions of locals in the scope where this lambda is defined,
    /// indexes must be gauranteed to be data on the heap.
    pub captures: Vec<Captured>,
    /// List of FFI functions (i.e. Rust functions)
    /// that can be called from this function.
    pub ffi: Vec<FFIFunction>,
}

impl Lambda {
    /// Creates a new empty `Lambda` to be filled.
    pub fn empty() -> Lambda {
        Lambda {
            decls:     0,
            code:      vec![],
            spans:     vec![],
            constants: vec![],
            captures:  vec![],
            ffi:       vec![],
        }
    }

    /// Emits an opcode as a byte.
    pub fn emit(&mut self, op: Opcode) {
        self.code.push(op as u8)
    }

    /// Emits a series of bytes.
    pub fn emit_bytes(&mut self, bytes: &mut Vec<u8>) {
        self.code.append(bytes)
    }

    /// Emits a span, should be called before an opcode is emmited.
    /// This function ties opcodes to spans in source.
    /// See index_span as well.
    pub fn emit_span(&mut self, span: &Span) {
        self.spans.push((self.code.len(), span.clone()))
    }

    /// Removes the last emitted byte.
    pub fn demit(&mut self) {
        self.code.pop();
    }

    /// Given some data, this function adds it to the constants table,
    /// and returns the data's index.
    /// The constants table is push only, so constants are identified by their index.
    /// The resulting usize can be split up into a number byte stream,
    /// and be inserted into the bytecode.
    pub fn index_data(&mut self, data: Data) -> usize {
        match self.constants.iter().position(|d| d == &data) {
            Some(d) => d,
            None => {
                self.constants.push(data);
                self.constants.len() - 1
            },
        }
    }

    /// Look up the nearest span at or before the index of a specific bytecode op.
    pub fn index_span(&self, index: usize) -> Span {
        let mut best = &Span::empty();

        for (i, span) in self.spans.iter() {
            if i > &index { break; }
            best = span;
        }

        return best.clone();
    }

    /// Adds a ffi function to the ffi table,
    /// without checking for duplicates.
    /// The `Compiler` ensures that functions are valid
    /// and not duplicated during codegen.
    pub fn add_ffi(&mut self, function: FFIFunction) -> usize {
        self.ffi.push(function);
        self.ffi.len() - 1
    }
}

impl fmt::Display for Lambda {
    /// Dump a human-readable breakdown of a `Lambda`'s bytecode.
    /// Including constants, captures, and variables declared.
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
        writeln!(f, "-- Dumping Constants:")?;
        for constant in self.constants.iter() {
            writeln!(f, "{:?}", constant)?;
        }

        // writeln!(f, "-- Dumping Spans:")?;
        // for span in self.spans.iter() {
        //     writeln!(f, "{:?}", span)?;
        // }

        writeln!(f, "-- Dumping Captures:")?;
        for capture in self.captures.iter() {
            writeln!(f, "{:?}", capture)?;
        }

        writeln!(f, "-- Dumping Variables: {}", self.decls)?;

        writeln!(f, "-- Dumping Bytecode:")?;
        writeln!(f, "Inst.   \tArgs\tValue?")?;
        let mut index = 0;

        while index < self.code.len() {
            index += 1;
            match Opcode::from_byte(self.code[index - 1]) {
                Opcode::Con => {
                    let (constant_index, consumed) = build_number(&self.code[index..]);
                    index += consumed;
                    writeln!(f, "Load Con\t{}\t{:?}", constant_index, self.constants[constant_index])?;
                },
                Opcode::NotInit => { writeln!(f, "NotInit \t\tDeclare variable")?; }
                Opcode::Del     => { writeln!(f, "Delete  \t\t--")?; },
                Opcode::Capture => {
                    let (local_index, consumed) = build_number(&self.code[index..]);
                    index += consumed;
                    writeln!(f, "Capture \t{}\tIndexed local moved to heap", local_index)?;
                },
                Opcode::Save => {
                    let (local_index, consumed) = build_number(&self.code[index..]);
                    index += consumed;
                    writeln!(f, "Save    \t{}\tIndexed local", local_index)?;
                },
                Opcode::SaveCap => {
                    let (upvalue_index, consumed) = build_number(&self.code[index..]);
                    index += consumed;
                    writeln!(f, "Save Cap\t{}\tIndexed upvalue on heap", upvalue_index)?;
                },
                Opcode::Load => {
                    let (local_index, consumed) = build_number(&self.code[index..]);
                    index += consumed;
                    writeln!(f, "Load    \t{}\tIndexed local", local_index)?;
                },
                Opcode::LoadCap => {
                    let (upvalue_index, consumed) = build_number(&self.code[index..]);
                    index += consumed;
                    writeln!(f, "Load Cap\t{}\tIndexed upvalue on heap", upvalue_index)?;
                },
                Opcode::Call => { writeln!(f, "Call    \t\tRun top function using next stack value")?; }
                Opcode::Return => {
                    let (num_locals, consumed) = build_number(&self.code[index..]);
                    index += consumed;
                    writeln!(f, "Return  \t{}\tLocals on stack deleted", num_locals)?;
                },
                Opcode::Closure => {
                    let (todo_index, consumed) = build_number(&self.code[index..]);
                    index += consumed;
                    writeln!(f, "Closure \t{}\tIndex of lambda to be wrapped", todo_index)?;
                },
                Opcode::Print   => { writeln!(f, "Print    \t\t--")?; },
                Opcode::Label   => { writeln!(f, "Label    \t\t--")?; },
                Opcode::Tuple => {
                    let (length, consumed) = build_number(&self.code[index..]);
                    index += consumed;
                    writeln!(f, "Tuple   \t{}\tValues tupled together", length)?;
                },
                Opcode::UnLabel => { writeln!(f, "UnLabel  \t\t--")?; },
                Opcode::UnData  => { writeln!(f, "UnData   \t\t--")?; },
                Opcode::UnTuple => {
                    let (item_index, consumed) = build_number(&self.code[index..]);
                    index += consumed;
                    writeln!(f, "UnTuple \t{}\tItem accessed", item_index)?;
                },
                Opcode::Copy    => { writeln!(f, "Copy     \t\t--")?; },
                Opcode::FFICall => {
                    let (ffi_index, consumed) = build_number(&self.code[index..]);
                    index += consumed;
                    writeln!(f, "Return  \t{}\tIndexed FFI function called", ffi_index)?;
                },
            }
        }

        Ok(())
    }
}