From aea9ce911a3d0ed666b60ffb4170c08f155e8514 Mon Sep 17 00:00:00 2001 From: Toni Date: Sun, 5 Jul 2026 15:22:50 +0200 Subject: [PATCH] std json parser --- examples/chip8.zr | 2 +- examples/curl.zr | 18 ++-- examples/euler/euler_13.zr | 2 +- examples/hello.zr | 4 - examples/quicksort.zr | 18 ---- src/codegen_x86_64.rs | 1 - src/typechecker.rs | 18 ++-- std/containers.zr | 47 +++++---- std/io.zr | 4 +- std/json.zr | 200 +++++++++++++++++++++++++++++++++++++ std/mem.zr | 5 +- std/net.zr | 6 +- std/os.zr | 19 +++- std/str.zr | 15 ++- 14 files changed, 275 insertions(+), 84 deletions(-) delete mode 100644 examples/hello.zr delete mode 100644 examples/quicksort.zr create mode 100644 std/json.zr diff --git a/examples/chip8.zr b/examples/chip8.zr index 10d87e2..3586275 100644 --- a/examples/chip8.zr +++ b/examples/chip8.zr @@ -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 diff --git a/examples/curl.zr b/examples/curl.zr index 9d4c741..c344acf 100644 --- a/examples/curl.zr +++ b/examples/curl.zr @@ -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 diff --git a/examples/euler/euler_13.zr b/examples/euler/euler_13.zr index ce082d1..68f5cf0 100644 --- a/examples/euler/euler_13.zr +++ b/examples/euler/euler_13.zr @@ -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)) diff --git a/examples/hello.zr b/examples/hello.zr deleted file mode 100644 index 8bdc52a..0000000 --- a/examples/hello.zr +++ /dev/null @@ -1,4 +0,0 @@ -include "std/io.zr" - -func main[] : i64 - io.println("Hello, World!") diff --git a/examples/quicksort.zr b/examples/quicksort.zr deleted file mode 100644 index c4ec09b..0000000 --- a/examples/quicksort.zr +++ /dev/null @@ -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() diff --git a/src/codegen_x86_64.rs b/src/codegen_x86_64.rs index d1e6c3f..c1d8763 100644 --- a/src/codegen_x86_64.rs +++ b/src/codegen_x86_64.rs @@ -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); diff --git a/src/typechecker.rs b/src/typechecker.rs index 372c846..936738a 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -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); } diff --git a/std/containers.zr b/std/containers.zr index 97dda67..bbafbba 100644 --- a/std/containers.zr +++ b/std/containers.zr @@ -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) diff --git a/std/io.zr b/std/io.zr index 6f4e6ea..e630365 100644 --- a/std/io.zr +++ b/std/io.zr @@ -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) diff --git a/std/json.zr b/std/json.zr new file mode 100644 index 0000000..261bec9 --- /dev/null +++ b/std/json.zr @@ -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) diff --git a/std/mem.zr b/std/mem.zr index 972d1e4..f855ea7 100644 --- a/std/mem.zr +++ b/std/mem.zr @@ -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() diff --git a/std/net.zr b/std/net.zr index 2c4c330..950476b 100644 --- a/std/net.zr +++ b/std/net.zr @@ -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 diff --git a/std/os.zr b/std/os.zr index dbaed96..6f61bb2 100644 --- a/std/os.zr +++ b/std/os.zr @@ -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 diff --git a/std/str.zr b/std/str.zr index 0a24cab..1b03342 100644 --- a/std/str.zr +++ b/std/str.zr @@ -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