commit ed1689dfbef1b30ab1f4f59083ab1bd1dffa9b0a
parent a112f1369a2f5e01f7bbe170dddabc06df571ca2
Author: Drew DeVault <sir@cmpwn.com>
Date: Mon, 9 May 2022 15:26:13 +0200
encoding::json: (mostly) implement value type
Signed-off-by: Drew DeVault <sir@cmpwn.com>
Diffstat:
6 files changed, 133 insertions(+), 9 deletions(-)
diff --git a/encoding/json/+test/lexer.ha b/encoding/json/+test/lexer.ha
@@ -22,7 +22,7 @@ use io;
const src = strings::toutf8(cases[i].0);
const src = bufio::fixed(src, io::mode::READ);
const lexer = lex(&src);
- defer finish(&lexer);
+ defer close(&lexer);
for (let j = 0z; j < len(cases[i].1); j += 1) {
const want = cases[i].1[j];
diff --git a/encoding/json/+test/value.ha b/encoding/json/+test/value.ha
@@ -0,0 +1,16 @@
+@test fn object() void = {
+ let obj = newobject();
+ defer finish(&obj);
+
+ set(&obj, "hello", "world");
+ set(&obj, "foo", "bar");
+ set(&obj, "the answer", 42.0);
+
+ assert(get(&obj, "hello") as str == "world");
+ assert(get(&obj, "foo") as str == "foo");
+ assert(get(&obj, "the answer") as str == 42.0);
+ assert(get(&obj, "nonexistent") is void);
+
+ del(&obj, "hello");
+ assert(get(&obj, "hello") is void);
+};
diff --git a/encoding/json/lex.ha b/encoding/json/lex.ha
@@ -14,7 +14,7 @@ export type lexer = struct {
};
// Creates a new JSON lexer. The caller can obtain tokens with [[next]] and
-// should pass the result to [[finish]] when they're done with it.
+// should pass the result to [[close]] when they're done with it.
export fn lex(src: io::handle) lexer = {
let buf: []u8 = alloc([0...], os::BUFSIZ);
return lexer {
@@ -25,7 +25,7 @@ export fn lex(src: io::handle) lexer = {
};
// Frees state associated with a JSON lexer.
-export fn finish(lex: *lexer) void = {
+export fn close(lex: *lexer) void = {
free(lex.buffer);
io::close(&lex.strbuf)!;
};
diff --git a/encoding/json/value.ha b/encoding/json/value.ha
@@ -0,0 +1,102 @@
+use hash::fnv;
+use strings;
+
+// TODO: Resize table as appropriate
+def DEFAULT_OBJECT_BUCKETS: size = 32;
+
+export type object = struct {
+ buckets: [][](str, value),
+};
+
+// A JSON value.
+export type value = (f64 | str | bool | _null | []value | object);
+
+// Initializes a new (empty) JSON object. Call [[finish]] to free associated
+// resources when you're done using it.
+export fn newobject() object = {
+ return object {
+ buckets = alloc([[]...], DEFAULT_OBJECT_BUCKETS),
+ };
+};
+
+// Gets a value from a JSON object. The return value is borrowed from the
+// object.
+export fn get(obj: *object, key: str) (*value | void) = {
+ const hash = fnv::string64(key): size;
+ const bucket = &obj.buckets[hash % len(obj.buckets)];
+ for (let i = 0z; i < len(bucket); i += 1) {
+ if (bucket[i].0 == key) {
+ return &bucket[i].1;
+ };
+ };
+};
+
+// Sets a value in a JSON object. Assumes ownership over the provided value.
+export fn set(obj: *object, key: str, val: const *value) void = {
+ const val = dup(val);
+ const hash = fnv::string64(key): size;
+ const bucket = &obj.buckets[hash % len(obj.buckets)];
+ for (let i = 0z; i < len(bucket); i += 1) {
+ if (bucket[i].0 == key) {
+ finish(&bucket[i].1);
+ bucket[i].1 = val;
+ return;
+ };
+ };
+ append(bucket, (strings::dup(key), val));
+};
+
+// Deletes a value from a JSON object.
+export fn del(obj: *object, key: str) void = {
+ const hash = fnv::string64(key): size;
+ const bucket = &obj.buckets[hash % len(obj.buckets)];
+ for (let i = 0z; i < len(bucket); i += 1) {
+ if (bucket[i].0 == key) {
+ free(bucket[i].0);
+ finish(&bucket[i].1);
+ delete(bucket[i]);
+ break;
+ };
+ };
+};
+
+// Duplicates a JSON value. The caller must pass the return value to [[finish]]
+// to free associated resources when they're done using it.
+export fn dup(val: *value) value = {
+ // XXX: Match overhaul
+ match (*val) {
+ case let s: str =>
+ return strings::dup(s);
+ case let v: []value =>
+ let new: []value = alloc([], len(v));
+ for (let i = 0z; i < len(v); i += 1) {
+ append(new, dup(&v[i]));
+ };
+ return new;
+ case let o: object =>
+ abort(); // TODO: Implement me after iter
+ };
+};
+
+// Frees state associated with a JSON value.
+export fn finish(val: *value) void = {
+ // XXX: Match overhaul
+ match (*val) {
+ case let s: str =>
+ free(s);
+ case let v: []value =>
+ for (let i = 0z; i < len(v); i += 1) {
+ finish(&v[i]);
+ };
+ free(v);
+ case let o: object =>
+ for (let i = 0z; i < len(o.buckets); i += 1) {
+ for (let j = 0z; j < len(o.buckets[i]); j += 1) {
+ free(&o.buckets[i][j].0);
+ finish(&o.buckets[i][j].1);
+ };
+ free(o.buckets[i]);
+ };
+ case => void;
+ };
+};
diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib
@@ -514,12 +514,15 @@ encoding_json() {
then
gen_srcs encoding::json \
types.ha \
- lex.ha
+ lex.ha \
+ value.ha
else
gen_srcs encoding::json \
types.ha \
lex.ha \
- +test/lexer.ha
+ value.ha \
+ +test/lexer.ha \
+ +test/value.ha
fi
gen_ssa encoding::json ascii bufio io strio os encoding::utf8 strings \
strconv
diff --git a/stdlib.mk b/stdlib.mk
@@ -1060,9 +1060,10 @@ $(HARECACHE)/encoding/hex/encoding_hex-any.ssa: $(stdlib_encoding_hex_any_srcs)
# encoding::json (+any)
stdlib_encoding_json_any_srcs = \
$(STDLIB)/encoding/json/types.ha \
- $(STDLIB)/encoding/json/lex.ha
+ $(STDLIB)/encoding/json/lex.ha \
+ $(STDLIB)/encoding/json/value.ha
-$(HARECACHE)/encoding/json/encoding_json-any.ssa: $(stdlib_encoding_json_any_srcs) $(stdlib_rt) $(stdlib_ascii_$(PLATFORM)) $(stdlib_bufio_$(PLATFORM)) $(stdlib_io_$(PLATFORM)) $(stdlib_strio_$(PLATFORM)) $(stdlib_os_$(PLATFORM)) $(stdlib_encoding_utf8_$(PLATFORM)) $(stdlib_strings_$(PLATFORM))
+$(HARECACHE)/encoding/json/encoding_json-any.ssa: $(stdlib_encoding_json_any_srcs) $(stdlib_rt) $(stdlib_ascii_$(PLATFORM)) $(stdlib_bufio_$(PLATFORM)) $(stdlib_io_$(PLATFORM)) $(stdlib_strio_$(PLATFORM)) $(stdlib_os_$(PLATFORM)) $(stdlib_encoding_utf8_$(PLATFORM)) $(stdlib_strings_$(PLATFORM)) $(stdlib_strconv_$(PLATFORM))
@printf 'HAREC \t$@\n'
@mkdir -p $(HARECACHE)/encoding/json
@HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Nencoding::json \
@@ -3160,9 +3161,11 @@ $(TESTCACHE)/encoding/hex/encoding_hex-any.ssa: $(testlib_encoding_hex_any_srcs)
testlib_encoding_json_any_srcs = \
$(STDLIB)/encoding/json/types.ha \
$(STDLIB)/encoding/json/lex.ha \
- $(STDLIB)/encoding/json/+test/lexer.ha
+ $(STDLIB)/encoding/json/value.ha \
+ $(STDLIB)/encoding/json/+test/lexer.ha \
+ $(STDLIB)/encoding/json/+test/value.ha
-$(TESTCACHE)/encoding/json/encoding_json-any.ssa: $(testlib_encoding_json_any_srcs) $(testlib_rt) $(testlib_ascii_$(PLATFORM)) $(testlib_bufio_$(PLATFORM)) $(testlib_io_$(PLATFORM)) $(testlib_strio_$(PLATFORM)) $(testlib_os_$(PLATFORM)) $(testlib_encoding_utf8_$(PLATFORM)) $(testlib_strings_$(PLATFORM))
+$(TESTCACHE)/encoding/json/encoding_json-any.ssa: $(testlib_encoding_json_any_srcs) $(testlib_rt) $(testlib_ascii_$(PLATFORM)) $(testlib_bufio_$(PLATFORM)) $(testlib_io_$(PLATFORM)) $(testlib_strio_$(PLATFORM)) $(testlib_os_$(PLATFORM)) $(testlib_encoding_utf8_$(PLATFORM)) $(testlib_strings_$(PLATFORM)) $(testlib_strconv_$(PLATFORM))
@printf 'HAREC \t$@\n'
@mkdir -p $(TESTCACHE)/encoding/json
@HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Nencoding::json \