Compare commits
5 Commits
2f632077b8
...
fee5dc3099
| Author | SHA1 | Date | |
|---|---|---|---|
| fee5dc3099 | |||
| 720218cf31 | |||
| 964bbe4ec0 | |||
| 78282679e7 | |||
| 7b09b505b5 |
@@ -26,15 +26,9 @@ func main[argc: i64, argv: ptr] : i64
|
||||
if i < url_len
|
||||
path = str.substr(url, i, url_len - i)
|
||||
|
||||
let addr: i64 = net.resolve(host)
|
||||
if addr <= 0
|
||||
io.println("ERROR: domain resolution failed")
|
||||
return 1
|
||||
let addr: i64 = must(net.resolve?(host))
|
||||
|
||||
let s: i64 = net.connect(addr, 80)
|
||||
if s < 0
|
||||
io.println("ERROR: connection failed")
|
||||
return 1
|
||||
let s: i64 = must(net.connect?(addr, 80))
|
||||
|
||||
// TODO: add string builder to std
|
||||
let req: str = "GET "
|
||||
|
||||
@@ -15,7 +15,7 @@ func part2[l1: Array, l2: Array] : void
|
||||
io.println_i64(out)
|
||||
|
||||
func main[] : i64
|
||||
let lines: Array = io.read_file("input.txt") |> str.split("\n")
|
||||
let lines: Array = must(io.read_text_file?("input.txt")) |> str.split("\n")
|
||||
|
||||
let l1: Array = []
|
||||
let l2: Array = []
|
||||
|
||||
@@ -40,7 +40,7 @@ func part2[data: Array] : void
|
||||
io.println_i64(out)
|
||||
|
||||
func main[] : i64
|
||||
let lines: Array = io.read_file("input.txt") |> str.split("\n")
|
||||
let lines: Array = must(io.read_text_file?("input.txt")) |> str.split("\n")
|
||||
|
||||
let data: Array = []
|
||||
for i in 0..lines->size
|
||||
|
||||
@@ -56,7 +56,7 @@ func part2[lines: Array] : void
|
||||
io.println_i64(out)
|
||||
|
||||
func main[] : i64
|
||||
let lines: Array = io.read_file("input.txt") |> str.split("\n")
|
||||
let lines: Array = must(io.read_text_file?("input.txt")) |> str.split("\n")
|
||||
|
||||
part1(lines)
|
||||
part2(lines)
|
||||
|
||||
@@ -47,7 +47,7 @@ func part2[updates: Array, rules_left: Array, rules_right: Array] : void
|
||||
io.println_i64(out)
|
||||
|
||||
func main[] : i64
|
||||
let data: Array = io.read_file("input.txt") |> str.split("\n\n")
|
||||
let data: Array = must(io.read_text_file?("input.txt")) |> str.split("\n\n")
|
||||
|
||||
let rules_left: Array = []
|
||||
let rules_right: Array = []
|
||||
|
||||
@@ -61,7 +61,7 @@ func part2[equations: Array] : void
|
||||
io.println_i64(out)
|
||||
|
||||
func main[] : i64
|
||||
let lines: Array = io.read_file("input.txt") |> str.split("\n")
|
||||
let lines: Array = must(io.read_text_file?("input.txt")) |> str.split("\n")
|
||||
let equations: Array = []
|
||||
|
||||
for i in 0..lines->size
|
||||
|
||||
@@ -47,7 +47,7 @@ func part2[lines: Array] : void
|
||||
io.println_i64(password)
|
||||
|
||||
func main[] : i64
|
||||
let lines: Array = io.read_file("input.txt") |> str.split("\n")
|
||||
let lines: Array = must(io.read_text_file?("input.txt")) |> str.split("\n")
|
||||
|
||||
part1(lines)
|
||||
part2(lines)
|
||||
|
||||
@@ -54,7 +54,7 @@ func part2[ranges: Array] : void
|
||||
io.println_i64(sum)
|
||||
|
||||
func main[] : i64
|
||||
let ranges: Array = io.read_file("input.txt") |> str.split(",")
|
||||
let ranges: Array = must(io.read_text_file?("input.txt")) |> str.split(",")
|
||||
|
||||
part1(ranges)
|
||||
part2(ranges)
|
||||
|
||||
@@ -45,7 +45,7 @@ func part2[lines: Array] : void
|
||||
io.println_i64(sum)
|
||||
|
||||
func main[] : i64
|
||||
let lines: Array = io.read_file("input.txt") |> str.split("\n")
|
||||
let lines: Array = must(io.read_text_file?("input.txt")) |> str.split("\n")
|
||||
|
||||
part1(lines)
|
||||
part2(lines)
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
func main[] : i64
|
||||
let s: i64 = net.listen(net.pack_addr(127, 0, 0, 1), 8000)
|
||||
if s < 0
|
||||
panic("failed to bind a socket")
|
||||
let s: i64 = must(net.listen?(net.pack_addr(127, 0, 0, 1), 8000))
|
||||
|
||||
io.println("Listening on port 8000...")
|
||||
let resp: str = mem.alloc(60000)
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
func main[] : i64
|
||||
let s: i64 = net.udp_listen(net.pack_addr(127, 0, 0, 1), 8000)
|
||||
if !s
|
||||
panic("failed to bind a socket")
|
||||
let s: i64 = must(net.udp_listen?(net.pack_addr(127, 0, 0, 1), 8000))
|
||||
|
||||
io.println("Listening on port 8000...")
|
||||
while true
|
||||
@@ -10,5 +8,5 @@ func main[] : i64
|
||||
net.UDPPacket.free(pkt)
|
||||
continue
|
||||
|
||||
net.udp_send_to(s, pkt->source_addr, pkt->buffer, pkt->size)
|
||||
net.udp_send_to(s, pkt->source_addr, pkt->data, pkt->size)
|
||||
net.UDPPacket.free(pkt)
|
||||
|
||||
@@ -17,6 +17,8 @@ impl Analyzer {
|
||||
functions: HashMap::from([
|
||||
("_builtin_heap_head".into(), 0),
|
||||
("_builtin_heap_tail".into(), 0),
|
||||
("_builtin_err_code".into(), 0),
|
||||
("_builtin_err_msg".into(), 0),
|
||||
("_builtin_read64".into(), 1),
|
||||
("_builtin_set64".into(), 2),
|
||||
("_builtin_syscall".into(), -1),
|
||||
|
||||
@@ -110,6 +110,8 @@ section .bss
|
||||
_heap_head: resq 1
|
||||
_heap_tail: resq 1
|
||||
_environ: resq 1
|
||||
_err_code: resq 1
|
||||
_err_msg: resq 1
|
||||
|
||||
section .text._builtin_heap_head
|
||||
_builtin_heap_head:
|
||||
@@ -121,6 +123,16 @@ _builtin_heap_tail:
|
||||
lea rax, [rel _heap_tail]
|
||||
ret
|
||||
|
||||
section .text._builtin_err_code
|
||||
_builtin_err_code:
|
||||
lea rax, [rel _err_code]
|
||||
ret
|
||||
|
||||
section .text._builtin_err_msg
|
||||
_builtin_err_msg:
|
||||
lea rax, [rel _err_msg]
|
||||
ret
|
||||
|
||||
section .text._builtin_read64
|
||||
_builtin_read64:
|
||||
mov rax, qword [rdi]
|
||||
@@ -734,6 +746,7 @@ _builtin_environ:
|
||||
Expr::New(struct_name) => {
|
||||
let struct_fields = &self.analyzer.structs[&struct_name.lexeme];
|
||||
|
||||
// TODO: panic on mem.alloc error
|
||||
let memory_size = struct_fields.len() * 8;
|
||||
emit!(&mut self.output, " mov rdi, {}", memory_size);
|
||||
emit!(&mut self.output, " call mem.alloc");
|
||||
|
||||
@@ -4,14 +4,17 @@ const SOCK_DGRAM = 2
|
||||
const SOL_SOCKET = 1
|
||||
const SO_REUSEADDR = 2
|
||||
|
||||
func net.listen[packed_host: i64, port: i64] : i64
|
||||
func net.listen?[packed_host: i64, port: i64] : i64
|
||||
err.clear()
|
||||
let s: i64 = _builtin_syscall(SYS_socket, AF_INET, SOCK_STREAM, 0)
|
||||
if s < 0
|
||||
err.set(ERR_SYSCALL_FAILED, "net.listen?: failed to create a socket")
|
||||
return -1
|
||||
|
||||
let optval = 1
|
||||
if _builtin_syscall(SYS_setsockopt, s, SOL_SOCKET, SO_REUSEADDR, ^optval, 8) < 0
|
||||
_builtin_syscall(SYS_close, s)
|
||||
err.set(ERR_SYSCALL_FAILED, "net.listen?: setsockopt() failed")
|
||||
return -1
|
||||
|
||||
let sa: ptr = mem.alloc(16)
|
||||
@@ -28,19 +31,23 @@ func net.listen[packed_host: i64, port: i64] : i64
|
||||
if _builtin_syscall(SYS_bind, s, sa, 16) < 0
|
||||
_builtin_syscall(SYS_close, s)
|
||||
mem.free(sa)
|
||||
err.set(ERR_SYSCALL_FAILED, "net.listen?: failed to bind")
|
||||
return -1
|
||||
mem.free(sa)
|
||||
|
||||
if _builtin_syscall(SYS_listen, s, 128) < 0
|
||||
_builtin_syscall(SYS_close, s)
|
||||
err.set(ERR_SYSCALL_FAILED, "net.listen?: listen() failed")
|
||||
return -1
|
||||
|
||||
return s
|
||||
|
||||
// TODO: resolve DNS
|
||||
func net.connect[packed_host: i64, port: i64] : i64
|
||||
func net.connect?[packed_host: i64, port: i64] : i64
|
||||
err.clear()
|
||||
let s: i64 = _builtin_syscall(SYS_socket, AF_INET, SOCK_STREAM, 0)
|
||||
if s < 0
|
||||
err.set(ERR_SYSCALL_FAILED, "net.connect?: failed to create a socket")
|
||||
return -1
|
||||
|
||||
let sa: ptr = mem.alloc(16)
|
||||
@@ -57,6 +64,7 @@ func net.connect[packed_host: i64, port: i64] : i64
|
||||
if _builtin_syscall(SYS_connect, s, sa, 16) < 0
|
||||
mem.free(sa)
|
||||
_builtin_syscall(SYS_close, s)
|
||||
err.set(ERR_CONNECTION_FAILED, "net.connect?: connection failed")
|
||||
return -1
|
||||
|
||||
mem.free(sa)
|
||||
@@ -86,12 +94,14 @@ struct net.UDPPacket
|
||||
source_addr: ptr
|
||||
size: i64
|
||||
|
||||
func net.create_udp_client[packed_host: i64, port: i64] : net.UDPSocket
|
||||
func net.create_udp_client?[packed_host: i64, port: i64] : net.UDPSocket
|
||||
err.clear()
|
||||
let s: net.UDPSocket = new net.UDPSocket
|
||||
|
||||
s->fd = _builtin_syscall(SYS_socket, AF_INET, SOCK_DGRAM, 0)
|
||||
if s->fd < 0
|
||||
mem.free(s)
|
||||
err.set(ERR_SYSCALL_FAILED, "net.create_udp_client?: failed to create a socket")
|
||||
return 0
|
||||
|
||||
s->addr = mem.alloc(16)
|
||||
@@ -106,13 +116,15 @@ func net.create_udp_client[packed_host: i64, port: i64] : net.UDPSocket
|
||||
s->addr[7] = packed_host & 255
|
||||
return s
|
||||
|
||||
func net.udp_listen[packed_host: i64, port: i64] : net.UDPSocket
|
||||
let s: net.UDPSocket = net.create_udp_client(packed_host, port)
|
||||
if !s
|
||||
func net.udp_listen?[packed_host: i64, port: i64] : net.UDPSocket
|
||||
err.clear()
|
||||
let s: net.UDPSocket = net.create_udp_client?(packed_host, port)
|
||||
if err.check()
|
||||
return 0
|
||||
|
||||
if _builtin_syscall(SYS_bind, s->fd, s->addr, 16) < 0
|
||||
net.UDPSocket.close_and_free(s)
|
||||
err.set(ERR_SYSCALL_FAILED, "net.udp_listen?: failed to bind")
|
||||
return 0
|
||||
|
||||
return s
|
||||
@@ -146,7 +158,7 @@ const DNS_RECURSION_DESIRED = 256
|
||||
|
||||
func net.encode_dns_name[domain: str] : io.Buffer
|
||||
let domain_len: i64 = str.len(domain)
|
||||
let buf: io.Buffer = io.Buffer.alloc(domain_len + 2)
|
||||
let buf: io.Buffer = must(io.Buffer.alloc?(domain_len + 2))
|
||||
let out_pos: i64 = 0
|
||||
let part_start: i64 = 0
|
||||
let i: i64 = 0
|
||||
@@ -165,7 +177,7 @@ func net.encode_dns_name[domain: str] : io.Buffer
|
||||
|
||||
func net.build_dns_query[domain_name: str, record_type: i64] : io.Buffer
|
||||
let name: io.Buffer = net.encode_dns_name(domain_name)
|
||||
let out: io.Buffer = io.Buffer.alloc(12 + name->size + 4)
|
||||
let out: io.Buffer = must(io.Buffer.alloc?(12 + name->size + 4))
|
||||
|
||||
// header
|
||||
let id: i64 = math.abs(os.urandom_i64() % 65536)
|
||||
@@ -184,13 +196,15 @@ func net.build_dns_query[domain_name: str, record_type: i64] : io.Buffer
|
||||
io.Buffer.free(name)
|
||||
return out
|
||||
|
||||
func net.resolve[domain: str] : i64
|
||||
// TODO: dont resolve IPs
|
||||
func net.resolve?[domain: str] : i64
|
||||
err.clear()
|
||||
let query: io.Buffer = net.build_dns_query(domain, DNS_TYPE_A)
|
||||
|
||||
// TODO: this probably shouldnt be hardcoded
|
||||
let s: net.UDPSocket = net.create_udp_client(net.pack_addr(8, 8, 8, 8), 53)
|
||||
if s == 0
|
||||
return 0
|
||||
let s: net.UDPSocket = net.create_udp_client?(net.pack_addr(8, 8, 8, 8), 53)
|
||||
if err.check()
|
||||
return -1
|
||||
|
||||
net.udp_send_to(s, s->addr, query->data, query->size)
|
||||
io.Buffer.free(query)
|
||||
|
||||
395
src/std/std.zr
395
src/std/std.zr
@@ -1,15 +1,46 @@
|
||||
const ERR_NONE = 0
|
||||
const ERR_SYSCALL_FAILED = 1
|
||||
const ERR_OPEN_FAILED = 2
|
||||
const ERR_READ_FAILED = 3
|
||||
const ERR_WRITE_FAILED = 4
|
||||
const ERR_ALLOC_FAILED = 5
|
||||
const ERR_CONNECTION_FAILED = 6
|
||||
|
||||
func err.code[] : i64
|
||||
return mem.read64(_builtin_err_code())
|
||||
|
||||
func err.msg[] : str
|
||||
return mem.read64(_builtin_err_msg())
|
||||
|
||||
func err.check[] : bool
|
||||
return err.code() != 0
|
||||
|
||||
func err.clear[] : void
|
||||
err.set(0, 0)
|
||||
|
||||
func err.set[code: i64, msg: str] : void
|
||||
mem.write64(_builtin_err_code(), code)
|
||||
mem.write64(_builtin_err_msg(), msg)
|
||||
|
||||
func must[x: any] : any
|
||||
if err.check()
|
||||
panic(err.msg())
|
||||
return x
|
||||
|
||||
func panic[msg: str] : void
|
||||
io.print("PANIC: ")
|
||||
io.println(msg)
|
||||
(0 / 0) // crashes program which is kinda better since you get a gdb backtrace
|
||||
// for gdb backtrace
|
||||
_builtin_syscall(SYS_kill, os.getpid(), 6)
|
||||
os.exit(1)
|
||||
|
||||
const MEM_BLOCK_SIZE = 24
|
||||
const MEM_BLOCK_SIZE = 32
|
||||
|
||||
struct mem.Block
|
||||
size: i64
|
||||
free: bool
|
||||
next: mem.Block
|
||||
prev: mem.Block
|
||||
|
||||
func mem._align[x: i64] : i64
|
||||
return (x + 7) & -8
|
||||
@@ -20,14 +51,19 @@ func mem._split_block[blk: mem.Block, needed: i64] : void
|
||||
new_blk->size = blk->size - needed - MEM_BLOCK_SIZE
|
||||
new_blk->free = true
|
||||
new_blk->next = blk->next
|
||||
new_blk->prev = blk
|
||||
|
||||
if blk->next
|
||||
let next: mem.Block = blk->next
|
||||
next->prev = new_blk
|
||||
else
|
||||
mem.write64(_builtin_heap_tail(), new_blk)
|
||||
|
||||
blk->size = needed
|
||||
blk->next = new_blk
|
||||
|
||||
if mem.read64(_builtin_heap_tail()) == blk
|
||||
mem.write64(_builtin_heap_tail(), new_blk)
|
||||
|
||||
func mem._request_space[size: i64] : mem.Block
|
||||
func mem._request_space?[size: i64] : mem.Block
|
||||
err.clear()
|
||||
let needed: i64 = size + MEM_BLOCK_SIZE
|
||||
let alloc_size: i64 = (needed + 4095) & -4096
|
||||
|
||||
@@ -36,55 +72,26 @@ func mem._request_space[size: i64] : mem.Block
|
||||
// fd = -1, offset = 0
|
||||
let blk: mem.Block = _builtin_syscall(SYS_mmap, 0, alloc_size, 3, 34, -1, 0)
|
||||
if blk == -1
|
||||
err.set(ERR_ALLOC_FAILED, "mem._request_space: mmap failed")
|
||||
return 0
|
||||
|
||||
blk->size = alloc_size - MEM_BLOCK_SIZE
|
||||
blk->free = false
|
||||
blk->next = 0
|
||||
blk->prev = 0
|
||||
|
||||
let tail: mem.Block = mem.read64(_builtin_heap_tail())
|
||||
if tail
|
||||
tail->next = blk
|
||||
blk->prev = tail
|
||||
|
||||
mem.write64(_builtin_heap_tail(), blk)
|
||||
return blk
|
||||
|
||||
func mem._coalesce[] : void
|
||||
let cur: mem.Block = mem.read64(_builtin_heap_head())
|
||||
let prev: mem.Block = 0
|
||||
|
||||
while cur
|
||||
let next: mem.Block = cur->next
|
||||
let expected_next: mem.Block = cur + MEM_BLOCK_SIZE + cur->size
|
||||
|
||||
if cur->free && next && next->free && expected_next == next
|
||||
cur->size = cur->size + MEM_BLOCK_SIZE + next->size
|
||||
cur->next = next->next
|
||||
if !cur->next
|
||||
mem.write64(_builtin_heap_tail(), cur)
|
||||
continue
|
||||
|
||||
let block_total: i64 = cur->size + MEM_BLOCK_SIZE
|
||||
if cur->free && (cur & 4095) == 0 && (block_total & 4095) == 0
|
||||
if prev
|
||||
prev->next = cur->next
|
||||
if !cur->next
|
||||
mem.write64(_builtin_heap_tail(), prev)
|
||||
else
|
||||
mem.write64(_builtin_heap_head(), cur->next)
|
||||
if !cur->next
|
||||
mem.write64(_builtin_heap_tail(), 0)
|
||||
|
||||
let saved_next: mem.Block = cur->next
|
||||
_builtin_syscall(11, cur, block_total)
|
||||
cur = saved_next
|
||||
continue
|
||||
|
||||
prev = cur
|
||||
cur = cur->next
|
||||
|
||||
func mem.alloc[size: i64] : ptr
|
||||
if size == 0
|
||||
func mem.alloc?[size: i64] : ptr
|
||||
err.clear()
|
||||
if size <= 0
|
||||
err.set(ERR_ALLOC_FAILED, "mem.alloc? called with non-positive size")
|
||||
return 0
|
||||
|
||||
size = mem._align(size)
|
||||
@@ -97,8 +104,8 @@ func mem.alloc[size: i64] : ptr
|
||||
return cur + MEM_BLOCK_SIZE
|
||||
cur = cur->next
|
||||
|
||||
let blk: mem.Block = mem._request_space(size)
|
||||
if !blk
|
||||
let blk: mem.Block = mem._request_space?(size)
|
||||
if err.check()
|
||||
return 0
|
||||
|
||||
if !mem.read64(_builtin_heap_head())
|
||||
@@ -108,17 +115,63 @@ func mem.alloc[size: i64] : ptr
|
||||
|
||||
return blk + MEM_BLOCK_SIZE
|
||||
|
||||
func mem.alloc[size: i64] : ptr
|
||||
return must(mem.alloc?(size))
|
||||
|
||||
func mem.free[x: ptr] : void
|
||||
if !x
|
||||
return 0
|
||||
|
||||
|
||||
let blk: mem.Block = x - MEM_BLOCK_SIZE
|
||||
blk->free = true
|
||||
mem._coalesce()
|
||||
|
||||
func mem.realloc[x: ptr, new_size: i64] : ptr
|
||||
let next: mem.Block = blk->next
|
||||
if next && next->free
|
||||
let expected_next: mem.Block = blk + MEM_BLOCK_SIZE + blk->size
|
||||
if expected_next == next
|
||||
blk->size = blk->size + MEM_BLOCK_SIZE + next->size
|
||||
blk->next = next->next
|
||||
if next->next
|
||||
let b: mem.Block = next->next
|
||||
b->prev = blk
|
||||
if mem.read64(_builtin_heap_tail()) == next
|
||||
mem.write64(_builtin_heap_tail(), blk)
|
||||
|
||||
let prev: mem.Block = blk->prev
|
||||
if prev && prev->free
|
||||
let expected_blk: mem.Block = prev + MEM_BLOCK_SIZE + prev->size
|
||||
if expected_blk == blk
|
||||
prev->size = prev->size + MEM_BLOCK_SIZE + blk->size
|
||||
prev->next = blk->next
|
||||
if blk->next
|
||||
let b: mem.Block = blk->next
|
||||
b->prev = prev
|
||||
if mem.read64(_builtin_heap_tail()) == blk
|
||||
mem.write64(_builtin_heap_tail(), prev)
|
||||
blk = prev
|
||||
|
||||
let block_total: i64 = blk->size + MEM_BLOCK_SIZE
|
||||
if (blk & 4095) == 0 && (block_total & 4095) == 0
|
||||
if blk->prev
|
||||
let b: mem.Block = blk->prev
|
||||
b->next = blk->next
|
||||
else
|
||||
mem.write64(_builtin_heap_head(), blk->next)
|
||||
if blk->next
|
||||
let b: mem.Block = blk->next
|
||||
b->prev = blk->prev
|
||||
else
|
||||
mem.write64(_builtin_heap_tail(), blk->prev)
|
||||
_builtin_syscall(SYS_munmap, blk, block_total)
|
||||
|
||||
func mem.realloc?[x: ptr, new_size: i64] : ptr
|
||||
err.clear()
|
||||
if !x
|
||||
return mem.alloc(new_size)
|
||||
return mem.alloc?(new_size)
|
||||
|
||||
if new_size < 0
|
||||
err.set(ERR_ALLOC_FAILED, "mem.realloc? called with negative size")
|
||||
return 0
|
||||
|
||||
if new_size == 0
|
||||
mem.free(x)
|
||||
@@ -139,17 +192,19 @@ func mem.realloc[x: ptr, new_size: i64] : ptr
|
||||
if combined >= new_size
|
||||
blk->size = combined
|
||||
blk->next = next->next
|
||||
if !blk->next
|
||||
if next->next
|
||||
let b: mem.Block = next->next
|
||||
b->prev = blk
|
||||
if mem.read64(_builtin_heap_tail()) == next
|
||||
mem.write64(_builtin_heap_tail(), blk)
|
||||
mem._split_block(blk, new_size)
|
||||
return x
|
||||
|
||||
let new_ptr: ptr = mem.alloc(new_size)
|
||||
if !new_ptr
|
||||
let new_ptr: ptr = mem.alloc?(new_size)
|
||||
if err.check()
|
||||
return 0
|
||||
|
||||
mem.copy(x, new_ptr, math.min(blk->size, new_size))
|
||||
|
||||
mem.free(x)
|
||||
return new_ptr
|
||||
|
||||
@@ -162,8 +217,14 @@ func mem.zero[x: ptr, size: i64] : void
|
||||
x[i] = 0
|
||||
|
||||
func mem.copy[src: ptr, dst: ptr, n: i64] : void
|
||||
for i in 0..n
|
||||
dst[i] = src[i]
|
||||
if dst > src
|
||||
let i: i64 = n - 1
|
||||
while i >= 0
|
||||
dst[i] = src[i]
|
||||
i = i - 1
|
||||
else
|
||||
for i in 0..n
|
||||
dst[i] = src[i]
|
||||
|
||||
func mem.read8[x: ptr] : u8
|
||||
return x[0]
|
||||
@@ -270,71 +331,100 @@ func io.read_line[]: str
|
||||
let buffer: str = mem.alloc(MAX_SIZE + 1)
|
||||
let n: i64 = _builtin_syscall(SYS_read, 0, buffer, MAX_SIZE)
|
||||
if n < 0
|
||||
mem.free(buffer)
|
||||
return ""
|
||||
buffer[n] = 0
|
||||
return buffer
|
||||
|
||||
func io.file_exists[path: str] : bool
|
||||
return _builtin_syscall(SYS_faccessat, -100, path, 0, 0) == 0
|
||||
|
||||
func io.read_file[path: str] : str
|
||||
let fd: i64 = _builtin_syscall(SYS_openat, -100, path, 0, 0)
|
||||
if fd <= 0
|
||||
panic("failed to open file") // TODO
|
||||
|
||||
let size: i64 = _builtin_syscall(SYS_lseek, fd, 0, 2)
|
||||
_builtin_syscall(SYS_lseek, fd, 0, 0)
|
||||
|
||||
let buffer: str = mem.alloc(size + 1)
|
||||
let n: i64 = _builtin_syscall(SYS_read, fd, buffer, size)
|
||||
_builtin_syscall(SYS_close, fd)
|
||||
n = 0
|
||||
buffer[n] = 0
|
||||
buffer = must(mem.realloc?(buffer, n + 1))
|
||||
return buffer
|
||||
|
||||
struct io.Buffer
|
||||
data: ptr
|
||||
size: i64
|
||||
|
||||
func io.Buffer.alloc[size: i64] : io.Buffer
|
||||
func io.Buffer.alloc?[size: i64] : io.Buffer
|
||||
err.clear()
|
||||
let buffer: io.Buffer = new io.Buffer
|
||||
buffer->size = size
|
||||
buffer->data = mem.alloc(size)
|
||||
buffer->data = mem.alloc?(size)
|
||||
if err.check()
|
||||
mem.free(buffer)
|
||||
return 0
|
||||
return buffer
|
||||
|
||||
func io.Buffer.free[buf: io.Buffer] : void
|
||||
mem.free(buf->data)
|
||||
mem.free(buf)
|
||||
|
||||
func io.read_binary_file[path: str] : io.Buffer
|
||||
func io.file_exists[path: str] : bool
|
||||
return _builtin_syscall(SYS_faccessat, -100, path, 0, 0) == 0
|
||||
|
||||
func io.read_text_file?[path: str] : str
|
||||
err.clear()
|
||||
let fd: i64 = _builtin_syscall(SYS_openat, -100, path, 0, 0)
|
||||
if fd < 0
|
||||
panic("failed to open file") // TODO
|
||||
|
||||
let buffer: io.Buffer = new io.Buffer
|
||||
buffer->size = _builtin_syscall(SYS_lseek, fd, 0, 2)
|
||||
err.set(ERR_OPEN_FAILED, "io.read_text_file?: failed to open file")
|
||||
return 0
|
||||
|
||||
let size: i64 = _builtin_syscall(SYS_lseek, fd, 0, 2)
|
||||
_builtin_syscall(SYS_lseek, fd, 0, 0)
|
||||
|
||||
buffer->data = mem.alloc(buffer->size)
|
||||
_builtin_syscall(SYS_read, fd, buffer->data, buffer->size)
|
||||
let buffer: str = mem.alloc?(size + 1)
|
||||
if err.check()
|
||||
_builtin_syscall(SYS_close, fd)
|
||||
return 0
|
||||
|
||||
let n: i64 = _builtin_syscall(SYS_read, fd, buffer, size)
|
||||
_builtin_syscall(SYS_close, fd)
|
||||
|
||||
if n != size
|
||||
mem.free(buffer)
|
||||
err.set(ERR_READ_FAILED, "io.read_text_file?: failed to read file")
|
||||
return 0
|
||||
|
||||
buffer[n] = 0
|
||||
return buffer
|
||||
|
||||
func io.write_file[path: str, content: str] : void
|
||||
let fd: i64 = _builtin_syscall(SYS_openat, -100, path, 0x241, 0o644)
|
||||
func io.read_binary_file?[path: str] : io.Buffer
|
||||
err.clear()
|
||||
let fd: i64 = _builtin_syscall(SYS_openat, -100, path, 0, 0)
|
||||
if fd < 0
|
||||
panic("failed to open file") // TODO
|
||||
err.set(ERR_OPEN_FAILED, "io.read_binary_file?: failed to open file")
|
||||
return 0
|
||||
|
||||
let buf: io.Buffer = new io.Buffer
|
||||
buf->size = _builtin_syscall(SYS_lseek, fd, 0, 2)
|
||||
_builtin_syscall(SYS_lseek, fd, 0, 0)
|
||||
|
||||
_builtin_syscall(SYS_write, fd, content, str.len(content))
|
||||
buf->data = mem.alloc?(buf->size)
|
||||
if err.check()
|
||||
io.Buffer.free(buf)
|
||||
_builtin_syscall(SYS_close, fd)
|
||||
return 0
|
||||
|
||||
let n: i64 = _builtin_syscall(SYS_read, fd, buf->data, buf->size)
|
||||
_builtin_syscall(SYS_close, fd)
|
||||
|
||||
func io.write_binary_file[path: str, content: ptr, size: i64] : void
|
||||
if n != buf->size
|
||||
io.Buffer.free(buf)
|
||||
err.set(ERR_READ_FAILED, "io.read_binary_file?: failed to read file")
|
||||
return 0
|
||||
|
||||
return buf
|
||||
|
||||
func io.write_file?[path: str, content: str] : void
|
||||
io.write_binary_file?(path, content, str.len(content))
|
||||
|
||||
func io.write_binary_file?[path: str, content: ptr, size: i64] : void
|
||||
err.clear()
|
||||
let fd: i64 = _builtin_syscall(SYS_openat, -100, path, 0x241, 0o644)
|
||||
if fd < 0
|
||||
panic("failed to open file") // TODO
|
||||
err.set(ERR_OPEN_FAILED, "io.write_binary_file?: failed to open file")
|
||||
return 0
|
||||
|
||||
_builtin_syscall(SYS_write, fd, content, size)
|
||||
let n: i64 = _builtin_syscall(SYS_write, fd, content, size)
|
||||
_builtin_syscall(SYS_close, fd)
|
||||
if n != size
|
||||
err.set(ERR_WRITE_FAILED, "io.write_binary_file?: failed to write file")
|
||||
return 0
|
||||
|
||||
func str.len[s: str] : i64
|
||||
let i = 0
|
||||
@@ -395,6 +485,9 @@ func str.find[haystack: str, needle: str] : i64
|
||||
|
||||
if needle_len == 0
|
||||
return 0
|
||||
|
||||
if needle_len > haystack_len
|
||||
return -1
|
||||
|
||||
for i in 0..(haystack_len - needle_len + 1)
|
||||
let match: bool = true
|
||||
@@ -418,7 +511,9 @@ func str.substr[s: str, start: i64, length: i64] : str
|
||||
func str.trim[s: str] : str
|
||||
let len: i64 = str.len(s)
|
||||
if len == 0
|
||||
return ""
|
||||
let out: str = mem.alloc(1)
|
||||
out[0] = 0
|
||||
return out
|
||||
|
||||
let start = 0
|
||||
let end: i64 = len - 1
|
||||
@@ -472,7 +567,6 @@ func str.reverse[s: str] : str
|
||||
out[len] = 0
|
||||
return out
|
||||
|
||||
// not sure this covers all the wacky edge cases
|
||||
func str.from_i64[n: i64] : str
|
||||
if n == 0
|
||||
let out: str = mem.alloc(2)
|
||||
@@ -482,19 +576,22 @@ func str.from_i64[n: i64] : str
|
||||
|
||||
let neg: bool = n < 0
|
||||
if neg
|
||||
// negating MIN_I64 causes overflow
|
||||
if n == -9223372036854775808
|
||||
return str.make_copy("-9223372036854775808")
|
||||
n = -n
|
||||
let buf: str = mem.alloc(21) // enough to fit -MAX_I64
|
||||
let i = 0
|
||||
let end = 20
|
||||
buf[end] = 0
|
||||
end = end - 1
|
||||
while n > 0
|
||||
let d: u8 = n % 10
|
||||
buf[i] = '0' + d
|
||||
buf[end] = '0' + (n % 10)
|
||||
n = n / 10
|
||||
i = i + 1
|
||||
end = end - 1
|
||||
if neg
|
||||
buf[i] = '-'
|
||||
i = i + 1
|
||||
buf[i] = 0
|
||||
let s: str = str.reverse(buf)
|
||||
buf[end] = '-'
|
||||
end = end - 1
|
||||
let s: str = str.make_copy(buf + end + 1)
|
||||
mem.free(buf)
|
||||
return s
|
||||
|
||||
@@ -507,12 +604,13 @@ func str.hex_from_i64[n: i64] : str
|
||||
out[1] = 0
|
||||
return out
|
||||
|
||||
let mask: i64 = (1 << 60) - 1
|
||||
let buf: str = mem.alloc(17)
|
||||
let len = 0
|
||||
|
||||
while n > 0
|
||||
while n != 0
|
||||
buf[len] = hex_chars[n & 15]
|
||||
n = n >> 4
|
||||
n = (n >> 4) & mask
|
||||
len = len + 1
|
||||
|
||||
let out: str = mem.alloc(len + 1)
|
||||
@@ -522,6 +620,7 @@ func str.hex_from_i64[n: i64] : str
|
||||
j = j + 1
|
||||
|
||||
out[len] = 0
|
||||
mem.free(buf)
|
||||
return out
|
||||
|
||||
func str.from_char[c: u8] : str
|
||||
@@ -572,6 +671,9 @@ func str._hex_digit_to_int[d: u8] : i64
|
||||
|
||||
func str.hex_decode[s: str] : str
|
||||
let s_len: i64 = str.len(s)
|
||||
if s_len % 2 != 0
|
||||
panic("invalid hex string passed to str.hex_decode")
|
||||
|
||||
let i = 0
|
||||
let j = 0
|
||||
let out: str = mem.alloc(s_len / 2 + 1)
|
||||
@@ -585,6 +687,8 @@ func str.hex_decode[s: str] : str
|
||||
return out
|
||||
|
||||
func math.gcd[a: i64, b: i64] : i64
|
||||
a = math.abs(a)
|
||||
b = math.abs(b)
|
||||
while b != 0
|
||||
let tmp: i64 = b
|
||||
b = a % b
|
||||
@@ -602,6 +706,8 @@ func math.max[a: i64, b: i64] : i64
|
||||
return b
|
||||
|
||||
func math.abs[n: i64] : i64
|
||||
if n == -9223372036854775808
|
||||
panic("MIN_I64 passed to math.abs")
|
||||
if n < 0
|
||||
return -n
|
||||
return n
|
||||
@@ -615,13 +721,15 @@ func math.sign[n: i64] : i64
|
||||
return 0
|
||||
|
||||
func math.pow[b: i64, e: i64] : i64
|
||||
if e < 0
|
||||
panic("negative exponent passed to math.pow")
|
||||
let out = 1
|
||||
for i in 0..e
|
||||
out = out * b
|
||||
return out
|
||||
|
||||
func math.lcm[a: i64, b: i64] : i64
|
||||
return (a * b) / math.gcd(a, b)
|
||||
return (a / math.gcd(a, b)) * b
|
||||
|
||||
func math.isqrt[n: i64] : i64
|
||||
if n < 0
|
||||
@@ -661,6 +769,15 @@ struct Array
|
||||
func array.new[] : Array
|
||||
return new Array
|
||||
|
||||
func array.new_with_size_zeroed[size: i64] : Array
|
||||
let xs: Array = new Array
|
||||
if size > 0
|
||||
xs->data = must(mem.realloc?(xs->data, size * 8))
|
||||
mem.zero(xs->data, size * 8)
|
||||
xs->size = size
|
||||
xs->capacity = size
|
||||
return xs
|
||||
|
||||
func array.nth[xs: Array, n: i64] : any
|
||||
if n < 0 || n >= xs->size
|
||||
panic("array.nth out of bounds")
|
||||
@@ -676,7 +793,7 @@ func array.push[xs: Array, x: any] : void
|
||||
let new_capacity = 4
|
||||
if xs->capacity != 0
|
||||
new_capacity = xs->capacity * 2
|
||||
xs->data = mem.realloc(xs->data, new_capacity * 8)
|
||||
xs->data = must(mem.realloc?(xs->data, new_capacity * 8))
|
||||
xs->capacity = new_capacity
|
||||
|
||||
mem.write64(xs->data + xs->size * 8, x)
|
||||
@@ -698,17 +815,17 @@ func array.slice[xs: Array, start: i64, length: i64] : Array
|
||||
if start < 0 || length < 0 || start + length > xs->size
|
||||
panic("array.slice out of bounds")
|
||||
|
||||
let new_array: Array = []
|
||||
let new_array: Array = array.new_with_size_zeroed(length)
|
||||
for i in 0..length
|
||||
array.push(new_array, array.nth(xs, start + i))
|
||||
array.set(new_array, i, array.nth(xs, start + i))
|
||||
return new_array
|
||||
|
||||
func array.concat[a: Array, b: Array] : Array
|
||||
let new_array: Array = []
|
||||
let new_array: Array = array.new_with_size_zeroed(a->size + b->size)
|
||||
for i in 0..a->size
|
||||
array.push(new_array, array.nth(a, i))
|
||||
array.set(new_array, i, array.nth(a, i))
|
||||
for i in 0..b->size
|
||||
array.push(new_array, array.nth(b, i))
|
||||
array.set(new_array, a->size + i, array.nth(b, i))
|
||||
return new_array
|
||||
|
||||
func alg.quicksort[arr: Array] : void
|
||||
@@ -742,9 +859,9 @@ func alg.count[arr: Array, item: any] : i64
|
||||
return count
|
||||
|
||||
func alg.map[arr: Array, fn: ptr] : Array
|
||||
let out: Array = []
|
||||
let out: Array = array.new_with_size_zeroed(arr->size)
|
||||
for i in 0..arr->size
|
||||
array.push(out, fn(array.nth(arr, i)))
|
||||
array.set(out, i, fn(array.nth(arr, i)))
|
||||
return out
|
||||
|
||||
func alg.filter[arr: Array, fn: ptr] : Array
|
||||
@@ -765,26 +882,44 @@ func os.exit[code: i64] : void
|
||||
func os.getpid[] : i64
|
||||
return _builtin_syscall(SYS_getpid)
|
||||
|
||||
struct os.Timespec
|
||||
tv_sec: i64
|
||||
tv_nsec: i64
|
||||
|
||||
func os.sleep[ms: i64] : void
|
||||
if ms < 0
|
||||
panic("negative time passed to os.sleep")
|
||||
// TODO: we really shouldnt need a heap allocation to sleep
|
||||
let req: os.Timeval = new os.Timeval
|
||||
let req: os.Timespec = new os.Timespec
|
||||
req->tv_sec = ms / 1000
|
||||
// yes, we should have a os.Timespec with tv_nsec but this works for now
|
||||
req->tv_usec = (ms % 1000) * 1000000
|
||||
req->tv_nsec = (ms % 1000) * 1000000
|
||||
_builtin_syscall(SYS_nanosleep, req, 0)
|
||||
mem.free(req)
|
||||
|
||||
func os.urandom[n: i64]: ptr
|
||||
func os.urandom?[n: i64]: ptr
|
||||
err.clear()
|
||||
let buffer: ptr = mem.alloc(n)
|
||||
if err.check()
|
||||
return 0
|
||||
|
||||
let fd: i64 = _builtin_syscall(SYS_openat, -100, "/dev/urandom", 0, 0)
|
||||
_builtin_syscall(SYS_read, fd, buffer, n)
|
||||
if fd < 0
|
||||
mem.free(buffer)
|
||||
err.set(ERR_READ_FAILED, "os.urandom?: failed to open /dev/urandom")
|
||||
return 0
|
||||
|
||||
let bytes_read: i64 = _builtin_syscall(SYS_read, fd, buffer, n)
|
||||
_builtin_syscall(SYS_close, fd)
|
||||
|
||||
if n != bytes_read
|
||||
mem.free(buffer)
|
||||
err.set(ERR_READ_FAILED, "os.urandom?: failed to read /dev/urandom")
|
||||
return 0
|
||||
|
||||
return buffer
|
||||
|
||||
func os.urandom_i64[]: i64
|
||||
let buffer: ptr = os.urandom(8)
|
||||
let buffer: ptr = must(os.urandom?(8))
|
||||
let n: i64 = mem.read64(buffer)
|
||||
mem.free(buffer)
|
||||
return n
|
||||
@@ -802,8 +937,13 @@ func os.time[] : i64
|
||||
return out
|
||||
|
||||
// voodoo magic
|
||||
func os.run_shell_command[command: str] : i64
|
||||
func os.run_shell_command?[command: str] : i64
|
||||
err.clear()
|
||||
let pid: i64 = _builtin_syscall(SYS_fork)
|
||||
if pid < 0
|
||||
err.set(ERR_SYSCALL_FAILED, "os.run_shell_command?: failed to fork")
|
||||
return -1
|
||||
|
||||
if pid == 0
|
||||
let argv: Array = ["sh", "-c", command, 0] // gets freed after child process exits
|
||||
_builtin_syscall(SYS_execve, "/bin/sh", argv->data, _builtin_environ())
|
||||
@@ -811,7 +951,8 @@ func os.run_shell_command[command: str] : i64
|
||||
else
|
||||
let status = 0
|
||||
let wp: i64 = _builtin_syscall(SYS_wait4, pid, ^status, 0, 0)
|
||||
if wp == -1
|
||||
if wp < 0
|
||||
err.set(ERR_SYSCALL_FAILED, "os.run_shell_command?: wait() failed")
|
||||
return -1
|
||||
let st: i64 = status & 0xffffffff
|
||||
|
||||
@@ -820,16 +961,28 @@ func os.run_shell_command[command: str] : i64
|
||||
else
|
||||
return -(st & 0x7f)
|
||||
|
||||
func os.list_directory[path: str] : Array
|
||||
func os.list_directory?[path: str] : Array
|
||||
err.clear()
|
||||
let fd: i64 = _builtin_syscall(SYS_openat, -100, path, 0, 0)
|
||||
if fd < 0
|
||||
return []
|
||||
err.set(ERR_OPEN_FAILED, "os.list_directory?: failed to open dir")
|
||||
return 0
|
||||
|
||||
let files: Array = []
|
||||
let buf: ptr = mem.alloc(1024)
|
||||
while true
|
||||
let n: i64 = _builtin_syscall(SYS_getdents64, fd, buf, 1024)
|
||||
if n <= 0
|
||||
if n < 0
|
||||
mem.free(buf)
|
||||
|
||||
for i in 0..files->size
|
||||
mem.free(array.nth(files, i))
|
||||
array.free(files)
|
||||
|
||||
_builtin_syscall(SYS_close, fd)
|
||||
err.set(ERR_SYSCALL_FAILED, "os.list_directory?: getdents64() failed")
|
||||
return 0
|
||||
else if n == 0
|
||||
break
|
||||
|
||||
let pos = 0
|
||||
|
||||
@@ -346,7 +346,11 @@ impl Tokenizer {
|
||||
}
|
||||
|
||||
fn scan_identifier(&mut self) -> Result<(), ZernError> {
|
||||
while self.peek().is_alphanumeric() || self.peek() == '_' || self.peek() == '.' {
|
||||
while self.peek().is_alphanumeric()
|
||||
|| self.peek() == '_'
|
||||
|| self.peek() == '.'
|
||||
|| self.peek() == '?'
|
||||
{
|
||||
self.advance();
|
||||
}
|
||||
|
||||
|
||||
10
test.zr
10
test.zr
@@ -1,6 +1,6 @@
|
||||
func run_test[x: str] : void
|
||||
let build_blacklist: Array = ["examples/puzzles", "examples/raylib.zr", "examples/chip8.zr", "examples/sqlite_todo.zr"]
|
||||
let run_blacklist: Array = ["/aoc", "guess_number.zr", "tcp_server.zr"]
|
||||
let run_blacklist: Array = ["/aoc", "guess_number.zr", "tcp_server.zr", "udp_server.zr"]
|
||||
|
||||
for i in 0..build_blacklist->size
|
||||
if str.equal(x, array.nth(build_blacklist, i))
|
||||
@@ -14,7 +14,7 @@ func run_test[x: str] : void
|
||||
io.print("... ")
|
||||
|
||||
let build_start_time: i64 = os.time()
|
||||
if os.run_shell_command(str.concat("./target/release/zern ", x)) != 0
|
||||
if must(os.run_shell_command?(str.concat("./target/release/zern ", x))) != 0
|
||||
os.exit(1)
|
||||
let build_end_time: i64 = os.time()
|
||||
|
||||
@@ -39,7 +39,7 @@ func run_test[x: str] : void
|
||||
run_cmd = str.concat(run_cmd, " test.zr")
|
||||
|
||||
let run_start_time: i64 = os.time()
|
||||
if os.run_shell_command(run_cmd) != 0
|
||||
if must(os.run_shell_command?(run_cmd)) != 0
|
||||
os.exit(1)
|
||||
let run_end_time: i64 = os.time()
|
||||
|
||||
@@ -50,14 +50,14 @@ func run_test[x: str] : void
|
||||
io.println("ms")
|
||||
|
||||
func run_directory[dir: str] : void
|
||||
let files: Array = os.list_directory(dir)
|
||||
let files: Array = must(os.list_directory?(dir))
|
||||
for i in 0..files->size
|
||||
run_test(str.concat(dir, array.nth(files, i)))
|
||||
|
||||
array.free(files)
|
||||
|
||||
func main[] : i64
|
||||
os.run_shell_command("cargo build --release")
|
||||
must(os.run_shell_command?("cargo build --release"))
|
||||
|
||||
run_directory("examples/")
|
||||
run_directory("examples/puzzles/")
|
||||
|
||||
Reference in New Issue
Block a user