Skip to content

Commit ad4beab

Browse files
committed
fix line continuation parsing
1 parent 69830bb commit ad4beab

File tree

4 files changed

+34
-57
lines changed

4 files changed

+34
-57
lines changed

parser/src/error.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ pub enum LexicalErrorType {
2727
DuplicateKeywordArgumentError,
2828
UnrecognizedToken { tok: char },
2929
FStringError(FStringErrorType),
30+
LineContinuationError,
3031
OtherError(String),
3132
}
3233

@@ -55,6 +56,9 @@ impl fmt::Display for LexicalErrorType {
5556
LexicalErrorType::UnrecognizedToken { tok } => {
5657
write!(f, "Got unexpected token {}", tok)
5758
}
59+
LexicalErrorType::LineContinuationError => {
60+
write!(f, "unexpected character after line continuation character")
61+
}
5862
LexicalErrorType::OtherError(msg) => write!(f, "{}", msg),
5963
}
6064
}

parser/src/lexer.rs

Lines changed: 13 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,7 @@ pub type LexResult = Result<Spanned, LexicalError>;
117117

118118
pub fn make_tokenizer<'a>(source: &'a str) -> impl Iterator<Item = LexResult> + 'a {
119119
let nlh = NewlineHandler::new(source.chars());
120-
let lch = LineContinuationHandler::new(nlh);
121-
Lexer::new(lch)
120+
Lexer::new(nlh)
122121
}
123122

124123
// The newline handler is an iterator which collapses different newline
@@ -178,61 +177,6 @@ where
178177
}
179178
}
180179

181-
// Glues \ and \n into a single line:
182-
pub struct LineContinuationHandler<T: Iterator<Item = char>> {
183-
source: T,
184-
chr0: Option<char>,
185-
chr1: Option<char>,
186-
}
187-
188-
impl<T> LineContinuationHandler<T>
189-
where
190-
T: Iterator<Item = char>,
191-
{
192-
pub fn new(source: T) -> Self {
193-
let mut nlh = LineContinuationHandler {
194-
source,
195-
chr0: None,
196-
chr1: None,
197-
};
198-
nlh.shift();
199-
nlh.shift();
200-
nlh
201-
}
202-
203-
fn shift(&mut self) -> Option<char> {
204-
let result = self.chr0;
205-
self.chr0 = self.chr1;
206-
self.chr1 = self.source.next();
207-
result
208-
}
209-
}
210-
211-
impl<T> Iterator for LineContinuationHandler<T>
212-
where
213-
T: Iterator<Item = char>,
214-
{
215-
type Item = char;
216-
217-
fn next(&mut self) -> Option<Self::Item> {
218-
// Collapse \r\n into \n
219-
loop {
220-
if self.chr0 == Some('\\') && self.chr1 == Some('\n') {
221-
// Skip backslash and newline
222-
self.shift();
223-
self.shift();
224-
// Idea: insert trailing newline here:
225-
// } else if self.chr0 != Some('\n') && self.chr1.is_none() {
226-
// self.chr1 = Some('\n');
227-
} else {
228-
break;
229-
}
230-
}
231-
232-
self.shift()
233-
}
234-
}
235-
236180
impl<T> Lexer<T>
237181
where
238182
T: Iterator<Item = char>,
@@ -1201,6 +1145,18 @@ where
12011145
self.next_char();
12021146
}
12031147
}
1148+
'\\' => {
1149+
self.next_char();
1150+
if let Some('\n') = self.chr0 {
1151+
self.next_char();
1152+
} else {
1153+
return Err(LexicalError {
1154+
error: LexicalErrorType::LineContinuationError,
1155+
location: self.get_pos(),
1156+
});
1157+
}
1158+
}
1159+
12041160
_ => {
12051161
let c = self.next_char();
12061162
return Err(LexicalError {

tests/snippets/funky_syntax.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import traceback
12

23
a = 2
34
b = 2 + 4 if a < 5 else 'boe'
@@ -12,3 +13,10 @@
1213
assert e(True) == 1
1314
assert e(False) == 0
1415

16+
try:
17+
a = "aaaa" + \
18+
"bbbb"
19+
1/0
20+
except ZeroDivisionError as ex:
21+
tb = traceback.extract_tb(ex.__traceback__)
22+
assert tb[0].lineno == 19

tests/snippets/invalid_syntax.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,12 @@ def valid_func():
5151

5252
with assert_raises(SyntaxError):
5353
compile('0xX', 'test.py', 'exec')
54+
55+
56+
src = """
57+
"aaaa" \a
58+
"bbbb"
59+
"""
60+
61+
with assert_raises(SyntaxError):
62+
compile(src, 'test.py', 'exec')

0 commit comments

Comments
 (0)