Skip to content

Commit 9779045

Browse files
committed
Improve REPL detecting when input needs to continue.
Full CPython compatibility with this requires actually parsing the input so far collected, and if it fails parsing due to lack of tokens, then continue collecting input. It's not worth doing it this way. Not having compatibility at this level does not hurt the goals of Micro Python.
1 parent 72d70cb commit 9779045

7 files changed

Lines changed: 78 additions & 68 deletions

File tree

py/repl.c

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,43 +14,66 @@ bool str_startswith_word(const char *str, const char *head) {
1414
return head[i] == '\0' && (str[i] == '\0' || !unichar_isalpha(str[i]));
1515
}
1616

17-
bool mp_repl_is_compound_stmt(const char *line) {
18-
// compound if line starts with a certain keyword
19-
if (
20-
str_startswith_word(line, "if")
21-
|| str_startswith_word(line, "while")
22-
|| str_startswith_word(line, "for")
23-
|| str_startswith_word(line, "try")
24-
|| str_startswith_word(line, "with")
25-
|| str_startswith_word(line, "def")
26-
|| str_startswith_word(line, "class")
27-
|| str_startswith_word(line, "@")
28-
) {
29-
return true;
17+
bool mp_repl_continue_with_input(const char *input) {
18+
// check for blank input
19+
if (input[0] == '\0') {
20+
return false;
3021
}
3122

32-
// also "compound" if unmatched open bracket or triple quote
23+
// check if input starts with a certain keyword
24+
bool starts_with_compound_keyword =
25+
input[0] == '@'
26+
|| str_startswith_word(input, "if")
27+
|| str_startswith_word(input, "while")
28+
|| str_startswith_word(input, "for")
29+
|| str_startswith_word(input, "try")
30+
|| str_startswith_word(input, "with")
31+
|| str_startswith_word(input, "def")
32+
|| str_startswith_word(input, "class")
33+
;
34+
35+
// check for unmatched open bracket or triple quote
36+
// TODO don't look at triple quotes inside single quotes
3337
int n_paren = 0;
3438
int n_brack = 0;
3539
int n_brace = 0;
3640
int in_triple_quote = 0;
37-
for (const char *l = line; *l; l++) {
38-
switch (*l) {
41+
const char *i;
42+
for (i = input; *i; i++) {
43+
switch (*i) {
3944
case '(': n_paren += 1; break;
4045
case ')': n_paren -= 1; break;
4146
case '[': n_brack += 1; break;
4247
case ']': n_brack -= 1; break;
4348
case '{': n_brace += 1; break;
4449
case '}': n_brace -= 1; break;
50+
case '\'':
51+
if (in_triple_quote != '"' && i[1] == '\'' && i[2] == '\'') {
52+
i += 2;
53+
in_triple_quote = '\'' - in_triple_quote;
54+
}
55+
break;
4556
case '"':
46-
if (l[1] == '"' && l[2] == '"') {
47-
l += 2;
48-
in_triple_quote = 1 - in_triple_quote;
57+
if (in_triple_quote != '\'' && i[1] == '"' && i[2] == '"') {
58+
i += 2;
59+
in_triple_quote = '"' - in_triple_quote;
4960
}
5061
break;
5162
}
5263
}
53-
return n_paren > 0 || n_brack > 0 || n_brace > 0 || in_triple_quote != 0;
64+
65+
// continue if unmatched brackets or quotes
66+
if (n_paren > 0 || n_brack > 0 || n_brace > 0 || in_triple_quote != 0) {
67+
return true;
68+
}
69+
70+
// continue if compound keyword and last line was not empty
71+
if (starts_with_compound_keyword && i[-1] != '\n') {
72+
return true;
73+
}
74+
75+
// otherwise, don't continue
76+
return false;
5477
}
5578

5679
#endif // MICROPY_ENABLE_REPL_HELPERS

py/repl.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
#if MICROPY_ENABLE_REPL_HELPERS
2-
bool mp_repl_is_compound_stmt(const char *line);
2+
bool mp_repl_continue_with_input(const char *input);
33
#endif

stm/pyexec.c

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -283,15 +283,12 @@ void pyexec_repl(void) {
283283
continue;
284284
}
285285

286-
if (mp_repl_is_compound_stmt(vstr_str(&line))) {
287-
for (;;) {
288-
vstr_add_char(&line, '\n');
289-
int len = vstr_len(&line);
290-
int ret = readline(&line, "... ");
291-
if (ret == VCP_CHAR_CTRL_D || vstr_len(&line) == len) {
292-
// done entering compound statement
293-
break;
294-
}
286+
while (mp_repl_continue_with_input(vstr_str(&line))) {
287+
vstr_add_char(&line, '\n');
288+
int ret = readline(&line, "... ");
289+
if (ret == VCP_CHAR_CTRL_D) {
290+
// stop entering compound statement
291+
break;
295292
}
296293
}
297294

stmhal/pyexec.c

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -204,15 +204,12 @@ int pyexec_friendly_repl(void) {
204204
continue;
205205
}
206206

207-
if (mp_repl_is_compound_stmt(vstr_str(&line))) {
208-
for (;;) {
209-
vstr_add_char(&line, '\n');
210-
int len = vstr_len(&line);
211-
int ret = readline(&line, "... ");
212-
if (ret == VCP_CHAR_CTRL_D || vstr_len(&line) == len) {
213-
// done entering compound statement
214-
break;
215-
}
207+
while (mp_repl_continue_with_input(vstr_str(&line))) {
208+
vstr_add_char(&line, '\n');
209+
int ret = readline(&line, "... ");
210+
if (ret == VCP_CHAR_CTRL_D) {
211+
// stop entering compound statement
212+
break;
216213
}
217214
}
218215

teensy/main.c

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -399,15 +399,12 @@ void do_repl(void) {
399399
continue;
400400
}
401401

402-
if (mp_repl_is_compound_stmt(vstr_str(&line))) {
403-
for (;;) {
404-
vstr_add_char(&line, '\n');
405-
int len = vstr_len(&line);
406-
int ret = readline(&line, "... ");
407-
if (ret == 0 || vstr_len(&line) == len) {
408-
// done entering compound statement
409-
break;
410-
}
402+
while (mp_repl_continue_with_input(vstr_str(&line))) {
403+
vstr_add_char(&line, '\n');
404+
int ret = readline(&line, "... ");
405+
if (ret == 0) {
406+
// stop entering compound statement
407+
break;
411408
}
412409
}
413410

unix/main.c

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -146,17 +146,15 @@ STATIC void do_repl(void) {
146146
// EOF
147147
return;
148148
}
149-
if (mp_repl_is_compound_stmt(line)) {
150-
for (;;) {
151-
char *line2 = prompt("... ");
152-
if (line2 == NULL || strlen(line2) == 0) {
153-
break;
154-
}
155-
char *line3 = strjoin(line, '\n', line2);
156-
free(line);
157-
free(line2);
158-
line = line3;
149+
while (mp_repl_continue_with_input(line)) {
150+
char *line2 = prompt("... ");
151+
if (line2 == NULL) {
152+
break;
159153
}
154+
char *line3 = strjoin(line, '\n', line2);
155+
free(line);
156+
free(line2);
157+
line = line3;
160158
}
161159

162160
mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, line, strlen(line), false);

windows/main.c

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -126,17 +126,15 @@ static void do_repl(void) {
126126
// EOF
127127
return;
128128
}
129-
if (mp_repl_is_compound_stmt(line)) {
130-
for (;;) {
131-
char *line2 = prompt("... ");
132-
if (line2 == NULL || strlen(line2) == 0) {
133-
break;
134-
}
135-
char *line3 = str_join(line, '\n', line2);
136-
free(line);
137-
free(line2);
138-
line = line3;
129+
while (mp_repl_continue_with_input(line)) {
130+
char *line2 = prompt("... ");
131+
if (line2 == NULL) {
132+
break;
139133
}
134+
char *line3 = str_join(line, '\n', line2);
135+
free(line);
136+
free(line2);
137+
line = line3;
140138
}
141139

142140
mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, line, strlen(line), false);

0 commit comments

Comments
 (0)