From 29be90bf95f2ea10c435e7b02f8c26626b956417 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Sun, 17 Jan 2021 13:53:22 +0100 Subject: =?UTF-8?q?Assertions=20with=20[eq]=20+=20better=20tests=20?= =?UTF-8?q?=F0=9F=A9=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/eval/call.rs | 28 ++++++++++++++---- src/parse/mod.rs | 10 +++---- src/parse/parser.rs | 85 +++++++++++++++++++++++++++-------------------------- 3 files changed, 72 insertions(+), 51 deletions(-) (limited to 'src') diff --git a/src/eval/call.rs b/src/eval/call.rs index 8e75f17c..5b0628a8 100644 --- a/src/eval/call.rs +++ b/src/eval/call.rs @@ -73,7 +73,7 @@ impl Args { where T: Cast>, { - self.pos.iter_mut().find_map(move |slot| try_cast(ctx, slot)) + (0 .. self.pos.len()).find_map(move |i| try_cast(ctx, &mut self.pos, i)) } /// Find and remove the first convertible positional argument, producing an @@ -97,7 +97,16 @@ impl Args { where T: Cast>, { - self.pos.iter_mut().filter_map(move |slot| try_cast(ctx, slot)) + let mut i = 0; + std::iter::from_fn(move || { + while i < self.pos.len() { + if let Some(val) = try_cast(ctx, &mut self.pos, i) { + return Some(val); + } + i += 1; + } + None + }) } /// Convert and remove the value for the given named argument, producing an @@ -163,17 +172,26 @@ where /// Try to cast the value in the slot into `T`, putting it back if the /// conversion fails. -fn try_cast(ctx: &mut EvalContext, slot: &mut Spanned) -> Option +fn try_cast( + ctx: &mut EvalContext, + vec: &mut Vec>, + i: usize, +) -> Option where T: Cast>, { // Replace with error placeholder when conversion works since error values // are ignored when generating "unexpected argument" errors. - let value = std::mem::replace(slot, Spanned::zero(Value::Error)); + let slot = &mut vec[i]; + let value = std::mem::replace(slot, Spanned::zero(Value::None)); let span = value.span; match T::cast(value) { - CastResult::Ok(t) => Some(t), + CastResult::Ok(t) => { + vec.remove(i); + Some(t) + } CastResult::Warn(t, m) => { + vec.remove(i); ctx.diag(warning!(span, "{}", m)); Some(t) } diff --git a/src/parse/mod.rs b/src/parse/mod.rs index b6836d38..7c92185d 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -117,7 +117,7 @@ fn heading(p: &mut Parser) -> NodeHeading { // Parse the heading contents. let mut contents = vec![]; - while p.check(|t| !matches!(t, Token::Space(n) if n > 0)) { + while p.check(|t| !matches!(t, Token::Space(n) if n >= 1)) { if let Some(node) = p.span_if(|p| node(p, &mut false)) { contents.push(node); } @@ -388,8 +388,8 @@ fn string(p: &mut Parser, token: TokenStr) -> String { /// Parse a let expresion. fn expr_let(p: &mut Parser) -> Option { p.push_mode(TokenMode::Code); - p.start_group(Group::Terminated); p.eat_assert(Token::Let); + p.start_group(Group::Expr); let pat = p.span_if(ident); let mut rhs = None; @@ -404,11 +404,11 @@ fn expr_let(p: &mut Parser) -> Option { p.diag_expected("identifier"); } - while !p.eof() { - p.diag_unexpected(); + p.pop_mode(); + if !p.eof() { + p.diag_expected("semicolon or line break"); } - p.pop_mode(); p.end_group(); pat.map(|pat| Expr::Let(ExprLet { pat, expr: rhs })) } diff --git a/src/parse/parser.rs b/src/parse/parser.rs index 2b5fe720..8c27c8f7 100644 --- a/src/parse/parser.rs +++ b/src/parse/parser.rs @@ -112,16 +112,14 @@ impl<'s> Parser<'s> { /// # Panics /// This panics if the next token does not start the given group. pub fn start_group(&mut self, group: Group) { + self.groups.push(group); match group { Group::Paren => self.eat_assert(Token::LeftParen), Group::Bracket => self.eat_assert(Token::LeftBracket), Group::Brace => self.eat_assert(Token::LeftBrace), - Group::Subheader => {} - Group::Terminated => {} + Group::Expr => self.repeek(), + Group::Subheader => self.repeek(), } - - self.groups.push(group); - self.repeek(); } /// Ends the parsing of a group and returns the span of the whole group. @@ -129,26 +127,21 @@ impl<'s> Parser<'s> { /// # Panics /// This panics if no group was started. pub fn end_group(&mut self) { - // Check that we are indeed at the end of the group. - debug_assert_eq!(self.peek(), None, "unfinished group"); - let group = self.groups.pop().expect("no started group"); self.repeek(); - let end = match group { - Group::Paren => Some(Token::RightParen), - Group::Bracket => Some(Token::RightBracket), - Group::Brace => Some(Token::RightBrace), - Group::Subheader => None, - Group::Terminated => Some(Token::Semicolon), + let (end, required) = match group { + Group::Paren => (Token::RightParen, true), + Group::Bracket => (Token::RightBracket, true), + Group::Brace => (Token::RightBrace, true), + Group::Expr => (Token::Semicolon, false), + Group::Subheader => return, }; - if let Some(token) = end { - if self.next == Some(token) { - self.bump(); - } else { - self.diag(error!(self.next_start, "expected {}", token.name())); - } + if self.next == Some(end) { + self.bump(); + } else if required { + self.diag(error!(self.next_start, "expected {}", end.name())); } } @@ -169,7 +162,7 @@ impl<'s> Parser<'s> { where F: FnOnce(&mut Self) -> Option, { - self.span(|p| f(p)).transpose() + self.span(f).transpose() } /// Consume the next token. @@ -269,17 +262,21 @@ impl<'s> Parser<'s> { match self.tokens.mode() { TokenMode::Markup => {} - TokenMode::Code => { - while matches!( - self.next, - Some(Token::Space(_)) | - Some(Token::LineComment(_)) | - Some(Token::BlockComment(_)) - ) { - self.next_start = self.tokens.pos(); - self.next = self.tokens.next(); + TokenMode::Code => loop { + match self.next { + Some(Token::Space(n)) => { + if n >= 1 && self.groups.last() == Some(&Group::Expr) { + break; + } + } + Some(Token::LineComment(_)) => {} + Some(Token::BlockComment(_)) => {} + _ => break, } - } + + self.next_start = self.tokens.pos(); + self.next = self.tokens.next(); + }, } self.repeek(); @@ -287,16 +284,22 @@ impl<'s> Parser<'s> { fn repeek(&mut self) { self.peeked = self.next; - if self.groups.contains(&match self.next { - Some(Token::RightParen) => Group::Paren, - Some(Token::RightBracket) => Group::Bracket, - Some(Token::RightBrace) => Group::Brace, - Some(Token::Pipe) => Group::Subheader, - Some(Token::Semicolon) => Group::Terminated, + let token = match self.next { + Some(token) => token, + None => return, + }; + + match token { + Token::RightParen if self.groups.contains(&Group::Paren) => {} + Token::RightBracket if self.groups.contains(&Group::Bracket) => {} + Token::RightBrace if self.groups.contains(&Group::Brace) => {} + Token::Semicolon if self.groups.contains(&Group::Expr) => {} + Token::Space(n) if n >= 1 && self.groups.last() == Some(&Group::Expr) => {} + Token::Pipe if self.groups.contains(&Group::Subheader) => {} _ => return, - }) { - self.peeked = None; } + + self.peeked = None; } } @@ -316,9 +319,9 @@ pub enum Group { Bracket, /// A curly-braced group: `{...}`. Brace, + /// A group ended by a semicolon or a line break: `;`, `\n`. + Expr, /// A group ended by a chained subheader or a closing bracket: /// `... >>`, `...]`. Subheader, - /// A group ended by a semicolon: `;`. - Terminated, } -- cgit v1.2.3