From 98adb0fd825aa3aca1bf531a0013227b56b5290c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvard=20Th=C3=B6rnros?= Date: Sat, 6 Feb 2021 12:26:33 +0100 Subject: print the pretty errors on failed tests --- src/lib.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index cca1a58..a377b29 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -734,7 +734,16 @@ mod tests { ($fn:ident, $prog:literal) => { #[test] fn $fn() { - $crate::run_string($prog, true, Vec::new()).unwrap(); + match $crate::run_string($prog, true, Vec::new()) { + Ok(()) => {}, + Err(errs) => { + for e in errs.iter() { + println!("{}", e); + } + println!(" {} - FAILED\n", stringify!($fn)); + unreachable!(); + } + } } }; ($fn:ident, $prog:literal, $errs:tt) => { -- cgit v1.2.1 From 6dfe62f7c305588023e74d6077f629a076c4769b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvard=20Th=C3=B6rnros?= Date: Sat, 6 Feb 2021 12:26:59 +0100 Subject: macro for trying to parse multiple things --- src/compiler.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/compiler.rs b/src/compiler.rs index d12ee4b..bd23cd8 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -39,6 +39,37 @@ macro_rules! expect { }; } +macro_rules! parse_branch { + ($compiler:expr, $block:expr, [ $( $call:expr ),* ]) => { + let block_length = $block.ops.len(); + let token_length = $compiler.curr; + let num_errors = $compiler.errors.len(); + let mut stored_errors = Vec::new(); + let mut success = false; + // We risk getting a lot of errors if we are in an invalid state + // when we start the parse. + while !$compiler.panic { + $( + $call; + if !$compiler.panic { + success = true; + break; + } + $compiler.panic = false; + $compiler.curr = token_length; + let thrown_errors = $compiler.errors.len() - num_errors - 1; + stored_errors.extend($compiler.errors.split_off(thrown_errors)); + $block.ops.truncate(block_length); + )* + break; + } + + if !success { + $compiler.errors.extend(stored_errors); + } + }; +} + nextable_enum!(Prec { No, Assert, -- cgit v1.2.1 From c3b655bad99c1c4110d2af18b5b5845b4b2a65df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvard=20Th=C3=B6rnros?= Date: Sat, 6 Feb 2021 12:27:34 +0100 Subject: use fancy macro in parse branches --- src/compiler.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/compiler.rs b/src/compiler.rs index bd23cd8..914f720 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -371,16 +371,10 @@ impl Compiler { } fn grouping_or_tuple(&mut self, block: &mut Block) { - let block_length = block.ops.len(); - let token_length = self.curr; - if self.try_tuple(block).is_err() { - block.ops.truncate(block_length); - self.curr = token_length; - self.grouping(block); - } + parse_branch!(self, block, [self.tuple(block), self.grouping(block)]); } - fn try_tuple(&mut self, block: &mut Block) -> Result<(), ()> { + fn tuple(&mut self, block: &mut Block) { expect!(self, Token::LeftParen, "Expected '(' at start of tuple"); let mut num_args = 0; @@ -398,18 +392,21 @@ impl Compiler { match self.peek() { Token::Comma => { self.eat(); }, Token::RightParen => {}, - _ => { return Err(()); }, + _ => { + error!(self, "Expected ',' or ')' in tuple"); + return; + }, } } } } if num_args == 1 { - return Err(()); + error!(self, "A tuple must contain more than 1 element."); + return; } expect!(self, Token::RightParen, "Expected ')' after tuple."); add_op(self, block, Op::Tuple(num_args)); - Ok(()) } fn grouping(&mut self, block: &mut Block) { -- cgit v1.2.1 From cf087b75ff3c2e872aed34c11a1f859ba1fbc64f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvard=20Th=C3=B6rnros?= Date: Sat, 6 Feb 2021 12:32:30 +0100 Subject: try_blob_field -> blob_field working like you expect --- src/compiler.rs | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/compiler.rs b/src/compiler.rs index 914f720..abe3a5b 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -946,7 +946,7 @@ impl Compiler { self.blobs.push(blob); } - fn try_blob_field(&mut self, block: &mut Block) -> Result<(), ()> { + fn blob_field(&mut self, block: &mut Block) { let name = match self.eat() { Token::Identifier(name) => name, _ => unreachable!(), @@ -965,7 +965,7 @@ impl Compiler { String::from(field) } else { error!(self, "Expected fieldname after '.'."); - return Err(()); + return; }; let op = match self.peek() { @@ -973,7 +973,7 @@ impl Compiler { self.eat(); self.expression(block); add_op(self, block, Op::Set(field)); - return Ok(()); + return; } Token::PlusEqual => Op::Add, @@ -992,21 +992,23 @@ impl Compiler { self.expression(block); add_op(self, block, op); add_op(self, block, Op::Set(field)); - return Ok(()); + return; } Token::LeftParen => { self.call(block); } Token::Newline => { - return Ok(()); + return; } _ => { - return Err(()); + error!(self, "Unexpected token when parsing blob-field."); + return; } } } } else { - Err(()) + error!(self, format!("Cannot find variable '{}'.", name)); + return; } } @@ -1031,14 +1033,7 @@ impl Compiler { } (Token::Identifier(_), Token::Dot, ..) => { - let block_length = block.ops.len(); - let token_length = self.curr; - // reset block and token stream if blob field fails - if self.try_blob_field(block).is_err() { - block.ops.truncate(block_length); - self.curr = token_length; - self.expression(block); - } + parse_branch!(self, block, [self.blob_field(block), self.expression(block)]); } (Token::Identifier(name), Token::Colon, ..) => { -- cgit v1.2.1 From cd074fb797521443415464e90fc9fc6fad0353ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvard=20Th=C3=B6rnros?= Date: Mon, 8 Feb 2021 23:02:55 +0100 Subject: add parse_branch rule for use with flow control --- src/compiler.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/compiler.rs b/src/compiler.rs index abe3a5b..2ca6159 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -68,6 +68,32 @@ macro_rules! parse_branch { $compiler.errors.extend(stored_errors); } }; + + ($compiler:expr, $block:expr, $call:expr) => { + { + let block_length = $block.ops.len(); + let token_length = $compiler.curr; + let num_errors = $compiler.errors.len(); + let mut stored_errors = Vec::new(); + let mut success = false; + // We risk getting a lot of errors if we are in an invalid state + // when we start the parse. + while !$compiler.panic { + $call; + if !$compiler.panic { + success = true; + break; + } + $compiler.panic = false; + $compiler.curr = token_length; + let thrown_errors = $compiler.errors.len() - num_errors - 1; + stored_errors.extend($compiler.errors.split_off(thrown_errors)); + $block.ops.truncate(block_length); + break; + } + success + } + }; } nextable_enum!(Prec { -- cgit v1.2.1 From aa6db87e9ffed8d1fe57c4bb980382b40b0b81f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvard=20Th=C3=B6rnros?= Date: Mon, 8 Feb 2021 23:05:50 +0100 Subject: unreachable -> panic --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index a377b29..eaa2399 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -741,7 +741,7 @@ mod tests { println!("{}", e); } println!(" {} - FAILED\n", stringify!($fn)); - unreachable!(); + panic!(); } } } -- cgit v1.2.1 From 1850c3a62898519ddd5937c0b29fbc6b2b4920c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvard=20Th=C3=B6rnros?= Date: Tue, 9 Feb 2021 00:01:12 +0100 Subject: test timeout for those pesky infinet loops --- src/lib.rs | 46 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index eaa2399..ee5dae8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -729,27 +729,55 @@ mod tests { }; } + use std::time::Duration; + use std::sync::mpsc; + use std::thread; + + // Shamelessly stolen from https://github.com/rust-lang/rfcs/issues/2798 + pub fn panic_after(d: Duration, f: F) -> T + where + T: Send + 'static, + F: FnOnce() -> T, + F: Send + 'static, + { + let (done_tx, done_rx) = mpsc::channel(); + let handle = thread::spawn(move || { + let val = f(); + done_tx.send(()).expect("Unable to send completion signal"); + val + }); + + match done_rx.recv_timeout(d) { + Ok(_) => handle.join().expect("Thread panicked"), + Err(_) => panic!("Thread took too long"), + } + } + #[macro_export] macro_rules! test_string { ($fn:ident, $prog:literal) => { #[test] fn $fn() { - match $crate::run_string($prog, true, Vec::new()) { - Ok(()) => {}, - Err(errs) => { - for e in errs.iter() { - println!("{}", e); + crate::tests::panic_after(std::time::Duration::from_millis(500), || { + match $crate::run_string($prog, true, Vec::new()) { + Ok(()) => {}, + Err(errs) => { + for e in errs.iter() { + println!("{}", e); + } + println!(" {} - FAILED\n", stringify!($fn)); + panic!(); } - println!(" {} - FAILED\n", stringify!($fn)); - panic!(); } - } + }); } }; ($fn:ident, $prog:literal, $errs:tt) => { #[test] fn $fn() { - $crate::assert_errs!($crate::run_string($prog, true, Vec::new()), $errs); + crate::tests::panic_after(std::time::Duration::from_millis(500), || { + $crate::assert_errs!($crate::run_string($prog, true, Vec::new()), $errs); + }) } } } -- cgit v1.2.1 From e37fc83ce9705eb8af2c7b7bfb927b38cf382726 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvard=20Th=C3=B6rnros?= Date: Tue, 9 Feb 2021 17:29:59 +0100 Subject: simplify the parse_branch --- src/compiler.rs | 72 ++++++++++++++++++++++++++++++++------------------------- 1 file changed, 40 insertions(+), 32 deletions(-) diff --git a/src/compiler.rs b/src/compiler.rs index 2ca6159..3a64ac6 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -41,32 +41,39 @@ macro_rules! expect { macro_rules! parse_branch { ($compiler:expr, $block:expr, [ $( $call:expr ),* ]) => { - let block_length = $block.ops.len(); - let token_length = $compiler.curr; - let num_errors = $compiler.errors.len(); - let mut stored_errors = Vec::new(); - let mut success = false; - // We risk getting a lot of errors if we are in an invalid state - // when we start the parse. - while !$compiler.panic { - $( - $call; - if !$compiler.panic { - success = true; - break; + { + let block_length = $block.ops.len(); + let token_length = $compiler.curr; + let num_errors = $compiler.errors.len(); + let mut stored_errors = Vec::new(); + + // Closures for early return on success. + let success = (|| { + // We risk getting a lot of errors if we are in an invalid state + // when we start the parse. + if $compiler.panic { + return false; } - $compiler.panic = false; - $compiler.curr = token_length; - let thrown_errors = $compiler.errors.len() - num_errors - 1; - stored_errors.extend($compiler.errors.split_off(thrown_errors)); - $block.ops.truncate(block_length); - )* - break; + $( + $call; + if !$compiler.panic { + return true; + } + $compiler.panic = false; + $compiler.curr = token_length; + let thrown_errors = $compiler.errors.len() - num_errors - 1; + stored_errors.extend($compiler.errors.split_off(thrown_errors)); + $block.ops.truncate(block_length); + )* + false + })(); + + if !success { + $compiler.errors.extend(stored_errors); + } + success } - if !success { - $compiler.errors.extend(stored_errors); - } }; ($compiler:expr, $block:expr, $call:expr) => { @@ -75,23 +82,24 @@ macro_rules! parse_branch { let token_length = $compiler.curr; let num_errors = $compiler.errors.len(); let mut stored_errors = Vec::new(); - let mut success = false; - // We risk getting a lot of errors if we are in an invalid state - // when we start the parse. - while !$compiler.panic { + // Closures for early return on success. + (|| { + // We risk getting a lot of errors if we are in an invalid state + // when we start the parse. + if $compiler.panic { + return false; + } $call; if !$compiler.panic { - success = true; - break; + return true; } $compiler.panic = false; $compiler.curr = token_length; let thrown_errors = $compiler.errors.len() - num_errors - 1; stored_errors.extend($compiler.errors.split_off(thrown_errors)); $block.ops.truncate(block_length); - break; - } - success + false + })() } }; } -- cgit v1.2.1