summaryrefslogtreecommitdiff
path: root/matrix-lang
diff options
context:
space:
mode:
authorFreya Murphy <freya@freyacat.org>2024-02-29 17:04:28 -0500
committerFreya Murphy <freya@freyacat.org>2024-02-29 17:04:28 -0500
commit5d2747e26f51cc2344a6bd95f93457248fdfebd8 (patch)
tree8755b4068166c3854d26817683ce438a771ab319 /matrix-lang
parentmore mat, sys, and os stdlib functions, better matrix printing, other fixes (diff)
downloadmatrix-5d2747e26f51cc2344a6bd95f93457248fdfebd8.tar.gz
matrix-5d2747e26f51cc2344a6bd95f93457248fdfebd8.tar.bz2
matrix-5d2747e26f51cc2344a6bd95f93457248fdfebd8.zip
fin prob
Diffstat (limited to '')
-rw-r--r--matrix-lang/Cargo.lock (renamed from matrix/Cargo.lock)0
-rw-r--r--matrix-lang/Cargo.toml (renamed from matrix/Cargo.toml)2
-rw-r--r--matrix-lang/src/ast.rs (renamed from matrix/src/ast.rs)100
-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
-rw-r--r--matrix-lang/src/chunk.rs (renamed from matrix/src/chunk.rs)71
-rw-r--r--matrix-lang/src/compiler.rs (renamed from matrix/src/compiler.rs)160
-rw-r--r--matrix-lang/src/lex.rs (renamed from matrix/src/lex.rs)177
-rw-r--r--matrix-lang/src/lib.rs42
-rw-r--r--matrix-lang/src/parse.rs (renamed from matrix/src/parse.rs)94
-rw-r--r--matrix-lang/src/prelude.rs60
-rw-r--r--matrix-lang/src/value/clone.rs54
-rw-r--r--matrix-lang/src/value/comp.rs373
-rw-r--r--matrix-lang/src/value/exception.rs78
-rw-r--r--matrix-lang/src/value/fmt.rs186
-rw-r--r--matrix-lang/src/value/function.rs43
-rw-r--r--matrix-lang/src/value/gc.rs (renamed from matrix/src/gc.rs)22
-rw-r--r--matrix-lang/src/value/hash.rs240
-rw-r--r--matrix-lang/src/value/index.rs230
-rw-r--r--matrix-lang/src/value/matrix.rs337
-rw-r--r--matrix-lang/src/value/mod.rs42
-rw-r--r--matrix-lang/src/value/value.rs522
-rw-r--r--matrix-lang/src/vm.rs (renamed from matrix/src/vm.rs)85
25 files changed, 3252 insertions, 372 deletions
diff --git a/matrix/Cargo.lock b/matrix-lang/Cargo.lock
index ed6e714..ed6e714 100644
--- a/matrix/Cargo.lock
+++ b/matrix-lang/Cargo.lock
diff --git a/matrix/Cargo.toml b/matrix-lang/Cargo.toml
index 64e9210..067f77c 100644
--- a/matrix/Cargo.toml
+++ b/matrix-lang/Cargo.toml
@@ -1,5 +1,5 @@
[package]
-name = "matrix"
+name = "matrix-lang"
version = "0.1.0"
edition = "2021"
diff --git a/matrix/src/ast.rs b/matrix-lang/src/ast.rs
index de63630..5720f76 100644
--- a/matrix/src/ast.rs
+++ b/matrix-lang/src/ast.rs
@@ -1,42 +1,70 @@
-use std::{rc::Rc, ops::{Neg, Not}, fmt::Debug};
-use crate::{lex::{Position, TokenData}, value::{Value, InlineList, InlineMatrix, InlineTable}, Result};
+use std::{ops::{Neg, Not}, fmt::{Debug, Display}};
+
+use crate::prelude::*;
+
+pub type AstName = (Rc<str>, Position);
+pub type InlineList = Vec<Expr>;
+pub type InlineMatrix = (usize, usize, Vec<Expr>);
+pub type InlineTable = Vec<(Expr, Expr)>;
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum UnaryOp {
// normal math
- Negate,
+ Negate = 1,
// equality
- Not,
+ Not = 2,
+}
+
+impl TryFrom<u8> for UnaryOp {
+ type Error = Exception;
+
+ fn try_from(value: u8) -> std::prelude::v1::Result<Self, Self::Error> {
+ if value < 1 || value > 2 {
+ Err(exception!(BINARY_EXCEPTION, "cannot convert {value} to UnaryOp"))
+ } else {
+ unsafe { Ok(std::mem::transmute(value)) }
+ }
+ }
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum BinaryOp {
// normal math
- Add,
- Subtract,
- Multiply,
- Divide,
- Modulo,
- Power,
+ Add = 1,
+ Subtract = 2,
+ Multiply = 3,
+ Divide = 4,
+ Modulo = 5,
+ Power = 6,
// binary math
- BitwiseAnd,
- BitwiseOr,
- BitwiseXor,
- BitwiseShiftLeft,
- BitwiseShiftRight,
+ BitwiseAnd = 7,
+ BitwiseOr = 8,
+ BitwiseXor = 9,
+ BitwiseShiftLeft = 10,
+ BitwiseShiftRight = 11,
// equality
- Equals,
- NotEquals,
- GreaterEquals,
- LessEquals,
- GreaterThan,
- LessThan,
+ Equals = 12,
+ NotEquals = 13,
+ GreaterEquals = 14,
+ LessEquals = 15,
+ GreaterThan = 16,
+ LessThan = 17,
// iter
- Range,
- RangeEq
+ Range = 18,
+ RangeEq = 19
}
-pub type AstName = (Rc<str>, Position);
+impl TryFrom<u8> for BinaryOp {
+ type Error = Exception;
+
+ fn try_from(value: u8) -> std::prelude::v1::Result<Self, Self::Error> {
+ if value < 1 || value > 19 {
+ Err(exception!(BINARY_EXCEPTION, "cannot convert {value} to BinaryOp"))
+ } else {
+ unsafe { Ok(std::mem::transmute(value)) }
+ }
+ }
+}
#[derive(Debug, Clone, PartialEq)]
pub enum ExprData {
@@ -75,6 +103,7 @@ pub enum ExprData {
Try(Box<Expr>, AstName, Box<Expr>),
Let(AstName, Box<Expr>),
+ Const(AstName, Box<Expr>),
Pipeline(Box<Expr>, Box<Expr>),
@@ -83,22 +112,24 @@ pub enum ExprData {
Return(Box<Expr>),
}
-#[derive(Clone, PartialEq)]
-pub struct Expr {
- pub data: ExprData,
- pub pos: Position
+impl Display for Expr {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{self:?}")
+ }
}
impl Debug for Expr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- if f.alternate() {
- write!(f, "{:#?}", self.data)
- } else {
- write!(f, "{:?}", self.data)
- }
+ write!(f, "{:?}", self.data)
}
}
+#[derive(Clone, PartialEq)]
+pub struct Expr {
+ pub data: ExprData,
+ pub pos: Position
+}
+
impl From<(ExprData, Position)> for Expr {
fn from(value: (ExprData, Position)) -> Self {
Self { data: value.0, pos: value.1 }
@@ -377,6 +408,9 @@ pub fn optimize(mut expr: Expr) -> Result<Expr> {
E::Let(ident, expr) => {
E::Let(ident, Box::new(optimize(*expr)?))
},
+ E::Const(ident, expr) => {
+ E::Const(ident, Box::new(optimize(*expr)?))
+ },
E::Assign(lhs, rhs) => {
let lhs = Box::new(optimize(*lhs)?);
let rhs = Box::new(optimize(*rhs)?);
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(())
+ }
+}
diff --git a/matrix/src/chunk.rs b/matrix-lang/src/chunk.rs
index 495b787..2fc3d9e 100644
--- a/matrix/src/chunk.rs
+++ b/matrix-lang/src/chunk.rs
@@ -1,5 +1,5 @@
-use crate::{value::Value, ast::{UnaryOp, BinaryOp}, vm::{Vm, StackFrame}, Result, lex::Position};
-use std::{fmt::{Debug, Display}, rc::Rc};
+use std::fmt::{Debug, Display};
+use crate::prelude::*;
#[derive(Clone, Default)]
pub struct Chunk {
@@ -18,53 +18,6 @@ impl Chunk {
}
}
-impl Debug for Chunk {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "Chunk({})", self.code.len())
- }
-}
-
-impl Display for Chunk {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- writeln!(f, "constants: ")?;
- for (i, c) in self.constants.iter().enumerate() {
- writeln!(f, " {i:04}: {c}")?;
- }
- writeln!(f, "code:")?;
- for (i, ins) in self.code.iter().enumerate() {
- writeln!(f, " {i:04}: {ins}")?;
- }
- Ok(())
- }
-}
-
-pub struct Function {
- pub name: Rc<str>,
- pub arity: usize,
- pub variadic: bool,
- pub fun: InnerFunction
-}
-
-#[derive(Clone)]
-pub enum InnerFunction {
- Compiled(Rc<Chunk>),
- Native(Rc<dyn Fn((&mut Vm, &mut StackFrame), Vec<Value>) -> Result<Value>>),
-}
-
-impl Debug for Function {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- use InnerFunction as F;
- match self.fun {
- F::Compiled(_) => {
- write!(f, "[Function {}]", self.name)
- },
- F::Native(_) => {
- write!(f, "[NativeFunction {}]", self.name)
- }
- }
- }
-}
-
#[derive(Clone, Debug)]
#[repr(align(4))]
pub enum Instruction {
@@ -113,6 +66,26 @@ pub enum Instruction {
Return,
}
+impl Debug for Chunk {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "[Chunk {}]", self.code.len())
+ }
+}
+
+impl Display for Chunk {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ writeln!(f, "constants: ")?;
+ for (i, c) in self.constants.iter().enumerate() {
+ writeln!(f, " {i:04}: {c}")?;
+ }
+ writeln!(f, "code:")?;
+ for (i, ins) in self.code.iter().enumerate() {
+ writeln!(f, " {i:04}: {ins}")?;
+ }
+ Ok(())
+ }
+}
+
impl Display for Instruction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use Instruction::*;
diff --git a/matrix/src/compiler.rs b/matrix-lang/src/compiler.rs
index 6b6a94b..95c6ccf 100644
--- a/matrix/src/compiler.rs
+++ b/matrix-lang/src/compiler.rs
@@ -1,14 +1,14 @@
-use std::{fmt::Display, rc::Rc, cell::RefCell};
-use crate::{ast::{Expr, ExprData, AstName}, chunk::{Function, Instruction, InnerFunction}, chunk::{Chunk, self}, value::Value, Result, lex::Position};
+use crate::prelude::*;
use Instruction as I;
use Value as V;
use ExprData as E;
pub type NamesTable = Rc<RefCell<Vec<Rc<str>>>>;
+pub type GlobalsTable = Rc<RefCell<Vec<Global>>>;
pub struct CompilerBuilder<'c> {
- globals: NamesTable,
+ globals: GlobalsTable,
names: NamesTable,
repl: bool,
debug: bool,
@@ -39,7 +39,7 @@ impl<'c> CompilerBuilder<'c> {
self
}
- pub fn globals(mut self, globals: NamesTable) -> Self {
+ pub fn globals(mut self, globals: GlobalsTable) -> Self {
self.globals = globals;
self
}
@@ -81,9 +81,9 @@ pub struct Compiler<'c> {
name: Rc<str>,
parent: Option<&'c Compiler<'c>>,
- locals: Vec<Rc<Local>>,
- globals: Rc<RefCell<Vec<Rc<str>>>>,
- names: Rc<RefCell<Vec<Rc<str>>>>,
+ locals: Vec<Local>,
+ globals: GlobalsTable,
+ names: NamesTable,
root_is_block: bool,
@@ -97,50 +97,25 @@ pub struct Compiler<'c> {
debug: bool,
}
+#[derive(Clone)]
struct Local {
name: Rc<str>,
idx: usize,
- scope: usize
-}
-
-#[derive(Debug, Clone)]
-pub enum InnerError {
- Undefined(Rc<str>),
- Redefined(Rc<str>),
- InvAssign(Expr),
- InvContinue,
- InvBreak,
- NotImplemented(&'static str),
-}
-
-#[derive(Debug, Clone)]
-pub struct Error {
- pos: Position,
- err: InnerError,
+ scope: usize,
+ is_const: bool,
}
-impl std::error::Error for self::Error {}
-
-impl Display for self::Error {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- use InnerError as E;
- write!(f, "parse failed at {}:{}, ", self.pos.row, self.pos.col)?;
- match &self.err {
- E::Undefined(name) => write!(f, "value {name} is undefined"),
- E::Redefined(name) => write!(f, "cannot redefine {name} in the same scope"),
- E::InvAssign(expr) => write!(f, "cannot assign to {expr:?}"),
- E::InvContinue => write!(f, "cannot continue outside a loop"),
- E::InvBreak => write!(f, "cannot break outside a loop"),
- E::NotImplemented(str) => write!(f, "{str} is not implemented yet")
- }
- }
+#[derive(Clone)]
+pub struct Global {
+ pub name: Rc<str>,
+ pub idx: usize,
+ pub is_const: bool,
}
-fn error<T>(err: InnerError, pos: Position) -> Result<T> {
- Err(self::Error {
- pos,
- err
- }.into())
+macro_rules! error {
+ ($($arg:tt)*) => {
+ exception!(COMPILE_EXCEPTION, $($arg)*)
+ };
}
impl<'c> Compiler<'c> {
@@ -185,35 +160,35 @@ impl<'c> Compiler<'c> {
};
}
- fn create_local(&mut self, name: Rc<str>) {
- let local = Local { name, idx: self.locals.len(), scope: self.scopes.len()};
- self.locals.push(Rc::new(local));
+ fn create_local(&mut self, name: Rc<str>, is_const: bool) -> Local {
+ let local = Local { name, idx: self.locals.len(), scope: self.scopes.len(), is_const };
+ self.locals.push(local.clone());
+ local
}
- fn create_global(&mut self, name: Rc<str>) -> usize {
- self.globals.borrow_mut().push(name);
- let c = self.globals.borrow().len() - 1;
- c
+ fn create_global(&mut self, name: Rc<str>, is_const: bool) -> Global {
+ let global = Global { name, idx: self.globals.borrow().len(), is_const };
+ self.globals.borrow_mut().push(global.clone());
+ global
}
- fn create_local_checked(&mut self, name: Rc<str>, pos: Position) -> Result<()> {
+ fn create_local_checked(&mut self, name: Rc<str>, is_const: bool, pos: Position) -> Result<Local> {
if let Some(local) = self.find_local(&name) {
if local.scope == self.scopes.len() {
- return error(InnerError::Redefined(name), pos)
+ return Err(error!("redefined {name}").pos(pos))
}
};
- self.create_local(name);
- Ok(())
+ Ok(self.create_local(name, is_const))
}
- fn create_global_checked(&mut self, name: Rc<str>, pos: Position) -> Result<usize> {
+ fn create_global_checked(&mut self, name: Rc<str>, is_const: bool, pos: Position) -> Result<Global> {
if let Some(_) = self.find_global(&name) {
- return error(InnerError::Redefined(name).into(), pos)
+ return Err(error!("redefined {name}").pos(pos))
}
- Ok(self.create_global(name))
+ Ok(self.create_global(name, is_const))
}
- fn find_local(&self, name: &str) -> Option<Rc<Local>> {
+ fn find_local(&self, name: &str) -> Option<Local> {
for local in self.locals.iter().rev() {
if local.name.as_ref() == name {
return Some(local.clone())
@@ -222,13 +197,13 @@ impl<'c> Compiler<'c> {
None
}
- fn find_global(&self, name: &str) -> Option<usize> {
+ fn find_global(&self, name: &str) -> Option<Global> {
if let Some(parent) = self.parent {
return parent.find_global(name)
}
- for (i, global) in self.globals.borrow().iter().enumerate() {
- if global.as_ref() == name {
- return Some(i)
+ for global in self.globals.borrow().iter() {
+ if global.name.as_ref() == name {
+ return Some(global.clone())
}
}
None
@@ -294,7 +269,7 @@ impl<'c> Compiler<'c> {
let chunk = self.compile_function(name.clone(), params, body)?;
let arity = params.len() - if *varadic { 1 } else { 0 };
let fun = Value::Function(Rc::new(
- chunk::Function {
+ Function {
name: name.0.clone(),
arity,
fun: InnerFunction::Compiled(chunk.into()),
@@ -304,10 +279,10 @@ impl<'c> Compiler<'c> {
self.emit_const(fun, expr.pos);
self.emit(I::Dup, expr.pos);
if self.can_make_globals() {
- let idx = self.create_global_checked(name.0.clone(), name.1)?;
- self.emit(I::StoreGlobal(idx as u16), expr.pos);
+ let global = self.create_global_checked(name.0.clone(), false, name.1)?;
+ self.emit(I::StoreGlobal(global.idx as u16), expr.pos);
} else {
- self.create_local_checked(name.0.clone(), name.1)?;
+ self.create_local_checked(name.0.clone(), false, name.1)?;
self.emit(I::CreateLocal, expr.pos);
}
},
@@ -316,7 +291,7 @@ impl<'c> Compiler<'c> {
let chunk = self.compile_function(name.clone(), params, body)?;
let arity = params.len() - if *varadic { 1 } else { 0 };
let fun = Value::Function(Rc::new(
- chunk::Function {
+ Function {
name: name.0.clone(),
arity,
fun: InnerFunction::Compiled(chunk.into()),
@@ -341,7 +316,7 @@ impl<'c> Compiler<'c> {
let jmpidx2 = self.emit_temp(expr.pos);
self.re_emit(I::Try(self.cur()), jmpidx);
self.begin_scope();
- self.create_local(err.0.clone());
+ self.create_local(err.0.clone(), true);
self.emit(I::CreateLocal, err.1);
self.compile_expr(catch)?;
self.end_scope();
@@ -378,7 +353,7 @@ impl<'c> Compiler<'c> {
let jumpidx = self.emit_temp(expr.pos);
self.loop_top.push((top as usize, self.scopes.len()));
self.begin_scope();
- self.create_local(name.0.clone());
+ self.create_local(name.0.clone(), true);
self.emit(I::CreateLocal, name.1);
self.compile_expr(expr)?;
self.emit(I::Discard(1), expr.pos);
@@ -407,10 +382,21 @@ impl<'c> Compiler<'c> {
self.compile_expr(expr)?;
self.emit(I::Dup, expr.pos);
if self.can_make_globals() {
- let global = self.create_global_checked(name.0.clone(), name.1)?;
- self.emit(I::StoreGlobal(global as u16), expr.pos);
+ let global = self.create_global_checked(name.0.clone(), false, name.1)?;
+ self.emit(I::StoreGlobal(global.idx as u16), expr.pos);
+ } else {
+ self.create_local_checked(name.0.clone(), false, name.1)?;
+ self.emit(I::CreateLocal, expr.pos);
+ }
+ },
+ E::Const(name, expr) => {
+ self.compile_expr(expr)?;
+ self.emit(I::Dup, expr.pos);
+ if self.can_make_globals() {
+ let global = self.create_global_checked(name.0.clone(), true, name.1)?;
+ self.emit(I::StoreGlobal(global.idx as u16), expr.pos);
} else {
- self.create_local_checked(name.0.clone(), name.1)?;
+ self.create_local_checked(name.0.clone(), true, name.1)?;
self.emit(I::CreateLocal, expr.pos);
}
},
@@ -420,7 +406,7 @@ impl<'c> Compiler<'c> {
self.collapse_scopes(scope);
self.emit(I::Jump(top as u16), expr.pos);
} else {
- return error(InnerError::InvContinue, expr.pos)
+ return Err(error!("invalid continue outside loop").pos(expr.pos))
}
},
E::Break => {
@@ -430,7 +416,7 @@ impl<'c> Compiler<'c> {
let tmpidx = self.emit_temp(expr.pos);
self.loop_bot.push(tmpidx);
} else {
- return error(InnerError::InvBreak, expr.pos)
+ return Err(error!("invalid break outside loop").pos(expr.pos))
}
},
E::Return(expr) => {
@@ -444,9 +430,9 @@ impl<'c> Compiler<'c> {
} else if let Some(local) = self.find_local(name) {
self.emit(I::LoadLocal(local.idx as u16), expr.pos);
} else if let Some(global) = self.find_global(name) {
- self.emit(I::LoadGlobal(global as u16), expr.pos);
+ self.emit(I::LoadGlobal(global.idx as u16), expr.pos);
} else {
- return error(InnerError::Undefined(name.clone()), expr.pos)
+ return Err(error!("variable '{name}' is undefined").pos(expr.pos))
};
},
E::Assign(lhs, rhs) => {
@@ -455,14 +441,20 @@ impl<'c> Compiler<'c> {
match &lhs.data {
E::Ident(name) if name.as_ref() != "_" => {
if let Some(local) = self.find_local(&name) {
+ if local.is_const {
+ return Err(error!("cannot assign to const '{name}'").pos(lhs.pos))
+ }
self.emit(I::StoreLocal(local.idx as u16), lhs.pos);
} else if let Some(global) = self.find_global(&name) {
- self.emit(I::StoreGlobal(global as u16), lhs.pos);
+ if global.is_const {
+ return Err(error!("cannot assign to const '{name}'").pos(lhs.pos))
+ }
+ self.emit(I::StoreGlobal(global.idx as u16), lhs.pos);
} else if self.can_make_globals() {
- let global = self.create_global_checked(name.clone(), lhs.pos)?;
- self.emit(I::StoreGlobal(global as u16), lhs.pos);
+ let global = self.create_global_checked(name.clone(), false, lhs.pos)?;
+ self.emit(I::StoreGlobal(global.idx as u16), lhs.pos);
} else {
- self.create_local_checked(name.clone(), lhs.pos)?;
+ self.create_local_checked(name.clone(), false, lhs.pos)?;
self.emit(I::CreateLocal, lhs.pos);
}
},
@@ -478,7 +470,7 @@ impl<'c> Compiler<'c> {
let name = self.get_name(ident.0.clone());
self.emit(I::StoreField(name as u16), expr.pos);
}
- _ => return error(InnerError::InvAssign(*lhs.clone()), lhs.pos)
+ _ => return Err(error!("assignment to {lhs} is not allowed").pos(lhs.pos))
}
}
E::UnaryOp(expr, op) => {
@@ -554,7 +546,7 @@ impl<'c> Compiler<'c> {
fn compile_function(&mut self, name: AstName, params: &Vec<AstName>, body: &Box<Expr>) -> Result<Chunk> {
let mut compiler = self.child(name.0);
for (name, pos) in params {
- compiler.create_local(name.clone());
+ compiler.create_local(name.clone(), false);
compiler.emit(I::CreateLocal, *pos);
}
compiler.compile_expr(body)?;
@@ -611,7 +603,7 @@ impl<'c> Compiler<'c> {
};
if self.loop_bot.len() > 0 {
- return error(InnerError::InvBreak, pos)
+ return Err(error!("invalid break outside loop").pos(pos))
}
if self.debug {
diff --git a/matrix/src/lex.rs b/matrix-lang/src/lex.rs
index 8a07234..b2487ad 100644
--- a/matrix/src/lex.rs
+++ b/matrix-lang/src/lex.rs
@@ -1,6 +1,5 @@
-use std::{rc::Rc, fmt::Debug};
-use regex::Regex;
-use crate::Result;
+use std::fmt::{Debug, Display};
+use crate::prelude::*;
pub struct RegexToken {
regex: Regex
@@ -30,6 +29,36 @@ impl From<RegexToken> for Regex {
}
}
+#[derive(Debug, Clone, PartialEq, Eq, Copy)]
+pub struct Position {
+ pub row: usize,
+ pub col: usize,
+}
+
+impl Default for Position {
+ fn default() -> Self {
+ Self { row: 1, col: 1 }
+ }
+}
+
+impl Display for Position {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}:{}", self.row, self.col)
+ }
+}
+
+impl Display for TokenData {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{self:?}")
+ }
+}
+
+impl Display for Token {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}", self.data)
+ }
+}
+
#[derive(Debug, PartialEq)]
pub enum TokenData {
//syntax
@@ -106,6 +135,7 @@ pub enum TokenData {
Else,
While,
Let,
+ Const,
Function,
True,
False,
@@ -133,48 +163,6 @@ pub struct Token {
pub blen: usize,
}
-#[derive(Debug)]
-pub enum Error {
- UnexpectedCharacter(char),
- ExpectedChar(char, char),
- InvalidCodepoint,
- UnexpectedEof,
- InvalidDigit(char),
- InvalidStringEscape(char),
- InvalidRegex(anyhow::Error),
- InvalidNumber(String),
-}
-
-impl std::fmt::Display for Error {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- use Error::*;
- match self {
- UnexpectedCharacter(char) => write!(f, "Unexpected char: '{char}'"),
- UnexpectedEof => write!(f, "Unexpected end of file"),
- ExpectedChar(expected, got) => write!(f, "Expected char: '{expected}', instead got: '{got}'"),
- InvalidCodepoint => write!(f, "Invalid codepoint"),
- InvalidDigit(char) => write!(f, "Invalid digit: '{char}'"),
- InvalidStringEscape(char) => write!(f, "Invalid string escape: '\\{char}"),
- InvalidRegex(err) => write!(f, "{err}"),
- InvalidNumber(num) => write!(f, "Invalid number: '{num}'")
- }
- }
-}
-
-impl std::error::Error for Error {}
-
-#[derive(Debug, Clone, PartialEq, Eq, Copy)]
-pub struct Position {
- pub row: usize,
- pub col: usize,
-}
-
-impl Default for Position {
- fn default() -> Self {
- Self { row: 1, col: 1 }
- }
-}
-
pub struct Lexer {
pub index: usize,
len: usize,
@@ -203,6 +191,12 @@ impl<T: Into<String>> From<T> for Lexer {
}
}
+macro_rules! error {
+ ($($arg:tt)*) => {
+ exception!(PARSE_EXCEPTION, $($arg)*)
+ };
+}
+
impl Lexer {
pub fn new<T: Into<String>>(input: T) -> Self {
let data: Vec<char> = input.into().chars().collect();
@@ -239,7 +233,7 @@ impl Lexer {
fn next_not_eof(&mut self) -> Result<char> {
let c = self.next();
if c == '\0' {
- return Err(Error::UnexpectedEof.into())
+ return Err(error!("unexpected end of file"))
}
Ok(c)
}
@@ -247,7 +241,7 @@ impl Lexer {
fn next_expect(&mut self, expected: char) -> Result<char> {
let c = self.next();
if c != expected {
- return Err(Error::ExpectedChar(expected, c).into())
+ return Err(error!("expected character '{c}'"))
}
Ok(c)
}
@@ -259,7 +253,6 @@ impl Lexer {
}
fn lex_string(&mut self, delimit: char) -> Result<Rc<str>> {
- use Error::*;
let mut buf = String::new();
@@ -291,8 +284,8 @@ impl Lexer {
let n1 = self.next_not_eof()?;
let n2 = self.next_not_eof()?;
buf.push(char::from_u32(
- n1.to_digit(16).ok_or(InvalidDigit(n1))? * 16 +
- n2.to_digit(16).ok_or(InvalidDigit(n2))?
+ n1.to_digit(16).ok_or(error!("invalid digit '{n1}'"))? * 16 +
+ n2.to_digit(16).ok_or(error!("invalid digit '{n2}'"))?
).unwrap());
},
'u' => {
@@ -302,15 +295,15 @@ impl Lexer {
let c = self.next_not_eof()?;
if c == '}' { break }
if n >= 0x1000_0000_u32 {
- return Err(InvalidCodepoint.into())
+ return Err(error!("invalid utf8 codepoint '{n}'"))
}
- n = n * 16 + c.to_digit(16).ok_or::<crate::Error>(InvalidDigit(c).into())?;
+ n = n * 16 + c.to_digit(16).ok_or(error!("invalid digit '{c}'"))?;
}
- let ch = char::from_u32(n).ok_or::<crate::Error>(InvalidCodepoint.into())?;
+ let ch = char::from_u32(n).ok_or(error!("invalid codepoint '{n}'"))?;
buf.push(ch);
},
- _ => return Err(InvalidStringEscape(next).into())
+ _ => return Err(error!("invalid string escape '\\{next}'"))
}
}
@@ -318,13 +311,12 @@ impl Lexer {
}
fn lex_ident(&mut self, initial: char) -> Result<TokenData> {
- use Error as E;
use TokenData as T;
let mut buf = std::string::String::new();
if !initial.is_initial_ident() {
- return Err(E::UnexpectedCharacter(initial).into())
+ return Err(error!("unexpected character '{initial}'"))
}
buf.push(initial);
@@ -342,6 +334,7 @@ impl Lexer {
"else" => T::Else,
"while" => T::While,
"let" => T::Let,
+ "const" => T::Const,
"fn" | "function" => T::Function,
"true" => T::True,
"false" => T::False,
@@ -364,7 +357,6 @@ impl Lexer {
fn lex_radix(&mut self, radix: i64, radix_char: char) -> Result<TokenData> {
use TokenData as T;
- use Error as E;
let mut n = 0i64;
let mut char_found = false;
@@ -375,7 +367,7 @@ impl Lexer {
n = n * radix + (i as i64);
char_found = true;
} else if self.peek().is_ident() {
- return Err(E::InvalidDigit(self.peek()).into())
+ return Err(error!("invalid digit '{}'", self.peek()))
} else {
break;
}
@@ -384,13 +376,11 @@ impl Lexer {
if char_found {
Ok(T::Int(n))
} else {
- Err(E::InvalidNumber(format!("0{radix_char}")).into())
+ Err(error!("invalid number radix specifier '0{radix_char}'"))
}
}
fn lex_number(&mut self, initial: char) -> Result<TokenData> {
- use Error as E;
-
if initial == '0' {
match self.peek() {
'x' => {
@@ -459,7 +449,7 @@ impl Lexer {
}
if self.peek().is_ident() {
- return Err(E::UnexpectedCharacter(self.peek()).into())
+ return Err(error!("unexpected character '{}'", self.peek()))
}
if let Ok(int) = buf.parse::<i64>() {
@@ -478,23 +468,11 @@ impl Lexer {
return Ok(T::Float(float))
}
- Err(E::InvalidNumber(buf).into())
+ Err(error!("invalid number '{buf}'"))
}
- fn peek_token_impl(&mut self, ignore_newlines: bool) -> Result<Token> {
- let idx = self.index;
- let pos = self.pos;
- let bidx = self.byte_len;
- let token = self.next_token_impl(ignore_newlines);
- self.index = idx;
- self.pos = pos;
- self.byte_len = bidx;
- token
- }
-
- fn next_token_impl(&mut self, ignore_newlines: bool) -> Result<Token> {
+ fn read_token(&mut self, ignore_newlines: bool) -> Result<Token> {
use TokenData as T;
- use Error as E;
self.skip_whitespace(ignore_newlines);
@@ -705,7 +683,7 @@ impl Lexer {
self.next();
T::Regex(regex::Regex::new(&self.lex_string(next)?)
.map(|e| e.into())
- .map_err(|e| E::InvalidRegex(e.into()))?)
+ .map_err(|e| error!("invalid regex: '{e}'"))?)
}
_ => {
self.lex_ident(char)?
@@ -744,7 +722,6 @@ impl Lexer {
let str_end = self.index;
let byte_end = self.byte_len;
let str = self.data[str_start..str_end].to_owned().into_iter().collect();
-
Ok(Token {
data,
pos,
@@ -754,19 +731,47 @@ impl Lexer {
})
}
- pub fn peek_token(&mut self) -> Result<Token> {
- self.peek_token_impl(true)
+ pub fn next_token(&mut self) -> Result<Token> {
+ let pos = self.pos;
+ match self.read_token(true) {
+ Ok(token) => Ok(token),
+ Err(e) => Err(e.pos(pos)),
+ }
}
- pub fn next_token(&mut self) -> Result<Token> {
- self.next_token_impl(true)
+ pub fn next_token_nl(&mut self) -> Result<Token> {
+ let pos = self.pos;
+ match self.read_token(false) {
+ Ok(token) => Ok(token),
+ Err(e) => Err(e.pos(pos)),
+ }
}
- pub fn peek_token_nl(&mut self) -> Result<Token> {
- self.peek_token_impl(false)
+ pub fn peek_token(&mut self) -> Result<Token> {
+ let idx = self.index;
+ let pos = self.pos;
+ let bidx = self.byte_len;
+ let token = self.read_token(true);
+ self.index = idx;
+ self.pos = pos;
+ self.byte_len = bidx;
+ match token {
+ Ok(token) => Ok(token),
+ Err(e) => Err(e.pos(pos)),
+ }
}
- pub fn next_token_nl(&mut self) -> Result<Token> {
- self.next_token_impl(false)
+ pub fn peek_token_nl(&mut self) -> Result<Token> {
+ let idx = self.index;
+ let pos = self.pos;
+ let bidx = self.byte_len;
+ let token = self.read_token(false);
+ self.index = idx;
+ self.pos = pos;
+ self.byte_len = bidx;
+ match token {
+ Ok(token) => Ok(token),
+ Err(e) => Err(e.pos(pos)),
+ }
}
}
diff --git a/matrix-lang/src/lib.rs b/matrix-lang/src/lib.rs
new file mode 100644
index 0000000..1452a09
--- /dev/null
+++ b/matrix-lang/src/lib.rs
@@ -0,0 +1,42 @@
+mod compiler;
+mod value;
+mod lex;
+mod vm;
+mod parse;
+mod chunk;
+mod ast;
+mod binary;
+
+pub mod prelude;
+
+#[macro_export]
+macro_rules! iter {
+ ($fn:expr) => {
+ $crate::prelude::Value::Iter(
+ ::std::rc::Rc::new(
+ $crate::prelude::Function {
+ name: ::std::rc::Rc::from("<iterator>"),
+ arity: 0,
+ variadic: false,
+ fun: $crate::prelude::InnerFunction::Native(
+ ::std::rc::Rc::new($fn
+ ))}))
+ };
+}
+
+#[macro_export]
+macro_rules! native {
+ ($name:expr, $arity:expr, $varadic:expr, $fn:expr) => {
+ $crate::prelude::Value::Function(
+ ::std::rc::Rc::new( $crate::prelude::Function {
+ name: std::rc::Rc::from($name),
+ arity: $arity,
+ variadic: $varadic,
+ fun: $crate::prelude::InnerFunction::Native(
+ ::std::rc::Rc::new($fn)
+ )
+ })
+ )
+ }
+}
+
diff --git a/matrix/src/parse.rs b/matrix-lang/src/parse.rs
index d967130..3a4c5f2 100644
--- a/matrix/src/parse.rs
+++ b/matrix-lang/src/parse.rs
@@ -1,6 +1,4 @@
-use std::{fmt::Display, rc::Rc};
-use num_complex::Complex64;
-use crate::{lex::{Lexer, self, Token, TokenData, Position}, ast::{Expr, BinaryOp, UnaryOp, optimize, ExprData, AstName}, value::{Value, self}, Result};
+use crate::prelude::*;
use Value as V;
use ExprData as E;
@@ -33,38 +31,6 @@ pub struct Parser {
optimize: bool
}
-#[derive(Debug)]
-pub enum Error {
- LexerError(crate::lex::Error),
- UnexpectedToken(Token),
- ExpectedToken(TokenData, Position),
- MatrixInvDomain(usize, usize, usize),
- NotAssignable(Expr),
- ValueError(value::Error),
-}
-
-impl Display for Error {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- use Error::*;
- match self {
- LexerError(err) => write!(f, "{err}"),
- UnexpectedToken(tok) => write!(f, "Unexpected token: '{:?}' at {}:{}", tok.data, tok.pos.row, tok.pos.col),
- ExpectedToken(tok, pos) => write!(f, "Expected token: '{tok:?}' at {}:{}", pos.row, pos.col),
- MatrixInvDomain(row, should, was) => write!(f, "In row {row} of matrix, domain was expected to be {should} but was given {was}"),
- NotAssignable(expr) => write!(f, "{expr:?} is not assignable"),
- ValueError(err) => write!(f, "{err}"),
- }
- }
-}
-
-impl From<lex::Error> for Error {
- fn from(value: lex::Error) -> Self {
- Self::LexerError(value)
- }
-}
-
-impl std::error::Error for Error {}
-
macro_rules! expr_parser {
($parser:ident, $pattern:pat, $fn:ident) => {{
let mut expr = $parser.$fn()?;
@@ -99,12 +65,18 @@ macro_rules! expr_parser_reverse {
}};
}
+macro_rules! error {
+ ($($arg:tt)*) => {
+ exception!(PARSE_EXCEPTION, $($arg)*)
+ };
+}
+
impl Parser {
fn force_token(&mut self, tok: TokenData) -> Result<TokenData> {
let next = self.lexer.next_token()?;
if next.data != tok {
- Err(Error::ExpectedToken(tok, next.pos).into())
+ Err(error!("expected token '{tok}'").pos(next.pos))
} else {
Ok(tok)
}
@@ -113,7 +85,7 @@ impl Parser {
fn force_token_nl(&mut self, tok: TokenData) -> Result<TokenData> {
let next = self.lexer.next_token_nl()?;
if next.data != tok {
- Err(Error::ExpectedToken(tok, next.pos).into())
+ Err(error!("expected token '{tok}'").pos(next.pos))
} else {
Ok(tok)
}
@@ -135,7 +107,7 @@ impl Parser {
match next.data {
T::Comma => continue,
T::RightParen => break,
- _ => return Err(Error::UnexpectedToken(next).into())
+ _ => return Err(error!("unexpected token '{next}'").pos(next.pos))
};
}
Ok(params)
@@ -157,7 +129,7 @@ impl Parser {
match next.data {
T::SemiColon => continue,
T::RightBrack => break,
- _ => return Err(Error::UnexpectedToken(next).into())
+ _ => return Err(error!("unexpected token '{next}'").pos(next.pos))
};
}
Ok(indicies)
@@ -193,7 +165,7 @@ impl Parser {
match next.data {
T::SemiColon => continue,
T::RightBrack => break,
- _ => return Err(Error::UnexpectedToken(next).into()),
+ _ => return Err(error!("unexpected token '{next}'").pos(next.pos))
};
}
if parts.len() == 1 {
@@ -201,9 +173,9 @@ impl Parser {
} else {
let codomain = parts.len();
let domain = parts[0].len();
- for (i, part) in parts.iter().enumerate() {
+ for part in parts.iter() {
if part.len() != domain {
- return Err(Error::MatrixInvDomain(i, domain, part.len()).into())
+ return Err(error!("matrix row domains do not match: {} != {}", domain, part.len()).pos(pos))
}
}
let mut data = Vec::new();
@@ -225,7 +197,7 @@ impl Parser {
},
T::Ident(ident) => (E::Literal(V::String(ident.to_string().into())), tok.pos).into(),
T::String(string) => (E::Literal(V::String(string.to_string().into())), tok.pos).into(),
- _ => return Err(Error::UnexpectedToken(tok).into())
+ t => return Err(error!("unexpected token '{t}'").pos(tok.pos))
})
}
@@ -247,7 +219,7 @@ impl Parser {
match next.data {
T::Comma => continue,
T::RightBrace => break,
- _ => return Err(Error::UnexpectedToken(next).into())
+ _ => return Err(error!("unexpected token '{next}'").pos(next.pos))
}
}
Ok((E::Table(table), pos).into())
@@ -273,7 +245,7 @@ impl Parser {
}
}
T::LeftParen => (),
- _ => return Err(Error::UnexpectedToken(tok).into()),
+ t => return Err(error!("unexpected token '{t}'").pos(tok.pos))
}
let mut params = Vec::new();
@@ -295,7 +267,7 @@ impl Parser {
}
T::Comma => continue,
T::RightParen => break,
- _ => return Err(Error::UnexpectedToken(next).into()),
+ _ => return Err(error!("unexpected token '{next}'").pos(next.pos))
}
}
@@ -307,7 +279,7 @@ impl Parser {
if let T::Ident(ident) = next.data {
Ok((ident, next.pos))
} else {
- Err(Error::UnexpectedToken(next).into())
+ Err(error!("unexpected token '{next}'").pos(next.pos))
}
}
@@ -327,7 +299,7 @@ impl Parser {
if let T::Ident(ident) = next.data {
Ok((ident, next.pos))
} else {
- Err(Error::UnexpectedToken(next).into())
+ Err(error!("unexpected token '{next}'").pos(next.pos))
}
}
@@ -419,6 +391,15 @@ impl Parser {
}
}
+ fn parse_const(&mut self) -> Result<Expr> {
+ let pos = self.lexer.peek_token()?.pos;
+ self.force_token(T::Const)?;
+ let ident = self.parse_ident_nl()?;
+ self.force_token(T::Assign)?;
+ let expr = self.parse_expr()?;
+ Ok((E::Const(ident, Box::new(expr)), pos).into())
+ }
+
fn parse_return(&mut self) -> Result<Expr> {
let pos = self.lexer.peek_token()?.pos;
self.force_token(T::Return)?;
@@ -431,7 +412,10 @@ impl Parser {
self.force_token(T::LeftBrace)?;
loop {
let expr = match self.lexer.peek_token()?.data {
- T::RightBrace => break,
+ T::RightBrace => {
+ self.lexer.next_token()?;
+ break
+ },
T::SemiColon => {
self.lexer.next_token()?;
continue;
@@ -443,12 +427,9 @@ impl Parser {
match next.data {
T::SemiColon => continue,
T::RightBrace => break,
- _ => return Err(Error::ExpectedToken(T::SemiColon, next.pos).into())
+ _ => return Err(error!("expected a semicolon").pos(next.pos))
}
}
- if self.lexer.peek_token()?.data == T::RightBrace {
- self.lexer.next_token()?;
- }
Ok((E::Block(block), pos).into())
}
@@ -474,7 +455,7 @@ impl Parser {
T::True => E::Literal(V::Bool(true)),
T::False => E::Literal(V::Bool(false)),
T::Ident(ident) => E::Ident(ident),
- _ => return Err(Error::UnexpectedToken(tok).into()),
+ t => return Err(error!("unexpected token '{t}'").pos(tok.pos))
};
Ok((data, tok.pos).into())
}
@@ -488,6 +469,7 @@ impl Parser {
While => self.parse_while(),
For => self.parse_for(),
Let => self.parse_let(),
+ Const => self.parse_const(),
LeftBrace => self.parse_block(),
Return => self.parse_return(),
If => self.parse_if(),
@@ -752,11 +734,11 @@ impl Parser {
};
let expr = self.parse_expr()?;
block.push(expr);
- let next = self.lexer.next_token()?;
+ let next = self.lexer.next_token_nl()?;
match next.data {
T::Eof => break,
T::SemiColon => continue,
- _ => return Err(Error::ExpectedToken(T::SemiColon, next.pos).into())
+ _ => return Err(error!("expected a semicolon").pos(next.pos))
};
}
Ok((E::Block(block), Position::default()).into())
diff --git a/matrix-lang/src/prelude.rs b/matrix-lang/src/prelude.rs
new file mode 100644
index 0000000..c5af0c8
--- /dev/null
+++ b/matrix-lang/src/prelude.rs
@@ -0,0 +1,60 @@
+pub type Result<T> = std::result::Result<T, Exception>;
+
+pub use crate::value::Value as Value;
+pub use crate::value::exception::Exception as Exception;
+pub use crate::value::matrix::Matrix as Matrix;
+pub use crate::value::gc::Gc as Gc;
+pub use crate::value::hash::ValueMap as ValueMap;
+pub use crate::value::function::Function as Function;
+pub use crate::value::function::InnerFunction as InnerFunction;
+
+pub use num_complex::Complex64 as Complex64;
+pub use num_rational::Rational64 as Rational64;
+pub use regex::Regex as Regex;
+pub use std::fs::File as File;
+
+pub use std::rc::Rc as Rc;
+pub use std::cell::RefCell as RefCell;
+
+pub use crate::exception as exception;
+pub use crate::native as native;
+pub use crate::iter as iter;
+
+pub use crate::value::exception::HASH_EXCEPTION as HASH_EXCEPTION;
+pub use crate::value::exception::VALUE_EXCEPTION as VALUE_EXCEPTION;
+pub use crate::value::exception::PARSE_EXCEPTION as PARSE_EXCEPTION;
+pub use crate::value::exception::COMPILE_EXCEPTION as COMPILE_EXCEPTION;
+pub use crate::value::exception::RUNTIME_EXCEPTION as RUNTIME_EXCEPTION;
+pub use crate::value::exception::BINARY_EXECPTION as BINARY_EXCEPTION;
+pub use crate::value::exception::IO_EXECPTION as IO_EXCEPTION;
+
+pub use crate::value::clone::ValueClone as ValueClone;
+pub use crate::value::hash::TryHash as TryHash;
+
+pub use crate::vm::Vm as Vm;
+pub use crate::vm::StackFrame as StackFrame;
+pub use crate::vm::Interupt as Interupt;
+
+pub use crate::lex::Lexer as Lexer;
+pub use crate::lex::Position as Position;
+pub use crate::lex::Token as Token;
+pub use crate::lex::TokenData as TokenData;
+pub use crate::parse::Parser as Parser;
+pub use crate::parse::ParserBuilder as ParserBuilder;
+pub use crate::compiler::Compiler as Compiler;
+pub use crate::compiler::CompilerBuilder as CompilerBuilder;
+pub use crate::compiler::NamesTable as NamesTable;
+pub use crate::compiler::GlobalsTable as GlobalsTable;
+pub use crate::compiler::Global as Global;
+
+pub use crate::ast::AstName as AstName;
+pub use crate::ast::Expr as Expr;
+pub use crate::ast::ExprData as ExprData;
+pub use crate::ast::BinaryOp as BinaryOp;
+pub use crate::ast::UnaryOp as UnaryOp;
+pub use crate::ast::optimize as optimize;
+
+pub use crate::chunk::Chunk as Chunk;
+pub use crate::chunk::Instruction as Instruction;
+
+pub use crate::binary::Program as Program;
diff --git a/matrix-lang/src/value/clone.rs b/matrix-lang/src/value/clone.rs
new file mode 100644
index 0000000..d5ac983
--- /dev/null
+++ b/matrix-lang/src/value/clone.rs
@@ -0,0 +1,54 @@
+use crate::prelude::*;
+
+pub trait ValueClone {
+ fn deep_clone(&self) -> Self;
+ fn shallow_clone(&self) -> Self;
+}
+
+impl ValueClone for Value {
+ fn deep_clone(&self) -> Self {
+ use Value as V;
+ match self {
+ V::List(l) => V::List(l.deep_clone()),
+ V::Table(t) => V::Table(t.deep_clone()),
+ V::Matrix(m) => V::Matrix(m.deep_clone()),
+ _ => self.clone()
+ }
+ }
+
+ fn shallow_clone(&self) -> Self {
+ use Value as V;
+ match self {
+ V::List(l) => V::List(l.shallow_clone()),
+ V::Table(t) => V::Table(t.shallow_clone()),
+ V::Matrix(m) => V::Matrix(m.shallow_clone()),
+ _ => self.clone()
+ }
+ }
+}
+
+impl ValueClone for Vec<Value> {
+ fn deep_clone(&self) -> Self {
+ let mut vals = Vec::new();
+ for val in self {
+ vals.push(val.deep_clone())
+ }
+ vals
+ }
+
+ fn shallow_clone(&self) -> Self {
+ self.clone()
+ }
+}
+
+impl ValueClone for Matrix {
+ fn deep_clone(&self) -> Self {
+ let values = self.values.deep_clone();
+ Self::new(self.domain, self.codomain, values)
+ }
+
+ fn shallow_clone(&self) -> Self {
+ let values = self.values.shallow_clone();
+ Self::new(self.domain, self.codomain, values)
+ }
+}
diff --git a/matrix-lang/src/value/comp.rs b/matrix-lang/src/value/comp.rs
new file mode 100644
index 0000000..3557927
--- /dev/null
+++ b/matrix-lang/src/value/comp.rs
@@ -0,0 +1,373 @@
+use std::{ops::{Add, Sub, Mul, Div, Shl, Shr, BitOr, BitAnd, BitXor, Neg, Not}, cmp::Ordering};
+use crate::prelude::*;
+
+fn ratio_to_f64(r: Rational64) -> f64 {
+ *r.numer() as f64 / *r.denom() as f64
+}
+
+macro_rules! error {
+ ($($arg:tt)*) => {
+ exception!(VALUE_EXCEPTION, $($arg)*)
+ };
+}
+
+///
+/// MATH OPERATIONS
+///
+
+fn ipow(n: i64, d: i64, p: i64) -> Result<(i64, i64)> {
+ Ok(match (n, d, p) {
+ (0, _, 0) => Err(error!("cannot exponent 0 ** 0"))?,
+ (0, _, _) => (0, 1),
+ (_, _, 0) => (1, 1),
+ (1, 1, _) => (1, 1),
+ (n, d, p) if p < 0 => (d.pow((-p) as u32), n.pow((-p) as u32)),
+ (n, d, p) => (n.pow(p as u32), d.pow(p as u32)),
+ })
+}
+
+fn promote(a: Value, b: Value) -> (Value, Value) {
+ use Value as V;
+ match (&a, &b) {
+ (V::Int(x), V::Ratio(..)) => (V::Ratio((*x).into()), b),
+ (V::Int(x), V::Float(..)) => (V::Float(*x as f64), b),
+ (V::Int(x), V::Complex(..)) => (V::Complex((*x as f64).into()), b),
+ (V::Ratio(x), V::Float(..)) => (V::Float(ratio_to_f64(*x)), b),
+ (V::Ratio(x), V::Complex(..)) => (V::Complex(ratio_to_f64(*x).into()), b),
+ (V::Float(x), V::Complex(..)) => (V::Complex((*x).into()), b),
+ (V::Ratio(..), V::Int(y)) => (a, V::Ratio((*y).into())),
+ (V::Float(..), V::Int(y)) => (a, V::Float(*y as f64)),
+ (V::Complex(..), V::Int(y)) => (a, V::Complex((*y as f64).into())),
+ (V::Float(..), V::Ratio(y)) => (a, V::Float(ratio_to_f64(*y))),
+ (V::Complex(..), V::Ratio(y)) => (a, V::Complex(ratio_to_f64(*y).into())),
+ (V::Complex(..), V::Float(y)) => (a, V::Complex((*y).into())),
+ (V::List(l1), V::List(l2)) if l1.len() > 0 && l2.len() > 0
+ => (V::Matrix(Matrix::from_list(l1.to_vec()).into()), V::Matrix(Matrix::from_list(l2.to_vec()).into())),
+ (_, V::List(l)) if l.len() > 0
+ => (a, V::Matrix(Matrix::from_list(l.to_vec()).into())),
+ (V::List(l), _) if l.len() > 0
+ => (V::Matrix(Matrix::from_list(l.to_vec()).into()), b),
+ _ => (a, b),
+ }
+}
+
+
+impl Add for Value {
+ type Output = Result<Self>;
+ fn add(self, rhs: Self) -> Self::Output {
+ use Value::*;
+ match promote(self, rhs) {
+ (Int(x), Int(y)) => Ok(Int(x + y)),
+ (Float(x), Float(y)) => Ok(Float(x + y)),
+ (Ratio(x), Ratio(y)) => Ok(Ratio(x + y)),
+ (Complex(x), Complex(y)) => Ok(Complex(x + y)),
+ (Matrix(x), Matrix(y)) => Ok(Matrix((x + y)?.into())),
+ (Matrix(x), r) => Ok(Matrix(x.increment(r)?.into())),
+ (l, Matrix(y)) => Ok(Matrix(y.increment(l)?.into())),
+ (String(str), value) => Ok(String(Rc::from(
+ format!("{str}{value}")
+ ))),
+ (value, String(str)) => Ok(String(Rc::from(
+ format!("{value}{str}")
+ ))),
+ (List(mut l1), List(l2)) => {
+ l1.extend_from_slice(&l2);
+ Ok(List(l1))
+ },
+ (l, r) => Err(error!("cannot add {l:?} + {r:?}"))
+ }
+ }
+}
+
+impl Sub for Value {
+ type Output = Result<Self>;
+ fn sub(self, rhs: Self) -> Self::Output {
+ use Value::*;
+ match promote(self, rhs) {
+ (Int(x), Int(y)) => Ok(Int(x - y)),
+ (Float(x), Float(y)) => Ok(Float(x - y)),
+ (Ratio(x), Ratio(y)) => Ok(Ratio(x - y)),
+ (Complex(x), Complex(y)) => Ok(Complex(x - y)),
+ (Matrix(x), Matrix(y)) => Ok(Matrix((x - y)?.into())),
+ (Matrix(x), r) => Ok(Matrix(x.decrement(r)?.into())),
+ (l, r) => Err(error!("cannot subtract {l:?} - {r:?}"))
+ }
+ }
+}
+
+impl Mul for Value {
+ type Output = Result<Self>;
+ fn mul(self, rhs: Value) -> Self::Output {
+ use Value::*;
+ match promote(self, rhs) {
+ (Int(x), Int(y)) => Ok(Int(x * y)),
+ (Float(x), Float(y)) => Ok(Float(x * y)),
+ (Ratio(x), Ratio(y)) => Ok(Ratio(x * y)),
+ (Complex(x), Complex(y)) => Ok(Complex(x * y)),
+ (Matrix(x), Matrix(y)) => Ok(Matrix((x * y)?.into())),
+ (Matrix(x), r) => Ok(Matrix(x.scale(r)?.into())),
+ (l, Matrix(y)) => Ok(Matrix(y.scale(l)?.into())),
+ (l, r) => Err(error!("cannot multiply {l:?} * {r:?}"))
+ }
+ }
+}
+
+impl Div for Value {
+ type Output = Result<Self>;
+ fn div(self, rhs: Value) -> Self::Output {
+ use Value::*;
+ match promote(self, rhs) {
+ (Int(_), Int(0)) => Err(error!("cannot divide by zero")),
+ (Int(x), Int(y)) => Ok(Ratio(Rational64::new(x, y))),
+ (Float(x), Float(y)) => Ok(Float(x / y)),
+ (Ratio(x), Ratio(y)) => Ok(Ratio(x / y)),
+ (Complex(x), Complex(y)) => Ok(Complex(x / y)),
+ (l, r) => Err(error!("cannot divide {l:?} / {r:?}"))
+ }
+ }
+}
+
+impl BitOr for Value {
+ type Output = Result<Self>;
+ fn bitor(self, rhs: Value) -> Self::Output {
+ use Value::*;
+ match promote(self, rhs) {
+ (Int(x), Int(y)) => Ok(Int(x | y)),
+ (l, r) => Err(error!("cannot bitwise or {l:?} | {r:?}"))
+ }
+ }
+}
+
+impl BitAnd for Value {
+ type Output = Result<Self>;
+ fn bitand(self, rhs: Value) -> Self::Output {
+ use Value::*;
+ match promote(self, rhs) {
+ (Int(x), Int(y)) => Ok(Int(x & y)),
+ (l, r) => Err(error!("cannot bitwise and {l:?} & {r:?}"))
+ }
+ }
+}
+
+impl BitXor for Value {
+ type Output = Result<Self>;
+ fn bitxor(self, rhs: Value) -> Self::Output {
+ use Value::*;
+ match promote(self, rhs) {
+ (Int(x), Int(y)) => Ok(Int(x ^ y)),
+ (l, r) => Err(error!("cannot bitwise xor {l:?} ^ {r:?}"))
+ }
+ }
+}
+
+impl Shl for Value {
+ type Output = Result<Self>;
+ fn shl(self, rhs: Value) -> Self::Output {
+ use Value::*;
+ match promote(self, rhs) {
+ (Int(x), Int(y)) => Ok(Int(x << y)),
+ (l, r) => Err(error!("cannot bitwise shift left {l:?} << {r:?}"))
+ }
+ }
+}
+
+impl Shr for Value {
+ type Output = Result<Self>;
+ fn shr(self, rhs: Value) -> Self::Output {
+ use Value::*;
+ match promote(self, rhs) {
+ (Int(x), Int(y)) => Ok(Int(x >> y)),
+ (l, r) => Err(error!("cannot bitwise shift right {l:?} >> {r:?}"))
+ }
+ }
+}
+
+impl PartialEq for Value {
+ fn eq(&self, other: &Self) -> bool {
+ use Value::*;
+ match (self, other) {
+ (Nil, Nil) => true,
+ (Bool(a), Bool(b)) => a == b,
+ (Int(a), Int(b)) => *a == *b,
+ (Ratio(a), Ratio(b)) => *a == *b,
+ (Float(a), Float(b)) => *a == *b,
+ (Complex(a), Complex(b)) => *a == *b,
+ (Int(a), Ratio(b)) => Rational64::from(*a) == *b,
+ (Ratio(a), Int(b)) => *a == Rational64::from(*b),
+ (Int(a), Float(b)) => *a as f64 == *b,
+ (Float(a), Int(b)) => *a == *b as f64,
+ (Int(a), Complex(b)) => Complex64::from(*a as f64) == *b,
+ (Complex(a), Int(b)) => *a == Complex64::from(*b as f64),
+ (Ratio(a), Float(b)) => ratio_to_f64(*a) == *b,
+ (Float(a), Ratio(b)) => *a == ratio_to_f64(*b),
+ (Ratio(a), Complex(b)) => Complex64::from(ratio_to_f64(*a)) == *b,
+ (Complex(a), Ratio(b)) => *a == Complex64::from(ratio_to_f64(*b)),
+ (Float(a), Complex(b)) => Complex64::from(*a) == *b,
+ (Complex(a), Float(b)) => *a == Complex64::from(*b),
+ (String(a), String(b)) => *a == *b,
+ (List(a), List(b)) => *a == *b,
+ (Matrix(a), Matrix(b)) => a == b,
+ _ => false,
+ }
+ }
+}
+
+impl PartialOrd for Value {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ use Value::*;
+ match (self, other) {
+ (Nil, Nil) => Some(Ordering::Equal),
+ (Bool(a), Bool(b)) => a.partial_cmp(b),
+ (Int(a), Int(b)) => a.partial_cmp(b),
+ (Ratio(a), Ratio(b)) => a.partial_cmp(b),
+ (Float(a), Float(b)) => a.partial_cmp(b),
+ (Int(a), Ratio(b)) => Rational64::from(*a).partial_cmp(b),
+ (Ratio(a), Int(b)) => a.partial_cmp(&Rational64::from(*b)),
+ (Int(a), Float(b)) => (*a as f64).partial_cmp(b),
+ (Float(a), Int(b)) => a.partial_cmp(&(*b as f64)),
+ (Ratio(a), Float(b)) => ratio_to_f64(*a).partial_cmp(b),
+ (Float(a), Ratio(b)) => a.partial_cmp(&ratio_to_f64(*b)),
+ (String(a), String(b)) => a.partial_cmp(b),
+ (List(a), List(b)) => a.partial_cmp(b),
+ (Matrix(a), Matrix(b)) => a.values.partial_cmp(&b.values),
+ _ => None,
+ }
+ }
+}
+
+impl Neg for Value {
+ type Output = Value;
+
+ fn neg(self) -> Self::Output {
+ use Value::*;
+ match self {
+ Bool(b) => Bool(!b),
+ Int(i) => Int(-i),
+ Float(f) => Float(-f),
+ Ratio(r) => Ratio(-r),
+ Complex(c) => Complex(-c),
+ _ => return Float(f64::NAN)
+ }
+ }
+}
+
+impl Not for Value {
+ type Output = bool;
+
+ fn not(self) -> Self::Output {
+ use Value as V;
+ match self {
+ V::Nil => true,
+ V::Bool(b) => !b,
+ V::Int(i) => i == 0,
+ V::Float(f) => f == 0.0,
+ V::Ratio(r) => *(r.numer()) == 0 || *(r.denom()) == 0,
+ V::Complex(c) => !c.is_normal(),
+ V::Regex(_) => false,
+ V::List(_) => false,
+ V::Matrix(_) => false,
+ V::Table(_) => false,
+ V::String(s) => s.as_ref() == "",
+ V::Function(_) => false,
+ V::Iter(_) => false,
+ V::Range(_) => false,
+ V::File(_) => false,
+ V::Exception(_) => false,
+ }
+ }
+}
+
+
+impl Value {
+
+ pub fn modulo(self, rhs: Value) -> Result<Self> {
+ use Value::*;
+ match promote(self, rhs) {
+ (Int(x), Int(y)) => Ok(Int(x % y)),
+ (Float(x), Float(y)) => Ok(Float(x % y)),
+ (Ratio(x), Ratio(y)) => Ok(Ratio(x % y)),
+ (Complex(x), Complex(y)) => Ok(Complex(x % y)),
+ (l, r) => Err(error!("cannot modulo: {l:?} % {r:?}"))
+ }
+ }
+
+ pub fn pow(self, rhs: Value) -> Result<Self> {
+ use Value::*;
+ if let (Ratio(x), Int(y)) = (&self, &rhs) {
+ return Ok(Ratio(ipow(*(*x).numer(), *(*x).denom(), *y)?.into()));
+ }
+ match promote(self, rhs) {
+ (Int(x), Int(y)) => Ok(Ratio(ipow(x, 1, y)?.into())),
+ (Float(x), Float(y)) => Ok(Float(x.powf(y))),
+ (Ratio(x), Ratio(y)) => Ok(Float(ratio_to_f64(x).powf(ratio_to_f64(y)))),
+ (Complex(x), Complex(y)) => Ok(Complex(x.powc(y))),
+ (l, r) => Err(error!("cannot exponent: {l:?} ** {r:?}"))
+ }
+ }
+
+ pub fn floaty(self) -> Self {
+ use Value as V;
+ match self {
+ V::Int(i) => V::Float(i as f64),
+ V::Ratio(r) => V::Float(ratio_to_f64(r)),
+ a => a
+ }
+ }
+
+ pub fn is_zero(&self) -> bool {
+ use Value as V;
+ match self {
+ V::Int(i) => *i == 0,
+ V::Float(f) => *f == 0.0 || *f == -0.0,
+ V::Ratio(r) => *r.numer() == 0,
+ _ => false,
+ }
+ }
+
+ pub fn binary_op(op: BinaryOp, lhs: Value, rhs: Value) -> Result<Self> {
+ use BinaryOp::*;
+ match op {
+ Add => lhs + rhs,
+ Subtract => lhs - rhs,
+ Multiply => lhs * rhs,
+ Divide => lhs / rhs,
+ Modulo => lhs.modulo(rhs),
+ Power => lhs.pow(rhs),
+ BitwiseAnd => lhs & rhs,
+ BitwiseOr => lhs | rhs,
+ BitwiseXor => lhs ^ rhs,
+ BitwiseShiftLeft => lhs << rhs,
+ BitwiseShiftRight => lhs >> rhs,
+ Equals => Ok(Self::Bool(lhs == rhs)),
+ NotEquals => Ok(Self::Bool(lhs != rhs)),
+ GreaterEquals => Ok(Self::Bool(lhs >= rhs)),
+ LessEquals => Ok(Self::Bool(lhs <= rhs)),
+ GreaterThan => Ok(Self::Bool(lhs > rhs)),
+ LessThan => Ok(Self::Bool(lhs < rhs)),
+ Range | RangeEq => {
+ let Value::Int(lhs) = lhs else {
+ return Err(error!("range can only take [Int]'s"))
+ };
+ let Value::Int(rhs) = rhs else {
+ return Err(error!("range can only take [Int]'s"))
+ };
+ Ok(Self::Range(Rc::new((lhs, rhs, op == RangeEq))))
+ },
+ }
+ }
+
+ pub fn unary_op(op: UnaryOp, val: Value) -> Value {
+ use UnaryOp::*;
+ match op {
+ Negate => -val,
+ Not => Self::Bool(!val),
+ }
+ }
+
+ pub fn to_regex(value: &str) -> Result<Self> {
+ match Regex::new(value) {
+ Ok(r) => Ok(Self::Regex(r.into())),
+ Err(e) => Err(error!("{e}")),
+ }
+ }
+}
diff --git a/matrix-lang/src/value/exception.rs b/matrix-lang/src/value/exception.rs
new file mode 100644
index 0000000..0df6f5c
--- /dev/null
+++ b/matrix-lang/src/value/exception.rs
@@ -0,0 +1,78 @@
+use std::{fmt::{Debug, Display}, error::Error};
+use crate::prelude::*;
+
+#[macro_export]
+macro_rules! exception {
+ ($type:expr) => {
+ $crate::prelude::Exception::new($type)
+ };
+ ($type:expr, $($arg:tt)*) => {
+ $crate::prelude::Exception::msg($type, format!($($arg)*).as_str())
+ };
+}
+
+pub const HASH_EXCEPTION: &'static str = "hash";
+pub const VALUE_EXCEPTION: &'static str = "value";
+pub const PARSE_EXCEPTION: &'static str = "parse";
+pub const COMPILE_EXCEPTION: &'static str = "compile";
+pub const RUNTIME_EXCEPTION: &'static str = "runtime";
+pub const BINARY_EXECPTION: &'static str = "binary";
+pub const IO_EXECPTION: &'static str = "io";
+
+#[derive(Clone)]
+pub struct Exception(Gc<ExceptionInner>);
+
+#[derive(Clone)]
+struct ExceptionInner {
+ ty: Rc<str>,
+ msg: Rc<str>,
+ trace: Vec<(Rc<str>, Position)>
+}
+
+impl Exception {
+ pub fn msg(ty: &str, msg: &str) -> Self {
+ Self(Gc::new(ExceptionInner { ty: ty.into(), msg: msg.into(), trace: Vec::new() }))
+ }
+
+ pub fn pos(mut self, pos: Position) -> Self {
+ self.0.trace.push((
+ Rc::from("<root>"),
+ pos
+ ));
+ self
+ }
+
+ pub fn trace(mut self, block: Rc<str>, pos: Position) -> Self {
+ self.0.trace.push((
+ block,
+ pos
+ ));
+ self
+ }
+}
+
+impl Display for Exception {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let ty = self.0.ty.as_ref();
+ let msg = self.0.msg.as_ref();
+ write!(f, "{}\n Type <{}>", msg, ty)?;
+ for (block, pos) in self.0.trace.iter() {
+ write!(f, "\n In {block} at {pos}\n")?;
+ }
+ Ok(())
+ }
+}
+
+impl Debug for Exception {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "[Exception {}]", self.0.ty.as_ref())
+ }
+}
+
+impl Error for Exception {}
+
+impl<T> From<Exception> for Result<T> {
+ fn from(value: Exception) -> Self {
+ Err(value)
+ }
+}
diff --git a/matrix-lang/src/value/fmt.rs b/matrix-lang/src/value/fmt.rs
new file mode 100644
index 0000000..f276bf1
--- /dev/null
+++ b/matrix-lang/src/value/fmt.rs
@@ -0,0 +1,186 @@
+use std::fmt::{Debug, Display};
+use crate::prelude::*;
+
+use Value as V;
+
+impl Debug for Value {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ V::Nil => write!(f, "[Nil]"),
+ V::Int(i) => write!(f, "[Int {i}]"),
+ V::Bool(b) => write!(f, "[Bool {b}]"),
+ V::Float(vf) => write!(f, "[Float {vf}]"),
+ V::Ratio(r) => write!(f, "[Ratio {r}]"),
+ V::Complex(c) => write!(f, "[Complex {c}]"),
+ V::Regex(r) => write!(f, "[Regex /{r}/]"),
+ V::String(s) => write!(f, "[String '{s}']"),
+ V::List(l) => write!(f, "[List {}]", l.len()),
+ V::Matrix(m) => write!(f, "[Matirx {}x{}]", m.domain, m.codomain),
+ V::Table(t) => write!(f, "[Table {}]", t.len()),
+ V::Function(vf) => write!(f, "[Function {}]", vf.name),
+ V::Range(r) => write!(f, "[Range {:?}..{:?}]", r.0, r.1),
+ V::Iter(_) => write!(f, "[Iterator]"),
+ V::Exception(_) => write!(f, "[Error]"),
+ V::File(_) => write!(f, "[File]"),
+ }
+ }
+}
+
+impl Display for Value {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let red;
+ let green;
+ let yellow;
+ let blue;
+ let pink;
+ let cyan;
+ let clear;
+
+ if f.alternate() {
+ red = "\x1b[31m";
+ green = "\x1b[32m";
+ yellow = "\x1b[33m";
+ blue = "\x1b[34m";
+ pink = "\x1b[35m";
+ cyan = "\x1b[36m";
+ clear = "\x1b[0m";
+ } else {
+ red = "";
+ green = "";
+ yellow = "";
+ blue = "";
+ pink = "";
+ cyan = "";
+ clear = "";
+ }
+
+ match self {
+ V::Nil => {write!(f, "{blue}nil{clear}")?;},
+ V::Bool(b) => {write!(f, "{yellow}{b}{clear}")?;},
+ V::Int(i) => {write!(f, "{yellow}{i}{clear}")?;},
+ V::Float(l) => {write!(f, "{yellow}{l}{clear}")?;},
+ V::Ratio(r) => {write!(f, "{yellow}{r}{clear}")?;},
+ V::Complex(c) => {write!(f, "{yellow}{c}{clear}")?;},
+ V::Regex(r) => {write!(f, "/{red}{r}{clear}/")?;},
+ V::String(s) => {
+ if f.alternate() {
+ write!(f, "{green}'{s}'{clear}")?;
+ } else {
+ write!(f, "{s}")?;
+ }
+ }
+ V::List(l) => {
+ if l.len() < 1 {
+ write!(f, "[]")?;
+ return Ok(())
+ }
+ write!(f, "[ ")?;
+ for (i, el) in l.iter().enumerate() {
+ if i != 0 {
+ write!(f, " ")?;
+ }
+ if f.alternate() {
+ write!(f, "{el:#}")?;
+ } else {
+ write!(f, "{el}")?;
+ }
+ }
+ write!(f, " ]")?;
+ },
+ V::Matrix(m) => {
+ if f.alternate() {
+ write!(f, "{m:#}")?;
+ } else {
+ write!(f, "{m}")?;
+ }
+ },
+ V::Table(t) => {
+ if f.alternate() {
+ write!(f, "{t:#}")?;
+ } else {
+ write!(f, "{t}")?;
+ }
+ },
+ V::Function(fun) => {
+ write!(f, "{cyan}{fun}{clear}")?;
+ }
+ V::Range(r) => {
+ if f.alternate() {
+ write!(f, "{:#}..{:#}", r.0, r.1)?;
+ } else {
+ write!(f, "{}..{}", r.0, r.1)?;
+ }
+ }
+ V::Iter(_) => {write!(f, "{pink}[Iterator]{clear}")?;},
+ V::File(_) => {write!(f, "{pink}[File]{clear}")?;},
+ V::Exception(e) => {write!(f, "{red}{e}{clear}")?;},
+ };
+ Ok(())
+ }
+}
+
+impl Debug for Matrix {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "[Matrix {}x{}]", self.domain, self.codomain)
+ }
+}
+
+impl Display for Matrix {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let mut max_cols = vec![0; self.domain];
+ let mut vals: Vec<String> = Vec::with_capacity(self.domain * self.codomain);
+ for row in 0..self.codomain {
+ for col in 0..self.domain {
+ let idx = col + row * self.domain;
+ let el = &self.values[idx];
+ let s = match f.alternate() {
+ true => format!("{:#}", el),
+ false => format!("{}", el)
+ };
+ max_cols[col] = max_cols[col].max(s.len());
+ vals.push(s);
+ }
+ }
+
+ write!(f, "\n")?;
+ for row in 0..self.codomain {
+ for col in 0..self.domain {
+ let idx = col + row * self.domain;
+ let s = vals[idx].as_str();
+ let width = max_cols[col];
+ write!(f, " {s:>width$}")?;
+ }
+ write!(f, "\n")?;
+ }
+
+ Ok(())
+ }
+}
+
+impl Debug for ValueMap {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "[Table {}]", self.len())
+ }
+}
+
+impl Display for ValueMap {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ if self.len() < 1 {
+ write!(f, "{{}}")?;
+ return Ok(())
+ }
+ write!(f, "{{ ")?;
+ for (i, (key, value)) in self.entries().enumerate() {
+ if i != 0 {
+ write!(f, ", ")?;
+ }
+ if f.alternate() {
+ write!(f, "{key:#} = {value:#}")?
+ } else {
+ write!(f, "{key} = {value}")?
+ }
+ }
+ write!(f, " }}")?;
+ Ok(())
+ }
+}
diff --git a/matrix-lang/src/value/function.rs b/matrix-lang/src/value/function.rs
new file mode 100644
index 0000000..38d8b0b
--- /dev/null
+++ b/matrix-lang/src/value/function.rs
@@ -0,0 +1,43 @@
+use crate::prelude::*;
+use std::fmt::{Debug, Display};
+
+pub struct Function {
+ pub name: Rc<str>,
+ pub arity: usize,
+ pub variadic: bool,
+ pub fun: InnerFunction
+}
+
+#[derive(Clone)]
+pub enum InnerFunction {
+ Compiled(Rc<Chunk>),
+ Native(Rc<dyn Fn((&mut Vm, &mut StackFrame), Vec<Value>) -> Result<Value>>),
+}
+
+impl Debug for Function {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ use InnerFunction as F;
+ match self.fun {
+ F::Compiled(_) => {
+ write!(f, "[Function {}]", self.name)
+ },
+ F::Native(_) => {
+ write!(f, "[Builtin {}]", self.name)
+ }
+ }
+ }
+}
+
+impl Display for Function {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ use InnerFunction as F;
+ match self.fun {
+ F::Compiled(_) => {
+ write!(f, "[Function {}]", self.name)
+ },
+ F::Native(_) => {
+ write!(f, "[Builtin {}]", self.name)
+ }
+ }
+ }
+}
diff --git a/matrix/src/gc.rs b/matrix-lang/src/value/gc.rs
index 7af020b..5ef8b80 100644
--- a/matrix/src/gc.rs
+++ b/matrix-lang/src/value/gc.rs
@@ -1,4 +1,5 @@
use std::{ops::{Index, IndexMut, Deref, DerefMut, Add, Sub, Mul}, marker::PhantomData, ptr::NonNull, fmt::{Debug, Display}};
+use crate::prelude::*;
pub struct Gc<T> {
ptr: NonNull<GcInner<T>>,
@@ -24,22 +25,31 @@ impl<T> Gc<T> {
}
impl <T: Clone> Gc<T> {
- pub fn clone_inside(&self) -> Self {
+ pub fn into_inner(self) -> T {
unsafe {
- let data = self.ptr.as_ref().data.clone();
- Self::new(data)
+ self.ptr.as_ref().data.clone()
}
}
- pub fn into_inner(self) -> T {
+ fn data(&self) -> T {
unsafe {
self.ptr.as_ref().data.clone()
}
}
+}
- fn data(&self) -> T {
+impl <T: ValueClone> ValueClone for Gc<T> {
+ fn deep_clone(&self) -> Self {
unsafe {
- self.ptr.as_ref().data.clone()
+ let data = self.ptr.as_ref().data.deep_clone();
+ Self::new(data)
+ }
+ }
+
+ fn shallow_clone(&self) -> Self {
+ unsafe {
+ let data = self.ptr.as_ref().data.shallow_clone();
+ Self::new(data)
}
}
}
diff --git a/matrix-lang/src/value/hash.rs b/matrix-lang/src/value/hash.rs
new file mode 100644
index 0000000..35c1343
--- /dev/null
+++ b/matrix-lang/src/value/hash.rs
@@ -0,0 +1,240 @@
+use crate::prelude::*;
+use std::hash::{Hash, DefaultHasher, Hasher};
+
+#[derive(Clone)]
+pub struct ValueMap {
+ values: Vec<ValueEntry>,
+ size: usize,
+ used: usize,
+}
+
+#[derive(Clone)]
+enum ValueEntry {
+ Empty,
+ Taken((Value, Value)),
+ Gravestone(Value),
+}
+
+impl Default for ValueMap {
+ fn default() -> Self {
+ ValueMap {
+ values: vec![ValueEntry::Empty; 8],
+ size: 8,
+ used: 0,
+ }
+ }
+}
+
+impl ValueMap {
+
+ fn get_index(values: &Vec<ValueEntry>, size: usize, key: &Value, hash: usize) -> usize {
+ let mut nearest_grave = None;
+ let start = hash & size;
+ for offset in 0..size {
+ let idx = (start + offset) % size;
+ match &values[idx] {
+ ValueEntry::Empty => return idx,
+ ValueEntry::Taken((marker, _)) => {
+ if marker.eq(key) { return idx };
+ continue;
+ },
+ ValueEntry::Gravestone(grave) => {
+ if grave.eq(key) { return idx };
+ if nearest_grave.is_none() {
+ nearest_grave = Some(idx);
+ }
+ continue;
+ },
+ }
+ }
+
+ match nearest_grave {
+ Some(idx) => idx,
+ None => panic!("cannot get value map index: full!!!"),
+ }
+ }
+
+ fn expand(&mut self) -> Result<()> {
+ let pairs = self.values.iter()
+ .filter_map(|e| {
+ match e {
+ ValueEntry::Taken(s) => Some(s.clone()),
+ _ => None
+ }
+ })
+ .collect::<Vec<(Value, Value)>>();
+
+ let new_used = pairs.len();
+ let new_size = self.size * 2 + pairs.len();
+ let mut new_values = vec![ValueEntry::Empty; new_size];
+
+ for (key, value) in pairs.into_iter() {
+ let hash = self.hash(&key)?;
+ let idx = ValueMap::get_index(&new_values, new_size, &key, hash);
+ new_values[idx] = ValueEntry::Taken((key, value));
+ }
+
+ self.used = new_used;
+ self.size = new_size;
+ self.values = new_values;
+
+ Ok(())
+ }
+
+ fn hash(&self, key: &Value) -> Result<usize> {
+ let mut hasher = DefaultHasher::new();
+ key.try_hash(&mut hasher)?;
+ Ok(hasher.finish() as usize)
+ }
+
+ pub fn get<'a>(&'a self, key: &Value) -> Result<Option<&'a Value>> {
+ let hash = self.hash(key)?;
+ let idx = ValueMap::get_index(&self.values, self.size, key, hash);
+ match &self.values[idx] {
+ ValueEntry::Taken((_, value)) => Ok(Some(value)),
+ _ => Ok(None)
+ }
+ }
+
+ pub fn insert(&mut self, key: Value, value: Value) -> Result<()> {
+ if self.used * 3 >= self.size * 2 {
+ self.expand()?;
+ }
+ let key = key.deep_clone();
+ let hash = self.hash(&key)?;
+ let idx = ValueMap::get_index(&self.values, self.size, &key, hash);
+ self.values[idx] = ValueEntry::Taken((key, value));
+ self.used += 1;
+ Ok(())
+ }
+
+ pub fn remove(&mut self, key: &Value) -> Result<Option<Value>> {
+ let hash = self.hash(key)?;
+ let idx = ValueMap::get_index(&self.values, self.size, key, hash);
+ let mut value = ValueEntry::Gravestone(key.clone());
+ std::mem::swap(&mut value, &mut self.values[idx]);
+ match value {
+ ValueEntry::Taken((_, v)) => {
+ self.used -= 1;
+ Ok(Some(v))
+ }
+ _ => Ok(None),
+ }
+ }
+
+ pub fn entries<'a>(&'a self) -> ValueMapIterator<'a> {
+ ValueMapIterator::new(self)
+ }
+
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ pub fn len(&self) -> usize {
+ self.used
+ }
+}
+
+pub struct ValueMapIterator<'a> {
+ map: &'a ValueMap,
+ index: usize
+}
+
+impl<'a> ValueMapIterator<'a> {
+ fn new(map: &'a ValueMap) -> Self {
+ Self { map, index: 0 }
+ }
+}
+
+impl<'a> Iterator for ValueMapIterator<'a> {
+ type Item = (&'a Value, &'a Value);
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let mut idx = self.index;
+ loop {
+ if idx >= self.map.size {
+ return None
+ }
+ use ValueEntry as E;
+ let E::Taken((k, v)) = &self.map.values[idx] else {
+ idx += 1;
+ continue;
+ };
+
+ self.index = idx + 1;
+ return Some((k, v))
+ }
+ }
+}
+
+pub trait TryHash {
+ fn try_hash<H: std::hash::Hasher>(&self, state: &mut H) -> Result<()>;
+}
+
+impl TryHash for Value {
+ fn try_hash<H: std::hash::Hasher>(&self, state: &mut H) -> Result<()> {
+ use Value::*;
+ match self {
+ Nil => 0x23845.hash(state),
+ Bool(b) => b.hash(state),
+ Int(i) => i.hash(state),
+ Ratio(r) => r.hash(state),
+ Regex(r) => r.as_str().hash(state),
+ String(s) => s.hash(state),
+ List(l) => {
+ for val in l.iter() {
+ val.try_hash(state)?;
+ }
+ }
+ Matrix(m) => {
+ m.domain.hash(state);
+ m.codomain.hash(state);
+ for val in m.values.iter() {
+ val.try_hash(state)?;
+ }
+ },
+ _ => return Err(exception!(HASH_EXCEPTION, "cannot hash {self:?}"))
+ };
+ Ok(())
+ }
+}
+
+impl ValueClone for ValueEntry {
+ fn deep_clone(&self) -> Self {
+ use ValueEntry as E;
+ match self {
+ E::Empty => E::Empty,
+ E::Taken((k, v)) => E::Taken((k.deep_clone(), v.deep_clone())),
+ E::Gravestone(g) => E::Gravestone(g.deep_clone()),
+ }
+ }
+
+ fn shallow_clone(&self) -> Self {
+ use ValueEntry as E;
+ match self {
+ E::Empty => E::Empty,
+ E::Taken((k, v)) => E::Taken((k.clone(), v.clone())),
+ E::Gravestone(g) => E::Gravestone(g.clone()),
+ }
+ }
+}
+
+impl ValueClone for ValueMap {
+ fn deep_clone(&self) -> Self {
+ let values = self.values.iter().map(|e| e.deep_clone()).collect();
+ Self {
+ values,
+ size: self.size,
+ used: self.used
+ }
+ }
+
+ fn shallow_clone(&self) -> Self {
+ let values = self.values.iter().map(|e| e.clone()).collect();
+ Self {
+ values,
+ size: self.size,
+ used: self.used
+ }
+ }
+}
diff --git a/matrix-lang/src/value/index.rs b/matrix-lang/src/value/index.rs
new file mode 100644
index 0000000..a5725e8
--- /dev/null
+++ b/matrix-lang/src/value/index.rs
@@ -0,0 +1,230 @@
+use std::cmp::Ordering;
+
+use crate::prelude::*;
+
+macro_rules! error {
+ ($($arg:tt)*) => {
+ Err(exception!(VALUE_EXCEPTION, $($arg)*))
+ };
+}
+
+impl Value {
+ pub fn val_cmp(&self, other: &Self) -> Result<Ordering> {
+ self.partial_cmp(other)
+ .map_or_else(|| error!("cannot compare: {self:?} and {other:?}"), Ok)
+ }
+
+ fn index_single(&self, index: &Value) -> Result<Self> {
+ use Value as V;
+ match (self, index) {
+ (V::Table(t), index) => {
+ Ok(t.get(index)?.unwrap_or(&Value::Nil).clone())
+ },
+ (V::List(l), V::Int(i)) => {
+ if *i < 0 || *i as usize >= l.len() {
+ return error!("{i} out of bounds for {self:?}")
+ }
+ Ok(l[*i as usize].clone())
+ },
+ (V::Matrix(m), V::Int(i)) => {
+ if *i < 0 || *i as usize >= m.values.len() {
+ return error!("{i} out of bounds for {self:?}")
+ }
+ Ok(m.values[*i as usize].clone())
+ },
+ _ => return error!("{index:?} cant index {self:?}")
+ }
+ }
+
+ fn index_multiple(&self, indexes: &Vec<Value>) -> Result<Self> {
+ use Value as V;
+ match self {
+ V::List(..) => {
+ let mut ret = Vec::new();
+ for index in indexes {
+ let res = self.index_single(index)?;
+ ret.push(res);
+ }
+ Ok(V::List(ret.into()))
+ }
+ V::Table(..) => {
+ let mut ret = ValueMap::new();
+ for index in indexes {
+ let res = self.index_single(index)?;
+ ret.insert(index.clone(), res)?;
+ }
+ Ok(V::Table(ret.into()))
+ }
+ V::Matrix(m) => {
+ let err = || error!("{self:?} can be index by [Int] or [Int;Int]");
+ if indexes.len() != 2 {
+ return err()
+ }
+ let lhs = indexes[0].clone();
+ let rhs = indexes[1].clone();
+ match (lhs, rhs) {
+ (V::Nil, V::Nil) => {
+ Ok(V::Matrix(m.shallow_clone()))
+ },
+ (V::Int(row), V::Nil) => {
+ let Some((_, row)) = m.rows().enumerate().filter(|(idx, _)| *idx as i64 == row).next() else {
+ return err();
+ };
+ let row: Vec<Value> = row.into_iter().map(|e| e.clone()).collect();
+ Ok(V::Matrix(Matrix::new(row.len(), 1, row).into()))
+ },
+ (V::Nil, V::Int(col)) => {
+ let Some((_, col)) = m.cols().enumerate().filter(|(idx, _)| *idx as i64 == col).next() else {
+ return err();
+ };
+ let col: Vec<Value> = col.into_iter().map(|e| e.clone()).collect();
+ Ok(V::Matrix(Matrix::new(1, col.len(), col).into()))
+ },
+ (V::Int(row), V::Int(col)) => {
+ if row < 0 || col < 0 {
+ return err();
+ }
+ m.get(row as usize, col as usize)
+ }
+ _ => return err()
+ }
+ }
+ _ => return error!("cannot index {self:?}")
+ }
+ }
+
+ pub fn index(&self, index: &Vec<Value>) -> Result<Self> {
+ if index.len() == 0 {
+ Ok(self.shallow_clone())
+ } else if index.len() == 1 {
+ self.index_single(&index[0])
+ } else {
+ self.index_multiple(index)
+ }
+ }
+
+ fn store_index_single(&mut self, index: &Value, store: Value) -> Result<()> {
+ use Value as V;
+ let err = format!("{self:?}");
+ match (self, index) {
+ (V::Table(t), index) => {
+ t.insert(index.clone(), store)
+ },
+ (V::List(l), V::Int(i)) => {
+ if *i < 0 || *i as usize >= l.len() {
+ return error!("{i} out of bounds for {err}")
+ }
+ l[*i as usize] = store;
+ Ok(())
+ },
+ (V::Matrix(m), V::Int(i)) => {
+ if *i < 0 || *i as usize >= m.values.len() {
+ return error!("{i} out of bounds for {err}")
+ }
+ m.values[*i as usize] = store;
+ Ok(())
+ },
+ _ => return error!("{index:?} cant index {err}")
+ }
+ }
+
+ fn store_index_multiple(&mut self, indexes: &Vec<Value>, store: Value) -> Result<()> {
+ use Value as V;
+ match self {
+ V::List(..) => {
+ for index in indexes {
+ self.store_index_single(index, store.clone())?;
+ }
+ Ok(())
+ }
+ V::Table(..) => {
+ for index in indexes {
+ self.store_index_single(index, store.clone())?;
+ }
+ Ok(())
+ }
+ _ => return error!("cannot index {self:?}")
+ }
+ }
+
+ pub fn store_index(&mut self, index: &Vec<Value>, store: Value) -> Result<()> {
+ if index.len() == 0 {
+ Ok(())
+ } else if index.len() == 1 {
+ self.store_index_single(&index[0], store)
+ } else {
+ self.store_index_multiple(index, store)
+ }
+ }
+
+ pub fn store_field_access(&mut self, ident: &str, val: Value) -> Result<()> {
+ use Value as V;
+ match self {
+ V::Table(t) => {
+ let key = V::String(Rc::from(ident));
+ Ok(t.insert(key, val)?)
+ },
+ _ => return error!("cannot field access assign {self:?}")
+ }
+ }
+
+ pub fn field_access(&self, ident: &str) -> Result<Self> {
+ use Value as V;
+ match self {
+ V::Table(t) => {
+ let key = V::String(Rc::from(ident));
+ Ok(t.get(&key)?.unwrap_or(&V::Nil).clone())
+ },
+ _ => return error!("cannot field access {self:?}")
+ }
+ }
+
+}
+
+impl Value {
+
+ pub fn into_iter_fn(self) -> Result<Rc<Function>> {
+ let Value::Iter(iter) = self.into_iter()? else {
+ return error!("bypassed iter check")
+ };
+ Ok(iter)
+ }
+
+ pub fn into_iter(self) -> Result<Self> {
+ use Value as V;
+ Ok(match self {
+ V::Iter(..) => self,
+ V::List(l) => {
+ let iter = RefCell::new(l.into_inner().into_iter());
+ iter!(move |_,_| {
+ match iter.borrow_mut().next() {
+ Some(v) => Ok(v),
+ None => Ok(V::Nil),
+ }
+ })
+ },
+ V::Range(r) => {
+ let r = (*r).clone();
+ let lhs = RefCell::new(r.0);
+ let rhs = r.1;
+ iter!(move |_,_| {
+ let val = *lhs.borrow();
+ let next = *lhs.borrow() + 1;
+ if (!r.2 && *lhs.borrow() < rhs) || (r.2 && *lhs.borrow() <= rhs) {
+ *lhs.borrow_mut() = next;
+ return Ok(Value::Int(val))
+ }
+ Ok(Value::Nil)
+ })
+ },
+ V::Function(f) => {
+ if f.arity > 0 || f.variadic {
+ return error!("iterator functions cannot be varadic or take arguments")
+ }
+ V::Iter(f)
+ },
+ val => return error!("cannot turn {val:?} into an iterator")
+ })
+ }
+}
+
diff --git a/matrix-lang/src/value/matrix.rs b/matrix-lang/src/value/matrix.rs
new file mode 100644
index 0000000..91e3ec2
--- /dev/null
+++ b/matrix-lang/src/value/matrix.rs
@@ -0,0 +1,337 @@
+use std::ops::{Add, Sub, Mul};
+
+use crate::prelude::*;
+
+#[derive(Clone)]
+pub struct Matrix {
+ pub domain: usize,
+ pub codomain: usize,
+ pub values: Vec<Value>
+}
+
+macro_rules! error {
+ ($($arg:tt)*) => {
+ exception!(VALUE_EXCEPTION, $($arg)*)
+ };
+}
+
+impl Matrix {
+ pub fn new(
+ domain: usize,
+ codomain: usize,
+ values: Vec<Value>
+ ) -> Self {
+ Self {
+ domain,
+ codomain,
+ values
+ }
+ }
+
+ pub fn from_list(
+ values: Vec<Value>
+ ) -> Self {
+ Self {
+ domain: values.len(),
+ codomain: 1,
+ values
+ }
+ }
+
+ pub fn empty(
+ domain: usize,
+ codomain: usize
+ ) -> Self {
+ let values = (0..(domain * codomain)).into_iter()
+ .map(|_| Value::Int(0))
+ .collect();
+ Self {
+ domain,
+ codomain,
+ values
+ }
+ }
+
+ pub fn get(&self, row: usize, col: usize) -> Result<Value> {
+ if row >= self.codomain || col >= self.domain {
+ return Err(error!("[{};{}] out of bounds for [Matrix {}x{}]", row, col, self.domain, self.codomain))
+ }
+ let idx = col + row * self.domain;
+ Ok(self.values[idx].clone())
+ }
+
+
+
+ pub fn set(&mut self, row: usize, col: usize, val: Value) -> Result<()> {
+ if row >= self.codomain || col >= self.domain {
+ return Err(error!("[{};{}] out of bounds for [Matrix {}x{}]", row, col, self.domain, self.codomain))
+ }
+ let idx = col + row * self.domain;
+ self.values[idx] = val;
+ Ok(())
+ }
+
+ pub fn rows<'a>(&'a self) -> MatrixRows<'a> {
+ MatrixRows {
+ matrix: self,
+ row: 0
+ }
+ }
+
+ pub fn cols<'a>(&'a self) -> MatrixCols<'a> {
+ MatrixCols {
+ matrix: self,
+ col: 0
+ }
+ }
+
+ // SPLCIE DOMAIN
+ pub fn splice_cols(&self, col_start: usize, col_end: usize) -> Result<Self> {
+ if col_start <= col_end || col_end > self.domain {
+ return Err(error!("[_;{}..{}] invalid for [Matrix {}x{}]", col_start, col_end, self.domain, self.codomain))
+ }
+
+ let mut cols = Vec::new();
+
+ for (i, col) in self.cols().enumerate() {
+ if i >= col_start && i < col_end {
+ cols.push(col);
+ }
+ }
+
+ let domain = cols.len();
+ let codomain = cols[0].len();
+ let mut res = Self::empty(domain, codomain);
+
+ for i in 0..domain {
+ for j in 0..codomain {
+ res.set(j, i, cols[i][j].clone())?;
+ }
+ }
+
+ Ok(res)
+ }
+
+ // SPLICE CODOMAIN
+ pub fn splice_rows(&self, row_start: usize, row_end: usize) -> Result<Self> {
+ if row_start <= row_end || row_end > self.codomain {
+ return Err(error!("[{}..{};_] invalid for [Matrix {}x{}]", row_start, row_end, self.domain, self.codomain))
+ }
+
+ let mut rows = Vec::new();
+
+ for (i, row) in self.rows().enumerate() {
+ if i >= row_start && i < row_end {
+ rows.push(row);
+ }
+ }
+
+ let domain = rows[0].len();
+ let codomain = rows.len();
+ let mut res = Self::empty(domain, codomain);
+
+ for i in 0..domain {
+ for j in 0..codomain {
+ res.set(j, i, rows[j][i].clone())?;
+ }
+ }
+
+ Ok(res)
+ }
+
+ pub fn increment(&self, increment: Value) -> Result<Self> {
+ let values = self.values.iter()
+ .map(|v| v.clone() + increment.clone())
+ .collect::<Result<Vec<Value>>>()?;
+ Ok(Matrix::new(self.domain, self.codomain, values))
+ }
+
+ pub fn decrement(&self, decrement: Value) -> Result<Self> {
+ let values = self.values.iter()
+ .map(|v| v.clone() - decrement.clone())
+ .collect::<Result<Vec<Value>>>()?;
+ Ok(Matrix::new(self.domain, self.codomain, values))
+ }
+
+ pub fn scale(&self, scale: Value) -> Result<Self> {
+ let values = self.values.iter()
+ .map(|v| v.clone() * scale.clone())
+ .collect::<Result<Vec<Value>>>()?;
+ Ok(Matrix::new(self.domain, self.codomain, values))
+ }
+
+ pub fn join_right(&self, other: &Matrix) -> Result<Self> {
+ if self.codomain != other.codomain {
+ return Err(error!("matrix codomain's do not match"))
+ }
+ let mut r1 = self.rows();
+ let mut r2 = other.rows();
+ let mut rows = Vec::new();
+ loop {
+ let Some(r1) = r1.next() else { break; };
+ let Some(r2) = r2.next() else { break; };
+
+ let mut row = r1
+ .into_iter()
+ .map(|v| v.clone())
+ .collect::<Vec<Value>>();
+
+ row.extend(r2.into_iter().map(|v| v.clone()));
+
+ rows.push(row);
+ }
+
+ let values = rows
+ .into_iter()
+ .reduce(|mut a,b| {a.extend(b); a})
+ .unwrap();
+
+ Ok(Matrix::new(self.domain + other.domain, self.codomain, values))
+ }
+
+ pub fn join_bottom(&self, other: &Matrix) -> Result<Self> {
+ if self.domain != other.domain {
+ return Err(error!("matrix domain's do not match"))
+ }
+ let mut values = self.values.clone();
+ values.extend(other.values.clone());
+ Ok(Matrix::new(self.domain, self.codomain + other.codomain, values))
+ }
+}
+
+impl PartialEq for Matrix {
+ fn eq(&self, other: &Self) -> bool {
+ if self.domain != other.domain || self.codomain != other.codomain {
+ return false
+ }
+
+ for i in 0..self.values.len() {
+ if self.values[i] != other.values[i] {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
+
+impl Add for Matrix {
+ type Output = Result<Self>;
+
+ fn add(self, rhs: Self) -> Self::Output {
+ if self.domain != rhs.domain || self.codomain != rhs.codomain {
+ return Err(error!("cannot add {self:?} + {rhs:?}"))
+ }
+ let mut res = Matrix::empty(self.domain, self.codomain);
+ for col in 0..self.domain {
+ for row in 0..self.codomain {
+ let add = self.get(row, col)? + rhs.get(row, col)?;
+ res.set(row, col, add?)?;
+ }
+ }
+ Ok(res)
+ }
+}
+
+impl Sub for Matrix {
+ type Output = Result<Self>;
+
+ fn sub(self, rhs: Self) -> Self::Output {
+ if self.domain != rhs.domain || self.codomain != rhs.codomain {
+ return Err(error!("cannot subtract {self:?} - {rhs:?}"))
+ }
+ let mut res = Matrix::empty(self.domain, self.codomain);
+ for col in 0..self.domain {
+ for row in 0..self.codomain {
+ let sub = self.get(row, col)? - rhs.get(row, col)?;
+ res.set(row, col, sub?)?;
+ }
+ }
+ Ok(res)
+ }
+}
+
+fn dot(lhs: Vec<&Value>, rhs: Vec<&Value>) -> Result<Value> {
+ let len = lhs.len();
+
+ let mut res = Value::Int(0);
+ for i in 0..len {
+ let val = (lhs[i].clone() * rhs[i].clone())?;
+ res = (res + val)?;
+ }
+
+ Ok(res)
+}
+
+impl Mul for Matrix {
+ type Output = Result<Self>;
+
+ fn mul(self, rhs: Self) -> Self::Output {
+ if self.domain != rhs.codomain {
+ return Err(error!("cannot multiply {self:?} * {rhs:?}"))
+ }
+ let mut res = Self::empty(rhs.domain, self.codomain);
+ for (i, row) in self.rows().enumerate() {
+ for (j, col) in rhs.cols().enumerate() {
+ let dot = dot(row.clone(), col.clone())?;
+ res.set(i, j, dot)?;
+ }
+ }
+ Ok(res)
+ }
+}
+
+pub struct MatrixRows<'a> {
+ matrix: &'a Matrix,
+ row: usize
+}
+
+impl<'a> Iterator for MatrixRows<'a> {
+ type Item = Vec<&'a Value>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.row >= self.matrix.codomain {
+ return None
+ }
+
+ let row_start = self.row * self.matrix.domain;
+ let row_end = row_start + self.matrix.domain;
+
+ let res = self.matrix.values
+ .iter()
+ .enumerate()
+ .filter(|(idx, _)| *idx >= row_start && *idx < row_end)
+ .map(|v| v.1)
+ .collect();
+
+ self.row += 1;
+
+ Some(res)
+ }
+}
+
+pub struct MatrixCols<'a> {
+ matrix: &'a Matrix,
+ col: usize
+}
+
+impl<'a> Iterator for MatrixCols<'a> {
+ type Item = Vec<&'a Value>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.col >= self.matrix.domain {
+ return None
+ }
+
+ let res = self.matrix.values
+ .iter()
+ .enumerate()
+ .filter(|(idx, _)| *idx % self.matrix.domain == self.col)
+ .map(|v| v.1)
+ .collect();
+
+ self.col += 1;
+
+ Some(res)
+ }
+}
diff --git a/matrix-lang/src/value/mod.rs b/matrix-lang/src/value/mod.rs
new file mode 100644
index 0000000..9094bb6
--- /dev/null
+++ b/matrix-lang/src/value/mod.rs
@@ -0,0 +1,42 @@
+pub mod comp;
+pub mod gc;
+pub mod matrix;
+pub mod hash;
+pub mod exception;
+pub mod function;
+pub mod fmt;
+pub mod index;
+pub mod clone;
+
+use crate::prelude::*;
+
+#[derive(Clone)]
+pub enum Value {
+ Nil,
+
+ Bool(bool),
+ Int(i64),
+ Float(f64),
+ Ratio(Rational64),
+ Complex(Complex64),
+
+ Regex(Rc<Regex>),
+ String(Rc<str>),
+
+ List(Gc<Vec<Value>>),
+ Matrix(Gc<Matrix>),
+ Table(Gc<ValueMap>),
+
+ Function(Rc<Function>),
+ Range(Rc<(i64, i64, bool)>),
+ Iter(Rc<Function>),
+ File(Rc<RefCell<File>>),
+
+ Exception(Exception),
+}
+
+impl From<&str> for Value {
+ fn from(value: &str) -> Self {
+ Value::String(Rc::from(value))
+ }
+}
diff --git a/matrix-lang/src/value/value.rs b/matrix-lang/src/value/value.rs
new file mode 100644
index 0000000..10d8398
--- /dev/null
+++ b/matrix-lang/src/value/value.rs
@@ -0,0 +1,522 @@
+use std::{
+ collections::HashMap,
+ rc::Rc,
+ hash::Hash,
+ fmt::{Display, Debug},
+ ops::{Add, Neg, Not, Sub, Div, Mul, BitOr, BitAnd, BitXor, Shl, Shr},
+ cmp::Ordering,
+ cell::RefCell, fs::File
+};
+
+use crate::iter;
+use num_complex::Complex64;
+use num_rational::Rational64;
+use regex::Regex;
+
+use crate::{ast::{Expr, BinaryOp, UnaryOp}, chunk::Function, Result, gc::Gc};
+
+
+pub type InlineList = Vec<Expr>;
+pub type InlineMatrix = (usize, usize, Vec<Expr>);
+pub type InlineTable = Vec<(Expr, Expr)>;
+
+#[derive(Debug)]
+pub struct Error(String);
+
+impl Display for self::Error {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}", self.0)
+ }
+}
+
+impl std::error::Error for self::Error {}
+
+macro_rules! error {
+ ($($arg:tt)*) => {
+ Err(self::Error(format!($($arg)*)).into())
+ };
+}
+
+
+impl From<&str> for Value {
+ fn from(value: &str) -> Self {
+ Value::String(value.into())
+ }
+}
+
+
+impl Debug for Value {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ use Value as V;
+ match self {
+ V::Nil => write!(f, "[Nil]"),
+ V::Bool(b) => write!(f, "[Bool {b}]"),
+ V::Int(i) => write!(f, "[Int {i}]"),
+ V::Float(vf) => write!(f, "[Float {vf}]"),
+ V::Ratio(r) => write!(f, "[Ratio {r}]"),
+ V::Complex(c) => write!(f, "[Complex {c}]"),
+ V::Regex(r) => write!(f, "[Regex /{r}/]"),
+ V::String(s) => write!(f, "[String '{s}']"),
+ V::List(l) => write!(f, "[List {}]", l.len()),
+ V::Matrix(m) => write!(f, "[Matirx {}x{}]", m.domain, m.codomain),
+ V::Table(t) => write!(f, "[Table {}]", t.0.len()),
+ V::Function(vf) => write!(f, "[Function {}]", vf.name),
+ V::Range(r) => write!(f, "[Range {:?}..{:?}]", r.0, r.1),
+ V::Iter(_) => write!(f, "[Iterator]"),
+ V::Error(_) => write!(f, "[Error]"),
+ V::File(_) => write!(f, "[File]"),
+ }
+ }
+}
+
+impl Display for Value {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ use Value as V;
+
+ let red;
+ let green;
+ let yellow;
+ let blue;
+ let pink;
+ let cyan;
+ let clear;
+
+ if f.alternate() {
+ red = "\x1b[31m";
+ green = "\x1b[32m";
+ yellow = "\x1b[33m";
+ blue = "\x1b[34m";
+ pink = "\x1b[35m";
+ cyan = "\x1b[36m";
+ clear = "\x1b[0m";
+ } else {
+ red = "";
+ green = "";
+ yellow = "";
+ blue = "";
+ pink = "";
+ cyan = "";
+ clear = "";
+ }
+
+ match self {
+ V::Nil => {write!(f, "{blue}nil{clear}")?;},
+ V::Bool(b) => {write!(f, "{yellow}{b}{clear}")?;},
+ V::Int(i) => {write!(f, "{yellow}{i}{clear}")?;},
+ V::Float(l) => {write!(f, "{yellow}{l}{clear}")?;},
+ V::Ratio(r) => {write!(f, "{yellow}{r}{clear}")?;},
+ V::Complex(c) => {write!(f, "{yellow}{c}{clear}")?;},
+ V::Regex(r) => {write!(f, "/{red}{r}{clear}/")?;},
+ V::String(s) => {
+ if f.alternate() {
+ write!(f, "{green}'{s}'{clear}")?;
+ } else {
+ write!(f, "{s}")?;
+ }
+ }
+ V::List(l) => {
+ write!(f, "[")?;
+ for (i, el) in l.iter().enumerate() {
+ if i != 0 {
+ write!(f, " ")?;
+ }
+ if f.alternate() {
+ write!(f, "{el:#}")?;
+ } else {
+ write!(f, "{el}")?;
+ }
+ }
+ write!(f, "]")?;
+ },
+ V::Matrix(m) => {
+ if f.alternate() {
+ write!(f, "{m:#}")?;
+ } else {
+ write!(f, "{m}")?;
+ }
+ },
+ V::Table(t) => {
+ write!(f, "{{")?;
+ for (i, (key, val)) in t.0.iter().enumerate() {
+ if i != 0 {
+ write!(f, ", ")?;
+ }
+ if f.alternate() {
+ write!(f, "{key:#} = {val:#}")?;
+ } else {
+ write!(f, "{key} = {val}")?;
+ }
+ }
+ write!(f, "}}")?;
+ },
+ V::Function(fun) => {
+ use crate::chunk::InnerFunction as F;
+ match fun.fun {
+ F::Compiled(_) => write!(f, "{cyan}[Function {}]{clear}", fun.name)?,
+ F::Native(_) => write!(f, "{cyan}[Builtin {}]{clear}", fun.name)?,
+ };
+ },
+ V::Range(r) => {
+ if f.alternate() {
+ write!(f, "{:#}..{:#}", r.0, r.1)?;
+ } else {
+ write!(f, "{}..{}", r.0, r.1)?;
+ }
+ }
+ V::Iter(_) => {write!(f, "{pink}[Iterator]{clear}")?;},
+ V::File(_) => {write!(f, "{pink}[File]{clear}")?;},
+ V::Error(e) => {write!(f, "{red}{e}{clear}")?;},
+ };
+ Ok(())
+ }
+}
+
+impl Value {
+ pub fn can_hash(&self) -> Result<()> {
+ use Value::*;
+ match self {
+ Nil => {},
+ Bool(_) => {},
+ Int(_) => {},
+ Ratio(_) => {},
+ Regex(_) => {},
+ String(_) => {}
+ List(l) => {
+ for val in l.iter() {
+ val.can_hash()?;
+ }
+ }
+ Matrix(m) => {
+ for val in m.values.iter() {
+ val.can_hash()?;
+ }
+ },
+ _ => return error!("cannot hash {self:?}")
+ }
+ Ok(())
+ }
+
+ pub fn is_zero(&self) -> bool {
+ use Value as V;
+ match self {
+ V::Int(i) => *i == 0,
+ V::Float(f) => *f == 0.0 || *f == -0.0,
+ V::Ratio(r) => *r.numer() == 0,
+ _ => false,
+ }
+ }
+}
+
+
+impl Value {
+ pub fn val_cmp(&self, other: &Self) -> Result<Ordering> {
+ self.partial_cmp(other)
+ .map_or_else(|| error!("cannot compare: {self:?} and {other:?}"), Ok)
+ }
+
+ fn index_single(&self, index: &Value) -> Result<Self> {
+ use Value as V;
+ match (self, index) {
+ (V::Table(t), index) => {
+ Ok(t.get(index)?.unwrap_or(&Value::Nil).clone())
+ },
+ (V::List(l), V::Int(i)) => {
+ if *i < 0 || *i as usize >= l.len() {
+ return error!("{i} out of bounds for {self:?}")
+ }
+ Ok(l[*i as usize].clone())
+ },
+ (V::Matrix(m), V::Int(i)) => {
+ if *i < 0 || *i as usize >= m.values.len() {
+ return error!("{i} out of bounds for {self:?}")
+ }
+ Ok(m.values[*i as usize].clone())
+ },
+ _ => return error!("{index:?} cant index {self:?}")
+ }
+ }
+
+ fn index_multiple(&self, indexes: &Vec<Value>) -> Result<Self> {
+ use Value as V;
+ match self {
+ V::List(..) => {
+ let mut ret = Vec::new();
+ for index in indexes {
+ let res = self.index_single(index)?;
+ ret.push(res);
+ }
+ Ok(V::List(ret.into()))
+ }
+ V::Table(..) => {
+ let mut ret = ValueMap::new();
+ for index in indexes {
+ let res = self.index_single(index)?;
+ ret.insert(index.clone(), res)?;
+ }
+ Ok(V::Table(ret.into()))
+ }
+ V::Matrix(m) => {
+ let err = || error!("{self:?} can be index by [Int] or [Int;Int]");
+ if indexes.len() != 2 {
+ return err()
+ }
+ let lhs = indexes[0].clone();
+ let rhs = indexes[1].clone();
+ match (lhs, rhs) {
+ (V::Nil, V::Nil) => {
+ Ok(V::Matrix(m.clone_inside()))
+ },
+ (V::Int(row), V::Nil) => {
+ let Some((_, row)) = m.rows().enumerate().filter(|(idx, _)| *idx as i64 == row).next() else {
+ return err();
+ };
+ let row: Vec<Value> = row.into_iter().map(|e| e.clone()).collect();
+ Ok(V::Matrix(Matrix::new(row.len(), 1, row).into()))
+ },
+ (V::Nil, V::Int(col)) => {
+ let Some((_, col)) = m.cols().enumerate().filter(|(idx, _)| *idx as i64 == col).next() else {
+ return err();
+ };
+ let col: Vec<Value> = col.into_iter().map(|e| e.clone()).collect();
+ Ok(V::Matrix(Matrix::new(1, col.len(), col).into()))
+ },
+ (V::Int(row), V::Int(col)) => {
+ if row < 0 || col < 0 {
+ return err();
+ }
+ m.get(row as usize, col as usize)
+ }
+ _ => return err()
+ }
+ }
+ _ => return error!("cannot index {self:?}")
+ }
+ }
+
+ pub fn index(&self, index: &Vec<Value>) -> Result<Self> {
+ if index.len() == 0 {
+ Ok(self.clone_inside())
+ } else if index.len() == 1 {
+ self.index_single(&index[0])
+ } else {
+ self.index_multiple(index)
+ }
+ }
+
+ fn store_index_single(&mut self, index: &Value, store: Value) -> Result<()> {
+ use Value as V;
+ let err = format!("{self:?}");
+ match (self, index) {
+ (V::Table(t), index) => {
+ t.insert(index.clone(), store)
+ },
+ (V::List(l), V::Int(i)) => {
+ if *i < 0 || *i as usize >= l.len() {
+ return error!("{i} out of bounds for {err}")
+ }
+ l[*i as usize] = store;
+ Ok(())
+ },
+ (V::Matrix(m), V::Int(i)) => {
+ if *i < 0 || *i as usize >= m.values.len() {
+ return error!("{i} out of bounds for {err}")
+ }
+ m.values[*i as usize] = store;
+ Ok(())
+ },
+ _ => return error!("{index:?} cant index {err}")
+ }
+ }
+
+ fn store_index_multiple(&mut self, indexes: &Vec<Value>, store: Value) -> Result<()> {
+ use Value as V;
+ match self {
+ V::List(..) => {
+ for index in indexes {
+ self.store_index_single(index, store.clone())?;
+ }
+ Ok(())
+ }
+ V::Table(..) => {
+ for index in indexes {
+ self.store_index_single(index, store.clone())?;
+ }
+ Ok(())
+ }
+ _ => return error!("cannot index {self:?}")
+ }
+ }
+
+ pub fn store_index(&mut self, index: &Vec<Value>, store: Value) -> Result<()> {
+ if index.len() == 0 {
+ Ok(())
+ } else if index.len() == 1 {
+ self.store_index_single(&index[0], store)
+ } else {
+ self.store_index_multiple(index, store)
+ }
+ }
+
+ pub fn store_field_access(&mut self, ident: &str, val: Value) -> Result<()> {
+ use Value as V;
+ match self {
+ V::Table(t) => {
+ let key = V::String(Rc::from(ident));
+ Ok(t.insert(key, val)?)
+ },
+ _ => return error!("cannot field access assign {self:?}")
+ }
+ }
+
+ pub fn field_access(&self, ident: &str) -> Result<Self> {
+ use Value as V;
+ match self {
+ V::Table(t) => {
+ let key = V::String(Rc::from(ident));
+ Ok(t.get(&key)?.unwrap_or(&V::Nil).clone())
+ },
+ _ => return error!("cannot field access {self:?}")
+ }
+ }
+
+ pub fn binary_op(op: BinaryOp, lhs: Value, rhs: Value) -> Result<Self> {
+ use BinaryOp::*;
+ match op {
+ Add => lhs + rhs,
+ Subtract => lhs - rhs,
+ Multiply => lhs * rhs,
+ Divide => lhs / rhs,
+ Modulo => lhs.modulo(rhs),
+ Power => lhs.pow(rhs),
+ BitwiseAnd => lhs & rhs,
+ BitwiseOr => lhs | rhs,
+ BitwiseXor => lhs ^ rhs,
+ BitwiseShiftLeft => lhs << rhs,
+ BitwiseShiftRight => lhs >> rhs,
+ Equals => Ok(Self::Bool(lhs == rhs)),
+ NotEquals => Ok(Self::Bool(lhs != rhs)),
+ GreaterEquals => Ok(Self::Bool(lhs >= rhs)),
+ LessEquals => Ok(Self::Bool(lhs <= rhs)),
+ GreaterThan => Ok(Self::Bool(lhs > rhs)),
+ LessThan => Ok(Self::Bool(lhs < rhs)),
+ Range | RangeEq => {
+ let Value::Int(lhs) = lhs else {
+ return error!("range can only take [Int]'s")
+ };
+ let Value::Int(rhs) = rhs else {
+ return error!("range can only take [Int]'s")
+ };
+ Ok(Self::Range(Rc::new((lhs, rhs, op == RangeEq))))
+ },
+ }
+ }
+
+ pub fn unary_op(op: UnaryOp, val: Value) -> Value {
+ use UnaryOp::*;
+ match op {
+ Negate => -val,
+ Not => Self::Bool(!val),
+ }
+ }
+}
+
+impl Neg for Value {
+ type Output = Value;
+
+ fn neg(self) -> Self::Output {
+ use Value::*;
+ match self {
+ Bool(b) => Bool(!b),
+ Int(i) => Int(-i),
+ Float(f) => Float(-f),
+ Ratio(r) => Ratio(-r),
+ Complex(c) => Complex(-c),
+ _ => return Float(f64::NAN)
+ }
+ }
+}
+
+impl Not for Value {
+ type Output = bool;
+
+ fn not(self) -> Self::Output {
+ use Value as V;
+ match self {
+ V::Nil => true,
+ V::Bool(b) => !b,
+ V::Int(i) => i == 0,
+ V::Float(f) => f == 0.0,
+ V::Ratio(r) => *(r.numer()) == 0 || *(r.denom()) == 0,
+ V::Complex(c) => !c.is_normal(),
+ V::Regex(_) => false,
+ V::List(_) => false,
+ V::Matrix(_) => false,
+ V::Table(_) => false,
+ V::String(s) => s.as_ref() == "",
+ V::Function(_) => false,
+ V::Iter(_) => false,
+ V::Range(_) => false,
+ V::File(_) => false,
+ V::Error(_) => false,
+ }
+ }
+}
+
+impl Value {
+
+ pub fn into_iter_fn(self) -> Result<Rc<Function>> {
+ let Value::Iter(iter) = self.into_iter()? else {
+ return error!("bypassed iter check")
+ };
+ Ok(iter)
+ }
+
+ pub fn into_iter(self) -> Result<Self> {
+ use Value as V;
+ Ok(match self {
+ V::Iter(..) => self,
+ V::List(l) => {
+ let iter = RefCell::new(l.into_inner().into_iter());
+ iter!(move |_,_| {
+ match iter.borrow_mut().next() {
+ Some(v) => Ok(v),
+ None => Ok(V::Nil),
+ }
+ })
+ },
+ V::Range(r) => {
+ let r = (*r).clone();
+ let lhs = RefCell::new(r.0);
+ let rhs = r.1;
+ iter!(move |_,_| {
+ let val = *lhs.borrow();
+ let next = *lhs.borrow() + 1;
+ if (!r.2 && *lhs.borrow() < rhs) || (r.2 && *lhs.borrow() <= rhs) {
+ *lhs.borrow_mut() = next;
+ return Ok(Value::Int(val))
+ }
+ Ok(Value::Nil)
+ })
+ },
+ V::Function(f) => {
+ if f.arity > 0 || f.variadic {
+ return error!("iterator functions cannot be varadic or take arguments")
+ }
+ V::Iter(f)
+ },
+ val => return error!("cannot turn {val:?} into an iterator")
+ })
+ }
+}
+
+fn dot(lhs: Vec<&Value>, rhs: Vec<&Value>) -> Result<Value> {
+ let len = lhs.len();
+
+ let mut res = Value::Int(0);
+ for i in 0..len {
+ let val = (lhs[i].clone() * rhs[i].clone())?;
+ res = (res + val)?;
+ }
+
+ Ok(res)
+}
diff --git a/matrix/src/vm.rs b/matrix-lang/src/vm.rs
index e511adf..bac6341 100644
--- a/matrix/src/vm.rs
+++ b/matrix-lang/src/vm.rs
@@ -1,33 +1,6 @@
-use std::{rc::Rc, fmt::{Debug, Display}, usize, ops::{Index, IndexMut}, collections::HashMap, cell::RefCell, sync::{atomic::{AtomicUsize, Ordering}, Arc}};
-use crate::{value::{Value, self, ValueMap, Matrix}, Result, gc::Gc, chunk::{Function, Instruction, Chunk, InnerFunction}, compiler::NamesTable, native};
+use std::{fmt::Display, ops::{IndexMut, Index}, sync::{atomic::{AtomicUsize, Ordering}, Arc}, collections::HashMap};
+use crate::prelude::*;
-#[derive(Debug)]
-pub enum Error {
- ValueError(value::Error),
- NotFunction(Value),
- InvArity(usize, usize, Rc<str>),
- ExecNative,
-}
-
-impl std::error::Error for Error {}
-
-impl Display for Error {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- use Error::*;
- match self {
- ValueError(err) => write!(f, "{err}"),
- NotFunction(v) => write!(f, "{v:?} is not a function"),
- InvArity(wanted, had, name) => write!(f, "function {name} takes {wanted} params but was given {had}"),
- ExecNative => write!(f, "cannot execute a native function"),
- }
- }
-}
-
-impl From<value::Error> for Error {
- fn from(value: value::Error) -> Self {
- Error::ValueError(value)
- }
-}
pub struct Stack<T> {
inner: Vec<T>
}
@@ -99,12 +72,12 @@ pub struct StackFrame {
}
struct VmError {
- err: crate::Error,
+ err: Exception,
frames: Vec<StackFrame>
}
-impl From<crate::Error> for VmError {
- fn from(err: crate::Error) -> Self {
+impl From<Exception> for VmError {
+ fn from(err: Exception) -> Self {
VmError {
err,
frames: Vec::new()
@@ -112,15 +85,6 @@ impl From<crate::Error> for VmError {
}
}
-impl From<self::Error> for VmError {
- fn from(err: self::Error) -> Self {
- VmError {
- err: err.into(),
- frames: Vec::new()
- }
- }
-}
-
type VmResult<T> = std::result::Result<T, VmError>;
impl StackFrame {
@@ -166,10 +130,16 @@ pub struct Vm {
trystack: Vec<TryScope>,
globals: Rc<RefCell<HashMap<u16, Value>>>,
names: NamesTable,
- global_names: NamesTable,
+ global_names: GlobalsTable,
interupt: Arc<AtomicUsize>,
}
+macro_rules! error {
+ ($($arg:tt)*) => {
+ exception!(RUNTIME_EXCEPTION, $($arg)*)
+ };
+}
+
impl Vm {
fn push(&mut self, val: Value) {
@@ -188,7 +158,7 @@ impl Vm {
self.names.clone()
}
- pub fn global_names(&self) -> NamesTable {
+ pub fn globals(&self) -> GlobalsTable {
self.global_names.clone()
}
@@ -210,7 +180,7 @@ impl Vm {
pub fn load_global(&mut self, value: Value, name: &str) {
let idx = self.global_names.borrow().len();
- self.global_names.borrow_mut().push(name.into());
+ self.global_names.borrow_mut().push(Global { idx, name: Rc::from(name), is_const: true });
self.globals.borrow_mut().insert(idx as u16, value);
}
@@ -220,7 +190,7 @@ impl Vm {
fn init_frame(&mut self, fun: Rc<Function>) -> Result<StackFrame> {
let InnerFunction::Compiled(ref compiled_chunk) = fun.fun else {
- return Err(self::Error::ExecNative.into())
+ return Err(error!("cannot create stack frame on builtin function"))
};
Ok(StackFrame::new(
self,
@@ -240,7 +210,7 @@ impl Vm {
match ins {
I::NoOp => {},
I::CreateLocal => self.locals.push(self.stack.pop()),
- I::LoadLocal(idx) => {self.stack.push(self.locals[idx as usize].clone());},
+ I::LoadLocal(idx) => {self.stack.push(self.locals[frame.lp + idx as usize].clone());},
I::StoreLocal(idx) => self.locals[frame.bp + idx as usize] = self.pop(),
I::DiscardLocals(count) => {self.locals.truncate(self.locals.len() - count as usize)},
I::LoadGlobal(idx) => {
@@ -325,11 +295,12 @@ impl Vm {
let fun = self.pop();
let Value::Function(fun) = fun else {
- return Err(Error::NotFunction(fun).into())
+ Err(error!("cannot call {fun} (not a function)"))?
};
if !fun.variadic && arity > fun.arity {
- return Err(Error::InvArity(fun.arity, arity as usize, fun.name.clone()).into())
+ let needs = fun.arity;
+ Err(error!("function {fun} takes {needs} args, given {arity}"))?
}
let mut params = self.stack.split_off(self.stack.len() - arity).inner;
@@ -416,14 +387,14 @@ impl Vm {
Ok(None)
}
- fn stack_trace(&mut self, frames: Vec<StackFrame>) -> String {
- let mut trace = String::from("\x1b[33m\x1b[1mStack Trace:\x1b[0m\n");
+ fn stack_trace(&mut self, frames: Vec<StackFrame>, e: Exception) -> Exception {
+ let mut e = e;
for frame in frames {
let ip = frame.ip - 1;
let pos = frame.body.pos[ip];
- trace.push_str(&format!(" {} at {}:{}\n", &frame.name, pos.row, pos.col));
+ e = e.trace(frame.name, pos);
}
- trace
+ e
}
fn exec_fn(&mut self, frame: &mut StackFrame, fun: Rc<Function>, params: Vec<Value>) -> VmResult<Value> {
@@ -438,7 +409,8 @@ impl Vm {
self.stack.push(param);
}
let mut new_frame = StackFrame::new(self, name, body.clone(), params_len, frame.depth + 1);
- self.exec(&mut new_frame)
+ let res = self.exec(&mut new_frame);
+ res
},
InnerFunction::Native(native_fun) => {
Ok(native_fun((self, frame), params)?)
@@ -462,7 +434,7 @@ impl Vm {
self.stack.truncate(catch.stack_len);
self.locals.truncate(catch.locals_len);
frame.ip = catch.err_idx;
- self.stack.push(Value::Error(err.err));
+ self.stack.push(Value::Exception(err.err));
} else {
let mut err = err;
err.frames.push(frame.clone());
@@ -478,13 +450,12 @@ impl Vm {
}
pub fn run(&mut self, fun: Rc<Function>) -> Result<Value> {
- let mut frame = self.init_frame(fun)?;
self.interupt.store(0, Ordering::SeqCst);
self.stack = Stack::new();
self.locals = Stack::new();
+ let mut frame = self.init_frame(fun)?;
self.exec(&mut frame).map_err(|e| {
- let trace = self.stack_trace(e.frames);
- (e.err, trace).into()
+ self.stack_trace(e.frames, e.err)
})
}
}