use std::fmt;
use crate::common::{
opcode::Opcode,
data::Data,
number::build_number,
span::Span,
};
use crate::core::ffi::FFIFunction;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Captured {
Local(usize),
Nonlocal(usize),
}
#[derive(Debug, Clone, PartialEq)]
pub struct Lambda {
pub decls: usize,
pub code: Vec<u8>,
pub spans: Vec<(usize, Span)>,
pub constants: Vec<Data>,
pub captures: Vec<Captured>,
pub ffi: Vec<FFIFunction>,
}
impl Lambda {
pub fn empty() -> Lambda {
Lambda {
decls: 0,
code: vec![],
spans: vec![],
constants: vec![],
captures: vec![],
ffi: vec![],
}
}
pub fn emit(&mut self, op: Opcode) {
self.code.push(op as u8)
}
pub fn emit_bytes(&mut self, bytes: &mut Vec<u8>) {
self.code.append(bytes)
}
pub fn emit_span(&mut self, span: &Span) {
self.spans.push((self.code.len(), span.clone()))
}
pub fn demit(&mut self) {
self.code.pop();
}
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
},
}
}
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();
}
pub fn add_ffi(&mut self, function: FFIFunction) -> usize {
self.ffi.push(function);
self.ffi.len() - 1
}
}
impl fmt::Display for Lambda {
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 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(())
}
}