diff --git a/parser/src/error.rs b/parser/src/error.rs index 86fa763874f..408fef3f6d8 100644 --- a/parser/src/error.rs +++ b/parser/src/error.rs @@ -27,6 +27,7 @@ pub enum LexicalErrorType { DuplicateKeywordArgumentError, UnrecognizedToken { tok: char }, FStringError(FStringErrorType), + LineContinuationError, OtherError(String), } @@ -55,6 +56,9 @@ impl fmt::Display for LexicalErrorType { LexicalErrorType::UnrecognizedToken { tok } => { write!(f, "Got unexpected token {}", tok) } + LexicalErrorType::LineContinuationError => { + write!(f, "unexpected character after line continuation character") + } LexicalErrorType::OtherError(msg) => write!(f, "{}", msg), } } diff --git a/parser/src/lexer.rs b/parser/src/lexer.rs index 2a70119676a..50b29b3e95b 100644 --- a/parser/src/lexer.rs +++ b/parser/src/lexer.rs @@ -117,8 +117,7 @@ pub type LexResult = Result; pub fn make_tokenizer<'a>(source: &'a str) -> impl Iterator + 'a { let nlh = NewlineHandler::new(source.chars()); - let lch = LineContinuationHandler::new(nlh); - Lexer::new(lch) + Lexer::new(nlh) } // The newline handler is an iterator which collapses different newline @@ -178,61 +177,6 @@ where } } -// Glues \ and \n into a single line: -pub struct LineContinuationHandler> { - source: T, - chr0: Option, - chr1: Option, -} - -impl LineContinuationHandler -where - T: Iterator, -{ - pub fn new(source: T) -> Self { - let mut nlh = LineContinuationHandler { - source, - chr0: None, - chr1: None, - }; - nlh.shift(); - nlh.shift(); - nlh - } - - fn shift(&mut self) -> Option { - let result = self.chr0; - self.chr0 = self.chr1; - self.chr1 = self.source.next(); - result - } -} - -impl Iterator for LineContinuationHandler -where - T: Iterator, -{ - type Item = char; - - fn next(&mut self) -> Option { - // Collapse \r\n into \n - loop { - if self.chr0 == Some('\\') && self.chr1 == Some('\n') { - // Skip backslash and newline - self.shift(); - self.shift(); - // Idea: insert trailing newline here: - // } else if self.chr0 != Some('\n') && self.chr1.is_none() { - // self.chr1 = Some('\n'); - } else { - break; - } - } - - self.shift() - } -} - impl Lexer where T: Iterator, @@ -1201,6 +1145,18 @@ where self.next_char(); } } + '\\' => { + self.next_char(); + if let Some('\n') = self.chr0 { + self.next_char(); + } else { + return Err(LexicalError { + error: LexicalErrorType::LineContinuationError, + location: self.get_pos(), + }); + } + } + _ => { let c = self.next_char(); return Err(LexicalError { diff --git a/tests/snippets/funky_syntax.py b/tests/snippets/funky_syntax.py index 28ef5964d5b..97cea1dff13 100644 --- a/tests/snippets/funky_syntax.py +++ b/tests/snippets/funky_syntax.py @@ -1,3 +1,4 @@ +import traceback a = 2 b = 2 + 4 if a < 5 else 'boe' @@ -12,3 +13,10 @@ assert e(True) == 1 assert e(False) == 0 +try: + a = "aaaa" + \ + "bbbb" + 1/0 +except ZeroDivisionError as ex: + tb = traceback.extract_tb(ex.__traceback__) + assert tb[0].lineno == 19 diff --git a/tests/snippets/invalid_syntax.py b/tests/snippets/invalid_syntax.py index cd903372e37..33428e94bf6 100644 --- a/tests/snippets/invalid_syntax.py +++ b/tests/snippets/invalid_syntax.py @@ -51,3 +51,12 @@ def valid_func(): with assert_raises(SyntaxError): compile('0xX', 'test.py', 'exec') + + +src = """ +"aaaa" \a +"bbbb" +""" + +with assert_raises(SyntaxError): + compile(src, 'test.py', 'exec')