Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 410246faee | |||
| 1689c4db3c |
@@ -1,6 +1,7 @@
|
|||||||
import "@source/lexer"
|
import "@source/lexer"
|
||||||
|
|
||||||
hide TokenId :: alias lexer.TokenId
|
hide TokenId :: alias lexer.TokenId
|
||||||
|
hide Token :: alias lexer.Token
|
||||||
|
|
||||||
NodeId :: distinct u32
|
NodeId :: distinct u32
|
||||||
ExtraId :: distinct u32
|
ExtraId :: distinct u32
|
||||||
@@ -48,3 +49,22 @@ NodeKind :: enum {
|
|||||||
|
|
||||||
invalid
|
invalid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# todo: render ast nodes as source code
|
||||||
|
|
||||||
|
render proc(node @Node, tokens []Token) void { }
|
||||||
|
|
||||||
|
render_literal proc(node @Node, tokens []Token, program []u8) []u8 ! lexer.ScanError {
|
||||||
|
tok :: tokens[node.main_token]
|
||||||
|
match node.kind {
|
||||||
|
.literal_int, .literal_float: {
|
||||||
|
result :: try lexer.scan_number(tok.start, program)
|
||||||
|
return program[tok.start..result.end]
|
||||||
|
}
|
||||||
|
.literal_string: {
|
||||||
|
result :: try lexer.scan_string(tok.start, program)
|
||||||
|
return program[tok.start..result.end]
|
||||||
|
}
|
||||||
|
else: unreachable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+68
-73
@@ -7,9 +7,10 @@ import "@std/debug"
|
|||||||
|
|
||||||
import "@source/strpool"
|
import "@source/strpool"
|
||||||
|
|
||||||
ErrorCode :: enum {
|
ScanError :: enum {
|
||||||
invalid_character,
|
invalid_character,
|
||||||
float_must_end_with_digit,
|
float_must_end_with_digit,
|
||||||
|
unterminated_string,
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorDetails :: struct {
|
ErrorDetails :: struct {
|
||||||
@@ -17,7 +18,7 @@ ErrorDetails :: struct {
|
|||||||
message []u8
|
message []u8
|
||||||
}
|
}
|
||||||
|
|
||||||
error_msg_map std.EnumMap(ErrorCode, ErrorDetails) :: enummap.init({
|
error_msg_map std.EnumMap(ScanError, ErrorDetails) :: enummap.init({
|
||||||
invalid_character = ErrorDetails {
|
invalid_character = ErrorDetails {
|
||||||
name = "L0",
|
name = "L0",
|
||||||
message = "invalid character",
|
message = "invalid character",
|
||||||
@@ -40,8 +41,8 @@ keywords std.StringMap(TokenKind) :: strmap.init([
|
|||||||
TokenId :: distinct u32
|
TokenId :: distinct u32
|
||||||
|
|
||||||
Diagnostic :: struct {
|
Diagnostic :: struct {
|
||||||
code ErrorCode
|
|
||||||
token TokenId
|
token TokenId
|
||||||
|
code ScanError
|
||||||
}
|
}
|
||||||
|
|
||||||
State :: struct {
|
State :: struct {
|
||||||
@@ -61,13 +62,13 @@ deinit proc(state @mut State) void {
|
|||||||
arraylist.deinit(&state.diagnostics)
|
arraylist.deinit(&state.diagnostics)
|
||||||
}
|
}
|
||||||
|
|
||||||
scan proc(state @mut State, input []u8) void ! (mem.AllocError | strpool.InternError) {
|
scan proc(state @mut State, program []u8) void ! (mem.AllocError | strpool.InternError) {
|
||||||
tokens :: &state.tokens
|
tokens :: &state.tokens
|
||||||
diagnostics :: &state.diagnostics
|
diagnostics :: &state.diagnostics
|
||||||
|
|
||||||
cursor usize = 0
|
cursor usize := 0
|
||||||
while cursor < input.len {
|
while cursor < program.len {
|
||||||
char :: input[cursor]
|
char :: program[cursor]
|
||||||
|
|
||||||
# whitespace
|
# whitespace
|
||||||
if char == '\n' {
|
if char == '\n' {
|
||||||
@@ -81,7 +82,7 @@ scan proc(state @mut State, input []u8) void ! (mem.AllocError | strpool.InternE
|
|||||||
|
|
||||||
# comments
|
# comments
|
||||||
if char == '#' {
|
if char == '#' {
|
||||||
while (cursor < input.len and input[cursor] != '\n') cursor += 1
|
while (cursor < program.len and program[cursor] != '\n') cursor += 1
|
||||||
cursor += 1 # also skip newline
|
cursor += 1 # also skip newline
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -92,17 +93,17 @@ scan proc(state @mut State, input []u8) void ! (mem.AllocError | strpool.InternE
|
|||||||
cursor += 1
|
cursor += 1
|
||||||
|
|
||||||
# scan whole identifier
|
# scan whole identifier
|
||||||
while (cursor < input.len and (
|
while (cursor < program.len and (
|
||||||
is_alpha(input[cursor]) or
|
is_alpha(program[cursor]) or
|
||||||
is_digit(input[cursor]) or
|
is_digit(program[cursor]) or
|
||||||
input[cursor] == '_'
|
program[cursor] == '_'
|
||||||
)) cursor += 1
|
)) cursor += 1
|
||||||
|
|
||||||
kind :: strmap.get(&keywords, input[start..cursor]) orelse .ident
|
kind :: strmap.get(&keywords, program[start..cursor]) orelse .ident
|
||||||
|
|
||||||
# don't intern keywords (already O(1) lookup via token kind)
|
# don't intern keywords (already O(1) lookup via token kind)
|
||||||
str_id :: if (kind == .ident)
|
str_id :: if (kind == .ident)
|
||||||
try strpool.intern(&strpool.strings, input[start..cursor])
|
try strpool.intern(&strpool.strings, program[start..cursor])
|
||||||
else
|
else
|
||||||
strpool.NO_ID
|
strpool.NO_ID
|
||||||
|
|
||||||
@@ -117,51 +118,35 @@ scan proc(state @mut State, input []u8) void ! (mem.AllocError | strpool.InternE
|
|||||||
# numeric literals
|
# numeric literals
|
||||||
if is_digit(char) {
|
if is_digit(char) {
|
||||||
start :: cursor
|
start :: cursor
|
||||||
has_decimal bool = false
|
result :: scan_number(start, program) catch |err| {
|
||||||
|
|
||||||
# scan integer part
|
|
||||||
while (cursor < input.len and is_digit(input[cursor])) cursor += 1
|
|
||||||
|
|
||||||
# check for decimal point
|
|
||||||
if cursor < input.len and input[cursor] == '.' {
|
|
||||||
has_decimal = true
|
|
||||||
cursor += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# assert that decimals follow the decimal point
|
|
||||||
if has_decimal and (cursor >= input.len or !is_digit(input[cursor])) {
|
|
||||||
token :: token_id(tokens.items.len)
|
token :: token_id(tokens.items.len)
|
||||||
try arraylist.append(diagnostics, Diagnostic{ token = token, code = .float_must_end_with_digit })
|
|
||||||
try add_token(tokens, Token{ kind = .invalid, start = start })
|
try add_token(tokens, Token{ kind = .invalid, start = start })
|
||||||
|
try arraylist.append(diagnostics, Diagnostic{ token = token, code = err })
|
||||||
|
cursor += 1
|
||||||
|
while (cursor < program.len and (is_digit(program[cursor]) or program[cursor] == '.')) cursor += 1
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
# scan decimal part
|
kind :: if (result.has_decimal) .float else .int
|
||||||
while (cursor < input.len and is_digit(input[cursor])) cursor += 1
|
|
||||||
|
|
||||||
kind :: if (has_decimal) .float else .int
|
|
||||||
try add_token(tokens, Token{ kind = kind, start = start })
|
try add_token(tokens, Token{ kind = kind, start = start })
|
||||||
|
cursor = result.end
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
# string literals
|
# string literals
|
||||||
if char == '"' {
|
if char == '"' {
|
||||||
start :: cursor
|
start :: cursor
|
||||||
cursor += 1
|
result :: scan_string(start, program) catch |err| {
|
||||||
|
token :: token_id(tokens.items.len)
|
||||||
while cursor < input.len and input[cursor] != '"' and input[cursor] != '\n' : cursor += 1 {
|
|
||||||
# ignore escaped characters
|
|
||||||
if (input[cursor] == '\\' and cursor + 1 < input.len) cursor += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if cursor < input.len and input[cursor] == '"' {
|
|
||||||
cursor += 1
|
|
||||||
try add_token(tokens, Token{ kind = .string, start = start })
|
|
||||||
} else {
|
|
||||||
try add_token(tokens, Token{ kind = .invalid, start = start })
|
try add_token(tokens, Token{ kind = .invalid, start = start })
|
||||||
|
try arraylist.append(diagnostics, Diagnostic{ token = token, code = err })
|
||||||
|
cursor += 1
|
||||||
|
while cursor < program.len and program[cursor] != '\n' : cursor += 1 {}
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try add_token(tokens, Token{ kind = .string, start = start })
|
||||||
|
cursor = result.end
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,7 +159,7 @@ scan proc(state @mut State, input []u8) void ! (mem.AllocError | strpool.InternE
|
|||||||
|
|
||||||
# immutable assignment or single colon
|
# immutable assignment or single colon
|
||||||
if char == ':' {
|
if char == ':' {
|
||||||
if cursor + 1 < input.len and input[cursor + 1] == ':' {
|
if cursor + 1 < program.len and program[cursor + 1] == ':' {
|
||||||
try add_token(tokens, Token{ kind = .double_colon, start = cursor })
|
try add_token(tokens, Token{ kind = .double_colon, start = cursor })
|
||||||
cursor += 2
|
cursor += 2
|
||||||
continue
|
continue
|
||||||
@@ -217,45 +202,55 @@ scan proc(state @mut State, input []u8) void ! (mem.AllocError | strpool.InternE
|
|||||||
try add_token(tokens, Token{ kind = .eof, start = cursor })
|
try add_token(tokens, Token{ kind = .eof, start = cursor })
|
||||||
}
|
}
|
||||||
|
|
||||||
#! returns the cursor position after scanning a number.
|
ScanNumResult :: struct {
|
||||||
scan_number proc(start usize, input []u8) usize {
|
end usize
|
||||||
debug.assert(is_digit(input[start]))
|
has_decimal bool
|
||||||
|
}
|
||||||
|
|
||||||
has_decimal bool = false
|
scan_number proc(start usize, program []u8) ScanNumResult ! ScanError {
|
||||||
cursor usize = start
|
cursor := start
|
||||||
|
has_decimal := false
|
||||||
|
|
||||||
# scan integer part
|
# scan integer part
|
||||||
while cursor < input.len and is_digit(input[cursor]) : cursor += 1 {}
|
while (cursor < program.len and is_digit(program[cursor])) cursor += 1
|
||||||
|
|
||||||
# check for decimal point
|
# check for decimal
|
||||||
if cursor < input.len and input[cursor] == '.' {
|
if cursor < program.len and program[cursor] == '.' {
|
||||||
has_decimal = true
|
has_decimal = true
|
||||||
cursor += 1
|
cursor += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# assert that decimals follow the decimal point
|
# assert non-terminating decimal
|
||||||
debug.assert(!(has_decimal and (cursor >= input.len or !is_digit(input[cursor]))))
|
if has_decimal and (cursor >= program.len or !is_digit(program[cursor])) {
|
||||||
|
return .float_must_end_with_digit
|
||||||
# scan decimal part
|
|
||||||
while cursor < input.len and is_digit(input[cursor]) : cursor += 1 {}
|
|
||||||
|
|
||||||
return cursor
|
|
||||||
}
|
|
||||||
|
|
||||||
#! returns the cursor position after scanning a string.
|
|
||||||
scan_string proc(start usize, input []u8) usize {
|
|
||||||
debug.assert(input[start] == '"')
|
|
||||||
|
|
||||||
cursor usize = start + 1
|
|
||||||
while cursor < input.len and input[cursor] != '"' and input[cursor] != '\n' : cursor += 1 {
|
|
||||||
# ignore escaped characters
|
|
||||||
if (input[cursor] == '\\' and cursor + 1 < input.len) cursor += 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
debug.assert(cursor < input.len and input[cursor] == '"')
|
# scan fractional part
|
||||||
cursor += 1
|
while (cursor < program.len and is_digit(program[cursor])) cursor += 1
|
||||||
|
|
||||||
return cursor
|
return ScanNumResult{
|
||||||
|
end = cursor,
|
||||||
|
has_decimal = has_decimal,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ScanStrResult :: struct { end usize }
|
||||||
|
|
||||||
|
scan_string proc(start usize, program []u8) ScanStrResult ! ScanError {
|
||||||
|
cursor := start + 1 # skip first `"`
|
||||||
|
|
||||||
|
# scan entire string
|
||||||
|
while cursor < program.len and program[cursor] != '"' and program[cursor] != '\n' : cursor += 1 {
|
||||||
|
# ignore escaped characters
|
||||||
|
if (program[cursor] == '\\' and cursor + 1 < program.len) cursor += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# assert string terminal
|
||||||
|
if (cursor >= program.len or program[cursor] != '"') return .unterminated_string
|
||||||
|
|
||||||
|
return ScanStrResult{
|
||||||
|
end = cursor + 1, # skip last `"`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hide is_whitespace proc(char u8) bool {
|
hide is_whitespace proc(char u8) bool {
|
||||||
|
|||||||
@@ -6,16 +6,19 @@ handles_keywords_identifiers_and_error_progress test {
|
|||||||
strpool.strings = strpool.init(mem.c_allocator)
|
strpool.strings = strpool.init(mem.c_allocator)
|
||||||
defer strpool.deinit(&strpool.strings)
|
defer strpool.deinit(&strpool.strings)
|
||||||
|
|
||||||
state State = init(mem.c_allocator)
|
state State := init(mem.c_allocator)
|
||||||
defer deinit(&state)
|
defer deinit(&state)
|
||||||
try scan(&state, "if name # comment\n@1.")
|
try scan(&state, "if name # comment\n@1. 2 3.5 \"hi\"")
|
||||||
|
|
||||||
try testing.expect_equal(5, state.tokens.items.len)
|
try testing.expect_equal(8, state.tokens.items.len)
|
||||||
try testing.expect_equal(TokenKind.if, state.tokens.items[0].kind)
|
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.ident, state.tokens.items[1].kind)
|
||||||
try testing.expect_equal(TokenKind.invalid, state.tokens.items[2].kind)
|
try testing.expect_equal(TokenKind.invalid, 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[3].kind)
|
||||||
try testing.expect_equal(TokenKind.eof, state.tokens.items[4].kind)
|
try testing.expect_equal(TokenKind.int, state.tokens.items[4].kind)
|
||||||
|
try testing.expect_equal(TokenKind.float, state.tokens.items[5].kind)
|
||||||
|
try testing.expect_equal(TokenKind.string, state.tokens.items[6].kind)
|
||||||
|
try testing.expect_equal(TokenKind.eof, state.tokens.items[7].kind)
|
||||||
try testing.expect_equal(2, state.diagnostics.items.len)
|
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)?)
|
try testing.expect_equal("name", strpool.get_str(&strpool.strings, state.tokens.items[1].str_id)?)
|
||||||
}
|
}
|
||||||
|
|||||||
+27
-9
@@ -10,13 +10,16 @@ test import "@std/strmap"
|
|||||||
import "@source/strpool"
|
import "@source/strpool"
|
||||||
import "@source/lexer"
|
import "@source/lexer"
|
||||||
import "@source/parser"
|
import "@source/parser"
|
||||||
|
import "@source/ast"
|
||||||
|
|
||||||
test import "@source/strpool"
|
test import "@source/strpool"
|
||||||
test import "@source/lexer"
|
test import "@source/lexer"
|
||||||
|
|
||||||
|
|
||||||
program ::
|
program ::
|
||||||
`# literals
|
`# literals
|
||||||
`x int :: 123
|
`x int :: 123.9
|
||||||
|
`y :: "hello"
|
||||||
|
|
||||||
main proc() void {
|
main proc() void {
|
||||||
strpool.strings = strpool.init(mem.c_allocator)
|
strpool.strings = strpool.init(mem.c_allocator)
|
||||||
@@ -24,7 +27,7 @@ main proc() void {
|
|||||||
|
|
||||||
debug.print("PROGRAM::[[\n{}\n]]\n\n", {program})
|
debug.print("PROGRAM::[[\n{}\n]]\n\n", {program})
|
||||||
|
|
||||||
scan_state lexer.State = lexer.init(mem.c_allocator)
|
scan_state := lexer.init(mem.c_allocator)
|
||||||
defer lexer.deinit(&scan_state)
|
defer lexer.deinit(&scan_state)
|
||||||
|
|
||||||
lexer.scan(&scan_state, program) catch |err| {
|
lexer.scan(&scan_state, program) catch |err| {
|
||||||
@@ -45,7 +48,7 @@ main proc() void {
|
|||||||
}
|
}
|
||||||
debug.print("]]\n\n", {})
|
debug.print("]]\n\n", {})
|
||||||
|
|
||||||
parse_state parser.State = parser.init(mem.c_allocator)
|
parse_state := parser.init(mem.c_allocator)
|
||||||
defer parser.deinit(&parse_state)
|
defer parser.deinit(&parse_state)
|
||||||
parser.parse(&parse_state, scan_state.tokens.items) catch |err| {
|
parser.parse(&parse_state, scan_state.tokens.items) catch |err| {
|
||||||
debug.print("failed to parse: {}\n", {err})
|
debug.print("failed to parse: {}\n", {err})
|
||||||
@@ -56,12 +59,17 @@ main proc() void {
|
|||||||
for parse_state.nodes.items |node, id| {
|
for parse_state.nodes.items |node, id| {
|
||||||
token :: scan_state.tokens.items[node.main_token]
|
token :: scan_state.tokens.items[node.main_token]
|
||||||
|
|
||||||
end :: if (token.kind == .int or token.kind == .float)
|
end :: if (token.kind == .int or token.kind == .float) lbl: {
|
||||||
lexer.scan_number(token.start, program)
|
res :: lexer.scan_number(token.start, program) catch {
|
||||||
else if (token.kind == .string)
|
yield :lbl token.start
|
||||||
lexer.scan_string(token.start, program)
|
}
|
||||||
else
|
yield :lbl res.end
|
||||||
token.start
|
} else if (token.kind == .string) lbl: {
|
||||||
|
res :: lexer.scan_string(token.start, program) catch {
|
||||||
|
yield :lbl token.start
|
||||||
|
}
|
||||||
|
yield :lbl res.end
|
||||||
|
} else token.start
|
||||||
|
|
||||||
debug.print("{} (id = {}): {}\n", {
|
debug.print("{} (id = {}): {}\n", {
|
||||||
node.kind,
|
node.kind,
|
||||||
@@ -71,5 +79,15 @@ main proc() void {
|
|||||||
#node.data1,
|
#node.data1,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
debug.print("]]\n\n", {})
|
||||||
|
|
||||||
|
debug.print("AST Render::[[\n", {})
|
||||||
|
literal :: ast.render_literal(
|
||||||
|
&parse_state.nodes.items[1],
|
||||||
|
scan_state.tokens.items,
|
||||||
|
program,
|
||||||
|
) catch "<invalid>"
|
||||||
|
|
||||||
|
debug.print("generated literal: {}\n", { literal })
|
||||||
debug.print("]]\n", {})
|
debug.print("]]\n", {})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import "@std/arraylist"
|
|||||||
import "@std/hashmap"
|
import "@std/hashmap"
|
||||||
|
|
||||||
# cross-cutting concern, hence global singleton (owned by main.hon)
|
# cross-cutting concern, hence global singleton (owned by main.hon)
|
||||||
strings StringPool = undefined
|
strings StringPool := undefined
|
||||||
|
|
||||||
InternError :: enum { out_of_space }
|
InternError :: enum { out_of_space }
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ import "@std/mem"
|
|||||||
import "@std/testing"
|
import "@std/testing"
|
||||||
|
|
||||||
handles_intern test {
|
handles_intern test {
|
||||||
pool StringPool = init(mem.c_allocator)
|
pool StringPool := init(mem.c_allocator)
|
||||||
defer deinit(&pool)
|
defer deinit(&pool)
|
||||||
|
|
||||||
input [5]mut u8 = ['h', 'e', 'l', 'l', 'o']
|
input [5]mut u8 := ['h', 'e', 'l', 'l', 'o']
|
||||||
id :: try intern(&pool, input[..])
|
id :: try intern(&pool, input[..])
|
||||||
duplicate :: try intern(&pool, "hello")
|
duplicate :: try intern(&pool, "hello")
|
||||||
input[0] = 'j'
|
input[0] = 'j'
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ init proc($T type, allocator mem.Allocator) ArrayList(T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
deinit proc($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 :: 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
|
||||||
@@ -28,9 +28,9 @@ hide reserve proc($T type, list @mut ArrayList(T), min_capacity usize) void ! me
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
new_capacity usize = 8
|
new_capacity := 8
|
||||||
if list.capacity >= 8 {
|
if list.capacity >= 8 {
|
||||||
half usize :: divtrunc!(list.capacity, 2)
|
half :: divtrunc!(list.capacity, 2)
|
||||||
if list.capacity > maxval!(usize) - half {
|
if list.capacity > maxval!(usize) - half {
|
||||||
new_capacity = min_capacity
|
new_capacity = min_capacity
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import "@std/mem"
|
|||||||
import "@std/testing"
|
import "@std/testing"
|
||||||
|
|
||||||
handles_append test {
|
handles_append test {
|
||||||
list ArrayList(i32) = init(mem.c_allocator)
|
list ArrayList(i32) := init(mem.c_allocator)
|
||||||
defer deinit(&list)
|
defer deinit(&list)
|
||||||
|
|
||||||
try append(&list, 42)
|
try append(&list, 42)
|
||||||
@@ -12,7 +12,7 @@ handles_append test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handles_clear test {
|
handles_clear test {
|
||||||
list ArrayList(i32) = init(mem.c_allocator)
|
list ArrayList(i32) := init(mem.c_allocator)
|
||||||
defer deinit(&list)
|
defer deinit(&list)
|
||||||
|
|
||||||
try append(&list, 42)
|
try append(&list, 42)
|
||||||
@@ -22,7 +22,7 @@ handles_clear test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handles_reserve test {
|
handles_reserve test {
|
||||||
list ArrayList(i32) = init(mem.c_allocator)
|
list ArrayList(i32) := init(mem.c_allocator)
|
||||||
defer deinit(&list)
|
defer deinit(&list)
|
||||||
|
|
||||||
try reserve(&list, 10)
|
try reserve(&list, 10)
|
||||||
|
|||||||
+4
-4
@@ -6,7 +6,7 @@ assert proc(ok bool) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
print proc($format []u8, $Args type, args Args) void {
|
print proc($format []u8, $Args type, args Args) void {
|
||||||
writer io.Writer :: io.Writer{
|
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) },
|
||||||
write = write,
|
write = write,
|
||||||
@@ -15,13 +15,13 @@ print proc($format []u8, $Args type, args Args) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hide write proc(_ ?@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 := bytes.len
|
||||||
maximum usize :: usize(maxval!(c_long))
|
maximum :: 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.write(handle.file_desc, bytes.ptr, c_ulong(request))
|
||||||
if count >= 0 {
|
if count >= 0 {
|
||||||
return usize(count)
|
return usize(count)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ 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) {
|
||||||
map EnumMap(E, V) = undefined
|
map EnumMap(E, V) := undefined
|
||||||
|
|
||||||
match typeinfo!(E) {
|
match typeinfo!(E) {
|
||||||
.enum |info|: inline for info.fields |field, i| {
|
.enum |info|: inline for info.fields |field, i| {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ TestEnum :: enum(u8) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handles_sparse_enum_get test {
|
handles_sparse_enum_get test {
|
||||||
names EnumMap(TestEnum, []u8) = init({
|
names EnumMap(TestEnum, []u8) := init({
|
||||||
ident = "identifier",
|
ident = "identifier",
|
||||||
int = "integer",
|
int = "integer",
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ get proc(
|
|||||||
if (map.count == 0) return null
|
if (map.count == 0) return null
|
||||||
|
|
||||||
hash :: normalize(hash_key(key))
|
hash :: normalize(hash_key(key))
|
||||||
idx usize = hash & (map.entries.len - 1)
|
idx := hash & (map.entries.len - 1)
|
||||||
|
|
||||||
while true {
|
while true {
|
||||||
entry :: map.entries[idx]
|
entry :: map.entries[idx]
|
||||||
@@ -96,7 +96,7 @@ put proc(
|
|||||||
if (entry.hash == 0) continue
|
if (entry.hash == 0) continue
|
||||||
|
|
||||||
# find an empty slot
|
# find an empty slot
|
||||||
idx usize = entry.hash & (new_entries.len - 1)
|
idx := entry.hash & (new_entries.len - 1)
|
||||||
while new_entries[idx].hash != 0 {
|
while new_entries[idx].hash != 0 {
|
||||||
idx = (idx + 1) & (new_entries.len - 1)
|
idx = (idx + 1) & (new_entries.len - 1)
|
||||||
}
|
}
|
||||||
@@ -110,7 +110,7 @@ put proc(
|
|||||||
|
|
||||||
# put new entry
|
# put new entry
|
||||||
hash :: normalize(hash_key(key))
|
hash :: normalize(hash_key(key))
|
||||||
idx usize = hash & (map.entries.len - 1)
|
idx := hash & (map.entries.len - 1)
|
||||||
|
|
||||||
while true {
|
while true {
|
||||||
entry :: map.entries[idx]
|
entry :: map.entries[idx]
|
||||||
@@ -142,8 +142,8 @@ hide normalize proc(hash usize) usize {
|
|||||||
#! FNV-1a hash implementation.
|
#! FNV-1a hash implementation.
|
||||||
#! note: vulnerable to collision attacks.
|
#! note: vulnerable to collision attacks.
|
||||||
hide str_hash proc(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
|
||||||
|
|
||||||
for key |byte| {
|
for key |byte| {
|
||||||
product u64 :: u64(hash xor u32(byte)) * prime
|
product u64 :: u64(hash xor u32(byte)) * prime
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import "@std/mem"
|
|||||||
import "@std/testing"
|
import "@std/testing"
|
||||||
|
|
||||||
handles_put_and_get test {
|
handles_put_and_get test {
|
||||||
map StringHashMap(u32) = init(mem.c_allocator)
|
map StringHashMap(u32) := init(mem.c_allocator)
|
||||||
defer deinit(&map)
|
defer deinit(&map)
|
||||||
|
|
||||||
try put(&map, "key", 42)
|
try put(&map, "key", 42)
|
||||||
|
|||||||
+3
-3
@@ -45,7 +45,7 @@ writer proc(file File) Writer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hide system_read proc(_ ?@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 := buffer.len
|
||||||
maximum usize :: usize(maxval!(c_long))
|
maximum usize :: usize(maxval!(c_long))
|
||||||
if (request > maximum) request = maximum
|
if (request > maximum) request = maximum
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ hide system_read proc(_ ?@mut anyopaque, handle Handle, buffer []mut u8) usize !
|
|||||||
}
|
}
|
||||||
|
|
||||||
hide system_write proc(_ ?@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 := bytes.len
|
||||||
maximum usize :: usize(maxval!(c_long))
|
maximum usize :: usize(maxval!(c_long))
|
||||||
if (request > maximum) request = maximum
|
if (request > maximum) request = maximum
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ hide system_write proc(_ ?@mut anyopaque, handle Handle, bytes []u8) usize ! Wri
|
|||||||
}
|
}
|
||||||
|
|
||||||
hide system_open proc(_ ?@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.O_RDONLY
|
||||||
match mode {
|
match mode {
|
||||||
.read_only: flags = c.O_RDONLY
|
.read_only: flags = c.O_RDONLY
|
||||||
.write_only: flags = c.O_WRONLY
|
.write_only: flags = c.O_WRONLY
|
||||||
|
|||||||
+19
-19
@@ -69,7 +69,7 @@ write proc(output Writer, bytes []u8) usize ! WriteError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
write_all proc(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 :: try write(output, bytes[offset..])
|
count usize :: try write(output, bytes[offset..])
|
||||||
if (count == 0) return .no_progress
|
if (count == 0) return .no_progress
|
||||||
@@ -120,13 +120,13 @@ print proc(output Writer, $format []u8, $Args type, args Args) void ! WriteError
|
|||||||
}
|
}
|
||||||
|
|
||||||
hide write_integer_signed proc(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 := buffer.len
|
||||||
current i64 = value
|
current := value
|
||||||
|
|
||||||
while true {
|
while true {
|
||||||
digit_value i64 :: rem!(current, i64(base))
|
digit_value i64 :: rem!(current, i64(base))
|
||||||
digit u8 = if (digit_value < 0)
|
digit := if (digit_value < 0)
|
||||||
u8(-digit_value)
|
u8(-digit_value)
|
||||||
else
|
else
|
||||||
u8(digit_value)
|
u8(digit_value)
|
||||||
@@ -151,9 +151,9 @@ hide write_integer_signed proc(output Writer, value i64, base u64, uppercase boo
|
|||||||
}
|
}
|
||||||
|
|
||||||
hide write_integer_unsigned proc(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 := buffer.len
|
||||||
current u64 = value
|
current := value
|
||||||
|
|
||||||
while true {
|
while true {
|
||||||
digit u8 :: u8(rem!(current, base))
|
digit u8 :: u8(rem!(current, base))
|
||||||
@@ -195,12 +195,12 @@ hide FormatToken :: struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hide parse_format proc($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 := 0
|
||||||
match typeinfo!(Args) {
|
match typeinfo!(Args) {
|
||||||
.record |r|: {
|
.record |r|: {
|
||||||
if (!r.is_tuple) compile_error!("io.print arguments must be a tuple")
|
if (!r.is_tuple) compile_error!("io.print arguments must be a tuple")
|
||||||
@@ -209,10 +209,10 @@ hide parse_format proc($N usize, $format []u8, $Args type) [N]mut FormatToken {
|
|||||||
else: compile_error!("io.print arguments must be a tuple")
|
else: compile_error!("io.print arguments must be a tuple")
|
||||||
}
|
}
|
||||||
|
|
||||||
token_count usize = 0
|
token_count := 0
|
||||||
argument_count usize = 0
|
argument_count := 0
|
||||||
literal_start usize = 0
|
literal_start := 0
|
||||||
cursor usize = 0
|
cursor usize := 0
|
||||||
while cursor < format.len {
|
while cursor < format.len {
|
||||||
byte :: format[cursor]
|
byte :: format[cursor]
|
||||||
if byte == '{' {
|
if byte == '{' {
|
||||||
@@ -242,8 +242,8 @@ hide parse_format proc($N usize, $format []u8, $Args type) [N]mut FormatToken {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
kind FormatTokenKind = .default
|
kind FormatTokenKind := .default
|
||||||
width usize = 2
|
width := 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")
|
||||||
@@ -362,8 +362,8 @@ hide write_integer proc(output Writer, $T type, value T, base u64, uppercase boo
|
|||||||
hide write_float proc(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 := 0
|
||||||
if sizeof!(T) == 4 {
|
if sizeof!(T) == 4 {
|
||||||
if (scientific) count = c.snprintf(ptrcast!(c_char, (&buffer).ptr), c_ulong(buffer.len), "%.8e", value)
|
if (scientific) 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)
|
||||||
@@ -403,7 +403,7 @@ hide write_character proc(output Writer, $T type, value T) void ! WriteError {
|
|||||||
if minval!(T) < 0 or maxval!(T) > 255 {
|
if minval!(T) < 0 or maxval!(T) > 255 {
|
||||||
compile_error!("io.print '{c}' requires an unsigned integer that fits in u8")
|
compile_error!("io.print '{c}' requires an unsigned integer that fits in u8")
|
||||||
}
|
}
|
||||||
buffer [1]u8 = [u8(value)]
|
buffer [1]u8 := [u8(value)]
|
||||||
try write_all(output, buffer[..])
|
try write_all(output, buffer[..])
|
||||||
}
|
}
|
||||||
.distinct |backing|: if scalar_or_distinct_type(backing) {
|
.distinct |backing|: if scalar_or_distinct_type(backing) {
|
||||||
|
|||||||
+11
-11
@@ -46,7 +46,7 @@ alloc proc($T type, allocator Allocator, count usize) []mut T ! AllocError {
|
|||||||
return .out_of_memory
|
return .out_of_memory
|
||||||
}
|
}
|
||||||
|
|
||||||
memory ?*mut u8 = raw_alloc(allocator, count * element_size, alignof!(T))
|
memory := raw_alloc(allocator, count * element_size, alignof!(T))
|
||||||
if memory |bytes| {
|
if memory |bytes| {
|
||||||
pointer *mut T :: ptrcast!(T, bytes)
|
pointer *mut T :: ptrcast!(T, bytes)
|
||||||
return pointer[..count]
|
return pointer[..count]
|
||||||
@@ -72,13 +72,13 @@ realloc proc($T type, allocator Allocator, memory []mut T, new_count usize) []mu
|
|||||||
if (element_size == 0) return empty_slice(T, new_count)
|
if (element_size == 0) 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 := 0
|
||||||
if memory.len != 0 {
|
if memory.len != 0 {
|
||||||
old_memory = ptrcast!(u8, memory.ptr)
|
old_memory = ptrcast!(u8, memory.ptr)
|
||||||
old_size = memory.len * element_size
|
old_size = memory.len * element_size
|
||||||
}
|
}
|
||||||
resized ?*mut u8 = raw_realloc(
|
resized := raw_realloc(
|
||||||
allocator,
|
allocator,
|
||||||
old_memory,
|
old_memory,
|
||||||
old_size,
|
old_size,
|
||||||
@@ -116,15 +116,15 @@ 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 # note: aarch64-macos libc malloc alignment assumption.
|
hide malloc_alignment usize :: 16 # note: aarch64-macos libc malloc alignment assumption.
|
||||||
|
|
||||||
hide power_of_two proc(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 := value
|
||||||
while current > 1 {
|
while current > 1 {
|
||||||
half usize = divtrunc!(current, 2)
|
half := divtrunc!(current, 2)
|
||||||
if (half * 2 != current) return false
|
if (half * 2 != current) return false
|
||||||
current = half
|
current = half
|
||||||
}
|
}
|
||||||
@@ -135,8 +135,8 @@ 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.posix_memalign((&memory).ptr, c_ulong(alignment), c_ulong(size))
|
||||||
if (status != 0) return null
|
if (status != 0) return null
|
||||||
|
|
||||||
return ptrcast!(u8, memory[0])
|
return ptrcast!(u8, memory[0])
|
||||||
@@ -161,9 +161,9 @@ hide c_realloc proc(
|
|||||||
return ptrcast!(u8, c.realloc(old_memory, c_ulong(new_size)))
|
return ptrcast!(u8, c.realloc(old_memory, c_ulong(new_size)))
|
||||||
}
|
}
|
||||||
|
|
||||||
new_memory ?*mut u8 = c_alloc(null, new_size, alignment)
|
new_memory := c_alloc(null, new_size, alignment)
|
||||||
if new_memory |new_bytes| {
|
if new_memory |new_bytes| {
|
||||||
copy_size usize = old_size
|
copy_size := old_size
|
||||||
if (new_size < copy_size) copy_size = new_size
|
if (new_size < copy_size) copy_size = new_size
|
||||||
memcopy!(new_bytes[..copy_size], old_memory[..copy_size])
|
memcopy!(new_bytes[..copy_size], old_memory[..copy_size])
|
||||||
c.free(old_memory)
|
c.free(old_memory)
|
||||||
|
|||||||
+3
-3
@@ -46,9 +46,9 @@ TypeInfo :: union(enum) {
|
|||||||
EnumFieldStruct proc($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
|
||||||
field_types [info.fields.len]mut type = undefined
|
field_types [info.fields.len]mut type := undefined
|
||||||
defaults [info.fields.len]mut ?Field = undefined
|
defaults [info.fields.len]mut ?Field := undefined
|
||||||
inline for info.fields |field, index| {
|
inline for info.fields |field, index| {
|
||||||
names[index] = field.name
|
names[index] = field.name
|
||||||
field_types[index] = Field
|
field_types[index] = Field
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ distinct_reflection_exposes_immediate_backing test {
|
|||||||
|
|
||||||
|
|
||||||
enum_field_struct_defaults test {
|
enum_field_struct_defaults test {
|
||||||
names TestNames = {
|
names TestNames := {
|
||||||
ident = "identifier",
|
ident = "identifier",
|
||||||
int = "integer",
|
int = "integer",
|
||||||
}
|
}
|
||||||
@@ -60,7 +60,7 @@ enum_field_struct_defaults test {
|
|||||||
try testing.expect(false)
|
try testing.expect(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
empty TestNames = {}
|
empty TestNames := {}
|
||||||
if field!(empty, "ident") |_| {
|
if field!(empty, "ident") |_| {
|
||||||
try testing.expect(false)
|
try testing.expect(false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ init proc($V type, $N usize, $entries [N]Pair(V)) StringMap(V) {
|
|||||||
compile_error!("static string map has too many entries")
|
compile_error!("static string map has too many entries")
|
||||||
}
|
}
|
||||||
|
|
||||||
keys [N]mut []u8 = undefined
|
keys [N]mut []u8 := undefined
|
||||||
values [N]mut V = undefined
|
values [N]mut V := undefined
|
||||||
|
|
||||||
# assert no duplicate keys
|
# assert no duplicate keys
|
||||||
for entries |entry, i| {
|
for entries |entry, i| {
|
||||||
@@ -38,7 +38,7 @@ init proc($V type, $N usize, $entries [N]Pair(V)) StringMap(V) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if N == 0 {
|
if N == 0 {
|
||||||
len_indexes [0]u32 = undefined
|
len_indexes [0]u32 := undefined
|
||||||
return StringMap(V){
|
return StringMap(V){
|
||||||
keys = keys[..],
|
keys = keys[..],
|
||||||
values = values[..],
|
values = values[..],
|
||||||
@@ -52,7 +52,7 @@ init proc($V type, $N usize, $entries [N]Pair(V)) StringMap(V) {
|
|||||||
for 1..N |i| {
|
for 1..N |i| {
|
||||||
key :: keys[i]
|
key :: keys[i]
|
||||||
value :: values[i]
|
value :: values[i]
|
||||||
j usize = i
|
j := i
|
||||||
while j > 0 and keys[j - 1].len > key.len : j -= 1 {
|
while j > 0 and keys[j - 1].len > key.len : j -= 1 {
|
||||||
keys[j] = keys[j - 1]
|
keys[j] = keys[j - 1]
|
||||||
values[j] = values[j - 1]
|
values[j] = values[j - 1]
|
||||||
@@ -63,8 +63,8 @@ init proc($V type, $N usize, $entries [N]Pair(V)) StringMap(V) {
|
|||||||
|
|
||||||
min_len u32 :: u32(keys[0].len)
|
min_len u32 :: u32(keys[0].len)
|
||||||
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| {
|
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)
|
||||||
@@ -82,10 +82,10 @@ init proc($V type, $N usize, $entries [N]Pair(V)) StringMap(V) {
|
|||||||
get proc($V type, map @StringMap(V), key []u8) ?V {
|
get proc($V type, map @StringMap(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(key.len)
|
||||||
if (length < map.min_len or length > map.max_len) return null
|
if (length < map.min_len or length > map.max_len) return null
|
||||||
|
|
||||||
idx usize = usize(map.len_indexes[usize(length)])
|
idx := usize(map.len_indexes[usize(length)])
|
||||||
while idx < map.keys.len : idx += 1 {
|
while idx < map.keys.len : idx += 1 {
|
||||||
candidate :: map.keys[idx]
|
candidate :: map.keys[idx]
|
||||||
if (candidate.len != key.len) return null # key not found
|
if (candidate.len != key.len) return null # key not found
|
||||||
|
|||||||
+38
-38
@@ -1,85 +1,85 @@
|
|||||||
import "@std/debug"
|
import "@std/debug"
|
||||||
import "@std/mem"
|
import "@std/mem"
|
||||||
|
|
||||||
Error :: enum {
|
Error :: enum {
|
||||||
expectation_failed
|
expectation_failed
|
||||||
}
|
}
|
||||||
|
|
||||||
SourceLocation :: struct {
|
SourceLocation :: struct {
|
||||||
file []u8
|
file []u8
|
||||||
line usize
|
line usize
|
||||||
column usize
|
column usize
|
||||||
}
|
}
|
||||||
|
|
||||||
expect proc(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,
|
||||||
location.line,
|
location.line,
|
||||||
location.column,
|
location.column,
|
||||||
})
|
})
|
||||||
return .expectation_failed
|
return .expectation_failed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
expect_equal proc($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| {
|
||||||
if actual |actual_value| {
|
if actual |actual_value| {
|
||||||
try expect_equal(expected_value, actual_value, location)
|
try expect_equal(expected_value, actual_value, location)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
debug.print("{s}:{d}:{d}: expected an optional value, found null\n", {
|
debug.print("{s}:{d}:{d}: expected an optional value, found null\n", {
|
||||||
location.file,
|
location.file,
|
||||||
location.line,
|
location.line,
|
||||||
location.column,
|
location.column,
|
||||||
})
|
})
|
||||||
return .expectation_failed
|
return .expectation_failed
|
||||||
}
|
}
|
||||||
if actual |_| {
|
if actual |_| {
|
||||||
debug.print("{s}:{d}:{d}: expected null, found an optional value\n", {
|
debug.print("{s}:{d}:{d}: expected null, found an optional value\n", {
|
||||||
location.file,
|
location.file,
|
||||||
location.line,
|
location.line,
|
||||||
location.column,
|
location.column,
|
||||||
})
|
})
|
||||||
return .expectation_failed
|
return .expectation_failed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.slice: if !mem.eql(expected, actual) {
|
.slice: if !mem.eql(expected, actual) {
|
||||||
debug.print("{s}:{d}:{d}: expected and actual slices differ\n", {
|
debug.print("{s}:{d}:{d}: expected and actual slices differ\n", {
|
||||||
location.file,
|
location.file,
|
||||||
location.line,
|
location.line,
|
||||||
location.column,
|
location.column,
|
||||||
})
|
})
|
||||||
return .expectation_failed
|
return .expectation_failed
|
||||||
}
|
}
|
||||||
else: if expected != actual {
|
else: if expected != actual {
|
||||||
debug.print("{s}:{d}:{d}: expected {}, found {}\n", {
|
debug.print("{s}:{d}:{d}: expected {}, found {}\n", {
|
||||||
location.file,
|
location.file,
|
||||||
location.line,
|
location.line,
|
||||||
location.column,
|
location.column,
|
||||||
expected,
|
expected,
|
||||||
actual,
|
actual,
|
||||||
})
|
})
|
||||||
return .expectation_failed
|
return .expectation_failed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
expect_type proc($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 proc(name []u8, callback *proc() 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
|
||||||
}
|
}
|
||||||
debug.print("{s}...[ok]\n", {name,})
|
debug.print("{s}...[ok]\n", {name,})
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
summary proc(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})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user