Skip to content

Commit d0f6af0

Browse files
authored
Merge pull request RustPython#4399 from branai/shell-continuing-fix
Fix IndentationError works differently with cpython in interective shell
2 parents 72c0a06 + 1bc9749 commit d0f6af0

File tree

2 files changed

+93
-23
lines changed

2 files changed

+93
-23
lines changed

compiler/parser/src/error.rs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -205,11 +205,23 @@ pub(crate) fn parse_error_from_lalrpop(
205205
source_path,
206206
}
207207
}
208-
LalrpopError::UnrecognizedEOF { location, .. } => ParseError {
209-
error: ParseErrorType::Eof,
210-
location,
211-
source_path,
212-
},
208+
LalrpopError::UnrecognizedEOF { location, expected } => {
209+
// This could be an initial indentation error that we should ignore
210+
let indent_error = expected == ["Indent"];
211+
if indent_error {
212+
ParseError {
213+
error: ParseErrorType::Lexical(LexicalErrorType::IndentationError),
214+
location,
215+
source_path,
216+
}
217+
} else {
218+
ParseError {
219+
error: ParseErrorType::Eof,
220+
location,
221+
source_path,
222+
}
223+
}
224+
}
213225
}
214226
}
215227

src/shell.rs

Lines changed: 76 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,28 @@ enum ShellExecResult {
1515
Continue,
1616
}
1717

18-
fn shell_exec(vm: &VirtualMachine, source: &str, scope: Scope) -> ShellExecResult {
18+
fn shell_exec(
19+
vm: &VirtualMachine,
20+
source: &str,
21+
scope: Scope,
22+
last_row: &mut usize,
23+
empty_line_given: bool,
24+
continuing: bool,
25+
) -> ShellExecResult {
1926
match vm.compile(source, compiler::Mode::Single, "<stdin>".to_owned()) {
20-
Ok(code) => match vm.run_code_obj(code, scope) {
21-
Ok(_val) => ShellExecResult::Ok,
22-
Err(err) => ShellExecResult::PyErr(err),
23-
},
27+
Ok(code) => {
28+
if empty_line_given || !continuing {
29+
// We want to execute the full code
30+
*last_row = 0;
31+
match vm.run_code_obj(code, scope) {
32+
Ok(_val) => ShellExecResult::Ok,
33+
Err(err) => ShellExecResult::PyErr(err),
34+
}
35+
} else {
36+
// We can just return an ok result
37+
ShellExecResult::Ok
38+
}
39+
}
2440
Err(CompileError {
2541
body:
2642
CompileErrorBody {
@@ -37,7 +53,31 @@ fn shell_exec(vm: &VirtualMachine, source: &str, scope: Scope) -> ShellExecResul
3753
},
3854
..
3955
}) => ShellExecResult::Continue,
40-
Err(err) => ShellExecResult::PyErr(vm.new_syntax_error(&err)),
56+
Err(err) => {
57+
// Indent error or something else?
58+
let indent_error = match err.body.error {
59+
CompileErrorType::Parse(ref p) => p.is_indentation_error(),
60+
_ => false,
61+
};
62+
63+
if indent_error && !empty_line_given {
64+
// The input line is not empty and it threw an indentation error
65+
let l = err.body.location;
66+
67+
// This is how we can mask unnecesary errors
68+
if l.row() > *last_row {
69+
*last_row = l.row();
70+
ShellExecResult::Continue
71+
} else {
72+
*last_row = 0;
73+
ShellExecResult::PyErr(vm.new_syntax_error(&err))
74+
}
75+
} else {
76+
// Throw the error for all other cases
77+
*last_row = 0;
78+
ShellExecResult::PyErr(vm.new_syntax_error(&err))
79+
}
80+
}
4181
}
4282
}
4383

@@ -60,6 +100,7 @@ pub fn run_shell(vm: &VirtualMachine, scope: Scope) -> PyResult<()> {
60100
}
61101

62102
let mut continuing = false;
103+
let mut last_row: usize = 0;
63104

64105
loop {
65106
let prompt_name = if continuing { "ps2" } else { "ps1" };
@@ -78,7 +119,7 @@ pub fn run_shell(vm: &VirtualMachine, scope: Scope) -> PyResult<()> {
78119

79120
repl.add_history_entry(line.trim_end()).unwrap();
80121

81-
let stop_continuing = line.is_empty();
122+
let empty_line_given = line.is_empty();
82123

83124
if full_input.is_empty() {
84125
full_input = line;
@@ -87,30 +128,47 @@ pub fn run_shell(vm: &VirtualMachine, scope: Scope) -> PyResult<()> {
87128
}
88129
full_input.push('\n');
89130

90-
if continuing {
91-
if stop_continuing {
92-
continuing = false;
93-
} else {
94-
continue;
95-
}
96-
}
97-
98-
match shell_exec(vm, &full_input, scope.clone()) {
131+
match shell_exec(
132+
vm,
133+
&full_input,
134+
scope.clone(),
135+
&mut last_row,
136+
empty_line_given,
137+
continuing,
138+
) {
99139
ShellExecResult::Ok => {
100-
full_input.clear();
101-
Ok(())
140+
if continuing {
141+
if empty_line_given {
142+
// We should be exiting continue mode
143+
continuing = false;
144+
full_input.clear();
145+
Ok(())
146+
} else {
147+
// We should stay in continue mode
148+
continuing = true;
149+
Ok(())
150+
}
151+
} else {
152+
// We aren't in continue mode so proceed normally
153+
last_row = 0;
154+
continuing = false;
155+
full_input.clear();
156+
Ok(())
157+
}
102158
}
103159
ShellExecResult::Continue => {
104160
continuing = true;
105161
Ok(())
106162
}
107163
ShellExecResult::PyErr(err) => {
164+
continuing = false;
108165
full_input.clear();
109166
Err(err)
110167
}
111168
}
112169
}
113170
ReadlineResult::Interrupt => {
171+
last_row = 0;
114172
continuing = false;
115173
full_input.clear();
116174
let keyboard_interrupt =

0 commit comments

Comments
 (0)