summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock144
-rw-r--r--Cargo.toml2
-rw-r--r--matrix-bin/Cargo.lock (renamed from matrix-repl/Cargo.lock)0
-rw-r--r--matrix-bin/Cargo.toml13
-rw-r--r--matrix-bin/src/main.rs104
-rw-r--r--matrix-bin/src/repl.rs25
-rw-r--r--matrix-repl/Cargo.toml10
-rw-r--r--matrix-repl/src/main.rs18
-rw-r--r--matrix/src/ast.rs431
-rw-r--r--matrix/src/chunk.rs116
-rw-r--r--matrix/src/compiler.rs546
-rw-r--r--matrix/src/gc.rs112
-rw-r--r--matrix/src/lex.rs114
-rw-r--r--matrix/src/lib.rs56
-rw-r--r--matrix/src/parse.rs436
-rw-r--r--matrix/src/value.rs610
-rw-r--r--matrix/src/vm.rs242
17 files changed, 2646 insertions, 333 deletions
diff --git a/Cargo.lock b/Cargo.lock
index f61fea6..30ca9f6 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -12,6 +12,54 @@ dependencies = [
]
[[package]]
+name = "anstream"
+version = "0.6.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
+dependencies = [
+ "anstyle",
+ "windows-sys",
+]
+
+[[package]]
name = "anyhow"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -36,6 +84,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
+name = "clap"
+version = "4.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
+
+[[package]]
name = "clipboard-win"
version = "5.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -45,6 +133,12 @@ dependencies = [
]
[[package]]
+name = "colorchoice"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
+
+[[package]]
name = "endian-type"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -78,6 +172,12 @@ dependencies = [
]
[[package]]
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+
+[[package]]
name = "home"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -115,9 +215,10 @@ dependencies = [
]
[[package]]
-name = "matrix-repl"
+name = "matrix-bin"
version = "0.1.0"
dependencies = [
+ "clap",
"matrix",
"rustyline",
]
@@ -199,6 +300,24 @@ dependencies = [
]
[[package]]
+name = "proc-macro2"
+version = "1.0.78"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
name = "radix_trie"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -279,6 +398,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
[[package]]
+name = "strsim"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01"
+
+[[package]]
+name = "syn"
+version = "2.0.48"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
name = "unicode-segmentation"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index cc3bc3f..f489827 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,3 +1,3 @@
[workspace]
resolver = "2"
-members = [ "matrix", "matrix-repl" ]
+members = [ "matrix", "matrix-bin" ]
diff --git a/matrix-repl/Cargo.lock b/matrix-bin/Cargo.lock
index f61fea6..f61fea6 100644
--- a/matrix-repl/Cargo.lock
+++ b/matrix-bin/Cargo.lock
diff --git a/matrix-bin/Cargo.toml b/matrix-bin/Cargo.toml
new file mode 100644
index 0000000..029923b
--- /dev/null
+++ b/matrix-bin/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "matrix-bin"
+version = "0.1.0"
+edition = "2021"
+
+[[bin]]
+name = "matrix"
+path = "src/main.rs"
+
+[dependencies]
+clap = { version = "4", features = [ "derive" ] }
+matrix = { path = "../matrix" }
+rustyline = "13"
diff --git a/matrix-bin/src/main.rs b/matrix-bin/src/main.rs
new file mode 100644
index 0000000..7e3c3c6
--- /dev/null
+++ b/matrix-bin/src/main.rs
@@ -0,0 +1,104 @@
+use std::{path::PathBuf, io::{self, Read, IsTerminal}, fs};
+use clap::Parser as ArgParser;
+use matrix::{compiler::{Compiler, CompilerBuilder}, vm::Vm, parse::{Parser, ParserBuilder}};
+use repl::Repl;
+
+mod repl;
+
+#[derive(Debug, ArgParser)]
+#[command(version, long_about = None)]
+pub struct Args {
+ /// A path to a input program. Uses stdin if not specified.
+ file: Option<PathBuf>,
+
+ /// Runs a repl, loading the provided program first
+ #[arg(short, long)]
+ repl: bool,
+
+ /// Print out debug information
+ #[arg(short, long)]
+ debug: bool,
+
+ /// Disables optimizations
+ #[arg(long)]
+ disable_optimizations: bool,
+}
+
+pub struct State<'a> {
+ parser: Parser,
+ compiler: Compiler<'a>,
+ vm: Vm,
+ repl: bool
+}
+
+impl<'a> State<'a> {
+ pub fn new (args: Args) -> (Self, Option<String>) {
+
+ let stdin = read_stdin();
+
+ let file;
+ if let Some(path) = &args.file {
+ file = Some(fs::read_to_string(path).unwrap());
+ } else if stdin.len() > 0 {
+ file = Some(stdin);
+ } else {
+ file = None;
+ }
+
+ let repl = args.repl || file.is_none();
+
+ let parser = ParserBuilder::new()
+ .optimize(!args.disable_optimizations)
+ .build();
+ let vm = Vm::new();
+ let compiler = CompilerBuilder::new()
+ .repl(repl)
+ .debug(args.debug)
+ .globals(vm.globals())
+ .build();
+
+ (Self { parser, vm, compiler, repl }, file)
+ }
+
+ pub fn execute(&mut self, code: String) -> matrix::Result<()> {
+ let ast = self.parser.parse(code)?;
+ let fun = self.compiler.compile(&ast)?;
+ let val = self.vm.run(fun)?;
+ if self.repl {
+ println!("{val}");
+ }
+ Ok(())
+ }
+
+}
+
+pub fn error(err: matrix::Error) {
+ println!("\x1b[31m\x1b[1mError:\x1b[0m {err}");
+}
+
+fn read_stdin() -> String {
+ let mut buffer = String::new();
+ let mut stdin = io::stdin();
+ if stdin.is_terminal() {
+ return String::new();
+ }
+ stdin.read_to_string(&mut buffer).unwrap();
+ buffer
+}
+
+fn main() {
+
+ let args = Args::parse();
+ let (mut state, file) = State::new(args);
+
+ if let Some(file) = file {
+ if let Err(err) = state.execute(file) {
+ error(err);
+ }
+ }
+
+ if state.repl {
+ Repl::new(state).run();
+ }
+
+}
diff --git a/matrix-bin/src/repl.rs b/matrix-bin/src/repl.rs
new file mode 100644
index 0000000..81fd289
--- /dev/null
+++ b/matrix-bin/src/repl.rs
@@ -0,0 +1,25 @@
+use crate::State;
+
+pub struct Repl<'a> {
+ state: State<'a>
+}
+
+impl<'a> Repl<'a> {
+
+ pub fn new(state: State<'a>) -> Self {
+ Self { state }
+ }
+
+ pub fn run(&mut self) {
+ let mut rl = rustyline::DefaultEditor::new().unwrap();
+ loop {
+ let Ok(line) = rl.readline(">> ") else {
+ break;
+ };
+ if let Err(err) = self.state.execute(line) {
+ crate::error(err);
+ }
+ }
+ }
+
+}
diff --git a/matrix-repl/Cargo.toml b/matrix-repl/Cargo.toml
deleted file mode 100644
index f1ec1d3..0000000
--- a/matrix-repl/Cargo.toml
+++ /dev/null
@@ -1,10 +0,0 @@
-[package]
-name = "matrix-repl"
-version = "0.1.0"
-edition = "2021"
-
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
-[dependencies]
-matrix = { path = "../matrix" }
-rustyline = "13"
diff --git a/matrix-repl/src/main.rs b/matrix-repl/src/main.rs
deleted file mode 100644
index 9c3d5f6..0000000
--- a/matrix-repl/src/main.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-use matrix::parse::Parser;
-
-fn main() {
-
- let mut rl = rustyline::DefaultEditor::new().unwrap();
-
- loop {
- let Ok(line) = rl.readline(">> ") else {
- break;
- };
- let ast = Parser::parse(line);
- match ast {
- Ok(ast) => println!("{ast:?}"),
- Err(err) => println!("{err}")
- }
- }
-
-}
diff --git a/matrix/src/ast.rs b/matrix/src/ast.rs
index b4d2c24..9a0dfaa 100644
--- a/matrix/src/ast.rs
+++ b/matrix/src/ast.rs
@@ -1,41 +1,7 @@
-use std::{rc::Rc, collections::HashMap};
+use std::{rc::Rc, ops::{Neg, Not}};
+use crate::{lex::Token, value::{Value, InlineList, InlineMatrix, InlineTable}, Result};
-use regex::Regex;
-use num_rational::Rational64;
-use num_complex::Complex64;
-
-use crate::lex::Token;
-
-pub type List = Vec<Value>;
-pub type Matrix = (u16, u16, Vec<Value>);
-pub type Table = HashMap<Rc<str>, Value>;
-
-pub type InlineList = Vec<Expr>;
-pub type InlineMatrix = (u16, u16, Vec<Expr>);
-pub type InlineTable = Vec<(Expr, Expr)>;
-
-pub type RcList = Rc<List>;
-pub type RcMatrix = Rc<Matrix>;
-pub type RcString = Rc<str>;
-pub type RcTable = Rc<Table>;
-pub type RcRegex = Rc<Regex>;
-
-#[derive(Debug)]
-pub enum Value {
- Nil,
- Bool(bool),
- Int(i64),
- Float(f64),
- Ratio(Rational64),
- Complex(Complex64),
- Regex(RcRegex),
- String(RcString),
- List(RcList),
- Matrix(RcMatrix),
- Table(RcTable),
-}
-
-#[derive(Debug)]
+#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum UnaryOp {
// normal math
Negate,
@@ -43,20 +9,7 @@ pub enum UnaryOp {
Not,
}
-impl TryFrom<Token> for UnaryOp {
- type Error = ();
-
- fn try_from(value: Token) -> Result<Self, Self::Error> {
- use Token::*;
- Ok(match value {
- Subtract => Self::Negate,
- Not => Self::Not,
- _ => return Err(())
- })
- }
-}
-
-#[derive(Debug)]
+#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum BinaryOp {
// normal math
Add,
@@ -78,44 +31,10 @@ pub enum BinaryOp {
LessEquals,
GreaterThan,
LessThan,
- And,
- Or,
- // assignment
- Assign
-}
-
-impl TryFrom<Token> for BinaryOp {
- type Error = ();
-
- fn try_from(value: Token) -> Result<Self, Self::Error> {
- use Token::*;
- Ok(match value {
- Equal => BinaryOp::Equals,
- NotEqual => Self::Equals,
- GreaterEqual => Self::GreaterEquals,
- LessEqual => Self::LessEquals,
- GreaterThan => Self::GreaterThan,
- LessThan => Self::LessThan,
- And => Self::And,
- Or => Self::Or,
- BitwiseShiftLeft => Self::BitwiseShiftLeft,
- BitwiseShiftRight => Self::BitwiseShiftRight,
- BitwiseAnd => Self::BitwiseAnd,
- BitwiseOr => Self::BitwiseOr,
- BitwiseXor => Self::BitwiseXor,
- Add => Self::Add,
- Subtract => Self::Subtract,
- Multiply => Self::Multiply,
- Divide => Self::Divide,
- Modulo => Self::Modulo,
- Power => Self::Power,
- Assign => Self::Assign,
- _ => return Err(())
- })
- }
+ Range,
}
-#[derive(Debug)]
+#[derive(Debug, Clone, PartialEq)]
pub enum Expr {
Literal(Value),
Ident(Rc<str>),
@@ -123,40 +42,344 @@ pub enum Expr {
UnaryOp(Box<Expr>, UnaryOp),
BinaryOp(Box<Expr>, Box<Expr>, BinaryOp),
- Let(Rc<str>, Box<Expr>),
-
Index(Box<Expr>, Vec<Expr>),
FnCall(Box<Expr>, Vec<Expr>),
+ FieldAccess(Box<Expr>, Box<Expr>),
List(InlineList),
Matrix(InlineMatrix),
Table(InlineTable),
- Block(Vec<Expr>),
- Function(Rc<str>, Vec<Rc<str>>, Vec<Expr>),
+ And(Box<Expr>, Box<Expr>),
+ Or(Box<Expr>, Box<Expr>),
+}
+
+#[derive(Debug, Clone, PartialEq)]
+pub enum Assign {
+ Expr(Expr),
+ Assign(Expr, Box<Assign>)
+}
+
+#[derive(Debug, Clone, PartialEq)]
+pub enum Stmt {
+ NoOp,
+
+ If(Expr, Box<Stmt>, Option<Box<Stmt>>),
+ Function(Rc<str>, Vec<Rc<str>>, Box<Stmt>),
+
+ Loop(Box<Stmt>),
+ While(Expr, Box<Stmt>),
+ DoWhile(Expr, Box<Stmt>),
+
+ Block(Vec<Stmt>),
+ Expr(Expr),
+
+ Let(Rc<str>, Expr),
+ Assign(Assign),
Continue,
Break,
- Return(Box<Expr>),
+ Return(Expr),
+}
- If(Box<Expr>, Box<Expr>, Option<Box<Expr>>),
- Loop(Vec<Expr>),
- While(Box<Expr>, Vec<Expr>),
- DoWhile(Box<Expr>, Vec<Expr>)
+impl Neg for BinaryOp {
+ type Output = Option<Self>;
+ fn neg(self) -> Self::Output {
+ use BinaryOp::*;
+ Some(match self {
+ Add => Subtract,
+ Subtract => Add,
+ _ => return None
+ })
+ }
}
-impl Expr {
- pub fn optimize(self) -> Self {
+impl Not for BinaryOp {
+ type Output = Option<Self>;
+ fn not(self) -> Self::Output {
+ use BinaryOp::*;
+ Some(match self {
+ Equals => NotEquals,
+ NotEquals => Equals,
+ GreaterEquals => LessThan,
+ LessEquals => GreaterThan,
+ GreaterThan => LessEquals,
+ LessThan => GreaterEquals,
+ _ => return None
+ })
+ }
+}
+
+impl Neg for Expr {
+ type Output = std::result::Result<Self, Self>;
+ fn neg(self) -> Self::Output {
use Expr::*;
- match self {
- Block(mut block) => {
- if block.len() == 1 {
- block.pop().unwrap().optimize()
+ Ok(match self {
+ Literal(v) => Literal(-v),
+ BinaryOp(lhs, rhs, op) => {
+ let Some(op_n) = -op else {
+ return Err(BinaryOp(lhs, rhs, op));
+ };
+ if let Ok(lhs) = -*lhs.clone() {
+ BinaryOp(Box::new(lhs), rhs, op_n)
+ } else if let Ok(rhs) = -*rhs.clone() {
+ BinaryOp(lhs, Box::new(rhs), op_n)
} else {
- Block(block.into_iter().map(|e| e.optimize()).collect())
+ return Err(BinaryOp(lhs, rhs, op))
+ }
+ },
+ UnaryOp(expr, op) => {
+ match op {
+ self::UnaryOp::Negate => *expr,
+ _ => return Err(UnaryOp(expr, op))
+ }
+ }
+ _ => return Err(self)
+ })
+ }
+}
+
+impl Not for Expr {
+ type Output = std::result::Result<Self, Self>;
+ fn not(self) -> Self::Output {
+ use Expr::*;
+ Ok(match self {
+ Literal(v) => Literal(Value::Bool(!v)),
+ UnaryOp(expr, op) => {
+ match op {
+ self::UnaryOp::Not => *expr,
+ _ => return Err(UnaryOp(expr, op))
}
}
- _ => self
+ _ => return Err(self)
+ })
+ }
+}
+
+fn optimize_expr(expr: Expr) -> Result<Expr> {
+ use Expr::*;
+ Ok(match expr {
+ UnaryOp(expr, op) => {
+ let expr = optimize_expr(*expr)?;
+ match match op {
+ self::UnaryOp::Negate => -expr,
+ self::UnaryOp::Not => !expr,
+ } {
+ Ok(expr) => expr,
+ Err(expr) => UnaryOp(Box::new(expr), op)
+ }
+ },
+ BinaryOp(lhs, rhs, op) => {
+ let lhs = optimize_expr(*lhs)?;
+ let rhs = optimize_expr(*rhs)?;
+ if let (Literal(l), Literal(r)) = (lhs.clone(), rhs.clone()) {
+ match Value::binary_op(op, l, r) {
+ Err(err) => return Err(err),
+ Ok(value) => return Ok(Expr::Literal(value)),
+ }
+ }
+ BinaryOp(Box::new(lhs), Box::new(rhs), op)
+ },
+ FnCall(ident, values) => {
+ FnCall(ident, values
+ .into_iter()
+ .map(optimize_expr)
+ .collect::<Result<Vec<Expr>>>()?)
+ }
+ FieldAccess(key, val) => {
+ let key = optimize_expr(*key)?;
+ let val = optimize_expr(*val)?;
+ FieldAccess(Box::new(key), Box::new(val))
+ },
+ List(list) =>
+ List(list.into_iter()
+ .map(optimize_expr)
+ .collect::<Result<Vec<Expr>>>()?),
+ Matrix(mat) =>
+ Matrix((mat.0, mat.1,
+ mat.2.into_iter().map(optimize_expr)
+ .collect::<Result<Vec<Expr>>>()?)),
+ Table(table) =>
+ Table(table
+ .into_iter()
+ .map(|(k, v)| {
+ let k = optimize_expr(k)?;
+ let v = optimize_expr(v)?;
+ Ok((k, v))
+ }).collect::<Result<Vec<(Expr, Expr)>>>()?),
+ And(lhs, rhs) => {
+ let lhs = optimize_expr(*lhs)?;
+ let rhs = optimize_expr(*rhs)?;
+ if let (Literal(l), r) = (lhs.clone(), rhs.clone()) {
+ match !!l.clone() {
+ true => r,
+ false => Literal(l),
+ }
+ } else {
+ And(Box::new(lhs), Box::new(rhs))
+ }
+ },
+ Or(lhs, rhs) => {
+ let lhs = optimize_expr(*lhs)?;
+ let rhs = optimize_expr(*rhs)?;
+ if let (Literal(l), r) = (lhs.clone(), rhs.clone()) {
+ match !l.clone() {
+ true => r,
+ false => Literal(l),
+ }
+ } else {
+ And(Box::new(lhs), Box::new(rhs))
+ }
+ },
+ _ => expr
+ })
+}
+
+fn optimize_assign(assign: Assign) -> Result<Assign> {
+ use self::Assign::*;
+ Ok(match assign {
+ Expr(expr) => {
+ let expr = optimize_expr(expr)?;
+ Expr(expr)
+ },
+ Assign(expr, assign) => {
+ let expr = optimize_expr(expr)?;
+ let assign = optimize_assign(*assign)?;
+ Assign(expr, Box::new(assign))
+ }
+ })
+}
+
+pub fn optimize(stmt: Stmt) -> Result<Stmt> {
+ use Stmt::*;
+ Ok(match stmt {
+ Block(b) => {
+ let b: Vec<Stmt> =
+ b.into_iter()
+ .map(optimize)
+ .collect::<Result<Vec<Stmt>>>()?
+ .into_iter()
+ .filter(|e| NoOp != *e)
+ .collect();
+ if b.is_empty() {
+ NoOp
+ } else {
+ Block(b)
+ }
+ },
+ If(cond, block, else_block) => {
+ let cond = optimize_expr(cond)?;
+ let block = optimize(*block)?;
+ let else_block = else_block.map(|e| optimize(*e)).transpose()?;
+ if let self::Expr::Literal(lit) = cond {
+ if !!lit {
+ return Ok(block)
+ }
+ return Ok(else_block.unwrap_or(NoOp))
+ }
+ If(cond, Box::new(block), else_block.map(|e| Box::new(e)))
+ },
+ While(cond, block) => {
+ let cond = optimize_expr(cond)?;
+ let block = optimize(*block)?;
+ if let self::Expr::Literal(lit) = cond {
+ if !!lit {
+ return Ok(Loop(Box::new(block)))
+ }
+ return Ok(NoOp)
+ }
+ While(cond, Box::new(block))
+ },
+ DoWhile(cond, block) => {
+ let cond = optimize_expr(cond)?;
+ let block = optimize(*block)?;
+ if let self::Expr::Literal(lit) = cond.clone() {
+ if !!lit {
+ return Ok(Loop(Box::new(block)))
+ }
+ }
+ DoWhile(cond, Box::new(block))
+ }
+ Loop(block) => {
+ let block = optimize(*block)?;
+ Loop(Box::new(block))
+ },
+ Function(ident, params, stmt) => {
+ let stmt = optimize(*stmt)?;
+ Function(ident, params, Box::new(stmt))
+ }
+ Expr(expr) => {
+ Expr(optimize_expr(expr)?)
+ },
+ Let(ident, expr) => {
+ Let(ident, optimize_expr(expr)?)
+ }
+ Assign(assign) => {
+ use self::Assign as A;
+ match assign {
+ A::Expr(expr) => {
+ let expr = optimize_expr(expr)?;
+ Expr(expr)
+ },
+ A::Assign(lhs, rhs) => {
+ let assign = optimize_assign(A::Assign(lhs, rhs))?;
+ Assign(assign)
+ },
+ }
+ }
+ Return(expr) => {
+ Return(optimize_expr(expr)?)
+ }
+ _ => stmt
+ })
+}
+
+impl From<Token> for UnaryOp {
+ fn from(value: Token) -> Self {
+ use Token::*;
+ match value {
+ Subtract => Self::Negate,
+ Not => Self::Not,
+ _ => panic!("aaaaa")
+ }
+ }
+}
+
+impl From<Token> for BinaryOp {
+ fn from(value: Token) -> Self {
+ use Token::*;
+ match value {
+ Equal => Self::Equals,
+ NotEqual => Self::NotEquals,
+ GreaterEqual => Self::GreaterEquals,
+ LessEqual => Self::LessEquals,
+ GreaterThan => Self::GreaterThan,
+ LessThan => Self::LessThan,
+ BitwiseShiftLeft => Self::BitwiseShiftLeft,
+ BitwiseShiftRight => Self::BitwiseShiftRight,
+ BitwiseAnd => Self::BitwiseAnd,
+ BitwiseOr => Self::BitwiseOr,
+ BitwiseXor => Self::BitwiseXor,
+ Add => Self::Add,
+ Subtract => Self::Subtract,
+ Multiply => Self::Multiply,
+ Divide => Self::Divide,
+ Modulo => Self::Modulo,
+ Power => Self::Power,
+ Range => Self::Range,
+ _ => panic!("aaaaa")
+ }
+ }
+}
+
+impl Expr {
+ pub fn is_assignable(&self) -> bool {
+ use Expr::*;
+ match self {
+ Ident(_) => true,
+ Index(_, _) => true,
+ FieldAccess(_, _) => true,
+ _ => false,
}
}
}
diff --git a/matrix/src/chunk.rs b/matrix/src/chunk.rs
new file mode 100644
index 0000000..d3d4bc8
--- /dev/null
+++ b/matrix/src/chunk.rs
@@ -0,0 +1,116 @@
+use crate::{value::Value, ast::{UnaryOp, BinaryOp}, Result};
+use std::{fmt::{Debug, Display}, rc::Rc};
+
+#[derive(Clone)]
+pub struct Chunk {
+ pub constants: Vec<Value>,
+ pub code: Vec<Instruction>
+}
+
+impl Chunk {
+ pub fn new() -> Self {
+ Self {
+ constants: Vec::new(),
+ code: Vec::new()
+ }
+ }
+
+ pub fn from_compiled(_buf: &[u8]) -> Result<Self> {
+ todo!()
+ }
+}
+
+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(())
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct Function {
+ pub name: Rc<str>,
+ pub arity: usize,
+ pub body: Chunk
+}
+
+#[derive(Clone, Debug)]
+#[repr(align(4))]
+pub enum Instruction {
+ NoOp,
+
+ Load(u16),
+ Store(u16),
+ LoadGlobal(u16),
+ StoreGlobal(u16),
+
+ Const(u16),
+ Int(i16),
+ True,
+ False,
+ Nil,
+
+ Dup, Discard(u16),
+
+ UnaryOp(UnaryOp),
+ BinaryOp(BinaryOp),
+
+ NewList(u16),
+ NewTable(u16),
+ NewMatrix(u16, u8),
+
+ Index(u8),
+ StoreIndex(u8),
+
+ Jump(u16),
+ JumpTrue(u16),
+ JumpFalse(u16),
+
+ Call(u8),
+ Return,
+}
+
+impl Display for Instruction {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ use Instruction::*;
+ match self {
+ NoOp => write!(f, "noop"),
+ Load(idx) => write!(f, "load \x1b[33m{idx}\x1b[0m"),
+ Store(idx) => write!(f, "store \x1b[33m{idx}\x1b[0m"),
+ LoadGlobal(name) => write!(f, "load global \x1b[36m{name}\x1b[0m"),
+ StoreGlobal(name) => write!(f, "store global \x1b[36m{name}\x1b[0m"),
+ Const(idx) => write!(f, "const \x1b[33m{idx}\x1b[0m"),
+ Int(idx) => write!(f, "push \x1b[34m{idx}\x1b[0m"),
+ True => write!(f, "push \x1b[34mtrue\x1b[0m"),
+ False => write!(f, "push \x1b[34mfalse\x1b[0m"),
+ Nil => write!(f, "push \x1b[34mnil\x1b[0m"),
+ Dup => write!(f, "duplicate"),
+ Discard(count) => write!(f, "discard \x1b[33m{count}\x1b[0m"),
+ UnaryOp(op) => write!(f, "unary \x1b[32m{op:?}\x1b[0m"),
+ BinaryOp(op) => write!(f, "binary \x1b[32m{op:?}\x1b[0m"),
+ NewList(len) => write!(f, "list \x1b[35m{len}\x1b[0m"),
+ NewTable(len) => write!(f, "table \x1b[35m{len}\x1b[0m"),
+ NewMatrix(len, codomain) => write!(f, "matrix \x1b[35m{}\x1b[0mx\x1b[35m{}\x1b[0m", *len / (*codomain as u16), codomain),
+ Index(idx) => write!(f, "index \x1b[33m{idx}\x1b[0m"),
+ StoreIndex(idx) => write!(f, "store_index \x1b[33m{idx}\x1b[0m"),
+ Jump(idx) => write!(f, "jump \x1b[33m{idx}\x1b[0m"),
+ JumpTrue(idx) => write!(f, "jumpt \x1b[33m{idx}\x1b[0m"),
+ JumpFalse(idx) => write!(f, "jumpf \x1b[33m{idx}\x1b[0m"),
+ Call(arity) => write!(f, "call \x1b[35m{arity}\x1b[0m"),
+ Return => write!(f, "return"),
+ }
+ }
+}
diff --git a/matrix/src/compiler.rs b/matrix/src/compiler.rs
new file mode 100644
index 0000000..b535369
--- /dev/null
+++ b/matrix/src/compiler.rs
@@ -0,0 +1,546 @@
+use std::{fmt::Display, rc::Rc, cell::RefCell};
+use crate::{ast::{Stmt, Expr, Assign}, chunk::{Function, Instruction}, chunk::{Chunk, self}, value::Value, Result};
+
+pub type Globals = Rc<RefCell<Vec<Rc<str>>>>;
+
+pub struct CompilerBuilder<'c> {
+ globals: Globals,
+ repl: bool,
+ debug: bool,
+ name: Rc<str>,
+ parent: Option<&'c Compiler<'c>>
+}
+
+impl<'c> CompilerBuilder<'c> {
+
+ pub fn new() -> Self {
+ Self {
+ globals: Rc::new(RefCell::new(Vec::new())),
+ repl: false,
+ debug: false,
+ name: "<root>".into(),
+ parent: None,
+ }
+ }
+
+ pub fn repl(mut self, repl: bool) -> Self {
+ self.repl = repl;
+ self
+ }
+
+ pub fn debug(mut self, debug: bool) -> Self {
+ self.debug = debug;
+ self
+ }
+
+ pub fn globals(mut self, globals: Globals) -> Self {
+ self.globals = globals;
+ self
+ }
+
+ pub fn parent(mut self, parent: &'c Compiler) -> Self {
+ self.parent = Some(parent);
+ self
+ }
+
+ pub fn name(mut self, name: Rc<str>) -> Self {
+ self.name = name;
+ self
+ }
+
+ pub fn build(self) -> Compiler<'c> {
+ Compiler {
+ name: self.name,
+ parent: self.parent,
+ globals: self.globals,
+ repl: self.repl,
+ debug: self.debug,
+ scope: 0,
+ locals: Vec::new(),
+ chunk: Chunk::new(),
+ loop_top: Vec::new(),
+ loop_bot: Vec::new(),
+ root_is_block: false,
+ }
+ }
+}
+
+pub struct Compiler<'c> {
+ name: Rc<str>,
+ parent: Option<&'c Compiler<'c>>,
+
+ locals: Vec<Rc<Local>>,
+ globals: Rc<RefCell<Vec<Rc<str>>>>,
+
+ root_is_block: bool,
+
+ scope: usize,
+ chunk: Chunk,
+
+ loop_top: Vec<(usize, usize)>,
+ loop_bot: Vec<usize>,
+
+ repl: bool,
+ debug: bool,
+}
+
+struct Local {
+ name: Rc<str>,
+ idx: usize,
+ scope: usize
+}
+
+#[derive(Debug, Clone)]
+pub enum Error {
+ Undefined(Rc<str>),
+ Redefined(Rc<str>),
+ InvContinue,
+ InvBreak,
+}
+
+impl std::error::Error for self::Error {}
+
+impl Display for self::Error {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ use self::Error::*;
+ match self {
+ Undefined(name) => write!(f, "value {name} is undefined"),
+ Redefined(name) => write!(f, "cannot redefine {name} in the same scope"),
+ InvContinue => write!(f, "cannot continue outside a loop"),
+ InvBreak => write!(f, "cannot break outside a loop"),
+ }
+ }
+}
+
+impl<'c> Compiler<'c> {
+
+ fn child(&'c self, name: Rc<str>) -> Self {
+ CompilerBuilder::new()
+ .name(name)
+ .debug(self.debug)
+ .repl(false)
+ .parent(self)
+ .build()
+ }
+
+ fn begin_scope(&mut self) {
+ if self.root_is_block {
+ self.root_is_block = false;
+ } else {
+ self.scope += 1;
+ }
+ }
+
+ fn get_scope_start(&self, scope: usize) -> usize {
+ let mut stack_cutoff = None;
+ for (i, local) in self.locals.iter().enumerate() {
+ if local.scope == scope {
+ stack_cutoff = Some(i);
+ break;
+ }
+ }
+ if let Some(cutoff) = stack_cutoff {
+ cutoff
+ } else {
+ self.locals.len()
+ }
+ }
+
+ fn get_scope_diff(&self, scope: usize) -> usize {
+ let cutoff = self.get_scope_start(scope);
+ self.locals.len() - cutoff
+ }
+
+ fn end_scope(&mut self) {
+ let cutoff = self.get_scope_start(self.scope);
+ if cutoff < self.locals.len() {
+ self.emit(Instruction::Discard((self.locals.len() - cutoff) as u16));
+ self.locals.truncate(cutoff);
+ };
+ if self.scope != 0 {
+ self.scope -= 1;
+ }
+ }
+
+ fn create_local(&mut self, name: Rc<str>) -> Rc<Local> {
+ let local = Local { name, idx: self.locals.len(), scope: self.scope };
+ self.locals.push(Rc::new(local));
+ self.locals[self.locals.len() - 1].clone()
+ }
+
+ 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_local_checked(&mut self, name: Rc<str>) -> Result<Rc<Local>> {
+ if let Some(local) = self.find_local(&name) {
+ if local.scope == self.scope {
+ return Err(Error::Redefined(name).into())
+ }
+ };
+ Ok(self.create_local(name))
+ }
+
+ fn create_global_checked(&mut self, name: Rc<str>) -> Result<usize> {
+ if let Some(_) = self.find_global(&name) {
+ return Err(Error::Redefined(name).into())
+ }
+ Ok(self.create_global(name))
+ }
+
+ fn find_local(&self, name: &str) -> Option<Rc<Local>> {
+ for local in self.locals.iter().rev() {
+ if local.name.as_ref() == name {
+ return Some(local.clone())
+ }
+ }
+ None
+ }
+
+ fn find_global(&self, name: &str) -> Option<usize> {
+ 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)
+ }
+ }
+ None
+ }
+
+ fn emit_const(&mut self, val: Value) {
+ // TODO: find constant if already exists
+ self.chunk.constants.push(val);
+ let c = self.chunk.constants.len() - 1;
+ self.emit(Instruction::Const(c as u16));
+ }
+
+ fn can_make_globals(&self) -> bool {
+ self.repl && self.parent.is_none() && self.scope == 0
+ }
+
+ fn compile_value(&mut self, val: &Value) {
+ use Value::*;
+ use Instruction as I;
+ match val {
+ Nil => self.emit(I::Nil),
+ Bool(b) => if *b { self.emit(I::True) } else { self.emit(I::False) },
+ Int(i) => {
+ if let Ok(i) = i16::try_from(*i) {
+ self.emit(I::Int(i));
+ } else {
+ self.emit_const(val.clone());
+ }
+ },
+ _ => self.emit_const(val.clone()),
+ }
+ }
+
+
+
+ fn compile_expr(&mut self, expr: &Expr) -> Result<()> {
+ use Expr::*;
+ use Instruction as I;
+ match expr {
+ Literal(val) => self.compile_value(val),
+ Ident(name) => {
+ if let Some(local) = self.find_local(name) {
+ self.emit(I::Load(local.idx as u16));
+ } else if let Some(global) = self.find_global(name) {
+ self.emit(I::LoadGlobal(global as u16));
+ } else {
+ return Err(Error::Undefined(name.clone()).into())
+ };
+ },
+ UnaryOp(expr, op) => {
+ self.compile_expr(expr)?;
+ self.emit(I::UnaryOp(*op));
+ },
+ BinaryOp(lhs, rhs, op) => {
+ self.compile_expr(lhs)?;
+ self.compile_expr(rhs)?;
+ self.emit(I::BinaryOp(*op));
+ },
+ Index(_, _) => todo!("index"),
+ FnCall(fun, params) => {
+ for expr in params {
+ self.compile_expr(expr)?;
+ }
+ self.compile_expr(fun)?;
+ self.emit(I::Call(params.len() as u8));
+ },
+ FieldAccess(_, _) => todo!("field access"),
+ List(list) => {
+ for expr in list {
+ self.compile_expr(expr)?;
+ }
+ self.emit(I::NewList(list.len() as u16));
+ },
+ Matrix(mat) => {
+ for expr in &mat.2 {
+ self.compile_expr(expr)?;
+ }
+ self.emit(I::NewMatrix(mat.2.len() as u16, mat.1 as u8));
+ },
+ Table(table) => {
+ for (key, value) in table {
+ self.compile_expr(key)?;
+ self.compile_expr(value)?;
+ }
+ self.emit(I::NewTable(table.len() as u16));
+ },
+ And(lhs, rhs) => {
+ self.compile_expr(lhs)?;
+ self.emit(I::Dup);
+ let jmpidx = self.emit_temp();
+ self.compile_expr(rhs)?;
+ self.re_emit(I::JumpFalse(self.cur()), jmpidx);
+ },
+ Or(lhs, rhs) => {
+ self.compile_expr(lhs)?;
+ self.emit(I::Dup);
+ let jmpidx = self.emit_temp();
+ self.compile_expr(rhs)?;
+ self.re_emit(I::JumpTrue(self.cur()), jmpidx);
+ },
+ };
+ Ok(())
+ }
+
+ fn finish_loop(&mut self) {
+ use Instruction as I;
+ self.loop_top.pop();
+ while let Some(tmp) = self.loop_bot.pop() {
+ self.re_emit(I::Jump(self.cur()), tmp as usize);
+ }
+ }
+
+ fn compile_assign(&mut self, assign: &Assign) -> Result<()> {
+ use Assign as A;
+ use Expr as E;
+ use Instruction as I;
+ match assign {
+ A::Expr(expr) => self.compile_expr(expr)?,
+ A::Assign(lhs, rhs) => {
+ self.compile_assign(rhs)?;
+ match lhs {
+ E::Ident(name) => {
+ if let Some(global) = self.find_global(&name) {
+ self.emit(I::Dup);
+ self.emit(I::StoreGlobal(global as u16));
+ } else if let Some(local) = self.find_local(&name) {
+ self.emit(I::Dup);
+ self.emit(I::Store(local.idx as u16));
+ } else if self.can_make_globals() {
+ let global = self.create_global(name.clone());
+ self.emit(I::Dup);
+ self.emit(I::StoreGlobal(global as u16));
+ } else {
+ self.create_local(name.clone());
+ self.emit(I::Dup);
+ }
+ },
+ E::Index(_, _) => todo!("index"),
+ E::FieldAccess(_, _) => todo!("field access"),
+ _ => panic!("this should be handeled by the parser!!!")
+ }
+ }
+ };
+ Ok(())
+ }
+
+ fn compile_stmt(&mut self, stmt: &Stmt) -> Result<()> {
+ use Stmt::*;
+ use Instruction as I;
+ match stmt {
+ NoOp => {},
+ If(cond, ifb, elseb) => {
+ self.compile_expr(cond)?;
+ let jmpidx = self.emit_temp();
+ self.compile_stmt(ifb)?;
+ self.re_emit(I::JumpFalse(self.cur()), jmpidx);
+ if let Some(elseb) = elseb {
+ self.compile_stmt(elseb)?;
+ }
+ },
+ Function(name, params, body) => {
+ let chunk = self.compile_function(name.clone(), params, body)?;
+ let fun = Value::Function(Rc::new(
+ chunk::Function {
+ name: name.clone(),
+ arity: params.len(),
+ body: chunk
+ }
+ ));
+ self.emit_const(fun);
+ if self.can_make_globals() {
+ let idx = self.create_global_checked(name.clone())?;
+ self.emit(I::StoreGlobal(idx as u16));
+ } else {
+ self.create_local_checked(name.clone())?;
+ }
+ },
+ Loop(stmt) => {
+ let idx = self.cur();
+ self.loop_top.push((idx as usize, self.scope));
+ self.compile_stmt(stmt)?;
+ self.emit(I::Jump(idx));
+ self.finish_loop();
+ },
+ While(cond, stmt) => {
+ let top = self.cur();
+ self.compile_expr(cond)?;
+ let jmpidx = self.emit_temp();
+ self.loop_top.push((top as usize, self.scope));
+ self.compile_stmt(stmt)?;
+ self.emit(I::Jump(top));
+ self.re_emit(I::JumpFalse(self.cur()), jmpidx);
+ self.finish_loop();
+ },
+ DoWhile(cond, stmt) => {
+ let top = self.cur();
+ self.loop_top.push((top as usize, self.scope));
+ self.compile_stmt(stmt)?;
+ self.compile_expr(cond)?;
+ self.emit(I::JumpTrue(top));
+ self.finish_loop();
+ },
+ Block(block) => {
+ self.begin_scope();
+ for stmt in block {
+ self.compile_stmt(stmt)?;
+ }
+ self.end_scope();
+ },
+ Expr(expr) => {
+ self.compile_expr(expr)?;
+ self.emit(I::Discard(1));
+ },
+ Assign(assign) => {
+ self.compile_assign(assign)?;
+ self.emit(I::Discard(1));
+ }
+ Let(name, expr) => {
+ self.compile_expr(expr)?;
+ if self.can_make_globals() {
+ let global = self.create_global_checked(name.clone())?;
+ self.emit(I::StoreGlobal(global as u16));
+ } else {
+ self.create_local_checked(name.clone())?;
+ }
+ },
+ Continue => {
+ let top = self.loop_top.pop();
+ if let Some((top, scope)) = top {
+ let diff = self.get_scope_diff(scope) as u16;
+ if diff > 0 {
+ self.emit(I::Discard(diff));
+ }
+ self.emit(I::Jump(top as u16));
+ } else {
+ return Err(Error::InvContinue.into())
+ }
+ },
+ Break => {
+ let top = self.loop_top.pop();
+ if let Some((_, scope)) = top {
+ self.emit(I::Discard(self.get_scope_diff(scope) as u16));
+ let tmpidx = self.emit_temp();
+ self.loop_bot.push(tmpidx);
+ } else {
+ return Err(Error::InvBreak.into())
+ }
+ },
+ Return(expr) => {
+ self.compile_expr(expr)?;
+ self.emit(I::Return);
+ },
+ };
+ Ok(())
+ }
+
+ fn compile_function(&mut self, name: Rc<str>, params: &Vec<Rc<str>>, body: &Box<Stmt>) -> Result<Chunk> {
+ let mut compiler = self.child(name);
+ for name in params {
+ compiler.create_local(name.clone());
+ }
+ compiler.compile_stmt(body)?;
+ compiler.finish()?;
+ Ok(compiler.chunk)
+ }
+
+ fn cur(&self) -> u16 {
+ self.chunk.code.len() as u16
+ }
+
+ fn emit_temp(&mut self) -> usize {
+ let idx = self.chunk.code.len();
+ self.emit(Instruction::NoOp);
+ idx
+ }
+
+ fn emit(&mut self, ins: Instruction) {
+ //println!("{}: {ins}", self.name);
+ self.chunk.code.push(ins);
+ }
+
+ fn re_emit(&mut self, ins: Instruction, idx: usize) {
+ //println!("{} at {}: {ins}", self.name, &self.chunk.code[idx]);
+ self.chunk.code[idx] = ins;
+ }
+
+ fn finish(&mut self) -> Result<()> {
+ use Instruction as I;
+ let ins = match self.chunk.code.last() {
+ Some(ins) => ins.clone(),
+ None => {
+ self.emit(I::Nil);
+ self.emit(I::Return);
+ if self.debug {
+ println!("{}\n{}", self.name, self.chunk);
+ }
+ return Ok(())
+ }
+ };
+ match ins {
+ I::Discard(amt) if self.repl => {
+ self.chunk.code.pop().unwrap();
+ if amt > 1 {
+ self.emit(I::Discard(amt - 1));
+ }
+ self.emit(I::Return);
+ }
+ I::Return => {},
+ _ => {
+ self.emit(I::Nil);
+ self.emit(I::Return);
+ }
+ };
+
+ if self.loop_bot.len() > 0 {
+ return Err(Error::InvBreak.into())
+ }
+
+ if self.debug {
+ println!("{}\n{}", self.name, self.chunk);
+ }
+ Ok(())
+ }
+
+ pub fn compile(
+ &mut self,
+ body: &Stmt,
+ ) -> Result<Rc<Function>> {
+ if let Stmt::Block(_) = body {
+ self.root_is_block = true;
+ }
+ self.chunk = Chunk::new();
+ self.compile_stmt(body)?;
+ self.finish()?;
+ let fun = Function { name: self.name.clone(), body: self.chunk.clone(), arity: 0 };
+ Ok(Rc::new(fun))
+ }
+}
diff --git a/matrix/src/gc.rs b/matrix/src/gc.rs
new file mode 100644
index 0000000..85129dc
--- /dev/null
+++ b/matrix/src/gc.rs
@@ -0,0 +1,112 @@
+use std::cmp::Ordering;
+use std::fmt::{Debug, Display};
+use std::marker::PhantomData;
+use std::ops::{Deref, DerefMut};
+use std::ptr::NonNull;
+use std::rc::Rc;
+
+pub struct Gc<T> {
+ ptr: NonNull<GcInner<T>>,
+ phantom: PhantomData<GcInner<T>>,
+}
+
+pub struct GcInner<T> {
+ rc: usize,
+ data: T,
+}
+
+impl<T> Gc<T> {
+ pub fn new(data: T) -> Gc<T> {
+ let boxed = Box::new(GcInner {
+ rc: 1,
+ data,
+ });
+ Self {
+ ptr: NonNull::new(Box::into_raw(boxed)).unwrap(),
+ phantom: PhantomData,
+ }
+ }
+}
+
+impl<T: Clone> From<Rc<T>> for Gc<T> {
+ fn from(value: Rc<T>) -> Self {
+ Self::new(Rc::unwrap_or_clone(value))
+ }
+}
+
+impl<T> From<T> for Gc<T> {
+ fn from(value: T) -> Self {
+ Self::new(value)
+ }
+}
+
+impl<T> Deref for Gc<T> {
+ type Target = T;
+ fn deref(&self) -> &T {
+ let inner = unsafe { self.ptr.as_ref() };
+ &inner.data
+ }
+}
+
+impl<T: Debug> Debug for Gc<T> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let inner = unsafe { self.ptr.as_ref() };
+ write!(f, "{:?}", inner.data)
+ }
+}
+
+impl<T: Display> Display for Gc<T> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let inner = unsafe { self.ptr.as_ref() };
+ write!(f, "{}", inner.data)
+ }
+}
+
+impl<T: PartialEq> PartialEq for Gc<T> {
+ fn eq(&self, other: &Self) -> bool {
+ let inner = unsafe { self.ptr.as_ref() };
+ let other = unsafe { other.ptr.as_ref() };
+ inner.data == other.data
+ }
+}
+
+impl<T: PartialOrd> PartialOrd for Gc<T> {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ let inner = unsafe { self.ptr.as_ref() };
+ let other = unsafe { other.ptr.as_ref() };
+ inner.data.partial_cmp(&other.data)
+ }
+}
+
+impl<T: Clone> DerefMut for Gc<T> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ let inner = unsafe { self.ptr.as_mut() };
+ if inner.rc > 1 {
+ *self = Self::new(inner.data.clone());
+ }
+ &mut inner.data
+ }
+}
+
+impl<T> Clone for Gc<T> {
+ fn clone(&self) -> Self {
+ let inner = unsafe { self.ptr.as_ptr().as_mut().unwrap() };
+ inner.rc += 1;
+ Self {
+ ptr: self.ptr,
+ phantom: PhantomData,
+ }
+ }
+}
+
+impl<T> Drop for Gc<T> {
+ fn drop(&mut self) {
+ let inner = unsafe { self.ptr.as_mut() };
+ inner.rc -= 1;
+ if inner.rc > 0 {
+ return;
+ }
+ unsafe { let _ = Box::from_raw(self.ptr.as_ptr()); }
+ }
+}
+
diff --git a/matrix/src/lex.rs b/matrix/src/lex.rs
index e49ccba..953a495 100644
--- a/matrix/src/lex.rs
+++ b/matrix/src/lex.rs
@@ -1,5 +1,6 @@
use std::{rc::Rc, fmt::Debug};
use regex::Regex;
+use crate::Result;
pub struct RegexToken {
regex: Regex
@@ -37,13 +38,16 @@ pub enum Token {
LeftBrack,
RightBrack,
LeftBrace,
+ LeftLeftBrace,
RightBrace,
+ RightRightBrace,
Assign,
Access,
SemiColon,
Arrow,
ThinArrow,
Comma,
+ Range,
// equality
Equal,
@@ -97,20 +101,6 @@ pub enum Token {
Eof,
}
-impl Token {
-
-}
-
-impl Token {
- /// Returns `true` if the token is [`Regex`].
- ///
- /// [`Regex`]: Token::Regex
- #[must_use]
- pub fn is_regex(&self) -> bool {
- matches!(self, Self::Regex(..))
- }
-}
-
#[derive(Debug)]
pub enum Error {
UnexpectedCharacter(char),
@@ -141,8 +131,6 @@ impl std::fmt::Display for Error {
impl std::error::Error for Error {}
-pub type Result<T> = std::result::Result<T, self::Error>;
-
pub struct Lexer {
pub index: usize,
len: usize,
@@ -156,10 +144,10 @@ trait IsIdent {
impl IsIdent for char {
fn is_initial_ident(&self) -> bool {
- return self.is_alphabetic() || *self == '_';
+ self.is_alphabetic() || *self == '_'
}
fn is_ident(&self) -> bool {
- return self.is_alphanumeric() || *self == '_';
+ self.is_alphanumeric() || *self == '_'
}
}
@@ -179,31 +167,29 @@ impl Lexer {
if self.index >= self.len {
return '\0';
}
- return self.data[self.index];
+ self.data[self.index]
}
fn next(&mut self) -> char {
let c = self.peek();
self.index += 1;
- return c;
+ c
}
fn next_not_eof(&mut self) -> Result<char> {
let c = self.next();
if c == '\0' {
- return Err(Error::UnexpectedEof)
- } else {
- return Ok(c)
+ return Err(Error::UnexpectedEof.into())
}
+ Ok(c)
}
fn next_expect(&mut self, expected: char) -> Result<char> {
let c = self.next();
if c != expected {
- return Err(Error::ExpectedChar(expected, c))
- } else {
- return Ok(c)
+ return Err(Error::ExpectedChar(expected, c).into())
}
+ Ok(c)
}
fn skip_whitespace(&mut self, ignore_newlines: bool) {
@@ -247,7 +233,7 @@ impl Lexer {
buf.push(char::from_u32(
n1.to_digit(16).ok_or(InvalidDigit(n1))? * 16 +
n2.to_digit(16).ok_or(InvalidDigit(n2))?
- ).unwrap())
+ ).unwrap());
},
'u' => {
self.next_expect('{')?;
@@ -255,16 +241,16 @@ impl Lexer {
loop {
let c = self.next_not_eof()?;
if c == '}' { break }
- if n >= 0x10000000u32 {
- return Err(InvalidCodepoint)
+ if n >= 0x1000_0000_u32 {
+ return Err(InvalidCodepoint.into())
}
- n = n * 16 + c.to_digit(16).ok_or(InvalidDigit(c))?;
+ n = n * 16 + c.to_digit(16).ok_or::<crate::Error>(InvalidDigit(c).into())?;
}
- let ch = char::from_u32(n).ok_or(InvalidCodepoint)?;
+ let ch = char::from_u32(n).ok_or::<crate::Error>(InvalidCodepoint.into())?;
buf.push(ch);
},
- _ => return Err(InvalidStringEscape(next))
+ _ => return Err(InvalidStringEscape(next).into())
}
}
@@ -278,7 +264,7 @@ impl Lexer {
let mut buf = std::string::String::new();
if !initial.is_initial_ident() {
- return Err(UnexpectedCharacter(initial))
+ return Err(UnexpectedCharacter(initial).into())
}
buf.push(initial);
@@ -296,7 +282,7 @@ impl Lexer {
"else" => Else,
"while" => While,
"let" => Let,
- "function" => Function,
+ "fn" | "function" => Function,
"true" => True,
"false" => False,
"nil" => Nil,
@@ -325,16 +311,16 @@ impl Lexer {
n = n * radix + (i as i64);
char_found = true;
} else if self.peek().is_ident() {
- return Err(InvalidDigit(self.peek()))
+ return Err(InvalidDigit(self.peek()).into())
} else {
break;
}
}
if char_found {
- return Ok(Int(n))
+ Ok(Int(n))
} else {
- return Err(InvalidNumber(format!("0{radix_char}")))
+ Err(InvalidNumber(format!("0{radix_char}")).into())
}
}
@@ -360,7 +346,7 @@ impl Lexer {
if initial != '.' {
loop {
- if !self.peek().is_digit(10) { break; }
+ if !self.peek().is_ascii_digit() { break; }
buf.push(self.next());
}
@@ -370,7 +356,7 @@ impl Lexer {
}
loop {
- if !self.peek().is_digit(10) { break; }
+ if !self.peek().is_ascii_digit() { break; }
buf.push(self.next());
}
@@ -381,7 +367,7 @@ impl Lexer {
}
loop {
- if !self.peek().is_digit(10) { break; }
+ if !self.peek().is_ascii_digit() { break; }
buf.push(self.next());
}
}
@@ -392,13 +378,12 @@ impl Lexer {
}
if self.peek().is_ident() || self.peek() == '.' {
- return Err(UnexpectedCharacter(self.peek()))
+ return Err(UnexpectedCharacter(self.peek()).into())
}
if let Ok(int) = buf.parse::<i64>() {
use Token::*;
- if self.peek() == 'i' {
- self.next();
+ if complex {
return Ok(Complex(int as f64))
}
return Ok(Int(int))
@@ -406,18 +391,13 @@ impl Lexer {
if let Ok(float) = buf.parse::<f64>() {
use Token::*;
- if self.peek() == 'i' {
- self.next();
+ if complex {
return Ok(Complex(float))
}
return Ok(Float(float))
}
- Err(Error::InvalidNumber(buf))
- }
-
- pub fn reset(&mut self) {
- self.index = 0;
+ Err(Error::InvalidNumber(buf).into())
}
fn peek_token_impl(&mut self, ignore_newlines: bool) -> Result<Token> {
@@ -427,16 +407,6 @@ impl Lexer {
token
}
- pub fn peek_multiple_tokens(&mut self, count: usize) -> Result<Vec<Token>> {
- let mut tokens = Vec::new();
- let idx = self.index;
- for _ in 0..count {
- tokens.push(self.next_token_impl(false)?);
- }
- self.index = idx;
- Ok(tokens)
- }
-
fn next_token_impl(&mut self, ignore_newlines: bool) -> Result<Token> {
use Token::*;
use Error::*;
@@ -454,8 +424,24 @@ impl Lexer {
')' => RightParen,
'[' => LeftBrack,
']' => RightBrack,
- '{' => LeftBrace,
- '}' => RightBrace,
+ '{' => {
+ match next {
+ '{' => {
+ self.next();
+ LeftLeftBrace
+ }
+ _ => LeftBrace
+ }
+ },
+ '}' => {
+ match next {
+ '}' => {
+ self.next();
+ RightRightBrace
+ }
+ _ => RightBrace
+ }
+ },
';' => SemiColon,
'+' => Add,
',' => Comma,
@@ -561,7 +547,9 @@ impl Lexer {
}
},
'.' => {
- if next.is_digit(10) {
+ if next == '.' {
+ Range
+ } else if next.is_digit(10) {
self.lex_number(char)?
} else {
Access
diff --git a/matrix/src/lib.rs b/matrix/src/lib.rs
index d93b89b..3c4732b 100644
--- a/matrix/src/lib.rs
+++ b/matrix/src/lib.rs
@@ -1,5 +1,55 @@
+use std::fmt::Display;
-mod ast;
-mod lex;
-
+pub mod compiler;
+pub mod value;
+pub mod gc;
+pub mod lex;
+pub mod vm;
pub mod parse;
+pub mod chunk;
+pub mod ast;
+
+#[derive(Debug)]
+pub struct Error(Box<ErrorInner>);
+
+impl std::error::Error for Error {}
+
+#[derive(Debug)]
+enum ErrorInner {
+ Lex(lex::Error),
+ Parse(parse::Error),
+ Value(value::Error),
+ Compile(compiler::Error),
+ Runtime(vm::Error),
+}
+
+impl Display for crate::Error {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ use crate::ErrorInner::*;
+ match self.0.as_ref() {
+ Lex(err) => write!(f, "{err}"),
+ Parse(err) => write!(f, "{err}"),
+ Value(err) => write!(f, "{err}"),
+ Compile(err) => write!(f, "{err}"),
+ Runtime(err) => write!(f, "{err}"),
+ }
+ }
+}
+
+macro_rules! from_error {
+ ($struct:ty, $tuple:tt) => {
+ impl From<$struct> for crate::Error {
+ fn from(value: $struct) -> Self {
+ crate::Error(Box::new(ErrorInner::$tuple(value)))
+ }
+ }
+ };
+}
+
+from_error!(lex::Error, Lex);
+from_error!(parse::Error, Parse);
+from_error!(value::Error, Value);
+from_error!(compiler::Error, Compile);
+from_error!(vm::Error, Runtime);
+
+pub type Result<T> = std::result::Result<T, crate::Error>;
diff --git a/matrix/src/parse.rs b/matrix/src/parse.rs
index 57ee2bf..9d1237b 100644
--- a/matrix/src/parse.rs
+++ b/matrix/src/parse.rs
@@ -1,10 +1,33 @@
use std::{fmt::Display, rc::Rc};
use num_complex::Complex64;
-use crate::{lex::{Lexer, self, Token}, ast::{Expr, Value, BinaryOp, UnaryOp}};
+use crate::{lex::{Lexer, self, Token}, ast::{Expr, BinaryOp, UnaryOp, Stmt, Assign, optimize, self}, gc::Gc, value::{Value, self}, Result};
+
+pub struct ParserBuilder {
+ optimize: bool
+}
+
+impl ParserBuilder {
+ pub fn new() -> Self {
+ Self { optimize: true }
+ }
+
+ pub fn optimize(mut self, optimize: bool) -> Self {
+ self.optimize = optimize;
+ self
+ }
+
+ pub fn build(self) -> Parser {
+ Parser {
+ lexer: Lexer::new(""),
+ optimize: self.optimize
+ }
+ }
+}
pub struct Parser {
- lexer: Lexer
+ lexer: Lexer,
+ optimize: bool
}
#[derive(Debug)]
@@ -13,10 +36,11 @@ pub enum Error {
UnexpectedToken(Token),
ExpectedToken(Token),
ExpectedTokenName(&'static str),
+ MatrixCoDomainError(usize, usize, usize),
+ NotAssignable(Expr),
+ ValueError(value::Error),
}
-pub type Result<T> = std::result::Result<T, Error>;
-
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use Error::*;
@@ -25,6 +49,9 @@ impl Display for Error {
UnexpectedToken(tok) => write!(f, "Unexpected token: '{tok:?}'"),
ExpectedToken(tok) => write!(f, "Expected token: '{tok:?}'"),
ExpectedTokenName(name) => write!(f, "Expected {name} token"),
+ MatrixCoDomainError(row, should, was) => write!(f, "In col {row} of matrix, codomain was expected to be {should} but was given {was}"),
+ NotAssignable(expr) => write!(f, "{expr:?} is not assignable"),
+ ValueError(err) => write!(f, "{err}"),
}
}
}
@@ -37,34 +64,34 @@ impl From<lex::Error> for Error {
impl std::error::Error for Error {}
-macro_rules! math_expr_parser {
+macro_rules! expr_parser {
($parser:ident, $pattern:pat, $fn:ident) => {{
- let mut math_expr = $parser.$fn()?;
+ let mut expr = $parser.$fn()?;
loop {
let tok = $parser.lexer.peek_token_nl()?;
match tok {
$pattern => {
$parser.lexer.next_token_nl()?;
let temp = $parser.$fn()?;
- math_expr = Expr::BinaryOp(Box::new(math_expr), Box::new(temp), BinaryOp::try_from(tok).unwrap())
+ expr = Expr::BinaryOp(Box::new(expr), Box::new(temp), BinaryOp::from(tok))
}
_ => break
}
}
- Ok(math_expr)
+ Ok(expr)
}};
}
-macro_rules! math_expr_parser_reverse {
+macro_rules! expr_parser_reverse {
($parser:ident, $pattern:pat, $fn:ident, $cur:ident) => {{
- let math_expr = $parser.$fn()?;
+ let expr = $parser.$fn()?;
let tok = $parser.lexer.peek_token_nl()?;
Ok(match tok {
$pattern => {
$parser.lexer.next_token_nl()?;
- Expr::BinaryOp(Box::new(math_expr), Box::new($parser.$cur()?), BinaryOp::try_from(tok).unwrap())
+ Expr::BinaryOp(Box::new(expr), Box::new($parser.$cur()?), BinaryOp::from(tok))
}
- _ => math_expr
+ _ => expr
})
}};
}
@@ -74,7 +101,7 @@ impl Parser {
fn force_token(&mut self, tok: Token) -> Result<Token> {
let next = self.lexer.next_token()?;
if next != tok {
- Err(Error::ExpectedToken(tok))
+ Err(Error::ExpectedToken(tok).into())
} else {
Ok(tok)
}
@@ -83,63 +110,138 @@ impl Parser {
fn force_token_nl(&mut self, tok: Token) -> Result<Token> {
let next = self.lexer.next_token_nl()?;
if next != tok {
- Err(Error::ExpectedToken(tok))
+ Err(Error::ExpectedToken(tok).into())
} else {
Ok(tok)
}
}
- fn parse_list(&mut self) -> Result<Expr> {
+ fn parse_fn_call(&mut self) -> Result<Vec<Expr>> {
+ self.force_token(Token::LeftParen)?;
+ let mut params = Vec::new();
+ loop {
+ let expr = match self.lexer.peek_token()? {
+ Token::RightParen => {
+ self.lexer.next_token()?;
+ break
+ },
+ _ => self.parse_expr()?
+ };
+ params.push(expr);
+ let next = self.lexer.next_token()?;
+ match next {
+ Token::Comma => continue,
+ Token::RightParen => break,
+ _ => return Err(Error::UnexpectedToken(next).into())
+ };
+ }
+ Ok(params)
+ }
+
+ fn parse_index(&mut self) -> Result<Vec<Expr>> {
self.force_token(Token::LeftBrack)?;
- let mut list = Vec::new();
- if self.lexer.peek_token()? == Token::RightBrack {
- self.lexer.next_token()?;
- return Ok(Expr::List(list))
+ let mut indicies = Vec::new();
+ loop {
+ let expr = match self.lexer.peek_token()? {
+ Token::RightBrack => {
+ self.lexer.next_token()?;
+ break
+ },
+ _ => self.parse_expr()?
+ };
+ indicies.push(expr);
+ let next = self.lexer.next_token()?;
+ match next {
+ Token::SemiColon => continue,
+ Token::RightBrack => break,
+ _ => return Err(Error::UnexpectedToken(next).into())
+ };
}
+ Ok(indicies)
+ }
+
+ fn parse_matrix_part(&mut self) -> Result<Vec<Expr>> {
+ let mut part = Vec::new();
loop {
- let expr = self.parse_math_expr()?;
- list.push(expr);
+ let expr = match self.lexer.peek_token()? {
+ Token::SemiColon => break,
+ Token::RightBrack => break,
+ _ => self.parse_expr()?
+ };
+ part.push(expr);
+ match self.lexer.peek_token()? {
+ Token::Comma => {
+ self.lexer.next_token()?;
+ },
+ _ => {},
+ };
+ }
+ Ok(part)
+ }
+
+ fn parse_matrix(&mut self) -> Result<Expr> {
+ self.force_token(Token::LeftBrack)?;
+ let mut parts = Vec::new();
+ loop {
+ let part = self.parse_matrix_part()?;
+ parts.push(part);
let next = self.lexer.next_token()?;
match next {
- Token::Comma => continue,
+ Token::SemiColon => continue,
Token::RightBrack => break,
- _ => return Err(Error::UnexpectedToken(next))
+ _ => return Err(Error::UnexpectedToken(next).into()),
+ };
+ }
+ if parts.len() == 1 {
+ Ok(Expr::List(parts.pop().unwrap()))
+ } else {
+ let codomain = parts[0].len();
+ let domain = parts.len();
+ for (i, part) in parts.iter().enumerate() {
+ if part.len() != codomain {
+ return Err(Error::MatrixCoDomainError(i, codomain, part.len()).into())
+ }
}
+ let mut data = Vec::new();
+ parts.reverse();
+ while let Some(part) = parts.pop() {
+ data.extend(part);
+ }
+ Ok(Expr::Matrix((domain, codomain, data)))
}
- Ok(Expr::List(list))
}
fn parse_table_key(&mut self) -> Result<Expr> {
let tok = self.lexer.next_token()?;
Ok(match tok {
Token::LeftBrack => {
- let expr = self.parse_math_expr()?;
+ let expr = self.parse_expr()?;
self.force_token(Token::RightBrack)?;
expr
},
Token::Ident(ident) => Expr::Ident(ident),
- Token::String(string) => Expr::Literal(Value::String(string)),
- _ => return Err(Error::UnexpectedToken(tok))
+ Token::String(string) => Expr::Literal(Value::String(string.to_string().into())),
+ _ => return Err(Error::UnexpectedToken(tok).into())
})
}
fn parse_table(&mut self) -> Result<Expr> {
- self.force_token(Token::LeftBrace)?;
+ self.force_token(Token::LeftLeftBrace)?;
let mut table = Vec::new();
- if self.lexer.peek_token()? == Token::RightBrace {
+ if self.lexer.peek_token()? == Token::RightRightBrace {
self.lexer.next_token()?;
return Ok(Expr::Table(table))
}
loop {
let key = self.parse_table_key()?;
self.force_token(Token::Assign)?;
- let value = self.parse_math_expr()?;
+ let value = self.parse_expr()?;
table.push((key, value));
let next = self.lexer.next_token()?;
match next {
Token::Comma => continue,
- Token::RightBrace => break,
- _ => return Err(Error::UnexpectedToken(next))
+ Token::RightRightBrace => break,
+ _ => return Err(Error::UnexpectedToken(next).into())
}
}
Ok(Expr::Table(table))
@@ -147,107 +249,170 @@ impl Parser {
fn parse_paren(&mut self) -> Result<Expr> {
self.force_token(Token::LeftParen)?;
- let math_expr = self.parse_math_expr()?;
+ let expr = self.parse_expr()?;
self.force_token(Token::RightParen)?;
- Ok(math_expr)
+ Ok(expr)
}
fn parse_term(&mut self) -> Result<Expr> {
use Token::*;
let tok = self.lexer.peek_token()?;
match tok {
- LeftBrack => return self.parse_list(),
- LeftBrace => return self.parse_table(),
+ LeftBrack => return self.parse_matrix(),
+ LeftLeftBrace => return self.parse_table(),
LeftParen => return self.parse_paren(),
_ => ()
}
self.lexer.next_token()?;
Ok(match tok {
+ Nil => Expr::Literal(Value::Nil),
Int(i) => Expr::Literal(Value::Int(i)),
Float(f) => Expr::Literal(Value::Float(f)),
Complex(c) => Expr::Literal(Value::Complex(Complex64::new(0.0, c))),
- Regex(r) => Expr::Literal(Value::Regex(Rc::new(r.into()))),
- String(s) => Expr::Literal(Value::String(s)),
+ Regex(r) => Expr::Literal(Value::Regex(Gc::new(r.into()))),
+ String(s) => Expr::Literal(Value::String(s.to_string().into())),
True => Expr::Literal(Value::Bool(true)),
False => Expr::Literal(Value::Bool(false)),
Ident(ident) => Expr::Ident(ident),
- _ => return Err(Error::UnexpectedToken(tok)),
+ _ => return Err(Error::UnexpectedToken(tok).into()),
})
}
- fn parse_math_expr_unary(&mut self) -> Result<Expr> {
+ fn parse_expr_expr_access(&mut self) -> Result<Expr> {
+ let mut expr = self.parse_term()?;
+ loop {
+ let tok = self.lexer.peek_token()?;
+ match tok {
+ Token::Access => {
+ self.force_token(Token::Access)?;
+ let temp = self.parse_term()?;
+ expr = Expr::FieldAccess(Box::new(expr), Box::new(temp));
+ },
+ _ => break
+ }
+ }
+ Ok(expr)
+ }
+
+ fn parse_expr_call(&mut self) -> Result<Expr> {
+ let mut expr = self.parse_expr_expr_access()?;
+ loop {
+ let tok = self.lexer.peek_token()?;
+ match tok {
+ Token::LeftBrack => {
+ let index = self.parse_index()?;
+ expr = Expr::Index(Box::new(expr), index);
+ },
+ Token::LeftParen => {
+ let params = self.parse_fn_call()?;
+ expr = Expr::FnCall(Box::new(expr), params);
+ }
+ _ => break
+ }
+ }
+ Ok(expr)
+ }
+
+ fn parse_expr_unary(&mut self) -> Result<Expr> {
let tok = self.lexer.peek_token_nl()?;
Ok(match tok {
Token::Not => {
self.lexer.next_token()?;
- Expr::UnaryOp(Box::new(self.parse_math_expr_unary()?), UnaryOp::Not)
+ Expr::UnaryOp(Box::new(self.parse_expr_unary()?), UnaryOp::Not)
}
Token::Subtract => {
self.lexer.next_token()?;
- Expr::UnaryOp(Box::new(self.parse_math_expr_unary()?), UnaryOp::Negate)
+ Expr::UnaryOp(Box::new(self.parse_expr_unary()?), UnaryOp::Negate)
}
- _ => self.parse_term()?
+ _ => self.parse_expr_call()?
})
}
- fn parse_math_expr_pow(&mut self) -> Result<Expr> {
- math_expr_parser_reverse!(
+ fn parse_expr_pow(&mut self) -> Result<Expr> {
+ expr_parser_reverse!(
self,
Token::Power,
- parse_math_expr_unary,
- parse_math_expr_pow
+ parse_expr_unary,
+ parse_expr_pow
)
}
- fn parse_math_expr_mult(&mut self) -> Result<Expr> {
- math_expr_parser!(self, Token::Multiply | Token::Divide | Token::Modulo, parse_math_expr_pow)
+ fn parse_expr_mult(&mut self) -> Result<Expr> {
+ expr_parser!(self, Token::Multiply | Token::Divide | Token::Modulo, parse_expr_pow)
}
- fn parse_math_expr_add(&mut self) -> Result<Expr> {
- math_expr_parser!(self, Token::Add | Token::Subtract, parse_math_expr_mult)
+ fn parse_expr_add(&mut self) -> Result<Expr> {
+ expr_parser!(self, Token::Add | Token::Subtract, parse_expr_mult)
}
- fn parse_math_expr_shift(&mut self) -> Result<Expr> {
- math_expr_parser!(
+ fn parse_expr_shift(&mut self) -> Result<Expr> {
+ expr_parser!(
self,
Token::BitwiseShiftLeft | Token::BitwiseShiftRight,
- parse_math_expr_add
+ parse_expr_add
)
}
- fn parse_math_expr_bit_and(&mut self) -> Result<Expr> {
- math_expr_parser!(self, Token::BitwiseAnd, parse_math_expr_shift)
+ fn parse_expr_bit_and(&mut self) -> Result<Expr> {
+ expr_parser!(self, Token::BitwiseAnd, parse_expr_shift)
}
- fn parse_math_expr_bit_or(&mut self) -> Result<Expr> {
- math_expr_parser!(self, Token::BitwiseOr, parse_math_expr_bit_and)
+ fn parse_expr_bit_or(&mut self) -> Result<Expr> {
+ expr_parser!(self, Token::BitwiseOr, parse_expr_bit_and)
}
- fn parse_math_expr_compare(&mut self) -> Result<Expr> {
- math_expr_parser!(
+ fn parse_expr_compare(&mut self) -> Result<Expr> {
+ expr_parser!(
self,
Token::Equal | Token::NotEqual |
Token::LessThan | Token::GreaterThan |
Token::LessEqual | Token::GreaterEqual,
- parse_math_expr_bit_or
+ parse_expr_bit_or
)
}
- fn parse_math_expr_and(&mut self) -> Result<Expr> {
- math_expr_parser!(self, Token::Add, parse_math_expr_compare)
+ fn parse_expr_and(&mut self) -> Result<Expr> {
+ let mut expr = self.parse_expr_compare()?;
+ loop {
+ let tok = self.lexer.peek_token()?;
+ match tok {
+ Token::And => {
+ self.force_token(Token::And)?;
+ let temp = self.parse_expr_compare()?;
+ expr = Expr::And(Box::new(expr), Box::new(temp));
+ },
+ _ => break
+ }
+ }
+ Ok(expr)
}
- fn parse_math_expr_or(&mut self) -> Result<Expr> {
- math_expr_parser!(self, Token::Or, parse_math_expr_and)
+ fn parse_expr(&mut self) -> Result<Expr> {
+ let mut expr = self.parse_expr_and()?;
+ loop {
+ let tok = self.lexer.peek_token()?;
+ match tok {
+ Token::Or => {
+ self.force_token(Token::Or)?;
+ let temp = self.parse_expr_and()?;
+ expr = Expr::Or(Box::new(expr), Box::new(temp));
+ },
+ _ => break
+ }
+ }
+ Ok(expr)
}
- fn parse_math_expr(&mut self) -> Result<Expr> {
- math_expr_parser_reverse!(
- self,
- Token::Assign,
- parse_math_expr_or,
- parse_math_expr
- )
+ fn parse_assign(&mut self) -> Result<Assign> {
+ let expr = self.parse_expr()?;
+ if !expr.is_assignable() {
+ return Ok(Assign::Expr(expr));
+ }
+ if self.lexer.peek_token()? != Token::Assign {
+ return Ok(Assign::Expr(expr));
+ }
+ self.lexer.next_token()?;
+ Ok(Assign::Assign(expr, Box::new(self.parse_assign()?)))
}
fn parse_params(&mut self) -> Result<Vec<Rc<str>>> {
@@ -256,7 +421,7 @@ impl Parser {
match tok {
Ident(ident) => return Ok(vec![ident]),
LeftParen => (),
- _ => return Err(Error::UnexpectedToken(tok)),
+ _ => return Err(Error::UnexpectedToken(tok).into()),
}
let mut params = Vec::new();
@@ -273,7 +438,7 @@ impl Parser {
match next {
Comma => continue,
RightParen => break,
- _ => return Err(Error::UnexpectedToken(next)),
+ _ => return Err(Error::UnexpectedToken(next).into()),
}
}
@@ -284,7 +449,7 @@ impl Parser {
if let Token::Ident(ident) = self.lexer.next_token()? {
Ok(ident)
} else {
- Err(Error::ExpectedTokenName("Ident"))
+ Err(Error::ExpectedTokenName("Ident").into())
}
}
@@ -292,160 +457,165 @@ impl Parser {
if let Token::Ident(ident) = self.lexer.next_token_nl()? {
Ok(ident)
} else {
- Err(Error::ExpectedTokenName("Ident"))
+ Err(Error::ExpectedTokenName("Ident").into())
}
}
- fn parse_function(&mut self) -> Result<Expr> {
+ fn parse_function(&mut self) -> Result<Stmt> {
self.force_token(Token::Function)?;
let ident = self.parse_ident()?;
let params = match self.lexer.peek_token()? {
Token::LeftBrace => vec![],
_ => self.parse_params()?,
};
- let block = self.parse_block()?;
- Ok(Expr::Function(ident, params, block))
+ let stmt = self.parse_stmt()?;
+ Ok(Stmt::Function(ident, params, Box::new(stmt)))
}
- fn parse_do_while(&mut self) -> Result<Expr> {
+ fn parse_do_while(&mut self) -> Result<Stmt> {
self.force_token(Token::Do)?;
- let block = self.parse_block()?;
+ let stmt = self.parse_stmt()?;
self.force_token(Token::While)?;
- let math_expr = self.parse_math_expr()?;
- Ok(Expr::DoWhile(Box::new(math_expr), block))
+ let expr = self.parse_expr()?;
+ Ok(Stmt::DoWhile(expr, Box::new(stmt)))
}
- fn parse_while(&mut self) -> Result<Expr> {
+ fn parse_while(&mut self) -> Result<Stmt> {
self.force_token(Token::While)?;
- let math_expr = self.parse_math_expr()?;
- let block = self.parse_block()?;
- Ok(Expr::While(Box::new(math_expr), block))
+ let expr = self.parse_expr()?;
+ let stmt = self.parse_stmt()?;
+ Ok(Stmt::While(expr, Box::new(stmt)))
}
- fn parse_loop(&mut self) -> Result<Expr> {
+ fn parse_loop(&mut self) -> Result<Stmt> {
self.force_token(Token::Loop)?;
- let block = self.parse_block()?;
- Ok(Expr::Loop(block))
+ let stmt = self.parse_stmt()?;
+ Ok(Stmt::Loop(Box::new(stmt)))
}
- fn parse_if(&mut self) -> Result<Expr> {
+ fn parse_if(&mut self) -> Result<Stmt> {
self.force_token(Token::If)?;
- let math_expr = Box::new(self.parse_expr()?);
- let expr = Box::new(self.parse_expr()?);
+ let expr = self.parse_expr()?;
+ let stmt = Box::new(self.parse_stmt()?);
if self.lexer.peek_token()? != Token::Else {
- return Ok(Expr::If(math_expr, expr, None))
+ return Ok(Stmt::If(expr, stmt, None))
}
self.lexer.next_token()?;
if self.lexer.peek_token()? == Token::If {
- Ok(Expr::If(math_expr, expr, Some(Box::new(self.parse_if()?))))
+ Ok(Stmt::If(expr, stmt, Some(Box::new(self.parse_if()?))))
} else {
- Ok(Expr::If(math_expr, expr, Some(Box::new(self.parse_expr()?))))
+ Ok(Stmt::If(expr, stmt, Some(Box::new(self.parse_stmt()?))))
}
}
- fn parse_let(&mut self) -> Result<Expr> {
+ fn parse_let(&mut self) -> Result<Stmt> {
self.force_token(Token::Let)?;
let ident = self.parse_ident_nl()?;
if self.lexer.peek_token_nl()? == Token::Assign {
self.force_token_nl(Token::Assign)?;
- Ok(Expr::Let(ident, Box::new(self.parse_math_expr()?)))
+ Ok(Stmt::Let(ident, self.parse_expr()?))
} else {
- Ok(Expr::Let(ident, Box::new(Expr::Literal(Value::Nil))))
+ Ok(Stmt::Let(ident, Expr::Literal(Value::Nil)))
}
}
- fn parse_return(&mut self) -> Result<Expr> {
+ fn parse_return(&mut self) -> Result<Stmt> {
self.force_token(Token::Return)?;
- Ok(Expr::Return(Box::new(self.parse_math_expr()?)))
+ Ok(Stmt::Return(self.parse_expr()?))
}
- fn parse_expr(&mut self) -> Result<Expr> {
+ fn parse_stmt(&mut self) -> Result<Stmt> {
use Token::*;
match self.lexer.peek_token()? {
Do => self.parse_do_while(),
While => self.parse_while(),
Let => self.parse_let(),
- LeftBrace => {
- let idx = self.lexer.index;
- if let Ok(table) = self.parse_math_expr() {
- Ok(table)
- } else {
- self.lexer.index = idx;
- Ok(Expr::Block(self.parse_block()?))
- }
- },
+ LeftBrace => self.parse_block(),
Return => self.parse_return(),
If => self.parse_if(),
Loop => self.parse_loop(),
Break => {
self.lexer.next_token()?;
- Ok(Expr::Break)
+ Ok(Stmt::Break)
},
Continue => {
self.lexer.next_token()?;
- Ok(Expr::Continue)
+ Ok(Stmt::Continue)
},
- _ => {
- let math_expr = self.parse_math_expr()?;
- Ok(math_expr)
+ _ => {
+ let assign = self.parse_assign()?;
+ Ok(match assign {
+ ast::Assign::Expr(expr) => Stmt::Expr(expr),
+ _ => Stmt::Assign(assign),
+ })
}
}
}
- fn parse_block(&mut self) -> Result<Vec<Expr>> {
+ fn parse_block(&mut self) -> Result<Stmt> {
let mut block = Vec::new();
self.force_token(Token::LeftBrace)?;
loop {
- let expr = match self.lexer.peek_token()? {
+ let stmt = match self.lexer.peek_token()? {
Token::RightBrace => break,
Token::SemiColon => {
self.lexer.next_token()?;
continue;
}
- _ => self.parse_expr()?
+ _ => self.parse_stmt()?
};
- block.push(expr);
+ block.push(stmt);
let next = self.lexer.next_token()?;
match next {
Token::SemiColon => continue,
Token::RightBrace => break,
- _ => return Err(Error::UnexpectedToken(next))
+ _ => return Err(Error::UnexpectedToken(next).into())
}
}
- self.force_token(Token::RightBrace)?;
- Ok(block)
+ if self.lexer.peek_token()? == Token::RightBrace {
+ self.lexer.next_token()?;
+ }
+ Ok(Stmt::Block(block))
}
- fn parse_root_expr(&mut self) -> Result<Expr> {
+ fn parse_root_stmt(&mut self) -> Result<Stmt> {
if self.lexer.peek_token()? == Token::Function {
self.parse_function()
} else {
- self.parse_expr()
+ self.parse_stmt()
}
}
- pub fn parse<T: Into<Lexer>>(into: T) -> Result<Expr> {
- let mut parser = Self { lexer: into.into() };
+ pub fn parse<T: Into<String>>(&mut self, into: T) -> Result<Stmt> {
+ let lexer = Lexer::new(into);
+ self.lexer = lexer;
+
let mut block = Vec::new();
loop {
- let expr = match parser.lexer.peek_token()? {
+ let expr = match self.lexer.peek_token()? {
Token::Eof => break,
Token::SemiColon => {
- parser.lexer.next_token()?;
+ self.lexer.next_token()?;
continue;
}
- _ => parser.parse_root_expr()?
+ _ => self.parse_root_stmt()?
};
block.push(expr);
- let next = parser.lexer.next_token()?;
+ let next = self.lexer.next_token()?;
match next {
Token::SemiColon => continue,
Token::Eof => break,
- _ => return Err(Error::UnexpectedToken(next))
+ _ => return Err(Error::UnexpectedToken(next).into())
}
}
- Ok(Expr::Block(block))
+
+ let ast = Stmt::Block(block);
+ if self.optimize {
+ Ok(optimize(ast)?)
+ } else {
+ Ok(ast)
+ }
}
}
diff --git a/matrix/src/value.rs b/matrix/src/value.rs
new file mode 100644
index 0000000..3ff7e56
--- /dev/null
+++ b/matrix/src/value.rs
@@ -0,0 +1,610 @@
+use std::{collections::HashMap, rc::Rc, hash::Hash, fmt::Display, ops::{Add, Neg, Not, Sub, Div, Mul, BitOr, BitAnd, BitXor, Shl, Shr}, cmp::Ordering};
+
+use num_complex::Complex64;
+use num_rational::Rational64;
+use regex::Regex;
+
+use crate::{ast::{Expr, BinaryOp, UnaryOp}, gc::Gc, chunk::Function, Result};
+
+pub type List = Vec<Value>;
+pub type Matrix = (usize, usize, Vec<Value>);
+pub type Table = ValueMap;
+
+pub type InlineList = Vec<Expr>;
+pub type InlineMatrix = (usize, usize, Vec<Expr>);
+pub type InlineTable = Vec<(Expr, Expr)>;
+
+#[derive(Debug, Clone)]
+pub struct ValueMap(HashMap<Value, Value>);
+
+impl ValueMap {
+ pub fn new() -> Self {
+ Self(HashMap::new())
+ }
+ pub fn get(&self, key: &Value) -> Result<Option<&Value>> {
+ key.can_hash()?;
+ Ok(self.0.get(key))
+ }
+ pub fn insert(&mut self, key: Value, value: Value) -> Result<()> {
+ key.can_hash()?;
+ self.0.insert(key, value);
+ Ok(())
+ }
+}
+
+#[derive(Debug)]
+pub enum Error {
+ Neg(Value),
+ Add(Value, Value),
+ Subtract(Value, Value),
+ Multiply(Value, Value),
+ Divide(Value, Value),
+ Modulo(Value, Value),
+ Exponent(Value, Value),
+ Compare(Value, Value),
+ IndexOutOfBounds(usize, usize),
+ Index(Value, Value),
+ Concat(Value, Value),
+ Bitwise(Value, Value),
+ DivideByZero,
+ ZeroExpZero,
+ CannotHash(Value),
+}
+
+impl Display for self::Error {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ use self::Error::*;
+ match self {
+ Neg(v) => write!(f, "cannot negate {v:?}"),
+ Add(a, b) => write!(f, "cannot add {a:?} to {b:?}"),
+ Subtract(a, b) => write!(f, "cannot subtract {a:?} from {b:?}"),
+ Multiply(a, b) => write!(f, "cannot multiply {a:?} and {b:?}"),
+ Divide(a, b) => write!(f, "cannot divide {a:?} and {b:?}"),
+ Modulo(a, b) => write!(f, "cannot modulo {a:?} and {b:?}"),
+ Exponent(a, b) => write!(f, "cannot compute {a:?} pow {b:?}"),
+ Compare(a, b) => write!(f, "cannot compare {a:?} and {b:?}"),
+ IndexOutOfBounds(a, b) => write!(f, "index {a} out of bounds for list of length {b}"),
+ Index(a, b) => write!(f, "cannot index {a:?} with {b:?}"),
+ Concat(a, b) => write!(f, "cannot concat {a:?} and {b:?}"),
+ Bitwise(a, b) => write!(f, "cannot bitwise between {a:?} and {b:?}"),
+ DivideByZero => write!(f, "attempted to divide by zero"),
+ ZeroExpZero => write!(f, "cannot exponent zero with zero"),
+ CannotHash(v) => write!(f, "cannot hash {v:?}"),
+ }
+ }
+}
+
+impl std::error::Error for self::Error {}
+
+#[derive(Debug, Clone)]
+pub enum Value {
+ Nil,
+ Bool(bool),
+ Int(i64),
+ Float(f64),
+ Ratio(Rational64),
+ Complex(Complex64),
+ Regex(Gc<Regex>),
+ String(Gc<String>),
+ List(Gc<List>),
+ Matrix(Gc<Matrix>),
+ Table(Gc<Table>),
+ Function(Rc<Function>),
+}
+
+impl Hash for Value {
+ fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+ 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.hash(state);
+ }
+ }
+ Matrix(m) => {
+ m.0.hash(state);
+ m.1.hash(state);
+ for val in m.2.iter() {
+ val.hash(state);
+ }
+ },
+ _ => panic!("tried to hash {self:?}")
+ };
+ }
+}
+
+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.2.iter() {
+ val.can_hash()?;
+ }
+ },
+ _ => return Err(Error::CannotHash(self.clone()).into())
+ }
+ Ok(())
+ }
+
+ pub fn boring_print(&self) -> String {
+ use Value::*;
+ match self {
+ Nil => format!("nil"),
+ Bool(b) => format!("{b}"),
+ Int(i) => format!("{i}"),
+ Float(l) => format!("{l}"),
+ Ratio(r) => format!("{r}"),
+ Complex(c) => format!("{c}"),
+ Regex(r) => format!("{r}"),
+ String(s) => format!("{s}"),
+ List(l) => {
+ let mut str = "[ ".to_string();
+ for (i, el) in l.iter().enumerate() {
+ if i != 0 {
+ str.push_str(" ");
+ }
+ str.push_str(&el.boring_print());
+ }
+ str.push_str(" ]");
+ str
+ },
+ Matrix(m) => {
+ let mut str = "[[ ".to_string();
+ for (i, el) in m.2.iter().enumerate() {
+ if i != 0 {
+ if (i % m.1) == 0 {
+ str.push_str(" ; ");
+ } else {
+ str.push_str(" ");
+ }
+ }
+ str.push_str(&el.boring_print());
+ }
+ str.push_str(" ]]");
+ str
+ },
+ Table(t) => {
+ let mut str = "{{ ".to_string();
+ for (i, (key, val)) in t.0.iter().enumerate() {
+ if i != 0 {
+ str.push_str(", ");
+ }
+ str.push_str(&key.boring_print());
+ str.push_str(" = ");
+ str.push_str(&val.boring_print());
+ }
+ str.push_str(" }}");
+ str
+ },
+ Function(fun) => {
+ format!("[Function: {}]", fun.name)
+ }
+ }
+ }
+
+ pub fn pretty_print(&self) -> String {
+ use Value::*;
+ match self {
+ Nil => format!("\x1b[34m{}\x1b[0m", self.boring_print()),
+ Bool(_) => format!("\x1b[33m{}\x1b[0m", self.boring_print()),
+ Int(_) => format!("\x1b[33m{}\x1b[0m", self.boring_print()),
+ Float(_) => format!("\x1b[33m{}\x1b[0m", self.boring_print()),
+ Ratio(_) => format!("\x1b[33m{}\x1b[0m", self.boring_print()),
+ Complex(_) => format!("\x1b[33m{}\x1b[0m", self.boring_print()),
+ Regex(_) => format!("\x1b[31m{}\x1b[0m", self.boring_print()),
+ String(_) => format!("\x1b[32m'{}'\x1b[0m", self.boring_print()),
+ List(l) => {
+ let mut str = "[ ".to_string();
+ for (i, el) in l.iter().enumerate() {
+ if i != 0 {
+ str.push_str(" ");
+ }
+ str.push_str(&el.pretty_print());
+ }
+ str.push_str(" ]");
+ str
+ },
+ Matrix(m) => {
+ let mut str = "[[ ".to_string();
+ for (i, el) in m.2.iter().enumerate() {
+ if i != 0 {
+ if (i % m.1) == 0 {
+ str.push_str(" ; ");
+ } else {
+ str.push_str(" ");
+ }
+ }
+ str.push_str(&el.pretty_print());
+ }
+ str.push_str(" ]]");
+ str
+ },
+ Table(t) => {
+ let mut str = "{{ ".to_string();
+ for (i, (key, val)) in t.0.iter().enumerate() {
+ if i != 0 {
+ str.push_str(", ");
+ }
+ str.push_str(&key.pretty_print());
+ str.push_str(" = ");
+ str.push_str(&val.pretty_print());
+ }
+ str.push_str(" }}");
+ str
+ },
+ Function(_) => {
+ format!("\x1b[36m{}\x1b[0m", self.boring_print())
+ }
+ }
+ }
+}
+
+impl Display for Value {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}", self.pretty_print())
+ }
+}
+
+fn ratio_to_f64(r: Rational64) -> f64 {
+ *r.numer() as f64 / *r.denom() as f64
+}
+
+fn promote(a: Value, b: Value) -> (Value, Value) {
+ use Value::*;
+ match (&a, &b) {
+ (Int(x), Ratio(..)) => (Ratio((*x).into()), b),
+ (Int(x), Float(..)) => (Float(*x as f64), b),
+ (Int(x), Complex(..)) => (Complex((*x as f64).into()), b),
+ (Ratio(x), Float(..)) => (Float(ratio_to_f64(*x)), b),
+ (Ratio(x), Complex(..)) => (Complex(ratio_to_f64(*x).into()), b),
+ (Float(x), Complex(..)) => (Complex((*x).into()), b),
+ (Ratio(..), Int(y)) => (a, Ratio((*y).into())),
+ (Float(..), Int(y)) => (a, Float(*y as f64)),
+ (Complex(..), Int(y)) => (a, Complex((*y as f64).into())),
+ (Float(..), Ratio(y)) => (a, Float(ratio_to_f64(*y))),
+ (Complex(..), Ratio(y)) => (a, Complex(ratio_to_f64(*y).into())),
+ (Complex(..), Float(y)) => (a, Complex((*y).into())),
+ _ => (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)),
+ (String(str), value) => Ok(String(Gc::new(
+ format!("{str}{}", value.boring_print())
+ ))),
+ (value, String(str)) => Ok(String(Gc::new(
+ format!("{}{str}", value.boring_print())
+ ))),
+ (List(mut l1), List(l2)) => {
+ l1.extend_from_slice(&l2);
+ Ok(List(l1))
+ },
+ (l, r) => Err(Error::Add(l, r).into())
+ }
+ }
+}
+
+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)),
+ (l, r) => Err(Error::Subtract(l, r).into())
+ }
+ }
+}
+
+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)),
+ (l, r) => Err(Error::Multiply(l, r).into())
+ }
+ }
+}
+
+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::DivideByZero.into()),
+ (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::Divide(l, r).into())
+ }
+ }
+}
+
+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::Bitwise(l, r).into())
+ }
+ }
+}
+
+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::Bitwise(l, r).into())
+ }
+ }
+}
+
+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::Bitwise(l, r).into())
+ }
+ }
+}
+
+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::Bitwise(l, r).into())
+ }
+ }
+}
+
+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::Bitwise(l, r).into())
+ }
+ }
+}
+
+fn ipow(n: i64, d: i64, p: i64) -> Result<(i64, i64)> {
+ Ok(match (n, d, p) {
+ (0, _, 0) => return Err(Error::ZeroExpZero.into()),
+ (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)),
+ })
+}
+
+impl Value {
+ pub fn modulo(self, rhs: Value) -> Result<Self> {
+ use Value::*;
+ match promote(self, rhs) {
+ (Int(x), Int(y)) => Ok(Int(x.rem_euclid(y))),
+ (Float(x), Float(y)) => Ok(Float(x.rem_euclid(y))),
+ (Ratio(_x), Ratio(_y)) => todo!("ratio modulo"),
+ (Complex(_x), Complex(_y)) => todo!("complex modulo"),
+ (l, r) => Err(Error::Modulo(r, l).into())
+ }
+ }
+
+ pub fn int_div(self, rhs: Value) -> Result<Self> {
+ use Value::*;
+ match promote(self, rhs) {
+ (Int(x), Int(y)) => Ok(Int(x.div_euclid(y))),
+ (Float(x), Float(y)) => Ok(Float(x.div_euclid(y))),
+ (Ratio(_x), Ratio(_y)) => todo!("ratio integer division"),
+ (Complex(_x), Complex(_y)) => todo!("complex integer division"),
+ (l, r) => Err(Error::Divide(l, r).into())
+ }
+ }
+
+ 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::Exponent(l, r).into())
+ }
+ }
+}
+
+impl Eq for Value {}
+
+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.0 == b.0 && *a.2 == *b.2,
+ _ => 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.2.partial_cmp(&b.2),
+ _ => None,
+ }
+ }
+}
+
+impl Value {
+ pub fn val_cmp(&self, other: &Self) -> Result<Ordering> {
+ self.partial_cmp(other)
+ .map_or_else(|| Err(Error::Compare(self.clone(), other.clone()).into()), Ok)
+ }
+
+ pub fn store_index(&self, _other: &Vec<Self>, _value: &Self) -> Result<()> {
+ todo!()
+ }
+
+ pub fn index(&self, _other: &Vec<Self>) -> Result<Self> {
+ todo!()
+ //use Value::*;
+ //match (self, other) {
+ // (List(l), Int(i)) => {
+ // if *i >= 0 && *i < l.len() as i64 {
+ // Ok(l[*i as usize].clone())
+ // } else {
+ // Err(Error::IndexOutOfBounds(*i as usize, l.len()))
+ // }
+ // },
+ // (l, r) => Err(Error::Index(l.clone(), r.clone()))
+ //}
+ }
+
+ 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 => todo!(),
+ }
+ }
+
+ 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::*;
+ match self {
+ Nil => true,
+ Bool(b) => !b,
+ Int(i) => i == 0,
+ Float(f) => f == 0.0,
+ Ratio(r) => *(r.numer()) == 0 || *(r.denom()) == 0,
+ Complex(c) => !c.is_normal(),
+ Regex(_) => false,
+ List(_) => false,
+ Matrix(_) => false,
+ Table(_) => false,
+ String(s) => s.as_str() == "",
+ Function(_) => false,
+ }
+ }
+}
+
diff --git a/matrix/src/vm.rs b/matrix/src/vm.rs
new file mode 100644
index 0000000..c63761f
--- /dev/null
+++ b/matrix/src/vm.rs
@@ -0,0 +1,242 @@
+use std::{collections::HashMap, rc::Rc, fmt::{Debug, Display}, usize, cell::RefCell, ops::{Index, IndexMut}};
+use crate::{value::{Value, self, ValueMap}, gc::Gc, chunk::{Function, Instruction}, compiler::Globals, Result};
+
+#[derive(Debug)]
+pub enum Error {
+ ValueError(value::Error),
+ NotFunction(Value),
+ InvArity(usize, usize, Rc<str>),
+ UndefinedGlobal(Rc<str>),
+}
+
+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}"),
+ UndefinedGlobal(name) => write!(f, "{name} is not defined"),
+ }
+ }
+}
+
+impl From<value::Error> for Error {
+ fn from(value: value::Error) -> Self {
+ Error::ValueError(value)
+ }
+}
+pub struct Stack<T> {
+ inner: Vec<T>
+}
+
+impl<T> Stack<T> {
+ pub fn new() -> Self {
+ Self { inner: vec![] }
+ }
+
+ pub fn pop(&mut self) -> T {
+ self.inner.pop().expect("stack empty")
+ }
+
+ pub fn push(&mut self, val: T) {
+ self.inner.push(val)
+ }
+
+ pub fn split_off(&mut self, len: usize) -> Self {
+ let inner = self.inner.split_off(len);
+ Self { inner }
+ }
+
+ pub fn truncate(&mut self, len: usize) {
+ self.inner.truncate(len);
+ }
+
+ pub fn len(&self) -> usize {
+ self.inner.len()
+ }
+}
+
+impl<T: Display> Display for Stack<T> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f,"[ ")?;
+ for (i, el) in self.inner.iter().enumerate() {
+ if i != 0 {
+ write!(f, ", ")?;
+ }
+ write!(f, "{el}")?;
+ }
+ write!(f, " ]")?;
+ Ok(())
+ }
+}
+
+impl<T> IndexMut<usize> for Stack<T> {
+ fn index_mut(&mut self, index: usize) -> &mut Self::Output {
+ &mut self.inner[index]
+ }
+}
+
+impl<T> Index<usize> for Stack<T> {
+ type Output = T;
+
+ fn index(&self, index: usize) -> &Self::Output {
+ &self.inner[index]
+ }
+}
+
+struct StackFrame {
+ fun: Rc<Function>,
+ ip: usize,
+ bp: usize,
+}
+
+impl StackFrame {
+ fn new(vm: &Vm, fun: Rc<Function>) -> Self {
+ Self {
+ bp: vm.stack.len() - fun.arity,
+ ip: 0,
+ fun,
+ }
+ }
+}
+
+pub struct Vm {
+ stack: Stack<Value>,
+ frames: Vec<StackFrame>,
+ global: HashMap<String, Value>,
+ global_table: Globals,
+}
+
+impl Vm {
+
+ fn push(&mut self, val: Value) {
+ self.stack.push(val)
+ }
+
+ fn pop(&mut self) -> Value {
+ self.stack.pop()
+ }
+
+ pub fn new() -> Self {
+ Self {
+ stack: Stack::new(),
+ frames: Vec::new(),
+ global: HashMap::new(),
+ global_table: Rc::new(RefCell::new(Vec::new()))
+ }
+ }
+
+ pub fn globals(&self) -> Globals {
+ self.global_table.clone()
+ }
+
+ pub fn run(&mut self, fun: Rc<Function>) -> Result<Value> {
+ let mut frame = StackFrame::new(&self, fun);
+ loop {
+ use Instruction::*;
+ let ins = frame.fun.body.code[frame.ip].clone();
+ frame.ip += 1;
+ match ins {
+ NoOp => {},
+ Load(idx) => self.push(self.stack[frame.bp + idx as usize].clone()),
+ Store(idx) => self.stack[frame.bp + idx as usize] = self.pop(),
+ LoadGlobal(idx) => {
+ let name = self.global_table.borrow()[idx as usize].clone();
+ let val = self.global
+ .get(name.as_ref())
+ .ok_or(self::Error::UndefinedGlobal(name.into()))?
+ .clone();
+ self.stack.push(val);
+ },
+ StoreGlobal(idx) => {
+ let name = self.global_table.borrow()[idx as usize].clone();
+ let store = self.pop();
+ self.global.insert(name.to_string(), store);
+ },
+ Const(idx) => self.push(frame.fun.body.constants[idx as usize].clone()),
+ Int(i) => self.push(Value::Int(i as i64)),
+ True => self.push(Value::Bool(true)),
+ False => self.push(Value::Bool(false)),
+ Nil => self.push(Value::Nil),
+ Dup => self.push(self.stack[self.stack.len() - 1].clone()),
+ Discard(count) => {self.stack.truncate(self.stack.len() - count as usize)},
+ UnaryOp(op) => {
+ let val = self.pop();
+ self.push(Value::unary_op(op, val));
+ },
+ BinaryOp(op) => {
+ let rhs = self.pop();
+ let lhs = self.pop();
+ self.push(Value::binary_op(op, lhs, rhs)?);
+ },
+ NewList(items) => {
+ let list = self.stack.split_off(self.stack.len() - items as usize);
+ self.push(Value::List(list.inner.into()));
+ },
+ NewTable(items) => {
+ let mut table = ValueMap::new();
+ for _ in 0..items {
+ let value = self.pop();
+ let key = self.pop();
+ table.insert(key, value)?;
+ }
+ self.push(Value::Table(table.into()))
+ },
+ NewMatrix(items, codomain) => {
+ let list = self.stack.split_off(self.stack.len() - items as usize);
+ let codomain = codomain as usize;
+ let domain = list.len() / codomain;
+ self.push(Value::Matrix(Gc::new((domain, codomain, list.inner))));
+ }
+ Index(count) => {
+ let index = self.stack.split_off(self.stack.len() - count as usize);
+ let collection = self.pop();
+ let value = collection.index(&index.inner)?;
+ self.stack.push(value);
+ },
+ StoreIndex(count) => {
+ let value = self.pop();
+ let index = self.stack.split_off(self.stack.len() - count as usize);
+ let collection = self.pop();
+ collection.store_index(&index.inner, &value)?;
+ }
+ Jump(idx) => frame.ip = idx as usize,
+ JumpTrue(idx) => {
+ if !!self.pop() {
+ frame.ip = idx as usize;
+ }
+ },
+ JumpFalse(idx) => {
+ if !self.pop() {
+ frame.ip = idx as usize;
+ }
+ },
+ Call(arity) => {
+ let fun = self.pop();
+ let Value::Function(fun) = fun else {
+ return Err(Error::NotFunction(fun).into())
+ };
+ if fun.arity != arity as usize {
+ return Err(Error::InvArity(fun.arity, arity as usize, fun.name.clone()).into())
+ }
+ let new_frame = StackFrame::new(&self, fun);
+ self.frames.push(frame);
+ frame = new_frame;
+ },
+ Return => {
+ let Some(prev_frame) = self.frames.pop() else {
+ break;
+ };
+ let ret = self.pop();
+ self.stack.truncate(frame.bp);
+ self.push(ret);
+ frame = prev_frame;
+ },
+ }
+ }
+ Ok(self.pop())
+ }
+}