klisp

an open source interpreter for the Kernel Programming Language.
git clone http://git.hanabi.in/repos/klisp.git
Log | Files | Refs | README

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 }