From ace046624d2e23fba67564a86af7f03ed8a48eae Mon Sep 17 00:00:00 2001 From: Freya Murphy Date: Thu, 29 Feb 2024 21:05:10 -0500 Subject: [PATCH] remove unwraps, fix utf8 --- matrix-bin/src/main.rs | 90 +++++++++++++++-------------- matrix-bin/src/repl.rs | 16 ++--- matrix-lang/src/binary/mod.rs | 6 +- matrix-lang/src/compiler.rs | 2 +- matrix-lang/src/lex.rs | 4 +- matrix-lang/src/parse.rs | 2 +- matrix-lang/src/prelude.rs | 1 + matrix-lang/src/value/exception.rs | 23 +++++++- matrix-lang/src/value/matrix.rs | 2 +- matrix-lang/src/vm.rs | 4 +- matrix-macros/src/lib.rs | 4 +- matrix-std/src/core.rs | 21 +++---- matrix-std/src/io.rs | 6 +- matrix-std/src/iter.rs | 2 +- matrix-std/src/math.rs | 4 +- matrix-std/src/sys.rs | 6 +- test.mat | 8 +++ test.matc | Bin 0 -> 203 bytes 18 files changed, 118 insertions(+), 83 deletions(-) create mode 100644 test.mat create mode 100644 test.matc diff --git a/matrix-bin/src/main.rs b/matrix-bin/src/main.rs index 64d1b3f..8321796 100644 --- a/matrix-bin/src/main.rs +++ b/matrix-bin/src/main.rs @@ -1,4 +1,4 @@ -use std::{path::PathBuf, io::{self, Read, IsTerminal}, fs, cell::RefCell, rc::Rc}; +use std::{path::PathBuf, io::{self, Read, IsTerminal}, cell::RefCell, rc::Rc}; use clap::{Parser as ArgParser, ColorChoice}; use matrix_lang::prelude::*; use repl::Repl; @@ -35,8 +35,8 @@ pub struct Args { pub enum Mode { Repl, - Execute(String), - Compile(String, PathBuf), + Execute(Vec), + Compile(Vec, PathBuf), } pub struct State<'a> { @@ -46,18 +46,26 @@ pub struct State<'a> { color: bool, } +pub fn error(err: Exception, state: &State) { + if state.color { + println!("\x1b[31mError:\x1b[0m {err}"); + } else { + println!("Error: {err}"); + } +} + impl<'a> State<'a> { - pub fn new (args: Args) -> (Self, Mode) { + pub fn new (args: Args) -> Result<(Self, Mode)> { - let stdin = read_stdin(); - - let file; + let mut buffer = Vec::new(); if let Some(path) = &args.file { - file = Some(fs::read_to_string(path).unwrap()); - } else if stdin.len() > 0 { - file = Some(stdin); + let mut f = File::open(path)?; + f.read_to_end(&mut buffer)?; } else { - file = None; + let mut stdin = io::stdin(); + if !stdin.is_terminal() { + stdin.read_to_end(&mut buffer)?; + } } let mode; @@ -74,11 +82,10 @@ impl<'a> State<'a> { PathBuf::from("matc.out") } }; - let file = file.unwrap_or(String::new()); - mode = Mode::Compile(file, path); + mode = Mode::Compile(buffer, path); repl = false; - } else if let Some(file) = file { - mode = Mode::Execute(file); + } else if buffer.len() > 0 { + mode = Mode::Execute(buffer); repl = false; } else { mode = Mode::Repl; @@ -106,16 +113,16 @@ impl<'a> State<'a> { Some(ColorChoice::Never) => false, }; - (Self { + Ok((Self { parser, vm: Rc::new(RefCell::new(vm)), compiler, color, - }, mode) + }, mode)) } pub fn execute(&mut self, fun: Rc) -> Result { - let val = self.vm.try_borrow_mut().unwrap().run(fun)?; + let val = self.vm.borrow_mut().run(fun)?; Ok(val) } @@ -125,47 +132,37 @@ impl<'a> State<'a> { Ok(fun) } - pub fn load_program(&mut self, body: String) -> Result> { - match Program::load(&body)? { + pub fn load_program(&mut self, mut buffer: Vec) -> Result> { + let res = Program::load(buffer.as_mut_slice())?; + match res { Some(fun) => { Ok(fun) }, None => { + let body = buffer_to_string(buffer)?; self.compile(body) }, } } } -pub fn error(err: Exception, state: &State) { - if state.color { - println!("\x1b[31mError:\x1b[0m {err}"); - } else { - println!("Error: {err}"); - } -} - -fn read_stdin() -> String { - let mut buffer = String::new(); - let mut stdin = io::stdin(); - if stdin.is_terminal() { - return String::new(); - } - stdin.read_to_string(&mut buffer).unwrap(); - buffer +fn buffer_to_string(buffer: Vec) -> Result { + String::from_utf8(buffer) + .map_err(|e| exception!(IO_EXCEPTION, "{e}")) } fn handle_mode(state: &mut State, mode: Mode) -> Result<()> { match mode { Mode::Repl => { let mut repl = Repl::new(state); - repl.run(); + repl.run()?; }, - Mode::Execute(body) => { - let fun = state.load_program(body)?; + Mode::Execute(buffer) => { + let fun = state.load_program(buffer)?; state.execute(fun)?; }, - Mode::Compile(body, path) => { + Mode::Compile(buffer, path) => { + let body = buffer_to_string(buffer)?; let fun = state.compile(body)?; let mut file = File::create(path).map_err(|e| exception!(IO_EXCEPTION, "{e}") @@ -176,12 +173,17 @@ fn handle_mode(state: &mut State, mode: Mode) -> Result<()> { Ok(()) } -fn main() { - +fn load() -> Result<()> { let args = Args::parse(); - let (mut state, mode) = State::new(args); - + let (mut state, mode) = State::new(args)?; if let Err(e) = handle_mode(&mut state, mode) { error(e, &state); } + Ok(()) +} + +fn main() { + if let Err(e) = load() { + println!("\x1b[31mFatal:\x1b[0m {e}"); + } } diff --git a/matrix-bin/src/repl.rs b/matrix-bin/src/repl.rs index fe9975f..444faef 100644 --- a/matrix-bin/src/repl.rs +++ b/matrix-bin/src/repl.rs @@ -20,12 +20,12 @@ impl<'s, 'a> Repl<'s, 'a> { Ok(val) } - pub fn run(&mut self) { + pub fn run(&mut self) -> Result<()> { let interupt = self.state.vm.borrow().interupt(); ctrlc::set_handler(move || { interupt.store(Interupt::KeyboardInterupt as usize, Ordering::SeqCst); - }).unwrap(); + }).exception()?; let config = Config::builder() .indent_size(4) @@ -39,10 +39,10 @@ impl<'s, 'a> Repl<'s, 'a> { let histfile = std::env::var("MATRIX_HISTORY").ok(); - let mut rl = Editor::with_config(config).unwrap(); + let mut rl = Editor::with_config(config).exception()?; rl.set_helper(Some(helper)); if let Some(hf) = &histfile { - rl.load_history(hf).ok(); + rl.load_history(hf).exception()?; } loop { @@ -52,7 +52,7 @@ impl<'s, 'a> Repl<'s, 'a> { Err(_) => continue, }; - rl.add_history_entry(&line).ok(); + rl.add_history_entry(&line).exception()?; match self.execute(line) { Ok(val) => { @@ -66,12 +66,14 @@ impl<'s, 'a> Repl<'s, 'a> { } Err(err) => crate::error(err, &self.state), }; - std::io::stdout().flush().ok(); + std::io::stdout().flush().exception()?; } if let Some(hf) = &histfile { - rl.save_history(hf).ok(); + rl.save_history(hf).exception()?; } + + Ok(()) } } diff --git a/matrix-lang/src/binary/mod.rs b/matrix-lang/src/binary/mod.rs index 53b3fe5..780d2a0 100644 --- a/matrix-lang/src/binary/mod.rs +++ b/matrix-lang/src/binary/mod.rs @@ -13,8 +13,7 @@ pub struct Program { const PROGRAM_HEADER: [u8; 5] = [0x00, 0x4d, 0x41, 0x54, 0x0a]; impl Program { - pub fn load(body: &str) -> Result>> { - let mut bytes = body.as_bytes(); + pub fn load(bytes: &mut [u8]) -> Result>> { if bytes.len() < 6 { return Ok(None) } @@ -22,7 +21,8 @@ impl Program { if header != &PROGRAM_HEADER { return Ok(None) } - let mut s = ProgramDeserializer::from(&mut bytes); + let mut a = &bytes[..]; + let mut s = ProgramDeserializer::from(&mut a); let program = ::deserialize(&mut s)?; s.finish()?; Ok(Some(program.fun.clone())) diff --git a/matrix-lang/src/compiler.rs b/matrix-lang/src/compiler.rs index 95c6ccf..da1ecf7 100644 --- a/matrix-lang/src/compiler.rs +++ b/matrix-lang/src/compiler.rs @@ -142,7 +142,7 @@ impl<'c> Compiler<'c> { fn collapse_scopes(&mut self, top_scope: usize) { let mut cutoff = usize::MAX; while self.scopes.len() > top_scope { - cutoff = self.scopes.pop().unwrap() + cutoff = self.scopes.pop().expect("bypassed compiler scope check") } if cutoff < self.locals.len() { self.emit(Instruction::DiscardLocals((self.locals.len() - cutoff) as u16), self.last_pos()); diff --git a/matrix-lang/src/lex.rs b/matrix-lang/src/lex.rs index b2487ad..82545c4 100644 --- a/matrix-lang/src/lex.rs +++ b/matrix-lang/src/lex.rs @@ -286,7 +286,7 @@ impl Lexer { buf.push(char::from_u32( n1.to_digit(16).ok_or(error!("invalid digit '{n1}'"))? * 16 + n2.to_digit(16).ok_or(error!("invalid digit '{n2}'"))? - ).unwrap()); + ).expect("bypassed digit check")); }, 'u' => { self.next_expect('{')?; @@ -416,7 +416,7 @@ impl Lexer { } } - let last: char = buf.chars().last().unwrap(); + let last: char = buf.chars().last().unwrap_or('\0'); let is_range = initial != '.' && last == '.' && self.peek() == '.'; if is_range { diff --git a/matrix-lang/src/parse.rs b/matrix-lang/src/parse.rs index c476650..04f09ee 100644 --- a/matrix-lang/src/parse.rs +++ b/matrix-lang/src/parse.rs @@ -169,7 +169,7 @@ impl Parser { }; } if parts.len() == 1 { - Ok((E::List(parts.pop().unwrap()), pos).into()) + Ok((E::List(parts.pop().expect("bypassed vec length")), pos).into()) } else { let codomain = parts.len(); let domain = parts[0].len(); diff --git a/matrix-lang/src/prelude.rs b/matrix-lang/src/prelude.rs index c5af0c8..cf51a63 100644 --- a/matrix-lang/src/prelude.rs +++ b/matrix-lang/src/prelude.rs @@ -2,6 +2,7 @@ pub type Result = std::result::Result; pub use crate::value::Value as Value; pub use crate::value::exception::Exception as Exception; +pub use crate::value::exception::Except as Except; pub use crate::value::matrix::Matrix as Matrix; pub use crate::value::gc::Gc as Gc; pub use crate::value::hash::ValueMap as ValueMap; diff --git a/matrix-lang/src/value/exception.rs b/matrix-lang/src/value/exception.rs index 0df6f5c..c4ae606 100644 --- a/matrix-lang/src/value/exception.rs +++ b/matrix-lang/src/value/exception.rs @@ -1,4 +1,4 @@ -use std::{fmt::{Debug, Display}, error::Error}; +use std::{fmt::{Debug, Display}, error::Error, io}; use crate::prelude::*; #[macro_export] @@ -76,3 +76,24 @@ impl From for Result { Err(value) } } + +impl From for Exception { + fn from(value: io::Error) -> Self { + exception!(IO_EXCEPTION, "{value}") + } +} + +pub trait Except { + type Output; + fn exception(self) -> Result; +} + +impl Except for std::result::Result { + type Output = T; + + fn exception(self) -> Result { + self.map_err(|e| { + Exception::msg("unknown", format!("{e}").as_str()) + }) + } +} diff --git a/matrix-lang/src/value/matrix.rs b/matrix-lang/src/value/matrix.rs index 91e3ec2..1d55210 100644 --- a/matrix-lang/src/value/matrix.rs +++ b/matrix-lang/src/value/matrix.rs @@ -184,7 +184,7 @@ impl Matrix { let values = rows .into_iter() .reduce(|mut a,b| {a.extend(b); a}) - .unwrap(); + .ok_or(error!("matrix row smashed"))?; Ok(Matrix::new(self.domain + other.domain, self.codomain, values)) } diff --git a/matrix-lang/src/vm.rs b/matrix-lang/src/vm.rs index bac6341..b8da01f 100644 --- a/matrix-lang/src/vm.rs +++ b/matrix-lang/src/vm.rs @@ -217,7 +217,7 @@ impl Vm { let val = self.globals .borrow_mut() .get(&idx) - .unwrap() + .ok_or(exception!(RUNTIME_EXCEPTION, "undefined global at index {idx}"))? .clone(); self.stack.push(val); }, @@ -380,7 +380,7 @@ impl Vm { self.trystack.push(scope); }, I::TryEnd => { - self.trystack.pop().unwrap(); + self.trystack.pop().ok_or(exception!(RUNTIME_EXCEPTION, "try stack smashed"))?; }, }; diff --git a/matrix-macros/src/lib.rs b/matrix-macros/src/lib.rs index a660053..f347956 100644 --- a/matrix-macros/src/lib.rs +++ b/matrix-macros/src/lib.rs @@ -17,9 +17,9 @@ impl Parse for NativeFuncParams { #[proc_macro_attribute] pub fn native_func(input: TokenStream, annotated_item: TokenStream) -> TokenStream { - let itemfn: ItemFn = syn::parse(annotated_item).unwrap(); + let itemfn: ItemFn = syn::parse(annotated_item).expect("invalid rust function"); - let input: NativeFuncParams = syn::parse(input).unwrap(); + let input: NativeFuncParams = syn::parse(input).expect("invalid native fn annotation"); let arity = input.arity; let variadic = input.variadic.is_some(); diff --git a/matrix-std/src/core.rs b/matrix-std/src/core.rs index 69b6d97..5b702d0 100644 --- a/matrix-std/src/core.rs +++ b/matrix-std/src/core.rs @@ -4,30 +4,31 @@ use matrix_lang::prelude::*; use matrix_macros::native_func; use crate::{VmArgs, next, error, unpack_args, unpack_varargs}; -fn to_radix(r: i64, mut n: i64) -> String { +fn to_radix(r: i64, mut n: i64) -> Result { let mut result = String::new(); let mut idx = 0; if n == 0 { result.push('0'); - return result + return Ok(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(); + let c = std::char::from_digit((n % r) as u32, r as u32) + .ok_or(exception!(RUNTIME_EXCEPTION, "given radix and digit created invalid number"))?; result.insert(idx, c); n /= r; } - result + Ok(result) } #[native_func(1)] fn bin(_: VmArgs, args: Vec) -> Result { let [value] = unpack_args!(args); match value { - Value::Int(n) => Ok(Value::String(to_radix(2, n).into())), + Value::Int(n) => Ok(Value::String(to_radix(2, n)?.into())), _ => error!("bin requires a integer agument") } } @@ -36,7 +37,7 @@ fn bin(_: VmArgs, args: Vec) -> Result { fn sex(_: VmArgs, args: Vec) -> Result { let [value] = unpack_args!(args); match value { - Value::Int(n) => Ok(Value::String(to_radix(6, n).into())), + Value::Int(n) => Ok(Value::String(to_radix(6, n)?.into())), _ => error!("sex requires a integer agument") } } @@ -45,7 +46,7 @@ fn sex(_: VmArgs, args: Vec) -> Result { fn oct(_: VmArgs, args: Vec) -> Result { let [value] = unpack_args!(args); match value { - Value::Int(n) => Ok(Value::String(to_radix(8, n).into())), + Value::Int(n) => Ok(Value::String(to_radix(8, n)?.into())), _ => error!("oct requires a integer agument") } } @@ -54,7 +55,7 @@ fn oct(_: VmArgs, args: Vec) -> Result { fn hex(_: VmArgs, args: Vec) -> Result { let [value] = unpack_args!(args); match value { - Value::Int(n) => Ok(Value::String(to_radix(16, n).into())), + Value::Int(n) => Ok(Value::String(to_radix(16, n)?.into())), _ => error!("hex requires a integer agument") } } @@ -63,7 +64,7 @@ fn hex(_: VmArgs, args: Vec) -> Result { fn radix(_: VmArgs, args: Vec) -> Result { let [radix, value] = unpack_args!(args); match (radix, value) { - (Value::Int(r), Value::Int(n)) => Ok(Value::String(to_radix(r, n).into())), + (Value::Int(r), Value::Int(n)) => Ok(Value::String(to_radix(r, n)?.into())), _ => error!("radix requires two integer aguments") } } @@ -136,7 +137,7 @@ fn ord(_: VmArgs, args: Vec) -> Result { if str.len() != 1 { return error!("ord requires a 1 length string") } - let char = str.chars().next().unwrap(); + let char = str.chars().next().unwrap_or('\0'); Ok(Value::Int(char as i64)) } diff --git a/matrix-std/src/io.rs b/matrix-std/src/io.rs index 19ff074..829ed28 100644 --- a/matrix-std/src/io.rs +++ b/matrix-std/src/io.rs @@ -106,7 +106,7 @@ fn file_read(_: VmArgs, args: Vec) -> Result { 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) { + if let Err(err) = file.borrow_mut().read_to_string(&mut contents) { return error!("cannot read file: '{err}'") }; Ok(Value::String(contents.into())) @@ -119,7 +119,7 @@ fn file_lines(_: VmArgs, args: Vec) -> Result { 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) { + if let Err(err) = file.borrow_mut().read_to_string(&mut contents) { return error!("cannot read file: '{err}'") }; let lines: Vec> = contents.split_inclusive("\n").map(|s| Rc::from(s)).collect(); @@ -139,7 +139,7 @@ fn file_write(_: VmArgs, args: Vec) -> Result { 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()) { + if let Err(err) = file.borrow_mut().write_all(content.as_bytes()) { return error!("cannot write file: '{err}'") }; Ok(Value::Nil) diff --git a/matrix-std/src/iter.rs b/matrix-std/src/iter.rs index 638755c..c0e0c63 100644 --- a/matrix-std/src/iter.rs +++ b/matrix-std/src/iter.rs @@ -365,7 +365,7 @@ fn unzip((vm, frame): VmArgs, args: Vec) -> Result { if vals.len() != 2 { return error!("unzip only works over a iterator of pairs"); } - let [l, r] = vals.try_into().unwrap(); + let [l, r] = vals.try_into().map_err(|_| exception!(RUNTIME_EXCEPTION, "can only unzip over a set of pairs"))?; ll.push(l); lr.push(r); } diff --git a/matrix-std/src/math.rs b/matrix-std/src/math.rs index 111544c..503192d 100644 --- a/matrix-std/src/math.rs +++ b/matrix-std/src/math.rs @@ -15,7 +15,7 @@ fn trans(_: VmArgs, args: Vec) -> Result { let values = mat .cols() .reduce(|mut a, b| {a.extend(b); a}) - .unwrap() + .ok_or(exception!(RUNTIME_EXCEPTION, "matrix values smashed"))? .into_iter() .map(|e| e.clone()) .collect(); @@ -164,7 +164,7 @@ fn mat_det(mat: Matrix) -> Result { .collect::>() ) .reduce(|mut a, b| {a.extend(b); a}) - .unwrap(); + .ok_or(exception!(RUNTIME_EXCEPTION, "matrix values smashed"))?; let sub = Matrix::new(mat.domain - 1, mat.domain - 1, sub_values); let val = mat.get(0, col)?; let part = (val * mat_det(sub)?)?; diff --git a/matrix-std/src/sys.rs b/matrix-std/src/sys.rs index 609e72d..8b54f0f 100644 --- a/matrix-std/src/sys.rs +++ b/matrix-std/src/sys.rs @@ -193,7 +193,7 @@ fn basename(_: VmArgs, args: Vec) -> Result { }; let path = PathBuf::from(value.to_string()); match path.file_name() { - Some(p) => Ok(Value::String(p.to_str().unwrap().into())), + Some(p) => Ok(Value::String(p.to_str().unwrap_or("").into())), None => Ok(Value::String(value.into())) } } @@ -209,7 +209,7 @@ fn dirname(_: VmArgs, args: Vec) -> Result { Some(p) => p, None => path.as_path() }; - let str = parent.as_os_str().to_str().unwrap(); + let str = parent.as_os_str().to_str().unwrap_or(""); match str { "" => Ok(Value::String(".".into())), s => Ok(Value::String(s.into())) @@ -226,7 +226,7 @@ fn realpath(_: VmArgs, args: Vec) -> Result { Ok(p) => p, Err(e) => return error!("could not get realpath: {e}") }; - Ok(Value::String(path.to_str().unwrap().into())) + Ok(Value::String(path.to_str().unwrap_or("").into())) } diff --git a/test.mat b/test.mat new file mode 100644 index 0000000..71a04dd --- /dev/null +++ b/test.mat @@ -0,0 +1,8 @@ +let a = 3 +{ + a = 4; + const bees = \a => a*2; + let j = bees(3); + a = j; +} +let g = a + 'aaaa'; diff --git a/test.matc b/test.matc new file mode 100644 index 0000000000000000000000000000000000000000..19c52ca9768c5ec10a9c268b124c058f4466bcb6 GIT binary patch literal 203 zcmXYrT?zs*41_0X`eW^~Dz}rxQXMWuud7+wFVjwVjGg}$tAV<~cMa!%+1%pH#AqbKI)Oq|9116lP*37jQ oE*pB$6MAjtiDJWeg^kGAu!D_u*Kl58$DYF`bgF&fo9cu30eXZCp#T5? literal 0 HcmV?d00001