summaryrefslogtreecommitdiff
path: root/matrix-lang/src/binary
diff options
context:
space:
mode:
Diffstat (limited to 'matrix-lang/src/binary')
-rw-r--r--matrix-lang/src/binary/deserialize.rs160
-rw-r--r--matrix-lang/src/binary/mod.rs154
-rw-r--r--matrix-lang/src/binary/prim.rs109
-rw-r--r--matrix-lang/src/binary/serialize.rs283
4 files changed, 706 insertions, 0 deletions
diff --git a/matrix-lang/src/binary/deserialize.rs b/matrix-lang/src/binary/deserialize.rs
new file mode 100644
index 0000000..679f6e5
--- /dev/null
+++ b/matrix-lang/src/binary/deserialize.rs
@@ -0,0 +1,160 @@
+use crate::prelude::*;
+
+use super::{prim::VarInt, Deserialize, Deserializer};
+
+macro_rules! error {
+ ($($arg:tt)*) => {
+ exception!(BINARY_EXCEPTION, $($arg)*)
+ };
+}
+
+impl Deserialize for Program {
+ fn deserialize<S: Deserializer>(s: &mut S) -> Result<Self> {
+ for _ in 0..5 { s.read::<u8>()?; } // skip header
+ let version: u8 = s.read()?;
+ if version != 0 {
+ return Err(error!("invalid program version {version}"))
+ }
+ let fun = <Rc<Function>>::deserialize(s)?;
+ Ok(Self { version, fun })
+ }
+}
+
+impl Deserialize for Instruction {
+ fn deserialize<S: Deserializer>(s: &mut S) -> Result<Self> {
+ use Instruction as I;
+ let ins: u8 = s.read()?;
+ let ins = match ins {
+ 0 => I::NoOp,
+ 1 => I::CreateLocal,
+ 2 => I::LoadLocal(s.read()?),
+ 3 => I::StoreLocal(s.read()?),
+ 4 => I::DiscardLocals(s.read()?),
+ 5 => I::LoadGlobal(s.read()?),
+ 6 => I::StoreGlobal(s.read()?),
+ 7 => I::Const(s.read()?),
+ 8 => I::Int(s.read()?),
+ 9 => I::True,
+ 10 => I::False,
+ 11 => I::Nil,
+ 12 => I::Dup,
+ 13 => I::Discard(s.read()?),
+ 14 => I::UnaryOp(UnaryOp::try_from(s.read::<u8>()?)?),
+ 15 => I::BinaryOp(BinaryOp::try_from(s.read::<u8>()?)?),
+ 16 => I::NewList(s.read()?),
+ 17 => I::NewTable(s.read()?),
+ 18 => I::NewMatrix(s.read()?, s.read()?),
+ 19 => I::Field(s.read()?),
+ 20 => I::StoreField(s.read()?),
+ 21 => I::Index(s.read()?),
+ 22 => I::StoreIndex(s.read()?),
+ 23 => I::Jump(s.read()?),
+ 24 => I::JumpTrue(s.read()?),
+ 25 => I::JumpFalse(s.read()?),
+ 26 => I::JumpNil(s.read()?),
+ 27 => I::IterCreate,
+ 28 => I::IterNext,
+ 29 => I::Try(s.read()?),
+ 30 => I::TryEnd,
+ 31 => I::Call(s.read()?),
+ 32 => I::Return,
+ n => return Err(error!("invalid instruction op code {n}"))
+ };
+ Ok(ins)
+ }
+}
+
+impl<T: Deserialize> Deserialize for Vec<T> {
+ fn deserialize<S: Deserializer>(s: &mut S) -> Result<Self> {
+ let len = s.read::<VarInt>()?.0;
+ let mut vec = Vec::with_capacity(len);
+ for _ in 0..len {
+ let v = T::deserialize(s)?;
+ vec.push(v);
+ }
+ Ok(vec)
+ }
+}
+
+impl<T: Deserialize> Deserialize for Gc<T> {
+ fn deserialize<S: Deserializer>(s: &mut S) -> Result<Self> {
+ Ok(Gc::new(T::deserialize(s)?))
+ }
+}
+
+impl<T: Deserialize> Deserialize for Rc<T> {
+ fn deserialize<S: Deserializer>(s: &mut S) -> Result<Self> {
+ Ok(Rc::new(T::deserialize(s)?))
+ }
+}
+
+impl Deserialize for Position {
+ fn deserialize<S: Deserializer>(s: &mut S) -> Result<Self> {
+ let row = s.read::<VarInt>()?.0;
+ let col = s.read::<VarInt>()?.0;
+ Ok(Self { row, col })
+ }
+}
+
+impl Deserialize for Chunk {
+ fn deserialize<S: Deserializer>(s: &mut S) -> Result<Self> {
+ let constants = <Vec<Value>>::deserialize(s)?;
+ let code = <Vec<Instruction>>::deserialize(s)?;
+ let pos = <Vec<Position>>::deserialize(s)?;
+ Ok(Self { constants, code, pos })
+ }
+}
+
+impl Deserialize for Value {
+ fn deserialize<S: Deserializer>(s: &mut S) -> Result<Self> {
+ use Value as V;
+ let ty = s.read::<u8>()?;
+ let value = match ty {
+ 0 => V::Nil,
+ 1 => V::Bool(s.read()?),
+ 2 => V::Int(s.read()?),
+ 3 => V::Float(s.read()?),
+ 4 => V::Ratio(Rational64::new(s.read()?, s.read()?)),
+ 5 => V::Complex(Complex64::new(s.read()?, s.read()?)),
+ 6 => V::to_regex(s.read::<String>()?.as_str())?,
+ 7 => V::String(s.read::<String>()?.into()),
+ 8 => V::List(<Vec<Value>>::deserialize(s)?.into()),
+ 9 => {
+ let domain = s.read()?;
+ let codomain = s.read()?;
+ let values = <Vec<Value>>::deserialize(s)?;
+ V::Matrix(Matrix::new(domain, codomain, values).into())
+ },
+ 10 => {
+ let len = s.read::<VarInt>()?.0;
+ let mut table = ValueMap::new();
+ for _ in 0..len {
+ let key = <Value>::deserialize(s)?;
+ let value = <Value>::deserialize(s)?;
+ table.insert(key, value)?;
+ }
+ V::Table(table.into())
+ },
+ 11 => V::Function(<Rc<Function>>::deserialize(s)?),
+ 12 => V::Range((s.read()?, s.read()?, s.read()?).into()),
+ 13 => V::Iter(<Rc<Function>>::deserialize(s)?),
+ n => return Err(error!("invalid value code {n}"))
+ };
+ Ok(value)
+ }
+}
+
+impl Deserialize for Function {
+ fn deserialize<S: Deserializer>(s: &mut S) -> Result<Self> {
+ let name = s.read::<String>()?;
+ let arity = s.read()?;
+ let variadic = s.read()?;
+ let chunk = <Chunk>::deserialize(s)?;
+ Ok(Function {
+ name: Rc::from(name.as_str()),
+ arity,
+ variadic,
+ fun: InnerFunction::Compiled(chunk.into())
+ })
+ }
+}
diff --git a/matrix-lang/src/binary/mod.rs b/matrix-lang/src/binary/mod.rs
new file mode 100644
index 0000000..53b3fe5
--- /dev/null
+++ b/matrix-lang/src/binary/mod.rs
@@ -0,0 +1,154 @@
+use crate::prelude::*;
+use std::io::{self, Read, Write};
+
+mod serialize;
+mod deserialize;
+mod prim;
+
+pub struct Program {
+ version: u8,
+ fun: Rc<Function>
+}
+
+const PROGRAM_HEADER: [u8; 5] = [0x00, 0x4d, 0x41, 0x54, 0x0a];
+
+impl Program {
+ pub fn load(body: &str) -> Result<Option<Rc<Function>>> {
+ let mut bytes = body.as_bytes();
+ if bytes.len() < 6 {
+ return Ok(None)
+ }
+ let header = &bytes[0..5];
+ if header != &PROGRAM_HEADER {
+ return Ok(None)
+ }
+ let mut s = ProgramDeserializer::from(&mut bytes);
+ let program = <Self>::deserialize(&mut s)?;
+ s.finish()?;
+ Ok(Some(program.fun.clone()))
+ }
+
+ pub fn save<W: Write>(fun: Rc<Function>, w: &mut W) -> Result<()> {
+ let mut s = ProgramSerializer::from(w);
+ let p = Program {
+ version: 0,
+ fun
+ };
+ s.serialize(&p)?;
+ s.finish()?;
+ Ok(())
+ }
+}
+
+pub trait Primitive : Sized {
+ fn write<W: Write>(&self, w: &mut W) -> io::Result<()>;
+ fn read<R: Read>(r: &mut R) -> io::Result<Self>;
+}
+
+pub trait Serialize : Sized {
+ fn serialize<S: Serializer>(&self, s: &mut S) -> Result<()>;
+}
+
+pub trait Serializer : Sized {
+ fn serialize<S: Serialize>(&mut self, val: &S) -> Result<()> {
+ val.serialize(self)
+ }
+ fn write<P: Primitive>(&mut self, val: P) -> Result<()>;
+}
+
+pub trait Deserialize : Sized {
+ fn deserialize<S: Deserializer>(s: &mut S) -> Result<Self>;
+}
+
+pub trait Deserializer : Sized {
+ fn deserialize<D: Deserialize>(&mut self) -> Result<D> {
+ D::deserialize(self)
+ }
+ fn read<P: Primitive>(&mut self) -> Result<P>;
+}
+
+macro_rules! error {
+ ($($arg:tt)*) => {
+ exception!(BINARY_EXCEPTION, $($arg)*)
+ };
+}
+
+pub struct ProgramSerializer<'w, W: Write> {
+ writer: &'w mut W,
+ checksum: u64,
+}
+
+impl<'w, W: Write> ProgramSerializer<'w, W> {
+ fn finish(self) -> Result<()> {
+ let bytes = self.checksum.to_le_bytes();
+ self.writer.write(&bytes).map_err(|e| error!("{e}"))?;
+ Ok(())
+ }
+}
+
+impl<'w, W: Write> Serializer for ProgramSerializer<'w, W> {
+ fn write<P: Primitive>(&mut self, val: P) -> Result<()> {
+ val.write(self).map_err(|e| error!("{e}"))
+ }
+}
+
+impl<'w, W: Write> Write for ProgramSerializer<'w, W> {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ for b in buf {
+ self.checksum %= 0xf1e3beef;
+ self.checksum += *b as u64;
+ }
+ self.writer.write(buf)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ self.writer.flush()
+ }
+}
+
+impl<'w, W: Write> From<&'w mut W> for ProgramSerializer<'w, W> {
+ fn from(writer: &'w mut W) -> Self {
+ Self { writer, checksum: 0xfe }
+ }
+}
+
+pub struct ProgramDeserializer<'r, R: Read> {
+ reader: &'r mut R,
+ checksum: u64,
+}
+
+impl<'r, R: Read> ProgramDeserializer<'r, R> {
+ fn finish(self) -> Result<()> {
+ let mut bytes = [0u8; 8];
+ self.reader.read_exact(&mut bytes).map_err(|e| error!("{e}"))?;
+ let checksum = u64::from_le_bytes(bytes);
+ if self.checksum != checksum {
+ return Err(error!("checksum doesnt match"))
+ }
+ Ok(())
+ }
+}
+
+impl<'r, R: Read> Deserializer for ProgramDeserializer<'r, R> {
+ fn read<P: Primitive>(&mut self) -> Result<P> {
+ P::read(self).map_err(|e| error!("{e}"))
+ }
+}
+
+impl<'r, R: Read> Read for ProgramDeserializer<'r, R> {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ let c = self.reader.read(buf)?;
+ for i in 0..c {
+ let b = buf[i];
+ self.checksum %= 0xf1e3beef;
+ self.checksum += b as u64;
+ }
+ Ok(c)
+ }
+}
+
+impl<'r, R: Read> From<&'r mut R> for ProgramDeserializer<'r, R> {
+ fn from(reader: &'r mut R) -> Self {
+ Self { reader, checksum: 0xfe }
+ }
+}
diff --git a/matrix-lang/src/binary/prim.rs b/matrix-lang/src/binary/prim.rs
new file mode 100644
index 0000000..44e6898
--- /dev/null
+++ b/matrix-lang/src/binary/prim.rs
@@ -0,0 +1,109 @@
+use super::Primitive;
+use std::io::{self, Read, Write};
+
+macro_rules! marshal_number {
+ ($type:ident, $byte:expr) => {
+ impl Primitive for $type {
+ fn write<W: Write>(&self, write: &mut W) -> io::Result<()> {
+ write.write(&self.to_le_bytes())?;
+ Ok(())
+ }
+
+ fn read<R: Read>(read: &mut R) -> io::Result<Self> {
+ let mut bytes = [0u8; $byte];
+ read.read_exact(&mut bytes)?;
+ Ok(Self::from_le_bytes(bytes))
+ }
+ }
+ };
+}
+
+marshal_number!(u8, 1);
+marshal_number!(u16, 2);
+marshal_number!(i16, 2);
+marshal_number!(u32, 4);
+marshal_number!(i64, 8);
+marshal_number!(f64, 8);
+
+impl Primitive for bool {
+ fn write<W: Write>(&self, write: &mut W) -> io::Result<()> {
+ if *self {
+ write.write(&[1u8; 1])?;
+ } else {
+ write.write(&[0u8; 1])?;
+ }
+ Ok(())
+ }
+
+ fn read<R: Read>(read: &mut R) -> io::Result<Self> {
+ let mut bytes = [0u8; 1];
+ read.read_exact(&mut bytes)?;
+ if bytes[0] == 1 {
+ Ok(true)
+ } else {
+ Ok(false)
+ }
+ }
+}
+
+impl Primitive for String {
+ fn write<W: Write>(&self, write: &mut W) -> io::Result<()> {
+ let bytes = self.as_bytes();
+ let len = bytes.len();
+ write.write(&(len as u32).to_le_bytes())?;
+ write.write(&bytes)?;
+ Ok(())
+ }
+
+ fn read<R: Read>(read: &mut R) -> io::Result<Self> {
+ let len = usize::read(read)?;
+ let mut bytes = vec![0u8; len];
+ read.read_exact(&mut bytes)?;
+ Ok(String::from_utf8_lossy(bytes.as_slice()).to_string())
+ }
+}
+
+impl Primitive for usize {
+ fn write<W: Write>(&self, write: &mut W) -> io::Result<()> {
+ (*self as u32).write(write)
+ }
+
+ fn read<R: Read>(read: &mut R) -> io::Result<Self> {
+ Ok(u32::read(read)? as usize)
+ }
+}
+
+pub struct VarInt(pub usize);
+
+impl Primitive for VarInt {
+ fn write<W: Write>(&self, write: &mut W) -> io::Result<()> {
+ let mut data = self.0;
+ loop {
+ let mut byte = (data & 0x7F) as u8;
+ data >>= 7;
+ if data != 0 {
+ byte += 0x80;
+ }
+ write.write(&[byte; 1])?;
+ if data == 0 {
+ return Ok(())
+ }
+ }
+ }
+
+ fn read<R: Read>(read: &mut R) -> io::Result<Self> {
+ let mut buf = [0];
+ let mut result = 0usize;
+ for count in 0..8 {
+ if read.read(&mut buf)? != 1 {
+ return Ok(Self(0))
+ }
+ let byte = buf[0];
+ result |= ((byte & 0x7F) as usize) << (7 * count);
+ if byte & 0x80 == 0 {
+ return Ok(Self(result))
+ }
+ }
+ Ok(Self(0))
+ }
+}
diff --git a/matrix-lang/src/binary/serialize.rs b/matrix-lang/src/binary/serialize.rs
new file mode 100644
index 0000000..2f2b199
--- /dev/null
+++ b/matrix-lang/src/binary/serialize.rs
@@ -0,0 +1,283 @@
+use crate::{prelude::*, binary::PROGRAM_HEADER};
+use std::ops::Deref;
+use super::{prim::VarInt, Program, Serializer, Serialize};
+
+macro_rules! error {
+ ($($arg:tt)*) => {
+ exception!(BINARY_EXCEPTION, $($arg)*)
+ };
+}
+
+impl Serialize for Program {
+ fn serialize<S: Serializer>(&self, s: &mut S) -> Result<()> {
+ for b in PROGRAM_HEADER {
+ s.write(b)?;
+ }
+ s.write(self.version)?;
+ s.serialize(self.fun.as_ref())?;
+ Ok(())
+ }
+}
+
+impl Serialize for Instruction {
+ fn serialize<S: Serializer>(&self, s: &mut S) -> Result<()> {
+ use Instruction as I;
+ match self {
+ I::NoOp => {
+ s.write(0u8)?;
+ },
+ I::CreateLocal => {
+ s.write(1u8)?;
+ },
+ I::LoadLocal(idx) => {
+ s.write(2u8)?;
+ s.write(*idx)?;
+ },
+ I::StoreLocal(idx) => {
+ s.write(3u8)?;
+ s.write(*idx)?;
+ },
+ I::DiscardLocals(idx) => {
+ s.write(4u8)?;
+ s.write(*idx)?;
+ },
+ I::LoadGlobal(idx) => {
+ s.write(5u8)?;
+ s.write(*idx)?;
+ }
+ I::StoreGlobal(idx) => {
+ s.write(6u8)?;
+ s.write(*idx)?;
+ },
+ I::Const(idx) => {
+ s.write(7u8)?;
+ s.write(*idx)?;
+ },
+ I::Int(i) => {
+ s.write(8u8)?;
+ s.write(*i)?;
+ },
+ I::True => {
+ s.write(9u8)?;
+ },
+ I::False => {
+ s.write(10u8)?;
+ },
+ I::Nil => {
+ s.write(11u8)?;
+ },
+ I::Dup => {
+ s.write(12u8)?;
+ },
+ I::Discard(idx) => {
+ s.write(13u8)?;
+ s.write(*idx)?;
+ },
+ I::UnaryOp(op) => {
+ s.write(14u8)?;
+ s.write(*op as u8)?;
+ },
+ I::BinaryOp(op) => {
+ s.write(15u8)?;
+ s.write(*op as u8)?;
+ },
+ I::NewList(len) => {
+ s.write(16u8)?;
+ s.write(*len)?;
+ },
+ I::NewTable(len) => {
+ s.write(17u8)?;
+ s.write(*len)?;
+ },
+ I::NewMatrix(d, c) => {
+ s.write(18u8)?;
+ s.write(*d)?;
+ s.write(*c)?;
+ },
+ I::Field(idx) => {
+ s.write(19u8)?;
+ s.write(*idx)?;
+ },
+ I::StoreField(idx) => {
+ s.write(20u8)?;
+ s.write(*idx)?;
+ },
+ I::Index(idx) => {
+ s.write(21u8)?;
+ s.write(*idx)?;
+ },
+ I::StoreIndex(idx) => {
+ s.write(22u8)?;
+ s.write(*idx)?;
+ },
+ I::Jump(ip) => {
+ s.write(23u8)?;
+ s.write(*ip)?;
+ },
+ I::JumpTrue(ip) => {
+ s.write(24u8)?;
+ s.write(*ip)?;
+ },
+ I::JumpFalse(ip) => {
+ s.write(25u8)?;
+ s.write(*ip)?;
+ },
+ I::JumpNil(ip) => {
+ s.write(26u8)?;
+ s.write(*ip)?;
+ },
+ I::IterCreate => {
+ s.write(27u8)?;
+ },
+ I::IterNext => {
+ s.write(28u8)?;
+ },
+ I::Try(ip) => {
+ s.write(29u8)?;
+ s.write(*ip)?;
+ },
+ I::TryEnd => {
+ s.write(30u8)?;
+ },
+ I::Call(arity) => {
+ s.write(31u8)?;
+ s.write(*arity)?;
+ },
+ I::Return => {
+ s.write(32u8)?;
+ },
+ };
+ Ok(())
+ }
+}
+
+impl<T: Serialize> Serialize for Vec<T> {
+ fn serialize<S: Serializer>(&self, s: &mut S) -> Result<()> {
+ s.write(VarInt(self.len()))?;
+ for val in self {
+ val.serialize(s)?;
+ }
+ Ok(())
+ }
+}
+
+impl<T: Serialize + Deref> Serialize for Gc<T> {
+ fn serialize<S: Serializer>(&self, s: &mut S) -> Result<()> {
+ self.deref().serialize(s)
+ }
+}
+
+impl<T: Serialize + Deref> Serialize for Rc<T> {
+ fn serialize<S: Serializer>(&self, s: &mut S) -> Result<()> {
+ self.deref().serialize(s)
+ }
+}
+
+impl Serialize for Position {
+ fn serialize<S: Serializer>(&self, s: &mut S) -> Result<()> {
+ s.write(VarInt(self.row))?;
+ s.write(VarInt(self.col))?;
+ Ok(())
+ }
+}
+
+impl Serialize for Chunk {
+ fn serialize<S: Serializer>(&self, s: &mut S) -> Result<()> {
+ self.constants.serialize(s)?;
+ self.code.serialize(s)?;
+ self.pos.serialize(s)?;
+ Ok(())
+ }
+}
+
+impl Serialize for Value {
+ fn serialize<S: Serializer>(&self, s: &mut S) -> Result<()> {
+ use Value as V;
+ match self {
+ V::Nil => {
+ s.write(0u8)?;
+ },
+ V::Bool(b) => {
+ s.write(1u8)?;
+ s.write(*b)?;
+ },
+ V::Int(i) => {
+ s.write(2u8)?;
+ s.write(*i)?;
+ },
+ V::Float(f) => {
+ s.write(3u8)?;
+ s.write(*f)?;
+ },
+ V::Ratio(r) => {
+ s.write(4u8)?;
+ s.write(*r.numer())?;
+ s.write(*r.denom())?;
+ },
+ V::Complex(c) => {
+ s.write(5u8)?;
+ s.write(c.re)?;
+ s.write(c.im)?;
+ },
+ V::Regex(r) => {
+ s.write(6u8)?;
+ s.write(r.to_string())?;
+ },
+ V::String(str) => {
+ s.write(7u8)?;
+ s.write(str.to_string())?;
+ },
+ V::List(l) => {
+ s.write(8u8)?;
+ l.serialize(s)?;
+ },
+ V::Matrix(m) => {
+ s.write(9u8)?;
+ s.write(m.domain)?;
+ s.write(m.codomain)?;
+ m.values.serialize(s)?;
+ },
+ V::Table(t) => {
+ s.write(10u8)?;
+ s.write(VarInt(t.len()))?;
+ for (key, value) in t.entries() {
+ key.serialize(s)?;
+ value.serialize(s)?;
+ }
+ },
+ V::Function(f) => {
+ s.write(11u8)?;
+ f.serialize(s)?;
+ },
+ V::Range(r) => {
+ s.write(12u8)?;
+ s.write(r.0)?;
+ s.write(r.1)?;
+ s.write(r.2)?;
+ },
+ V::Iter(f) => {
+ s.write(13u8)?;
+ f.serialize(s)?;
+ },
+ V::File(_) => return Err(error!("cannot compile file")),
+ V::Exception(_) => return Err(error!("cannot compile exception")),
+ };
+ Ok(())
+ }
+}
+
+impl Serialize for Function {
+ fn serialize<S: Serializer>(&self, s: &mut S) -> Result<()> {
+ s.write(self.name.to_string())?;
+ s.write(self.arity)?;
+ s.write(self.variadic)?;
+ use InnerFunction as F;
+ match &self.fun {
+ F::Compiled(c) => {
+ c.serialize(s)?;
+ },
+ F::Native(_) => return Err(error!("cannot compile native function")),
+ };
+ Ok(())
+ }
+}