std json parser

This commit is contained in:
2026-07-05 15:22:50 +02:00
parent 873aa3e1cc
commit aea9ce911a
14 changed files with 275 additions and 84 deletions

View File

@@ -38,14 +38,14 @@ func Array.push[xs: Array, x: any] : void
func Array.free[xs: Array] : void
if xs->data != 0
xs->data->free()
mem.free(xs)
(xs as ptr)->free()
func Array.free_with_items[xs: Array] : void
if xs->data != 0
for i in 0..xs->size
mem.free(xs->nth(i))
(xs->nth(i) as ptr)->free()
xs->data->free()
mem.free(xs)
(xs as ptr)->free()
func Array.pop[xs: Array] : any
if xs->size == 0
@@ -137,7 +137,7 @@ func str.split[haystack: str, needle: str]: Array
return result
else
for i in 0..haystack_len
result->push(haystack->substr(i, 1))
result->push(haystack->substr_n(i, 1))
return result
start := 0
@@ -150,13 +150,13 @@ func str.split[haystack: str, needle: str]: Array
match = false
break
if match
result->push(haystack->substr(start, i - start))
result->push(haystack->substr_n(start, i - start))
start = i + needle_len
i += needle_len
continue
i += 1
result->push(haystack->substr(start, haystack_len - start))
result->push(haystack->substr_n(start, haystack_len - start))
return result
const HashMap._TABLE_SIZE = 100
@@ -214,27 +214,36 @@ func HashMap.delete[map: HashMap, key: str] : void
map->table->set(index, current->next)
current->key->free()
mem.free(current)
(current as ptr)->free()
return
prev = current
current = current->next
func HashMap.keys[map: HashMap] : Array
out := []
for i in 0..HashMap._TABLE_SIZE
current : HashMap._Node = map->table->nth(i)
while current as ptr
out->push(current->key)
current = current->next
return out
func HashMap.free_with_keys[map: HashMap] : void
for i in 0..HashMap._TABLE_SIZE
current : HashMap._Node = map->table->nth(i)
while current as ptr
tmp := current
current = current->next
tmp->key->free()
(tmp as ptr)->free()
map->table->free()
(map as ptr)->free()
func HashMap._djb2[key: str] : i64
hash := 5381
for i in 0..key->len()
hash = ((hash << 5) + hash) + key[i]
hash = (hash & 0x7fffffffffffffff) // prevent negative
return hash % HashMap._TABLE_SIZE
func HashMap.free[map: HashMap] : void
for i in 0..HashMap._TABLE_SIZE
current : HashMap._Node = map->table->nth(i)
while current as ptr
tmp := current
current = current->next
tmp->key->free()
mem.free(tmp)
map->table->free()
mem.free(map)

View File

@@ -85,9 +85,7 @@ func io.read_line[] : str
if c == '\n'
break
b->append_char(c)
s := b->build()
b->destroy()
return s
return b->build_and_free_buffer()
func io.read_text_file[path: str] : str, bool
fd := _builtin_syscall(SYS_openat, AT_FDCWD, path, O_RDONLY, 0)

200
std/json.zr Normal file
View File

