Skip to content

Commit 7986ba2

Browse files
committed
continue REPL until block is terminated
1 parent 831e7e0 commit 7986ba2

File tree

6 files changed

+97
-60
lines changed

6 files changed

+97
-60
lines changed

include/interactive/prompt.h

+5-4
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@
2424

2525

2626
typedef struct {
27-
char *buf;
27+
char *buf; // prompt buffer
2828
size_t buf_len;
2929
size_t buf_cap;
30-
size_t cursor_pos;
30+
size_t cursor_pos_in_line; // cursor position in the current line
31+
size_t prev_line_end; // where the previous line ended
3132
char ps1[256];
3233
struct termios termios_og;
3334
struct termios termios_new;
@@ -39,8 +40,8 @@ void prompt_free(Prompt *prompt);
3940
/*
4041
* Returns true if interpreter shall continue else false
4142
*/
42-
bool prompt_run(Prompt *prompt);
43-
void prompt_reset(Prompt *prompt);
43+
void prompt_run(Prompt *prompt, bool reset);
44+
void prompt_set_ps1(Prompt *prompt, char *ps1);
4445

4546

4647
#endif /* PROMPT_H */

include/interpreter/parser.h

+6
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,13 @@
2525

2626

2727
/* types */
28+
typedef enum {
29+
PET_EXPECTED_RBRACE,
30+
} ParseErrorType;
31+
2832
typedef struct parse_error_t ParseError;
2933
struct parse_error_t {
34+
ParseErrorType err_type;
3035
char *msg;
3136
Token *failed; // the token that caused the error
3237
ParseError *next;
@@ -46,6 +51,7 @@ typedef struct {
4651
typedef struct {
4752
size_t n_errors;
4853
ParseError *perr_head;
54+
ParseError *perr_tail;
4955
ArrayList stmts;
5056
} ParseResult;
5157

src/builtin/read.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ int builtin_read(Interpreter *interpreter, ArenaLL *ast_nodes)
5353

5454
Prompt prompt;
5555
prompt_init(&prompt, ">>>");
56-
prompt_run(&prompt);
56+
prompt_run(&prompt, false);
5757
StrView input = (StrView){ .view = prompt.buf, .size = prompt.buf_len - 2 };
5858

5959
SlashObj *str = gc_new_T(interpreter, &str_type_info);

src/interactive/prompt.c

+58-51
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
* (Originally code for the Valery project)
3-
* Copyright (C) 2022-2023 Nicolai Brand (https://lytix.dev)
3+
* Copyright (C) 2022-2024 Nicolai Brand (https://lytix.dev)
44
*
55
* This program is free software: you can redistribute it and/or modify
66
* it under the terms of the GNU General Public License as published by
@@ -26,10 +26,9 @@
2626
#include "interactive/prompt.h"
2727

2828

29-
#define cursor_right(n) printf("\033[%zuC", (size_t)(n))
30-
#define cursor_left(n) printf("\033[%zuD", (size_t)(n))
31-
#define cursor_goto(x) printf("\033[%zu", (size_t)(x))
32-
#define flush_line() printf("\33[2K\r")
29+
#define CURSOR_LEFT(n) printf("\033[%zuD", (size_t)(n))
30+
#define FLUSH_LINE() printf("\33[2K\r")
31+
#define PROMPT_ABS_POS(prompt) (prompt)->cursor_pos_in_line + (prompt)->prev_line_end
3332

3433

3534
typedef enum {
@@ -41,7 +40,8 @@ typedef enum {
4140
KEY_ARROW_RIGHT = 67,
4241
KEY_ARROW_LEFT = 68,
4342

44-
KEY_BACKSPACE = 127
43+
KEY_BACKSPACE = 127,
44+
KEY_TAB = '\t',
4545
} PromptKey;
4646

4747

@@ -65,11 +65,13 @@ static void termconf_end(Prompt *prompt)
6565

6666
static void prompt_show(Prompt *prompt)
6767
{
68-
flush_line();
69-
printf("%s%s", prompt->ps1, prompt->buf);
68+
FLUSH_LINE();
69+
printf("%s%s", prompt->ps1, prompt->buf + prompt->prev_line_end);
7070
/* move the cursor to its corresponding position */
71-
if (prompt->cursor_pos != prompt->buf_len)
72-
cursor_left(prompt->buf_len - prompt->cursor_pos);
71+
if (PROMPT_ABS_POS(prompt) != prompt->buf_len) {
72+
ssize_t left_shift = PROMPT_ABS_POS(prompt) - prompt->buf_len;
73+
CURSOR_LEFT(-left_shift);
74+
}
7375
}
7476

7577
/* returns the type of arrow consumed from the terminal input buffer */
@@ -89,7 +91,6 @@ static void prompt_buf_ensure_capacity(Prompt *prompt)
8991
/* -1 because we always have to have space for the sentinel null byte */
9092
if (prompt->buf_len >= prompt->buf_cap - 1) {
9193
prompt->buf_cap *= 2;
92-
printf("\nREALLOC\n\n");
9394
prompt->buf = realloc(prompt->buf, prompt->buf_cap);
9495
}
9596
}
@@ -99,14 +100,15 @@ static void prompt_buf_append(Prompt *prompt, char c)
99100
prompt_buf_ensure_capacity(prompt);
100101
prompt->buf[prompt->buf_len++] = c;
101102
prompt->buf[prompt->buf_len] = 0;
102-
prompt->cursor_pos++;
103+
prompt->cursor_pos_in_line++;
103104
}
104105

105106
static void prompt_buf_remove_at_cursor(Prompt *prompt)
106107
{
107108
char tmp[prompt->buf_len];
108-
strncpy(tmp, prompt->buf, prompt->cursor_pos - 1);
109-
strcpy(tmp + prompt->cursor_pos - 1, prompt->buf + prompt->cursor_pos);
109+
size_t current_pos = PROMPT_ABS_POS(prompt);
110+
strncpy(tmp, prompt->buf, current_pos - 1);
111+
strcpy(tmp + current_pos - 1, prompt->buf + current_pos);
110112
strcpy(prompt->buf, tmp);
111113
}
112114

@@ -115,13 +117,14 @@ static void prompt_buf_insert_at_cursor(Prompt *prompt, char c)
115117
/* ensure enough capacity for 'c' */
116118
prompt_buf_ensure_capacity(prompt);
117119

120+
size_t current_pos = PROMPT_ABS_POS(prompt);
118121
char tmp[prompt->buf_len];
119-
strncpy(tmp, prompt->buf, prompt->cursor_pos);
120-
tmp[prompt->cursor_pos] = c;
121-
strcpy(tmp + prompt->cursor_pos + 1, prompt->buf + prompt->cursor_pos);
122+
strncpy(tmp, prompt->buf, current_pos);
123+
tmp[current_pos] = c;
124+
strcpy(tmp + current_pos + 1, prompt->buf + current_pos);
122125
strcpy(prompt->buf, tmp);
123126

124-
prompt->cursor_pos++;
127+
prompt->cursor_pos_in_line++;
125128
prompt->buf_len++;
126129
prompt_buf_ensure_capacity(prompt);
127130
prompt->buf[prompt->buf_len] = 0;
@@ -130,31 +133,37 @@ static void prompt_buf_insert_at_cursor(Prompt *prompt, char c)
130133
static void handle_backspace(Prompt *prompt)
131134
{
132135
/* ignore backspace leftmost position */
133-
if (prompt->cursor_pos == 0)
136+
if (prompt->cursor_pos_in_line == 0)
134137
return;
135138

136139
prompt_buf_remove_at_cursor(prompt);
137-
prompt->cursor_pos--;
140+
prompt->cursor_pos_in_line--;
138141
prompt->buf_len--;
139142
prompt->buf[prompt->buf_len] = 0;
140143
}
141144

142145
static void handle_arrow(Prompt *prompt)
143146
{
144147
PromptKey arrow = get_arrow_type();
145-
if (arrow == KEY_ARROW_LEFT && prompt->cursor_pos != 0) {
146-
cursor_left(1);
147-
prompt->cursor_pos--;
148-
} else if (arrow == KEY_ARROW_RIGHT && prompt->cursor_pos != prompt->buf_len) {
149-
cursor_right(1);
150-
prompt->cursor_pos++;
151-
}
148+
if (arrow == KEY_ARROW_LEFT && prompt->cursor_pos_in_line != 0)
149+
prompt->cursor_pos_in_line--;
150+
else if (arrow == KEY_ARROW_RIGHT && PROMPT_ABS_POS(prompt) != prompt->buf_len)
151+
prompt->cursor_pos_in_line++;
152152
}
153153

154+
static void prompt_reset(Prompt *prompt, bool continuation)
155+
{
156+
prompt->cursor_pos_in_line = 0;
157+
if (!continuation) {
158+
prompt->prev_line_end = 0;
159+
prompt->buf_len = 0;
160+
prompt->buf[0] = 0;
161+
}
162+
}
154163

155-
bool prompt_run(Prompt *prompt)
164+
void prompt_run(Prompt *prompt, bool continuation)
156165
{
157-
prompt_reset(prompt);
166+
prompt_reset(prompt, continuation);
158167
prompt_show(prompt);
159168
char ch;
160169
while (EOF != (ch = getchar()) && ch != '\n') {
@@ -167,8 +176,13 @@ bool prompt_run(Prompt *prompt)
167176
handle_arrow(prompt);
168177
break;
169178

179+
case KEY_TAB:
180+
for (size_t i = 0; i < 4; i++)
181+
prompt_buf_insert_at_cursor(prompt, ' ');
182+
break;
183+
170184
default:
171-
if (prompt->cursor_pos == prompt->buf_len)
185+
if (PROMPT_ABS_POS(prompt) == prompt->buf_len)
172186
prompt_buf_append(prompt, ch);
173187
else
174188
prompt_buf_insert_at_cursor(prompt, ch);
@@ -177,43 +191,36 @@ bool prompt_run(Prompt *prompt)
177191
prompt_show(prompt);
178192
}
179193

180-
// TODO: exit builtin means this branch is never taken
181-
if (strcmp(prompt->buf, "exit") == 0)
182-
return false;
183-
184-
putchar('\n');
185194
prompt_buf_append(prompt, '\n');
186195
prompt_buf_append(prompt, EOF);
187-
return true;
188-
}
196+
prompt->prev_line_end = prompt->buf_len - 1; /* -1 because prev line ends before EOF */
189197

190-
void prompt_reset(Prompt *prompt)
191-
{
192-
prompt->cursor_pos = 0;
193-
prompt->buf_len = 0;
194-
prompt->buf[0] = 0;
198+
putchar('\n');
195199
}
196200

197-
198-
void prompt_init(Prompt *prompt, char *ps1)
201+
void prompt_set_ps1(Prompt *prompt, char *ps1)
199202
{
200-
prompt->cursor_pos = 0;
201-
/* init buffer */
202-
prompt->buf_len = 0;
203-
prompt->buf_cap = 1024;
204-
prompt->buf = malloc(prompt->buf_cap);
205-
206203
size_t ps1_len = 0;
207204
if (ps1 != NULL)
208205
ps1_len = strlen(ps1);
209206
if (ps1_len > 256 || ps1_len == 0) {
210-
strcpy(prompt->ps1, "->");
207+
strcpy(prompt->ps1, "-> ");
211208
ps1_len = 2;
212209
} else {
213210
strncpy(prompt->ps1, ps1, ps1_len);
214211
}
215212
prompt->ps1[ps1_len] = 0;
213+
}
216214

215+
void prompt_init(Prompt *prompt, char *ps1)
216+
{
217+
prompt->cursor_pos_in_line = 0;
218+
prompt->prev_line_end = 0;
219+
/* init buffer */
220+
prompt->buf_len = 0;
221+
prompt->buf_cap = 1024;
222+
prompt->buf = malloc(prompt->buf_cap);
223+
prompt_set_ps1(prompt, ps1);
217224
termconf_begin(prompt);
218225
}
219226

src/interpreter/parser.c

+2
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ static void handle_parse_err(Parser *parser, char *msg);
8181
static ParseError *new_parse_error(Arena *arena, char *msg, Token *failed)
8282
{
8383
ParseError *error = m_arena_alloc_struct(arena, ParseError);
84+
error->err_type = PET_EXPECTED_RBRACE;
8485
size_t msg_len = strlen(msg); // TODO: could we avoid strlen somehow?
8586
char *msg_arena = m_arena_alloc(arena, msg_len + 1);
8687
memcpy(msg_arena, msg, msg_len + 1);
@@ -935,5 +936,6 @@ ParseResult parse(Arena *ast_arena, ArrayList *tokens, char *input)
935936

936937
return (ParseResult){ .n_errors = parser.n_errors,
937938
.perr_head = parser.perr_head,
939+
.perr_tail = parser.perr_tail,
938940
.stmts = statements };
939941
}

src/main.c

+25-4
Original file line numberDiff line numberDiff line change
@@ -42,25 +42,46 @@ void interactive(int argc, char **argv)
4242
ast_arena_init(&ast_arena);
4343
Prompt prompt;
4444
prompt_init(&prompt, "-> ");
45+
bool inside_block = false;
4546

46-
while (prompt_run(&prompt)) {
47-
m_arena_clear(&ast_arena);
47+
while (true) {
48+
prompt_run(&prompt, inside_block);
4849

4950
Lexer lex_result = lex(&ast_arena, prompt.buf, prompt.buf_len);
5051
if (lex_result.had_error) {
5152
arraylist_free(&lex_result.tokens);
53+
m_arena_clear(&ast_arena);
5254
continue;
5355
}
5456

5557
ParseResult parse_result = parse(&ast_arena, &lex_result.tokens, prompt.buf);
56-
if (parse_result.n_errors == 0)
58+
if (parse_result.n_errors == 0) {
5759
interpreter_run(&interpreter, &parse_result.stmts);
58-
else
60+
} else if ((parse_result.n_errors == 1 || inside_block) &&
61+
parse_result.perr_tail->err_type == PET_EXPECTED_RBRACE) {
62+
/* */
63+
prompt_set_ps1(&prompt, ".. ");
64+
inside_block = true;
65+
prompt.buf[--prompt.buf_len] = 0; /* Pop EOF. We are continuing. */
66+
67+
arraylist_free(&lex_result.tokens);
68+
arraylist_free(&parse_result.stmts);
69+
m_arena_clear(&ast_arena);
70+
71+
continue;
72+
} else {
5973
report_all_parse_errors(parse_result.perr_head, prompt.buf);
74+
}
75+
76+
if (inside_block) {
77+
prompt_set_ps1(&prompt, "-> ");
78+
inside_block = false;
79+
}
6080

6181
// TODO: these should maybe be reset (e.i. set size to 0), not freed
6282
arraylist_free(&lex_result.tokens);
6383
arraylist_free(&parse_result.stmts);
84+
m_arena_clear(&ast_arena);
6485
}
6586

6687
ast_arena_release(&ast_arena);

0 commit comments

Comments
 (0)