krepl.c (8180B)
1 /* 2 ** krepl.c 3 ** klisp repl 4 ** See Copyright Notice in klisp.h 5 */ 6 #include <stdio.h> 7 #include <setjmp.h> 8 9 #include "klisp.h" 10 #include "kstate.h" 11 #include "kobject.h" 12 #include "kcontinuation.h" 13 #include "kenvironment.h" 14 #include "kerror.h" 15 #include "kread.h" 16 #include "kwrite.h" 17 #include "kstring.h" 18 #include "krepl.h" 19 #include "ksymbol.h" 20 #include "kport.h" 21 #include "kpair.h" 22 #include "ktable.h" /* for names */ 23 #include "kghelpers.h" /* for do_pass_value */ 24 25 /* Continuations */ 26 void do_repl_read(klisp_State *K); 27 void do_repl_eval(klisp_State *K); 28 void do_repl_loop(klisp_State *K); 29 void do_repl_int_error(klisp_State *K); 30 31 32 /* TODO add names & source info to the repl continuations */ 33 34 /* the underlying function of the read cont */ 35 void do_repl_read(klisp_State *K) 36 { 37 TValue *xparams = K->next_xparams; 38 TValue obj = K->next_value; 39 klisp_assert(ttisnil(K->next_env)); 40 UNUSED(xparams); 41 UNUSED(obj); 42 43 /* show prompt */ 44 fprintf(stdout, KLISP_PROMPT); 45 46 TValue port = kcdr(G(K)->kd_in_port_key); 47 klisp_assert(kfport_file(port) == stdin); 48 /* Workaround to the problem of the dangling '\n' in repl 49 (from previous line) */ 50 kread_clear_leading_whitespace_from_port(K, port); 51 kport_reset_source_info(port); /* always start with a clean source info */ 52 obj = kread_from_port(K, port, true); /* read mutable pairs */ 53 kapply_cc(K, obj); 54 } 55 56 /* the underlying function of the eval cont */ 57 void do_repl_eval(klisp_State *K) 58 { 59 TValue *xparams = K->next_xparams; 60 TValue obj = K->next_value; 61 klisp_assert(ttisnil(K->next_env)); 62 /* 63 ** xparams[0]: dynamic environment 64 */ 65 TValue denv = xparams[0]; 66 67 if (ttiseof(obj)) { 68 /* read [EOF], should terminate the repl */ 69 /* this will in turn call main_cont */ 70 /* print a newline to allow the shell a fresh line */ 71 printf("\n"); 72 /* This is ok because there is no interception possible */ 73 kset_cc(K, G(K)->root_cont); 74 kapply_cc(K, KINERT); 75 } else { 76 /* save the source code info of the object in loop_cont 77 before evaling */ 78 #if KTRACK_SI 79 kset_source_info(K, kget_cc(K), ktry_get_si(K, obj)); 80 #endif 81 82 ktail_eval(K, obj, denv); 83 } 84 } 85 86 /* this is called from both do_repl_loop and do_repl_int_error */ 87 /* GC: assumes denv is NOT rooted */ 88 void create_loop(klisp_State *K, TValue denv) 89 { 90 krooted_tvs_push(K, denv); 91 92 /* TODO this should be factored out, it is quite common */ 93 TValue error_int = kmake_operative(K, do_repl_int_error, 1, denv); 94 krooted_tvs_pop(K); /* already in cont */ 95 krooted_tvs_push(K, error_int); 96 TValue exit_guard = kcons(K, G(K)->error_cont, error_int); 97 krooted_tvs_pop(K); /* already in guard */ 98 krooted_tvs_push(K, exit_guard); 99 TValue exit_guards = kcons(K, exit_guard, KNIL); 100 krooted_tvs_pop(K); /* already in guards */ 101 krooted_tvs_push(K, exit_guards); 102 103 TValue entry_guards = KNIL; 104 105 /* this is needed for interception code */ 106 TValue env = kmake_empty_environment(K); 107 krooted_tvs_push(K, env); 108 TValue outer_cont = kmake_continuation(K, G(K)->root_cont, 109 do_pass_value, 2, entry_guards, env); 110 kset_outer_cont(outer_cont); 111 krooted_tvs_push(K, outer_cont); 112 TValue inner_cont = kmake_continuation(K, outer_cont, 113 do_pass_value, 2, exit_guards, env); 114 kset_inner_cont(inner_cont); 115 krooted_tvs_pop(K); krooted_tvs_pop(K); krooted_tvs_pop(K); 116 117 /* stack is empty now */ 118 krooted_tvs_push(K, inner_cont); 119 120 TValue loop_cont = 121 kmake_continuation(K, inner_cont, do_repl_loop, 1, denv); 122 krooted_tvs_pop(K); /* in loop cont */ 123 krooted_tvs_push(K, loop_cont); 124 TValue eval_cont = kmake_continuation(K, loop_cont, do_repl_eval, 1, denv); 125 krooted_tvs_pop(K); /* in eval cont */ 126 krooted_tvs_push(K, eval_cont); 127 TValue read_cont = kmake_continuation(K, eval_cont, do_repl_read, 0); 128 kset_cc(K, read_cont); 129 krooted_tvs_pop(K); 130 kapply_cc(K, KINERT); 131 } 132 133 /* the underlying function of the write & loop cont */ 134 void do_repl_loop(klisp_State *K) 135 { 136 TValue *xparams = K->next_xparams; 137 TValue obj = K->next_value; 138 klisp_assert(ttisnil(K->next_env)); 139 /* 140 ** xparams[0]: dynamic environment 141 */ 142 143 TValue port = kcdr(G(K)->kd_out_port_key); 144 klisp_assert(kfport_file(port) == stdout); 145 146 /* false: quote strings, escape chars */ 147 kwrite_display_to_port(K, port, obj, false); 148 kwrite_newline_to_port(K, port); 149 150 TValue denv = xparams[0]; 151 create_loop(K, denv); 152 } 153 154 /* the underlying function of the error cont */ 155 void do_repl_int_error(klisp_State *K) 156 { 157 TValue *xparams = K->next_xparams; 158 TValue ptree = K->next_value; 159 TValue denv = K->next_env; 160 klisp_assert(ttisenvironment(K->next_env)); 161 /* 162 ** xparams[0]: dynamic environment 163 */ 164 165 UNUSED(denv); 166 167 /* 168 ** ptree is (object divert) 169 */ 170 TValue obj = kcar(ptree); 171 TValue divert = kcadr(ptree); 172 173 /* FOR NOW used only for irritant list */ 174 TValue port = kcdr(G(K)->kd_error_port_key); 175 klisp_assert(ttisfport(port) && kfport_file(port) == stderr); 176 177 /* TEMP: obj should be an error obj */ 178 if (ttiserror(obj)) { 179 Error *err_obj = tv2error(obj); 180 TValue who = err_obj->who; 181 char *who_str; 182 /* TEMP? */ 183 if (ttiscontinuation(who)) 184 who = tv2cont(who)->comb; 185 186 if (ttisstring(who)) { 187 who_str = kstring_buf(who); 188 #if KTRACK_NAMES 189 } else if (khas_name(who)) { 190 TValue name = kget_name(K, who); 191 who_str = ksymbol_buf(name); 192 #endif 193 } else { 194 who_str = "?"; 195 } 196 char *msg = kstring_buf(err_obj->msg); 197 fprintf(stderr, "\n*ERROR*: \n"); 198 fprintf(stderr, "%s: %s", who_str, msg); 199 200 krooted_tvs_push(K, obj); 201 202 /* Msg + irritants */ 203 /* TODO move to a new function */ 204 if (!ttisnil(err_obj->irritants)) { 205 fprintf(stderr, ": "); 206 kwrite_display_to_port(K, port, err_obj->irritants, false); 207 } 208 kwrite_newline_to_port(K, port); 209 210 #if KTRACK_NAMES 211 #if KTRACK_SI 212 /* Location */ 213 /* TODO move to a new function */ 214 /* MAYBE: remove */ 215 if (khas_name(who) || khas_si(who)) { 216 fprintf(stderr, "Location: "); 217 kwrite_display_to_port(K, port, who, false); 218 kwrite_newline_to_port(K, port); 219 } 220 221 /* Backtrace */ 222 /* TODO move to a new function */ 223 TValue tv_cont = err_obj->cont; 224 fprintf(stderr, "Backtrace: \n"); 225 while(ttiscontinuation(tv_cont)) { 226 kwrite_display_to_port(K, port, tv_cont, false); 227 kwrite_newline_to_port(K, port); 228 Continuation *cont = tv2cont(tv_cont); 229 tv_cont = cont->parent; 230 } 231 /* add extra newline at the end */ 232 kwrite_newline_to_port(K, port); 233 #endif 234 #endif 235 krooted_tvs_pop(K); 236 } else { 237 fprintf(stderr, "\n*ERROR*: not an error object passed to " 238 "error continuation\n\n"); 239 } 240 241 UNUSED(divert); 242 TValue old_denv = xparams[0]; 243 /* this is the same as a divert */ 244 create_loop(K, old_denv); 245 } 246 247 /* call this to init the repl in a newly created klisp state */ 248 /* the standard environment should be in K->next_env */ 249 /* LOCK: the GIL should be acquired */ 250 void kinit_repl(klisp_State *K) 251 { 252 TValue std_env = K->next_env; 253 254 #if KTRACK_SI 255 /* save the root cont in next_si to let the loop continuations have 256 source info, this is hackish but works */ 257 258 K->next_si = ktry_get_si(K, G(K)->root_cont); 259 #endif 260 261 /* GC: create_loop will root std_env */ 262 create_loop(K, std_env); 263 } 264 265 /* init continuation names */ 266 void kinit_repl_cont_names(klisp_State *K) 267 { 268 /* XXX lock? */ 269 Table *t = tv2table(G(K)->cont_name_table); 270 add_cont_name(K, t, do_repl_read, "repl-read"); 271 add_cont_name(K, t, do_repl_eval, "repl-eval"); 272 add_cont_name(K, t, do_repl_loop, "repl-print-loop"); 273 add_cont_name(K, t, do_repl_int_error, "repl-int-error"); 274 }