From 3b74e780ecc2f5cd41b10460600317fecb9bc8e8 Mon Sep 17 00:00:00 2001 From: philippeitis <33013301+philippeitis@users.noreply.github.com> Date: Wed, 26 Feb 2020 21:07:46 -0800 Subject: [PATCH 1/3] Add ExpectedRbrace error, fix error messages. --- parser/src/error.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/parser/src/error.rs b/parser/src/error.rs index 6afc4627b..6a8c0340c 100644 --- a/parser/src/error.rs +++ b/parser/src/error.rs @@ -81,6 +81,7 @@ pub struct FStringError { pub enum FStringErrorType { UnclosedLbrace, UnopenedRbrace, + ExpectedRbrace, InvalidExpression(Box), InvalidConversionFlag, EmptyExpression, @@ -91,8 +92,9 @@ pub enum FStringErrorType { impl fmt::Display for FStringErrorType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - FStringErrorType::UnclosedLbrace => write!(f, "Unclosed '('"), - FStringErrorType::UnopenedRbrace => write!(f, "Unopened ')'"), + FStringErrorType::UnclosedLbrace => write!(f, "Unclosed '{{'"), + FStringErrorType::UnopenedRbrace => write!(f, "Unopened '}}'"), + FStringErrorType::ExpectedRbrace => write!(f, "Expected '}}' after conversion flag."), FStringErrorType::InvalidExpression(error) => { write!(f, "Invalid expression: {}", error) } From a21a2d477385fda7be82ca83b3ae97c49ce25396 Mon Sep 17 00:00:00 2001 From: philippeitis <33013301+philippeitis@users.noreply.github.com> Date: Wed, 26 Feb 2020 21:09:25 -0800 Subject: [PATCH 2/3] Handle != in f-string braces, raise EmptyExpression when expected. --- parser/src/fstring.rs | 52 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/parser/src/fstring.rs b/parser/src/fstring.rs index 5ca92c484..5a24b12a8 100644 --- a/parser/src/fstring.rs +++ b/parser/src/fstring.rs @@ -30,17 +30,39 @@ impl<'a> FStringParser<'a> { while let Some(ch) = self.chars.next() { match ch { '!' if delims.is_empty() => { - conversion = Some(match self.chars.next() { - Some('s') => ConversionFlag::Str, - Some('a') => ConversionFlag::Ascii, - Some('r') => ConversionFlag::Repr, - Some(_) => { - return Err(InvalidConversionFlag); + let x = self.chars.next(); + if let Some('=') = x { + expression.push(ch); + expression.push('='); + } else { + if expression.is_empty() { + return Err(EmptyExpression); } - None => { - break; + + conversion = Some(match x { + Some('s') => ConversionFlag::Str, + Some('a') => ConversionFlag::Ascii, + Some('r') => ConversionFlag::Repr, + Some(_) => { + return Err(InvalidConversionFlag); + } + None => { + break; + } + }); + + match self.chars.peek() { + Some('}') => { + continue; + } + Some(_) => { + return Err(ExpectedRbrace); + } + None => { + break; + } } - }) + } } ':' if delims.is_empty() => { let mut nested = false; @@ -294,6 +316,11 @@ mod tests { fn test_parse_invalid_fstring() { assert_eq!(parse_fstring("{"), Err(UnclosedLbrace)); assert_eq!(parse_fstring("}"), Err(UnopenedRbrace)); + assert_eq!(parse_fstring("{5!a"), Err(UnclosedLbrace)); + assert_eq!(parse_fstring("{5!a1}"), Err(ExpectedRbrace)); + assert_eq!(parse_fstring("abc{!a 'cat'}"), Err(EmptyExpression)); + assert_eq!(parse_fstring("{!x}"), Err(EmptyExpression)); + assert_eq!(parse_fstring("{a:{a:{b}}"), Err(ExpressionNestedTooDeeply)); assert_eq!(parse_fstring("{a:b}}"), Err(UnopenedRbrace)); assert_eq!(parse_fstring("{a:{b}"), Err(UnclosedLbrace)); @@ -301,4 +328,11 @@ mod tests { // TODO: check for InvalidExpression enum? assert!(parse_fstring("{class}").is_err()); } + + #[test] + fn test_parse_fstring_not_equals() { + let source = String::from("{1 != 2}"); + let parse_ast = parse_fstring(&source); + assert_ne!(parse_ast, Err(InvalidConversionFlag)); + } } From e037e37018f657d8742cbf89ff87ab4f4d5777d5 Mon Sep 17 00:00:00 2001 From: philippeitis <33013301+philippeitis@users.noreply.github.com> Date: Thu, 27 Feb 2020 11:34:55 -0800 Subject: [PATCH 3/3] Simplify code, have errors match CPython implementation. --- parser/src/fstring.rs | 65 +++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 36 deletions(-) diff --git a/parser/src/fstring.rs b/parser/src/fstring.rs index 5a24b12a8..79a752469 100644 --- a/parser/src/fstring.rs +++ b/parser/src/fstring.rs @@ -29,39 +29,25 @@ impl<'a> FStringParser<'a> { while let Some(ch) = self.chars.next() { match ch { - '!' if delims.is_empty() => { - let x = self.chars.next(); - if let Some('=') = x { - expression.push(ch); - expression.push('='); - } else { - if expression.is_empty() { - return Err(EmptyExpression); - } + '!' if delims.is_empty() && self.chars.peek() != Some(&'=') => { + if expression.trim().is_empty() { + return Err(EmptyExpression); + } - conversion = Some(match x { - Some('s') => ConversionFlag::Str, - Some('a') => ConversionFlag::Ascii, - Some('r') => ConversionFlag::Repr, - Some(_) => { - return Err(InvalidConversionFlag); - } - None => { - break; - } - }); - - match self.chars.peek() { - Some('}') => { - continue; - } - Some(_) => { - return Err(ExpectedRbrace); - } - None => { - break; - } + conversion = Some(match self.chars.next() { + Some('s') => ConversionFlag::Str, + Some('a') => ConversionFlag::Ascii, + Some('r') => ConversionFlag::Repr, + Some(_) => { + return Err(InvalidConversionFlag); } + None => { + return Err(ExpectedRbrace); + } + }); + + if self.chars.peek() != Some(&'}') { + return Err(ExpectedRbrace); } } ':' if delims.is_empty() => { @@ -314,16 +300,23 @@ mod tests { #[test] fn test_parse_invalid_fstring() { - assert_eq!(parse_fstring("{"), Err(UnclosedLbrace)); - assert_eq!(parse_fstring("}"), Err(UnopenedRbrace)); - assert_eq!(parse_fstring("{5!a"), Err(UnclosedLbrace)); + assert_eq!(parse_fstring("{5!a"), Err(ExpectedRbrace)); assert_eq!(parse_fstring("{5!a1}"), Err(ExpectedRbrace)); + assert_eq!(parse_fstring("{5!"), Err(ExpectedRbrace)); + assert_eq!(parse_fstring("abc{!a 'cat'}"), Err(EmptyExpression)); - assert_eq!(parse_fstring("{!x}"), Err(EmptyExpression)); + assert_eq!(parse_fstring("{!a"), Err(EmptyExpression)); + assert_eq!(parse_fstring("{ !a}"), Err(EmptyExpression)); + + assert_eq!(parse_fstring("{5!}"), Err(InvalidConversionFlag)); + assert_eq!(parse_fstring("{5!x}"), Err(InvalidConversionFlag)); assert_eq!(parse_fstring("{a:{a:{b}}"), Err(ExpressionNestedTooDeeply)); + assert_eq!(parse_fstring("{a:b}}"), Err(UnopenedRbrace)); + assert_eq!(parse_fstring("}"), Err(UnopenedRbrace)); assert_eq!(parse_fstring("{a:{b}"), Err(UnclosedLbrace)); + assert_eq!(parse_fstring("{"), Err(UnclosedLbrace)); // TODO: check for InvalidExpression enum? assert!(parse_fstring("{class}").is_err()); @@ -333,6 +326,6 @@ mod tests { fn test_parse_fstring_not_equals() { let source = String::from("{1 != 2}"); let parse_ast = parse_fstring(&source); - assert_ne!(parse_ast, Err(InvalidConversionFlag)); + assert!(parse_ast.is_ok()); } }