This commit is contained in:
Freya Murphy 2024-02-29 17:04:28 -05:00
parent 508c4fa1b8
commit 5d2747e26f
Signed by: freya
GPG key ID: 744AB800E383AE52
44 changed files with 3527 additions and 2276 deletions

88
Cargo.lock generated
View file

@ -13,9 +13,9 @@ dependencies = [
[[package]]
name = "anstream"
version = "0.6.12"
version = "0.6.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540"
checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb"
dependencies = [
"anstyle",
"anstyle-parse",
@ -114,7 +114,7 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.48",
"syn 2.0.52",
]
[[package]]
@ -125,9 +125,9 @@ checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
[[package]]
name = "clipboard-win"
version = "5.1.0"
version = "5.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ec832972fefb8cf9313b45a0d1945e29c9c251f1d4c6eafc5fe2124c02d2e81"
checksum = "12f9a0700e0127ba15d1d52dd742097f821cd9c65939303a44d970465040a297"
dependencies = [
"error-code",
]
@ -166,9 +166,9 @@ dependencies = [
[[package]]
name = "error-code"
version = "3.0.0"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "281e452d3bad4005426416cdba5ccfd4f5c1280e10099e21db27f7c1c28347fc"
checksum = "a0474425d51df81997e2f90a21591180b38eccf27292d755f3e30750225c175b"
[[package]]
name = "fd-lock"
@ -221,12 +221,23 @@ checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
[[package]]
name = "log"
version = "0.4.20"
version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]]
name = "matrix"
name = "matrix-bin"
version = "0.1.0"
dependencies = [
"clap",
"ctrlc",
"matrix-lang",
"matrix-std",
"rustyline",
]
[[package]]
name = "matrix-lang"
version = "0.1.0"
dependencies = [
"anyhow",
@ -235,32 +246,21 @@ dependencies = [
"regex",
]
[[package]]
name = "matrix-bin"
version = "0.1.0"
dependencies = [
"clap",
"ctrlc",
"matrix",
"matrix-stdlib",
"rustyline",
]
[[package]]
name = "matrix-macros"
version = "0.1.0"
dependencies = [
"matrix",
"matrix-lang",
"quote",
"syn 1.0.109",
]
[[package]]
name = "matrix-stdlib"
name = "matrix-std"
version = "0.1.0"
dependencies = [
"anyhow",
"matrix",
"matrix-lang",
"matrix-macros",
"os_info",
"rand",
@ -490,7 +490,7 @@ checksum = "e5af959c8bf6af1aff6d2b463a57f71aae53d1332da58419e30ad8dc7011d951"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.48",
"syn 2.0.52",
]
[[package]]
@ -510,7 +510,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.48",
"syn 2.0.52",
]
[[package]]
@ -538,9 +538,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.48"
version = "2.0.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07"
dependencies = [
"proc-macro2",
"quote",
@ -610,9 +610,9 @@ dependencies = [
[[package]]
name = "windows-targets"
version = "0.52.0"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
@ -625,42 +625,42 @@ dependencies = [
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.0"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.0"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
[[package]]
name = "windows_i686_gnu"
version = "0.52.0"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
[[package]]
name = "windows_i686_msvc"
version = "0.52.0"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.0"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.0"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.0"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"

View file

@ -1,3 +1,3 @@
[workspace]
resolver = "2"
members = [ "matrix", "matrix-bin" , "matrix-macros", "matrix-stdlib"]
members = [ "matrix-lang", "matrix-bin" , "matrix-macros", "matrix-std"]

385
matrix-bin/Cargo.lock generated
View file

@ -1,385 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
dependencies = [
"memchr",
]
[[package]]
name = "anyhow"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
version = "2.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clipboard-win"
version = "5.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ec832972fefb8cf9313b45a0d1945e29c9c251f1d4c6eafc5fe2124c02d2e81"
dependencies = [
"error-code",
]
[[package]]
name = "endian-type"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
[[package]]
name = "errno"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "error-code"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "281e452d3bad4005426416cdba5ccfd4f5c1280e10099e21db27f7c1c28347fc"
[[package]]
name = "fd-lock"
version = "4.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947"
dependencies = [
"cfg-if",
"rustix",
"windows-sys",
]
[[package]]
name = "home"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
dependencies = [
"windows-sys",
]
[[package]]
name = "libc"
version = "0.2.153"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
[[package]]
name = "linux-raw-sys"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
[[package]]
name = "log"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "matrix"
version = "0.1.0"
dependencies = [
"anyhow",
"num-complex",
"num-rational",
"regex",
]
[[package]]
name = "matrix-repl"
version = "0.1.0"
dependencies = [
"matrix",
"rustyline",
]
[[package]]
name = "memchr"
version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
[[package]]
name = "nibble_vec"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43"
dependencies = [
"smallvec",
]
[[package]]
name = "nix"
version = "0.27.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
dependencies = [
"bitflags",
"cfg-if",
"libc",
]
[[package]]
name = "num-bigint"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-complex"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6"
dependencies = [
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
dependencies = [
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
dependencies = [
"autocfg",
"num-bigint",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
dependencies = [
"autocfg",
]
[[package]]
name = "radix_trie"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd"
dependencies = [
"endian-type",
"nibble_vec",
]
[[package]]
name = "regex"
version = "1.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "rustix"
version = "0.38.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "rustyline"
version = "13.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02a2d683a4ac90aeef5b1013933f6d977bd37d51ff3f4dad829d4931a7e6be86"
dependencies = [
"bitflags",
"cfg-if",
"clipboard-win",
"fd-lock",
"home",
"libc",
"log",
"memchr",
"nix",
"radix_trie",
"unicode-segmentation",
"unicode-width",
"utf8parse",
"winapi",
]
[[package]]
name = "smallvec"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
[[package]]
name = "unicode-segmentation"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
[[package]]
name = "unicode-width"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
[[package]]
name = "utf8parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
[[package]]
name = "windows_i686_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
[[package]]
name = "windows_i686_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"

View file

@ -10,6 +10,6 @@ path = "src/main.rs"
[dependencies]
clap = { version = "4", features = [ "derive" ] }
ctrlc = "3"
matrix = { path = "../matrix" }
matrix-stdlib = { path = "../matrix-stdlib" }
matrix-lang = { path = "../matrix-lang" }
matrix-std = { path = "../matrix-std" }
rustyline = { version = "13", features = [ "derive" ] }

View file

@ -1,7 +1,6 @@
use std::{borrow::Cow, rc::Rc, cell::RefCell};
use matrix::{lex::{Lexer, TokenData, Token}, vm::Vm};
use rustyline::{validate::{Validator, ValidationResult, ValidationContext}, highlight::Highlighter, Helper, Hinter, completion::Completer};
use matrix_lang::prelude::*;
#[derive(Helper, Hinter)]
pub struct MatrixHelper {
@ -205,6 +204,7 @@ impl Highlighter for MatrixHelper {
T::Else |
T::While |
T::Let |
T::Const |
T::Function |
T::Continue |
T::Break |
@ -290,12 +290,12 @@ impl Completer for MatrixHelper {
}
let _ = (line, pos, ctx);
let globals = self.vm.borrow().global_names();
let globals = self.vm.borrow().globals();
let names: Vec<Rc<str>> = globals
.borrow()
.clone()
.into_iter()
.filter(|n| n.starts_with(&buf))
.filter_map(|n| if n.name.starts_with(&buf) { Some(n.name.clone()) } else { None })
.collect();
if buf.is_empty() {

View file

@ -1,6 +1,6 @@
use std::{path::PathBuf, io::{self, Read, IsTerminal}, fs, cell::RefCell, rc::Rc};
use clap::{Parser as ArgParser, ColorChoice};
use matrix::{compiler::{Compiler, CompilerBuilder}, vm::Vm, parse::{Parser, ParserBuilder}, value::Value};
use matrix_lang::prelude::*;
use repl::Repl;
mod repl;
@ -12,16 +12,20 @@ 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
/// Compiles the given program
#[arg(short, long)]
repl: bool,
compile: bool,
/// Optional output for compiled output
#[arg(short, long)]
output: Option<PathBuf>,
/// Print out debug information
#[arg(short, long)]
debug: bool,
/// Choses color
#[arg(short, long)]
#[arg(long)]
color: Option<ColorChoice>,
/// Disables optimizations
@ -29,18 +33,21 @@ pub struct Args {
disable_optimizations: bool,
}
pub enum Mode {
Repl,
Execute(String),
Compile(String, PathBuf),
}
pub struct State<'a> {
parser: Parser,
compiler: Compiler<'a>,
vm: Rc<RefCell<Vm>>,
repl: bool,
color: bool,
#[allow(unused)]
debug: bool,
}
impl<'a> State<'a> {
pub fn new (args: Args) -> (Self, Option<String>) {
pub fn new (args: Args) -> (Self, Mode) {
let stdin = read_stdin();
@ -53,20 +60,43 @@ impl<'a> State<'a> {
file = None;
}
let repl = args.repl || file.is_none();
let mode;
let repl;
if args.compile {
let path = match (args.output, args.file) {
(Some(path), _) => path,
(None, Some(path)) => {
let mut path = path.clone();
path.set_extension("matc");
path
},
(None, None) => {
PathBuf::from("matc.out")
}
};
let file = file.unwrap_or(String::new());
mode = Mode::Compile(file, path);
repl = false;
} else if let Some(file) = file {
mode = Mode::Execute(file);
repl = false;
} else {
mode = Mode::Repl;
repl = true;
}
let mut vm = Vm::new();
let parser = ParserBuilder::new()
.optimize(!args.disable_optimizations)
.build();
let mut vm = Vm::new();
let compiler = CompilerBuilder::new()
.repl(repl)
.debug(args.debug)
.names(vm.names())
.globals(vm.global_names())
.globals(vm.globals())
.build();
matrix_stdlib::load(&mut vm);
matrix_std::load(&mut vm);
let color = match args.color {
Some(ColorChoice::Auto) | None => {
@ -76,21 +106,40 @@ impl<'a> State<'a> {
Some(ColorChoice::Never) => false,
};
(Self { parser, vm: Rc::new(RefCell::new(vm)), compiler, repl, debug: args.debug, color }, file)
(Self {
parser,
vm: Rc::new(RefCell::new(vm)),
compiler,
color,
}, mode)
}
pub fn execute(&mut self, code: String) -> matrix::Result<Value> {
let ast = self.parser.parse(code)?;
let fun = self.compiler.compile(&ast)?;
pub fn execute(&mut self, fun: Rc<Function>) -> Result<Value> {
let val = self.vm.try_borrow_mut().unwrap().run(fun)?;
Ok(val)
}
pub fn compile(&mut self, code: String) -> Result<Rc<Function>> {
let ast = self.parser.parse(code)?;
let fun = self.compiler.compile(&ast)?;
Ok(fun)
}
pub fn load_program(&mut self, body: String) -> Result<Rc<Function>> {
match Program::load(&body)? {
Some(fun) => {
Ok(fun)
},
None => {
self.compile(body)
},
}
}
}
pub fn error(err: matrix::Error, state: &State) {
pub fn error(err: Exception, state: &State) {
if state.color {
println!("\x1b[31m\x1b[1mError:\x1b[0m {err}");
println!("\x1b[31mError:\x1b[0m {err}");
} else {
println!("Error: {err}");
}
@ -106,19 +155,33 @@ fn read_stdin() -> String {
buffer
}
fn handle_mode(state: &mut State, mode: Mode) -> Result<()> {
match mode {
Mode::Repl => {
let mut repl = Repl::new(state);
repl.run();
},
Mode::Execute(body) => {
let fun = state.load_program(body)?;
state.execute(fun)?;
},
Mode::Compile(body, path) => {
let fun = state.compile(body)?;
let mut file = File::create(path).map_err(|e|
exception!(IO_EXCEPTION, "{e}")
)?;
Program::save(fun, &mut file)?;
}
};
Ok(())
}
fn main() {
let args = Args::parse();
let (mut state, file) = State::new(args);
let (mut state, mode) = State::new(args);
if let Some(file) = file {
if let Err(err) = state.execute(file) {
error(err, &state);
}
if let Err(e) = handle_mode(&mut state, mode) {
error(e, &state);
}
if state.repl {
Repl::new(state).run();
}
}

View file

@ -1,21 +1,26 @@
use std::{io::Write, sync::atomic::Ordering};
use matrix::{value::Value, vm::Interupt};
use rustyline::{Config, EditMode, ColorMode, Editor, CompletionType};
use rustyline::{Config, EditMode, ColorMode, Editor, CompletionType, error::ReadlineError};
use matrix_lang::prelude::*;
use crate::{State, helper::MatrixHelper};
pub struct Repl<'a> {
state: State<'a>
pub struct Repl<'s, 'a> {
state: &'s mut State<'a>
}
impl<'a> Repl<'a> {
impl<'s, 'a> Repl<'s, 'a> {
pub fn new(state: State<'a>) -> Self {
pub fn new(state: &'s mut State<'a>) -> Self {
Self { state }
}
pub fn run(&mut self) {
fn execute(&mut self, line: String) -> Result<Value> {
let fun = self.state.compile(line)?;
let val = self.state.execute(fun)?;
Ok(val)
}
pub fn run(&mut self) {
let interupt = self.state.vm.borrow().interupt();
ctrlc::set_handler(move || {
@ -23,26 +28,33 @@ impl<'a> Repl<'a> {
}).unwrap();
let config = Config::builder()
.indent_size(4)
.edit_mode(EditMode::Emacs)
.check_cursor_position(true)
.completion_type(CompletionType::List)
.edit_mode(EditMode::Emacs)
.color_mode(if self.state.color { ColorMode::Enabled } else { ColorMode::Disabled })
.build();
let helper = MatrixHelper::new(self.state.vm.clone());
let histfile = std::env::var("MATRIX_HISTORY").ok();
let mut rl = Editor::with_config(config).unwrap();
rl.set_helper(Some(helper));
if let Some(hf) = &histfile {
rl.load_history(hf).ok();
}
loop {
let Ok(line) = rl.readline(">> ") else {
break;
let line = match rl.readline(">> ") {
Ok(line) => line,
Err(ReadlineError::Eof) => break,
Err(_) => continue,
};
if let Err(_) = rl.add_history_entry(&line) {
break;
};
match self.state.execute(line) {
Err(err) => crate::error(err, &self.state),
rl.add_history_entry(&line).ok();
match self.execute(line) {
Ok(val) => {
if val != Value::Nil {
if self.state.color {
@ -52,8 +64,13 @@ impl<'a> Repl<'a> {
}
}
}
}
let _ = std::io::stdout().flush();
Err(err) => crate::error(err, &self.state),
};
std::io::stdout().flush().ok();
}
if let Some(hf) = &histfile {
rl.save_history(hf).ok();
}
}

View file

@ -1,5 +1,5 @@
[package]
name = "matrix"
name = "matrix-lang"
version = "0.1.0"
edition = "2021"

View file

@ -1,42 +1,70 @@
use std::{rc::Rc, ops::{Neg, Not}, fmt::Debug};
use crate::{lex::{Position, TokenData}, value::{Value, InlineList, InlineMatrix, InlineTable}, Result};
use std::{ops::{Neg, Not}, fmt::{Debug, Display}};
use crate::prelude::*;
pub type AstName = (Rc<str>, Position);
pub type InlineList = Vec<Expr>;
pub type InlineMatrix = (usize, usize, Vec<Expr>);
pub type InlineTable = Vec<(Expr, Expr)>;
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum UnaryOp {
// normal math
Negate,
Negate = 1,
// equality
Not,
Not = 2,
}
impl TryFrom<u8> for UnaryOp {
type Error = Exception;
fn try_from(value: u8) -> std::prelude::v1::Result<Self, Self::Error> {
if value < 1 || value > 2 {
Err(exception!(BINARY_EXCEPTION, "cannot convert {value} to UnaryOp"))
} else {
unsafe { Ok(std::mem::transmute(value)) }
}
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum BinaryOp {
// normal math
Add,
Subtract,
Multiply,
Divide,
Modulo,
Power,
Add = 1,
Subtract = 2,
Multiply = 3,
Divide = 4,
Modulo = 5,
Power = 6,
// binary math
BitwiseAnd,
BitwiseOr,
BitwiseXor,
BitwiseShiftLeft,
BitwiseShiftRight,
BitwiseAnd = 7,
BitwiseOr = 8,
BitwiseXor = 9,
BitwiseShiftLeft = 10,
BitwiseShiftRight = 11,
// equality
Equals,
NotEquals,
GreaterEquals,
LessEquals,
GreaterThan,
LessThan,
Equals = 12,
NotEquals = 13,
GreaterEquals = 14,
LessEquals = 15,
GreaterThan = 16,
LessThan = 17,
// iter
Range,
RangeEq
Range = 18,
RangeEq = 19
}
pub type AstName = (Rc<str>, Position);
impl TryFrom<u8> for BinaryOp {
type Error = Exception;
fn try_from(value: u8) -> std::prelude::v1::Result<Self, Self::Error> {
if value < 1 || value > 19 {
Err(exception!(BINARY_EXCEPTION, "cannot convert {value} to BinaryOp"))
} else {
unsafe { Ok(std::mem::transmute(value)) }
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum ExprData {
@ -75,6 +103,7 @@ pub enum ExprData {
Try(Box<Expr>, AstName, Box<Expr>),
Let(AstName, Box<Expr>),
Const(AstName, Box<Expr>),
Pipeline(Box<Expr>, Box<Expr>),
@ -83,22 +112,24 @@ pub enum ExprData {
Return(Box<Expr>),
}
#[derive(Clone, PartialEq)]
pub struct Expr {
pub data: ExprData,
pub pos: Position
impl Display for Expr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl Debug for Expr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if f.alternate() {
write!(f, "{:#?}", self.data)
} else {
write!(f, "{:?}", self.data)
}
write!(f, "{:?}", self.data)
}
}
#[derive(Clone, PartialEq)]
pub struct Expr {
pub data: ExprData,
pub pos: Position
}
impl From<(ExprData, Position)> for Expr {
fn from(value: (ExprData, Position)) -> Self {
Self { data: value.0, pos: value.1 }
@ -377,6 +408,9 @@ pub fn optimize(mut expr: Expr) -> Result<Expr> {
E::Let(ident, expr) => {
E::Let(ident, Box::new(optimize(*expr)?))
},
E::Const(ident, expr) => {
E::Const(ident, Box::new(optimize(*expr)?))
},
E::Assign(lhs, rhs) => {
let lhs = Box::new(optimize(*lhs)?);
let rhs = Box::new(optimize(*rhs)?);

View file

@ -0,0 +1,160 @@
use crate::prelude::*;
use super::{prim::VarInt, Deserialize, Deserializer};
macro_rules! error {
($($arg:tt)*) => {
exception!(BINARY_EXCEPTION, $($arg)*)
};
}
impl Deserialize for Program {
fn deserialize<S: Deserializer>(s: &mut S) -> Result<Self> {
for _ in 0..5 { s.read::<u8>()?; } // skip header
let version: u8 = s.read()?;
if version != 0 {
return Err(error!("invalid program version {version}"))
}
let fun = <Rc<Function>>::deserialize(s)?;
Ok(Self { version, fun })
}
}
impl Deserialize for Instruction {
fn deserialize<S: Deserializer>(s: &mut S) -> Result<Self> {
use Instruction as I;
let ins: u8 = s.read()?;
let ins = match ins {
0 => I::NoOp,
1 => I::CreateLocal,
2 => I::LoadLocal(s.read()?),
3 => I::StoreLocal(s.read()?),
4 => I::DiscardLocals(s.read()?),
5 => I::LoadGlobal(s.read()?),
6 => I::StoreGlobal(s.read()?),
7 => I::Const(s.read()?),
8 => I::Int(s.read()?),
9 => I::True,
10 => I::False,
11 => I::Nil,
12 => I::Dup,
13 => I::Discard(s.read()?),
14 => I::UnaryOp(UnaryOp::try_from(s.read::<u8>()?)?),
15 => I::BinaryOp(BinaryOp::try_from(s.read::<u8>()?)?),
16 => I::NewList(s.read()?),
17 => I::NewTable(s.read()?),
18 => I::NewMatrix(s.read()?, s.read()?),
19 => I::Field(s.read()?),
20 => I::StoreField(s.read()?),
21 => I::Index(s.read()?),
22 => I::StoreIndex(s.read()?),
23 => I::Jump(s.read()?),
24 => I::JumpTrue(s.read()?),
25 => I::JumpFalse(s.read()?),
26 => I::JumpNil(s.read()?),
27 => I::IterCreate,
28 => I::IterNext,
29 => I::Try(s.read()?),
30 => I::TryEnd,
31 => I::Call(s.read()?),
32 => I::Return,
n => return Err(error!("invalid instruction op code {n}"))
};
Ok(ins)
}
}
impl<T: Deserialize> Deserialize for Vec<T> {
fn deserialize<S: Deserializer>(s: &mut S) -> Result<Self> {
let len = s.read::<VarInt>()?.0;
let mut vec = Vec::with_capacity(len);
for _ in 0..len {
let v = T::deserialize(s)?;
vec.push(v);
}
Ok(vec)
}
}
impl<T: Deserialize> Deserialize for Gc<T> {
fn deserialize<S: Deserializer>(s: &mut S) -> Result<Self> {
Ok(Gc::new(T::deserialize(s)?))
}
}
impl<T: Deserialize> Deserialize for Rc<T> {
fn deserialize<S: Deserializer>(s: &mut S) -> Result<Self> {
Ok(Rc::new(T::deserialize(s)?))
}
}
impl Deserialize for Position {
fn deserialize<S: Deserializer>(s: &mut S) -> Result<Self> {
let row = s.read::<VarInt>()?.0;
let col = s.read::<VarInt>()?.0;
Ok(Self { row, col })
}
}
impl Deserialize for Chunk {
fn deserialize<S: Deserializer>(s: &mut S) -> Result<Self> {
let constants = <Vec<Value>>::deserialize(s)?;
let code = <Vec<Instruction>>::deserialize(s)?;
let pos = <Vec<Position>>::deserialize(s)?;
Ok(Self { constants, code, pos })
}
}
impl Deserialize for Value {
fn deserialize<S: Deserializer>(s: &mut S) -> Result<Self> {
use Value as V;
let ty = s.read::<u8>()?;
let value = match ty {
0 => V::Nil,
1 => V::Bool(s.read()?),
2 => V::Int(s.read()?),
3 => V::Float(s.read()?),
4 => V::Ratio(Rational64::new(s.read()?, s.read()?)),
5 => V::Complex(Complex64::new(s.read()?, s.read()?)),
6 => V::to_regex(s.read::<String>()?.as_str())?,
7 => V::String(s.read::<String>()?.into()),
8 => V::List(<Vec<Value>>::deserialize(s)?.into()),
9 => {
let domain = s.read()?;
let codomain = s.read()?;
let values = <Vec<Value>>::deserialize(s)?;
V::Matrix(Matrix::new(domain, codomain, values).into())
},
10 => {
let len = s.read::<VarInt>()?.0;
let mut table = ValueMap::new();
for _ in 0..len {
let key = <Value>::deserialize(s)?;
let value = <Value>::deserialize(s)?;
table.insert(key, value)?;
}
V::Table(table.into())
},
11 => V::Function(<Rc<Function>>::deserialize(s)?),
12 => V::Range((s.read()?, s.read()?, s.read()?).into()),
13 => V::Iter(<Rc<Function>>::deserialize(s)?),
n => return Err(error!("invalid value code {n}"))
};
Ok(value)
}
}
impl Deserialize for Function {
fn deserialize<S: Deserializer>(s: &mut S) -> Result<Self> {
let name = s.read::<String>()?;
let arity = s.read()?;
let variadic = s.read()?;
let chunk = <Chunk>::deserialize(s)?;
Ok(Function {
name: Rc::from(name.as_str()),
arity,
variadic,
fun: InnerFunction::Compiled(chunk.into())
})
}
}

View file

@ -0,0 +1,154 @@
use crate::prelude::*;
use std::io::{self, Read, Write};
mod serialize;
mod deserialize;
mod prim;
pub struct Program {
version: u8,
fun: Rc<Function>
}
const PROGRAM_HEADER: [u8; 5] = [0x00, 0x4d, 0x41, 0x54, 0x0a];
impl Program {
pub fn load(body: &str) -> Result<Option<Rc<Function>>> {
let mut bytes = body.as_bytes();
if bytes.len() < 6 {
return Ok(None)
}
let header = &bytes[0..5];
if header != &PROGRAM_HEADER {
return Ok(None)
}
let mut s = ProgramDeserializer::from(&mut bytes);
let program = <Self>::deserialize(&mut s)?;
s.finish()?;
Ok(Some(program.fun.clone()))
}
pub fn save<W: Write>(fun: Rc<Function>, w: &mut W) -> Result<()> {
let mut s = ProgramSerializer::from(w);
let p = Program {
version: 0,
fun
};
s.serialize(&p)?;
s.finish()?;
Ok(())
}
}
pub trait Primitive : Sized {
fn write<W: Write>(&self, w: &mut W) -> io::Result<()>;
fn read<R: Read>(r: &mut R) -> io::Result<Self>;
}
pub trait Serialize : Sized {
fn serialize<S: Serializer>(&self, s: &mut S) -> Result<()>;
}
pub trait Serializer : Sized {
fn serialize<S: Serialize>(&mut self, val: &S) -> Result<()> {
val.serialize(self)
}
fn write<P: Primitive>(&mut self, val: P) -> Result<()>;
}
pub trait Deserialize : Sized {
fn deserialize<S: Deserializer>(s: &mut S) -> Result<Self>;
}
pub trait Deserializer : Sized {
fn deserialize<D: Deserialize>(&mut self) -> Result<D> {
D::deserialize(self)
}
fn read<P: Primitive>(&mut self) -> Result<P>;
}
macro_rules! error {
($($arg:tt)*) => {
exception!(BINARY_EXCEPTION, $($arg)*)
};
}
pub struct ProgramSerializer<'w, W: Write> {
writer: &'w mut W,
checksum: u64,
}
impl<'w, W: Write> ProgramSerializer<'w, W> {
fn finish(self) -> Result<()> {
let bytes = self.checksum.to_le_bytes();
self.writer.write(&bytes).map_err(|e| error!("{e}"))?;
Ok(())
}
}
impl<'w, W: Write> Serializer for ProgramSerializer<'w, W> {
fn write<P: Primitive>(&mut self, val: P) -> Result<()> {
val.write(self).map_err(|e| error!("{e}"))
}
}
impl<'w, W: Write> Write for ProgramSerializer<'w, W> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
for b in buf {
self.checksum %= 0xf1e3beef;
self.checksum += *b as u64;
}
self.writer.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.writer.flush()
}
}
impl<'w, W: Write> From<&'w mut W> for ProgramSerializer<'w, W> {
fn from(writer: &'w mut W) -> Self {
Self { writer, checksum: 0xfe }
}
}
pub struct ProgramDeserializer<'r, R: Read> {
reader: &'r mut R,
checksum: u64,
}
impl<'r, R: Read> ProgramDeserializer<'r, R> {
fn finish(self) -> Result<()> {
let mut bytes = [0u8; 8];
self.reader.read_exact(&mut bytes).map_err(|e| error!("{e}"))?;
let checksum = u64::from_le_bytes(bytes);
if self.checksum != checksum {
return Err(error!("checksum doesnt match"))
}
Ok(())
}
}
impl<'r, R: Read> Deserializer for ProgramDeserializer<'r, R> {
fn read<P: Primitive>(&mut self) -> Result<P> {
P::read(self).map_err(|e| error!("{e}"))
}
}
impl<'r, R: Read> Read for ProgramDeserializer<'r, R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let c = self.reader.read(buf)?;
for i in 0..c {
let b = buf[i];
self.checksum %= 0xf1e3beef;
self.checksum += b as u64;
}
Ok(c)
}
}
impl<'r, R: Read> From<&'r mut R> for ProgramDeserializer<'r, R> {
fn from(reader: &'r mut R) -> Self {
Self { reader, checksum: 0xfe }
}
}

View file

@ -0,0 +1,109 @@
use super::Primitive;
use std::io::{self, Read, Write};
macro_rules! marshal_number {
($type:ident, $byte:expr) => {
impl Primitive for $type {
fn write<W: Write>(&self, write: &mut W) -> io::Result<()> {
write.write(&self.to_le_bytes())?;
Ok(())
}
fn read<R: Read>(read: &mut R) -> io::Result<Self> {
let mut bytes = [0u8; $byte];
read.read_exact(&mut bytes)?;
Ok(Self::from_le_bytes(bytes))
}
}
};
}
marshal_number!(u8, 1);
marshal_number!(u16, 2);
marshal_number!(i16, 2);
marshal_number!(u32, 4);
marshal_number!(i64, 8);
marshal_number!(f64, 8);
impl Primitive for bool {
fn write<W: Write>(&self, write: &mut W) -> io::Result<()> {
if *self {
write.write(&[1u8; 1])?;
} else {
write.write(&[0u8; 1])?;
}
Ok(())
}
fn read<R: Read>(read: &mut R) -> io::Result<Self> {
let mut bytes = [0u8; 1];
read.read_exact(&mut bytes)?;
if bytes[0] == 1 {
Ok(true)
} else {
Ok(false)
}
}
}
impl Primitive for String {
fn write<W: Write>(&self, write: &mut W) -> io::Result<()> {
let bytes = self.as_bytes();
let len = bytes.len();
write.write(&(len as u32).to_le_bytes())?;
write.write(&bytes)?;
Ok(())
}
fn read<R: Read>(read: &mut R) -> io::Result<Self> {
let len = usize::read(read)?;
let mut bytes = vec![0u8; len];
read.read_exact(&mut bytes)?;
Ok(String::from_utf8_lossy(bytes.as_slice()).to_string())
}
}
impl Primitive for usize {
fn write<W: Write>(&self, write: &mut W) -> io::Result<()> {
(*self as u32).write(write)
}
fn read<R: Read>(read: &mut R) -> io::Result<Self> {
Ok(u32::read(read)? as usize)
}
}
pub struct VarInt(pub usize);
impl Primitive for VarInt {
fn write<W: Write>(&self, write: &mut W) -> io::Result<()> {
let mut data = self.0;
loop {
let mut byte = (data & 0x7F) as u8;
data >>= 7;
if data != 0 {
byte += 0x80;
}
write.write(&[byte; 1])?;
if data == 0 {
return Ok(())
}
}
}
fn read<R: Read>(read: &mut R) -> io::Result<Self> {
let mut buf = [0];
let mut result = 0usize;
for count in 0..8 {
if read.read(&mut buf)? != 1 {
return Ok(Self(0))
}
let byte = buf[0];
result |= ((byte & 0x7F) as usize) << (7 * count);
if byte & 0x80 == 0 {
return Ok(Self(result))
}
}
Ok(Self(0))
}
}

View file

@ -0,0 +1,283 @@
use crate::{prelude::*, binary::PROGRAM_HEADER};
use std::ops::Deref;
use super::{prim::VarInt, Program, Serializer, Serialize};
macro_rules! error {
($($arg:tt)*) => {
exception!(BINARY_EXCEPTION, $($arg)*)
};
}
impl Serialize for Program {
fn serialize<S: Serializer>(&self, s: &mut S) -> Result<()> {
for b in PROGRAM_HEADER {
s.write(b)?;
}
s.write(self.version)?;
s.serialize(self.fun.as_ref())?;
Ok(())
}
}
impl Serialize for Instruction {
fn serialize<S: Serializer>(&self, s: &mut S) -> Result<()> {
use Instruction as I;
match self {
I::NoOp => {
s.write(0u8)?;
},
I::CreateLocal => {
s.write(1u8)?;
},
I::LoadLocal(idx) => {
s.write(2u8)?;
s.write(*idx)?;
},
I::StoreLocal(idx) => {
s.write(3u8)?;
s.write(*idx)?;
},
I::DiscardLocals(idx) => {
s.write(4u8)?;
s.write(*idx)?;
},
I::LoadGlobal(idx) => {
s.write(5u8)?;
s.write(*idx)?;
}
I::StoreGlobal(idx) => {
s.write(6u8)?;
s.write(*idx)?;
},
I::Const(idx) => {
s.write(7u8)?;
s.write(*idx)?;
},
I::Int(i) => {
s.write(8u8)?;
s.write(*i)?;
},
I::True => {
s.write(9u8)?;
},
I::False => {
s.write(10u8)?;
},
I::Nil => {
s.write(11u8)?;
},
I::Dup => {
s.write(12u8)?;
},
I::Discard(idx) => {
s.write(13u8)?;
s.write(*idx)?;
},
I::UnaryOp(op) => {
s.write(14u8)?;
s.write(*op as u8)?;
},
I::BinaryOp(op) => {
s.write(15u8)?;
s.write(*op as u8)?;
},
I::NewList(len) => {
s.write(16u8)?;
s.write(*len)?;
},
I::NewTable(len) => {
s.write(17u8)?;
s.write(*len)?;
},
I::NewMatrix(d, c) => {
s.write(18u8)?;
s.write(*d)?;
s.write(*c)?;
},
I::Field(idx) => {
s.write(19u8)?;
s.write(*idx)?;
},
I::StoreField(idx) => {
s.write(20u8)?;
s.write(*idx)?;
},
I::Index(idx) => {
s.write(21u8)?;
s.write(*idx)?;
},
I::StoreIndex(idx) => {
s.write(22u8)?;
s.write(*idx)?;
},
I::Jump(ip) => {
s.write(23u8)?;
s.write(*ip)?;
},
I::JumpTrue(ip) => {
s.write(24u8)?;
s.write(*ip)?;
},
I::JumpFalse(ip) => {
s.write(25u8)?;
s.write(*ip)?;
},
I::JumpNil(ip) => {
s.write(26u8)?;
s.write(*ip)?;
},
I::IterCreate => {
s.write(27u8)?;
},
I::IterNext => {
s.write(28u8)?;
},
I::Try(ip) => {
s.write(29u8)?;
s.write(*ip)?;
},
I::TryEnd => {
s.write(30u8)?;
},
I::Call(arity) => {
s.write(31u8)?;
s.write(*arity)?;
},
I::Return => {
s.write(32u8)?;
},
};
Ok(())
}
}
impl<T: Serialize> Serialize for Vec<T> {
fn serialize<S: Serializer>(&self, s: &mut S) -> Result<()> {
s.write(VarInt(self.len()))?;
for val in self {
val.serialize(s)?;
}
Ok(())
}
}
impl<T: Serialize + Deref> Serialize for Gc<T> {
fn serialize<S: Serializer>(&self, s: &mut S) -> Result<()> {
self.deref().serialize(s)
}
}
impl<T: Serialize + Deref> Serialize for Rc<T> {
fn serialize<S: Serializer>(&self, s: &mut S) -> Result<()> {
self.deref().serialize(s)
}
}
impl Serialize for Position {
fn serialize<S: Serializer>(&self, s: &mut S) -> Result<()> {
s.write(VarInt(self.row))?;
s.write(VarInt(self.col))?;
Ok(())
}
}
impl Serialize for Chunk {
fn serialize<S: Serializer>(&self, s: &mut S) -> Result<()> {
self.constants.serialize(s)?;
self.code.serialize(s)?;
self.pos.serialize(s)?;
Ok(())
}
}
impl Serialize for Value {
fn serialize<S: Serializer>(&self, s: &mut S) -> Result<()> {
use Value as V;
match self {
V::Nil => {
s.write(0u8)?;
},
V::Bool(b) => {
s.write(1u8)?;
s.write(*b)?;
},
V::Int(i) => {
s.write(2u8)?;
s.write(*i)?;
},
V::Float(f) => {
s.write(3u8)?;
s.write(*f)?;
},
V::Ratio(r) => {
s.write(4u8)?;
s.write(*r.numer())?;
s.write(*r.denom())?;
},
V::Complex(c) => {
s.write(5u8)?;
s.write(c.re)?;
s.write(c.im)?;
},
V::Regex(r) => {
s.write(6u8)?;
s.write(r.to_string())?;
},
V::String(str) => {
s.write(7u8)?;
s.write(str.to_string())?;
},
V::List(l) => {
s.write(8u8)?;
l.serialize(s)?;
},
V::Matrix(m) => {
s.write(9u8)?;
s.write(m.domain)?;
s.write(m.codomain)?;
m.values.serialize(s)?;
},
V::Table(t) => {
s.write(10u8)?;
s.write(VarInt(t.len()))?;
for (key, value) in t.entries() {
key.serialize(s)?;
value.serialize(s)?;
}
},
V::Function(f) => {
s.write(11u8)?;
f.serialize(s)?;
},
V::Range(r) => {
s.write(12u8)?;
s.write(r.0)?;
s.write(r.1)?;
s.write(r.2)?;
},
V::Iter(f) => {
s.write(13u8)?;
f.serialize(s)?;
},
V::File(_) => return Err(error!("cannot compile file")),
V::Exception(_) => return Err(error!("cannot compile exception")),
};
Ok(())
}
}
impl Serialize for Function {
fn serialize<S: Serializer>(&self, s: &mut S) -> Result<()> {
s.write(self.name.to_string())?;
s.write(self.arity)?;
s.write(self.variadic)?;
use InnerFunction as F;
match &self.fun {
F::Compiled(c) => {
c.serialize(s)?;
},
F::Native(_) => return Err(error!("cannot compile native function")),
};
Ok(())
}
}

View file

@ -1,5 +1,5 @@
use crate::{value::Value, ast::{UnaryOp, BinaryOp}, vm::{Vm, StackFrame}, Result, lex::Position};
use std::{fmt::{Debug, Display}, rc::Rc};
use std::fmt::{Debug, Display};
use crate::prelude::*;
#[derive(Clone, Default)]
pub struct Chunk {
@ -18,53 +18,6 @@ impl Chunk {
}
}
impl Debug for Chunk {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Chunk({})", self.code.len())
}
}
impl Display for Chunk {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "constants: ")?;
for (i, c) in self.constants.iter().enumerate() {
writeln!(f, " {i:04}: {c}")?;
}
writeln!(f, "code:")?;
for (i, ins) in self.code.iter().enumerate() {
writeln!(f, " {i:04}: {ins}")?;
}
Ok(())
}
}
pub struct Function {
pub name: Rc<str>,
pub arity: usize,
pub variadic: bool,
pub fun: InnerFunction
}
#[derive(Clone)]
pub enum InnerFunction {
Compiled(Rc<Chunk>),
Native(Rc<dyn Fn((&mut Vm, &mut StackFrame), Vec<Value>) -> Result<Value>>),
}
impl Debug for Function {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use InnerFunction as F;
match self.fun {
F::Compiled(_) => {
write!(f, "[Function {}]", self.name)
},
F::Native(_) => {
write!(f, "[NativeFunction {}]", self.name)
}
}
}
}
#[derive(Clone, Debug)]
#[repr(align(4))]
pub enum Instruction {
@ -113,6 +66,26 @@ pub enum Instruction {
Return,
}
impl Debug for Chunk {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "[Chunk {}]", self.code.len())
}
}
impl Display for Chunk {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "constants: ")?;
for (i, c) in self.constants.iter().enumerate() {
writeln!(f, " {i:04}: {c}")?;
}
writeln!(f, "code:")?;
for (i, ins) in self.code.iter().enumerate() {
writeln!(f, " {i:04}: {ins}")?;
}
Ok(())
}
}
impl Display for Instruction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use Instruction::*;

View file

@ -1,14 +1,14 @@
use std::{fmt::Display, rc::Rc, cell::RefCell};
use crate::{ast::{Expr, ExprData, AstName}, chunk::{Function, Instruction, InnerFunction}, chunk::{Chunk, self}, value::Value, Result, lex::Position};
use crate::prelude::*;
use Instruction as I;
use Value as V;
use ExprData as E;
pub type NamesTable = Rc<RefCell<Vec<Rc<str>>>>;
pub type GlobalsTable = Rc<RefCell<Vec<Global>>>;
pub struct CompilerBuilder<'c> {
globals: NamesTable,
globals: GlobalsTable,
names: NamesTable,
repl: bool,
debug: bool,
@ -39,7 +39,7 @@ impl<'c> CompilerBuilder<'c> {
self
}
pub fn globals(mut self, globals: NamesTable) -> Self {
pub fn globals(mut self, globals: GlobalsTable) -> Self {
self.globals = globals;
self
}
@ -81,9 +81,9 @@ pub struct Compiler<'c> {
name: Rc<str>,
parent: Option<&'c Compiler<'c>>,
locals: Vec<Rc<Local>>,
globals: Rc<RefCell<Vec<Rc<str>>>>,
names: Rc<RefCell<Vec<Rc<str>>>>,
locals: Vec<Local>,
globals: GlobalsTable,
names: NamesTable,
root_is_block: bool,
@ -97,50 +97,25 @@ pub struct Compiler<'c> {
debug: bool,
}
#[derive(Clone)]
struct Local {
name: Rc<str>,
idx: usize,
scope: usize
scope: usize,
is_const: bool,
}
#[derive(Debug, Clone)]
pub enum InnerError {
Undefined(Rc<str>),
Redefined(Rc<str>),
InvAssign(Expr),
InvContinue,
InvBreak,
NotImplemented(&'static str),
#[derive(Clone)]
pub struct Global {
pub name: Rc<str>,
pub idx: usize,
pub is_const: bool,
}
#[derive(Debug, Clone)]
pub struct Error {
pos: Position,
err: InnerError,
}
impl std::error::Error for self::Error {}
impl Display for self::Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use InnerError as E;
write!(f, "parse failed at {}:{}, ", self.pos.row, self.pos.col)?;
match &self.err {
E::Undefined(name) => write!(f, "value {name} is undefined"),
E::Redefined(name) => write!(f, "cannot redefine {name} in the same scope"),
E::InvAssign(expr) => write!(f, "cannot assign to {expr:?}"),
E::InvContinue => write!(f, "cannot continue outside a loop"),
E::InvBreak => write!(f, "cannot break outside a loop"),
E::NotImplemented(str) => write!(f, "{str} is not implemented yet")
}
}
}
fn error<T>(err: InnerError, pos: Position) -> Result<T> {
Err(self::Error {
pos,
err
}.into())
macro_rules! error {
($($arg:tt)*) => {
exception!(COMPILE_EXCEPTION, $($arg)*)
};
}
impl<'c> Compiler<'c> {
@ -185,35 +160,35 @@ impl<'c> Compiler<'c> {
};
}
fn create_local(&mut self, name: Rc<str>) {
let local = Local { name, idx: self.locals.len(), scope: self.scopes.len()};
self.locals.push(Rc::new(local));
fn create_local(&mut self, name: Rc<str>, is_const: bool) -> Local {
let local = Local { name, idx: self.locals.len(), scope: self.scopes.len(), is_const };
self.locals.push(local.clone());
local
}
fn create_global(&mut self, name: Rc<str>) -> usize {
self.globals.borrow_mut().push(name);
let c = self.globals.borrow().len() - 1;
c
fn create_global(&mut self, name: Rc<str>, is_const: bool) -> Global {
let global = Global { name, idx: self.globals.borrow().len(), is_const };
self.globals.borrow_mut().push(global.clone());
global
}
fn create_local_checked(&mut self, name: Rc<str>, pos: Position) -> Result<()> {
fn create_local_checked(&mut self, name: Rc<str>, is_const: bool, pos: Position) -> Result<Local> {
if let Some(local) = self.find_local(&name) {
if local.scope == self.scopes.len() {
return error(InnerError::Redefined(name), pos)
return Err(error!("redefined {name}").pos(pos))
}
};
self.create_local(name);
Ok(())
Ok(self.create_local(name, is_const))
}
fn create_global_checked(&mut self, name: Rc<str>, pos: Position) -> Result<usize> {
fn create_global_checked(&mut self, name: Rc<str>, is_const: bool, pos: Position) -> Result<Global> {
if let Some(_) = self.find_global(&name) {
return error(InnerError::Redefined(name).into(), pos)
return Err(error!("redefined {name}").pos(pos))
}
Ok(self.create_global(name))
Ok(self.create_global(name, is_const))
}
fn find_local(&self, name: &str) -> Option<Rc<Local>> {
fn find_local(&self, name: &str) -> Option<Local> {
for local in self.locals.iter().rev() {
if local.name.as_ref() == name {
return Some(local.clone())
@ -222,13 +197,13 @@ impl<'c> Compiler<'c> {
None
}
fn find_global(&self, name: &str) -> Option<usize> {
fn find_global(&self, name: &str) -> Option<Global> {
if let Some(parent) = self.parent {
return parent.find_global(name)
}
for (i, global) in self.globals.borrow().iter().enumerate() {
if global.as_ref() == name {
return Some(i)
for global in self.globals.borrow().iter() {
if global.name.as_ref() == name {
return Some(global.clone())
}
}
None
@ -294,7 +269,7 @@ impl<'c> Compiler<'c> {
let chunk = self.compile_function(name.clone(), params, body)?;
let arity = params.len() - if *varadic { 1 } else { 0 };
let fun = Value::Function(Rc::new(
chunk::Function {
Function {
name: name.0.clone(),
arity,
fun: InnerFunction::Compiled(chunk.into()),
@ -304,10 +279,10 @@ impl<'c> Compiler<'c> {
self.emit_const(fun, expr.pos);
self.emit(I::Dup, expr.pos);
if self.can_make_globals() {
let idx = self.create_global_checked(name.0.clone(), name.1)?;
self.emit(I::StoreGlobal(idx as u16), expr.pos);
let global = self.create_global_checked(name.0.clone(), false, name.1)?;
self.emit(I::StoreGlobal(global.idx as u16), expr.pos);
} else {
self.create_local_checked(name.0.clone(), name.1)?;
self.create_local_checked(name.0.clone(), false, name.1)?;
self.emit(I::CreateLocal, expr.pos);
}
},
@ -316,7 +291,7 @@ impl<'c> Compiler<'c> {
let chunk = self.compile_function(name.clone(), params, body)?;
let arity = params.len() - if *varadic { 1 } else { 0 };
let fun = Value::Function(Rc::new(
chunk::Function {
Function {
name: name.0.clone(),
arity,
fun: InnerFunction::Compiled(chunk.into()),
@ -341,7 +316,7 @@ impl<'c> Compiler<'c> {
let jmpidx2 = self.emit_temp(expr.pos);
self.re_emit(I::Try(self.cur()), jmpidx);
self.begin_scope();
self.create_local(err.0.clone());
self.create_local(err.0.clone(), true);
self.emit(I::CreateLocal, err.1);
self.compile_expr(catch)?;
self.end_scope();
@ -378,7 +353,7 @@ impl<'c> Compiler<'c> {
let jumpidx = self.emit_temp(expr.pos);
self.loop_top.push((top as usize, self.scopes.len()));
self.begin_scope();
self.create_local(name.0.clone());
self.create_local(name.0.clone(), true);
self.emit(I::CreateLocal, name.1);
self.compile_expr(expr)?;
self.emit(I::Discard(1), expr.pos);
@ -407,10 +382,21 @@ impl<'c> Compiler<'c> {
self.compile_expr(expr)?;
self.emit(I::Dup, expr.pos);
if self.can_make_globals() {
let global = self.create_global_checked(name.0.clone(), name.1)?;
self.emit(I::StoreGlobal(global as u16), expr.pos);
let global = self.create_global_checked(name.0.clone(), false, name.1)?;
self.emit(I::StoreGlobal(global.idx as u16), expr.pos);
} else {
self.create_local_checked(name.0.clone(), name.1)?;
self.create_local_checked(name.0.clone(), false, name.1)?;
self.emit(I::CreateLocal, expr.pos);
}
},
E::Const(name, expr) => {
self.compile_expr(expr)?;
self.emit(I::Dup, expr.pos);
if self.can_make_globals() {
let global = self.create_global_checked(name.0.clone(), true, name.1)?;
self.emit(I::StoreGlobal(global.idx as u16), expr.pos);
} else {
self.create_local_checked(name.0.clone(), true, name.1)?;
self.emit(I::CreateLocal, expr.pos);
}
},
@ -420,7 +406,7 @@ impl<'c> Compiler<'c> {
self.collapse_scopes(scope);
self.emit(I::Jump(top as u16), expr.pos);
} else {
return error(InnerError::InvContinue, expr.pos)
return Err(error!("invalid continue outside loop").pos(expr.pos))
}
},
E::Break => {
@ -430,7 +416,7 @@ impl<'c> Compiler<'c> {
let tmpidx = self.emit_temp(expr.pos);
self.loop_bot.push(tmpidx);
} else {
return error(InnerError::InvBreak, expr.pos)
return Err(error!("invalid break outside loop").pos(expr.pos))
}
},
E::Return(expr) => {
@ -444,9 +430,9 @@ impl<'c> Compiler<'c> {
} else if let Some(local) = self.find_local(name) {
self.emit(I::LoadLocal(local.idx as u16), expr.pos);
} else if let Some(global) = self.find_global(name) {
self.emit(I::LoadGlobal(global as u16), expr.pos);
self.emit(I::LoadGlobal(global.idx as u16), expr.pos);
} else {
return error(InnerError::Undefined(name.clone()), expr.pos)
return Err(error!("variable '{name}' is undefined").pos(expr.pos))
};
},
E::Assign(lhs, rhs) => {
@ -455,14 +441,20 @@ impl<'c> Compiler<'c> {
match &lhs.data {
E::Ident(name) if name.as_ref() != "_" => {
if let Some(local) = self.find_local(&name) {
if local.is_const {
return Err(error!("cannot assign to const '{name}'").pos(lhs.pos))
}
self.emit(I::StoreLocal(local.idx as u16), lhs.pos);
} else if let Some(global) = self.find_global(&name) {
self.emit(I::StoreGlobal(global as u16), lhs.pos);
if global.is_const {
return Err(error!("cannot assign to const '{name}'").pos(lhs.pos))
}
self.emit(I::StoreGlobal(global.idx as u16), lhs.pos);
} else if self.can_make_globals() {
let global = self.create_global_checked(name.clone(), lhs.pos)?;
self.emit(I::StoreGlobal(global as u16), lhs.pos);
let global = self.create_global_checked(name.clone(), false, lhs.pos)?;
self.emit(I::StoreGlobal(global.idx as u16), lhs.pos);
} else {
self.create_local_checked(name.clone(), lhs.pos)?;
self.create_local_checked(name.clone(), false, lhs.pos)?;
self.emit(I::CreateLocal, lhs.pos);
}
},
@ -478,7 +470,7 @@ impl<'c> Compiler<'c> {
let name = self.get_name(ident.0.clone());
self.emit(I::StoreField(name as u16), expr.pos);
}
_ => return error(InnerError::InvAssign(*lhs.clone()), lhs.pos)
_ => return Err(error!("assignment to {lhs} is not allowed").pos(lhs.pos))
}
}
E::UnaryOp(expr, op) => {
@ -554,7 +546,7 @@ impl<'c> Compiler<'c> {
fn compile_function(&mut self, name: AstName, params: &Vec<AstName>, body: &Box<Expr>) -> Result<Chunk> {
let mut compiler = self.child(name.0);
for (name, pos) in params {
compiler.create_local(name.clone());
compiler.create_local(name.clone(), false);
compiler.emit(I::CreateLocal, *pos);
}
compiler.compile_expr(body)?;
@ -611,7 +603,7 @@ impl<'c> Compiler<'c> {
};
if self.loop_bot.len() > 0 {
return error(InnerError::InvBreak, pos)
return Err(error!("invalid break outside loop").pos(pos))
}
if self.debug {

View file

@ -1,6 +1,5 @@
use std::{rc::Rc, fmt::Debug};
use regex::Regex;
use crate::Result;
use std::fmt::{Debug, Display};
use crate::prelude::*;
pub struct RegexToken {
regex: Regex
@ -30,6 +29,36 @@ impl From<RegexToken> for Regex {
}
}
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
pub struct Position {
pub row: usize,
pub col: usize,
}
impl Default for Position {
fn default() -> Self {
Self { row: 1, col: 1 }
}
}
impl Display for Position {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}:{}", self.row, self.col)
}
}
impl Display for TokenData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl Display for Token {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.data)
}
}
#[derive(Debug, PartialEq)]
pub enum TokenData {
//syntax
@ -106,6 +135,7 @@ pub enum TokenData {
Else,
While,
Let,
Const,
Function,
True,
False,
@ -133,48 +163,6 @@ pub struct Token {
pub blen: usize,
}
#[derive(Debug)]
pub enum Error {
UnexpectedCharacter(char),
ExpectedChar(char, char),
InvalidCodepoint,
UnexpectedEof,
InvalidDigit(char),
InvalidStringEscape(char),
InvalidRegex(anyhow::Error),
InvalidNumber(String),
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use Error::*;
match self {
UnexpectedCharacter(char) => write!(f, "Unexpected char: '{char}'"),
UnexpectedEof => write!(f, "Unexpected end of file"),
ExpectedChar(expected, got) => write!(f, "Expected char: '{expected}', instead got: '{got}'"),
InvalidCodepoint => write!(f, "Invalid codepoint"),
InvalidDigit(char) => write!(f, "Invalid digit: '{char}'"),
InvalidStringEscape(char) => write!(f, "Invalid string escape: '\\{char}"),
InvalidRegex(err) => write!(f, "{err}"),
InvalidNumber(num) => write!(f, "Invalid number: '{num}'")
}
}
}
impl std::error::Error for Error {}
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
pub struct Position {
pub row: usize,
pub col: usize,
}
impl Default for Position {
fn default() -> Self {
Self { row: 1, col: 1 }
}
}
pub struct Lexer {
pub index: usize,
len: usize,
@ -203,6 +191,12 @@ impl<T: Into<String>> From<T> for Lexer {
}
}
macro_rules! error {
($($arg:tt)*) => {
exception!(PARSE_EXCEPTION, $($arg)*)
};
}
impl Lexer {
pub fn new<T: Into<String>>(input: T) -> Self {
let data: Vec<char> = input.into().chars().collect();
@ -239,7 +233,7 @@ impl Lexer {
fn next_not_eof(&mut self) -> Result<char> {
let c = self.next();
if c == '\0' {
return Err(Error::UnexpectedEof.into())
return Err(error!("unexpected end of file"))
}
Ok(c)
}
@ -247,7 +241,7 @@ impl Lexer {
fn next_expect(&mut self, expected: char) -> Result<char> {
let c = self.next();
if c != expected {
return Err(Error::ExpectedChar(expected, c).into())
return Err(error!("expected character '{c}'"))
}
Ok(c)
}
@ -259,7 +253,6 @@ impl Lexer {
}
fn lex_string(&mut self, delimit: char) -> Result<Rc<str>> {
use Error::*;
let mut buf = String::new();
@ -291,8 +284,8 @@ impl Lexer {
let n1 = self.next_not_eof()?;
let n2 = self.next_not_eof()?;
buf.push(char::from_u32(
n1.to_digit(16).ok_or(InvalidDigit(n1))? * 16 +
n2.to_digit(16).ok_or(InvalidDigit(n2))?
n1.to_digit(16).ok_or(error!("invalid digit '{n1}'"))? * 16 +
n2.to_digit(16).ok_or(error!("invalid digit '{n2}'"))?
).unwrap());
},
'u' => {
@ -302,15 +295,15 @@ impl Lexer {
let c = self.next_not_eof()?;
if c == '}' { break }
if n >= 0x1000_0000_u32 {
return Err(InvalidCodepoint.into())
return Err(error!("invalid utf8 codepoint '{n}'"))
}
n = n * 16 + c.to_digit(16).ok_or::<crate::Error>(InvalidDigit(c).into())?;
n = n * 16 + c.to_digit(16).ok_or(error!("invalid digit '{c}'"))?;
}
let ch = char::from_u32(n).ok_or::<crate::Error>(InvalidCodepoint.into())?;
let ch = char::from_u32(n).ok_or(error!("invalid codepoint '{n}'"))?;
buf.push(ch);
},
_ => return Err(InvalidStringEscape(next).into())
_ => return Err(error!("invalid string escape '\\{next}'"))
}
}
@ -318,13 +311,12 @@ impl Lexer {
}
fn lex_ident(&mut self, initial: char) -> Result<TokenData> {
use Error as E;
use TokenData as T;
let mut buf = std::string::String::new();
if !initial.is_initial_ident() {
return Err(E::UnexpectedCharacter(initial).into())
return Err(error!("unexpected character '{initial}'"))
}
buf.push(initial);
@ -342,6 +334,7 @@ impl Lexer {
"else" => T::Else,
"while" => T::While,
"let" => T::Let,
"const" => T::Const,
"fn" | "function" => T::Function,
"true" => T::True,
"false" => T::False,
@ -364,7 +357,6 @@ impl Lexer {
fn lex_radix(&mut self, radix: i64, radix_char: char) -> Result<TokenData> {
use TokenData as T;
use Error as E;
let mut n = 0i64;
let mut char_found = false;
@ -375,7 +367,7 @@ impl Lexer {
n = n * radix + (i as i64);
char_found = true;
} else if self.peek().is_ident() {
return Err(E::InvalidDigit(self.peek()).into())
return Err(error!("invalid digit '{}'", self.peek()))
} else {
break;
}
@ -384,13 +376,11 @@ impl Lexer {
if char_found {
Ok(T::Int(n))
} else {
Err(E::InvalidNumber(format!("0{radix_char}")).into())
Err(error!("invalid number radix specifier '0{radix_char}'"))
}
}
fn lex_number(&mut self, initial: char) -> Result<TokenData> {
use Error as E;
if initial == '0' {
match self.peek() {
'x' => {
@ -459,7 +449,7 @@ impl Lexer {
}
if self.peek().is_ident() {
return Err(E::UnexpectedCharacter(self.peek()).into())
return Err(error!("unexpected character '{}'", self.peek()))
}
if let Ok(int) = buf.parse::<i64>() {
@ -478,23 +468,11 @@ impl Lexer {
return Ok(T::Float(float))
}
Err(E::InvalidNumber(buf).into())
Err(error!("invalid number '{buf}'"))
}
fn peek_token_impl(&mut self, ignore_newlines: bool) -> Result<Token> {
let idx = self.index;
let pos = self.pos;
let bidx = self.byte_len;
let token = self.next_token_impl(ignore_newlines);
self.index = idx;
self.pos = pos;
self.byte_len = bidx;
token
}
fn next_token_impl(&mut self, ignore_newlines: bool) -> Result<Token> {
fn read_token(&mut self, ignore_newlines: bool) -> Result<Token> {
use TokenData as T;
use Error as E;
self.skip_whitespace(ignore_newlines);
@ -705,7 +683,7 @@ impl Lexer {
self.next();
T::Regex(regex::Regex::new(&self.lex_string(next)?)
.map(|e| e.into())
.map_err(|e| E::InvalidRegex(e.into()))?)
.map_err(|e| error!("invalid regex: '{e}'"))?)
}
_ => {
self.lex_ident(char)?
@ -744,7 +722,6 @@ impl Lexer {
let str_end = self.index;
let byte_end = self.byte_len;
let str = self.data[str_start..str_end].to_owned().into_iter().collect();
Ok(Token {
data,
pos,
@ -754,19 +731,47 @@ impl Lexer {
})
}
pub fn peek_token(&mut self) -> Result<Token> {
self.peek_token_impl(true)
}
pub fn next_token(&mut self) -> Result<Token> {
self.next_token_impl(true)
}
pub fn peek_token_nl(&mut self) -> Result<Token> {
self.peek_token_impl(false)
let pos = self.pos;
match self.read_token(true) {
Ok(token) => Ok(token),
Err(e) => Err(e.pos(pos)),
}
}
pub fn next_token_nl(&mut self) -> Result<Token> {
self.next_token_impl(false)
let pos = self.pos;
match self.read_token(false) {
Ok(token) => Ok(token),
Err(e) => Err(e.pos(pos)),
}
}
pub fn peek_token(&mut self) -> Result<Token> {
let idx = self.index;
let pos = self.pos;
let bidx = self.byte_len;
let token = self.read_token(true);
self.index = idx;
self.pos = pos;
self.byte_len = bidx;
match token {
Ok(token) => Ok(token),
Err(e) => Err(e.pos(pos)),
}
}
pub fn peek_token_nl(&mut self) -> Result<Token> {
let idx = self.index;
let pos = self.pos;
let bidx = self.byte_len;
let token = self.read_token(false);
self.index = idx;
self.pos = pos;
self.byte_len = bidx;
match token {
Ok(token) => Ok(token),
Err(e) => Err(e.pos(pos)),
}
}
}

42
matrix-lang/src/lib.rs Normal file
View file

@ -0,0 +1,42 @@
mod compiler;
mod value;
mod lex;
mod vm;
mod parse;
mod chunk;
mod ast;
mod binary;
pub mod prelude;
#[macro_export]
macro_rules! iter {
($fn:expr) => {
$crate::prelude::Value::Iter(
::std::rc::Rc::new(
$crate::prelude::Function {
name: ::std::rc::Rc::from("<iterator>"),
arity: 0,
variadic: false,
fun: $crate::prelude::InnerFunction::Native(
::std::rc::Rc::new($fn
))}))
};
}
#[macro_export]
macro_rules! native {
($name:expr, $arity:expr, $varadic:expr, $fn:expr) => {
$crate::prelude::Value::Function(
::std::rc::Rc::new( $crate::prelude::Function {
name: std::rc::Rc::from($name),
arity: $arity,
variadic: $varadic,
fun: $crate::prelude::InnerFunction::Native(
::std::rc::Rc::new($fn)
)
})
)
}
}

View file

@ -1,6 +1,4 @@
use std::{fmt::Display, rc::Rc};
use num_complex::Complex64;
use crate::{lex::{Lexer, self, Token, TokenData, Position}, ast::{Expr, BinaryOp, UnaryOp, optimize, ExprData, AstName}, value::{Value, self}, Result};
use crate::prelude::*;
use Value as V;
use ExprData as E;
@ -33,38 +31,6 @@ pub struct Parser {
optimize: bool
}
#[derive(Debug)]
pub enum Error {
LexerError(crate::lex::Error),
UnexpectedToken(Token),
ExpectedToken(TokenData, Position),
MatrixInvDomain(usize, usize, usize),
NotAssignable(Expr),
ValueError(value::Error),
}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use Error::*;
match self {
LexerError(err) => write!(f, "{err}"),
UnexpectedToken(tok) => write!(f, "Unexpected token: '{:?}' at {}:{}", tok.data, tok.pos.row, tok.pos.col),
ExpectedToken(tok, pos) => write!(f, "Expected token: '{tok:?}' at {}:{}", pos.row, pos.col),
MatrixInvDomain(row, should, was) => write!(f, "In row {row} of matrix, domain was expected to be {should} but was given {was}"),
NotAssignable(expr) => write!(f, "{expr:?} is not assignable"),
ValueError(err) => write!(f, "{err}"),
}
}
}
impl From<lex::Error> for Error {
fn from(value: lex::Error) -> Self {
Self::LexerError(value)
}
}
impl std::error::Error for Error {}
macro_rules! expr_parser {
($parser:ident, $pattern:pat, $fn:ident) => {{
let mut expr = $parser.$fn()?;
@ -99,12 +65,18 @@ macro_rules! expr_parser_reverse {
}};
}
macro_rules! error {
($($arg:tt)*) => {
exception!(PARSE_EXCEPTION, $($arg)*)
};
}
impl Parser {
fn force_token(&mut self, tok: TokenData) -> Result<TokenData> {
let next = self.lexer.next_token()?;
if next.data != tok {
Err(Error::ExpectedToken(tok, next.pos).into())
Err(error!("expected token '{tok}'").pos(next.pos))
} else {
Ok(tok)
}
@ -113,7 +85,7 @@ impl Parser {
fn force_token_nl(&mut self, tok: TokenData) -> Result<TokenData> {
let next = self.lexer.next_token_nl()?;
if next.data != tok {
Err(Error::ExpectedToken(tok, next.pos).into())
Err(error!("expected token '{tok}'").pos(next.pos))
} else {
Ok(tok)
}
@ -135,7 +107,7 @@ impl Parser {
match next.data {
T::Comma => continue,
T::RightParen => break,
_ => return Err(Error::UnexpectedToken(next).into())
_ => return Err(error!("unexpected token '{next}'").pos(next.pos))
};
}
Ok(params)
@ -157,7 +129,7 @@ impl Parser {
match next.data {
T::SemiColon => continue,
T::RightBrack => break,
_ => return Err(Error::UnexpectedToken(next).into())
_ => return Err(error!("unexpected token '{next}'").pos(next.pos))
};
}
Ok(indicies)
@ -193,7 +165,7 @@ impl Parser {
match next.data {
T::SemiColon => continue,
T::RightBrack => break,
_ => return Err(Error::UnexpectedToken(next).into()),
_ => return Err(error!("unexpected token '{next}'").pos(next.pos))
};
}
if parts.len() == 1 {
@ -201,9 +173,9 @@ impl Parser {
} else {
let codomain = parts.len();
let domain = parts[0].len();
for (i, part) in parts.iter().enumerate() {
for part in parts.iter() {
if part.len() != domain {
return Err(Error::MatrixInvDomain(i, domain, part.len()).into())
return Err(error!("matrix row domains do not match: {} != {}", domain, part.len()).pos(pos))
}
}
let mut data = Vec::new();
@ -225,7 +197,7 @@ impl Parser {
},
T::Ident(ident) => (E::Literal(V::String(ident.to_string().into())), tok.pos).into(),
T::String(string) => (E::Literal(V::String(string.to_string().into())), tok.pos).into(),
_ => return Err(Error::UnexpectedToken(tok).into())
t => return Err(error!("unexpected token '{t}'").pos(tok.pos))
})
}
@ -247,7 +219,7 @@ impl Parser {
match next.data {
T::Comma => continue,
T::RightBrace => break,
_ => return Err(Error::UnexpectedToken(next).into())
_ => return Err(error!("unexpected token '{next}'").pos(next.pos))
}
}
Ok((E::Table(table), pos).into())
@ -273,7 +245,7 @@ impl Parser {
}
}
T::LeftParen => (),
_ => return Err(Error::UnexpectedToken(tok).into()),
t => return Err(error!("unexpected token '{t}'").pos(tok.pos))
}
let mut params = Vec::new();
@ -295,7 +267,7 @@ impl Parser {
}
T::Comma => continue,
T::RightParen => break,
_ => return Err(Error::UnexpectedToken(next).into()),
_ => return Err(error!("unexpected token '{next}'").pos(next.pos))
}
}
@ -307,7 +279,7 @@ impl Parser {
if let T::Ident(ident) = next.data {
Ok((ident, next.pos))
} else {
Err(Error::UnexpectedToken(next).into())
Err(error!("unexpected token '{next}'").pos(next.pos))
}
}
@ -327,7 +299,7 @@ impl Parser {
if let T::Ident(ident) = next.data {
Ok((ident, next.pos))
} else {
Err(Error::UnexpectedToken(next).into())
Err(error!("unexpected token '{next}'").pos(next.pos))
}
}
@ -419,6 +391,15 @@ impl Parser {
}
}
fn parse_const(&mut self) -> Result<Expr> {
let pos = self.lexer.peek_token()?.pos;
self.force_token(T::Const)?;
let ident = self.parse_ident_nl()?;
self.force_token(T::Assign)?;
let expr = self.parse_expr()?;
Ok((E::Const(ident, Box::new(expr)), pos).into())
}
fn parse_return(&mut self) -> Result<Expr> {
let pos = self.lexer.peek_token()?.pos;
self.force_token(T::Return)?;
@ -431,7 +412,10 @@ impl Parser {
self.force_token(T::LeftBrace)?;
loop {
let expr = match self.lexer.peek_token()?.data {
T::RightBrace => break,
T::RightBrace => {
self.lexer.next_token()?;
break
},
T::SemiColon => {
self.lexer.next_token()?;
continue;
@ -443,12 +427,9 @@ impl Parser {
match next.data {
T::SemiColon => continue,
T::RightBrace => break,
_ => return Err(Error::ExpectedToken(T::SemiColon, next.pos).into())
_ => return Err(error!("expected a semicolon").pos(next.pos))
}
}
if self.lexer.peek_token()?.data == T::RightBrace {
self.lexer.next_token()?;
}
Ok((E::Block(block), pos).into())
}
@ -474,7 +455,7 @@ impl Parser {
T::True => E::Literal(V::Bool(true)),
T::False => E::Literal(V::Bool(false)),
T::Ident(ident) => E::Ident(ident),
_ => return Err(Error::UnexpectedToken(tok).into()),
t => return Err(error!("unexpected token '{t}'").pos(tok.pos))
};
Ok((data, tok.pos).into())
}
@ -488,6 +469,7 @@ impl Parser {
While => self.parse_while(),
For => self.parse_for(),
Let => self.parse_let(),
Const => self.parse_const(),
LeftBrace => self.parse_block(),
Return => self.parse_return(),
If => self.parse_if(),
@ -752,11 +734,11 @@ impl Parser {
};
let expr = self.parse_expr()?;
block.push(expr);
let next = self.lexer.next_token()?;
let next = self.lexer.next_token_nl()?;
match next.data {
T::Eof => break,
T::SemiColon => continue,
_ => return Err(Error::ExpectedToken(T::SemiColon, next.pos).into())
_ => return Err(error!("expected a semicolon").pos(next.pos))
};
}
Ok((E::Block(block), Position::default()).into())

View file

@ -0,0 +1,60 @@
pub type Result<T> = std::result::Result<T, Exception>;
pub use crate::value::Value as Value;
pub use crate::value::exception::Exception as Exception;
pub use crate::value::matrix::Matrix as Matrix;
pub use crate::value::gc::Gc as Gc;
pub use crate::value::hash::ValueMap as ValueMap;
pub use crate::value::function::Function as Function;
pub use crate::value::function::InnerFunction as InnerFunction;
pub use num_complex::Complex64 as Complex64;
pub use num_rational::Rational64 as Rational64;
pub use regex::Regex as Regex;
pub use std::fs::File as File;
pub use std::rc::Rc as Rc;
pub use std::cell::RefCell as RefCell;
pub use crate::exception as exception;
pub use crate::native as native;
pub use crate::iter as iter;
pub use crate::value::exception::HASH_EXCEPTION as HASH_EXCEPTION;
pub use crate::value::exception::VALUE_EXCEPTION as VALUE_EXCEPTION;
pub use crate::value::exception::PARSE_EXCEPTION as PARSE_EXCEPTION;
pub use crate::value::exception::COMPILE_EXCEPTION as COMPILE_EXCEPTION;
pub use crate::value::exception::RUNTIME_EXCEPTION as RUNTIME_EXCEPTION;
pub use crate::value::exception::BINARY_EXECPTION as BINARY_EXCEPTION;
pub use crate::value::exception::IO_EXECPTION as IO_EXCEPTION;
pub use crate::value::clone::ValueClone as ValueClone;
pub use crate::value::hash::TryHash as TryHash;
pub use crate::vm::Vm as Vm;
pub use crate::vm::StackFrame as StackFrame;
pub use crate::vm::Interupt as Interupt;
pub use crate::lex::Lexer as Lexer;
pub use crate::lex::Position as Position;
pub use crate::lex::Token as Token;
pub use crate::lex::TokenData as TokenData;
pub use crate::parse::Parser as Parser;
pub use crate::parse::ParserBuilder as ParserBuilder;
pub use crate::compiler::Compiler as Compiler;
pub use crate::compiler::CompilerBuilder as CompilerBuilder;
pub use crate::compiler::NamesTable as NamesTable;
pub use crate::compiler::GlobalsTable as GlobalsTable;
pub use crate::compiler::Global as Global;
pub use crate::ast::AstName as AstName;
pub use crate::ast::Expr as Expr;
pub use crate::ast::ExprData as ExprData;
pub use crate::ast::BinaryOp as BinaryOp;
pub use crate::ast::UnaryOp as UnaryOp;
pub use crate::ast::optimize as optimize;
pub use crate::chunk::Chunk as Chunk;
pub use crate::chunk::Instruction as Instruction;
pub use crate::binary::Program as Program;

View file

@ -0,0 +1,54 @@
use crate::prelude::*;
pub trait ValueClone {
fn deep_clone(&self) -> Self;
fn shallow_clone(&self) -> Self;
}
impl ValueClone for Value {
fn deep_clone(&self) -> Self {
use Value as V;
match self {
V::List(l) => V::List(l.deep_clone()),
V::Table(t) => V::Table(t.deep_clone()),
V::Matrix(m) => V::Matrix(m.deep_clone()),
_ => self.clone()
}
}
fn shallow_clone(&self) -> Self {
use Value as V;
match self {
V::List(l) => V::List(l.shallow_clone()),
V::Table(t) => V::Table(t.shallow_clone()),
V::Matrix(m) => V::Matrix(m.shallow_clone()),
_ => self.clone()
}
}
}
impl ValueClone for Vec<Value> {
fn deep_clone(&self) -> Self {
let mut vals = Vec::new();
for val in self {
vals.push(val.deep_clone())
}
vals
}
fn shallow_clone(&self) -> Self {
self.clone()
}
}
impl ValueClone for Matrix {
fn deep_clone(&self) -> Self {
let values = self.values.deep_clone();
Self::new(self.domain, self.codomain, values)
}
fn shallow_clone(&self) -> Self {
let values = self.values.shallow_clone();
Self::new(self.domain, self.codomain, values)
}
}

View file

@ -0,0 +1,373 @@
use std::{ops::{Add, Sub, Mul, Div, Shl, Shr, BitOr, BitAnd, BitXor, Neg, Not}, cmp::Ordering};
use crate::prelude::*;
fn ratio_to_f64(r: Rational64) -> f64 {
*r.numer() as f64 / *r.denom() as f64
}
macro_rules! error {
($($arg:tt)*) => {
exception!(VALUE_EXCEPTION, $($arg)*)
};
}
///
/// MATH OPERATIONS
///
fn ipow(n: i64, d: i64, p: i64) -> Result<(i64, i64)> {
Ok(match (n, d, p) {
(0, _, 0) => Err(error!("cannot exponent 0 ** 0"))?,
(0, _, _) => (0, 1),
(_, _, 0) => (1, 1),
(1, 1, _) => (1, 1),
(n, d, p) if p < 0 => (d.pow((-p) as u32), n.pow((-p) as u32)),
(n, d, p) => (n.pow(p as u32), d.pow(p as u32)),
})
}
fn promote(a: Value, b: Value) -> (Value, Value) {
use Value as V;
match (&a, &b) {
(V::Int(x), V::Ratio(..)) => (V::Ratio((*x).into()), b),
(V::Int(x), V::Float(..)) => (V::Float(*x as f64), b),
(V::Int(x), V::Complex(..)) => (V::Complex((*x as f64).into()), b),
(V::Ratio(x), V::Float(..)) => (V::Float(ratio_to_f64(*x)), b),
(V::Ratio(x), V::Complex(..)) => (V::Complex(ratio_to_f64(*x).into()), b),
(V::Float(x), V::Complex(..)) => (V::Complex((*x).into()), b),
(V::Ratio(..), V::Int(y)) => (a, V::Ratio((*y).into())),
(V::Float(..), V::Int(y)) => (a, V::Float(*y as f64)),
(V::Complex(..), V::Int(y)) => (a, V::Complex((*y as f64).into())),
(V::Float(..), V::Ratio(y)) => (a, V::Float(ratio_to_f64(*y))),
(V::Complex(..), V::Ratio(y)) => (a, V::Complex(ratio_to_f64(*y).into())),
(V::Complex(..), V::Float(y)) => (a, V::Complex((*y).into())),
(V::List(l1), V::List(l2)) if l1.len() > 0 && l2.len() > 0
=> (V::Matrix(Matrix::from_list(l1.to_vec()).into()), V::Matrix(Matrix::from_list(l2.to_vec()).into())),
(_, V::List(l)) if l.len() > 0
=> (a, V::Matrix(Matrix::from_list(l.to_vec()).into())),
(V::List(l), _) if l.len() > 0
=> (V::Matrix(Matrix::from_list(l.to_vec()).into()), b),
_ => (a, b),
}
}
impl Add for Value {
type Output = Result<Self>;
fn add(self, rhs: Self) -> Self::Output {
use Value::*;
match promote(self, rhs) {
(Int(x), Int(y)) => Ok(Int(x + y)),
(Float(x), Float(y)) => Ok(Float(x + y)),
(Ratio(x), Ratio(y)) => Ok(Ratio(x + y)),
(Complex(x), Complex(y)) => Ok(Complex(x + y)),
(Matrix(x), Matrix(y)) => Ok(Matrix((x + y)?.into())),
(Matrix(x), r) => Ok(Matrix(x.increment(r)?.into())),
(l, Matrix(y)) => Ok(Matrix(y.increment(l)?.into())),
(String(str), value) => Ok(String(Rc::from(
format!("{str}{value}")
))),
(value, String(str)) => Ok(String(Rc::from(
format!("{value}{str}")
))),
(List(mut l1), List(l2)) => {
l1.extend_from_slice(&l2);
Ok(List(l1))
},
(l, r) => Err(error!("cannot add {l:?} + {r:?}"))
}
}
}
impl Sub for Value {
type Output = Result<Self>;
fn sub(self, rhs: Self) -> Self::Output {
use Value::*;
match promote(self, rhs) {
(Int(x), Int(y)) => Ok(Int(x - y)),
(Float(x), Float(y)) => Ok(Float(x - y)),
(Ratio(x), Ratio(y)) => Ok(Ratio(x - y)),
(Complex(x), Complex(y)) => Ok(Complex(x - y)),
(Matrix(x), Matrix(y)) => Ok(Matrix((x - y)?.into())),
(Matrix(x), r) => Ok(Matrix(x.decrement(r)?.into())),
(l, r) => Err(error!("cannot subtract {l:?} - {r:?}"))
}
}
}
impl Mul for Value {
type Output = Result<Self>;
fn mul(self, rhs: Value) -> Self::Output {
use Value::*;
match promote(self, rhs) {
(Int(x), Int(y)) => Ok(Int(x * y)),
(Float(x), Float(y)) => Ok(Float(x * y)),
(Ratio(x), Ratio(y)) => Ok(Ratio(x * y)),
(Complex(x), Complex(y)) => Ok(Complex(x * y)),
(Matrix(x), Matrix(y)) => Ok(Matrix((x * y)?.into())),
(Matrix(x), r) => Ok(Matrix(x.scale(r)?.into())),
(l, Matrix(y)) => Ok(Matrix(y.scale(l)?.into())),
(l, r) => Err(error!("cannot multiply {l:?} * {r:?}"))
}
}
}
impl Div for Value {
type Output = Result<Self>;
fn div(self, rhs: Value) -> Self::Output {
use Value::*;
match promote(self, rhs) {
(Int(_), Int(0)) => Err(error!("cannot divide by zero")),
(Int(x), Int(y)) => Ok(Ratio(Rational64::new(x, y))),
(Float(x), Float(y)) => Ok(Float(x / y)),
(Ratio(x), Ratio(y)) => Ok(Ratio(x / y)),
(Complex(x), Complex(y)) => Ok(Complex(x / y)),
(l, r) => Err(error!("cannot divide {l:?} / {r:?}"))
}
}
}
impl BitOr for Value {
type Output = Result<Self>;
fn bitor(self, rhs: Value) -> Self::Output {
use Value::*;
match promote(self, rhs) {
(Int(x), Int(y)) => Ok(Int(x | y)),
(l, r) => Err(error!("cannot bitwise or {l:?} | {r:?}"))
}
}
}
impl BitAnd for Value {
type Output = Result<Self>;
fn bitand(self, rhs: Value) -> Self::Output {
use Value::*;
match promote(self, rhs) {
(Int(x), Int(y)) => Ok(Int(x & y)),
(l, r) => Err(error!("cannot bitwise and {l:?} & {r:?}"))
}
}
}
impl BitXor for Value {
type Output = Result<Self>;
fn bitxor(self, rhs: Value) -> Self::Output {
use Value::*;
match promote(self, rhs) {
(Int(x), Int(y)) => Ok(Int(x ^ y)),
(l, r) => Err(error!("cannot bitwise xor {l:?} ^ {r:?}"))
}
}
}
impl Shl for Value {
type Output = Result<Self>;
fn shl(self, rhs: Value) -> Self::Output {
use Value::*;
match promote(self, rhs) {
(Int(x), Int(y)) => Ok(Int(x << y)),
(l, r) => Err(error!("cannot bitwise shift left {l:?} << {r:?}"))
}
}
}
impl Shr for Value {
type Output = Result<Self>;
fn shr(self, rhs: Value) -> Self::Output {
use Value::*;
match promote(self, rhs) {
(Int(x), Int(y)) => Ok(Int(x >> y)),
(l, r) => Err(error!("cannot bitwise shift right {l:?} >> {r:?}"))
}
}
}
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
use Value::*;
match (self, other) {
(Nil, Nil) => true,
(Bool(a), Bool(b)) => a == b,
(Int(a), Int(b)) => *a == *b,
(Ratio(a), Ratio(b)) => *a == *b,
(Float(a), Float(b)) => *a == *b,
(Complex(a), Complex(b)) => *a == *b,
(Int(a), Ratio(b)) => Rational64::from(*a) == *b,
(Ratio(a), Int(b)) => *a == Rational64::from(*b),
(Int(a), Float(b)) => *a as f64 == *b,
(Float(a), Int(b)) => *a == *b as f64,
(Int(a), Complex(b)) => Complex64::from(*a as f64) == *b,
(Complex(a), Int(b)) => *a == Complex64::from(*b as f64),
(Ratio(a), Float(b)) => ratio_to_f64(*a) == *b,
(Float(a), Ratio(b)) => *a == ratio_to_f64(*b),
(Ratio(a), Complex(b)) => Complex64::from(ratio_to_f64(*a)) == *b,
(Complex(a), Ratio(b)) => *a == Complex64::from(ratio_to_f64(*b)),
(Float(a), Complex(b)) => Complex64::from(*a) == *b,
(Complex(a), Float(b)) => *a == Complex64::from(*b),
(String(a), String(b)) => *a == *b,
(List(a), List(b)) => *a == *b,
(Matrix(a), Matrix(b)) => a == b,
_ => false,
}
}
}
impl PartialOrd for Value {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
use Value::*;
match (self, other) {
(Nil, Nil) => Some(Ordering::Equal),
(Bool(a), Bool(b)) => a.partial_cmp(b),
(Int(a), Int(b)) => a.partial_cmp(b),
(Ratio(a), Ratio(b)) => a.partial_cmp(b),
(Float(a), Float(b)) => a.partial_cmp(b),
(Int(a), Ratio(b)) => Rational64::from(*a).partial_cmp(b),
(Ratio(a), Int(b)) => a.partial_cmp(&Rational64::from(*b)),
(Int(a), Float(b)) => (*a as f64).partial_cmp(b),
(Float(a), Int(b)) => a.partial_cmp(&(*b as f64)),
(Ratio(a), Float(b)) => ratio_to_f64(*a).partial_cmp(b),
(Float(a), Ratio(b)) => a.partial_cmp(&ratio_to_f64(*b)),
(String(a), String(b)) => a.partial_cmp(b),
(List(a), List(b)) => a.partial_cmp(b),
(Matrix(a), Matrix(b)) => a.values.partial_cmp(&b.values),
_ => None,
}
}
}
impl Neg for Value {
type Output = Value;
fn neg(self) -> Self::Output {
use Value::*;
match self {
Bool(b) => Bool(!b),
Int(i) => Int(-i),
Float(f) => Float(-f),
Ratio(r) => Ratio(-r),
Complex(c) => Complex(-c),
_ => return Float(f64::NAN)
}
}
}
impl Not for Value {
type Output = bool;
fn not(self) -> Self::Output {
use Value as V;
match self {
V::Nil => true,
V::Bool(b) => !b,
V::Int(i) => i == 0,
V::Float(f) => f == 0.0,
V::Ratio(r) => *(r.numer()) == 0 || *(r.denom()) == 0,
V::Complex(c) => !c.is_normal(),
V::Regex(_) => false,
V::List(_) => false,
V::Matrix(_) => false,
V::Table(_) => false,
V::String(s) => s.as_ref() == "",
V::Function(_) => false,
V::Iter(_) => false,
V::Range(_) => false,
V::File(_) => false,
V::Exception(_) => false,
}
}
}
impl Value {
pub fn modulo(self, rhs: Value) -> Result<Self> {
use Value::*;
match promote(self, rhs) {
(Int(x), Int(y)) => Ok(Int(x % y)),
(Float(x), Float(y)) => Ok(Float(x % y)),
(Ratio(x), Ratio(y)) => Ok(Ratio(x % y)),
(Complex(x), Complex(y)) => Ok(Complex(x % y)),
(l, r) => Err(error!("cannot modulo: {l:?} % {r:?}"))
}
}
pub fn pow(self, rhs: Value) -> Result<Self> {
use Value::*;
if let (Ratio(x), Int(y)) = (&self, &rhs) {
return Ok(Ratio(ipow(*(*x).numer(), *(*x).denom(), *y)?.into()));
}
match promote(self, rhs) {
(Int(x), Int(y)) => Ok(Ratio(ipow(x, 1, y)?.into())),
(Float(x), Float(y)) => Ok(Float(x.powf(y))),
(Ratio(x), Ratio(y)) => Ok(Float(ratio_to_f64(x).powf(ratio_to_f64(y)))),
(Complex(x), Complex(y)) => Ok(Complex(x.powc(y))),
(l, r) => Err(error!("cannot exponent: {l:?} ** {r:?}"))
}
}
pub fn floaty(self) -> Self {
use Value as V;
match self {
V::Int(i) => V::Float(i as f64),
V::Ratio(r) => V::Float(ratio_to_f64(r)),
a => a
}
}
pub fn is_zero(&self) -> bool {
use Value as V;
match self {
V::Int(i) => *i == 0,
V::Float(f) => *f == 0.0 || *f == -0.0,
V::Ratio(r) => *r.numer() == 0,
_ => false,
}
}
pub fn binary_op(op: BinaryOp, lhs: Value, rhs: Value) -> Result<Self> {
use BinaryOp::*;
match op {
Add => lhs + rhs,
Subtract => lhs - rhs,
Multiply => lhs * rhs,
Divide => lhs / rhs,
Modulo => lhs.modulo(rhs),
Power => lhs.pow(rhs),
BitwiseAnd => lhs & rhs,
BitwiseOr => lhs | rhs,
BitwiseXor => lhs ^ rhs,
BitwiseShiftLeft => lhs << rhs,
BitwiseShiftRight => lhs >> rhs,
Equals => Ok(Self::Bool(lhs == rhs)),
NotEquals => Ok(Self::Bool(lhs != rhs)),
GreaterEquals => Ok(Self::Bool(lhs >= rhs)),
LessEquals => Ok(Self::Bool(lhs <= rhs)),
GreaterThan => Ok(Self::Bool(lhs > rhs)),
LessThan => Ok(Self::Bool(lhs < rhs)),
Range | RangeEq => {
let Value::Int(lhs) = lhs else {
return Err(error!("range can only take [Int]'s"))
};
let Value::Int(rhs) = rhs else {
return Err(error!("range can only take [Int]'s"))
};
Ok(Self::Range(Rc::new((lhs, rhs, op == RangeEq))))
},
}
}
pub fn unary_op(op: UnaryOp, val: Value) -> Value {
use UnaryOp::*;
match op {
Negate => -val,
Not => Self::Bool(!val),
}
}
pub fn to_regex(value: &str) -> Result<Self> {
match Regex::new(value) {
Ok(r) => Ok(Self::Regex(r.into())),
Err(e) => Err(error!("{e}")),
}
}
}

View file

@ -0,0 +1,78 @@
use std::{fmt::{Debug, Display}, error::Error};
use crate::prelude::*;
#[macro_export]
macro_rules! exception {
($type:expr) => {
$crate::prelude::Exception::new($type)
};
($type:expr, $($arg:tt)*) => {
$crate::prelude::Exception::msg($type, format!($($arg)*).as_str())
};
}
pub const HASH_EXCEPTION: &'static str = "hash";
pub const VALUE_EXCEPTION: &'static str = "value";
pub const PARSE_EXCEPTION: &'static str = "parse";
pub const COMPILE_EXCEPTION: &'static str = "compile";
pub const RUNTIME_EXCEPTION: &'static str = "runtime";
pub const BINARY_EXECPTION: &'static str = "binary";
pub const IO_EXECPTION: &'static str = "io";
#[derive(Clone)]
pub struct Exception(Gc<ExceptionInner>);
#[derive(Clone)]
struct ExceptionInner {
ty: Rc<str>,
msg: Rc<str>,
trace: Vec<(Rc<str>, Position)>
}
impl Exception {
pub fn msg(ty: &str, msg: &str) -> Self {
Self(Gc::new(ExceptionInner { ty: ty.into(), msg: msg.into(), trace: Vec::new() }))
}
pub fn pos(mut self, pos: Position) -> Self {
self.0.trace.push((
Rc::from("<root>"),
pos
));
self
}
pub fn trace(mut self, block: Rc<str>, pos: Position) -> Self {
self.0.trace.push((
block,
pos
));
self
}
}
impl Display for Exception {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let ty = self.0.ty.as_ref();
let msg = self.0.msg.as_ref();
write!(f, "{}\n Type <{}>", msg, ty)?;
for (block, pos) in self.0.trace.iter() {
write!(f, "\n In {block} at {pos}\n")?;
}
Ok(())
}
}
impl Debug for Exception {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "[Exception {}]", self.0.ty.as_ref())
}
}
impl Error for Exception {}
impl<T> From<Exception> for Result<T> {
fn from(value: Exception) -> Self {
Err(value)
}
}

View file

@ -0,0 +1,186 @@
use std::fmt::{Debug, Display};
use crate::prelude::*;
use Value as V;
impl Debug for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
V::Nil => write!(f, "[Nil]"),
V::Int(i) => write!(f, "[Int {i}]"),
V::Bool(b) => write!(f, "[Bool {b}]"),
V::Float(vf) => write!(f, "[Float {vf}]"),
V::Ratio(r) => write!(f, "[Ratio {r}]"),
V::Complex(c) => write!(f, "[Complex {c}]"),
V::Regex(r) => write!(f, "[Regex /{r}/]"),
V::String(s) => write!(f, "[String '{s}']"),
V::List(l) => write!(f, "[List {}]", l.len()),
V::Matrix(m) => write!(f, "[Matirx {}x{}]", m.domain, m.codomain),
V::Table(t) => write!(f, "[Table {}]", t.len()),
V::Function(vf) => write!(f, "[Function {}]", vf.name),
V::Range(r) => write!(f, "[Range {:?}..{:?}]", r.0, r.1),
V::Iter(_) => write!(f, "[Iterator]"),
V::Exception(_) => write!(f, "[Error]"),
V::File(_) => write!(f, "[File]"),
}
}
}
impl Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let red;
let green;
let yellow;
let blue;
let pink;
let cyan;
let clear;
if f.alternate() {
red = "\x1b[31m";
green = "\x1b[32m";
yellow = "\x1b[33m";
blue = "\x1b[34m";
pink = "\x1b[35m";
cyan = "\x1b[36m";
clear = "\x1b[0m";
} else {
red = "";
green = "";
yellow = "";
blue = "";
pink = "";
cyan = "";
clear = "";
}
match self {
V::Nil => {write!(f, "{blue}nil{clear}")?;},
V::Bool(b) => {write!(f, "{yellow}{b}{clear}")?;},
V::Int(i) => {write!(f, "{yellow}{i}{clear}")?;},
V::Float(l) => {write!(f, "{yellow}{l}{clear}")?;},
V::Ratio(r) => {write!(f, "{yellow}{r}{clear}")?;},
V::Complex(c) => {write!(f, "{yellow}{c}{clear}")?;},
V::Regex(r) => {write!(f, "/{red}{r}{clear}/")?;},
V::String(s) => {
if f.alternate() {
write!(f, "{green}'{s}'{clear}")?;
} else {
write!(f, "{s}")?;
}
}
V::List(l) => {
if l.len() < 1 {
write!(f, "[]")?;
return Ok(())
}
write!(f, "[ ")?;
for (i, el) in l.iter().enumerate() {
if i != 0 {
write!(f, " ")?;
}
if f.alternate() {
write!(f, "{el:#}")?;
} else {
write!(f, "{el}")?;
}
}
write!(f, " ]")?;
},
V::Matrix(m) => {
if f.alternate() {
write!(f, "{m:#}")?;
} else {
write!(f, "{m}")?;
}
},
V::Table(t) => {
if f.alternate() {
write!(f, "{t:#}")?;
} else {
write!(f, "{t}")?;
}
},
V::Function(fun) => {
write!(f, "{cyan}{fun}{clear}")?;
}
V::Range(r) => {
if f.alternate() {
write!(f, "{:#}..{:#}", r.0, r.1)?;
} else {
write!(f, "{}..{}", r.0, r.1)?;
}
}
V::Iter(_) => {write!(f, "{pink}[Iterator]{clear}")?;},
V::File(_) => {write!(f, "{pink}[File]{clear}")?;},
V::Exception(e) => {write!(f, "{red}{e}{clear}")?;},
};
Ok(())
}
}
impl Debug for Matrix {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "[Matrix {}x{}]", self.domain, self.codomain)
}
}
impl Display for Matrix {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut max_cols = vec![0; self.domain];
let mut vals: Vec<String> = Vec::with_capacity(self.domain * self.codomain);
for row in 0..self.codomain {
for col in 0..self.domain {
let idx = col + row * self.domain;
let el = &self.values[idx];
let s = match f.alternate() {
true => format!("{:#}", el),
false => format!("{}", el)
};
max_cols[col] = max_cols[col].max(s.len());
vals.push(s);
}
}
write!(f, "\n")?;
for row in 0..self.codomain {
for col in 0..self.domain {
let idx = col + row * self.domain;
let s = vals[idx].as_str();
let width = max_cols[col];
write!(f, " {s:>width$}")?;
}
write!(f, "\n")?;
}
Ok(())
}
}
impl Debug for ValueMap {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "[Table {}]", self.len())
}
}
impl Display for ValueMap {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.len() < 1 {
write!(f, "{{}}")?;
return Ok(())
}
write!(f, "{{ ")?;
for (i, (key, value)) in self.entries().enumerate() {
if i != 0 {
write!(f, ", ")?;
}
if f.alternate() {
write!(f, "{key:#} = {value:#}")?
} else {
write!(f, "{key} = {value}")?
}
}
write!(f, " }}")?;
Ok(())
}
}

View file

@ -0,0 +1,43 @@
use crate::prelude::*;
use std::fmt::{Debug, Display};
pub struct Function {
pub name: Rc<str>,
pub arity: usize,
pub variadic: bool,
pub fun: InnerFunction
}
#[derive(Clone)]
pub enum InnerFunction {
Compiled(Rc<Chunk>),
Native(Rc<dyn Fn((&mut Vm, &mut StackFrame), Vec<Value>) -> Result<Value>>),
}
impl Debug for Function {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use InnerFunction as F;
match self.fun {
F::Compiled(_) => {
write!(f, "[Function {}]", self.name)
},
F::Native(_) => {
write!(f, "[Builtin {}]", self.name)
}
}
}
}
impl Display for Function {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use InnerFunction as F;
match self.fun {
F::Compiled(_) => {
write!(f, "[Function {}]", self.name)
},
F::Native(_) => {
write!(f, "[Builtin {}]", self.name)
}
}
}
}

View file

@ -1,4 +1,5 @@
use std::{ops::{Index, IndexMut, Deref, DerefMut, Add, Sub, Mul}, marker::PhantomData, ptr::NonNull, fmt::{Debug, Display}};
use crate::prelude::*;
pub struct Gc<T> {
ptr: NonNull<GcInner<T>>,
@ -24,13 +25,6 @@ impl<T> Gc<T> {
}
impl <T: Clone> Gc<T> {
pub fn clone_inside(&self) -> Self {
unsafe {
let data = self.ptr.as_ref().data.clone();
Self::new(data)
}
}
pub fn into_inner(self) -> T {
unsafe {
self.ptr.as_ref().data.clone()
@ -44,6 +38,22 @@ impl <T: Clone> Gc<T> {
}
}
impl <T: ValueClone> ValueClone for Gc<T> {
fn deep_clone(&self) -> Self {
unsafe {
let data = self.ptr.as_ref().data.deep_clone();
Self::new(data)
}
}
fn shallow_clone(&self) -> Self {
unsafe {
let data = self.ptr.as_ref().data.shallow_clone();
Self::new(data)
}
}
}
impl<T> From<T> for Gc<T> {
fn from(value: T) -> Self {
Gc::new(value)

View file

@ -0,0 +1,240 @@
use crate::prelude::*;
use std::hash::{Hash, DefaultHasher, Hasher};
#[derive(Clone)]
pub struct ValueMap {
values: Vec<ValueEntry>,
size: usize,
used: usize,
}
#[derive(Clone)]
enum ValueEntry {
Empty,
Taken((Value, Value)),
Gravestone(Value),
}
impl Default for ValueMap {
fn default() -> Self {
ValueMap {
values: vec![ValueEntry::Empty; 8],
size: 8,
used: 0,
}
}
}
impl ValueMap {
fn get_index(values: &Vec<ValueEntry>, size: usize, key: &Value, hash: usize) -> usize {
let mut nearest_grave = None;
let start = hash & size;
for offset in 0..size {
let idx = (start + offset) % size;
match &values[idx] {
ValueEntry::Empty => return idx,
ValueEntry::Taken((marker, _)) => {
if marker.eq(key) { return idx };
continue;
},
ValueEntry::Gravestone(grave) => {
if grave.eq(key) { return idx };
if nearest_grave.is_none() {
nearest_grave = Some(idx);
}
continue;
},
}
}
match nearest_grave {
Some(idx) => idx,
None => panic!("cannot get value map index: full!!!"),
}
}
fn expand(&mut self) -> Result<()> {
let pairs = self.values.iter()
.filter_map(|e| {
match e {
ValueEntry::Taken(s) => Some(s.clone()),
_ => None
}
})
.collect::<Vec<(Value, Value)>>();
let new_used = pairs.len();
let new_size = self.size * 2 + pairs.len();
let mut new_values = vec![ValueEntry::Empty; new_size];
for (key, value) in pairs.into_iter() {
let hash = self.hash(&key)?;
let idx = ValueMap::get_index(&new_values, new_size, &key, hash);
new_values[idx] = ValueEntry::Taken((key, value));
}
self.used = new_used;
self.size = new_size;
self.values = new_values;
Ok(())
}
fn hash(&self, key: &Value) -> Result<usize> {
let mut hasher = DefaultHasher::new();
key.try_hash(&mut hasher)?;
Ok(hasher.finish() as usize)
}
pub fn get<'a>(&'a self, key: &Value) -> Result<Option<&'a Value>> {
let hash = self.hash(key)?;
let idx = ValueMap::get_index(&self.values, self.size, key, hash);
match &self.values[idx] {
ValueEntry::Taken((_, value)) => Ok(Some(value)),
_ => Ok(None)
}
}
pub fn insert(&mut self, key: Value, value: Value) -> Result<()> {
if self.used * 3 >= self.size * 2 {
self.expand()?;
}
let key = key.deep_clone();
let hash = self.hash(&key)?;
let idx = ValueMap::get_index(&self.values, self.size, &key, hash);
self.values[idx] = ValueEntry::Taken((key, value));
self.used += 1;
Ok(())
}
pub fn remove(&mut self, key: &Value) -> Result<Option<Value>> {
let hash = self.hash(key)?;
let idx = ValueMap::get_index(&self.values, self.size, key, hash);
let mut value = ValueEntry::Gravestone(key.clone());
std::mem::swap(&mut value, &mut self.values[idx]);
match value {
ValueEntry::Taken((_, v)) => {
self.used -= 1;
Ok(Some(v))
}
_ => Ok(None),
}
}
pub fn entries<'a>(&'a self) -> ValueMapIterator<'a> {
ValueMapIterator::new(self)
}
pub fn new() -> Self {
Self::default()
}
pub fn len(&self) -> usize {
self.used
}
}
pub struct ValueMapIterator<'a> {
map: &'a ValueMap,
index: usize
}
impl<'a> ValueMapIterator<'a> {
fn new(map: &'a ValueMap) -> Self {
Self { map, index: 0 }
}
}
impl<'a> Iterator for ValueMapIterator<'a> {
type Item = (&'a Value, &'a Value);
fn next(&mut self) -> Option<Self::Item> {
let mut idx = self.index;
loop {
if idx >= self.map.size {
return None
}
use ValueEntry as E;
let E::Taken((k, v)) = &self.map.values[idx] else {
idx += 1;
continue;
};
self.index = idx + 1;
return Some((k, v))
}
}
}
pub trait TryHash {
fn try_hash<H: std::hash::Hasher>(&self, state: &mut H) -> Result<()>;
}
impl TryHash for Value {
fn try_hash<H: std::hash::Hasher>(&self, state: &mut H) -> Result<()> {
use Value::*;
match self {
Nil => 0x23845.hash(state),
Bool(b) => b.hash(state),
Int(i) => i.hash(state),
Ratio(r) => r.hash(state),
Regex(r) => r.as_str().hash(state),
String(s) => s.hash(state),
List(l) => {
for val in l.iter() {
val.try_hash(state)?;
}
}
Matrix(m) => {
m.domain.hash(state);
m.codomain.hash(state);
for val in m.values.iter() {
val.try_hash(state)?;
}
},
_ => return Err(exception!(HASH_EXCEPTION, "cannot hash {self:?}"))
};
Ok(())
}
}
impl ValueClone for ValueEntry {
fn deep_clone(&self) -> Self {
use ValueEntry as E;
match self {
E::Empty => E::Empty,
E::Taken((k, v)) => E::Taken((k.deep_clone(), v.deep_clone())),
E::Gravestone(g) => E::Gravestone(g.deep_clone()),
}
}
fn shallow_clone(&self) -> Self {
use ValueEntry as E;
match self {
E::Empty => E::Empty,
E::Taken((k, v)) => E::Taken((k.clone(), v.clone())),
E::Gravestone(g) => E::Gravestone(g.clone()),
}
}
}
impl ValueClone for ValueMap {
fn deep_clone(&self) -> Self {
let values = self.values.iter().map(|e| e.deep_clone()).collect();
Self {
values,
size: self.size,
used: self.used
}
}
fn shallow_clone(&self) -> Self {
let values = self.values.iter().map(|e| e.clone()).collect();
Self {
values,
size: self.size,
used: self.used
}
}
}

View file

@ -0,0 +1,230 @@
use std::cmp::Ordering;
use crate::prelude::*;
macro_rules! error {
($($arg:tt)*) => {
Err(exception!(VALUE_EXCEPTION, $($arg)*))
};
}
impl Value {
pub fn val_cmp(&self, other: &Self) -> Result<Ordering> {
self.partial_cmp(other)
.map_or_else(|| error!("cannot compare: {self:?} and {other:?}"), Ok)
}
fn index_single(&self, index: &Value) -> Result<Self> {
use Value as V;
match (self, index) {
(V::Table(t), index) => {
Ok(t.get(index)?.unwrap_or(&Value::Nil).clone())
},
(V::List(l), V::Int(i)) => {
if *i < 0 || *i as usize >= l.len() {
return error!("{i} out of bounds for {self:?}")
}
Ok(l[*i as usize].clone())
},
(V::Matrix(m), V::Int(i)) => {
if *i < 0 || *i as usize >= m.values.len() {
return error!("{i} out of bounds for {self:?}")
}
Ok(m.values[*i as usize].clone())
},
_ => return error!("{index:?} cant index {self:?}")
}
}
fn index_multiple(&self, indexes: &Vec<Value>) -> Result<Self> {
use Value as V;
match self {
V::List(..) => {
let mut ret = Vec::new();
for index in indexes {
let res = self.index_single(index)?;
ret.push(res);
}
Ok(V::List(ret.into()))
}
V::Table(..) => {
let mut ret = ValueMap::new();
for index in indexes {
let res = self.index_single(index)?;
ret.insert(index.clone(), res)?;
}
Ok(V::Table(ret.into()))
}
V::Matrix(m) => {
let err = || error!("{self:?} can be index by [Int] or [Int;Int]");
if indexes.len() != 2 {
return err()
}
let lhs = indexes[0].clone();
let rhs = indexes[1].clone();
match (lhs, rhs) {
(V::Nil, V::Nil) => {
Ok(V::Matrix(m.shallow_clone()))
},
(V::Int(row), V::Nil) => {
let Some((_, row)) = m.rows().enumerate().filter(|(idx, _)| *idx as i64 == row).next() else {
return err();
};
let row: Vec<Value> = row.into_iter().map(|e| e.clone()).collect();
Ok(V::Matrix(Matrix::new(row.len(), 1, row).into()))
},
(V::Nil, V::Int(col)) => {
let Some((_, col)) = m.cols().enumerate().filter(|(idx, _)| *idx as i64 == col).next() else {
return err();
};
let col: Vec<Value> = col.into_iter().map(|e| e.clone()).collect();
Ok(V::Matrix(Matrix::new(1, col.len(), col).into()))
},
(V::Int(row), V::Int(col)) => {
if row < 0 || col < 0 {
return err();
}
m.get(row as usize, col as usize)
}
_ => return err()
}
}
_ => return error!("cannot index {self:?}")
}
}
pub fn index(&self, index: &Vec<Value>) -> Result<Self> {
if index.len() == 0 {
Ok(self.shallow_clone())
} else if index.len() == 1 {
self.index_single(&index[0])
} else {
self.index_multiple(index)
}
}
fn store_index_single(&mut self, index: &Value, store: Value) -> Result<()> {
use Value as V;
let err = format!("{self:?}");
match (self, index) {
(V::Table(t), index) => {
t.insert(index.clone(), store)
},
(V::List(l), V::Int(i)) => {
if *i < 0 || *i as usize >= l.len() {
return error!("{i} out of bounds for {err}")
}
l[*i as usize] = store;
Ok(())
},
(V::Matrix(m), V::Int(i)) => {
if *i < 0 || *i as usize >= m.values.len() {
return error!("{i} out of bounds for {err}")
}
m.values[*i as usize] = store;
Ok(())
},
_ => return error!("{index:?} cant index {err}")
}
}
fn store_index_multiple(&mut self, indexes: &Vec<Value>, store: Value) -> Result<()> {
use Value as V;
match self {
V::List(..) => {
for index in indexes {
self.store_index_single(index, store.clone())?;
}
Ok(())
}
V::Table(..) => {
for index in indexes {
self.store_index_single(index, store.clone())?;
}
Ok(())
}
_ => return error!("cannot index {self:?}")
}
}
pub fn store_index(&mut self, index: &Vec<Value>, store: Value) -> Result<()> {
if index.len() == 0 {
Ok(())
} else if index.len() == 1 {
self.store_index_single(&index[0], store)
} else {
self.store_index_multiple(index, store)
}
}
pub fn store_field_access(&mut self, ident: &str, val: Value) -> Result<()> {
use Value as V;
match self {
V::Table(t) => {
let key = V::String(Rc::from(ident));
Ok(t.insert(key, val)?)
},
_ => return error!("cannot field access assign {self:?}")
}
}
pub fn field_access(&self, ident: &str) -> Result<Self> {
use Value as V;
match self {
V::Table(t) => {
let key = V::String(Rc::from(ident));
Ok(t.get(&key)?.unwrap_or(&V::Nil).clone())
},
_ => return error!("cannot field access {self:?}")
}
}
}
impl Value {
pub fn into_iter_fn(self) -> Result<Rc<Function>> {
let Value::Iter(iter) = self.into_iter()? else {
return error!("bypassed iter check")
};
Ok(iter)
}
pub fn into_iter(self) -> Result<Self> {
use Value as V;
Ok(match self {
V::Iter(..) => self,
V::List(l) => {
let iter = RefCell::new(l.into_inner().into_iter());
iter!(move |_,_| {
match iter.borrow_mut().next() {
Some(v) => Ok(v),
None => Ok(V::Nil),
}
})
},
V::Range(r) => {
let r = (*r).clone();
let lhs = RefCell::new(r.0);
let rhs = r.1;
iter!(move |_,_| {
let val = *lhs.borrow();
let next = *lhs.borrow() + 1;
if (!r.2 && *lhs.borrow() < rhs) || (r.2 && *lhs.borrow() <= rhs) {
*lhs.borrow_mut() = next;
return Ok(Value::Int(val))
}
Ok(Value::Nil)
})
},
V::Function(f) => {
if f.arity > 0 || f.variadic {
return error!("iterator functions cannot be varadic or take arguments")
}
V::Iter(f)
},
val => return error!("cannot turn {val:?} into an iterator")
})
}
}

View file

@ -0,0 +1,337 @@
use std::ops::{Add, Sub, Mul};
use crate::prelude::*;
#[derive(Clone)]
pub struct Matrix {
pub domain: usize,
pub codomain: usize,
pub values: Vec<Value>
}
macro_rules! error {
($($arg:tt)*) => {
exception!(VALUE_EXCEPTION, $($arg)*)
};
}
impl Matrix {
pub fn new(
domain: usize,
codomain: usize,
values: Vec<Value>
) -> Self {
Self {
domain,
codomain,
values
}
}
pub fn from_list(
values: Vec<Value>
) -> Self {
Self {
domain: values.len(),
codomain: 1,
values
}
}
pub fn empty(
domain: usize,
codomain: usize
) -> Self {
let values = (0..(domain * codomain)).into_iter()
.map(|_| Value::Int(0))
.collect();
Self {
domain,
codomain,
values
}
}
pub fn get(&self, row: usize, col: usize) -> Result<Value> {
if row >= self.codomain || col >= self.domain {
return Err(error!("[{};{}] out of bounds for [Matrix {}x{}]", row, col, self.domain, self.codomain))
}
let idx = col + row * self.domain;
Ok(self.values[idx].clone())
}
pub fn set(&mut self, row: usize, col: usize, val: Value) -> Result<()> {
if row >= self.codomain || col >= self.domain {
return Err(error!("[{};{}] out of bounds for [Matrix {}x{}]", row, col, self.domain, self.codomain))
}
let idx = col + row * self.domain;
self.values[idx] = val;
Ok(())
}
pub fn rows<'a>(&'a self) -> MatrixRows<'a> {
MatrixRows {
matrix: self,
row: 0
}
}
pub fn cols<'a>(&'a self) -> MatrixCols<'a> {
MatrixCols {
matrix: self,
col: 0
}
}
// SPLCIE DOMAIN
pub fn splice_cols(&self, col_start: usize, col_end: usize) -> Result<Self> {
if col_start <= col_end || col_end > self.domain {
return Err(error!("[_;{}..{}] invalid for [Matrix {}x{}]", col_start, col_end, self.domain, self.codomain))
}
let mut cols = Vec::new();
for (i, col) in self.cols().enumerate() {
if i >= col_start && i < col_end {
cols.push(col);
}
}
let domain = cols.len();
let codomain = cols[0].len();
let mut res = Self::empty(domain, codomain);
for i in 0..domain {
for j in 0..codomain {
res.set(j, i, cols[i][j].clone())?;
}
}
Ok(res)
}
// SPLICE CODOMAIN
pub fn splice_rows(&self, row_start: usize, row_end: usize) -> Result<Self> {
if row_start <= row_end || row_end > self.codomain {
return Err(error!("[{}..{};_] invalid for [Matrix {}x{}]", row_start, row_end, self.domain, self.codomain))
}
let mut rows = Vec::new();
for (i, row) in self.rows().enumerate() {
if i >= row_start && i < row_end {
rows.push(row);
}
}
let domain = rows[0].len();
let codomain = rows.len();
let mut res = Self::empty(domain, codomain);
for i in 0..domain {
for j in 0..codomain {
res.set(j, i, rows[j][i].clone())?;
}
}
Ok(res)
}
pub fn increment(&self, increment: Value) -> Result<Self> {
let values = self.values.iter()
.map(|v| v.clone() + increment.clone())
.collect::<Result<Vec<Value>>>()?;
Ok(Matrix::new(self.domain, self.codomain, values))
}
pub fn decrement(&self, decrement: Value) -> Result<Self> {
let values = self.values.iter()
.map(|v| v.clone() - decrement.clone())
.collect::<Result<Vec<Value>>>()?;
Ok(Matrix::new(self.domain, self.codomain, values))
}
pub fn scale(&self, scale: Value) -> Result<Self> {
let values = self.values.iter()
.map(|v| v.clone() * scale.clone())
.collect::<Result<Vec<Value>>>()?;
Ok(Matrix::new(self.domain, self.codomain, values))
}
pub fn join_right(&self, other: &Matrix) -> Result<Self> {
if self.codomain != other.codomain {
return Err(error!("matrix codomain's do not match"))
}
let mut r1 = self.rows();
let mut r2 = other.rows();
let mut rows = Vec::new();
loop {
let Some(r1) = r1.next() else { break; };
let Some(r2) = r2.next() else { break; };
let mut row = r1
.into_iter()
.map(|v| v.clone())
.collect::<Vec<Value>>();
row.extend(r2.into_iter().map(|v| v.clone()));
rows.push(row);
}
let values = rows
.into_iter()
.reduce(|mut a,b| {a.extend(b); a})
.unwrap();
Ok(Matrix::new(self.domain + other.domain, self.codomain, values))
}
pub fn join_bottom(&self, other: &Matrix) -> Result<Self> {
if self.domain != other.domain {
return Err(error!("matrix domain's do not match"))
}
let mut values = self.values.clone();
values.extend(other.values.clone());
Ok(Matrix::new(self.domain, self.codomain + other.codomain, values))
}
}
impl PartialEq for Matrix {
fn eq(&self, other: &Self) -> bool {
if self.domain != other.domain || self.codomain != other.codomain {
return false
}
for i in 0..self.values.len() {
if self.values[i] != other.values[i] {
return false;
}
}
return true;
}
}
impl Add for Matrix {
type Output = Result<Self>;
fn add(self, rhs: Self) -> Self::Output {
if self.domain != rhs.domain || self.codomain != rhs.codomain {
return Err(error!("cannot add {self:?} + {rhs:?}"))
}
let mut res = Matrix::empty(self.domain, self.codomain);
for col in 0..self.domain {
for row in 0..self.codomain {
let add = self.get(row, col)? + rhs.get(row, col)?;
res.set(row, col, add?)?;
}
}
Ok(res)
}
}
impl Sub for Matrix {
type Output = Result<Self>;
fn sub(self, rhs: Self) -> Self::Output {
if self.domain != rhs.domain || self.codomain != rhs.codomain {
return Err(error!("cannot subtract {self:?} - {rhs:?}"))
}
let mut res = Matrix::empty(self.domain, self.codomain);
for col in 0..self.domain {
for row in 0..self.codomain {
let sub = self.get(row, col)? - rhs.get(row, col)?;
res.set(row, col, sub?)?;
}
}
Ok(res)
}
}
fn dot(lhs: Vec<&Value>, rhs: Vec<&Value>) -> Result<Value> {
let len = lhs.len();
let mut res = Value::Int(0);
for i in 0..len {
let val = (lhs[i].clone() * rhs[i].clone())?;
res = (res + val)?;
}
Ok(res)
}
impl Mul for Matrix {
type Output = Result<Self>;
fn mul(self, rhs: Self) -> Self::Output {
if self.domain != rhs.codomain {
return Err(error!("cannot multiply {self:?} * {rhs:?}"))
}
let mut res = Self::empty(rhs.domain, self.codomain);
for (i, row) in self.rows().enumerate() {
for (j, col) in rhs.cols().enumerate() {
let dot = dot(row.clone(), col.clone())?;
res.set(i, j, dot)?;
}
}
Ok(res)
}
}
pub struct MatrixRows<'a> {
matrix: &'a Matrix,
row: usize
}
impl<'a> Iterator for MatrixRows<'a> {
type Item = Vec<&'a Value>;
fn next(&mut self) -> Option<Self::Item> {
if self.row >= self.matrix.codomain {
return None
}
let row_start = self.row * self.matrix.domain;
let row_end = row_start + self.matrix.domain;
let res = self.matrix.values
.iter()
.enumerate()
.filter(|(idx, _)| *idx >= row_start && *idx < row_end)
.map(|v| v.1)
.collect();
self.row += 1;
Some(res)
}
}
pub struct MatrixCols<'a> {
matrix: &'a Matrix,
col: usize
}
impl<'a> Iterator for MatrixCols<'a> {
type Item = Vec<&'a Value>;
fn next(&mut self) -> Option<Self::Item> {
if self.col >= self.matrix.domain {
return None
}
let res = self.matrix.values
.iter()
.enumerate()
.filter(|(idx, _)| *idx % self.matrix.domain == self.col)
.map(|v| v.1)
.collect();
self.col += 1;
Some(res)
}
}

View file

@ -0,0 +1,42 @@
pub mod comp;
pub mod gc;
pub mod matrix;
pub mod hash;
pub mod exception;
pub mod function;
pub mod fmt;
pub mod index;
pub mod clone;
use crate::prelude::*;
#[derive(Clone)]
pub enum Value {
Nil,
Bool(bool),
Int(i64),
Float(f64),
Ratio(Rational64),
Complex(Complex64),
Regex(Rc<Regex>),
String(Rc<str>),
List(Gc<Vec<Value>>),
Matrix(Gc<Matrix>),
Table(Gc<ValueMap>),
Function(Rc<Function>),
Range(Rc<(i64, i64, bool)>),
Iter(Rc<Function>),
File(Rc<RefCell<File>>),
Exception(Exception),
}
impl From<&str> for Value {
fn from(value: &str) -> Self {
Value::String(Rc::from(value))
}
}

View file

@ -0,0 +1,522 @@
use std::{
collections::HashMap,
rc::Rc,
hash::Hash,
fmt::{Display, Debug},
ops::{Add, Neg, Not, Sub, Div, Mul, BitOr, BitAnd, BitXor, Shl, Shr},
cmp::Ordering,
cell::RefCell, fs::File
};
use crate::iter;
use num_complex::Complex64;
use num_rational::Rational64;
use regex::Regex;
use crate::{ast::{Expr, BinaryOp, UnaryOp}, chunk::Function, Result, gc::Gc};
pub type InlineList = Vec<Expr>;
pub type InlineMatrix = (usize, usize, Vec<Expr>);
pub type InlineTable = Vec<(Expr, Expr)>;
#[derive(Debug)]
pub struct Error(String);
impl Display for self::Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl std::error::Error for self::Error {}
macro_rules! error {
($($arg:tt)*) => {
Err(self::Error(format!($($arg)*)).into())
};
}
impl From<&str> for Value {
fn from(value: &str) -> Self {
Value::String(value.into())
}
}
impl Debug for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use Value as V;
match self {
V::Nil => write!(f, "[Nil]"),
V::Bool(b) => write!(f, "[Bool {b}]"),
V::Int(i) => write!(f, "[Int {i}]"),
V::Float(vf) => write!(f, "[Float {vf}]"),
V::Ratio(r) => write!(f, "[Ratio {r}]"),
V::Complex(c) => write!(f, "[Complex {c}]"),
V::Regex(r) => write!(f, "[Regex /{r}/]"),
V::String(s) => write!(f, "[String '{s}']"),
V::List(l) => write!(f, "[List {}]", l.len()),
V::Matrix(m) => write!(f, "[Matirx {}x{}]", m.domain, m.codomain),
V::Table(t) => write!(f, "[Table {}]", t.0.len()),
V::Function(vf) => write!(f, "[Function {}]", vf.name),
V::Range(r) => write!(f, "[Range {:?}..{:?}]", r.0, r.1),
V::Iter(_) => write!(f, "[Iterator]"),
V::Error(_) => write!(f, "[Error]"),
V::File(_) => write!(f, "[File]"),
}
}
}
impl Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use Value as V;
let red;
let green;
let yellow;
let blue;
let pink;
let cyan;
let clear;
if f.alternate() {
red = "\x1b[31m";
green = "\x1b[32m";
yellow = "\x1b[33m";
blue = "\x1b[34m";
pink = "\x1b[35m";
cyan = "\x1b[36m";
clear = "\x1b[0m";
} else {
red = "";
green = "";
yellow = "";
blue = "";
pink = "";
cyan = "";
clear = "";
}
match self {
V::Nil => {write!(f, "{blue}nil{clear}")?;},
V::Bool(b) => {write!(f, "{yellow}{b}{clear}")?;},
V::Int(i) => {write!(f, "{yellow}{i}{clear}")?;},
V::Float(l) => {write!(f, "{yellow}{l}{clear}")?;},
V::Ratio(r) => {write!(f, "{yellow}{r}{clear}")?;},
V::Complex(c) => {write!(f, "{yellow}{c}{clear}")?;},
V::Regex(r) => {write!(f, "/{red}{r}{clear}/")?;},
V::String(s) => {
if f.alternate() {
write!(f, "{green}'{s}'{clear}")?;
} else {
write!(f, "{s}")?;
}
}
V::List(l) => {
write!(f, "[")?;
for (i, el) in l.iter().enumerate() {
if i != 0 {
write!(f, " ")?;
}
if f.alternate() {
write!(f, "{el:#}")?;
} else {
write!(f, "{el}")?;
}
}
write!(f, "]")?;
},
V::Matrix(m) => {
if f.alternate() {
write!(f, "{m:#}")?;
} else {
write!(f, "{m}")?;
}
},
V::Table(t) => {
write!(f, "{{")?;
for (i, (key, val)) in t.0.iter().enumerate() {
if i != 0 {
write!(f, ", ")?;
}
if f.alternate() {
write!(f, "{key:#} = {val:#}")?;
} else {
write!(f, "{key} = {val}")?;
}
}
write!(f, "}}")?;
},
V::Function(fun) => {
use crate::chunk::InnerFunction as F;
match fun.fun {
F::Compiled(_) => write!(f, "{cyan}[Function {}]{clear}", fun.name)?,
F::Native(_) => write!(f, "{cyan}[Builtin {}]{clear}", fun.name)?,
};
},
V::Range(r) => {
if f.alternate() {
write!(f, "{:#}..{:#}", r.0, r.1)?;
} else {
write!(f, "{}..{}", r.0, r.1)?;
}
}
V::Iter(_) => {write!(f, "{pink}[Iterator]{clear}")?;},
V::File(_) => {write!(f, "{pink}[File]{clear}")?;},
V::Error(e) => {write!(f, "{red}{e}{clear}")?;},
};
Ok(())
}
}
impl Value {
pub fn can_hash(&self) -> Result<()> {
use Value::*;
match self {
Nil => {},
Bool(_) => {},
Int(_) => {},
Ratio(_) => {},
Regex(_) => {},
String(_) => {}
List(l) => {
for val in l.iter() {
val.can_hash()?;
}
}
Matrix(m) => {
for val in m.values.iter() {
val.can_hash()?;
}
},
_ => return error!("cannot hash {self:?}")
}
Ok(())
}
pub fn is_zero(&self) -> bool {
use Value as V;
match self {
V::Int(i) => *i == 0,
V::Float(f) => *f == 0.0 || *f == -0.0,
V::Ratio(r) => *r.numer() == 0,
_ => false,
}
}
}
impl Value {
pub fn val_cmp(&self, other: &Self) -> Result<Ordering> {
self.partial_cmp(other)
.map_or_else(|| error!("cannot compare: {self:?} and {other:?}"), Ok)
}
fn index_single(&self, index: &Value) -> Result<Self> {
use Value as V;
match (self, index) {
(V::Table(t), index) => {
Ok(t.get(index)?.unwrap_or(&Value::Nil).clone())
},
(V::List(l), V::Int(i)) => {
if *i < 0 || *i as usize >= l.len() {
return error!("{i} out of bounds for {self:?}")
}
Ok(l[*i as usize].clone())
},
(V::Matrix(m), V::Int(i)) => {
if *i < 0 || *i as usize >= m.values.len() {
return error!("{i} out of bounds for {self:?}")
}
Ok(m.values[*i as usize].clone())
},
_ => return error!("{index:?} cant index {self:?}")
}
}
fn index_multiple(&self, indexes: &Vec<Value>) -> Result<Self> {
use Value as V;
match self {
V::List(..) => {
let mut ret = Vec::new();
for index in indexes {
let res = self.index_single(index)?;
ret.push(res);
}
Ok(V::List(ret.into()))
}
V::Table(..) => {
let mut ret = ValueMap::new();
for index in indexes {
let res = self.index_single(index)?;
ret.insert(index.clone(), res)?;
}
Ok(V::Table(ret.into()))
}
V::Matrix(m) => {
let err = || error!("{self:?} can be index by [Int] or [Int;Int]");
if indexes.len() != 2 {
return err()
}
let lhs = indexes[0].clone();
let rhs = indexes[1].clone();
match (lhs, rhs) {
(V::Nil, V::Nil) => {
Ok(V::Matrix(m.clone_inside()))
},
(V::Int(row), V::Nil) => {
let Some((_, row)) = m.rows().enumerate().filter(|(idx, _)| *idx as i64 == row).next() else {
return err();
};
let row: Vec<Value> = row.into_iter().map(|e| e.clone()).collect();
Ok(V::Matrix(Matrix::new(row.len(), 1, row).into()))
},
(V::Nil, V::Int(col)) => {
let Some((_, col)) = m.cols().enumerate().filter(|(idx, _)| *idx as i64 == col).next() else {
return err();
};
let col: Vec<Value> = col.into_iter().map(|e| e.clone()).collect();
Ok(V::Matrix(Matrix::new(1, col.len(), col).into()))
},
(V::Int(row), V::Int(col)) => {
if row < 0 || col < 0 {
return err();
}
m.get(row as usize, col as usize)
}
_ => return err()
}
}
_ => return error!("cannot index {self:?}")
}
}
pub fn index(&self, index: &Vec<Value>) -> Result<Self> {
if index.len() == 0 {
Ok(self.clone_inside())
} else if index.len() == 1 {
self.index_single(&index[0])
} else {
self.index_multiple(index)
}
}
fn store_index_single(&mut self, index: &Value, store: Value) -> Result<()> {
use Value as V;
let err = format!("{self:?}");
match (self, index) {
(V::Table(t), index) => {
t.insert(index.clone(), store)
},
(V::List(l), V::Int(i)) => {
if *i < 0 || *i as usize >= l.len() {
return error!("{i} out of bounds for {err}")
}
l[*i as usize] = store;
Ok(())
},
(V::Matrix(m), V::Int(i)) => {
if *i < 0 || *i as usize >= m.values.len() {
return error!("{i} out of bounds for {err}")
}
m.values[*i as usize] = store;
Ok(())
},
_ => return error!("{index:?} cant index {err}")
}
}
fn store_index_multiple(&mut self, indexes: &Vec<Value>, store: Value) -> Result<()> {
use Value as V;
match self {
V::List(..) => {
for index in indexes {
self.store_index_single(index, store.clone())?;
}
Ok(())
}
V::Table(..) => {
for index in indexes {
self.store_index_single(index, store.clone())?;
}
Ok(())
}
_ => return error!("cannot index {self:?}")
}
}
pub fn store_index(&mut self, index: &Vec<Value>, store: Value) -> Result<()> {
if index.len() == 0 {
Ok(())
} else if index.len() == 1 {
self.store_index_single(&index[0], store)
} else {
self.store_index_multiple(index, store)
}
}
pub fn store_field_access(&mut self, ident: &str, val: Value) -> Result<()> {
use Value as V;
match self {
V::Table(t) => {
let key = V::String(Rc::from(ident));
Ok(t.insert(key, val)?)
},
_ => return error!("cannot field access assign {self:?}")
}
}
pub fn field_access(&self, ident: &str) -> Result<Self> {
use Value as V;
match self {
V::Table(t) => {
let key = V::String(Rc::from(ident));
Ok(t.get(&key)?.unwrap_or(&V::Nil).clone())
},
_ => return error!("cannot field access {self:?}")
}
}
pub fn binary_op(op: BinaryOp, lhs: Value, rhs: Value) -> Result<Self> {
use BinaryOp::*;
match op {
Add => lhs + rhs,
Subtract => lhs - rhs,
Multiply => lhs * rhs,
Divide => lhs / rhs,
Modulo => lhs.modulo(rhs),
Power => lhs.pow(rhs),
BitwiseAnd => lhs & rhs,
BitwiseOr => lhs | rhs,
BitwiseXor => lhs ^ rhs,
BitwiseShiftLeft => lhs << rhs,
BitwiseShiftRight => lhs >> rhs,
Equals => Ok(Self::Bool(lhs == rhs)),
NotEquals => Ok(Self::Bool(lhs != rhs)),
GreaterEquals => Ok(Self::Bool(lhs >= rhs)),
LessEquals => Ok(Self::Bool(lhs <= rhs)),
GreaterThan => Ok(Self::Bool(lhs > rhs)),
LessThan => Ok(Self::Bool(lhs < rhs)),
Range | RangeEq => {
let Value::Int(lhs) = lhs else {
return error!("range can only take [Int]'s")
};
let Value::Int(rhs) = rhs else {
return error!("range can only take [Int]'s")
};
Ok(Self::Range(Rc::new((lhs, rhs, op == RangeEq))))
},
}
}
pub fn unary_op(op: UnaryOp, val: Value) -> Value {
use UnaryOp::*;
match op {
Negate => -val,
Not => Self::Bool(!val),
}
}
}
impl Neg for Value {
type Output = Value;
fn neg(self) -> Self::Output {
use Value::*;
match self {
Bool(b) => Bool(!b),
Int(i) => Int(-i),
Float(f) => Float(-f),
Ratio(r) => Ratio(-r),
Complex(c) => Complex(-c),
_ => return Float(f64::NAN)
}
}
}
impl Not for Value {
type Output = bool;
fn not(self) -> Self::Output {
use Value as V;
match self {
V::Nil => true,
V::Bool(b) => !b,
V::Int(i) => i == 0,
V::Float(f) => f == 0.0,
V::Ratio(r) => *(r.numer()) == 0 || *(r.denom()) == 0,
V::Complex(c) => !c.is_normal(),
V::Regex(_) => false,
V::List(_) => false,
V::Matrix(_) => false,
V::Table(_) => false,
V::String(s) => s.as_ref() == "",
V::Function(_) => false,
V::Iter(_) => false,
V::Range(_) => false,
V::File(_) => false,
V::Error(_) => false,
}
}
}
impl Value {
pub fn into_iter_fn(self) -> Result<Rc<Function>> {
let Value::Iter(iter) = self.into_iter()? else {
return error!("bypassed iter check")
};
Ok(iter)
}
pub fn into_iter(self) -> Result<Self> {
use Value as V;
Ok(match self {
V::Iter(..) => self,
V::List(l) => {
let iter = RefCell::new(l.into_inner().into_iter());
iter!(move |_,_| {
match iter.borrow_mut().next() {
Some(v) => Ok(v),
None => Ok(V::Nil),
}
})
},
V::Range(r) => {
let r = (*r).clone();
let lhs = RefCell::new(r.0);
let rhs = r.1;
iter!(move |_,_| {
let val = *lhs.borrow();
let next = *lhs.borrow() + 1;
if (!r.2 && *lhs.borrow() < rhs) || (r.2 && *lhs.borrow() <= rhs) {
*lhs.borrow_mut() = next;
return Ok(Value::Int(val))
}
Ok(Value::Nil)
})
},
V::Function(f) => {
if f.arity > 0 || f.variadic {
return error!("iterator functions cannot be varadic or take arguments")
}
V::Iter(f)
},
val => return error!("cannot turn {val:?} into an iterator")
})
}
}
fn dot(lhs: Vec<&Value>, rhs: Vec<&Value>) -> Result<Value> {
let len = lhs.len();
let mut res = Value::Int(0);
for i in 0..len {
let val = (lhs[i].clone() * rhs[i].clone())?;
res = (res + val)?;
}
Ok(res)
}

View file

@ -1,33 +1,6 @@
use std::{rc::Rc, fmt::{Debug, Display}, usize, ops::{Index, IndexMut}, collections::HashMap, cell::RefCell, sync::{atomic::{AtomicUsize, Ordering}, Arc}};
use crate::{value::{Value, self, ValueMap, Matrix}, Result, gc::Gc, chunk::{Function, Instruction, Chunk, InnerFunction}, compiler::NamesTable, native};
use std::{fmt::Display, ops::{IndexMut, Index}, sync::{atomic::{AtomicUsize, Ordering}, Arc}, collections::HashMap};
use crate::prelude::*;
#[derive(Debug)]
pub enum Error {
ValueError(value::Error),
NotFunction(Value),
InvArity(usize, usize, Rc<str>),
ExecNative,
}
impl std::error::Error for Error {}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use Error::*;
match self {
ValueError(err) => write!(f, "{err}"),
NotFunction(v) => write!(f, "{v:?} is not a function"),
InvArity(wanted, had, name) => write!(f, "function {name} takes {wanted} params but was given {had}"),
ExecNative => write!(f, "cannot execute a native function"),
}
}
}
impl From<value::Error> for Error {
fn from(value: value::Error) -> Self {
Error::ValueError(value)
}
}
pub struct Stack<T> {
inner: Vec<T>
}
@ -99,12 +72,12 @@ pub struct StackFrame {
}
struct VmError {
err: crate::Error,
err: Exception,
frames: Vec<StackFrame>
}
impl From<crate::Error> for VmError {
fn from(err: crate::Error) -> Self {
impl From<Exception> for VmError {
fn from(err: Exception) -> Self {
VmError {
err,
frames: Vec::new()
@ -112,15 +85,6 @@ impl From<crate::Error> for VmError {
}
}
impl From<self::Error> for VmError {
fn from(err: self::Error) -> Self {
VmError {
err: err.into(),
frames: Vec::new()
}
}
}
type VmResult<T> = std::result::Result<T, VmError>;
impl StackFrame {
@ -166,10 +130,16 @@ pub struct Vm {
trystack: Vec<TryScope>,
globals: Rc<RefCell<HashMap<u16, Value>>>,
names: NamesTable,
global_names: NamesTable,
global_names: GlobalsTable,
interupt: Arc<AtomicUsize>,
}
macro_rules! error {
($($arg:tt)*) => {
exception!(RUNTIME_EXCEPTION, $($arg)*)
};
}
impl Vm {
fn push(&mut self, val: Value) {
@ -188,7 +158,7 @@ impl Vm {
self.names.clone()
}
pub fn global_names(&self) -> NamesTable {
pub fn globals(&self) -> GlobalsTable {
self.global_names.clone()
}
@ -210,7 +180,7 @@ impl Vm {
pub fn load_global(&mut self, value: Value, name: &str) {
let idx = self.global_names.borrow().len();
self.global_names.borrow_mut().push(name.into());
self.global_names.borrow_mut().push(Global { idx, name: Rc::from(name), is_const: true });
self.globals.borrow_mut().insert(idx as u16, value);
}
@ -220,7 +190,7 @@ impl Vm {
fn init_frame(&mut self, fun: Rc<Function>) -> Result<StackFrame> {
let InnerFunction::Compiled(ref compiled_chunk) = fun.fun else {
return Err(self::Error::ExecNative.into())
return Err(error!("cannot create stack frame on builtin function"))
};
Ok(StackFrame::new(
self,
@ -240,7 +210,7 @@ impl Vm {
match ins {
I::NoOp => {},
I::CreateLocal => self.locals.push(self.stack.pop()),
I::LoadLocal(idx) => {self.stack.push(self.locals[idx as usize].clone());},
I::LoadLocal(idx) => {self.stack.push(self.locals[frame.lp + idx as usize].clone());},
I::StoreLocal(idx) => self.locals[frame.bp + idx as usize] = self.pop(),
I::DiscardLocals(count) => {self.locals.truncate(self.locals.len() - count as usize)},
I::LoadGlobal(idx) => {
@ -325,11 +295,12 @@ impl Vm {
let fun = self.pop();
let Value::Function(fun) = fun else {
return Err(Error::NotFunction(fun).into())
Err(error!("cannot call {fun} (not a function)"))?
};
if !fun.variadic && arity > fun.arity {
return Err(Error::InvArity(fun.arity, arity as usize, fun.name.clone()).into())
let needs = fun.arity;
Err(error!("function {fun} takes {needs} args, given {arity}"))?
}
let mut params = self.stack.split_off(self.stack.len() - arity).inner;
@ -416,14 +387,14 @@ impl Vm {
Ok(None)
}
fn stack_trace(&mut self, frames: Vec<StackFrame>) -> String {
let mut trace = String::from("\x1b[33m\x1b[1mStack Trace:\x1b[0m\n");
fn stack_trace(&mut self, frames: Vec<StackFrame>, e: Exception) -> Exception {
let mut e = e;
for frame in frames {
let ip = frame.ip - 1;
let pos = frame.body.pos[ip];
trace.push_str(&format!(" {} at {}:{}\n", &frame.name, pos.row, pos.col));
e = e.trace(frame.name, pos);
}
trace
e
}
fn exec_fn(&mut self, frame: &mut StackFrame, fun: Rc<Function>, params: Vec<Value>) -> VmResult<Value> {
@ -438,7 +409,8 @@ impl Vm {
self.stack.push(param);
}
let mut new_frame = StackFrame::new(self, name, body.clone(), params_len, frame.depth + 1);
self.exec(&mut new_frame)
let res = self.exec(&mut new_frame);
res
},
InnerFunction::Native(native_fun) => {
Ok(native_fun((self, frame), params)?)
@ -462,7 +434,7 @@ impl Vm {
self.stack.truncate(catch.stack_len);
self.locals.truncate(catch.locals_len);
frame.ip = catch.err_idx;
self.stack.push(Value::Error(err.err));
self.stack.push(Value::Exception(err.err));
} else {
let mut err = err;
err.frames.push(frame.clone());
@ -478,13 +450,12 @@ impl Vm {
}
pub fn run(&mut self, fun: Rc<Function>) -> Result<Value> {
let mut frame = self.init_frame(fun)?;
self.interupt.store(0, Ordering::SeqCst);
self.stack = Stack::new();
self.locals = Stack::new();
let mut frame = self.init_frame(fun)?;
self.exec(&mut frame).map_err(|e| {
let trace = self.stack_trace(e.frames);
(e.err, trace).into()
self.stack_trace(e.frames, e.err)
})
}
}

View file

@ -9,4 +9,4 @@ proc-macro = true
[dependencies]
syn = { version = "1", features = ["full"] }
quote = "1"
matrix = { path = "../matrix" }
matrix-lang = { path = "../matrix-lang" }

View file

@ -37,12 +37,12 @@ pub fn native_func(input: TokenStream, annotated_item: TokenStream) -> TokenStre
assert!(itemfn.sig.variadic.is_none(), "item must not be variadic");
let expanded = quote! {
#visibility fn #name() -> ::std::rc::Rc< ::matrix::chunk::Function> {
::std::rc::Rc::new( ::matrix::chunk::Function {
#visibility fn #name() -> ::std::rc::Rc< ::matrix_lang::prelude::Function> {
::std::rc::Rc::new( ::matrix_lang::prelude::Function {
name: ::std::rc::Rc::from( #name_str ),
arity: #arity,
variadic: #variadic,
fun: ::matrix::chunk::InnerFunction::Native(
fun: ::matrix_lang::prelude::InnerFunction::Native(
::std::rc::Rc::new(
|#inputs| #output #block
)

View file

@ -1,5 +1,5 @@
[package]
name = "matrix-stdlib"
name = "matrix-std"
version = "0.1.0"
edition = "2021"
@ -7,7 +7,7 @@ edition = "2021"
[dependencies]
anyhow = "1"
matrix = { path = "../matrix" }
matrix-lang = { path = "../matrix-lang" }
matrix-macros = { path = "../matrix-macros" }
os_info = "3"
rand = "0.8"

View file

@ -1,10 +1,8 @@
use std::hash::{DefaultHasher, Hash, Hasher};
use matrix::{vm::Vm, value::Value, unpack_args, Result, unpack_varargs};
use matrix_macros::native_func;
use std::hash::{DefaultHasher, Hasher};
use rand::Rng;
use crate::{VmArgs, next, error};
use matrix_lang::prelude::*;
use matrix_macros::native_func;
use crate::{VmArgs, next, error, unpack_args, unpack_varargs};
fn to_radix(r: i64, mut n: i64) -> String {
let mut result = String::new();
@ -123,11 +121,8 @@ fn remove(_: VmArgs, args: Vec<Value>) -> Result<Value> {
#[native_func(1)]
fn hash(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [value] = unpack_args!(args);
if let Err(e) = value.can_hash() {
return Err(e)
};
let mut hasher = DefaultHasher::new();
value.hash(&mut hasher);
value.try_hash(&mut hasher)?;
let fin = hasher.finish();
Ok(Value::Int(fin as u32 as i64))
}
@ -201,8 +196,10 @@ fn sort(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let Value::List(mut list) = value else {
return error!("sort requires a list")
};
let hi = list.len() - 1;
quicksort(list.as_mut(), 0, hi);
if list.len() > 1 {
let hi = list.len() - 1;
quicksort(list.as_mut(), 0, hi);
}
Ok(Value::List(list))
}

View file

@ -1,8 +1,7 @@
use std::{io::{self, Read, Write}, cell::RefCell, fs::OpenOptions, rc::Rc};
use matrix::{value::Value, self, vm::Vm, Result, unpack_varargs, iter, unpack_args};
use matrix_lang::prelude::*;
use matrix_macros::native_func;
use crate::{error, VmArgs};
use crate::{error, VmArgs, unpack_varargs, unpack_args};
#[native_func(0..)]
fn print(_: VmArgs, args: Vec<Value>) -> Result<Value> {

View file

@ -1,7 +1,7 @@
use std::{cell::RefCell, rc::Rc};
use matrix::{iter, vm::Vm, value::Value, Result, unpack_varargs, unpack_args};
use matrix_lang::prelude::*;
use matrix_macros::native_func;
use crate::{error, next, VmArgs};
use crate::{error, next, VmArgs, unpack_args, unpack_varargs};
use Value as V;
@ -54,7 +54,7 @@ fn range(_: VmArgs, args: Vec<Value>) -> Result<Value> {
Ok(iter!(move |_,_| {
let curr = *(i.borrow());
*(i.borrow_mut()) = curr + step;
if curr >= max {
if (step >= 0 && curr >= max) || (step <= 0 && curr <= max) {
return Ok(V::Nil)
} else {
return Ok(V::Int(curr))
@ -235,7 +235,7 @@ fn lines(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let Value::String(str) = str else {
return error!("lines arg must be a string")
};
let lines: Vec<Rc<str>> = str.split_inclusive("\n").map(|s| Rc::from(s)).collect();
let lines: Vec<Rc<str>> = str.split("\n").map(|s| Rc::from(s)).collect();
let res = RefCell::new(lines.into_iter());
Ok(iter!(move |_,_| {
match res.borrow_mut().next() {
@ -245,7 +245,7 @@ fn lines(_: VmArgs, args: Vec<Value>) -> Result<Value> {
}))
}
#[native_func(1)]
#[native_func(2)]
fn skip((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
let [count, iter] = unpack_args!(args);
let iter = iter.into_iter_fn()?;
@ -291,25 +291,32 @@ fn alternate(_: VmArgs, args: Vec<Value>) -> Result<Value> {
}
#[native_func(2)]
fn intersperse(_: VmArgs, args: Vec<Value>) -> Result<Value> {
fn intersperse((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
let [value, iter] = unpack_args!(args);
let iter = iter.into_iter_fn()?;
let flag = RefCell::new(Some(0));
let next = RefCell::new(next!(vm, frame, iter)?);
Ok(iter!(move |(vm, frame),_| {
let f = *flag.borrow();
match f {
Some(0) => {
let val = next!(vm, frame, iter)?;
if val == Value::Nil {
let val = next.borrow();
if *val == Value::Nil {
*flag.borrow_mut() = None;
} else {
*flag.borrow_mut() = Some(1);
}
Ok(val)
Ok(val.clone())
},
Some(1) => {
*next.borrow_mut() = next!(vm, frame, iter)?;
if *next.borrow() == Value::Nil {
*flag.borrow_mut() = None;
return Ok(Value::Nil)
} else {
*flag.borrow_mut() = Some(0);
}
let val = value.clone();
*flag.borrow_mut() = Some(0);
Ok(val)
},
_ => Ok(Value::Nil)
@ -331,6 +338,7 @@ fn zip(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let vr = next!(vm, frame, ir)?;
if vl == Value::Nil || vr == Value::Nil {
*flag.borrow_mut() = false;
return Ok(Value::Nil)
}
Ok(Value::List(vec![vl, vr].into()))
},
@ -401,7 +409,7 @@ fn take((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
Ok(Value::List(values.into()))
}
#[native_func(2)]
#[native_func(1)]
fn last((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
let [iter] = unpack_args!(args);
let iter = iter.into_iter_fn()?;
@ -414,7 +422,7 @@ fn last((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
Ok(last)
}
#[native_func(2)]
#[native_func(1)]
fn next((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
let [iter] = unpack_args!(args);
let iter = iter.into_iter_fn()?;

50
matrix-std/src/lib.rs Normal file
View file

@ -0,0 +1,50 @@
mod core;
mod sys;
mod math;
mod io;
mod iter;
use matrix_lang::prelude::*;
pub(crate) type VmArgs<'a, 'b> = (&'a mut Vm, &'b mut StackFrame);
macro_rules! error {
($($arg:tt)*) => {
Err(::matrix_lang::prelude::exception!(::matrix_lang::prelude::RUNTIME_EXCEPTION, $($arg)*))
};
}
macro_rules! next {
($vm:expr, $frame:expr, $iter:expr) => {
$vm.run_fn($frame, $iter.clone(), vec![])
};
}
macro_rules! unpack_args {
($e:expr) => {
$e.try_into().expect("bypassed arity check")
};
}
macro_rules! unpack_varargs {
($e:expr) => {{
let mut args = $e;
let matrix_lang::prelude::Value::List(varargs) = args.pop().expect("bypassed arity check") else {
panic!("bypassed arity check")
};
let varargs = varargs.into_inner();
(args.try_into().expect("bypassed arity check"), varargs)
}};
}
pub(crate) use error;
pub(crate) use next;
pub(crate) use unpack_args;
pub(crate) use unpack_varargs;
pub fn load(vm: &mut Vm) {
core::load(vm);
sys::load(vm);
io::load(vm);
iter::load(vm);
math::load(vm);
}

View file

@ -1,9 +1,8 @@
use core::f64;
use std::f64::{consts::{PI, E, TAU}, NAN, INFINITY};
use matrix::{vm::Vm, value::{Value, Matrix}, Result, unpack_args, Rational64, Complex64};
use matrix_lang::prelude::*;
use matrix_macros::native_func;
use crate::{error, VmArgs};
use crate::{error, VmArgs, unpack_args};
#[native_func(1)]
fn trans(_: VmArgs, args: Vec<Value>) -> Result<Value> {
@ -280,7 +279,7 @@ macro_rules! trig {
fn $type(_: VmArgs, args: Vec<Value>) -> Result<Value> {
use Value as V;
let [value] = unpack_args!(args);
match value.promote_trig() {
match value.floaty() {
V::Float(f) => Ok(V::Float(f.$type())),
V::Complex(c) => Ok(V::Complex(c.$type())),
v => error!("cannot compute {} on {v}", stringify!($type))
@ -295,7 +294,7 @@ macro_rules! trigf {
fn $str(_: VmArgs, args: Vec<Value>) -> Result<Value> {
use Value as V;
let [value] = unpack_args!(args);
match value.promote_trig() {
match value.floaty() {
V::Float(f) => Ok(V::Float(f.$type())),
v => error!("cannot compute {} on {v}", stringify!($str))
}
@ -307,7 +306,7 @@ macro_rules! trigf {
fn log(_: VmArgs, args: Vec<Value>) -> Result<Value> {
use Value as V;
let [base, value] = unpack_args!(args);
match (base.promote_trig(), value.promote_trig()) {
match (base.floaty(), value.floaty()) {
(V::Float(base), V::Float(arg)) => Ok(V::Float(arg.log(base))),
(V::Float(base), V::Complex(arg)) => Ok(V::Complex(arg.log(base))),
(V::Complex(base), V::Float(arg)) => Ok(V::Complex(arg.ln() / base.ln())),
@ -463,7 +462,7 @@ fn im(_: VmArgs, args: Vec<Value>) -> Result<Value> {
#[native_func(1)]
fn cis(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [value] = unpack_args!(args);
match value.promote_trig() {
match value.floaty() {
Value::Float(f) => Ok(Value::Complex(Complex64::cis(f))),
Value::Complex(c) => Ok((Value::Complex(Complex64::cis(c.re)) * Value::Float((-c.im).exp()))?),
_ => error!("cis can only take floats")

View file

@ -1,9 +1,9 @@
use std::{process::{exit, Command, Stdio, Child}, env, rc::Rc, io::{Read, self}, cell::RefCell, fs::{File, self}, os::fd::FromRawFd, sync::OnceLock, path::PathBuf};
use matrix::{vm::Vm, value::{Value, ValueMap}, unpack_args, Result, gc::Gc};
use matrix_lang::prelude::*;
use matrix_macros::native_func;
use os_info::Info;
use crate::{VmArgs, error};
use crate::{VmArgs, error, unpack_args};
#[native_func(1)]
fn sys_exit(_: VmArgs, args: Vec<Value>) -> Result<Value> {

View file

@ -1,32 +0,0 @@
use matrix::vm::{Vm, StackFrame};
mod core;
mod sys;
mod math;
mod io;
mod iter;
pub(crate) type VmArgs<'a, 'b> = (&'a mut Vm, &'b mut StackFrame);
macro_rules! error {
($($arg:tt)*) => {
Err(format!($($arg)*).into())
};
}
macro_rules! next {
($vm:expr, $frame:expr, $iter:expr) => {
$vm.run_fn($frame, $iter.clone(), vec![])
};
}
pub(crate) use error;
pub(crate) use next;
pub fn load(vm: &mut Vm) {
core::load(vm);
sys::load(vm);
io::load(vm);
iter::load(vm);
math::load(vm);
}

View file

@ -1,122 +0,0 @@
use std::{fmt::Display, rc::Rc};
pub mod compiler;
pub mod value;
pub mod gc;
pub mod lex;
pub mod vm;
pub mod parse;
pub mod chunk;
pub mod ast;
pub use ::num_complex::Complex64 as Complex64;
pub use ::num_rational::Rational64 as Rational64;
#[derive(Debug, Clone)]
pub struct Error(Rc<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),
External(anyhow::Error),
Traced(Box<Error>, String),
Any(String),
}
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}"),
External(err) => write!(f, "{err}"),
Traced(err, trace) => write!(f, "{err}\n{trace}"),
Any(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(Rc::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);
from_error!(anyhow::Error, External);
from_error!(String, Any);
impl From<(Error, String)> for Error {
fn from(value: (Error, String)) -> Self {
Self(Rc::new(ErrorInner::Traced(Box::new(value.0), value.1)))
}
}
#[macro_export]
macro_rules! iter {
($fn:expr) => {
$crate::value::Value::Iter(
::std::rc::Rc::new(
$crate::chunk::Function {
name: ::std::rc::Rc::from("<iterator>"),
arity: 0,
variadic: false,
fun: $crate::chunk::InnerFunction::Native(
::std::rc::Rc::new($fn
))}))
};
}
#[macro_export]
macro_rules! native {
($name:expr, $arity:expr, $varadic:expr, $fn:expr) => {
$crate::value::Value::Function(
::std::rc::Rc::new( $crate::chunk::Function {
name: std::rc::Rc::from($name),
arity: $arity,
variadic: $varadic,
fun: $crate::chunk::InnerFunction::Native(
::std::rc::Rc::new($fn)
)
})
)
}
}
#[macro_export]
macro_rules! unpack_args {
($e:expr) => {
$e.try_into().expect("bypassed arity check")
};
}
#[macro_export]
macro_rules! unpack_varargs {
($e:expr) => {{
let mut args = $e;
let $crate::value::Value::List(varargs) = args.pop().expect("bypassed arity check") else {
panic!("bypassed arity check")
};
let varargs = varargs.into_inner();
(args.try_into().expect("bypassed arity check"), varargs)
}};
}
pub type Result<T> = std::result::Result<T, crate::Error>;

File diff suppressed because it is too large Load diff