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 matrix_lang::prelude::*;
use repl::Repl;
@ -35,8 +35,8 @@ pub struct Args {
pub enum Mode {
Repl,
Execute(String),
Compile(String, PathBuf),
Execute(Vec<u8>),
Compile(Vec<u8>, 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<Function>) -> Result<Value> {
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<Rc<Function>> {
match Program::load(&body)? {
pub fn load_program(&mut self, mut buffer: Vec<u8>) -> Result<Rc<Function>> {
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<u8>) -> Result<String> {
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}");
}
}

View file

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

View file

@ -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<Option<Rc<Function>>> {
let mut bytes = body.as_bytes();
pub fn load(bytes: &mut [u8]) -> Result<Option<Rc<Function>>> {
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 = <Self>::deserialize(&mut s)?;
s.finish()?;
Ok(Some(program.fun.clone()))

View file

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

View file

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

View file

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

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::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;

View file

@ -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<T> From<Exception> for Result<T> {
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
.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))
}

View file

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

View file

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

View file

@ -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<String> {
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<Value>) -> Result<Value> {
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<Value>) -> Result<Value> {
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())),
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<Value>) -> Result<Value> {
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())),
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<Value>) -> Result<Value> {
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())),
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<Value>) -> Result<Value> {
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())),
(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<Value>) -> Result<Value> {
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))
}

View file

@ -106,7 +106,7 @@ fn file_read(_: VmArgs, args: Vec<Value>) -> Result<Value> {
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<Value>) -> Result<Value> {
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<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")
};
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)

View file

@ -365,7 +365,7 @@ fn unzip((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
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);
}

View file

@ -15,7 +15,7 @@ fn trans(_: VmArgs, args: Vec<Value>) -> Result<Value> {
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<Value> {
.collect::<Vec<Value>>()
)
.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)?)?;

View file

@ -193,7 +193,7 @@ fn basename(_: VmArgs, args: Vec<Value>) -> Result<Value> {
};
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<Value>) -> Result<Value> {
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<Value>) -> Result<Value> {
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()))
}

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.