summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFreya Murphy <freya@freyacat.org>2024-02-26 19:00:42 -0500
committerFreya Murphy <freya@freyacat.org>2024-02-26 19:00:42 -0500
commit158bcae00dbe2af50e51468ad003fb594a858e6d (patch)
treed5639b745b2a24e417e8003694a8994354e2d708
parentfn call fix (diff)
downloadmatrix-158bcae00dbe2af50e51468ad003fb594a858e6d.tar.gz
matrix-158bcae00dbe2af50e51468ad003fb594a858e6d.tar.bz2
matrix-158bcae00dbe2af50e51468ad003fb594a858e6d.zip
changes
Diffstat (limited to '')
-rw-r--r--Cargo.lock12
-rw-r--r--matrix-bin/Cargo.toml2
-rw-r--r--matrix-bin/src/helper.rs312
-rw-r--r--matrix-bin/src/main.rs38
-rw-r--r--matrix-bin/src/repl.rs27
-rw-r--r--matrix-macros/src/lib.rs13
-rw-r--r--matrix-stdlib/src/core.rs181
-rw-r--r--matrix-stdlib/src/io.rs181
-rw-r--r--matrix-stdlib/src/iter.rs544
-rw-r--r--matrix-stdlib/src/lib.rs20
-rw-r--r--matrix-stdlib/src/math.rs518
-rw-r--r--matrix-stdlib/src/sys.rs99
-rw-r--r--matrix/src/ast.rs275
-rw-r--r--matrix/src/chunk.rs25
-rw-r--r--matrix/src/compiler.rs281
-rw-r--r--matrix/src/gc.rs6
-rw-r--r--matrix/src/lex.rs312
-rw-r--r--matrix/src/lib.rs72
-rw-r--r--matrix/src/parse.rs322
-rw-r--r--matrix/src/value.rs580
-rw-r--r--matrix/src/vm.rs461
21 files changed, 3435 insertions, 846 deletions
diff --git a/Cargo.lock b/Cargo.lock
index a3a118e..6414157 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -415,6 +415,7 @@ dependencies = [
"memchr",
"nix",
"radix_trie",
+ "rustyline-derive",
"unicode-segmentation",
"unicode-width",
"utf8parse",
@@ -422,6 +423,17 @@ dependencies = [
]
[[package]]
+name = "rustyline-derive"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5af959c8bf6af1aff6d2b463a57f71aae53d1332da58419e30ad8dc7011d951"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
name = "smallvec"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/matrix-bin/Cargo.toml b/matrix-bin/Cargo.toml
index c4b4d5a..bdb8fb8 100644
--- a/matrix-bin/Cargo.toml
+++ b/matrix-bin/Cargo.toml
@@ -12,4 +12,4 @@ clap = { version = "4", features = [ "derive" ] }
ctrlc = "3"
matrix = { path = "../matrix" }
matrix-stdlib = { path = "../matrix-stdlib" }
-rustyline = "13"
+rustyline = { version = "13", features = [ "derive" ] }
diff --git a/matrix-bin/src/helper.rs b/matrix-bin/src/helper.rs
new file mode 100644
index 0000000..c0ac5ec
--- /dev/null
+++ b/matrix-bin/src/helper.rs
@@ -0,0 +1,312 @@
+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};
+
+#[derive(Helper, Hinter)]
+pub struct MatrixHelper {
+ vm: Rc<RefCell<Vm>>
+}
+
+impl MatrixHelper {
+ pub fn new(vm: Rc<RefCell<Vm>>) -> Self {
+ Self { vm }
+ }
+}
+
+macro_rules! unmatched {
+ ($token:expr, $matched:expr) => {
+ Ok(::rustyline::validate::ValidationResult::Invalid(Some(format!("Token '{:?}' at {}:{} does not have a matching '{:?}'", $token.data, $token.pos.row, $token.pos.col, $matched))))
+ };
+}
+
+impl Validator for MatrixHelper {
+
+ fn validate(&self, ctx: &mut ValidationContext) -> rustyline::Result<ValidationResult> {
+ let mut lexer = Lexer::new(ctx.input());
+ let mut stack = Vec::new();
+
+ loop {
+ use TokenData as T;
+ let token = match lexer.next_token() {
+ Ok(t) if t.data == T::Eof => break,
+ Ok(t) => t,
+ Err(err) => return Ok(ValidationResult::Invalid(Some(format!("{err}"))))
+ };
+
+ match token.data {
+ T::LeftParen |
+ T::LeftBrack |
+ T::LeftBrace |
+ T::For |
+ T::Try => { stack.push(token.data); },
+ T::RightParen =>
+ match stack.pop() {
+ Some(T::LeftParen) => {},
+ _ => return unmatched!(token, T::LeftParen)
+ },
+ T::RightBrack =>
+ match stack.pop() {
+ Some(T::LeftBrack) => {},
+ _ => return unmatched!(token, T::LeftBrack)
+ },
+ T::RightBrace =>
+ match stack.pop() {
+ Some(T::LeftBrace) => {},
+ _ => return unmatched!(token, T::LeftBrace)
+ },
+ T::In =>
+ match stack.pop() {
+ Some(T::For) => {},
+ _ => return unmatched!(token, T::For)
+ },
+ T::Catch =>
+ match stack.pop() {
+ Some(T::Try) => {},
+ _ => return unmatched!(token, T::Try)
+ },
+ _ => {}
+ };
+ }
+
+ if stack.is_empty() {
+ return Ok(ValidationResult::Valid(None))
+ } else {
+ return Ok(ValidationResult::Incomplete)
+ }
+ }
+
+ fn validate_while_typing(&self) -> bool {
+ true
+ }
+}
+
+fn find_matching_bracket(line: &str, pos: usize) -> Option<usize> {
+ if pos >= line.len() {
+ return None
+ }
+ let c = line.as_bytes()[pos];
+ let (target, fwd) = match c {
+ b'(' => (b')', true),
+ b')' => (b'(', false),
+ b'[' => (b']', true),
+ b']' => (b'[', false),
+ b'{' => (b'}', true),
+ b'}' => (b'{', false),
+ _ => return None,
+ };
+ let mut depth = 0;
+ let mut idx = 0;
+ if fwd {
+ let bytes = &line.as_bytes()[pos+1..];
+ for &b in bytes {
+ if b == c {
+ depth += 1;
+ } else if b == target {
+ if depth == 0 {
+ return Some(pos + idx + 1)
+ } else {
+ depth -= 1;
+ }
+ }
+ idx += 1;
+ }
+ } else {
+ let bytes = &line.as_bytes()[..pos];
+ for &b in bytes.iter().rev() {
+ if b == c {
+ depth += 1;
+ } else if b == target {
+ if depth == 0 {
+ return Some(pos - idx - 1)
+ } else {
+ depth -= 1;
+ }
+ }
+ idx += 1;
+ }
+ }
+ None
+}
+
+fn get_token_at(line: &str, pos: usize) -> Option<Token> {
+ use TokenData as T;
+ let mut lexer = Lexer::new(line);
+ loop {
+ match lexer.next_token() {
+ Ok(Token { data: T::Eof, ..}) => return None,
+ Ok(t) if t.bidx <= pos && (t.bidx + t.blen) > pos => return Some(t),
+ Err(_) => return None,
+ _ => continue
+ }
+ }
+}
+
+fn find_matching_ident(line: &str, pos: usize) -> Option<Rc<str>> {
+ use TokenData as T;
+ match get_token_at(line, pos) {
+ Some(Token { data: T::Ident(ident), ..}) => Some(ident),
+ _ => return None
+ }
+}
+
+impl Highlighter for MatrixHelper {
+ fn highlight<'l>(&self, line: &'l str, pos: usize) -> Cow<'l, str> {
+
+ let matching = find_matching_bracket(line, pos);
+ let ident = find_matching_ident(line, pos);
+
+ use TokenData as T;
+ let mut lexer = Lexer::new(line);
+ let mut buf = String::new();
+ let mut last = 0;
+ loop {
+ let token = match lexer.next_token() {
+ Ok(t) if t.data == T::Eof => break,
+ Ok(t) => t,
+ Err(_) => break
+ };
+ let bl = token.bidx;
+ let br = token.bidx + token.blen;
+ buf += &line[last..bl];
+ last = br;
+
+ let color = match token.data {
+ T::LeftParen |
+ T::RightParen |
+ T::LeftBrack |
+ T::RightBrack |
+ T::LeftBrace |
+ T::RightBrace
+ => {
+ match matching {
+ Some(idx) if bl == idx || bl == pos => "\x1b[33;40m",
+ _ => ""
+ }
+ }
+ T::Int(_) |
+ T::True |
+ T::False |
+ T::Nil |
+ T::Float(_) |
+ T::Complex(_)
+ => "\x1b[33m",
+ T::Ident(tok) => {
+ match (ident.as_ref(), lexer.peek_token()) {
+ (Some(ident), Ok(t)) if t.data == T::LeftParen && ident.as_ref() == tok.as_ref() => "\x1b[34;40m",
+ (Some(ident), _) if ident.as_ref() == tok.as_ref() => "\x1b[40m",
+ (_, Ok(t)) if t.data == T::LeftParen => "\x1b[34m",
+ _ => ""
+ }
+ }
+ T::Regex(_) => "\x1b[31m",
+ T::String(_) => "\x1b[32m",
+ T::If |
+ T::Else |
+ T::While |
+ T::Let |
+ T::Function |
+ T::Continue |
+ T::Break |
+ T::Do |
+ T::Loop |
+ T::Return |
+ T::For |
+ T::In |
+ T::Try |
+ T::Catch
+ => "\x1b[38;2;203;166;247m",
+ _ => {
+ buf += &token.str;
+ continue;
+ }
+ };
+
+ let clear = "\x1b[0m";
+
+ buf += &format!("{color}{}{clear}", token.str);
+ }
+
+ buf += &line[last..];
+
+ Cow::Owned(buf)
+ }
+
+ fn highlight_prompt<'b, 's: 'b, 'p: 'b>(
+ &'s self,
+ prompt: &'p str,
+ default: bool,
+ ) -> std::borrow::Cow<'b, str> {
+ let _ = default;
+ Cow::Owned(format!("\x1b[35m{prompt}\x1b[0m"))
+ }
+
+ fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
+ Cow::Owned(format!(" \x1b[31;40m Error: \x1b[0;40m{hint} \x1b[0m"))
+ }
+
+ fn highlight_candidate<'c>(
+ &self,
+ candidate: &'c str,
+ completion: rustyline::CompletionType,
+ ) -> std::borrow::Cow<'c, str> {
+ let _ = completion;
+ Cow::Borrowed(candidate)
+ }
+
+ fn highlight_char(&self, line: &str, _pos: usize, forced: bool) -> bool {
+ forced || !line.is_empty()
+ }
+}
+
+impl Completer for MatrixHelper {
+ type Candidate = Rc<str>;
+
+ fn complete(
+ &self,
+ line: &str,
+ pos: usize,
+ ctx: &rustyline::Context<'_>,
+ ) -> rustyline::Result<(usize, Vec<Self::Candidate>)> {
+
+ let mut idx = 0;
+ let mut buf = String::new();
+ let mut start = 0;
+
+ for char in line.chars() {
+ if buf.is_empty() && char.is_alphabetic() {
+ start = idx;
+ buf.push(char);
+ } else if !buf.is_empty() && char.is_alphanumeric() {
+ buf.push(char);
+ } else {
+ if idx >= pos {
+ break;
+ } else {
+ buf.clear();
+ }
+ }
+ idx += char.len_utf8();
+ }
+
+ let _ = (line, pos, ctx);
+ let globals = self.vm.borrow().global_names();
+ let names: Vec<Rc<str>> = globals
+ .borrow()
+ .clone()
+ .into_iter()
+ .filter(|n| n.starts_with(&buf))
+ .collect();
+
+ if buf.is_empty() {
+ start = pos;
+ }
+
+ Ok((start, names))
+ }
+
+ fn update(&self, line: &mut rustyline::line_buffer::LineBuffer, start: usize, elected: &str, cl: &mut rustyline::Changeset) {
+ let end = line.pos();
+ line.replace(start..end, elected, cl);
+ }
+}
diff --git a/matrix-bin/src/main.rs b/matrix-bin/src/main.rs
index 909ee77..225b353 100644
--- a/matrix-bin/src/main.rs
+++ b/matrix-bin/src/main.rs
@@ -1,9 +1,10 @@
-use std::{path::PathBuf, io::{self, Read, IsTerminal}, fs};
-use clap::Parser as ArgParser;
+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 repl::Repl;
mod repl;
+mod helper;
#[derive(Debug, ArgParser)]
#[command(version, long_about = None)]
@@ -19,6 +20,10 @@ pub struct Args {
#[arg(short, long)]
debug: bool,
+ /// Choses color
+ #[arg(short, long)]
+ color: Option<ColorChoice>,
+
/// Disables optimizations
#[arg(long)]
disable_optimizations: bool,
@@ -27,8 +32,11 @@ pub struct Args {
pub struct State<'a> {
parser: Parser,
compiler: Compiler<'a>,
- vm: Vm,
- repl: bool
+ vm: Rc<RefCell<Vm>>,
+ repl: bool,
+ color: bool,
+ #[allow(unused)]
+ debug: bool,
}
impl<'a> State<'a> {
@@ -60,20 +68,32 @@ impl<'a> State<'a> {
matrix_stdlib::load(&mut vm);
- (Self { parser, vm, compiler, repl }, file)
+ let color = match args.color {
+ Some(ColorChoice::Auto) | None => {
+ io::stdout().is_terminal()
+ },
+ Some(ColorChoice::Always) => true,
+ Some(ColorChoice::Never) => false,
+ };
+
+ (Self { parser, vm: Rc::new(RefCell::new(vm)), compiler, repl, debug: args.debug, color }, file)
}
pub fn execute(&mut self, code: String) -> matrix::Result<Value> {
let ast = self.parser.parse(code)?;
let fun = self.compiler.compile(&ast)?;
- let val = self.vm.run(fun)?;
+ let val = self.vm.try_borrow_mut().unwrap().run(fun)?;
Ok(val)
}
}
-pub fn error(err: matrix::Error) {
- println!("\x1b[31m\x1b[1mError:\x1b[0m {err}");
+pub fn error(err: matrix::Error, state: &State) {
+ if state.color {
+ println!("\x1b[31m\x1b[1mError:\x1b[0m {err}");
+ } else {
+ println!("Error: {err}");
+ }
}
fn read_stdin() -> String {
@@ -93,7 +113,7 @@ fn main() {
if let Some(file) = file {
if let Err(err) = state.execute(file) {
- error(err);
+ error(err, &state);
}
}
diff --git a/matrix-bin/src/repl.rs b/matrix-bin/src/repl.rs
index 1b5addc..f2964d4 100644
--- a/matrix-bin/src/repl.rs
+++ b/matrix-bin/src/repl.rs
@@ -1,9 +1,9 @@
use std::{io::Write, sync::atomic::Ordering};
use matrix::{value::Value, vm::Interupt};
-use rustyline::Config;
+use rustyline::{Config, EditMode, ColorMode, Editor, CompletionType};
-use crate::State;
+use crate::{State, helper::MatrixHelper};
pub struct Repl<'a> {
state: State<'a>
@@ -17,24 +17,39 @@ impl<'a> Repl<'a> {
pub fn run(&mut self) {
- let interupt = self.state.vm.interupt();
+ let interupt = self.state.vm.borrow().interupt();
ctrlc::set_handler(move || {
interupt.store(Interupt::KeyboardInterupt as usize, Ordering::SeqCst);
}).unwrap();
let config = Config::builder()
.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 mut rl = rustyline::DefaultEditor::with_config(config).unwrap();
+
+ let helper = MatrixHelper::new(self.state.vm.clone());
+
+ let mut rl = Editor::with_config(config).unwrap();
+ rl.set_helper(Some(helper));
+
loop {
let Ok(line) = rl.readline(">> ") else {
break;
};
+ if let Err(_) = rl.add_history_entry(&line) {
+ break;
+ };
match self.state.execute(line) {
- Err(err) => crate::error(err),
+ Err(err) => crate::error(err, &self.state),
Ok(val) => {
if val != Value::Nil {
- println!("{val}");
+ if self.state.color {
+ println!("{val:#}");
+ } else {
+ println!("{val}");
+ }
}
}
}
diff --git a/matrix-macros/src/lib.rs b/matrix-macros/src/lib.rs
index 8099fd6..b12d30b 100644
--- a/matrix-macros/src/lib.rs
+++ b/matrix-macros/src/lib.rs
@@ -1,16 +1,15 @@
use proc_macro::TokenStream;
-use syn::{ItemFn, parse::Parse, Token, LitBool, LitInt};
+use syn::{ItemFn, parse::Parse, Token, LitInt};
use quote::quote;
struct NativeFuncParams {
arity: LitInt,
- variadic: LitBool,
+ variadic: Option<Token![..]>,
}
impl Parse for NativeFuncParams {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let arity = input.parse()?;
- input.parse::<Token![,]>()?;
let variadic = input.parse()?;
Ok(Self { arity , variadic })
}
@@ -22,7 +21,7 @@ pub fn native_func(input: TokenStream, annotated_item: TokenStream) -> TokenStre
let input: NativeFuncParams = syn::parse(input).unwrap();
let arity = input.arity;
- let variadic = input.variadic;
+ let variadic = input.variadic.is_some();
let visibility = itemfn.vis;
let block = itemfn.block;
@@ -43,7 +42,11 @@ pub fn native_func(input: TokenStream, annotated_item: TokenStream) -> TokenStre
name: ::std::rc::Rc::from( #name_str ),
arity: #arity,
variadic: #variadic,
- fun: ::matrix::chunk::InnerFunction::Native(Box::new(|#inputs| #output #block))
+ fun: ::matrix::chunk::InnerFunction::Native(
+ ::std::rc::Rc::new(
+ |#inputs| #output #block
+ )
+ )
})
}
};
diff --git a/matrix-stdlib/src/core.rs b/matrix-stdlib/src/core.rs
new file mode 100644
index 0000000..183c142
--- /dev/null
+++ b/matrix-stdlib/src/core.rs
@@ -0,0 +1,181 @@
+use std::hash::{DefaultHasher, Hash, Hasher};
+
+use matrix::{vm::Vm, value::Value, unpack_args, Result, unpack_varargs};
+use matrix_macros::native_func;
+use crate::{VmArgs, error};
+
+
+fn to_radix(r: i64, mut n: i64) -> String {
+ let mut result = String::new();
+ let mut idx = 0;
+ if n == 0 {
+ result.push('0');
+ return result
+ } else if n < 0 {
+ n = -n;
+ idx = 1;
+ result.push('-');
+ }
+ while n != 0 {
+ let c = std::char::from_digit((n % r) as u32, r as u32).unwrap();
+ result.insert(idx, c);
+ n /= r;
+ }
+ result
+}
+
+#[native_func(1)]
+fn bin(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [value] = unpack_args!(args);
+ match value {
+ Value::Int(n) => Ok(Value::String(to_radix(2, n).into())),
+ _ => error!("bin requires a integer agument")
+ }
+}
+
+#[native_func(1)]
+fn sex(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [value] = unpack_args!(args);
+ match value {
+ Value::Int(n) => Ok(Value::String(to_radix(6, n).into())),
+ _ => error!("sex requires a integer agument")
+ }
+}
+
+#[native_func(1)]
+fn oct(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [value] = unpack_args!(args);
+ match value {
+ Value::Int(n) => Ok(Value::String(to_radix(8, n).into())),
+ _ => error!("oct requires a integer agument")
+ }
+}
+
+#[native_func(1)]
+fn hex(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [value] = unpack_args!(args);
+ match value {
+ Value::Int(n) => Ok(Value::String(to_radix(16, n).into())),
+ _ => error!("hex requires a integer agument")
+ }
+}
+
+#[native_func(2)]
+fn radix(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [radix, value] = unpack_args!(args);
+ match (radix, value) {
+ (Value::Int(r), Value::Int(n)) => Ok(Value::String(to_radix(r, n).into())),
+ _ => error!("radix requires two integer aguments")
+ }
+}
+
+#[native_func(1..)]
+fn append(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let ([list], args) = unpack_varargs!(args);
+ let Value::List(mut list) = list else {
+ return error!("append requires a list")
+ };
+ for arg in args {
+ list.push(arg);
+ };
+ Ok(Value::List(list))
+}
+
+#[native_func(2)]
+fn push(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [list, value] = unpack_args!(args);
+ let Value::List(mut list) = list else {
+ return error!("push requires a list")
+ };
+ list.push(value);
+ Ok(Value::List(list))
+}
+
+#[native_func(1)]
+fn pop(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [list] = unpack_args!(args);
+ let Value::List(mut list) = list else {
+ return error!("pop requires a list")
+ };
+ match list.pop() {
+ Some(v) => Ok(v),
+ None => Ok(Value::Nil)
+ }
+}
+
+#[native_func(2)]
+fn remove(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [list, index] = unpack_args!(args);
+ let Value::List(mut list) = list else {
+ return error!("remove requires a list")
+ };
+ let Value::Int(i) = index else {
+ return error!("remove reuqires a int index");
+ };
+ if i < 0 || i as usize >= list.len() {
+ Ok(Value::Nil)
+ } else {
+ Ok(list.remove(i as usize))
+ }
+}
+
+#[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);
+ let fin = hasher.finish();
+ Ok(Value::Int(fin as u32 as i64))
+}
+
+#[native_func(1)]
+fn ord(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [value] = unpack_args!(args);
+ let Value::String(str) = value else {
+ return error!("ord requires a 1 length string")
+ };
+ if str.len() != 1 {
+ return error!("ord requires a 1 length string")
+ }
+ let char = str.chars().next().unwrap();
+ Ok(Value::Int(char as i64))
+}
+
+#[native_func(1)]
+fn chr(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [value] = unpack_args!(args);
+ let Value::Int(i) = value else {
+ return error!("chr requires an int")
+ };
+ match char::from_u32(i as u32) {
+ Some(c) => Ok(Value::String(String::from(c).into())),
+ None => error!("unable to get char from: {}", i as u32)
+ }
+}
+
+#[native_func(1)]
+fn str(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [value] = unpack_args!(args);
+ Ok(Value::String(format!("{value}").into()))
+}
+
+pub fn load(vm: &mut Vm) {
+ vm.load_global_fn(bin(), "bin");
+ vm.load_global_fn(sex(), "sex");
+ vm.load_global_fn(oct(), "oct");
+ vm.load_global_fn(hex(), "hex");
+ vm.load_global_fn(radix(), "radix");
+ vm.load_global_fn(str(), "str");
+
+ vm.load_global_fn(append(), "append");
+ vm.load_global_fn(push(), "push");
+ vm.load_global_fn(pop(), "pop");
+ vm.load_global_fn(remove(), "remove");
+
+ vm.load_global_fn(hash(), "hash");
+ vm.load_global_fn(ord(), "ord");
+ vm.load_global_fn(chr(), "chr");
+}
diff --git a/matrix-stdlib/src/io.rs b/matrix-stdlib/src/io.rs
index 288e99e..d72248c 100644
--- a/matrix-stdlib/src/io.rs
+++ b/matrix-stdlib/src/io.rs
@@ -1,36 +1,175 @@
-use matrix::{value::Value, self, vm::Vm, Result};
+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_macros::native_func;
+use crate::{error, VmArgs};
-#[native_func(1, true)]
-fn print(_vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
- let [values] = args.try_into().unwrap();
- if let Value::List(list) = values {
- for (i, value) in list.iter().enumerate() {
- print!("{}", value.boring_print());
- if i != 0 {
- print!(" ");
- }
+#[native_func(0..)]
+fn print(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let ([], varags) = unpack_varargs!(args);
+ for (i, value) in varags.into_iter().enumerate() {
+ if i != 0 {
+ print!(" ");
}
+ print!("{value}");
}
Ok(Value::Nil)
}
-#[native_func(1, true)]
-fn println(_vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
- let [values] = args.try_into().unwrap();
- if let Value::List(list) = values {
- for (i, value) in list.iter().enumerate() {
- print!("{}", value.boring_print());
- if i != 0 {
- print!(" ");
- }
+#[native_func(0..)]
+fn println(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let ([], varags) = unpack_varargs!(args);
+ for (i, value) in varags.into_iter().enumerate() {
+ if i != 0 {
+ print!(" ");
}
+ print!("{value}");
}
print!("\n");
Ok(Value::Nil)
}
+#[native_func(0)]
+fn readln(_: VmArgs, _: Vec<Value>) -> Result<Value> {
+ let mut input = String::new();
+ match io::stdin().read_line(&mut input) {
+ Ok(_) => {
+ match input.pop() {
+ Some(c) if c == '\n' => {},
+ Some(c) => input.push(c),
+ None => {}
+ };
+ Ok(Value::String(input.into()))
+ },
+ Err(err) => error!("cant read from stdin: {err}")
+ }
+}
+
+#[native_func(1)]
+fn input(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [prompt] = unpack_args!(args);
+ let mut input = String::new();
+ print!("{prompt}");
+ let _ = io::stdout().flush();
+ match io::stdin().read_line(&mut input) {
+ Ok(_) => {
+ match input.pop() {
+ Some(c) if c == '\n' => {},
+ Some(c) => input.push(c),
+ None => {}
+ };
+ Ok(Value::String(input.into()))
+ },
+ Err(err) => error!("cant read from stdin: {err}")
+ }
+}
+
+#[native_func(0)]
+fn readlines(_: VmArgs, _: Vec<Value>) -> Result<Value> {
+ let lines = RefCell::new(io::stdin().lines());
+ Ok(iter!(move |_,_| {
+ match lines.borrow_mut().next() {
+ Some(Ok(line)) => Ok(Value::String(line.into())),
+ _ => Ok(Value::Nil)
+ }
+ }))
+}
+
+#[native_func(2)]
+fn file_open(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [path, mode] = unpack_args!(args);
+ let Value::String(mode) = mode else {
+ return error!("open mode must be a string")
+ };
+ let Value::String(path) = path else {
+ return error!("open path must be a string")
+ };
+ let file = match mode.as_ref() {
+ "r" => OpenOptions::new().read(true).open(path.as_ref()),
+ "w" => OpenOptions::new().write(true).create(true).truncate(true).open(path.as_ref()),
+ "a" => OpenOptions::new().write(true).create(true).append(true).open(path.as_ref()),
+ "r+" => OpenOptions::new().read(true).write(true).open(path.as_ref()),
+ "w+" => OpenOptions::new().read(true).write(true).create(true).truncate(true).open(path.as_ref()),
+ "a+" => OpenOptions::new().read(true).write(true).create(true).append(true).open(path.as_ref()),
+ _ => return error!("invalid open mode: {mode}")
+ };
+
+ match file {
+ Ok(file) => Ok(Value::File(Rc::new(RefCell::new(file)))),
+ Err(err) => return error!("cannot open '{path}': {err}")
+ }
+}
+
+#[native_func(1)]
+fn file_read(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [file] = unpack_args!(args);
+ let Value::File(file) = file else {
+ return error!("file read requires a file")
+ };
+ let mut contents = String::new();
+ if let Err(err) = file.try_borrow_mut().unwrap().read_to_string(&mut contents) {
+ return error!("cannot read file: '{err}'")
+ };
+ Ok(Value::String(contents.into()))
+}
+
+#[native_func(1)]
+fn file_lines(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [file] = unpack_args!(args);
+ let Value::File(file) = file else {
+ return error!("file read requires a file")
+ };
+ let mut contents = String::new();
+ if let Err(err) = file.try_borrow_mut().unwrap().read_to_string(&mut contents) {
+ return error!("cannot read file: '{err}'")
+ };
+ let lines: Vec<Rc<str>> = contents.split_inclusive("\n").map(|s| Rc::from(s)).collect();
+ let lines = RefCell::new(lines.into_iter());
+ Ok(iter!(move |_,_| {
+ match lines.borrow_mut().next() {
+ Some(line) => Ok(Value::String(line)),
+ None => Ok(Value::Nil)
+ }
+ }))
+}
+
+#[native_func(2)]
+fn file_write(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [file, content] = unpack_args!(args);
+ let Value::File(file) = file else {
+ return error!("file write requires a file")
+ };
+ let content = format!("{content}");
+ if let Err(err) = file.try_borrow_mut().unwrap().write_all(content.as_bytes()) {
+ return error!("cannot write file: '{err}'")
+ };
+ Ok(Value::Nil)
+}
+
+#[native_func(0..)]
+fn throw(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let ([], varargs) = unpack_varargs!(args);
+
+ let mut str = String::new();
+ for (i, v) in varargs.into_iter().enumerate() {
+ if i != 0 {
+ str.push_str(" ");
+ }
+ str.push_str(&format!("{v}"));
+ }
+
+ error!("{str}")
+}
+
pub fn load(vm: &mut Vm) {
- vm.load_native_fn(print());
- vm.load_native_fn(println());
+ vm.load_global_fn(print(), "print");
+ vm.load_global_fn(println(), "println");
+ vm.load_global_fn(readln(), "readln");
+ vm.load_global_fn(input(), "input");
+ vm.load_global_fn(readlines(), "readlines");
+ vm.load_global_fn(file_open(), "file_open");
+ vm.load_global_fn(file_read(), "file_read");
+ vm.load_global_fn(file_lines(), "file_lines");
+ vm.load_global_fn(file_write(), "file_write");
+ vm.load_global_fn(throw(), "throw");
}
diff --git a/matrix-stdlib/src/iter.rs b/matrix-stdlib/src/iter.rs
new file mode 100644
index 0000000..74056ce
--- /dev/null
+++ b/matrix-stdlib/src/iter.rs
@@ -0,0 +1,544 @@
+use std::{cell::RefCell, rc::Rc};
+use matrix::{iter, vm::Vm, value::Value, Result, unpack_varargs, unpack_args};
+use matrix_macros::native_func;
+use crate::{error, VmArgs};
+
+use Value as V;
+
+macro_rules! next {
+ ($vm:expr, $frame:expr, $iter:expr) => {
+ $vm.run_fn($frame, $iter.clone(), vec![])
+ };
+}
+
+#[native_func(1)]
+fn len(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [value] = unpack_args!(args);
+ let len = match value {
+ V::List(l) => l.len(),
+ V::Matrix(m) => m.values.len(),
+ V::String(s) => s.len(),
+ V::Table(t) => t.len(),
+ _ => 1
+ };
+ Ok(V::Int(len as i64))
+}
+
+#[native_func(1)]
+fn sum((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [iter] = unpack_args!(args);
+ let iter = iter.into_iter_fn()?;
+ let mut res = next!(vm, frame, iter)?;
+ if res == Value::Nil {
+ return Ok(res)
+ };
+ loop {
+ let next = next!(vm, frame, iter)?;
+ if next == Value::Nil { break }
+ res = (res + next)?;
+ }
+ Ok(res)
+}
+
+#[native_func(0..)]
+fn range(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let ([], varargs) = unpack_varargs!(args);
+ let (min, max, step) = match varargs.as_slice() {
+ [V::Int(max)] => {
+ (0, *max, 1)
+ },
+ [V::Int(min), V::Int(max)] => {
+ (*min, *max, 1)
+ },
+ [V::Int(min), V::Int(max), V::Int(step)] => {
+ (*min, *max, *step)
+ },
+ _ => return error!("range takes 1 to 3 [Int]'s")
+ };
+
+ let i = RefCell::new(min);
+ Ok(iter!(move |_,_| {
+ let curr = *(i.borrow());
+ *(i.borrow_mut()) = curr + step;
+ if curr >= max {
+ return Ok(V::Nil)
+ } else {
+ return Ok(V::Int(curr))
+ }
+ }))
+}
+
+#[native_func(1)]
+fn iter(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [value] = unpack_args!(args);
+ Ok(value.into_iter()?)
+}
+
+#[native_func(1)]
+fn once(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [value] = unpack_args!(args);
+ let first = RefCell::new(false);
+ Ok(iter!(move |_,_| {
+ if *first.borrow() == false {
+ *first.borrow_mut() = true;
+ Ok(value.clone())
+ } else {
+ Ok(Value::Nil)
+ }
+ }))
+}
+
+#[native_func(1)]
+fn list((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [iter] = unpack_args!(args);
+ let iter = iter.into_iter_fn()?;
+ let mut list = Vec::new();
+ loop {
+ let res = next!(vm, frame, iter)?;
+ if res == Value::Nil { break }
+ list.push(res);
+ }
+ Ok(Value::List(list.into()))
+}
+
+#[native_func(2)]
+fn map(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [fun, iter] = unpack_args!(args);
+ let iter = iter.into_iter_fn()?;
+ let Value::Function(fun) = fun else {
+ return error!("map 1st arg must be a function")
+ };
+ Ok(iter!(move |(vm,frame),_| {
+ let input = next!(vm, frame, iter)?;
+ if input == Value::Nil {
+ return Ok(input)
+ }
+ vm.run_fn(frame, fun.clone(), vec![input])
+ }))
+}
+
+#[native_func(2)]
+fn fold((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [fun, iter] = unpack_args!(args);
+ let iter = iter.into_iter_fn()?;
+ let Value::Function(fun) = fun else {
+ return error!("fold 1st arg must be a function")
+ };
+ let mut res = next!(vm, frame, iter)?;
+ if res == Value::Nil {
+ return Ok(Value::Nil)
+ };
+ loop {
+ let next = next!(vm, frame, iter)?;
+ if next == Value::Nil {
+ return Ok(res)
+ };
+ res = vm.run_fn(frame, fun.clone(), vec![res, next])?;
+ }
+}
+
+#[native_func(3)]
+fn foldi((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [init, fun, iter] = unpack_args!(args);
+ let iter = iter.into_iter_fn()?;
+ let Value::Function(fun) = fun else {
+ return error!("foldi 1st arg must be a function")
+ };
+ let mut res = init;
+ loop {
+ let next = next!(vm, frame, iter)?;
+ if next == Value::Nil {
+ return Ok(res)
+ };
+ res = vm.run_fn(frame, fun.clone(), vec![res, next])?;
+ }
+}
+
+#[native_func(1)]
+fn count((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [iter] = unpack_args!(args);
+ let iter = iter.into_iter_fn()?;
+ let mut len = 0;
+ loop {
+ let res = vm.run_fn(frame, iter.clone(), vec![])?;
+ if res == Value::Nil { break };
+ len += 1;
+ }
+ Ok(Value::Int(len))
+}
+
+#[native_func(3)]
+fn scan(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [init, fun, iter] = unpack_args!(args);
+ let iter = iter.into_iter_fn()?;
+ let Value::Function(fun) = fun else {
+ return error!("scan 2nd arg must be a function")
+ };
+ let res = RefCell::new(init);
+ Ok(iter!(move |(vm,frame),_| {
+ let next = next!(vm, frame, iter)?;
+ if next == Value::Nil {
+ return Ok(Value::Nil)
+ };
+ let res_next = vm.run_fn(frame, fun.clone(), vec![res.borrow().clone(), next])?;
+ *res.borrow_mut() = res_next;
+ Ok(res.borrow().clone())
+ }))
+}
+
+#[native_func(2)]
+fn filter(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [fun, iter] = unpack_args!(args);
+ let iter = iter.into_iter_fn()?;
+ let Value::Function(fun) = fun else {
+ return error!("filter 1st arg must be a function")
+ };
+ Ok(iter!(move |(vm,frame),_| {
+ loop {
+ let next = next!(vm, frame, iter)?;
+ if next == Value::Nil {
+ return Ok(Value::Nil)
+ }
+ let res = vm.run_fn(frame, fun.clone(), vec![next.clone()])?;
+ if !!res {
+ return Ok(next)
+ }
+ }
+ }))
+}
+
+#[native_func(0..)]
+fn chain(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let ([], iters) = unpack_varargs!(args);
+
+ let mut chaind = Vec::new();
+ for iter in iters {
+ chaind.push(iter.into_iter_fn()?);
+ }
+
+ chaind.reverse();
+ let chaind = RefCell::new(chaind);
+ Ok(iter!(move |(vm,frame), _| {
+ loop {
+ let curr = match chaind.borrow_mut().last() {
+ Some(iter) => iter.clone(),
+ None => return Ok(Value::Nil)
+ };
+ match vm.run_fn(frame, curr.clone(), vec![]) {
+ Ok(Value::Nil) => {
+ chaind.borrow_mut().pop();
+ continue;
+ },
+ v => return v
+ };
+ }
+ }))
+}
+
+#[native_func(1)]
+fn lines(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [str] = unpack_args!(args);
+ 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 res = RefCell::new(lines.into_iter());
+ Ok(iter!(move |_,_| {
+ match res.borrow_mut().next() {
+ Some(line) => Ok(Value::String(line)),
+ None => Ok(Value::Nil)
+ }
+ }))
+}
+
+#[native_func(1)]
+fn skip((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [count, iter] = unpack_args!(args);
+ let iter = iter.into_iter_fn()?;
+ let Value::Int(i) = count else {
+ return error!("skip count requires a int")
+ };
+ for _ in 0..i {
+ next!(vm, frame, iter)?;
+ }
+ Ok(Value::Iter(iter))
+}
+
+#[native_func(2)]
+fn alternate(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [il, ir] = unpack_args!(args);
+ let il = il.into_iter_fn()?;
+ let ir = ir.into_iter_fn()?;
+ let flag = RefCell::new(Some(0));
+ Ok(iter!(move |(vm, frame),_| {
+ let f = *flag.borrow();
+ match f {
+ Some(0) => {
+ let val = next!(vm, frame, il)?;
+ if val == Value::Nil {
+ *flag.borrow_mut() = None;
+ } else {
+ *flag.borrow_mut() = Some(1);
+ }
+ Ok(val)
+ },
+ Some(1) => {
+ let val = next!(vm, frame, ir)?;
+ if val == Value::Nil {
+ *flag.borrow_mut() = None;
+ } else {
+ *flag.borrow_mut() = Some(0);
+ }
+ Ok(val)
+ },
+ _ => Ok(Value::Nil)
+ }
+ }))
+}
+
+#[native_func(2)]
+fn intersperse(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [value, iter] = unpack_args!(args);
+ let iter = iter.into_iter_fn()?;
+ let flag = RefCell::new(Some(0));
+ Ok(iter!(move |(vm, frame),_| {
+ let f = *flag.borrow();
+ match f {
+ Some(0) => {
+ let val = next!(vm, frame, iter)?;
+ if val == Value::Nil {
+ *flag.borrow_mut() = None;
+ } else {
+ *flag.borrow_mut() = Some(1);
+ }
+ Ok(val)
+ },
+ Some(1) => {
+ let val = value.clone();
+ *flag.borrow_mut() = Some(0);
+ Ok(val)
+ },
+ _ => Ok(Value::Nil)
+ }
+ }))
+}
+
+#[native_func(2)]
+fn zip(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [il, ir] = unpack_args!(args);
+ let il = il.into_iter_fn()?;
+ let ir = ir.into_iter_fn()?;
+ let flag = RefCell::new(true);
+ Ok(iter!(move |(vm, frame),_| {
+ let f = *flag.borrow();
+ match f {
+ true => {
+ let vl = next!(vm, frame, il)?;
+ let vr = next!(vm, frame, ir)?;
+ if vl == Value::Nil || vr == Value::Nil {
+ *flag.borrow_mut() = false;
+ }
+ Ok(Value::List(vec![vl, vr].into()))
+ },
+ _ => Ok(Value::Nil)
+ }
+ }))
+}
+
+#[native_func(1)]
+fn unzip((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [iter] = unpack_args!(args);
+ let iter = iter.into_iter_fn()?;
+ let mut ll = Vec::new();
+ let mut lr = Vec::new();
+ loop {
+ let val = next!(vm, frame, iter)?;
+ if val == Value::Nil {
+ break;
+ }
+ let Value::List(vals) = val else {
+ return error!("unzip only works over a iterator of pairs");
+ };
+ let vals = vals.into_inner();
+ if vals.len() != 2 {
+ return error!("unzip only works over a iterator of pairs");
+ }
+ let [l, r] = vals.try_into().unwrap();
+ ll.push(l);
+ lr.push(r);
+ }
+ let ll = Value::List(ll.into());
+ let lr = Value::List(lr.into());
+ Ok(Value::List(vec![ll, lr].into()))
+}
+
+#[native_func(1)]
+fn cycle((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [iter] = unpack_args!(args);
+ let iter = iter.into_iter_fn()?;
+ let mut values = Vec::new();
+ loop {
+ let val = next!(vm, frame, iter)?;
+ if val == Value::Nil { break };
+ values.push(val);
+ }
+ let idx = RefCell::new(0_usize);
+ Ok(iter!(move |_,_| {
+ let i = *idx.borrow();
+ *idx.borrow_mut() += 1;
+ *idx.borrow_mut() %= values.len();
+ Ok(values[i].clone())
+ }))
+}
+
+#[native_func(2)]
+fn take((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [count, iter] = unpack_args!(args);
+ let iter = iter.into_iter_fn()?;
+ let Value::Int(count) = count else {
+ return error!("take requires an int amount to collect")
+ };
+ let mut values = Vec::new();
+ for _ in 0..count {
+ let val = next!(vm, frame, iter)?;
+ if val == Value::Nil { break };
+ values.push(val);
+ }
+ Ok(Value::List(values.into()))
+}
+
+#[native_func(2)]
+fn last((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [iter] = unpack_args!(args);
+ let iter = iter.into_iter_fn()?;
+ let mut last = Value::Nil;
+ loop {
+ let val = next!(vm, frame, iter)?;
+ if val == Value::Nil { break };
+ last = val;
+ }
+ Ok(last)
+}
+
+#[native_func(2)]
+fn next((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [iter] = unpack_args!(args);
+ let iter = iter.into_iter_fn()?;
+ let val = next!(vm, frame, iter)?;
+ Ok(val)
+}
+
+#[native_func(1)]
+fn min((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [iter] = unpack_args!(args);
+ let iter = iter.into_iter_fn()?;
+ let mut min = Value::Nil;
+ loop {
+ let val = next!(vm, frame, iter)?;
+ if val == Value::Nil { break };
+ if min == Value::Nil || val < min {
+ min = val;
+ }
+ }
+ Ok(min)
+}
+
+#[native_func(1)]
+fn max((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [iter] = unpack_args!(args);
+ let iter = iter.into_iter_fn()?;
+ let mut max = Value::Nil;
+ loop {
+ let val = next!(vm, frame, iter)?;
+ if val == Value::Nil { break };
+ if max == Value::Nil || val > max {
+ max = val;
+ }
+ }
+ Ok(max)
+}
+
+#[native_func(1)]
+fn rev((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [iter] = unpack_args!(args);
+ let iter = iter.into_iter_fn()?;
+ let mut values = Vec::new();
+ loop {
+ let val = next!(vm, frame, iter)?;
+ if val == Value::Nil { break };
+ values.push(val);
+ }
+ let values = RefCell::new(values);
+ Ok(iter!(move |_,_| {
+ match values.borrow_mut().pop() {
+ Some(v) => Ok(v),
+ None => Ok(Value::Nil)
+ }
+ }))
+}
+
+#[native_func(1)]
+fn enumerate(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [iter] = unpack_args!(args);
+ let iter = iter.into_iter_fn()?;
+
+ let idx = RefCell::new(0_i64);
+ Ok(iter!(move |(vm, frame),_| {
+ let val = next!(vm, frame, iter)?;
+ if val == Value::Nil {
+ return Ok(Value::Nil)
+ };
+ let curr = *idx.borrow();
+ *idx.borrow_mut() += 1;
+ Ok(Value::List(vec![Value::Int(curr), val].into()))
+ }))
+}
+
+#[native_func(1)]
+fn iterable(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [value] = unpack_args!(args);
+ use Value as V;
+ match value {
+ V::Function(_) |
+ V::List(_) |
+ V::Range(_) |
+ V::Iter(_)
+ => Ok(V::Bool(true)),
+ _ => Ok(V::Bool(false))
+ }
+}
+
+pub fn load(vm: &mut Vm) {
+ vm.load_global_fn(take(), "take");
+ vm.load_global_fn(unzip(), "unzip");
+ vm.load_global_fn(count(), "count");
+ vm.load_global_fn(len(), "len");
+ vm.load_global_fn(sum(), "sum");
+ vm.load_global_fn(min(), "min");
+ vm.load_global_fn(max(), "max");
+ vm.load_global_fn(next(), "next");
+ vm.load_global_fn(last(), "last");
+
+ vm.load_global_fn(list(), "list");
+ vm.load_global_fn(fold(), "fold");
+ vm.load_global_fn(foldi(), "foldi");
+ vm.load_global_fn(scan(), "scan");
+ vm.load_global_fn(chain(), "chain");
+ vm.load_global_fn(lines(), "lines");
+ vm.load_global_fn(skip(), "skip");
+
+ vm.load_global_fn(once(), "once");
+ vm.load_global_fn(iter(), "iter");
+ vm.load_global_fn(range(), "range");
+ vm.load_global_fn(map(), "map");
+ vm.load_global_fn(filter(), "filter");
+ vm.load_global_fn(skip(), "skip");
+ vm.load_global_fn(zip(), "zip");
+ vm.load_global_fn(cycle(), "cycle");
+ vm.load_global_fn(alternate(), "alternate");
+ vm.load_global_fn(intersperse(), "intersperse");
+ vm.load_global_fn(rev(), "rev");
+ vm.load_global_fn(enumerate(), "enumerate");
+
+ vm.load_global_fn(iterable(), "iterable");
+}
diff --git a/matrix-stdlib/src/lib.rs b/matrix-stdlib/src/lib.rs
index 6e0cfc1..334de90 100644
--- a/matrix-stdlib/src/lib.rs
+++ b/matrix-stdlib/src/lib.rs
@@ -1,7 +1,25 @@
-use matrix::vm::Vm;
+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())
+ };
+}
+
+pub(crate) use error;
pub fn load(vm: &mut Vm) {
+ core::load(vm);
+ sys::load(vm);
io::load(vm);
+ iter::load(vm);
+ math::load(vm);
}
diff --git a/matrix-stdlib/src/math.rs b/matrix-stdlib/src/math.rs
new file mode 100644
index 0000000..3226af5
--- /dev/null
+++ b/matrix-stdlib/src/math.rs
@@ -0,0 +1,518 @@
+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_macros::native_func;
+use crate::{error, VmArgs};
+
+#[native_func(1)]
+fn trans(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [value] = unpack_args!(args);
+ let mat = match value {
+ Value::Matrix(m) => m,
+ Value::List(l) => Matrix::from_list(l.to_vec()).into(),
+ _ => return error!("trans must be given a matrix")
+ };
+ let values = mat
+ .cols()
+ .reduce(|mut a, b| {a.extend(b); a})
+ .unwrap()
+ .into_iter()
+ .map(|e| e.clone())
+ .collect();
+ Ok(Value::Matrix(Matrix::new(mat.codomain, mat.domain, values).into()))
+}
+
+fn mat_gauss_row_operation(
+ r1: usize,
+ r2: usize,
+ scale: Value,
+ mat: &mut Matrix
+) -> Result<()> {
+ for col in 0..mat.domain {
+ let r1v = mat.get(r1, col)?;
+ let r2v = mat.get(r2, col)?;
+ let res = (r1v - (r2v * scale.clone())?)?;
+ mat.set(r1, col, res)?;
+ }
+ Ok(())
+}
+
+fn mat_swap_rows(
+ r1: usize,
+ r2: usize,
+ mat: &mut Matrix
+) -> Result<()> {
+ let cols = mat.domain;
+ for col in 0..cols {
+ let a = mat.get(r1, col)?;
+ let b = mat.get(r2, col)?;
+ mat.set(r2, col, a)?;
+ mat.set(r1, col, b)?;
+ }
+ Ok(())
+}
+
+fn mat_find_non_zero_col(
+ mat: &Matrix
+) -> Option<usize> {
+ for (i,col) in mat.cols().enumerate() {
+ for val in col.iter() {
+ if **val != Value::Int(0) {
+ return Some(i)
+ }
+ }
+ }
+ return None
+}
+
+fn mat_scale_pivot_row(
+ row: usize,
+ mat: &mut Matrix
+) -> Result<()> {
+ let scale = mat.get(row, row)?;
+ if scale.is_zero() {
+ return Ok(())
+ }
+ for col in 0..mat.domain {
+ let res = (mat.get(row, col)?.clone() / scale.clone())?;
+ mat.set(row, col, res)?;
+ }
+ Ok(())
+}
+
+fn mat_get_non_zero_pivot_row(
+ row: usize,
+ mat: &mut Matrix,
+) -> Result<()> {
+ let col = row;
+ let test = mat.get(row, col)?;
+ if test.is_zero() {
+ for r in row..mat.codomain {
+ let cur = mat.get(r, col)?;
+ if !cur.is_zero() {
+ mat_swap_rows(row, r, mat)?;
+ break;
+ }
+ }
+ }
+ mat_scale_pivot_row(row, mat)?;
+ Ok(())
+}
+
+fn mat_rref(mat: Matrix) -> Result<Matrix> {
+ let mut mat = mat;
+ let Some(start) = mat_find_non_zero_col(&mat) else {
+ return Ok(mat)
+ };
+ let end = mat.domain.min(mat.codomain);
+ for col in start..end {
+ let pivot_row = col;
+ mat_get_non_zero_pivot_row(pivot_row, &mut mat)?;
+ if mat.get(pivot_row, col)?.is_zero() {
+ break
+ }
+ for row in 0..mat.codomain {
+ if row == pivot_row { continue; };
+ let scale = mat.get(row, col)?;
+ mat_gauss_row_operation(row, pivot_row, scale, &mut mat)?;
+ }
+ }
+ Ok(mat)
+}
+
+#[native_func(1)]
+fn rref(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [value] = unpack_args!(args);
+ let mat = match value {
+ Value::Matrix(m) => m,
+ Value::List(l) => Matrix::from_list(l.to_vec()).into(),
+ _ => return error!("trans must be given a matrix")
+ };
+ Ok(Value::Matrix(mat_rref(mat.into_inner())?.into()))
+}
+
+fn mat_det(mat: Matrix) -> Result<Value> {
+ if mat.domain == 1 {
+ return Ok(mat.get(0,0)?)
+ }
+ if mat.domain == 2 {
+ let a = mat.get(0,0)? * mat.get(1,1)?;
+ let b = mat.get(0,1)? * mat.get(1,0)?;
+ return Ok((a? - b?)?)
+ }
+ let mut res = Value::Int(0);
+ for col in 0..mat.domain {
+ let sub_values = mat.rows()
+ .skip(1)
+ .map(|r|
+ r.into_iter()
+ .enumerate()
+ .filter(|(idx,_)| *idx != col)
+ .map(|(_, v)| v.clone())
+ .collect::<Vec<Value>>()
+ )
+ .reduce(|mut a, b| {a.extend(b); a})
+ .unwrap();
+ let sub = Matrix::new(mat.domain - 1, mat.domain - 1, sub_values);
+ let val = mat.get(0, col)?;
+ let part = (val * mat_det(sub)?)?;
+ if col % 2 == 0 {
+ res = (res + part)?;
+ } else {
+ res = (res - part)?;
+ }
+ }
+ Ok(res)
+}
+
+#[native_func(1)]
+fn det(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [value] = unpack_args!(args);
+ let mat = match value {
+ Value::Matrix(m) if m.domain == m.codomain => m,
+ Value::List(l) if l.len() == 1 => Matrix::from_list(l.to_vec()).into(),
+ _ => return error!("det requires a square matrix")
+ };
+ let mat = mat.into_inner();
+ Ok(mat_det(mat)?)
+}
+
+fn mat_ident(dim: usize) -> Matrix {
+ let len = dim * dim;
+ let mut values = vec![Value::Int(0); len];
+ let mut idx = 0;
+ loop {
+ if idx >= len { break };
+ values[idx] = Value::Int(1);
+ idx += dim + 1;
+ }
+ Matrix::new(dim, dim, values)
+}
+
+#[native_func(1)]
+fn ident(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [value] = unpack_args!(args);
+ let dim = match value {
+ Value::Int(i) if i > 0 => i,
+ Value::Ratio(r)
+ if *r.denom() == 1 &&
+ *r.numer() > 0
+ => *r.numer(),
+ _ => return error!("ident requries a positive [Int] dimension")
+ };
+ Ok(Value::Matrix(mat_ident(dim as usize).into()))
+}
+
+fn mat_splith(mat: Matrix) -> (Matrix, Matrix) {
+ let mut m1 = Vec::new();
+ let mut m2 = Vec::new();
+
+ mat.rows()
+ .for_each(|r| {
+ let split = r.len() / 2;
+ r.into_iter().enumerate().for_each(|(i, v)| {
+ if i < split {
+ m1.push(v.clone());
+ } else {
+ m2.push(v.clone());
+ }
+ })
+ });
+
+ let m1 = Matrix::new(mat.domain/2, mat.codomain, m1);
+ let m2 = Matrix::new(mat.domain/2, mat.codomain, m2);
+ (m1, m2)
+}
+
+#[native_func(1)]
+fn inv(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [value] = unpack_args!(args);
+ let mat = match value {
+ Value::Matrix(m) if m.domain == m.codomain => m,
+ Value::List(l) if l.len() == 1 => Matrix::from_list(l.to_vec()).into(),
+ _ => return error!("det requires a square matrix")
+ };
+ let mat = mat.into_inner();
+ let ident = mat_ident(mat.domain);
+ let joined = mat.join_right(&ident)?;
+ let refed = mat_rref(joined)?;
+ let (new_ident, new_inv) = mat_splith(refed);
+
+ if new_ident == ident {
+ Ok(Value::Matrix(new_inv.into()))
+ } else {
+ error!("matrix does not have an inverse")
+ }
+}
+
+macro_rules! mathr {
+ ($type:ident) => {
+ #[native_func(1)]
+ fn $type(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ use Value as V;
+ let [value] = unpack_args!(args);
+ match value {
+ V::Int(i) => Ok(V::Int(i)),
+ V::Ratio(r) => Ok(V::Ratio(r.$type())),
+ V::Float(f) => Ok(V::Float(f.$type())),
+ v => error!("cannot compute {} on {v}", stringify!($type))
+ }
+ }
+ };
+}
+
+macro_rules! trig {
+ ($type:ident) => {
+ #[native_func(1)]
+ fn $type(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ use Value as V;
+ let [value] = unpack_args!(args);
+ match value.promote_trig() {
+ V::Float(f) => Ok(V::Float(f.$type())),
+ V::Complex(c) => Ok(V::Complex(c.$type())),
+ v => error!("cannot compute {} on {v}", stringify!($type))
+ }
+ }
+ };
+}
+
+macro_rules! trigf {
+ ($type:ident, $str:ident) => {
+ #[native_func(1)]
+ fn $str(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ use Value as V;
+ let [value] = unpack_args!(args);
+ match value.promote_trig() {
+ V::Float(f) => Ok(V::Float(f.$type())),
+ v => error!("cannot compute {} on {v}", stringify!($str))
+ }
+ }
+ };
+}
+
+#[native_func(2)]
+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()) {
+ (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())),
+ (V::Complex(base), V::Complex(arg)) => Ok(V::Complex(arg.ln() / base.ln())),
+ (base, arg) => error!("cannot compute log base {base} argument {arg}")
+ }
+}
+
+#[native_func(1)]
+fn abs(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ use Value as V;
+ let [value] = unpack_args!(args);
+ match value {
+ V::Int(i) => Ok(V::Int(i.abs())),
+ V::Float(f) => Ok(V::Float(f.abs())),
+ V::Ratio(r) => Ok(V::Ratio(Rational64::new(r.numer().abs(), r.denom().abs()))),
+ V::Complex(c) => Ok(V::Float(c.norm())),
+ arg => error!("cannot compute abs for {arg}")
+ }
+}
+
+#[native_func(1)]
+fn fract(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ use Value as V;
+ let [value] = unpack_args!(args);
+ match value {
+ V::Int(_) => Ok(V::Int(0)),
+ V::Float(f) => Ok(V::Float(f.fract())),
+ V::Ratio(r) => Ok(V::Ratio(r.fract())),
+ arg => error!("cannot compute fract for {arg}")
+ }
+}
+
+#[native_func(1)]
+fn sign(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ use Value as V;
+ let [value] = unpack_args!(args);
+ match value {
+ V::Int(i) => Ok(V::Int(i.signum())),
+ V::Ratio(r) => Ok(V::Int(r.numer().signum())),
+ V::Float(f) => Ok(V::Float(f.signum())),
+ arg => error!("cannot compute sign for {arg}")
+ }
+}
+
+#[native_func(1)]
+fn int(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ use Value as V;
+ let [value] = unpack_args!(args);
+ match value {
+ V::Int(i) => Ok(V::Int(i)),
+ V::Ratio(r) => Ok(V::Int(r.numer() / r.denom())),
+ V::Float(f) => Ok(V::Int(f as i64)),
+ V::Complex(c) => Ok(V::Int(c.re as i64)),
+ arg => error!("cannot cast {arg} to int")
+ }
+}
+
+#[native_func(1)]
+fn ratio(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ use Value as V;
+ let [value] = unpack_args!(args);
+ match value {
+ V::Int(i) => Ok(V::Ratio(Rational64::new(i, 1))),
+ V::Ratio(r) => Ok(V::Ratio(r)),
+ V::Float(f) => Ok(V::Ratio(Rational64::approximate_float(f).unwrap_or(Rational64::new(0, 1)))),
+ V::Complex(c) => Ok(V::Ratio(Rational64::approximate_float(c.re).unwrap_or(Rational64::new(0, 1)))),
+ arg => error!("cannot cast {arg} to ratio")
+ }
+}
+
+#[native_func(1)]
+fn float(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ use Value as V;
+ let [value] = unpack_args!(args);
+ match value {
+ V::Int(i) => Ok(V::Float(i as f64)),
+ V::Ratio(r) => Ok(V::Float((*r.numer() as f64) / (*r.denom() as f64))),
+ V::Float(f) => Ok(V::Float(f)),
+ V::Complex(c) => Ok(V::Float(c.re)),
+ arg => error!("cannot cast {arg} to float")
+ }
+}
+
+#[native_func(1)]
+fn complex(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ use Value as V;
+ let [value] = unpack_args!(args);
+ match value {
+ V::Int(i) => Ok(V::Complex(Complex64::new(i as f64, 0.0))),
+ V::Ratio(r) => Ok(V::Complex(Complex64::new((*r.numer() as f64) / (*r.denom() as f64), 0.0))),
+ V::Float(f) => Ok(V::Complex(Complex64::new(f, 0.0))),
+ V::Complex(c) => Ok(V::Complex(c)),
+ arg => error!("cannot cast {arg} to float")
+ }
+}
+
+#[native_func(1)]
+fn numer(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ use Value as V;
+ let [value] = unpack_args!(args);
+ match value {
+ V::Int(i) => Ok(V::Int(i)),
+ V::Ratio(r) => Ok(V::Int(*r.numer())),
+ _ => error!("numer can only take a integer or ratio")
+ }
+}
+
+#[native_func(1)]
+fn denom(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ use Value as V;
+ let [value] = unpack_args!(args);
+ match value {
+ V::Int(_) => Ok(V::Int(1)),
+ V::Ratio(r) => Ok(V::Int(*r.denom())),
+ _ => error!("denom can only take a integer or ratio")
+ }
+}
+
+#[native_func(1)]
+fn re(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ use Value as V;
+ let [value] = unpack_args!(args);
+ match value {
+ V::Int(_) | V::Ratio(_) | V::Float(_) => Ok(value),
+ V::Complex(c) => Ok(V::Float(c.re)),
+ _ => error!("re can only take a valid number")
+ }
+}
+
+#[native_func(1)]
+fn im(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ use Value as V;
+ let [value] = unpack_args!(args);
+ match value {
+ V::Int(_) | V::Ratio(_) | V::Float(_ )=> Ok(V::Int(0)),
+ V::Complex(c) => Ok(V::Float(c.im)),
+ _ => error!("re can only take a valid number")
+ }
+}
+
+mathr!(floor);
+mathr!(ceil);
+mathr!(round);
+mathr!(trunc);
+trig!(sqrt);
+trig!(cbrt);
+trig!(ln);
+trig!(log2);
+trig!(log10);
+trig!(exp);
+trig!(exp2);
+trig!(sin);
+trig!(cos);
+trig!(tan);
+trig!(sinh);
+trig!(cosh);
+trig!(tanh);
+trig!(asin);
+trig!(acos);
+trig!(atan);
+trig!(asinh);
+trig!(acosh);
+trig!(atanh);
+trigf!(to_degrees, deg);
+trigf!(to_radians, rad);
+
+pub fn load(vm: &mut Vm) {
+ vm.load_global_fn(trans(), "trans");
+ vm.load_global_fn(rref(), "rref");
+ vm.load_global_fn(det(), "det");
+ vm.load_global_fn(ident(), "ident");
+ vm.load_global_fn(inv(), "inv");
+
+ vm.load_global(Value::Float(PI), "pi");
+ vm.load_global(Value::Float(TAU), "tau");
+ vm.load_global(Value::Float(E), "e");
+ vm.load_global(Value::Float(NAN), "nan");
+ vm.load_global(Value::Float(NAN), "NaN");
+ vm.load_global(Value::Float(INFINITY), "inf");
+
+ vm.load_global_fn(int(), "int");
+ vm.load_global_fn(ratio(), "ratio");
+ vm.load_global_fn(float(), "float");
+ vm.load_global_fn(complex(), "complex");
+ vm.load_global_fn(abs(), "abs");
+ vm.load_global_fn(sign(), "sign");
+ vm.load_global_fn(floor(), "floor");
+ vm.load_global_fn(ceil(), "ceil");
+ vm.load_global_fn(round(), "round");
+ vm.load_global_fn(trunc(), "trunc");
+ vm.load_global_fn(fract(), "fract");
+ vm.load_global_fn(sqrt(), "sqrt");
+ vm.load_global_fn(cbrt(), "cbrt");
+ vm.load_global_fn(ln(), "ln");
+ vm.load_global_fn(log(), "log");
+ vm.load_global_fn(log2(), "log2");
+ vm.load_global_fn(log10(), "log10");
+ vm.load_global_fn(exp(), "exp");
+ vm.load_global_fn(exp2(), "exp2");
+ vm.load_global_fn(sin(), "sin");
+ vm.load_global_fn(cos(), "cos");
+ vm.load_global_fn(tan(), "tan");
+ vm.load_global_fn(sinh(), "sinh");
+ vm.load_global_fn(cosh(), "cosh");
+ vm.load_global_fn(tanh(), "tanh");
+ vm.load_global_fn(asin(), "asin");
+ vm.load_global_fn(acos(), "acos");
+ vm.load_global_fn(atan(), "atan");
+ vm.load_global_fn(asinh(), "asinh");
+ vm.load_global_fn(acosh(), "acosh");
+ vm.load_global_fn(atanh(), "atanh");
+ vm.load_global_fn(deg(), "deg");
+ vm.load_global_fn(rad(), "rad");
+
+ vm.load_global_fn(denom(), "denom");
+ vm.load_global_fn(numer(), "numer");
+ vm.load_global_fn(re(), "re");
+ vm.load_global_fn(im(), "im");
+}
diff --git a/matrix-stdlib/src/sys.rs b/matrix-stdlib/src/sys.rs
new file mode 100644
index 0000000..e91e635
--- /dev/null
+++ b/matrix-stdlib/src/sys.rs
@@ -0,0 +1,99 @@
+use std::{process::{exit, Command, Stdio}, env, rc::Rc, io::Read};
+
+use matrix::{vm::Vm, value::{Value, ValueMap}, unpack_args, Result, gc::Gc};
+use matrix_macros::native_func;
+use crate::{VmArgs, error};
+
+#[native_func(1)]
+fn sys_exit(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [value] = unpack_args!(args);
+ let Value::Int(i) = value else {
+ return error!("exit requires a int exit code")
+ };
+ exit(i as i32);
+}
+
+#[native_func(0)]
+fn argv(_: VmArgs, _: Vec<Value>) -> Result<Value> {
+ Ok(Value::List(
+ Gc::new(
+ env::args()
+ .map(|a| Value::String(Rc::from(a.as_str())))
+ .collect()
+ )))
+}
+
+#[native_func(1)]
+fn env(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [value] = unpack_args!(args);
+ let Value::String(value) = value else {
+ return error!("env requires a string name")
+ };
+ match std::env::var(value.as_ref()) {
+ Ok(v) => Ok(Value::String(v.into())),
+ Err(e) => error!("couldn't read env var: {e}")
+ }
+}
+
+#[native_func(2)]
+fn exec(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [cmd, args] = unpack_args!(args);
+ let (cmd, args) = match (cmd, args) {
+ (Value::String(s), Value::List(l)) => (s, l.into_inner()),
+ _ => return error!("exec requires a string cmd and string argument list")
+ };
+ let mut sargs = Vec::new();
+ for arg in args {
+ let Value::String(arg) = arg else {
+ return error!("exec requires a string cmd and string argument list")
+ };
+ sargs.push(arg.to_string());
+ };
+ let cmd = Command::new(cmd.to_string())
+ .args(sargs)
+ .stdin(Stdio::piped())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn();
+ let mut child = match cmd {
+ Ok(c) => c,
+ Err(e) => return error!("error executing command: {e}")
+ };
+ let status = match child.wait() {
+ Ok(s) => s,
+ Err(e) => return error!("error executing command: {e}")
+ };
+
+ let stdout = match child.stdout {
+ Some(ref mut out) => {
+ let mut buf = String::new();
+ let _ = out.read_to_string(&mut buf);
+ buf
+ },
+ None => String::new()
+ };
+
+ let stderr = match child.stderr {
+ Some(ref mut err) => {
+ let mut buf = String::new();
+ let _ = err.read_to_string(&mut buf);
+ buf
+ },
+ None => String::new()
+ };
+
+ let mut res = ValueMap::new();
+ res.insert(Value::from("success"), Value::Bool(status.success()))?;
+ res.insert(Value::from("code"), Value::Int(status.code().unwrap_or(0) as i64))?;
+ res.insert(Value::from("out"), Value::String(stdout.into()))?;
+ res.insert(Value::from("err"), Value::String(stderr.into()))?;
+
+ Ok(Value::Table(res.into()))
+}
+
+pub fn load(vm: &mut Vm) {
+ vm.load_global_fn(sys_exit(), "exit");
+ vm.load_global_fn(argv(), "argv");
+ vm.load_global_fn(exec(), "exec");
+ vm.load_global_fn(env(), "env");
+}
diff --git a/matrix/src/ast.rs b/matrix/src/ast.rs
index 68e325e..de63630 100644
--- a/matrix/src/ast.rs
+++ b/matrix/src/ast.rs
@@ -1,5 +1,5 @@
-use std::{rc::Rc, ops::{Neg, Not}};
-use crate::{lex::Token, value::{Value, InlineList, InlineMatrix, InlineTable}, Result};
+use std::{rc::Rc, ops::{Neg, Not}, fmt::Debug};
+use crate::{lex::{Position, TokenData}, value::{Value, InlineList, InlineMatrix, InlineTable}, Result};
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum UnaryOp {
@@ -31,10 +31,15 @@ pub enum BinaryOp {
LessEquals,
GreaterThan,
LessThan,
+ // iter
+ Range,
+ RangeEq
}
+pub type AstName = (Rc<str>, Position);
+
#[derive(Debug, Clone, PartialEq)]
-pub enum Expr {
+pub enum ExprData {
NoOp,
Literal(Value),
@@ -45,7 +50,7 @@ pub enum Expr {
Index(Box<Expr>, Vec<Expr>),
FnCall(Box<Expr>, Vec<Expr>),
- FieldAccess(Box<Expr>, Rc<str>),
+ FieldAccess(Box<Expr>, AstName),
List(InlineList),
Matrix(InlineMatrix),
@@ -57,22 +62,49 @@ pub enum Expr {
Assign(Box<Expr>, Box<Expr>),
If(Box<Expr>, Box<Expr>, Option<Box<Expr>>),
- Function(Rc<str>, Vec<Rc<str>>, Box<Expr>, bool),
- Lambda(Vec<Rc<str>>, Box<Expr>, bool),
+ Function(AstName, Vec<AstName>, Box<Expr>, bool),
+ Lambda(Vec<AstName>, Box<Expr>, bool),
Loop(Box<Expr>),
While(Box<Expr>, Box<Expr>),
DoWhile(Box<Expr>, Box<Expr>),
+ For(AstName, Box<Expr>, Box<Expr>),
Block(Vec<Expr>),
- Let(Rc<str>, Box<Expr>),
+ Try(Box<Expr>, AstName, Box<Expr>),
+
+ Let(AstName, Box<Expr>),
+
+ Pipeline(Box<Expr>, Box<Expr>),
Continue,
Break,
Return(Box<Expr>),
}
+#[derive(Clone, PartialEq)]
+pub struct Expr {
+ pub data: ExprData,
+ pub pos: Position
+}
+
+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)
+ }
+ }
+}
+
+impl From<(ExprData, Position)> for Expr {
+ fn from(value: (ExprData, Position)) -> Self {
+ Self { data: value.0, pos: value.1 }
+ }
+}
+
impl Neg for BinaryOp {
type Output = Option<Self>;
fn neg(self) -> Self::Output {
@@ -103,73 +135,94 @@ impl Not for BinaryOp {
impl Neg for Expr {
type Output = std::result::Result<Self, Self>;
- fn neg(self) -> Self::Output {
- use Expr::*;
- Ok(match self {
- Literal(v) => Literal(-v),
- BinaryOp(lhs, rhs, op) => {
+ fn neg(mut self) -> Self::Output {
+ use ExprData as E;
+ let mut pos = self.pos;
+ let data = match self.data {
+ E::Literal(v) => E::Literal(-v),
+ E::BinaryOp(lhs, rhs, op) => {
let Some(op_n) = -op else {
- return Err(BinaryOp(lhs, rhs, op));
+ return Err((E::BinaryOp(lhs, rhs, op), pos).into());
};
if let Ok(lhs) = -*lhs.clone() {
- BinaryOp(Box::new(lhs), rhs, op_n)
+ pos = lhs.pos;
+ E::BinaryOp(Box::new(lhs), rhs, op_n)
} else if let Ok(rhs) = -*rhs.clone() {
- BinaryOp(lhs, Box::new(rhs), op_n)
+ pos = rhs.pos;
+ E::BinaryOp(lhs, Box::new(rhs), op_n)
} else {
- return Err(BinaryOp(lhs, rhs, op))
+ return Err((E::BinaryOp(lhs, rhs, op), pos).into());
}
},
- UnaryOp(expr, op) => {
+ E::UnaryOp(expr, op) => {
match op {
- self::UnaryOp::Negate => *expr,
- _ => return Err(UnaryOp(expr, op))
+ UnaryOp::Negate => {
+ pos = expr.pos;
+ expr.data
+ },
+ _ => return Err((E::UnaryOp(expr, op), pos).into())
}
}
- _ => return Err(self)
- })
+ data => return Err((data, pos).into())
+ };
+ self.data = data;
+ self.pos = pos;
+ Ok(self)
}
}
impl Not for Expr {
type Output = std::result::Result<Self, Self>;
- fn not(self) -> Self::Output {
- use Expr::*;
- Ok(match self {
- Literal(v) => Literal(Value::Bool(!v)),
- UnaryOp(expr, op) => {
+ fn not(mut self) -> Self::Output {
+ use ExprData as E;
+ let mut pos = self.pos;
+ let data = match self.data {
+ E::Literal(v) => E::Literal(Value::Bool(!v)),
+ E::UnaryOp(expr, op) => {
match op {
- self::UnaryOp::Not => *expr,
- _ => return Err(UnaryOp(expr, op))
+ self::UnaryOp::Not => {
+ pos = expr.pos;
+ expr.data
+ },
+ _ => return Err((E::UnaryOp(expr, op), pos).into())
}
}
- _ => return Err(self)
- })
+ data => return Err((data, pos).into())
+ };
+ self.data = data;
+ self.pos = pos;
+ Ok(self)
}
}
-pub fn optimize(expr: Expr) -> Result<Expr> {
- use Expr as E;
- Ok(match expr {
+pub fn optimize(mut expr: Expr) -> Result<Expr> {
+ use ExprData as E;
+ let mut pos = expr.pos;
+ let data: ExprData = match expr.data {
E::UnaryOp(expr, op) => {
let expr = optimize(*expr)?;
match match op {
UnaryOp::Negate => -expr,
UnaryOp::Not => !expr,
} {
- Ok(expr) => expr,
+ Ok(expr) => {
+ pos = expr.pos;
+ expr.data
+ },
Err(expr) => E::UnaryOp(Box::new(expr), op)
}
},
E::BinaryOp(lhs, rhs, op) => {
let lhs = optimize(*lhs)?;
let rhs = optimize(*rhs)?;
- if let (E::Literal(l), E::Literal(r)) = (lhs.clone(), rhs.clone()) {
+ if let (E::Literal(l), E::Literal(r)) = (lhs.clone().data, rhs.clone().data) {
match Value::binary_op(op, l, r) {
Err(err) => return Err(err),
- Ok(value) => return Ok(Expr::Literal(value)),
+ Ok(value) => E::Literal(value),
}
+ } else {
+ E::BinaryOp(Box::new(lhs), Box::new(rhs), op)
}
- E::BinaryOp(Box::new(lhs), Box::new(rhs), op)
},
E::FnCall(ident, values) => {
E::FnCall(ident, values
@@ -200,10 +253,16 @@ pub fn optimize(expr: Expr) -> Result<Expr> {
E::And(lhs, rhs) => {
let lhs = optimize(*lhs)?;
let rhs = optimize(*rhs)?;
- if let (E::Literal(l), r) = (lhs.clone(), rhs.clone()) {
+ if let (E::Literal(l), r) = (lhs.clone().data, rhs.clone().data) {
match !!l.clone() {
- true => r,
- false => E::Literal(l),
+ true => {
+ pos = rhs.pos;
+ r
+ },
+ false => {
+ pos = lhs.pos;
+ E::Literal(l)
+ },
}
} else {
E::And(Box::new(lhs), Box::new(rhs))
@@ -212,10 +271,16 @@ pub fn optimize(expr: Expr) -> Result<Expr> {
E::Or(lhs, rhs) => {
let lhs = optimize(*lhs)?;
let rhs = optimize(*rhs)?;
- if let (E::Literal(l), r) = (lhs.clone(), rhs.clone()) {
+ if let (E::Literal(l), r) = (lhs.clone().data, rhs.clone().data) {
match !l.clone() {
- true => r,
- false => E::Literal(l),
+ true => {
+ pos = rhs.pos;
+ r
+ },
+ false => {
+ pos = lhs.pos;
+ E::Literal(l)
+ },
}
} else {
E::And(Box::new(lhs), Box::new(rhs))
@@ -230,10 +295,10 @@ pub fn optimize(expr: Expr) -> Result<Expr> {
.into_iter()
.enumerate()
.filter(|(i, e)| {
- if let E::Literal(_) = e {
+ if let E::Literal(_) = e.data {
return i + 1 == len
}
- E::NoOp != *e
+ E::NoOp != e.data
})
.map(|e| e.1)
.collect();
@@ -247,39 +312,60 @@ pub fn optimize(expr: Expr) -> Result<Expr> {
let cond = optimize(*cond)?;
let block = optimize(*block)?;
let else_block = else_block.map(|e| optimize(*e)).transpose()?;
- if let E::Literal(lit) = cond {
+ if let E::Literal(lit) = cond.data {
if !!lit {
- return Ok(block)
+ pos = block.pos;
+ block.data
+ } else if let Some(else_block) = else_block {
+ pos = else_block.pos;
+ else_block.data
+ } else {
+ E::NoOp
}
- return Ok(else_block.unwrap_or(E::NoOp))
+ } else {
+ E::If(Box::new(cond), Box::new(block), else_block.map(|e| Box::new(e)))
}
- E::If(Box::new(cond), Box::new(block), else_block.map(|e| Box::new(e)))
},
E::While(cond, block) => {
let cond = optimize(*cond)?;
let block = optimize(*block)?;
- if let E::Literal(lit) = cond {
+ if let E::Literal(lit) = cond.data {
if !!lit {
- return Ok(E::Loop(Box::new(block)))
+ E::Loop(Box::new(block))
+ } else {
+ E::NoOp
}
- return Ok(E::NoOp)
+ } else {
+ E::While(Box::new(cond), Box::new(block))
}
- E::While(Box::new(cond), Box::new(block))
},
+ E::For(name, cond, block) => {
+ let cond = optimize(*cond)?;
+ let block = optimize(*block)?;
+ E::For(name, Box::new(cond), Box::new(block))
+ }
E::DoWhile(block, cond) => {
let cond = optimize(*cond)?;
let block = optimize(*block)?;
- if let E::Literal(lit) = cond.clone() {
- if !!lit {
- return Ok(E::Loop(Box::new(block)))
+ if let E::Literal(lit) = &cond.data {
+ if !!lit.clone() {
+ E::Loop(Box::new(block))
+ } else {
+ E::DoWhile(Box::new(block), Box::new(cond))
}
+ } else {
+ E::DoWhile(Box::new(block), Box::new(cond))
}
- E::DoWhile(Box::new(block), Box::new(cond))
}
E::Loop(block) => {
let block = Box::new(optimize(*block)?);
E::Loop(block)
},
+ E::Try(expr, err, catch) => {
+ let expr = Box::new(optimize(*expr)?);
+ let catch = Box::new(optimize(*catch)?);
+ E::Try(expr, err, catch)
+ },
E::Function(ident, params, stmt, varadic) => {
let stmt = Box::new(optimize(*stmt)?);
E::Function(ident, params, stmt, varadic)
@@ -297,44 +383,53 @@ pub fn optimize(expr: Expr) -> Result<Expr> {
E::Assign(lhs, rhs)
},
E::Return(expr) => {
- E::Return(Box::new(optimize(*expr)?))
+ let expr = Box::new(optimize(*expr)?);
+ E::Return(expr)
},
- _ => expr
- })
+ E::Pipeline(lhs, rhs) => {
+ let lhs = Box::new(optimize(*lhs)?);
+ let rhs = Box::new(optimize(*rhs)?);
+ E::Pipeline(lhs, rhs)
+ }
+ data => data
+ };
+ expr.data = data;
+ expr.pos = pos;
+ Ok(expr)
}
-impl From<Token> for UnaryOp {
- fn from(value: Token) -> Self {
- use Token::*;
+impl From<TokenData> for UnaryOp {
+ fn from(value: TokenData) -> Self {
+ use TokenData as T;
match value {
- Subtract => Self::Negate,
- Not => Self::Not,
+ T::Subtract => Self::Negate,
+ T::Not => Self::Not,
_ => panic!("aaaaa")
}
}
}
-impl From<Token> for BinaryOp {
- fn from(value: Token) -> Self {
- use Token::*;
+impl From<TokenData> for BinaryOp {
+ fn from(value: TokenData) -> Self {
+ use TokenData as T;
match value {
- Equal => Self::Equals,
- NotEqual => Self::NotEquals,
- GreaterEqual => Self::GreaterEquals,
- LessEqual => Self::LessEquals,
- GreaterThan => Self::GreaterThan,
- LessThan => Self::LessThan,
- BitwiseShiftLeft => Self::BitwiseShiftLeft,
- BitwiseShiftRight => Self::BitwiseShiftRight,
- BitwiseAnd => Self::BitwiseAnd,
- BitwiseOr => Self::BitwiseOr,
- BitwiseXor => Self::BitwiseXor,
- Add => Self::Add,
- Subtract => Self::Subtract,
- Multiply => Self::Multiply,
- Divide => Self::Divide,
- Modulo => Self::Modulo,
- Power => Self::Power,
+ T::Equal => Self::Equals,
+ T::NotEqual => Self::NotEquals,
+ T::GreaterEqual => Self::GreaterEquals,
+ T::LessEqual => Self::LessEquals,
+ T::GreaterThan => Self::GreaterThan,
+ T::LessThan => Self::LessThan,
+ T::BitwiseShiftLeft => Self::BitwiseShiftLeft,
+ T::BitwiseShiftRight => Self::BitwiseShiftRight,
+ T::BitwiseAnd => Self::BitwiseAnd,
+ T::BitwiseOr => Self::BitwiseOr,
+ T::BitwiseXor => Self::BitwiseXor,
+ T::Add => Self::Add,
+ T::Subtract => Self::Subtract,
+ T::Multiply => Self::Multiply,
+ T::Divide => Self::Divide,
+ T::Modulo => Self::Modulo,
+ T::Power => Self::Power,
_ => panic!("aaaaa")
}
}
@@ -342,11 +437,11 @@ impl From<Token> for BinaryOp {
impl Expr {
pub fn is_assignable(&self) -> bool {
- use Expr::*;
- match self {
- Ident(_) => true,
- Index(_, _) => true,
- FieldAccess(_, _) => true,
+ use ExprData as E;
+ match self.data {
+ E::Ident(_) => true,
+ E::Index(_, _) => true,
+ E::FieldAccess(_, _) => true,
_ => false,
}
}
diff --git a/matrix/src/chunk.rs b/matrix/src/chunk.rs
index 19ff8e6..495b787 100644
--- a/matrix/src/chunk.rs
+++ b/matrix/src/chunk.rs
@@ -1,17 +1,19 @@
-use crate::{value::Value, ast::{UnaryOp, BinaryOp}, vm::Vm, Result};
+use crate::{value::Value, ast::{UnaryOp, BinaryOp}, vm::{Vm, StackFrame}, Result, lex::Position};
use std::{fmt::{Debug, Display}, rc::Rc};
-#[derive(Clone)]
+#[derive(Clone, Default)]
pub struct Chunk {
pub constants: Vec<Value>,
- pub code: Vec<Instruction>
+ pub code: Vec<Instruction>,
+ pub pos: Vec<Position>,
}
impl Chunk {
pub fn new() -> Self {
Self {
constants: Vec::new(),
- code: Vec::new()
+ code: Vec::new(),
+ pos: Vec::new(),
}
}
}
@@ -43,9 +45,10 @@ pub struct Function {
pub fun: InnerFunction
}
+#[derive(Clone)]
pub enum InnerFunction {
Compiled(Rc<Chunk>),
- Native(Box<dyn Fn(&mut Vm, Vec<Value>) -> Result<Value>>),
+ Native(Rc<dyn Fn((&mut Vm, &mut StackFrame), Vec<Value>) -> Result<Value>>),
}
impl Debug for Function {
@@ -98,6 +101,13 @@ pub enum Instruction {
Jump(u16),
JumpTrue(u16),
JumpFalse(u16),
+ JumpNil(u16),
+
+ IterCreate,
+ IterNext,
+
+ Try(u16),
+ TryEnd,
Call(u8),
Return,
@@ -131,10 +141,15 @@ impl Display for Instruction {
Jump(idx) => write!(f, "jump \x1b[33m{idx}\x1b[0m"),
JumpTrue(idx) => write!(f, "jumpt \x1b[33m{idx}\x1b[0m"),
JumpFalse(idx) => write!(f, "jumpf \x1b[33m{idx}\x1b[0m"),
+ JumpNil(idx) => write!(f, "jumpn \x1b[33m{idx}\x1b[0m"),
Call(arity) => write!(f, "call \x1b[35m{arity}\x1b[0m"),
Return => write!(f, "return"),
+ IterCreate => write!(f, "iter create"),
+ IterNext => write!(f, "iter next"),
Field(name_idx) => write!(f, "field \x1b[33m{name_idx}\x1b[0m"),
StoreField(name_idx) => write!(f, "store field \x1b[33m{name_idx}\x1b[0m"),
+ Try(idx) => write!(f, "try \x1b[33m{idx}\x1b[0m"),
+ TryEnd => write!(f, "try end"),
}
}
}
diff --git a/matrix/src/compiler.rs b/matrix/src/compiler.rs
index a516807..6b6a94b 100644
--- a/matrix/src/compiler.rs
+++ b/matrix/src/compiler.rs
@@ -1,9 +1,9 @@
use std::{fmt::Display, rc::Rc, cell::RefCell};
-use crate::{ast::Expr, chunk::{Function, Instruction, InnerFunction}, chunk::{Chunk, self}, value::Value, Result};
+use crate::{ast::{Expr, ExprData, AstName}, chunk::{Function, Instruction, InnerFunction}, chunk::{Chunk, self}, value::Value, Result, lex::Position};
use Instruction as I;
use Value as V;
-use Expr as E;
+use ExprData as E;
pub type NamesTable = Rc<RefCell<Vec<Rc<str>>>>;
@@ -104,7 +104,7 @@ struct Local {
}
#[derive(Debug, Clone)]
-pub enum Error {
+pub enum InnerError {
Undefined(Rc<str>),
Redefined(Rc<str>),
InvAssign(Expr),
@@ -113,22 +113,36 @@ pub enum Error {
NotImplemented(&'static str),
}
+#[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 self::Error::*;
- match self {
- Undefined(name) => write!(f, "value {name} is undefined"),
- Redefined(name) => write!(f, "cannot redefine {name} in the same scope"),
- InvAssign(expr) => write!(f, "cannot assign to {expr:?}"),
- InvContinue => write!(f, "cannot continue outside a loop"),
- InvBreak => write!(f, "cannot break outside a loop"),
- NotImplemented(str) => write!(f, "{str} is not implemented yet")
+ 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())
+}
+
impl<'c> Compiler<'c> {
fn child(&'c self, name: Rc<str>) -> Self {
@@ -156,7 +170,7 @@ impl<'c> Compiler<'c> {
cutoff = self.scopes.pop().unwrap()
}
if cutoff < self.locals.len() {
- self.emit(Instruction::DiscardLocals((self.locals.len() - cutoff) as u16));
+ self.emit(Instruction::DiscardLocals((self.locals.len() - cutoff) as u16), self.last_pos());
self.locals.truncate(cutoff);
};
}
@@ -166,7 +180,7 @@ impl<'c> Compiler<'c> {
return;
};
if cutoff < self.locals.len() {
- self.emit(Instruction::DiscardLocals((self.locals.len() - cutoff) as u16));
+ self.emit(Instruction::DiscardLocals((self.locals.len() - cutoff) as u16), self.last_pos());
self.locals.truncate(cutoff);
};
}
@@ -182,19 +196,19 @@ impl<'c> Compiler<'c> {
c
}
- fn create_local_checked(&mut self, name: Rc<str>) -> Result<()> {
+ fn create_local_checked(&mut self, name: Rc<str>, pos: Position) -> Result<()> {
if let Some(local) = self.find_local(&name) {
if local.scope == self.scopes.len() {
- return Err(Error::Redefined(name).into())
+ return error(InnerError::Redefined(name), pos)
}
};
self.create_local(name);
Ok(())
}
- fn create_global_checked(&mut self, name: Rc<str>) -> Result<usize> {
+ fn create_global_checked(&mut self, name: Rc<str>, pos: Position) -> Result<usize> {
if let Some(_) = self.find_global(&name) {
- return Err(Error::Redefined(name).into())
+ return error(InnerError::Redefined(name).into(), pos)
}
Ok(self.create_global(name))
}
@@ -227,29 +241,29 @@ impl<'c> Compiler<'c> {
idx
}
- fn emit_const(&mut self, val: Value) {
+ fn emit_const(&mut self, val: Value, pos: Position) {
// TODO: find constant if already exists
self.chunk.constants.push(val);
let c = self.chunk.constants.len() - 1;
- self.emit(Instruction::Const(c as u16));
+ self.emit(Instruction::Const(c as u16), pos);
}
fn can_make_globals(&self) -> bool {
self.repl && self.parent.is_none() && self.scopes.len() == 0
}
- fn compile_value(&mut self, val: &Value) {
+ fn compile_value(&mut self, val: &Value, pos: Position) {
match val {
- V::Nil => self.emit(I::Nil),
- V::Bool(b) => if *b { self.emit(I::True) } else { self.emit(I::False) },
+ V::Nil => self.emit(I::Nil, pos),
+ V::Bool(b) => if *b { self.emit(I::True, pos) } else { self.emit(I::False, pos) },
V::Int(i) => {
if let Ok(i) = i16::try_from(*i) {
- self.emit(I::Int(i));
+ self.emit(I::Int(i), pos);
} else {
- self.emit_const(val.clone());
+ self.emit_const(val.clone(), pos);
}
},
- _ => self.emit_const(val.clone()),
+ _ => self.emit_const(val.clone(), pos),
}
}
@@ -263,152 +277,193 @@ impl<'c> Compiler<'c> {
}
fn compile_expr(&mut self, expr: &Expr) -> Result<()> {
- match expr {
- E::NoOp => self.emit(I::Nil),
+ match &expr.data {
+ E::NoOp => self.emit(I::Nil, expr.pos),
E::If(cond, ifb, elseb) => {
self.compile_expr(cond)?;
- let jmpidx = self.emit_temp();
+ let jmpidx = self.emit_temp(expr.pos);
self.compile_expr(ifb)?;
+ let jmpidx2 = self.emit_temp(expr.pos);
self.re_emit(I::JumpFalse(self.cur()), jmpidx);
if let Some(elseb) = elseb {
self.compile_expr(elseb)?;
}
+ self.re_emit(I::Jump(self.cur()), jmpidx2);
},
E::Function(name, params, body, varadic) => {
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 {
- name: name.clone(),
- arity: params.len(),
+ name: name.0.clone(),
+ arity,
fun: InnerFunction::Compiled(chunk.into()),
variadic: *varadic
}
));
- self.emit_const(fun);
- self.emit(I::Dup);
+ self.emit_const(fun, expr.pos);
+ self.emit(I::Dup, expr.pos);
if self.can_make_globals() {
- let idx = self.create_global_checked(name.clone())?;
- self.emit(I::StoreGlobal(idx as u16));
+ let idx = self.create_global_checked(name.0.clone(), name.1)?;
+ self.emit(I::StoreGlobal(idx as u16), expr.pos);
} else {
- self.create_local_checked(name.clone())?;
- self.emit(I::CreateLocal);
+ self.create_local_checked(name.0.clone(), name.1)?;
+ self.emit(I::CreateLocal, expr.pos);
}
},
E::Lambda(params, body, varadic) => {
- let name: Rc<str> = Rc::from("<lambda>");
+ let name: AstName = (Rc::from("<lambda>"), expr.pos);
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 {
- name: name.clone(),
- arity: params.len(),
+ name: name.0.clone(),
+ arity,
fun: InnerFunction::Compiled(chunk.into()),
variadic: *varadic
}
));
- self.emit_const(fun);
+ self.emit_const(fun, expr.pos);
},
E::Loop(expr) => {
let idx = self.cur();
self.loop_top.push((idx as usize, self.scopes.len()));
self.compile_expr(expr)?;
- self.emit(I::Discard(1));
- self.emit(I::Jump(idx));
+ self.emit(I::Discard(1), expr.pos);
+ self.emit(I::Jump(idx), expr.pos);
self.finish_loop();
- self.emit(I::Nil);
+ self.emit(I::Nil, expr.pos);
+ },
+ E::Try(expr, err, catch) => {
+ let jmpidx = self.emit_temp(expr.pos);
+ self.compile_expr(expr)?;
+ self.emit(I::TryEnd, expr.pos);
+ 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.emit(I::CreateLocal, err.1);
+ self.compile_expr(catch)?;
+ self.end_scope();
+ self.re_emit(I::Jump(self.cur() as u16), jmpidx2);
},
E::While(cond, expr) => {
let top = self.cur();
self.compile_expr(cond)?;
- let jmpidx = self.emit_temp();
+ let jmpidx = self.emit_temp(expr.pos);
self.loop_top.push((top as usize, self.scopes.len()));
self.compile_expr(expr)?;
- self.emit(I::Discard(1));
- self.emit(I::Jump(top));
+ self.emit(I::Discard(1), expr.pos);
+ self.emit(I::Jump(top), expr.pos);
self.re_emit(I::JumpFalse(self.cur()), jmpidx);
self.finish_loop();
- self.emit(I::Nil);
+ self.emit(I::Nil, expr.pos);
},
E::DoWhile(expr, cond) => {
let top = self.cur();
self.loop_top.push((top as usize, self.scopes.len()));
self.compile_expr(expr)?;
- self.emit(I::Discard(1));
+ self.emit(I::Discard(1), expr.pos);
self.compile_expr(cond)?;
- self.emit(I::JumpTrue(top));
+ self.emit(I::JumpTrue(top), expr.pos);
self.finish_loop();
- self.emit(I::Nil);
+ self.emit(I::Nil, expr.pos);
},
+ E::For(name, cond, expr) => {
+ self.compile_expr(cond)?;
+ self.emit(I::IterCreate, cond.pos);
+ let top = self.cur();
+ self.emit(I::IterNext, cond.pos);
+ self.emit(I::Dup, expr.pos);
+ 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.emit(I::CreateLocal, name.1);
+ self.compile_expr(expr)?;
+ self.emit(I::Discard(1), expr.pos);
+ self.end_scope();
+ self.emit(I::Jump(top), expr.pos);
+ self.re_emit(I::JumpNil(self.cur()), jumpidx);
+ self.finish_loop();
+ self.emit(I::Discard(2), expr.pos);
+ self.emit(I::Nil, expr.pos);
+ }
E::Block(block) => {
+ if block.len() == 0 {
+ self.emit(I::Nil, expr.pos);
+ return Ok(());
+ }
self.begin_scope();
for (i, expr) in block.iter().enumerate() {
self.compile_expr(expr)?;
if i + 1 != block.len() {
- self.emit(I::Discard(1));
+ self.emit(I::Discard(1), expr.pos);
}
}
self.end_scope();
},
E::Let(name, expr) => {
self.compile_expr(expr)?;
- self.emit(I::Dup);
+ self.emit(I::Dup, expr.pos);
if self.can_make_globals() {
- let global = self.create_global_checked(name.clone())?;
- self.emit(I::StoreGlobal(global as u16));
+ let global = self.create_global_checked(name.0.clone(), name.1)?;
+ self.emit(I::StoreGlobal(global as u16), expr.pos);
} else {
- self.create_local_checked(name.clone())?;
- self.emit(I::CreateLocal);
+ self.create_local_checked(name.0.clone(), name.1)?;
+ self.emit(I::CreateLocal, expr.pos);
}
},
E::Continue => {
let top = self.loop_top.pop();
if let Some((top, scope)) = top {
self.collapse_scopes(scope);
- self.emit(I::Jump(top as u16));
+ self.emit(I::Jump(top as u16), expr.pos);
} else {
- return Err(Error::InvContinue.into())
+ return error(InnerError::InvContinue, expr.pos)
}
},
E::Break => {
let top = self.loop_top.pop();
if let Some((_, scope)) = top {
self.collapse_scopes(scope);
- let tmpidx = self.emit_temp();
+ let tmpidx = self.emit_temp(expr.pos);
self.loop_bot.push(tmpidx);
} else {
- return Err(Error::InvBreak.into())
+ return error(InnerError::InvBreak, expr.pos)
}
},
E::Return(expr) => {
self.compile_expr(expr)?;
- self.emit(I::Return);
+ self.emit(I::Return, expr.pos);
},
- E::Literal(val) => self.compile_value(val),
+ E::Literal(val) => self.compile_value(val, expr.pos),
E::Ident(name) => {
if name.as_ref() == "_" {
- self.emit(I::Nil);
+ self.emit(I::Nil, expr.pos);
} else if let Some(local) = self.find_local(name) {
- self.emit(I::LoadLocal(local.idx as u16));
+ 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));
+ self.emit(I::LoadGlobal(global as u16), expr.pos);
} else {
- return Err(self::Error::Undefined(name.clone()).into())
+ return error(InnerError::Undefined(name.clone()), expr.pos)
};
},
E::Assign(lhs, rhs) => {
self.compile_expr(rhs)?;
- self.emit(I::Dup);
- match lhs.as_ref() {
+ self.emit(I::Dup, rhs.pos);
+ match &lhs.data {
E::Ident(name) if name.as_ref() != "_" => {
if let Some(local) = self.find_local(&name) {
- self.emit(I::StoreLocal(local.idx as u16));
+ 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));
+ self.emit(I::StoreGlobal(global as u16), lhs.pos);
} else if self.can_make_globals() {
- let global = self.create_global_checked(name.clone())?;
- self.emit(I::StoreGlobal(global as u16));
+ let global = self.create_global_checked(name.clone(), lhs.pos)?;
+ self.emit(I::StoreGlobal(global as u16), lhs.pos);
} else {
- self.create_local_checked(name.clone())?;
- self.emit(I::CreateLocal);
+ self.create_local_checked(name.clone(), lhs.pos)?;
+ self.emit(I::CreateLocal, lhs.pos);
}
},
E::Index(expr, params) => {
@@ -416,89 +471,94 @@ impl<'c> Compiler<'c> {
for param in params {
self.compile_expr(param)?;
}
- self.emit(I::StoreIndex(params.len() as u8));
+ self.emit(I::StoreIndex(params.len() as u8), expr.pos);
},
E::FieldAccess(expr, ident) => {
self.compile_expr(expr)?;
- let name = self.get_name(ident.clone());
- self.emit(I::StoreField(name as u16));
+ let name = self.get_name(ident.0.clone());
+ self.emit(I::StoreField(name as u16), expr.pos);
}
- _ => return Err(self::Error::InvAssign(*lhs.clone()).into())
+ _ => return error(InnerError::InvAssign(*lhs.clone()), lhs.pos)
}
}
E::UnaryOp(expr, op) => {
self.compile_expr(expr)?;
- self.emit(I::UnaryOp(*op));
+ self.emit(I::UnaryOp(*op), expr.pos);
},
E::BinaryOp(lhs, rhs, op) => {
self.compile_expr(lhs)?;
self.compile_expr(rhs)?;
- self.emit(I::BinaryOp(*op));
+ self.emit(I::BinaryOp(*op), lhs.pos);
},
E::Index(expr, params) => {
self.compile_expr(expr)?;
for param in params {
self.compile_expr(param)?;
}
- self.emit(I::Index(params.len() as u8))
+ self.emit(I::Index(params.len() as u8), expr.pos)
},
E::FnCall(fun, params) => {
for expr in params {
self.compile_expr(expr)?;
}
self.compile_expr(fun)?;
- self.emit(I::Call(params.len() as u8));
+ self.emit(I::Call(params.len() as u8), expr.pos);
},
E::FieldAccess(expr, field) => {
self.compile_expr(expr)?;
- let idx = self.get_name(field.clone());
- self.emit(I::Field(idx as u16))
+ let idx = self.get_name(field.0.clone());
+ self.emit(I::Field(idx as u16), expr.pos)
}
E::List(list) => {
for expr in list {
self.compile_expr(expr)?;
}
- self.emit(I::NewList(list.len() as u16));
+ self.emit(I::NewList(list.len() as u16), expr.pos);
},
E::Matrix(mat) => {
for expr in &mat.2 {
self.compile_expr(expr)?;
}
- self.emit(I::NewMatrix(mat.2.len() as u16, mat.0 as u8));
+ self.emit(I::NewMatrix(mat.2.len() as u16, mat.0 as u8), expr.pos);
},
E::Table(table) => {
for (key, value) in table {
self.compile_expr(key)?;
self.compile_expr(value)?;
}
- self.emit(I::NewTable(table.len() as u16));
+ self.emit(I::NewTable(table.len() as u16), expr.pos);
},
E::And(lhs, rhs) => {
self.compile_expr(lhs)?;
- self.emit(I::Dup);
- let jmpidx = self.emit_temp();
+ self.emit(I::Dup, lhs.pos);
+ let jmpidx = self.emit_temp(lhs.pos);
self.compile_expr(rhs)?;
self.re_emit(I::JumpFalse(self.cur()), jmpidx);
},
E::Or(lhs, rhs) => {
self.compile_expr(lhs)?;
- self.emit(I::Dup);
- let jmpidx = self.emit_temp();
+ self.emit(I::Dup, lhs.pos);
+ let jmpidx = self.emit_temp(lhs.pos);
self.compile_expr(rhs)?;
self.re_emit(I::JumpTrue(self.cur()), jmpidx);
},
+ E::Pipeline(lhs, rhs) => {
+ self.compile_expr(lhs)?;
+ self.compile_expr(rhs)?;
+ self.emit(I::Call(1), expr.pos);
+ }
};
Ok(())
}
- fn compile_function(&mut self, name: Rc<str>, params: &Vec<Rc<str>>, body: &Box<Expr>) -> Result<Chunk> {
- let mut compiler = self.child(name);
- for name in params {
+ 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.emit(I::CreateLocal);
+ compiler.emit(I::CreateLocal, *pos);
}
compiler.compile_expr(body)?;
- compiler.finish()?;
+ compiler.finish(name.1)?;
Ok(compiler.chunk)
}
@@ -506,15 +566,16 @@ impl<'c> Compiler<'c> {
self.chunk.code.len() as u16
}
- fn emit_temp(&mut self) -> usize {
+ fn emit_temp(&mut self, pos: Position) -> usize {
let idx = self.chunk.code.len();
- self.emit(Instruction::NoOp);
+ self.emit(Instruction::NoOp, pos);
idx
}
- fn emit(&mut self, ins: Instruction) {
+ fn emit(&mut self, ins: Instruction, pos: Position) {
//println!("{}: {ins}", self.name);
self.chunk.code.push(ins);
+ self.chunk.pos.push(pos);
}
fn re_emit(&mut self, ins: Instruction, idx: usize) {
@@ -522,12 +583,20 @@ impl<'c> Compiler<'c> {
self.chunk.code[idx] = ins;
}
- fn finish(&mut self) -> Result<()> {
+ fn last_pos(&self) -> Position {
+ if let Some(pos) = self.chunk.pos.last() {
+ *pos
+ } else {
+ Position::default()
+ }
+ }
+
+ fn finish(&mut self, pos: Position) -> Result<()> {
let ins = match self.chunk.code.last() {
Some(ins) => ins.clone(),
None => {
- self.emit(I::Nil);
- self.emit(I::Return);
+ self.emit(I::Nil, pos);
+ self.emit(I::Return, pos);
if self.debug {
println!("{}\n{}", self.name, self.chunk);
}
@@ -537,12 +606,12 @@ impl<'c> Compiler<'c> {
match ins {
I::Return => {},
_ => {
- self.emit(I::Return);
+ self.emit(I::Return, pos);
}
};
if self.loop_bot.len() > 0 {
- return Err(Error::InvBreak.into())
+ return error(InnerError::InvBreak, pos)
}
if self.debug {
@@ -555,12 +624,12 @@ impl<'c> Compiler<'c> {
&mut self,
body: &Expr,
) -> Result<Rc<Function>> {
- if let Expr::Block(_) = body {
+ if let ExprData::Block(_) = &body.data {
self.root_is_block = true;
}
self.chunk = Chunk::new();
self.compile_expr(body)?;
- self.finish()?;
+ self.finish(self.last_pos())?;
let fun = Function { name: self.name.clone(), fun: InnerFunction::Compiled(self.chunk.clone().into()), arity: 0, variadic: false };
Ok(Rc::new(fun))
}
diff --git a/matrix/src/gc.rs b/matrix/src/gc.rs
index 0c60849..8fba633 100644
--- a/matrix/src/gc.rs
+++ b/matrix/src/gc.rs
@@ -31,6 +31,12 @@ impl <T: Clone> Gc<T> {
}
}
+ pub fn into_inner(self) -> T {
+ unsafe {
+ self.ptr.as_ref().data.clone()
+ }
+ }
+
fn data(&self) -> T {
unsafe {
self.ptr.as_ref().data.clone()
diff --git a/matrix/src/lex.rs b/matrix/src/lex.rs
index 16032f9..8a07234 100644
--- a/matrix/src/lex.rs
+++ b/matrix/src/lex.rs
@@ -31,7 +31,7 @@ impl From<RegexToken> for Regex {
}
#[derive(Debug, PartialEq)]
-pub enum Token {
+pub enum TokenData {
//syntax
LeftParen,
RightParen,
@@ -46,9 +46,11 @@ pub enum Token {
ThinArrow,
Comma,
Range,
+ RangeEq,
Colon,
Backslash,
Varadic,
+ Pipe,
// math
Regex(RegexToken),
@@ -113,11 +115,24 @@ pub enum Token {
Do,
Loop,
Return,
+ For,
+ In,
+ Try,
+ Catch,
// eof
Eof,
}
+#[derive(Debug, PartialEq)]
+pub struct Token {
+ pub data: TokenData,
+ pub pos: Position,
+ pub str: String,
+ pub bidx: usize,
+ pub blen: usize,
+}
+
#[derive(Debug)]
pub enum Error {
UnexpectedCharacter(char),
@@ -148,10 +163,24 @@ impl std::fmt::Display for Error {
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,
data: Vec<char>,
+ pos: Position,
+ byte_len: usize,
}
trait IsIdent {
@@ -177,7 +206,13 @@ impl<T: Into<String>> From<T> for Lexer {
impl Lexer {
pub fn new<T: Into<String>>(input: T) -> Self {
let data: Vec<char> = input.into().chars().collect();
- Self { index: 0, len: data.len(), data }
+ Self {
+ index: 0,
+ len: data.len(),
+ data,
+ pos: Position::default(),
+ byte_len: 0
+ }
}
fn peek(&self) -> char {
@@ -190,6 +225,14 @@ impl Lexer {
fn next(&mut self) -> char {
let c = self.peek();
self.index += 1;
+ self.byte_len += c.len_utf8();
+
+ self.pos.col += 1;
+ if c == '\n' {
+ self.pos.col = 1;
+ self.pos.row += 1;
+ }
+
c
}
@@ -274,14 +317,14 @@ impl Lexer {
Ok(buf.into())
}
- fn lex_ident(&mut self, initial: char) -> Result<Token> {
- use Error::*;
- use Token::*;
+ 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(UnexpectedCharacter(initial).into())
+ return Err(E::UnexpectedCharacter(initial).into())
}
buf.push(initial);
@@ -295,29 +338,33 @@ impl Lexer {
}
Ok(match buf.as_str() {
- "if" => If,
- "else" => Else,
- "while" => While,
- "let" => Let,
- "fn" | "function" => Function,
- "true" => True,
- "false" => False,
- "nil" => Nil,
- "continue" => Continue,
- "break" => Break,
- "do" => Do,
- "loop" => Loop,
- "and" => And,
- "or" => Or,
- "not" => Not,
- "return" => Return,
- _ => Ident(buf.into())
+ "if" => T::If,
+ "else" => T::Else,
+ "while" => T::While,
+ "let" => T::Let,
+ "fn" | "function" => T::Function,
+ "true" => T::True,
+ "false" => T::False,
+ "nil" => T::Nil,
+ "continue" => T::Continue,
+ "break" => T::Break,
+ "do" => T::Do,
+ "loop" => T::Loop,
+ "and" => T::And,
+ "or" => T::Or,
+ "not" => T::Not,
+ "return" => T::Return,
+ "for" => T::For,
+ "in" => T::In,
+ "try" => T::Try,
+ "catch" => T::Catch,
+ _ => T::Ident(buf.into())
})
}
- fn lex_radix(&mut self, radix: i64, radix_char: char) -> Result<Token> {
- use Token::*;
- use Error::*;
+ 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;
@@ -328,21 +375,21 @@ impl Lexer {
n = n * radix + (i as i64);
char_found = true;
} else if self.peek().is_ident() {
- return Err(InvalidDigit(self.peek()).into())
+ return Err(E::InvalidDigit(self.peek()).into())
} else {
break;
}
}
if char_found {
- Ok(Int(n))
+ Ok(T::Int(n))
} else {
- Err(InvalidNumber(format!("0{radix_char}")).into())
+ Err(E::InvalidNumber(format!("0{radix_char}")).into())
}
}
- fn lex_number(&mut self, initial: char) -> Result<Token> {
- use Error::*;
+ fn lex_number(&mut self, initial: char) -> Result<TokenData> {
+ use Error as E;
if initial == '0' {
match self.peek() {
@@ -361,6 +408,10 @@ impl Lexer {
let mut buf = String::new();
buf.push(initial);
+ let mut pos = self.pos;
+ let mut idx = self.index;
+ let mut bidx = self.byte_len;
+
if initial != '.' {
loop {
if !self.peek().is_ascii_digit() { break; }
@@ -368,24 +419,37 @@ impl Lexer {
}
if self.peek() == '.' {
+ pos = self.pos;
+ idx = self.index;
+ bidx = self.byte_len;
buf.push(self.next());
}
}
- loop {
- if !self.peek().is_ascii_digit() { break; }
- buf.push(self.next());
- }
+ let last: char = buf.chars().last().unwrap();
+ let is_range = initial != '.' && last == '.' && self.peek() == '.';
- if self.peek() == 'e' || self.peek() == 'E' {
- buf.push(self.next());
- if self.peek() == '+' || self.peek() == '-' {
+ if is_range {
+ self.pos = pos;
+ self.index = idx;
+ self.byte_len = bidx;
+ buf.pop();
+ } else {
+ loop {
+ if !self.peek().is_ascii_digit() { break; }
buf.push(self.next());
}
- loop {
- if !self.peek().is_ascii_digit() { break; }
+ if self.peek() == 'e' || self.peek() == 'E' {
buf.push(self.next());
+ if self.peek() == '+' || self.peek() == '-' {
+ buf.push(self.next());
+ }
+
+ loop {
+ if !self.peek().is_ascii_digit() { break; }
+ buf.push(self.next());
+ }
}
}
@@ -394,87 +458,103 @@ impl Lexer {
self.next();
}
- if self.peek().is_ident() || self.peek() == '.' {
- return Err(UnexpectedCharacter(self.peek()).into())
+ if self.peek().is_ident() {
+ return Err(E::UnexpectedCharacter(self.peek()).into())
}
if let Ok(int) = buf.parse::<i64>() {
- use Token::*;
+ use TokenData as T;
if complex {
- return Ok(Complex(int as f64))
+ return Ok(T::Complex(int as f64))
}
- return Ok(Int(int))
+ return Ok(T::Int(int))
}
if let Ok(float) = buf.parse::<f64>() {
- use Token::*;
+ use TokenData as T;
if complex {
- return Ok(Complex(float))
+ return Ok(T::Complex(float))
}
- return Ok(Float(float))
+ return Ok(T::Float(float))
}
- Err(Error::InvalidNumber(buf).into())
+ Err(E::InvalidNumber(buf).into())
}
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> {
- use Token::*;
- use Error::*;
+ use TokenData as T;
+ use Error as E;
self.skip_whitespace(ignore_newlines);
+
+ let str_start = self.index;
+ let byte_start = self.byte_len;
+
+ let pos = self.pos;
let char = self.next();
let next = self.peek();
if char == '\0' {
- return Ok(if ignore_newlines { Eof } else { SemiColon });
+ let data = if ignore_newlines { T::Eof } else { T::SemiColon };
+ return Ok(Token {
+ data,
+ pos,
+ str: String::new(),
+ bidx: byte_start,
+ blen: 0,
+ })
}
- Ok(match char {
- '\n' => SemiColon,
- '(' => LeftParen,
- ')' => RightParen,
- '[' => LeftBrack,
- ']' => RightBrack,
- '{' => LeftBrace,
- '}' => RightBrace,
- ':' => Colon,
- '\\' => Backslash,
- ';' => SemiColon,
+ let data = match char {
+ '\n' => T::SemiColon,
+ '(' => T::LeftParen,
+ ')' => T::RightParen,
+ '[' => T::LeftBrack,
+ ']' => T::RightBrack,
+ '{' => T::LeftBrace,
+ '}' => T::RightBrace,
+ ':' => T::Colon,
+ '\\' => T::Backslash,
+ ';' => T::SemiColon,
'+' => {
match next {
'=' => {
self.next();
- AssignAdd
+ T::AssignAdd
}
- _ => Add
+ _ => T::Add
}
},
'/' => {
match next {
'=' => {
self.next();
- AssignDivide
+ T::AssignDivide
}
- _ => Divide
+ _ => T::Divide
}
},
'%' => {
match next {
'=' => {
self.next();
- AssignModulo
+ T::AssignModulo
}
- _ => Modulo
+ _ => T::Modulo
}
},
- ',' => Comma,
+ ',' => T::Comma,
'*' => {
match next {
'*' => {
@@ -482,25 +562,25 @@ impl Lexer {
match self.peek() {
'=' => {
self.next();
- AssignPower
+ T::AssignPower
},
- _ => Power
+ _ => T::Power
}
},
'=' => {
self.next();
- AssignMultiply
+ T::AssignMultiply
}
- _ => Multiply
+ _ => T::Multiply
}
},
'!' => {
match next {
'=' => {
self.next();
- NotEqual
+ T::NotEqual
}
- _ => Not
+ _ => T::Not
}
}
'&' => {
@@ -510,16 +590,16 @@ impl Lexer {
match self.peek() {
'=' => {
self.next();
- AssignAnd
+ T::AssignAnd
},
- _ => And
+ _ => T::And
}
},
'=' => {
self.next();
- AssignBitwiseAnd
+ T::AssignBitwiseAnd
},
- _ => BitwiseAnd
+ _ => T::BitwiseAnd
}
},
'|' => {
@@ -529,42 +609,46 @@ impl Lexer {
match self.peek() {
'=' => {
self.next();
- AssignOr
+ T::AssignOr
},
- _ => Or
+ _ => T::Or
}
},
'=' => {
self.next();
- AssignBitwiseOr
+ T::AssignBitwiseOr
+ },
+ '>' => {
+ self.next();
+ T::Pipe
},
- _ => BitwiseOr
+ _ => T::BitwiseOr
}
},
'-' => {
match next {
'>' => {
self.next();
- ThinArrow
+ T::ThinArrow
},
'=' => {
self.next();
- AssignSubtract
+ T::AssignSubtract
},
- _ => Subtract
+ _ => T::Subtract
}
},
'=' => {
match next {
'>' => {
self.next();
- Arrow
+ T::Arrow
}
'=' => {
self.next();
- Equal
+ T::Equal
}
- _ => Assign
+ _ => T::Assign
}
},
'>' => {
@@ -574,16 +658,16 @@ impl Lexer {
match self.peek() {
'=' => {
self.next();
- AssignBitwiseShiftRight
+ T::AssignBitwiseShiftRight
},
- _ => BitwiseShiftRight
+ _ => T::BitwiseShiftRight
}
}
'=' => {
self.next();
- GreaterEqual
+ T::GreaterEqual
}
- _ => GreaterThan
+ _ => T::GreaterThan
}
},
'<' => {
@@ -593,35 +677,35 @@ impl Lexer {
match self.peek() {
'=' => {
self.next();
- AssignBitwiseShiftLeft
+ T::AssignBitwiseShiftLeft
},
- _ => BitwiseShiftLeft
+ _ => T::BitwiseShiftLeft
}
}
'=' => {
self.next();
- LessEqual
+ T::LessEqual
}
- _ => LessThan
+ _ => T::LessThan
}
},
'^' => {
match next {
'=' => {
self.next();
- AssignBitwiseXor
+ T::AssignBitwiseXor
},
- _ => BitwiseXor
+ _ => T::BitwiseXor
}
}
- '\'' | '\"' => String(self.lex_string(char)?),
+ '\'' | '\"' => T::String(self.lex_string(char)?),
'r' => {
match next {
'\'' | '\"' => {
self.next();
- Regex(regex::Regex::new(&self.lex_string(next)?)
+ T::Regex(regex::Regex::new(&self.lex_string(next)?)
.map(|e| e.into())
- .map_err(|e| InvalidRegex(e.into()))?)
+ .map_err(|e| E::InvalidRegex(e.into()))?)
}
_ => {
self.lex_ident(char)?
@@ -634,14 +718,18 @@ impl Lexer {
match self.peek() {
'.' => {
self.next();
- Varadic
+ T::Varadic
},
- _ => Range
+ '=' => {
+ self.next();
+ T::RangeEq
+ },
+ _ => T::Range
}
} else if next.is_digit(10) {
self.lex_number(char)?
} else {
- Access
+ T::Access
}
},
_ => {
@@ -651,6 +739,18 @@ impl Lexer {
self.lex_ident(char)?
}
},
+ };
+
+ 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,
+ str,
+ bidx: byte_start,
+ blen: byte_end - byte_start
})
}
diff --git a/matrix/src/lib.rs b/matrix/src/lib.rs
index fbc1aac..5a9be9f 100644
--- a/matrix/src/lib.rs
+++ b/matrix/src/lib.rs
@@ -1,4 +1,4 @@
-use std::fmt::Display;
+use std::{fmt::Display, rc::Rc};
pub mod compiler;
pub mod value;
@@ -9,8 +9,11 @@ pub mod parse;
pub mod chunk;
pub mod ast;
-#[derive(Debug)]
-pub struct Error(Box<ErrorInner>);
+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 {}
@@ -22,6 +25,8 @@ enum ErrorInner {
Compile(compiler::Error),
Runtime(vm::Error),
External(anyhow::Error),
+ Traced(Box<Error>, String),
+ Any(String),
}
impl Display for crate::Error {
@@ -34,6 +39,8 @@ impl Display for crate::Error {
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}"),
}
}
}
@@ -42,7 +49,7 @@ macro_rules! from_error {
($struct:ty, $tuple:tt) => {
impl From<$struct> for crate::Error {
fn from(value: $struct) -> Self {
- crate::Error(Box::new(ErrorInner::$tuple(value)))
+ crate::Error(Rc::new(ErrorInner::$tuple(value)))
}
}
};
@@ -54,5 +61,62 @@ 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>;
diff --git a/matrix/src/parse.rs b/matrix/src/parse.rs
index f3814fb..d967130 100644
--- a/matrix/src/parse.rs
+++ b/matrix/src/parse.rs
@@ -1,11 +1,10 @@
use std::{fmt::Display, rc::Rc};
use num_complex::Complex64;
-
-use crate::{lex::{Lexer, self, Token}, ast::{Expr, BinaryOp, UnaryOp, optimize}, value::{Value, self}, Result};
+use crate::{lex::{Lexer, self, Token, TokenData, Position}, ast::{Expr, BinaryOp, UnaryOp, optimize, ExprData, AstName}, value::{Value, self}, Result};
use Value as V;
-use Expr as E;
-use Token as T;
+use ExprData as E;
+use TokenData as T;
pub struct ParserBuilder {
optimize: bool
@@ -38,8 +37,7 @@ pub struct Parser {
pub enum Error {
LexerError(crate::lex::Error),
UnexpectedToken(Token),
- ExpectedToken(Token),
- ExpectedTokenName(&'static str),
+ ExpectedToken(TokenData, Position),
MatrixInvDomain(usize, usize, usize),
NotAssignable(Expr),
ValueError(value::Error),
@@ -50,9 +48,8 @@ impl Display for Error {
use Error::*;
match self {
LexerError(err) => write!(f, "{err}"),
- UnexpectedToken(tok) => write!(f, "Unexpected token: '{tok:?}'"),
- ExpectedToken(tok) => write!(f, "Expected token: '{tok:?}'"),
- ExpectedTokenName(name) => write!(f, "Expected {name} token"),
+ 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}"),
@@ -71,13 +68,14 @@ impl std::error::Error for Error {}
macro_rules! expr_parser {
($parser:ident, $pattern:pat, $fn:ident) => {{
let mut expr = $parser.$fn()?;
+ let pos = expr.pos;
loop {
let tok = $parser.lexer.peek_token_nl()?;
- match tok {
+ match tok.data {
$pattern => {
$parser.lexer.next_token_nl()?;
let temp = $parser.$fn()?;
- expr = E::BinaryOp(Box::new(expr), Box::new(temp), BinaryOp::from(tok))
+ expr = (E::BinaryOp(Box::new(expr), Box::new(temp), BinaryOp::from(tok.data)), pos).into()
}
_ => break
}
@@ -90,10 +88,11 @@ macro_rules! expr_parser_reverse {
($parser:ident, $pattern:pat, $fn:ident, $cur:ident) => {{
let expr = $parser.$fn()?;
let tok = $parser.lexer.peek_token_nl()?;
- Ok(match tok {
+ let pos = tok.pos;
+ Ok(match tok.data {
$pattern => {
$parser.lexer.next_token_nl()?;
- E::BinaryOp(Box::new(expr), Box::new($parser.$cur()?), BinaryOp::from(tok))
+ (E::BinaryOp(Box::new(expr), Box::new($parser.$cur()?), BinaryOp::from(tok.data)), pos).into()
}
_ => expr
})
@@ -102,19 +101,19 @@ macro_rules! expr_parser_reverse {
impl Parser {
- fn force_token(&mut self, tok: Token) -> Result<Token> {
+ fn force_token(&mut self, tok: TokenData) -> Result<TokenData> {
let next = self.lexer.next_token()?;
- if next != tok {
- Err(Error::ExpectedToken(tok).into())
+ if next.data != tok {
+ Err(Error::ExpectedToken(tok, next.pos).into())
} else {
Ok(tok)
}
}
- fn force_token_nl(&mut self, tok: Token) -> Result<Token> {
+ fn force_token_nl(&mut self, tok: TokenData) -> Result<TokenData> {
let next = self.lexer.next_token_nl()?;
- if next != tok {
- Err(Error::ExpectedToken(tok).into())
+ if next.data != tok {
+ Err(Error::ExpectedToken(tok, next.pos).into())
} else {
Ok(tok)
}
@@ -124,7 +123,7 @@ impl Parser {
self.force_token(T::LeftParen)?;
let mut params = Vec::new();
loop {
- let expr = match self.lexer.peek_token()? {
+ let expr = match self.lexer.peek_token()?.data {
T::RightParen => {
self.lexer.next_token()?;
break
@@ -133,7 +132,7 @@ impl Parser {
};
params.push(expr);
let next = self.lexer.next_token()?;
- match next {
+ match next.data {
T::Comma => continue,
T::RightParen => break,
_ => return Err(Error::UnexpectedToken(next).into())
@@ -146,7 +145,7 @@ impl Parser {
self.force_token(T::LeftBrack)?;
let mut indicies = Vec::new();
loop {
- let expr = match self.lexer.peek_token()? {
+ let expr = match self.lexer.peek_token()?.data {
T::RightBrack => {
self.lexer.next_token()?;
break
@@ -155,7 +154,7 @@ impl Parser {
};
indicies.push(expr);
let next = self.lexer.next_token()?;
- match next {
+ match next.data {
T::SemiColon => continue,
T::RightBrack => break,
_ => return Err(Error::UnexpectedToken(next).into())
@@ -167,13 +166,13 @@ impl Parser {
fn parse_matrix_part(&mut self) -> Result<Vec<Expr>> {
let mut part = Vec::new();
loop {
- let expr = match self.lexer.peek_token()? {
+ let expr = match self.lexer.peek_token()?.data {
T::SemiColon => break,
T::RightBrack => break,
_ => self.parse_expr()?
};
part.push(expr);
- match self.lexer.peek_token()? {
+ match self.lexer.peek_token()?.data {
T::Comma => {
self.lexer.next_token()?;
},
@@ -184,20 +183,21 @@ impl Parser {
}
fn parse_matrix(&mut self) -> Result<Expr> {
+ let pos = self.lexer.peek_token()?.pos;
self.force_token(T::LeftBrack)?;
let mut parts = Vec::new();
loop {
let part = self.parse_matrix_part()?;
parts.push(part);
let next = self.lexer.next_token()?;
- match next {
+ match next.data {
T::SemiColon => continue,
T::RightBrack => break,
_ => return Err(Error::UnexpectedToken(next).into()),
};
}
if parts.len() == 1 {
- Ok(E::List(parts.pop().unwrap()))
+ Ok((E::List(parts.pop().unwrap()), pos).into())
} else {
let codomain = parts.len();
let domain = parts[0].len();
@@ -211,31 +211,32 @@ impl Parser {
while let Some(part) = parts.pop() {
data.extend(part);
}
- Ok(E::Matrix((domain, codomain, data)))
+ Ok((E::Matrix((domain, codomain, data)), pos).into())
}
}
fn parse_table_key(&mut self) -> Result<Expr> {
let tok = self.lexer.next_token()?;
- Ok(match tok {
+ Ok(match tok.data {
T::LeftBrack => {
let expr = self.parse_expr()?;
self.force_token(T::RightBrack)?;
expr
},
- T::Ident(ident) => E::Literal(V::String(ident.to_string().into())),
- T::String(string) => E::Literal(V::String(string.to_string().into())),
+ 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())
})
}
fn parse_table(&mut self) -> Result<Expr> {
+ let pos = self.lexer.peek_token()?.pos;
self.force_token(T::Colon)?;
self.force_token(T::LeftBrace)?;
let mut table = Vec::new();
- if self.lexer.peek_token()? == T::RightBrace {
+ if self.lexer.peek_token()?.data == T::RightBrace {
self.lexer.next_token()?;
- return Ok(E::Table(table))
+ return Ok((E::Table(table), pos).into())
}
loop {
let key = self.parse_table_key()?;
@@ -243,13 +244,13 @@ impl Parser {
let value = self.parse_expr()?;
table.push((key, value));
let next = self.lexer.next_token()?;
- match next {
+ match next.data {
T::Comma => continue,
T::RightBrace => break,
_ => return Err(Error::UnexpectedToken(next).into())
}
}
- Ok(E::Table(table))
+ Ok((E::Table(table), pos).into())
}
fn parse_paren(&mut self) -> Result<Expr> {
@@ -259,28 +260,26 @@ impl Parser {
Ok(expr)
}
- fn parse_params(&mut self) -> Result<(Vec<Rc<str>>, bool)> {
- use T::*;
+ fn parse_params(&mut self) -> Result<(Vec<(Rc<str>, Position)>, bool)> {
let tok = self.lexer.next_token()?;
- match tok {
- Ident(ident) => {
- let params = vec![ident];
- if self.lexer.peek_token()? == T::Varadic {
+ match tok.data {
+ T::Ident(ident) => {
+ let params = vec![(ident, tok.pos)];
+ if self.lexer.peek_token()?.data == T::Varadic {
self.lexer.next_token()?;
return Ok((params, true))
} else {
return Ok((params, false))
}
}
- LeftParen => (),
+ T::LeftParen => (),
_ => return Err(Error::UnexpectedToken(tok).into()),
}
let mut params = Vec::new();
let mut varadic = false;
- if self.lexer.peek_token()? == T::RightParen {
- self.lexer.next_token()?;
+ if self.lexer.peek_token()?.data == T::RightParen {
return Ok((params, varadic));
}
@@ -288,14 +287,14 @@ impl Parser {
let ident = self.parse_ident()?;
params.push(ident);
let next = self.lexer.next_token()?;
- match next {
- Varadic => {
+ match next.data {
+ T::Varadic => {
varadic = true;
self.force_token(T::RightParen)?;
break;
}
- Comma => continue,
- RightParen => break,
+ T::Comma => continue,
+ T::RightParen => break,
_ => return Err(Error::UnexpectedToken(next).into()),
}
}
@@ -303,103 +302,135 @@ impl Parser {
Ok((params, varadic))
}
- fn parse_ident(&mut self) -> Result<Rc<str>> {
- if let T::Ident(ident) = self.lexer.next_token()? {
- Ok(ident)
+ fn parse_ident(&mut self) -> Result<AstName> {
+ let next = self.lexer.next_token()?;
+ if let T::Ident(ident) = next.data {
+ Ok((ident, next.pos))
} else {
- Err(Error::ExpectedTokenName("Ident").into())
+ Err(Error::UnexpectedToken(next).into())
}
}
- fn parse_ident_nl(&mut self) -> Result<Rc<str>> {
- if let T::Ident(ident) = self.lexer.next_token_nl()? {
+ fn parse_wrapped_ident(&mut self) -> Result<AstName> {
+ if self.lexer.peek_token()?.data == T::LeftParen {
+ self.lexer.next_token()?;
+ let ident = self.parse_ident()?;
+ self.force_token(T::RightParen)?;
Ok(ident)
} else {
- Err(Error::ExpectedTokenName("Ident").into())
+ self.parse_ident()
+ }
+ }
+
+ fn parse_ident_nl(&mut self) -> Result<AstName> {
+ let next = self.lexer.next_token_nl()?;
+ if let T::Ident(ident) = next.data {
+ Ok((ident, next.pos))
+ } else {
+ Err(Error::UnexpectedToken(next).into())
}
}
fn parse_function(&mut self) -> Result<Expr> {
+ let pos = self.lexer.peek_token()?.pos;
self.force_token(T::Function)?;
let ident = self.parse_ident()?;
- let (params, varadic) = match self.lexer.peek_token()? {
+ let (params, varadic) = match self.lexer.peek_token()?.data {
T::LeftBrace => (vec![], false),
_ => self.parse_params()?,
};
let expr = self.parse_expr()?;
- Ok(E::Function(ident, params, Box::new(expr), varadic))
+ Ok((E::Function(ident, params, Box::new(expr), varadic), pos).into())
}
fn parse_lambda(&mut self) -> Result<Expr> {
+ let pos = self.lexer.peek_token()?.pos;
self.force_token(T::Backslash)?;
- let (params, varadic) = match self.lexer.peek_token()? {
+ let (params, varadic) = match self.lexer.peek_token()?.data {
T::Arrow => (vec![], false),
_ => self.parse_params()?,
};
self.force_token(T::Arrow)?;
let expr = self.parse_expr()?;
- Ok(E::Lambda(params, Box::new(expr), varadic))
+ Ok((E::Lambda(params, Box::new(expr), varadic), pos).into())
}
fn parse_do_while(&mut self) -> Result<Expr> {
+ let pos = self.lexer.peek_token()?.pos;
self.force_token(T::Do)?;
let expr = Box::new(self.parse_expr()?);
self.force_token(T::While)?;
let cond = Box::new(self.parse_expr()?);
- Ok(E::DoWhile(expr, cond))
+ Ok((E::DoWhile(expr, cond), pos).into())
}
fn parse_while(&mut self) -> Result<Expr> {
+ let pos = self.lexer.peek_token()?.pos;
self.force_token(T::While)?;
let cond = Box::new(self.parse_expr()?);
let expr = Box::new(self.parse_expr()?);
- Ok(E::While(cond, expr))
+ Ok((E::While(cond, expr), pos).into())
}
fn parse_loop(&mut self) -> Result<Expr> {
+ let pos = self.lexer.peek_token()?.pos;
self.force_token(T::Loop)?;
let expr = self.parse_expr()?;
- Ok(E::Loop(Box::new(expr)))
+ Ok((E::Loop(Box::new(expr)), pos).into())
+ }
+
+ fn parse_for(&mut self) -> Result<Expr> {
+ let pos = self.lexer.peek_token()?.pos;
+ self.force_token(T::For)?;
+ let name = self.parse_ident()?;
+ self.force_token(T::In)?;
+ let cond = Box::new(self.parse_expr()?);
+ let expr = Box::new(self.parse_expr()?);
+ Ok((E::For(name, cond, expr), pos).into())
}
fn parse_if(&mut self) -> Result<Expr> {
+ let pos = self.lexer.peek_token()?.pos;
self.force_token(T::If)?;
let cond = Box::new(self.parse_expr()?);
let expr = Box::new(self.parse_expr()?);
- if self.lexer.peek_token()? != T::Else {
- return Ok(E::If(cond, expr, None))
+ if self.lexer.peek_token()?.data != T::Else {
+ return Ok((E::If(cond, expr, None), pos).into())
}
self.lexer.next_token()?;
- if self.lexer.peek_token()? == T::If {
- Ok(E::If(cond, expr, Some(Box::new(self.parse_if()?))))
+ if self.lexer.peek_token()?.data == T::If {
+ Ok((E::If(cond, expr, Some(Box::new(self.parse_if()?))), pos).into())
} else {
- Ok(E::If(cond, expr, Some(Box::new(self.parse_expr()?))))
+ Ok((E::If(cond, expr, Some(Box::new(self.parse_expr()?))), pos).into())
}
}
fn parse_let(&mut self) -> Result<Expr> {
+ let pos = self.lexer.peek_token()?.pos;
self.force_token(T::Let)?;
let ident = self.parse_ident_nl()?;
- if self.lexer.peek_token_nl()? == T::Assign {
+ if self.lexer.peek_token_nl()?.data == T::Assign {
self.force_token_nl(T::Assign)?;
- Ok(E::Let(ident, Box::new(self.parse_expr()?)))
+ Ok((E::Let(ident, Box::new(self.parse_expr()?)), pos).into())
} else {
- Ok(E::Let(ident, Box::new(E::Literal(V::Nil))))
+ Ok((E::Let(ident, Box::new((E::Literal(V::Nil), pos).into())), pos).into())
}
}
fn parse_return(&mut self) -> Result<Expr> {
+ let pos = self.lexer.peek_token()?.pos;
self.force_token(T::Return)?;
- Ok(E::Return(Box::new(self.parse_expr()?)))
+ Ok((E::Return(Box::new(self.parse_expr()?)), pos).into())
}
fn parse_block(&mut self) -> Result<Expr> {
let mut block = Vec::new();
+ let pos = self.lexer.peek_token()?.pos;
self.force_token(T::LeftBrace)?;
loop {
- let expr = match self.lexer.peek_token()? {
+ let expr = match self.lexer.peek_token()?.data {
T::RightBrace => break,
T::SemiColon => {
self.lexer.next_token()?;
@@ -408,22 +439,32 @@ impl Parser {
_ => self.parse_expr()?
};
block.push(expr);
- let next = self.lexer.next_token()?;
- match next {
+ let next = self.lexer.next_token_nl()?;
+ match next.data {
T::SemiColon => continue,
T::RightBrace => break,
- _ => return Err(Error::ExpectedToken(T::SemiColon).into())
+ _ => return Err(Error::ExpectedToken(T::SemiColon, next.pos).into())
}
}
- if self.lexer.peek_token()? == T::RightBrace {
+ if self.lexer.peek_token()?.data == T::RightBrace {
self.lexer.next_token()?;
}
- Ok(E::Block(block))
+ Ok((E::Block(block), pos).into())
+ }
+
+ fn parse_try(&mut self) -> Result<Expr> {
+ let pos = self.lexer.peek_token()?.pos;
+ self.force_token(T::Try)?;
+ let expr = self.parse_expr()?;
+ self.force_token(T::Catch)?;
+ let ident = self.parse_wrapped_ident()?;
+ let catch = self.parse_expr()?;
+ Ok((E::Try(Box::new(expr), ident, Box::new(catch)), pos).into())
}
fn parse_value(&mut self) -> Result<Expr> {
let tok = self.lexer.next_token()?;
- Ok(match tok {
+ let data = match tok.data {
T::Nil => E::Literal(V::Nil),
T::Int(i) => E::Literal(V::Int(i)),
T::Float(f) => E::Literal(V::Float(f)),
@@ -434,28 +475,31 @@ impl Parser {
T::False => E::Literal(V::Bool(false)),
T::Ident(ident) => E::Ident(ident),
_ => return Err(Error::UnexpectedToken(tok).into()),
- })
+ };
+ Ok((data, tok.pos).into())
}
fn parse_term(&mut self) -> Result<Expr> {
use T::*;
- match self.lexer.peek_token()? {
+ match self.lexer.peek_token()?.data {
Function => self.parse_function(),
Backslash => self.parse_lambda(),
Do => self.parse_do_while(),
While => self.parse_while(),
+ For => self.parse_for(),
Let => self.parse_let(),
LeftBrace => self.parse_block(),
Return => self.parse_return(),
If => self.parse_if(),
Loop => self.parse_loop(),
+ Try => self.parse_try(),
Break => {
- self.lexer.next_token()?;
- Ok(E::Break)
+ let next = self.lexer.next_token()?;
+ Ok((E::Break, next.pos).into())
},
Continue => {
- self.lexer.next_token()?;
- Ok(E::Continue)
+ let next = self.lexer.next_token()?;
+ Ok((E::Continue, next.pos).into())
},
LeftBrack => self.parse_matrix(),
Colon => self.parse_table(),
@@ -466,13 +510,14 @@ impl Parser {
fn parse_expr_expr_access(&mut self) -> Result<Expr> {
let mut expr = self.parse_term()?;
+ let pos = expr.pos;
loop {
let tok = self.lexer.peek_token()?;
- match tok {
+ match tok.data {
T::Access => {
self.force_token(T::Access)?;
let temp = self.parse_ident()?;
- expr = E::FieldAccess(Box::new(expr), temp);
+ expr = (E::FieldAccess(Box::new(expr), temp), pos).into();
},
_ => break
}
@@ -482,16 +527,17 @@ impl Parser {
fn parse_expr_call(&mut self) -> Result<Expr> {
let mut expr = self.parse_expr_expr_access()?;
+ let pos = expr.pos;
loop {
let tok = self.lexer.peek_token()?;
- match tok {
+ match tok.data {
T::LeftBrack => {
let index = self.parse_index()?;
- expr = E::Index(Box::new(expr), index);
+ expr = (E::Index(Box::new(expr), index), pos).into();
},
T::LeftParen => {
let params = self.parse_fn_call()?;
- expr = E::FnCall(Box::new(expr), params);
+ expr = (E::FnCall(Box::new(expr), params), pos).into();
}
_ => break
}
@@ -501,14 +547,14 @@ impl Parser {
fn parse_expr_unary(&mut self) -> Result<Expr> {
let tok = self.lexer.peek_token_nl()?;
- Ok(match tok {
+ Ok(match tok.data {
T::Not => {
self.lexer.next_token()?;
- E::UnaryOp(Box::new(self.parse_expr_unary()?), UnaryOp::Not)
+ (E::UnaryOp(Box::new(self.parse_expr_unary()?), UnaryOp::Not), tok.pos).into()
}
T::Subtract => {
self.lexer.next_token()?;
- E::UnaryOp(Box::new(self.parse_expr_unary()?), UnaryOp::Negate)
+ (E::UnaryOp(Box::new(self.parse_expr_unary()?), UnaryOp::Negate), tok.pos).into()
}
_ => self.parse_expr_call()?
})
@@ -559,13 +605,14 @@ impl Parser {
fn parse_expr_and(&mut self) -> Result<Expr> {
let mut expr = self.parse_expr_compare()?;
+ let pos = expr.pos;
loop {
let tok = self.lexer.peek_token()?;
- match tok {
+ match tok.data {
T::And => {
self.force_token(T::And)?;
let temp = self.parse_expr_compare()?;
- expr = E::And(Box::new(expr), Box::new(temp));
+ expr = (E::And(Box::new(expr), Box::new(temp)), pos).into();
},
_ => break
}
@@ -575,13 +622,14 @@ impl Parser {
fn parse_expr_or(&mut self) -> Result<Expr> {
let mut expr = self.parse_expr_and()?;
+ let pos = expr.pos;
loop {
let tok = self.lexer.peek_token()?;
- match tok {
+ match tok.data {
T::Or => {
self.force_token(T::Or)?;
let temp = self.parse_expr_and()?;
- expr = E::Or(Box::new(expr), Box::new(temp));
+ expr = (E::Or(Box::new(expr), Box::new(temp)), pos).into();
},
_ => break
}
@@ -589,75 +637,112 @@ impl Parser {
Ok(expr)
}
- fn parse_expr(&mut self) -> Result<Expr> {
- use BinaryOp as B;
+ fn parse_expr_range(&mut self) -> Result<Expr> {
let expr = self.parse_expr_or()?;
+ let pos = expr.pos;
+ match self.lexer.peek_token()?.data {
+ T::Range => {
+ self.lexer.next_token()?;
+ let temp = self.parse_expr_or()?;
+ Ok((E::BinaryOp(Box::new(expr), Box::new(temp), BinaryOp::Range), pos).into())
+ },
+ T::RangeEq => {
+ self.lexer.next_token()?;
+ let temp = self.parse_expr_or()?;
+ Ok((E::BinaryOp(Box::new(expr), Box::new(temp), BinaryOp::RangeEq), pos).into())
+ },
+ _ => Ok(expr)
+ }
+ }
+
+ fn parse_expr_op_assign(&mut self) -> Result<Expr> {
+ use BinaryOp as B;
+ let expr = self.parse_expr_range()?;
let tok = self.lexer.peek_token_nl()?;
- Ok(match tok {
+ let pos = tok.pos;
+ let data: ExprData = match tok.data {
T::Assign => {
self.lexer.next_token_nl()?;
E::Assign(Box::new(expr.clone()),Box::new(self.parse_expr()?))
},
T::AssignAnd => {
self.lexer.next_token_nl()?;
- E::Assign(Box::new(expr.clone()),Box::new(E::And(Box::new(expr), Box::new(self.parse_expr()?))))
+ E::Assign(Box::new(expr.clone()),Box::new((E::And(Box::new(expr), Box::new(self.parse_expr()?)), pos).into()))
},
T::AssignOr => {
self.lexer.next_token_nl()?;
- E::Assign(Box::new(expr.clone()),Box::new(E::Or(Box::new(expr), Box::new(self.parse_expr()?))))
+ E::Assign(Box::new(expr.clone()),Box::new((E::Or(Box::new(expr), Box::new(self.parse_expr()?)),pos).into()))
},
T::AssignAdd => {
self.lexer.next_token_nl()?;
- E::Assign(Box::new(expr.clone()),Box::new(E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::Add)))
+ E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::Add),pos).into()))
},
T::AssignSubtract => {
self.lexer.next_token_nl()?;
- E::Assign(Box::new(expr.clone()),Box::new(E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::Subtract)))
+ E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::Subtract),pos).into()))
},
T::AssignMultiply => {
self.lexer.next_token_nl()?;
- E::Assign(Box::new(expr.clone()),Box::new(E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::Multiply)))
+ E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::Multiply),pos).into()))
},
T::AssignDivide => {
self.lexer.next_token_nl()?;
- E::Assign(Box::new(expr.clone()),Box::new(E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::Divide)))
+ E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::Divide),pos).into()))
},
T::AssignModulo => {
self.lexer.next_token_nl()?;
- E::Assign(Box::new(expr.clone()),Box::new(E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::Modulo)))
+ E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::Modulo),pos).into()))
},
T::AssignPower => {
self.lexer.next_token_nl()?;
- E::Assign(Box::new(expr.clone()),Box::new(E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::Power)))
+ E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::Power),pos).into()))
},
T::AssignBitwiseAnd => {
self.lexer.next_token_nl()?;
- E::Assign(Box::new(expr.clone()),Box::new(E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::BitwiseAnd)))
+ E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::BitwiseAnd),pos).into()))
},
T::AssignBitwiseOr => {
self.lexer.next_token_nl()?;
- E::Assign(Box::new(expr.clone()),Box::new(E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::BitwiseOr)))
+ E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::BitwiseOr),pos).into()))
},
T::AssignBitwiseXor => {
self.lexer.next_token_nl()?;
- E::Assign(Box::new(expr.clone()),Box::new(E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::BitwiseXor)))
+ E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::BitwiseXor),pos).into()))
},
T::AssignBitwiseShiftLeft => {
self.lexer.next_token_nl()?;
- E::Assign(Box::new(expr.clone()),Box::new(E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::BitwiseShiftLeft)))
+ E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::BitwiseShiftLeft),pos).into()))
},
T::AssignBitwiseShiftRight => {
self.lexer.next_token_nl()?;
- E::Assign(Box::new(expr.clone()),Box::new(E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::BitwiseShiftRight)))
+ E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::BitwiseShiftRight),pos).into()))
},
- _ => expr
- })
+ _ => expr.data
+ };
+ Ok((data, pos).into())
+ }
+
+ fn parse_expr(&mut self) -> Result<Expr> {
+ let mut expr = self.parse_expr_op_assign()?;
+ let pos = expr.pos;
+ loop {
+ let tok = self.lexer.peek_token()?;
+ match tok.data {
+ T::Pipe => {
+ self.force_token(T::Pipe)?;
+ let temp = self.parse_expr_op_assign()?;
+ expr = (E::Pipeline(Box::new(expr), Box::new(temp)), pos).into();
+ },
+ _ => break
+ }
+ }
+ Ok(expr)
}
fn parse_root(&mut self) -> Result<Expr> {
let mut block = Vec::new();
loop {
- match self.lexer.peek_token()? {
+ match self.lexer.peek_token()?.data {
T::Eof => break,
T::SemiColon => {
self.lexer.next_token()?;
@@ -667,13 +752,14 @@ impl Parser {
};
let expr = self.parse_expr()?;
block.push(expr);
- match self.lexer.next_token()? {
+ let next = self.lexer.next_token()?;
+ match next.data {
T::Eof => break,
T::SemiColon => continue,
- _ => return Err(Error::ExpectedToken(T::SemiColon).into())
+ _ => return Err(Error::ExpectedToken(T::SemiColon, next.pos).into())
};
}
- Ok(E::Block(block))
+ Ok((E::Block(block), Position::default()).into())
}
pub fn parse<T: Into<String>>(&mut self, into: T) -> Result<Expr> {
diff --git a/matrix/src/value.rs b/matrix/src/value.rs
index 2c86226..3ebc48a 100644
--- a/matrix/src/value.rs
+++ b/matrix/src/value.rs
@@ -1,5 +1,14 @@
-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};
+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;
@@ -29,67 +38,29 @@ impl ValueMap {
self.0.insert(key, value);
Ok(())
}
+ pub fn len(&self) -> usize {
+ self.0.len()
+ }
}
#[derive(Debug)]
-pub enum Error {
- Neg(Value),
- Add(Value, Value),
- Subtract(Value, Value),
- Multiply(Value, Value),
- Divide(Value, Value),
- Modulo(Value, Value),
- Exponent(Value, Value),
- Compare(Value, Value),
- IndexOutOfBounds(i64, usize),
- MatrixOutOfBounds(usize, usize, usize, usize),
- MatrixIndexOutOfBounds(i64, usize, usize),
- MatrixInvSlice(usize, usize),
- MatrixMultiply(usize, usize),
- MatrixAdd(usize, usize, usize, usize),
- CannotIndex(Value),
- BadIndex(Value, Value),
- Concat(Value, Value),
- Bitwise(Value, Value),
- DivideByZero,
- ZeroExpZero,
- CannotHash(Value),
- FieldAccess(Value),
-}
+pub struct Error(String);
impl Display for self::Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- use self::Error::*;
- match self {
- Neg(v) => write!(f, "cannot negate {v:?}"),
- Add(a, b) => write!(f, "cannot add {a:?} to {b:?}"),
- Subtract(a, b) => write!(f, "cannot subtract {a:?} from {b:?}"),
- Multiply(a, b) => write!(f, "cannot multiply {a:?} and {b:?}"),
- Divide(a, b) => write!(f, "cannot divide {a:?} and {b:?}"),
- Modulo(a, b) => write!(f, "cannot modulo {a:?} and {b:?}"),
- Exponent(a, b) => write!(f, "cannot compute {a:?} pow {b:?}"),
- Compare(a, b) => write!(f, "cannot compare {a:?} and {b:?}"),
- IndexOutOfBounds(a, b) => write!(f, "index {a} out of bounds for list of length {b}"),
- MatrixOutOfBounds(a, b, c, d) => write!(f, "row col ({a},{b}) is out of bounds for matrix {c}x{d}"),
- MatrixIndexOutOfBounds(i, d, c) => write!(f, "row index {i} is out of bounds for matrix {d}x{c}"),
- MatrixMultiply(c, d) => write!(f, "cannot multiply a matrix with the domain of {d} and matrix with the codomain of {c}"),
- MatrixAdd(a, b, c, d) => write!(f, "cannot add a {a}x{b} matrix and a {c}x{d} matrix"),
- MatrixInvSlice(s, e) => write!(f, "invalid matrix slice {s}..{e}"),
- BadIndex(a, b) => write!(f, "cannot index {a:?} with {b:?}"),
- CannotIndex(a) => write!(f, "cannot index {a:?}"),
- Concat(a, b) => write!(f, "cannot concat {a:?} and {b:?}"),
- Bitwise(a, b) => write!(f, "cannot bitwise between {a:?} and {b:?}"),
- DivideByZero => write!(f, "attempted to divide by zero"),
- ZeroExpZero => write!(f, "cannot exponent zero with zero"),
- CannotHash(v) => write!(f, "cannot hash {v:?}"),
- FieldAccess(a) => write!(f, "{a:?} cannot be field accessed"),
- }
+ write!(f, "{}", self.0)
}
}
impl std::error::Error for self::Error {}
-#[derive(Debug, Clone)]
+macro_rules! error {
+ ($($arg:tt)*) => {
+ Err(self::Error(format!($($arg)*)).into())
+ };
+}
+
+#[derive(Clone)]
pub enum Value {
Nil,
Bool(bool),
@@ -98,11 +69,21 @@ pub enum Value {
Ratio(Rational64),
Complex(Complex64),
Regex(Rc<Regex>),
- String(Rc<String>),
+ String(Rc<str>),
List(Gc<List>),
Matrix(Gc<Matrix>),
Table(Gc<Table>),
Function(Rc<Function>),
+ Range(Rc<(i64, i64, bool)>),
+ Iter(Rc<Function>),
+ File(Rc<RefCell<File>>),
+ Error(crate::Error),
+}
+
+impl From<&str> for Value {
+ fn from(value: &str) -> Self {
+ Value::String(value.into())
+ }
}
impl Hash for Value {
@@ -132,145 +113,175 @@ impl Hash for Value {
}
}
-impl Value {
- pub fn can_hash(&self) -> Result<()> {
- use Value::*;
+impl Debug for Value {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ use Value as V;
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 Err(Error::CannotHash(self.clone()).into())
+ 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]"),
}
- Ok(())
}
+}
+
+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 = "";
+ }
- pub fn boring_print(&self) -> String {
- use Value::*;
match self {
- Nil => format!("nil"),
- Bool(b) => format!("{b}"),
- Int(i) => format!("{i}"),
- Float(l) => format!("{l}"),
- Ratio(r) => format!("{r}"),
- Complex(c) => format!("{c}"),
- Regex(r) => format!("/{r}/"),
- String(s) => format!("{s}"),
- List(l) => {
- let mut str = "[".to_string();
+ 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 {
- str.push_str(" ");
+ write!(f, " ")?;
+ }
+ if f.alternate() {
+ write!(f, "{el:#}")?;
+ } else {
+ write!(f, "{el}")?;
}
- str.push_str(&el.boring_print());
}
- str.push_str("]");
- str
+ write!(f, "]")?;
},
- Matrix(m) => {
- let mut str = "\n".to_string();
+ V::Matrix(m) => {
+ write!(f, "\n")?;
for row in m.rows() {
- str.push_str(" ");
- for (i, v) in row.into_iter().enumerate() {
+ write!(f, " ")?;
+ for (i, el) in row.into_iter().enumerate() {
if i != 0 {
- str.push(' ');
+ write!(f, " ")?;
+ }
+ if f.alternate() {
+ write!(f, "{el:#}")?;
+ } else {
+ write!(f, "{el}")?;
}
- str.push_str(&v.boring_print());
}
- str.push('\n');
+ write!(f, "\n")?;
}
- str
},
- Table(t) => {
- let mut str = "{".to_string();
+ V::Table(t) => {
+ write!(f, "{{")?;
for (i, (key, val)) in t.0.iter().enumerate() {
if i != 0 {
- str.push_str(", ");
+ write!(f, ", ")?;
+ }
+ if f.alternate() {
+ write!(f, "{key:#} = {val:#}")?;
+ } else {
+ write!(f, "{key} = {val}")?;
}
- str.push_str(&key.boring_print());
- str.push_str(" = ");
- str.push_str(&val.boring_print());
}
- str.push_str("}");
- str
+ write!(f, "}}")?;
},
- Function(fun) => {
- format!("{fun:?}")
+ 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(())
}
+}
- pub fn pretty_print(&self) -> String {
+impl Value {
+ pub fn can_hash(&self) -> Result<()> {
use Value::*;
match self {
- Nil => format!("\x1b[34m{}\x1b[0m", self.boring_print()),
- Bool(_) => format!("\x1b[33m{}\x1b[0m", self.boring_print()),
- Int(_) => format!("\x1b[33m{}\x1b[0m", self.boring_print()),
- Float(_) => format!("\x1b[33m{}\x1b[0m", self.boring_print()),
- Ratio(_) => format!("\x1b[33m{}\x1b[0m", self.boring_print()),
- Complex(_) => format!("\x1b[33m{}\x1b[0m", self.boring_print()),
- Regex(_) => format!("\x1b[31m{}\x1b[0m", self.boring_print()),
- String(_) => format!("\x1b[32m'{}'\x1b[0m", self.boring_print()),
+ Nil => {},
+ Bool(_) => {},
+ Int(_) => {},
+ Ratio(_) => {},
+ Regex(_) => {},
+ String(_) => {}
List(l) => {
- let mut str = "[".to_string();
- for (i, el) in l.iter().enumerate() {
- if i != 0 {
- str.push_str(" ");
- }
- str.push_str(&el.pretty_print());
+ for val in l.iter() {
+ val.can_hash()?;
}
- str.push_str("]");
- str
- },
+ }
Matrix(m) => {
- let mut str = "\n".to_string();
- for row in m.rows() {
- str.push_str(" ");
- for (i, v) in row.into_iter().enumerate() {
- if i != 0 {
- str.push(' ');
- }
- str.push_str(&v.pretty_print());
- }
- str.push('\n');
- }
- str
- },
- Table(t) => {
- let mut str = "{".to_string();
- for (i, (key, val)) in t.0.iter().enumerate() {
- if i != 0 {
- str.push_str(", ");
- }
- str.push_str(&key.pretty_print());
- str.push_str(" = ");
- str.push_str(&val.pretty_print());
+ for val in m.values.iter() {
+ val.can_hash()?;
}
- str.push_str("}");
- str
- },
- Function(_) => {
- format!("\x1b[36m{}\x1b[0m", self.boring_print())
},
+ _ => return error!("cannot hash {self:?}")
}
+ Ok(())
}
-}
-impl Display for Value {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "{}", self.pretty_print())
+ 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,
+ }
}
}
@@ -278,6 +289,17 @@ fn ratio_to_f64(r: Rational64) -> f64 {
*r.numer() as f64 / *r.denom() as f64
}
+impl Value {
+ pub fn promote_trig(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
+ }
+ }
+}
+
fn promote(a: Value, b: Value) -> (Value, Value) {
use Value as V;
match (&a, &b) {
@@ -293,9 +315,12 @@ fn promote(a: Value, b: Value) -> (Value, Value) {
(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)) => (V::Matrix(Matrix::from_list(l1.to_vec()).into()), V::Matrix(Matrix::from_list(l2.to_vec()).into())),
- (_, V::List(l)) => (a, V::Matrix(Matrix::from_list(l.to_vec()).into())),
- (V::List(l), _) => (V::Matrix(Matrix::from_list(l.to_vec()).into()), b),
+ (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),
}
}
@@ -310,17 +335,19 @@ impl Add for Value {
(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())),
- (String(str), value) => Ok(String(Rc::new(
- format!("{str}{}", value.boring_print())
+ (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::new(
- format!("{}{str}", value.boring_print())
+ (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::Add(l, r).into())
+ (l, r) => error!("cannot add: {l:?} + {r:?}")
}
}
}
@@ -335,7 +362,8 @@ impl Sub for Value {
(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())),
- (l, r) => Err(Error::Subtract(l, r).into())
+ (Matrix(x), r) => Ok(Matrix(x.decrement(r)?.into())),
+ (l, r) => error!("cannot subtract: {l:?} - {r:?}")
}
}
}
@@ -352,7 +380,7 @@ impl Mul for Value {
(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::Multiply(l, r).into())
+ (l, r) => error!("cannot multiply: {l:?} * {r:?}")
}
}
}
@@ -362,13 +390,13 @@ impl Div for Value {
fn div(self, rhs: Value) -> Self::Output {
use Value::*;
match promote(self, rhs) {
- (Int(_), Int(0)) => Err(Error::DivideByZero.into()),
+ (Int(_), Int(0)) => 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::Divide(l, r).into())
- }
+ (l, r) => error!("cannot divide: {l:?} / {r:?}")
+ }
}
}
@@ -378,7 +406,7 @@ impl BitOr for Value {
use Value::*;
match promote(self, rhs) {
(Int(x), Int(y)) => Ok(Int(x | y)),
- (l, r) => Err(Error::Bitwise(l, r).into())
+ (l, r) => error!("cannot bitwise or: {l:?} | {r:?}")
}
}
}
@@ -389,7 +417,7 @@ impl BitAnd for Value {
use Value::*;
match promote(self, rhs) {
(Int(x), Int(y)) => Ok(Int(x & y)),
- (l, r) => Err(Error::Bitwise(l, r).into())
+ (l, r) => error!("cannot bitwise and: {l:?} & {r:?}")
}
}
}
@@ -400,7 +428,7 @@ impl BitXor for Value {
use Value::*;
match promote(self, rhs) {
(Int(x), Int(y)) => Ok(Int(x ^ y)),
- (l, r) => Err(Error::Bitwise(l, r).into())
+ (l, r) => error!("cannot bitwise xor: {l:?} ^ {r:?}")
}
}
}
@@ -411,7 +439,7 @@ impl Shl for Value {
use Value::*;
match promote(self, rhs) {
(Int(x), Int(y)) => Ok(Int(x << y)),
- (l, r) => Err(Error::Bitwise(l, r).into())
+ (l, r) => error!("cannot bitwise shift left: {l:?} << {r:?}")
}
}
}
@@ -422,14 +450,14 @@ impl Shr for Value {
use Value::*;
match promote(self, rhs) {
(Int(x), Int(y)) => Ok(Int(x >> y)),
- (l, r) => Err(Error::Bitwise(l, r).into())
+ (l, r) => error!("cannot bitwise shift right: {l:?} >> {r:?}")
}
}
}
fn ipow(n: i64, d: i64, p: i64) -> Result<(i64, i64)> {
Ok(match (n, d, p) {
- (0, _, 0) => return Err(Error::ZeroExpZero.into()),
+ (0, _, 0) => return error!("cannot exponent: 0 ** 0"),
(0, _, _) => (0, 1),
(_, _, 0) => (1, 1),
(1, 1, _) => (1, 1),
@@ -447,7 +475,7 @@ impl Value {
(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::Modulo(r, l).into())
+ (l, r) => error!("cannot modulo: {l:?} % {r:?}")
}
}
@@ -461,7 +489,7 @@ impl Value {
(Float(x), Float(y)) => Ok(Float(x.powf(y))),
(Ratio(x), Ratio(y)) => Ok(Float(ratio_to_f64(x).powf(ratio_to_f64(y)))),
(Complex(x), Complex(y)) => Ok(Complex(x.powc(y))),
- (l, r) => Err(Error::Exponent(l, r).into())
+ (l, r) => error!("cannot exponent: {l:?} ** {r:?}")
}
}
@@ -534,7 +562,7 @@ impl PartialOrd for Value {
impl Value {
pub fn val_cmp(&self, other: &Self) -> Result<Ordering> {
self.partial_cmp(other)
- .map_or_else(|| Err(Error::Compare(self.clone(), other.clone()).into()), Ok)
+ .map_or_else(|| error!("cannot compare: {self:?} and {other:?}"), Ok)
}
fn index_single(&self, index: &Value) -> Result<Self> {
@@ -545,17 +573,17 @@ impl Value {
},
(V::List(l), V::Int(i)) => {
if *i < 0 || *i as usize >= l.len() {
- return Err(Error::IndexOutOfBounds(*i, l.len()).into())
+ 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 Err(Error::MatrixIndexOutOfBounds(*i, m.domain, m.codomain).into())
+ return error!("{i} out of bounds for {self:?}")
}
Ok(m.values[*i as usize].clone())
},
- _ => return Err(self::Error::BadIndex(self.clone(), index.clone()).into())
+ _ => return error!("{index:?} cant index {self:?}")
}
}
@@ -579,7 +607,7 @@ impl Value {
Ok(V::Table(ret.into()))
}
V::Matrix(m) => {
- let err = || Err(self::Error::BadIndex(self.clone(), V::List(indexes.clone().into())).into());
+ let err = || error!("{self:?} can be index by [Int] or [Int;Int]");
if indexes.len() != 2 {
return err()
}
@@ -612,7 +640,7 @@ impl Value {
_ => return err()
}
}
- _ => return Err(self::Error::CannotIndex(self.clone()).into())
+ _ => return error!("cannot index {self:?}")
}
}
@@ -628,26 +656,26 @@ impl Value {
fn store_index_single(&mut self, index: &Value, store: Value) -> Result<()> {
use Value as V;
- let err = self.clone();
+ 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 Err(Error::IndexOutOfBounds(*i, l.len()).into())
+ 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 Err(Error::MatrixIndexOutOfBounds(*i, m.domain, m.codomain).into())
+ return error!("{i} out of bounds for {err}")
}
m.values[*i as usize] = store;
Ok(())
},
- _ => return Err(self::Error::BadIndex(err, index.clone()).into())
+ _ => return error!("{index:?} cant index {err}")
}
}
@@ -666,7 +694,7 @@ impl Value {
}
Ok(())
}
- _ => return Err(self::Error::CannotIndex(self.clone()).into())
+ _ => return error!("cannot index {self:?}")
}
}
@@ -684,10 +712,10 @@ impl Value {
use Value as V;
match self {
V::Table(t) => {
- let key = V::String(Rc::new(ident.to_string()));
+ let key = V::String(Rc::from(ident));
Ok(t.insert(key, val)?)
},
- _ => return Err(self::Error::FieldAccess(self.clone()).into())
+ _ => return error!("cannot field access assign {self:?}")
}
}
@@ -695,10 +723,10 @@ impl Value {
use Value as V;
match self {
V::Table(t) => {
- let key = V::String(Rc::new(ident.to_string()));
+ let key = V::String(Rc::from(ident));
Ok(t.get(&key)?.unwrap_or(&V::Nil).clone())
},
- _ => return Err(self::Error::FieldAccess(self.clone()).into())
+ _ => return error!("cannot field access {self:?}")
}
}
@@ -722,6 +750,15 @@ impl Value {
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))))
+ },
}
}
@@ -754,24 +791,75 @@ impl Not for Value {
type Output = bool;
fn not(self) -> Self::Output {
- use Value::*;
+ use Value as V;
match self {
- Nil => true,
- Bool(b) => !b,
- Int(i) => i == 0,
- Float(f) => f == 0.0,
- Ratio(r) => *(r.numer()) == 0 || *(r.denom()) == 0,
- Complex(c) => !c.is_normal(),
- Regex(_) => false,
- List(_) => false,
- Matrix(_) => false,
- Table(_) => false,
- String(s) => s.as_str() == "",
- Function(_) => false,
+ 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();
@@ -830,9 +918,9 @@ impl Matrix {
pub fn get(&self, row: usize, col: usize) -> Result<Value> {
if row >= self.codomain || col >= self.domain {
- return Err(self::Error::MatrixOutOfBounds(row, col, self.domain, self.codomain).into())
+ return error!("[{};{}] out of bounds for [Matrix {}x{}]", row, col, self.domain, self.codomain)
}
- let idx = col + row * self.codomain;
+ let idx = col + row * self.domain;
Ok(self.values[idx].clone())
}
@@ -840,9 +928,9 @@ impl Matrix {
pub fn set(&mut self, row: usize, col: usize, val: Value) -> Result<()> {
if row >= self.codomain || col >= self.domain {
- return Err(self::Error::MatrixOutOfBounds(row, col, self.domain, self.codomain).into())
+ return error!("[{};{}] out of bounds for [Matrix {}x{}]", row, col, self.domain, self.codomain)
}
- let idx = col + row * self.codomain;
+ let idx = col + row * self.domain;
self.values[idx] = val;
Ok(())
}
@@ -863,8 +951,8 @@ impl Matrix {
// SPLCIE DOMAIN
pub fn splice_cols(&self, col_start: usize, col_end: usize) -> Result<Self> {
- if col_start <= col_end {
- return Err(self::Error::MatrixInvSlice(col_start, col_end).into());
+ if col_start <= col_end || col_end > self.domain {
+ return error!("[_;{}..{}] invalid for [Matrix {}x{}]", col_start, col_end, self.domain, self.codomain)
}
let mut cols = Vec::new();
@@ -890,8 +978,8 @@ impl Matrix {
// SPLICE CODOMAIN
pub fn splice_rows(&self, row_start: usize, row_end: usize) -> Result<Self> {
- if row_start <= row_end {
- return Err(self::Error::MatrixInvSlice(row_start, row_end).into());
+ if row_start <= row_end || row_end > self.codomain {
+ return error!("[{}..{};_] invalid for [Matrix {}x{}]", row_start, row_end, self.domain, self.codomain)
}
let mut rows = Vec::new();
@@ -915,12 +1003,64 @@ impl Matrix {
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.domain != other.domain || self.codomain != other.codomain {
+ return error!("matrix dimensions 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 * 2, self.codomain, values))
+ }
+
+ pub fn join_bottom(&self, other: &Matrix) -> Result<Self> {
+ if self.domain != other.domain || self.codomain != other.codomain {
+ return error!("matrix dimensions do not match");
+ }
+ let mut values = self.values.clone();
+ values.extend(other.values.clone());
+ Ok(Matrix::new(self.domain, self.codomain * 2, values))
+ }
}
impl Debug for Matrix {
@@ -950,7 +1090,9 @@ impl Add for Matrix {
fn add(self, rhs: Self) -> Self::Output {
if self.domain != rhs.domain || self.codomain != rhs.codomain {
- return Err(self::Error::MatrixAdd(self.domain, self.codomain, rhs.domain, rhs.codomain).into())
+ return error!("cannot add: [Matrix {}x{}] + [Matrix {}x{}]",
+ self.domain, self.codomain,
+ rhs.domain, rhs.codomain)
}
let mut res = Matrix::empty(self.domain, self.codomain);
for col in 0..self.domain {
@@ -968,7 +1110,9 @@ impl Sub for Matrix {
fn sub(self, rhs: Self) -> Self::Output {
if self.domain != rhs.domain || self.codomain != rhs.codomain {
- return Err(self::Error::MatrixAdd(self.domain, self.codomain, rhs.domain, rhs.codomain).into())
+ return error!("cannot sub: [Matrix {}x{}] - [Matrix {}x{}]",
+ self.domain, self.codomain,
+ rhs.domain, rhs.codomain)
}
let mut res = Matrix::empty(self.domain, self.codomain);
for col in 0..self.domain {
@@ -986,7 +1130,9 @@ impl Mul for Matrix {
fn mul(self, rhs: Self) -> Self::Output {
if self.domain != rhs.codomain {
- return Err(self::Error::MatrixMultiply(self.domain, rhs.codomain).into())
+ return error!("cannot multiply: [Matrix {}x{}] * [Matrix {}x{}]",
+ self.domain, self.codomain,
+ rhs.domain, rhs.codomain)
}
let mut res = Self::empty(rhs.domain, self.codomain);
for (i, row) in self.rows().enumerate() {
diff --git a/matrix/src/vm.rs b/matrix/src/vm.rs
index fec34b5..f98dc72 100644
--- a/matrix/src/vm.rs
+++ b/matrix/src/vm.rs
@@ -1,12 +1,11 @@
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}, gc::Gc, chunk::{Function, Instruction, Chunk, InnerFunction}, Result, compiler::NamesTable};
+use crate::{value::{Value, self, ValueMap, Matrix}, Result, gc::Gc, chunk::{Function, Instruction, Chunk, InnerFunction}, compiler::NamesTable, native};
#[derive(Debug)]
pub enum Error {
ValueError(value::Error),
NotFunction(Value),
InvArity(usize, usize, Rc<str>),
- UndefinedGlobal(Rc<str>),
ExecNative,
}
@@ -19,7 +18,6 @@ impl Display for Error {
ValueError(err) => write!(f, "{err}"),
NotFunction(v) => write!(f, "{v:?} is not a function"),
InvArity(wanted, had, name) => write!(f, "function {name} takes {wanted} params but was given {had}"),
- UndefinedGlobal(name) => write!(f, "{name} is not defined"),
ExecNative => write!(f, "cannot execute a native function"),
}
}
@@ -89,21 +87,64 @@ impl<T> Index<usize> for Stack<T> {
}
}
-struct StackFrame {
+#[derive(Clone)]
+pub struct StackFrame {
#[allow(dead_code)]
name: Rc<str>,
body: Rc<Chunk>,
ip: usize,
bp: usize,
+ lp: usize,
+ depth: usize,
}
+struct VmError {
+ err: crate::Error,
+ frames: Vec<StackFrame>
+}
+
+impl From<crate::Error> for VmError {
+ fn from(err: crate::Error) -> Self {
+ VmError {
+ err,
+ frames: Vec::new()
+ }
+ }
+}
+
+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 {
- fn new(vm: &Vm, name: Rc<str>, body: Rc<Chunk>, arity: usize) -> Self {
+ fn new(vm: &Vm, name: Rc<str>, body: Rc<Chunk>, arity: usize, depth: usize) -> Self {
Self {
name,
body,
bp: vm.stack.len() - arity,
+ lp: vm.locals.len(),
ip: 0,
+ depth
+ }
+ }
+}
+
+impl Default for StackFrame {
+ fn default() -> Self {
+ StackFrame {
+ name: Rc::from("<root>"),
+ body: Rc::new(Chunk::default()),
+ bp: 0,
+ lp: 0,
+ ip: 0,
+ depth: 0
}
}
}
@@ -112,10 +153,17 @@ pub enum Interupt {
KeyboardInterupt = 1
}
+struct TryScope {
+ stack_len: usize,
+ locals_len: usize,
+ frame_depth: usize,
+ err_idx: usize,
+}
+
pub struct Vm {
stack: Stack<Value>,
locals: Stack<Value>,
- frames: Vec<StackFrame>,
+ trystack: Vec<TryScope>,
globals: HashMap<u16, Value>,
names: NamesTable,
global_names: NamesTable,
@@ -132,6 +180,10 @@ impl Vm {
self.stack.pop()
}
+ fn top(&mut self) -> Value {
+ self.stack[self.stack.len() - 1].clone()
+ }
+
pub fn names(&self) -> NamesTable {
self.names.clone()
}
@@ -148,7 +200,7 @@ impl Vm {
Self {
stack: Stack::new(),
locals: Stack::new(),
- frames: Vec::new(),
+ trystack: Vec::new(),
globals: HashMap::new(),
names: Rc::new(RefCell::new(Vec::new())),
global_names: Rc::new(RefCell::new(Vec::new())),
@@ -156,10 +208,14 @@ impl Vm {
}
}
- pub fn load_native_fn(&mut self, fun: Rc<Function>) {
+ pub fn load_global(&mut self, value: Value, name: &str) {
let idx = self.global_names.borrow().len();
- self.global_names.borrow_mut().push(fun.name.clone());
- self.globals.insert(idx as u16, Value::Function(fun));
+ self.global_names.borrow_mut().push(name.into());
+ self.globals.insert(idx as u16, value);
+ }
+
+ pub fn load_global_fn(&mut self, value: Rc<Function>, name: &str) {
+ self.load_global(Value::Function(value), name)
}
fn init_frame(&mut self, fun: Rc<Function>) -> Result<StackFrame> {
@@ -170,7 +226,8 @@ impl Vm {
self,
fun.name.clone(),
compiled_chunk.clone(),
- fun.arity
+ fun.arity,
+ 0
))
}
@@ -178,162 +235,252 @@ impl Vm {
self.interupt.load(Ordering::Relaxed) != 0
}
- 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();
+ fn exec_ins(&mut self, frame: &mut StackFrame, ins: Instruction) -> VmResult<Option<Value>> {
+ use Instruction as I;
+ 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::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) => {
+ let val = self.globals
+ .get(&idx)
+ .unwrap()
+ .clone();
+ self.stack.push(val);
+ },
+ I::StoreGlobal(idx) => {
+ let val = self.pop();
+ self.globals.insert(idx, val);
+ },
+ I::Const(idx) => self.push(frame.body.constants[idx as usize].clone()),
+ I::Int(i) => self.push(Value::Int(i as i64)),
+ I::True => self.push(Value::Bool(true)),
+ I::False => self.push(Value::Bool(false)),
+ I::Nil => self.push(Value::Nil),
+ I::Dup => self.push(self.stack[self.stack.len() - 1].clone()),
+ I::Discard(count) => {self.stack.truncate(self.stack.len() - count as usize)},
+ I::UnaryOp(op) => {
+ let val = self.pop();
+ self.push(Value::unary_op(op, val));
+ },
+ I::BinaryOp(op) => {
+ let rhs = self.pop();
+ let lhs = self.pop();
+ self.push(Value::binary_op(op, lhs, rhs)?);
+ },
+ I::NewList(items) => {
+ let list = self.stack.split_off(self.stack.len() - items as usize);
+ self.push(Value::List(list.inner.into()));
+ },
+ I::NewTable(items) => {
+ let mut table = ValueMap::new();
+ for _ in 0..items {
+ let value = self.pop();
+ let key = self.pop();
+ table.insert(key, value)?;
+ }
+ self.push(Value::Table(table.into()))
+ },
+ I::NewMatrix(items, domain) => {
+ let values = self.stack.split_off(self.stack.len() - items as usize).inner;
+ let domain = domain as usize;
+ let codomain = values.len() / domain;
+ self.push(Value::Matrix(Gc::new(Matrix::new(domain, codomain, values))));
+ }
+ I::Jump(idx) => {
+ if self.check_interupt() {
+ return Ok(Some(Value::Nil))
+ }
+ frame.ip = idx as usize;
+ }
+ I::JumpTrue(idx) => {
+ if self.check_interupt() {
+ return Ok(Some(Value::Nil))
+ }
+ if !!self.pop() {
+ frame.ip = idx as usize;
+ }
+ },
+ I::JumpFalse(idx) => {
+ if self.check_interupt() {
+ return Ok(Some(Value::Nil))
+ }
+ if !self.pop() {
+ frame.ip = idx as usize;
+ }
+ },
+ I::JumpNil(idx) => {
+ if self.check_interupt() {
+ return Ok(Some(Value::Nil))
+ }
+ if self.pop() == Value::Nil {
+ frame.ip = idx as usize;
+ }
+ },
+ I::Call(arity) => {
+ let arity = arity as usize;
- loop {
- use Instruction::*;
- let ins = frame.body.code[frame.ip].clone();
- frame.ip += 1;
+ let fun = self.pop();
+ let Value::Function(fun) = fun else {
+ return Err(Error::NotFunction(fun).into())
+ };
- match ins {
- NoOp => {},
- CreateLocal => self.locals.push(self.stack.pop()),
- LoadLocal(idx) => {self.stack.push(self.locals[idx as usize].clone());},
- StoreLocal(idx) => self.locals[frame.bp + idx as usize] = self.pop(),
- DiscardLocals(count) => {self.locals.truncate(self.locals.len() - count as usize)},
- LoadGlobal(idx) => {
- let val = self.globals
- .get(&idx)
- .unwrap()
- .clone();
+ if !fun.variadic && arity > fun.arity {
+ return Err(Error::InvArity(fun.arity, arity as usize, fun.name.clone()).into())
+ }
+
+ let mut params = self.stack.split_off(self.stack.len() - arity).inner;
+
+ if params.len() < fun.arity {
+ let new_arity = fun.arity - arity;
+ let val = native!("<partial>", new_arity, false, move |(vm, vm_frame), args| {
+ let mut params = params.clone();
+ params.extend(args);
+ vm.exec_fn(vm_frame, fun.clone(), params).map_err(|e| e.err)
+ });
self.stack.push(val);
- },
- StoreGlobal(idx) => {
- let val = self.pop();
- self.globals.insert(idx, val);
- },
- Const(idx) => self.push(frame.body.constants[idx as usize].clone()),
- Int(i) => self.push(Value::Int(i as i64)),
- True => self.push(Value::Bool(true)),
- False => self.push(Value::Bool(false)),
- Nil => self.push(Value::Nil),
- Dup => self.push(self.stack[self.stack.len() - 1].clone()),
- Discard(count) => {self.stack.truncate(self.stack.len() - count as usize)},
- UnaryOp(op) => {
- let val = self.pop();
- self.push(Value::unary_op(op, val));
- },
- BinaryOp(op) => {
- let rhs = self.pop();
- let lhs = self.pop();
- self.push(Value::binary_op(op, lhs, rhs)?);
- },
- NewList(items) => {
- let list = self.stack.split_off(self.stack.len() - items as usize);
- self.push(Value::List(list.inner.into()));
- },
- NewTable(items) => {
- let mut table = ValueMap::new();
- for _ in 0..items {
- let value = self.pop();
- let key = self.pop();
- table.insert(key, value)?;
- }
- self.push(Value::Table(table.into()))
- },
- NewMatrix(items, domain) => {
- let values = self.stack.split_off(self.stack.len() - items as usize).inner;
- let domain = domain as usize;
- let codomain = values.len() / domain;
- self.push(Value::Matrix(Gc::new(Matrix::new(domain, codomain, values))));
+ return Ok(None);
}
- Jump(idx) => {
- if self.check_interupt() {
- return Ok(Value::Nil)
+
+ if fun.variadic {
+ if arity == fun.arity {
+ params.push(Value::List(vec![].into()));
+ } else {
+ let count = arity - fun.arity;
+ let varadic_params = params.split_off(params.len() - count);
+ params.push(Value::List(varadic_params.into()));
}
- frame.ip = idx as usize;
}
- JumpTrue(idx) => {
- if self.check_interupt() {
- return Ok(Value::Nil)
- }
- if !!self.pop() {
- frame.ip = idx as usize;
- }
- },
- JumpFalse(idx) => {
- if self.check_interupt() {
- return Ok(Value::Nil)
- }
- if !self.pop() {
- frame.ip = idx as usize;
- }
- },
- Call(arity) => {
- let fun = self.pop();
- let Value::Function(fun) = fun else {
- return Err(Error::NotFunction(fun).into())
- };
- let arity = arity as usize;
- let normal_args = if fun.variadic { fun.arity - 1 } else { fun.arity };
- if (fun.variadic && arity < normal_args) || (!fun.variadic && arity != normal_args) {
- return Err(Error::InvArity(normal_args, arity as usize, fun.name.clone()).into())
- }
- if fun.variadic {
- if arity == normal_args {
- self.push(Value::List(vec![].into()));
- } else if arity > normal_args {
- let count = arity - normal_args;
- let varadic_params = self.stack.split_off(self.stack.len() - count).inner;
- self.push(Value::List(varadic_params.into()));
- }
- }
- let name = fun.name.clone();
- match &fun.fun {
- InnerFunction::Compiled(body) => {
- let new_frame = StackFrame::new(self, name, body.clone(), fun.arity);
- self.frames.push(frame);
- frame = new_frame;
- },
- InnerFunction::Native(native_fun) => {
- let params = self.stack.split_off(self.stack.len() - fun.arity).inner;
- let res = native_fun(self, params)?;
- self.stack.push(res);
+
+ let res = self.exec_fn(frame, fun, params)?;
+ self.push(res);
+ },
+ I::Return => {
+ if self.check_interupt() {
+ return Ok(Some(Value::Nil))
+ }
+ let ret = self.pop();
+ self.stack.truncate(frame.bp);
+ self.locals.truncate(frame.lp);
+ return Ok(Some(ret))
+ },
+ I::Field(name_idx) => {
+ let expr = self.pop();
+ let name = self.names.borrow()[name_idx as usize].clone();
+ self.push(expr.field_access(&name)?);
+ },
+ I::StoreField(name_idx) => {
+ let mut expr = self.pop();
+ let value = self.pop();
+ let name = self.names.borrow()[name_idx as usize].clone();
+ expr.store_field_access(&name, value)?;
+ },
+ I::Index(count) => {
+ let index = self.stack.split_off(self.stack.len() - count as usize);
+ let collection = self.pop();
+ let value = collection.index(&index.inner)?;
+ self.stack.push(value);
+ },
+ I::StoreIndex(count) => {
+ let index = self.stack.split_off(self.stack.len() - count as usize);
+ let mut collection = self.pop();
+ let value = self.pop();
+ collection.store_index(&index.inner, value)?;
+ },
+ I::IterCreate => {
+ let val = self.pop().into_iter();
+ self.push(val?);
+ },
+ I::IterNext => {
+ let Value::Iter(iter) = self.top() else { panic!("bypassed iter check"); };
+ let val = self.exec_fn(frame, iter, vec![])?;
+ self.push(val);
+ },
+ I::Try(idx) => {
+ let scope = TryScope {
+ err_idx: idx as usize,
+ frame_depth: frame.depth,
+ stack_len: self.stack.len(),
+ locals_len: self.locals.len(),
+ };
+ self.trystack.push(scope);
+ },
+ I::TryEnd => {
+ self.trystack.pop().unwrap();
+ },
+ };
+
+ Ok(None)
+ }
+
+ fn stack_trace(&mut self, frames: Vec<StackFrame>) -> String {
+ let mut trace = String::from("\x1b[33m\x1b[1mStack Trace:\x1b[0m\n");
+ 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));
+ }
+ trace
+ }
+
+ fn exec_fn(&mut self, frame: &mut StackFrame, fun: Rc<Function>, params: Vec<Value>) -> VmResult<Value> {
+ let name = fun.name.clone();
+ let params_len = params.len();
+ match &fun.fun {
+ InnerFunction::Compiled(body) => {
+ for param in params {
+ self.stack.push(param);
+ }
+ let mut new_frame = StackFrame::new(self, name, body.clone(), params_len, frame.depth + 1);
+ self.exec(&mut new_frame)
+ },
+ InnerFunction::Native(native_fun) => {
+ Ok(native_fun((self, frame), params)?)
+ }
+ }
+ }
+
+ fn exec(&mut self, frame: &mut StackFrame) -> VmResult<Value> {
+ loop {
+ let ins = frame.body.code[frame.ip].clone();
+ frame.ip += 1;
+ match self.exec_ins(frame, ins) {
+ Ok(Some(val)) => return Ok(val),
+ Ok(None) => {},
+ Err(err) => {
+ if let Some(catch) = self.trystack.pop() {
+ if frame.depth != catch.frame_depth {
+ self.trystack.push(catch);
+ return Err(err);
}
+ self.stack.truncate(catch.stack_len);
+ self.locals.truncate(catch.locals_len);
+ frame.ip = catch.err_idx;
+ self.stack.push(Value::Error(err.err));
+ } else {
+ let mut err = err;
+ err.frames.push(frame.clone());
+ return Err(err)
}
},
- Return => {
- if self.check_interupt() {
- return Ok(Value::Nil)
- }
- let Some(prev_frame) = self.frames.pop() else {
- break;
- };
- let ret = self.pop();
- self.stack.truncate(frame.bp);
- self.locals.truncate(0);
- self.push(ret);
- frame = prev_frame;
- },
- Field(name_idx) => {
- let expr = self.pop();
- let name = self.names.borrow()[name_idx as usize].clone();
- self.push(expr.field_access(&name)?);
- },
- StoreField(name_idx) => {
- let mut expr = self.pop();
- let value = self.pop();
- let name = self.names.borrow()[name_idx as usize].clone();
- expr.store_field_access(&name, value)?;
- },
- Index(count) => {
- let index = self.stack.split_off(self.stack.len() - count as usize);
- let collection = self.pop();
- let value = collection.index(&index.inner)?;
- self.stack.push(value);
- },
- StoreIndex(count) => {
- let index = self.stack.split_off(self.stack.len() - count as usize);
- let mut collection = self.pop();
- let value = self.pop();
- collection.store_index(&index.inner, value)?;
- }
}
}
- Ok(self.pop())
+ }
+
+ pub fn run_fn(&mut self, frame: &mut StackFrame, fun: Rc<Function>, params: Vec<Value>) -> Result<Value> {
+ self.exec_fn(frame, fun, params).map_err(|e| e.err)
+ }
+
+ 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();
+ self.exec(&mut frame).map_err(|e| {
+ let trace = self.stack_trace(e.frames);
+ (e.err, trace).into()
+ })
}
}