Compare commits

..

2 Commits

19 changed files with 816 additions and 405 deletions
+4
View File
@@ -65,6 +65,10 @@ range bounds that are compound or unary expressions require their own parenthese
name resolution (`@source/sema`) runs after parsing. it predeclares package names and each block's direct locals, allowing forward references and mutually recursive functions. parameters and top-level function-body locals share a scope; each conditional arm and loop body introduces a child scope whose names do not escape it. signatures resolve names in the enclosing package scope. shadowing is forbidden, including reuse of builtin type names (`int`, `float`, `i64`, `f64`, `usize`, `bool`, `string`, `void`); sibling scopes may independently reuse names. name resolution (`@source/sema`) runs after parsing. it predeclares package names and each block's direct locals, allowing forward references and mutually recursive functions. parameters and top-level function-body locals share a scope; each conditional arm and loop body introduces a child scope whose names do not escape it. signatures resolve names in the enclosing package scope. shadowing is forbidden, including reuse of builtin type names (`int`, `float`, `i64`, `f64`, `usize`, `bool`, `string`, `void`); sibling scopes may independently reuse names.
identifier interning is compilation-owned: initialize a `strpool.StringPool`, pass it to `lexer.scan` and `sema.resolve` before the diagnostic store, and release it after lowering. scanning interns identifier spellings once per pool; parsing preserves their IDs in compact AST storage, exposed through `ast.name_id`, while retaining source spans for diagnostics. name resolution keys scopes by those IDs and interns builtin names into the same pool, so identifier hashing and equality do not scan spelling bytes. IDs are meaningful only within their originating pool; independent compilations may assign the same numeric ID to different names. interning failures propagate as infrastructure errors rather than producing an invalid identifier ID. IR retains no pool dependency.
resolver scopes use `@std/hashmap.AutoHashMap(K, V)`, which shares `init`, `put`, `get` and `deinit` with custom `HashMap` and `StringHashMap`. automatic keys support integers, booleans, enums, distinct types, fixed arrays and records recursively; record padding is ignored. slices, floating-point values, pointers, optionals and unions require explicit hashing and equality through `HashMap` (`StringHashMap` supplies content-based byte-string operations). keys and values are stored by value; referenced resources remain caller-owned. the builtin declaration count is inferred, and builtin ID storage derives its fixed stack-array length from that declaration array rather than repeating a count or initializer.
`sema.resolve` returns an owned binding table indexed by AST `NodeId`, without modifying the AST. `sema.binding` identifies a declaration, builtin type, or invalid binding carrying its diagnostic ID; nodes with no binding return `.none`. release the result with `sema.deinit`. unknown names and duplicate declarations add source-spanned diagnostics; invalid syntax retains its existing diagnostic identity. same-scope duplicates retain the first declaration for recovery, while forbidden inner declarations invalidate that name's uses rather than silently binding them to an outer declaration. `sema.resolve` returns an owned binding table indexed by AST `NodeId`, without modifying the AST. `sema.binding` identifies a declaration, builtin type, or invalid binding carrying its diagnostic ID; nodes with no binding return `.none`. release the result with `sema.deinit`. unknown names and duplicate declarations add source-spanned diagnostics; invalid syntax retains its existing diagnostic identity. same-scope duplicates retain the first declaration for recovery, while forbidden inner declarations invalidate that name's uses rather than silently binding them to an outer declaration.
type checking (`@source/sema/check`) runs after name resolution. `check.check` borrows the AST and binding table and returns owned per-node types and interned array descriptors; query with `check.type_of` and `check.array_type`, and release with `check.deinit`. declarations infer their initializer types, including forward references and function aliases; function types identify the declaration supplying the callable signature. annotations support builtin types and fixed arrays; type names are not ordinary values. `void` is valid as a function result, not as a parameter or stored value. type checking (`@source/sema/check`) runs after name resolution. `check.check` borrows the AST and binding table and returns owned per-node types and interned array descriptors; query with `check.type_of` and `check.array_type`, and release with `check.deinit`. declarations infer their initializer types, including forward references and function aliases; function types identify the declaration supplying the callable signature. annotations support builtin types and fixed arrays; type names are not ordinary values. `void` is valid as a function result, not as a parameter or stored value.
+1
View File
@@ -0,0 +1 @@
- [ ] stdlib: add an auto-hashmap implementation (inspired by zig)
+37 -6
View File
@@ -16,6 +16,7 @@ import "@std/debug"
import "@std/mem" import "@std/mem"
import "@std/arraylist" import "@std/arraylist"
import "@source/diagnostics" import "@source/diagnostics"
import "@source/strpool"
Span :: alias diagnostics.Span Span :: alias diagnostics.Span
DiagnosticId :: alias diagnostics.DiagnosticId DiagnosticId :: alias diagnostics.DiagnosticId
@@ -114,6 +115,7 @@ NodeKind :: enum {
NodeData :: union { NodeData :: union {
node_id NodeId node_id NodeId
extra u32 extra u32
name_id strpool.StringId
diagnostic DiagnosticId diagnostic DiagnosticId
mutability Mutability mutability Mutability
unary_operator UnaryOperator unary_operator UnaryOperator
@@ -295,8 +297,15 @@ add_literal proc(builder @mut Builder, kind LiteralKind, span Span) NodeId ! mem
return try add_node(builder, Node{ kind = node_kind }, span) return try add_node(builder, Node{ kind = node_kind }, span)
} }
add_identifier proc(builder @mut Builder, span Span) NodeId ! mem.AllocError { add_identifier proc(
return try add_node(builder, Node{ kind = .expr_identifier }, span) builder @mut Builder,
span Span,
interned_name strpool.StringId,
) NodeId ! mem.AllocError {
return try add_node(builder, Node{
kind = .expr_identifier,
main = NodeData{ name_id = interned_name },
}, span)
} }
add_unary proc( add_unary proc(
@@ -475,6 +484,7 @@ add_statement_decl proc(
builder @mut Builder, builder @mut Builder,
span Span, span Span,
name Span, name Span,
interned_name strpool.StringId,
type ?NodeId, type ?NodeId,
mutability Mutability, mutability Mutability,
initializer NodeId, initializer NodeId,
@@ -488,16 +498,23 @@ add_statement_decl proc(
kind = .stmt_decl, kind = .stmt_decl,
main = NodeData{ extra = extra }, main = NodeData{ extra = extra },
data0 = NodeData{ mutability = mutability }, data0 = NodeData{ mutability = mutability },
data1 = NodeData{ name_id = interned_name },
}, span) }, span)
} }
add_param proc(builder @mut Builder, span, name Span, type NodeId) NodeId ! mem.AllocError { add_param proc(
builder @mut Builder,
span, name Span,
interned_name strpool.StringId,
type NodeId,
) NodeId ! mem.AllocError {
extra :: try add_extra(builder, name.start) extra :: try add_extra(builder, name.start)
_ = try add_extra(builder, name.end) _ = try add_extra(builder, name.end)
return try add_node(builder, Node{ return try add_node(builder, Node{
kind = .param_decl, kind = .param_decl,
main = NodeData{ extra = extra }, main = NodeData{ extra = extra },
data0 = NodeData{ node_id = type }, data0 = NodeData{ node_id = type },
data1 = NodeData{ name_id = interned_name },
}, span) }, span)
} }
@@ -505,6 +522,7 @@ add_function_decl proc(
builder @mut Builder, builder @mut Builder,
span Span, span Span,
name Span, name Span,
interned_name strpool.StringId,
return_type NodeId, return_type NodeId,
params []NodeId, params []NodeId,
body NodeId, body NodeId,
@@ -513,6 +531,7 @@ add_function_decl proc(
_ = try add_extra(builder, name.start) _ = try add_extra(builder, name.start)
_ = try add_extra(builder, name.end) _ = try add_extra(builder, name.end)
_ = try add_extra(builder, u32(return_type)) _ = try add_extra(builder, u32(return_type))
_ = try add_extra(builder, u32(interned_name))
for (params) |parameter| _ = try add_extra(builder, u32(parameter)) for (params) |parameter| _ = try add_extra(builder, u32(parameter))
return try add_node(builder, Node{ return try add_node(builder, Node{
kind = .func_decl, kind = .func_decl,
@@ -579,6 +598,18 @@ node_count proc(tree @Tree) usize {
return tree.storage.nodes.items.len return tree.storage.nodes.items.len
} }
#! returns the identifier's identity in the compilation-owned string pool.
#! accepts identifiers, declarations, and parameters; source spans remain available in typed views.
name_id proc(tree @Tree, id NodeId) strpool.StringId {
node :: get_node(&tree.storage, id)
return match node.kind {
.expr_identifier: node.main.name_id
.stmt_decl, .param_decl: node.data1.name_id
.func_decl: strpool.StringId(tree.storage.extra.items[usize(node.main.extra) + 3])
else: unreachable
}
}
package_decl_count proc(tree @Tree) usize { package_decl_count proc(tree @Tree) usize {
root :: get_node(&tree.storage, tree.root) root :: get_node(&tree.storage, tree.root)
debug.assert(root.kind == .package) debug.assert(root.kind == .package)
@@ -707,14 +738,14 @@ function_decl proc(tree @Tree, id NodeId) FunctionDeclView {
function_param_count proc(tree @Tree, id NodeId) usize { function_param_count proc(tree @Tree, id NodeId) usize {
node :: get_node(&tree.storage, id) node :: get_node(&tree.storage, id)
debug.assert(node.kind == .func_decl) debug.assert(node.kind == .func_decl)
return usize(node.data1.extra - node.main.extra - 3) return usize(node.data1.extra - node.main.extra - 4)
} }
function_param proc(tree @Tree, id NodeId, index usize) NodeId { function_param proc(tree @Tree, id NodeId, index usize) NodeId {
node :: get_node(&tree.storage, id) node :: get_node(&tree.storage, id)
debug.assert(node.kind == .func_decl) debug.assert(node.kind == .func_decl)
debug.assert(index < usize(node.data1.extra - node.main.extra - 3)) debug.assert(index < usize(node.data1.extra - node.main.extra - 4))
return NodeId(tree.storage.extra.items[usize(node.main.extra) + 3 + index]) return NodeId(tree.storage.extra.items[usize(node.main.extra) + 4 + index])
} }
param proc(tree @Tree, id NodeId) ParamView { param proc(tree @Tree, id NodeId) ParamView {
+11 -1
View File
@@ -1,7 +1,11 @@
import "@std/mem" import "@std/mem"
import "@std/testing" import "@std/testing"
import "@source/strpool"
exposes_typed_declaration_views test { exposes_typed_declaration_views test {
strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strings)
value_id :: try strpool.intern(&strings, "value")
builder := init_builder(mem.c_allocator, "value :: 42") builder := init_builder(mem.c_allocator, "value :: 42")
defer deinit_builder(&builder) defer deinit_builder(&builder)
@@ -11,6 +15,7 @@ exposes_typed_declaration_views test {
&builder, &builder,
Span{ start = 0, end = 11 }, Span{ start = 0, end = 11 },
value_name, value_name,
value_id,
null, null,
.constant, .constant,
value, value,
@@ -23,6 +28,7 @@ exposes_typed_declaration_views test {
try testing.expect_equal(1, package_decl_count(&tree)) try testing.expect_equal(1, package_decl_count(&tree))
decl :: package_decl(&tree, 0) decl :: package_decl(&tree, 0)
try testing.expect_equal(DeclKind.statement, decl_kind(&tree, decl)) try testing.expect_equal(DeclKind.statement, decl_kind(&tree, decl))
try testing.expect_equal(value_id, name_id(&tree, decl))
statement :: statement_decl(&tree, decl) statement :: statement_decl(&tree, decl)
try testing.expect_equal(value_name.start, statement.name.start) try testing.expect_equal(value_name.start, statement.name.start)
@@ -34,11 +40,14 @@ exposes_typed_declaration_views test {
} }
exposes_array_type_literal_and_index_views test { exposes_array_type_literal_and_index_views test {
strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strings)
element_id :: try strpool.intern(&strings, "int")
builder := init_builder(mem.c_allocator, "") builder := init_builder(mem.c_allocator, "")
defer deinit_builder(&builder) defer deinit_builder(&builder)
span :: Span{ start = 0, end = 0 } span :: Span{ start = 0, end = 0 }
count :: try add_literal(&builder, .int, span) count :: try add_literal(&builder, .int, span)
element :: try add_identifier(&builder, span) element :: try add_identifier(&builder, span, element_id)
row :: try add_array_type(&builder, span, count, element, false) row :: try add_array_type(&builder, span, count, element, false)
matrix :: try add_array_type(&builder, span, null, row, true) matrix :: try add_array_type(&builder, span, null, row, true)
elements [2]NodeId :: [count, element] elements [2]NodeId :: [count, element]
@@ -55,6 +64,7 @@ exposes_array_type_literal_and_index_views test {
try testing.expect_equal(count, array_type(&tree, row).count?) try testing.expect_equal(count, array_type(&tree, row).count?)
try testing.expect(!array_type(&tree, row).mutable) try testing.expect(!array_type(&tree, row).mutable)
try testing.expect_equal(element, array_type(&tree, row).element) try testing.expect_equal(element, array_type(&tree, row).element)
try testing.expect_equal(element_id, name_id(&tree, element))
try testing.expect_equal(ExprKind.array_literal, expr_kind(&tree, literal)) try testing.expect_equal(ExprKind.array_literal, expr_kind(&tree, literal))
try testing.expect_equal(2, array_element_count(&tree, literal)) try testing.expect_equal(2, array_element_count(&tree, literal))
try testing.expect_equal(count, array_element(&tree, literal, 0)) try testing.expect_equal(count, array_element(&tree, literal, 0))
+3 -3
View File
@@ -7,8 +7,8 @@ import "@source/parser"
import "@source/ast" import "@source/ast"
renders_array_annotations_literals_and_indexed_assignments test { renders_array_annotations_literals_and_indexed_assignments test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -19,7 +19,7 @@ renders_array_annotations_literals_and_indexed_assignments test {
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
renderer := init(mem.c_allocator) renderer := init(mem.c_allocator)
+12 -8
View File
@@ -36,7 +36,7 @@ keywords strmap.StaticStringMap(TokenKind) :: strmap.init([
]) ])
#! owns token storage and retains its allocator; release with deinit. #! owns token storage and retains its allocator; release with deinit.
#! identifier IDs refer to strings in the separately owned strpool.strings. #! identifier IDs belong to the compilation's separately owned string pool.
State :: struct { State :: struct {
tokens arraylist.ArrayList(Token) tokens arraylist.ArrayList(Token)
} }
@@ -46,17 +46,17 @@ init proc(allocator mem.Allocator) State {
return State{ tokens = arraylist.init(allocator) } return State{ tokens = arraylist.init(allocator) }
} }
#! releases token storage with the retained allocator, without releasing strpool.strings. #! releases token storage with the retained allocator, without releasing identifier strings.
deinit proc(state @mut State) void { deinit proc(state @mut State) void {
arraylist.deinit(&state.tokens) arraylist.deinit(&state.tokens)
} }
#! borrows `program` and `diagnostic_store` during scanning; tokens remain state-owned. #! borrows `program`, `pool`, and `diagnostic_store`; tokens remain state-owned.
#! token allocation uses the retained allocator; identifiers use strpool.strings's allocator. #! identifier storage uses the pool's allocator; keep the pool alive while using their IDs.
#! initialize strpool.strings before scanning and keep it alive while using identifier IDs.
scan proc( scan proc(
state @mut State, state @mut State,
program []u8, program []u8,
pool @mut strpool.StringPool,
diagnostic_store @mut diagnostics.Store, diagnostic_store @mut diagnostics.Store,
) void ! (mem.AllocError | strpool.InternError | diagnostics.CapacityError | SourceError) { ) void ! (mem.AllocError | strpool.InternError | diagnostics.CapacityError | SourceError) {
if (program.len > usize(maxval!(u32))) return .source_too_large if (program.len > usize(maxval!(u32))) return .source_too_large
@@ -82,7 +82,7 @@ scan proc(
if is_alpha(char) or char == '_' { if is_alpha(char) or char == '_' {
start :: cursor start :: cursor
result :: scan_ident(start, program) result :: try scan_ident(start, program, pool)
try add_token(state, Token{ try add_token(state, Token{
span = make_span(start, result.end), span = make_span(start, result.end),
kind = result.kind, kind = result.kind,
@@ -286,7 +286,11 @@ ScanIdentResult :: struct {
} }
@hide:file @hide:file
scan_ident proc(start usize, program []u8) ScanIdentResult { scan_ident proc(
start usize,
program []u8,
pool @mut strpool.StringPool,
) ScanIdentResult ! (mem.AllocError | strpool.InternError) {
cursor := start + 1 cursor := start + 1
while ( while (
cursor < program.len cursor < program.len
@@ -302,7 +306,7 @@ scan_ident proc(start usize, program []u8) ScanIdentResult {
# don't intern keywords (already O(1) lookup via token kind) # don't intern keywords (already O(1) lookup via token kind)
kind :: strmap.get(&keywords, program[start..cursor]) orelse .ident kind :: strmap.get(&keywords, program[start..cursor]) orelse .ident
str_id :: if (kind == .ident) str_id :: if (kind == .ident)
strpool.intern(&strpool.strings, program[start..cursor]) catch strpool.no_str try strpool.intern(pool, program[start..cursor])
else else
strpool.no_str strpool.no_str
return ScanIdentResult{ end = cursor, kind = kind, str_id = str_id } return ScanIdentResult{ end = cursor, kind = kind, str_id = str_id }
+103 -26
View File
@@ -4,14 +4,14 @@ import "@source/diagnostics"
import "@source/strpool" import "@source/strpool"
reports_invalid_input_and_keeps_scanning test { reports_invalid_input_and_keeps_scanning test {
strpool.strings = strpool.init(mem.c_allocator) pool := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&pool)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
state := init(mem.c_allocator) state := init(mem.c_allocator)
defer deinit(&state) defer deinit(&state)
try scan(&state, "if name # comment\n$1. 2 3.5 \"hi\"", &diagnostic_store) try scan(&state, "if name # comment\n$1. 2 3.5 \"hi\"", &pool, &diagnostic_store)
try testing.expect_equal(8, 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)
@@ -25,21 +25,21 @@ reports_invalid_input_and_keeps_scanning test {
try testing.expect_equal(2, diagnostics.count(&diagnostic_store)) try testing.expect_equal(2, diagnostics.count(&diagnostic_store))
try testing.expect_equal( try testing.expect_equal(
"name", "name",
strpool.get_str(&strpool.strings, state.tokens.items[1].data.str_id)?, strpool.get_str(&pool, state.tokens.items[1].data.str_id)?,
) )
try testing.expect_equal(u32(3), state.tokens.items[1].span.start) try testing.expect_equal(u32(3), state.tokens.items[1].span.start)
try testing.expect_equal(u32(7), state.tokens.items[1].span.end) try testing.expect_equal(u32(7), state.tokens.items[1].span.end)
} }
reserves_boolean_keywords_and_scans_comparisons test { reserves_boolean_keywords_and_scans_comparisons test {
strpool.strings = strpool.init(mem.c_allocator) pool := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&pool)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
state := init(mem.c_allocator) state := init(mem.c_allocator)
defer deinit(&state) defer deinit(&state)
source :: "true false bool trueish == != < <= > >=" source :: "true false bool trueish == != < <= > >="
try scan(&state, source, &diagnostic_store) try scan(&state, source, &pool, &diagnostic_store)
expected [11]TokenKind :: [ expected [11]TokenKind :: [
TokenKind.true, .false, .ident, .ident, TokenKind.true, .false, .ident, .ident,
.equal_equal, .not_equal, .less, .less_equal, .greater, .greater_equal, .eof, .equal_equal, .not_equal, .less, .less_equal, .greater, .greater_equal, .eof,
@@ -50,29 +50,29 @@ reserves_boolean_keywords_and_scans_comparisons test {
try testing.expect_equal(u32(4), state.tokens.items[0].span.end) try testing.expect_equal(u32(4), state.tokens.items[0].span.end)
try testing.expect_equal(u32(5), state.tokens.items[1].span.start) try testing.expect_equal(u32(5), state.tokens.items[1].span.start)
try testing.expect_equal(u32(10), state.tokens.items[1].span.end) try testing.expect_equal(u32(10), state.tokens.items[1].span.end)
try testing.expect_equal("bool", strpool.get_str(&strpool.strings, state.tokens.items[2].data.str_id)?) try testing.expect_equal("bool", strpool.get_str(&pool, state.tokens.items[2].data.str_id)?)
try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
} }
scans_logical_operators_without_splitting_identifiers test { scans_logical_operators_without_splitting_identifiers test {
strpool.strings = strpool.init(mem.c_allocator) pool := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&pool)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
state := init(mem.c_allocator) state := init(mem.c_allocator)
defer deinit(&state) defer deinit(&state)
source :: "and or android orbit and_1 or2 ! != !! !true" source :: "and or android orbit and_1 or2 ! != !! !true"
try scan(&state, source, &diagnostic_store) try scan(&state, source, &pool, &diagnostic_store)
expected [13]TokenKind :: [ expected [13]TokenKind :: [
TokenKind.and, .or, .ident, .ident, .ident, .ident, TokenKind.and, .or, .ident, .ident, .ident, .ident,
.bang, .not_equal, .bang, .bang, .bang, .true, .eof, .bang, .not_equal, .bang, .bang, .bang, .true, .eof,
] ]
try testing.expect_equal(expected.len, state.tokens.items.len) try testing.expect_equal(expected.len, state.tokens.items.len)
for (expected) |kind, index| try testing.expect_equal(kind, state.tokens.items[index].kind) for (expected) |kind, index| try testing.expect_equal(kind, state.tokens.items[index].kind)
try testing.expect_equal("android", strpool.get_str(&strpool.strings, state.tokens.items[2].data.str_id)?) try testing.expect_equal("android", strpool.get_str(&pool, state.tokens.items[2].data.str_id)?)
try testing.expect_equal("orbit", strpool.get_str(&strpool.strings, state.tokens.items[3].data.str_id)?) try testing.expect_equal("orbit", strpool.get_str(&pool, state.tokens.items[3].data.str_id)?)
try testing.expect_equal("and_1", strpool.get_str(&strpool.strings, state.tokens.items[4].data.str_id)?) try testing.expect_equal("and_1", strpool.get_str(&pool, state.tokens.items[4].data.str_id)?)
try testing.expect_equal("or2", strpool.get_str(&strpool.strings, state.tokens.items[5].data.str_id)?) try testing.expect_equal("or2", strpool.get_str(&pool, state.tokens.items[5].data.str_id)?)
try testing.expect_equal(u32(31), state.tokens.items[6].span.start) try testing.expect_equal(u32(31), state.tokens.items[6].span.start)
try testing.expect_equal(u32(32), state.tokens.items[6].span.end) try testing.expect_equal(u32(32), state.tokens.items[6].span.end)
try testing.expect_equal(u32(33), state.tokens.items[7].span.start) try testing.expect_equal(u32(33), state.tokens.items[7].span.start)
@@ -81,14 +81,14 @@ scans_logical_operators_without_splitting_identifiers test {
} }
scans_loop_controls_with_exact_keyword_boundaries test { scans_loop_controls_with_exact_keyword_boundaries test {
strpool.strings = strpool.init(mem.c_allocator) pool := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&pool)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
state := init(mem.c_allocator) state := init(mem.c_allocator)
defer deinit(&state) defer deinit(&state)
source :: "break continue breakfast continued break_1 continue2" source :: "break continue breakfast continued break_1 continue2"
try scan(&state, source, &diagnostic_store) try scan(&state, source, &pool, &diagnostic_store)
expected [7]TokenKind :: [.break, .continue, .ident, .ident, .ident, .ident, .eof] expected [7]TokenKind :: [.break, .continue, .ident, .ident, .ident, .ident, .eof]
try testing.expect_equal(expected.len, state.tokens.items.len) try testing.expect_equal(expected.len, state.tokens.items.len)
for (expected) |kind, index| try testing.expect_equal(kind, state.tokens.items[index].kind) for (expected) |kind, index| try testing.expect_equal(kind, state.tokens.items[index].kind)
@@ -96,19 +96,19 @@ scans_loop_controls_with_exact_keyword_boundaries test {
try testing.expect_equal(u32(5), state.tokens.items[0].span.end) try testing.expect_equal(u32(5), state.tokens.items[0].span.end)
try testing.expect_equal(u32(6), state.tokens.items[1].span.start) try testing.expect_equal(u32(6), state.tokens.items[1].span.start)
try testing.expect_equal(u32(14), state.tokens.items[1].span.end) try testing.expect_equal(u32(14), state.tokens.items[1].span.end)
try testing.expect_equal("breakfast", strpool.get_str(&strpool.strings, state.tokens.items[2].data.str_id)?) try testing.expect_equal("breakfast", strpool.get_str(&pool, state.tokens.items[2].data.str_id)?)
try testing.expect_equal("continued", strpool.get_str(&strpool.strings, state.tokens.items[3].data.str_id)?) try testing.expect_equal("continued", strpool.get_str(&pool, state.tokens.items[3].data.str_id)?)
try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
} }
scans_ranges_without_consuming_float_dots test { scans_ranges_without_consuming_float_dots test {
strpool.strings = strpool.init(mem.c_allocator) pool := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&pool)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
state := init(mem.c_allocator) state := init(mem.c_allocator)
defer deinit(&state) defer deinit(&state)
try scan(&state, "0..10 0..=10 1.5..2.5 |@item, _| ... 1.", &diagnostic_store) try scan(&state, "0..10 0..=10 1.5..2.5 |@item, _| ... 1.", &pool, &diagnostic_store)
expected [19]TokenKind :: [ expected [19]TokenKind :: [
.int, .range, .int, .int, .range_inclusive, .int, .int, .range, .int, .int, .range_inclusive, .int,
.float, .range, .float, .pipe, .at, .ident, .comma, .ident, .pipe, .float, .range, .float, .pipe, .at, .ident, .comma, .ident, .pipe,
@@ -124,13 +124,13 @@ scans_ranges_without_consuming_float_dots test {
} }
scans_array_delimiters_and_mut_keyword test { scans_array_delimiters_and_mut_keyword test {
strpool.strings = strpool.init(mem.c_allocator) pool := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&pool)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
state := init(mem.c_allocator) state := init(mem.c_allocator)
defer deinit(&state) defer deinit(&state)
try scan(&state, "[_]mut [2]i64 [1,2] [] mutable mut_1", &diagnostic_store) try scan(&state, "[_]mut [2]i64 [1,2] [] mutable mut_1", &pool, &diagnostic_store)
expected [18]TokenKind :: [ expected [18]TokenKind :: [
.open_bracket, .ident, .close_bracket, .mut, .open_bracket, .ident, .close_bracket, .mut,
.open_bracket, .int, .close_bracket, .ident, .open_bracket, .int, .close_bracket, .ident,
@@ -141,3 +141,80 @@ scans_array_delimiters_and_mut_keyword test {
for (expected) |kind, index| try testing.expect_equal(kind, state.tokens.items[index].kind) for (expected) |kind, index| try testing.expect_equal(kind, state.tokens.items[index].kind)
try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
} }
reuses_identifiers_without_interning_keywords test {
pool := strpool.init(mem.c_allocator)
defer strpool.deinit(&pool)
diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store)
state := init(mem.c_allocator)
defer deinit(&state)
try scan(&state, "alpha beta alpha if beta", &pool, &diagnostic_store)
first :: state.tokens.items[0].data.str_id
second :: state.tokens.items[1].data.str_id
try testing.expect(first != second)
try testing.expect_equal(first, state.tokens.items[2].data.str_id)
try testing.expect_equal(second, state.tokens.items[4].data.str_id)
try testing.expect_equal(null, strpool.get_id(&pool, "if"))
try testing.expect_equal(usize(2), pool.strings.items.len)
}
@hide:file
AllocationState :: struct {
remaining usize
live usize = 0
}
@hide:file
limited_alloc proc(context ?@mut anyopaque, size usize, alignment usize) ?*mut u8 {
state @mut AllocationState :: ptrcast!(context?)
if (state.remaining == 0) return null
state.remaining -= 1
memory :: mem.raw_alloc(mem.c_allocator, size, alignment)
if (memory) |_| state.live += 1
return memory
}
@hide:file
limited_realloc proc(context ?@mut anyopaque, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8 {
state @mut AllocationState :: ptrcast!(context?)
if (state.remaining == 0) return null
state.remaining -= 1
resized :: mem.raw_realloc(mem.c_allocator, memory, old_size, new_size, alignment)
if (resized) |_| {
if (memory) |_| {} else state.live += 1
}
return resized
}
@hide:file
limited_free proc(context ?@mut anyopaque, memory ?*mut u8, size usize, alignment usize) void {
state @mut AllocationState :: ptrcast!(context?)
if (memory) |_| state.live -= 1
mem.raw_free(mem.c_allocator, memory, size, alignment)
}
@hide:file
limited_vtable :: mem.AllocatorVTable{ alloc = limited_alloc, realloc = limited_realloc, free = limited_free }
propagates_identifier_interning_allocation_failure test {
for (usize(0)..usize(3)) |remaining| {
allocation := AllocationState{ remaining = remaining }
allocator :: mem.Allocator{ context = &allocation, vtable = &limited_vtable }
pool := strpool.init(allocator)
diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store)
state := init(mem.c_allocator)
defer deinit(&state)
failed := false
scan(&state, "identifier", &pool, &diagnostic_store) catch |error| {
failed = error == .out_of_memory
}
empty :: state.tokens.items.len == 0 and pool.strings.items.len == 0
absent :: strpool.get_id(&pool, "identifier") == null
strpool.deinit(&pool)
try testing.expect(failed and empty and absent)
try testing.expect_equal(usize(0), diagnostics.count(&diagnostic_store))
try testing.expect_equal(usize(0), allocation.live)
}
}
+3 -9
View File
@@ -69,16 +69,10 @@ TokenKind :: enum {
render_token proc(token Token, program []u8) void { render_token proc(token Token, program []u8) void {
match token.kind { match token.kind {
.ident: { .ident, .string, .int, .float:
if (strpool.get_str(&strpool.strings, token.data.str_id)) |str| {
debug.print("{}({})\n", {token.kind, str})
} else {
debug.print("{}({})\n", {token.kind, program[token.span.start..token.span.end]})
}
}
.string, .int, .float:
debug.print("{}({})\n", {token.kind, program[token.span.start..token.span.end]}) debug.print("{}({})\n", {token.kind, program[token.span.start..token.span.end]})
else: debug.print("{}\n", {token.kind}) else:
debug.print("{}\n", {token.kind})
} }
} }
+8 -8
View File
@@ -20,14 +20,14 @@ compile_owned proc(text []u8, diagnostic_store @mut diagnostics.Store) ir.Module
source :: try mem.alloc(u8, mem.c_allocator, text.len) source :: try mem.alloc(u8, mem.c_allocator, text.len)
defer mem.free(mem.c_allocator, source) defer mem.free(mem.c_allocator, source)
for (text) |byte, i| source[i] = byte for (text) |byte, i| source[i] = byte
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, diagnostic_store) try lexer.scan(&scan_state, source, &strings, diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, diagnostic_store) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, diagnostic_store)
defer sema.deinit(&resolution) defer sema.deinit(&resolution)
checked := try typecheck.check(mem.c_allocator, &tree, &resolution, diagnostic_store) checked := try typecheck.check(mem.c_allocator, &tree, &resolution, diagnostic_store)
defer typecheck.deinit(&checked) defer typecheck.deinit(&checked)
@@ -469,8 +469,8 @@ limited_free proc(context ?@mut anyopaque, memory ?*mut u8, size usize, alignmen
limited_vtable :: mem.AllocatorVTable{ alloc = limited_alloc, realloc = limited_realloc, free = limited_free } limited_vtable :: mem.AllocatorVTable{ alloc = limited_alloc, realloc = limited_realloc, free = limited_free }
releases_temporaries_and_partially_owned_modules_on_allocation_failure test { releases_temporaries_and_partially_owned_modules_on_allocation_failure test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -529,10 +529,10 @@ releases_temporaries_and_partially_owned_modules_on_allocation_failure test {
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store)
defer sema.deinit(&resolution) defer sema.deinit(&resolution)
checked := try typecheck.check(mem.c_allocator, &tree, &resolution, &diagnostic_store) checked := try typecheck.check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer typecheck.deinit(&checked) defer typecheck.deinit(&checked)
+4 -4
View File
@@ -112,14 +112,14 @@ read_source proc(allocator mem.Allocator, runtime io.Io, path [;0]u8) arraylist.
@hide:file @hide:file
compile proc(allocator mem.Allocator, source []u8, diagnostic_store @mut diagnostics.Store) ir.Module ! DriverError { compile proc(allocator mem.Allocator, source []u8, diagnostic_store @mut diagnostics.Store) ir.Module ! DriverError {
strpool.strings = strpool.init(allocator) strings := strpool.init(allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
scan_state := lexer.init(allocator) scan_state := lexer.init(allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, diagnostic_store) try lexer.scan(&scan_state, source, &strings, diagnostic_store)
tree := try parser.parse(allocator, scan_state.tokens.items, source, diagnostic_store) tree := try parser.parse(allocator, scan_state.tokens.items, source, diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
resolution := try sema.resolve(allocator, &tree, diagnostic_store) resolution := try sema.resolve(allocator, &tree, &strings, diagnostic_store)
defer sema.deinit(&resolution) defer sema.deinit(&resolution)
checked := try typecheck.check(allocator, &tree, &resolution, diagnostic_store) checked := try typecheck.check(allocator, &tree, &resolution, diagnostic_store)
defer typecheck.deinit(&checked) defer typecheck.deinit(&checked)
+11 -8
View File
@@ -95,25 +95,26 @@ parse_func_decl proc(state @mut State) ast.NodeId ! ParseError {
params arraylist.ArrayList(ast.NodeId) := arraylist.init(state.allocator) params arraylist.ArrayList(ast.NodeId) := arraylist.init(state.allocator)
defer arraylist.deinit(&params) defer arraylist.deinit(&params)
names arraylist.ArrayList(ast.Span) := arraylist.init(state.allocator) names arraylist.ArrayList(lexer.Token) := arraylist.init(state.allocator)
defer arraylist.deinit(&names) defer arraylist.deinit(&names)
while !next_is(state, .close_paren) { while !next_is(state, .close_paren) {
arraylist.clear(&names) arraylist.clear(&names)
while true { while true {
token :: try consume(state, .ident) token :: try consume(state, .ident)
try arraylist.append(&names, token.span) try arraylist.append(&names, token)
if (next_is(state, .ident) or next_is(state, .open_bracket)) break if (next_is(state, .ident) or next_is(state, .open_bracket)) break
_ = try consume(state, .comma) _ = try consume(state, .comma)
} }
type_id :: try parse_type(state) type_id :: try parse_type(state)
type_span :: ast.builder_span(&state.builder, type_id) type_span :: ast.builder_span(&state.builder, type_id)
for (names.items) |name_span| { for (names.items) |parameter_name| {
param_id :: try ast.add_param( param_id :: try ast.add_param(
&state.builder, &state.builder,
ast.Span{ start = name_span.start, end = type_span.end }, ast.Span{ start = parameter_name.span.start, end = type_span.end },
name_span, parameter_name.span,
parameter_name.data.str_id,
type_id, type_id,
) )
try arraylist.append(&params, param_id) try arraylist.append(&params, param_id)
@@ -129,6 +130,7 @@ parse_func_decl proc(state @mut State) ast.NodeId ! ParseError {
&state.builder, &state.builder,
ast.Span{ start = name.span.start, end = body_span.end }, ast.Span{ start = name.span.start, end = body_span.end },
name.span, name.span,
name.data.str_id,
return_type, return_type,
params.items, params.items,
body, body,
@@ -315,7 +317,7 @@ parse_capture_name proc(state @mut State) ?ast.NodeId ! ParseError {
if ( if (
name.span.end != name.span.start + 1 name.span.end != name.span.start + 1
or state.builder.source[usize(name.span.start)] != '_' or state.builder.source[usize(name.span.start)] != '_'
) capture = try ast.add_identifier(&state.builder, name.span) ) capture = try ast.add_identifier(&state.builder, name.span, name.data.str_id)
return capture return capture
} }
@@ -480,6 +482,7 @@ parse_statement_decl proc(state @mut State) ast.NodeId ! ParseError {
&state.builder, &state.builder,
ast.Span{ start = name.span.start, end = end }, ast.Span{ start = name.span.start, end = end },
name.span, name.span,
name.data.str_id,
type_id, type_id,
mutability, mutability,
initializer, initializer,
@@ -511,7 +514,7 @@ parse_type proc(state @mut State) ast.NodeId ! ParseError {
) )
} }
token :: try consume(state, .ident) token :: try consume(state, .ident)
return try ast.add_identifier(&state.builder, token.span) return try ast.add_identifier(&state.builder, token.span, token.data.str_id)
} }
@hide:file @hide:file
@@ -774,7 +777,7 @@ parse_primary proc(state @mut State) ast.NodeId ! ParseError {
} }
.ident: { .ident: {
state.next_token += 1 state.next_token += 1
return try ast.add_identifier(&state.builder, token.span) return try ast.add_identifier(&state.builder, token.span, token.data.str_id)
} }
else: { else: {
diagnostic :: if (token.data.diagnostic != diagnostics.no_diagnostic) diagnostic :: if (token.data.diagnostic != diagnostics.no_diagnostic)
+130 -105
View File
@@ -8,14 +8,14 @@ import "@source/lexer"
import "@source/ast" import "@source/ast"
parses_expression_precedence_through_tree_views test { parses_expression_precedence_through_tree_views test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: "value :: 1 + 2 * 3\n" source :: "value :: 1 + 2 * 3\n"
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
@@ -24,6 +24,7 @@ parses_expression_precedence_through_tree_views test {
declaration :: ast.package_decl(&tree, 0) declaration :: ast.package_decl(&tree, 0)
statement :: ast.statement_decl(&tree, declaration) statement :: ast.statement_decl(&tree, declaration)
try testing.expect_equal("value", ast.source_text(&tree, statement.name)) try testing.expect_equal("value", ast.source_text(&tree, statement.name))
try testing.expect_equal("value", strpool.get_str(&strings, ast.name_id(&tree, declaration))?)
try testing.expect_equal(ast.ExprKind.binary, ast.expr_kind(&tree, statement.initializer)) try testing.expect_equal(ast.ExprKind.binary, ast.expr_kind(&tree, statement.initializer))
add :: ast.binary_expr(&tree, statement.initializer) add :: ast.binary_expr(&tree, statement.initializer)
try testing.expect_equal(ast.BinaryOperator.add, add.operator) try testing.expect_equal(ast.BinaryOperator.add, add.operator)
@@ -35,14 +36,14 @@ parses_expression_precedence_through_tree_views test {
} }
parses_grouped_expression_precedence test { parses_grouped_expression_precedence test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: "value :: a + ((b - c)) * 2 / 3\n" source :: "value :: a + ((b - c)) * 2 / 3\n"
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
@@ -66,8 +67,8 @@ parses_grouped_expression_precedence test {
} }
parses_function_structure_through_tree_views test { parses_function_structure_through_tree_views test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -76,7 +77,7 @@ parses_function_structure_through_tree_views test {
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
@@ -85,6 +86,7 @@ parses_function_structure_through_tree_views test {
try testing.expect_equal(ast.DeclKind.function, ast.decl_kind(&tree, declaration)) try testing.expect_equal(ast.DeclKind.function, ast.decl_kind(&tree, declaration))
function :: ast.function_decl(&tree, declaration) function :: ast.function_decl(&tree, declaration)
try testing.expect_equal("grouped", ast.source_text(&tree, function.name)) try testing.expect_equal("grouped", ast.source_text(&tree, function.name))
try testing.expect_equal("grouped", strpool.get_str(&strings, ast.name_id(&tree, declaration))?)
try testing.expect_equal(3, ast.function_param_count(&tree, declaration)) try testing.expect_equal(3, ast.function_param_count(&tree, declaration))
first :: ast.param(&tree, ast.function_param(&tree, declaration, 0)) first :: ast.param(&tree, ast.function_param(&tree, declaration, 0))
second :: ast.param(&tree, ast.function_param(&tree, declaration, 1)) second :: ast.param(&tree, ast.function_param(&tree, declaration, 1))
@@ -92,6 +94,17 @@ parses_function_structure_through_tree_views test {
try testing.expect_equal("a", ast.source_text(&tree, first.name)) try testing.expect_equal("a", ast.source_text(&tree, first.name))
try testing.expect_equal("b", ast.source_text(&tree, second.name)) try testing.expect_equal("b", ast.source_text(&tree, second.name))
try testing.expect_equal("c", ast.source_text(&tree, third.name)) try testing.expect_equal("c", ast.source_text(&tree, third.name))
try testing.expect_equal(
"a", strpool.get_str(&strings, ast.name_id(&tree, ast.function_param(&tree, declaration, 0)))?,
)
try testing.expect_equal(
"b", strpool.get_str(&strings, ast.name_id(&tree, ast.function_param(&tree, declaration, 1)))?,
)
try testing.expect_equal(
"c", strpool.get_str(&strings, ast.name_id(&tree, ast.function_param(&tree, declaration, 2)))?,
)
try testing.expect_equal("T", strpool.get_str(&strings, ast.name_id(&tree, first.type))?)
try testing.expect_equal("U", strpool.get_str(&strings, ast.name_id(&tree, third.type))?)
try testing.expect_equal(first.type, second.type) try testing.expect_equal(first.type, second.type)
try testing.expect(first.type != third.type) try testing.expect(first.type != third.type)
try testing.expect_equal(1, ast.block_statement_count(&tree, function.body)) try testing.expect_equal(1, ast.block_statement_count(&tree, function.body))
@@ -99,14 +112,14 @@ parses_function_structure_through_tree_views test {
} }
recovers_invalid_declaration_and_preserves_sibling test { recovers_invalid_declaration_and_preserves_sibling test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: "@ broken\nvalid :: 1\n" source :: "@ broken\nvalid :: 1\n"
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
@@ -123,8 +136,8 @@ recovers_invalid_declaration_and_preserves_sibling test {
} }
recovers_malformed_groups_and_preserves_following_statements test { recovers_malformed_groups_and_preserves_following_statements test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -137,7 +150,7 @@ recovers_malformed_groups_and_preserves_following_statements test {
`after :: 4 `after :: 4
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
@@ -162,8 +175,8 @@ recovers_malformed_groups_and_preserves_following_statements test {
} }
parses_call_statements_and_postfix_precedence test { parses_call_statements_and_postfix_precedence test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -174,7 +187,7 @@ parses_call_statements_and_postfix_precedence test {
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
@@ -238,8 +251,8 @@ parses_call_statements_and_postfix_precedence test {
} }
recovers_malformed_calls_and_preserves_siblings test { recovers_malformed_calls_and_preserves_siblings test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -253,7 +266,7 @@ recovers_malformed_calls_and_preserves_siblings test {
`tail :: f() `tail :: f()
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
@@ -288,8 +301,8 @@ recovers_malformed_calls_and_preserves_siblings test {
} }
parses_return_boundaries_and_recovers_trailing_tokens test { parses_return_boundaries_and_recovers_trailing_tokens test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -303,7 +316,7 @@ parses_return_boundaries_and_recovers_trailing_tokens test {
` return } ` return }
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
@@ -333,15 +346,15 @@ parses_return_boundaries_and_recovers_trailing_tokens test {
} }
parses_boolean_comparison_precedence_and_associativity test { parses_boolean_comparison_precedence_and_associativity test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
`value :: -f() * 2 + 3 < 4 <= 5 > 6 >= 7 == true != false `value :: -f() * 2 + 3 < 4 <= 5 > 6 >= 7 == true != false
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
different :: ast.binary_expr(&tree, ast.statement_decl(&tree, ast.package_decl(&tree, 0)).initializer) different :: ast.binary_expr(&tree, ast.statement_decl(&tree, ast.package_decl(&tree, 0)).initializer)
@@ -368,8 +381,8 @@ parses_boolean_comparison_precedence_and_associativity test {
} }
parses_factorial_and_canonical_conditional_arms test { parses_factorial_and_canonical_conditional_arms test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -385,7 +398,7 @@ parses_factorial_and_canonical_conditional_arms test {
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
factorial :: ast.function_decl(&tree, ast.package_decl(&tree, 0)) factorial :: ast.function_decl(&tree, ast.package_decl(&tree, 0))
@@ -410,14 +423,14 @@ parses_factorial_and_canonical_conditional_arms test {
} }
attaches_dangling_else_to_nearest_if test { attaches_dangling_else_to_nearest_if test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: "main proc() int { if (true) if (false) return 1 else return 2 else return 3 }" source :: "main proc() int { if (true) if (false) return 1 else return 2 else return 3 }"
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
body :: ast.function_decl(&tree, ast.package_decl(&tree, 0)).body body :: ast.function_decl(&tree, ast.package_decl(&tree, 0)).body
@@ -431,8 +444,8 @@ attaches_dangling_else_to_nearest_if test {
} }
recovers_conditional_conditions_and_arms_without_losing_siblings test { recovers_conditional_conditions_and_arms_without_losing_siblings test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -447,7 +460,7 @@ recovers_conditional_conditions_and_arms_without_losing_siblings test {
`tail :: 9 `tail :: 9
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
try testing.expect_equal(2, ast.package_decl_count(&tree)) try testing.expect_equal(2, ast.package_decl_count(&tree))
@@ -475,8 +488,8 @@ recovers_conditional_conditions_and_arms_without_losing_siblings test {
} }
recovers_invalid_guard_syntax_and_missing_arm_at_enclosing_brace test { recovers_invalid_guard_syntax_and_missing_arm_at_enclosing_brace test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -488,7 +501,7 @@ recovers_invalid_guard_syntax_and_missing_arm_at_enclosing_brace test {
`tail :: 4 `tail :: 4
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
try testing.expect_equal(2, ast.package_decl_count(&tree)) try testing.expect_equal(2, ast.package_decl_count(&tree))
@@ -513,8 +526,8 @@ recovers_invalid_guard_syntax_and_missing_arm_at_enclosing_brace test {
} }
parses_iterative_factorial_assignments_and_loop test { parses_iterative_factorial_assignments_and_loop test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -529,7 +542,7 @@ parses_iterative_factorial_assignments_and_loop test {
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
@@ -552,8 +565,8 @@ parses_iterative_factorial_assignments_and_loop test {
} }
parses_assignment_precedence_grouped_span_and_rhs_boundaries test { parses_assignment_precedence_grouped_span_and_rhs_boundaries test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -564,7 +577,7 @@ parses_assignment_precedence_grouped_span_and_rhs_boundaries test {
` value = 6 } ` value = 6 }
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
@@ -590,8 +603,8 @@ parses_assignment_precedence_grouped_span_and_rhs_boundaries test {
} }
parses_nested_loops_and_preserves_nearest_if_else test { parses_nested_loops_and_preserves_nearest_if_else test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -604,7 +617,7 @@ parses_nested_loops_and_preserves_nearest_if_else test {
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
@@ -628,8 +641,8 @@ parses_nested_loops_and_preserves_nearest_if_else test {
} }
recovers_loop_guards_bodies_and_assignments_without_losing_braces test { recovers_loop_guards_bodies_and_assignments_without_losing_braces test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -645,7 +658,7 @@ recovers_loop_guards_bodies_and_assignments_without_losing_braces test {
`tail :: 8 `tail :: 8
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
@@ -675,8 +688,8 @@ recovers_loop_guards_bodies_and_assignments_without_losing_braces test {
} }
rejects_top_level_assignment_loop_else_and_assignment_expressions test { rejects_top_level_assignment_loop_else_and_assignment_expressions test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -689,7 +702,7 @@ rejects_top_level_assignment_loop_else_and_assignment_expressions test {
`tail :: 6 `tail :: 6
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
@@ -707,8 +720,8 @@ rejects_top_level_assignment_loop_else_and_assignment_expressions test {
} }
recovers_nested_braces_in_malformed_loop_body_and_assignment test { recovers_nested_braces_in_malformed_loop_body_and_assignment test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -724,7 +737,7 @@ recovers_nested_braces_in_malformed_loop_body_and_assignment test {
`tail :: 8 `tail :: 8
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
@@ -742,14 +755,14 @@ recovers_nested_braces_in_malformed_loop_body_and_assignment test {
} }
parses_logical_precedence_and_associativity test { parses_logical_precedence_and_associativity test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: "value :: !probe() == a + b * c < d and e and f or g or h\n" source :: "value :: !probe() == a + b * c < d and e and f or g or h\n"
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
@@ -780,14 +793,14 @@ parses_logical_precedence_and_associativity test {
} }
parses_right_associative_mixed_prefix_operators test { parses_right_associative_mixed_prefix_operators test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: "value :: !-!probe()\nother :: -!false\n" source :: "value :: !-!probe()\nother :: -!false\n"
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
@@ -809,8 +822,8 @@ parses_right_associative_mixed_prefix_operators test {
} }
parses_logical_expressions_in_all_expression_positions test { parses_logical_expressions_in_all_expression_positions test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -853,7 +866,7 @@ parses_logical_expressions_in_all_expression_positions test {
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
@@ -884,8 +897,8 @@ parses_logical_expressions_in_all_expression_positions test {
} }
multiline_group_errors_preserve_statement_boundaries test { multiline_group_errors_preserve_statement_boundaries test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -900,7 +913,7 @@ multiline_group_errors_preserve_statement_boundaries test {
`tail :: 9 `tail :: 9
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
@@ -918,8 +931,8 @@ multiline_group_errors_preserve_statement_boundaries test {
} }
recovers_missing_logical_operands_and_preserves_enclosing_nodes test { recovers_missing_logical_operands_and_preserves_enclosing_nodes test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -932,7 +945,7 @@ recovers_missing_logical_operands_and_preserves_enclosing_nodes test {
`after :: true `after :: true
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
@@ -964,8 +977,8 @@ recovers_missing_logical_operands_and_preserves_enclosing_nodes test {
} }
parses_leaf_loop_controls_and_nearest_else test { parses_leaf_loop_controls_and_nearest_else test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -979,7 +992,7 @@ parses_leaf_loop_controls_and_nearest_else test {
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
@@ -1006,8 +1019,8 @@ parses_leaf_loop_controls_and_nearest_else test {
} }
rejects_control_tails_and_preserves_following_declarations test { rejects_control_tails_and_preserves_following_declarations test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -1029,7 +1042,7 @@ rejects_control_tails_and_preserves_following_declarations test {
`last :: 4 `last :: 4
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
try testing.expect_equal(9, diagnostics.count(&diagnostic_store)) try testing.expect_equal(9, diagnostics.count(&diagnostic_store))
@@ -1057,8 +1070,8 @@ rejects_control_tails_and_preserves_following_declarations test {
} }
preserves_control_bodies_after_malformed_guards test { preserves_control_bodies_after_malformed_guards test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -1070,7 +1083,7 @@ preserves_control_bodies_after_malformed_guards test {
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
try testing.expect_equal(2, diagnostics.count(&diagnostic_store)) try testing.expect_equal(2, diagnostics.count(&diagnostic_store))
@@ -1086,8 +1099,8 @@ preserves_control_bodies_after_malformed_guards test {
} }
parses_range_endpoints_and_capture_shapes test { parses_range_endpoints_and_capture_shapes test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -1104,7 +1117,7 @@ parses_range_endpoints_and_capture_shapes test {
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
@@ -1119,6 +1132,9 @@ parses_range_endpoints_and_capture_shapes test {
if (first.capture) |capture| { if (first.capture) |capture| {
try testing.expect_equal(ast.ExprKind.identifier, ast.expr_kind(&tree, capture)) try testing.expect_equal(ast.ExprKind.identifier, ast.expr_kind(&tree, capture))
try testing.expect_equal("item", ast.source_text(&tree, ast.node_span(&tree, capture))) try testing.expect_equal("item", ast.source_text(&tree, ast.node_span(&tree, capture)))
try testing.expect_equal("item", strpool.get_str(&strings, ast.name_id(&tree, capture))?)
reference :: ast.statement_expr(&tree, ast.block_statement(&tree, first.body, 0))
try testing.expect_equal(ast.name_id(&tree, capture), ast.name_id(&tree, reference))
} else return .expectation_failed } else return .expectation_failed
second :: ast.for_stmt(&tree, ast.block_statement(&tree, body, 1)) second :: ast.for_stmt(&tree, ast.block_statement(&tree, body, 1))
try testing.expect(second.capture == null) try testing.expect(second.capture == null)
@@ -1129,11 +1145,16 @@ parses_range_endpoints_and_capture_shapes test {
try testing.expect(third.pointer_capture and third.index_capture and third.index == null) try testing.expect(third.pointer_capture and third.index_capture and third.index == null)
fourth :: ast.for_stmt(&tree, ast.block_statement(&tree, body, 3)) fourth :: ast.for_stmt(&tree, ast.block_statement(&tree, body, 3))
try testing.expect(fourth.index_capture and fourth.index != null) try testing.expect(fourth.index_capture and fourth.index != null)
try testing.expect_equal(ast.name_id(&tree, first.capture?), ast.name_id(&tree, third.capture?))
try testing.expect_equal(ast.name_id(&tree, first.capture?), ast.name_id(&tree, fourth.capture?))
try testing.expect_equal("index", strpool.get_str(&strings, ast.name_id(&tree, fourth.index?))?)
index_reference :: ast.statement_expr(&tree, ast.block_statement(&tree, fourth.body, 0))
try testing.expect_equal(ast.name_id(&tree, fourth.index?), ast.name_id(&tree, index_reference))
} }
rejects_bro_invalid_range_headers_without_losing_siblings test { rejects_bro_invalid_range_headers_without_losing_siblings test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
cases [13][]u8 :: [ cases [13][]u8 :: [
"for -2..1 |i| {}", "for -2..1 |i| {}",
"for 0..limit+1 |i| {}", "for 0..limit+1 |i| {}",
@@ -1159,7 +1180,7 @@ rejects_bro_invalid_range_headers_without_losing_siblings test {
for ("\nkept :: 7\n}\nafter :: 9\n") |byte| try arraylist.append(&source, byte) for ("\nkept :: 7\n}\nafter :: 9\n") |byte| try arraylist.append(&source, byte)
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source.items, &diagnostic_store) try lexer.scan(&scan_state, source.items, &strings, &diagnostic_store)
tree := try parse(mem.c_allocator, scan_state.tokens.items, source.items, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source.items, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
try testing.expect(diagnostics.count(&diagnostic_store) > 0) try testing.expect(diagnostics.count(&diagnostic_store) > 0)
@@ -1175,8 +1196,8 @@ rejects_bro_invalid_range_headers_without_losing_siblings test {
} }
range_loop_preserves_nearest_else_and_canonical_body test { range_loop_preserves_nearest_else_and_canonical_body test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -1186,7 +1207,7 @@ range_loop_preserves_nearest_else_and_canonical_body test {
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
@@ -1202,8 +1223,8 @@ range_loop_preserves_nearest_else_and_canonical_body test {
} }
parses_recursive_array_annotations_literals_and_grouped_parameters test { parses_recursive_array_annotations_literals_and_grouped_parameters test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -1219,7 +1240,7 @@ parses_recursive_array_annotations_literals_and_grouped_parameters test {
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
@@ -1232,6 +1253,10 @@ parses_recursive_array_annotations_literals_and_grouped_parameters test {
try testing.expect(inner.mutable) try testing.expect(inner.mutable)
try testing.expect_equal(ast.ExprKind.identifier, ast.expr_kind(&tree, inner.count?)) try testing.expect_equal(ast.ExprKind.identifier, ast.expr_kind(&tree, inner.count?))
try testing.expect_equal("i64", ast.source_text(&tree, ast.node_span(&tree, inner.element))) try testing.expect_equal("i64", ast.source_text(&tree, ast.node_span(&tree, inner.element)))
try testing.expect_equal(
ast.name_id(&tree, ast.package_decl(&tree, 3)), ast.name_id(&tree, inner.count?),
)
try testing.expect_equal("i64", strpool.get_str(&strings, ast.name_id(&tree, inner.element))?)
try testing.expect_equal(2, ast.array_element_count(&tree, declaration.initializer)) try testing.expect_equal(2, ast.array_element_count(&tree, declaration.initializer))
for (0..ast.array_element_count(&tree, declaration.initializer)) |i| { for (0..ast.array_element_count(&tree, declaration.initializer)) |i| {
try testing.expect_equal(2, ast.array_element_count( try testing.expect_equal(2, ast.array_element_count(
@@ -1253,8 +1278,8 @@ parses_recursive_array_annotations_literals_and_grouped_parameters test {
} }
parses_postfix_indices_and_general_assignment_targets test { parses_postfix_indices_and_general_assignment_targets test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -1266,7 +1291,7 @@ parses_postfix_indices_and_general_assignment_targets test {
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
@@ -1293,8 +1318,8 @@ parses_postfix_indices_and_general_assignment_targets test {
} }
mirrors_array_type_and_leading_declaration_ambiguities test { mirrors_array_type_and_leading_declaration_ambiguities test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -1307,7 +1332,7 @@ mirrors_array_type_and_leading_declaration_ambiguities test {
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
try testing.expect_equal(2, diagnostics.count(&diagnostic_store)) try testing.expect_equal(2, diagnostics.count(&diagnostic_store))
@@ -1325,8 +1350,8 @@ mirrors_array_type_and_leading_declaration_ambiguities test {
} }
preserves_array_error_ids_control_bodies_and_following_siblings test { preserves_array_error_ids_control_bodies_and_following_siblings test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -1340,7 +1365,7 @@ preserves_array_error_ids_control_bodies_and_following_siblings test {
`tail :: 4 `tail :: 4
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
try testing.expect_equal(2, diagnostics.count(&diagnostic_store)) try testing.expect_equal(2, diagnostics.count(&diagnostic_store))
tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
@@ -1370,8 +1395,8 @@ preserves_array_error_ids_control_bodies_and_following_siblings test {
} }
recovers_nested_square_and_parenthesis_delimiters_without_consuming_siblings test { recovers_nested_square_and_parenthesis_delimiters_without_consuming_siblings test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -1385,7 +1410,7 @@ recovers_nested_square_and_parenthesis_delimiters_without_consuming_siblings tes
`tail :: 5 `tail :: 5
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
try testing.expect(diagnostics.count(&diagnostic_store) > 0) try testing.expect(diagnostics.count(&diagnostic_store) > 0)
+123 -137
View File
@@ -46,8 +46,8 @@ code_count proc(diagnostic_store @diagnostics.Store, code diagnostics.Code) usiz
} }
propagates_numeric_context_before_defaulting_forward_and_mutable_aliases test { propagates_numeric_context_before_defaulting_forward_and_mutable_aliases test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -66,10 +66,10 @@ propagates_numeric_context_before_defaulting_forward_and_mutable_aliases test {
`nested float :: quotient / (alias + 1) `nested float :: quotient / (alias + 1)
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store)
defer sema.deinit(&resolution) defer sema.deinit(&resolution)
try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
@@ -102,8 +102,8 @@ propagates_numeric_context_before_defaulting_forward_and_mutable_aliases test {
} }
rejects_fixed_family_conversion_integer_division_and_conflicting_demands test { rejects_fixed_family_conversion_integer_division_and_conflicting_demands test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -124,10 +124,10 @@ rejects_fixed_family_conversion_integer_division_and_conflicting_demands test {
`good :: 9 `good :: 9
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store)
defer sema.deinit(&resolution) defer sema.deinit(&resolution)
try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
@@ -169,8 +169,8 @@ rejects_fixed_family_conversion_integer_division_and_conflicting_demands test {
} }
checks_forward_alias_calls_grouped_parameters_and_mutual_recursion test { checks_forward_alias_calls_grouped_parameters_and_mutual_recursion test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -189,10 +189,10 @@ checks_forward_alias_calls_grouped_parameters_and_mutual_recursion test {
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store)
defer sema.deinit(&resolution) defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result) defer deinit(&result)
@@ -244,8 +244,8 @@ checks_forward_alias_calls_grouped_parameters_and_mutual_recursion test {
} }
separates_type_annotations_from_values_and_restricts_void_storage test { separates_type_annotations_from_values_and_restricts_void_storage test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -266,10 +266,10 @@ separates_type_annotations_from_values_and_restricts_void_storage test {
`good string :: "ok" `good string :: "ok"
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store)
defer sema.deinit(&resolution) defer sema.deinit(&resolution)
try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
@@ -336,8 +336,8 @@ separates_type_annotations_from_values_and_restricts_void_storage test {
} }
reports_nonvoid_fallthrough_without_poisoning_signatures_and_checks_unused_bodies test { reports_nonvoid_fallthrough_without_poisoning_signatures_and_checks_unused_bodies test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -353,10 +353,10 @@ reports_nonvoid_fallthrough_without_poisoning_signatures_and_checks_unused_bodie
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store)
defer sema.deinit(&resolution) defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result) defer deinit(&result)
@@ -391,8 +391,8 @@ reports_nonvoid_fallthrough_without_poisoning_signatures_and_checks_unused_bodie
} }
checks_arguments_even_for_wrong_arity_noncallable_and_chained_callees test { checks_arguments_even_for_wrong_arity_noncallable_and_chained_callees test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -410,10 +410,10 @@ checks_arguments_even_for_wrong_arity_noncallable_and_chained_callees test {
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store)
defer sema.deinit(&resolution) defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result) defer deinit(&result)
@@ -455,8 +455,8 @@ checks_arguments_even_for_wrong_arity_noncallable_and_chained_callees test {
} }
terminates_unanchored_cycles_and_keeps_grounded_cycles_typed test { terminates_unanchored_cycles_and_keeps_grounded_cycles_typed test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -474,10 +474,10 @@ terminates_unanchored_cycles_and_keeps_grounded_cycles_typed test {
`contextual_b :: contextual_a `contextual_b :: contextual_a
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store)
defer sema.deinit(&resolution) defer sema.deinit(&resolution)
try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
@@ -530,8 +530,8 @@ terminates_unanchored_cycles_and_keeps_grounded_cycles_typed test {
} }
preserves_existing_syntax_name_and_duplicate_ids_while_checking_siblings test { preserves_existing_syntax_name_and_duplicate_ids_while_checking_siblings test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -548,10 +548,10 @@ preserves_existing_syntax_name_and_duplicate_ids_while_checking_siblings test {
`same :: -"initializer" `same :: -"initializer"
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store)
defer sema.deinit(&resolution) defer sema.deinit(&resolution)
previous_count :: diagnostics.count(&diagnostic_store) previous_count :: diagnostics.count(&diagnostic_store)
function :: ast.package_decl(&tree, 1) function :: ast.package_decl(&tree, 1)
@@ -608,8 +608,8 @@ preserves_existing_syntax_name_and_duplicate_ids_while_checking_siblings test {
} }
returns_supply_numeric_context_and_resolve_forward_local_and_parameter_names test { returns_supply_numeric_context_and_resolve_forward_local_and_parameter_names test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -624,10 +624,10 @@ returns_supply_numeric_context_and_resolve_forward_local_and_parameter_names tes
`seed :: 3 `seed :: 3
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store)
defer sema.deinit(&resolution) defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result) defer deinit(&result)
@@ -666,8 +666,8 @@ returns_supply_numeric_context_and_resolve_forward_local_and_parameter_names tes
} }
return_errors_preserve_identity_check_unreachable_code_and_suppress_missing_return_cascades test { return_errors_preserve_identity_check_unreachable_code_and_suppress_missing_return_cascades test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -687,10 +687,10 @@ return_errors_preserve_identity_check_unreachable_code_and_suppress_missing_retu
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store)
defer sema.deinit(&resolution) defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result) defer deinit(&result)
@@ -733,8 +733,8 @@ return_errors_preserve_identity_check_unreachable_code_and_suppress_missing_retu
} }
checks_bool_comparisons_recursive_returns_and_nested_numeric_context test { checks_bool_comparisons_recursive_returns_and_nested_numeric_context test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -769,10 +769,10 @@ checks_bool_comparisons_recursive_returns_and_nested_numeric_context test {
`main proc() bool { return identity(flag) } `main proc() bool { return identity(flag) }
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store)
defer sema.deinit(&resolution) defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result) defer deinit(&result)
@@ -815,8 +815,8 @@ checks_bool_comparisons_recursive_returns_and_nested_numeric_context test {
} }
rejects_bool_numeric_mixing_and_preserves_invalid_condition_identity test { rejects_bool_numeric_mixing_and_preserves_invalid_condition_identity test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -835,10 +835,10 @@ rejects_bool_numeric_mixing_and_preserves_invalid_condition_identity test {
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store)
defer sema.deinit(&resolution) defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result) defer deinit(&result)
@@ -882,8 +882,8 @@ rejects_bool_numeric_mixing_and_preserves_invalid_condition_identity test {
} }
analyzes_return_paths_after_trap_propagation_without_poisoning_signatures test { analyzes_return_paths_after_trap_propagation_without_poisoning_signatures test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -918,10 +918,10 @@ analyzes_return_paths_after_trap_propagation_without_poisoning_signatures test {
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store)
defer sema.deinit(&resolution) defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result) defer deinit(&result)
@@ -970,8 +970,8 @@ analyzes_return_paths_after_trap_propagation_without_poisoning_signatures test {
} }
assignments_preserve_established_types_and_contextualize_numeric_values test { assignments_preserve_established_types_and_contextualize_numeric_values test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -996,10 +996,10 @@ assignments_preserve_established_types_and_contextualize_numeric_values test {
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store)
defer sema.deinit(&resolution) defer sema.deinit(&resolution)
try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
@@ -1065,8 +1065,8 @@ assignments_preserve_established_types_and_contextualize_numeric_values test {
} }
assignment_target_errors_preserve_bindings_and_independent_rhs_diagnostics test { assignment_target_errors_preserve_bindings_and_independent_rhs_diagnostics test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -1084,10 +1084,10 @@ assignment_target_errors_preserve_bindings_and_independent_rhs_diagnostics test
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store)
defer sema.deinit(&resolution) defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result) defer deinit(&result)
@@ -1124,8 +1124,8 @@ assignment_target_errors_preserve_bindings_and_independent_rhs_diagnostics test
} }
function_assignment_checks_complete_signatures_without_retargeting_calls test { function_assignment_checks_complete_signatures_without_retargeting_calls test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -1145,10 +1145,10 @@ function_assignment_checks_complete_signatures_without_retargeting_calls test {
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store)
defer sema.deinit(&resolution) defer sema.deinit(&resolution)
try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
@@ -1178,8 +1178,8 @@ function_assignment_checks_complete_signatures_without_retargeting_calls test {
} }
while_checks_nested_bodies_without_claiming_a_zero_iteration_return test { while_checks_nested_bodies_without_claiming_a_zero_iteration_return test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -1203,10 +1203,10 @@ while_checks_nested_bodies_without_claiming_a_zero_iteration_return test {
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store)
defer sema.deinit(&resolution) defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result) defer deinit(&result)
@@ -1251,8 +1251,8 @@ while_checks_nested_bodies_without_claiming_a_zero_iteration_return test {
} }
logical_types_preserve_comparison_context_and_assignment_isolation test { logical_types_preserve_comparison_context_and_assignment_isolation test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -1270,10 +1270,10 @@ logical_types_preserve_comparison_context_and_assignment_isolation test {
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store)
defer sema.deinit(&resolution) defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result) defer deinit(&result)
@@ -1316,8 +1316,8 @@ logical_types_preserve_comparison_context_and_assignment_isolation test {
} }
logical_operand_errors_are_independent_and_skipped_rhs_preserves_bool test { logical_operand_errors_are_independent_and_skipped_rhs_preserves_bool test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -1342,10 +1342,10 @@ logical_operand_errors_are_independent_and_skipped_rhs_preserves_bool test {
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store)
defer sema.deinit(&resolution) defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result) defer deinit(&result)
@@ -1412,8 +1412,8 @@ logical_operand_errors_are_independent_and_skipped_rhs_preserves_bool test {
} }
logical_external_mismatches_and_always_demanded_not_preserve_errors test { logical_external_mismatches_and_always_demanded_not_preserve_errors test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -1435,10 +1435,10 @@ logical_external_mismatches_and_always_demanded_not_preserve_errors test {
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store)
defer sema.deinit(&resolution) defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result) defer deinit(&result)
@@ -1491,8 +1491,8 @@ logical_external_mismatches_and_always_demanded_not_preserve_errors test {
} }
short_circuit_skip_paths_do_not_guarantee_a_return test { short_circuit_skip_paths_do_not_guarantee_a_return test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -1514,10 +1514,10 @@ short_circuit_skip_paths_do_not_guarantee_a_return test {
`take proc(value bool) void {} `take proc(value bool) void {}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store)
defer sema.deinit(&resolution) defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result) defer deinit(&result)
@@ -1550,8 +1550,8 @@ short_circuit_skip_paths_do_not_guarantee_a_return test {
} }
loop_controls_follow_lexical_bodies_and_check_unreachable_code test { loop_controls_follow_lexical_bodies_and_check_unreachable_code test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -1583,10 +1583,10 @@ loop_controls_follow_lexical_bodies_and_check_unreachable_code test {
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store)
defer sema.deinit(&resolution) defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result) defer deinit(&result)
@@ -1639,8 +1639,8 @@ loop_controls_follow_lexical_bodies_and_check_unreachable_code test {
} }
loop_controls_do_not_guarantee_function_returns test { loop_controls_do_not_guarantee_function_returns test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -1661,10 +1661,10 @@ loop_controls_do_not_guarantee_function_returns test {
`invalid proc() int { break } `invalid proc() int { break }
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store)
defer sema.deinit(&resolution) defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result) defer deinit(&result)
@@ -1694,8 +1694,8 @@ loop_controls_do_not_guarantee_function_returns test {
} }
range_bounds_receive_integer_context_without_joining_the_range_or_capture test { range_bounds_receive_integer_context_without_joining_the_range_or_capture test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -1714,10 +1714,10 @@ range_bounds_receive_integer_context_without_joining_the_range_or_capture test {
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store)
defer sema.deinit(&resolution) defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result) defer deinit(&result)
@@ -1753,8 +1753,8 @@ range_bounds_receive_integer_context_without_joining_the_range_or_capture test {
} }
range_checks_both_bounds_and_preserves_fixed_declarations_and_source_errors test { range_checks_both_bounds_and_preserves_fixed_declarations_and_source_errors test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -1776,10 +1776,10 @@ range_checks_both_bounds_and_preserves_fixed_declarations_and_source_errors test
`flexible := 3 `flexible := 3
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store)
defer sema.deinit(&resolution) defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result) defer deinit(&result)
@@ -1826,8 +1826,8 @@ range_checks_both_bounds_and_preserves_fixed_declarations_and_source_errors test
} }
range_values_nonrange_iterables_and_capture_modes_have_explicit_errors test { range_values_nonrange_iterables_and_capture_modes_have_explicit_errors test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -1851,10 +1851,10 @@ range_values_nonrange_iterables_and_capture_modes_have_explicit_errors test {
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store)
defer sema.deinit(&resolution) defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result) defer deinit(&result)
@@ -1901,8 +1901,8 @@ range_values_nonrange_iterables_and_capture_modes_have_explicit_errors test {
} }
range_captures_are_fixed_readonly_and_assignment_values_do_not_retarget_bounds test { range_captures_are_fixed_readonly_and_assignment_values_do_not_retarget_bounds test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -1927,10 +1927,10 @@ range_captures_are_fixed_readonly_and_assignment_values_do_not_retarget_bounds t
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store)
defer sema.deinit(&resolution) defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result) defer deinit(&result)
@@ -1986,8 +1986,8 @@ range_captures_are_fixed_readonly_and_assignment_values_do_not_retarget_bounds t
} }
range_loop_return_flow_and_controls_are_lexical_and_body_errors_are_isolated test { range_loop_return_flow_and_controls_are_lexical_and_body_errors_are_isolated test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -2025,10 +2025,10 @@ range_loop_return_flow_and_controls_are_lexical_and_body_errors_are_isolated tes
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store)
defer sema.deinit(&resolution) defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result) defer deinit(&result)
@@ -2080,8 +2080,8 @@ range_loop_return_flow_and_controls_are_lexical_and_body_errors_are_isolated tes
} }
invalid_range_capture_bindings_keep_resolver_diagnostic_ids_without_erasing_headers test { invalid_range_capture_bindings_keep_resolver_diagnostic_ids_without_erasing_headers test {
strpool.strings = strpool.init(mem.c_allocator) strings := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&strings)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -2094,10 +2094,10 @@ invalid_range_capture_bindings_keep_resolver_diagnostic_ids_without_erasing_head
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store)
defer sema.deinit(&resolution) defer sema.deinit(&resolution)
previous_count :: diagnostics.count(&diagnostic_store) previous_count :: diagnostics.count(&diagnostic_store)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
@@ -2132,6 +2132,7 @@ invalid_range_capture_bindings_keep_resolver_diagnostic_ids_without_erasing_head
@hide:file @hide:file
ArrayFixture :: struct { ArrayFixture :: struct {
strings strpool.StringPool
tree ast.Tree tree ast.Tree
resolution sema.Resolution resolution sema.Resolution
diagnostics diagnostics.Store diagnostics diagnostics.Store
@@ -2140,17 +2141,19 @@ ArrayFixture :: struct {
@hide:file @hide:file
array_fixture proc(source []u8) ArrayFixture ! (mem.AllocError | strpool.InternError | diagnostics.CapacityError | lexer.SourceError) { array_fixture proc(source []u8) ArrayFixture ! (mem.AllocError | strpool.InternError | diagnostics.CapacityError | lexer.SourceError) {
strings := strpool.init(mem.c_allocator)
errdefer strpool.deinit(&strings)
store := diagnostics.init(mem.c_allocator) store := diagnostics.init(mem.c_allocator)
errdefer diagnostics.deinit(&store) errdefer diagnostics.deinit(&store)
scan := lexer.init(mem.c_allocator) scan := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan) defer lexer.deinit(&scan)
try lexer.scan(&scan, source, &store) try lexer.scan(&scan, source, &strings, &store)
tree := try parser.parse(mem.c_allocator, scan.tokens.items, source, &store) tree := try parser.parse(mem.c_allocator, scan.tokens.items, source, &store)
errdefer ast.deinit(&tree) errdefer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &store) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &store)
errdefer sema.deinit(&resolution) errdefer sema.deinit(&resolution)
result :: try check(mem.c_allocator, &tree, &resolution, &store) result :: try check(mem.c_allocator, &tree, &resolution, &store)
return ArrayFixture{ tree = tree, resolution = resolution, diagnostics = store, result = result } return ArrayFixture{ strings = strings, tree = tree, resolution = resolution, diagnostics = store, result = result }
} }
@hide:file @hide:file
@@ -2159,6 +2162,7 @@ deinit_array_fixture proc(fixture @mut ArrayFixture) void {
sema.deinit(&fixture.resolution) sema.deinit(&fixture.resolution)
ast.deinit(&fixture.tree) ast.deinit(&fixture.tree)
diagnostics.deinit(&fixture.diagnostics) diagnostics.deinit(&fixture.diagnostics)
strpool.deinit(&fixture.strings)
} }
@hide:file @hide:file
@@ -2172,8 +2176,6 @@ expect_array proc(result @Result, id ast.NodeId, len usize, mutable bool, elemen
} }
fixed_arrays_contextualize_forward_aliases_counts_nested_literals_and_signatures test { fixed_arrays_contextualize_forward_aliases_counts_nested_literals_and_signatures test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
source :: source ::
`context [2]f64 :: alias `context [2]f64 :: alias
`alias :: seed `alias :: seed
@@ -2225,8 +2227,6 @@ fixed_arrays_contextualize_forward_aliases_counts_nested_literals_and_signatures
} }
nested_array_numeric_context_flows_through_index_children_before_defaults test { nested_array_numeric_context_flows_through_index_children_before_defaults test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
source :: source ::
`first float :: later[0][0] `first float :: later[0][0]
`later := [[1, 2], [3, 4]] `later := [[1, 2], [3, 4]]
@@ -2262,8 +2262,6 @@ nested_array_numeric_context_flows_through_index_children_before_defaults test {
} }
fixed_index_errors_are_occurrence_local_and_preserve_sources_and_siblings test { fixed_index_errors_are_occurrence_local_and_preserve_sources_and_siblings test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
source :: source ::
`fixed i64 :: 0 `fixed i64 :: 0
`inferred := 0 `inferred := 0
@@ -2298,8 +2296,6 @@ fixed_index_errors_are_occurrence_local_and_preserve_sources_and_siblings test {
} }
array_lengths_require_supported_constexpr_and_finalize_nested_inferred_counts test { array_lengths_require_supported_constexpr_and_finalize_nested_inferred_counts test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
source :: source ::
`two :: tail `two :: tail
`tail :: 2 `tail :: 2
@@ -2355,8 +2351,6 @@ array_lengths_require_supported_constexpr_and_finalize_nested_inferred_counts te
} }
indexed_writes_require_mutable_local_root_and_each_array_level_but_copies_are_values test { indexed_writes_require_mutable_local_root_and_each_array_level_but_copies_are_values test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
source :: source ::
`global [1]mut i64 := [1] `global [1]mut i64 := [1]
`main proc(parameter [1]mut i64) void { `main proc(parameter [1]mut i64) void {
@@ -2408,8 +2402,6 @@ indexed_writes_require_mutable_local_root_and_each_array_level_but_copies_are_va
} }
usize_arithmetic_ranges_and_readonly_captures_keep_the_bound_family test { usize_arithmetic_ranges_and_readonly_captures_keep_the_bound_family test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
source :: source ::
`limit usize :: 18446744073709551615 `limit usize :: 18446744073709551615
`arithmetic usize :: (limit - 2) * 1 + 1 `arithmetic usize :: (limit - 2) * 1 + 1
@@ -2470,8 +2462,6 @@ usize_arithmetic_ranges_and_readonly_captures_keep_the_bound_family test {
} }
array_assignment_rhs_is_contextual_but_does_not_retarget_established_sources test { array_assignment_rhs_is_contextual_but_does_not_retarget_established_sources test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
source :: source ::
`main proc() void { `main proc() void {
` target [2]mut f64 := [1, 2] ` target [2]mut f64 := [1, 2]
@@ -2513,8 +2503,6 @@ array_assignment_rhs_is_contextual_but_does_not_retarget_established_sources tes
} }
array_signature_identity_includes_recursive_counts_and_mutability test { array_signature_identity_includes_recursive_counts_and_mutability test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
source :: source ::
`accept proc(value [1]mut i64) [1]mut i64 { return value } `accept proc(value [1]mut i64) [1]mut i64 { return value }
`different_count proc(value [2]mut i64) [1]mut i64 { return [1] } `different_count proc(value [2]mut i64) [1]mut i64 { return [1] }
@@ -2559,8 +2547,6 @@ array_signature_identity_includes_recursive_counts_and_mutability test {
} }
inferred_counts_follow_forward_fixed_aliases_empty_shapes_and_reject_local_counts test { inferred_counts_follow_forward_fixed_aliases_empty_shapes_and_reject_local_counts test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
source :: source ::
`forward [_]i64 :: later `forward [_]i64 :: later
`later [_]i64 :: [1, 2] `later [_]i64 :: [1, 2]
+64 -27
View File
@@ -1,7 +1,7 @@
import "@std/debug" import "@std/debug"
import "@std/mem" import "@std/mem"
import "@std/hashmap" import "@std/hashmap"
import "@std/strmap" import "@source/strpool"
import "@source/ast" import "@source/ast"
import "@source/diagnostics" import "@source/diagnostics"
@@ -14,16 +14,33 @@ BuiltinType :: enum {
} }
@hide:file @hide:file
builtins strmap.StaticStringMap(BuiltinType) :: strmap.init([ Builtin :: struct {
{ "int", .int }, name []u8
{ "float", .float }, type BuiltinType
{ "bool", .bool }, }
{ "string", .string },
{ "void", .void }, @hide:file
{ "i64", .i64 }, builtins [_]Builtin :: [
{ "f64", .f64 }, { name = "int", type = .int },
{ "usize", .usize }, { name = "float", type = .float },
]) { name = "bool", type = .bool },
{ name = "string", type = .string },
{ name = "void", type = .void },
{ name = "i64", type = .i64 },
{ name = "f64", type = .f64 },
{ name = "usize", type = .usize },
]
@hide:file
intern_builtins proc(
$N usize,
pool @mut strpool.StringPool,
entries @[N]Builtin,
) [N]mut strpool.StringId ! (mem.AllocError | strpool.InternError) {
ids [N]mut strpool.StringId := undefined
for (entries) |builtin, index| ids[index] = try strpool.intern(pool, builtin.name)
return ids
}
BindingKind :: enum { none, declaration, builtin, invalid } BindingKind :: enum { none, declaration, builtin, invalid }
@@ -50,7 +67,7 @@ Resolution :: struct { storage Storage }
@hide:file @hide:file
Scope :: struct { Scope :: struct {
names hashmap.StringHashMap(Binding) names hashmap.AutoHashMap(strpool.StringId, Binding)
parent ?@Scope parent ?@Scope
} }
@@ -59,29 +76,36 @@ State :: struct {
tree @ast.Tree tree @ast.Tree
diagnostics @mut diagnostics.Store diagnostics @mut diagnostics.Store
bindings []mut Binding bindings []mut Binding
builtin_ids []strpool.StringId
allocator mem.Allocator allocator mem.Allocator
} }
#! borrows `tree` and `diagnostic_store` during resolution, without retaining their storage. #! borrows `tree`, its identifier `pool`, and `diagnostic_store` during resolution.
#! `allocator` supplies caller-owned bindings and temporary scopes released before return. #! `allocator` supplies caller-owned bindings and temporary scopes released before return.
#! release the returned Resolution with deinit, which uses the retained allocator. #! release the returned Resolution with deinit, which uses the retained allocator.
resolve proc( resolve proc(
allocator mem.Allocator, allocator mem.Allocator,
tree @ast.Tree, tree @ast.Tree,
pool @mut strpool.StringPool,
diagnostic_store @mut diagnostics.Store, diagnostic_store @mut diagnostics.Store,
) Resolution ! (mem.AllocError | diagnostics.CapacityError) { ) Resolution ! (mem.AllocError | strpool.InternError | diagnostics.CapacityError) {
bindings :: try mem.alloc(Binding, allocator, ast.node_count(tree)) bindings :: try mem.alloc(Binding, allocator, ast.node_count(tree))
errdefer mem.free(allocator, bindings) errdefer mem.free(allocator, bindings)
mem.set(bindings, Binding{}) mem.set(bindings, Binding{})
builtin_ids :: try intern_builtins(pool, &builtins)
state := State{ state := State{
tree = tree, tree = tree,
diagnostics = diagnostic_store, diagnostics = diagnostic_store,
bindings = bindings, bindings = bindings,
builtin_ids = builtin_ids[..],
allocator = allocator, allocator = allocator,
} }
package_scope := Scope{ names = hashmap.init(allocator), parent = null } package_scope := Scope{
names = hashmap.init(allocator),
parent = null,
}
defer hashmap.deinit(&package_scope.names) defer hashmap.deinit(&package_scope.names)
for (0..ast.package_decl_count(tree)) |i| { for (0..ast.package_decl_count(tree)) |i| {
id :: ast.package_decl(tree, i) id :: ast.package_decl(tree, i)
@@ -115,19 +139,22 @@ binding proc(resolution @Resolution, id ast.NodeId) Binding {
} }
@hide:file @hide:file
lookup proc(scope ?@Scope, name []u8) ?Binding { lookup proc(state @State, scope ?@Scope, name strpool.StringId) ?Binding {
if scope |current| { if scope |current| {
if hashmap.get(&current.names, name) |found| return found if hashmap.get(&current.names, name) |found| return found
return lookup(current.parent, name) return lookup(state, current.parent, name)
} }
if strmap.get(&builtins, name) |type| { for (state.builtin_ids) |id, index| {
return Binding{ kind = .builtin, data = BindingData{ builtin = type } } if (id == name) return Binding{
kind = .builtin,
data = BindingData{ builtin = builtins[index].type },
}
} }
return null return null
} }
@hide:file @hide:file
put_name proc(scope @mut Scope, name []u8, value Binding) void ! mem.AllocError { put_name proc(scope @mut Scope, name strpool.StringId, value Binding) void ! mem.AllocError {
hashmap.put(&scope.names, name, value) catch |err| { hashmap.put(&scope.names, name, value) catch |err| {
match err { match err {
.key_exists: unreachable .key_exists: unreachable
@@ -143,7 +170,7 @@ predeclare proc(
id ast.NodeId, id ast.NodeId,
name_span ast.Span, name_span ast.Span,
) void ! (mem.AllocError | diagnostics.CapacityError) { ) void ! (mem.AllocError | diagnostics.CapacityError) {
name :: ast.source_text(state.tree, name_span) name :: ast.name_id(state.tree, id)
if hashmap.get(&scope.names, name) |_| { if hashmap.get(&scope.names, name) |_| {
diagnostic :: try diagnostics.add(state.diagnostics, .duplicate_name, name_span) diagnostic :: try diagnostics.add(state.diagnostics, .duplicate_name, name_span)
state.bindings[usize(id)] = Binding{ state.bindings[usize(id)] = Binding{
@@ -154,7 +181,7 @@ predeclare proc(
} }
value := Binding{ kind = .declaration, data = BindingData{ declaration = id } } value := Binding{ kind = .declaration, data = BindingData{ declaration = id } }
if lookup(scope.parent, name) |_| { if lookup(state, scope.parent, name) |_| {
diagnostic :: try diagnostics.add(state.diagnostics, .duplicate_name, name_span) diagnostic :: try diagnostics.add(state.diagnostics, .duplicate_name, name_span)
# poison the inner name rather than silently binding uses to its ancestor. # poison the inner name rather than silently binding uses to its ancestor.
value = Binding{ kind = .invalid, data = BindingData{ diagnostic = diagnostic } } value = Binding{ kind = .invalid, data = BindingData{ diagnostic = diagnostic } }
@@ -178,7 +205,10 @@ resolve_function proc(
try resolve_expr(state, package_scope, ast.param(tree, parameter).type) try resolve_expr(state, package_scope, ast.param(tree, parameter).type)
} }
scope := Scope{ names = hashmap.init(state.allocator), parent = package_scope } scope := Scope{
names = hashmap.init(state.allocator),
parent = package_scope,
}
defer hashmap.deinit(&scope.names) defer hashmap.deinit(&scope.names)
for (0..ast.function_param_count(tree, id)) |i| { for (0..ast.function_param_count(tree, id)) |i| {
parameter :: ast.function_param(tree, id, i) parameter :: ast.function_param(tree, id, i)
@@ -189,7 +219,10 @@ resolve_function proc(
@hide:file @hide:file
resolve_arm proc(state @mut State, parent @Scope, id ast.NodeId) void ! (mem.AllocError | diagnostics.CapacityError) { resolve_arm proc(state @mut State, parent @Scope, id ast.NodeId) void ! (mem.AllocError | diagnostics.CapacityError) {
scope := Scope{ names = hashmap.init(state.allocator), parent = parent } scope := Scope{
names = hashmap.init(state.allocator),
parent = parent,
}
defer hashmap.deinit(&scope.names) defer hashmap.deinit(&scope.names)
try resolve_block(state, &scope, id) try resolve_block(state, &scope, id)
} }
@@ -231,7 +264,10 @@ resolve_block proc(state @mut State, scope @mut Scope, id ast.NodeId) void ! (me
.for_stmt: { .for_stmt: {
loop :: ast.for_stmt(tree, statement) loop :: ast.for_stmt(tree, statement)
try resolve_expr(state, scope, loop.iterable) try resolve_expr(state, scope, loop.iterable)
body_scope := Scope{ names = hashmap.init(state.allocator), parent = scope } body_scope := Scope{
names = hashmap.init(state.allocator),
parent = scope,
}
defer hashmap.deinit(&body_scope.names) defer hashmap.deinit(&body_scope.names)
if (loop.capture) |capture| { if (loop.capture) |capture| {
try predeclare(state, &body_scope, capture, ast.node_span(tree, capture)) try predeclare(state, &body_scope, capture, ast.node_span(tree, capture))
@@ -279,8 +315,8 @@ resolve_expr proc(
.literal_int, .literal_float, .literal_bool, .literal_string: {} .literal_int, .literal_float, .literal_bool, .literal_string: {}
.identifier: { .identifier: {
span :: ast.node_span(tree, id) span :: ast.node_span(tree, id)
name :: ast.source_text(tree, span) name :: ast.name_id(tree, id)
if lookup(scope, name) |found| { if lookup(state, scope, name) |found| {
state.bindings[usize(id)] = found state.bindings[usize(id)] = found
} else { } else {
diagnostic :: try diagnostics.add(state.diagnostics, .unknown_name, span) diagnostic :: try diagnostics.add(state.diagnostics, .unknown_name, span)
@@ -325,3 +361,4 @@ resolve_expr proc(
.invalid: resolve_invalid(state, id) .invalid: resolve_invalid(state, id)
} }
} }
+98 -44
View File
@@ -8,8 +8,8 @@ import "@source/parser"
import "@source/ast" import "@source/ast"
resolves_forward_references_recursion_and_builtin_signatures test { resolves_forward_references_recursion_and_builtin_signatures test {
strpool.strings = strpool.init(mem.c_allocator) pool := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&pool)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -25,10 +25,10 @@ resolves_forward_references_recursion_and_builtin_signatures test {
`later int :: 1 `later int :: 1
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &pool, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
resolution := try resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try resolve(mem.c_allocator, &tree, &pool, &diagnostic_store)
defer deinit(&resolution) defer deinit(&resolution)
first :: ast.package_decl(&tree, 0) first :: ast.package_decl(&tree, 0)
@@ -110,8 +110,8 @@ resolves_forward_references_recursion_and_builtin_signatures test {
} }
rejects_shadowing_and_duplicates_without_cascades test { rejects_shadowing_and_duplicates_without_cascades test {
strpool.strings = strpool.init(mem.c_allocator) pool := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&pool)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -132,11 +132,11 @@ rejects_shadowing_and_duplicates_without_cascades test {
`same :: same `same :: same
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &pool, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
resolution := try resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try resolve(mem.c_allocator, &tree, &pool, &diagnostic_store)
defer deinit(&resolution) defer deinit(&resolution)
function :: ast.package_decl(&tree, 0) function :: ast.package_decl(&tree, 0)
@@ -228,8 +228,8 @@ rejects_shadowing_and_duplicates_without_cascades test {
} }
preserves_syntax_diagnostics_and_resolves_shared_unknown_types_once test { preserves_syntax_diagnostics_and_resolves_shared_unknown_types_once test {
strpool.strings = strpool.init(mem.c_allocator) pool := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&pool)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -243,7 +243,7 @@ preserves_syntax_diagnostics_and_resolves_shared_unknown_types_once test {
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &pool, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
syntax_count :: diagnostics.count(&diagnostic_store) syntax_count :: diagnostics.count(&diagnostic_store)
@@ -255,7 +255,7 @@ preserves_syntax_diagnostics_and_resolves_shared_unknown_types_once test {
try testing.expect_equal(ast.ExprKind.invalid, ast.expr_kind(&tree, empty)) try testing.expect_equal(ast.ExprKind.invalid, ast.expr_kind(&tree, empty))
invalid_statement :: ast.block_statement(&tree, view.body, 4) invalid_statement :: ast.block_statement(&tree, view.body, 4)
try testing.expect_equal(ast.StmtKind.invalid, ast.stmt_kind(&tree, invalid_statement)) try testing.expect_equal(ast.StmtKind.invalid, ast.stmt_kind(&tree, invalid_statement))
resolution := try resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try resolve(mem.c_allocator, &tree, &pool, &diagnostic_store)
defer deinit(&resolution) defer deinit(&resolution)
invalid_nodes [3]ast.NodeId :: [invalid_decl, empty, invalid_statement] invalid_nodes [3]ast.NodeId :: [invalid_decl, empty, invalid_statement]
@@ -300,8 +300,8 @@ preserves_syntax_diagnostics_and_resolves_shared_unknown_types_once test {
} }
resolves_conditional_arm_scopes_without_leaks_or_shadowing test { resolves_conditional_arm_scopes_without_leaks_or_shadowing test {
strpool.strings = strpool.init(mem.c_allocator) pool := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&pool)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -323,11 +323,11 @@ resolves_conditional_arm_scopes_without_leaks_or_shadowing test {
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &pool, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
resolution := try resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try resolve(mem.c_allocator, &tree, &pool, &diagnostic_store)
defer deinit(&resolution) defer deinit(&resolution)
function :: ast.package_decl(&tree, 0) function :: ast.package_decl(&tree, 0)
@@ -368,8 +368,8 @@ resolves_conditional_arm_scopes_without_leaks_or_shadowing test {
} }
resolves_loop_assignments_in_lexical_scopes_with_forward_local_bindings test { resolves_loop_assignments_in_lexical_scopes_with_forward_local_bindings test {
strpool.strings = strpool.init(mem.c_allocator) pool := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&pool)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -394,11 +394,11 @@ resolves_loop_assignments_in_lexical_scopes_with_forward_local_bindings test {
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &pool, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
resolution := try resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try resolve(mem.c_allocator, &tree, &pool, &diagnostic_store)
defer deinit(&resolution) defer deinit(&resolution)
function :: ast.package_decl(&tree, 0) function :: ast.package_decl(&tree, 0)
@@ -441,8 +441,8 @@ resolves_loop_assignments_in_lexical_scopes_with_forward_local_bindings test {
} }
resolves_all_code_and_preserves_scopes_after_loop_controls test { resolves_all_code_and_preserves_scopes_after_loop_controls test {
strpool.strings = strpool.init(mem.c_allocator) pool := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&pool)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -464,11 +464,11 @@ resolves_all_code_and_preserves_scopes_after_loop_controls test {
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &pool, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
resolution := try resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try resolve(mem.c_allocator, &tree, &pool, &diagnostic_store)
defer deinit(&resolution) defer deinit(&resolution)
try testing.expect_equal(4, diagnostics.count(&diagnostic_store)) try testing.expect_equal(4, diagnostics.count(&diagnostic_store))
body :: ast.function_decl(&tree, ast.package_decl(&tree, 0)).body body :: ast.function_decl(&tree, ast.package_decl(&tree, 0)).body
@@ -487,8 +487,8 @@ resolves_all_code_and_preserves_scopes_after_loop_controls test {
} }
range_bounds_use_outer_scope_and_captures_are_body_declarations test { range_bounds_use_outer_scope_and_captures_are_body_declarations test {
strpool.strings = strpool.init(mem.c_allocator) pool := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&pool)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -506,10 +506,10 @@ range_bounds_use_outer_scope_and_captures_are_body_declarations test {
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &pool, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
resolution := try resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try resolve(mem.c_allocator, &tree, &pool, &diagnostic_store)
defer deinit(&resolution) defer deinit(&resolution)
body :: ast.function_decl(&tree, ast.package_decl(&tree, 0)).body body :: ast.function_decl(&tree, ast.package_decl(&tree, 0)).body
loop :: ast.for_stmt(&tree, ast.block_statement(&tree, body, 0)) loop :: ast.for_stmt(&tree, ast.block_statement(&tree, body, 0))
@@ -540,8 +540,8 @@ range_bounds_use_outer_scope_and_captures_are_body_declarations test {
} }
range_captures_preserve_duplicate_builtin_and_discard_errors test { range_captures_preserve_duplicate_builtin_and_discard_errors test {
strpool.strings = strpool.init(mem.c_allocator) pool := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&pool)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -563,10 +563,10 @@ range_captures_preserve_duplicate_builtin_and_discard_errors test {
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &pool, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
resolution := try resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try resolve(mem.c_allocator, &tree, &pool, &diagnostic_store)
defer deinit(&resolution) defer deinit(&resolution)
body :: ast.function_decl(&tree, ast.package_decl(&tree, 0)).body body :: ast.function_decl(&tree, ast.package_decl(&tree, 0)).body
for (usize(1)..usize(3)) |index| { for (usize(1)..usize(3)) |index| {
@@ -605,8 +605,8 @@ range_captures_preserve_duplicate_builtin_and_discard_errors test {
} }
resolves_array_counts_elements_indices_and_unreachable_targets test { resolves_array_counts_elements_indices_and_unreachable_targets test {
strpool.strings = strpool.init(mem.c_allocator) pool := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&pool)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -621,10 +621,10 @@ resolves_array_counts_elements_indices_and_unreachable_targets test {
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &pool, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
resolution := try resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try resolve(mem.c_allocator, &tree, &pool, &diagnostic_store)
defer deinit(&resolution) defer deinit(&resolution)
try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
size :: ast.package_decl(&tree, 0) size :: ast.package_decl(&tree, 0)
@@ -669,8 +669,8 @@ resolves_array_counts_elements_indices_and_unreachable_targets test {
} }
array_signature_counts_use_package_scope_and_local_counts_see_parameters test { array_signature_counts_use_package_scope_and_local_counts_see_parameters test {
strpool.strings = strpool.init(mem.c_allocator) pool := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&pool)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -682,11 +682,11 @@ array_signature_counts_use_package_scope_and_local_counts_see_parameters test {
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &pool, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
resolution := try resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try resolve(mem.c_allocator, &tree, &pool, &diagnostic_store)
defer deinit(&resolution) defer deinit(&resolution)
try testing.expect_equal(2, diagnostics.count(&diagnostic_store)) try testing.expect_equal(2, diagnostics.count(&diagnostic_store))
function :: ast.package_decl(&tree, 1) function :: ast.package_decl(&tree, 1)
@@ -710,8 +710,8 @@ array_signature_counts_use_package_scope_and_local_counts_see_parameters test {
} }
resolves_every_array_child_error_and_preserves_lexer_diagnostics test { resolves_every_array_child_error_and_preserves_lexer_diagnostics test {
strpool.strings = strpool.init(mem.c_allocator) pool := strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings) defer strpool.deinit(&pool)
diagnostic_store := diagnostics.init(mem.c_allocator) diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store) defer diagnostics.deinit(&diagnostic_store)
source :: source ::
@@ -723,11 +723,11 @@ resolves_every_array_child_error_and_preserves_lexer_diagnostics test {
`} `}
scan_state := lexer.init(mem.c_allocator) scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state) defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store) try lexer.scan(&scan_state, source, &pool, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree) defer ast.deinit(&tree)
try testing.expect_equal(2, diagnostics.count(&diagnostic_store)) try testing.expect_equal(2, diagnostics.count(&diagnostic_store))
resolution := try resolve(mem.c_allocator, &tree, &diagnostic_store) resolution := try resolve(mem.c_allocator, &tree, &pool, &diagnostic_store)
defer deinit(&resolution) defer deinit(&resolution)
try testing.expect_equal(10, diagnostics.count(&diagnostic_store)) try testing.expect_equal(10, diagnostics.count(&diagnostic_store))
body :: ast.function_decl(&tree, ast.package_decl(&tree, 0)).body body :: ast.function_decl(&tree, ast.package_decl(&tree, 0)).body
@@ -755,3 +755,57 @@ resolves_every_array_child_error_and_preserves_lexer_diagnostics test {
try testing.expect_equal(diagnostics.DiagnosticId(0), binding(&resolution, malformed_count).data.diagnostic) try testing.expect_equal(diagnostics.DiagnosticId(0), binding(&resolution, malformed_count).data.diagnostic)
try testing.expect_equal(diagnostics.DiagnosticId(1), binding(&resolution, malformed_element).data.diagnostic) try testing.expect_equal(diagnostics.DiagnosticId(1), binding(&resolution, malformed_element).data.diagnostic)
} }
resolves_interleaved_compilations_with_independent_identifier_orders test {
first_pool := strpool.init(mem.c_allocator)
defer strpool.deinit(&first_pool)
second_pool := strpool.init(mem.c_allocator)
defer strpool.deinit(&second_pool)
first_diagnostics := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&first_diagnostics)
second_diagnostics := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&second_diagnostics)
first_source :: "alpha :: beta\nbeta i64 :: 1\nmain proc() bool { return true }\n"
second_source :: "beta :: alpha\nalpha bool :: false\nmain proc() i64 { return 1 }\n"
first_scan := lexer.init(mem.c_allocator)
defer lexer.deinit(&first_scan)
second_scan := lexer.init(mem.c_allocator)
defer lexer.deinit(&second_scan)
try lexer.scan(&first_scan, first_source, &first_pool, &first_diagnostics)
first_tree := try parser.parse(mem.c_allocator, first_scan.tokens.items, first_source, &first_diagnostics)
defer ast.deinit(&first_tree)
try lexer.scan(&second_scan, second_source, &second_pool, &second_diagnostics)
second_tree := try parser.parse(mem.c_allocator, second_scan.tokens.items, second_source, &second_diagnostics)
defer ast.deinit(&second_tree)
first_name :: ast.name_id(&first_tree, ast.package_decl(&first_tree, 0))
second_name :: ast.name_id(&second_tree, ast.package_decl(&second_tree, 0))
try testing.expect_equal(first_name, second_name)
try testing.expect_equal("alpha", strpool.get_str(&first_pool, first_name)?)
try testing.expect_equal("beta", strpool.get_str(&second_pool, second_name)?)
first_resolution := try resolve(mem.c_allocator, &first_tree, &first_pool, &first_diagnostics)
defer deinit(&first_resolution)
second_resolution := try resolve(mem.c_allocator, &second_tree, &second_pool, &second_diagnostics)
defer deinit(&second_resolution)
first_decl :: ast.package_decl(&first_tree, 0)
second_decl :: ast.package_decl(&second_tree, 0)
first_target :: ast.package_decl(&first_tree, 1)
second_target :: ast.package_decl(&second_tree, 1)
first_use :: ast.statement_decl(&first_tree, first_decl).initializer
second_use :: ast.statement_decl(&second_tree, second_decl).initializer
try testing.expect_equal(BindingKind.declaration, binding(&first_resolution, first_use).kind)
try testing.expect_equal(first_target, binding(&first_resolution, first_use).data.declaration)
try testing.expect_equal(BindingKind.declaration, binding(&second_resolution, second_use).kind)
try testing.expect_equal(second_target, binding(&second_resolution, second_use).data.declaration)
first_type :: ast.statement_decl(&first_tree, first_target).type?
second_type :: ast.statement_decl(&second_tree, second_target).type?
try testing.expect_equal(BindingKind.builtin, binding(&first_resolution, first_type).kind)
try testing.expect_equal(BuiltinType.i64, binding(&first_resolution, first_type).data.builtin)
try testing.expect_equal(BindingKind.builtin, binding(&second_resolution, second_type).kind)
try testing.expect_equal(BuiltinType.bool, binding(&second_resolution, second_type).data.builtin)
first_return :: ast.function_decl(&first_tree, ast.package_decl(&first_tree, 2)).return_type
second_return :: ast.function_decl(&second_tree, ast.package_decl(&second_tree, 2)).return_type
try testing.expect_equal(BuiltinType.bool, binding(&first_resolution, first_return).data.builtin)
try testing.expect_equal(BuiltinType.i64, binding(&second_resolution, second_return).data.builtin)
try testing.expect_equal(usize(0), diagnostics.count(&first_diagnostics))
try testing.expect_equal(usize(0), diagnostics.count(&second_diagnostics))
}
+1 -3
View File
@@ -4,13 +4,11 @@ import "@std/hashmap"
InternError :: enum { out_of_space } InternError :: enum { out_of_space }
#! identifies a string only within its owning pool.
StringId :: alias u32 StringId :: alias u32
no_str :: maxval!(StringId) no_str :: maxval!(StringId)
#! global pool owned by main.hon; initialize before scanning and release with deinit.
strings StringPool := undefined
#! retains its allocator and owns interned copies; release all storage with deinit. #! retains its allocator and owns interned copies; release all storage with deinit.
StringPool :: struct { StringPool :: struct {
ids hashmap.StringHashMap(StringId) # string → id ids hashmap.StringHashMap(StringId) # string → id
+16
View File
@@ -22,3 +22,19 @@ handles_interning test {
try testing.expect_equal(null, invalid_str) try testing.expect_equal(null, invalid_str)
try testing.expect_equal("hello", hello_str?) try testing.expect_equal("hello", hello_str?)
} }
keeps_independent_pool_ids_local test {
first := init(mem.c_allocator)
defer deinit(&first)
second := init(mem.c_allocator)
defer deinit(&second)
hello :: try intern(&first, "hello")
world :: try intern(&second, "world")
try testing.expect_equal(hello, world)
try testing.expect_equal("hello", get_str(&first, hello)?)
try testing.expect_equal("world", get_str(&second, world)?)
different :: try intern(&first, "world")
try testing.expect(different != hello)
try testing.expect_equal(hello, try intern(&first, "hello"))
try testing.expect_equal(world, try intern(&second, "world"))
}
+112 -16
View File
@@ -1,4 +1,5 @@
import "@std/mem" import "@std/mem"
import "@std/meta"
PutError :: enum { key_exists } PutError :: enum { key_exists }
@@ -13,10 +14,11 @@ Entry proc($K, $V type) type {
#! owns entry storage and retains its allocator; release storage with deinit. #! owns entry storage and retains its allocator; release storage with deinit.
#! keys and values are stored by value; any resources they reference remain caller-owned. #! keys and values are stored by value; any resources they reference remain caller-owned.
#! null callbacks select automatic operations; equal keys must always produce equal hashes.
HashMap proc( HashMap proc(
$K, $V type, $K, $V type,
$hash_key proc(key K) usize, $hash_key ?proc(key K) usize,
$keys_eql proc(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)
@@ -29,11 +31,18 @@ StringHashMap proc($V type) type {
return HashMap([]u8, V, str_hash, str_eql) return HashMap([]u8, V, str_hash, str_eql)
} }
#! automatic value hashing for integers, booleans, enums, distinct types, arrays and records.
#! slices and floating-point keys require explicit hash/equality operations through HashMap.
#! record padding is ignored; pointer, union and optional keys are not supported.
AutoHashMap proc($K, $V type) type {
return HashMap(K, V, null, null)
}
#! retain `allocator` for later growth and cleanup; this call does not allocate. #! retain `allocator` for later growth and cleanup; this call does not allocate.
init proc( init proc(
$K, $V type, $K, $V type,
$hash_key proc(key K) usize, $hash_key ?proc(key K) usize,
$keys_eql proc(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){
@@ -47,29 +56,38 @@ init proc(
#! note: this operation invalidates the map. #! note: this operation invalidates the map.
deinit proc( deinit proc(
$K, $V type, $K, $V type,
$hash_key proc(key K) usize, $hash_key ?proc(key K) usize,
$keys_eql proc(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 proc( get proc(
$K, $V type, $K, $V type,
$hash_key proc(key K) usize, $hash_key ?proc(key K) usize,
$keys_eql proc(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 {
if (map.count == 0) return null if (map.count == 0) return null
hash :: normalize(hash_key(key)) hash usize := 0
if $(hash_key == null) {
hash = normalize(auto_hash(K, key))
} else {
hash = normalize(call_hash(K, hash_key?, key))
}
idx := hash & (map.entries.len - 1) idx := hash & (map.entries.len - 1)
while true { while true {
entry :: map.entries[idx] entry :: map.entries[idx]
if (entry.hash == 0) return null if (entry.hash == 0) return null
if (entry.hash == hash and keys_eql(entry.key, key)) { if (entry.hash == hash) {
return entry.value if $(keys_eql == null) {
if (auto_eql(K, entry.key, key)) return entry.value
} else {
if (call_eql(K, keys_eql?, entry.key, key)) return entry.value
}
} }
idx = (idx + 1) & (map.entries.len - 1) idx = (idx + 1) & (map.entries.len - 1)
@@ -78,8 +96,8 @@ get proc(
put proc( put proc(
$K, $V type, $K, $V type,
$hash_key proc(key K) usize, $hash_key ?proc(key K) usize,
$keys_eql proc(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,
@@ -107,7 +125,12 @@ put proc(
mem.free(map.allocator, old_entries) mem.free(map.allocator, old_entries)
} }
hash :: normalize(hash_key(key)) hash usize := 0
if $(hash_key == null) {
hash = normalize(auto_hash(K, key))
} else {
hash = normalize(call_hash(K, hash_key?, key))
}
idx := hash & (map.entries.len - 1) idx := hash & (map.entries.len - 1)
while true { while true {
@@ -122,14 +145,29 @@ put proc(
return return
} }
if entry.hash == hash and keys_eql(entry.key, key) { if (entry.hash == hash) {
return .key_exists if $(keys_eql == null) {
if (auto_eql(K, entry.key, key)) return .key_exists
} else {
if (call_eql(K, keys_eql?, entry.key, key)) return .key_exists
}
} }
idx = (idx + 1) & (map.entries.len - 1) idx = (idx + 1) & (map.entries.len - 1)
} }
} }
# concrete comptime callbacks use named calls so their specializations are registered.
@hide:file
call_hash proc($K type, $callback proc(key K) usize, key K) usize {
return callback(key)
}
@hide:file
call_eql proc($K type, $callback proc(a, b K) bool, a, b K) bool {
return callback(a, b)
}
@hide:file @hide:file
normalize proc(hash usize) usize { 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
@@ -155,3 +193,61 @@ str_hash proc(key []u8) usize {
@hide:file @hide:file
str_eql proc(a, b []u8) bool { return mem.eql(a, b) } str_eql proc(a, b []u8) bool { return mem.eql(a, b) }
@hide:file
auto_hash proc($K type, key K) usize {
match typeinfo!(K) {
.integer: {
bytes @[sizeof!(K)]u8 :: ptrcast!(&key)
return str_hash(bytes[..])
}
.bool: return if (key) 1 else 0
.enum |info|: {
inline for (info.fields) |field| {
if (key == field!(K, field.name)) return field.index
}
unreachable
}
.distinct |backing|: return auto_hash(ptrcast!(@backing, &key)^)
.array: {
hash usize := 0
for (key) |element| hash = combine_hash(hash, auto_hash(element))
return hash
}
.record |info|: {
hash usize := 0
inline for (info.fields) |field| {
hash = combine_hash(hash, auto_hash(field!(key, field.name)))
}
return hash
}
else: compile_error!("AutoHashMap key requires integers, booleans, enums, distinct types, arrays or records; use HashMap for custom hashing")
}
}
@hide:file
combine_hash proc(left, right usize) usize {
# rotate before mixing so ordered fields do not cancel like a plain xor.
return ((left << 5) xor (left >> (sizeof!(usize) * 8 - 5))) xor right
}
@hide:file
auto_eql proc($K type, left, right K) bool {
match typeinfo!(K) {
.integer, .bool, .enum: return left == right
.distinct |backing|: return auto_eql(ptrcast!(@backing, &left)^, ptrcast!(@backing, &right)^)
.array: {
for (left) |element, index| {
if (!auto_eql(element, right[index])) return false
}
return true
}
.record |info|: {
inline for (info.fields) |field| {
if (!auto_eql(field!(left, field.name), field!(right, field.name))) return false
}
return true
}
else: compile_error!("AutoHashMap key requires integers, booleans, enums, distinct types, arrays or records; use HashMap for custom equality")
}
}
+75
View File
@@ -11,3 +11,78 @@ handles_put_and_get test {
try testing.expect_type(?u32, value) try testing.expect_type(?u32, value)
try testing.expect_equal(42, value?) try testing.expect_equal(42, value?)
} }
@hide:file
collision_hash proc(_ u32) usize { return 0 }
@hide:file
collision_eql proc(left, right u32) bool {
return rem!(left, 100) == rem!(right, 100)
}
custom_callbacks_preserve_collisions_and_equivalent_keys test {
map HashMap(u32, u32, collision_hash, collision_eql) := init(mem.c_allocator)
defer deinit(&map)
for (u32(1)..u32(32)) |key| try put(&map, key, key * 2)
for (u32(1)..u32(32)) |key| {
try testing.expect_equal(key * 2, get(&map, key + 100)?)
}
try testing.expect_equal(null, get(&map, 99))
duplicate := false
put(&map, 101, 999) catch |error| { duplicate = error == .key_exists }
try testing.expect(duplicate)
try testing.expect_equal(u32(31), u32(map.count))
try testing.expect_equal(u32(2), get(&map, 1)?)
}
auto_integer_keys_preserve_boundaries_and_rehashing test {
map AutoHashMap(i64, usize) := init(mem.c_allocator)
defer deinit(&map)
keys [_]i64 :: [minval!(i64), -1, 0, 1, maxval!(i64)]
for (keys) |key, index| try put(&map, key, index)
for (i64(2)..i64(100)) |key| try put(&map, key, usize(key))
for (keys) |key, index| try testing.expect_equal(index, get(&map, key)?)
for (i64(2)..i64(100)) |key| try testing.expect_equal(usize(key), get(&map, key)?)
try testing.expect_equal(null, get(&map, -2))
duplicate := false
put(&map, -1, 999) catch |error| { duplicate = error == .key_exists }
try testing.expect(duplicate)
try testing.expect_equal(usize(1), get(&map, -1)?)
wide AutoHashMap(u64, bool) := init(mem.c_allocator)
defer deinit(&wide)
try put(&wide, maxval!(u64), true)
try testing.expect_equal(true, get(&wide, maxval!(u64))?)
}
@hide:file
TestKeyKind :: enum(u8) { first = 3, second = 91 }
@hide:file
TestKeyId :: distinct u32
@hide:file
TestKey :: struct {
enabled bool
id TestKeyId
kind TestKeyKind
indices [2]u16
}
auto_record_keys_use_field_values_and_array_order test {
map AutoHashMap(TestKey, i32) := init(mem.c_allocator)
defer deinit(&map)
first :: TestKey{ enabled = true, id = TestKeyId(17), kind = .first, indices = [2, 9] }
reversed :: TestKey{ enabled = true, id = TestKeyId(17), kind = .first, indices = [9, 2] }
second :: TestKey{ enabled = false, id = TestKeyId(18), kind = .second, indices = [2, 9] }
try put(&map, first, 42)
try put(&map, reversed, 51)
try put(&map, second, 63)
try testing.expect_equal(i32(42), get(&map, TestKey{
enabled = true, id = TestKeyId(17), kind = .first, indices = [2, 9],
})?)
try testing.expect_equal(i32(51), get(&map, reversed)?)
try testing.expect_equal(i32(63), get(&map, second)?)
try testing.expect_equal(null, get(&map, TestKey{
enabled = true, id = TestKeyId(18), kind = .first, indices = [2, 9],
}))
}