From 158bcae00dbe2af50e51468ad003fb594a858e6d Mon Sep 17 00:00:00 2001 From: Freya Murphy Date: Mon, 26 Feb 2024 19:00:42 -0500 Subject: [PATCH] changes --- Cargo.lock | 12 + matrix-bin/Cargo.toml | 2 +- matrix-bin/src/helper.rs | 312 ++++++++++++++++++++ matrix-bin/src/main.rs | 38 ++- matrix-bin/src/repl.rs | 27 +- matrix-macros/src/lib.rs | 13 +- matrix-stdlib/src/core.rs | 181 ++++++++++++ matrix-stdlib/src/io.rs | 185 ++++++++++-- matrix-stdlib/src/iter.rs | 544 ++++++++++++++++++++++++++++++++++ matrix-stdlib/src/lib.rs | 20 +- matrix-stdlib/src/math.rs | 518 +++++++++++++++++++++++++++++++++ matrix-stdlib/src/sys.rs | 99 +++++++ matrix/src/ast.rs | 275 ++++++++++++------ matrix/src/chunk.rs | 25 +- matrix/src/compiler.rs | 281 +++++++++++------- matrix/src/gc.rs | 6 + matrix/src/lex.rs | 314 +++++++++++++------- matrix/src/lib.rs | 72 ++++- matrix/src/parse.rs | 322 ++++++++++++-------- matrix/src/value.rs | 596 ++++++++++++++++++++++++-------------- matrix/src/vm.rs | 473 +++++++++++++++++++----------- 21 files changed, 3452 insertions(+), 863 deletions(-) create mode 100644 matrix-bin/src/helper.rs create mode 100644 matrix-stdlib/src/core.rs create mode 100644 matrix-stdlib/src/iter.rs create mode 100644 matrix-stdlib/src/math.rs create mode 100644 matrix-stdlib/src/sys.rs diff --git a/Cargo.lock b/Cargo.lock index a3a118e..6414157 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -415,12 +415,24 @@ dependencies = [ "memchr", "nix", "radix_trie", + "rustyline-derive", "unicode-segmentation", "unicode-width", "utf8parse", "winapi", ] +[[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" 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> +} + +impl MatrixHelper { + pub fn new(vm: Rc>) -> 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 { + 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 { + 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 { + 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> { + 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; + + fn complete( + &self, + line: &str, + pos: usize, + ctx: &rustyline::Context<'_>, + ) -> rustyline::Result<(usize, Vec)> { + + 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> = 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, + /// 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>, + 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 { 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, } impl Parse for NativeFuncParams { fn parse(input: syn::parse::ParseStream) -> syn::Result { let arity = input.parse()?; - input.parse::()?; 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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 matrix_macros::native_func; +use std::{io::{self, Read, Write}, cell::RefCell, fs::OpenOptions, rc::Rc}; -#[native_func(1, true)] -fn print(_vm: &mut Vm, args: Vec) -> Result { - 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!(" "); - } +use matrix::{value::Value, self, vm::Vm, Result, unpack_varargs, iter, unpack_args}; +use matrix_macros::native_func; +use crate::{error, VmArgs}; + +#[native_func(0..)] +fn print(_: VmArgs, args: Vec) -> Result { + 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) -> Result { - 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) -> Result { + let ([], varags) = unpack_varargs!(args); + for (i, value) in varags.into_iter().enumerate() { + if i != 0 { + print!(" "); } + print!("{value}"); } print!("\n"); Ok(Value::Nil) } -pub fn load(vm: &mut Vm) { - vm.load_native_fn(print()); - vm.load_native_fn(println()); +#[native_func(0)] +fn readln(_: VmArgs, _: Vec) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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> = 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) -> Result { + 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) -> Result { + 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_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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + let [value] = unpack_args!(args); + Ok(value.into_iter()?) +} + +#[native_func(1)] +fn once(_: VmArgs, args: Vec) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + let [str] = unpack_args!(args); + let Value::String(str) = str else { + return error!("lines arg must be a string") + }; + let lines: Vec> = 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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 { + 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 { + 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) -> Result { + 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 { + 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::>() + ) + .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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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, Position); + #[derive(Debug, Clone, PartialEq)] -pub enum Expr { +pub enum ExprData { NoOp, Literal(Value), @@ -45,7 +50,7 @@ pub enum Expr { Index(Box, Vec), FnCall(Box, Vec), - FieldAccess(Box, Rc), + FieldAccess(Box, AstName), List(InlineList), Matrix(InlineMatrix), @@ -57,22 +62,49 @@ pub enum Expr { Assign(Box, Box), If(Box, Box, Option>), - Function(Rc, Vec>, Box, bool), - Lambda(Vec>, Box, bool), + Function(AstName, Vec, Box, bool), + Lambda(Vec, Box, bool), Loop(Box), While(Box, Box), DoWhile(Box, Box), + For(AstName, Box, Box), Block(Vec), - Let(Rc, Box), + Try(Box, AstName, Box), + + Let(AstName, Box), + + Pipeline(Box, Box), Continue, Break, Return(Box), } +#[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; fn neg(self) -> Self::Output { @@ -103,73 +135,94 @@ impl Not for BinaryOp { impl Neg for Expr { type Output = std::result::Result; - fn neg(self) -> Self::Output { - use Expr::*; - Ok(match self { - Literal(v) => Literal(-v), - BinaryOp(lhs, rhs, op) => { + 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; - 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 { - use Expr as E; - Ok(match expr { +pub fn optimize(mut expr: Expr) -> Result { + 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 { 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 { 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 { .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 { 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 { 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 for UnaryOp { - fn from(value: Token) -> Self { - use Token::*; +impl From 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 for BinaryOp { - fn from(value: Token) -> Self { - use Token::*; +impl From 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 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, - pub code: Vec + pub code: Vec, + pub pos: Vec, } 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), - Native(Box) -> Result>), + Native(Rc) -> Result>), } 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>>>; @@ -104,7 +104,7 @@ struct Local { } #[derive(Debug, Clone)] -pub enum Error { +pub enum InnerError { Undefined(Rc), Redefined(Rc), 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(err: InnerError, pos: Position) -> Result { + Err(self::Error { + pos, + err + }.into()) +} + impl<'c> Compiler<'c> { fn child(&'c self, name: Rc) -> 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) -> Result<()> { + fn create_local_checked(&mut self, name: Rc, 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) -> Result { + fn create_global_checked(&mut self, name: Rc, pos: Position) -> Result { 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 = Rc::from(""); + let name: AstName = (Rc::from(""), 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, params: &Vec>, body: &Box) -> Result { - let mut compiler = self.child(name); - for name in params { + fn compile_function(&mut self, name: AstName, params: &Vec, body: &Box) -> Result { + 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> { - 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 Gc { } } + 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 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, + pos: Position, + byte_len: usize, } trait IsIdent { @@ -177,7 +206,13 @@ impl> From for Lexer { impl Lexer { pub fn new>(input: T) -> Self { let data: Vec = 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 { - use Error::*; - use Token::*; + fn lex_ident(&mut self, initial: char) -> Result { + 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 { - use Token::*; - use Error::*; + fn lex_radix(&mut self, radix: i64, radix_char: char) -> Result { + 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 { - use Error::*; + fn lex_number(&mut self, initial: char) -> Result { + 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,25 +419,38 @@ 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()); - } - - if self.peek() == 'e' || self.peek() == 'E' { - buf.push(self.next()); - if self.peek() == '+' || self.peek() == '-' { - buf.push(self.next()); - } + let last: char = buf.chars().last().unwrap(); + let is_range = initial != '.' && last == '.' && 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()); } + + 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()); + } + } } let complex = self.peek() == 'i'; @@ -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::() { - 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::() { - 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 { 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 { - 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 }, - _ => BitwiseOr + '>' => { + self.next(); + T::Pipe + }, + _ => 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); +pub use ::num_complex::Complex64 as Complex64; +pub use ::num_rational::Rational64 as Rational64; + +#[derive(Debug, Clone)] +pub struct Error(Rc); impl std::error::Error for Error {} @@ -22,6 +25,8 @@ enum ErrorInner { Compile(compiler::Error), Runtime(vm::Error), External(anyhow::Error), + Traced(Box, 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(""), + 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 = std::result::Result; 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 { + fn force_token(&mut self, tok: TokenData) -> Result { 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 { + fn force_token_nl(&mut self, tok: TokenData) -> Result { 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> { 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 { + 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 { 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 { + 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 { @@ -259,28 +260,26 @@ impl Parser { Ok(expr) } - fn parse_params(&mut self) -> Result<(Vec>, bool)> { - use T::*; + fn parse_params(&mut self) -> Result<(Vec<(Rc, 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> { - if let T::Ident(ident) = self.lexer.next_token()? { - Ok(ident) + fn parse_ident(&mut self) -> Result { + 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> { - if let T::Ident(ident) = self.lexer.next_token_nl()? { + fn parse_wrapped_ident(&mut self) -> Result { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { 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 { + 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { - use BinaryOp as B; + fn parse_expr_range(&mut self) -> Result { 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 { + 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 { + 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 { 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>(&mut self, into: T) -> Result { 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), - String(Rc), + String(Rc), List(Gc), Matrix(Gc), Table(Gc), Function(Rc), + Range(Rc<(i64, i64, bool)>), + Iter(Rc), + File(Rc>), + Error(crate::Error), +} + +impl From<&str> for Value { + fn from(value: &str) -> Self { + Value::String(value.into()) + } } impl Hash for Value { @@ -132,6 +113,142 @@ impl Hash for Value { } } +impl Debug for Value { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use Value as V; + match self { + V::Nil => write!(f, "[Nil]"), + V::Bool(b) => write!(f, "[Bool {b}]"), + V::Int(i) => write!(f, "[Int {i}]"), + V::Float(vf) => write!(f, "[Float {vf}]"), + V::Ratio(r) => write!(f, "[Ratio {r}]"), + V::Complex(c) => write!(f, "[Complex {c}]"), + V::Regex(r) => write!(f, "[Regex /{r}/]"), + V::String(s) => write!(f, "[String '{s}']"), + V::List(l) => write!(f, "[List {}]", l.len()), + V::Matrix(m) => write!(f, "[Matirx {}x{}]", m.domain, m.codomain), + V::Table(t) => write!(f, "[Table {}]", t.0.len()), + V::Function(vf) => write!(f, "[Function {}]", vf.name), + V::Range(r) => write!(f, "[Range {:?}..{:?}]", r.0, r.1), + V::Iter(_) => write!(f, "[Iterator]"), + V::Error(_) => write!(f, "[Error]"), + V::File(_) => write!(f, "[File]"), + } + } +} + +impl Display for Value { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use Value as V; + + let red; + let green; + let yellow; + let blue; + let pink; + let cyan; + let clear; + + if f.alternate() { + red = "\x1b[31m"; + green = "\x1b[32m"; + yellow = "\x1b[33m"; + blue = "\x1b[34m"; + pink = "\x1b[35m"; + cyan = "\x1b[36m"; + clear = "\x1b[0m"; + } else { + red = ""; + green = ""; + yellow = ""; + blue = ""; + pink = ""; + cyan = ""; + clear = ""; + } + + match self { + V::Nil => {write!(f, "{blue}nil{clear}")?;}, + V::Bool(b) => {write!(f, "{yellow}{b}{clear}")?;}, + V::Int(i) => {write!(f, "{yellow}{i}{clear}")?;}, + V::Float(l) => {write!(f, "{yellow}{l}{clear}")?;}, + V::Ratio(r) => {write!(f, "{yellow}{r}{clear}")?;}, + V::Complex(c) => {write!(f, "{yellow}{c}{clear}")?;}, + V::Regex(r) => {write!(f, "/{red}{r}{clear}/")?;}, + V::String(s) => { + if f.alternate() { + write!(f, "{green}'{s}'{clear}")?; + } else { + write!(f, "{s}")?; + } + } + V::List(l) => { + write!(f, "[")?; + for (i, el) in l.iter().enumerate() { + if i != 0 { + write!(f, " ")?; + } + if f.alternate() { + write!(f, "{el:#}")?; + } else { + write!(f, "{el}")?; + } + } + write!(f, "]")?; + }, + V::Matrix(m) => { + write!(f, "\n")?; + for row in m.rows() { + write!(f, " ")?; + for (i, el) in row.into_iter().enumerate() { + if i != 0 { + write!(f, " ")?; + } + if f.alternate() { + write!(f, "{el:#}")?; + } else { + write!(f, "{el}")?; + } + } + write!(f, "\n")?; + } + }, + V::Table(t) => { + write!(f, "{{")?; + for (i, (key, val)) in t.0.iter().enumerate() { + if i != 0 { + write!(f, ", ")?; + } + if f.alternate() { + write!(f, "{key:#} = {val:#}")?; + } else { + write!(f, "{key} = {val}")?; + } + } + write!(f, "}}")?; + }, + V::Function(fun) => { + use crate::chunk::InnerFunction as F; + match fun.fun { + F::Compiled(_) => write!(f, "{cyan}[Function {}]{clear}", fun.name)?, + F::Native(_) => write!(f, "{cyan}[Builtin {}]{clear}", fun.name)?, + }; + }, + V::Range(r) => { + if f.alternate() { + write!(f, "{:#}..{:#}", r.0, r.1)?; + } else { + write!(f, "{}..{}", r.0, r.1)?; + } + } + V::Iter(_) => {write!(f, "{pink}[Iterator]{clear}")?;}, + V::File(_) => {write!(f, "{pink}[File]{clear}")?;}, + V::Error(e) => {write!(f, "{red}{e}{clear}")?;}, + }; + Ok(()) + } +} + impl Value { pub fn can_hash(&self) -> Result<()> { use Value::*; @@ -152,132 +269,37 @@ impl Value { val.can_hash()?; } }, - _ => return Err(Error::CannotHash(self.clone()).into()) + _ => return error!("cannot hash {self:?}") } Ok(()) } - pub fn boring_print(&self) -> String { - use Value::*; + pub fn is_zero(&self) -> bool { + use Value as V; match self { - Nil => format!("nil"), - Bool(b) => format!("{b}"), - Int(i) => format!("{i}"), - Float(l) => format!("{l}"), - Ratio(r) => format!("{r}"), - Complex(c) => format!("{c}"), - Regex(r) => format!("/{r}/"), - String(s) => format!("{s}"), - List(l) => { - let mut str = "[".to_string(); - for (i, el) in l.iter().enumerate() { - if i != 0 { - str.push_str(" "); - } - str.push_str(&el.boring_print()); - } - str.push_str("]"); - str - }, - Matrix(m) => { - let mut str = "\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.boring_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.boring_print()); - str.push_str(" = "); - str.push_str(&val.boring_print()); - } - str.push_str("}"); - str - }, - Function(fun) => { - format!("{fun:?}") - } + V::Int(i) => *i == 0, + V::Float(f) => *f == 0.0 || *f == -0.0, + V::Ratio(r) => *r.numer() == 0, + _ => false, } } - - pub fn pretty_print(&self) -> String { - use Value::*; - match self { - Nil => format!("\x1b[34m{}\x1b[0m", self.boring_print()), - Bool(_) => format!("\x1b[33m{}\x1b[0m", self.boring_print()), - Int(_) => format!("\x1b[33m{}\x1b[0m", self.boring_print()), - Float(_) => format!("\x1b[33m{}\x1b[0m", self.boring_print()), - Ratio(_) => format!("\x1b[33m{}\x1b[0m", self.boring_print()), - Complex(_) => format!("\x1b[33m{}\x1b[0m", self.boring_print()), - Regex(_) => format!("\x1b[31m{}\x1b[0m", self.boring_print()), - String(_) => format!("\x1b[32m'{}'\x1b[0m", self.boring_print()), - List(l) => { - let mut str = "[".to_string(); - for (i, el) in l.iter().enumerate() { - if i != 0 { - str.push_str(" "); - } - str.push_str(&el.pretty_print()); - } - str.push_str("]"); - str - }, - Matrix(m) => { - let mut str = "\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()); - } - str.push_str("}"); - str - }, - Function(_) => { - format!("\x1b[36m{}\x1b[0m", self.boring_print()) - }, - } - } -} - -impl Display for Value { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.pretty_print()) - } } fn ratio_to_f64(r: Rational64) -> f64 { *r.numer() as f64 / *r.denom() as f64 } +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 { 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 { @@ -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> { + let Value::Iter(iter) = self.into_iter()? else { + return error!("bypassed iter check") + }; + Ok(iter) + } + + pub fn into_iter(self) -> Result { + 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 { let len = lhs.len(); @@ -830,9 +918,9 @@ impl Matrix { pub fn get(&self, row: usize, col: usize) -> 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; 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 { - 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 { - 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 { + let values = self.values.iter() + .map(|v| v.clone() + increment.clone()) + .collect::>>()?; + Ok(Matrix::new(self.domain, self.codomain, values)) + } + + pub fn decrement(&self, decrement: Value) -> Result { + let values = self.values.iter() + .map(|v| v.clone() - decrement.clone()) + .collect::>>()?; + Ok(Matrix::new(self.domain, self.codomain, values)) + } + pub fn scale(&self, scale: Value) -> Result { let values = self.values.iter() .map(|v| v.clone() * scale.clone()) .collect::>>()?; Ok(Matrix::new(self.domain, self.codomain, values)) } + + pub fn join_right(&self, other: &Matrix) -> Result { + 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::>(); + + 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 { + 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), - UndefinedGlobal(Rc), 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 Index for Stack { } } -struct StackFrame { +#[derive(Clone)] +pub struct StackFrame { #[allow(dead_code)] name: Rc, body: Rc, ip: usize, bp: usize, + lp: usize, + depth: usize, } +struct VmError { + err: crate::Error, + frames: Vec +} + +impl From for VmError { + fn from(err: crate::Error) -> Self { + VmError { + err, + frames: Vec::new() + } + } +} + +impl From for VmError { + fn from(err: self::Error) -> Self { + VmError { + err: err.into(), + frames: Vec::new() + } + } +} + +type VmResult = std::result::Result; + impl StackFrame { - fn new(vm: &Vm, name: Rc, body: Rc, arity: usize) -> Self { + fn new(vm: &Vm, name: Rc, body: Rc, 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(""), + 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, locals: Stack, - frames: Vec, + trystack: Vec, globals: HashMap, 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) { + 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, name: &str) { + self.load_global(Value::Function(value), name) } fn init_frame(&mut self, fun: Rc) -> Result { @@ -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 } + fn exec_ins(&mut self, frame: &mut StackFrame, ins: Instruction) -> VmResult> { + 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; + + let fun = self.pop(); + let Value::Function(fun) = fun else { + return Err(Error::NotFunction(fun).into()) + }; + + 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!("", 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); + return Ok(None); + } + + 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())); + } + } + + 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) -> 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, params: Vec) -> VmResult { + 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 { + 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) + } + }, + } + } + } + + pub fn run_fn(&mut self, frame: &mut StackFrame, fun: Rc, params: Vec) -> Result { + self.exec_fn(frame, fun, params).map_err(|e| e.err) + } + pub fn run(&mut self, fun: Rc) -> Result { let mut frame = self.init_frame(fun)?; self.interupt.store(0, Ordering::SeqCst); self.stack = Stack::new(); self.locals = Stack::new(); - - loop { - use Instruction::*; - let ins = frame.body.code[frame.ip].clone(); - frame.ip += 1; - - 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(); - 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)))); - } - Jump(idx) => { - if self.check_interupt() { - return Ok(Value::Nil) - } - 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); - } - } - }, - 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()) + self.exec(&mut frame).map_err(|e| { + let trace = self.stack_trace(e.frames); + (e.err, trace).into() + }) } }