@@ -0,0 +1,200 @@
include "std/containers.zr"
const JSON_OBJECT = 0
const JSON_ARRAY = 1
const JSON_STRING = 2
const JSON_NUMBER = 3
const JSON_NULL = 4
const JSON_BOOL = 5
struct json.Value
type: i64
value: any
func json.Value.dump[v: json.Value] : str
s := new str.Builder
if v->type == JSON_OBJECT
map := v->value as HashMap
keys := map->keys()
s->append_char('{')
for i in 0..keys->size
if i > 0
s->append_char(',')
s->append_char('"')
s->append(keys->nth(i) as str)
s->append_char('"')
s->append_char(':')
value, _ := map->get(keys->nth(i) as str)
s->append((value as json.Value)->dump())
s->append_char('}')
keys->free()
else if v->type == JSON_ARRAY
arr := v->value as Array
s->append_char('[')
for i in 0..arr->size
if i > 0
s->append_char(',')
s->append((arr->nth(i) as json.Value)->dump())
s->append_char(']')
else if v->type == JSON_STRING
// TODO: escape sequences
s->append_char('"')
s->append(v->value as str)
s->append_char('"')
else if v->type == JSON_NUMBER
s->append((v->value as i64)->to_str())
else if v->type == JSON_NULL
s->append("null")
else if v->type == JSON_BOOL
if v->value as bool
s->append("true")
else
s->append("false")
else
panic("json.Value.dump: unknown value type")
return s->build_and_free_buffer()
func json.Value.free[v: json.Value] : void
if v->type == JSON_OBJECT
map := v->value as HashMap
keys := map->keys()
for i in 0..keys->size
value, _ := map->get(keys->nth(i) as str)
(value as json.Value)->free()
keys->free()
map->free_with_keys()
else if v->type == JSON_ARRAY
arr := v->value as Array
for i in 0..arr->size
(arr->nth(i) as json.Value)->free()
arr->free()
else if v->type == JSON_STRING
(v->value as str)->free()
(v as ptr)->free()
func json.parse[s: str] : json.Value
i := 0
return json._parse_value(s, ^i)
func json._parse_value[s: str, i: ptr] : json.Value
out := new* json.Value
json._skip_whitespace(s, i)
if s[mem.read64(i)] == 0
panic("json.parse: unexpected EOF")
if s[mem.read64(i)] == '{'
mem.write64(i, mem.read64(i) + 1)
json._skip_whitespace(s, i)
out->type = JSON_OBJECT
out->value = HashMap.new()
if s[mem.read64(i)] != '}'
while true
json._skip_whitespace(s, i)
key := json._parse_string(s, i)
json._skip_whitespace(s, i)
if s[mem.read64(i)] != ':'
panic("json.parse: expected ':'")
mem.write64(i, mem.read64(i) + 1)
json._skip_whitespace(s, i)
value := json._parse_value(s, i)
(out->value as HashMap)->insert(key, value)
key->free()
json._skip_whitespace(s, i)
if s[mem.read64(i)] == ','
mem.write64(i, mem.read64(i) + 1)
else
break
json._skip_whitespace(s, i)
if s[mem.read64(i)] != '}'
panic("json.parse: expected '}'")
mem.write64(i, mem.read64(i) + 1)
else if s[mem.read64(i)] == '['
mem.write64(i, mem.read64(i) + 1)
json._skip_whitespace(s, i)
out->type = JSON_ARRAY
out->value = []
while s[mem.read64(i)] != ']'
json._skip_whitespace(s, i)
(out->value as Array)->push(json._parse_value(s, i))
json._skip_whitespace(s, i)
if s[mem.read64(i)] == ','
mem.write64(i, mem.read64(i) + 1)
else
break
json._skip_whitespace(s, i)
if s[mem.read64(i)] != ']'
panic("json.parse: expected ']'")
mem.write64(i, mem.read64(i) + 1)
else if s[mem.read64(i)] == '"'
out->type = JSON_STRING
out->value = json._parse_string(s, i)
else if s[mem.read64(i)]->is_digit() || s[mem.read64(i)] == '-'
start := mem.read64(i)
if s[mem.read64(i)] == '-'
mem.write64(i, mem.read64(i) + 1)
while s[mem.read64(i)]->is_digit()
mem.write64(i, mem.read64(i) + 1)
// TODO: float support once str.parse_f64 is a thing
if s[mem.read64(i)] == '.'
panic("json.parse: parsing floats not implemented yet")
len := mem.read64(i) - start
num_str := s->substr_n(start, len)
out->type = JSON_NUMBER
out->value = num_str->parse_i64()
num_str->free()
else if s[mem.read64(i)] == 'n' && s[mem.read64(i)+1] == 'u' && s[mem.read64(i)+2] == 'l' && s[mem.read64(i)+3] == 'l'
out->type = JSON_NULL
mem.write64(i, mem.read64(i) + 4)
else if s[mem.read64(i)] == 't' && s[mem.read64(i)+1] == 'r' && s[mem.read64(i)+2] == 'u' && s[mem.read64(i)+3] == 'e'
out->type = JSON_BOOL
out->value = true
mem.write64(i, mem.read64(i) + 4)
else if s[mem.read64(i)] == 'f' && s[mem.read64(i)+1] == 'a' && s[mem.read64(i)+2] == 'l' && s[mem.read64(i)+3] == 's' && s[mem.read64(i)+4] == 'e'
out->type = JSON_BOOL
out->value = false
mem.write64(i, mem.read64(i) + 5)
else
panic("json.parse: unexpected character")
return out
func json._parse_string[s: str, i: ptr] : str
if s[mem.read64(i)] != '"'
panic("json.parse: expected '\"'")
mem.write64(i, mem.read64(i) + 1)
start := mem.read64(i)
// TODO: escape sequences
while s[mem.read64(i)] != '"'
if s[mem.read64(i)] == 0
panic("json.parse: unexpected EOF")
mem.write64(i, mem.read64(i) + 1)
len := mem.read64(i) - start
mem.write64(i, mem.read64(i) + 1)
return s->substr_n(start, len)
func json._skip_whitespace[s: str, i: ptr] : void
while s[mem.read64(i)]->is_whitespace()
mem.write64(i, mem.read64(i) + 1)

