things
This commit is contained in:
parent
e297b7a62d
commit
ef4ada62f8
17 changed files with 2674 additions and 361 deletions
144
Cargo.lock
generated
144
Cargo.lock
generated
|
@ -11,6 +11,54 @@ dependencies = [
|
||||||
"memchr",
|
"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]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.79"
|
version = "1.0.79"
|
||||||
|
@ -35,6 +83,46 @@ version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
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]]
|
[[package]]
|
||||||
name = "clipboard-win"
|
name = "clipboard-win"
|
||||||
version = "5.1.0"
|
version = "5.1.0"
|
||||||
|
@ -44,6 +132,12 @@ dependencies = [
|
||||||
"error-code",
|
"error-code",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorchoice"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "endian-type"
|
name = "endian-type"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
|
@ -77,6 +171,12 @@ dependencies = [
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "home"
|
name = "home"
|
||||||
version = "0.5.9"
|
version = "0.5.9"
|
||||||
|
@ -115,9 +215,10 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "matrix-repl"
|
name = "matrix-bin"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"clap",
|
||||||
"matrix",
|
"matrix",
|
||||||
"rustyline",
|
"rustyline",
|
||||||
]
|
]
|
||||||
|
@ -198,6 +299,24 @@ dependencies = [
|
||||||
"autocfg",
|
"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]]
|
[[package]]
|
||||||
name = "radix_trie"
|
name = "radix_trie"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
|
@ -278,6 +397,29 @@ version = "1.13.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
|
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]]
|
[[package]]
|
||||||
name = "unicode-segmentation"
|
name = "unicode-segmentation"
|
||||||
version = "1.11.0"
|
version = "1.11.0"
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
members = [ "matrix", "matrix-repl" ]
|
members = [ "matrix", "matrix-bin" ]
|
||||||
|
|
0
matrix-repl/Cargo.lock → matrix-bin/Cargo.lock
generated
0
matrix-repl/Cargo.lock → matrix-bin/Cargo.lock
generated
13
matrix-bin/Cargo.toml
Normal file
13
matrix-bin/Cargo.toml
Normal 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
104
matrix-bin/src/main.rs
Normal 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
25
matrix-bin/src/repl.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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"
|
|
|
@ -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}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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;
|
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||||
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)]
|
|
||||||
pub enum UnaryOp {
|
pub enum UnaryOp {
|
||||||
// normal math
|
// normal math
|
||||||
Negate,
|
Negate,
|
||||||
|
@ -43,20 +9,7 @@ pub enum UnaryOp {
|
||||||
Not,
|
Not,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<Token> for UnaryOp {
|
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||||
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)]
|
|
||||||
pub enum BinaryOp {
|
pub enum BinaryOp {
|
||||||
// normal math
|
// normal math
|
||||||
Add,
|
Add,
|
||||||
|
@ -78,26 +31,330 @@ pub enum BinaryOp {
|
||||||
LessEquals,
|
LessEquals,
|
||||||
GreaterThan,
|
GreaterThan,
|
||||||
LessThan,
|
LessThan,
|
||||||
And,
|
Range,
|
||||||
Or,
|
|
||||||
// assignment
|
|
||||||
Assign
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<Token> for BinaryOp {
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
type Error = ();
|
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::*;
|
use Token::*;
|
||||||
Ok(match value {
|
match value {
|
||||||
Equal => BinaryOp::Equals,
|
Subtract => Self::Negate,
|
||||||
NotEqual => Self::Equals,
|
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,
|
GreaterEqual => Self::GreaterEquals,
|
||||||
LessEqual => Self::LessEquals,
|
LessEqual => Self::LessEquals,
|
||||||
GreaterThan => Self::GreaterThan,
|
GreaterThan => Self::GreaterThan,
|
||||||
LessThan => Self::LessThan,
|
LessThan => Self::LessThan,
|
||||||
And => Self::And,
|
|
||||||
Or => Self::Or,
|
|
||||||
BitwiseShiftLeft => Self::BitwiseShiftLeft,
|
BitwiseShiftLeft => Self::BitwiseShiftLeft,
|
||||||
BitwiseShiftRight => Self::BitwiseShiftRight,
|
BitwiseShiftRight => Self::BitwiseShiftRight,
|
||||||
BitwiseAnd => Self::BitwiseAnd,
|
BitwiseAnd => Self::BitwiseAnd,
|
||||||
|
@ -109,54 +366,20 @@ impl TryFrom<Token> for BinaryOp {
|
||||||
Divide => Self::Divide,
|
Divide => Self::Divide,
|
||||||
Modulo => Self::Modulo,
|
Modulo => Self::Modulo,
|
||||||
Power => Self::Power,
|
Power => Self::Power,
|
||||||
Assign => Self::Assign,
|
Range => Self::Range,
|
||||||
_ => return Err(())
|
_ => panic!("aaaaa")
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
impl Expr {
|
||||||
pub enum Expr {
|
pub fn is_assignable(&self) -> bool {
|
||||||
Literal(Value),
|
use Expr::*;
|
||||||
Ident(Rc<str>),
|
match self {
|
||||||
|
Ident(_) => true,
|
||||||
UnaryOp(Box<Expr>, UnaryOp),
|
Index(_, _) => true,
|
||||||
BinaryOp(Box<Expr>, Box<Expr>, BinaryOp),
|
FieldAccess(_, _) => true,
|
||||||
|
_ => false,
|
||||||
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 {
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
116
matrix/src/chunk.rs
Normal file
116
matrix/src/chunk.rs
Normal 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
546
matrix/src/compiler.rs
Normal 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
112
matrix/src/gc.rs
Normal 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()); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use std::{rc::Rc, fmt::Debug};
|
use std::{rc::Rc, fmt::Debug};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
use crate::Result;
|
||||||
|
|
||||||
pub struct RegexToken {
|
pub struct RegexToken {
|
||||||
regex: Regex
|
regex: Regex
|
||||||
|
@ -37,13 +38,16 @@ pub enum Token {
|
||||||
LeftBrack,
|
LeftBrack,
|
||||||
RightBrack,
|
RightBrack,
|
||||||
LeftBrace,
|
LeftBrace,
|
||||||
|
LeftLeftBrace,
|
||||||
RightBrace,
|
RightBrace,
|
||||||
|
RightRightBrace,
|
||||||
Assign,
|
Assign,
|
||||||
Access,
|
Access,
|
||||||
SemiColon,
|
SemiColon,
|
||||||
Arrow,
|
Arrow,
|
||||||
ThinArrow,
|
ThinArrow,
|
||||||
Comma,
|
Comma,
|
||||||
|
Range,
|
||||||
|
|
||||||
// equality
|
// equality
|
||||||
Equal,
|
Equal,
|
||||||
|
@ -97,20 +101,6 @@ pub enum Token {
|
||||||
Eof,
|
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)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
UnexpectedCharacter(char),
|
UnexpectedCharacter(char),
|
||||||
|
@ -141,8 +131,6 @@ impl std::fmt::Display for Error {
|
||||||
|
|
||||||
impl std::error::Error for Error {}
|
impl std::error::Error for Error {}
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, self::Error>;
|
|
||||||
|
|
||||||
pub struct Lexer {
|
pub struct Lexer {
|
||||||
pub index: usize,
|
pub index: usize,
|
||||||
len: usize,
|
len: usize,
|
||||||
|
@ -156,10 +144,10 @@ trait IsIdent {
|
||||||
|
|
||||||
impl IsIdent for char {
|
impl IsIdent for char {
|
||||||
fn is_initial_ident(&self) -> bool {
|
fn is_initial_ident(&self) -> bool {
|
||||||
return self.is_alphabetic() || *self == '_';
|
self.is_alphabetic() || *self == '_'
|
||||||
}
|
}
|
||||||
fn is_ident(&self) -> bool {
|
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 {
|
if self.index >= self.len {
|
||||||
return '\0';
|
return '\0';
|
||||||
}
|
}
|
||||||
return self.data[self.index];
|
self.data[self.index]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next(&mut self) -> char {
|
fn next(&mut self) -> char {
|
||||||
let c = self.peek();
|
let c = self.peek();
|
||||||
self.index += 1;
|
self.index += 1;
|
||||||
return c;
|
c
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_not_eof(&mut self) -> Result<char> {
|
fn next_not_eof(&mut self) -> Result<char> {
|
||||||
let c = self.next();
|
let c = self.next();
|
||||||
if c == '\0' {
|
if c == '\0' {
|
||||||
return Err(Error::UnexpectedEof)
|
return Err(Error::UnexpectedEof.into())
|
||||||
} else {
|
|
||||||
return Ok(c)
|
|
||||||
}
|
}
|
||||||
|
Ok(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_expect(&mut self, expected: char) -> Result<char> {
|
fn next_expect(&mut self, expected: char) -> Result<char> {
|
||||||
let c = self.next();
|
let c = self.next();
|
||||||
if c != expected {
|
if c != expected {
|
||||||
return Err(Error::ExpectedChar(expected, c))
|
return Err(Error::ExpectedChar(expected, c).into())
|
||||||
} else {
|
|
||||||
return Ok(c)
|
|
||||||
}
|
}
|
||||||
|
Ok(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn skip_whitespace(&mut self, ignore_newlines: bool) {
|
fn skip_whitespace(&mut self, ignore_newlines: bool) {
|
||||||
|
@ -247,7 +233,7 @@ impl Lexer {
|
||||||
buf.push(char::from_u32(
|
buf.push(char::from_u32(
|
||||||
n1.to_digit(16).ok_or(InvalidDigit(n1))? * 16 +
|
n1.to_digit(16).ok_or(InvalidDigit(n1))? * 16 +
|
||||||
n2.to_digit(16).ok_or(InvalidDigit(n2))?
|
n2.to_digit(16).ok_or(InvalidDigit(n2))?
|
||||||
).unwrap())
|
).unwrap());
|
||||||
},
|
},
|
||||||
'u' => {
|
'u' => {
|
||||||
self.next_expect('{')?;
|
self.next_expect('{')?;
|
||||||
|
@ -255,16 +241,16 @@ impl Lexer {
|
||||||
loop {
|
loop {
|
||||||
let c = self.next_not_eof()?;
|
let c = self.next_not_eof()?;
|
||||||
if c == '}' { break }
|
if c == '}' { break }
|
||||||
if n >= 0x10000000u32 {
|
if n >= 0x1000_0000_u32 {
|
||||||
return Err(InvalidCodepoint)
|
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);
|
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();
|
let mut buf = std::string::String::new();
|
||||||
|
|
||||||
if !initial.is_initial_ident() {
|
if !initial.is_initial_ident() {
|
||||||
return Err(UnexpectedCharacter(initial))
|
return Err(UnexpectedCharacter(initial).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
buf.push(initial);
|
buf.push(initial);
|
||||||
|
@ -296,7 +282,7 @@ impl Lexer {
|
||||||
"else" => Else,
|
"else" => Else,
|
||||||
"while" => While,
|
"while" => While,
|
||||||
"let" => Let,
|
"let" => Let,
|
||||||
"function" => Function,
|
"fn" | "function" => Function,
|
||||||
"true" => True,
|
"true" => True,
|
||||||
"false" => False,
|
"false" => False,
|
||||||
"nil" => Nil,
|
"nil" => Nil,
|
||||||
|
@ -325,16 +311,16 @@ impl Lexer {
|
||||||
n = n * radix + (i as i64);
|
n = n * radix + (i as i64);
|
||||||
char_found = true;
|
char_found = true;
|
||||||
} else if self.peek().is_ident() {
|
} else if self.peek().is_ident() {
|
||||||
return Err(InvalidDigit(self.peek()))
|
return Err(InvalidDigit(self.peek()).into())
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if char_found {
|
if char_found {
|
||||||
return Ok(Int(n))
|
Ok(Int(n))
|
||||||
} else {
|
} else {
|
||||||
return Err(InvalidNumber(format!("0{radix_char}")))
|
Err(InvalidNumber(format!("0{radix_char}")).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -360,7 +346,7 @@ impl Lexer {
|
||||||
|
|
||||||
if initial != '.' {
|
if initial != '.' {
|
||||||
loop {
|
loop {
|
||||||
if !self.peek().is_digit(10) { break; }
|
if !self.peek().is_ascii_digit() { break; }
|
||||||
buf.push(self.next());
|
buf.push(self.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -370,7 +356,7 @@ impl Lexer {
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if !self.peek().is_digit(10) { break; }
|
if !self.peek().is_ascii_digit() { break; }
|
||||||
buf.push(self.next());
|
buf.push(self.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,7 +367,7 @@ impl Lexer {
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if !self.peek().is_digit(10) { break; }
|
if !self.peek().is_ascii_digit() { break; }
|
||||||
buf.push(self.next());
|
buf.push(self.next());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -392,13 +378,12 @@ impl Lexer {
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.peek().is_ident() || self.peek() == '.' {
|
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>() {
|
if let Ok(int) = buf.parse::<i64>() {
|
||||||
use Token::*;
|
use Token::*;
|
||||||
if self.peek() == 'i' {
|
if complex {
|
||||||
self.next();
|
|
||||||
return Ok(Complex(int as f64))
|
return Ok(Complex(int as f64))
|
||||||
}
|
}
|
||||||
return Ok(Int(int))
|
return Ok(Int(int))
|
||||||
|
@ -406,18 +391,13 @@ impl Lexer {
|
||||||
|
|
||||||
if let Ok(float) = buf.parse::<f64>() {
|
if let Ok(float) = buf.parse::<f64>() {
|
||||||
use Token::*;
|
use Token::*;
|
||||||
if self.peek() == 'i' {
|
if complex {
|
||||||
self.next();
|
|
||||||
return Ok(Complex(float))
|
return Ok(Complex(float))
|
||||||
}
|
}
|
||||||
return Ok(Float(float))
|
return Ok(Float(float))
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(Error::InvalidNumber(buf))
|
Err(Error::InvalidNumber(buf).into())
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reset(&mut self) {
|
|
||||||
self.index = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn peek_token_impl(&mut self, ignore_newlines: bool) -> Result<Token> {
|
fn peek_token_impl(&mut self, ignore_newlines: bool) -> Result<Token> {
|
||||||
|
@ -427,16 +407,6 @@ impl Lexer {
|
||||||
token
|
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> {
|
fn next_token_impl(&mut self, ignore_newlines: bool) -> Result<Token> {
|
||||||
use Token::*;
|
use Token::*;
|
||||||
use Error::*;
|
use Error::*;
|
||||||
|
@ -454,8 +424,24 @@ impl Lexer {
|
||||||
')' => RightParen,
|
')' => RightParen,
|
||||||
'[' => LeftBrack,
|
'[' => LeftBrack,
|
||||||
']' => RightBrack,
|
']' => RightBrack,
|
||||||
'{' => LeftBrace,
|
'{' => {
|
||||||
'}' => RightBrace,
|
match next {
|
||||||
|
'{' => {
|
||||||
|
self.next();
|
||||||
|
LeftLeftBrace
|
||||||
|
}
|
||||||
|
_ => LeftBrace
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'}' => {
|
||||||
|
match next {
|
||||||
|
'}' => {
|
||||||
|
self.next();
|
||||||
|
RightRightBrace
|
||||||
|
}
|
||||||
|
_ => RightBrace
|
||||||
|
}
|
||||||
|
},
|
||||||
';' => SemiColon,
|
';' => SemiColon,
|
||||||
'+' => Add,
|
'+' => Add,
|
||||||
',' => Comma,
|
',' => 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)?
|
self.lex_number(char)?
|
||||||
} else {
|
} else {
|
||||||
Access
|
Access
|
||||||
|
|
|
@ -1,5 +1,55 @@
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
mod ast;
|
pub mod compiler;
|
||||||
mod lex;
|
pub mod value;
|
||||||
|
pub mod gc;
|
||||||
|
pub mod lex;
|
||||||
|
pub mod vm;
|
||||||
pub mod parse;
|
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>;
|
||||||
|
|
|
@ -1,10 +1,33 @@
|
||||||
use std::{fmt::Display, rc::Rc};
|
use std::{fmt::Display, rc::Rc};
|
||||||
use num_complex::Complex64;
|
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 {
|
pub struct Parser {
|
||||||
lexer: Lexer
|
lexer: Lexer,
|
||||||
|
optimize: bool
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -13,10 +36,11 @@ pub enum Error {
|
||||||
UnexpectedToken(Token),
|
UnexpectedToken(Token),
|
||||||
ExpectedToken(Token),
|
ExpectedToken(Token),
|
||||||
ExpectedTokenName(&'static str),
|
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 {
|
impl Display for Error {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
use Error::*;
|
use Error::*;
|
||||||
|
@ -25,6 +49,9 @@ impl Display for Error {
|
||||||
UnexpectedToken(tok) => write!(f, "Unexpected token: '{tok:?}'"),
|
UnexpectedToken(tok) => write!(f, "Unexpected token: '{tok:?}'"),
|
||||||
ExpectedToken(tok) => write!(f, "Expected token: '{tok:?}'"),
|
ExpectedToken(tok) => write!(f, "Expected token: '{tok:?}'"),
|
||||||
ExpectedTokenName(name) => write!(f, "Expected {name} token"),
|
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 {}
|
impl std::error::Error for Error {}
|
||||||
|
|
||||||
macro_rules! math_expr_parser {
|
macro_rules! expr_parser {
|
||||||
($parser:ident, $pattern:pat, $fn:ident) => {{
|
($parser:ident, $pattern:pat, $fn:ident) => {{
|
||||||
let mut math_expr = $parser.$fn()?;
|
let mut expr = $parser.$fn()?;
|
||||||
loop {
|
loop {
|
||||||
let tok = $parser.lexer.peek_token_nl()?;
|
let tok = $parser.lexer.peek_token_nl()?;
|
||||||
match tok {
|
match tok {
|
||||||
$pattern => {
|
$pattern => {
|
||||||
$parser.lexer.next_token_nl()?;
|
$parser.lexer.next_token_nl()?;
|
||||||
let temp = $parser.$fn()?;
|
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
|
_ => 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) => {{
|
($parser:ident, $pattern:pat, $fn:ident, $cur:ident) => {{
|
||||||
let math_expr = $parser.$fn()?;
|
let expr = $parser.$fn()?;
|
||||||
let tok = $parser.lexer.peek_token_nl()?;
|
let tok = $parser.lexer.peek_token_nl()?;
|
||||||
Ok(match tok {
|
Ok(match tok {
|
||||||
$pattern => {
|
$pattern => {
|
||||||
$parser.lexer.next_token_nl()?;
|
$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> {
|
fn force_token(&mut self, tok: Token) -> Result<Token> {
|
||||||
let next = self.lexer.next_token()?;
|
let next = self.lexer.next_token()?;
|
||||||
if next != tok {
|
if next != tok {
|
||||||
Err(Error::ExpectedToken(tok))
|
Err(Error::ExpectedToken(tok).into())
|
||||||
} else {
|
} else {
|
||||||
Ok(tok)
|
Ok(tok)
|
||||||
}
|
}
|
||||||
|
@ -83,63 +110,138 @@ impl Parser {
|
||||||
fn force_token_nl(&mut self, tok: Token) -> Result<Token> {
|
fn force_token_nl(&mut self, tok: Token) -> Result<Token> {
|
||||||
let next = self.lexer.next_token_nl()?;
|
let next = self.lexer.next_token_nl()?;
|
||||||
if next != tok {
|
if next != tok {
|
||||||
Err(Error::ExpectedToken(tok))
|
Err(Error::ExpectedToken(tok).into())
|
||||||
} else {
|
} else {
|
||||||
Ok(tok)
|
Ok(tok)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_list(&mut self) -> Result<Expr> {
|
fn parse_fn_call(&mut self) -> Result<Vec<Expr>> {
|
||||||
self.force_token(Token::LeftBrack)?;
|
self.force_token(Token::LeftParen)?;
|
||||||
let mut list = Vec::new();
|
let mut params = Vec::new();
|
||||||
if self.lexer.peek_token()? == Token::RightBrack {
|
|
||||||
self.lexer.next_token()?;
|
|
||||||
return Ok(Expr::List(list))
|
|
||||||
}
|
|
||||||
loop {
|
loop {
|
||||||
let expr = self.parse_math_expr()?;
|
let expr = match self.lexer.peek_token()? {
|
||||||
list.push(expr);
|
Token::RightParen => {
|
||||||
|
self.lexer.next_token()?;
|
||||||
|
break
|
||||||
|
},
|
||||||
|
_ => self.parse_expr()?
|
||||||
|
};
|
||||||
|
params.push(expr);
|
||||||
let next = self.lexer.next_token()?;
|
let next = self.lexer.next_token()?;
|
||||||
match next {
|
match next {
|
||||||
Token::Comma => continue,
|
Token::Comma => continue,
|
||||||
Token::RightBrack => break,
|
Token::RightParen => break,
|
||||||
_ => return Err(Error::UnexpectedToken(next))
|
_ => 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).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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut data = Vec::new();
|
||||||
|
parts.reverse();
|
||||||
|
while let Some(part) = parts.pop() {
|
||||||
|
data.extend(part);
|
||||||
|
}
|
||||||
|
Ok(Expr::Matrix((domain, codomain, data)))
|
||||||
}
|
}
|
||||||
Ok(Expr::List(list))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_table_key(&mut self) -> Result<Expr> {
|
fn parse_table_key(&mut self) -> Result<Expr> {
|
||||||
let tok = self.lexer.next_token()?;
|
let tok = self.lexer.next_token()?;
|
||||||
Ok(match tok {
|
Ok(match tok {
|
||||||
Token::LeftBrack => {
|
Token::LeftBrack => {
|
||||||
let expr = self.parse_math_expr()?;
|
let expr = self.parse_expr()?;
|
||||||
self.force_token(Token::RightBrack)?;
|
self.force_token(Token::RightBrack)?;
|
||||||
expr
|
expr
|
||||||
},
|
},
|
||||||
Token::Ident(ident) => Expr::Ident(ident),
|
Token::Ident(ident) => Expr::Ident(ident),
|
||||||
Token::String(string) => Expr::Literal(Value::String(string)),
|
Token::String(string) => Expr::Literal(Value::String(string.to_string().into())),
|
||||||
_ => return Err(Error::UnexpectedToken(tok))
|
_ => return Err(Error::UnexpectedToken(tok).into())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_table(&mut self) -> Result<Expr> {
|
fn parse_table(&mut self) -> Result<Expr> {
|
||||||
self.force_token(Token::LeftBrace)?;
|
self.force_token(Token::LeftLeftBrace)?;
|
||||||
let mut table = Vec::new();
|
let mut table = Vec::new();
|
||||||
if self.lexer.peek_token()? == Token::RightBrace {
|
if self.lexer.peek_token()? == Token::RightRightBrace {
|
||||||
self.lexer.next_token()?;
|
self.lexer.next_token()?;
|
||||||
return Ok(Expr::Table(table))
|
return Ok(Expr::Table(table))
|
||||||
}
|
}
|
||||||
loop {
|
loop {
|
||||||
let key = self.parse_table_key()?;
|
let key = self.parse_table_key()?;
|
||||||
self.force_token(Token::Assign)?;
|
self.force_token(Token::Assign)?;
|
||||||
let value = self.parse_math_expr()?;
|
let value = self.parse_expr()?;
|
||||||
table.push((key, value));
|
table.push((key, value));
|
||||||
let next = self.lexer.next_token()?;
|
let next = self.lexer.next_token()?;
|
||||||
match next {
|
match next {
|
||||||
Token::Comma => continue,
|
Token::Comma => continue,
|
||||||
Token::RightBrace => break,
|
Token::RightRightBrace => break,
|
||||||
_ => return Err(Error::UnexpectedToken(next))
|
_ => return Err(Error::UnexpectedToken(next).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Expr::Table(table))
|
Ok(Expr::Table(table))
|
||||||
|
@ -147,107 +249,170 @@ impl Parser {
|
||||||
|
|
||||||
fn parse_paren(&mut self) -> Result<Expr> {
|
fn parse_paren(&mut self) -> Result<Expr> {
|
||||||
self.force_token(Token::LeftParen)?;
|
self.force_token(Token::LeftParen)?;
|
||||||
let math_expr = self.parse_math_expr()?;
|
let expr = self.parse_expr()?;
|
||||||
self.force_token(Token::RightParen)?;
|
self.force_token(Token::RightParen)?;
|
||||||
Ok(math_expr)
|
Ok(expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_term(&mut self) -> Result<Expr> {
|
fn parse_term(&mut self) -> Result<Expr> {
|
||||||
use Token::*;
|
use Token::*;
|
||||||
let tok = self.lexer.peek_token()?;
|
let tok = self.lexer.peek_token()?;
|
||||||
match tok {
|
match tok {
|
||||||
LeftBrack => return self.parse_list(),
|
LeftBrack => return self.parse_matrix(),
|
||||||
LeftBrace => return self.parse_table(),
|
LeftLeftBrace => return self.parse_table(),
|
||||||
LeftParen => return self.parse_paren(),
|
LeftParen => return self.parse_paren(),
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
self.lexer.next_token()?;
|
self.lexer.next_token()?;
|
||||||
Ok(match tok {
|
Ok(match tok {
|
||||||
|
Nil => Expr::Literal(Value::Nil),
|
||||||
Int(i) => Expr::Literal(Value::Int(i)),
|
Int(i) => Expr::Literal(Value::Int(i)),
|
||||||
Float(f) => Expr::Literal(Value::Float(f)),
|
Float(f) => Expr::Literal(Value::Float(f)),
|
||||||
Complex(c) => Expr::Literal(Value::Complex(Complex64::new(0.0, c))),
|
Complex(c) => Expr::Literal(Value::Complex(Complex64::new(0.0, c))),
|
||||||
Regex(r) => Expr::Literal(Value::Regex(Rc::new(r.into()))),
|
Regex(r) => Expr::Literal(Value::Regex(Gc::new(r.into()))),
|
||||||
String(s) => Expr::Literal(Value::String(s)),
|
String(s) => Expr::Literal(Value::String(s.to_string().into())),
|
||||||
True => Expr::Literal(Value::Bool(true)),
|
True => Expr::Literal(Value::Bool(true)),
|
||||||
False => Expr::Literal(Value::Bool(false)),
|
False => Expr::Literal(Value::Bool(false)),
|
||||||
Ident(ident) => Expr::Ident(ident),
|
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()?;
|
let tok = self.lexer.peek_token_nl()?;
|
||||||
Ok(match tok {
|
Ok(match tok {
|
||||||
Token::Not => {
|
Token::Not => {
|
||||||
self.lexer.next_token()?;
|
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 => {
|
Token::Subtract => {
|
||||||
self.lexer.next_token()?;
|
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> {
|
fn parse_expr_pow(&mut self) -> Result<Expr> {
|
||||||
math_expr_parser_reverse!(
|
expr_parser_reverse!(
|
||||||
self,
|
self,
|
||||||
Token::Power,
|
Token::Power,
|
||||||
parse_math_expr_unary,
|
parse_expr_unary,
|
||||||
parse_math_expr_pow
|
parse_expr_pow
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_math_expr_mult(&mut self) -> Result<Expr> {
|
fn parse_expr_mult(&mut self) -> Result<Expr> {
|
||||||
math_expr_parser!(self, Token::Multiply | Token::Divide | Token::Modulo, parse_math_expr_pow)
|
expr_parser!(self, Token::Multiply | Token::Divide | Token::Modulo, parse_expr_pow)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_math_expr_add(&mut self) -> Result<Expr> {
|
fn parse_expr_add(&mut self) -> Result<Expr> {
|
||||||
math_expr_parser!(self, Token::Add | Token::Subtract, parse_math_expr_mult)
|
expr_parser!(self, Token::Add | Token::Subtract, parse_expr_mult)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_math_expr_shift(&mut self) -> Result<Expr> {
|
fn parse_expr_shift(&mut self) -> Result<Expr> {
|
||||||
math_expr_parser!(
|
expr_parser!(
|
||||||
self,
|
self,
|
||||||
Token::BitwiseShiftLeft | Token::BitwiseShiftRight,
|
Token::BitwiseShiftLeft | Token::BitwiseShiftRight,
|
||||||
parse_math_expr_add
|
parse_expr_add
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_math_expr_bit_and(&mut self) -> Result<Expr> {
|
fn parse_expr_bit_and(&mut self) -> Result<Expr> {
|
||||||
math_expr_parser!(self, Token::BitwiseAnd, parse_math_expr_shift)
|
expr_parser!(self, Token::BitwiseAnd, parse_expr_shift)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_math_expr_bit_or(&mut self) -> Result<Expr> {
|
fn parse_expr_bit_or(&mut self) -> Result<Expr> {
|
||||||
math_expr_parser!(self, Token::BitwiseOr, parse_math_expr_bit_and)
|
expr_parser!(self, Token::BitwiseOr, parse_expr_bit_and)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_math_expr_compare(&mut self) -> Result<Expr> {
|
fn parse_expr_compare(&mut self) -> Result<Expr> {
|
||||||
math_expr_parser!(
|
expr_parser!(
|
||||||
self,
|
self,
|
||||||
Token::Equal | Token::NotEqual |
|
Token::Equal | Token::NotEqual |
|
||||||
Token::LessThan | Token::GreaterThan |
|
Token::LessThan | Token::GreaterThan |
|
||||||
Token::LessEqual | Token::GreaterEqual,
|
Token::LessEqual | Token::GreaterEqual,
|
||||||
parse_math_expr_bit_or
|
parse_expr_bit_or
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_math_expr_and(&mut self) -> Result<Expr> {
|
fn parse_expr_and(&mut self) -> Result<Expr> {
|
||||||
math_expr_parser!(self, Token::Add, parse_math_expr_compare)
|
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> {
|
fn parse_expr(&mut self) -> Result<Expr> {
|
||||||
math_expr_parser!(self, Token::Or, parse_math_expr_and)
|
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> {
|
fn parse_assign(&mut self) -> Result<Assign> {
|
||||||
math_expr_parser_reverse!(
|
let expr = self.parse_expr()?;
|
||||||
self,
|
if !expr.is_assignable() {
|
||||||
Token::Assign,
|
return Ok(Assign::Expr(expr));
|
||||||
parse_math_expr_or,
|
}
|
||||||
parse_math_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>>> {
|
fn parse_params(&mut self) -> Result<Vec<Rc<str>>> {
|
||||||
|
@ -256,7 +421,7 @@ impl Parser {
|
||||||
match tok {
|
match tok {
|
||||||
Ident(ident) => return Ok(vec![ident]),
|
Ident(ident) => return Ok(vec![ident]),
|
||||||
LeftParen => (),
|
LeftParen => (),
|
||||||
_ => return Err(Error::UnexpectedToken(tok)),
|
_ => return Err(Error::UnexpectedToken(tok).into()),
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut params = Vec::new();
|
let mut params = Vec::new();
|
||||||
|
@ -273,7 +438,7 @@ impl Parser {
|
||||||
match next {
|
match next {
|
||||||
Comma => continue,
|
Comma => continue,
|
||||||
RightParen => break,
|
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()? {
|
if let Token::Ident(ident) = self.lexer.next_token()? {
|
||||||
Ok(ident)
|
Ok(ident)
|
||||||
} else {
|
} 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()? {
|
if let Token::Ident(ident) = self.lexer.next_token_nl()? {
|
||||||
Ok(ident)
|
Ok(ident)
|
||||||
} else {
|
} 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)?;
|
self.force_token(Token::Function)?;
|
||||||
let ident = self.parse_ident()?;
|
let ident = self.parse_ident()?;
|
||||||
let params = match self.lexer.peek_token()? {
|
let params = match self.lexer.peek_token()? {
|
||||||
Token::LeftBrace => vec![],
|
Token::LeftBrace => vec![],
|
||||||
_ => self.parse_params()?,
|
_ => self.parse_params()?,
|
||||||
};
|
};
|
||||||
let block = self.parse_block()?;
|
let stmt = self.parse_stmt()?;
|
||||||
Ok(Expr::Function(ident, params, block))
|
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)?;
|
self.force_token(Token::Do)?;
|
||||||
let block = self.parse_block()?;
|
let stmt = self.parse_stmt()?;
|
||||||
self.force_token(Token::While)?;
|
self.force_token(Token::While)?;
|
||||||
let math_expr = self.parse_math_expr()?;
|
let expr = self.parse_expr()?;
|
||||||
Ok(Expr::DoWhile(Box::new(math_expr), block))
|
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)?;
|
self.force_token(Token::While)?;
|
||||||
let math_expr = self.parse_math_expr()?;
|
let expr = self.parse_expr()?;
|
||||||
let block = self.parse_block()?;
|
let stmt = self.parse_stmt()?;
|
||||||
Ok(Expr::While(Box::new(math_expr), block))
|
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)?;
|
self.force_token(Token::Loop)?;
|
||||||
let block = self.parse_block()?;
|
let stmt = self.parse_stmt()?;
|
||||||
Ok(Expr::Loop(block))
|
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)?;
|
self.force_token(Token::If)?;
|
||||||
let math_expr = Box::new(self.parse_expr()?);
|
let expr = self.parse_expr()?;
|
||||||
let expr = Box::new(self.parse_expr()?);
|
let stmt = Box::new(self.parse_stmt()?);
|
||||||
|
|
||||||
if self.lexer.peek_token()? != Token::Else {
|
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()?;
|
self.lexer.next_token()?;
|
||||||
|
|
||||||
if self.lexer.peek_token()? == Token::If {
|
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 {
|
} 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)?;
|
self.force_token(Token::Let)?;
|
||||||
let ident = self.parse_ident_nl()?;
|
let ident = self.parse_ident_nl()?;
|
||||||
if self.lexer.peek_token_nl()? == Token::Assign {
|
if self.lexer.peek_token_nl()? == Token::Assign {
|
||||||
self.force_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 {
|
} 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)?;
|
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::*;
|
use Token::*;
|
||||||
match self.lexer.peek_token()? {
|
match self.lexer.peek_token()? {
|
||||||
Do => self.parse_do_while(),
|
Do => self.parse_do_while(),
|
||||||
While => self.parse_while(),
|
While => self.parse_while(),
|
||||||
Let => self.parse_let(),
|
Let => self.parse_let(),
|
||||||
LeftBrace => {
|
LeftBrace => self.parse_block(),
|
||||||
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()?))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Return => self.parse_return(),
|
Return => self.parse_return(),
|
||||||
If => self.parse_if(),
|
If => self.parse_if(),
|
||||||
Loop => self.parse_loop(),
|
Loop => self.parse_loop(),
|
||||||
Break => {
|
Break => {
|
||||||
self.lexer.next_token()?;
|
self.lexer.next_token()?;
|
||||||
Ok(Expr::Break)
|
Ok(Stmt::Break)
|
||||||
},
|
},
|
||||||
Continue => {
|
Continue => {
|
||||||
self.lexer.next_token()?;
|
self.lexer.next_token()?;
|
||||||
Ok(Expr::Continue)
|
Ok(Stmt::Continue)
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
let math_expr = self.parse_math_expr()?;
|
let assign = self.parse_assign()?;
|
||||||
Ok(math_expr)
|
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();
|
let mut block = Vec::new();
|
||||||
self.force_token(Token::LeftBrace)?;
|
self.force_token(Token::LeftBrace)?;
|
||||||
loop {
|
loop {
|
||||||
let expr = match self.lexer.peek_token()? {
|
let stmt = match self.lexer.peek_token()? {
|
||||||
Token::RightBrace => break,
|
Token::RightBrace => break,
|
||||||
Token::SemiColon => {
|
Token::SemiColon => {
|
||||||
self.lexer.next_token()?;
|
self.lexer.next_token()?;
|
||||||
continue;
|
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);
|
block.push(expr);
|
||||||
let next = self.lexer.next_token()?;
|
let next = self.lexer.next_token()?;
|
||||||
match next {
|
match next {
|
||||||
Token::SemiColon => continue,
|
Token::SemiColon => continue,
|
||||||
Token::RightBrace => break,
|
Token::Eof => break,
|
||||||
_ => return Err(Error::UnexpectedToken(next))
|
_ => return Err(Error::UnexpectedToken(next).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.force_token(Token::RightBrace)?;
|
|
||||||
Ok(block)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_root_expr(&mut self) -> Result<Expr> {
|
let ast = Stmt::Block(block);
|
||||||
if self.lexer.peek_token()? == Token::Function {
|
if self.optimize {
|
||||||
self.parse_function()
|
Ok(optimize(ast)?)
|
||||||
} else {
|
} 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
610
matrix/src/value.rs
Normal 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
242
matrix/src/vm.rs
Normal 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())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue