dir import
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
|
pkg :: #import "modules/testpkg";
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Comprehensive Smoke Test — exercises every spec feature
|
// Comprehensive Smoke Test — exercises every spec feature
|
||||||
@@ -1351,5 +1352,13 @@ END;
|
|||||||
print("{}\n", 5 in (1, 2, 3)); // false
|
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");
|
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|
|
const imported_mod = if (cache.get(resolved_path)) |cached|
|
||||||
cached
|
cached
|
||||||
else blk: {
|
else blk: {
|
||||||
// Read imported file
|
// Try as file first
|
||||||
const imp_bytes = std.Io.Dir.readFileAlloc(.cwd(), io, resolved_path, allocator, .limited(10 * 1024 * 1024)) catch {
|
if (std.Io.Dir.readFileAlloc(.cwd(), io, resolved_path, allocator, .limited(10 * 1024 * 1024))) |imp_bytes| {
|
||||||
if (diagnostics) |diags| {
|
const imp_source = try allocator.dupeZ(u8, imp_bytes);
|
||||||
diags.addFmt(.err, decl.span, "cannot read import '{s}'", .{resolved_path});
|
|
||||||
}
|
|
||||||
return error.ImportError;
|
|
||||||
};
|
|
||||||
const imp_source = try allocator.dupeZ(u8, imp_bytes);
|
|
||||||
|
|
||||||
if (source_map) |sm| {
|
if (source_map) |sm| {
|
||||||
sm.put(resolved_path, imp_source) catch {};
|
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| {
|
if (imp.name) |ns_name| {
|
||||||
@@ -148,3 +154,92 @@ pub fn resolveImports(
|
|||||||
try mod.finalize(allocator, &decl_list);
|
try mod.finalize(allocator, &decl_list);
|
||||||
return mod;
|
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
|
||||||
true
|
true
|
||||||
false
|
false
|
||||||
|
--- directory imports ---
|
||||||
|
7
|
||||||
|
30
|
||||||
|
hello from testpkg
|
||||||
=== DONE ===
|
=== DONE ===
|
||||||
|
|||||||
Reference in New Issue
Block a user