Skip to content

Commit 92c0656

Browse files
committed
Improve REPL compount statement detection.
1 parent 9d63932 commit 92c0656

5 files changed

Lines changed: 52 additions & 24 deletions

File tree

py/lexer.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010

1111
#define TAB_SIZE (8)
1212

13+
// TODO seems that CPython allows NULL byte in the input stream
14+
// don't know if that's intentional or not, but we don't allow it
15+
1316
struct _py_lexer_t {
1417
const char *name; // name of source
1518
void *stream_data; // data for stream

py/repl.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#include "misc.h"
2+
#include "repl.h"
3+
4+
bool str_startswith_word(const char *str, const char *head) {
5+
int i;
6+
for (i = 0; str[i] && head[i]; i++) {
7+
if (str[i] != head[i]) {
8+
return false;
9+
}
10+
}
11+
return head[i] == '\0' && (str[i] == '\0' || !g_unichar_isalpha(str[i]));
12+
}
13+
14+
bool py_repl_is_compound_stmt(const char *line) {
15+
// compound if line starts with a certain keyword
16+
if (
17+
str_startswith_word(line, "if")
18+
|| str_startswith_word(line, "while")
19+
|| str_startswith_word(line, "for")
20+
|| str_startswith_word(line, "true")
21+
|| str_startswith_word(line, "with")
22+
|| str_startswith_word(line, "def")
23+
|| str_startswith_word(line, "class")
24+
|| str_startswith_word(line, "@")
25+
) {
26+
return true;
27+
}
28+
29+
// also "compound" if unmatched open bracket
30+
int n_paren = 0;
31+
int n_brack = 0;
32+
int n_brace = 0;
33+
for (const char *l = line; *l; l++) {
34+
switch (*l) {
35+
case '(': n_paren += 1; break;
36+
case ')': n_paren -= 1; break;
37+
case '[': n_brack += 1; break;
38+
case ']': n_brack -= 1; break;
39+
case '{': n_brace += 1; break;
40+
case '}': n_brace -= 1; break;
41+
}
42+
}
43+
return n_paren > 0 || n_brack > 0 || n_brace > 0;
44+
}

py/repl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
bool py_repl_is_compound_stmt(const char *line);

unix/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ PY_O = \
3030
emitinlinethumb.o \
3131
runtime.o \
3232
vm.o \
33+
repl.o \
3334

3435
OBJ = $(addprefix $(BUILD)/, $(SRC_C:.c=.o) $(PY_O))
3536
LIB = -lreadline

unix/main.c

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,32 +10,10 @@
1010
#include "parse.h"
1111
#include "compile.h"
1212
#include "runtime.h"
13+
#include "repl.h"
1314

1415
#include <readline/readline.h>
1516

16-
bool str_startswith_word(const char *str, const char *head) {
17-
int i;
18-
for (i = 0; str[i] && head[i]; i++) {
19-
if (str[i] != head[i]) {
20-
return false;
21-
}
22-
}
23-
return head[i] == '\0' && (str[i] == '\0' || !g_unichar_isalpha(str[i]));
24-
}
25-
26-
bool is_compound_stmt(const char *line) {
27-
// TODO also "compound" if unmatched open bracket
28-
return
29-
str_startswith_word(line, "if")
30-
|| str_startswith_word(line, "while")
31-
|| str_startswith_word(line, "for")
32-
|| str_startswith_word(line, "true")
33-
|| str_startswith_word(line, "with")
34-
|| str_startswith_word(line, "def")
35-
|| str_startswith_word(line, "class")
36-
|| str_startswith_word(line, "@");
37-
}
38-
3917
char *str_join(const char *s1, int sep_char, const char *s2) {
4018
int l1 = strlen(s1);
4119
int l2 = strlen(s2);
@@ -46,6 +24,7 @@ char *str_join(const char *s1, int sep_char, const char *s2) {
4624
l1 += 1;
4725
}
4826
memcpy(s + l1, s2, l2);
27+
s[l1 + l2] = 0;
4928
return s;
5029
}
5130

@@ -56,7 +35,7 @@ void do_repl() {
5635
// EOF
5736
return;
5837
}
59-
if (is_compound_stmt(line)) {
38+
if (py_repl_is_compound_stmt(line)) {
6039
for (;;) {
6140
char *line2 = readline("... ");
6241
if (line2 == NULL || strlen(line2) == 0) {

0 commit comments

Comments
 (0)