diff options
| author | Edvard Thörnros <edvard.thornros@gmail.com> | 2021-02-09 17:46:00 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-02-09 17:46:00 +0100 |
| commit | be4559ed0af1d12401365b5ffeae99885842314f (patch) | |
| tree | fa7059306b50499494c0b9b22bd9f980f30d7574 /src | |
| parent | a41d008487c26dae5dcdf6e0be9323ad2e06c827 (diff) | |
| parent | e37fc83ce9705eb8af2c7b7bfb927b38cf382726 (diff) | |
| download | sylt-be4559ed0af1d12401365b5ffeae99885842314f.tar.gz | |
Merge pull request #56 from FredTheDino/parse-branch-macro
parse branch macro
Diffstat (limited to 'src')
| -rw-r--r-- | src/compiler.rs | 109 | ||||
| -rw-r--r-- | src/lib.rs | 41 |
2 files changed, 122 insertions, 28 deletions
diff --git a/src/compiler.rs b/src/compiler.rs index 193d16c..7f0d32d 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -39,6 +39,71 @@ 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(); + + // 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; + } + $( + $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 + } + + }; + + ($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(); + // 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 { + 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 + })() + } + }; +} + nextable_enum!(Prec { No, Assert, @@ -366,16 +431,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; @@ -393,18 +452,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) { @@ -948,7 +1010,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!(), @@ -967,7 +1029,7 @@ impl Compiler { String::from(field) } else { error!(self, "Expected fieldname after '.'."); - return Err(()); + return; }; let field = self.intern_string(field); @@ -976,7 +1038,7 @@ impl Compiler { self.eat(); self.expression(block); add_op(self, block, Op::Set(field)); - return Ok(()); + return; } Token::PlusEqual => Op::Add, @@ -995,21 +1057,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; } } @@ -1034,14 +1098,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, ..) => { @@ -736,18 +736,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<T, F>(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() { - $crate::run_string($prog, true, Vec::new()).unwrap(); + 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!(); + } + } + }); } }; ($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); + }) } } } |
