use std::collections::HashMap;
use super::opcode::Op;
use crate::emacs_core::value::{LambdaParams, Value, ValueKind};
#[derive(Clone, Debug)]
pub struct ByteCodeFunction {
pub ops: Vec<Op>,
pub constants: Vec<Value>,
pub max_stack: u16,
pub params: LambdaParams,
pub lexical: bool,
pub env: Option<Value>,
pub gnu_byte_offset_map: Option<HashMap<usize, usize>>,
pub docstring: Option<String>,
pub doc_form: Option<Value>,
pub interactive: Option<Value>,
}
impl ByteCodeFunction {
pub fn new(params: LambdaParams) -> Self {
Self {
ops: Vec::new(),
constants: Vec::new(),
max_stack: 0,
params,
lexical: false,
env: None,
gnu_byte_offset_map: None,
docstring: None,
doc_form: None,
interactive: None,
}
}
pub fn add_constant(&mut self, value: Value) -> u16 {
for (i, existing) in self.constants.iter().enumerate() {
match (value.kind(), existing.kind()) {
(ValueKind::Fixnum(a), ValueKind::Fixnum(b)) if a == b => return i as u16,
(ValueKind::Symbol(a), ValueKind::Symbol(b)) if a == b => return i as u16,
(ValueKind::Symbol(a), ValueKind::Symbol(b)) if a == b => return i as u16,
(ValueKind::Symbol(a), ValueKind::Symbol(b)) if a == b => return i as u16,
(ValueKind::Nil, ValueKind::Nil) => return i as u16,
(ValueKind::T, ValueKind::T) => return i as u16,
(ValueKind::Symbol(a), ValueKind::Symbol(b)) if a == b => return i as u16,
_ => {}
}
}
let idx = self.constants.len() as u16;
self.constants.push(value);
idx
}
pub fn add_symbol(&mut self, name: &str) -> u16 {
self.add_constant(Value::symbol(name))
}
pub fn emit(&mut self, op: Op) {
self.ops.push(op);
}
pub fn current_offset(&self) -> u32 {
self.ops.len() as u32
}
pub fn patch_jump(&mut self, instr_idx: u32, target: u32) {
let idx = instr_idx as usize;
match &mut self.ops[idx] {
Op::Goto(addr)
| Op::GotoIfNil(addr)
| Op::GotoIfNotNil(addr)
| Op::GotoIfNilElsePop(addr)
| Op::GotoIfNotNilElsePop(addr)
| Op::PushConditionCase(addr)
| Op::PushConditionCaseRaw(addr)
| Op::PushCatch(addr) => {
*addr = target;
}
_ => panic!("patch_jump on non-jump instruction at {}", idx),
}
}
pub fn disassemble(&self) -> String {
let mut out = String::new();
out.push_str(&format!(
"bytecode function ({} ops, {} constants, stack {})\n",
self.ops.len(),
self.constants.len(),
self.max_stack
));
out.push_str("constants:\n");
for (i, c) in self.constants.iter().enumerate() {
out.push_str(&format!(" {}: {}\n", i, c));
}
out.push_str("code:\n");
for (i, op) in self.ops.iter().enumerate() {
out.push_str(&format!(" {:4}: {}\n", i, op.disasm(&self.constants)));
}
out
}
}
#[cfg(test)]
#[path = "chunk_test.rs"]
mod tests;