pipes
This commit is contained in:
116
src/sema.zig
116
src/sema.zig
@@ -250,6 +250,9 @@ pub const Analyzer = struct {
|
||||
}
|
||||
self.popScope();
|
||||
},
|
||||
.ufcs_alias => |ua| {
|
||||
try self.addSymbol(ua.name, .function, null, node.span);
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
@@ -505,7 +508,7 @@ pub const Analyzer = struct {
|
||||
try self.analyzeNode(val);
|
||||
}
|
||||
},
|
||||
.enum_decl, .struct_decl, .union_decl, .array_type_expr, .slice_type_expr, .array_literal, .parameterized_type_expr, .index_expr, .slice_expr, .insert_expr => {},
|
||||
.enum_decl, .struct_decl, .union_decl, .array_type_expr, .slice_type_expr, .array_literal, .parameterized_type_expr, .index_expr, .slice_expr, .insert_expr, .ufcs_alias => {},
|
||||
.namespace_decl => |ns| {
|
||||
try self.pushScope();
|
||||
for (ns.decls) |d| {
|
||||
@@ -543,7 +546,8 @@ pub const Analyzer = struct {
|
||||
|
||||
fn addSymbol(self: *Analyzer, name: []const u8, kind: SymbolKind, ty: ?Type, span: Span) !void {
|
||||
// Check for duplicate using the symbol index
|
||||
if (self.symbol_index.get(name)) |indices| {
|
||||
// Variables are allowed to shadow in the same scope (sx semantics)
|
||||
if (kind != .variable) if (self.symbol_index.get(name)) |indices| {
|
||||
const scope_start: u32 = if (self.scope_starts.items.len > 0)
|
||||
self.scope_starts.items[self.scope_starts.items.len - 1]
|
||||
else
|
||||
@@ -561,7 +565,7 @@ pub const Analyzer = struct {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
try self.symbols.append(self.allocator, .{
|
||||
.name = name,
|
||||
@@ -579,6 +583,11 @@ pub const Analyzer = struct {
|
||||
try gop.value_ptr.append(self.allocator, idx);
|
||||
}
|
||||
|
||||
/// Check if a symbol name has been registered.
|
||||
pub fn hasSymbol(self: *const Analyzer, name: []const u8) bool {
|
||||
return self.symbol_index.contains(name);
|
||||
}
|
||||
|
||||
/// Pre-register an imported symbol so references in this file can resolve to it.
|
||||
pub fn preRegisterSymbol(self: *Analyzer, sym: Symbol) !void {
|
||||
try self.symbols.append(self.allocator, sym);
|
||||
@@ -710,7 +719,17 @@ pub const Analyzer = struct {
|
||||
},
|
||||
.for_expr => |fe| {
|
||||
try self.analyzeNode(fe.iterable);
|
||||
try self.pushScope();
|
||||
if (!std.mem.eql(u8, fe.capture_name, "_")) {
|
||||
try self.addSymbol(fe.capture_name, .variable, null, node.span);
|
||||
}
|
||||
if (fe.index_name) |idx_name| {
|
||||
if (!std.mem.eql(u8, idx_name, "_")) {
|
||||
try self.addSymbol(idx_name, .variable, .{ .signed = 64 }, node.span);
|
||||
}
|
||||
}
|
||||
try self.analyzeNode(fe.body);
|
||||
self.popScope();
|
||||
},
|
||||
.spread_expr => |se| try self.analyzeNode(se.operand),
|
||||
.break_expr, .continue_expr => {},
|
||||
@@ -784,8 +803,12 @@ pub const Analyzer = struct {
|
||||
.index_expr,
|
||||
.slice_expr,
|
||||
.tuple_type_expr,
|
||||
.ufcs_alias,
|
||||
=> {},
|
||||
.ufcs_alias => |ua| {
|
||||
// Register the alias name as a function and resolve the target
|
||||
try self.addSymbol(ua.name, .function, null, node.span);
|
||||
try self.resolveIdentifier(ua.target, node.span);
|
||||
},
|
||||
.tuple_literal => |tl| {
|
||||
for (tl.elements) |elem| {
|
||||
try self.analyzeNode(elem.value);
|
||||
@@ -1329,3 +1352,88 @@ test "sema: var_decl infers struct type from parameterized call literal" {
|
||||
}
|
||||
try std.testing.expect(found_list);
|
||||
}
|
||||
|
||||
test "sema: variable shadowing in same scope is allowed" {
|
||||
const parser_mod = @import("parser.zig");
|
||||
|
||||
// Two variables with the same name in the same function body — sx allows this
|
||||
const source = "main :: () { x : s64 = 1; x : f64 = 2.0; }";
|
||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
var parser = parser_mod.Parser.init(alloc, source);
|
||||
const root = try parser.parse();
|
||||
|
||||
var analyzer = Analyzer.init(alloc);
|
||||
const result = try analyzer.analyze(root);
|
||||
|
||||
// Should have NO diagnostics — variable shadowing is allowed
|
||||
for (result.diagnostics) |d| {
|
||||
if (std.mem.eql(u8, d.message, "duplicate declaration")) {
|
||||
return error.TestUnexpectedResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test "sema: ufcs_alias registers symbol" {
|
||||
const parser_mod = @import("parser.zig");
|
||||
|
||||
const source = "add :: (a: s64, b: s64) -> s64 { a + b; } main :: () { sum :: ufcs add; sum(1, 2); }";
|
||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
var parser = parser_mod.Parser.init(alloc, source);
|
||||
const root = try parser.parse();
|
||||
|
||||
var analyzer = Analyzer.init(alloc);
|
||||
const result = try analyzer.analyze(root);
|
||||
|
||||
// `sum` should be registered as a symbol — no "undefined variable" diagnostic
|
||||
for (result.diagnostics) |d| {
|
||||
if (std.mem.eql(u8, d.message, "undefined variable")) {
|
||||
return error.TestUnexpectedResult;
|
||||
}
|
||||
}
|
||||
|
||||
// Should find `sum` in symbols
|
||||
var found_sum = false;
|
||||
for (result.symbols) |sym| {
|
||||
if (std.mem.eql(u8, sym.name, "sum")) {
|
||||
found_sum = true;
|
||||
try std.testing.expectEqual(SymbolKind.function, sym.kind);
|
||||
break;
|
||||
}
|
||||
}
|
||||
try std.testing.expect(found_sum);
|
||||
}
|
||||
|
||||
test "sema: top-level ufcs_alias registers symbol" {
|
||||
const parser_mod = @import("parser.zig");
|
||||
|
||||
const source = "add :: (a: s64, b: s64) -> s64 { a + b; } sum :: ufcs add;";
|
||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
var parser = parser_mod.Parser.init(alloc, source);
|
||||
const root = try parser.parse();
|
||||
|
||||
var analyzer = Analyzer.init(alloc);
|
||||
const result = try analyzer.analyze(root);
|
||||
|
||||
// No diagnostics
|
||||
try std.testing.expectEqual(@as(usize, 0), result.diagnostics.len);
|
||||
|
||||
// Should find `sum` as function symbol
|
||||
var found_sum = false;
|
||||
for (result.symbols) |sym| {
|
||||
if (std.mem.eql(u8, sym.name, "sum")) {
|
||||
found_sum = true;
|
||||
try std.testing.expectEqual(SymbolKind.function, sym.kind);
|
||||
break;
|
||||
}
|
||||
}
|
||||
try std.testing.expect(found_sum);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user