This commit is contained in:
Murphy 2024-02-26 19:00:42 -05:00
parent ad8700371f
commit 158bcae00d
Signed by: freya
GPG key ID: 744AB800E383AE52
21 changed files with 3452 additions and 863 deletions

12
Cargo.lock generated
View file

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

View file

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

312
matrix-bin/src/helper.rs Normal file
View file

@ -0,0 +1,312 @@
use std::{borrow::Cow, rc::Rc, cell::RefCell};
use matrix::{lex::{Lexer, TokenData, Token}, vm::Vm};
use rustyline::{validate::{Validator, ValidationResult, ValidationContext}, highlight::Highlighter, Helper, Hinter, completion::Completer};
#[derive(Helper, Hinter)]
pub struct MatrixHelper {
vm: Rc<RefCell<Vm>>
}
impl MatrixHelper {
pub fn new(vm: Rc<RefCell<Vm>>) -> Self {
Self { vm }
}
}
macro_rules! unmatched {
($token:expr, $matched:expr) => {
Ok(::rustyline::validate::ValidationResult::Invalid(Some(format!("Token '{:?}' at {}:{} does not have a matching '{:?}'", $token.data, $token.pos.row, $token.pos.col, $matched))))
};
}
impl Validator for MatrixHelper {
fn validate(&self, ctx: &mut ValidationContext) -> rustyline::Result<ValidationResult> {
let mut lexer = Lexer::new(ctx.input());
let mut stack = Vec::new();
loop {
use TokenData as T;
let token = match lexer.next_token() {
Ok(t) if t.data == T::Eof => break,
Ok(t) => t,
Err(err) => return Ok(ValidationResult::Invalid(Some(format!("{err}"))))
};
match token.data {
T::LeftParen |
T::LeftBrack |
T::LeftBrace |
T::For |
T::Try => { stack.push(token.data); },
T::RightParen =>
match stack.pop() {
Some(T::LeftParen) => {},
_ => return unmatched!(token, T::LeftParen)
},
T::RightBrack =>
match stack.pop() {
Some(T::LeftBrack) => {},
_ => return unmatched!(token, T::LeftBrack)
},
T::RightBrace =>
match stack.pop() {
Some(T::LeftBrace) => {},
_ => return unmatched!(token, T::LeftBrace)
},
T::In =>
match stack.pop() {
Some(T::For) => {},
_ => return unmatched!(token, T::For)
},
T::Catch =>
match stack.pop() {
Some(T::Try) => {},
_ => return unmatched!(token, T::Try)
},
_ => {}
};
}
if stack.is_empty() {
return Ok(ValidationResult::Valid(None))
} else {
return Ok(ValidationResult::Incomplete)
}
}
fn validate_while_typing(&self) -> bool {
true
}
}
fn find_matching_bracket(line: &str, pos: usize) -> Option<usize> {
if pos >= line.len() {
return None
}
let c = line.as_bytes()[pos];
let (target, fwd) = match c {
b'(' => (b')', true),
b')' => (b'(', false),
b'[' => (b']', true),
b']' => (b'[', false),
b'{' => (b'}', true),
b'}' => (b'{', false),
_ => return None,
};
let mut depth = 0;
let mut idx = 0;
if fwd {
let bytes = &line.as_bytes()[pos+1..];
for &b in bytes {
if b == c {
depth += 1;
} else if b == target {
if depth == 0 {
return Some(pos + idx + 1)
} else {
depth -= 1;
}
}
idx += 1;
}
} else {
let bytes = &line.as_bytes()[..pos];
for &b in bytes.iter().rev() {
if b == c {
depth += 1;
} else if b == target {
if depth == 0 {
return Some(pos - idx - 1)
} else {
depth -= 1;
}
}
idx += 1;
}
}
None
}
fn get_token_at(line: &str, pos: usize) -> Option<Token> {
use TokenData as T;
let mut lexer = Lexer::new(line);
loop {
match lexer.next_token() {
Ok(Token { data: T::Eof, ..}) => return None,
Ok(t) if t.bidx <= pos && (t.bidx + t.blen) > pos => return Some(t),
Err(_) => return None,
_ => continue
}
}
}
fn find_matching_ident(line: &str, pos: usize) -> Option<Rc<str>> {
use TokenData as T;
match get_token_at(line, pos) {
Some(Token { data: T::Ident(ident), ..}) => Some(ident),
_ => return None
}
}
impl Highlighter for MatrixHelper {
fn highlight<'l>(&self, line: &'l str, pos: usize) -> Cow<'l, str> {
let matching = find_matching_bracket(line, pos);
let ident = find_matching_ident(line, pos);
use TokenData as T;
let mut lexer = Lexer::new(line);
let mut buf = String::new();
let mut last = 0;
loop {
let token = match lexer.next_token() {
Ok(t) if t.data == T::Eof => break,
Ok(t) => t,
Err(_) => break
};
let bl = token.bidx;
let br = token.bidx + token.blen;
buf += &line[last..bl];
last = br;
let color = match token.data {
T::LeftParen |
T::RightParen |
T::LeftBrack |
T::RightBrack |
T::LeftBrace |
T::RightBrace
=> {
match matching {
Some(idx) if bl == idx || bl == pos => "\x1b[33;40m",
_ => ""
}
}
T::Int(_) |
T::True |
T::False |
T::Nil |
T::Float(_) |
T::Complex(_)
=> "\x1b[33m",
T::Ident(tok) => {
match (ident.as_ref(), lexer.peek_token()) {
(Some(ident), Ok(t)) if t.data == T::LeftParen && ident.as_ref() == tok.as_ref() => "\x1b[34;40m",
(Some(ident), _) if ident.as_ref() == tok.as_ref() => "\x1b[40m",
(_, Ok(t)) if t.data == T::LeftParen => "\x1b[34m",
_ => ""
}
}
T::Regex(_) => "\x1b[31m",
T::String(_) => "\x1b[32m",
T::If |
T::Else |
T::While |
T::Let |
T::Function |
T::Continue |
T::Break |
T::Do |
T::Loop |
T::Return |
T::For |
T::In |
T::Try |
T::Catch
=> "\x1b[38;2;203;166;247m",
_ => {
buf += &token.str;
continue;
}
};
let clear = "\x1b[0m";
buf += &format!("{color}{}{clear}", token.str);
}
buf += &line[last..];
Cow::Owned(buf)
}
fn highlight_prompt<'b, 's: 'b, 'p: 'b>(
&'s self,
prompt: &'p str,
default: bool,
) -> std::borrow::Cow<'b, str> {
let _ = default;
Cow::Owned(format!("\x1b[35m{prompt}\x1b[0m"))
}
fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
Cow::Owned(format!(" \x1b[31;40m Error: \x1b[0;40m{hint} \x1b[0m"))
}
fn highlight_candidate<'c>(
&self,
candidate: &'c str,
completion: rustyline::CompletionType,
) -> std::borrow::Cow<'c, str> {
let _ = completion;
Cow::Borrowed(candidate)
}
fn highlight_char(&self, line: &str, _pos: usize, forced: bool) -> bool {
forced || !line.is_empty()
}
}
impl Completer for MatrixHelper {
type Candidate = Rc<str>;
fn complete(
&self,
line: &str,
pos: usize,
ctx: &rustyline::Context<'_>,
) -> rustyline::Result<(usize, Vec<Self::Candidate>)> {
let mut idx = 0;
let mut buf = String::new();
let mut start = 0;
for char in line.chars() {
if buf.is_empty() && char.is_alphabetic() {
start = idx;
buf.push(char);
} else if !buf.is_empty() && char.is_alphanumeric() {
buf.push(char);
} else {
if idx >= pos {
break;
} else {
buf.clear();
}
}
idx += char.len_utf8();
}
let _ = (line, pos, ctx);
let globals = self.vm.borrow().global_names();
let names: Vec<Rc<str>> = globals
.borrow()
.clone()
.into_iter()
.filter(|n| n.starts_with(&buf))
.collect();
if buf.is_empty() {
start = pos;
}
Ok((start, names))
}
fn update(&self, line: &mut rustyline::line_buffer::LineBuffer, start: usize, elected: &str, cl: &mut rustyline::Changeset) {
let end = line.pos();
line.replace(start..end, elected, cl);
}
}

View file

@ -1,9 +1,10 @@
use std::{path::PathBuf, io::{self, Read, IsTerminal}, fs};
use clap::Parser as ArgParser;
use std::{path::PathBuf, io::{self, Read, IsTerminal}, fs, cell::RefCell, rc::Rc};
use clap::{Parser as ArgParser, ColorChoice};
use matrix::{compiler::{Compiler, CompilerBuilder}, vm::Vm, parse::{Parser, ParserBuilder}, value::Value};
use repl::Repl;
mod repl;
mod helper;
#[derive(Debug, ArgParser)]
#[command(version, long_about = None)]
@ -19,6 +20,10 @@ pub struct Args {
#[arg(short, long)]
debug: bool,
/// Choses color
#[arg(short, long)]
color: Option<ColorChoice>,
/// Disables optimizations
#[arg(long)]
disable_optimizations: bool,
@ -27,8 +32,11 @@ pub struct Args {
pub struct State<'a> {
parser: Parser,
compiler: Compiler<'a>,
vm: Vm,
repl: bool
vm: Rc<RefCell<Vm>>,
repl: bool,
color: bool,
#[allow(unused)]
debug: bool,
}
impl<'a> State<'a> {
@ -60,20 +68,32 @@ impl<'a> State<'a> {
matrix_stdlib::load(&mut vm);
(Self { parser, vm, compiler, repl }, file)
let color = match args.color {
Some(ColorChoice::Auto) | None => {
io::stdout().is_terminal()
},
Some(ColorChoice::Always) => true,
Some(ColorChoice::Never) => false,
};
(Self { parser, vm: Rc::new(RefCell::new(vm)), compiler, repl, debug: args.debug, color }, file)
}
pub fn execute(&mut self, code: String) -> matrix::Result<Value> {
let ast = self.parser.parse(code)?;
let fun = self.compiler.compile(&ast)?;
let val = self.vm.run(fun)?;
let val = self.vm.try_borrow_mut().unwrap().run(fun)?;
Ok(val)
}
}
pub fn error(err: matrix::Error) {
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);
}
}

View file

@ -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,27 +17,42 @@ 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 {
if self.state.color {
println!("{val:#}");
} else {
println!("{val}");
}
}
}
}
let _ = std::io::stdout().flush();
}
}

View file

@ -1,16 +1,15 @@
use proc_macro::TokenStream;
use syn::{ItemFn, parse::Parse, Token, LitBool, LitInt};
use syn::{ItemFn, parse::Parse, Token, LitInt};
use quote::quote;
struct NativeFuncParams {
arity: LitInt,
variadic: LitBool,
variadic: Option<Token![..]>,
}
impl Parse for NativeFuncParams {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let arity = input.parse()?;
input.parse::<Token![,]>()?;
let variadic = input.parse()?;
Ok(Self { arity , variadic })
}
@ -22,7 +21,7 @@ pub fn native_func(input: TokenStream, annotated_item: TokenStream) -> TokenStre
let input: NativeFuncParams = syn::parse(input).unwrap();
let arity = input.arity;
let variadic = input.variadic;
let variadic = input.variadic.is_some();
let visibility = itemfn.vis;
let block = itemfn.block;
@ -43,7 +42,11 @@ pub fn native_func(input: TokenStream, annotated_item: TokenStream) -> TokenStre
name: ::std::rc::Rc::from( #name_str ),
arity: #arity,
variadic: #variadic,
fun: ::matrix::chunk::InnerFunction::Native(Box::new(|#inputs| #output #block))
fun: ::matrix::chunk::InnerFunction::Native(
::std::rc::Rc::new(
|#inputs| #output #block
)
)
})
}
};

181
matrix-stdlib/src/core.rs Normal file
View file

@ -0,0 +1,181 @@
use std::hash::{DefaultHasher, Hash, Hasher};
use matrix::{vm::Vm, value::Value, unpack_args, Result, unpack_varargs};
use matrix_macros::native_func;
use crate::{VmArgs, error};
fn to_radix(r: i64, mut n: i64) -> String {
let mut result = String::new();
let mut idx = 0;
if n == 0 {
result.push('0');
return result
} else if n < 0 {
n = -n;
idx = 1;
result.push('-');
}
while n != 0 {
let c = std::char::from_digit((n % r) as u32, r as u32).unwrap();
result.insert(idx, c);
n /= r;
}
result
}
#[native_func(1)]
fn bin(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [value] = unpack_args!(args);
match value {
Value::Int(n) => Ok(Value::String(to_radix(2, n).into())),
_ => error!("bin requires a integer agument")
}
}
#[native_func(1)]
fn sex(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [value] = unpack_args!(args);
match value {
Value::Int(n) => Ok(Value::String(to_radix(6, n).into())),
_ => error!("sex requires a integer agument")
}
}
#[native_func(1)]
fn oct(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [value] = unpack_args!(args);
match value {
Value::Int(n) => Ok(Value::String(to_radix(8, n).into())),
_ => error!("oct requires a integer agument")
}
}
#[native_func(1)]
fn hex(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [value] = unpack_args!(args);
match value {
Value::Int(n) => Ok(Value::String(to_radix(16, n).into())),
_ => error!("hex requires a integer agument")
}
}
#[native_func(2)]
fn radix(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [radix, value] = unpack_args!(args);
match (radix, value) {
(Value::Int(r), Value::Int(n)) => Ok(Value::String(to_radix(r, n).into())),
_ => error!("radix requires two integer aguments")
}
}
#[native_func(1..)]
fn append(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let ([list], args) = unpack_varargs!(args);
let Value::List(mut list) = list else {
return error!("append requires a list")
};
for arg in args {
list.push(arg);
};
Ok(Value::List(list))
}
#[native_func(2)]
fn push(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [list, value] = unpack_args!(args);
let Value::List(mut list) = list else {
return error!("push requires a list")
};
list.push(value);
Ok(Value::List(list))
}
#[native_func(1)]
fn pop(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [list] = unpack_args!(args);
let Value::List(mut list) = list else {
return error!("pop requires a list")
};
match list.pop() {
Some(v) => Ok(v),
None => Ok(Value::Nil)
}
}
#[native_func(2)]
fn remove(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [list, index] = unpack_args!(args);
let Value::List(mut list) = list else {
return error!("remove requires a list")
};
let Value::Int(i) = index else {
return error!("remove reuqires a int index");
};
if i < 0 || i as usize >= list.len() {
Ok(Value::Nil)
} else {
Ok(list.remove(i as usize))
}
}
#[native_func(1)]
fn hash(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [value] = unpack_args!(args);
if let Err(e) = value.can_hash() {
return Err(e)
};
let mut hasher = DefaultHasher::new();
value.hash(&mut hasher);
let fin = hasher.finish();
Ok(Value::Int(fin as u32 as i64))
}
#[native_func(1)]
fn ord(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [value] = unpack_args!(args);
let Value::String(str) = value else {
return error!("ord requires a 1 length string")
};
if str.len() != 1 {
return error!("ord requires a 1 length string")
}
let char = str.chars().next().unwrap();
Ok(Value::Int(char as i64))
}
#[native_func(1)]
fn chr(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [value] = unpack_args!(args);
let Value::Int(i) = value else {
return error!("chr requires an int")
};
match char::from_u32(i as u32) {
Some(c) => Ok(Value::String(String::from(c).into())),
None => error!("unable to get char from: {}", i as u32)
}
}
#[native_func(1)]
fn str(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [value] = unpack_args!(args);
Ok(Value::String(format!("{value}").into()))
}
pub fn load(vm: &mut Vm) {
vm.load_global_fn(bin(), "bin");
vm.load_global_fn(sex(), "sex");
vm.load_global_fn(oct(), "oct");
vm.load_global_fn(hex(), "hex");
vm.load_global_fn(radix(), "radix");
vm.load_global_fn(str(), "str");
vm.load_global_fn(append(), "append");
vm.load_global_fn(push(), "push");
vm.load_global_fn(pop(), "pop");
vm.load_global_fn(remove(), "remove");
vm.load_global_fn(hash(), "hash");
vm.load_global_fn(ord(), "ord");
vm.load_global_fn(chr(), "chr");
}

View file

