dir import
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
#import "modules/std.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
|
||||
// ============================================================
|
||||
// Comprehensive Smoke Test — exercises every spec feature
|
||||
@@ -1351,5 +1352,13 @@ END;
|
||||
print("{}\n", 5 in (1, 2, 3)); // false
|
||||
}
|
||||
|
||||
// --- Directory imports ---
|
||||
{
|
||||
print("--- directory imports ---\n");
|
||||
print("{}\n", pkg.add(3, 4)); // 7
|
||||
print("{}\n", pkg.mul(5, 6)); // 30
|
||||
print("{}\n", pkg.hello()); // hello from testpkg
|
||||
}
|
||||
|
||||
print("=== DONE ===\n");
|
||||
}
|
||||
|
||||
1
examples/modules/testpkg/greet.sx
Normal file
1
examples/modules/testpkg/greet.sx
Normal file
@@ -0,0 +1 @@
|
||||
hello :: () -> string { "hello from testpkg"; }
|
||||
2
examples/modules/testpkg/math.sx
Normal file
2
examples/modules/testpkg/math.sx
Normal file
@@ -0,0 +1,2 @@
|
||||
add :: (a: s32, b: s32) -> s32 { a + b; }
|
||||
mul :: (a: s32, b: s32) -> s32 { a * b; }
|
||||
151
src/imports.zig
151
src/imports.zig
@@ -106,36 +106,42 @@ pub fn resolveImports(
|
||||
const imported_mod = if (cache.get(resolved_path)) |cached|
|
||||
cached
|
||||
else blk: {
|
||||
// Read imported file
|
||||
const imp_bytes = std.Io.Dir.readFileAlloc(.cwd(), io, resolved_path, allocator, .limited(10 * 1024 * 1024)) catch {
|
||||
if (diagnostics) |diags| {
|
||||
diags.addFmt(.err, decl.span, "cannot read import '{s}'", .{resolved_path});
|
||||
}
|
||||
return error.ImportError;
|
||||
};
|
||||
const imp_source = try allocator.dupeZ(u8, imp_bytes);
|
||||
// Try as file first
|
||||
if (std.Io.Dir.readFileAlloc(.cwd(), io, resolved_path, allocator, .limited(10 * 1024 * 1024))) |imp_bytes| {
|
||||
const imp_source = try allocator.dupeZ(u8, imp_bytes);
|
||||
|
||||
if (source_map) |sm| {
|
||||
sm.put(resolved_path, imp_source) catch {};
|
||||
if (source_map) |sm| {
|
||||
sm.put(resolved_path, imp_source) catch {};
|
||||
}
|
||||
|
||||
var p = parser.Parser.init(allocator, imp_source);
|
||||
const imp_root = p.parse() catch {
|
||||
if (diagnostics) |diags| {
|
||||
diags.addFmt(.err, decl.span, "parse error in '{s}': {s}", .{ resolved_path, p.err_msg orelse "unknown" });
|
||||
}
|
||||
return error.ImportError;
|
||||
};
|
||||
|
||||
// Push onto chain before recursing, pop after
|
||||
try chain.put(resolved_path, {});
|
||||
const imp_dir = dirName(resolved_path);
|
||||
const result = try resolveImports(allocator, io, imp_root, imp_dir, resolved_path, chain, cache, source_map, diagnostics);
|
||||
_ = chain.remove(resolved_path);
|
||||
|
||||
// Cache
|
||||
try cache.put(resolved_path, result);
|
||||
break :blk result;
|
||||
} else |_| {
|
||||
// File read failed — try as directory import
|
||||
const result = resolveDirectoryImport(allocator, io, resolved_path, chain, cache, source_map, diagnostics, decl.span) catch {
|
||||
if (diagnostics) |diags| {
|
||||
diags.addFmt(.err, decl.span, "cannot read import '{s}' (not a file or directory)", .{resolved_path});
|
||||
}
|
||||
return error.ImportError;
|
||||
};
|
||||
try cache.put(resolved_path, result);
|
||||
break :blk result;
|
||||
}
|
||||
|
||||
var p = parser.Parser.init(allocator, imp_source);
|
||||
const imp_root = p.parse() catch {
|
||||
if (diagnostics) |diags| {
|
||||
diags.addFmt(.err, decl.span, "parse error in '{s}': {s}", .{ resolved_path, p.err_msg orelse "unknown" });
|
||||
}
|
||||
return error.ImportError;
|
||||
};
|
||||
|
||||
// Push onto chain before recursing, pop after
|
||||
try chain.put(resolved_path, {});
|
||||
const imp_dir = dirName(resolved_path);
|
||||
const result = try resolveImports(allocator, io, imp_root, imp_dir, resolved_path, chain, cache, source_map, diagnostics);
|
||||
_ = chain.remove(resolved_path);
|
||||
|
||||
// Cache
|
||||
try cache.put(resolved_path, result);
|
||||
break :blk result;
|
||||
};
|
||||
|
||||
if (imp.name) |ns_name| {
|
||||
@@ -148,3 +154,92 @@ pub fn resolveImports(
|
||||
try mod.finalize(allocator, &decl_list);
|
||||
return mod;
|
||||
}
|
||||
|
||||
/// Resolve a directory import by aggregating all .sx files in the directory.
|
||||
fn resolveDirectoryImport(
|
||||
allocator: std.mem.Allocator,
|
||||
io: std.Io,
|
||||
dir_path: []const u8,
|
||||
chain: *std.StringHashMap(void),
|
||||
cache: *ModuleCache,
|
||||
source_map: ?*std.StringHashMap([:0]const u8),
|
||||
diagnostics: ?*errors.DiagnosticList,
|
||||
span: ast.Span,
|
||||
) anyerror!ResolvedModule {
|
||||
// Open the directory with iteration capability
|
||||
const dir = std.Io.Dir.openDir(.cwd(), io, dir_path, .{ .iterate = true }) catch {
|
||||
return error.ImportError;
|
||||
};
|
||||
defer dir.close(io);
|
||||
|
||||
// Collect all .sx file names
|
||||
var file_names = std.ArrayList([]const u8).empty;
|
||||
var it = dir.iterate();
|
||||
while (it.next(io) catch null) |entry| {
|
||||
if (entry.kind != .file) continue;
|
||||
if (!std.mem.endsWith(u8, entry.name, ".sx")) continue;
|
||||
const name_copy = try allocator.dupe(u8, entry.name);
|
||||
try file_names.append(allocator, name_copy);
|
||||
}
|
||||
|
||||
// Sort alphabetically for deterministic ordering
|
||||
std.mem.sort([]const u8, file_names.items, {}, struct {
|
||||
fn lessThan(_: void, a: []const u8, b: []const u8) bool {
|
||||
return std.mem.order(u8, a, b) == .lt;
|
||||
}
|
||||
}.lessThan);
|
||||
|
||||
// Add directory to chain for circular import detection
|
||||
try chain.put(dir_path, {});
|
||||
defer _ = chain.remove(dir_path);
|
||||
|
||||
// Merge all files into a combined module
|
||||
var combined = ResolvedModule{
|
||||
.path = dir_path,
|
||||
.decls = &.{},
|
||||
.scope = std.StringHashMap(void).init(allocator),
|
||||
};
|
||||
var decl_list = std.ArrayList(*Node).empty;
|
||||
|
||||
for (file_names.items) |file_name| {
|
||||
const file_path = try std.fmt.allocPrint(allocator, "{s}/{s}", .{ dir_path, file_name });
|
||||
|
||||
if (chain.contains(file_path)) continue;
|
||||
|
||||
const file_mod = if (cache.get(file_path)) |cached|
|
||||
cached
|
||||
else file_blk: {
|
||||
const imp_bytes = std.Io.Dir.readFileAlloc(.cwd(), io, file_path, allocator, .limited(10 * 1024 * 1024)) catch {
|
||||
if (diagnostics) |diags| {
|
||||
diags.addFmt(.err, span, "cannot read '{s}' in directory import", .{file_path});
|
||||
}
|
||||
return error.ImportError;
|
||||
};
|
||||
const imp_source = try allocator.dupeZ(u8, imp_bytes);
|
||||
|
||||
if (source_map) |sm| {
|
||||
sm.put(file_path, imp_source) catch {};
|
||||
}
|
||||
|
||||
var p = parser.Parser.init(allocator, imp_source);
|
||||
const imp_root = p.parse() catch {
|
||||
if (diagnostics) |diags| {
|
||||
diags.addFmt(.err, span, "parse error in '{s}': {s}", .{ file_path, p.err_msg orelse "unknown" });
|
||||
}
|
||||
return error.ImportError;
|
||||
};
|
||||
|
||||
try chain.put(file_path, {});
|
||||
const result = try resolveImports(allocator, io, imp_root, dir_path, file_path, chain, cache, source_map, diagnostics);
|
||||
_ = chain.remove(file_path);
|
||||
|
||||
try cache.put(file_path, result);
|
||||
break :file_blk result;
|
||||
};
|
||||
|
||||
try combined.mergeFlat(allocator, &decl_list, file_mod);
|
||||
}
|
||||
|
||||
try combined.finalize(allocator, &decl_list);
|
||||
return combined;
|
||||
}
|
||||
|
||||
@@ -352,4 +352,8 @@ true
|
||||
true
|
||||
true
|
||||
false
|
||||
--- directory imports ---
|
||||
7
|
||||
30
|
||||
hello from testpkg
|
||||
=== DONE ===
|
||||
|
||||
Reference in New Issue
Block a user