changes
This commit is contained in:
parent
ad8700371f
commit
158bcae00d
21 changed files with 3452 additions and 863 deletions
12
Cargo.lock
generated
12
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -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
312
matrix-bin/src/helper.rs
Normal 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);
|
||||
}
|
||||
}
|
|
@ -1,9 +1,10 @@
|
|||
use std::{path::PathBuf, io::{self, Read, IsTerminal}, fs};
|
||||
use clap::Parser as ArgParser;
|
||||
use std::{path::PathBuf, io::{self, Read, IsTerminal}, fs, cell::RefCell, rc::Rc};
|
||||
use clap::{Parser as ArgParser, ColorChoice};
|
||||
use matrix::{compiler::{Compiler, CompilerBuilder}, vm::Vm, parse::{Parser, ParserBuilder}, value::Value};
|
||||
use repl::Repl;
|
||||
|
||||
mod repl;
|
||||
mod helper;
|
||||
|
||||
#[derive(Debug, ArgParser)]
|
||||
#[command(version, long_about = None)]
|
||||
|
@ -19,6 +20,10 @@ pub struct Args {
|
|||
#[arg(short, long)]
|
||||
debug: bool,
|
||||
|
||||
/// Choses color
|
||||
#[arg(short, long)]
|
||||
color: Option<ColorChoice>,
|
||||
|
||||
/// Disables optimizations
|
||||
#[arg(long)]
|
||||
disable_optimizations: bool,
|
||||
|
@ -27,8 +32,11 @@ pub struct Args {
|
|||
pub struct State<'a> {
|
||||
parser: Parser,
|
||||
compiler: Compiler<'a>,
|
||||
vm: Vm,
|
||||
repl: bool
|
||||
vm: Rc<RefCell<Vm>>,
|
||||
repl: bool,
|
||||
color: bool,
|
||||
#[allow(unused)]
|
||||
debug: bool,
|
||||
}
|
||||
|
||||
impl<'a> State<'a> {
|
||||
|
@ -60,20 +68,32 @@ impl<'a> State<'a> {
|
|||
|
||||
matrix_stdlib::load(&mut vm);
|
||||
|
||||
(Self { parser, vm, compiler, repl }, file)
|
||||
let color = match args.color {
|
||||
Some(ColorChoice::Auto) | None => {
|
||||
io::stdout().is_terminal()
|
||||
},
|
||||
Some(ColorChoice::Always) => true,
|
||||
Some(ColorChoice::Never) => false,
|
||||
};
|
||||
|
||||
(Self { parser, vm: Rc::new(RefCell::new(vm)), compiler, repl, debug: args.debug, color }, file)
|
||||
}
|
||||
|
||||
pub fn execute(&mut self, code: String) -> matrix::Result<Value> {
|
||||
let ast = self.parser.parse(code)?;
|
||||
let fun = self.compiler.compile(&ast)?;
|
||||
let val = self.vm.run(fun)?;
|
||||
let val = self.vm.try_borrow_mut().unwrap().run(fun)?;
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn error(err: matrix::Error) {
|
||||
println!("\x1b[31m\x1b[1mError:\x1b[0m {err}");
|
||||
pub fn error(err: matrix::Error, state: &State) {
|
||||
if state.color {
|
||||
println!("\x1b[31m\x1b[1mError:\x1b[0m {err}");
|
||||
} else {
|
||||
println!("Error: {err}");
|
||||
}
|
||||
}
|
||||
|
||||
fn read_stdin() -> String {
|
||||
|
@ -93,7 +113,7 @@ fn main() {
|
|||
|
||||
if let Some(file) = file {
|
||||
if let Err(err) = state.execute(file) {
|
||||
error(err);
|
||||
error(err, &state);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use std::{io::Write, sync::atomic::Ordering};
|
||||
|
||||
use matrix::{value::Value, vm::Interupt};
|
||||
use rustyline::Config;
|
||||
use rustyline::{Config, EditMode, ColorMode, Editor, CompletionType};
|
||||
|
||||
use crate::State;
|
||||
use crate::{State, helper::MatrixHelper};
|
||||
|
||||
pub struct Repl<'a> {
|
||||
state: State<'a>
|
||||
|
@ -17,24 +17,39 @@ impl<'a> Repl<'a> {
|
|||
|
||||
pub fn run(&mut self) {
|
||||
|
||||
let interupt = self.state.vm.interupt();
|
||||
let interupt = self.state.vm.borrow().interupt();
|
||||
ctrlc::set_handler(move || {
|
||||
interupt.store(Interupt::KeyboardInterupt as usize, Ordering::SeqCst);
|
||||
}).unwrap();
|
||||
|
||||
let config = Config::builder()
|
||||
.check_cursor_position(true)
|
||||
.completion_type(CompletionType::List)
|
||||
.edit_mode(EditMode::Emacs)
|
||||
.color_mode(if self.state.color { ColorMode::Enabled } else { ColorMode::Disabled })
|
||||
.build();
|
||||
let mut rl = rustyline::DefaultEditor::with_config(config).unwrap();
|
||||
|
||||
let helper = MatrixHelper::new(self.state.vm.clone());
|
||||
|
||||
let mut rl = Editor::with_config(config).unwrap();
|
||||
rl.set_helper(Some(helper));
|
||||
|
||||
loop {
|
||||
let Ok(line) = rl.readline(">> ") else {
|
||||
break;
|
||||
};
|
||||
if let Err(_) = rl.add_history_entry(&line) {
|
||||
break;
|
||||
};
|
||||
match self.state.execute(line) {
|
||||
Err(err) => crate::error(err),
|
||||
Err(err) => crate::error(err, &self.state),
|
||||
Ok(val) => {
|
||||
if val != Value::Nil {
|
||||
println!("{val}");
|
||||
if self.state.color {
|
||||
println!("{val:#}");
|
||||
} else {
|
||||
println!("{val}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
181
matrix-stdlib/src/core.rs
Normal 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");
|
||||
}
|
|
@ -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());
|
||||
if i != 0 {
|
||||
print!(" ");
|
||||
}
|
||||
use matrix::{value::Value, self, vm::Vm, Result, unpack_varargs, iter, unpack_args};
|
||||
use matrix_macros::native_func;
|
||||
use crate::{error, VmArgs};
|
||||
|
||||
#[native_func(0..)]
|
||||
fn print(_: VmArgs, args: Vec<Value>) -> Result<Value> {
|
||||
let ([], varags) = unpack_varargs!(args);
|
||||
for (i, value) in varags.into_iter().enumerate() {
|
||||
if i != 0 {
|
||||
print!(" ");
|
||||
}
|
||||
print!("{value}");
|
||||
}
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
|
||||
#[native_func(1, true)]
|
||||
fn println(_vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [values] = args.try_into().unwrap();
|
||||
if let Value::List(list) = values {
|
||||
for (i, value) in list.iter().enumerate() {
|
||||
print!("{}", value.boring_print());
|
||||
if i != 0 {
|
||||
print!(" ");
|
||||
}
|
||||
#[native_func(0..)]
|
||||
fn println(_: VmArgs, args: Vec<Value>) -> Result<Value> {
|
||||
let ([], varags) = unpack_varargs!(args);
|
||||
for (i, value) in varags.into_iter().enumerate() {
|
||||
if i != 0 {
|
||||
print!(" ");
|
||||
}
|
||||
print!("{value}");
|
||||
}
|
||||
print!("\n");
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
|
||||
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
544
matrix-stdlib/src/iter.rs
Normal 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");
|
||||
}
|
|
@ -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
518
matrix-stdlib/src/math.rs
Normal 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
99
matrix-stdlib/src/sys.rs
Normal 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");
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
use std::{rc::Rc, ops::{Neg, Not}};
|
||||
use crate::{lex::Token, value::{Value, InlineList, InlineMatrix, InlineTable}, Result};
|
||||
use std::{rc::Rc, ops::{Neg, Not}, fmt::Debug};
|
||||
use crate::{lex::{Position, TokenData}, value::{Value, InlineList, InlineMatrix, InlineTable}, Result};
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub enum UnaryOp {
|
||||
|
@ -31,10 +31,15 @@ pub enum BinaryOp {
|
|||
LessEquals,
|
||||
GreaterThan,
|
||||
LessThan,
|
||||
// iter
|
||||
Range,
|
||||
RangeEq
|
||||
}
|
||||
|
||||
pub type AstName = (Rc<str>, Position);
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Expr {
|
||||
pub enum ExprData {
|
||||
NoOp,
|
||||
|
||||
Literal(Value),
|
||||
|
@ -45,7 +50,7 @@ pub enum Expr {
|
|||
|
||||
Index(Box<Expr>, Vec<Expr>),
|
||||
FnCall(Box<Expr>, Vec<Expr>),
|
||||
FieldAccess(Box<Expr>, Rc<str>),
|
||||
FieldAccess(Box<Expr>, AstName),
|
||||
|
||||
List(InlineList),
|
||||
Matrix(InlineMatrix),
|
||||
|
@ -57,22 +62,49 @@ pub enum Expr {
|
|||
Assign(Box<Expr>, Box<Expr>),
|
||||
|
||||
If(Box<Expr>, Box<Expr>, Option<Box<Expr>>),
|
||||
Function(Rc<str>, Vec<Rc<str>>, Box<Expr>, bool),
|
||||
Lambda(Vec<Rc<str>>, Box<Expr>, bool),
|
||||
Function(AstName, Vec<AstName>, Box<Expr>, bool),
|
||||
Lambda(Vec<AstName>, Box<Expr>, bool),
|
||||
|
||||
Loop(Box<Expr>),
|
||||
While(Box<Expr>, Box<Expr>),
|
||||
DoWhile(Box<Expr>, Box<Expr>),
|
||||
For(AstName, Box<Expr>, Box<Expr>),
|
||||
|
||||
Block(Vec<Expr>),
|
||||
|
||||
Let(Rc<str>, Box<Expr>),
|
||||
Try(Box<Expr>, AstName, Box<Expr>),
|
||||
|
||||
Let(AstName, Box<Expr>),
|
||||
|
||||
Pipeline(Box<Expr>, Box<Expr>),
|
||||
|
||||
Continue,
|
||||
Break,
|
||||
Return(Box<Expr>),
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct Expr {
|
||||
pub data: ExprData,
|
||||
pub pos: Position
|
||||
}
|
||||
|
||||
impl Debug for Expr {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if f.alternate() {
|
||||
write!(f, "{:#?}", self.data)
|
||||
} else {
|
||||
write!(f, "{:?}", self.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(ExprData, Position)> for Expr {
|
||||
fn from(value: (ExprData, Position)) -> Self {
|
||||
Self { data: value.0, pos: value.1 }
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for BinaryOp {
|
||||
type Output = Option<Self>;
|
||||
fn neg(self) -> Self::Output {
|
||||
|
@ -103,73 +135,94 @@ impl Not for BinaryOp {
|
|||
|
||||
impl Neg for Expr {
|
||||
type Output = std::result::Result<Self, Self>;
|
||||
fn neg(self) -> Self::Output {
|
||||
use Expr::*;
|
||||
Ok(match self {
|
||||
Literal(v) => Literal(-v),
|
||||
BinaryOp(lhs, rhs, op) => {
|
||||
fn neg(mut self) -> Self::Output {
|
||||
use ExprData as E;
|
||||
let mut pos = self.pos;
|
||||
let data = match self.data {
|
||||
E::Literal(v) => E::Literal(-v),
|
||||
E::BinaryOp(lhs, rhs, op) => {
|
||||
let Some(op_n) = -op else {
|
||||
return Err(BinaryOp(lhs, rhs, op));
|
||||
return Err((E::BinaryOp(lhs, rhs, op), pos).into());
|
||||
};
|
||||
if let Ok(lhs) = -*lhs.clone() {
|
||||
BinaryOp(Box::new(lhs), rhs, op_n)
|
||||
pos = lhs.pos;
|
||||
E::BinaryOp(Box::new(lhs), rhs, op_n)
|
||||
} else if let Ok(rhs) = -*rhs.clone() {
|
||||
BinaryOp(lhs, Box::new(rhs), op_n)
|
||||
pos = rhs.pos;
|
||||
E::BinaryOp(lhs, Box::new(rhs), op_n)
|
||||
} else {
|
||||
return Err(BinaryOp(lhs, rhs, op))
|
||||
return Err((E::BinaryOp(lhs, rhs, op), pos).into());
|
||||
}
|
||||
},
|
||||
UnaryOp(expr, op) => {
|
||||
E::UnaryOp(expr, op) => {
|
||||
match op {
|
||||
self::UnaryOp::Negate => *expr,
|
||||
_ => return Err(UnaryOp(expr, op))
|
||||
UnaryOp::Negate => {
|
||||
pos = expr.pos;
|
||||
expr.data
|
||||
},
|
||||
_ => return Err((E::UnaryOp(expr, op), pos).into())
|
||||
}
|
||||
}
|
||||
_ => return Err(self)
|
||||
})
|
||||
data => return Err((data, pos).into())
|
||||
};
|
||||
self.data = data;
|
||||
self.pos = pos;
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Not for Expr {
|
||||
type Output = std::result::Result<Self, Self>;
|
||||
fn not(self) -> Self::Output {
|
||||
use Expr::*;
|
||||
Ok(match self {
|
||||
Literal(v) => Literal(Value::Bool(!v)),
|
||||
UnaryOp(expr, op) => {
|
||||
fn not(mut self) -> Self::Output {
|
||||
use ExprData as E;
|
||||
let mut pos = self.pos;
|
||||
let data = match self.data {
|
||||
E::Literal(v) => E::Literal(Value::Bool(!v)),
|
||||
E::UnaryOp(expr, op) => {
|
||||
match op {
|
||||
self::UnaryOp::Not => *expr,
|
||||
_ => return Err(UnaryOp(expr, op))
|
||||
self::UnaryOp::Not => {
|
||||
pos = expr.pos;
|
||||
expr.data
|
||||
},
|
||||
_ => return Err((E::UnaryOp(expr, op), pos).into())
|
||||
}
|
||||
}
|
||||
_ => return Err(self)
|
||||
})
|
||||
data => return Err((data, pos).into())
|
||||
};
|
||||
self.data = data;
|
||||
self.pos = pos;
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn optimize(expr: Expr) -> Result<Expr> {
|
||||
use Expr as E;
|
||||
Ok(match expr {
|
||||
pub fn optimize(mut expr: Expr) -> Result<Expr> {
|
||||
use ExprData as E;
|
||||
let mut pos = expr.pos;
|
||||
let data: ExprData = match expr.data {
|
||||
E::UnaryOp(expr, op) => {
|
||||
let expr = optimize(*expr)?;
|
||||
match match op {
|
||||
UnaryOp::Negate => -expr,
|
||||
UnaryOp::Not => !expr,
|
||||
} {
|
||||
Ok(expr) => expr,
|
||||
Ok(expr) => {
|
||||
pos = expr.pos;
|
||||
expr.data
|
||||
},
|
||||
Err(expr) => E::UnaryOp(Box::new(expr), op)
|
||||
}
|
||||
},
|
||||
E::BinaryOp(lhs, rhs, op) => {
|
||||
let lhs = optimize(*lhs)?;
|
||||
let rhs = optimize(*rhs)?;
|
||||
if let (E::Literal(l), E::Literal(r)) = (lhs.clone(), rhs.clone()) {
|
||||
if let (E::Literal(l), E::Literal(r)) = (lhs.clone().data, rhs.clone().data) {
|
||||
match Value::binary_op(op, l, r) {
|
||||
Err(err) => return Err(err),
|
||||
Ok(value) => return Ok(Expr::Literal(value)),
|
||||
Ok(value) => E::Literal(value),
|
||||
}
|
||||
} else {
|
||||
E::BinaryOp(Box::new(lhs), Box::new(rhs), op)
|
||||
}
|
||||
E::BinaryOp(Box::new(lhs), Box::new(rhs), op)
|
||||
},
|
||||
E::FnCall(ident, values) => {
|
||||
E::FnCall(ident, values
|
||||
|
@ -200,10 +253,16 @@ pub fn optimize(expr: Expr) -> Result<Expr> {
|
|||
E::And(lhs, rhs) => {
|
||||
let lhs = optimize(*lhs)?;
|
||||
let rhs = optimize(*rhs)?;
|
||||
if let (E::Literal(l), r) = (lhs.clone(), rhs.clone()) {
|
||||
if let (E::Literal(l), r) = (lhs.clone().data, rhs.clone().data) {
|
||||
match !!l.clone() {
|
||||
true => r,
|
||||
false => E::Literal(l),
|
||||
true => {
|
||||
pos = rhs.pos;
|
||||
r
|
||||
},
|
||||
false => {
|
||||
pos = lhs.pos;
|
||||
E::Literal(l)
|
||||
},
|
||||
}
|
||||
} else {
|
||||
E::And(Box::new(lhs), Box::new(rhs))
|
||||
|
@ -212,10 +271,16 @@ pub fn optimize(expr: Expr) -> Result<Expr> {
|
|||
E::Or(lhs, rhs) => {
|
||||
let lhs = optimize(*lhs)?;
|
||||
let rhs = optimize(*rhs)?;
|
||||
if let (E::Literal(l), r) = (lhs.clone(), rhs.clone()) {
|
||||
if let (E::Literal(l), r) = (lhs.clone().data, rhs.clone().data) {
|
||||
match !l.clone() {
|
||||
true => r,
|
||||
false => E::Literal(l),
|
||||
true => {
|
||||
pos = rhs.pos;
|
||||
r
|
||||
},
|
||||
false => {
|
||||
pos = lhs.pos;
|
||||
E::Literal(l)
|
||||
},
|
||||
}
|
||||
} else {
|
||||
E::And(Box::new(lhs), Box::new(rhs))
|
||||
|
@ -230,10 +295,10 @@ pub fn optimize(expr: Expr) -> Result<Expr> {
|
|||
.into_iter()
|
||||
.enumerate()
|
||||
.filter(|(i, e)| {
|
||||
if let E::Literal(_) = e {
|
||||
if let E::Literal(_) = e.data {
|
||||
return i + 1 == len
|
||||
}
|
||||
E::NoOp != *e
|
||||
E::NoOp != e.data
|
||||
})
|
||||
.map(|e| e.1)
|
||||
.collect();
|
||||
|
@ -247,39 +312,60 @@ pub fn optimize(expr: Expr) -> Result<Expr> {
|
|||
let cond = optimize(*cond)?;
|
||||
let block = optimize(*block)?;
|
||||
let else_block = else_block.map(|e| optimize(*e)).transpose()?;
|
||||
if let E::Literal(lit) = cond {
|
||||
if let E::Literal(lit) = cond.data {
|
||||
if !!lit {
|
||||
return Ok(block)
|
||||
pos = block.pos;
|
||||
block.data
|
||||
} else if let Some(else_block) = else_block {
|
||||
pos = else_block.pos;
|
||||
else_block.data
|
||||
} else {
|
||||
E::NoOp
|
||||
}
|
||||
return Ok(else_block.unwrap_or(E::NoOp))
|
||||
} else {
|
||||
E::If(Box::new(cond), Box::new(block), else_block.map(|e| Box::new(e)))
|
||||
}
|
||||
E::If(Box::new(cond), Box::new(block), else_block.map(|e| Box::new(e)))
|
||||
},
|
||||
E::While(cond, block) => {
|
||||
let cond = optimize(*cond)?;
|
||||
let block = optimize(*block)?;
|
||||
if let E::Literal(lit) = cond {
|
||||
if let E::Literal(lit) = cond.data {
|
||||
if !!lit {
|
||||
return Ok(E::Loop(Box::new(block)))
|
||||
E::Loop(Box::new(block))
|
||||
} else {
|
||||
E::NoOp
|
||||
}
|
||||
return Ok(E::NoOp)
|
||||
} else {
|
||||
E::While(Box::new(cond), Box::new(block))
|
||||
}
|
||||
E::While(Box::new(cond), Box::new(block))
|
||||
},
|
||||
E::For(name, cond, block) => {
|
||||
let cond = optimize(*cond)?;
|
||||
let block = optimize(*block)?;
|
||||
E::For(name, Box::new(cond), Box::new(block))
|
||||
}
|
||||
E::DoWhile(block, cond) => {
|
||||
let cond = optimize(*cond)?;
|
||||
let block = optimize(*block)?;
|
||||
if let E::Literal(lit) = cond.clone() {
|
||||
if !!lit {
|
||||
return Ok(E::Loop(Box::new(block)))
|
||||
if let E::Literal(lit) = &cond.data {
|
||||
if !!lit.clone() {
|
||||
E::Loop(Box::new(block))
|
||||
} else {
|
||||
E::DoWhile(Box::new(block), Box::new(cond))
|
||||
}
|
||||
} else {
|
||||
E::DoWhile(Box::new(block), Box::new(cond))
|
||||
}
|
||||
E::DoWhile(Box::new(block), Box::new(cond))
|
||||
}
|
||||
E::Loop(block) => {
|
||||
let block = Box::new(optimize(*block)?);
|
||||
E::Loop(block)
|
||||
},
|
||||
E::Try(expr, err, catch) => {
|
||||
let expr = Box::new(optimize(*expr)?);
|
||||
let catch = Box::new(optimize(*catch)?);
|
||||
E::Try(expr, err, catch)
|
||||
},
|
||||
E::Function(ident, params, stmt, varadic) => {
|
||||
let stmt = Box::new(optimize(*stmt)?);
|
||||
E::Function(ident, params, stmt, varadic)
|
||||
|
@ -297,44 +383,53 @@ pub fn optimize(expr: Expr) -> Result<Expr> {
|
|||
E::Assign(lhs, rhs)
|
||||
},
|
||||
E::Return(expr) => {
|
||||
E::Return(Box::new(optimize(*expr)?))
|
||||
let expr = Box::new(optimize(*expr)?);
|
||||
E::Return(expr)
|
||||
},
|
||||
_ => expr
|
||||
})
|
||||
E::Pipeline(lhs, rhs) => {
|
||||
let lhs = Box::new(optimize(*lhs)?);
|
||||
let rhs = Box::new(optimize(*rhs)?);
|
||||
E::Pipeline(lhs, rhs)
|
||||
}
|
||||
data => data
|
||||
};
|
||||
expr.data = data;
|
||||
expr.pos = pos;
|
||||
Ok(expr)
|
||||
}
|
||||
|
||||
impl From<Token> for UnaryOp {
|
||||
fn from(value: Token) -> Self {
|
||||
use Token::*;
|
||||
impl From<TokenData> for UnaryOp {
|
||||
fn from(value: TokenData) -> Self {
|
||||
use TokenData as T;
|
||||
match value {
|
||||
Subtract => Self::Negate,
|
||||
Not => Self::Not,
|
||||
T::Subtract => Self::Negate,
|
||||
T::Not => Self::Not,
|
||||
_ => panic!("aaaaa")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Token> for BinaryOp {
|
||||
fn from(value: Token) -> Self {
|
||||
use Token::*;
|
||||
impl From<TokenData> for BinaryOp {
|
||||
fn from(value: TokenData) -> Self {
|
||||
use TokenData as T;
|
||||
match value {
|
||||
Equal => Self::Equals,
|
||||
NotEqual => Self::NotEquals,
|
||||
GreaterEqual => Self::GreaterEquals,
|
||||
LessEqual => Self::LessEquals,
|
||||
GreaterThan => Self::GreaterThan,
|
||||
LessThan => Self::LessThan,
|
||||
BitwiseShiftLeft => Self::BitwiseShiftLeft,
|
||||
BitwiseShiftRight => Self::BitwiseShiftRight,
|
||||
BitwiseAnd => Self::BitwiseAnd,
|
||||
BitwiseOr => Self::BitwiseOr,
|
||||
BitwiseXor => Self::BitwiseXor,
|
||||
Add => Self::Add,
|
||||
Subtract => Self::Subtract,
|
||||
Multiply => Self::Multiply,
|
||||
Divide => Self::Divide,
|
||||
Modulo => Self::Modulo,
|
||||
Power => Self::Power,
|
||||
T::Equal => Self::Equals,
|
||||
T::NotEqual => Self::NotEquals,
|
||||
T::GreaterEqual => Self::GreaterEquals,
|
||||
T::LessEqual => Self::LessEquals,
|
||||
T::GreaterThan => Self::GreaterThan,
|
||||
T::LessThan => Self::LessThan,
|
||||
T::BitwiseShiftLeft => Self::BitwiseShiftLeft,
|
||||
T::BitwiseShiftRight => Self::BitwiseShiftRight,
|
||||
T::BitwiseAnd => Self::BitwiseAnd,
|
||||
T::BitwiseOr => Self::BitwiseOr,
|
||||
T::BitwiseXor => Self::BitwiseXor,
|
||||
T::Add => Self::Add,
|
||||
T::Subtract => Self::Subtract,
|
||||
T::Multiply => Self::Multiply,
|
||||
T::Divide => Self::Divide,
|
||||
T::Modulo => Self::Modulo,
|
||||
T::Power => Self::Power,
|
||||
_ => panic!("aaaaa")
|
||||
}
|
||||
}
|
||||
|
@ -342,11 +437,11 @@ impl From<Token> for BinaryOp {
|
|||
|
||||
impl Expr {
|
||||
pub fn is_assignable(&self) -> bool {
|
||||
use Expr::*;
|
||||
match self {
|
||||
Ident(_) => true,
|
||||
Index(_, _) => true,
|
||||
FieldAccess(_, _) => true,
|
||||
use ExprData as E;
|
||||
match self.data {
|
||||
E::Ident(_) => true,
|
||||
E::Index(_, _) => true,
|
||||
E::FieldAccess(_, _) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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,25 +419,38 @@ impl Lexer {
|
|||
}
|
||||
|
||||
if self.peek() == '.' {
|
||||
pos = self.pos;
|
||||
idx = self.index;
|
||||
bidx = self.byte_len;
|
||||
buf.push(self.next());
|
||||
}
|
||||
}
|
||||
|
||||
loop {
|
||||
if !self.peek().is_ascii_digit() { break; }
|
||||
buf.push(self.next());
|
||||
}
|
||||
|
||||
if self.peek() == 'e' || self.peek() == 'E' {
|
||||
buf.push(self.next());
|
||||
if self.peek() == '+' || self.peek() == '-' {
|
||||
buf.push(self.next());
|
||||
}
|
||||
let last: char = buf.chars().last().unwrap();
|
||||
let is_range = initial != '.' && last == '.' && self.peek() == '.';
|
||||
|
||||
if is_range {
|
||||
self.pos = pos;
|
||||
self.index = idx;
|
||||
self.byte_len = bidx;
|
||||
buf.pop();
|
||||
} else {
|
||||
loop {
|
||||
if !self.peek().is_ascii_digit() { break; }
|
||||
buf.push(self.next());
|
||||
}
|
||||
|
||||
if self.peek() == 'e' || self.peek() == 'E' {
|
||||
buf.push(self.next());
|
||||
if self.peek() == '+' || self.peek() == '-' {
|
||||
buf.push(self.next());
|
||||
}
|
||||
|
||||
loop {
|
||||
if !self.peek().is_ascii_digit() { break; }
|
||||
buf.push(self.next());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let complex = self.peek() == 'i';
|
||||
|
@ -394,87 +458,103 @@ impl Lexer {
|
|||
self.next();
|
||||
}
|
||||
|
||||
if self.peek().is_ident() || self.peek() == '.' {
|
||||
return Err(UnexpectedCharacter(self.peek()).into())
|
||||
if self.peek().is_ident() {
|
||||
return Err(E::UnexpectedCharacter(self.peek()).into())
|
||||
}
|
||||
|
||||
if let Ok(int) = buf.parse::<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
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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,132 +269,37 @@ impl Value {
|
|||
val.can_hash()?;
|
||||
}
|
||||
},
|
||||
_ => return Err(Error::CannotHash(self.clone()).into())
|
||||
_ => return error!("cannot hash {self:?}")
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn boring_print(&self) -> String {
|
||||
use Value::*;
|
||||
pub fn is_zero(&self) -> bool {
|
||||
use Value as V;
|
||||
match self {
|
||||
Nil => format!("nil"),
|
||||
Bool(b) => format!("{b}"),
|
||||
Int(i) => format!("{i}"),
|
||||
Float(l) => format!("{l}"),
|
||||
Ratio(r) => format!("{r}"),
|
||||
Complex(c) => format!("{c}"),
|
||||
Regex(r) => format!("/{r}/"),
|
||||
String(s) => format!("{s}"),
|
||||
List(l) => {
|
||||
let mut str = "[".to_string();
|
||||
for (i, el) in l.iter().enumerate() {
|
||||
if i != 0 {
|
||||
str.push_str(" ");
|
||||
}
|
||||
str.push_str(&el.boring_print());
|
||||
}
|
||||
str.push_str("]");
|
||||
str
|
||||
},
|
||||
Matrix(m) => {
|
||||
let mut str = "\n".to_string();
|
||||
for row in m.rows() {
|
||||
str.push_str(" ");
|
||||
for (i, v) in row.into_iter().enumerate() {
|
||||
if i != 0 {
|
||||
str.push(' ');
|
||||
}
|
||||
str.push_str(&v.boring_print());
|
||||
}
|
||||
str.push('\n');
|
||||
}
|
||||
str
|
||||
},
|
||||
Table(t) => {
|
||||
let mut str = "{".to_string();
|
||||
for (i, (key, val)) in t.0.iter().enumerate() {
|
||||
if i != 0 {
|
||||
str.push_str(", ");
|
||||
}
|
||||
str.push_str(&key.boring_print());
|
||||
str.push_str(" = ");
|
||||
str.push_str(&val.boring_print());
|
||||
}
|
||||
str.push_str("}");
|
||||
str
|
||||
},
|
||||
Function(fun) => {
|
||||
format!("{fun:?}")
|
||||
}
|
||||
V::Int(i) => *i == 0,
|
||||
V::Float(f) => *f == 0.0 || *f == -0.0,
|
||||
V::Ratio(r) => *r.numer() == 0,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pretty_print(&self) -> String {
|
||||
use Value::*;
|
||||
match self {
|
||||
Nil => format!("\x1b[34m{}\x1b[0m", self.boring_print()),
|
||||
Bool(_) => format!("\x1b[33m{}\x1b[0m", self.boring_print()),
|
||||
Int(_) => format!("\x1b[33m{}\x1b[0m", self.boring_print()),
|
||||
Float(_) => format!("\x1b[33m{}\x1b[0m", self.boring_print()),
|
||||
Ratio(_) => format!("\x1b[33m{}\x1b[0m", self.boring_print()),
|
||||
Complex(_) => format!("\x1b[33m{}\x1b[0m", self.boring_print()),
|
||||
Regex(_) => format!("\x1b[31m{}\x1b[0m", self.boring_print()),
|
||||
String(_) => format!("\x1b[32m'{}'\x1b[0m", self.boring_print()),
|
||||
List(l) => {
|
||||
let mut str = "[".to_string();
|
||||
for (i, el) in l.iter().enumerate() {
|
||||
if i != 0 {
|
||||
str.push_str(" ");
|
||||
}
|
||||
str.push_str(&el.pretty_print());
|
||||
}
|
||||
str.push_str("]");
|
||||
str
|
||||
},
|
||||
Matrix(m) => {
|
||||
let mut str = "\n".to_string();
|
||||
for row in m.rows() {
|
||||
str.push_str(" ");
|
||||
for (i, v) in row.into_iter().enumerate() {
|
||||
if i != 0 {
|
||||
str.push(' ');
|
||||
}
|
||||
str.push_str(&v.pretty_print());
|
||||
}
|
||||
str.push('\n');
|
||||
}
|
||||
str
|
||||
},
|
||||
Table(t) => {
|
||||
let mut str = "{".to_string();
|
||||
for (i, (key, val)) in t.0.iter().enumerate() {
|
||||
if i != 0 {
|
||||
str.push_str(", ");
|
||||
}
|
||||
str.push_str(&key.pretty_print());
|
||||
str.push_str(" = ");
|
||||
str.push_str(&val.pretty_print());
|
||||
}
|
||||
str.push_str("}");
|
||||
str
|
||||
},
|
||||
Function(_) => {
|
||||
format!("\x1b[36m{}\x1b[0m", self.boring_print())
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Value {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.pretty_print())
|
||||
}
|
||||
}
|
||||
|
||||
fn ratio_to_f64(r: Rational64) -> f64 {
|
||||
*r.numer() as f64 / *r.denom() as f64
|
||||
}
|
||||
|
||||
impl Value {
|
||||
pub fn promote_trig(self) -> Self {
|
||||
use Value as V;
|
||||
match self {
|
||||
V::Int(i) => V::Float(i as f64),
|
||||
V::Ratio(r) => V::Float(ratio_to_f64(r)),
|
||||
a => a
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn promote(a: Value, b: Value) -> (Value, Value) {
|
||||
use Value as V;
|
||||
match (&a, &b) {
|
||||
|
@ -293,9 +315,12 @@ fn promote(a: Value, b: Value) -> (Value, Value) {
|
|||
(V::Float(..), V::Ratio(y)) => (a, V::Float(ratio_to_f64(*y))),
|
||||
(V::Complex(..), V::Ratio(y)) => (a, V::Complex(ratio_to_f64(*y).into())),
|
||||
(V::Complex(..), V::Float(y)) => (a, V::Complex((*y).into())),
|
||||
(V::List(l1), V::List(l2)) => (V::Matrix(Matrix::from_list(l1.to_vec()).into()), V::Matrix(Matrix::from_list(l2.to_vec()).into())),
|
||||
(_, V::List(l)) => (a, V::Matrix(Matrix::from_list(l.to_vec()).into())),
|
||||
(V::List(l), _) => (V::Matrix(Matrix::from_list(l.to_vec()).into()), b),
|
||||
(V::List(l1), V::List(l2)) if l1.len() > 0 && l2.len() > 0
|
||||
=> (V::Matrix(Matrix::from_list(l1.to_vec()).into()), V::Matrix(Matrix::from_list(l2.to_vec()).into())),
|
||||
(_, V::List(l)) if l.len() > 0
|
||||
=> (a, V::Matrix(Matrix::from_list(l.to_vec()).into())),
|
||||
(V::List(l), _) if l.len() > 0
|
||||
=> (V::Matrix(Matrix::from_list(l.to_vec()).into()), b),
|
||||
_ => (a, b),
|
||||
}
|
||||
}
|
||||
|
@ -310,17 +335,19 @@ impl Add for Value {
|
|||
(Ratio(x), Ratio(y)) => Ok(Ratio(x + y)),
|
||||
(Complex(x), Complex(y)) => Ok(Complex(x + y)),
|
||||
(Matrix(x), Matrix(y)) => Ok(Matrix((x + y)?.into())),
|
||||
(String(str), value) => Ok(String(Rc::new(
|
||||
format!("{str}{}", value.boring_print())
|
||||
(Matrix(x), r) => Ok(Matrix(x.increment(r)?.into())),
|
||||
(l, Matrix(y)) => Ok(Matrix(y.increment(l)?.into())),
|
||||
(String(str), value) => Ok(String(Rc::from(
|
||||
format!("{str}{value}")
|
||||
))),
|
||||
(value, String(str)) => Ok(String(Rc::new(
|
||||
format!("{}{str}", value.boring_print())
|
||||
(value, String(str)) => Ok(String(Rc::from(
|
||||
format!("{value}{str}")
|
||||
))),
|
||||
(List(mut l1), List(l2)) => {
|
||||
l1.extend_from_slice(&l2);
|
||||
Ok(List(l1))
|
||||
},
|
||||
(l, r) => Err(Error::Add(l, r).into())
|
||||
(l, r) => error!("cannot add: {l:?} + {r:?}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -335,7 +362,8 @@ impl Sub for Value {
|
|||
(Ratio(x), Ratio(y)) => Ok(Ratio(x - y)),
|
||||
(Complex(x), Complex(y)) => Ok(Complex(x - y)),
|
||||
(Matrix(x), Matrix(y)) => Ok(Matrix((x - y)?.into())),
|
||||
(l, r) => Err(Error::Subtract(l, r).into())
|
||||
(Matrix(x), r) => Ok(Matrix(x.decrement(r)?.into())),
|
||||
(l, r) => error!("cannot subtract: {l:?} - {r:?}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -352,7 +380,7 @@ impl Mul for Value {
|
|||
(Matrix(x), Matrix(y)) => Ok(Matrix((x * y)?.into())),
|
||||
(Matrix(x), r) => Ok(Matrix(x.scale(r)?.into())),
|
||||
(l, Matrix(y)) => Ok(Matrix(y.scale(l)?.into())),
|
||||
(l, r) => Err(Error::Multiply(l, r).into())
|
||||
(l, r) => error!("cannot multiply: {l:?} * {r:?}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -362,13 +390,13 @@ impl Div for Value {
|
|||
fn div(self, rhs: Value) -> Self::Output {
|
||||
use Value::*;
|
||||
match promote(self, rhs) {
|
||||
(Int(_), Int(0)) => Err(Error::DivideByZero.into()),
|
||||
(Int(_), Int(0)) => error!("cannot divide by zero"),
|
||||
(Int(x), Int(y)) => Ok(Ratio(Rational64::new(x, y))),
|
||||
(Float(x), Float(y)) => Ok(Float(x / y)),
|
||||
(Ratio(x), Ratio(y)) => Ok(Ratio(x / y)),
|
||||
(Complex(x), Complex(y)) => Ok(Complex(x / y)),
|
||||
(l, r) => Err(Error::Divide(l, r).into())
|
||||
}
|
||||
(l, r) => error!("cannot divide: {l:?} / {r:?}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -378,7 +406,7 @@ impl BitOr for Value {
|
|||
use Value::*;
|
||||
match promote(self, rhs) {
|
||||
(Int(x), Int(y)) => Ok(Int(x | y)),
|
||||
(l, r) => Err(Error::Bitwise(l, r).into())
|
||||
(l, r) => error!("cannot bitwise or: {l:?} | {r:?}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -389,7 +417,7 @@ impl BitAnd for Value {
|
|||
use Value::*;
|
||||
match promote(self, rhs) {
|
||||
(Int(x), Int(y)) => Ok(Int(x & y)),
|
||||
(l, r) => Err(Error::Bitwise(l, r).into())
|
||||
(l, r) => error!("cannot bitwise and: {l:?} & {r:?}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -400,7 +428,7 @@ impl BitXor for Value {
|
|||
use Value::*;
|
||||
match promote(self, rhs) {
|
||||
(Int(x), Int(y)) => Ok(Int(x ^ y)),
|
||||
(l, r) => Err(Error::Bitwise(l, r).into())
|
||||
(l, r) => error!("cannot bitwise xor: {l:?} ^ {r:?}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -411,7 +439,7 @@ impl Shl for Value {
|
|||
use Value::*;
|
||||
match promote(self, rhs) {
|
||||
(Int(x), Int(y)) => Ok(Int(x << y)),
|
||||
(l, r) => Err(Error::Bitwise(l, r).into())
|
||||
(l, r) => error!("cannot bitwise shift left: {l:?} << {r:?}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -422,14 +450,14 @@ impl Shr for Value {
|
|||
use Value::*;
|
||||
match promote(self, rhs) {
|
||||
(Int(x), Int(y)) => Ok(Int(x >> y)),
|
||||
(l, r) => Err(Error::Bitwise(l, r).into())
|
||||
(l, r) => error!("cannot bitwise shift right: {l:?} >> {r:?}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn ipow(n: i64, d: i64, p: i64) -> Result<(i64, i64)> {
|
||||
Ok(match (n, d, p) {
|
||||
(0, _, 0) => return Err(Error::ZeroExpZero.into()),
|
||||
(0, _, 0) => return error!("cannot exponent: 0 ** 0"),
|
||||
(0, _, _) => (0, 1),
|
||||
(_, _, 0) => (1, 1),
|
||||
(1, 1, _) => (1, 1),
|
||||
|
@ -447,7 +475,7 @@ impl Value {
|
|||
(Float(x), Float(y)) => Ok(Float(x % y)),
|
||||
(Ratio(x), Ratio(y)) => Ok(Ratio(x % y)),
|
||||
(Complex(x), Complex(y)) => Ok(Complex(x % y)),
|
||||
(l, r) => Err(Error::Modulo(r, l).into())
|
||||
(l, r) => error!("cannot modulo: {l:?} % {r:?}")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -461,7 +489,7 @@ impl Value {
|
|||
(Float(x), Float(y)) => Ok(Float(x.powf(y))),
|
||||
(Ratio(x), Ratio(y)) => Ok(Float(ratio_to_f64(x).powf(ratio_to_f64(y)))),
|
||||
(Complex(x), Complex(y)) => Ok(Complex(x.powc(y))),
|
||||
(l, r) => Err(Error::Exponent(l, r).into())
|
||||
(l, r) => error!("cannot exponent: {l:?} ** {r:?}")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -534,7 +562,7 @@ impl PartialOrd for Value {
|
|||
impl Value {
|
||||
pub fn val_cmp(&self, other: &Self) -> Result<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() {
|
||||
|
|
473
matrix/src/vm.rs
473
matrix/src/vm.rs
|
@ -1,12 +1,11 @@
|
|||
use std::{rc::Rc, fmt::{Debug, Display}, usize, ops::{Index, IndexMut}, collections::HashMap, cell::RefCell, sync::{atomic::{AtomicUsize, Ordering}, Arc}};
|
||||
use crate::{value::{Value, self, ValueMap, Matrix}, gc::Gc, chunk::{Function, Instruction, Chunk, InnerFunction}, Result, compiler::NamesTable};
|
||||
use crate::{value::{Value, self, ValueMap, Matrix}, Result, gc::Gc, chunk::{Function, Instruction, Chunk, InnerFunction}, compiler::NamesTable, native};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
ValueError(value::Error),
|
||||
NotFunction(Value),
|
||||
InvArity(usize, usize, Rc<str>),
|
||||
UndefinedGlobal(Rc<str>),
|
||||
ExecNative,
|
||||
}
|
||||
|
||||
|
@ -19,7 +18,6 @@ impl Display for Error {
|
|||
ValueError(err) => write!(f, "{err}"),
|
||||
NotFunction(v) => write!(f, "{v:?} is not a function"),
|
||||
InvArity(wanted, had, name) => write!(f, "function {name} takes {wanted} params but was given {had}"),
|
||||
UndefinedGlobal(name) => write!(f, "{name} is not defined"),
|
||||
ExecNative => write!(f, "cannot execute a native function"),
|
||||
}
|
||||
}
|
||||
|
@ -89,21 +87,64 @@ impl<T> Index<usize> for Stack<T> {
|
|||
}
|
||||
}
|
||||
|
||||
struct StackFrame {
|
||||
#[derive(Clone)]
|
||||
pub struct StackFrame {
|
||||
#[allow(dead_code)]
|
||||
name: Rc<str>,
|
||||
body: Rc<Chunk>,
|
||||
ip: usize,
|
||||
bp: usize,
|
||||
lp: usize,
|
||||
depth: usize,
|
||||
}
|
||||
|
||||
struct VmError {
|
||||
err: crate::Error,
|
||||
frames: Vec<StackFrame>
|
||||
}
|
||||
|
||||
impl From<crate::Error> for VmError {
|
||||
fn from(err: crate::Error) -> Self {
|
||||
VmError {
|
||||
err,
|
||||
frames: Vec::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<self::Error> for VmError {
|
||||
fn from(err: self::Error) -> Self {
|
||||
VmError {
|
||||
err: err.into(),
|
||||
frames: Vec::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type VmResult<T> = std::result::Result<T, VmError>;
|
||||
|
||||
impl StackFrame {
|
||||
fn new(vm: &Vm, name: Rc<str>, body: Rc<Chunk>, arity: usize) -> Self {
|
||||
fn new(vm: &Vm, name: Rc<str>, body: Rc<Chunk>, arity: usize, depth: usize) -> Self {
|
||||
Self {
|
||||
name,
|
||||
body,
|
||||
bp: vm.stack.len() - arity,
|
||||
lp: vm.locals.len(),
|
||||
ip: 0,
|
||||
depth
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for StackFrame {
|
||||
fn default() -> Self {
|
||||
StackFrame {
|
||||
name: Rc::from("<root>"),
|
||||
body: Rc::new(Chunk::default()),
|
||||
bp: 0,
|
||||
lp: 0,
|
||||
ip: 0,
|
||||
depth: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -112,10 +153,17 @@ pub enum Interupt {
|
|||
KeyboardInterupt = 1
|
||||
}
|
||||
|
||||
struct TryScope {
|
||||
stack_len: usize,
|
||||
locals_len: usize,
|
||||
frame_depth: usize,
|
||||
err_idx: usize,
|
||||
}
|
||||
|
||||
pub struct Vm {
|
||||
stack: Stack<Value>,
|
||||
locals: Stack<Value>,
|
||||
frames: Vec<StackFrame>,
|
||||
trystack: Vec<TryScope>,
|
||||
globals: HashMap<u16, Value>,
|
||||
names: NamesTable,
|
||||
global_names: NamesTable,
|
||||
|
@ -132,6 +180,10 @@ impl Vm {
|
|||
self.stack.pop()
|
||||
}
|
||||
|
||||
fn top(&mut self) -> Value {
|
||||
self.stack[self.stack.len() - 1].clone()
|
||||
}
|
||||
|
||||
pub fn names(&self) -> NamesTable {
|
||||
self.names.clone()
|
||||
}
|
||||
|
@ -148,7 +200,7 @@ impl Vm {
|
|||
Self {
|
||||
stack: Stack::new(),
|
||||
locals: Stack::new(),
|
||||
frames: Vec::new(),
|
||||
trystack: Vec::new(),
|
||||
globals: HashMap::new(),
|
||||
names: Rc::new(RefCell::new(Vec::new())),
|
||||
global_names: Rc::new(RefCell::new(Vec::new())),
|
||||
|
@ -156,10 +208,14 @@ impl Vm {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn load_native_fn(&mut self, fun: Rc<Function>) {
|
||||
pub fn load_global(&mut self, value: Value, name: &str) {
|
||||
let idx = self.global_names.borrow().len();
|
||||
self.global_names.borrow_mut().push(fun.name.clone());
|
||||
self.globals.insert(idx as u16, Value::Function(fun));
|
||||
self.global_names.borrow_mut().push(name.into());
|
||||
self.globals.insert(idx as u16, value);
|
||||
}
|
||||
|
||||
pub fn load_global_fn(&mut self, value: Rc<Function>, name: &str) {
|
||||
self.load_global(Value::Function(value), name)
|
||||
}
|
||||
|
||||
fn init_frame(&mut self, fun: Rc<Function>) -> Result<StackFrame> {
|
||||
|
@ -170,7 +226,8 @@ impl Vm {
|
|||
self,
|
||||
fun.name.clone(),
|
||||
compiled_chunk.clone(),
|
||||
fun.arity
|
||||
fun.arity,
|
||||
0
|
||||
))
|
||||
}
|
||||
|
||||
|
@ -178,162 +235,252 @@ impl Vm {
|
|||
self.interupt.load(Ordering::Relaxed) != 0
|
||||
}
|
||||
|
||||
fn exec_ins(&mut self, frame: &mut StackFrame, ins: Instruction) -> VmResult<Option<Value>> {
|
||||
use Instruction as I;
|
||||
match ins {
|
||||
I::NoOp => {},
|
||||
I::CreateLocal => self.locals.push(self.stack.pop()),
|
||||
I::LoadLocal(idx) => {self.stack.push(self.locals[idx as usize].clone());},
|
||||
I::StoreLocal(idx) => self.locals[frame.bp + idx as usize] = self.pop(),
|
||||
I::DiscardLocals(count) => {self.locals.truncate(self.locals.len() - count as usize)},
|
||||
I::LoadGlobal(idx) => {
|
||||
let val = self.globals
|
||||
.get(&idx)
|
||||
.unwrap()
|
||||
.clone();
|
||||
self.stack.push(val);
|
||||
},
|
||||
I::StoreGlobal(idx) => {
|
||||
let val = self.pop();
|
||||
self.globals.insert(idx, val);
|
||||
},
|
||||
I::Const(idx) => self.push(frame.body.constants[idx as usize].clone()),
|
||||
I::Int(i) => self.push(Value::Int(i as i64)),
|
||||
I::True => self.push(Value::Bool(true)),
|
||||
I::False => self.push(Value::Bool(false)),
|
||||
I::Nil => self.push(Value::Nil),
|
||||
I::Dup => self.push(self.stack[self.stack.len() - 1].clone()),
|
||||
I::Discard(count) => {self.stack.truncate(self.stack.len() - count as usize)},
|
||||
I::UnaryOp(op) => {
|
||||
let val = self.pop();
|
||||
self.push(Value::unary_op(op, val));
|
||||
},
|
||||
I::BinaryOp(op) => {
|
||||
let rhs = self.pop();
|
||||
let lhs = self.pop();
|
||||
self.push(Value::binary_op(op, lhs, rhs)?);
|
||||
},
|
||||
I::NewList(items) => {
|
||||
let list = self.stack.split_off(self.stack.len() - items as usize);
|
||||
self.push(Value::List(list.inner.into()));
|
||||
},
|
||||
I::NewTable(items) => {
|
||||
let mut table = ValueMap::new();
|
||||
for _ in 0..items {
|
||||
let value = self.pop();
|
||||
let key = self.pop();
|
||||
table.insert(key, value)?;
|
||||
}
|
||||
self.push(Value::Table(table.into()))
|
||||
},
|
||||
I::NewMatrix(items, domain) => {
|
||||
let values = self.stack.split_off(self.stack.len() - items as usize).inner;
|
||||
let domain = domain as usize;
|
||||
let codomain = values.len() / domain;
|
||||
self.push(Value::Matrix(Gc::new(Matrix::new(domain, codomain, values))));
|
||||
}
|
||||
I::Jump(idx) => {
|
||||
if self.check_interupt() {
|
||||
return Ok(Some(Value::Nil))
|
||||
}
|
||||
frame.ip = idx as usize;
|
||||
}
|
||||
I::JumpTrue(idx) => {
|
||||
if self.check_interupt() {
|
||||
return Ok(Some(Value::Nil))
|
||||
}
|
||||
if !!self.pop() {
|
||||
frame.ip = idx as usize;
|
||||
}
|
||||
},
|
||||
I::JumpFalse(idx) => {
|
||||
if self.check_interupt() {
|
||||
return Ok(Some(Value::Nil))
|
||||
}
|
||||
if !self.pop() {
|
||||
frame.ip = idx as usize;
|
||||
}
|
||||
},
|
||||
I::JumpNil(idx) => {
|
||||
if self.check_interupt() {
|
||||
return Ok(Some(Value::Nil))
|
||||
}
|
||||
if self.pop() == Value::Nil {
|
||||
frame.ip = idx as usize;
|
||||
}
|
||||
},
|
||||
I::Call(arity) => {
|
||||
let arity = arity as usize;
|
||||
|
||||
let fun = self.pop();
|
||||
let Value::Function(fun) = fun else {
|
||||
return Err(Error::NotFunction(fun).into())
|
||||
};
|
||||
|
||||
if !fun.variadic && arity > fun.arity {
|
||||
return Err(Error::InvArity(fun.arity, arity as usize, fun.name.clone()).into())
|
||||
}
|
||||
|
||||
let mut params = self.stack.split_off(self.stack.len() - arity).inner;
|
||||
|
||||
if params.len() < fun.arity {
|
||||
let new_arity = fun.arity - arity;
|
||||
let val = native!("<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 == fun.arity {
|
||||
params.push(Value::List(vec![].into()));
|
||||
} else {
|
||||
let count = arity - fun.arity;
|
||||
let varadic_params = params.split_off(params.len() - count);
|
||||
params.push(Value::List(varadic_params.into()));
|
||||
}
|
||||
}
|
||||
|
||||
let res = self.exec_fn(frame, fun, params)?;
|
||||
self.push(res);
|
||||
},
|
||||
I::Return => {
|
||||
if self.check_interupt() {
|
||||
return Ok(Some(Value::Nil))
|
||||
}
|
||||
let ret = self.pop();
|
||||
self.stack.truncate(frame.bp);
|
||||
self.locals.truncate(frame.lp);
|
||||
return Ok(Some(ret))
|
||||
},
|
||||
I::Field(name_idx) => {
|
||||
let expr = self.pop();
|
||||
let name = self.names.borrow()[name_idx as usize].clone();
|
||||
self.push(expr.field_access(&name)?);
|
||||
},
|
||||
I::StoreField(name_idx) => {
|
||||
let mut expr = self.pop();
|
||||
let value = self.pop();
|
||||
let name = self.names.borrow()[name_idx as usize].clone();
|
||||
expr.store_field_access(&name, value)?;
|
||||
},
|
||||
I::Index(count) => {
|
||||
let index = self.stack.split_off(self.stack.len() - count as usize);
|
||||
let collection = self.pop();
|
||||
let value = collection.index(&index.inner)?;
|
||||
self.stack.push(value);
|
||||
},
|
||||
I::StoreIndex(count) => {
|
||||
let index = self.stack.split_off(self.stack.len() - count as usize);
|
||||
let mut collection = self.pop();
|
||||
let value = self.pop();
|
||||
collection.store_index(&index.inner, value)?;
|
||||
},
|
||||
I::IterCreate => {
|
||||
let val = self.pop().into_iter();
|
||||
self.push(val?);
|
||||
},
|
||||
I::IterNext => {
|
||||
let Value::Iter(iter) = self.top() else { panic!("bypassed iter check"); };
|
||||
let val = self.exec_fn(frame, iter, vec![])?;
|
||||
self.push(val);
|
||||
},
|
||||
I::Try(idx) => {
|
||||
let scope = TryScope {
|
||||
err_idx: idx as usize,
|
||||
frame_depth: frame.depth,
|
||||
stack_len: self.stack.len(),
|
||||
locals_len: self.locals.len(),
|
||||
};
|
||||
self.trystack.push(scope);
|
||||
},
|
||||
I::TryEnd => {
|
||||
self.trystack.pop().unwrap();
|
||||
},
|
||||
};
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn stack_trace(&mut self, frames: Vec<StackFrame>) -> String {
|
||||
let mut trace = String::from("\x1b[33m\x1b[1mStack Trace:\x1b[0m\n");
|
||||
for frame in frames {
|
||||
let ip = frame.ip - 1;
|
||||
let pos = frame.body.pos[ip];
|
||||
trace.push_str(&format!(" {} at {}:{}\n", &frame.name, pos.row, pos.col));
|
||||
}
|
||||
trace
|
||||
}
|
||||
|
||||
fn exec_fn(&mut self, frame: &mut StackFrame, fun: Rc<Function>, params: Vec<Value>) -> VmResult<Value> {
|
||||
let name = fun.name.clone();
|
||||
let params_len = params.len();
|
||||
match &fun.fun {
|
||||
InnerFunction::Compiled(body) => {
|
||||
for param in params {
|
||||
self.stack.push(param);
|
||||
}
|
||||
let mut new_frame = StackFrame::new(self, name, body.clone(), params_len, frame.depth + 1);
|
||||
self.exec(&mut new_frame)
|
||||
},
|
||||
InnerFunction::Native(native_fun) => {
|
||||
Ok(native_fun((self, frame), params)?)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn exec(&mut self, frame: &mut StackFrame) -> VmResult<Value> {
|
||||
loop {
|
||||
let ins = frame.body.code[frame.ip].clone();
|
||||
frame.ip += 1;
|
||||
match self.exec_ins(frame, ins) {
|
||||
Ok(Some(val)) => return Ok(val),
|
||||
Ok(None) => {},
|
||||
Err(err) => {
|
||||
if let Some(catch) = self.trystack.pop() {
|
||||
if frame.depth != catch.frame_depth {
|
||||
self.trystack.push(catch);
|
||||
return Err(err);
|
||||
}
|
||||
self.stack.truncate(catch.stack_len);
|
||||
self.locals.truncate(catch.locals_len);
|
||||
frame.ip = catch.err_idx;
|
||||
self.stack.push(Value::Error(err.err));
|
||||
} else {
|
||||
let mut err = err;
|
||||
err.frames.push(frame.clone());
|
||||
return Err(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
loop {
|
||||
use Instruction::*;
|
||||
let ins = frame.body.code[frame.ip].clone();
|
||||
frame.ip += 1;
|
||||
|
||||
match ins {
|
||||
NoOp => {},
|
||||
CreateLocal => self.locals.push(self.stack.pop()),
|
||||
LoadLocal(idx) => {self.stack.push(self.locals[idx as usize].clone());},
|
||||
StoreLocal(idx) => self.locals[frame.bp + idx as usize] = self.pop(),
|
||||
DiscardLocals(count) => {self.locals.truncate(self.locals.len() - count as usize)},
|
||||
LoadGlobal(idx) => {
|
||||
let val = self.globals
|
||||
.get(&idx)
|
||||
.unwrap()
|
||||
.clone();
|
||||
self.stack.push(val);
|
||||
},
|
||||
StoreGlobal(idx) => {
|
||||
let val = self.pop();
|
||||
self.globals.insert(idx, val);
|
||||
},
|
||||
Const(idx) => self.push(frame.body.constants[idx as usize].clone()),
|
||||
Int(i) => self.push(Value::Int(i as i64)),
|
||||
True => self.push(Value::Bool(true)),
|
||||
False => self.push(Value::Bool(false)),
|
||||
Nil => self.push(Value::Nil),
|
||||
Dup => self.push(self.stack[self.stack.len() - 1].clone()),
|
||||
Discard(count) => {self.stack.truncate(self.stack.len() - count as usize)},
|
||||
UnaryOp(op) => {
|
||||
let val = self.pop();
|
||||
self.push(Value::unary_op(op, val));
|
||||
},
|
||||
BinaryOp(op) => {
|
||||
let rhs = self.pop();
|
||||
let lhs = self.pop();
|
||||
self.push(Value::binary_op(op, lhs, rhs)?);
|
||||
},
|
||||
NewList(items) => {
|
||||
let list = self.stack.split_off(self.stack.len() - items as usize);
|
||||
self.push(Value::List(list.inner.into()));
|
||||
},
|
||||
NewTable(items) => {
|
||||
let mut table = ValueMap::new();
|
||||
for _ in 0..items {
|
||||
let value = self.pop();
|
||||
let key = self.pop();
|
||||
table.insert(key, value)?;
|
||||
}
|
||||
self.push(Value::Table(table.into()))
|
||||
},
|
||||
NewMatrix(items, domain) => {
|
||||
let values = self.stack.split_off(self.stack.len() - items as usize).inner;
|
||||
let domain = domain as usize;
|
||||
let codomain = values.len() / domain;
|
||||
self.push(Value::Matrix(Gc::new(Matrix::new(domain, codomain, values))));
|
||||
}
|
||||
Jump(idx) => {
|
||||
if self.check_interupt() {
|
||||
return Ok(Value::Nil)
|
||||
}
|
||||
frame.ip = idx as usize;
|
||||
}
|
||||
JumpTrue(idx) => {
|
||||
if self.check_interupt() {
|
||||
return Ok(Value::Nil)
|
||||
}
|
||||
if !!self.pop() {
|
||||
frame.ip = idx as usize;
|
||||
}
|
||||
},
|
||||
JumpFalse(idx) => {
|
||||
if self.check_interupt() {
|
||||
return Ok(Value::Nil)
|
||||
}
|
||||
if !self.pop() {
|
||||
frame.ip = idx as usize;
|
||||
}
|
||||
},
|
||||
Call(arity) => {
|
||||
let fun = self.pop();
|
||||
let Value::Function(fun) = fun else {
|
||||
return Err(Error::NotFunction(fun).into())
|
||||
};
|
||||
let arity = arity as usize;
|
||||
let normal_args = if fun.variadic { fun.arity - 1 } else { fun.arity };
|
||||
if (fun.variadic && arity < normal_args) || (!fun.variadic && arity != normal_args) {
|
||||
return Err(Error::InvArity(normal_args, arity as usize, fun.name.clone()).into())
|
||||
}
|
||||
if fun.variadic {
|
||||
if arity == normal_args {
|
||||
self.push(Value::List(vec![].into()));
|
||||
} else if arity > normal_args {
|
||||
let count = arity - normal_args;
|
||||
let varadic_params = self.stack.split_off(self.stack.len() - count).inner;
|
||||
self.push(Value::List(varadic_params.into()));
|
||||
}
|
||||
}
|
||||
let name = fun.name.clone();
|
||||
match &fun.fun {
|
||||
InnerFunction::Compiled(body) => {
|
||||
let new_frame = StackFrame::new(self, name, body.clone(), fun.arity);
|
||||
self.frames.push(frame);
|
||||
frame = new_frame;
|
||||
},
|
||||
InnerFunction::Native(native_fun) => {
|
||||
let params = self.stack.split_off(self.stack.len() - fun.arity).inner;
|
||||
let res = native_fun(self, params)?;
|
||||
self.stack.push(res);
|
||||
}
|
||||
}
|
||||
},
|
||||
Return => {
|
||||
if self.check_interupt() {
|
||||
return Ok(Value::Nil)
|
||||
}
|
||||
let Some(prev_frame) = self.frames.pop() else {
|
||||
break;
|
||||
};
|
||||
let ret = self.pop();
|
||||
self.stack.truncate(frame.bp);
|
||||
self.locals.truncate(0);
|
||||
self.push(ret);
|
||||
frame = prev_frame;
|
||||
},
|
||||
Field(name_idx) => {
|
||||
let expr = self.pop();
|
||||
let name = self.names.borrow()[name_idx as usize].clone();
|
||||
self.push(expr.field_access(&name)?);
|
||||
},
|
||||
StoreField(name_idx) => {
|
||||
let mut expr = self.pop();
|
||||
let value = self.pop();
|
||||
let name = self.names.borrow()[name_idx as usize].clone();
|
||||
expr.store_field_access(&name, value)?;
|
||||
},
|
||||
Index(count) => {
|
||||
let index = self.stack.split_off(self.stack.len() - count as usize);
|
||||
let collection = self.pop();
|
||||
let value = collection.index(&index.inner)?;
|
||||
self.stack.push(value);
|
||||
},
|
||||
StoreIndex(count) => {
|
||||
let index = self.stack.split_off(self.stack.len() - count as usize);
|
||||
let mut collection = self.pop();
|
||||
let value = self.pop();
|
||||
collection.store_index(&index.inner, value)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(self.pop())
|
||||
self.exec(&mut frame).map_err(|e| {
|
||||
let trace = self.stack_trace(e.frames);
|
||||
(e.err, trace).into()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue