use std::collections::HashMap;
use byteorder;
use byteorder::{BigEndian, ByteOrder, WriteBytesExt};
use strum::{EnumCount, EnumIter};
#[derive(Hash, Eq, Debug, Clone, PartialEq, PartialOrd)]
pub struct Instructions {
pub data: Vec<u8>,
}
pub struct OpcodeDefinition {
pub(crate) name: &'static str,
operand_width: Vec<i32>,
}
#[repr(u8)]
#[derive(Debug, Hash, Eq, Clone, Copy, PartialEq, EnumCount, EnumIter)]
pub enum Opcode {
OpConst,
OpAdd,
OpPop,
OpSub,
OpMul,
OpDiv,
OpTrue,
OpFalse,
OpEqual,
OpNotEqual,
OpGreaterThan,
OpMinus,
OpBang,
OpJumpNotTruthy,
OpJump,
OpNull,
OpGetGlobal,
OpSetGlobal,
OpArray,
OpHash,
OpIndex,
OpCall,
OpReturnValue,
OpReturn,
OpGetLocal,
OpSetLocal,
OpGetBuiltin,
OpClosure,
OpGetFree,
OpCurrentClosure,
}
lazy_static! {
pub static ref DEFINITIONS: HashMap<Opcode, OpcodeDefinition> = {
let mut m = HashMap::new();
m.insert(
Opcode::OpConst,
OpcodeDefinition {
name: "OpConst",
operand_width: vec![2],
},
);
m.insert(
Opcode::OpAdd,
OpcodeDefinition {
name: "OpAdd",
operand_width: vec![],
},
);
m.insert(
Opcode::OpPop,
OpcodeDefinition {
name: "OpPop",
operand_width: vec![],
},
);
m.insert(
Opcode::OpSub,
OpcodeDefinition {
name: "OpSub",
operand_width: vec![],
},
);
m.insert(
Opcode::OpMul,
OpcodeDefinition {
name: "OpMul",
operand_width: vec![],
},
);
m.insert(
Opcode::OpDiv,
OpcodeDefinition {
name: "OpDiv",
operand_width: vec![],
},
);
m.insert(
Opcode::OpTrue,
OpcodeDefinition {
name: "OpTrue",
operand_width: vec![],
},
);
m.insert(
Opcode::OpFalse,
OpcodeDefinition {
name: "OpFalse",
operand_width: vec![],
},
);
m.insert(
Opcode::OpEqual,
OpcodeDefinition {
name: "OpEqual",
operand_width: vec![],
},
);
m.insert(
Opcode::OpNotEqual,
OpcodeDefinition {
name: "OpNotEqual",
operand_width: vec![],
},
);
m.insert(
Opcode::OpGreaterThan,
OpcodeDefinition {
name: "OpGreatThan",
operand_width: vec![],
},
);
m.insert(
Opcode::OpMinus,
OpcodeDefinition {
name: "OpMinus",
operand_width: vec![],
},
);
m.insert(
Opcode::OpBang,
OpcodeDefinition {
name: "OpBang",
operand_width: vec![],
},
);
m.insert(
Opcode::OpJumpNotTruthy,
OpcodeDefinition {
name: "OpJumpNotTruthy",
operand_width: vec![2],
},
);
m.insert(
Opcode::OpJump,
OpcodeDefinition {
name: "OpJump",
operand_width: vec![2],
},
);
m.insert(
Opcode::OpNull,
OpcodeDefinition {
name: "OpNull",
operand_width: vec![],
},
);
m.insert(
Opcode::OpGetGlobal,
OpcodeDefinition {
name: "OpGetGlobal",
operand_width: vec![2],
},
);
m.insert(
Opcode::OpSetGlobal,
OpcodeDefinition {
name: "OpSetGlobal",
operand_width: vec![2],
},
);
m.insert(
Opcode::OpArray,
OpcodeDefinition {
name: "OpArray",
operand_width: vec![2],
},
);
m.insert(
Opcode::OpHash,
OpcodeDefinition {
name: "OpHash",
operand_width: vec![2],
},
);
m.insert(
Opcode::OpIndex,
OpcodeDefinition {
name: "OpIndex",
operand_width: vec![],
},
);
m.insert(
Opcode::OpCall,
OpcodeDefinition {
name: "OpCall",
operand_width: vec![1],
},
);
m.insert(
Opcode::OpReturn,
OpcodeDefinition {
name: "OpReturn",
operand_width: vec![],
},
);
m.insert(
Opcode::OpReturnValue,
OpcodeDefinition {
name: "OpReturnValue",
operand_width: vec![],
},
);
m.insert(
Opcode::OpGetLocal,
OpcodeDefinition {
name: "OpGetLocal",
operand_width: vec![1],
},
);
m.insert(
Opcode::OpSetLocal,
OpcodeDefinition {
name: "OpSetLocal",
operand_width: vec![1],
},
);
m.insert(
Opcode::OpGetBuiltin,
OpcodeDefinition {
name: "OpGetBuiltin",
operand_width: vec![1],
},
);
m.insert(
Opcode::OpClosure,
OpcodeDefinition {
name: "OpClosure",
operand_width: vec![2, 1],
},
);
m.insert(
Opcode::OpGetFree,
OpcodeDefinition {
name: "OpGetFree",
operand_width: vec![1],
},
);
m.insert(
Opcode::OpCurrentClosure,
OpcodeDefinition {
name: "OpCurrentClosure",
operand_width: vec![],
},
);
return m;
};
}
pub fn make_instructions(op: Opcode, operands: &Vec<usize>) -> Instructions {
let mut instructions = Vec::new();
instructions.push(op as u8);
let widths = &DEFINITIONS.get(&op).unwrap().operand_width;
for (o, w) in operands.into_iter().zip(widths) {
match w {
2 => {
instructions.write_u16::<BigEndian>(*o as u16).unwrap();
}
1 => {
instructions.write_u8(*o as u8).unwrap();
}
_ => {
panic!("unsupported operand width {}", w)
}
}
}
return Instructions {
data: instructions,
};
}
pub fn read_operands(def: &OpcodeDefinition, ins: &[u8]) -> (Vec<usize>, usize) {
let mut operands = Vec::with_capacity(def.operand_width.len());
let mut offset = 0;
for w in &def.operand_width {
match w {
2 => {
operands.push(BigEndian::read_u16(&ins[offset..offset + 2]) as usize);
offset = offset + 2;
}
1 => {
operands.push(ins[offset] as usize);
offset = offset + 1;
}
0 => {}
_ => {
panic!("unsupported operand width {} for read", w)
}
}
}
return (operands, offset);
}
pub fn concat_instructions(expected: &Vec<Instructions>) -> Instructions {
let mut out = Instructions {
data: vec![],
};
for instruction in expected {
out = out.merge_instructions(instruction)
}
return out;
}
pub fn cast_u8_to_opcode(op: u8) -> Opcode {
return unsafe { ::std::mem::transmute(op) };
}
impl Instructions {
pub fn string(&self) -> String {
let mut ret = String::new();
let mut i = 0;
while i < self.data.len() {
let op: u8 = *self.data.get(i).unwrap();
let opcode = cast_u8_to_opcode(op);
let definition = DEFINITIONS.get(&opcode).unwrap();
let (operands, read_size) = read_operands(definition, &self.data[i + 1..]);
ret.push_str(&format!("{:04} {}\n", i, Self::fmt_instructions(definition, &operands)));
i = i + 1 + read_size;
}
return ret;
}
fn fmt_instructions(def: &OpcodeDefinition, operands: &Vec<usize>) -> String {
match def.operand_width.len() {
2 => format!("{} {} {}", def.name, operands[0], operands[1]),
1 => format!("{} {}", def.name, operands[0]),
0 => format!("{}", def.name),
_ => {
panic!("unsupported operand width {}", def.operand_width.len());
}
}
}
pub fn merge_instructions(&self, other: &Instructions) -> Instructions {
let ins = vec![self, other];
return Instructions {
data: ins
.iter()
.fold(vec![], |sum, &i| [sum.as_slice(), i.data.as_slice()].concat()),
};
}
}