hare

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

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:
Mencoding/json/+test/lexer.ha | 2+-
Aencoding/json/+test/value.ha | 16++++++++++++++++
Mencoding/json/lex.ha | 4++--
Aencoding/json/value.ha | 102+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mscripts/gen-stdlib | 7+++++--
Mstdlib.mk | 11+++++++----
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 \