@ -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<Value>) -> Result<Value> {
let [values] = args.try_into().unwrap();
if let Value::List(list) = values {
for (i, value) in list.iter().enumerate() {
print!("{}", value.boring_print());
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<Value>) -> Result<Value> {
let ([], varags) = unpack_varargs!(args);
for (i, value) in varags.into_iter().enumerate() {
if i != 0 {
print!(" ");
}
}
print!("{value}");
}
Ok(Value::Nil)
}
#[native_func(1, true)]
fn println(_vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [values] = args.try_into().unwrap();
if let Value::List(list) = values {
for (i, value) in list.iter().enumerate() {
print!("{}", value.boring_print());
#[native_func(0..)]
fn println(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let ([], varags) = unpack_varargs!(args);
for (i, value) in varags.into_iter().enumerate() {
if i != 0 {
print!(" ");
}
}
print!("{value}");
}
print!("\n");
Ok(Value::Nil)
}
pub fn load(vm: &mut Vm) {
vm.load_native_fn(print());
vm.load_native_fn(println());
#[native_func(0)]
fn readln(_: VmArgs, _: Vec<Value>) -> Result<Value> {
let mut input = String::new();
match io::stdin().read_line(&mut input) {
Ok(_) => {
match input.pop() {
Some(c) if c == '\n' => {},
Some(c) => input.push(c),
None => {}
};
Ok(Value::String(input.into()))
},
Err(err) => error!("cant read from stdin: {err}")
}
}
#[native_func(1)]
fn input(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [prompt] = unpack_args!(args);
let mut input = String::new();
print!("{prompt}");
let _ = io::stdout().flush();
match io::stdin().read_line(&mut input) {
Ok(_) => {
match input.pop() {
Some(c) if c == '\n' => {},
Some(c) => input.push(c),
None => {}
};
Ok(Value::String(input.into()))
},
Err(err) => error!("cant read from stdin: {err}")
}
}
#[native_func(0)]
fn readlines(_: VmArgs, _: Vec<Value>) -> Result<Value> {
let lines = RefCell::new(io::stdin().lines());
Ok(iter!(move |_,_| {
match lines.borrow_mut().next() {
Some(Ok(line)) => Ok(Value::String(line.into())),
_ => Ok(Value::Nil)
}
}))
}
#[native_func(2)]
fn file_open(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [path, mode] = unpack_args!(args);
let Value::String(mode) = mode else {
return error!("open mode must be a string")
};
let Value::String(path) = path else {
return error!("open path must be a string")
};
let file = match mode.as_ref() {
"r" => OpenOptions::new().read(true).open(path.as_ref()),
"w" => OpenOptions::new().write(true).create(true).truncate(true).open(path.as_ref()),
"a" => OpenOptions::new().write(true).create(true).append(true).open(path.as_ref()),
"r+" => OpenOptions::new().read(true).write(true).open(path.as_ref()),
"w+" => OpenOptions::new().read(true).write(true).create(true).truncate(true).open(path.as_ref()),
"a+" => OpenOptions::new().read(true).write(true).create(true).append(true).open(path.as_ref()),
_ => return error!("invalid open mode: {mode}")
};
match file {
Ok(file) => Ok(Value::File(Rc::new(RefCell::new(file)))),
Err(err) => return error!("cannot open '{path}': {err}")
}
}
#[native_func(1)]
fn file_read(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [file] = unpack_args!(args);
let Value::File(file) = file else {
return error!("file read requires a file")
};
let mut contents = String::new();
if let Err(err) = file.try_borrow_mut().unwrap().read_to_string(&mut contents) {
return error!("cannot read file: '{err}'")
};
Ok(Value::String(contents.into()))
}
#[native_func(1)]
fn file_lines(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [file] = unpack_args!(args);
let Value::File(file) = file else {
return error!("file read requires a file")
};
let mut contents = String::new();
if let Err(err) = file.try_borrow_mut().unwrap().read_to_string(&mut contents) {
return error!("cannot read file: '{err}'")
};
let lines: Vec<Rc<str>> = contents.split_inclusive("\n").map(|s| Rc::from(s)).collect();
let lines = RefCell::new(lines.into_iter());
Ok(iter!(move |_,_| {
match lines.borrow_mut().next() {
Some(line) => Ok(Value::String(line)),
None => Ok(Value::Nil)
}
}))
}
#[native_func(2)]
fn file_write(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [file, content] = unpack_args!(args);
let Value::File(file) = file else {
return error!("file write requires a file")
};
let content = format!("{content}");
if let Err(err) = file.try_borrow_mut().unwrap().write_all(content.as_bytes()) {
return error!("cannot write file: '{err}'")
};
Ok(Value::Nil)
}
#[native_func(0..)]
fn throw(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let ([], varargs) = unpack_varargs!(args);
let mut str = String::new();
for (i, v) in varargs.into_iter().enumerate() {
if i != 0 {
str.push_str(" ");
}
str.push_str(&format!("{v}"));
}
error!("{str}")
}
pub fn load(vm: &mut Vm) {
vm.load_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");
}

544
matrix-stdlib/src/iter.rs Normal file
View file

@ -0,0 +1,544 @@
use std::{cell::RefCell, rc::Rc};
use matrix::{iter, vm::Vm, value::Value, Result, unpack_varargs, unpack_args};
use matrix_macros::native_func;
use crate::{error, VmArgs};
use Value as V;
macro_rules! next {
($vm:expr, $frame:expr, $iter:expr) => {
$vm.run_fn($frame, $iter.clone(), vec![])
};
}
#[native_func(1)]
fn len(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [value] = unpack_args!(args);
let len = match value {
V::List(l) => l.len(),
V::Matrix(m) => m.values.len(),
V::String(s) => s.len(),
V::Table(t) => t.len(),
_ => 1
};
Ok(V::Int(len as i64))
}
#[native_func(1)]
fn sum((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
let [iter] = unpack_args!(args);
let iter = iter.into_iter_fn()?;
let mut res = next!(vm, frame, iter)?;
if res == Value::Nil {
return Ok(res)
};
loop {
let next = next!(vm, frame, iter)?;
if next == Value::Nil { break }
res = (res + next)?;
}
Ok(res)
}
#[native_func(0..)]
fn range(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let ([], varargs) = unpack_varargs!(args);
let (min, max, step) = match varargs.as_slice() {
[V::Int(max)] => {
(0, *max, 1)
},
[V::Int(min), V::Int(max)] => {
(*min, *max, 1)
},
[V::Int(min), V::Int(max), V::Int(step)] => {
(*min, *max, *step)
},
_ => return error!("range takes 1 to 3 [Int]'s")
};
let i = RefCell::new(min);
Ok(iter!(move |_,_| {
let curr = *(i.borrow());
*(i.borrow_mut()) = curr + step;
if curr >= max {
return Ok(V::Nil)
} else {
return Ok(V::Int(curr))
}
}))
}
#[native_func(1)]
fn iter(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [value] = unpack_args!(args);
Ok(value.into_iter()?)
}
#[native_func(1)]
fn once(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [value] = unpack_args!(args);
let first = RefCell::new(false);
Ok(iter!(move |_,_| {
if *first.borrow() == false {
*first.borrow_mut() = true;
Ok(value.clone())
} else {
Ok(Value::Nil)
}
}))
}
#[native_func(1)]
fn list((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
let [iter] = unpack_args!(args);
let iter = iter.into_iter_fn()?;
let mut list = Vec::new();
loop {
let res = next!(vm, frame, iter)?;
if res == Value::Nil { break }
list.push(res);
}
Ok(Value::List(list.into()))
}
#[native_func(2)]
fn map(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [fun, iter] = unpack_args!(args);
let iter = iter.into_iter_fn()?;
let Value::Function(fun) = fun else {
return error!("map 1st arg must be a function")
};
Ok(iter!(move |(vm,frame),_| {
let input = next!(vm, frame, iter)?;
if input == Value::Nil {
return Ok(input)
}
vm.run_fn(frame, fun.clone(), vec![input])
}))
}
#[native_func(2)]
fn fold((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
let [fun, iter] = unpack_args!(args);
let iter = iter.into_iter_fn()?;
let Value::Function(fun) = fun else {
return error!("fold 1st arg must be a function")
};
let mut res = next!(vm, frame, iter)?;
if res == Value::Nil {
return Ok(Value::Nil)
};
loop {
let next = next!(vm, frame, iter)?;
if next == Value::Nil {
return Ok(res)
};
res = vm.run_fn(frame, fun.clone(), vec![res, next])?;
}
}
#[native_func(3)]
fn foldi((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
let [init, fun, iter] = unpack_args!(args);
let iter = iter.into_iter_fn()?;
let Value::Function(fun) = fun else {
return error!("foldi 1st arg must be a function")
};
let mut res = init;
loop {
let next = next!(vm, frame, iter)?;
if next == Value::Nil {
return Ok(res)
};
res = vm.run_fn(frame, fun.clone(), vec![res, next])?;
}
}
#[native_func(1)]
fn count((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
let [iter] = unpack_args!(args);
let iter = iter.into_iter_fn()?;
let mut len = 0;
loop {
let res = vm.run_fn(frame, iter.clone(), vec![])?;
if res == Value::Nil { break };
len += 1;
}
Ok(Value::Int(len))
}
#[native_func(3)]
fn scan(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [init, fun, iter] = unpack_args!(args);
let iter = iter.into_iter_fn()?;
let Value::Function(fun) = fun else {
return error!("scan 2nd arg must be a function")
};
let res = RefCell::new(init);
Ok(iter!(move |(vm,frame),_| {
let next = next!(vm, frame, iter)?;
if next == Value::Nil {
return Ok(Value::Nil)
};
let res_next = vm.run_fn(frame, fun.clone(), vec![res.borrow().clone(), next])?;
*res.borrow_mut() = res_next;
Ok(res.borrow().clone())
}))
}
#[native_func(2)]
fn filter(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [fun, iter] = unpack_args!(args);
let iter = iter.into_iter_fn()?;
let Value::Function(fun) = fun else {
return error!("filter 1st arg must be a function")
};
Ok(iter!(move |(vm,frame),_| {
loop {
let next = next!(vm, frame, iter)?;
if next == Value::Nil {
return Ok(Value::Nil)
}
let res = vm.run_fn(frame, fun.clone(), vec![next.clone()])?;
if !!res {
return Ok(next)
}
}
}))
}
#[native_func(0..)]
fn chain(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let ([], iters) = unpack_varargs!(args);
let mut chaind = Vec::new();
for iter in iters {
chaind.push(iter.into_iter_fn()?);
}
chaind.reverse();
let chaind = RefCell::new(chaind);
Ok(iter!(move |(vm,frame), _| {
loop {
let curr = match chaind.borrow_mut().last() {
Some(iter) => iter.clone(),
None => return Ok(Value::Nil)
};
match vm.run_fn(frame, curr.clone(), vec![]) {
Ok(Value::Nil) => {
chaind.borrow_mut().pop();
continue;
},
v => return v
};
}
}))
}
#[native_func(1)]
fn lines(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [str] = unpack_args!(args);
let Value::String(str) = str else {
return error!("lines arg must be a string")
};
let lines: Vec<Rc<str>> = str.split_inclusive("\n").map(|s| Rc::from(s)).collect();
let res = RefCell::new(lines.into_iter());
Ok(iter!(move |_,_| {
match res.borrow_mut().next() {
Some(line) => Ok(Value::String(line)),
None => Ok(Value::Nil)
}
}))
}
#[native_func(1)]
fn skip((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
let [count, iter] = unpack_args!(args);
let iter = iter.into_iter_fn()?;
let Value::Int(i) = count else {
return error!("skip count requires a int")
};
for _ in 0..i {
next!(vm, frame, iter)?;
}
Ok(Value::Iter(iter))
}
#[native_func(2)]
fn alternate(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [il, ir] = unpack_args!(args);
let il = il.into_iter_fn()?;
let ir = ir.into_iter_fn()?;
let flag = RefCell::new(Some(0));
Ok(iter!(move |(vm, frame),_| {
let f = *flag.borrow();
match f {
Some(0) => {
let val = next!(vm, frame, il)?;
if val == Value::Nil {
*flag.borrow_mut() = None;
} else {
*flag.borrow_mut() = Some(1);
}
Ok(val)
},
Some(1) => {
let val = next!(vm, frame, ir)?;
if val == Value::Nil {
*flag.borrow_mut() = None;
} else {
*flag.borrow_mut() = Some(0);
}
Ok(val)
},
_ => Ok(Value::Nil)
}
}))
}
#[native_func(2)]
fn intersperse(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [value, iter] = unpack_args!(args);
let iter = iter.into_iter_fn()?;
let flag = RefCell::new(Some(0));
Ok(iter!(move |(vm, frame),_| {
let f = *flag.borrow();
match f {
Some(0) => {
let val = next!(vm, frame, iter)?;
if val == Value::Nil {
*flag.borrow_mut() = None;
} else {
*flag.borrow_mut() = Some(1);
}
Ok(val)
},
Some(1) => {
let val = value.clone();
*flag.borrow_mut() = Some(0);
Ok(val)
},
_ => Ok(Value::Nil)
}
}))
}
#[native_func(2)]
fn zip(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [il, ir] = unpack_args!(args);
let il = il.into_iter_fn()?;
let ir = ir.into_iter_fn()?;
let flag = RefCell::new(true);
Ok(iter!(move |(vm, frame),_| {
let f = *flag.borrow();
match f {
true => {
let vl = next!(vm, frame, il)?;
let vr = next!(vm, frame, ir)?;
if vl == Value::Nil || vr == Value::Nil {
*flag.borrow_mut() = false;
}
Ok(Value::List(vec![vl, vr].into()))
},
_ => Ok(Value::Nil)
}
}))
}
#[native_func(1)]
fn unzip((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
let [iter] = unpack_args!(args);
let iter = iter.into_iter_fn()?;
let mut ll = Vec::new();
let mut lr = Vec::new();
loop {
let val = next!(vm, frame, iter)?;
if val == Value::Nil {
break;
}
let Value::List(vals) = val else {
return error!("unzip only works over a iterator of pairs");
};
let vals = vals.into_inner();
if vals.len() != 2 {
return error!("unzip only works over a iterator of pairs");
}
let [l, r] = vals.try_into().unwrap();
ll.push(l);
lr.push(r);
}
let ll = Value::List(ll.into());
let lr = Value::List(lr.into());
Ok(Value::List(vec![ll, lr].into()))
}
#[native_func(1)]
fn cycle((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
let [iter] = unpack_args!(args);
let iter = iter.into_iter_fn()?;
let mut values = Vec::new();
loop {
let val = next!(vm, frame, iter)?;
if val == Value::Nil { break };
values.push(val);
}
let idx = RefCell::new(0_usize);
Ok(iter!(move |_,_| {
let i = *idx.borrow();
*idx.borrow_mut() += 1;
*idx.borrow_mut() %= values.len();
Ok(values[i].clone())
}))
}
#[native_func(2)]
fn take((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
let [count, iter] = unpack_args!(args);
let iter = iter.into_iter_fn()?;
let Value::Int(count) = count else {
return error!("take requires an int amount to collect")
};
let mut values = Vec::new();
for _ in 0..count {
let val = next!(vm, frame, iter)?;
if val == Value::Nil { break };
values.push(val);
}
Ok(Value::List(values.into()))
}
#[native_func(2)]
fn last((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
let [iter] = unpack_args!(args);
let iter = iter.into_iter_fn()?;
let mut last = Value::Nil;
loop {
let val = next!(vm, frame, iter)?;
if val == Value::Nil { break };
last = val;
}
Ok(last)
}
#[native_func(2)]
fn next((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
let [iter] = unpack_args!(args);
let iter = iter.into_iter_fn()?;
let val = next!(vm, frame, iter)?;
Ok(val)
}
#[native_func(1)]
fn min((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
let [iter] = unpack_args!(args);
let iter = iter.into_iter_fn()?;
let mut min = Value::Nil;
loop {
let val = next!(vm, frame, iter)?;
if val == Value::Nil { break };
if min == Value::Nil || val < min {
min = val;
}
}
Ok(min)
}
#[native_func(1)]
fn max((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
let [iter] = unpack_args!(args);
let iter = iter.into_iter_fn()?;
let mut max = Value::Nil;
loop {
let val = next!(vm, frame, iter)?;
if val == Value::Nil { break };
if max == Value::Nil || val > max {
max = val;
}
}
Ok(max)
}
#[native_func(1)]
fn rev((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
let [iter] = unpack_args!(args);
let iter = iter.into_iter_fn()?;
let mut values = Vec::new();
loop {
let val = next!(vm, frame, iter)?;
if val == Value::Nil { break };
values.push(val);
}
let values = RefCell::new(values);
Ok(iter!(move |_,_| {
match values.borrow_mut().pop() {
Some(v) => Ok(v),
None => Ok(Value::Nil)
}
}))
}
#[native_func(1)]
fn enumerate(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [iter] = unpack_args!(args);
let iter = iter.into_iter_fn()?;
let idx = RefCell::new(0_i64);
Ok(iter!(move |(vm, frame),_| {
let val = next!(vm, frame, iter)?;
if val == Value::Nil {
return Ok(Value::Nil)
};
let curr = *idx.borrow();
*idx.borrow_mut() += 1;
Ok(Value::List(vec![Value::Int(curr), val].into()))
}))
}
#[native_func(1)]
fn iterable(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [value] = unpack_args!(args);
use Value as V;
match value {
V::Function(_) |
V::List(_) |
V::Range(_) |
V::Iter(_)
=> Ok(V::Bool(true)),
_ => Ok(V::Bool(false))
}
}
pub fn load(vm: &mut Vm) {
vm.load_global_fn(take(), "take");
vm.load_global_fn(unzip(), "unzip");
vm.load_global_fn(count(), "count");
vm.load_global_fn(len(), "len");
vm.load_global_fn(sum(), "sum");
vm.load_global_fn(min(), "min");
vm.load_global_fn(max(), "max");
vm.load_global_fn(next(), "next");
vm.load_global_fn(last(), "last");
vm.load_global_fn(list(), "list");
vm.load_global_fn(fold(), "fold");
vm.load_global_fn(foldi(), "foldi");
vm.load_global_fn(scan(), "scan");
vm.load_global_fn(chain(), "chain");
vm.load_global_fn(lines(), "lines");
vm.load_global_fn(skip(), "skip");
vm.load_global_fn(once(), "once");
vm.load_global_fn(iter(), "iter");
vm.load_global_fn(range(), "range");
vm.load_global_fn(map(), "map");
vm.load_global_fn(filter(), "filter");
vm.load_global_fn(skip(), "skip");
vm.load_global_fn(zip(), "zip");
vm.load_global_fn(cycle(), "cycle");
vm.load_global_fn(alternate(), "alternate");
vm.load_global_fn(intersperse(), "intersperse");
vm.load_global_fn(rev(), "rev");
vm.load_global_fn(enumerate(), "enumerate");
vm.load_global_fn(iterable(), "iterable");
}

View file

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

518
matrix-stdlib/src/math.rs Normal file
View file

@ -0,0 +1,518 @@
use core::f64;
use std::f64::{consts::{PI, E, TAU}, NAN, INFINITY};
use matrix::{vm::Vm, value::{Value, Matrix}, Result, unpack_args, Rational64, Complex64};
use matrix_macros::native_func;
use crate::{error, VmArgs};
#[native_func(1)]
fn trans(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [value] = unpack_args!(args);
let mat = match value {
Value::Matrix(m) => m,
Value::List(l) => Matrix::from_list(l.to_vec()).into(),
_ => return error!("trans must be given a matrix")
};
let values = mat
.cols()
.reduce(|mut a, b| {a.extend(b); a})
.unwrap()
.into_iter()
.map(|e| e.clone())
.collect();
Ok(Value::Matrix(Matrix::new(mat.codomain, mat.domain, values).into()))
}
fn mat_gauss_row_operation(
r1: usize,
r2: usize,
scale: Value,
mat: &mut Matrix
) -> Result<()> {
for col in 0..mat.domain {
let r1v = mat.get(r1, col)?;
let r2v = mat.get(r2, col)?;
let res = (r1v - (r2v * scale.clone())?)?;
mat.set(r1, col, res)?;
}
Ok(())
}
fn mat_swap_rows(
r1: usize,
r2: usize,
mat: &mut Matrix
) -> Result<()> {
let cols = mat.domain;
for col in 0..cols {
let a = mat.get(r1, col)?;
let b = mat.get(r2, col)?;
mat.set(r2, col, a)?;
mat.set(r1, col, b)?;
}
Ok(())
}
fn mat_find_non_zero_col(
mat: &Matrix
) -> Option<usize> {
for (i,col) in mat.cols().enumerate() {
for val in col.iter() {
if **val != Value::Int(0) {
return Some(i)
}
}
}
return None
}
fn mat_scale_pivot_row(
row: usize,
mat: &mut Matrix
) -> Result<()> {
let scale = mat.get(row, row)?;
if scale.is_zero() {
return Ok(())
}
for col in 0..mat.domain {
let res = (mat.get(row, col)?.clone() / scale.clone())?;
mat.set(row, col, res)?;
}
Ok(())
}
fn mat_get_non_zero_pivot_row(
row: usize,
mat: &mut Matrix,
) -> Result<()> {
let col = row;
let test = mat.get(row, col)?;
if test.is_zero() {
for r in row..mat.codomain {
let cur = mat.get(r, col)?;
if !cur.is_zero() {
mat_swap_rows(row, r, mat)?;
break;
}
}
}
mat_scale_pivot_row(row, mat)?;
Ok(())
}
fn mat_rref(mat: Matrix) -> Result<Matrix> {
let mut mat = mat;
let Some(start) = mat_find_non_zero_col(&mat) else {
return Ok(mat)
};
let end = mat.domain.min(mat.codomain);
for col in start..end {
let pivot_row = col;
mat_get_non_zero_pivot_row(pivot_row, &mut mat)?;
if mat.get(pivot_row, col)?.is_zero() {
break
}
for row in 0..mat.codomain {
if row == pivot_row { continue; };
let scale = mat.get(row, col)?;
mat_gauss_row_operation(row, pivot_row, scale, &mut mat)?;
}
}
Ok(mat)
}
#[native_func(1)]
fn rref(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [value] = unpack_args!(args);
let mat = match value {
Value::Matrix(m) => m,
Value::List(l) => Matrix::from_list(l.to_vec()).into(),
_ => return error!("trans must be given a matrix")
};
Ok(Value::Matrix(mat_rref(mat.into_inner())?.into()))
}
fn mat_det(mat: Matrix) -> Result<Value> {
if mat.domain == 1 {
return Ok(mat.get(0,0)?)
}
if mat.domain == 2 {
let a = mat.get(0,0)? * mat.get(1,1)?;
let b = mat.get(0,1)? * mat.get(1,0)?;
return Ok((a? - b?)?)
}
let mut res = Value::Int(0);
for col in 0..mat.domain {
let sub_values = mat.rows()
.skip(1)
.map(|r|
r.into_iter()
.enumerate()
.filter(|(idx,_)| *idx != col)
.map(|(_, v)| v.clone())
.collect::<Vec<Value>>()
)
.reduce(|mut a, b| {a.extend(b); a})
.unwrap();
let sub = Matrix::new(mat.domain - 1, mat.domain - 1, sub_values);
let val = mat.get(0, col)?;
let part = (val * mat_det(sub)?)?;
if col % 2 == 0 {
res = (res + part)?;
} else {
res = (res - part)?;
}
}
Ok(res)
}
#[native_func(1)]
fn det(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [value] = unpack_args!(args);
let mat = match value {
Value::Matrix(m) if m.domain == m.codomain => m,
Value::List(l) if l.len() == 1 => Matrix::from_list(l.to_vec()).into(),
_ => return error!("det requires a square matrix")
};
let mat = mat.into_inner();
Ok(mat_det(mat)?)
}
fn mat_ident(dim: usize) -> Matrix {
let len = dim * dim;
let mut values = vec![Value::Int(0); len];
let mut idx = 0;
loop {
if idx >= len { break };
values[idx] = Value::Int(1);
idx += dim + 1;
}
Matrix::new(dim, dim, values)
}
#[native_func(1)]
fn ident(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [value] = unpack_args!(args);
let dim = match value {
Value::Int(i) if i > 0 => i,
Value::Ratio(r)
if *r.denom() == 1 &&
*r.numer() > 0
=> *r.numer(),
_ => return error!("ident requries a positive [Int] dimension")
};
Ok(Value::Matrix(mat_ident(dim as usize).into()))
}
fn mat_splith(mat: Matrix) -> (Matrix, Matrix) {
let mut m1 = Vec::new();
let mut m2 = Vec::new();
mat.rows()
.for_each(|r| {
let split = r.len() / 2;
r.into_iter().enumerate().for_each(|(i, v)| {
if i < split {
m1.push(v.clone());
} else {
m2.push(v.clone());
}
})
});
let m1 = Matrix::new(mat.domain/2, mat.codomain, m1);
let m2 = Matrix::new(mat.domain/2, mat.codomain, m2);
(m1, m2)
}
#[native_func(1)]
fn inv(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [value] = unpack_args!(args);
let mat = match value {
Value::Matrix(m) if m.domain == m.codomain => m,
Value::List(l) if l.len() == 1 => Matrix::from_list(l.to_vec()).into(),
_ => return error!("det requires a square matrix")
};
let mat = mat.into_inner();
let ident = mat_ident(mat.domain);
let joined = mat.join_right(&ident)?;
let refed = mat_rref(joined)?;
let (new_ident, new_inv) = mat_splith(refed);
if new_ident == ident {
Ok(Value::Matrix(new_inv.into()))
} else {
error!("matrix does not have an inverse")
}
}
macro_rules! mathr {
($type:ident) => {
#[native_func(1)]
fn $type(_: VmArgs, args: Vec<Value>) -> Result<Value> {
use Value as V;
let [value] = unpack_args!(args);
match value {
V::Int(i) => Ok(V::Int(i)),
V::Ratio(r) => Ok(V::Ratio(r.$type())),
V::Float(f) => Ok(V::Float(f.$type())),
v => error!("cannot compute {} on {v}", stringify!($type))
}
}
};
}
macro_rules! trig {
($type:ident) => {
#[native_func(1)]
fn $type(_: VmArgs, args: Vec<Value>) -> Result<Value> {
use Value as V;
let [value] = unpack_args!(args);
match value.promote_trig() {
V::Float(f) => Ok(V::Float(f.$type())),
V::Complex(c) => Ok(V::Complex(c.$type())),
v => error!("cannot compute {} on {v}", stringify!($type))
}
}
};
}
macro_rules! trigf {
($type:ident, $str:ident) => {
#[native_func(1)]
fn $str(_: VmArgs, args: Vec<Value>) -> Result<Value> {
use Value as V;
let [value] = unpack_args!(args);
match value.promote_trig() {
V::Float(f) => Ok(V::Float(f.$type())),
v => error!("cannot compute {} on {v}", stringify!($str))
}
}
};
}
#[native_func(2)]
fn log(_: VmArgs, args: Vec<Value>) -> Result<Value> {
use Value as V;
let [base, value] = unpack_args!(args);
match (base.promote_trig(), value.promote_trig()) {
(V::Float(base), V::Float(arg)) => Ok(V::Float(arg.log(base))),
(V::Float(base), V::Complex(arg)) => Ok(V::Complex(arg.log(base))),
(V::Complex(base), V::Float(arg)) => Ok(V::Complex(arg.ln() / base.ln())),
(V::Complex(base), V::Complex(arg)) => Ok(V::Complex(arg.ln() / base.ln())),
(base, arg) => error!("cannot compute log base {base} argument {arg}")
}
}
#[native_func(1)]
fn abs(_: VmArgs, args: Vec<Value>) -> Result<Value> {
use Value as V;
let [value] = unpack_args!(args);
match value {
V::Int(i) => Ok(V::Int(i.abs())),
V::Float(f) => Ok(V::Float(f.abs())),
V::Ratio(r) => Ok(V::Ratio(Rational64::new(r.numer().abs(), r.denom().abs()))),
V::Complex(c) => Ok(V::Float(c.norm())),
arg => error!("cannot compute abs for {arg}")
}
}
#[native_func(1)]
fn fract(_: VmArgs, args: Vec<Value>) -> Result<Value> {
use Value as V;
let [value] = unpack_args!(args);
match value {
V::Int(_) => Ok(V::Int(0)),
V::Float(f) => Ok(V::Float(f.fract())),
V::Ratio(r) => Ok(V::Ratio(r.fract())),
arg => error!("cannot compute fract for {arg}")
}
}
#[native_func(1)]
fn sign(_: VmArgs, args: Vec<Value>) -> Result<Value> {
use Value as V;
let [value] = unpack_args!(args);
match value {
V::Int(i) => Ok(V::Int(i.signum())),
V::Ratio(r) => Ok(V::Int(r.numer().signum())),
V::Float(f) => Ok(V::Float(f.signum())),
arg => error!("cannot compute sign for {arg}")
}
}
#[native_func(1)]
fn int(_: VmArgs, args: Vec<Value>) -> Result<Value> {
use Value as V;
let [value] = unpack_args!(args);
match value {
V::Int(i) => Ok(V::Int(i)),
V::Ratio(r) => Ok(V::Int(r.numer() / r.denom())),
V::Float(f) => Ok(V::Int(f as i64)),
V::Complex(c) => Ok(V::Int(c.re as i64)),
arg => error!("cannot cast {arg} to int")
}
}
#[native_func(1)]
fn ratio(_: VmArgs, args: Vec<Value>) -> Result<Value> {
use Value as V;
let [value] = unpack_args!(args);
match value {
V::Int(i) => Ok(V::Ratio(Rational64::new(i, 1))),
V::Ratio(r) => Ok(V::Ratio(r)),
V::Float(f) => Ok(V::Ratio(Rational64::approximate_float(f).unwrap_or(Rational64::new(0, 1)))),
V::Complex(c) => Ok(V::Ratio(Rational64::approximate_float(c.re).unwrap_or(Rational64::new(0, 1)))),
arg => error!("cannot cast {arg} to ratio")
}
}
#[native_func(1)]
fn float(_: VmArgs, args: Vec<Value>) -> Result<Value> {
use Value as V;
let [value] = unpack_args!(args);
match value {
V::Int(i) => Ok(V::Float(i as f64)),
V::Ratio(r) => Ok(V::Float((*r.numer() as f64) / (*r.denom() as f64))),
V::Float(f) => Ok(V::Float(f)),
V::Complex(c) => Ok(V::Float(c.re)),
arg => error!("cannot cast {arg} to float")
}
}
#[native_func(1)]
fn complex(_: VmArgs, args: Vec<Value>) -> Result<Value> {
use Value as V;
let [value] = unpack_args!(args);
match value {
V::Int(i) => Ok(V::Complex(Complex64::new(i as f64, 0.0))),
V::Ratio(r) => Ok(V::Complex(Complex64::new((*r.numer() as f64) / (*r.denom() as f64), 0.0))),
V::Float(f) => Ok(V::Complex(Complex64::new(f, 0.0))),
V::Complex(c) => Ok(V::Complex(c)),
arg => error!("cannot cast {arg} to float")
}
}
#[native_func(1)]
fn numer(_: VmArgs, args: Vec<Value>) -> Result<Value> {
use Value as V;
let [value] = unpack_args!(args);
match value {
V::Int(i) => Ok(V::Int(i)),
V::Ratio(r) => Ok(V::Int(*r.numer())),
_ => error!("numer can only take a integer or ratio")
}
}
#[native_func(1)]
fn denom(_: VmArgs, args: Vec<Value>) -> Result<Value> {
use Value as V;
let [value] = unpack_args!(args);
match value {
V::Int(_) => Ok(V::Int(1)),
V::Ratio(r) => Ok(V::Int(*r.denom())),
_ => error!("denom can only take a integer or ratio")
}
}
#[native_func(1)]
fn re(_: VmArgs, args: Vec<Value>) -> Result<Value> {
use Value as V;
let [value] = unpack_args!(args);
match value {
V::Int(_) | V::Ratio(_) | V::Float(_) => Ok(value),
V::Complex(c) => Ok(V::Float(c.re)),
_ => error!("re can only take a valid number")
}
}
#[native_func(1)]
fn im(_: VmArgs, args: Vec<Value>) -> Result<Value> {
use Value as V;
let [value] = unpack_args!(args);
match value {
V::Int(_) | V::Ratio(_) | V::Float(_ )=> Ok(V::Int(0)),
V::Complex(c) => Ok(V::Float(c.im)),
_ => error!("re can only take a valid number")
}
}
mathr!(floor);
mathr!(ceil);
mathr!(round);
mathr!(trunc);
trig!(sqrt);
trig!(cbrt);
trig!(ln);
trig!(log2);
trig!(log10);
trig!(exp);
trig!(exp2);
trig!(sin);
trig!(cos);
trig!(tan);
trig!(sinh);
trig!(cosh);
trig!(tanh);
trig!(asin);
trig!(acos);
trig!(atan);
trig!(asinh);
trig!(acosh);
trig!(atanh);
trigf!(to_degrees, deg);
trigf!(to_radians, rad);
pub fn load(vm: &mut Vm) {
vm.load_global_fn(trans(), "trans");
vm.load_global_fn(rref(), "rref");
vm.load_global_fn(det(), "det");
vm.load_global_fn(ident(), "ident");
vm.load_global_fn(inv(), "inv");
vm.load_global(Value::Float(PI), "pi");
vm.load_global(Value::Float(TAU), "tau");
vm.load_global(Value::Float(E), "e");
vm.load_global(Value::Float(NAN), "nan");
vm.load_global(Value::Float(NAN), "NaN");
vm.load_global(Value::Float(INFINITY), "inf");
vm.load_global_fn(int(), "int");
vm.load_global_fn(ratio(), "ratio");
vm.load_global_fn(float(), "float");
vm.load_global_fn(complex(), "complex");
vm.load_global_fn(abs(), "abs");
vm.load_global_fn(sign(), "sign");
vm.load_global_fn(floor(), "floor");
vm.load_global_fn(ceil(), "ceil");
vm.load_global_fn(round(), "round");
vm.load_global_fn(trunc(), "trunc");
vm.load_global_fn(fract(), "fract");
vm.load_global_fn(sqrt(), "sqrt");
vm.load_global_fn(cbrt(), "cbrt");
vm.load_global_fn(ln(), "ln");
vm.load_global_fn(log(), "log");
vm.load_global_fn(log2(), "log2");
vm.load_global_fn(log10(), "log10");
vm.load_global_fn(exp(), "exp");
vm.load_global_fn(exp2(), "exp2");
vm.load_global_fn(sin(), "sin");
vm.load_global_fn(cos(), "cos");
vm.load_global_fn(tan(), "tan");
vm.load_global_fn(sinh(), "sinh");
vm.load_global_fn(cosh(), "cosh");
vm.load_global_fn(tanh(), "tanh");
vm.load_global_fn(asin(), "asin");
vm.load_global_fn(acos(), "acos");
vm.load_global_fn(atan(), "atan");
vm.load_global_fn(asinh(), "asinh");
vm.load_global_fn(acosh(), "acosh");
vm.load_global_fn(atanh(), "atanh");
vm.load_global_fn(deg(), "deg");
vm.load_global_fn(rad(), "rad");
vm.load_global_fn(denom(), "denom");
vm.load_global_fn(numer(), "numer");
vm.load_global_fn(re(), "re");
vm.load_global_fn(im(), "im");
}

99
matrix-stdlib/src/sys.rs Normal file
View file

@ -0,0 +1,99 @@
use std::{process::{exit, Command, Stdio}, env, rc::Rc, io::Read};
use matrix::{vm::Vm, value::{Value, ValueMap}, unpack_args, Result, gc::Gc};
use matrix_macros::native_func;
use crate::{VmArgs, error};
#[native_func(1)]
fn sys_exit(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [value] = unpack_args!(args);
let Value::Int(i) = value else {
return error!("exit requires a int exit code")
};
exit(i as i32);
}
#[native_func(0)]
fn argv(_: VmArgs, _: Vec<Value>) -> Result<Value> {
Ok(Value::List(
Gc::new(
env::args()
.map(|a| Value::String(Rc::from(a.as_str())))
.collect()
)))
}
#[native_func(1)]
fn env(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [value] = unpack_args!(args);
let Value::String(value) = value else {
return error!("env requires a string name")
};
match std::env::var(value.as_ref()) {
Ok(v) => Ok(Value::String(v.into())),
Err(e) => error!("couldn't read env var: {e}")
}
}
#[native_func(2)]
fn exec(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [cmd, args] = unpack_args!(args);
let (cmd, args) = match (cmd, args) {
(Value::String(s), Value::List(l)) => (s, l.into_inner()),
_ => return error!("exec requires a string cmd and string argument list")
};
let mut sargs = Vec::new();
for arg in args {
let Value::String(arg) = arg else {
return error!("exec requires a string cmd and string argument list")
};
sargs.push(arg.to_string());
};
let cmd = Command::new(cmd.to_string())
.args(sargs)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn();
let mut child = match cmd {
Ok(c) => c,
Err(e) => return error!("error executing command: {e}")
};
let status = match child.wait() {
Ok(s) => s,
Err(e) => return error!("error executing command: {e}")
};
let stdout = match child.stdout {
Some(ref mut out) => {
let mut buf = String::new();
let _ = out.read_to_string(&mut buf);
buf
},
None => String::new()
};
let stderr = match child.stderr {
Some(ref mut err) => {
let mut buf = String::new();
let _ = err.read_to_string(&mut buf);
buf
},
None => String::new()
};
let mut res = ValueMap::new();
res.insert(Value::from("success"), Value::Bool(status.success()))?;
res.insert(Value::from("code"), Value::Int(status.code().unwrap_or(0) as i64))?;
res.insert(Value::from("out"), Value::String(stdout.into()))?;
res.insert(Value::from("err"), Value::String(stderr.into()))?;
Ok(Value::Table(res.into()))
}
pub fn load(vm: &mut Vm) {
vm.load_global_fn(sys_exit(), "exit");
vm.load_global_fn(argv(), "argv");
vm.load_global_fn(exec(), "exec");
vm.load_global_fn(env(), "env");
}

View file

@ -1,5 +1,5 @@
use std::{rc::Rc, ops::{Neg, Not}};
use crate::{lex::Token, value::{Value, InlineList, InlineMatrix, InlineTable}, Result};
use std::{rc::Rc, ops::{Neg, Not}, fmt::Debug};
use crate::{lex::{Position, TokenData}, value::{Value, InlineList, InlineMatrix, InlineTable}, Result};
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum UnaryOp {
@ -31,10 +31,15 @@ pub enum BinaryOp {
LessEquals,
GreaterThan,
LessThan,
// iter
Range,
RangeEq
}
pub type AstName = (Rc<str>, Position);
#[derive(Debug, Clone, PartialEq)]
pub enum Expr {
pub enum ExprData {
NoOp,
Literal(Value),
@ -45,7 +50,7 @@ pub enum Expr {
Index(Box<Expr>, Vec<Expr>),
FnCall(Box<Expr>, Vec<Expr>),
FieldAccess(Box<Expr>, Rc<str>),
FieldAccess(Box<Expr>, AstName),
List(InlineList),
Matrix(InlineMatrix),
@ -57,22 +62,49 @@ pub enum Expr {
Assign(Box<Expr>, Box<Expr>),
If(Box<Expr>, Box<Expr>, Option<Box<Expr>>),
Function(Rc<str>, Vec<Rc<str>>, Box<Expr>, bool),
Lambda(Vec<Rc<str>>, Box<Expr>, bool),
Function(AstName, Vec<AstName>, Box<Expr>, bool),
Lambda(Vec<AstName>, Box<Expr>, bool),
Loop(Box<Expr>),
While(Box<Expr>, Box<Expr>),
DoWhile(Box<Expr>, Box<Expr>),
For(AstName, Box<Expr>, Box<Expr>),
Block(Vec<Expr>),
Let(Rc<str>, Box<Expr>),
Try(Box<Expr>, AstName, Box<Expr>),
Let(AstName, Box<Expr>),
Pipeline(Box<Expr>, Box<Expr>),
Continue,
Break,
Return(Box<Expr>),
}
#[derive(Clone, PartialEq)]
pub struct Expr {
pub data: ExprData,
pub pos: Position
}
impl Debug for Expr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if f.alternate() {
write!(f, "{:#?}", self.data)
} else {
write!(f, "{:?}", self.data)
}
}
}
impl From<(ExprData, Position)> for Expr {
fn from(value: (ExprData, Position)) -> Self {
Self { data: value.0, pos: value.1 }
}
}
impl Neg for BinaryOp {
type Output = Option<Self>;
fn neg(self) -> Self::Output {
@ -103,73 +135,94 @@ impl Not for BinaryOp {
impl Neg for Expr {
type Output = std::result::Result<Self, Self>;
fn neg(self) -> Self::Output {
use Expr::*;
Ok(match self {
Literal(v) => Literal(-v),
BinaryOp(lhs, rhs, op) => {
fn neg(mut self) -> Self::Output {
use ExprData as E;
let mut pos = self.pos;
let data = match self.data {
E::Literal(v) => E::Literal(-v),
E::BinaryOp(lhs, rhs, op) => {
let Some(op_n) = -op else {
return Err(BinaryOp(lhs, rhs, op));
return Err((E::BinaryOp(lhs, rhs, op), pos).into());
};
if let Ok(lhs) = -*lhs.clone() {
BinaryOp(Box::new(lhs), rhs, op_n)
pos = lhs.pos;
E::BinaryOp(Box::new(lhs), rhs, op_n)
} else if let Ok(rhs) = -*rhs.clone() {
BinaryOp(lhs, Box::new(rhs), op_n)
pos = rhs.pos;
E::BinaryOp(lhs, Box::new(rhs), op_n)
} else {
return Err(BinaryOp(lhs, rhs, op))
return Err((E::BinaryOp(lhs, rhs, op), pos).into());
}
},
UnaryOp(expr, op) => {
E::UnaryOp(expr, op) => {
match op {
self::UnaryOp::Negate => *expr,
_ => return Err(UnaryOp(expr, op))
UnaryOp::Negate => {
pos = expr.pos;
expr.data
},
_ => return Err((E::UnaryOp(expr, op), pos).into())
}
}
_ => return Err(self)
})
data => return Err((data, pos).into())
};
self.data = data;
self.pos = pos;
Ok(self)
}
}
impl Not for Expr {
type Output = std::result::Result<Self, Self>;
fn not(self) -> Self::Output {
use Expr::*;
Ok(match self {
Literal(v) => Literal(Value::Bool(!v)),
UnaryOp(expr, op) => {
fn not(mut self) -> Self::Output {
use ExprData as E;
let mut pos = self.pos;
let data = match self.data {
E::Literal(v) => E::Literal(Value::Bool(!v)),
E::UnaryOp(expr, op) => {
match op {
self::UnaryOp::Not => *expr,
_ => return Err(UnaryOp(expr, op))
self::UnaryOp::Not => {
pos = expr.pos;
expr.data
},
_ => return Err((E::UnaryOp(expr, op), pos).into())
}
}
_ => return Err(self)
})
data => return Err((data, pos).into())
};
self.data = data;
self.pos = pos;
Ok(self)
}
}
pub fn optimize(expr: Expr) -> Result<Expr> {
use Expr as E;
Ok(match expr {
pub fn optimize(mut expr: Expr) -> Result<Expr> {
use ExprData as E;
let mut pos = expr.pos;
let data: ExprData = match expr.data {
E::UnaryOp(expr, op) => {
let expr = optimize(*expr)?;
match match op {
UnaryOp::Negate => -expr,
UnaryOp::Not => !expr,
} {
Ok(expr) => expr,
Ok(expr) => {
pos = expr.pos;
expr.data
},
Err(expr) => E::UnaryOp(Box::new(expr), op)
}
},
E::BinaryOp(lhs, rhs, op) => {
let lhs = optimize(*lhs)?;
let rhs = optimize(*rhs)?;
if let (E::Literal(l), E::Literal(r)) = (lhs.clone(), rhs.clone()) {
if let (E::Literal(l), E::Literal(r)) = (lhs.clone().data, rhs.clone().data) {
match Value::binary_op(op, l, r) {
Err(err) => return Err(err),
Ok(value) => return Ok(Expr::Literal(value)),
}
Ok(value) => E::Literal(value),
}
} else {
E::BinaryOp(Box::new(lhs), Box::new(rhs), op)
}
},
E::FnCall(ident, values) => {
E::FnCall(ident, values
@ -200,10 +253,16 @@ pub fn optimize(expr: Expr) -> Result<Expr> {
E::And(lhs, rhs) => {
let lhs = optimize(*lhs)?;
let rhs = optimize(*rhs)?;
if let (E::Literal(l), r) = (lhs.clone(), rhs.clone()) {
if let (E::Literal(l), r) = (lhs.clone().data, rhs.clone().data) {
match !!l.clone() {
true => r,
false => E::Literal(l),
true => {
pos = rhs.pos;
r
},
false => {
pos = lhs.pos;
E::Literal(l)
},
}
} else {
E::And(Box::new(lhs), Box::new(rhs))
@ -212,10 +271,16 @@ pub fn optimize(expr: Expr) -> Result<Expr> {
E::Or(lhs, rhs) => {
let lhs = optimize(*lhs)?;
let rhs = optimize(*rhs)?;
if let (E::Literal(l), r) = (lhs.clone(), rhs.clone()) {
if let (E::Literal(l), r) = (lhs.clone().data, rhs.clone().data) {
match !l.clone() {
true => r,
false => E::Literal(l),
true => {
pos = rhs.pos;
r
},
false => {
pos = lhs.pos;
E::Literal(l)
},
}
} else {
E::And(Box::new(lhs), Box::new(rhs))
@ -230,10 +295,10 @@ pub fn optimize(expr: Expr) -> Result<Expr> {
.into_iter()
.enumerate()
.filter(|(i, e)| {
if let E::Literal(_) = e {
if let E::Literal(_) = e.data {
return i + 1 == len
}
E::NoOp != *e
E::NoOp != e.data
})
.map(|e| e.1)
.collect();
@ -247,39 +312,60 @@ pub fn optimize(expr: Expr) -> Result<Expr> {
let cond = optimize(*cond)?;
let block = optimize(*block)?;
let else_block = else_block.map(|e| optimize(*e)).transpose()?;
if let E::Literal(lit) = cond {
if let E::Literal(lit) = cond.data {
if !!lit {
return Ok(block)
}
return Ok(else_block.unwrap_or(E::NoOp))
pos = block.pos;
block.data
} else if let Some(else_block) = else_block {
pos = else_block.pos;
else_block.data
} else {
E::NoOp
}
} else {
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)))
}
return Ok(E::NoOp)
E::Loop(Box::new(block))
} else {
E::NoOp
}
} else {
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::Loop(block) => {
let block = Box::new(optimize(*block)?);
E::Loop(block)
},
E::Try(expr, err, catch) => {
let expr = Box::new(optimize(*expr)?);
let catch = Box::new(optimize(*catch)?);
E::Try(expr, err, catch)
},
E::Function(ident, params, stmt, varadic) => {
let stmt = Box::new(optimize(*stmt)?);
E::Function(ident, params, stmt, varadic)
@ -297,44 +383,53 @@ pub fn optimize(expr: Expr) -> Result<Expr> {
E::Assign(lhs, rhs)
},
E::Return(expr) => {
E::Return(Box::new(optimize(*expr)?))
let expr = Box::new(optimize(*expr)?);
E::Return(expr)
},
_ => expr
})
E::Pipeline(lhs, rhs) => {
let lhs = Box::new(optimize(*lhs)?);
let rhs = Box::new(optimize(*rhs)?);
E::Pipeline(lhs, rhs)
}
data => data
};
expr.data = data;
expr.pos = pos;
Ok(expr)
}
impl From<Token> for UnaryOp {
fn from(value: Token) -> Self {
use Token::*;
impl From<TokenData> for UnaryOp {
fn from(value: TokenData) -> Self {
use TokenData as T;
match value {
Subtract => Self::Negate,
Not => Self::Not,
T::Subtract => Self::Negate,
T::Not => Self::Not,
_ => panic!("aaaaa")
}
}
}
impl From<Token> for BinaryOp {
fn from(value: Token) -> Self {
use Token::*;
impl From<TokenData> for BinaryOp {
fn from(value: TokenData) -> Self {
use TokenData as T;
match value {
Equal => Self::Equals,
NotEqual => Self::NotEquals,
GreaterEqual => Self::GreaterEquals,
LessEqual => Self::LessEquals,
GreaterThan => Self::GreaterThan,
LessThan => Self::LessThan,
BitwiseShiftLeft => Self::BitwiseShiftLeft,
BitwiseShiftRight => Self::BitwiseShiftRight,
BitwiseAnd => Self::BitwiseAnd,
BitwiseOr => Self::BitwiseOr,
BitwiseXor => Self::BitwiseXor,
Add => Self::Add,
Subtract => Self::Subtract,
Multiply => Self::Multiply,
Divide => Self::Divide,
Modulo => Self::Modulo,
Power => Self::Power,
T::Equal => Self::Equals,
T::NotEqual => Self::NotEquals,
T::GreaterEqual => Self::GreaterEquals,
T::LessEqual => Self::LessEquals,
T::GreaterThan => Self::GreaterThan,
T::LessThan => Self::LessThan,
T::BitwiseShiftLeft => Self::BitwiseShiftLeft,
T::BitwiseShiftRight => Self::BitwiseShiftRight,
T::BitwiseAnd => Self::BitwiseAnd,
T::BitwiseOr => Self::BitwiseOr,
T::BitwiseXor => Self::BitwiseXor,
T::Add => Self::Add,
T::Subtract => Self::Subtract,
T::Multiply => Self::Multiply,
T::Divide => Self::Divide,
T::Modulo => Self::Modulo,
T::Power => Self::Power,
_ => panic!("aaaaa")
}
}
@ -342,11 +437,11 @@ impl From<Token> for BinaryOp {
impl Expr {
pub fn is_assignable(&self) -> bool {
use Expr::*;
match self {
Ident(_) => true,
Index(_, _) => true,
FieldAccess(_, _) => true,
use ExprData as E;
match self.data {
E::Ident(_) => true,
E::Index(_, _) => true,
E::FieldAccess(_, _) => true,
_ => false,
}
}

View file

@ -1,17 +1,19 @@
use crate::{value::Value, ast::{UnaryOp, BinaryOp}, vm::Vm, Result};
use crate::{value::Value, ast::{UnaryOp, BinaryOp}, vm::{Vm, StackFrame}, Result, lex::Position};
use std::{fmt::{Debug, Display}, rc::Rc};
#[derive(Clone)]
#[derive(Clone, Default)]
pub struct Chunk {
pub constants: Vec<Value>,
pub code: Vec<Instruction>
pub code: Vec<Instruction>,
pub pos: Vec<Position>,
}
impl Chunk {
pub fn new() -> Self {
Self {
constants: Vec::new(),
code: Vec::new()
code: Vec::new(),
pos: Vec::new(),
}
}
}
@ -43,9 +45,10 @@ pub struct Function {
pub fun: InnerFunction
}
#[derive(Clone)]
pub enum InnerFunction {
Compiled(Rc<Chunk>),
Native(Box<dyn Fn(&mut Vm, Vec<Value>) -> Result<Value>>),
Native(Rc<dyn Fn((&mut Vm, &mut StackFrame), Vec<Value>) -> Result<Value>>),
}
impl Debug for Function {
@ -98,6 +101,13 @@ pub enum Instruction {
Jump(u16),
JumpTrue(u16),
JumpFalse(u16),
JumpNil(u16),
IterCreate,
IterNext,
Try(u16),
TryEnd,
Call(u8),
Return,
@ -131,10 +141,15 @@ impl Display for Instruction {
Jump(idx) => write!(f, "jump \x1b[33m{idx}\x1b[0m"),
JumpTrue(idx) => write!(f, "jumpt \x1b[33m{idx}\x1b[0m"),
JumpFalse(idx) => write!(f, "jumpf \x1b[33m{idx}\x1b[0m"),
JumpNil(idx) => write!(f, "jumpn \x1b[33m{idx}\x1b[0m"),
Call(arity) => write!(f, "call \x1b[35m{arity}\x1b[0m"),
Return => write!(f, "return"),
IterCreate => write!(f, "iter create"),
IterNext => write!(f, "iter next"),
Field(name_idx) => write!(f, "field \x1b[33m{name_idx}\x1b[0m"),
StoreField(name_idx) => write!(f, "store field \x1b[33m{name_idx}\x1b[0m"),
Try(idx) => write!(f, "try \x1b[33m{idx}\x1b[0m"),
TryEnd => write!(f, "try end"),
}
}
}

View file

@ -1,9 +1,9 @@
use std::{fmt::Display, rc::Rc, cell::RefCell};
use crate::{ast::Expr, chunk::{Function, Instruction, InnerFunction}, chunk::{Chunk, self}, value::Value, Result};
use crate::{ast::{Expr, ExprData, AstName}, chunk::{Function, Instruction, InnerFunction}, chunk::{Chunk, self}, value::Value, Result, lex::Position};
use Instruction as I;
use Value as V;
use Expr as E;
use ExprData as E;
pub type NamesTable = Rc<RefCell<Vec<Rc<str>>>>;
@ -104,7 +104,7 @@ struct Local {
}
#[derive(Debug, Clone)]
pub enum Error {
pub enum InnerError {
Undefined(Rc<str>),
Redefined(Rc<str>),
InvAssign(Expr),
@ -113,22 +113,36 @@ pub enum Error {
NotImplemented(&'static str),
}
#[derive(Debug, Clone)]
pub struct Error {
pos: Position,
err: InnerError,
}
impl std::error::Error for self::Error {}
impl Display for self::Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use self::Error::*;
match self {
Undefined(name) => write!(f, "value {name} is undefined"),
Redefined(name) => write!(f, "cannot redefine {name} in the same scope"),
InvAssign(expr) => write!(f, "cannot assign to {expr:?}"),
InvContinue => write!(f, "cannot continue outside a loop"),
InvBreak => write!(f, "cannot break outside a loop"),
NotImplemented(str) => write!(f, "{str} is not implemented yet")
use InnerError as E;
write!(f, "parse failed at {}:{}, ", self.pos.row, self.pos.col)?;
match &self.err {
E::Undefined(name) => write!(f, "value {name} is undefined"),
E::Redefined(name) => write!(f, "cannot redefine {name} in the same scope"),
E::InvAssign(expr) => write!(f, "cannot assign to {expr:?}"),
E::InvContinue => write!(f, "cannot continue outside a loop"),
E::InvBreak => write!(f, "cannot break outside a loop"),
E::NotImplemented(str) => write!(f, "{str} is not implemented yet")
}
}
}
fn error<T>(err: InnerError, pos: Position) -> Result<T> {
Err(self::Error {
pos,
err
}.into())
}
impl<'c> Compiler<'c> {
fn child(&'c self, name: Rc<str>) -> Self {
@ -156,7 +170,7 @@ impl<'c> Compiler<'c> {
cutoff = self.scopes.pop().unwrap()
}
if cutoff < self.locals.len() {
self.emit(Instruction::DiscardLocals((self.locals.len() - cutoff) as u16));
self.emit(Instruction::DiscardLocals((self.locals.len() - cutoff) as u16), self.last_pos());
self.locals.truncate(cutoff);
};
}
@ -166,7 +180,7 @@ impl<'c> Compiler<'c> {
return;
};
if cutoff < self.locals.len() {
self.emit(Instruction::DiscardLocals((self.locals.len() - cutoff) as u16));
self.emit(Instruction::DiscardLocals((self.locals.len() - cutoff) as u16), self.last_pos());
self.locals.truncate(cutoff);
};
}
@ -182,19 +196,19 @@ impl<'c> Compiler<'c> {
c
}
fn create_local_checked(&mut self, name: Rc<str>) -> Result<()> {
fn create_local_checked(&mut self, name: Rc<str>, pos: Position) -> Result<()> {
if let Some(local) = self.find_local(&name) {
if local.scope == self.scopes.len() {
return Err(Error::Redefined(name).into())
return error(InnerError::Redefined(name), pos)
}
};
self.create_local(name);
Ok(())
}
fn create_global_checked(&mut self, name: Rc<str>) -> Result<usize> {
fn create_global_checked(&mut self, name: Rc<str>, pos: Position) -> Result<usize> {
if let Some(_) = self.find_global(&name) {
return Err(Error::Redefined(name).into())
return error(InnerError::Redefined(name).into(), pos)
}
Ok(self.create_global(name))
}
@ -227,29 +241,29 @@ impl<'c> Compiler<'c> {
idx
}
fn emit_const(&mut self, val: Value) {
fn emit_const(&mut self, val: Value, pos: Position) {
// TODO: find constant if already exists
self.chunk.constants.push(val);
let c = self.chunk.constants.len() - 1;
self.emit(Instruction::Const(c as u16));
self.emit(Instruction::Const(c as u16), pos);
}
fn can_make_globals(&self) -> bool {
self.repl && self.parent.is_none() && self.scopes.len() == 0
}
fn compile_value(&mut self, val: &Value) {
fn compile_value(&mut self, val: &Value, pos: Position) {
match val {
V::Nil => self.emit(I::Nil),
V::Bool(b) => if *b { self.emit(I::True) } else { self.emit(I::False) },
V::Nil => self.emit(I::Nil, pos),
V::Bool(b) => if *b { self.emit(I::True, pos) } else { self.emit(I::False, pos) },
V::Int(i) => {
if let Ok(i) = i16::try_from(*i) {
self.emit(I::Int(i));
self.emit(I::Int(i), pos);
} else {
self.emit_const(val.clone());
self.emit_const(val.clone(), pos);
}
},
_ => self.emit_const(val.clone()),
_ => self.emit_const(val.clone(), pos),
}
}
@ -263,152 +277,193 @@ impl<'c> Compiler<'c> {
}
fn compile_expr(&mut self, expr: &Expr) -> Result<()> {
match expr {
E::NoOp => self.emit(I::Nil),
match &expr.data {
E::NoOp => self.emit(I::Nil, expr.pos),
E::If(cond, ifb, elseb) => {
self.compile_expr(cond)?;
let jmpidx = self.emit_temp();
let jmpidx = self.emit_temp(expr.pos);
self.compile_expr(ifb)?;
let jmpidx2 = self.emit_temp(expr.pos);
self.re_emit(I::JumpFalse(self.cur()), jmpidx);
if let Some(elseb) = elseb {
self.compile_expr(elseb)?;
}
self.re_emit(I::Jump(self.cur()), jmpidx2);
},
E::Function(name, params, body, varadic) => {
let chunk = self.compile_function(name.clone(), params, body)?;
let arity = params.len() - if *varadic { 1 } else { 0 };
let fun = Value::Function(Rc::new(
chunk::Function {
name: name.clone(),
arity: params.len(),
name: name.0.clone(),
arity,
fun: InnerFunction::Compiled(chunk.into()),
variadic: *varadic
}
));
self.emit_const(fun);
self.emit(I::Dup);
self.emit_const(fun, expr.pos);
self.emit(I::Dup, expr.pos);
if self.can_make_globals() {
let idx = self.create_global_checked(name.clone())?;
self.emit(I::StoreGlobal(idx as u16));
let idx = self.create_global_checked(name.0.clone(), name.1)?;
self.emit(I::StoreGlobal(idx as u16), expr.pos);
} else {
self.create_local_checked(name.clone())?;
self.emit(I::CreateLocal);
self.create_local_checked(name.0.clone(), name.1)?;
self.emit(I::CreateLocal, expr.pos);
}
},
E::Lambda(params, body, varadic) => {
let name: Rc<str> = Rc::from("<lambda>");
let name: AstName = (Rc::from("<lambda>"), expr.pos);
let chunk = self.compile_function(name.clone(), params, body)?;
let arity = params.len() - if *varadic { 1 } else { 0 };
let fun = Value::Function(Rc::new(
chunk::Function {
name: name.clone(),
arity: params.len(),
name: name.0.clone(),
arity,
fun: InnerFunction::Compiled(chunk.into()),
variadic: *varadic
}
));
self.emit_const(fun);
self.emit_const(fun, expr.pos);
},
E::Loop(expr) => {
let idx = self.cur();
self.loop_top.push((idx as usize, self.scopes.len()));
self.compile_expr(expr)?;
self.emit(I::Discard(1));
self.emit(I::Jump(idx));
self.emit(I::Discard(1), expr.pos);
self.emit(I::Jump(idx), expr.pos);
self.finish_loop();
self.emit(I::Nil);
self.emit(I::Nil, expr.pos);
},
E::Try(expr, err, catch) => {
let jmpidx = self.emit_temp(expr.pos);
self.compile_expr(expr)?;
self.emit(I::TryEnd, expr.pos);
let jmpidx2 = self.emit_temp(expr.pos);
self.re_emit(I::Try(self.cur()), jmpidx);
self.begin_scope();
self.create_local(err.0.clone());
self.emit(I::CreateLocal, err.1);
self.compile_expr(catch)?;
self.end_scope();
self.re_emit(I::Jump(self.cur() as u16), jmpidx2);
},
E::While(cond, expr) => {
let top = self.cur();
self.compile_expr(cond)?;
let jmpidx = self.emit_temp();
let jmpidx = self.emit_temp(expr.pos);
self.loop_top.push((top as usize, self.scopes.len()));
self.compile_expr(expr)?;
self.emit(I::Discard(1));
self.emit(I::Jump(top));
self.emit(I::Discard(1), expr.pos);
self.emit(I::Jump(top), expr.pos);
self.re_emit(I::JumpFalse(self.cur()), jmpidx);
self.finish_loop();
self.emit(I::Nil);
self.emit(I::Nil, expr.pos);
},
E::DoWhile(expr, cond) => {
let top = self.cur();
self.loop_top.push((top as usize, self.scopes.len()));
self.compile_expr(expr)?;
self.emit(I::Discard(1));
self.emit(I::Discard(1), expr.pos);
self.compile_expr(cond)?;
self.emit(I::JumpTrue(top));
self.emit(I::JumpTrue(top), expr.pos);
self.finish_loop();
self.emit(I::Nil);
self.emit(I::Nil, expr.pos);
},
E::For(name, cond, expr) => {
self.compile_expr(cond)?;
self.emit(I::IterCreate, cond.pos);
let top = self.cur();
self.emit(I::IterNext, cond.pos);
self.emit(I::Dup, expr.pos);
let jumpidx = self.emit_temp(expr.pos);
self.loop_top.push((top as usize, self.scopes.len()));
self.begin_scope();
self.create_local(name.0.clone());
self.emit(I::CreateLocal, name.1);
self.compile_expr(expr)?;
self.emit(I::Discard(1), expr.pos);
self.end_scope();
self.emit(I::Jump(top), expr.pos);
self.re_emit(I::JumpNil(self.cur()), jumpidx);
self.finish_loop();
self.emit(I::Discard(2), expr.pos);
self.emit(I::Nil, expr.pos);
}
E::Block(block) => {
if block.len() == 0 {
self.emit(I::Nil, expr.pos);
return Ok(());
}
self.begin_scope();
for (i, expr) in block.iter().enumerate() {
self.compile_expr(expr)?;
if i + 1 != block.len() {
self.emit(I::Discard(1));
self.emit(I::Discard(1), expr.pos);
}
}
self.end_scope();
},
E::Let(name, expr) => {
self.compile_expr(expr)?;
self.emit(I::Dup);
self.emit(I::Dup, expr.pos);
if self.can_make_globals() {
let global = self.create_global_checked(name.clone())?;
self.emit(I::StoreGlobal(global as u16));
let global = self.create_global_checked(name.0.clone(), name.1)?;
self.emit(I::StoreGlobal(global as u16), expr.pos);
} else {
self.create_local_checked(name.clone())?;
self.emit(I::CreateLocal);
self.create_local_checked(name.0.clone(), name.1)?;
self.emit(I::CreateLocal, expr.pos);
}
},
E::Continue => {
let top = self.loop_top.pop();
if let Some((top, scope)) = top {
self.collapse_scopes(scope);
self.emit(I::Jump(top as u16));
self.emit(I::Jump(top as u16), expr.pos);
} else {
return Err(Error::InvContinue.into())
return error(InnerError::InvContinue, expr.pos)
}
},
E::Break => {
let top = self.loop_top.pop();
if let Some((_, scope)) = top {
self.collapse_scopes(scope);
let tmpidx = self.emit_temp();
let tmpidx = self.emit_temp(expr.pos);
self.loop_bot.push(tmpidx);
} else {
return Err(Error::InvBreak.into())
return error(InnerError::InvBreak, expr.pos)
}
},
E::Return(expr) => {
self.compile_expr(expr)?;
self.emit(I::Return);
self.emit(I::Return, expr.pos);
},
E::Literal(val) => self.compile_value(val),
E::Literal(val) => self.compile_value(val, expr.pos),
E::Ident(name) => {
if name.as_ref() == "_" {
self.emit(I::Nil);
self.emit(I::Nil, expr.pos);
} else if let Some(local) = self.find_local(name) {
self.emit(I::LoadLocal(local.idx as u16));
self.emit(I::LoadLocal(local.idx as u16), expr.pos);
} else if let Some(global) = self.find_global(name) {
self.emit(I::LoadGlobal(global as u16));
self.emit(I::LoadGlobal(global as u16), expr.pos);
} else {
return Err(self::Error::Undefined(name.clone()).into())
return error(InnerError::Undefined(name.clone()), expr.pos)
};
},
E::Assign(lhs, rhs) => {
self.compile_expr(rhs)?;
self.emit(I::Dup);
match lhs.as_ref() {
self.emit(I::Dup, rhs.pos);
match &lhs.data {
E::Ident(name) if name.as_ref() != "_" => {
if let Some(local) = self.find_local(&name) {
self.emit(I::StoreLocal(local.idx as u16));
self.emit(I::StoreLocal(local.idx as u16), lhs.pos);
} else if let Some(global) = self.find_global(&name) {
self.emit(I::StoreGlobal(global as u16));
self.emit(I::StoreGlobal(global as u16), lhs.pos);
} else if self.can_make_globals() {
let global = self.create_global_checked(name.clone())?;
self.emit(I::StoreGlobal(global as u16));
let global = self.create_global_checked(name.clone(), lhs.pos)?;
self.emit(I::StoreGlobal(global as u16), lhs.pos);
} else {
self.create_local_checked(name.clone())?;
self.emit(I::CreateLocal);
self.create_local_checked(name.clone(), lhs.pos)?;
self.emit(I::CreateLocal, lhs.pos);
}
},
E::Index(expr, params) => {
@ -416,89 +471,94 @@ impl<'c> Compiler<'c> {
for param in params {
self.compile_expr(param)?;
}
self.emit(I::StoreIndex(params.len() as u8));
self.emit(I::StoreIndex(params.len() as u8), expr.pos);
},
E::FieldAccess(expr, ident) => {
self.compile_expr(expr)?;
let name = self.get_name(ident.clone());
self.emit(I::StoreField(name as u16));
let name = self.get_name(ident.0.clone());
self.emit(I::StoreField(name as u16), expr.pos);
}
_ => return Err(self::Error::InvAssign(*lhs.clone()).into())
_ => return error(InnerError::InvAssign(*lhs.clone()), lhs.pos)
}
}
E::UnaryOp(expr, op) => {
self.compile_expr(expr)?;
self.emit(I::UnaryOp(*op));
self.emit(I::UnaryOp(*op), expr.pos);
},
E::BinaryOp(lhs, rhs, op) => {
self.compile_expr(lhs)?;
self.compile_expr(rhs)?;
self.emit(I::BinaryOp(*op));
self.emit(I::BinaryOp(*op), lhs.pos);
},
E::Index(expr, params) => {
self.compile_expr(expr)?;
for param in params {
self.compile_expr(param)?;
}
self.emit(I::Index(params.len() as u8))
self.emit(I::Index(params.len() as u8), expr.pos)
},
E::FnCall(fun, params) => {
for expr in params {
self.compile_expr(expr)?;
}
self.compile_expr(fun)?;
self.emit(I::Call(params.len() as u8));
self.emit(I::Call(params.len() as u8), expr.pos);
},
E::FieldAccess(expr, field) => {
self.compile_expr(expr)?;
let idx = self.get_name(field.clone());
self.emit(I::Field(idx as u16))
let idx = self.get_name(field.0.clone());
self.emit(I::Field(idx as u16), expr.pos)
}
E::List(list) => {
for expr in list {
self.compile_expr(expr)?;
}
self.emit(I::NewList(list.len() as u16));
self.emit(I::NewList(list.len() as u16), expr.pos);
},
E::Matrix(mat) => {
for expr in &mat.2 {
self.compile_expr(expr)?;
}
self.emit(I::NewMatrix(mat.2.len() as u16, mat.0 as u8));
self.emit(I::NewMatrix(mat.2.len() as u16, mat.0 as u8), expr.pos);
},
E::Table(table) => {
for (key, value) in table {
self.compile_expr(key)?;
self.compile_expr(value)?;
}
self.emit(I::NewTable(table.len() as u16));
self.emit(I::NewTable(table.len() as u16), expr.pos);
},
E::And(lhs, rhs) => {
self.compile_expr(lhs)?;
self.emit(I::Dup);
let jmpidx = self.emit_temp();
self.emit(I::Dup, lhs.pos);
let jmpidx = self.emit_temp(lhs.pos);
self.compile_expr(rhs)?;
self.re_emit(I::JumpFalse(self.cur()), jmpidx);
},
E::Or(lhs, rhs) => {
self.compile_expr(lhs)?;
self.emit(I::Dup);
let jmpidx = self.emit_temp();
self.emit(I::Dup, lhs.pos);
let jmpidx = self.emit_temp(lhs.pos);
self.compile_expr(rhs)?;
self.re_emit(I::JumpTrue(self.cur()), jmpidx);
},
E::Pipeline(lhs, rhs) => {
self.compile_expr(lhs)?;
self.compile_expr(rhs)?;
self.emit(I::Call(1), expr.pos);
}
};
Ok(())
}
fn compile_function(&mut self, name: Rc<str>, params: &Vec<Rc<str>>, body: &Box<Expr>) -> Result<Chunk> {
let mut compiler = self.child(name);
for name in params {
fn compile_function(&mut self, name: AstName, params: &Vec<AstName>, body: &Box<Expr>) -> Result<Chunk> {
let mut compiler = self.child(name.0);
for (name, pos) in params {
compiler.create_local(name.clone());
compiler.emit(I::CreateLocal);
compiler.emit(I::CreateLocal, *pos);
}
compiler.compile_expr(body)?;
compiler.finish()?;
compiler.finish(name.1)?;
Ok(compiler.chunk)
}
@ -506,15 +566,16 @@ impl<'c> Compiler<'c> {
self.chunk.code.len() as u16
}
fn emit_temp(&mut self) -> usize {
fn emit_temp(&mut self, pos: Position) -> usize {
let idx = self.chunk.code.len();
self.emit(Instruction::NoOp);
self.emit(Instruction::NoOp, pos);
idx
}
fn emit(&mut self, ins: Instruction) {
fn emit(&mut self, ins: Instruction, pos: Position) {
//println!("{}: {ins}", self.name);
self.chunk.code.push(ins);
self.chunk.pos.push(pos);
}
fn re_emit(&mut self, ins: Instruction, idx: usize) {
@ -522,12 +583,20 @@ impl<'c> Compiler<'c> {
self.chunk.code[idx] = ins;
}
fn finish(&mut self) -> Result<()> {
fn last_pos(&self) -> Position {
if let Some(pos) = self.chunk.pos.last() {
*pos
} else {
Position::default()
}
}
fn finish(&mut self, pos: Position) -> Result<()> {
let ins = match self.chunk.code.last() {
Some(ins) => ins.clone(),
None => {
self.emit(I::Nil);
self.emit(I::Return);
self.emit(I::Nil, pos);
self.emit(I::Return, pos);
if self.debug {
println!("{}\n{}", self.name, self.chunk);
}
@ -537,12 +606,12 @@ impl<'c> Compiler<'c> {
match ins {
I::Return => {},
_ => {
self.emit(I::Return);
self.emit(I::Return, pos);
}
};
if self.loop_bot.len() > 0 {
return Err(Error::InvBreak.into())
return error(InnerError::InvBreak, pos)
}
if self.debug {
@ -555,12 +624,12 @@ impl<'c> Compiler<'c> {
&mut self,
body: &Expr,
) -> Result<Rc<Function>> {
if let Expr::Block(_) = body {
if let ExprData::Block(_) = &body.data {
self.root_is_block = true;
}
self.chunk = Chunk::new();
self.compile_expr(body)?;
self.finish()?;
self.finish(self.last_pos())?;
let fun = Function { name: self.name.clone(), fun: InnerFunction::Compiled(self.chunk.clone().into()), arity: 0, variadic: false };
Ok(Rc::new(fun))
}

View file

@ -31,6 +31,12 @@ impl <T: Clone> Gc<T> {
}
}
pub fn into_inner(self) -> T {
unsafe {
self.ptr.as_ref().data.clone()
}
}
fn data(&self) -> T {
unsafe {
self.ptr.as_ref().data.clone()

View file

@ -31,7 +31,7 @@ impl From<RegexToken> for Regex {
}
#[derive(Debug, PartialEq)]
pub enum Token {
pub enum TokenData {
//syntax
LeftParen,
RightParen,
@ -46,9 +46,11 @@ pub enum Token {
ThinArrow,
Comma,
Range,
RangeEq,
Colon,
Backslash,
Varadic,
Pipe,
// math
Regex(RegexToken),
@ -113,11 +115,24 @@ pub enum Token {
Do,
Loop,
Return,
For,
In,
Try,
Catch,
// eof
Eof,
}
#[derive(Debug, PartialEq)]
pub struct Token {
pub data: TokenData,
pub pos: Position,
pub str: String,
pub bidx: usize,
pub blen: usize,
}
#[derive(Debug)]
pub enum Error {
UnexpectedCharacter(char),
@ -148,10 +163,24 @@ impl std::fmt::Display for Error {
impl std::error::Error for Error {}
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
pub struct Position {
pub row: usize,
pub col: usize,
}
impl Default for Position {
fn default() -> Self {
Self { row: 1, col: 1 }
}
}
pub struct Lexer {
pub index: usize,
len: usize,
data: Vec<char>,
pos: Position,
byte_len: usize,
}
trait IsIdent {
@ -177,7 +206,13 @@ impl<T: Into<String>> From<T> for Lexer {
impl Lexer {
pub fn new<T: Into<String>>(input: T) -> Self {
let data: Vec<char> = input.into().chars().collect();
Self { index: 0, len: data.len(), data }
Self {
index: 0,
len: data.len(),
data,
pos: Position::default(),
byte_len: 0
}
}
fn peek(&self) -> char {
@ -190,6 +225,14 @@ impl Lexer {
fn next(&mut self) -> char {
let c = self.peek();
self.index += 1;
self.byte_len += c.len_utf8();
self.pos.col += 1;
if c == '\n' {
self.pos.col = 1;
self.pos.row += 1;
}
c
}
@ -274,14 +317,14 @@ impl Lexer {
Ok(buf.into())
}
fn lex_ident(&mut self, initial: char) -> Result<Token> {
use Error::*;
use Token::*;
fn lex_ident(&mut self, initial: char) -> Result<TokenData> {
use Error as E;
use TokenData as T;
let mut buf = std::string::String::new();
if !initial.is_initial_ident() {
return Err(UnexpectedCharacter(initial).into())
return Err(E::UnexpectedCharacter(initial).into())
}
buf.push(initial);
@ -295,29 +338,33 @@ impl Lexer {
}
Ok(match buf.as_str() {
"if" => If,
"else" => Else,
"while" => While,
"let" => Let,
"fn" | "function" => Function,
"true" => True,
"false" => False,
"nil" => Nil,
"continue" => Continue,
"break" => Break,
"do" => Do,
"loop" => Loop,
"and" => And,
"or" => Or,
"not" => Not,
"return" => Return,
_ => Ident(buf.into())
"if" => T::If,
"else" => T::Else,
"while" => T::While,
"let" => T::Let,
"fn" | "function" => T::Function,
"true" => T::True,
"false" => T::False,
"nil" => T::Nil,
"continue" => T::Continue,
"break" => T::Break,
"do" => T::Do,
"loop" => T::Loop,
"and" => T::And,
"or" => T::Or,
"not" => T::Not,
"return" => T::Return,
"for" => T::For,
"in" => T::In,
"try" => T::Try,
"catch" => T::Catch,
_ => T::Ident(buf.into())
})
}
fn lex_radix(&mut self, radix: i64, radix_char: char) -> Result<Token> {
use Token::*;
use Error::*;
fn lex_radix(&mut self, radix: i64, radix_char: char) -> Result<TokenData> {
use TokenData as T;
use Error as E;
let mut n = 0i64;
let mut char_found = false;
@ -328,21 +375,21 @@ impl Lexer {
n = n * radix + (i as i64);
char_found = true;
} else if self.peek().is_ident() {
return Err(InvalidDigit(self.peek()).into())
return Err(E::InvalidDigit(self.peek()).into())
} else {
break;
}
}
if char_found {
Ok(Int(n))
Ok(T::Int(n))
} else {
Err(InvalidNumber(format!("0{radix_char}")).into())
Err(E::InvalidNumber(format!("0{radix_char}")).into())
}
}
fn lex_number(&mut self, initial: char) -> Result<Token> {
use Error::*;
fn lex_number(&mut self, initial: char) -> Result<TokenData> {
use Error as E;
if initial == '0' {
match self.peek() {
@ -361,6 +408,10 @@ impl Lexer {
let mut buf = String::new();
buf.push(initial);
let mut pos = self.pos;
let mut idx = self.index;
let mut bidx = self.byte_len;
if initial != '.' {
loop {
if !self.peek().is_ascii_digit() { break; }
@ -368,10 +419,22 @@ impl Lexer {
}
if self.peek() == '.' {
pos = self.pos;
idx = self.index;
bidx = self.byte_len;
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());
@ -388,93 +451,110 @@ impl Lexer {
buf.push(self.next());
}
}
}
let complex = self.peek() == 'i';
if complex {
self.next();
}
if self.peek().is_ident() || self.peek() == '.' {
return Err(UnexpectedCharacter(self.peek()).into())
if self.peek().is_ident() {
return Err(E::UnexpectedCharacter(self.peek()).into())
}
if let Ok(int) = buf.parse::<i64>() {
use Token::*;
use TokenData as T;
if complex {
return Ok(Complex(int as f64))
return Ok(T::Complex(int as f64))
}
return Ok(Int(int))
return Ok(T::Int(int))
}
if let Ok(float) = buf.parse::<f64>() {
use Token::*;
use TokenData as T;
if complex {
return Ok(Complex(float))
return Ok(T::Complex(float))
}
return Ok(Float(float))
return Ok(T::Float(float))
}
Err(Error::InvalidNumber(buf).into())
Err(E::InvalidNumber(buf).into())
}
fn peek_token_impl(&mut self, ignore_newlines: bool) -> Result<Token> {
let idx = self.index;
let pos = self.pos;
let bidx = self.byte_len;
let token = self.next_token_impl(ignore_newlines);
self.index = idx;
self.pos = pos;
self.byte_len = bidx;
token
}
fn next_token_impl(&mut self, ignore_newlines: bool) -> Result<Token> {
use Token::*;
use Error::*;
use TokenData as T;
use Error as E;
self.skip_whitespace(ignore_newlines);
let str_start = self.index;
let byte_start = self.byte_len;
let pos = self.pos;
let char = self.next();
let next = self.peek();
if char == '\0' {
return Ok(if ignore_newlines { Eof } else { SemiColon });
let data = if ignore_newlines { T::Eof } else { T::SemiColon };
return Ok(Token {
data,
pos,
str: String::new(),
bidx: byte_start,
blen: 0,
})
}
Ok(match char {
'\n' => SemiColon,
'(' => LeftParen,
')' => RightParen,
'[' => LeftBrack,
']' => RightBrack,
'{' => LeftBrace,
'}' => RightBrace,
':' => Colon,
'\\' => Backslash,
';' => SemiColon,
let data = match char {
'\n' => T::SemiColon,
'(' => T::LeftParen,
')' => T::RightParen,
'[' => T::LeftBrack,
']' => T::RightBrack,
'{' => T::LeftBrace,
'}' => T::RightBrace,
':' => T::Colon,
'\\' => T::Backslash,
';' => T::SemiColon,
'+' => {
match next {
'=' => {
self.next();
AssignAdd
T::AssignAdd
}
_ => Add
_ => T::Add
}
},
'/' => {
match next {
'=' => {
self.next();
AssignDivide
T::AssignDivide
}
_ => Divide
_ => T::Divide
}
},
'%' => {
match next {
'=' => {
self.next();
AssignModulo
T::AssignModulo
}
_ => Modulo
_ => T::Modulo
}
},
',' => Comma,
',' => T::Comma,
'*' => {
match next {
'*' => {
@ -482,25 +562,25 @@ impl Lexer {
match self.peek() {
'=' => {
self.next();
AssignPower
T::AssignPower
},
_ => Power
_ => T::Power
}
},
'=' => {
self.next();
AssignMultiply
T::AssignMultiply
}
_ => Multiply
_ => T::Multiply
}
},
'!' => {
match next {
'=' => {
self.next();
NotEqual
T::NotEqual
}
_ => Not
_ => T::Not
}
}
'&' => {
@ -510,16 +590,16 @@ impl Lexer {
match self.peek() {
'=' => {
self.next();
AssignAnd
T::AssignAnd
},
_ => And
_ => T::And
}
},
'=' => {
self.next();
AssignBitwiseAnd
T::AssignBitwiseAnd
},
_ => BitwiseAnd
_ => T::BitwiseAnd
}
},
'|' => {
@ -529,42 +609,46 @@ impl Lexer {
match self.peek() {
'=' => {
self.next();
AssignOr
T::AssignOr
},
_ => Or
_ => T::Or
}
},
'=' => {
self.next();
AssignBitwiseOr
T::AssignBitwiseOr
},
_ => 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
})
}

View file

@ -1,4 +1,4 @@
use std::fmt::Display;
use std::{fmt::Display, rc::Rc};
pub mod compiler;
pub mod value;
@ -9,8 +9,11 @@ pub mod parse;
pub mod chunk;
pub mod ast;
#[derive(Debug)]
pub struct Error(Box<ErrorInner>);
pub use ::num_complex::Complex64 as Complex64;
pub use ::num_rational::Rational64 as Rational64;
#[derive(Debug, Clone)]
pub struct Error(Rc<ErrorInner>);
impl std::error::Error for Error {}
@ -22,6 +25,8 @@ enum ErrorInner {
Compile(compiler::Error),
Runtime(vm::Error),
External(anyhow::Error),
Traced(Box<Error>, String),
Any(String),
}
impl Display for crate::Error {
@ -34,6 +39,8 @@ impl Display for crate::Error {
Compile(err) => write!(f, "{err}"),
Runtime(err) => write!(f, "{err}"),
External(err) => write!(f, "{err}"),
Traced(err, trace) => write!(f, "{err}\n{trace}"),
Any(err) => write!(f, "{err}"),
}
}
}
@ -42,7 +49,7 @@ macro_rules! from_error {
($struct:ty, $tuple:tt) => {
impl From<$struct> for crate::Error {
fn from(value: $struct) -> Self {
crate::Error(Box::new(ErrorInner::$tuple(value)))
crate::Error(Rc::new(ErrorInner::$tuple(value)))
}
}
};
@ -54,5 +61,62 @@ from_error!(value::Error, Value);
from_error!(compiler::Error, Compile);
from_error!(vm::Error, Runtime);
from_error!(anyhow::Error, External);
from_error!(String, Any);
impl From<(Error, String)> for Error {
fn from(value: (Error, String)) -> Self {
Self(Rc::new(ErrorInner::Traced(Box::new(value.0), value.1)))
}
}
#[macro_export]
macro_rules! iter {
($fn:expr) => {
$crate::value::Value::Iter(
::std::rc::Rc::new(
$crate::chunk::Function {
name: ::std::rc::Rc::from("<iterator>"),
arity: 0,
variadic: false,
fun: $crate::chunk::InnerFunction::Native(
::std::rc::Rc::new($fn
))}))
};
}
#[macro_export]
macro_rules! native {
($name:expr, $arity:expr, $varadic:expr, $fn:expr) => {
$crate::value::Value::Function(
::std::rc::Rc::new( $crate::chunk::Function {
name: std::rc::Rc::from($name),
arity: $arity,
variadic: $varadic,
fun: $crate::chunk::InnerFunction::Native(
::std::rc::Rc::new($fn)
)
})
)
}
}
#[macro_export]
macro_rules! unpack_args {
($e:expr) => {
$e.try_into().expect("bypassed arity check")
};
}
#[macro_export]
macro_rules! unpack_varargs {
($e:expr) => {{
let mut args = $e;
let $crate::value::Value::List(varargs) = args.pop().expect("bypassed arity check") else {
panic!("bypassed arity check")
};
let varargs = varargs.into_inner();
(args.try_into().expect("bypassed arity check"), varargs)
}};
}
pub type Result<T> = std::result::Result<T, crate::Error>;

View file

@ -1,11 +1,10 @@
use std::{fmt::Display, rc::Rc};
use num_complex::Complex64;
use crate::{lex::{Lexer, self, Token}, ast::{Expr, BinaryOp, UnaryOp, optimize}, value::{Value, self}, Result};
use crate::{lex::{Lexer, self, Token, TokenData, Position}, ast::{Expr, BinaryOp, UnaryOp, optimize, ExprData, AstName}, value::{Value, self}, Result};
use Value as V;
use Expr as E;
use Token as T;
use ExprData as E;
use TokenData as T;
pub struct ParserBuilder {
optimize: bool
@ -38,8 +37,7 @@ pub struct Parser {
pub enum Error {
LexerError(crate::lex::Error),
UnexpectedToken(Token),
ExpectedToken(Token),
ExpectedTokenName(&'static str),
ExpectedToken(TokenData, Position),
MatrixInvDomain(usize, usize, usize),
NotAssignable(Expr),
ValueError(value::Error),
@ -50,9 +48,8 @@ impl Display for Error {
use Error::*;
match self {
LexerError(err) => write!(f, "{err}"),
UnexpectedToken(tok) => write!(f, "Unexpected token: '{tok:?}'"),
ExpectedToken(tok) => write!(f, "Expected token: '{tok:?}'"),
ExpectedTokenName(name) => write!(f, "Expected {name} token"),
UnexpectedToken(tok) => write!(f, "Unexpected token: '{:?}' at {}:{}", tok.data, tok.pos.row, tok.pos.col),
ExpectedToken(tok, pos) => write!(f, "Expected token: '{tok:?}' at {}:{}", pos.row, pos.col),
MatrixInvDomain(row, should, was) => write!(f, "In row {row} of matrix, domain was expected to be {should} but was given {was}"),
NotAssignable(expr) => write!(f, "{expr:?} is not assignable"),
ValueError(err) => write!(f, "{err}"),
@ -71,13 +68,14 @@ impl std::error::Error for Error {}
macro_rules! expr_parser {
($parser:ident, $pattern:pat, $fn:ident) => {{
let mut expr = $parser.$fn()?;
let pos = expr.pos;
loop {
let tok = $parser.lexer.peek_token_nl()?;
match tok {
match tok.data {
$pattern => {
$parser.lexer.next_token_nl()?;
let temp = $parser.$fn()?;
expr = E::BinaryOp(Box::new(expr), Box::new(temp), BinaryOp::from(tok))
expr = (E::BinaryOp(Box::new(expr), Box::new(temp), BinaryOp::from(tok.data)), pos).into()
}
_ => break
}
@ -90,10 +88,11 @@ macro_rules! expr_parser_reverse {
($parser:ident, $pattern:pat, $fn:ident, $cur:ident) => {{
let expr = $parser.$fn()?;
let tok = $parser.lexer.peek_token_nl()?;
Ok(match tok {
let pos = tok.pos;
Ok(match tok.data {
$pattern => {
$parser.lexer.next_token_nl()?;
E::BinaryOp(Box::new(expr), Box::new($parser.$cur()?), BinaryOp::from(tok))
(E::BinaryOp(Box::new(expr), Box::new($parser.$cur()?), BinaryOp::from(tok.data)), pos).into()
}
_ => expr
})
@ -102,19 +101,19 @@ macro_rules! expr_parser_reverse {
impl Parser {
fn force_token(&mut self, tok: Token) -> Result<Token> {
fn force_token(&mut self, tok: TokenData) -> Result<TokenData> {
let next = self.lexer.next_token()?;
if next != tok {
Err(Error::ExpectedToken(tok).into())
if next.data != tok {
Err(Error::ExpectedToken(tok, next.pos).into())
} else {
Ok(tok)
}
}
fn force_token_nl(&mut self, tok: Token) -> Result<Token> {
fn force_token_nl(&mut self, tok: TokenData) -> Result<TokenData> {
let next = self.lexer.next_token_nl()?;
if next != tok {
Err(Error::ExpectedToken(tok).into())
if next.data != tok {
Err(Error::ExpectedToken(tok, next.pos).into())
} else {
Ok(tok)
}
@ -124,7 +123,7 @@ impl Parser {
self.force_token(T::LeftParen)?;
let mut params = Vec::new();
loop {
let expr = match self.lexer.peek_token()? {
let expr = match self.lexer.peek_token()?.data {
T::RightParen => {
self.lexer.next_token()?;
break
@ -133,7 +132,7 @@ impl Parser {
};
params.push(expr);
let next = self.lexer.next_token()?;
match next {
match next.data {
T::Comma => continue,
T::RightParen => break,
_ => return Err(Error::UnexpectedToken(next).into())
@ -146,7 +145,7 @@ impl Parser {
self.force_token(T::LeftBrack)?;
let mut indicies = Vec::new();
loop {
let expr = match self.lexer.peek_token()? {
let expr = match self.lexer.peek_token()?.data {
T::RightBrack => {
self.lexer.next_token()?;
break
@ -155,7 +154,7 @@ impl Parser {
};
indicies.push(expr);
let next = self.lexer.next_token()?;
match next {
match next.data {
T::SemiColon => continue,
T::RightBrack => break,
_ => return Err(Error::UnexpectedToken(next).into())
@ -167,13 +166,13 @@ impl Parser {
fn parse_matrix_part(&mut self) -> Result<Vec<Expr>> {
let mut part = Vec::new();
loop {
let expr = match self.lexer.peek_token()? {
let expr = match self.lexer.peek_token()?.data {
T::SemiColon => break,
T::RightBrack => break,
_ => self.parse_expr()?
};
part.push(expr);
match self.lexer.peek_token()? {
match self.lexer.peek_token()?.data {
T::Comma => {
self.lexer.next_token()?;
},
@ -184,20 +183,21 @@ impl Parser {
}
fn parse_matrix(&mut self) -> Result<Expr> {
let pos = self.lexer.peek_token()?.pos;
self.force_token(T::LeftBrack)?;
let mut parts = Vec::new();
loop {
let part = self.parse_matrix_part()?;
parts.push(part);
let next = self.lexer.next_token()?;
match next {
match next.data {
T::SemiColon => continue,
T::RightBrack => break,
_ => return Err(Error::UnexpectedToken(next).into()),
};
}
if parts.len() == 1 {
Ok(E::List(parts.pop().unwrap()))
Ok((E::List(parts.pop().unwrap()), pos).into())
} else {
let codomain = parts.len();
let domain = parts[0].len();
@ -211,31 +211,32 @@ impl Parser {
while let Some(part) = parts.pop() {
data.extend(part);
}
Ok(E::Matrix((domain, codomain, data)))
Ok((E::Matrix((domain, codomain, data)), pos).into())
}
}
fn parse_table_key(&mut self) -> Result<Expr> {
let tok = self.lexer.next_token()?;
Ok(match tok {
Ok(match tok.data {
T::LeftBrack => {
let expr = self.parse_expr()?;
self.force_token(T::RightBrack)?;
expr
},
T::Ident(ident) => E::Literal(V::String(ident.to_string().into())),
T::String(string) => E::Literal(V::String(string.to_string().into())),
T::Ident(ident) => (E::Literal(V::String(ident.to_string().into())), tok.pos).into(),
T::String(string) => (E::Literal(V::String(string.to_string().into())), tok.pos).into(),
_ => return Err(Error::UnexpectedToken(tok).into())
})
}
fn parse_table(&mut self) -> Result<Expr> {
let pos = self.lexer.peek_token()?.pos;
self.force_token(T::Colon)?;
self.force_token(T::LeftBrace)?;
let mut table = Vec::new();
if self.lexer.peek_token()? == T::RightBrace {
if self.lexer.peek_token()?.data == T::RightBrace {
self.lexer.next_token()?;
return Ok(E::Table(table))
return Ok((E::Table(table), pos).into())
}
loop {
let key = self.parse_table_key()?;
@ -243,13 +244,13 @@ impl Parser {
let value = self.parse_expr()?;
table.push((key, value));
let next = self.lexer.next_token()?;
match next {
match next.data {
T::Comma => continue,
T::RightBrace => break,
_ => return Err(Error::UnexpectedToken(next).into())
}
}
Ok(E::Table(table))
Ok((E::Table(table), pos).into())
}
fn parse_paren(&mut self) -> Result<Expr> {
@ -259,28 +260,26 @@ impl Parser {
Ok(expr)
}
fn parse_params(&mut self) -> Result<(Vec<Rc<str>>, bool)> {
use T::*;
fn parse_params(&mut self) -> Result<(Vec<(Rc<str>, Position)>, bool)> {
let tok = self.lexer.next_token()?;
match tok {
Ident(ident) => {
let params = vec![ident];
if self.lexer.peek_token()? == T::Varadic {
match tok.data {
T::Ident(ident) => {
let params = vec![(ident, tok.pos)];
if self.lexer.peek_token()?.data == T::Varadic {
self.lexer.next_token()?;
return Ok((params, true))
} else {
return Ok((params, false))
}
}
LeftParen => (),
T::LeftParen => (),
_ => return Err(Error::UnexpectedToken(tok).into()),
}
let mut params = Vec::new();
let mut varadic = false;
if self.lexer.peek_token()? == T::RightParen {
self.lexer.next_token()?;
if self.lexer.peek_token()?.data == T::RightParen {
return Ok((params, varadic));
}
@ -288,14 +287,14 @@ impl Parser {
let ident = self.parse_ident()?;
params.push(ident);
let next = self.lexer.next_token()?;
match next {
Varadic => {
match next.data {
T::Varadic => {
varadic = true;
self.force_token(T::RightParen)?;
break;
}
Comma => continue,
RightParen => break,
T::Comma => continue,
T::RightParen => break,
_ => return Err(Error::UnexpectedToken(next).into()),
}
}
@ -303,103 +302,135 @@ impl Parser {
Ok((params, varadic))
}
fn parse_ident(&mut self) -> Result<Rc<str>> {
if let T::Ident(ident) = self.lexer.next_token()? {
Ok(ident)
fn parse_ident(&mut self) -> Result<AstName> {
let next = self.lexer.next_token()?;
if let T::Ident(ident) = next.data {
Ok((ident, next.pos))
} else {
Err(Error::ExpectedTokenName("Ident").into())
Err(Error::UnexpectedToken(next).into())
}
}
fn parse_ident_nl(&mut self) -> Result<Rc<str>> {
if let T::Ident(ident) = self.lexer.next_token_nl()? {
fn parse_wrapped_ident(&mut self) -> Result<AstName> {
if self.lexer.peek_token()?.data == T::LeftParen {
self.lexer.next_token()?;
let ident = self.parse_ident()?;
self.force_token(T::RightParen)?;
Ok(ident)
} else {
Err(Error::ExpectedTokenName("Ident").into())
self.parse_ident()
}
}
fn parse_ident_nl(&mut self) -> Result<AstName> {
let next = self.lexer.next_token_nl()?;
if let T::Ident(ident) = next.data {
Ok((ident, next.pos))
} else {
Err(Error::UnexpectedToken(next).into())
}
}
fn parse_function(&mut self) -> Result<Expr> {
let pos = self.lexer.peek_token()?.pos;
self.force_token(T::Function)?;
let ident = self.parse_ident()?;
let (params, varadic) = match self.lexer.peek_token()? {
let (params, varadic) = match self.lexer.peek_token()?.data {
T::LeftBrace => (vec![], false),
_ => self.parse_params()?,
};
let expr = self.parse_expr()?;
Ok(E::Function(ident, params, Box::new(expr), varadic))
Ok((E::Function(ident, params, Box::new(expr), varadic), pos).into())
}
fn parse_lambda(&mut self) -> Result<Expr> {
let pos = self.lexer.peek_token()?.pos;
self.force_token(T::Backslash)?;
let (params, varadic) = match self.lexer.peek_token()? {
let (params, varadic) = match self.lexer.peek_token()?.data {
T::Arrow => (vec![], false),
_ => self.parse_params()?,
};
self.force_token(T::Arrow)?;
let expr = self.parse_expr()?;
Ok(E::Lambda(params, Box::new(expr), varadic))
Ok((E::Lambda(params, Box::new(expr), varadic), pos).into())
}
fn parse_do_while(&mut self) -> Result<Expr> {
let pos = self.lexer.peek_token()?.pos;
self.force_token(T::Do)?;
let expr = Box::new(self.parse_expr()?);
self.force_token(T::While)?;
let cond = Box::new(self.parse_expr()?);
Ok(E::DoWhile(expr, cond))
Ok((E::DoWhile(expr, cond), pos).into())
}
fn parse_while(&mut self) -> Result<Expr> {
let pos = self.lexer.peek_token()?.pos;
self.force_token(T::While)?;
let cond = Box::new(self.parse_expr()?);
let expr = Box::new(self.parse_expr()?);
Ok(E::While(cond, expr))
Ok((E::While(cond, expr), pos).into())
}
fn parse_loop(&mut self) -> Result<Expr> {
let pos = self.lexer.peek_token()?.pos;
self.force_token(T::Loop)?;
let expr = self.parse_expr()?;
Ok(E::Loop(Box::new(expr)))
Ok((E::Loop(Box::new(expr)), pos).into())
}
fn parse_for(&mut self) -> Result<Expr> {
let pos = self.lexer.peek_token()?.pos;
self.force_token(T::For)?;
let name = self.parse_ident()?;
self.force_token(T::In)?;
let cond = Box::new(self.parse_expr()?);
let expr = Box::new(self.parse_expr()?);
Ok((E::For(name, cond, expr), pos).into())
}
fn parse_if(&mut self) -> Result<Expr> {
let pos = self.lexer.peek_token()?.pos;
self.force_token(T::If)?;
let cond = Box::new(self.parse_expr()?);
let expr = Box::new(self.parse_expr()?);
if self.lexer.peek_token()? != T::Else {
return Ok(E::If(cond, expr, None))
if self.lexer.peek_token()?.data != T::Else {
return Ok((E::If(cond, expr, None), pos).into())
}
self.lexer.next_token()?;
if self.lexer.peek_token()? == T::If {
Ok(E::If(cond, expr, Some(Box::new(self.parse_if()?))))
if self.lexer.peek_token()?.data == T::If {
Ok((E::If(cond, expr, Some(Box::new(self.parse_if()?))), pos).into())
} else {
Ok(E::If(cond, expr, Some(Box::new(self.parse_expr()?))))
Ok((E::If(cond, expr, Some(Box::new(self.parse_expr()?))), pos).into())
}
}
fn parse_let(&mut self) -> Result<Expr> {
let pos = self.lexer.peek_token()?.pos;
self.force_token(T::Let)?;
let ident = self.parse_ident_nl()?;
if self.lexer.peek_token_nl()? == T::Assign {
if self.lexer.peek_token_nl()?.data == T::Assign {
self.force_token_nl(T::Assign)?;
Ok(E::Let(ident, Box::new(self.parse_expr()?)))
Ok((E::Let(ident, Box::new(self.parse_expr()?)), pos).into())
} else {
Ok(E::Let(ident, Box::new(E::Literal(V::Nil))))
Ok((E::Let(ident, Box::new((E::Literal(V::Nil), pos).into())), pos).into())
}
}
fn parse_return(&mut self) -> Result<Expr> {
let pos = self.lexer.peek_token()?.pos;
self.force_token(T::Return)?;
Ok(E::Return(Box::new(self.parse_expr()?)))
Ok((E::Return(Box::new(self.parse_expr()?)), pos).into())
}
fn parse_block(&mut self) -> Result<Expr> {
let mut block = Vec::new();
let pos = self.lexer.peek_token()?.pos;
self.force_token(T::LeftBrace)?;
loop {
let expr = match self.lexer.peek_token()? {
let expr = match self.lexer.peek_token()?.data {
T::RightBrace => break,
T::SemiColon => {
self.lexer.next_token()?;
@ -408,22 +439,32 @@ impl Parser {
_ => self.parse_expr()?
};
block.push(expr);
let next = self.lexer.next_token()?;
match next {
let next = self.lexer.next_token_nl()?;
match next.data {
T::SemiColon => continue,
T::RightBrace => break,
_ => return Err(Error::ExpectedToken(T::SemiColon).into())
_ => return Err(Error::ExpectedToken(T::SemiColon, next.pos).into())
}
}
if self.lexer.peek_token()? == T::RightBrace {
if self.lexer.peek_token()?.data == T::RightBrace {
self.lexer.next_token()?;
}
Ok(E::Block(block))
Ok((E::Block(block), pos).into())
}
fn parse_try(&mut self) -> Result<Expr> {
let pos = self.lexer.peek_token()?.pos;
self.force_token(T::Try)?;
let expr = self.parse_expr()?;
self.force_token(T::Catch)?;
let ident = self.parse_wrapped_ident()?;
let catch = self.parse_expr()?;
Ok((E::Try(Box::new(expr), ident, Box::new(catch)), pos).into())
}
fn parse_value(&mut self) -> Result<Expr> {
let tok = self.lexer.next_token()?;
Ok(match tok {
let data = match tok.data {
T::Nil => E::Literal(V::Nil),
T::Int(i) => E::Literal(V::Int(i)),
T::Float(f) => E::Literal(V::Float(f)),
@ -434,28 +475,31 @@ impl Parser {
T::False => E::Literal(V::Bool(false)),
T::Ident(ident) => E::Ident(ident),
_ => return Err(Error::UnexpectedToken(tok).into()),
})
};
Ok((data, tok.pos).into())
}
fn parse_term(&mut self) -> Result<Expr> {
use T::*;
match self.lexer.peek_token()? {
match self.lexer.peek_token()?.data {
Function => self.parse_function(),
Backslash => self.parse_lambda(),
Do => self.parse_do_while(),
While => self.parse_while(),
For => self.parse_for(),
Let => self.parse_let(),
LeftBrace => self.parse_block(),
Return => self.parse_return(),
If => self.parse_if(),
Loop => self.parse_loop(),
Try => self.parse_try(),
Break => {
self.lexer.next_token()?;
Ok(E::Break)
let next = self.lexer.next_token()?;
Ok((E::Break, next.pos).into())
},
Continue => {
self.lexer.next_token()?;
Ok(E::Continue)
let next = self.lexer.next_token()?;
Ok((E::Continue, next.pos).into())
},
LeftBrack => self.parse_matrix(),
Colon => self.parse_table(),
@ -466,13 +510,14 @@ impl Parser {
fn parse_expr_expr_access(&mut self) -> Result<Expr> {
let mut expr = self.parse_term()?;
let pos = expr.pos;
loop {
let tok = self.lexer.peek_token()?;
match tok {
match tok.data {
T::Access => {
self.force_token(T::Access)?;
let temp = self.parse_ident()?;
expr = E::FieldAccess(Box::new(expr), temp);
expr = (E::FieldAccess(Box::new(expr), temp), pos).into();
},
_ => break
}
@ -482,16 +527,17 @@ impl Parser {
fn parse_expr_call(&mut self) -> Result<Expr> {
let mut expr = self.parse_expr_expr_access()?;
let pos = expr.pos;
loop {
let tok = self.lexer.peek_token()?;
match tok {
match tok.data {
T::LeftBrack => {
let index = self.parse_index()?;
expr = E::Index(Box::new(expr), index);
expr = (E::Index(Box::new(expr), index), pos).into();
},
T::LeftParen => {
let params = self.parse_fn_call()?;
expr = E::FnCall(Box::new(expr), params);
expr = (E::FnCall(Box::new(expr), params), pos).into();
}
_ => break
}
@ -501,14 +547,14 @@ impl Parser {
fn parse_expr_unary(&mut self) -> Result<Expr> {
let tok = self.lexer.peek_token_nl()?;
Ok(match tok {
Ok(match tok.data {
T::Not => {
self.lexer.next_token()?;
E::UnaryOp(Box::new(self.parse_expr_unary()?), UnaryOp::Not)
(E::UnaryOp(Box::new(self.parse_expr_unary()?), UnaryOp::Not), tok.pos).into()
}
T::Subtract => {
self.lexer.next_token()?;
E::UnaryOp(Box::new(self.parse_expr_unary()?), UnaryOp::Negate)
(E::UnaryOp(Box::new(self.parse_expr_unary()?), UnaryOp::Negate), tok.pos).into()
}
_ => self.parse_expr_call()?
})
@ -559,13 +605,14 @@ impl Parser {
fn parse_expr_and(&mut self) -> Result<Expr> {
let mut expr = self.parse_expr_compare()?;
let pos = expr.pos;
loop {
let tok = self.lexer.peek_token()?;
match tok {
match tok.data {
T::And => {
self.force_token(T::And)?;
let temp = self.parse_expr_compare()?;
expr = E::And(Box::new(expr), Box::new(temp));
expr = (E::And(Box::new(expr), Box::new(temp)), pos).into();
},
_ => break
}
@ -575,13 +622,14 @@ impl Parser {
fn parse_expr_or(&mut self) -> Result<Expr> {
let mut expr = self.parse_expr_and()?;
let pos = expr.pos;
loop {
let tok = self.lexer.peek_token()?;
match tok {
match tok.data {
T::Or => {
self.force_token(T::Or)?;
let temp = self.parse_expr_and()?;
expr = E::Or(Box::new(expr), Box::new(temp));
expr = (E::Or(Box::new(expr), Box::new(temp)), pos).into();
},
_ => break
}
@ -589,75 +637,112 @@ impl Parser {
Ok(expr)
}
fn parse_expr(&mut self) -> Result<Expr> {
use BinaryOp as B;
fn parse_expr_range(&mut self) -> Result<Expr> {
let expr = self.parse_expr_or()?;
let pos = expr.pos;
match self.lexer.peek_token()?.data {
T::Range => {
self.lexer.next_token()?;
let temp = self.parse_expr_or()?;
Ok((E::BinaryOp(Box::new(expr), Box::new(temp), BinaryOp::Range), pos).into())
},
T::RangeEq => {
self.lexer.next_token()?;
let temp = self.parse_expr_or()?;
Ok((E::BinaryOp(Box::new(expr), Box::new(temp), BinaryOp::RangeEq), pos).into())
},
_ => Ok(expr)
}
}
fn parse_expr_op_assign(&mut self) -> Result<Expr> {
use BinaryOp as B;
let expr = self.parse_expr_range()?;
let tok = self.lexer.peek_token_nl()?;
Ok(match tok {
let pos = tok.pos;
let data: ExprData = match tok.data {
T::Assign => {
self.lexer.next_token_nl()?;
E::Assign(Box::new(expr.clone()),Box::new(self.parse_expr()?))
},
T::AssignAnd => {
self.lexer.next_token_nl()?;
E::Assign(Box::new(expr.clone()),Box::new(E::And(Box::new(expr), Box::new(self.parse_expr()?))))
E::Assign(Box::new(expr.clone()),Box::new((E::And(Box::new(expr), Box::new(self.parse_expr()?)), pos).into()))
},
T::AssignOr => {
self.lexer.next_token_nl()?;
E::Assign(Box::new(expr.clone()),Box::new(E::Or(Box::new(expr), Box::new(self.parse_expr()?))))
E::Assign(Box::new(expr.clone()),Box::new((E::Or(Box::new(expr), Box::new(self.parse_expr()?)),pos).into()))
},
T::AssignAdd => {
self.lexer.next_token_nl()?;
E::Assign(Box::new(expr.clone()),Box::new(E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::Add)))
E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::Add),pos).into()))
},
T::AssignSubtract => {
self.lexer.next_token_nl()?;
E::Assign(Box::new(expr.clone()),Box::new(E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::Subtract)))
E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::Subtract),pos).into()))
},
T::AssignMultiply => {
self.lexer.next_token_nl()?;
E::Assign(Box::new(expr.clone()),Box::new(E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::Multiply)))
E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::Multiply),pos).into()))
},
T::AssignDivide => {
self.lexer.next_token_nl()?;
E::Assign(Box::new(expr.clone()),Box::new(E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::Divide)))
E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::Divide),pos).into()))
},
T::AssignModulo => {
self.lexer.next_token_nl()?;
E::Assign(Box::new(expr.clone()),Box::new(E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::Modulo)))
E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::Modulo),pos).into()))
},
T::AssignPower => {
self.lexer.next_token_nl()?;
E::Assign(Box::new(expr.clone()),Box::new(E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::Power)))
E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::Power),pos).into()))
},
T::AssignBitwiseAnd => {
self.lexer.next_token_nl()?;
E::Assign(Box::new(expr.clone()),Box::new(E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::BitwiseAnd)))
E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::BitwiseAnd),pos).into()))
},
T::AssignBitwiseOr => {
self.lexer.next_token_nl()?;
E::Assign(Box::new(expr.clone()),Box::new(E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::BitwiseOr)))
E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::BitwiseOr),pos).into()))
},
T::AssignBitwiseXor => {
self.lexer.next_token_nl()?;
E::Assign(Box::new(expr.clone()),Box::new(E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::BitwiseXor)))
E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::BitwiseXor),pos).into()))
},
T::AssignBitwiseShiftLeft => {
self.lexer.next_token_nl()?;
E::Assign(Box::new(expr.clone()),Box::new(E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::BitwiseShiftLeft)))
E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::BitwiseShiftLeft),pos).into()))
},
T::AssignBitwiseShiftRight => {
self.lexer.next_token_nl()?;
E::Assign(Box::new(expr.clone()),Box::new(E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::BitwiseShiftRight)))
E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::BitwiseShiftRight),pos).into()))
},
_ => expr
})
_ => expr.data
};
Ok((data, pos).into())
}
fn parse_expr(&mut self) -> Result<Expr> {
let mut expr = self.parse_expr_op_assign()?;
let pos = expr.pos;
loop {
let tok = self.lexer.peek_token()?;
match tok.data {
T::Pipe => {
self.force_token(T::Pipe)?;
let temp = self.parse_expr_op_assign()?;
expr = (E::Pipeline(Box::new(expr), Box::new(temp)), pos).into();
},
_ => break
}
}
Ok(expr)
}
fn parse_root(&mut self) -> Result<Expr> {
let mut block = Vec::new();
loop {
match self.lexer.peek_token()? {
match self.lexer.peek_token()?.data {
T::Eof => break,
T::SemiColon => {
self.lexer.next_token()?;
@ -667,13 +752,14 @@ impl Parser {
};
let expr = self.parse_expr()?;
block.push(expr);
match self.lexer.next_token()? {
let next = self.lexer.next_token()?;
match next.data {
T::Eof => break,
T::SemiColon => continue,
_ => return Err(Error::ExpectedToken(T::SemiColon).into())
_ => return Err(Error::ExpectedToken(T::SemiColon, next.pos).into())
};
}
Ok(E::Block(block))
Ok((E::Block(block), Position::default()).into())
}
pub fn parse<T: Into<String>>(&mut self, into: T) -> Result<Expr> {

View file

@ -1,5 +1,14 @@
use std::{collections::HashMap, rc::Rc, hash::Hash, fmt::{Display, Debug}, ops::{Add, Neg, Not, Sub, Div, Mul, BitOr, BitAnd, BitXor, Shl, Shr}, cmp::Ordering};
use std::{
collections::HashMap,
rc::Rc,
hash::Hash,
fmt::{Display, Debug},
ops::{Add, Neg, Not, Sub, Div, Mul, BitOr, BitAnd, BitXor, Shl, Shr},
cmp::Ordering,
cell::RefCell, fs::File
};
use crate::iter;
use num_complex::Complex64;
use num_rational::Rational64;
use regex::Regex;
@ -29,67 +38,29 @@ impl ValueMap {
self.0.insert(key, value);
Ok(())
}
pub fn len(&self) -> usize {
self.0.len()
}
}
#[derive(Debug)]
pub enum Error {
Neg(Value),
Add(Value, Value),
Subtract(Value, Value),
Multiply(Value, Value),
Divide(Value, Value),
Modulo(Value, Value),
Exponent(Value, Value),
Compare(Value, Value),
IndexOutOfBounds(i64, usize),
MatrixOutOfBounds(usize, usize, usize, usize),
MatrixIndexOutOfBounds(i64, usize, usize),
MatrixInvSlice(usize, usize),
MatrixMultiply(usize, usize),
MatrixAdd(usize, usize, usize, usize),
CannotIndex(Value),
BadIndex(Value, Value),
Concat(Value, Value),
Bitwise(Value, Value),
DivideByZero,
ZeroExpZero,
CannotHash(Value),
FieldAccess(Value),
}
pub struct Error(String);
impl Display for self::Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use self::Error::*;
match self {
Neg(v) => write!(f, "cannot negate {v:?}"),
Add(a, b) => write!(f, "cannot add {a:?} to {b:?}"),
Subtract(a, b) => write!(f, "cannot subtract {a:?} from {b:?}"),
Multiply(a, b) => write!(f, "cannot multiply {a:?} and {b:?}"),
Divide(a, b) => write!(f, "cannot divide {a:?} and {b:?}"),
Modulo(a, b) => write!(f, "cannot modulo {a:?} and {b:?}"),
Exponent(a, b) => write!(f, "cannot compute {a:?} pow {b:?}"),
Compare(a, b) => write!(f, "cannot compare {a:?} and {b:?}"),
IndexOutOfBounds(a, b) => write!(f, "index {a} out of bounds for list of length {b}"),
MatrixOutOfBounds(a, b, c, d) => write!(f, "row col ({a},{b}) is out of bounds for matrix {c}x{d}"),
MatrixIndexOutOfBounds(i, d, c) => write!(f, "row index {i} is out of bounds for matrix {d}x{c}"),
MatrixMultiply(c, d) => write!(f, "cannot multiply a matrix with the domain of {d} and matrix with the codomain of {c}"),
MatrixAdd(a, b, c, d) => write!(f, "cannot add a {a}x{b} matrix and a {c}x{d} matrix"),
MatrixInvSlice(s, e) => write!(f, "invalid matrix slice {s}..{e}"),
BadIndex(a, b) => write!(f, "cannot index {a:?} with {b:?}"),
CannotIndex(a) => write!(f, "cannot index {a:?}"),
Concat(a, b) => write!(f, "cannot concat {a:?} and {b:?}"),
Bitwise(a, b) => write!(f, "cannot bitwise between {a:?} and {b:?}"),
DivideByZero => write!(f, "attempted to divide by zero"),
ZeroExpZero => write!(f, "cannot exponent zero with zero"),
CannotHash(v) => write!(f, "cannot hash {v:?}"),
FieldAccess(a) => write!(f, "{a:?} cannot be field accessed"),
}
write!(f, "{}", self.0)
}
}
impl std::error::Error for self::Error {}
#[derive(Debug, Clone)]
macro_rules! error {
($($arg:tt)*) => {
Err(self::Error(format!($($arg)*)).into())
};
}
#[derive(Clone)]
pub enum Value {
Nil,
Bool(bool),
@ -98,11 +69,21 @@ pub enum Value {
Ratio(Rational64),
Complex(Complex64),
Regex(Rc<Regex>),
String(Rc<String>),
String(Rc<str>),
List(Gc<List>),
Matrix(Gc<Matrix>),
Table(Gc<Table>),
Function(Rc<Function>),
Range(Rc<(i64, i64, bool)>),
Iter(Rc<Function>),
File(Rc<RefCell<File>>),
Error(crate::Error),
}
impl From<&str> for Value {
fn from(value: &str) -> Self {
Value::String(value.into())
}
}
impl Hash for Value {
@ -132,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,125 +269,19 @@ 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(" ");
V::Int(i) => *i == 0,
V::Float(f) => *f == 0.0 || *f == -0.0,
V::Ratio(r) => *r.numer() == 0,
_ => false,
}
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:?}")
}
}
}
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())
}
}
@ -278,6 +289,17 @@ fn ratio_to_f64(r: Rational64) -> f64 {
*r.numer() as f64 / *r.denom() as f64
}
impl Value {
pub fn promote_trig(self) -> Self {
use Value as V;
match self {
V::Int(i) => V::Float(i as f64),
V::Ratio(r) => V::Float(ratio_to_f64(r)),
a => a
}
}
}
fn promote(a: Value, b: Value) -> (Value, Value) {
use Value as V;
match (&a, &b) {
@ -293,9 +315,12 @@ fn promote(a: Value, b: Value) -> (Value, Value) {
(V::Float(..), V::Ratio(y)) => (a, V::Float(ratio_to_f64(*y))),
(V::Complex(..), V::Ratio(y)) => (a, V::Complex(ratio_to_f64(*y).into())),
(V::Complex(..), V::Float(y)) => (a, V::Complex((*y).into())),
(V::List(l1), V::List(l2)) => (V::Matrix(Matrix::from_list(l1.to_vec()).into()), V::Matrix(Matrix::from_list(l2.to_vec()).into())),
(_, V::List(l)) => (a, V::Matrix(Matrix::from_list(l.to_vec()).into())),
(V::List(l), _) => (V::Matrix(Matrix::from_list(l.to_vec()).into()), b),
(V::List(l1), V::List(l2)) if l1.len() > 0 && l2.len() > 0
=> (V::Matrix(Matrix::from_list(l1.to_vec()).into()), V::Matrix(Matrix::from_list(l2.to_vec()).into())),
(_, V::List(l)) if l.len() > 0
=> (a, V::Matrix(Matrix::from_list(l.to_vec()).into())),
(V::List(l), _) if l.len() > 0
=> (V::Matrix(Matrix::from_list(l.to_vec()).into()), b),
_ => (a, b),
}
}
@ -310,17 +335,19 @@ impl Add for Value {
(Ratio(x), Ratio(y)) => Ok(Ratio(x + y)),
(Complex(x), Complex(y)) => Ok(Complex(x + y)),
(Matrix(x), Matrix(y)) => Ok(Matrix((x + y)?.into())),
(String(str), value) => Ok(String(Rc::new(
format!("{str}{}", value.boring_print())
(Matrix(x), r) => Ok(Matrix(x.increment(r)?.into())),
(l, Matrix(y)) => Ok(Matrix(y.increment(l)?.into())),
(String(str), value) => Ok(String(Rc::from(
format!("{str}{value}")
))),
(value, String(str)) => Ok(String(Rc::new(
format!("{}{str}", value.boring_print())
(value, String(str)) => Ok(String(Rc::from(
format!("{value}{str}")
))),
(List(mut l1), List(l2)) => {
l1.extend_from_slice(&l2);
Ok(List(l1))
},
(l, r) => Err(Error::Add(l, r).into())
(l, r) => error!("cannot add: {l:?} + {r:?}")
}
}
}
@ -335,7 +362,8 @@ impl Sub for Value {
(Ratio(x), Ratio(y)) => Ok(Ratio(x - y)),
(Complex(x), Complex(y)) => Ok(Complex(x - y)),
(Matrix(x), Matrix(y)) => Ok(Matrix((x - y)?.into())),
(l, r) => Err(Error::Subtract(l, r).into())
(Matrix(x), r) => Ok(Matrix(x.decrement(r)?.into())),
(l, r) => error!("cannot subtract: {l:?} - {r:?}")
}
}
}
@ -352,7 +380,7 @@ impl Mul for Value {
(Matrix(x), Matrix(y)) => Ok(Matrix((x * y)?.into())),
(Matrix(x), r) => Ok(Matrix(x.scale(r)?.into())),
(l, Matrix(y)) => Ok(Matrix(y.scale(l)?.into())),
(l, r) => Err(Error::Multiply(l, r).into())
(l, r) => error!("cannot multiply: {l:?} * {r:?}")
}
}
}
@ -362,12 +390,12 @@ impl Div for Value {
fn div(self, rhs: Value) -> Self::Output {
use Value::*;
match promote(self, rhs) {
(Int(_), Int(0)) => Err(Error::DivideByZero.into()),
(Int(_), Int(0)) => error!("cannot divide by zero"),
(Int(x), Int(y)) => Ok(Ratio(Rational64::new(x, y))),
(Float(x), Float(y)) => Ok(Float(x / y)),
(Ratio(x), Ratio(y)) => Ok(Ratio(x / y)),
(Complex(x), Complex(y)) => Ok(Complex(x / y)),
(l, r) => Err(Error::Divide(l, r).into())
(l, r) => error!("cannot divide: {l:?} / {r:?}")
}
}
}
@ -378,7 +406,7 @@ impl BitOr for Value {
use Value::*;
match promote(self, rhs) {
(Int(x), Int(y)) => Ok(Int(x | y)),
(l, r) => Err(Error::Bitwise(l, r).into())
(l, r) => error!("cannot bitwise or: {l:?} | {r:?}")
}
}
}
@ -389,7 +417,7 @@ impl BitAnd for Value {
use Value::*;
match promote(self, rhs) {
(Int(x), Int(y)) => Ok(Int(x & y)),
(l, r) => Err(Error::Bitwise(l, r).into())
(l, r) => error!("cannot bitwise and: {l:?} & {r:?}")
}
}
}
@ -400,7 +428,7 @@ impl BitXor for Value {
use Value::*;
match promote(self, rhs) {
(Int(x), Int(y)) => Ok(Int(x ^ y)),
(l, r) => Err(Error::Bitwise(l, r).into())
(l, r) => error!("cannot bitwise xor: {l:?} ^ {r:?}")
}
}
}
@ -411,7 +439,7 @@ impl Shl for Value {
use Value::*;
match promote(self, rhs) {
(Int(x), Int(y)) => Ok(Int(x << y)),
(l, r) => Err(Error::Bitwise(l, r).into())
(l, r) => error!("cannot bitwise shift left: {l:?} << {r:?}")
}
}
}
@ -422,14 +450,14 @@ impl Shr for Value {
use Value::*;
match promote(self, rhs) {
(Int(x), Int(y)) => Ok(Int(x >> y)),
(l, r) => Err(Error::Bitwise(l, r).into())
(l, r) => error!("cannot bitwise shift right: {l:?} >> {r:?}")
}
}
}
fn ipow(n: i64, d: i64, p: i64) -> Result<(i64, i64)> {
Ok(match (n, d, p) {
(0, _, 0) => return Err(Error::ZeroExpZero.into()),
(0, _, 0) => return error!("cannot exponent: 0 ** 0"),
(0, _, _) => (0, 1),
(_, _, 0) => (1, 1),
(1, 1, _) => (1, 1),
@ -447,7 +475,7 @@ impl Value {
(Float(x), Float(y)) => Ok(Float(x % y)),
(Ratio(x), Ratio(y)) => Ok(Ratio(x % y)),
(Complex(x), Complex(y)) => Ok(Complex(x % y)),
(l, r) => Err(Error::Modulo(r, l).into())
(l, r) => error!("cannot modulo: {l:?} % {r:?}")
}
}
@ -461,7 +489,7 @@ impl Value {
(Float(x), Float(y)) => Ok(Float(x.powf(y))),
(Ratio(x), Ratio(y)) => Ok(Float(ratio_to_f64(x).powf(ratio_to_f64(y)))),
(Complex(x), Complex(y)) => Ok(Complex(x.powc(y))),
(l, r) => Err(Error::Exponent(l, r).into())
(l, r) => error!("cannot exponent: {l:?} ** {r:?}")
}
}
@ -534,7 +562,7 @@ impl PartialOrd for Value {
impl Value {
pub fn val_cmp(&self, other: &Self) -> Result<Ordering> {
self.partial_cmp(other)
.map_or_else(|| Err(Error::Compare(self.clone(), other.clone()).into()), Ok)
.map_or_else(|| error!("cannot compare: {self:?} and {other:?}"), Ok)
}
fn index_single(&self, index: &Value) -> Result<Self> {
@ -545,17 +573,17 @@ impl Value {
},
(V::List(l), V::Int(i)) => {
if *i < 0 || *i as usize >= l.len() {
return Err(Error::IndexOutOfBounds(*i, l.len()).into())
return error!("{i} out of bounds for {self:?}")
}
Ok(l[*i as usize].clone())
},
(V::Matrix(m), V::Int(i)) => {
if *i < 0 || *i as usize >= m.values.len() {
return Err(Error::MatrixIndexOutOfBounds(*i, m.domain, m.codomain).into())
return error!("{i} out of bounds for {self:?}")
}
Ok(m.values[*i as usize].clone())
},
_ => return Err(self::Error::BadIndex(self.clone(), index.clone()).into())
_ => return error!("{index:?} cant index {self:?}")
}
}
@ -579,7 +607,7 @@ impl Value {
Ok(V::Table(ret.into()))
}
V::Matrix(m) => {
let err = || Err(self::Error::BadIndex(self.clone(), V::List(indexes.clone().into())).into());
let err = || error!("{self:?} can be index by [Int] or [Int;Int]");
if indexes.len() != 2 {
return err()
}
@ -612,7 +640,7 @@ impl Value {
_ => return err()
}
}
_ => return Err(self::Error::CannotIndex(self.clone()).into())
_ => return error!("cannot index {self:?}")
}
}
@ -628,26 +656,26 @@ impl Value {
fn store_index_single(&mut self, index: &Value, store: Value) -> Result<()> {
use Value as V;
let err = self.clone();
let err = format!("{self:?}");
match (self, index) {
(V::Table(t), index) => {
t.insert(index.clone(), store)
},
(V::List(l), V::Int(i)) => {
if *i < 0 || *i as usize >= l.len() {
return Err(Error::IndexOutOfBounds(*i, l.len()).into())
return error!("{i} out of bounds for {err}")
}
l[*i as usize] = store;
Ok(())
},
(V::Matrix(m), V::Int(i)) => {
if *i < 0 || *i as usize >= m.values.len() {
return Err(Error::MatrixIndexOutOfBounds(*i, m.domain, m.codomain).into())
return error!("{i} out of bounds for {err}")
}
m.values[*i as usize] = store;
Ok(())
},
_ => return Err(self::Error::BadIndex(err, index.clone()).into())
_ => return error!("{index:?} cant index {err}")
}
}
@ -666,7 +694,7 @@ impl Value {
}
Ok(())
}
_ => return Err(self::Error::CannotIndex(self.clone()).into())
_ => return error!("cannot index {self:?}")
}
}
@ -684,10 +712,10 @@ impl Value {
use Value as V;
match self {
V::Table(t) => {
let key = V::String(Rc::new(ident.to_string()));
let key = V::String(Rc::from(ident));
Ok(t.insert(key, val)?)
},
_ => return Err(self::Error::FieldAccess(self.clone()).into())
_ => return error!("cannot field access assign {self:?}")
}
}
@ -695,10 +723,10 @@ impl Value {
use Value as V;
match self {
V::Table(t) => {
let key = V::String(Rc::new(ident.to_string()));
let key = V::String(Rc::from(ident));
Ok(t.get(&key)?.unwrap_or(&V::Nil).clone())
},
_ => return Err(self::Error::FieldAccess(self.clone()).into())
_ => return error!("cannot field access {self:?}")
}
}
@ -722,6 +750,15 @@ impl Value {
LessEquals => Ok(Self::Bool(lhs <= rhs)),
GreaterThan => Ok(Self::Bool(lhs > rhs)),
LessThan => Ok(Self::Bool(lhs < rhs)),
Range | RangeEq => {
let Value::Int(lhs) = lhs else {
return error!("range can only take [Int]'s")
};
let Value::Int(rhs) = rhs else {
return error!("range can only take [Int]'s")
};
Ok(Self::Range(Rc::new((lhs, rhs, op == RangeEq))))
},
}
}
@ -754,24 +791,75 @@ impl Not for Value {
type Output = bool;
fn not(self) -> Self::Output {
use Value::*;
use Value as V;
match self {
Nil => true,
Bool(b) => !b,
Int(i) => i == 0,
Float(f) => f == 0.0,
Ratio(r) => *(r.numer()) == 0 || *(r.denom()) == 0,
Complex(c) => !c.is_normal(),
Regex(_) => false,
List(_) => false,
Matrix(_) => false,
Table(_) => false,
String(s) => s.as_str() == "",
Function(_) => false,
V::Nil => true,
V::Bool(b) => !b,
V::Int(i) => i == 0,
V::Float(f) => f == 0.0,
V::Ratio(r) => *(r.numer()) == 0 || *(r.denom()) == 0,
V::Complex(c) => !c.is_normal(),
V::Regex(_) => false,
V::List(_) => false,
V::Matrix(_) => false,
V::Table(_) => false,
V::String(s) => s.as_ref() == "",
V::Function(_) => false,
V::Iter(_) => false,
V::Range(_) => false,
V::File(_) => false,
V::Error(_) => false,
}
}
}
impl Value {
pub fn into_iter_fn(self) -> Result<Rc<Function>> {
let Value::Iter(iter) = self.into_iter()? else {
return error!("bypassed iter check")
};
Ok(iter)
}
pub fn into_iter(self) -> Result<Self> {
use Value as V;
Ok(match self {
V::Iter(..) => self,
V::List(l) => {
let iter = RefCell::new(l.into_inner().into_iter());
iter!(move |_,_| {
match iter.borrow_mut().next() {
Some(v) => Ok(v),
None => Ok(V::Nil),
}
})
},
V::Range(r) => {
let r = (*r).clone();
let lhs = RefCell::new(r.0);
let rhs = r.1;
iter!(move |_,_| {
let val = *lhs.borrow();
let next = *lhs.borrow() + 1;
if (!r.2 && *lhs.borrow() < rhs) || (r.2 && *lhs.borrow() <= rhs) {
*lhs.borrow_mut() = next;
return Ok(Value::Int(val))
}
Ok(Value::Nil)
})
},
V::Function(f) => {
if f.arity > 0 || f.variadic {
return error!("iterator functions cannot be varadic or take arguments")
}
V::Iter(f)
},
val => return error!("cannot turn {val:?} into an iterator")
})
}
}
fn dot(lhs: Vec<&Value>, rhs: Vec<&Value>) -> Result<Value> {
let len = lhs.len();
@ -830,9 +918,9 @@ impl Matrix {
pub fn get(&self, row: usize, col: usize) -> Result<Value> {
if row >= self.codomain || col >= self.domain {
return Err(self::Error::MatrixOutOfBounds(row, col, self.domain, self.codomain).into())
return error!("[{};{}] out of bounds for [Matrix {}x{}]", row, col, self.domain, self.codomain)
}
let idx = col + row * self.codomain;
let idx = col + row * self.domain;
Ok(self.values[idx].clone())
}
@ -840,9 +928,9 @@ impl Matrix {
pub fn set(&mut self, row: usize, col: usize, val: Value) -> Result<()> {
if row >= self.codomain || col >= self.domain {
return Err(self::Error::MatrixOutOfBounds(row, col, self.domain, self.codomain).into())
return error!("[{};{}] out of bounds for [Matrix {}x{}]", row, col, self.domain, self.codomain)
}
let idx = col + row * self.codomain;
let idx = col + row * self.domain;
self.values[idx] = val;
Ok(())
}
@ -863,8 +951,8 @@ impl Matrix {
// SPLCIE DOMAIN
pub fn splice_cols(&self, col_start: usize, col_end: usize) -> Result<Self> {
if col_start <= col_end {
return Err(self::Error::MatrixInvSlice(col_start, col_end).into());
if col_start <= col_end || col_end > self.domain {
return error!("[_;{}..{}] invalid for [Matrix {}x{}]", col_start, col_end, self.domain, self.codomain)
}
let mut cols = Vec::new();
@ -890,8 +978,8 @@ impl Matrix {
// SPLICE CODOMAIN
pub fn splice_rows(&self, row_start: usize, row_end: usize) -> Result<Self> {
if row_start <= row_end {
return Err(self::Error::MatrixInvSlice(row_start, row_end).into());
if row_start <= row_end || row_end > self.codomain {
return error!("[{}..{};_] invalid for [Matrix {}x{}]", row_start, row_end, self.domain, self.codomain)
}
let mut rows = Vec::new();
@ -915,12 +1003,64 @@ impl Matrix {
Ok(res)
}
pub fn increment(&self, increment: Value) -> Result<Self> {
let values = self.values.iter()
.map(|v| v.clone() + increment.clone())
.collect::<Result<Vec<Value>>>()?;
Ok(Matrix::new(self.domain, self.codomain, values))
}
pub fn decrement(&self, decrement: Value) -> Result<Self> {
let values = self.values.iter()
.map(|v| v.clone() - decrement.clone())
.collect::<Result<Vec<Value>>>()?;
Ok(Matrix::new(self.domain, self.codomain, values))
}
pub fn scale(&self, scale: Value) -> Result<Self> {
let values = self.values.iter()
.map(|v| v.clone() * scale.clone())
.collect::<Result<Vec<Value>>>()?;
Ok(Matrix::new(self.domain, self.codomain, values))
}
pub fn join_right(&self, other: &Matrix) -> Result<Self> {
if self.domain != other.domain || self.codomain != other.codomain {
return error!("matrix dimensions do not match");
}
let mut r1 = self.rows();
let mut r2 = other.rows();
let mut rows = Vec::new();
loop {
let Some(r1) = r1.next() else { break; };
let Some(r2) = r2.next() else { break; };
let mut row = r1
.into_iter()
.map(|v| v.clone())
.collect::<Vec<Value>>();
row.extend(r2.into_iter().map(|v| v.clone()));
rows.push(row);
}
let values = rows
.into_iter()
.reduce(|mut a,b| {a.extend(b); a})
.unwrap();
Ok(Matrix::new(self.domain * 2, self.codomain, values))
}
pub fn join_bottom(&self, other: &Matrix) -> Result<Self> {
if self.domain != other.domain || self.codomain != other.codomain {
return error!("matrix dimensions do not match");
}
let mut values = self.values.clone();
values.extend(other.values.clone());
Ok(Matrix::new(self.domain, self.codomain * 2, values))
}
}
impl Debug for Matrix {
@ -950,7 +1090,9 @@ impl Add for Matrix {
fn add(self, rhs: Self) -> Self::Output {
if self.domain != rhs.domain || self.codomain != rhs.codomain {
return Err(self::Error::MatrixAdd(self.domain, self.codomain, rhs.domain, rhs.codomain).into())
return error!("cannot add: [Matrix {}x{}] + [Matrix {}x{}]",
self.domain, self.codomain,
rhs.domain, rhs.codomain)
}
let mut res = Matrix::empty(self.domain, self.codomain);
for col in 0..self.domain {
@ -968,7 +1110,9 @@ impl Sub for Matrix {
fn sub(self, rhs: Self) -> Self::Output {
if self.domain != rhs.domain || self.codomain != rhs.codomain {
return Err(self::Error::MatrixAdd(self.domain, self.codomain, rhs.domain, rhs.codomain).into())
return error!("cannot sub: [Matrix {}x{}] - [Matrix {}x{}]",
self.domain, self.codomain,
rhs.domain, rhs.codomain)
}
let mut res = Matrix::empty(self.domain, self.codomain);
for col in 0..self.domain {
@ -986,7 +1130,9 @@ impl Mul for Matrix {
fn mul(self, rhs: Self) -> Self::Output {
if self.domain != rhs.codomain {
return Err(self::Error::MatrixMultiply(self.domain, rhs.codomain).into())
return error!("cannot multiply: [Matrix {}x{}] * [Matrix {}x{}]",
self.domain, self.codomain,
rhs.domain, rhs.codomain)
}
let mut res = Self::empty(rhs.domain, self.codomain);
for (i, row) in self.rows().enumerate() {

View file

@ -1,12 +1,11 @@
use std::{rc::Rc, fmt::{Debug, Display}, usize, ops::{Index, IndexMut}, collections::HashMap, cell::RefCell, sync::{atomic::{AtomicUsize, Ordering}, Arc}};
use crate::{value::{Value, self, ValueMap, Matrix}, gc::Gc, chunk::{Function, Instruction, Chunk, InnerFunction}, Result, compiler::NamesTable};
use crate::{value::{Value, self, ValueMap, Matrix}, Result, gc::Gc, chunk::{Function, Instruction, Chunk, InnerFunction}, compiler::NamesTable, native};
#[derive(Debug)]
pub enum Error {
ValueError(value::Error),
NotFunction(Value),
InvArity(usize, usize, Rc<str>),
UndefinedGlobal(Rc<str>),
ExecNative,
}
@ -19,7 +18,6 @@ impl Display for Error {
ValueError(err) => write!(f, "{err}"),
NotFunction(v) => write!(f, "{v:?} is not a function"),
InvArity(wanted, had, name) => write!(f, "function {name} takes {wanted} params but was given {had}"),
UndefinedGlobal(name) => write!(f, "{name} is not defined"),
ExecNative => write!(f, "cannot execute a native function"),
}
}
@ -89,21 +87,64 @@ impl<T> Index<usize> for Stack<T> {
}
}
struct StackFrame {
#[derive(Clone)]
pub struct StackFrame {
#[allow(dead_code)]
name: Rc<str>,
body: Rc<Chunk>,
ip: usize,
bp: usize,
lp: usize,
depth: usize,
}
struct VmError {
err: crate::Error,
frames: Vec<StackFrame>
}
impl From<crate::Error> for VmError {
fn from(err: crate::Error) -> Self {
VmError {
err,
frames: Vec::new()
}
}
}
impl From<self::Error> for VmError {
fn from(err: self::Error) -> Self {
VmError {
err: err.into(),
frames: Vec::new()
}
}
}
type VmResult<T> = std::result::Result<T, VmError>;
impl StackFrame {
fn new(vm: &Vm, name: Rc<str>, body: Rc<Chunk>, arity: usize) -> Self {
fn new(vm: &Vm, name: Rc<str>, body: Rc<Chunk>, arity: usize, depth: usize) -> Self {
Self {
name,
body,
bp: vm.stack.len() - arity,
lp: vm.locals.len(),
ip: 0,
depth
}
}
}
impl Default for StackFrame {
fn default() -> Self {
StackFrame {
name: Rc::from("<root>"),
body: Rc::new(Chunk::default()),
bp: 0,
lp: 0,
ip: 0,
depth: 0
}
}
}
@ -112,10 +153,17 @@ pub enum Interupt {
KeyboardInterupt = 1
}
struct TryScope {
stack_len: usize,
locals_len: usize,
frame_depth: usize,
err_idx: usize,
}
pub struct Vm {
stack: Stack<Value>,
locals: Stack<Value>,
frames: Vec<StackFrame>,
trystack: Vec<TryScope>,
globals: HashMap<u16, Value>,
names: NamesTable,
global_names: NamesTable,
@ -132,6 +180,10 @@ impl Vm {
self.stack.pop()
}
fn top(&mut self) -> Value {
self.stack[self.stack.len() - 1].clone()
}
pub fn names(&self) -> NamesTable {
self.names.clone()
}
@ -148,7 +200,7 @@ impl Vm {
Self {
stack: Stack::new(),
locals: Stack::new(),
frames: Vec::new(),
trystack: Vec::new(),
globals: HashMap::new(),
names: Rc::new(RefCell::new(Vec::new())),
global_names: Rc::new(RefCell::new(Vec::new())),
@ -156,10 +208,14 @@ impl Vm {
}
}
pub fn load_native_fn(&mut self, fun: Rc<Function>) {
pub fn load_global(&mut self, value: Value, name: &str) {
let idx = self.global_names.borrow().len();
self.global_names.borrow_mut().push(fun.name.clone());
self.globals.insert(idx as u16, Value::Function(fun));
self.global_names.borrow_mut().push(name.into());
self.globals.insert(idx as u16, value);
}
pub fn load_global_fn(&mut self, value: Rc<Function>, name: &str) {
self.load_global(Value::Function(value), name)
}
fn init_frame(&mut self, fun: Rc<Function>) -> Result<StackFrame> {
@ -170,7 +226,8 @@ impl Vm {
self,
fun.name.clone(),
compiled_chunk.clone(),
fun.arity
fun.arity,
0
))
}
@ -178,55 +235,46 @@ impl Vm {
self.interupt.load(Ordering::Relaxed) != 0
}
pub fn run(&mut self, fun: Rc<Function>) -> Result<Value> {
let mut frame = self.init_frame(fun)?;
self.interupt.store(0, Ordering::SeqCst);
self.stack = Stack::new();
self.locals = Stack::new();
loop {
use Instruction::*;
let ins = frame.body.code[frame.ip].clone();
frame.ip += 1;
fn exec_ins(&mut self, frame: &mut StackFrame, ins: Instruction) -> VmResult<Option<Value>> {
use Instruction as I;
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) => {
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);
},
StoreGlobal(idx) => {
I::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) => {
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));
},
BinaryOp(op) => {
I::BinaryOp(op) => {
let rhs = self.pop();
let lhs = self.pop();
self.push(Value::binary_op(op, lhs, rhs)?);
},
NewList(items) => {
I::NewList(items) => {
let list = self.stack.split_off(self.stack.len() - items as usize);
self.push(Value::List(list.inner.into()));
},
NewTable(items) => {
I::NewTable(items) => {
let mut table = ValueMap::new();
for _ in 0..items {
let value = self.pop();
@ -235,105 +283,204 @@ impl Vm {
}
self.push(Value::Table(table.into()))
},
NewMatrix(items, domain) => {
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))));
}
Jump(idx) => {
I::Jump(idx) => {
if self.check_interupt() {
return Ok(Value::Nil)
return Ok(Some(Value::Nil))
}
frame.ip = idx as usize;
}
JumpTrue(idx) => {
I::JumpTrue(idx) => {
if self.check_interupt() {
return Ok(Value::Nil)
return Ok(Some(Value::Nil))
}
if !!self.pop() {
frame.ip = idx as usize;
}
},
JumpFalse(idx) => {
I::JumpFalse(idx) => {
if self.check_interupt() {
return Ok(Value::Nil)
return Ok(Some(Value::Nil))
}
if !self.pop() {
frame.ip = idx as usize;
}
},
Call(arity) => {
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())
};
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 && arity > fun.arity {
return Err(Error::InvArity(fun.arity, arity as usize, fun.name.clone()).into())
}
let mut params = self.stack.split_off(self.stack.len() - arity).inner;
if params.len() < fun.arity {
let new_arity = fun.arity - arity;
let val = native!("<partial>", new_arity, false, move |(vm, vm_frame), args| {
let mut params = params.clone();
params.extend(args);
vm.exec_fn(vm_frame, fun.clone(), params).map_err(|e| e.err)
});
self.stack.push(val);
return Ok(None);
}
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()));
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 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;
let res = self.exec_fn(frame, fun, params)?;
self.push(res);
},
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 => {
I::Return => {
if self.check_interupt() {
return Ok(Value::Nil)
return Ok(Some(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;
self.locals.truncate(frame.lp);
return Ok(Some(ret))
},
Field(name_idx) => {
I::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) => {
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)?;
},
Index(count) => {
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);
},
StoreIndex(count) => {
I::StoreIndex(count) => {
let index = self.stack.split_off(self.stack.len() - count as usize);
let mut collection = self.pop();
let value = self.pop();
collection.store_index(&index.inner, value)?;
},
I::IterCreate => {
let val = self.pop().into_iter();
self.push(val?);
},
I::IterNext => {
let Value::Iter(iter) = self.top() else { panic!("bypassed iter check"); };
let val = self.exec_fn(frame, iter, vec![])?;
self.push(val);
},
I::Try(idx) => {
let scope = TryScope {
err_idx: idx as usize,
frame_depth: frame.depth,
stack_len: self.stack.len(),
locals_len: self.locals.len(),
};
self.trystack.push(scope);
},
I::TryEnd => {
self.trystack.pop().unwrap();
},
};
Ok(None)
}
fn stack_trace(&mut self, frames: Vec<StackFrame>) -> String {
let mut trace = String::from("\x1b[33m\x1b[1mStack Trace:\x1b[0m\n");
for frame in frames {
let ip = frame.ip - 1;
let pos = frame.body.pos[ip];
trace.push_str(&format!(" {} at {}:{}\n", &frame.name, pos.row, pos.col));
}
trace
}
fn exec_fn(&mut self, frame: &mut StackFrame, fun: Rc<Function>, params: Vec<Value>) -> VmResult<Value> {
let name = fun.name.clone();
let params_len = params.len();
match &fun.fun {
InnerFunction::Compiled(body) => {
for param in params {
self.stack.push(param);
}
let mut new_frame = StackFrame::new(self, name, body.clone(), params_len, frame.depth + 1);
self.exec(&mut new_frame)
},
InnerFunction::Native(native_fun) => {
Ok(native_fun((self, frame), params)?)
}
}
}
Ok(self.pop())
fn exec(&mut self, frame: &mut StackFrame) -> VmResult<Value> {
loop {
let ins = frame.body.code[frame.ip].clone();
frame.ip += 1;
match self.exec_ins(frame, ins) {
Ok(Some(val)) => return Ok(val),
Ok(None) => {},
Err(err) => {
if let Some(catch) = self.trystack.pop() {
if frame.depth != catch.frame_depth {
self.trystack.push(catch);
return Err(err);
}
self.stack.truncate(catch.stack_len);
self.locals.truncate(catch.locals_len);
frame.ip = catch.err_idx;
self.stack.push(Value::Error(err.err));
} else {
let mut err = err;
err.frames.push(frame.clone());
return Err(err)
}
},
}
}
}
pub fn run_fn(&mut self, frame: &mut StackFrame, fun: Rc<Function>, params: Vec<Value>) -> Result<Value> {
self.exec_fn(frame, fun, params).map_err(|e| e.err)
}
pub fn run(&mut self, fun: Rc<Function>) -> Result<Value> {
let mut frame = self.init_frame(fun)?;
self.interupt.store(0, Ordering::SeqCst);
self.stack = Stack::new();
self.locals = Stack::new();
self.exec(&mut frame).map_err(|e| {
let trace = self.stack_trace(e.frames);
(e.err, trace).into()
})
}
}