Skip to main content

mailang_bytecode/
format.rs

1//! Binary bytecode container format (`.mailangbc`).
2//!
3//! Layout (all multi-byte integers little-endian):
4//!
5//! ```text
6//! magic:   b"MAILBC01"          (8 bytes)
7//! version: u16                  (currently 1)
8//! flags:   u16                  (reserved, 0)
9//! main:    u32                  (main chunk index)
10//! n_globals: u32
11//!   for each global: u32 len + utf8 bytes
12//! n_chunks: u32
13//!   for each chunk:
14//!     name: u32 len + utf8 bytes
15//!     n_const: u32
16//!       for each constant: Value
17//!     n_instr: u32
18//!       for each instruction: opcode u8, has_operand u8, operand u32 (if has_operand), line u32
19//! ```
20//!
21//! Value tags (u8):
22//! 0 Null, 1 Bool, 2 Int, 3 Float, 4 Str, 5 Char,
23//! 6 Array, 7 Map, 8 Tuple, 9 Function, 10 Closure,
24//! 11 Class, 12 Instance, 13 Ok, 14 Err, 15 Some, 16 Builtin
25
26use crate::{Bytecode, Chunk, ClassObj, ClosureObj, FunctionObj, Instruction, Opcode, Value};
27use alloc::boxed::Box;
28use alloc::rc::Rc;
29use alloc::string::{String, ToString};
30use alloc::vec::Vec;
31use core::cell::RefCell;
32
33pub const FORMAT_MAGIC: &[u8; 8] = b"MAILBC01";
34pub const FORMAT_VERSION: u16 = 1;
35
36#[derive(Debug, Clone, PartialEq, Eq)]
37pub enum BytecodeFormatError {
38    Truncated,
39    BadMagic,
40    UnsupportedVersion(u16),
41    InvalidOpcode(u8),
42    InvalidTag(u8),
43    InvalidUtf8,
44    TrailingData,
45}
46
47struct Writer {
48    buf: Vec<u8>,
49}
50
51impl Writer {
52    fn new() -> Self {
53        Self { buf: Vec::new() }
54    }
55
56    fn u8(&mut self, v: u8) {
57        self.buf.push(v);
58    }
59
60    fn u16(&mut self, v: u16) {
61        self.buf.extend_from_slice(&v.to_le_bytes());
62    }
63
64    fn u32(&mut self, v: u32) {
65        self.buf.extend_from_slice(&v.to_le_bytes());
66    }
67
68    fn i64(&mut self, v: i64) {
69        self.buf.extend_from_slice(&v.to_le_bytes());
70    }
71
72    fn f64(&mut self, v: f64) {
73        self.buf.extend_from_slice(&v.to_le_bytes());
74    }
75
76    fn str(&mut self, s: &str) {
77        self.u32(s.len() as u32);
78        self.buf.extend_from_slice(s.as_bytes());
79    }
80
81    fn value(&mut self, v: &Value) {
82        match v {
83            Value::Null => self.u8(0),
84            Value::Bool(b) => {
85                self.u8(1);
86                self.u8(*b as u8);
87            }
88            Value::Int(n) => {
89                self.u8(2);
90                self.i64(*n);
91            }
92            Value::Float(n) => {
93                self.u8(3);
94                self.f64(*n);
95            }
96            Value::Str(s) => {
97                self.u8(4);
98                self.str(s);
99            }
100            Value::Char(c) => {
101                self.u8(5);
102                self.u32(*c as u32);
103            }
104            Value::Array(items) => {
105                self.u8(6);
106                let items = items.borrow();
107                self.u32(items.len() as u32);
108                for item in items.iter() {
109                    self.value(item);
110                }
111            }
112            Value::Map(entries) => {
113                self.u8(7);
114                let entries = entries.borrow();
115                self.u32(entries.len() as u32);
116                for (k, val) in entries.iter() {
117                    self.value(k);
118                    self.value(val);
119                }
120            }
121            Value::Tuple(items) => {
122                self.u8(8);
123                let items = items.borrow();
124                self.u32(items.len() as u32);
125                for item in items.iter() {
126                    self.value(item);
127                }
128            }
129            Value::Function(f) => {
130                self.u8(9);
131                self.str(&f.name);
132                self.u32(f.arity as u32);
133                self.u32(f.required as u32);
134                self.u32(f.chunk_index as u32);
135            }
136            Value::Closure(c) => {
137                self.u8(10);
138                self.u32(c.function_index as u32);
139                self.u32(c.arity as u32);
140                self.u32(c.required as u32);
141                self.u32(c.upvalues.len() as u32);
142                for u in &c.upvalues {
143                    self.u32(*u as u32);
144                }
145            }
146            Value::Class(cls) => {
147                self.u8(11);
148                self.str(&cls.name);
149                self.u32(cls.methods.len() as u32);
150                for (mname, ci) in cls.methods.iter() {
151                    self.str(mname);
152                    self.u32(*ci as u32);
153                }
154                match &cls.superclass {
155                    Some(s) => {
156                        self.u8(1);
157                        self.str(s);
158                    }
159                    None => self.u8(0),
160                }
161                self.u32(cls.properties.len() as u32);
162                for (pname, pval) in cls.properties.iter() {
163                    self.str(pname);
164                    self.value(pval);
165                }
166            }
167            Value::Instance {
168                class_index,
169                fields,
170            } => {
171                self.u8(12);
172                self.u32(*class_index as u32);
173                let fields = fields.borrow();
174                self.u32(fields.len() as u32);
175                for (fname, fval) in fields.iter() {
176                    self.str(fname);
177                    self.value(fval);
178                }
179            }
180            Value::Ok(inner) => {
181                self.u8(13);
182                self.value(inner);
183            }
184            Value::Err(inner) => {
185                self.u8(14);
186                self.value(inner);
187            }
188            Value::Some(inner) => {
189                self.u8(15);
190                self.value(inner);
191            }
192            Value::Builtin { name, arity } => {
193                self.u8(16);
194                self.str(name);
195                self.u32(*arity as u32);
196            }
197        }
198    }
199}
200
201struct Reader<'a> {
202    data: &'a [u8],
203    pos: usize,
204}
205
206impl<'a> Reader<'a> {
207    fn new(data: &'a [u8]) -> Self {
208        Self { data, pos: 0 }
209    }
210
211    fn remaining(&self) -> usize {
212        self.data.len().saturating_sub(self.pos)
213    }
214
215    fn take(&mut self, n: usize) -> Result<&'a [u8], BytecodeFormatError> {
216        if self.remaining() < n {
217            return Err(BytecodeFormatError::Truncated);
218        }
219        let s = &self.data[self.pos..self.pos + n];
220        self.pos += n;
221        Ok(s)
222    }
223
224    fn u8(&mut self) -> Result<u8, BytecodeFormatError> {
225        Ok(self.take(1)?[0])
226    }
227
228    fn u16(&mut self) -> Result<u16, BytecodeFormatError> {
229        let b = self.take(2)?;
230        Ok(u16::from_le_bytes([b[0], b[1]]))
231    }
232
233    fn u32(&mut self) -> Result<u32, BytecodeFormatError> {
234        let b = self.take(4)?;
235        Ok(u32::from_le_bytes([b[0], b[1], b[2], b[3]]))
236    }
237
238    fn u64(&mut self) -> Result<u64, BytecodeFormatError> {
239        let b = self.take(8)?;
240        let mut a = [0u8; 8];
241        a.copy_from_slice(b);
242        Ok(u64::from_le_bytes(a))
243    }
244
245    fn i64(&mut self) -> Result<i64, BytecodeFormatError> {
246        Ok(self.u64()? as i64)
247    }
248
249    fn f64(&mut self) -> Result<f64, BytecodeFormatError> {
250        Ok(f64::from_bits(self.u64()?))
251    }
252
253    fn str(&mut self) -> Result<String, BytecodeFormatError> {
254        let len = self.u32()? as usize;
255        let bytes = self.take(len)?;
256        core::str::from_utf8(bytes)
257            .map(|s| s.to_string())
258            .map_err(|_| BytecodeFormatError::InvalidUtf8)
259    }
260
261    fn value(&mut self) -> Result<Value, BytecodeFormatError> {
262        let tag = self.u8()?;
263        match tag {
264            0 => Ok(Value::Null),
265            1 => Ok(Value::Bool(self.u8()? != 0)),
266            2 => Ok(Value::Int(self.i64()?)),
267            3 => Ok(Value::Float(self.f64()?)),
268            4 => {
269                let s = self.str()?;
270                Ok(Value::Str(Rc::from(s.as_str())))
271            }
272            5 => {
273                let cp = self.u32()?;
274                let c = char::from_u32(cp).ok_or(BytecodeFormatError::InvalidTag(tag))?;
275                Ok(Value::Char(c))
276            }
277            6 => {
278                let n = self.u32()? as usize;
279                let mut items = Vec::with_capacity(n);
280                for _ in 0..n {
281                    items.push(self.value()?);
282                }
283                Ok(Value::Array(Rc::new(RefCell::new(items))))
284            }
285            7 => {
286                let n = self.u32()? as usize;
287                let mut entries = Vec::with_capacity(n);
288                for _ in 0..n {
289                    let k = self.value()?;
290                    let v = self.value()?;
291                    entries.push((k, v));
292                }
293                Ok(Value::Map(Rc::new(RefCell::new(entries))))
294            }
295            8 => {
296                let n = self.u32()? as usize;
297                let mut items = Vec::with_capacity(n);
298                for _ in 0..n {
299                    items.push(self.value()?);
300                }
301                Ok(Value::Tuple(Rc::new(RefCell::new(items))))
302            }
303            9 => {
304                let name = self.str()?;
305                let arity = self.u32()? as usize;
306                let required = self.u32()? as usize;
307                let chunk_index = self.u32()? as usize;
308                Ok(Value::Function(Rc::new(FunctionObj {
309                    name: Rc::from(name.as_str()),
310                    arity,
311                    required,
312                    chunk_index,
313                })))
314            }
315            10 => {
316                let function_index = self.u32()? as usize;
317                let arity = self.u32()? as usize;
318                let required = self.u32()? as usize;
319                let n = self.u32()? as usize;
320                let mut upvalues = Vec::with_capacity(n);
321                for _ in 0..n {
322                    upvalues.push(self.u32()? as usize);
323                }
324                Ok(Value::Closure(Rc::new(ClosureObj {
325                    function_index,
326                    arity,
327                    required,
328                    upvalues,
329                })))
330            }
331            11 => {
332                let name = self.str()?;
333                let n_methods = self.u32()? as usize;
334                let mut methods = Vec::with_capacity(n_methods);
335                for _ in 0..n_methods {
336                    let mname = self.str()?;
337                    let ci = self.u32()? as usize;
338                    methods.push((mname, ci));
339                }
340                let has_super = self.u8()?;
341                let superclass = if has_super == 1 {
342                    Some(Rc::from(self.str()?.as_str()))
343                } else {
344                    None
345                };
346                let n_props = self.u32()? as usize;
347                let mut properties = Vec::with_capacity(n_props);
348                for _ in 0..n_props {
349                    let pname = self.str()?;
350                    let pval = self.value()?;
351                    properties.push((pname, pval));
352                }
353                Ok(Value::Class(Rc::new(ClassObj {
354                    name: Rc::from(name.as_str()),
355                    methods: Rc::new(methods),
356                    superclass,
357                    properties: Rc::new(properties),
358                })))
359            }
360            12 => {
361                let class_index = self.u32()? as usize;
362                let n = self.u32()? as usize;
363                let mut fields = Vec::with_capacity(n);
364                for _ in 0..n {
365                    let fname = self.str()?;
366                    let fval = self.value()?;
367                    fields.push((fname, fval));
368                }
369                Ok(Value::Instance {
370                    class_index,
371                    fields: Rc::new(RefCell::new(fields)),
372                })
373            }
374            13 => Ok(Value::Ok(Box::new(self.value()?))),
375            14 => Ok(Value::Err(Box::new(self.value()?))),
376            15 => Ok(Value::Some(Box::new(self.value()?))),
377            16 => {
378                let name = self.str()?;
379                let arity = self.u32()? as usize;
380                Ok(Value::Builtin {
381                    name: Rc::from(name.as_str()),
382                    arity,
383                })
384            }
385            other => Err(BytecodeFormatError::InvalidTag(other)),
386        }
387    }
388}
389
390/// Encode bytecode into the `.mailangbc` container.
391pub fn encode(bc: &Bytecode) -> Vec<u8> {
392    let mut w = Writer::new();
393    w.buf.extend_from_slice(FORMAT_MAGIC);
394    w.u16(FORMAT_VERSION);
395    w.u16(0); // flags
396    w.u32(bc.main_chunk as u32);
397
398    w.u32(bc.global_names.len() as u32);
399    for name in &bc.global_names {
400        w.str(name);
401    }
402
403    w.u32(bc.chunks.len() as u32);
404    for chunk in &bc.chunks {
405        w.str(&chunk.name);
406        w.u32(chunk.constants.len() as u32);
407        for c in &chunk.constants {
408            w.value(c);
409        }
410        w.u32(chunk.instructions.len() as u32);
411        for ins in &chunk.instructions {
412            w.u8(ins.opcode.as_u8());
413            match ins.operand {
414                Some(op) => {
415                    w.u8(1);
416                    w.u32(op);
417                }
418                None => w.u8(0),
419            }
420            w.u32(ins.line);
421        }
422    }
423    w.buf
424}
425
426/// Decode a `.mailangbc` container into bytecode.
427pub fn decode(data: &[u8]) -> Result<Bytecode, BytecodeFormatError> {
428    let mut r = Reader::new(data);
429    let magic = r.take(8)?;
430    if magic != FORMAT_MAGIC {
431        return Err(BytecodeFormatError::BadMagic);
432    }
433    let version = r.u16()?;
434    if version != FORMAT_VERSION {
435        return Err(BytecodeFormatError::UnsupportedVersion(version));
436    }
437    let _flags = r.u16()?;
438    let main_chunk = r.u32()? as usize;
439
440    let n_globals = r.u32()? as usize;
441    let mut global_names = Vec::with_capacity(n_globals);
442    for _ in 0..n_globals {
443        global_names.push(r.str()?);
444    }
445
446    let n_chunks = r.u32()? as usize;
447    let mut chunks = Vec::with_capacity(n_chunks);
448    for _ in 0..n_chunks {
449        let name = r.str()?;
450        let n_const = r.u32()? as usize;
451        let mut constants = Vec::with_capacity(n_const);
452        for _ in 0..n_const {
453            constants.push(r.value()?);
454        }
455        let n_instr = r.u32()? as usize;
456        let mut instructions = Vec::with_capacity(n_instr);
457        for _ in 0..n_instr {
458            let op_u8 = r.u8()?;
459            let opcode = Opcode::from_u8(op_u8).ok_or(BytecodeFormatError::InvalidOpcode(op_u8))?;
460            let has_operand = r.u8()?;
461            let operand = if has_operand == 1 {
462                Some(r.u32()?)
463            } else {
464                None
465            };
466            let line = r.u32()?;
467            instructions.push(Instruction {
468                opcode,
469                operand,
470                line,
471            });
472        }
473        chunks.push(Chunk {
474            instructions,
475            constants,
476            name,
477        });
478    }
479
480    if r.remaining() != 0 {
481        return Err(BytecodeFormatError::TrailingData);
482    }
483
484    Ok(Bytecode {
485        chunks,
486        main_chunk,
487        global_names,
488    })
489}
490
491#[cfg(test)]
492mod tests {
493    use super::*;
494    use crate::Bytecode;
495
496    #[test]
497    fn roundtrip_empty() {
498        let bc = Bytecode::new();
499        let bytes = encode(&bc);
500        let back = decode(&bytes).unwrap();
501        assert_eq!(bc, back);
502    }
503
504    #[test]
505    fn roundtrip_simple_program() {
506        let mut bc = Bytecode::new();
507        let slot = bc.intern_global("x");
508        let main = &mut bc.chunks[0];
509        main.emit(Opcode::Push, Some(0), 1);
510        main.emit(Opcode::StoreGlobal, Some(slot), 1);
511        main.emit(Opcode::Halt, None, 2);
512        main.add_constant(Value::Int(42));
513
514        let bytes = encode(&bc);
515        let back = decode(&bytes).unwrap();
516        assert_eq!(bc, back);
517    }
518
519    #[test]
520    fn reject_bad_magic() {
521        assert_eq!(decode(b"NOTMAGIC0000"), Err(BytecodeFormatError::BadMagic));
522    }
523}