refactor and func -> proc rename

This commit is contained in:
2026-07-23 11:07:30 +02:00
parent 0338e35e75
commit ad54a00893
18 changed files with 317 additions and 311 deletions
+40 -43
View File
@@ -1,15 +1,18 @@
import "@std" import "@std"
import "@std/mem" import "@std/mem"
import "@std/arraylist" import "@std/arraylist"
#import "@std/static_string_map" import "@std/strmap"
#keywords std.StaticStringMap(TokenKind) :: static_string_map.init([ import "@source/strpool"
# { "func", .func },
# { "return", .return }, keywords std.StaticStringMap(TokenKind) :: strmap.init([
# { "if", .if }, { "proc", .proc },
# { "for", .for }, { "return", .return },
# { "else", .else }, { "if", .if },
#]) { "for", .for },
{ "else", .else },
{ "while", .while },
])
TokenIndex :: alias usize TokenIndex :: alias usize
@@ -23,19 +26,19 @@ State :: struct {
diagnostics std.ArrayList(ScanDiagnostic) diagnostics std.ArrayList(ScanDiagnostic)
} }
init func(allocator mem.Allocator) State { init proc(allocator mem.Allocator) State {
return State { return State {
tokens = arraylist.init(allocator), tokens = arraylist.init(allocator),
diagnostics = arraylist.init(allocator), diagnostics = arraylist.init(allocator),
} }
} }
deinit func(state @mut State) void { deinit proc(state @mut State) void {
arraylist.deinit(&state.tokens) arraylist.deinit(&state.tokens)
arraylist.deinit(&state.diagnostics) arraylist.deinit(&state.diagnostics)
} }
scan func(state @mut State, input []u8) void ! mem.AllocError { scan proc(state @mut State, input []u8) void ! (mem.AllocError | strpool.InternError) {
tokens :: &state.tokens tokens :: &state.tokens
diagnostics :: &state.diagnostics diagnostics :: &state.diagnostics
@@ -56,7 +59,6 @@ scan func(state @mut State, input []u8) void ! mem.AllocError {
# comments # comments
if char == '#' { if char == '#' {
while cursor < input.len and input[cursor] != '\n' : cursor += 1 {} while cursor < input.len and input[cursor] != '\n' : cursor += 1 {}
cursor += 1
continue continue
} }
@@ -64,11 +66,27 @@ scan func(state @mut State, input []u8) void ! mem.AllocError {
if is_alpha(char) or char == '_' { if is_alpha(char) or char == '_' {
start :: cursor start :: cursor
cursor += 1 cursor += 1
while cursor < input.len and (is_alpha(input[cursor]) or is_digit(input[cursor]) or input[cursor] == '_') {
cursor += 1 # scan whole identifier
} while (cursor < input.len and (
kind :: ident_keyword_map(input[start..cursor]) is_alpha(input[cursor]) or
try arraylist.append(tokens, Token{ kind = kind, start = start }) is_digit(input[cursor]) or
input[cursor] == '_'
)) : cursor += 1 {}
kind :: strmap.get(&keywords, input[start..cursor]) orelse .ident
# don't intern keywords (already O(1) lookup via token kind)
str_id :: if (kind == .ident)
try strpool.intern(&strpool.strings, input[start..cursor])
else
strpool.NoId
try arraylist.append(tokens, Token{
kind = kind,
start = start,
str_id = str_id
})
continue continue
} }
@@ -89,7 +107,7 @@ scan func(state @mut State, input []u8) void ! mem.AllocError {
} }
# assert that decimals follow the decimal point # assert that decimals follow the decimal point
if has_decimal and cursor < input.len and !is_digit(input[cursor]) { if has_decimal and (cursor >= input.len or !is_digit(input[cursor])) {
token :: tokens.items.len token :: tokens.items.len
try arraylist.append(tokens, Token{ kind = .invalid, start = start }) try arraylist.append(tokens, Token{ kind = .invalid, start = start })
try arraylist.append(diagnostics, ScanDiagnostic{ token = token, message = "float must end with a digit" }) try arraylist.append(diagnostics, ScanDiagnostic{ token = token, message = "float must end with a digit" })
@@ -167,47 +185,26 @@ scan func(state @mut State, input []u8) void ! mem.AllocError {
token :: tokens.items.len token :: tokens.items.len
try arraylist.append(tokens, Token{ kind = .invalid, start = cursor }) try arraylist.append(tokens, Token{ kind = .invalid, start = cursor })
try arraylist.append(diagnostics, ScanDiagnostic{ token = token, message = "invalid character" }) try arraylist.append(diagnostics, ScanDiagnostic{ token = token, message = "invalid character" })
cursor += 1
} }
try arraylist.append(tokens, Token{ kind = .eof, start = cursor }) try arraylist.append(tokens, Token{ kind = .eof, start = cursor })
} }
hide is_whitespace func(char u8) bool { hide is_whitespace proc(char u8) bool {
return char == ' ' or char == '\t' or char == '\n' or char == '\r' return char == ' ' or char == '\t' or char == '\n' or char == '\r'
} }
hide is_alpha func(char u8) bool { hide is_alpha proc(char u8) bool {
return match char { return match char {
'a'..='z', 'A'..='Z': true 'a'..='z', 'A'..='Z': true
else: false else: false
} }
} }
hide is_digit func(char u8) bool { hide is_digit proc(char u8) bool {
return match char { return match char {
'0'..='9': true '0'..='9': true
else: false else: false
} }
} }
# fixme: replace with a static string map once implemented
hide ident_keyword_map func(ident []u8) TokenKind {
if mem.eql(u8, ident, "if") return .if
if mem.eql(u8, ident, "else") return .else
if mem.eql(u8, ident, "for") return .for
if mem.eql(u8, ident, "while") return .while
if mem.eql(u8, ident, "func") return .func
if mem.eql(u8, ident, "return") return .return
return .ident
}
# fixme(brolang): this function fails to infer the enum type from the return values due to the optional
#hide ident_keyword_map func(ident []u8) ?TokenKind {
# if mem.eql(u8, ident, "if") return .if
# if mem.eql(u8, ident, "else") return .else
# if mem.eql(u8, ident, "for") return .for
# if mem.eql(u8, ident, "while") return .while
# if mem.eql(u8, ident, "func") return .func
# if mem.eql(u8, ident, "return") return .return
# return null
#}
+22
View File
@@ -0,0 +1,22 @@
import "@source/strpool"
import "@std/mem"
import "@std/testing"
handles_keywords_identifiers_and_error_progress test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
state State = init(mem.c_allocator)
defer deinit(&state)
try scan(&state, "if name # comment\n@1.")
try testing.expect_equal(6, state.tokens.items.len)
try testing.expect_equal(TokenKind.if, state.tokens.items[0].kind)
try testing.expect_equal(TokenKind.ident, state.tokens.items[1].kind)
try testing.expect_equal(TokenKind.newline, state.tokens.items[2].kind)
try testing.expect_equal(TokenKind.invalid, state.tokens.items[3].kind)
try testing.expect_equal(TokenKind.invalid, state.tokens.items[4].kind)
try testing.expect_equal(TokenKind.eof, state.tokens.items[5].kind)
try testing.expect_equal(2, state.diagnostics.items.len)
try testing.expect_equal("name", strpool.get_str(&strpool.strings, state.tokens.items[1].str_id)?)
}
+4 -1
View File
@@ -1,9 +1,11 @@
import "@source/strpool"
TokenKind :: enum { TokenKind :: enum {
if if
else else
for for
while while
func proc
return return
ident ident
@@ -28,4 +30,5 @@ TokenKind :: enum {
Token :: struct { Token :: struct {
kind TokenKind kind TokenKind
start int start int
str_id strpool.StringId = strpool.NoId
} }
+7 -9
View File
@@ -4,12 +4,13 @@ import "@std/mem"
test import "@std/enums" test import "@std/enums"
test import "@std/arraylist" test import "@std/arraylist"
test import "@std/hashmap" test import "@std/hashmap"
test import "@std/static_string_map" test import "@std/strmap"
import "@source/strpool" import "@source/strpool"
import "@source/lexer" import "@source/lexer"
test import "@source/strpool" test import "@source/strpool"
test import "@source/lexer"
program :: program ::
`# these are immutable `# these are immutable
@@ -24,17 +25,14 @@ program ::
`else `else
`for `for
`while `while
`func `proc
`return `return
` `
`main func() void {} `main proc() void {}
# cross-cutting concern, hence global singleton main proc() void {
strings StringPool = undefined strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
main func() void {
strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strings)
debug.print("PROGRAM::[[\n{}\n]]\n\n", {program}) debug.print("PROGRAM::[[\n{}\n]]\n\n", {program})
+17 -9
View File
@@ -3,7 +3,13 @@ import "@std/arraylist"
import "@std/hashmap" import "@std/hashmap"
import "@std/testing" import "@std/testing"
StringId :: alias usize # cross-cutting concern, hence global singleton (owned by main.hon)
strings StringPool = undefined
InternError :: enum { out_of_space }
StringId :: alias u32
NoId :: maxval!(StringId)
StringPool :: struct { StringPool :: struct {
ids hashmap.StringHashMap(StringId) # string → id ids hashmap.StringHashMap(StringId) # string → id
@@ -11,7 +17,7 @@ StringPool :: struct {
allocator mem.Allocator allocator mem.Allocator
} }
init func(allocator mem.Allocator) StringPool { init proc(allocator mem.Allocator) StringPool {
return StringPool{ return StringPool{
ids = hashmap.init(allocator), ids = hashmap.init(allocator),
strings = arraylist.init(allocator), strings = arraylist.init(allocator),
@@ -19,7 +25,7 @@ init func(allocator mem.Allocator) StringPool {
} }
} }
deinit func(pool @mut StringPool) void { deinit proc(pool @mut StringPool) void {
hashmap.deinit(&pool.ids) hashmap.deinit(&pool.ids)
for pool.strings.items |str| { for pool.strings.items |str| {
mem.free(pool.allocator, str) mem.free(pool.allocator, str)
@@ -27,10 +33,12 @@ deinit func(pool @mut StringPool) void {
arraylist.deinit(&pool.strings) arraylist.deinit(&pool.strings)
} }
intern func(pool @mut StringPool, str []u8) StringId ! mem.AllocError { intern proc(pool @mut StringPool, str []u8) StringId ! (mem.AllocError | InternError) {
if hashmap.get(&pool.ids, str) |id| return id if hashmap.get(&pool.ids, str) |id| return id
id :: pool.strings.items.len if (pool.strings.items.len >= usize(NoId)) return .out_of_space
id StringId :: StringId(pool.strings.items.len)
owned_str []mut u8 :: try mem.alloc(u8, pool.allocator, str.len) owned_str []mut u8 :: try mem.alloc(u8, pool.allocator, str.len)
errdefer mem.free(pool.allocator, owned_str) errdefer mem.free(pool.allocator, owned_str)
memcopy!(owned_str, str) memcopy!(owned_str, str)
@@ -48,12 +56,12 @@ intern func(pool @mut StringPool, str []u8) StringId ! mem.AllocError {
return id return id
} }
get_str func(pool @StringPool, id StringId) ?[]u8 { get_str proc(pool @StringPool, id StringId) ?[]u8 {
if (id >= pool.strings.items.len) return null if (usize(id) >= pool.strings.items.len) return null
return pool.strings.items[id] return pool.strings.items[usize(id)]
} }
get_id func(pool @StringPool, str []u8) ?StringId { get_id proc(pool @StringPool, str []u8) ?StringId {
return hashmap.get(&pool.ids, str) return hashmap.get(&pool.ids, str)
} }
+7 -7
View File
@@ -1,6 +1,6 @@
import "@std/mem" import "@std/mem"
ArrayList func($T type) type { ArrayList proc($T type) type {
return struct { return struct {
items []mut T items []mut T
capacity usize capacity usize
@@ -8,7 +8,7 @@ ArrayList func($T type) type {
} }
} }
init func($T type, allocator mem.Allocator) ArrayList(T) { init proc($T type, allocator mem.Allocator) ArrayList(T) {
return ArrayList(T) { return ArrayList(T) {
items = mem.empty(T), items = mem.empty(T),
capacity = 0, capacity = 0,
@@ -16,14 +16,14 @@ init func($T type, allocator mem.Allocator) ArrayList(T) {
} }
} }
deinit func($T type, list @mut ArrayList(T)) void { deinit proc($T type, list @mut ArrayList(T)) void {
allocation []mut T :: list.items.ptr[..list.capacity] allocation []mut T :: list.items.ptr[..list.capacity]
mem.free(list.allocator, allocation) mem.free(list.allocator, allocation)
list.items = mem.empty(T) list.items = mem.empty(T)
list.capacity = 0 list.capacity = 0
} }
reserve func($T type, list @mut ArrayList(T), min_capacity usize) void ! mem.AllocError { reserve proc($T type, list @mut ArrayList(T), min_capacity usize) void ! mem.AllocError {
if min_capacity <= list.capacity { if min_capacity <= list.capacity {
return return
} }
@@ -51,7 +51,7 @@ reserve func($T type, list @mut ArrayList(T), min_capacity usize) void ! mem.All
return return
} }
append func($T type, list @mut ArrayList(T), value T) void ! mem.AllocError { append proc($T type, list @mut ArrayList(T), value T) void ! mem.AllocError {
length usize :: list.items.len length usize :: list.items.len
if length == maxval!(usize) { if length == maxval!(usize) {
return .out_of_memory return .out_of_memory
@@ -62,13 +62,13 @@ append func($T type, list @mut ArrayList(T), value T) void ! mem.AllocError {
return return
} }
pop func($T type, list @mut ArrayList(T)) ?T { pop proc($T type, list @mut ArrayList(T)) ?T {
if (list.items.len == 0) return null if (list.items.len == 0) return null
value :: list.items[list.items.len - 1] value :: list.items[list.items.len - 1]
list.items = list.items.ptr[..list.items.len - 1] list.items = list.items.ptr[..list.items.len - 1]
return value return value
} }
clear func($T type, list @mut ArrayList(T)) void { clear proc($T type, list @mut ArrayList(T)) void {
list.items = list.items.ptr[..0] list.items = list.items.ptr[..0]
} }
+2 -2
View File
@@ -1,7 +1,7 @@
import "@ffi/c" import "@ffi/c"
import "@std/io" import "@std/io"
print func($format []u8, $Args type, args Args) void { print proc($format []u8, $Args type, args Args) void {
writer io.Writer :: io.Writer{ writer io.Writer :: io.Writer{
context = null, context = null,
handle = io.Handle{ file_desc = c_int(io.Stream.stderr) }, handle = io.Handle{ file_desc = c_int(io.Stream.stderr) },
@@ -10,7 +10,7 @@ print func($format []u8, $Args type, args Args) void {
io.print(writer, format, Args, args) catch |_| {} io.print(writer, format, Args, args) catch |_| {}
} }
hide write func(_ ?@mut anyopaque, handle io.Handle, bytes []u8) usize ! io.WriteError { hide write proc(_ ?@mut anyopaque, handle io.Handle, bytes []u8) usize ! io.WriteError {
request usize = bytes.len request usize = bytes.len
maximum usize :: usize(maxval!(c_long)) maximum usize :: usize(maxval!(c_long))
if request > maximum { if request > maximum {
+3 -3
View File
@@ -1,6 +1,6 @@
import "@std/meta" import "@std/meta"
EnumMap func($E, $V type) type { EnumMap proc($E, $V type) type {
match typeinfo!(E) { match typeinfo!(E) {
.enum |info|: return struct { .enum |info|: return struct {
present [info.fields.len]mut bool # fixme: replace with bitset present [info.fields.len]mut bool # fixme: replace with bitset
@@ -10,7 +10,7 @@ EnumMap func($E, $V type) type {
} }
} }
init func( init proc(
$E, $V type, $E, $V type,
values meta.EnumFieldStruct(E, ?V, some!(null)), values meta.EnumFieldStruct(E, ?V, some!(null)),
) EnumMap(E, V) { ) EnumMap(E, V) {
@@ -31,7 +31,7 @@ init func(
return map return map
} }
get func($E, $V type, map @EnumMap(E, V), key E) ?V { get proc($E, $V type, map @EnumMap(E, V), key E) ?V {
# fixme: linear lookup; implement an enum index/discriminant map for O(1) lookup # fixme: linear lookup; implement an enum index/discriminant map for O(1) lookup
match typeinfo!(E) { match typeinfo!(E) {
.enum |info|: expand for info.fields |field, i| { .enum |info|: expand for info.fields |field, i| {
+22 -26
View File
@@ -2,7 +2,7 @@ import "@std/mem"
PutError :: enum { key_exists } PutError :: enum { key_exists }
Entry func($K, $V type) type { Entry proc($K, $V type) type {
return struct { return struct {
# hash = 0 means empty # hash = 0 means empty
hash usize = 0 hash usize = 0
@@ -11,10 +11,10 @@ Entry func($K, $V type) type {
} }
} }
HashMap func( HashMap proc(
$K, $V type, $K, $V type,
$hash_key func(key K) usize, $hash_key proc(key K) usize,
$keys_eql func(a, b K) bool, $keys_eql proc(a, b K) bool,
) type { ) type {
return struct { return struct {
entries []mut Entry(K, V) entries []mut Entry(K, V)
@@ -23,14 +23,14 @@ HashMap func(
} }
} }
StringHashMap func($V type) type { StringHashMap proc($V type) type {
return HashMap([]u8, V, str_hash, str_eql) return HashMap([]u8, V, str_hash, str_eql)
} }
init func( init proc(
$K, $V type, $K, $V type,
$hash_key func(key K) usize, $hash_key proc(key K) usize,
$keys_eql func(a, b K) bool, $keys_eql proc(a, b K) bool,
allocator mem.Allocator, allocator mem.Allocator,
) HashMap(K, V, hash_key, keys_eql) { ) HashMap(K, V, hash_key, keys_eql) {
return HashMap(K, V, hash_key, keys_eql){ return HashMap(K, V, hash_key, keys_eql){
@@ -42,17 +42,17 @@ init func(
#! free the entries in the hash map. #! free the entries in the hash map.
#! note: this operation invalidates the map. #! note: this operation invalidates the map.
deinit func( deinit proc(
$K, $V type, $K, $V type,
$hash_key func(key K) usize, $hash_key proc(key K) usize,
$keys_eql func(a, b K) bool, $keys_eql proc(a, b K) bool,
map @HashMap(K, V, hash_key, keys_eql), map @HashMap(K, V, hash_key, keys_eql),
) void { mem.free(map.allocator, map.entries) } ) void { mem.free(map.allocator, map.entries) }
get func( get proc(
$K, $V type, $K, $V type,
$hash_key func(key K) usize, $hash_key proc(key K) usize,
$keys_eql func(a, b K) bool, $keys_eql proc(a, b K) bool,
map @HashMap(K, V, hash_key, keys_eql), map @HashMap(K, V, hash_key, keys_eql),
key K, key K,
) ?V { ) ?V {
@@ -73,10 +73,10 @@ get func(
} }
} }
put func( put proc(
$K, $V type, $K, $V type,
$hash_key func(key K) usize, $hash_key proc(key K) usize,
$keys_eql func(a, b K) bool, $keys_eql proc(a, b K) bool,
map @mut HashMap(K, V, hash_key, keys_eql), map @mut HashMap(K, V, hash_key, keys_eql),
key K, key K,
value V, value V,
@@ -89,9 +89,7 @@ put func(
new_entries :: try mem.alloc(Entry(K, V), map.allocator, new_size) new_entries :: try mem.alloc(Entry(K, V), map.allocator, new_size)
# zero new entries # zero new entries
for 0..new_entries.len |i| { for (0..new_entries.len) |i| new_entries[i].hash = 0
new_entries[i].hash = 0
}
# move old entries # move old entries
for old_entries |entry| { for old_entries |entry| {
@@ -126,7 +124,7 @@ put func(
return return
} }
if (entry.hash == hash and keys_eql(entry.key, key)) { if entry.hash == hash and keys_eql(entry.key, key) {
return .key_exists return .key_exists
} }
@@ -134,7 +132,7 @@ put func(
} }
} }
hide normalize func(hash usize) usize { hide normalize proc(hash usize) usize {
# mapping both 0 and 1 to 1 is safe because equality resolves # mapping both 0 and 1 to 1 is safe because equality resolves
# collisions (since hash and key must both be equal). # collisions (since hash and key must both be equal).
if (hash == 0) return 1 if (hash == 0) return 1
@@ -143,7 +141,7 @@ hide normalize func(hash usize) usize {
#! FNV-1a hash implementation. #! FNV-1a hash implementation.
#! note: vulnerable to collision attacks. #! note: vulnerable to collision attacks.
hide str_hash func(key []u8) usize { hide str_hash proc(key []u8) usize {
hash u32 = 2166136261 # offset basis hash u32 = 2166136261 # offset basis
prime u32 = 16777619 prime u32 = 16777619
@@ -155,6 +153,4 @@ hide str_hash func(key []u8) usize {
return usize(hash) return usize(hash)
} }
hide str_eql func(a, b []u8) bool { hide str_eql proc(a, b []u8) bool { return mem.eql(a, b) }
return mem.eql(a, b)
}
+32 -52
View File
@@ -19,18 +19,16 @@ CloseError :: enum {
close_failed close_failed
} }
open func(io Io, path [;0]u8, mode FileMode) File ! OpenError { open proc(io Io, path [;0]u8, mode FileMode) File ! OpenError {
handle Handle :: io.vtable.open(io.context, path, mode) catch |err| { handle Handle :: try io.vtable.open(io.context, path, mode)
return err return File{ io = io, handle = handle }
}
return File {io = io, handle = handle}
} }
close func(file File) void ! CloseError { close proc(file File) void ! CloseError {
try file.io.vtable.close(file.io.context, file.handle) try file.io.vtable.close(file.io.context, file.handle)
} }
reader func(file File) Reader { reader proc(file File) Reader {
return Reader { return Reader {
context = file.io.context, context = file.io.context,
handle = file.handle, handle = file.handle,
@@ -38,7 +36,7 @@ reader func(file File) Reader {
} }
} }
writer func(file File) Writer { writer proc(file File) Writer {
return Writer { return Writer {
context = file.io.context, context = file.io.context,
handle = file.handle, handle = file.handle,
@@ -46,51 +44,39 @@ writer func(file File) Writer {
} }
} }
hide system_read func(_ ?@mut anyopaque, handle Handle, buffer []mut u8) usize ! ReadError { hide system_read proc(_ ?@mut anyopaque, handle Handle, buffer []mut u8) usize ! ReadError {
request usize = buffer.len request usize = buffer.len
maximum usize :: usize(maxval!(c_long)) maximum usize :: usize(maxval!(c_long))
if request > maximum { if (request > maximum) request = maximum
request = maximum
}
while true { while true {
count c_long :: c.read(handle.file_desc, buffer.ptr, c_ulong(request)) count c_long :: c.read(handle.file_desc, buffer.ptr, c_ulong(request))
if count >= 0 { if (count >= 0) return usize(count)
return usize(count)
}
errno c_int :: c.__error()?^ errno c_int :: c.__error()?^
if errno == c.EINTR { if (errno == c.EINTR) continue
continue if (errno == c.EBADF) return .not_open_for_reading
}
if errno == c.EBADF {
return .not_open_for_reading
}
return .read_failed return .read_failed
} }
} }
hide system_write func(_ ?@mut anyopaque, handle Handle, bytes []u8) usize ! WriteError { hide system_write proc(_ ?@mut anyopaque, handle Handle, bytes []u8) usize ! WriteError {
request usize = bytes.len request usize = bytes.len
maximum usize :: usize(maxval!(c_long)) maximum usize :: usize(maxval!(c_long))
if request > maximum { if (request > maximum) request = maximum
request = maximum
}
while true { while true {
count c_long :: c.write(handle.file_desc, bytes.ptr, c_ulong(request)) count c_long :: c.write(handle.file_desc, bytes.ptr, c_ulong(request))
if count >= 0 { if (count >= 0) return usize(count)
return usize(count)
}
errno c_int :: c.__error()?^ errno c_int :: c.__error()?^
if errno == c.EINTR { if (errno == c.EINTR) continue
continue if (errno == c.EBADF) return .not_open_for_writing
}
if errno == c.EBADF {
return .not_open_for_writing
}
return .write_failed return .write_failed
} }
} }
hide system_open func(_ ?@mut anyopaque, path [;0]u8, mode FileMode) Handle ! OpenError { hide system_open proc(_ ?@mut anyopaque, path [;0]u8, mode FileMode) Handle ! OpenError {
flags c_int = c.O_RDONLY flags c_int = c.O_RDONLY
match mode { match mode {
.read_only: flags = c.O_RDONLY .read_only: flags = c.O_RDONLY
@@ -99,31 +85,25 @@ hide system_open func(_ ?@mut anyopaque, path [;0]u8, mode FileMode) Handle ! Op
} }
while true { while true {
fd c_int :: c.open(ptrcast!(c_char, path.ptr), flags) fd c_int :: c.open(ptrcast!(c_char, path.ptr), flags)
if fd >= 0 { if (fd >= 0) return Handle{ file_desc = fd }
return Handle {file_desc = fd} if (c.__error()?^ != c.EINTR) return .open_failed
}
if c.__error()?^ != c.EINTR {
return .open_failed
}
} }
} }
hide system_close func(_ ?@mut anyopaque, handle Handle) void ! CloseError { hide system_close proc(_ ?@mut anyopaque, handle Handle) void ! CloseError {
if c.close(handle.file_desc) != 0 { if (c.close(handle.file_desc) != 0) return .close_failed
return .close_failed
}
} }
hide system_stdin func(_ ?@mut anyopaque) Handle { hide system_stdin proc(_ ?@mut anyopaque) Handle {
return Handle {file_desc = c_int(Stream.stdin)} return Handle{ file_desc = c_int(Stream.stdin) }
} }
hide system_stdout func(_ ?@mut anyopaque) Handle { hide system_stdout proc(_ ?@mut anyopaque) Handle {
return Handle {file_desc = c_int(Stream.stdout)} return Handle{ file_desc = c_int(Stream.stdout) }
} }
hide system_stderr func(_ ?@mut anyopaque) Handle { hide system_stderr proc(_ ?@mut anyopaque) Handle {
return Handle {file_desc = c_int(Stream.stderr)} return Handle{ file_desc = c_int(Stream.stderr) }
} }
hide system_vtable IoVTable :: IoVTable { hide system_vtable IoVTable :: IoVTable {
@@ -136,7 +116,7 @@ hide system_vtable IoVTable :: IoVTable {
stderr = system_stderr, stderr = system_stderr,
} }
hide system func() Io { hide system proc() Io {
return Io { return Io {
context = null, context = null,
vtable = &system_vtable, vtable = &system_vtable,
+118 -117
View File
@@ -29,64 +29,55 @@ Io :: struct {
} }
IoVTable :: struct { IoVTable :: struct {
read @func(context ?@mut anyopaque, handle Handle, buffer []mut u8) usize ! ReadError read @proc(context ?@mut anyopaque, handle Handle, buffer []mut u8) usize ! ReadError
write @func(context ?@mut anyopaque, handle Handle, bytes []u8) usize ! WriteError write @proc(context ?@mut anyopaque, handle Handle, bytes []u8) usize ! WriteError
open @func(context ?@mut anyopaque, path [;0]u8, mode FileMode) Handle ! OpenError open @proc(context ?@mut anyopaque, path [;0]u8, mode FileMode) Handle ! OpenError
close @func(context ?@mut anyopaque, handle Handle) void ! CloseError close @proc(context ?@mut anyopaque, handle Handle) void ! CloseError
stdin @func(context ?@mut anyopaque) Handle stdin @proc(context ?@mut anyopaque) Handle
stdout @func(context ?@mut anyopaque) Handle stdout @proc(context ?@mut anyopaque) Handle
stderr @func(context ?@mut anyopaque) Handle stderr @proc(context ?@mut anyopaque) Handle
} }
Reader :: struct { Reader :: struct {
context ?@mut anyopaque context ?@mut anyopaque
handle Handle handle Handle
read @func(context ?@mut anyopaque, handle Handle, buffer []mut u8) usize ! ReadError read @proc(context ?@mut anyopaque, handle Handle, buffer []mut u8) usize ! ReadError
} }
Writer :: struct { Writer :: struct {
context ?@mut anyopaque context ?@mut anyopaque
handle Handle handle Handle
write @func(context ?@mut anyopaque, handle Handle, bytes []u8) usize ! WriteError write @proc(context ?@mut anyopaque, handle Handle, bytes []u8) usize ! WriteError
} }
read func(input Reader, buffer []mut u8) usize ! ReadError { read proc(input Reader, buffer []mut u8) usize ! ReadError {
if buffer.len == 0 { if (buffer.len == 0) return 0
return 0
}
count usize :: try input.read(input.context, input.handle, buffer) count usize :: try input.read(input.context, input.handle, buffer)
if count > buffer.len { if (count > buffer.len) return .read_failed
return .read_failed
}
return count return count
} }
write func(output Writer, bytes []u8) usize ! WriteError { write proc(output Writer, bytes []u8) usize ! WriteError {
if bytes.len == 0 { if (bytes.len == 0) return 0
return 0
}
count usize :: try output.write(output.context, output.handle, bytes) count usize :: try output.write(output.context, output.handle, bytes)
if count > bytes.len { if (count > bytes.len) return .write_failed
return .write_failed
}
return count return count
} }
write_all func(output Writer, bytes []u8) void ! WriteError { write_all proc(output Writer, bytes []u8) void ! WriteError {
offset usize = 0 offset usize = 0
while offset < bytes.len { while offset < bytes.len {
count usize :: write(output, bytes[offset..]) catch |err| { count usize :: try write(output, bytes[offset..])
return err if (count == 0) return .no_progress
}
if count == 0 {
return .no_progress
}
offset += count offset += count
} }
return
} }
stdin func(io Io) Reader { stdin proc(io Io) Reader {
return Reader { return Reader {
context = io.context, context = io.context,
handle = io.vtable.stdin(io.context), handle = io.vtable.stdin(io.context),
@@ -94,7 +85,7 @@ stdin func(io Io) Reader {
} }
} }
stdout func(io Io) Writer { stdout proc(io Io) Writer {
return Writer { return Writer {
context = io.context, context = io.context,
handle = io.vtable.stdout(io.context), handle = io.vtable.stdout(io.context),
@@ -102,7 +93,7 @@ stdout func(io Io) Writer {
} }
} }
stderr func(io Io) Writer { stderr proc(io Io) Writer {
return Writer { return Writer {
context = io.context, context = io.context,
handle = io.vtable.stderr(io.context), handle = io.vtable.stderr(io.context),
@@ -110,7 +101,7 @@ stderr func(io Io) Writer {
} }
} }
print func(output Writer, $format []u8, $Args type, args Args) void ! WriteError { print proc(output Writer, $format []u8, $Args type, args Args) void ! WriteError {
expand for parse_format(format.len, format, Args) |token| { expand for parse_format(format.len, format, Args) |token| {
match token.kind { match token.kind {
.unused: break .unused: break
@@ -128,60 +119,58 @@ print func(output Writer, $format []u8, $Args type, args Args) void ! WriteError
} }
} }
hide write_integer_signed func(output Writer, value i64, base u64, uppercase bool) void ! WriteError { hide write_integer_signed proc(output Writer, value i64, base u64, uppercase bool) void ! WriteError {
buffer [65]mut u8 = undefined buffer [65]mut u8 = undefined
end usize = buffer.len end usize = buffer.len
current i64 = value current i64 = value
while true { while true {
digit_value i64 :: rem!(current, i64(base)) digit_value i64 :: rem!(current, i64(base))
digit u8 = 0 digit u8 = if (digit_value < 0)
if digit_value < 0 { u8(-digit_value)
digit = u8(-digit_value) else
} else { u8(digit_value)
digit = u8(digit_value)
}
end -= 1 end -= 1
if digit < 10 { buffer[end] = if (digit < 10)
buffer[end] = '0' + digit '0' + digit
} else if uppercase { else if (uppercase)
buffer[end] = 'A' + digit - 10 'A' + digit - 10
} else { else
buffer[end] = 'a' + digit - 10 'a' + digit - 10
}
current = divtrunc!(current, i64(base)) current = divtrunc!(current, i64(base))
if current == 0 { if (current == 0) break
break
}
} }
if value < 0 { if value < 0 {
end -= 1 end -= 1
buffer[end] = '-' buffer[end] = '-'
} }
try write_all(output, buffer[end..]) try write_all(output, buffer[end..])
return
} }
hide write_integer_unsigned func(output Writer, value u64, base u64, uppercase bool) void ! WriteError { hide write_integer_unsigned proc(output Writer, value u64, base u64, uppercase bool) void ! WriteError {
buffer [65]mut u8 = undefined buffer [65]mut u8 = undefined
end usize = buffer.len end usize = buffer.len
current u64 = value current u64 = value
while true { while true {
digit u8 :: u8(rem!(current, base)) digit u8 :: u8(rem!(current, base))
end -= 1 end -= 1
if digit < 10 { buffer[end] = if (digit < 10)
buffer[end] = '0' + digit '0' + digit
} else if uppercase { else if (uppercase)
buffer[end] = 'A' + digit - 10 'A' + digit - 10
} else { else
buffer[end] = 'a' + digit - 10 'a' + digit - 10
}
current = divtrunc!(current, base) current = divtrunc!(current, base)
if current == 0 { if (current == 0) break
break
}
} }
try write_all(output, buffer[end..]) try write_all(output, buffer[end..])
return
} }
hide FormatTokenKind :: enum { hide FormatTokenKind :: enum {
@@ -205,18 +194,17 @@ hide FormatToken :: struct {
field []u8 field []u8
} }
hide parse_format func($N usize, $format []u8, $Args type) [N]mut FormatToken { hide parse_format proc($N usize, $format []u8, $Args type) [N]mut FormatToken {
tokens [N]mut FormatToken = undefined tokens [N]mut FormatToken = undefined
for (usize(0))..format.len |index| { for (usize(0))..format.len |index| {
tokens[index] = FormatToken {kind = .unused, start = 0, end = 0, field = ""} tokens[index] = FormatToken{ kind = .unused, start = 0, end = 0, field = "" }
} }
field_count usize = 0 field_count usize = 0
match typeinfo!(Args) { match typeinfo!(Args) {
.record |record|: { .record |r|: {
if !record.is_tuple { if (!r.is_tuple) compile_error!("io.print arguments must be a tuple")
compile_error!("io.print arguments must be a tuple") field_count = r.fields.len
}
field_count = record.fields.len
} }
else: compile_error!("io.print arguments must be a tuple") else: compile_error!("io.print arguments must be a tuple")
} }
@@ -232,68 +220,84 @@ hide parse_format func($N usize, $format []u8, $Args type) [N]mut FormatToken {
compile_error!("io.print format has an unmatched '{'") compile_error!("io.print format has an unmatched '{'")
} }
if cursor > literal_start { if cursor > literal_start {
tokens[token_count] = FormatToken {kind = .literal, start = literal_start, end = cursor, field = ""} tokens[token_count] = FormatToken{
kind = .literal,
start = literal_start,
end = cursor,
field = "",
}
token_count += 1 token_count += 1
} }
next :: format[cursor + 1] next :: format[cursor + 1]
if next == '{' { if next == '{' {
tokens[token_count] = FormatToken {kind = .literal, start = cursor, end = cursor + 1, field = ""} tokens[token_count] = FormatToken{
kind = .literal,
start = cursor,
end = cursor + 1,
field = "",
}
token_count += 1 token_count += 1
cursor += 2 cursor += 2
literal_start = cursor literal_start = cursor
continue continue
} }
kind FormatTokenKind = .default kind FormatTokenKind = .default
width usize = 2 width usize = 2
if next != '}' { if next != '}' {
if cursor + 2 >= format.len or format[cursor + 2] != '}' { if cursor + 2 >= format.len or format[cursor + 2] != '}' {
compile_error!("io.print format expects a one-character specifier") compile_error!("io.print format expects a one-character specifier")
} }
width = 3 width = 3
if next == 's' { if (next == 's') kind = .string
kind = .string else if (next == 'd') kind = .decimal
} else if next == 'd' { else if (next == 'b') kind = .binary
kind = .decimal else if (next == 'o') kind = .octal
} else if next == 'b' { else if (next == 'x') kind = .hex_lower
kind = .binary else if (next == 'X') kind = .hex_upper
} else if next == 'o' { else if (next == 'c') kind = .character
kind = .octal else if (next == 'e') kind = .scientific
} else if next == 'x' { else compile_error!("io.print format has an unknown specifier")
kind = .hex_lower
} else if next == 'X' {
kind = .hex_upper
} else if next == 'c' {
kind = .character
} else if next == 'e' {
kind = .scientific
} else {
compile_error!("io.print format has an unknown specifier")
}
} }
if argument_count >= field_count { if argument_count >= field_count {
compile_error!("io.print format argument count does not match the tuple") compile_error!("io.print format argument count does not match the tuple")
} }
tokens[token_count] = FormatToken {
tokens[token_count] = FormatToken{
kind = kind, kind = kind,
start = 0, start = 0,
end = 0, end = 0,
field = format_field_name(Args, argument_count), field = format_field_name(Args, argument_count),
} }
token_count += 1 token_count += 1
argument_count += 1 argument_count += 1
cursor += width cursor += width
literal_start = cursor literal_start = cursor
continue continue
} }
if byte == '}' { if byte == '}' {
if cursor + 1 >= format.len or format[cursor + 1] != '}' { if cursor + 1 >= format.len or format[cursor + 1] != '}' {
compile_error!("io.print format has an unmatched '}'") compile_error!("io.print format has an unmatched '}'")
} }
if cursor > literal_start { if cursor > literal_start {
tokens[token_count] = FormatToken {kind = .literal, start = literal_start, end = cursor, field = ""} tokens[token_count] = FormatToken{
kind = .literal,
start = literal_start,
end = cursor,
field = "",
}
token_count += 1 token_count += 1
} }
tokens[token_count] = FormatToken {kind = .literal, start = cursor, end = cursor + 1, field = ""} tokens[token_count] = FormatToken{
kind = .literal,
start = cursor,
end = cursor + 1,
field = "",
}
token_count += 1 token_count += 1
cursor += 2 cursor += 2
literal_start = cursor literal_start = cursor
@@ -301,23 +305,30 @@ hide parse_format func($N usize, $format []u8, $Args type) [N]mut FormatToken {
} }
cursor += 1 cursor += 1
} }
if literal_start < format.len { if literal_start < format.len {
tokens[token_count] = FormatToken {kind = .literal, start = literal_start, end = format.len, field = ""} tokens[token_count] = FormatToken{
kind = .literal,
start = literal_start,
end = format.len,
field = "",
} }
}
if argument_count != field_count { if argument_count != field_count {
compile_error!("io.print format argument count does not match the tuple") compile_error!("io.print format argument count does not match the tuple")
} }
return tokens return tokens
} }
hide format_field_name func($T type, index usize) []u8 { hide format_field_name proc($T type, index usize) []u8 {
match typeinfo!(T) { match typeinfo!(T) {
.record |record|: return record.fields[index].name .record |r|: return r.fields[index].name
else: compile_error!("io.print arguments must be a tuple") else: compile_error!("io.print arguments must be a tuple")
} }
} }
hide write_integer func(output Writer, $T type, value T, base u64, uppercase bool) void ! WriteError { hide write_integer proc(output Writer, $T type, value T, base u64, uppercase bool) void ! WriteError {
match typeinfo!(T) { match typeinfo!(T) {
.integer: if minval!(T) < 0 { .integer: if minval!(T) < 0 {
try write_integer_signed(output, i64(value), base, uppercase) try write_integer_signed(output, i64(value), base, uppercase)
@@ -326,46 +337,38 @@ hide write_integer func(output Writer, $T type, value T, base u64, uppercase boo
} }
else: compile_error!("io.print integer format requires an integer argument") else: compile_error!("io.print integer format requires an integer argument")
} }
return
} }
# note: libc keeps float formatting small; replace it with a native shortest-roundtrip writer if locale independence matters. # note: libc keeps float formatting small; replace it with a native shortest-roundtrip writer if locale independence matters.
hide write_float func(output Writer, $T type, value T, scientific bool) void ! WriteError { hide write_float proc(output Writer, $T type, value T, scientific bool) void ! WriteError {
match typeinfo!(T) { match typeinfo!(T) {
.float: { .float: {
buffer [64]mut u8 = undefined buffer [64]mut u8 = undefined
count c_int = 0 count c_int = 0
if sizeof!(T) == 4 { if sizeof!(T) == 4 {
if scientific { if (scientific) count = c.snprintf(ptrcast!(c_char, (&buffer).ptr), c_ulong(buffer.len), "%.8e", value)
count = c.snprintf(ptrcast!(c_char, (&buffer).ptr), c_ulong(buffer.len), "%.8e", value) else count = c.snprintf(ptrcast!(c_char, (&buffer).ptr), c_ulong(buffer.len), "%.9g", value)
} else {
count = c.snprintf(ptrcast!(c_char, (&buffer).ptr), c_ulong(buffer.len), "%.9g", value)
}
} else if scientific { } else if scientific {
count = c.snprintf(ptrcast!(c_char, (&buffer).ptr), c_ulong(buffer.len), "%.16e", value) count = c.snprintf(ptrcast!(c_char, (&buffer).ptr), c_ulong(buffer.len), "%.16e", value)
} else { } else {
count = c.snprintf(ptrcast!(c_char, (&buffer).ptr), c_ulong(buffer.len), "%.17g", value) count = c.snprintf(ptrcast!(c_char, (&buffer).ptr), c_ulong(buffer.len), "%.17g", value)
} }
if count < 0 or usize(count) >= buffer.len { if (count < 0 or usize(count) >= buffer.len) return .write_failed
return .write_failed
}
try write_all(output, buffer[0..usize(count)]) try write_all(output, buffer[0..usize(count)])
} }
else: compile_error!("io.print float format requires a float argument") else: compile_error!("io.print float format requires a float argument")
} }
return
} }
hide write_decimal func(output Writer, $T type, value T) void ! WriteError { hide write_decimal proc(output Writer, $T type, value T) void ! WriteError {
match typeinfo!(T) { match typeinfo!(T) {
.integer: try write_integer(output, value, 10, false) .integer: try write_integer(output, value, 10, false)
.float: try write_float(output, value, false) .float: try write_float(output, value, false)
else: compile_error!("io.print '{d}' requires an integer or float argument") else: compile_error!("io.print '{d}' requires an integer or float argument")
} }
return
} }
hide write_character func(output Writer, $T type, value T) void ! WriteError { hide write_character proc(output Writer, $T type, value T) void ! WriteError {
match typeinfo!(T) { match typeinfo!(T) {
.integer: { .integer: {
if minval!(T) < 0 or maxval!(T) > 255 { if minval!(T) < 0 or maxval!(T) > 255 {
@@ -376,10 +379,9 @@ hide write_character func(output Writer, $T type, value T) void ! WriteError {
} }
else: compile_error!("io.print '{c}' requires an unsigned integer that fits in u8") else: compile_error!("io.print '{c}' requires an unsigned integer that fits in u8")
} }
return
} }
hide write_default func(output Writer, $T type, value T) void ! WriteError { hide write_default proc(output Writer, $T type, value T) void ! WriteError {
match typeinfo!(T) { match typeinfo!(T) {
.bool: if value { .bool: if value {
try write_all(output, "true") try write_all(output, "true")
@@ -403,5 +405,4 @@ hide write_default func(output Writer, $T type, value T) void ! WriteError {
} }
else: compile_error!("io.print '{}' does not support this argument type") else: compile_error!("io.print '{}' does not support this argument type")
} }
return
} }
+27 -27
View File
@@ -10,24 +10,24 @@ Allocator :: struct {
} }
AllocatorVTable :: struct { AllocatorVTable :: struct {
alloc @func(context ?@mut anyopaque, size usize, alignment usize) ?*mut u8 alloc @proc(context ?@mut anyopaque, size usize, alignment usize) ?*mut u8
realloc @func(context ?@mut anyopaque, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8 realloc @proc(context ?@mut anyopaque, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8
free @func(context ?@mut anyopaque, memory ?*mut u8, size usize, alignment usize) void free @proc(context ?@mut anyopaque, memory ?*mut u8, size usize, alignment usize) void
} }
raw_alloc func(allocator Allocator, size usize, alignment usize) ?*mut u8 { raw_alloc proc(allocator Allocator, size usize, alignment usize) ?*mut u8 {
return allocator.vtable.alloc(allocator.context, size, alignment) return allocator.vtable.alloc(allocator.context, size, alignment)
} }
raw_realloc func(allocator Allocator, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8 { raw_realloc proc(allocator Allocator, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8 {
return allocator.vtable.realloc(allocator.context, memory, old_size, new_size, alignment) return allocator.vtable.realloc(allocator.context, memory, old_size, new_size, alignment)
} }
raw_free func(allocator Allocator, memory ?*mut u8, size usize, alignment usize) void { raw_free proc(allocator Allocator, memory ?*mut u8, size usize, alignment usize) void {
allocator.vtable.free(allocator.context, memory, size, alignment) allocator.vtable.free(allocator.context, memory, size, alignment)
} }
eql func($T type, left, right []T) bool { eql proc($T type, left, right []T) bool {
if (left.len != right.len) return false if (left.len != right.len) return false
for (0..left.len) |i| if (left[i] != right[i]) { for (0..left.len) |i| if (left[i] != right[i]) {
return false return false
@@ -36,7 +36,7 @@ eql func($T type, left, right []T) bool {
} }
#! allocate memory for a slice of type `T` with `count` elements. #! allocate memory for a slice of type `T` with `count` elements.
alloc func($T type, allocator Allocator, count usize) []mut T ! AllocError { alloc proc($T type, allocator Allocator, count usize) []mut T ! AllocError {
if (count == 0) return empty_slice(T, 0) if (count == 0) return empty_slice(T, 0)
element_size usize :: sizeof!(T) element_size usize :: sizeof!(T)
@@ -51,13 +51,14 @@ alloc func($T type, allocator Allocator, count usize) []mut T ! AllocError {
pointer *mut T :: ptrcast!(T, bytes) pointer *mut T :: ptrcast!(T, bytes)
return pointer[..count] return pointer[..count]
} }
return .out_of_memory return .out_of_memory
} }
#! reallocate memory for a slice of type `T` with `new_count` elements. #! reallocate memory for a slice of type `T` with `new_count` elements.
#! reallocating with `new_count == 0` will free the memory and return an empty slice. #! reallocating with `new_count == 0` will free the memory and return an empty slice.
#! note: memory must be reallocated with the same allocator that was used to allocate it. #! note: memory must be reallocated with the same allocator that was used to allocate it.
realloc func($T type, allocator Allocator, memory []mut T, new_count usize) []mut T ! AllocError { realloc proc($T type, allocator Allocator, memory []mut T, new_count usize) []mut T ! AllocError {
if new_count == memory.len { if new_count == memory.len {
return memory return memory
} }
@@ -68,12 +69,8 @@ realloc func($T type, allocator Allocator, memory []mut T, new_count usize) []mu
} }
element_size usize :: sizeof!(T) element_size usize :: sizeof!(T)
if element_size == 0 { if (element_size == 0) return empty_slice(T, new_count)
return empty_slice(T, new_count) if (new_count > divtrunc!(maxval!(usize), element_size)) return .out_of_memory
}
if new_count > divtrunc!(maxval!(usize), element_size) {
return .out_of_memory
}
old_memory ?*mut u8 = null old_memory ?*mut u8 = null
old_size usize = 0 old_size usize = 0
@@ -98,7 +95,7 @@ realloc func($T type, allocator Allocator, memory []mut T, new_count usize) []mu
#! free memory allocated for a slice of type `T`. #! free memory allocated for a slice of type `T`.
#! note: memory must be freed with the same allocator that was used to allocate it. #! note: memory must be freed with the same allocator that was used to allocate it.
free func($T type, allocator Allocator, memory []T) void { free proc($T type, allocator Allocator, memory []T) void {
if (memory.len == 0 or sizeof!(T) == 0) return if (memory.len == 0 or sizeof!(T) == 0) return
raw_free(allocator, ptrcast!( raw_free(allocator, ptrcast!(
u8, u8,
@@ -109,21 +106,21 @@ free func($T type, allocator Allocator, memory []T) void {
} }
#! get an empty slice of type `T` with `count` elements. #! get an empty slice of type `T` with `count` elements.
empty_slice func($T type, count usize) []mut T { empty_slice proc($T type, count usize) []mut T {
pointer *mut T :: ptrcast!(T, (&empty_storage).ptr) pointer *mut T :: ptrcast!(T, (&empty_storage).ptr)
return pointer[..count] return pointer[..count]
} }
#! get an empty slice of type `T` with 0 elements. #! get an empty slice of type `T` with 0 elements.
empty func($T type) []mut T { empty proc($T type) []mut T {
return empty_slice(T, 0) return empty_slice(T, 0)
} }
hide empty_storage [1]mut u64 = [0] hide empty_storage [1]mut u64 = [0]
hide malloc_alignment usize :: 16 # ponytail: aarch64-macos libc malloc alignment assumption. hide malloc_alignment usize :: 16 # note: aarch64-macos libc malloc alignment assumption.
hide power_of_two func(value usize) bool { hide power_of_two proc(value usize) bool {
if (value == 0) return false if (value == 0) return false
current usize = value current usize = value
while current > 1 { while current > 1 {
@@ -134,12 +131,9 @@ hide power_of_two func(value usize) bool {
return true return true
} }
hide c_alloc func(_ ?@mut anyopaque, size usize, alignment usize) ?*mut u8 { hide c_alloc proc(_ ?@mut anyopaque, size usize, alignment usize) ?*mut u8 {
if (power_of_two(alignment) == false) return null if (power_of_two(alignment) == false) return null
if (alignment <= malloc_alignment) return ptrcast!(u8, c.malloc(c_ulong(size)))
if alignment <= malloc_alignment {
return ptrcast!(u8, c.malloc(c_ulong(size)))
}
memory [1]mut ?*mut anyopaque = [null] memory [1]mut ?*mut anyopaque = [null]
status c_int = c.posix_memalign((&memory).ptr, c_ulong(alignment), c_ulong(size)) status c_int = c.posix_memalign((&memory).ptr, c_ulong(alignment), c_ulong(size))
@@ -148,7 +142,13 @@ hide c_alloc func(_ ?@mut anyopaque, size usize, alignment usize) ?*mut u8 {
return ptrcast!(u8, memory[0]) return ptrcast!(u8, memory[0])
} }
hide c_realloc func(_ ?@mut anyopaque, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8 { hide c_realloc proc(
_ ?@mut anyopaque,
memory ?*mut u8,
old_size usize,
new_size usize,
alignment usize,
) ?*mut u8 {
if (power_of_two(alignment) == false) return null if (power_of_two(alignment) == false) return null
if new_size == 0 { if new_size == 0 {
@@ -174,7 +174,7 @@ hide c_realloc func(_ ?@mut anyopaque, memory ?*mut u8, old_size usize, new_size
return c_alloc(null, new_size, alignment) return c_alloc(null, new_size, alignment)
} }
hide c_free func(_ ?@mut anyopaque, memory ?*mut u8, _ usize, _ usize) void { hide c_free proc(_ ?@mut anyopaque, memory ?*mut u8, _ usize, _ usize) void {
c.free(memory) c.free(memory)
} }
+1 -1
View File
@@ -43,7 +43,7 @@ TypeInfo :: union(enum) {
distinct void distinct void
} }
EnumFieldStruct func($E, $Field type, $default ?Field) type { EnumFieldStruct proc($E, $Field type, $default ?Field) type {
match typeinfo!(E) { match typeinfo!(E) {
.enum |info|: { .enum |info|: {
names [info.fields.len]mut []u8 = undefined names [info.fields.len]mut []u8 = undefined
+1 -1
View File
@@ -9,7 +9,7 @@ TestTokenKind :: enum(u8) {
TestNames :: alias EnumFieldStruct(TestTokenKind, ?[]u8, some!(null)) TestNames :: alias EnumFieldStruct(TestTokenKind, ?[]u8, some!(null))
TestArrayAlias :: alias [3]u16 TestArrayAlias :: alias [3]u16
hide array_info_matches func($Array, $Child type, $len usize) bool { hide array_info_matches proc($Array, $Child type, $len usize) bool {
match typeinfo!(Array) { match typeinfo!(Array) {
.array |info|: return info.child == Child and info.len == len .array |info|: return info.child == Child and info.len == len
else: return false else: return false
+2 -2
View File
@@ -2,10 +2,10 @@ import "io"
import "enums" import "enums"
import "hashmap" import "hashmap"
import "arraylist" import "arraylist"
import "static_string_map" import "strmap"
Io :: alias io.Io Io :: alias io.Io
EnumMap :: alias enums.EnumMap EnumMap :: alias enums.EnumMap
ArrayList :: alias arraylist.ArrayList ArrayList :: alias arraylist.ArrayList
StringHashMap :: alias hashmap.StringHashMap StringHashMap :: alias hashmap.StringHashMap
StaticStringMap :: alias static_string_map.StaticStringMap StaticStringMap :: alias strmap.StaticStringMap
@@ -1,6 +1,6 @@
import "@std/mem" import "@std/mem"
StaticStringMap func($V type) type { StaticStringMap proc($V type) type {
return struct { return struct {
keys [][]u8 keys [][]u8
values []V values []V
@@ -10,11 +10,11 @@ StaticStringMap func($V type) type {
} }
} }
hide Pair func($V type) type { hide Pair proc($V type) type {
return struct { []u8, V } return struct { []u8, V }
} }
init func($V type, $N usize, $entries [N]Pair(V)) StaticStringMap(V) { init proc($V type, $N usize, $entries [N]Pair(V)) StaticStringMap(V) {
if N > usize(maxval!(u32)) { if N > usize(maxval!(u32)) {
compile_error!("static string map has too many entries") compile_error!("static string map has too many entries")
} }
@@ -64,7 +64,7 @@ init func($V type, $N usize, $entries [N]Pair(V)) StaticStringMap(V) {
max_len u32 :: u32(keys[N - 1].len) max_len u32 :: u32(keys[N - 1].len)
len_indexes [usize(max_len) + 1]mut u32 = undefined len_indexes [usize(max_len) + 1]mut u32 = undefined
entry_index usize = 0 entry_index usize = 0
for 0..=(usize(max_len)) |length| { # fixme: for casts and function calls, we should be able to omit the surrounding parentheses in the range for 0..=usize(max_len) |length| {
while entry_index < N and keys[entry_index].len < length : entry_index += 1 {} while entry_index < N and keys[entry_index].len < length : entry_index += 1 {}
len_indexes[length] = u32(entry_index) len_indexes[length] = u32(entry_index)
} }
@@ -78,7 +78,7 @@ init func($V type, $N usize, $entries [N]Pair(V)) StaticStringMap(V) {
} }
} }
get func($V type, map @StaticStringMap(V), key []u8) ?V { get proc($V type, map @StaticStringMap(V), key []u8) ?V {
if (map.keys.len == 0 or key.len > maxval!(u32)) return null if (map.keys.len == 0 or key.len > maxval!(u32)) return null
length u32 = u32(key.len) length u32 = u32(key.len)
@@ -90,4 +90,5 @@ get func($V type, map @StaticStringMap(V), key []u8) ?V {
if (candidate.len != key.len) return null if (candidate.len != key.len) return null
if mem.eql(u8, candidate, key) return map.values[idx] if mem.eql(u8, candidate, key) return map.values[idx]
} }
return null
} }
+5 -5
View File
@@ -11,7 +11,7 @@ SourceLocation :: struct {
column usize column usize
} }
expect func(condition bool, location SourceLocation) void ! Error { expect proc(condition bool, location SourceLocation) void ! Error {
if !condition { if !condition {
debug.print("{s}:{d}:{d}: expectation failed\n", { debug.print("{s}:{d}:{d}: expectation failed\n", {
location.file, location.file,
@@ -22,7 +22,7 @@ expect func(condition bool, location SourceLocation) void ! Error {
} }
} }
expect_equal func($T type, expected, actual T, location SourceLocation) void ! Error { expect_equal proc($T type, expected, actual T, location SourceLocation) void ! Error {
match typeinfo!(T) { match typeinfo!(T) {
.optional: { .optional: {
if expected |expected_value| { if expected |expected_value| {
@@ -67,11 +67,11 @@ expect_equal func($T type, expected, actual T, location SourceLocation) void ! E
} }
} }
expect_type func($Expected, $Actual type, _ Actual, location SourceLocation) void ! Error { expect_type proc($Expected, $Actual type, _ Actual, location SourceLocation) void ! Error {
try expect($(Expected == Actual), location) try expect($(Expected == Actual), location)
} }
run func(name []u8, callback *func() void ! Error) bool { run proc(name []u8, callback *proc() void ! Error) bool {
callback() catch |_| { callback() catch |_| {
debug.print("{s}...[failed]\n", {name,}) debug.print("{s}...[failed]\n", {name,})
return false return false
@@ -80,6 +80,6 @@ run func(name []u8, callback *func() void ! Error) bool {
return true return true
} }
summary func(passed, failed i32) void { summary proc(passed, failed i32) void {
debug.print("{d} passed, {d} failed\n", {passed, failed}) debug.print("{d} passed, {d} failed\n", {passed, failed})
} }