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.chunk_index as u32);
134            }
135            Value::Closure(c) => {
136                self.u8(10);
137                self.u32(c.function_index as u32);
138                self.u32(c.arity as u32);
139                self.u32(c.upvalues.len() as u32);
140                for u in &c.upvalues {
141                    self.u32(*u as u32);
142                }
143            }
144            Value::Class(cls) => {
145                self.u8(11);
146                self.str(&cls.name);
147                self.u32(cls.methods.len() as u32);
148                for (mname, ci) in cls.methods.iter() {
149                    self.str(mname);
150                    self.u32(*ci as u32);
151                }
152                match &cls.superclass {
153                    Some(s) => {
154                        self.u8(1);
155                        self.str(s);
156                    }
157                    None => self.u8(0),
158                }
159                self.u32(cls.properties.len() as u32);
160                for (pname, pval) in cls.properties.iter() {
161                    self.str(pname);
162                    self.value(pval);
163                }
164            }
165            Value::Instance {
166                class_index,
167                fields,
168            } => {
169                self.u8(12);
170                self.u32(*class_index as u32);
171                let fields = fields.borrow();
172                self.u32(fields.len() as u32);
173                for (fname, fval) in fields.iter() {
174                    self.str(fname);
175                    self.value(fval);
176                }
177            }
178            Value::Ok(inner) => {
179                self.u8(13);
180                self.value(inner);
181            }
182            Value::Err(inner) => {
183                self.u8(14);
184                self.value(inner);
185            }
186            Value::Some(inner) => {
187                self.u8(15);
188                self.value(inner);
189            }
190            Value::Builtin { name, arity } => {
191                self.u8(16);
192                self.str(name);
193                self.u32(*arity as u32);
194            }
195        }
196    }
197}
198
199struct Reader<'a> {
200    data: &'a [u8],
201    pos: usize,
202}
203
204impl<'a> Reader<'a> {
205    fn new(data: &'a [u8]) -> Self {
206        Self { data, pos: 0 }
207    }
208
209    fn remaining(&self) -> usize {
210        self.data.len().saturating_sub(self.pos)
211    }
212
213    fn take(&mut self, n: usize) -> Result<&'a [u8], BytecodeFormatError> {
214        if self.remaining() < n {
215            return Err(BytecodeFormatError::Truncated);
216        }
217        let s = &self.data[self.pos..self.pos + n];
218        self.pos += n;
219        Ok(s)
220    }
221
222    fn u8(&mut self) -> Result<u8, BytecodeFormatError> {
223        Ok(self.take(1)?[0])
224    }
225
226    fn u16(&mut self) -> Result<u16, BytecodeFormatError> {
227        let b = self.take(2)?;
228        Ok(u16::from_le_bytes([b[0], b[1]]))
229    }
230
231    fn u32(&mut self) -> Result<u32, BytecodeFormatError> {
232        let b = self.take(4)?;
233        Ok(u32::from_le_bytes([b[0], b[1], b[2], b[3]]))
234    }
235
236    fn u64(&mut self) -> Result<u64, BytecodeFormatError> {
237        let b = self.take(8)?;
238        let mut a = [0u8; 8];
239        a.copy_from_slice(b);
240        Ok(u64::from_le_bytes(a))
241    }
242
243    fn i64(&mut self) -> Result<i64, BytecodeFormatError> {
244        Ok(self.u64()? as i64)
245    }
246
247    fn f64(&mut self) -> Result<f64, BytecodeFormatError> {
248        Ok(f64::from_bits(self.u64()?))
249    }
250
251    fn str(&mut self) -> Result<String, BytecodeFormatError> {
252        let len = self.u32()? as usize;
253        let bytes = self.take(len)?;
254        core::str::from_utf8(bytes)
255            .map(|s| s.to_string())
256            .map_err(|_| BytecodeFormatError::InvalidUtf8)
257    }
258
259    fn value(&mut self) -> Result<Value, BytecodeFormatError> {
260        let tag = self.u8()?;
261        match tag {
262            0 => Ok(Value::Null),
263            1 => Ok(Value::Bool(self.u8()? != 0)),
264            2 => Ok(Value::Int(self.i64()?)),
265            3 => Ok(Value::Float(self.f64()?)),
266            4 => {
267                let s = self.str()?;
268                Ok(Value::Str(Rc::from(s.as_str())))
269            }
270            5 => {
271                let cp = self.u32()?;
272                let c = char::from_u32(cp).ok_or(BytecodeFormatError::InvalidTag(tag))?;
273                Ok(Value::Char(c))
274            }
275            6 => {
276                let n = self.u32()? as usize;
277                let mut items = Vec::with_capacity(n);
278                for _ in 0..n {
279                    items.push(self.value()?);
280                }
281                Ok(Value::Array(Rc::new(RefCell::new(items))))
282            }
283            7 => {
284                let n = self.u32()? as usize;
285                let mut entries = Vec::with_capacity(n);
286                for _ in 0..n {
287                    let k = self.value()?;
288                    let v = self.value()?;
289                    entries.push((k, v));
290                }
291                Ok(Value::Map(Rc::new(RefCell::new(entries))))
292            }
293            8 => {
294                let n = self.u32()? as usize;
295                let mut items = Vec::with_capacity(n);
296                for _ in 0..n {
297                    items.push(self.value()?);
298                }
299                Ok(Value::Tuple(Rc::new(RefCell::new(items))))
300            }
301            9 => {
302                let name = self.str()?;
303                let arity = self.u32()? as usize;
304                let chunk_index = self.u32()? as usize;
305                Ok(Value::Function(Rc::new(FunctionObj {
306                    name: Rc::from(name.as_str()),
307                    arity,
308                    chunk_index,
309                })))
310            }
311            10 => {
312                let function_index = self.u32()? as usize;
313                let arity = self.u32()? as usize;
314                let n = self.u32()? as usize;
315                let mut upvalues = Vec::with_capacity(n);
316                for _ in 0..n {
317                    upvalues.push(self.u32()? as usize);
318                }
319                Ok(Value::Closure(Rc::new(ClosureObj {
320                    function_index,
321                    arity,
322                    upvalues,
323                })))
324            }
325            11 => {
326                let name = self.str()?;
327                let n_methods = self.u32()? as usize;
328                let mut methods = Vec::with_capacity(n_methods);
329                for _ in 0..n_methods {
330                    let mname = self.str()?;
331                    let ci = self.u32()? as usize;
332                    methods.push((mname, ci));
333                }
334                let has_super = self.u8()?;
335                let superclass = if has_super == 1 {
336                    Some(Rc::from(self.str()?.as_str()))
337                } else {
338                    None
339                };
340                let n_props = self.u32()? as usize;
341                let mut properties = Vec::with_capacity(n_props);
342                for _ in 0..n_props {
343                    let pname = self.str()?;
344                    let pval = self.value()?;
345                    properties.push((pname, pval));
346                }
347                Ok(Value::Class(Rc::new(ClassObj {
348                    name: Rc::from(name.as_str()),
349                    methods: Rc::new(methods),
350                    superclass,
351                    properties: Rc::new(properties),
352                })))
353            }
354            12 => {
355                let class_index = self.u32()? as usize;
356                let n = self.u32()? as usize;
357                let mut fields = Vec::with_capacity(n);
358                for _ in 0..n {
359                    let fname = self.str()?;
360                    let fval = self.value()?;
361                    fields.push((fname, fval));
362                }
363                Ok(Value::Instance {
364                    class_index,
365                    fields: Rc::new(RefCell::new(fields)),
366                })
367            }
368            13 => Ok(Value::Ok(Box::new(self.value()?))),
369            14 => Ok(Value::Err(Box::new(self.value()?))),
370            15 => Ok(Value::Some(Box::new(self.value()?))),
371            16 => {
372                let name = self.str()?;
373                let arity = self.u32()? as usize;
374                Ok(Value::Builtin {
375                    name: Rc::from(name.as_str()),
376                    arity,
377                })
378            }
379            other => Err(BytecodeFormatError::InvalidTag(other)),
380        }
381    }
382}
383
384/// Encode bytecode into the `.mailangbc` container.
385pub fn encode(bc: &Bytecode) -> Vec<u8> {
386    let mut w = Writer::new();
387    w.buf.extend_from_slice(FORMAT_MAGIC);
388    w.u16(FORMAT_VERSION);
389    w.u16(0); // flags
390    w.u32(bc.main_chunk as u32);
391
392    w.u32(bc.global_names.len() as u32);
393    for name in &bc.global_names {
394        w.str(name);
395    }
396
397    w.u32(bc.chunks.len() as u32);
398    for chunk in &bc.chunks {
399        w.str(&chunk.name);
400        w.u32(chunk.constants.len() as u32);
401        for c in &chunk.constants {
402            w.value(c);
403        }
404        w.u32(chunk.instructions.len() as u32);
405        for ins in &chunk.instructions {
406            w.u8(ins.opcode.as_u8());
407            match ins.operand {
408                Some(op) => {
409                    w.u8(1);
410                    w.u32(op);
411                }
412                None => w.u8(0),
413            }
414            w.u32(ins.line);
415        }
416    }
417    w.buf
418}
419
420/// Decode a `.mailangbc` container into bytecode.
421pub fn decode(data: &[u8]) -> Result<Bytecode, BytecodeFormatError> {
422    let mut r = Reader::new(data);
423    let magic = r.take(8)?;
424    if magic != FORMAT_MAGIC {
425        return Err(BytecodeFormatError::BadMagic);
426    }
427    let version = r.u16()?;
428    if version != FORMAT_VERSION {
429        return Err(BytecodeFormatError::UnsupportedVersion(version));
430    }
431    let _flags = r.u16()?;
432    let main_chunk = r.u32()? as usize;
433
434    let n_globals = r.u32()? as usize;
435    let mut global_names = Vec::with_capacity(n_globals);
436    for _ in 0..n_globals {
437        global_names.push(r.str()?);
438    }
439
440    let n_chunks = r.u32()? as usize;
441    let mut chunks = Vec::with_capacity(n_chunks);
442    for _ in 0..n_chunks {
443        let name = r.str()?;
444        let n_const = r.u32()? as usize;
445        let mut constants = Vec::with_capacity(n_const);
446        for _ in 0..n_const {
447            constants.push(r.value()?);
448        }
449        let n_instr = r.u32()? as usize;
450        let mut instructions = Vec::with_capacity(n_instr);
451        for _ in 0..n_instr {
452            let op_u8 = r.u8()?;
453            let opcode = Opcode::from_u8(op_u8).ok_or(BytecodeFormatError::InvalidOpcode(op_u8))?;
454            let has_operand = r.u8()?;
455            let operand = if has_operand == 1 {
456                Some(r.u32()?)
457            } else {
458                None
459            };
460            let line = r.u32()?;
461            instructions.push(Instruction {
462                opcode,
463                operand,
464                line,
465            });
466        }
467        chunks.push(Chunk {
468            instructions,
469            constants,
470            name,
471        });
472    }
473
474    if r.remaining() != 0 {
475        return Err(BytecodeFormatError::TrailingData);
476    }
477
478    Ok(Bytecode {
479        chunks,
480        main_chunk,
481        global_names,
482    })
483}
484
485#[cfg(test)]
486mod tests {
487    use super::*;
488    use crate::Bytecode;
489
490    #[test]
491    fn roundtrip_empty() {
492        let bc = Bytecode::new();
493        let bytes = encode(&bc);
494        let back = decode(&bytes).unwrap();
495        assert_eq!(bc, back);
496    }
497
498    #[test]
499    fn roundtrip_simple_program() {
500        let mut bc = Bytecode::new();
501        let slot = bc.intern_global("x");
502        let main = &mut bc.chunks[0];
503        main.emit(Opcode::Push, Some(0), 1);
504        main.emit(Opcode::StoreGlobal, Some(slot), 1);
505        main.emit(Opcode::Halt, None, 2);
506        main.add_constant(Value::Int(42));
507
508        let bytes = encode(&bc);
509        let back = decode(&bytes).unwrap();
510        assert_eq!(bc, back);
511    }
512
513    #[test]
514    fn reject_bad_magic() {
515        assert_eq!(decode(b"NOTMAGIC0000"), Err(BytecodeFormatError::BadMagic));
516    }
517}