klisp

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

kgpair_mut.c (16369B)


      1 /*
      2 ** kgpair_mut.c
      3 ** Pair mutation features for the ground environment
      4 ** See Copyright Notice in klisp.h
      5 */
      6 
      7 #include <assert.h>
      8 #include <stdio.h>
      9 #include <stdlib.h>
     10 #include <stdbool.h>
     11 #include <stdint.h>
     12 
     13 #include "kstate.h"
     14 #include "kobject.h"
     15 #include "kpair.h"
     16 #include "kcontinuation.h"
     17 #include "ksymbol.h"
     18 #include "kerror.h"
     19 
     20 #include "kghelpers.h"
     21 #include "kgpair_mut.h"
     22 
     23 /* 4.7.1 set-car!, set-cdr! */
     24 void set_carB(klisp_State *K)
     25 {
     26     TValue *xparams = K->next_xparams;
     27     TValue ptree = K->next_value;
     28     TValue denv = K->next_env;
     29     klisp_assert(ttisenvironment(K->next_env));
     30     (void) denv;
     31     (void) xparams;
     32     bind_2tp(K, ptree, "pair", ttispair, pair, 
     33              "any", anytype, new_car);
     34 
     35     if(!kis_mutable(pair)) {
     36 	    klispE_throw_simple(K, "immutable pair");
     37 	    return;
     38     }
     39     kset_car(pair, new_car);
     40     kapply_cc(K, KINERT);
     41 }
     42 
     43 void set_cdrB(klisp_State *K)
     44 {
     45     TValue *xparams = K->next_xparams;
     46     TValue ptree = K->next_value;
     47     TValue denv = K->next_env;
     48     klisp_assert(ttisenvironment(K->next_env));
     49     (void) denv;
     50     (void) xparams;
     51     bind_2tp(K, ptree, "pair", ttispair, pair, 
     52              "any", anytype, new_cdr);
     53     
     54     if(!kis_mutable(pair)) {
     55 	    klispE_throw_simple(K, "immutable pair");
     56 	    return;
     57     }
     58     kset_cdr(pair, new_cdr);
     59     kapply_cc(K, KINERT);
     60 }
     61 
     62 /* Helper for copy-es-immutable & copy-es */
     63 void copy_es(klisp_State *K)
     64 {
     65     TValue *xparams = K->next_xparams;
     66     TValue ptree = K->next_value;
     67     TValue denv = K->next_env;
     68     klisp_assert(ttisenvironment(K->next_env));
     69 
     70     UNUSED(denv);
     71 
     72     /*
     73     ** xparams[0]: copy-es-immutable symbol
     74     ** xparams[1]: boolean (#t: use mutable pairs, #f: use immutable pairs)
     75     */
     76     bool mut_flag = bvalue(xparams[1]);
     77     bind_1p(K, ptree, obj);
     78 
     79     TValue copy = copy_es_immutable_h(K, obj, mut_flag);
     80     kapply_cc(K, copy);
     81 }
     82 
     83 /* 4.7.2 copy-es-immutable */
     84 /* uses copy_es */
     85 
     86 /* 5.8.1 encycle! */
     87 void encycleB(klisp_State *K)
     88 {
     89     TValue *xparams = K->next_xparams;
     90     TValue ptree = K->next_value;
     91     TValue denv = K->next_env;
     92     klisp_assert(ttisenvironment(K->next_env));
     93 /* ASK John: can the object be a cyclic list of length less than k1+k2? 
     94    the wording of the report seems to indicate that can't be the case, 
     95    and here it makes sense to forbid it because otherwise the list-metrics 
     96    of the result would differ with the expected ones (cf list-tail). 
     97    So here an error is signaled if the improper list cyclic with less pairs
     98    than needed */
     99     UNUSED(denv);
    100     UNUSED(xparams);
    101 
    102     bind_3tp(K, ptree, "any", anytype, obj,
    103              "exact integer", keintegerp, tk1,
    104              "exact integer", keintegerp, tk2);
    105 
    106     if (knegativep(tk1) || knegativep(tk2)) {
    107         klispE_throw_simple(K, "negative index");
    108         return;
    109     }
    110 
    111     if (!ttisfixint(tk1) || !ttisfixint(tk2)) {
    112         /* no list can have that many pairs */
    113         klispE_throw_simple(K, "non pair found while traversing "
    114                             "object");
    115         return;
    116     }
    117 
    118     int32_t k1 = ivalue(tk1);
    119     int32_t k2 = ivalue(tk2);
    120 
    121     TValue tail = obj;
    122 
    123     while(k1 != 0) {
    124         if (!ttispair(tail)) {
    125             unmark_list(K, obj);
    126             klispE_throw_simple(K, "non pair found while traversing "
    127                                 "object");
    128             return;
    129         } else if (kis_marked(tail)) {
    130             unmark_list(K, obj);
    131             klispE_throw_simple(K, "too few pairs in cyclic list");
    132             return;
    133         }
    134         kmark(tail);
    135         tail = kcdr(tail);
    136         --k1;
    137     }
    138 
    139     TValue fcp = tail;
    140 
    141     /* if k2 == 0 do nothing (but this still checks that the obj
    142        has at least k1 pairs */
    143     if (k2 != 0) {
    144         --k2; /* to have cycle length k2 we should discard k2-1 pairs */
    145         /* REFACTOR: should probably refactor this to avoid the 
    146            duplicated checks */
    147         while(k2 != 0) {
    148             if (!ttispair(tail)) {
    149                 unmark_list(K, obj);
    150                 klispE_throw_simple(K, "non pair found while traversing "
    151                                     "object");
    152                 return;
    153             } else if (kis_marked(tail)) {
    154                 unmark_list(K, obj);
    155                 klispE_throw_simple(K, "too few pairs in cyclic list");
    156                 return;
    157             }
    158             kmark(tail);
    159             tail = kcdr(tail);
    160             --k2;
    161         }
    162         if (!ttispair(tail)) {
    163             unmark_list(K, obj);
    164             klispE_throw_simple(K, "non pair found while traversing "
    165                                 "object");
    166             return;
    167         } else if (kis_marked(tail)) {
    168             unmark_list(K, obj);
    169             klispE_throw_simple(K, "too few pairs in cyclic list");
    170             return;
    171         } else if (!kis_mutable(tail)) {
    172             unmark_list(K, obj);
    173             klispE_throw_simple(K, "immutable pair");
    174             return;
    175         } else {
    176             kset_cdr(tail, fcp);
    177         }
    178     }
    179     unmark_list(K, obj);
    180     kapply_cc(K, KINERT);
    181 }
    182 
    183 /* 6.?? list-set! */
    184 void list_setB(klisp_State *K)
    185 {
    186     TValue *xparams = K->next_xparams;
    187     TValue ptree = K->next_value;
    188     TValue denv = K->next_env;
    189     klisp_assert(ttisenvironment(K->next_env));
    190 /* ASK John: can the object be an improper list? 
    191    We foolow list-tail here and allow it */
    192     UNUSED(denv);
    193     UNUSED(xparams);
    194 
    195     bind_3tp(K, ptree, "any", anytype, obj,
    196              "exact integer", keintegerp, tk, 
    197              "any", anytype, val);
    198 
    199     if (knegativep(tk)) {
    200         klispE_throw_simple(K, "negative index");
    201         return;
    202     }
    203 
    204     int32_t k = (ttisfixint(tk))? ivalue(tk)
    205         : ksmallest_index(K, obj, tk);
    206 
    207     while(k) {
    208         if (!ttispair(obj)) {
    209             klispE_throw_simple(K, "non pair found while traversing "
    210                                 "object");
    211             return;
    212         }
    213         obj = kcdr(obj);
    214         --k;
    215     }
    216 
    217     if (!ttispair(obj)) {
    218         klispE_throw_simple(K, "non pair found while traversing "
    219                             "object");
    220     } else if (kis_immutable(obj)) {
    221         /* this could be checked before, but the error here seems better */
    222         klispE_throw_simple(K, "immutable pair");
    223     } else {
    224         kset_car(obj, val);
    225         kapply_cc(K, KINERT);
    226     }
    227 }
    228 
    229 /* Helpers for append! */
    230 static inline void appendB_clear_last_pairs(klisp_State *K, TValue ls)
    231 {
    232     UNUSED(K);
    233     while(ttispair(ls) && kis_marked(ls)) {
    234         TValue first = ls;
    235         ls = kget_mark(ls);
    236         kunmark(first);
    237     }
    238 }
    239 
    240 /* Check that all lists (except last) are acyclic lists with non repeated mutable 
    241    last pair (if not nil), return a list of objects so that the cdr of the odd
    242    objects (1 based) should be set to the next object in the list (this will
    243    encycle! the result if necessary) */
    244 
    245 /* GC: Assumes lss is rooted */
    246 TValue appendB_get_lss_endpoints(klisp_State *K, TValue lss, int32_t apairs, 
    247                                  int32_t cpairs)
    248 {
    249     TValue elist = kcons(K, KNIL, KNIL);
    250     krooted_vars_push(K, &elist);
    251     TValue last_pair = elist;
    252     TValue tail = lss;
    253     /* this is a list of last pairs using the marks to link the pairs) */
    254     TValue last_pairs = KNIL;
    255     TValue last_apair = KNIL;
    256 
    257     while(apairs != 0 || cpairs != 0) {
    258         int32_t pairs;
    259 	
    260         if (apairs == 0) {
    261             /* this is the first run of the loop (if there is no acyclic part) 
    262                or the second run of the loop (the cyclic part), 
    263                must remember the last acyclic pair to encycle! the result */
    264             last_apair = last_pair;
    265             pairs = cpairs;
    266         } else {
    267             /* this is the first (maybe only) run of the loop 
    268                (the acyclic part) */
    269             pairs = apairs;
    270         }
    271 
    272         while(pairs--) {
    273             TValue first = kcar(tail);
    274             tail = kcdr(tail);
    275 
    276             /* skip over non final nils, but final nil
    277                should be added as last pair to let the result
    278                be even */
    279             if (ttisnil(first)) {
    280                 if (ttisnil(tail)) {
    281                     kset_cdr(last_pair, kcons(K, first, KNIL));
    282                 }
    283                 continue; 
    284             }
    285 
    286             TValue ftail = first;
    287             TValue flastp = first;
    288 
    289             /* find the last pair to check the object */
    290             while(ttispair(ftail) && !kis_marked(ftail)) {
    291                 kmark(ftail);
    292                 flastp = ftail; /* remember last pair */
    293                 ftail = kcdr(ftail);
    294             }
    295 	
    296             /* can't unmark the list till the errors are checked,
    297                otherwise the unmarking may be incorrect */
    298             if (ttisnil(tail)) {
    299                 /* last argument has special treatment */
    300                 if (ttispair(ftail) && ttisnil(kcdr(ftail))) {
    301                     /* repeated last pair, this is the only check
    302                        that is done on the last argument */
    303                     appendB_clear_last_pairs(K, last_pairs);
    304                     unmark_list(K, first);
    305                     klispE_throw_simple(K, "repeated last pairs");
    306                     return KINERT;
    307                 } else {
    308                     unmark_list(K, first);
    309                     /* add last object to the endpoints list, don't add
    310                        its last pair */
    311                     kset_cdr(last_pair, kcons(K, first, KNIL));
    312                 }
    313             } else { /* non final argument, must be an acyclic list 
    314                         with unique, mutable last pair */
    315                 if (ttisnil(ftail)) {
    316                     /* acyclic list with non repeated last pair,
    317                        check mutability */
    318                     unmark_list(K, first);
    319                     if (kis_immutable(flastp)) {
    320                         appendB_clear_last_pairs(K, last_pairs);
    321                         klispE_throw_simple(K, "immutable pair found");
    322                         return KINERT;
    323                     }
    324                     /* add the last pair to the list of last pairs */
    325                     kset_mark(flastp, last_pairs);
    326                     last_pairs = flastp;
    327 		
    328                     /* add both the first and last pair to the endpoints 
    329                        list */
    330                     TValue new_pair = kcons(K, first, KNIL);
    331                     kset_cdr(last_pair, new_pair);
    332                     last_pair = new_pair;
    333                     new_pair = kcons(K, flastp, KNIL);
    334                     kset_cdr(last_pair, new_pair);
    335                     last_pair = new_pair;
    336                 } else {
    337                     /* impoper list or repeated last pair or cyclic list */
    338                     appendB_clear_last_pairs(K, last_pairs);
    339                     unmark_list(K, first);
    340 
    341                     if (ttispair(ftail)) {
    342                         if (ttisnil(kcdr(ftail))) {
    343                             klispE_throw_simple(K, "repeated last pairs");
    344                         } else {
    345                             klispE_throw_simple(K, "cyclic list as non last "
    346                                                 "argument");
    347                         }  
    348                     } else {
    349                         klispE_throw_simple(K, "improper list as non last "
    350                                             "argument");
    351                     }
    352                     return KINERT;
    353                 }
    354             }
    355         }
    356         if (apairs != 0) {
    357             /* acyclic part done */
    358             apairs = 0;
    359         } else {
    360             /* cyclic part done, program encycle if necessary */
    361             cpairs = 0;
    362             if (!tv_equal(last_apair, last_pair)) {
    363                 TValue first_cpair = kcadr(last_apair);
    364                 kset_cdr(last_pair, kcons(K, first_cpair, KNIL));
    365             } else {
    366                 /* all elements of the cycle are (), add extra
    367                    nil to simplify the code setting the cdrs */
    368                 kset_cdr(last_pair, kcons(K, KNIL, KNIL));
    369             }
    370         }
    371     }
    372 
    373     appendB_clear_last_pairs(K, last_pairs);
    374 
    375     /* discard the first element (there is always one) because it
    376        isn't necessary, the list is used to set the last pairs of
    377        the objects to the correspoding next first pair */
    378     krooted_vars_pop(K);
    379     return kcdr(kcdr(elist));
    380 }
    381 
    382 /* 6.4.1 append! */
    383 void appendB(klisp_State *K)
    384 {
    385     TValue *xparams = K->next_xparams;
    386     TValue ptree = K->next_value;
    387     TValue denv = K->next_env;
    388     klisp_assert(ttisenvironment(K->next_env));
    389     UNUSED(xparams);
    390     UNUSED(denv);
    391     if (ttisnil(ptree)) {
    392         klispE_throw_simple(K, "no lists");
    393         return;
    394     } else if (!ttispair(ptree)) {
    395         klispE_throw_simple(K, "bad ptree");
    396         return;
    397     } else if (ttisnil(kcar(ptree))) {
    398         klispE_throw_simple(K, "empty first list");
    399         return;
    400     }
    401     TValue lss = ptree;
    402     TValue first_ls = kcar(lss);
    403     int32_t pairs, cpairs;
    404     /* ASK John: if encycle! has only one argument, can't it be cyclic? 
    405        the report says no, but the wording is poor */
    406     check_list(K, false, first_ls, NULL, NULL);
    407     check_list(K, true, lss, &pairs, &cpairs);
    408     int32_t apairs = pairs - cpairs;
    409 
    410     TValue endpoints = 
    411         appendB_get_lss_endpoints(K, lss, apairs, cpairs);
    412     /* connect all the last pairs to the corresponding next first pair,
    413        endpoints is even */
    414     while(!ttisnil(endpoints)) {
    415         TValue first = kcar(endpoints);
    416         endpoints = kcdr(endpoints);
    417         TValue second = kcar(endpoints);
    418         endpoints = kcdr(endpoints);
    419         kset_cdr(first, second);
    420     }
    421     kapply_cc(K, KINERT);
    422 }
    423 
    424 /* 6.4.2 copy-es */
    425 /* uses copy_es helper (above copy-es-immutable) */
    426 
    427 /* 6.4.3 assq */
    428 /* REFACTOR: do just one pass, maybe use generalized accum function */
    429 void assq(klisp_State *K)
    430 {
    431     TValue *xparams = K->next_xparams;
    432     TValue ptree = K->next_value;
    433     TValue denv = K->next_env;
    434     klisp_assert(ttisenvironment(K->next_env));
    435     UNUSED(xparams);
    436     UNUSED(denv);
    437 
    438     bind_2p(K, ptree, obj, ls);
    439     /* first pass, check structure */
    440     int32_t pairs;
    441     check_typed_list(K, kpairp, true, ls, &pairs, NULL);
    442     TValue tail = ls;
    443     TValue res = KNIL;
    444     while(pairs--) {
    445         TValue first = kcar(tail);
    446         if (eq2p(K, kcar(first), obj)) {
    447             res = first;
    448             break;
    449         }
    450         tail = kcdr(tail);
    451     }
    452 
    453     kapply_cc(K, res);
    454 }
    455 
    456 /* 6.4.3 memq? */
    457 /* REFACTOR: do just one pass, maybe use generalized accum function */
    458 void memqp(klisp_State *K)
    459 {
    460     TValue *xparams = K->next_xparams;
    461     TValue ptree = K->next_value;
    462     TValue denv = K->next_env;
    463     klisp_assert(ttisenvironment(K->next_env));
    464     UNUSED(xparams);
    465     UNUSED(denv);
    466 
    467     bind_2p(K, ptree, obj, ls);
    468     /* first pass, check structure */
    469     int32_t pairs;
    470     check_list(K, true, ls, &pairs, NULL);
    471     TValue tail = ls;
    472     TValue res = KFALSE;
    473     while(pairs--) {
    474         TValue first = kcar(tail);
    475         if (eq2p(K, first, obj)) {
    476             res = KTRUE;
    477             break;
    478         }
    479         tail = kcdr(tail);
    480     }
    481 
    482     kapply_cc(K, res);
    483 }
    484 
    485 /* ?.? immutable-pair?, mutable-pair */
    486 /* use ftypep */
    487 
    488 /* init ground */
    489 void kinit_pair_mut_ground_env(klisp_State *K)
    490 {
    491     TValue ground_env = G(K)->ground_env;
    492     TValue symbol, value;
    493 
    494     /* 4.7.1 set-car!, set-cdr! */
    495     add_applicative(K, ground_env, "set-car!", set_carB, 0);
    496     add_applicative(K, ground_env, "set-cdr!", set_cdrB, 0);
    497     /* 4.7.2 copy-es-immutable */
    498     add_applicative(K, ground_env, "copy-es-immutable", copy_es, 2, symbol, 
    499                     b2tv(false));
    500     /* 5.8.1 encycle! */
    501     add_applicative(K, ground_env, "encycle!", encycleB, 0);
    502     /* 6.?? list-set! */
    503     add_applicative(K, ground_env, "list-set!", list_setB, 0);
    504     /* 6.4.1 append! */
    505     add_applicative(K, ground_env, "append!", appendB, 0);
    506     /* 6.4.2 copy-es */
    507     add_applicative(K, ground_env, "copy-es", copy_es, 2, symbol, b2tv(true));
    508     /* 6.4.3 assq */
    509     add_applicative(K, ground_env, "assq", assq, 0);
    510     /* 6.4.3 memq? */
    511     add_applicative(K, ground_env, "memq?", memqp, 0);
    512     /* ?.? immutable-pair?, mutable-pair? */
    513     add_applicative(K, ground_env, "immutable-pair?", ftypep, 2, symbol, 
    514                     p2tv(kimmutable_pairp));
    515     add_applicative(K, ground_env, "mutable-pair?", ftypep, 2, symbol, 
    516                     p2tv(kmutable_pairp));
    517 }