From cd94a43cc97098b287ac3d168b4f652a51545e2a Mon Sep 17 00:00:00 2001 From: Brett Broadhurst Date: Thu, 26 Mar 2026 00:42:08 -0600 Subject: [PATCH] fix: simple knot tests --- src/AstGen.zig | 30 ++++++++++--------- src/Parse.zig | 2 +- src/Sema.zig | 23 ++++++++++++++- src/compile.zig | 1 + src/parser_tests.zig | 35 +++++++++++++++++++++++ testing/regression/syntax/empty-knots.ink | 12 -------- testing/regression/syntax/simple-knot.ink | 13 --------- 7 files changed, 75 insertions(+), 41 deletions(-) delete mode 100644 testing/regression/syntax/empty-knots.ink delete mode 100644 testing/regression/syntax/simple-knot.ink diff --git a/src/AstGen.zig b/src/AstGen.zig index 2a1ca5a..07942ee 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -1410,11 +1410,11 @@ fn knotDecl(gi: *GenIr, parent_scope: *Scope, decl_node: *const Ast.Node) InnerE .data = .{ .payload = undefined }, }); + var node_index: usize = 0; var child_block = gi.makeSubBlock(); defer child_block.unstack(); const knot_inst = try gi.makePayloadNode(.decl_knot); - var child_scope = parent_scope.makeChild(); defer child_scope.deinit(); @@ -1432,18 +1432,18 @@ fn knotDecl(gi: *GenIr, parent_scope: *Scope, decl_node: *const Ast.Node) InnerE }); } } - - var start_index: usize = 0; - const first_child = nested_decls_list[0]; - if (first_child.tag == .block_stmt) { - try blockStmt(&child_block, &child_scope, first_child); - start_index += 1; + if (nested_decls_list.len > 0) { + const first_child = nested_decls_list[0]; + if (first_child.tag == .block_stmt) { + try blockStmt(&child_block, &child_scope, first_child); + node_index += 1; + } } var nested_block = child_block.makeSubBlock(); defer nested_block.unstack(); - for (nested_decls_list[start_index..]) |nested_decl_node| { + for (nested_decls_list[node_index..]) |nested_decl_node| { switch (nested_decl_node.tag) { .stitch_decl => try stitchDecl(&nested_block, &child_scope, nested_decl_node), .function_decl => try functionDecl(&nested_block, &child_scope, nested_decl_node), @@ -1469,18 +1469,20 @@ fn file(gi: *GenIr, scope: *Scope, file_node: *const Ast.Node) InnerError!void { }, }); + var node_index: usize = 0; var file_scope = gi.makeSubBlock(); defer file_scope.unstack(); // TODO: Make sure this is non-nullable. const nested_decls_list = file_node.data.list.items orelse return; - if (nested_decls_list.len == 0) return; - - const first_child = nested_decls_list[0]; - if (first_child.tag == .block_stmt) { - try defaultBlock(&file_scope, scope, first_child); + if (nested_decls_list.len > 0) { + const first_child = nested_decls_list[0]; + if (first_child.tag == .block_stmt) { + try defaultBlock(&file_scope, scope, first_child); + node_index += 1; + } } - for (nested_decls_list[1..]) |child_node| { + for (nested_decls_list[node_index..]) |child_node| { switch (child_node.tag) { .knot_decl => try knotDecl(gi, scope, child_node), .stitch_decl => try stitchDecl(gi, scope, child_node), diff --git a/src/Parse.zig b/src/Parse.zig index d5d7fae..20f07f7 100644 --- a/src/Parse.zig +++ b/src/Parse.zig @@ -247,7 +247,7 @@ fn popScratch(p: *Parse, context: *const StmtContext) *Ast.Node { fn nodeListFromScratch(p: *Parse, start_offset: usize, end_offset: usize) Error![]*Ast.Node { const span = end_offset - start_offset; - assert(span > 0); + assert(span >= 0); const list = try p.arena.alloc(*Ast.Node, span); defer p.scratch.shrinkRetainingCapacity(start_offset); diff --git a/src/Sema.zig b/src/Sema.zig index 1b66069..19988a0 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -90,7 +90,7 @@ pub fn lookupInNamespace( var scope: ?*Module.Namespace = namespace; while (scope) |s| : (scope = s.parent) { if (s.decls.get(ident)) |decl| switch (decl.tag) { - .knot => return .{ .knot = .{ + .knot, .stitch => return .{ .knot = .{ .namespace = decl.namespace.?, .const_index = ident, } }, @@ -814,6 +814,27 @@ pub fn analyzeTopLevelDecl( try analyzeNestedDecl(sema, child_namespace, st); } }, + .decl_stitch => { + const child_namespace = try sema.module.createNamespace(namespace); + const gop = try namespace.decls.getOrPut(sema.arena, decl_name); + if (gop.found_existing) { + return sema.fail(src_loc, "duplicate identifier", .{}); + } else { + gop.value_ptr.* = .{ + .tag = .stitch, + .decl_inst = extra.value, + .args_count = 0, + .namespace = child_namespace, + }; + } + + try sema.module.queueWorkItem(.{ + .tag = .stitch, + .decl_name = decl_name, + .inst_index = extra.value, + .namespace = child_namespace, + }); + }, else => unreachable, } } diff --git a/src/compile.zig b/src/compile.zig index 90baa3d..ea70b73 100644 --- a/src/compile.zig +++ b/src/compile.zig @@ -205,6 +205,7 @@ pub const Module = struct { pub const Tag = enum { knot, + stitch, variable, }; }; diff --git a/src/parser_tests.zig b/src/parser_tests.zig index 01d75df..d9d56a1 100644 --- a/src/parser_tests.zig +++ b/src/parser_tests.zig @@ -93,6 +93,41 @@ test "parser: temporary assignment" { ); } +test "parser: simple knot" { + try testEqual( + \\=== knot === + \\Hello, world! + , + \\File "" + \\`--KnotDecl + \\ |--KnotProto + \\ | `--Identifier `knot` + \\ `--BlockStmt + \\ `--ContentStmt + \\ `--Content + \\ `--StringLiteral `Hello, world!` + \\ + , + ); +} + +test "parser: empty knot declarations" { + try testEqual( + \\== a + \\== b + , + \\File "" + \\|--KnotDecl + \\| `--KnotProto + \\| `--Identifier `a` + \\`--KnotDecl + \\ `--KnotProto + \\ `--Identifier `b` + \\ + , + ); +} + fn testEqual(source_bytes: [:0]const u8, expected_ast: []const u8) !void { const gpa = std.testing.allocator; var arena_allocator = std.heap.ArenaAllocator.init(gpa); diff --git a/testing/regression/syntax/empty-knots.ink b/testing/regression/syntax/empty-knots.ink deleted file mode 100644 index dffcaf6..0000000 --- a/testing/regression/syntax/empty-knots.ink +++ /dev/null @@ -1,12 +0,0 @@ -// RUN: %ink-compiler --stdin --compile-only --dump-ast < %s | FileCheck %s - -// CHECK: File "" -// CHECK-NEXT: |--KnotDecl -// CHECK-NEXT: | `--KnotProto -// CHECK-NEXT: | `--Identifier `a` -// CHECK-NEXT: `--KnotDecl -// CHECK-NEXT: `--KnotProto -// CHECK-NEXT: `--Identifier `b` - -== a -== b diff --git a/testing/regression/syntax/simple-knot.ink b/testing/regression/syntax/simple-knot.ink deleted file mode 100644 index a08acb0..0000000 --- a/testing/regression/syntax/simple-knot.ink +++ /dev/null @@ -1,13 +0,0 @@ -// RUN: %ink-compiler --stdin --compile-only --dump-ast < %s | FileCheck %s - -// CHECK: File "" -// CHECK-NEXT: `--KnotDecl -// CHECK-NEXT: |--KnotProto -// CHECK-NEXT: | `--Identifier `knot` -// CHECK-NEXT: `--BlockStmt -// CHECK-NEXT: `--ContentStmt -// CHECK-NEXT: `--Content -// CHECK-NEXT: `--StringLiteral `Hello, world!` - -=== knot === -Hello, world!