@@ -94,6 +94,190 @@ static int load_extension_input(const char* arg, char** err_out) {
9494 return extensions_load_library (arg , NULL , err_out );
9595}
9696
97+ static int buf_append (char * * buf , size_t * len , size_t * cap , const char * s ) {
98+ if (!buf || !len || !cap || !s ) return -1 ;
99+ size_t add = strlen (s );
100+ if (* len + add + 1 > * cap ) {
101+ size_t new_cap = (* cap == 0 ) ? 256 : * cap ;
102+ while (* len + add + 1 > new_cap ) new_cap *= 2 ;
103+ char * next = realloc (* buf , new_cap );
104+ if (!next ) return -1 ;
105+ * buf = next ;
106+ * cap = new_cap ;
107+ }
108+ memcpy (* buf + * len , s , add );
109+ * len += add ;
110+ (* buf )[* len ] = '\0' ;
111+ return 0 ;
112+ }
113+
114+ static char * read_line_dynamic (FILE * in ) {
115+ if (!in ) return NULL ;
116+ char * line = NULL ;
117+ size_t len = 0 ;
118+ size_t cap = 0 ;
119+ char chunk [512 ];
120+
121+ while (fgets (chunk , sizeof (chunk ), in )) {
122+ if (buf_append (& line , & len , & cap , chunk ) != 0 ) {
123+ free (line );
124+ return NULL ;
125+ }
126+ size_t n = strlen (chunk );
127+ if (n > 0 && chunk [n - 1 ] == '\n' ) break ;
128+ }
129+
130+ if (len == 0 && feof (in )) {
131+ free (line );
132+ return NULL ;
133+ }
134+
135+ if (!line ) {
136+ line = strdup ("" );
137+ }
138+ return line ;
139+ }
140+
141+ static int is_exit_meta_command (const char * text ) {
142+ if (!text ) return 0 ;
143+ const char * p = text ;
144+ while (* p == ' ' || * p == '\t' || * p == '\r' || * p == '\n' ) p ++ ;
145+ if (strncmp (p , ".exit" , 5 ) != 0 ) return 0 ;
146+ p += 5 ;
147+ while (* p == ' ' || * p == '\t' || * p == '\r' || * p == '\n' ) p ++ ;
148+ return * p == '\0' ;
149+ }
150+
151+ static void repl_update_line_state (const char * line , int * brace_depth , int * line_continuation ) {
152+ if (!line || !brace_depth || !line_continuation ) return ;
153+
154+ int in_single = 0 ;
155+ int in_double = 0 ;
156+ int escaped = 0 ;
157+ size_t comment_pos = strlen (line );
158+
159+ for (size_t i = 0 ; line [i ] != '\0' ; i ++ ) {
160+ char c = line [i ];
161+ if (escaped ) {
162+ escaped = 0 ;
163+ continue ;
164+ }
165+ if (in_single ) {
166+ if (c == '\\' ) escaped = 1 ;
167+ else if (c == '\'' ) in_single = 0 ;
168+ continue ;
169+ }
170+ if (in_double ) {
171+ if (c == '\\' ) escaped = 1 ;
172+ else if (c == '"' ) in_double = 0 ;
173+ continue ;
174+ }
175+
176+ if (c == '!' ) {
177+ comment_pos = i ;
178+ break ;
179+ }
180+ if (c == '\'' ) {
181+ in_single = 1 ;
182+ continue ;
183+ }
184+ if (c == '"' ) {
185+ in_double = 1 ;
186+ continue ;
187+ }
188+ if (c == '{' ) {
189+ (* brace_depth )++ ;
190+ } else if (c == '}' && * brace_depth > 0 ) {
191+ (* brace_depth )-- ;
192+ }
193+ }
194+
195+ size_t end = comment_pos ;
196+ while (end > 0 ) {
197+ char c = line [end - 1 ];
198+ if (c == ' ' || c == '\t' || c == '\r' || c == '\n' ) {
199+ end -- ;
200+ continue ;
201+ }
202+ break ;
203+ }
204+ * line_continuation = (end > 0 && line [end - 1 ] == '^' ) ? 1 : 0 ;
205+ }
206+
207+ static int run_repl (void ) {
208+ Interpreter interp ;
209+ interpreter_init (& interp , "<repl>" );
210+
211+ char * entry = NULL ;
212+ size_t entry_len = 0 ;
213+ size_t entry_cap = 0 ;
214+ int brace_depth = 0 ;
215+ int line_continuation = 0 ;
216+ fprintf (stdout , "\x1b[38;2;153;221;255mPrefix REPL. Enter statements, blank line to run buffer.\033[0m\n" );
217+
218+ for (;;) {
219+ int in_continuation = (brace_depth > 0 ) || line_continuation ;
220+ fputs (in_continuation ? "\x1b[38;2;153;221;255m..>\033[0m " : "\x1b[38;2;153;221;255m>>>\033[0m " , stdout );
221+ fflush (stdout );
222+
223+ char * line = read_line_dynamic (stdin );
224+ int eof = (line == NULL );
225+
226+ if (!eof ) {
227+ if (buf_append (& entry , & entry_len , & entry_cap , line ) != 0 ) {
228+ free (line );
229+ free (entry );
230+ interpreter_destroy (& interp );
231+ fprintf (stderr , "Out of memory\n" );
232+ return PREFIX_ERROR_MEMORY ;
233+ }
234+ repl_update_line_state (line , & brace_depth , & line_continuation );
235+ free (line );
236+ }
237+
238+ if (!eof && ((brace_depth > 0 ) || line_continuation )) {
239+ continue ;
240+ }
241+
242+ if (entry_len == 0 ) {
243+ if (eof ) break ;
244+ continue ;
245+ }
246+
247+ if (is_exit_meta_command (entry )) {
248+ break ;
249+ }
250+
251+ Lexer lex ;
252+ lexer_init (& lex , entry , "<repl>" );
253+
254+ Parser parser ;
255+ parser_init (& parser , & lex );
256+ Stmt * program = parser_parse (& parser );
257+
258+ if (!parser .had_error ) {
259+ ExecResult res = exec_program_in_env (& interp , program , interp .global_env );
260+ if (res .status == EXEC_ERROR ) {
261+ fprintf (stderr , "Runtime error: %s at %d:%d\n" ,
262+ res .error ? res .error : "error" ,
263+ res .error_line ,
264+ res .error_column );
265+ }
266+ }
267+
268+ entry_len = 0 ;
269+ if (entry ) entry [0 ] = '\0' ;
270+ brace_depth = 0 ;
271+ line_continuation = 0 ;
272+
273+ if (eof ) break ;
274+ }
275+
276+ free (entry );
277+ interpreter_destroy (& interp );
278+ return PREFIX_SUCCESS ;
279+ }
280+
97281int main (int argc , char * * argv ) {
98282 const char * path = NULL ;
99283 int verbose_flag = 0 ;
@@ -232,13 +416,10 @@ int main(int argc, char** argv) {
232416 }
233417
234418 if (!path ) {
235- if (explicit_ext_count > 0 ) {
236- fprintf (stderr , "No program provided. REPL mode is not implemented in this build.\n" );
237- extensions_shutdown ();
238- builtins_reset_dynamic ();
239- return PREFIX_SUCCESS ;
240- }
241- path = "../test.pre" ;
419+ int repl_rc = run_repl ();
420+ extensions_shutdown ();
421+ builtins_reset_dynamic ();
422+ return repl_rc ;
242423 }
243424
244425 (void )verbose_flag ;
0 commit comments