remove unwraps, fix utf8

This commit is contained in:
Freya Murphy 2024-02-29 21:05:10 -05:00
parent e492ad9a4a
commit ace046624d
Signed by: freya
GPG key ID: 744AB800E383AE52
18 changed files with 118 additions and 83 deletions

View file

@ -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 clap::{Parser as ArgParser, ColorChoice};
use matrix_lang::prelude::*; use matrix_lang::prelude::*;
use repl::Repl; use repl::Repl;
@ -35,8 +35,8 @@ pub struct Args {
pub enum Mode { pub enum Mode {
Repl, Repl,
Execute(String), Execute(Vec<u8>),
Compile(String, PathBuf), Compile(Vec<u8>, PathBuf),
} }
pub struct State<'a> { pub struct State<'a> {
@ -46,18 +46,26 @@ pub struct State<'a> {
color: bool, 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> { impl<'a> State<'a> {
pub fn new (args: Args) -> (Self, Mode) { pub fn new (args: Args) -> Result<(Self, Mode)> {
let stdin = read_stdin(); let mut buffer = Vec::new();
let file;
if let Some(path) = &args.file { if let Some(path) = &args.file {
file = Some(fs::read_to_string(path).unwrap()); let mut f = File::open(path)?;
} else if stdin.len() > 0 { f.read_to_end(&mut buffer)?;
file = Some(stdin);
} else { } else {
file = None; let mut stdin = io::stdin();
if !stdin.is_terminal() {
stdin.read_to_end(&mut buffer)?;
}
} }
let mode; let mode;
@ -74,11 +82,10 @@ impl<'a> State<'a> {
PathBuf::from("matc.out") PathBuf::from("matc.out")
} }
}; };
let file = file.unwrap_or(String::new()); mode = Mode::Compile(buffer, path);
mode = Mode::Compile(file, path);
repl = false; repl = false;
} else if let Some(file) = file { } else if buffer.len() > 0 {
mode = Mode::Execute(file); mode = Mode::Execute(buffer);
repl = false; repl = false;
} else { } else {
mode = Mode::Repl; mode = Mode::Repl;
@ -106,16 +113,16 @@ impl<'a> State<'a> {
Some(ColorChoice::Never) => false, Some(ColorChoice::Never) => false,
}; };
(Self { Ok((Self {
parser, parser,
vm: Rc::new(RefCell::new(vm)), vm: Rc::new(RefCell::new(vm)),
compiler, compiler,
color, color,
}, mode) }, mode))
} }
pub fn execute(&mut self, fun: Rc<Function>) -> Result<Value> { pub fn execute(&mut self, fun: Rc<Function>) -> Result<Value> {
let val = self.vm.try_borrow_mut().unwrap().run(fun)?; let val = self.vm.borrow_mut().run(fun)?;
Ok(val) Ok(val)
} }
@ -125,47 +132,37 @@ impl<'a> State<'a> {
Ok(fun) Ok(fun)
} }
pub fn load_program(&mut self, body: String) -> Result<Rc<Function>> { pub fn load_program(&mut self, mut buffer: Vec<u8>) -> Result<Rc<Function>> {
match Program::load(&body)? { let res = Program::load(buffer.as_mut_slice())?;
match res {
Some(fun) => { Some(fun) => {
Ok(fun) Ok(fun)
}, },
None => { None => {
let body = buffer_to_string(buffer)?;
self.compile(body) self.compile(body)
}, },
} }
} }
} }
pub fn error(err: Exception, state: &State) { fn buffer_to_string(buffer: Vec<u8>) -> Result<String> {
if state.color { String::from_utf8(buffer)
println!("\x1b[31mError:\x1b[0m {err}"); .map_err(|e| exception!(IO_EXCEPTION, "{e}"))
} 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 handle_mode(state: &mut State, mode: Mode) -> Result<()> { fn handle_mode(state: &mut State, mode: Mode) -> Result<()> {
match mode { match mode {
Mode::Repl => { Mode::Repl => {
let mut repl = Repl::new(state); let mut repl = Repl::new(state);
repl.run(); repl.run()?;
}, },
Mode::Execute(body) => { Mode::Execute(buffer) => {
let fun = state.load_program(body)?; let fun = state.load_program(buffer)?;
state.execute(fun)?; state.execute(fun)?;
}, },
Mode::Compile(body, path) => { Mode::Compile(buffer, path) => {
let body = buffer_to_string(buffer)?;
let fun = state.compile(body)?; let fun = state.compile(body)?;
let mut file = File::create(path).map_err(|e| let mut file = File::create(path).map_err(|e|
exception!(IO_EXCEPTION, "{e}") exception!(IO_EXCEPTION, "{e}")
@ -176,12 +173,17 @@ fn handle_mode(state: &mut State, mode: Mode) -> Result<()> {
Ok(()) Ok(())
} }
fn main() { fn load() -> Result<()> {
let args = Args::parse(); 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) { if let Err(e) = handle_mode(&mut state, mode) {
error(e, &state); error(e, &state);
} }
Ok(())
}
fn main() {
if let Err(e) = load() {
println!("\x1b[31mFatal:\x1b[0m {e}");
}
} }

View file

@ -20,12 +20,12 @@ impl<'s, 'a> Repl<'s, 'a> {
Ok(val) Ok(val)
} }
pub fn run(&mut self) { pub fn run(&mut self) -> Result<()> {
let interupt = self.state.vm.borrow().interupt(); let interupt = self.state.vm.borrow().interupt();
ctrlc::set_handler(move || { ctrlc::set_handler(move || {
interupt.store(Interupt::KeyboardInterupt as usize, Ordering::SeqCst); interupt.store(Interupt::KeyboardInterupt as usize, Ordering::SeqCst);
}).unwrap(); }).exception()?;
let config = Config::builder() let config = Config::builder()
.indent_size(4) .indent_size(4)
@ -39,10 +39,10 @@ impl<'s, 'a> Repl<'s, 'a> {
let histfile = std::env::var("MATRIX_HISTORY").ok(); 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)); rl.set_helper(Some(helper));
if let Some(hf) = &histfile { if let Some(hf) = &histfile {
rl.load_history(hf).ok(); rl.load_history(hf).exception()?;
} }
loop { loop {
@ -52,7 +52,7 @@ impl<'s, 'a> Repl<'s, 'a> {
Err(_) => continue, Err(_) => continue,
}; };
rl.add_history_entry(&line).ok(); rl.add_history_entry(&line).exception()?;
match self.execute(line) { match self.execute(line) {
Ok(val) => { Ok(val) => {
@ -66,12 +66,14 @@ impl<'s, 'a> Repl<'s, 'a> {
} }
Err(err) => crate::error(err, &self.state), Err(err) => crate::error(err, &self.state),
}; };
std::io::stdout().flush().ok(); std::io::stdout().flush().exception()?;
} }
if let Some(hf) = &histfile { if let Some(hf) = &histfile {
rl.save_history(hf).ok(); rl.save_history(hf).exception()?;
} }
Ok(())
} }
} }

View file

@ -13,8 +13,7 @@ pub struct Program {
const PROGRAM_HEADER: [u8; 5] = [0x00, 0x4d, 0x41, 0x54, 0x0a]; const PROGRAM_HEADER: [u8; 5] = [0x00, 0x4d, 0x41, 0x54, 0x0a];
impl Program { impl Program {
pub fn load(body: &str) -> Result<Option<Rc<Function>>> { pub fn load(bytes: &mut [u8]) -> Result<Option<Rc<Function>>> {
let mut bytes = body.as_bytes();
if bytes.len() < 6 { if bytes.len() < 6 {
return Ok(None) return Ok(None)
} }
@ -22,7 +21,8 @@ impl Program {
if header != &PROGRAM_HEADER { if header != &PROGRAM_HEADER {
return Ok(None) return Ok(None)
} }
let mut s = ProgramDeserializer::from(&mut bytes); let mut a = &bytes[..];
let mut s = ProgramDeserializer::from(&mut a);
let program = <Self>::deserialize(&mut s)?; let program = <Self>::deserialize(&mut s)?;
s.finish()?; s.finish()?;
Ok(Some(program.fun.clone())) Ok(Some(program.fun.clone()))

View file

@ -142,7 +142,7 @@ impl<'c> Compiler<'c> {
fn collapse_scopes(&mut self, top_scope: usize) { fn collapse_scopes(&mut self, top_scope: usize) {
let mut cutoff = usize::MAX; let mut cutoff = usize::MAX;
while self.scopes.len() > top_scope { 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() { if cutoff < self.locals.len() {
self.emit(Instruction::DiscardLocals((self.locals.len() - cutoff) as u16), self.last_pos()); self.emit(Instruction::DiscardLocals((self.locals.len() - cutoff) as u16), self.last_pos());

View file

@ -286,7 +286,7 @@ impl Lexer {
buf.push(char::from_u32( buf.push(char::from_u32(
n1.to_digit(16).ok_or(error!("invalid digit '{n1}'"))? * 16 + n1.to_digit(16).ok_or(error!("invalid digit '{n1}'"))? * 16 +
n2.to_digit(16).ok_or(error!("invalid digit '{n2}'"))? n2.to_digit(16).ok_or(error!("invalid digit '{n2}'"))?
).unwrap()); ).expect("bypassed digit check"));
}, },
'u' => { 'u' => {
self.next_expect('{')?; 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() == '.'; let is_range = initial != '.' && last == '.' && self.peek() == '.';
if is_range { if is_range {

View file

@ -169,7 +169,7 @@ impl Parser {
}; };
} }
if parts.len() == 1 { if parts.len() == 1 {
Ok((E::List(parts.pop().unwrap()), pos).into()) Ok((E::List(parts.pop().expect("bypassed vec length")), pos).into())
} else { } else {
let codomain = parts.len(); let codomain = parts.len();
let domain = parts[0].len(); let domain = parts[0].len();

View file

@ -2,6 +2,7 @@ pub type Result<T> = std::result::Result<T, Exception>;
pub use crate::value::Value as Value; pub use crate::value::Value as Value;
pub use crate::value::exception::Exception as Exception; 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::matrix::Matrix as Matrix;
pub use crate::value::gc::Gc as Gc; pub use crate::value::gc::Gc as Gc;
pub use crate::value::hash::ValueMap as ValueMap; pub use crate::value::hash::ValueMap as ValueMap;

View file

@ -1,4 +1,4 @@
use std::{fmt::{Debug, Display}, error::Error}; use std::{fmt::{Debug, Display}, error::Error, io};
use crate::prelude::*; use crate::prelude::*;
#[macro_export] #[macro_export]
@ -76,3 +76,24 @@ impl<T> From<Exception> for Result<T> {
Err(value) Err(value)
} }
} }
impl From<io::Error> for Exception {
fn from(value: io::Error) -> Self {
exception!(IO_EXCEPTION, "{value}")
}
}
pub trait Except {
type Output;
fn exception(self) -> Result<Self::Output>;
}
impl<T, E: std::error::Error> Except for std::result::Result<T, E> {
type Output = T;
fn exception(self) -> Result<Self::Output> {
self.map_err(|e| {
Exception::msg("unknown", format!("{e}").as_str())
})
}
}

View file

@ -184,7 +184,7 @@ impl Matrix {
let values = rows let values = rows
.into_iter() .into_iter()
.reduce(|mut a,b| {a.extend(b); a}) .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)) Ok(Matrix::new(self.domain + other.domain, self.codomain, values))
} }

View file

@ -217,7 +217,7 @@ impl Vm {
let val = self.globals let val = self.globals
.borrow_mut() .borrow_mut()
.get(&idx) .get(&idx)
.unwrap() .ok_or(exception!(RUNTIME_EXCEPTION, "undefined global at index {idx}"))?
.clone(); .clone();
self.stack.push(val); self.stack.push(val);
}, },
@ -380,7 +380,7 @@ impl Vm {
self.trystack.push(scope); self.trystack.push(scope);
}, },
I::TryEnd => { I::TryEnd => {
self.trystack.pop().unwrap(); self.trystack.pop().ok_or(exception!(RUNTIME_EXCEPTION, "try stack smashed"))?;
}, },
}; };

View file

@ -17,9 +17,9 @@ impl Parse for NativeFuncParams {
#[proc_macro_attribute] #[proc_macro_attribute]
pub fn native_func(input: TokenStream, annotated_item: TokenStream) -> TokenStream { 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 arity = input.arity;
let variadic = input.variadic.is_some(); let variadic = input.variadic.is_some();

View file

@ -4,30 +4,31 @@ use matrix_lang::prelude::*;
use matrix_macros::native_func; use matrix_macros::native_func;
use crate::{VmArgs, next, error, unpack_args, unpack_varargs}; 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<String> {
let mut result = String::new(); let mut result = String::new();
let mut idx = 0; let mut idx = 0;
if n == 0 { if n == 0 {
result.push('0'); result.push('0');
return result return Ok(result)
} else if n < 0 { } else if n < 0 {
n = -n; n = -n;
idx = 1; idx = 1;
result.push('-'); result.push('-');
} }
while n != 0 { 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); result.insert(idx, c);
n /= r; n /= r;
} }
result Ok(result)
} }
#[native_func(1)] #[native_func(1)]
fn bin(_: VmArgs, args: Vec<Value>) -> Result<Value> { fn bin(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [value] = unpack_args!(args); let [value] = unpack_args!(args);
match value { 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") _ => error!("bin requires a integer agument")
} }
} }
@ -36,7 +37,7 @@ fn bin(_: VmArgs, args: Vec<Value>) -> Result<Value> {
fn sex(_: VmArgs, args: Vec<Value>) -> Result<Value> { fn sex(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [value] = unpack_args!(args); let [value] = unpack_args!(args);
match value { 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") _ => error!("sex requires a integer agument")
} }
} }
@ -45,7 +46,7 @@ fn sex(_: VmArgs, args: Vec<Value>) -> Result<Value> {
fn oct(_: VmArgs, args: Vec<Value>) -> Result<Value> { fn oct(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [value] = unpack_args!(args); let [value] = unpack_args!(args);
match value { 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") _ => error!("oct requires a integer agument")
} }
} }
@ -54,7 +55,7 @@ fn oct(_: VmArgs, args: Vec<Value>) -> Result<Value> {
fn hex(_: VmArgs, args: Vec<Value>) -> Result<Value> { fn hex(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [value] = unpack_args!(args); let [value] = unpack_args!(args);
match value { 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") _ => error!("hex requires a integer agument")
} }
} }
@ -63,7 +64,7 @@ fn hex(_: VmArgs, args: Vec<Value>) -> Result<Value> {
fn radix(_: VmArgs, args: Vec<Value>) -> Result<Value> { fn radix(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let [radix, value] = unpack_args!(args); let [radix, value] = unpack_args!(args);
match (radix, value) { 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") _ => error!("radix requires two integer aguments")
} }
} }
@ -136,7 +137,7 @@ fn ord(_: VmArgs, args: Vec<Value>) -> Result<Value> {
if str.len() != 1 { if str.len() != 1 {
return error!("ord requires a 1 length string") 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)) Ok(Value::Int(char as i64))
} }

View file

@ -106,7 +106,7 @@ fn file_read(_: VmArgs, args: Vec<Value>) -> Result<Value> {
return error!("file read requires a file") return error!("file read requires a file")
}; };
let mut contents = String::new(); 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}'") return error!("cannot read file: '{err}'")
}; };
Ok(Value::String(contents.into())) Ok(Value::String(contents.into()))
@ -119,7 +119,7 @@ fn file_lines(_: VmArgs, args: Vec<Value>) -> Result<Value> {
return error!("file read requires a file") return error!("file read requires a file")
}; };
let mut contents = String::new(); 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}'") return error!("cannot read file: '{err}'")
}; };
let lines: Vec<Rc<str>> = contents.split_inclusive("\n").map(|s| Rc::from(s)).collect(); let lines: Vec<Rc<str>> = contents.split_inclusive("\n").map(|s| Rc::from(s)).collect();
@ -139,7 +139,7 @@ fn file_write(_: VmArgs, args: Vec<Value>) -> Result<Value> {
return error!("file write requires a file") return error!("file write requires a file")
}; };
let content = format!("{content}"); 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}'") return error!("cannot write file: '{err}'")
}; };
Ok(Value::Nil) Ok(Value::Nil)

