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

@@ -52,7 +52,7 @@ func CHIP8.free[c: CHIP8] : void
c->keyboard->free()
c->display->free()
c->keyboard_map->free()
mem.free(c)
(c as ptr)->free()
func CHIP8.disassemble[c: CHIP8, ins_count: i64] : void
for i in 0..ins_count

View File

@@ -12,7 +12,7 @@ func main[argc: i64, argv: ptr] : i64
io.println("ERROR: invalid url (Did you forget \"http://\"?)")
return 1
if !url->substr(0, 7)->equal("http://")
if !url->substr_n(0, 7)->equal("http://")
io.println("ERROR: only http scheme is supported")
return 1
@@ -24,23 +24,19 @@ func main[argc: i64, argv: ptr] : i64
break
i += 1
host := url->substr(host_start, i - host_start)
host := url->substr_n(host_start, i - host_start)
path := "/"
if i < url_len
path = url->substr(i, url_len - i)
path = url->substr_n(i, url_len - i)
s, ok := net.connect(host, 80)
if !ok
panic("failed to connect")
req := new str.Builder
req->append("GET ")
req->append(path)
req->append(" HTTP/1.0\r\nHost: ")
req->append(host)
req->append("\r\nConnection: close\r\n\r\n")
net.send(s, req->data, req->size)
req->destroy()
// seems reasonable
req := _stackalloc(4096)
req_size := str.format_into(req as str, 4096, "GET %s HTTP/1.0\r\nHost: %s\r\nConnection: close\r\n\r\n", path, host)
net.send(s, req, req_size)
header_buf := mem.alloc(8192) as str
header_size := 0

View File

@@ -2,4 +2,4 @@ include "std/io.zr"
func main[] : i64
n := 37107287533 + 46376937677 + 74324986199 + 91942213363 + 23067588207 + 89261670696 + 28112879812 + 44274228917 + 47451445736 + 70386486105 + 62176457141 + 64906352462 + 92575867718 + 58203565325 + 80181199384 + 35398664372 + 86515506006 + 71693888707 + 54370070576 + 53282654108 + 36123272525 + 45876576172 + 17423706905 + 81142660418 + 51934325451 + 62467221648 + 15732444386 + 55037687525 + 18336384825 + 80386287592 + 78182833757 + 16726320100 + 48403098129 + 87086987551 + 59959406895 + 69793950679 + 41052684708 + 65378607361 + 35829035317 + 94953759765 + 88902802571 + 25267680276 + 36270218540 + 24074486908 + 91430288197 + 34413065578 + 23053081172 + 11487696932 + 63783299490 + 67720186971 + 95548255300 + 76085327132 + 37774242535 + 23701913275 + 29798860272 + 18495701454 + 38298203783 + 34829543829 + 40957953066 + 29746152185 + 41698116222 + 62467957194 + 23189706772 + 86188088225 + 11306739708 + 82959174767 + 97623331044 + 42846280183 + 55121603546 + 32238195734 + 75506164965 + 62177842752 + 32924185707 + 99518671430 + 73267460800 + 76841822524 + 97142617910 + 87783646182 + 10848802521 + 71329612474 + 62184073572 + 66627891981 + 60661826293 + 85786944089 + 66024396409 + 64913982680 + 16730939319 + 94809377245 + 78639167021 + 15368713711 + 40789923115 + 44889911501 + 41503128880 + 81234880673 + 82616570773 + 22918802058 + 77158542502 + 72107838435 + 20849603980 + 53503534226
io.println(n->to_str()->substr(0, 10))
io.println(n->to_str()->substr_n(0, 10))

View File

@@ -1,4 +0,0 @@
include "std/io.zr"
func main[] : i64
io.println("Hello, World!")

View File

@@ -1,18 +0,0 @@
include "std/io.zr"
include "std/os.zr"
func main[] : i64
arr := []
for i in 0..10
arr->push(os.urandom_i64()->abs() % 1000)
for i in 0..arr->size
io.println_i64(arr->nth(i))
io.println("------------")
arr->quicksort()
for i in 0..arr->size
io.println_i64(arr->nth(i))
arr->free()

View File

@@ -812,7 +812,6 @@ _builtin_environ:
if *use_heap {
emit!(&mut self.output, " mov rdi, {}", memory_size);
emit!(&mut self.output, " call mem.alloc");
emit!(&mut self.output, " push rax");
} else {
let aligned_size = (memory_size + 15) & !15;
emit!(&mut self.output, " sub rsp, {}", aligned_size);

View File

@@ -596,6 +596,15 @@ impl<'a> TypeChecker<'a> {
match &func_type.params {
FnParams::Normal(params) => {
if params.is_empty() || params[0] != receiver_type {
return error!(
method.loc,
format!(
"first parameter of the method must be of type {}",
receiver_type
)
);
}
if params.len() != args.len() + 1 {
return error!(
method.loc,
@@ -606,15 +615,6 @@ impl<'a> TypeChecker<'a> {
)
);
}
if params[0] != receiver_type {
return error!(
method.loc,
format!(
"first parameter of the method must be of type {}",
receiver_type
)
);
}
for (i, arg) in args.iter().enumerate() {
expect_type!(self.typecheck_expr(env, arg)?, params[i + 1], method.loc);
}

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