diff --git a/Cargo.lock b/Cargo.lock index f61fea6..30ca9f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,54 @@ dependencies = [ "memchr", ] +[[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" @@ -35,6 +83,46 @@ version = "1.0.0" 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" @@ -44,6 +132,12 @@ dependencies = [ "error-code", ] +[[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" @@ -77,6 +171,12 @@ dependencies = [ "windows-sys", ] +[[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" @@ -115,9 +215,10 @@ dependencies = [ ] [[package]] -name = "matrix-repl" +name = "matrix-bin" version = "0.1.0" dependencies = [ + "clap", "matrix", "rustyline", ] @@ -198,6 +299,24 @@ dependencies = [ "autocfg", ] +[[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" @@ -278,6 +397,29 @@ version = "1.13.1" 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" 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 similarity index 100% rename from matrix-repl/Cargo.lock rename to 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, + + /// 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) { + + 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; -pub type Matrix = (u16, u16, Vec); -pub type Table = HashMap, Value>; - -pub type InlineList = Vec; -pub type InlineMatrix = (u16, u16, Vec); -pub type InlineTable = Vec<(Expr, Expr)>; - -pub type RcList = Rc; -pub type RcMatrix = Rc; -pub type RcString = Rc; -pub type RcTable = Rc; -pub type RcRegex = Rc; - -#[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 for UnaryOp { - type Error = (); - - fn try_from(value: Token) -> Result { - 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,26 +31,330 @@ pub enum BinaryOp { LessEquals, GreaterThan, LessThan, - And, - Or, - // assignment - Assign + Range, } -impl TryFrom for BinaryOp { - type Error = (); +#[derive(Debug, Clone, PartialEq)] +pub enum Expr { + Literal(Value), + Ident(Rc), - fn try_from(value: Token) -> Result { + UnaryOp(Box, UnaryOp), + BinaryOp(Box, Box, BinaryOp), + + Index(Box, Vec), + FnCall(Box, Vec), + FieldAccess(Box, Box), + + List(InlineList), + Matrix(InlineMatrix), + Table(InlineTable), + + And(Box, Box), + Or(Box, Box), +} + +#[derive(Debug, Clone, PartialEq)] +pub enum Assign { + Expr(Expr), + Assign(Expr, Box) +} + +#[derive(Debug, Clone, PartialEq)] +pub enum Stmt { + NoOp, + + If(Expr, Box, Option>), + Function(Rc, Vec>, Box), + + Loop(Box), + While(Expr, Box), + DoWhile(Expr, Box), + + Block(Vec), + Expr(Expr), + + Let(Rc, Expr), + Assign(Assign), + + Continue, + Break, + Return(Expr), +} + +impl Neg for BinaryOp { + type Output = Option; + fn neg(self) -> Self::Output { + use BinaryOp::*; + Some(match self { + Add => Subtract, + Subtract => Add, + _ => return None + }) + } +} + +impl Not for BinaryOp { + type Output = Option; + 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; + fn neg(self) -> Self::Output { + use Expr::*; + 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 { + 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; + 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)) + } + } + _ => return Err(self) + }) + } +} + +fn optimize_expr(expr: Expr) -> Result { + 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::>>()?) + } + 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::>>()?), + Matrix(mat) => + Matrix((mat.0, mat.1, + mat.2.into_iter().map(optimize_expr) + .collect::>>()?)), + Table(table) => + Table(table + .into_iter() + .map(|(k, v)| { + let k = optimize_expr(k)?; + let v = optimize_expr(v)?; + Ok((k, v)) + }).collect::>>()?), + 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 { + 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 { + use Stmt::*; + Ok(match stmt { + Block(b) => { + let b: Vec = + b.into_iter() + .map(optimize) + .collect::>>()? + .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 for UnaryOp { + fn from(value: Token) -> Self { use Token::*; - Ok(match value { - Equal => BinaryOp::Equals, - NotEqual => Self::Equals, + match value { + Subtract => Self::Negate, + Not => Self::Not, + _ => panic!("aaaaa") + } + } +} + +impl From 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, - And => Self::And, - Or => Self::Or, BitwiseShiftLeft => Self::BitwiseShiftLeft, BitwiseShiftRight => Self::BitwiseShiftRight, BitwiseAnd => Self::BitwiseAnd, @@ -109,54 +366,20 @@ impl TryFrom for BinaryOp { Divide => Self::Divide, Modulo => Self::Modulo, Power => Self::Power, - Assign => Self::Assign, - _ => return Err(()) - }) - } -} - -#[derive(Debug)] -pub enum Expr { - Literal(Value), - Ident(Rc), - - UnaryOp(Box, UnaryOp), - BinaryOp(Box, Box, BinaryOp), - - Let(Rc, Box), - - Index(Box, Vec), - FnCall(Box, Vec), - - List(InlineList), - Matrix(InlineMatrix), - Table(InlineTable), - - Block(Vec), - Function(Rc, Vec>, Vec), - - Continue, - Break, - Return(Box), - - If(Box, Box, Option>), - Loop(Vec), - While(Box, Vec), - DoWhile(Box, Vec) -} - -impl Expr { - pub fn optimize(self) -> Self { - use Expr::*; - match self { - Block(mut block) => { - if block.len() == 1 { - block.pop().unwrap().optimize() - } else { - Block(block.into_iter().map(|e| e.optimize()).collect()) - } - } - _ => self + 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, + pub code: Vec +} + +impl Chunk { + pub fn new() -> Self { + Self { + constants: Vec::new(), + code: Vec::new() + } + } + + pub fn from_compiled(_buf: &[u8]) -> Result { + 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, + 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>>>; + +pub struct CompilerBuilder<'c> { + globals: Globals, + repl: bool, + debug: bool, + name: Rc, + 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: "".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) -> 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, + parent: Option<&'c Compiler<'c>>, + + locals: Vec>, + globals: Rc>>>, + + root_is_block: bool, + + scope: usize, + chunk: Chunk, + + loop_top: Vec<(usize, usize)>, + loop_bot: Vec, + + repl: bool, + debug: bool, +} + +struct Local { + name: Rc, + idx: usize, + scope: usize +} + +#[derive(Debug, Clone)] +pub enum Error { + Undefined(Rc), + Redefined(Rc), + 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) -> 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) -> Rc { + 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) -> usize { + self.globals.borrow_mut().push(name); + let c = self.globals.borrow().len() - 1; + c + } + + fn create_local_checked(&mut self, name: Rc) -> Result> { + 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) -> Result { + 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> { + 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 { + 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, params: &Vec>, body: &Box) -> Result { + 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> { + 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 { + ptr: NonNull>, + phantom: PhantomData>, +} + +pub struct GcInner { + rc: usize, + data: T, +} + +impl Gc { + pub fn new(data: T) -> Gc { + let boxed = Box::new(GcInner { + rc: 1, + data, + }); + Self { + ptr: NonNull::new(Box::into_raw(boxed)).unwrap(), + phantom: PhantomData, + } + } +} + +impl From> for Gc { + fn from(value: Rc) -> Self { + Self::new(Rc::unwrap_or_clone(value)) + } +} + +impl From for Gc { + fn from(value: T) -> Self { + Self::new(value) + } +} + +impl Deref for Gc { + type Target = T; + fn deref(&self) -> &T { + let inner = unsafe { self.ptr.as_ref() }; + &inner.data + } +} + +impl Debug for Gc { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let inner = unsafe { self.ptr.as_ref() }; + write!(f, "{:?}", inner.data) + } +} + +impl Display for Gc { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let inner = unsafe { self.ptr.as_ref() }; + write!(f, "{}", inner.data) + } +} + +impl PartialEq for Gc { + 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 PartialOrd for Gc { + fn partial_cmp(&self, other: &Self) -> Option { + let inner = unsafe { self.ptr.as_ref() }; + let other = unsafe { other.ptr.as_ref() }; + inner.data.partial_cmp(&other.data) + } +} + +impl DerefMut for Gc { + 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 Clone for Gc { + fn clone(&self) -> Self { + let inner = unsafe { self.ptr.as_ptr().as_mut().unwrap() }; + inner.rc += 1; + Self { + ptr: self.ptr, + phantom: PhantomData, + } + } +} + +impl Drop for Gc { + 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 = std::result::Result; - 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 { 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 { 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::(InvalidDigit(c).into())?; } - let ch = char::from_u32(n).ok_or(InvalidCodepoint)?; + let ch = char::from_u32(n).ok_or::(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::() { 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::() { 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 { @@ -427,16 +407,6 @@ impl Lexer { token } - pub fn peek_multiple_tokens(&mut self, count: usize) -> Result> { - 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 { 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); + +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 = std::result::Result; 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 = std::result::Result; - 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 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 { 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 { 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 { - 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)) - } + fn parse_fn_call(&mut self) -> Result> { + self.force_token(Token::LeftParen)?; + let mut params = Vec::new(); loop { - let expr = self.parse_math_expr()?; - list.push(expr); + 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::RightBrack => break, - _ => return Err(Error::UnexpectedToken(next)) - } + Token::RightParen => break, + _ => return Err(Error::UnexpectedToken(next).into()) + }; + } + Ok(params) + } + + fn parse_index(&mut self) -> Result> { + self.force_token(Token::LeftBrack)?; + 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> { + let mut part = Vec::new(); + loop { + 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 { + 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::SemiColon => continue, + Token::RightBrack => break, + _ => 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 { 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 { - 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 { 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 { 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 { + fn parse_expr_expr_access(&mut self) -> Result { + 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 { + 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 { 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 { - math_expr_parser_reverse!( + fn parse_expr_pow(&mut self) -> Result { + 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 { - math_expr_parser!(self, Token::Multiply | Token::Divide | Token::Modulo, parse_math_expr_pow) + fn parse_expr_mult(&mut self) -> Result { + expr_parser!(self, Token::Multiply | Token::Divide | Token::Modulo, parse_expr_pow) } - fn parse_math_expr_add(&mut self) -> Result { - math_expr_parser!(self, Token::Add | Token::Subtract, parse_math_expr_mult) + fn parse_expr_add(&mut self) -> Result { + expr_parser!(self, Token::Add | Token::Subtract, parse_expr_mult) } - fn parse_math_expr_shift(&mut self) -> Result { - math_expr_parser!( + fn parse_expr_shift(&mut self) -> Result { + expr_parser!( self, Token::BitwiseShiftLeft | Token::BitwiseShiftRight, - parse_math_expr_add + parse_expr_add ) } - fn parse_math_expr_bit_and(&mut self) -> Result { - math_expr_parser!(self, Token::BitwiseAnd, parse_math_expr_shift) + fn parse_expr_bit_and(&mut self) -> Result { + expr_parser!(self, Token::BitwiseAnd, parse_expr_shift) } - fn parse_math_expr_bit_or(&mut self) -> Result { - math_expr_parser!(self, Token::BitwiseOr, parse_math_expr_bit_and) + fn parse_expr_bit_or(&mut self) -> Result { + expr_parser!(self, Token::BitwiseOr, parse_expr_bit_and) } - fn parse_math_expr_compare(&mut self) -> Result { - math_expr_parser!( + fn parse_expr_compare(&mut self) -> Result { + 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 { - math_expr_parser!(self, Token::Add, parse_math_expr_compare) + fn parse_expr_and(&mut self) -> Result { + 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 { - math_expr_parser!(self, Token::Or, parse_math_expr_and) + fn parse_expr(&mut self) -> Result { + 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 { - math_expr_parser_reverse!( - self, - Token::Assign, - parse_math_expr_or, - parse_math_expr - ) + fn parse_assign(&mut self) -> Result { + 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>> { @@ -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 { + fn parse_function(&mut self) -> Result { 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 { + fn parse_do_while(&mut self) -> Result { 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 { + fn parse_while(&mut self) -> Result { 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 { + fn parse_loop(&mut self) -> Result { 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 { + fn parse_if(&mut self) -> Result { 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 { + fn parse_let(&mut self) -> Result { 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 { + fn parse_return(&mut self) -> Result { 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 { + fn parse_stmt(&mut self) -> Result { 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> { + fn parse_block(&mut self) -> Result { 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(stmt); + let next = self.lexer.next_token()?; + match next { + Token::SemiColon => continue, + Token::RightBrace => break, + _ => return Err(Error::UnexpectedToken(next).into()) + } + } + if self.lexer.peek_token()? == Token::RightBrace { + self.lexer.next_token()?; + } + Ok(Stmt::Block(block)) + } + + fn parse_root_stmt(&mut self) -> Result { + if self.lexer.peek_token()? == Token::Function { + self.parse_function() + } else { + self.parse_stmt() + } + } + + pub fn parse>(&mut self, into: T) -> Result { + let lexer = Lexer::new(into); + self.lexer = lexer; + + let mut block = Vec::new(); + loop { + let expr = match self.lexer.peek_token()? { + Token::Eof => break, + Token::SemiColon => { + self.lexer.next_token()?; + continue; + } + _ => self.parse_root_stmt()? }; block.push(expr); let next = self.lexer.next_token()?; match next { Token::SemiColon => continue, - Token::RightBrace => break, - _ => return Err(Error::UnexpectedToken(next)) + Token::Eof => break, + _ => return Err(Error::UnexpectedToken(next).into()) } } - self.force_token(Token::RightBrace)?; - Ok(block) - } - fn parse_root_expr(&mut self) -> Result { - if self.lexer.peek_token()? == Token::Function { - self.parse_function() + let ast = Stmt::Block(block); + if self.optimize { + Ok(optimize(ast)?) } else { - self.parse_expr() + Ok(ast) } } - - pub fn parse>(into: T) -> Result { - let mut parser = Self { lexer: into.into() }; - let mut block = Vec::new(); - loop { - let expr = match parser.lexer.peek_token()? { - Token::Eof => break, - Token::SemiColon => { - parser.lexer.next_token()?; - continue; - } - _ => parser.parse_root_expr()? - }; - block.push(expr); - let next = parser.lexer.next_token()?; - match next { - Token::SemiColon => continue, - Token::Eof => break, - _ => return Err(Error::UnexpectedToken(next)) - } - } - Ok(Expr::Block(block)) - } } 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; +pub type Matrix = (usize, usize, Vec); +pub type Table = ValueMap; + +pub type InlineList = Vec; +pub type InlineMatrix = (usize, usize, Vec); +pub type InlineTable = Vec<(Expr, Expr)>; + +#[derive(Debug, Clone)] +pub struct ValueMap(HashMap); + +impl ValueMap { + pub fn new() -> Self { + Self(HashMap::new()) + } + pub fn get(&self, key: &Value) -> Result> { + 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), + String(Gc), + List(Gc), + Matrix(Gc), + Table(Gc
), + Function(Rc), +} + +impl Hash for Value { + fn hash(&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; + 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; + 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; + 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; + 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; + 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; + 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; + 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; + 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; + 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 { + 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 { + 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 { + 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 { + 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 { + self.partial_cmp(other) + .map_or_else(|| Err(Error::Compare(self.clone(), other.clone()).into()), Ok) + } + + pub fn store_index(&self, _other: &Vec, _value: &Self) -> Result<()> { + todo!() + } + + pub fn index(&self, _other: &Vec) -> Result { + 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 { + 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), + UndefinedGlobal(Rc), +} + +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 for Error { + fn from(value: value::Error) -> Self { + Error::ValueError(value) + } +} +pub struct Stack { + inner: Vec +} + +impl Stack { + 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 Display for Stack { + 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 IndexMut for Stack { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + &mut self.inner[index] + } +} + +impl Index for Stack { + type Output = T; + + fn index(&self, index: usize) -> &Self::Output { + &self.inner[index] + } +} + +struct StackFrame { + fun: Rc, + ip: usize, + bp: usize, +} + +impl StackFrame { + fn new(vm: &Vm, fun: Rc) -> Self { + Self { + bp: vm.stack.len() - fun.arity, + ip: 0, + fun, + } + } +} + +pub struct Vm { + stack: Stack, + frames: Vec, + global: HashMap, + 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) -> Result { + 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()) + } +}