This commit is contained in:
Freya Murphy 2024-02-19 18:38:15 -05:00
parent e297b7a62d
commit ef4ada62f8
Signed by: freya
GPG key ID: 744AB800E383AE52
17 changed files with 2674 additions and 361 deletions

144
Cargo.lock generated
View file

@ -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"

View file

@ -1,3 +1,3 @@
[workspace]
resolver = "2"
members = [ "matrix", "matrix-repl" ]
members = [ "matrix", "matrix-bin" ]

13
matrix-bin/Cargo.toml Normal file
View file

@ -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"

104
matrix-bin/src/main.rs Normal file
View file

@ -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();
}
}

25
matrix-bin/src/repl.rs Normal file
View file

@ -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);
}
}
}
}

View file

@ -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"

View file

@ -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}")
}
}
}

View file

@ -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,26 +31,330 @@ pub enum BinaryOp {
LessEquals,
GreaterThan,
LessThan,
And,
Or,
// assignment
Assign
Range,
}
impl TryFrom<Token> for BinaryOp {
type Error = ();
#[derive(Debug, Clone, PartialEq)]
pub enum Expr {
Literal(Value),
Ident(Rc<str>),
fn try_from(value: Token) -> Result<Self, Self::Error> {
UnaryOp(Box<Expr>, UnaryOp),
BinaryOp(Box<Expr>, Box<Expr>, BinaryOp),
Index(Box<Expr>, Vec<Expr>),
FnCall(Box<Expr>, Vec<Expr>),
FieldAccess(Box<Expr>, Box<Expr>),
List(InlineList),
Matrix(InlineMatrix),
Table(InlineTable),
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(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 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::*;
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<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))
}
}
_ => 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::*;
Ok(match value {
Equal => BinaryOp::Equals,
NotEqual => Self::Equals,
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,
And => Self::And,
Or => Self::Or,
BitwiseShiftLeft => Self::BitwiseShiftLeft,
BitwiseShiftRight => Self::BitwiseShiftRight,
BitwiseAnd => Self::BitwiseAnd,
@ -109,54 +366,20 @@ impl TryFrom<Token> for BinaryOp {
Divide => Self::Divide,
Modulo => Self::Modulo,
Power => Self::Power,
Assign => Self::Assign,
_ => return Err(())
})
Range => Self::Range,
_ => panic!("aaaaa")
}
}
}
#[derive(Debug)]
pub enum Expr {
Literal(Value),
Ident(Rc<str>),
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>),
List(InlineList),
Matrix(InlineMatrix),
Table(InlineTable),
Block(Vec<Expr>),
Function(Rc<str>, Vec<Rc<str>>, Vec<Expr>),
Continue,
Break,
Return(Box<Expr>),
If(Box<Expr>, Box<Expr>, Option<Box<Expr>>),
Loop(Vec<Expr>),
While(Box<Expr>, Vec<Expr>),
DoWhile(Box<Expr>, Vec<Expr>)
}
impl Expr {
pub fn optimize(self) -> Self {
pub fn is_assignable(&self) -> bool {
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
Ident(_) => true,
Index(_, _) => true,
FieldAccess(_, _) => true,
_ => false,
}
}
}

116
matrix/src/chunk.rs Normal file
View file

@ -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"),
}
}
}

546
matrix/src/compiler.rs Normal file
View file

@ -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))
}
}

112
matrix/src/gc.rs Normal file
View file

@ -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()); }
}
}

View file

@ -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

View file

@ -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>;

View file

@ -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> {
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<Vec<Expr>> {
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::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 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))
_ => 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 = 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::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())
}
}
Ok(Expr::List(list))
let mut data = Vec::new();
parts.reverse();
while let Some(part) = parts.pop() {
data.extend(part);
}
Ok(Expr::Matrix((domain, codomain, data)))
}
}
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(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<Stmt> {
if self.lexer.peek_token()? == Token::Function {
self.parse_function()
} else {
self.parse_stmt()
}
}
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 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<Expr> {
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<T: Into<Lexer>>(into: T) -> Result<Expr> {
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))
}
}

610
matrix/src/value.rs Normal file
View file

@ -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,
}
}
}

242
matrix/src/vm.rs Normal file
View file

@ -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())
}
}