View File

@@ -72,9 +72,6 @@ func mem.alloc[size: i64] : ptr
return blk as ptr + MEM_BLOCK_SIZE
func mem.free[x: any] : void
ptr.free(x as ptr)
func ptr.free[x: ptr] : void
if x == 0
return
@@ -237,4 +234,4 @@ func Blob.alloc[size: i64] : Blob
func Blob.free[buf: Blob] : void
buf->data->free()
mem.free(buf)
(buf as ptr)->free()

View File

@@ -101,7 +101,7 @@ func net.UDPSocket.receive[s: net.UDPSocket, size: i64] : net.UDPPacket
func net.UDPSocket.close_and_free[s: net.UDPSocket] : void
_builtin_syscall(SYS_close, s->fd)
s->addr->free()
mem.free(s)
(s as ptr)->free()
func net.create_udp_client[host: str, port: i64] : net.UDPSocket, bool
s := new* net.UDPSocket
@@ -112,7 +112,7 @@ func net.create_udp_client[host: str, port: i64] : net.UDPSocket, bool
s->fd = _builtin_syscall(SYS_socket, AF_INET, SOCK_DGRAM, 0)
if s->fd < 0
mem.free(s)
(s as ptr)->free()
return 0 as net.UDPSocket, false
s->addr = mem.alloc(16)
@@ -141,7 +141,7 @@ struct net.UDPPacket
func net.UDPPacket.free[pkt: net.UDPPacket] : void
pkt->data->free()
pkt->source_addr->free()
mem.free(pkt)
(pkt as ptr)->free()
func net.resolve[domain: str] : i64, bool
// if domain is already an ip, skip dns resolution

View File

@@ -9,7 +9,7 @@ func os.exit[code: i64] : void
func os.getpid[] : i64
return _builtin_syscall(SYS_getpid)
func os.getenv[name: str] : str
func os.getenv[name: str] : str, bool
name_len := name->len()
i := 0
while true
@@ -17,15 +17,24 @@ func os.getenv[name: str] : str
if entry as ptr == 0
break
if entry->equal_n(name, name_len) && entry[name_len] == '='
return (entry as ptr + name_len + 1) as str
return (entry as ptr + name_len + 1) as str, true
i += 1
return ""
return 0 as str, false
func os.basename[path: str] : str
i := path->len() - 1
len := path->len()
end := len
while end > 0 && path[end - 1] == '/'
end -= 1
if end == 0
return "/"
i := end - 1
while i >= 0 && path[i] != '/'
i -= 1
return path->substr(i + 1, path->len() - i - 1)
return path->substr_n(i + 1, end - i - 1)
struct os.Timespec
tv_sec: i64

View File

@@ -29,7 +29,7 @@ func u8.to_str[c: u8] : str
return s
func str.free[s: str] : void
mem.free(s)
(s as ptr)->free()
func str.len[s: str] : i64
i := 0
@@ -103,7 +103,7 @@ func str.format_into[..] : i64
n += 1
i += 1
else if s[0] == 'f'
// TODO: fix when we implement f64 params
// TODO: use arrow when we implement f64 params
f64.to_str_buf(_var_arg(i), tmp as ptr)
tmp_len := tmp->len()
remaining := size - n - 1
@@ -166,7 +166,7 @@ func str.find[haystack: str, needle: str] : i64
return i
return -1
func str.substr[s: str, start: i64, length: i64] : str
func str.substr_n[s: str, start: i64, length: i64] : str
out := mem.alloc(length + 1) as str
mem.copy(s as ptr + start, out as ptr, length)
out[length] = 0
@@ -188,7 +188,7 @@ func str.trim[s: str] : str
while end >= start && s[end]->is_whitespace()
end -= 1
return s->substr(start, end - start + 1)
return s->substr_n(start, end - start + 1)
func str.reverse[s: str] : str
len := s->len()
@@ -286,7 +286,12 @@ func str.Builder.build[b: str.Builder] : str
s[b->size] = 0
return s
func str.Builder.destroy[b: str.Builder] : void
func str.Builder.build_and_free_buffer[b: str.Builder] : str
s := b->build()
b->free_buffer()
return s
func str.Builder.free_buffer[b: str.Builder] : void
if b->data != 0
b->data->free()
b->size = 0