View file

@ -365,7 +365,7 @@ fn unzip((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
if vals.len() != 2 { if vals.len() != 2 {
return error!("unzip only works over a iterator of pairs"); 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); ll.push(l);
lr.push(r); lr.push(r);
} }

View file

@ -15,7 +15,7 @@ fn trans(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let values = mat let values = mat
.cols() .cols()
.reduce(|mut a, b| {a.extend(b); a}) .reduce(|mut a, b| {a.extend(b); a})
.unwrap() .ok_or(exception!(RUNTIME_EXCEPTION, "matrix values smashed"))?
.into_iter() .into_iter()
.map(|e| e.clone()) .map(|e| e.clone())
.collect(); .collect();
@ -164,7 +164,7 @@ fn mat_det(mat: Matrix) -> Result<Value> {
.collect::<Vec<Value>>() .collect::<Vec<Value>>()
) )
.reduce(|mut a, b| {a.extend(b); a}) .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 sub = Matrix::new(mat.domain - 1, mat.domain - 1, sub_values);
let val = mat.get(0, col)?; let val = mat.get(0, col)?;
let part = (val * mat_det(sub)?)?; let part = (val * mat_det(sub)?)?;

View file

@ -193,7 +193,7 @@ fn basename(_: VmArgs, args: Vec<Value>) -> Result<Value> {
}; };
let path = PathBuf::from(value.to_string()); let path = PathBuf::from(value.to_string());
match path.file_name() { 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())) None => Ok(Value::String(value.into()))
} }
} }
@ -209,7 +209,7 @@ fn dirname(_: VmArgs, args: Vec<Value>) -> Result<Value> {
Some(p) => p, Some(p) => p,
None => path.as_path() 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 { match str {
"" => Ok(Value::String(".".into())), "" => Ok(Value::String(".".into())),
s => Ok(Value::String(s.into())) s => Ok(Value::String(s.into()))
@ -226,7 +226,7 @@ fn realpath(_: VmArgs, args: Vec<Value>) -> Result<Value> {
Ok(p) => p, Ok(p) => p,
Err(e) => return error!("could not get realpath: {e}") 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()))
} }

8
test.mat Normal file
View file

@ -0,0 +1,8 @@
let a = 3
{
a = 4;
const bees = \a => a*2;
let j = bees(3);
a = j;
}
let g = a + 'aaaa';

BIN
test.matc Normal file

Binary file not shown.