use crate::interpreter::{Instr, InstrPart};
use crate::types::Value;
const MAGIC: &[u8; 4] = b"LKB1";
pub fn encode(instrs: &[Instr]) -> Vec<u8> {
let mut out = Vec::new();
out.extend_from_slice(MAGIC);
write_u32(&mut out, instrs.len() as u32);
for instr in instrs {
write_instr(&mut out, instr);
}
out
}
pub fn decode(bytes: &[u8]) -> Result<Vec<Instr>, String> {
if bytes.len() < 4 || &bytes[..4] != MAGIC {
return Err("Invalid bytecode header".to_string());
}
let mut cur = Cursor::new(&bytes[4..]);
let count = cur.read_u32()? as usize;
let mut instrs = Vec::with_capacity(count);
for _ in 0..count {
instrs.push(cur.read_instr()?);
}
Ok(instrs)
}
pub fn write_file(path: &str, instrs: &[Instr]) -> Result<(), String> {
let bytes = encode(instrs);
std::fs::write(path, bytes).map_err(|e| format!("Cannot write bytecode: {}", e))
}
pub fn read_file(path: &str) -> Result<Vec<Instr>, String> {
let bytes = std::fs::read(path).map_err(|e| format!("Cannot read bytecode: {}", e))?;
decode(&bytes)
}
fn write_instr(out: &mut Vec<u8>, instr: &Instr) {
match instr {
Instr::Push(v) => {
write_u8(out, 0);
write_value(out, v);
}
Instr::Load(name) => {
write_u8(out, 1);
write_string(out, name);
}
Instr::Store(name) => {
write_u8(out, 2);
write_string(out, name);
}
Instr::Pop => write_u8(out, 3),
Instr::Declare {
name,
is_mut,
type_hint,
} => {
write_u8(out, 4);
write_string(out, name);
write_u8(out, if *is_mut { 1 } else { 0 });
write_opt_string(out, type_hint.as_deref());
}
Instr::BinOp(op) => {
write_u8(out, 5);
write_string(out, op);
}
Instr::MakeArray(n) => {
write_u8(out, 6);
write_u32(out, *n as u32);
}
Instr::Range => write_u8(out, 7),
Instr::Call { name, argc } => {
write_u8(out, 8);
write_string(out, name);
write_u32(out, *argc as u32);
}
Instr::If {
cond,
then_block,
else_block,
} => {
write_u8(out, 9);
write_instrs(out, cond);
write_instrs(out, then_block);
write_instrs(out, else_block);
}
Instr::Loop { body } => {
write_u8(out, 10);
write_instrs(out, body);
}
Instr::While { cond, body } => {
write_u8(out, 11);
write_instrs(out, cond);
write_instrs(out, body);
}
Instr::For { var, iter, body } => {
write_u8(out, 12);
write_string(out, var);
write_instrs(out, iter);
write_instrs(out, body);
}
Instr::FnDef { name, params, body } => {
write_u8(out, 13);
write_string(out, name);
write_strings(out, params);
write_instrs(out, body);
}
Instr::Return => write_u8(out, 14),
Instr::Break => write_u8(out, 15),
Instr::Switch {
value,
cases,
default,
} => {
write_u8(out, 16);
write_instrs(out, value);
write_u32(out, cases.len() as u32);
for (case_val, body) in cases {
write_instrs(out, case_val);
write_instrs(out, body);
}
match default {
Some(body) => {
write_u8(out, 1);
write_instrs(out, body);
}
None => write_u8(out, 0),
}
}
Instr::Try { body, catch_body } => {
write_u8(out, 17);
write_instrs(out, body);
write_instrs(out, catch_body);
}
Instr::Interpolated(parts) => {
write_u8(out, 18);
write_u32(out, parts.len() as u32);
for part in parts {
match part {
InstrPart::Text(t) => {
write_u8(out, 0);
write_string(out, t);
}
InstrPart::Expr(code) => {
write_u8(out, 1);
write_instrs(out, code);
}
}
}
}
Instr::Import(path) => {
write_u8(out, 19);
write_string(out, path);
}
}
}
fn write_instrs(out: &mut Vec<u8>, instrs: &[Instr]) {
write_u32(out, instrs.len() as u32);
for instr in instrs {
write_instr(out, instr);
}
}
fn write_strings(out: &mut Vec<u8>, strings: &[String]) {
write_u32(out, strings.len() as u32);
for s in strings {
write_string(out, s);
}
}
fn write_opt_string(out: &mut Vec<u8>, value: Option<&str>) {
match value {
Some(s) => {
write_u8(out, 1);
write_string(out, s);
}
None => write_u8(out, 0),
}
}
fn write_value(out: &mut Vec<u8>, value: &Value) {
match value {
Value::Null => write_u8(out, 0),
Value::Int(n) => {
write_u8(out, 1);
write_i64(out, *n);
}
Value::Float(n) => {
write_u8(out, 2);
write_f64(out, *n);
}
Value::Bool(b) => {
write_u8(out, 3);
write_u8(out, if *b { 1 } else { 0 });
}
Value::Str(s) => {
write_u8(out, 4);
write_string(out, s);
}
Value::Array(arr) => {
write_u8(out, 5);
write_u32(out, arr.len() as u32);
for v in arr {
write_value(out, v);
}
}
}
}
fn write_u8(out: &mut Vec<u8>, n: u8) {
out.push(n);
}
fn write_u32(out: &mut Vec<u8>, n: u32) {
out.extend_from_slice(&n.to_le_bytes());
}
fn write_i64(out: &mut Vec<u8>, n: i64) {
out.extend_from_slice(&n.to_le_bytes());
}
fn write_f64(out: &mut Vec<u8>, n: f64) {
out.extend_from_slice(&n.to_le_bytes());
}
fn write_string(out: &mut Vec<u8>, s: &str) {
write_u32(out, s.len() as u32);
out.extend_from_slice(s.as_bytes());
}
struct Cursor<'a> {
bytes: &'a [u8],
pos: usize,
}
impl<'a> Cursor<'a> {
fn new(bytes: &'a [u8]) -> Self {
Self { bytes, pos: 0 }
}
fn read_u8(&mut self) -> Result<u8, String> {
if self.pos >= self.bytes.len() {
return Err("Unexpected end of bytecode".to_string());
}
let v = self.bytes[self.pos];
self.pos += 1;
Ok(v)
}
fn read_u32(&mut self) -> Result<u32, String> {
let mut buf = [0u8; 4];
self.read_exact(&mut buf)?;
Ok(u32::from_le_bytes(buf))
}
fn read_i64(&mut self) -> Result<i64, String> {
let mut buf = [0u8; 8];
self.read_exact(&mut buf)?;
Ok(i64::from_le_bytes(buf))
}
fn read_f64(&mut self) -> Result<f64, String> {
let mut buf = [0u8; 8];
self.read_exact(&mut buf)?;
Ok(f64::from_le_bytes(buf))
}
fn read_exact(&mut self, out: &mut [u8]) -> Result<(), String> {
if self.pos + out.len() > self.bytes.len() {
return Err("Unexpected end of bytecode".to_string());
}
out.copy_from_slice(&self.bytes[self.pos..self.pos + out.len()]);
self.pos += out.len();
Ok(())
}
fn read_string(&mut self) -> Result<String, String> {
let len = self.read_u32()? as usize;
if self.pos + len > self.bytes.len() {
return Err("Unexpected end of bytecode".to_string());
}
let s = std::str::from_utf8(&self.bytes[self.pos..self.pos + len])
.map_err(|_| "Invalid UTF-8 in bytecode".to_string())?;
self.pos += len;
Ok(s.to_string())
}
fn read_opt_string(&mut self) -> Result<Option<String>, String> {
let flag = self.read_u8()?;
if flag == 0 {
Ok(None)
} else {
Ok(Some(self.read_string()?))
}
}
fn read_value(&mut self) -> Result<Value, String> {
match self.read_u8()? {
0 => Ok(Value::Null),
1 => Ok(Value::Int(self.read_i64()?)),
2 => Ok(Value::Float(self.read_f64()?)),
3 => Ok(Value::Bool(self.read_u8()? != 0)),
4 => Ok(Value::Str(self.read_string()?)),
5 => {
let len = self.read_u32()? as usize;
let mut arr = Vec::with_capacity(len);
for _ in 0..len {
arr.push(self.read_value()?);
}
Ok(Value::Array(arr))
}
_ => Err("Invalid value tag in bytecode".to_string()),
}
}
fn read_instrs(&mut self) -> Result<Vec<Instr>, String> {
let len = self.read_u32()? as usize;
let mut instrs = Vec::with_capacity(len);
for _ in 0..len {
instrs.push(self.read_instr()?);
}
Ok(instrs)
}
fn read_strings(&mut self) -> Result<Vec<String>, String> {
let len = self.read_u32()? as usize;
let mut out = Vec::with_capacity(len);
for _ in 0..len {
out.push(self.read_string()?);
}
Ok(out)
}
fn read_instr(&mut self) -> Result<Instr, String> {
match self.read_u8()? {
0 => Ok(Instr::Push(self.read_value()?)),
1 => Ok(Instr::Load(self.read_string()?)),
2 => Ok(Instr::Store(self.read_string()?)),
3 => Ok(Instr::Pop),
4 => Ok(Instr::Declare {
name: self.read_string()?,
is_mut: self.read_u8()? != 0,
type_hint: self.read_opt_string()?,
}),
5 => Ok(Instr::BinOp(self.read_string()?)),
6 => Ok(Instr::MakeArray(self.read_u32()? as usize)),
7 => Ok(Instr::Range),
8 => Ok(Instr::Call {
name: self.read_string()?,
argc: self.read_u32()? as usize,
}),
9 => Ok(Instr::If {
cond: self.read_instrs()?,
then_block: self.read_instrs()?,
else_block: self.read_instrs()?,
}),
10 => Ok(Instr::Loop {
body: self.read_instrs()?,
}),
11 => Ok(Instr::While {
cond: self.read_instrs()?,
body: self.read_instrs()?,
}),
12 => Ok(Instr::For {
var: self.read_string()?,
iter: self.read_instrs()?,
body: self.read_instrs()?,
}),
13 => Ok(Instr::FnDef {
name: self.read_string()?,
params: self.read_strings()?,
body: self.read_instrs()?,
}),
14 => Ok(Instr::Return),
15 => Ok(Instr::Break),
16 => {
let value = self.read_instrs()?;
let count = self.read_u32()? as usize;
let mut cases = Vec::with_capacity(count);
for _ in 0..count {
let case_val = self.read_instrs()?;
let body = self.read_instrs()?;
cases.push((case_val, body));
}
let has_default = self.read_u8()? != 0;
let default = if has_default {
Some(self.read_instrs()?)
} else {
None
};
Ok(Instr::Switch {
value,
cases,
default,
})
}
17 => Ok(Instr::Try {
body: self.read_instrs()?,
catch_body: self.read_instrs()?,
}),
18 => {
let count = self.read_u32()? as usize;
let mut parts = Vec::with_capacity(count);
for _ in 0..count {
let tag = self.read_u8()?;
if tag == 0 {
parts.push(InstrPart::Text(self.read_string()?));
} else {
parts.push(InstrPart::Expr(self.read_instrs()?));
}
}
Ok(Instr::Interpolated(parts))
}
19 => Ok(Instr::Import(self.read_string()?)),
_ => Err("Invalid instruction tag in bytecode".to_string()),
}
}
}