diff options
| author | Edvard Thörnros <edvard.thornros@gmail.com> | 2021-02-19 18:07:21 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-02-19 18:07:21 +0100 |
| commit | fe41db5dceecb01a0ada18ea2fc369abb1498aa3 (patch) | |
| tree | 539e63178a3a687bc4df92d53712cd9b028bf0e9 | |
| parent | f024e88de53c24fd5e5e2fb2d66947dc93262af8 (diff) | |
| parent | 7e330b8d622183696ddf3c7f8140c6510804e0a0 (diff) | |
| download | sylt-fe41db5dceecb01a0ada18ea2fc369abb1498aa3.tar.gz | |
Merge branch 'main' into unusued-variables
| -rw-r--r-- | benches/sylt_benchmark.rs | 56 | ||||
| -rw-r--r-- | progs/bench/fib.sy | 10 | ||||
| -rw-r--r-- | progs/bench/fib_iter.sy | 14 | ||||
| -rw-r--r-- | progs/bench/sum.sy | 6 | ||||
| -rw-r--r-- | src/error.rs | 59 | ||||
| -rw-r--r-- | src/lib.rs | 29 | ||||
| -rw-r--r-- | src/vm.rs | 97 |
7 files changed, 170 insertions, 101 deletions
diff --git a/benches/sylt_benchmark.rs b/benches/sylt_benchmark.rs index a68a762..e77a916 100644 --- a/benches/sylt_benchmark.rs +++ b/benches/sylt_benchmark.rs @@ -1,42 +1,30 @@ use criterion::{criterion_group, criterion_main, Criterion}; +use std::fs; use std::path::Path; -pub fn fib_50(c: &mut Criterion) { - let prog = -" -j := 0 -for , j < 1000, j = j + 1 { - a := 0 - b := 1 - - for i := 0, i < 50, i = i + 1 { - c := a - a = b - b = c + b - } - a <=> 12586269025 -} -"; - let compiled = sylt::compiler::compile("main", Path::new("prog"), sylt::tokenizer::string_to_tokens(prog)).unwrap(); - c.bench_function("fib 50", |b| b.iter(|| sylt::vm::run_block(&compiled).unwrap())); +macro_rules! bench_file { + ( $name:ident ) => { + pub fn $name(c: &mut Criterion) { + let prog = fs::read_to_string(Path::new(&format!("progs/bench/{}.sy", stringify!($name)))) + .unwrap(); + c.bench_function(stringify!($name), |b| { + b.iter(|| { + sylt::run_string(&prog, false, Vec::new()).unwrap(); + }) + }); + } + }; } -pub fn fib_90(c: &mut Criterion) { - let prog = -" -a := 0 -b := 1 +macro_rules! bench { + ( [ $( $name:ident ),* ] ) => { + $(bench_file!($name);)* -for i := 0, i < 90, i = i + 1 { - c := a - a = b - b = c + b -} -a <=> 2880067194370816120 -"; - let compiled = sylt::compiler::compile("main", Path::new("prog"), sylt::tokenizer::string_to_tokens(prog)).unwrap(); - c.bench_function("fib 90", |b| b.iter(|| sylt::vm::run_block(&compiled).unwrap())); + criterion_group!(benches, $( $name ),* ); + criterion_main!(benches); + } } -criterion_group!(benches, fib_50, fib_90); -criterion_main!(benches); +// List of all benchmarks to run +bench!([fib, fib_iter, sum]); + diff --git a/progs/bench/fib.sy b/progs/bench/fib.sy new file mode 100644 index 0000000..de68f5c --- /dev/null +++ b/progs/bench/fib.sy @@ -0,0 +1,10 @@ +// The worst implementation of Fibonacci calculations +// possible. FYI, it can be done in constant time. +fib :: fn a:int -> int { + if a < 2 { + ret a + } + ret fib(a - 1) + fib(a - 2) +} +// 23 is around where things start getting slow. +fib(23) <=> 28657 diff --git a/progs/bench/fib_iter.sy b/progs/bench/fib_iter.sy new file mode 100644 index 0000000..c51469a --- /dev/null +++ b/progs/bench/fib_iter.sy @@ -0,0 +1,14 @@ +// A Fibonacci implementation that is a little +// less awful. But we run it 1000 times instead. +j := 0 +for , j < 1000, j = j + 1 { + a := 0 + b := 1 + + for i := 0, i < 50, i = i + 1 { + c := a + a = b + b = c + b + } + a <=> 12586269025 +} diff --git a/progs/bench/sum.sy b/progs/bench/sum.sy new file mode 100644 index 0000000..bb6870f --- /dev/null +++ b/progs/bench/sum.sy @@ -0,0 +1,6 @@ +// Adds the numbers 0 to 10000 +sum := 0 +for i := 0, i <= 100000, i += 1 { + sum += i +} +sum <=> 5000050000 diff --git a/src/error.rs b/src/error.rs index deab89e..c2ad228 100644 --- a/src/error.rs +++ b/src/error.rs @@ -12,9 +12,16 @@ use crate::tokenizer::Token; #[derive(Debug, Clone)] pub enum ErrorKind { TypeError(Op, Vec<Type>), + TypeMismatch(Type, Type), + CannotInfer(Type, Type), + ArgumentType(Vec<Type>, Vec<Type>), + IndexError(Value, Type), + /// (External function, parameters) ExternTypeMismatch(String, Vec<Type>), - RuntimeTypeError(Op, Vec<Value>), + ValueError(Op, Vec<Value>), + UnknownField(Value, String), + ArgumentCount(usize, usize), /// (Indexed value, length, index) IndexOutOfBounds(Value, usize, usize), @@ -42,40 +49,69 @@ impl fmt::Display for ErrorKind { let types = types .iter() .fold(String::new(), |a, v| { format!("{}{:?}, ", a, v) }); - write!(f, "{} Cannot apply {:?} to types {}", "Type Error".bold(), op, types) + write!(f, "Cannot apply {:?} to types {}", op, types) + } + ErrorKind::TypeMismatch(a, b) => { + write!(f, "Expected '{:?}' and got '{:?}'.", a, b) + } + ErrorKind::CannotInfer(a, b) => { + write!(f, "Failed to infer type '{:?}' from '{:?}'.", a, b) + } + ErrorKind::ArgumentType(a, b) => { + let expected = a + .iter() + .fold(String::new(), |a, v| { format!("{}{:?}, ", a, v) }); + let given = b + .iter() + .fold(String::new(), |a, v| { format!("{}{:?}, ", a, v) }); + write!(f, "Argument types do not match, expected [{:?}] but got [{:?}]", + expected, given) } ErrorKind::IndexOutOfBounds(value, len, slot) => { - write!(f, "{} for {:?} - length is {} but index is {}", "Index Error".bold(), value, len, slot) + write!(f, "Failed to index for {:?} - length is {} but index is {}", + value, len, slot) } ErrorKind::ExternTypeMismatch(name, types) => { - write!(f, "{} Extern function '{}' doesn't accept argument(s) with type(s) {:?}", "Type Error".bold(), name, types) + write!(f, "Extern function '{}' doesn't accept argument(s) with type(s) {:?}", + name, types) } - ErrorKind::RuntimeTypeError(op, values) => { + ErrorKind::ValueError(op, values) => { let values = values .iter() .fold(String::new(), |a, v| { format!("{}{:?}, ", a, v) }); - write!(f, "{} Cannot apply {:?} to values {}", "Runtime Type Error".bold(), op, values) + write!(f, "Cannot apply {:?} to values {}", op, values) } ErrorKind::AssertFailed => { - write!(f, "{}", "Assertion failed".bold()) + write!(f, "Assertion failed") } ErrorKind::SyntaxError(line, token) => { - write!(f, "{} on line {} at token {:?}", "Syntax Error".bold(), line, token) + write!(f, "Syntax Error on line {} at token {:?}", line, token) } ErrorKind::Unreachable => { - write!(f, "{}", "Unreachable".bold()) + write!(f, "Reached unreachable code.") } ErrorKind::InvalidProgram => { write!(f, "{}", "[!!] Invalid program [!!]".bold()) } + ErrorKind::IndexError(value, slot) => { + write!(f, "Cannot index value '{:?}' with type '{:?}'.", value, slot) + } + ErrorKind::UnknownField(obj, field) => { + write!(f, "Cannot find field '{}' on {:?}", field, obj) + } + ErrorKind::ArgumentCount(expected, given) => { + write!(f, "Incorrect argument count, expected {} but got {}.", + expected, given) + } } } } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let prompt = "*****".red(); let message = match &self.message { - Some(s) => format!("\n{} {}", ">>>".red(), s), + Some(s) => format!("\n{} {}", prompt, s), None => String::from(""), }; @@ -87,7 +123,8 @@ impl fmt::Display for Error { String::new() }; - write!(f, "\n<{}> {}:{} {}{}{}\n", "ERR".red(), self.file.display().blue(), self.line.blue(), self.kind, message, line) + write!(f, "\n {} {}:{} \n{} {}{}{}\n", "ERR".red(), + self.file.display().blue(), self.line.blue(), prompt, self.kind, message, line) } } @@ -771,9 +771,14 @@ mod tests { #[macro_export] macro_rules! assert_errs { ($result:expr, [ $( $kind:pat ),* ]) => { - eprintln!("{} => {:?}", stringify!($result), $result); - assert!(matches!( - $result.unwrap_err().as_slice(), + let errs = if let Err(errs) = $result { + errs + } else { + eprintln!(" Program succeeded when it should've failed"); + unreachable!(); + }; + if !matches!( + errs.as_slice(), &[$($crate::error::Error { kind: $kind, file: _, @@ -781,7 +786,19 @@ mod tests { message: _, }, )*] - )) + ) { + eprintln!("Unexpected errors"); + eprint!(" Got: ["); + for err in errs { + eprint!(" ErrorKind::{:?} ", err.kind); + } + eprint!("]\n Want: ["); + $( + eprint!(" {} ", stringify!($kind)); + )* + eprintln!("]"); + assert!(false); + } }; } @@ -831,8 +848,8 @@ mod tests { for e in errs.iter() { println!("{}", e); } - println!(" {} - FAILED\n", stringify!($fn)); - panic!(); + eprintln!(" {} - failed\n", stringify!($fn)); + unreachable!(); } } }); @@ -16,7 +16,7 @@ macro_rules! error { return Err($thing.error($kind, None)); }; ( $thing:expr, $kind:expr, $msg:expr) => { - return Err($thing.error($kind, Some($msg))); + return Err($thing.error($kind, Some(String::from($msg)))); }; } @@ -26,7 +26,7 @@ macro_rules! one_op { let b = $fun(&a); if b.is_nil() { $self.push(b); - error!($self, ErrorKind::RuntimeTypeError($op, vec![a])); + error!($self, ErrorKind::TypeError($op, vec![a.into()])); } $self.push(b); }; @@ -38,7 +38,7 @@ macro_rules! two_op { let c = $fun(&a, &b); if c.is_nil() { $self.push(c); - error!($self, ErrorKind::RuntimeTypeError($op, vec![a, b])); + error!($self, ErrorKind::TypeError($op, vec![a.into(), b.into()])); } $self.push(c); }; @@ -280,7 +280,7 @@ impl VM { Value::Function(ups, block) }, value => error!(self, - ErrorKind::RuntimeTypeError(op, vec![value.clone()]), + ErrorKind::ValueError(op, vec![value.clone()]), format!("Not a function {:?}.", value)), }; self.constants[slot] = constant; @@ -301,7 +301,7 @@ impl VM { } (val, slot) => { self.stack.push(Value::Nil); - error!(self, ErrorKind::RuntimeTypeError(op, vec![val, slot]), String::from("Cannot index type")); + error!(self, ErrorKind::IndexError(val, slot.into())); } } } @@ -313,7 +313,7 @@ impl VM { let slot = ty.fields.get(field).unwrap().0; self.push(values.borrow()[slot].clone()); } else { - error!(self, ErrorKind::RuntimeTypeError(op, vec![inst])); + error!(self, ErrorKind::UnknownField(inst, field.clone())); } } @@ -324,7 +324,7 @@ impl VM { let slot = ty.fields.get(field).unwrap().0; values.borrow_mut()[slot] = value; } else { - error!(self, ErrorKind::RuntimeTypeError(op, vec![inst])); + error!(self, ErrorKind::UnknownField(inst, field.clone())); } } @@ -432,10 +432,7 @@ impl VM { let inner = block.borrow(); let args = inner.args(); if args.len() != num_args { - error!(self, - ErrorKind::InvalidProgram, - format!("Invalid number of arguments, got {} expected {}.", - num_args, args.len())); + error!(self, ErrorKind::ArgumentCount(args.len(), num_args)); } if self.print_blocks { @@ -452,7 +449,7 @@ impl VM { let extern_func = self.extern_functions[slot]; let res = match extern_func(&self.stack[new_base+1..], false) { Ok(value) => value, - Err(ek) => error!(self, ek, "Wrong arguments to external function".to_string()), + Err(ek) => error!(self, ek, "Failed in external function."), }; self.stack.truncate(new_base); self.push(res); @@ -504,8 +501,8 @@ impl VM { self.frame().block.borrow().ops[self.frame().ip]); } - // Initalizes the VM for running. Run cannot be called before this. - pub(crate) fn init(&mut self, prog: &Prog) { + #[doc(hidden)] + pub fn init(&mut self, prog: &Prog) { let block = Rc::clone(&prog.blocks[0]); self.constants = prog.constants.clone(); self.strings = prog.strings.clone(); @@ -582,10 +579,7 @@ impl VM { *ty = suggestion.clone(); } else { if ty != suggestion { - error!(self, - ErrorKind::TypeError(op, - vec![ty.clone(), suggestion.clone()]), - "Failed to infer type.".to_string()); + error!(self, ErrorKind::CannotInfer(ty.clone(), suggestion.clone())); } } }; @@ -603,8 +597,9 @@ impl VM { let value = Value::from(ty.fields.get(field).unwrap().1.clone()); self.push(value); } else { + let field = field.clone(); self.push(Value::Nil); - error!(self, ErrorKind::RuntimeTypeError(op, vec![inst])); + error!(self, ErrorKind::UnknownField(inst, field)); } } @@ -614,11 +609,13 @@ impl VM { if let Value::Instance(ty, _) = inst { let ty = &ty.fields.get(field).unwrap().1; - if ty != &Type::from(&value) { - error!(self, ErrorKind::RuntimeTypeError(op, vec![Value::from(ty)])); + let expected = Type::from(&value); + if ty != &expected { + error!(self, ErrorKind::TypeMismatch(expected, ty.clone()), + "Types of field and variable do not match."); } } else { - error!(self, ErrorKind::RuntimeTypeError(op, vec![inst])); + error!(self, ErrorKind::UnknownField(inst, field.clone())); } } @@ -635,8 +632,18 @@ impl VM { let var = self.frame().block.borrow().upvalues[slot].2.clone(); let up = self.pop().into(); if var != up { - error!(self, ErrorKind::TypeError(op, vec![var, up]), - "Incorrect type for upvalue.".to_string()); + error!(self, ErrorKind::TypeMismatch(up, var), + "Captured varibles type doesn't match upvalue."); + } + } + + Op::AssignLocal(slot) => { + let slot = self.frame().stack_offset + slot; + let curr = Type::from(&self.stack[slot]); + let other = Type::from(self.pop()); + if curr != other { + error!(self, ErrorKind::TypeMismatch(curr, other), + "Cannot assign to different type."); } } @@ -645,9 +652,8 @@ impl VM { let inner = self.frame().block.borrow(); let ret = inner.ret(); if Type::from(&a) != *ret { - error!(self, ErrorKind::TypeError(op, vec![a.into(), - ret.clone()]), - "Not matching return type.".to_string()); + error!(self, ErrorKind::TypeMismatch(a.into(), ret.clone()), + "Value does not match return type."); } } @@ -662,12 +668,8 @@ impl VM { (Type::Unknown, top_type) if top_type != Type::Unknown => {} (a, b) if a != &b => { - error!(self, - ErrorKind::TypeError( - op, - vec![a.clone(), b.clone()]), - format!("Tried to assign a type {:?} to type {:?}.", a, b) - ); + error!(self, ErrorKind::TypeMismatch(a.clone(), b.clone()), + "Cannot assign mismatching types."); } _ => {} } @@ -706,19 +708,13 @@ impl VM { let inner = block.borrow(); let args = inner.args(); if args.len() != num_args { - error!(self, - ErrorKind::InvalidProgram, - format!("Invalid number of arguments, got {} expected {}.", - num_args, args.len())); + error!(self, ErrorKind::ArgumentCount(args.len(), num_args)); } let stack_args = &self.stack[self.stack.len() - args.len()..]; let stack_args: Vec<_> = stack_args.iter().map(|x| x.into()).collect(); if args != &stack_args { - error!(self, - ErrorKind::TypeError(op, vec![]), - format!("Expected args of type {:?} but got {:?}.", - args, stack_args)); + error!(self, ErrorKind::ArgumentType(args.clone(), stack_args)); } self.stack[new_base] = block.borrow().ret().into(); @@ -732,7 +728,7 @@ impl VM { Err(ek) => { self.stack.truncate(new_base); self.push(Value::Nil); - error!(self, ek, "Wrong arguments to external function".to_string()) + error!(self, ek, "Error from external function.") } }; self.stack.truncate(new_base); @@ -833,23 +829,24 @@ impl VM { mod tests { mod typing { use crate::error::ErrorKind; - use crate::test_string; + use crate::{test_string, Op, Type}; test_string!(uncallable_type, " f := fn i: int { i() } f", - [ErrorKind::InvalidProgram]); + [ErrorKind::ValueError(Op::Call(0), _)]); + + test_string!(invalid_assign, "a := 1\na = 0.1\na", + [ErrorKind::TypeMismatch(Type::Int, Type::Float)]); test_string!(wrong_params, " - f : fn -> int = fn a: int -> int {} - f", - [ErrorKind::TypeError(_, _), ErrorKind::TypeError(_, _)]); + f : fn -> int = fn a: int -> int {}\nf", + [ErrorKind::TypeMismatch(_, _), ErrorKind::TypeMismatch(Type::Void, Type::Int)]); test_string!(wrong_ret, " - f : fn -> int = fn {} - f", - [ErrorKind::TypeError(_, _)]); + f : fn -> int = fn {}\nf", + [ErrorKind::TypeMismatch(_, _)]); } } |
