diff options
-rw-r--r-- | eval.c | 28 | ||||
-rw-r--r-- | hash.c | 141 | ||||
-rw-r--r-- | hash.h | 12 | ||||
-rw-r--r-- | lib.c | 1 | ||||
-rw-r--r-- | parser.c | 8 | ||||
-rw-r--r-- | parser.h | 1 | ||||
-rw-r--r-- | txr.1 | 79 |
7 files changed, 171 insertions, 99 deletions
@@ -6488,15 +6488,22 @@ void eval_init(void) &call_f, &iter_begin_f, &iter_from_binding_f, &iter_more_f, &iter_item_f, &iter_step_f, &unbound_s, &origin_hash, &const_foldable_hash, convert(val *, 0)); - top_fb = make_hash(t, t, nil); - top_vb = make_hash(t, t, nil); - top_mb = make_hash(t, t, nil); - top_smb = make_hash(t, t, nil); - special = make_hash(t, t, nil); - builtin = make_hash(t, t, nil); + top_fb = make_hash(nil, nil, nil); + top_vb = make_hash(nil, nil, nil); + top_mb = make_hash(nil, nil, nil); + top_smb = make_hash(nil, nil, nil); + special = make_hash(nil, nil, nil); + builtin = make_hash(nil, nil, nil); op_table = make_hash(nil, nil, nil); pm_table = make_hash(nil, nil, nil); + tweak_hash(top_fb, hash_weak_and); + tweak_hash(top_vb, hash_weak_and); + tweak_hash(top_mb, hash_weak_and); + tweak_hash(top_smb, hash_weak_and); + tweak_hash(special, hash_weak_and); + tweak_hash(builtin, hash_weak_and); + call_f = func_n1v(generic_funcall); iter_begin_f = func_n1(iter_begin); iter_from_binding_f = chain(cdr_f, iter_begin_f, nao); @@ -7325,15 +7332,6 @@ void eval_init(void) void eval_compat_fixup(int compat_ver) { - if (compat_ver <= 266) { - tweak_hash(top_fb, t, nil); - tweak_hash(top_vb, t, nil); - tweak_hash(top_mb, t, nil); - tweak_hash(top_smb, t, nil); - tweak_hash(special, t, nil); - tweak_hash(builtin, t, nil); - } - if (compat_ver <= 257) reg_fun(intern(lit("lexical-var-p"), user_package), func_n2(old_lexical_var_p)); @@ -50,13 +50,6 @@ #include "time.h" #include "hash.h" -typedef enum hash_flags { - hash_weak_none = 0, - hash_weak_keys = 1, - hash_weak_vals = 2, - hash_weak_both = 3 -} hash_flags_t; - typedef enum hash_type { hash_type_eq, hash_type_eql, @@ -91,7 +84,7 @@ static_forward(struct hash_ops hash_eq_ops); static_forward(struct hash_ops hash_eql_ops); static_forward(struct hash_ops hash_equal_ops); -val weak_keys_k, weak_vals_k, userdata_k; +val weak_keys_k, weak_vals_k, weak_and_k, weak_or_k, userdata_k; val equal_based_k, eql_based_k, eq_based_k; val hash_seed_s; @@ -534,7 +527,7 @@ static void hash_print_op(val hash, val out, val pretty, struct strm_ctx *ctx) put_char(chr(' '), out); need_space = 1; switch (h->flags) { - case hash_weak_both: + case hash_weak_or: obj_print_impl(weak_keys_k, out, pretty, ctx); put_char(chr(' '), out); /* fallthrough */ @@ -544,6 +537,9 @@ static void hash_print_op(val hash, val out, val pretty, struct strm_ctx *ctx) case hash_weak_keys: obj_print_impl(weak_keys_k, out, pretty, ctx); break; + case hash_weak_and: + obj_print_impl(weak_and_k, out, pretty, ctx); + break; default: break; } @@ -634,7 +630,8 @@ static void hash_mark(val hash) } } break; - case hash_weak_both: + case hash_weak_or: + case hash_weak_and: /* mark nothing */ break; } @@ -809,6 +806,13 @@ static val do_make_hash(val weak_keys, val weak_vals, h->usecount = 0; + if (weak_keys) { + if (weak_keys == weak_and_k) + h->flags = hash_weak_and; + else if (weak_keys == weak_or_k) + h->flags = hash_weak_or; + } + switch (type) { case hash_type_eq: h->hops = &hash_eq_ops; @@ -826,13 +830,11 @@ static val do_make_hash(val weak_keys, val weak_vals, } } -val tweak_hash(val hash, val weak_keys, val weak_vals) +void tweak_hash(val hash, hash_flags_t flags) { - val self = lit("tweak-hash"); + val self = lit("internal-error"); struct hash *h = coerce(struct hash *, cobj_handle(self, hash, hash_cls)); - int flags = ((weak_vals != nil) << 1) | (weak_keys != nil); - h->flags = convert(hash_flags_t, flags); - return hash; + h->flags = flags; } val make_seeded_hash(val weak_keys, val weak_vals, val equal_based, val seed) @@ -1280,51 +1282,50 @@ static void do_weak_tables(void) } } break; - case hash_weak_both: + case hash_weak_and: /* Sweep through all entries. Delete any which have keys - or values that are garbage. */ - if (opt_compat && opt_compat <= 266) { - for (i = 0; i < h->modulus; i++) { - val *pchain = &h->table->v.vec[i]; - val *iter; - - for (iter = pchain; *iter; ) { - val entry = us_car(*iter); - if (!gc_is_reachable(us_car(entry)) || !gc_is_reachable(us_cdr(entry))) - { - *iter = us_cdr(*iter); + and values that are garbage. */ + for (i = 0; i < h->modulus; i++) { + val *pchain = &h->table->v.vec[i]; + val *iter; + + for (iter = pchain; *iter; ) { + val entry = us_car(*iter); + if (!gc_is_reachable(us_car(entry)) && !gc_is_reachable(us_cdr(entry))) { + *iter = us_cdr(*iter); #if CONFIG_EXTRA_DEBUGGING - if (!gc_is_reachable(us_car(entry)) && us_car(entry) == break_obj) - breakpt(); - if (!gc_is_reachable(us_cdr(entry)) && us_cdr(entry) == break_obj) - breakpt(); + if (!gc_is_reachable(us_car(entry)) && us_car(entry) == break_obj) + breakpt(); + if (!gc_is_reachable(us_cdr(entry)) && us_cdr(entry) == break_obj) + breakpt(); #endif - } else { - iter = us_cdr_p(*iter); - c++; - } + } else { + iter = us_cdr_p(*iter); + c++; } } - } else { - for (i = 0; i < h->modulus; i++) { - val *pchain = &h->table->v.vec[i]; - val *iter; - - for (iter = pchain; *iter; ) { - val entry = us_car(*iter); - if (!gc_is_reachable(us_car(entry)) && !gc_is_reachable(us_cdr(entry))) - { - *iter = us_cdr(*iter); + } + break; + case hash_weak_or: + /* Sweep through all entries. Delete any which have keys + or values that are garbage. */ + for (i = 0; i < h->modulus; i++) { + val *pchain = &h->table->v.vec[i]; + val *iter; + + for (iter = pchain; *iter; ) { + val entry = us_car(*iter); + if (!gc_is_reachable(us_car(entry)) || !gc_is_reachable(us_cdr(entry))) { + *iter = us_cdr(*iter); #if CONFIG_EXTRA_DEBUGGING - if (!gc_is_reachable(us_car(entry)) && us_car(entry) == break_obj) - breakpt(); - if (!gc_is_reachable(us_cdr(entry)) && us_cdr(entry) == break_obj) - breakpt(); + if (!gc_is_reachable(us_car(entry)) && us_car(entry) == break_obj) + breakpt(); + if (!gc_is_reachable(us_cdr(entry)) && us_cdr(entry) == break_obj) + breakpt(); #endif - } else { - iter = us_cdr_p(*iter); - c++; - } + } else { + iter = us_cdr_p(*iter); + c++; } } } @@ -1404,7 +1405,8 @@ static val equal_based_p(val equal, val eql, val eq, val wkeys) val hashv(struct args *args) { - val wkeys = nil, wvals = nil, equal = nil, eql = nil; + val self = lit("hash"); + val wkeys = nil, wvals = nil, equal = nil, eql = nil, wand = nil, wor = nil; val eq = nil, userdata = nil; struct args_bool_key akv[] = { { weak_keys_k, nil, &wkeys }, @@ -1412,14 +1414,31 @@ val hashv(struct args *args) { equal_based_k, nil, &equal }, { eql_based_k, nil, &eql }, { eq_based_k, nil, &eq }, + { weak_and_k, nil, &wand }, + { weak_or_k, nil, &wor }, { userdata_k, t, &userdata } }; - val ebp = (args_keys_extract(args, akv, sizeof akv / sizeof akv[0]), - equal_based_p(equal, eql, eq, wkeys)); - val hash = if3(eq, make_eq_hash(wkeys, wvals), make_hash(wkeys, wvals, ebp)); - if (userdata) - set_hash_userdata(hash, userdata); - return hash; + + args_keys_extract(args, akv, sizeof akv / sizeof akv[0]); + + if (wand && wor) + uw_throwf(error_s, lit("~a: both ~s and ~s specified"), + self, weak_and_k, weak_or_k, nao); + + if (wand) + wkeys = weak_and_k; + else if (wor) + wkeys = weak_or_k; + + { + val ebp = equal_based_p(equal, eql, eq, wkeys); + val hash = if3(eq, + make_eq_hash(wkeys, wvals), + make_hash(wkeys, wvals, ebp)); + if (userdata) + set_hash_userdata(hash, userdata); + return hash; + } } val hashl(val arglist) @@ -1943,6 +1962,8 @@ void hash_init(void) { weak_keys_k = intern(lit("weak-keys"), keyword_package); weak_vals_k = intern(lit("weak-vals"), keyword_package); + weak_and_k = intern(lit("weak-and"), keyword_package); + weak_or_k = intern(lit("weak-or"), keyword_package); equal_based_k = intern(lit("equal-based"), keyword_package); eql_based_k = intern(lit("eql-based"), keyword_package); eq_based_k = intern(lit("eq-based"), keyword_package); @@ -25,6 +25,14 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +typedef enum hash_flags { + hash_weak_none = 0, + hash_weak_keys = 1, + hash_weak_vals = 2, + hash_weak_or = 3, + hash_weak_and = 4, +} hash_flags_t; + struct hash_iter { struct hash_iter *next; val hash; @@ -32,14 +40,14 @@ struct hash_iter { val cons; }; -extern val weak_keys_k, weak_vals_k, userdata_k; +extern val weak_keys_k, weak_vals_k, weak_and_k, weak_or_k, userdata_k; extern val equal_based_k, eql_based_k, eq_based_k; extern struct cobj_class *hash_cls; ucnum equal_hash(val obj, int *count, ucnum); val make_seeded_hash(val weak_keys, val weak_vals, val equal_based, val seed); -val tweak_hash(val hash, val weak_keys, val weak_vals); +void tweak_hash(val hash, hash_flags_t); val make_hash(val weak_keys, val weak_vals, val equal_based); val make_eq_hash(val weak_keys, val weak_vals); val make_similar_hash(val existing); @@ -14052,7 +14052,6 @@ int compat_fixup(int compat_ver) eval_compat_fixup(compat_ver); rand_compat_fixup(compat_ver); - parse_compat_fixup(compat_ver); arith_compat_fixup(compat_ver); ffi_compat_fixup(compat_ver); regex_compat_fixup(compat_ver); @@ -1878,7 +1878,7 @@ void parse_init(void) parser_cls = cobj_register(parser_s); protect(&stream_parser_hash, &unique_s, &catch_all, convert(val *, 0)); - stream_parser_hash = make_hash(t, t, nil); + stream_parser_hash = make_hash(weak_and_k, nil, nil); catch_all = cons(t, nil); parser_l_init(); @@ -1897,9 +1897,3 @@ void parse_init(void) reg_fun(intern(lit("repl"), system_package), func_n4(repl)); reg_mac(json_s, func_n2(me_json)); } - -void parse_compat_fixup(int compat_ver) -{ - if (compat_ver <= 266) - tweak_hash(stream_parser_hash, t, nil); -} @@ -148,4 +148,3 @@ val parser_set_lineno(val self, val stream, val lineno); val parser_errors(val parser); val parse_errors(val stream); void parse_init(void); -void parse_compat_fixup(int compat_ver); @@ -51064,15 +51064,18 @@ entries disappear. This happens even if the values are themselves reachable. Vice versa, when an object appearing as a value in one or more weak-value hash tables becomes unreachable, those entries disappear, even if the keys are reachable. When a hash table has both weak keys and weak values, then an -entry is removed when neither its key nor its value is reachable. In other -words, if either the key or value is reachable, the entry is retained. -Note: this behavior differed in \*(TX 266 and earlier versions. +the behavior is one of two possible semantics. Under the +.codn or -semantics, +the hash table entry is removed if either the key or the value is unreachable. +Under the +.codn and -semantics, +the entry is removed only if both the key and value are unreachable. If the keys of a weak-key hash table are reachable from the values, or if the values of a weak-key hash table are reachable from the keys, then the weak semantics is defeated for the affected entries: the hash table retains those entries as if it were an ordinary table. A hash table with both weak keys and -values does not have this issue. +values does not have this issue, regardless of its semantics. An open traversal of a hash table is performed by the .code maphash @@ -51123,7 +51126,7 @@ becomes unspecified. These functions construct a new hash table. .code make-hash -takes three mandatory Boolean arguments. The +takes three mandatory Boolean arguments. The Boolean .meta weak-keys argument specifies whether the hash table shall have weak keys. The .meta weak-vals @@ -51131,6 +51134,39 @@ argument specifies whether it shall have weak values, and .meta equal-based specifies whether it is .codn equal -based. + +If the +.meta weak-keys +argument is one of the keywords +.code :weak-and +or +.code :weak-or +then the hash table shall have both weak keys and weak values, with the +semantics implied by the keyword: +.code :weak-and +specifies +.codn and -semantics +and +.code :weak-or +specifies +code or -semantics. +The +.meta weak-vals +argument is then ignored. + +If both +.meta weak-keys +and +.meta weak-values +are true, and +.meta weak-keys +is not one of the keywords +.code :weak-and +or +.codn :weak-or , +then the hash table has +.codn or -semantics. + The .code hash function defaults all three of these properties to false, @@ -51159,6 +51195,8 @@ function provides an alternative interface. It accepts optional keyword arguments. The supported keyword symbols are: .codn :weak-keys , .codn :weak-vals , +.codn :weak-and , +.codn :weak-or , .codn :equal-based , .code :eql-based .code :eq-based @@ -51185,11 +51223,33 @@ function produces an hash table by default. If -.code :weak-keys +.codn :weak-keys , +.code :weak-and +or +.code :weak-or is specified, then .code :equal-based may not be specified. +At most one of +.code :weak-and +or +.code :weak-or +may be specified. If either of these is specified, then the +.code :weak-keys +and +.code :weak-values +keywords are redundant and unnecessary. + +If +.code :weak-keys +and +.code :weak-values +are both specified, and +.code :weak-and +isn't specified, the situation is equivalent to +.codn :weak-or . + If .code :userdata is present, it must be followed by an argument value; that value @@ -83088,13 +83148,6 @@ of these version values, the described behaviors are provided if is given an argument which is equal or lower. For instance .code "-C 103" selects the behaviors described below for version 105, but not those for 102. -.IP 266 -Until \*(TX 266, hash tables with both weak values and keys are subject to -a conjunctive rule for entry retention: entries are retained if both the -key and value are reachable objects. If either the key or value is unreachable, -the entry is eligible for removal. The behavior changed in favor of a disjunctive -rule: the hash entry is retained if either the key or value is reachable. -A compatibility option value of 266 or lower restores the old behavior. .IP 265 Until \*(TX 265, the .code with-resources |