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 }