hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

value.ha (4759B)


      1 // License: MPL-2.0
      2 // (c) 2022 Drew DeVault <sir@cmpwn.com>
      3 use hash::fnv;
      4 use strings;
      5 
      6 // TODO: Resize table as appropriate
      7 export def OBJECT_BUCKETS: size = 32;
      8 
      9 export type object = struct {
     10 	buckets: [OBJECT_BUCKETS][](str, value),
     11 };
     12 
     13 // A JSON value.
     14 export type value = (f64 | str | bool | _null | []value | object);
     15 
     16 // Initializes a new (empty) JSON object. Call [[finish]] to free associated
     17 // resources when you're done using it.
     18 export fn newobject() object = {
     19 	return object { ... };
     20 };
     21 
     22 // Gets a value from a JSON object. The return value is borrowed from the
     23 // object.
     24 export fn get(obj: *object, key: str) (*value | void) = {
     25 	const hash = fnv::string(key);
     26 	const bucket = &obj.buckets[hash % len(obj.buckets)];
     27 	for (let i = 0z; i < len(bucket); i += 1) {
     28 		if (bucket[i].0 == key) {
     29 			return &bucket[i].1;
     30 		};
     31 	};
     32 };
     33 
     34 // Sets a value in a JSON object. The key and value will be duplicated.
     35 export fn set(obj: *object, key: const str, val: const value) void = {
     36 	const hash = fnv::string(key);
     37 	const bucket = &obj.buckets[hash % len(obj.buckets)];
     38 	for (let i = 0z; i < len(bucket); i += 1) {
     39 		if (bucket[i].0 == key) {
     40 			finish(bucket[i].1);
     41 			bucket[i].1 = dup(val);
     42 			return;
     43 		};
     44 	};
     45 	append(bucket, (strings::dup(key), dup(val)));
     46 };
     47 
     48 // Deletes values from a JSON object, if they are present.
     49 export fn del(obj: *object, keys: const str...) void = {
     50 	for (let i = 0z; i < len(keys); i += 1) {
     51 		const key = keys[i];
     52 		const hash = fnv::string(key);
     53 		const bucket = &obj.buckets[hash % len(obj.buckets)];
     54 		for (let i = 0z; i < len(bucket); i += 1) {
     55 			if (bucket[i].0 == key) {
     56 				free(bucket[i].0);
     57 				finish(bucket[i].1);
     58 				delete(bucket[i]);
     59 				break;
     60 			};
     61 		};
     62 	};
     63 };
     64 
     65 // Clears all values from a JSON object, leaving it empty.
     66 export fn reset(obj: *object) void = {
     67 	let it = iter(obj);
     68 	for (true) match (next(&it)) {
     69 	case void =>
     70 		break;
     71 	case let v: (const str, const *value) =>
     72 		del(obj, v.0);
     73 	};
     74 };
     75 
     76 export type iterator = struct {
     77 	obj: *object,
     78 	i: size,
     79 	j: size,
     80 };
     81 
     82 // Creates an iterator that enumerates over the key/value pairs in an
     83 // [[object]].
     84 export fn iter(obj: *object) iterator = {
     85 	return iterator { obj = obj, ... };
     86 };
     87 
     88 // Returns the next key/value pair from this iterator, or void if none remain.
     89 export fn next(iter: *iterator) ((const str, const *value) | void) = {
     90 	for (iter.i < len(iter.obj.buckets); iter.i += 1) {
     91 		const bucket = &iter.obj.buckets[iter.i];
     92 		for (iter.j < len(bucket)) {
     93 			const key = bucket[iter.j].0;
     94 			const val = &bucket[iter.j].1;
     95 			iter.j += 1;
     96 			return (key, val);
     97 		};
     98 		iter.j = 0;
     99 	};
    100 };
    101 
    102 // Duplicates a JSON value. The caller must pass the return value to [[finish]]
    103 // to free associated resources when they're done using it.
    104 export fn dup(val: value) value = {
    105 	match (val) {
    106 	case let s: str =>
    107 		return strings::dup(s);
    108 	case let v: []value =>
    109 		let new: []value = alloc([], len(v));
    110 		for (let i = 0z; i < len(v); i += 1) {
    111 			append(new, dup(v[i]));
    112 		};
    113 		return new;
    114 	case let o: object =>
    115 		let new = newobject();
    116 		const i = iter(&o);
    117 		for (true) {
    118 			const pair = match (next(&i)) {
    119 			case void =>
    120 				break;
    121 			case let pair: (const str, const *value) =>
    122 				yield pair;
    123 			};
    124 			set(&new, pair.0, *pair.1);
    125 		};
    126 		return new;
    127 	case =>
    128 		return val;
    129 	};
    130 };
    131 
    132 // Checks two JSON values for equality.
    133 export fn equal(a: value, b: value) bool = {
    134 	match (a) {
    135 	case _null =>
    136 		return b is _null;
    137 	case let a: bool =>
    138 		return b is bool && a == b as bool;
    139 	case let a: f64 =>
    140 		return b is f64 && a == b as f64;
    141 	case let a: str =>
    142 		return b is str && a == b as str;
    143 	case let a: []value =>
    144 		if (!(b is []value)) return false;
    145 		const b = b as []value;
    146 		if (len(a) != len(b)) return false;
    147 		for (let i = 0z; i < len(a); i += 1) {
    148 			if (!equal(a[i], b[i])) {
    149 				return false;
    150 			};
    151 		};
    152 		return true;
    153 	case let a: object =>
    154 		if (!(b is object)) return false;
    155 		let a = iter(&a), b = iter(&(b as object));
    156 		for (true) match (next(&a)) {
    157 		case let a: (const str, const *value) =>
    158 			match (next(&b)) {
    159 			case let b: (const str, const *value) =>
    160 				if (a.0 != b.0 || !equal(*a.1, *b.1)) {
    161 					return false;
    162 				};
    163 			};
    164 		case void =>
    165 			return next(&b) is void;
    166 		};
    167 		return true;
    168 	};
    169 };
    170 
    171 // Frees state associated with a JSON value.
    172 export fn finish(val: value) void = {
    173 	match (val) {
    174 	case let s: str =>
    175 		free(s);
    176 	case let v: []value =>
    177 		for (let i = 0z; i < len(v); i += 1) {
    178 			finish(v[i]);
    179 		};
    180 		free(v);
    181 	case let o: object =>
    182 		for (let i = 0z; i < len(o.buckets); i += 1) {
    183 			const bucket = &o.buckets[i];
    184 			for (let j = 0z; j < len(bucket); j += 1) {
    185 				free(bucket[j].0);
    186 				finish(bucket[j].1);
    187 			};
    188 			// TODO: https://todo.sr.ht/~sircmpwn/hare/690
    189 			//free(bucket);
    190 		};
    191 	case => void;
    192 	};
    193 };