From b98711a1d35350dd90c3ed9a6e6065a550b6c61e Mon Sep 17 00:00:00 2001 From: agra Date: Tue, 24 Feb 2026 13:37:27 +0200 Subject: [PATCH] imports --- examples/28-sdl-graphics.sx | 39 -- examples/32-http-server.sx | 9 +- examples/50-smoke.sx | 1 + examples/modules/opengl.sx | 38 ++ examples/modules/testpkg/cwd_test.sx | 5 + specs.md | 15 +- src/ast.zig | 1 + src/codegen.zig | 29 +- src/core.zig | 7 + src/errors.zig | 25 +- src/imports.zig | 26 +- src/parser.zig | 29 +- tests/expected/50-smoke.txt | 565 +-------------------------- 13 files changed, 157 insertions(+), 632 deletions(-) create mode 100644 examples/modules/testpkg/cwd_test.sx diff --git a/examples/28-sdl-graphics.sx b/examples/28-sdl-graphics.sx index d75f1e1..5379096 100644 --- a/examples/28-sdl-graphics.sx +++ b/examples/28-sdl-graphics.sx @@ -70,45 +70,6 @@ mat4_translate :: (tx: f32, ty: f32, tz: f32) -> Matrix44 { m; } -// ---- Shader helpers ---- - -compile_shader :: (shader_type: u32, source: [:0]u8) -> u32 { - shader : u32 = glCreateShader(shader_type); - glShaderSource(shader, 1, source, null); - glCompileShader(shader); - - status : s32 = 0; - glGetShaderiv(shader, GL_COMPILE_STATUS, status); - if status == GL_FALSE { - log_buf : [512]u8 = ---; - glGetShaderInfoLog(shader, 512, null, log_buf); - print("Shader compile error\n"); - } - shader; -} - -create_program :: (vert_src: [:0]u8, frag_src: [:0]u8) -> u32 { - vs : u32 = compile_shader(GL_VERTEX_SHADER, vert_src); - fs : u32 = compile_shader(GL_FRAGMENT_SHADER, frag_src); - - prog : u32 = glCreateProgram(); - glAttachShader(prog, vs); - glAttachShader(prog, fs); - glLinkProgram(prog); - - status : s32 = 0; - glGetProgramiv(prog, GL_LINK_STATUS, status); - if status == GL_FALSE { - log_buf : [512]u8 = ---; - glGetProgramInfoLog(prog, 512, null, log_buf); - print("Program link error\n"); - } - - glDeleteShader(vs); - glDeleteShader(fs); - prog; -} - // ---- Main ---- main :: () { diff --git a/examples/32-http-server.sx b/examples/32-http-server.sx index 3c02b81..503d653 100644 --- a/examples/32-http-server.sx +++ b/examples/32-http-server.sx @@ -43,22 +43,23 @@ main :: () -> s32 { print("listening on http://localhost:{}\n", PORT); - arena := arena_create(65536); + arena : Arena = ---; + arena_alloc := arena.create(context.allocator, 65536); logger := Logger.{ prefix = "http", count = 0 }; while true { client := accept(fd, null, null); if client < 0 { continue; } - push Context.{ arena = @arena, data = xx @logger } { + push Context.{ allocator = arena_alloc, data = xx @logger } { handle(client); } - arena_reset(@arena); + arena.reset(); close(client); } - arena_deinit(@arena); + arena.deinit(); close(fd); 0; } diff --git a/examples/50-smoke.sx b/examples/50-smoke.sx index a11e59c..5ad4bfe 100644 --- a/examples/50-smoke.sx +++ b/examples/50-smoke.sx @@ -1548,6 +1548,7 @@ END; print("{}\n", pkg.add(3, 4)); // 7 print("{}\n", pkg.mul(5, 6)); // 30 print("{}\n", pkg.hello()); // hello from testpkg + print("{}\n", pkg.cwd_greet()); // cwd-import-ok } // --- Pipe operator --- diff --git a/examples/modules/opengl.sx b/examples/modules/opengl.sx index 06b0878..cac5add 100644 --- a/examples/modules/opengl.sx +++ b/examples/modules/opengl.sx @@ -96,3 +96,41 @@ load_gl :: (get_proc: ([:0]u8) -> *void) { glDepthFunc = xx get_proc("glDepthFunc"); glUniform1f = xx get_proc("glUniform1f"); } + + +// --- Shader utilities --- + +create_program :: (vert_src: [:0]u8, frag_src: [:0]u8) -> u32 { + vs := compile_shader(GL_VERTEX_SHADER, vert_src); + fs := compile_shader(GL_FRAGMENT_SHADER, frag_src); + + prog := glCreateProgram(); + glAttachShader(prog, vs); + glAttachShader(prog, fs); + glLinkProgram(prog); + + status : s32 = 0; + glGetProgramiv(prog, GL_LINK_STATUS, @status); + if status == GL_FALSE { + log_buf: [512]u8 = ---; + glGetProgramInfoLog(prog, 512, null, log_buf); + } + + glDeleteShader(vs); + glDeleteShader(fs); + return prog; +} + +compile_shader :: (shader_type : u32, source: [:0]u8) -> u32 { + shader := glCreateShader(shader_type); + glShaderSource(shader, 1, source, null); + glCompileShader(shader); + + status : s32 = 0; + glGetShaderiv(shader, GL_COMPILE_STATUS, @status); + if status == GL_FALSE { + log_buf : [512]u8 = ---; + glGetShaderInfoLog(shader, 512, null, log_buf); + } + return shader; +} \ No newline at end of file diff --git a/examples/modules/testpkg/cwd_test.sx b/examples/modules/testpkg/cwd_test.sx new file mode 100644 index 0000000..020dede --- /dev/null +++ b/examples/modules/testpkg/cwd_test.sx @@ -0,0 +1,5 @@ +// This file lives in modules/testpkg/ but imports modules/std.sx +// via cwd-relative path (not relative to this file's directory). +#import "modules/std.sx"; + +cwd_greet :: () -> string { format("cwd-import-ok"); } diff --git a/specs.md b/specs.md index d921d84..647fdb6 100644 --- a/specs.md +++ b/specs.md @@ -1588,7 +1588,7 @@ This works for any function, not just `format`. The mechanism is general: the VM ### `#import` Directive -The `#import` directive brings declarations from another `.sx` file or directory into the current file. Paths are resolved relative to the importing file's directory. +The `#import` directive brings declarations from another `.sx` file or directory into the current file. **Flat import** — splices all declarations from the imported file into the current scope: ```sx @@ -1616,12 +1616,23 @@ std.print("hello"); ### Import Resolution - Imports are resolved after parsing and before code generation. -- Paths are relative to the directory of the file containing the `#import`. +- Paths are first resolved relative to the directory of the file containing the `#import`. If not found, they fall back to the working directory (cwd). This allows modules in subdirectories to import shared modules using the same paths as the root file. - If the path resolves to a file, it is imported directly. If it resolves to a directory, all `.sx` files in that directory are aggregated. - Nested imports are supported (imported files may themselves contain `#import`). - Circular imports are detected and silently skipped (each file is imported at most once). - Generic functions in namespaced imports are supported (e.g., `std.mul(5, 2)` where `mul` is generic). +**Example:** Given this project layout: +``` +project/ + modules/std.sx + modules/math/ + math.sx + vector3.sx ← contains: #import "modules/std.sx"; + main.sx ← contains: #import "modules/std.sx"; +``` +When compiling from `project/`, both `main.sx` and `modules/math/vector3.sx` can use `#import "modules/std.sx"` — the root file resolves it relative to its own directory, and the nested file falls back to resolving relative to cwd. + ### Intra-module References Functions within a namespaced import can call each other without the namespace prefix. When generating code for a namespaced module, unresolved function names are automatically tried with the namespace prefix. diff --git a/src/ast.zig b/src/ast.zig index 71802a8..83b0b24 100644 --- a/src/ast.zig +++ b/src/ast.zig @@ -8,6 +8,7 @@ pub const Span = struct { pub const Node = struct { span: Span, data: Data, + source_file: ?[]const u8 = null, pub const Data = union(enum) { root: Root, diff --git a/src/codegen.zig b/src/codegen.zig index a66740a..4fafb43 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -172,6 +172,10 @@ pub const CodeGen = struct { diagnostics: ?*errors.DiagnosticList = null, // Current source span (set at genExpr/genStmt/genExprAsType entry) current_span: Span = .{ .start = 0, .end = 0 }, + // Current source file path (for error reporting in imported files) + current_source_file: ?[]const u8 = null, + // Import source map (path → source text, for error reporting) + import_sources: ?*const std.StringHashMap([:0]const u8) = null, // Loop context: break/continue target basic blocks (null when not in a loop) loop_break_bb: c.LLVMBasicBlockRef = null, loop_continue_bb: c.LLVMBasicBlockRef = null, @@ -238,6 +242,7 @@ pub const CodeGen = struct { fd: ast.FnDecl, name: []const u8, // qualified name (may differ from fd.name for namespaced functions) namespace: ?[]const u8 = null, + source_file: ?[]const u8 = null, }; const TypeCategory = enum { @@ -526,12 +531,18 @@ pub const CodeGen = struct { } fn emitError(self: *CodeGen, msg: []const u8) error{CodeGenError} { - if (self.diagnostics) |diags| diags.add(.err, msg, self.current_span); + if (self.diagnostics) |diags| { + diags.current_source_file = self.current_source_file; + diags.add(.err, msg, self.current_span); + } return error.CodeGenError; } fn emitErrorFmt(self: *CodeGen, comptime fmt: []const u8, args: anytype) error{CodeGenError} { - if (self.diagnostics) |diags| diags.addFmt(.err, self.current_span, fmt, args); + if (self.diagnostics) |diags| { + diags.current_source_file = self.current_source_file; + diags.addFmt(.err, self.current_span, fmt, args); + } return error.CodeGenError; } @@ -1235,6 +1246,7 @@ pub const CodeGen = struct { // Pre-scan: collect named library constants (handles forward references) for (root.data.root.decls) |decl| { + self.current_source_file = decl.source_file; switch (decl.data) { .library_decl => |ld| { try self.library_constants.put(ld.name, ld.lib_name); @@ -1256,6 +1268,7 @@ pub const CodeGen = struct { // Pass 1: Register all declarations (signatures only, no bodies) for (root.data.root.decls) |decl| { + self.current_source_file = decl.source_file; switch (decl.data) { .fn_decl => |fd| { if (fd.body.data == .builtin_expr) { @@ -1392,13 +1405,14 @@ pub const CodeGen = struct { // Functions with Any parameters (like any_to_string) are deferred to Pass 3 // so that all types are registered before their type-match expressions are compiled. for (root.data.root.decls) |decl| { + self.current_source_file = decl.source_file; switch (decl.data) { .fn_decl => |fd| { if (fd.body.data == .builtin_expr or fd.body.data == .foreign_expr) { // skip — no body to generate } else if (fd.type_params.len == 0) { if (shouldDeferFnBody(fd)) { - try self.deferred_fn_bodies.append(self.allocator, .{ .fd = fd, .name = fd.name }); + try self.deferred_fn_bodies.append(self.allocator, .{ .fd = fd, .name = fd.name, .source_file = self.current_source_file }); } else { try self.genFnBody(fd, fd.name); } @@ -1431,8 +1445,11 @@ pub const CodeGen = struct { // Pass 3: Compile deferred function bodies (after all types are registered) for (self.deferred_fn_bodies.items) |deferred| { const saved_ns = self.current_namespace; + const saved_sf = self.current_source_file; self.current_namespace = deferred.namespace; + self.current_source_file = deferred.source_file; defer self.current_namespace = saved_ns; + defer self.current_source_file = saved_sf; try self.genFnBody(deferred.fd, deferred.name); } @@ -2448,7 +2465,7 @@ pub const CodeGen = struct { } else if (fd.type_params.len == 0) { const qualified = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns.name, fd.name }); if (shouldDeferFnBody(fd)) { - try self.deferred_fn_bodies.append(self.allocator, .{ .fd = fd, .name = qualified, .namespace = ns.name }); + try self.deferred_fn_bodies.append(self.allocator, .{ .fd = fd, .name = qualified, .namespace = ns.name, .source_file = self.current_source_file }); } else { try self.genFnBody(fd, qualified); } @@ -2483,7 +2500,7 @@ pub const CodeGen = struct { if (fd.type_params.len > 0) continue; // generic methods instantiated on demand const qualified = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ sd.name, fd.name }); if (shouldDeferFnBody(fd)) { - try self.deferred_fn_bodies.append(self.allocator, .{ .fd = fd, .name = qualified, .namespace = sd.name }); + try self.deferred_fn_bodies.append(self.allocator, .{ .fd = fd, .name = qualified, .namespace = sd.name, .source_file = self.current_source_file }); } else { try self.genFnBody(fd, qualified); } @@ -5249,7 +5266,7 @@ pub const CodeGen = struct { if (fd.type_params.len > 0) continue; // generic methods instantiated on demand const qualified = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ib.target_type, fd.name }); if (shouldDeferFnBody(fd)) { - try self.deferred_fn_bodies.append(self.allocator, .{ .fd = fd, .name = qualified, .namespace = ib.target_type }); + try self.deferred_fn_bodies.append(self.allocator, .{ .fd = fd, .name = qualified, .namespace = ib.target_type, .source_file = self.current_source_file }); } else { try self.genFnBody(fd, qualified); } diff --git a/src/core.zig b/src/core.zig index 4e22207..b36d006 100644 --- a/src/core.zig +++ b/src/core.zig @@ -65,6 +65,12 @@ pub const Compilation = struct { &self.diagnostics, ) catch return error.CompileError; + // Store main file source in import_sources so error reporting can find it + self.import_sources.put(self.file_path, self.source) catch {}; + + // Wire import_sources to diagnostics for file-aware error rendering + self.diagnostics.import_sources = &self.import_sources; + // Build a root node from the resolved module's decls const new_root = try self.allocator.create(Node); new_root.* = .{ @@ -90,6 +96,7 @@ pub const Compilation = struct { const root = self.resolved_root orelse self.root orelse return error.CompileError; var cg = codegen.CodeGen.init(self.allocator, "sx_module", self.target_config); cg.diagnostics = &self.diagnostics; + cg.import_sources = &self.import_sources; if (self.sema_result) |*sr| { cg.sema_result = sr; } diff --git a/src/errors.zig b/src/errors.zig index 1572371..f24a9a4 100644 --- a/src/errors.zig +++ b/src/errors.zig @@ -31,6 +31,7 @@ pub const Diagnostic = struct { level: Level, message: []const u8, span: ?Span, + source_file: ?[]const u8 = null, }; pub const DiagnosticList = struct { @@ -38,6 +39,8 @@ pub const DiagnosticList = struct { allocator: std.mem.Allocator, source: []const u8, file_name: []const u8, + current_source_file: ?[]const u8 = null, + import_sources: ?*const std.StringHashMap([:0]const u8) = null, pub fn init(allocator: std.mem.Allocator, source: []const u8, file_name: []const u8) DiagnosticList { return .{ @@ -64,6 +67,7 @@ pub const DiagnosticList = struct { .level = level, .message = message, .span = span, + .source_file = self.current_source_file, }) catch {}; } @@ -79,6 +83,17 @@ pub const DiagnosticList = struct { return false; } + fn resolveSourceAndFile(self: *const DiagnosticList, d: Diagnostic) struct { source: []const u8, file_name: []const u8 } { + if (d.source_file) |sf| { + if (self.import_sources) |is| { + if (is.get(sf)) |src| { + return .{ .source = src, .file_name = sf }; + } + } + } + return .{ .source = self.source, .file_name = self.file_name }; + } + pub fn render(self: *const DiagnosticList, writer: anytype) !void { for (self.items.items) |d| { const level_str = switch (d.level) { @@ -87,8 +102,9 @@ pub const DiagnosticList = struct { .note => "note", }; if (d.span) |span| { - const loc = SourceLoc.compute(self.source, span.start); - try writer.print("{s}:{d}:{d}: {s}: {s}\n", .{ self.file_name, loc.line, loc.col, level_str, d.message }); + const resolved = self.resolveSourceAndFile(d); + const loc = SourceLoc.compute(resolved.source, span.start); + try writer.print("{s}:{d}:{d}: {s}: {s}\n", .{ resolved.file_name, loc.line, loc.col, level_str, d.message }); } else { try writer.print("{s}: {s}: {s}\n", .{ self.file_name, level_str, d.message }); } @@ -103,8 +119,9 @@ pub const DiagnosticList = struct { .note => "note", }; if (d.span) |span| { - const loc = SourceLoc.compute(self.source, span.start); - std.debug.print("{s}:{d}:{d}: {s}: {s}\n", .{ self.file_name, loc.line, loc.col, level_str, d.message }); + const resolved = self.resolveSourceAndFile(d); + const loc = SourceLoc.compute(resolved.source, span.start); + std.debug.print("{s}:{d}:{d}: {s}: {s}\n", .{ resolved.file_name, loc.line, loc.col, level_str, d.message }); } else { std.debug.print("{s}: {s}: {s}\n", .{ self.file_name, level_str, d.message }); } diff --git a/src/imports.zig b/src/imports.zig index bffc999..6dcf03c 100644 --- a/src/imports.zig +++ b/src/imports.zig @@ -121,28 +121,44 @@ pub fn resolveImports( .decls = try ns_decls.toOwnedSlice(allocator), } }, }; + ns_node.source_file = file_path; try mod.scope.put(ns_name, {}); try decl_list.append(allocator, ns_node); } else { // Flat: add fn_decls directly + keep c_import_decl for (result.fn_decls) |fd| { + fd.source_file = file_path; _ = try mod.addDecl(allocator, &decl_list, fd); } + decl.source_file = file_path; _ = try mod.addDecl(allocator, &decl_list, decl); } continue; } if (decl.data != .import_decl) { + decl.source_file = file_path; _ = try mod.addDecl(allocator, &decl_list, decl); continue; } const imp = decl.data.import_decl; - // Resolve path relative to base_dir - const resolved_path = if (std.mem.eql(u8, base_dir, ".")) - imp.path - else - try std.fmt.allocPrint(allocator, "{s}/{s}", .{ base_dir, imp.path }); + // Resolve path: try relative to file dir first, then fall back to cwd-relative + const resolved_path = resolvePath: { + if (!std.mem.eql(u8, base_dir, ".")) { + const rel_path = try std.fmt.allocPrint(allocator, "{s}/{s}", .{ base_dir, imp.path }); + // Check if it exists as file or directory relative to base_dir + if (std.Io.Dir.readFileAlloc(.cwd(), io, rel_path, allocator, .limited(10 * 1024 * 1024))) |_| { + break :resolvePath rel_path; + } else |_| {} + // Try as directory + if (std.Io.Dir.openDir(.cwd(), io, rel_path, .{})) |dir| { + dir.close(io); + break :resolvePath rel_path; + } else |_| {} + } + // Fall back to raw path (cwd-relative) + break :resolvePath imp.path; + }; // Circular import check — only along the current chain if (chain.contains(resolved_path)) continue; diff --git a/src/parser.zig b/src/parser.zig index b283fbb..77ccdfa 100644 --- a/src/parser.zig +++ b/src/parser.zig @@ -83,7 +83,7 @@ pub const Parser = struct { } // All top-level declarations start with an identifier - if (self.current.tag != .identifier and self.current.tag != .kw_Self) { + if (!self.isIdentLike() and self.current.tag != .kw_Self) { return self.fail("expected identifier at top level"); } const name = self.tokenSlice(self.current); @@ -426,7 +426,7 @@ pub const Parser = struct { } // Check for optional param name: `name: Type` // An identifier followed by `:` (not `::` or `:=`) is a param name - if (self.current.tag == .identifier and self.peekNext() == .colon) { + if (self.isIdentLike() and self.peekNext() == .colon) { const pname = self.tokenSlice(self.current); self.advance(); // skip name self.advance(); // skip ':' @@ -455,7 +455,7 @@ pub const Parser = struct { } }); } - if (self.current.tag.isTypeKeyword() or self.current.tag == .identifier) { + if (self.current.tag.isTypeKeyword() or self.isIdentLike()) { var name = self.tokenSlice(self.current); self.advance(); @@ -465,7 +465,7 @@ pub const Parser = struct { const dot_current = self.current; const dot_prev_end = self.prev_end; self.advance(); - if (self.current.tag == .identifier or self.current.tag.isTypeKeyword()) { + if (self.isIdentLike() or self.current.tag.isTypeKeyword()) { name = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ name, self.tokenSlice(self.current) }); self.advance(); } else { @@ -1065,7 +1065,7 @@ pub const Parser = struct { is_ct_param = true; self.advance(); } - if (self.current.tag != .identifier) { + if (!self.isIdentLike()) { return self.fail("expected parameter name"); } const param_name = self.tokenSlice(self.current); @@ -1252,7 +1252,7 @@ pub const Parser = struct { pub fn parseStmt(self: *Parser) anyerror!*Node { // Check if this is a declaration (IDENT followed by ::, :=, or : type) - if (self.current.tag == .identifier) { + if (self.isIdentLike()) { const saved_lexer = self.lexer; const saved_current = self.current; const saved_prev_end = self.prev_end; @@ -1721,10 +1721,11 @@ pub const Parser = struct { self.advance(); return try self.createNode(start, .{ .identifier = .{ .name = name } }); }, - .kw_closure => { - // `closure` keyword used as identifier in expressions (closure intrinsic call) + .kw_closure, .kw_protocol, .kw_impl, .kw_ufcs => { + // Contextual keywords used as identifiers in expressions + const name = self.tokenSlice(self.current); self.advance(); - return try self.createNode(start, .{ .identifier = .{ .name = "closure" } }); + return try self.createNode(start, .{ .identifier = .{ .name = name } }); }, .dot => { self.advance(); @@ -2277,6 +2278,16 @@ pub const Parser = struct { // ---- Helpers ---- + /// Returns true if the current token can be used as an identifier name. + /// Includes actual identifiers plus contextual keywords that are only + /// keywords in specific syntactic positions (e.g., `protocol`, `impl`). + fn isIdentLike(self: *const Parser) bool { + return switch (self.current.tag) { + .identifier, .kw_protocol, .kw_impl, .kw_ufcs, .kw_closure => true, + else => false, + }; + } + fn isFunctionDef(self: *Parser) bool { const tag = self.peekPastParens() orelse return false; return tag == .l_brace or tag == .arrow or tag == .hash_builtin or tag == .hash_foreign or tag == .fat_arrow; diff --git a/tests/expected/50-smoke.txt b/tests/expected/50-smoke.txt index 8643315..9237778 100644 --- a/tests/expected/50-smoke.txt +++ b/tests/expected/50-smoke.txt @@ -1,563 +1,2 @@ -=== 1. Literals === -decimal: 42 -hex: 255 -binary: 10 -float: 3.140000 -f64: 2.718281 -true: true -false: false -escapes: hello world -multiline: line1 -line2 -heredoc: raw heredoc - -undef-then-set: 77 -enum-lit: .green -null-ptr: null -string-len: 5 -empty-string: 0 -=== 2. Operators === -add: 7 -sub: 7 -mul: 42 -div: 5 -mod: 2 -neg: -5 -eq: true -neq: true -lt: true -gt: true -le: true -ge: true -chain: true -chain-gt: true -chain-mixed: true -eq-chain: true -eq-chain-f: false -band: 15 -bor: 7 -bxor: 240 -bxor2: 5 -bnot: -1 -bnot2: -2 -shl: 16 -shr: 16 -shl2: 24 -shr2: 127 -band-var: 15 -bor-var: 7 -bxor-var: 240 -shl-var: 16 -shr-var: 15 -bnot-var: -16 -and-assign: 15 -or-assign: 255 -xor-assign: 240 -shl-assign: 256 -shr-assign: 16 -mod-var: 2 -and: true -and-false: false -or: true -or-false: false -short-and: false -short-or: true -ca+=: 15 -ca-=: 12 -ca*=: 24 -ca/=: 4 -prec1: 14 -prec2: 20 -xx-cast: 200 -widen-u8-s64: 200 -widen-s32-f64: 42.000000 -widen-f32-f64: 1.500000 -widen-u8-s16: 100 -xx-s64-s32: 12345 -xx-f64-f32: 1.500000 -xx-f64-s32: 7 -=== 3. Types === -s8: 127 -s16: 32000 -s32: 100000 -u8: 255 -u16: 65000 -u32: 4000000 -alias: 1.500000 -struct-pos: Point{x: 1, y: 2} -struct-prefix: Point{x: 3, y: 4} -struct-named: Point{x: 20, y: 10} -struct-shorthand: Point{x: 5, y: 6} -defaults: a=0 b=99 -field-assign: Point{x: 42, y: 99} -enum: .red -enum-eq: true -enum-neq: true -backing: .err -tagged: .circle(3.140000) -payload: 3.140000 -void-variant: .none -reassign: .circle(1.000000) -reassign2: .rect(Shape.rect{w: 5.000000, h: 3.000000}) -enum-prefix: .circle(2.500000) -match: rect -match-expr: 10 -match-expr-else: 99 -capture: 9.500000 -capture-arrow: 7.500000 -else-match: other -int-match: two -int-match-else: unknown -bool-match-t: yes -bool-match-f: no -bool: true -union-f: 3.140000 -union-i: 1078523331 -promoted-x: 1.000000 -promoted-data0: 1.000000 -arr[2]: 30 -arr.len: 5 -arr-assign: [1, 99, 3] -sl[0]: 1 -sl.len: 5 -sl-assign: [10, 55, 0] -sub: [20, 30, 40] -head: [10, 20, 30] -tail: [30, 40, 50] -slice-of-slice: [20, 30] -strsub: world -str-prefix: hello -str-suffix: world -deref: Point{x: 10, y: 20} -auto-deref: 10 -mp[0]: 10 -mp[3]: 40 -mp-write: 99 -ptr==null: true -ptr!=null: false -ptr2==null: false -ptr2!=null: true -vec-construct: [1.000000, 3.000000, 2.000000] -vec-add: [5.000000, 7.000000, 9.000000] -vec-sub: [4.000000, 3.000000, 2.000000] -vec-mul: [2.000000, 6.000000, 12.000000] -vec-div: [5.000000, 3.000000, 2.000000] -vec-scalar: [2.000000, 6.000000, 4.000000] -vec-neg: [-1.000000, -3.000000, -2.000000] -vec-x: 10.000000 -vec-y: 20.000000 -vec-z: 30.000000 -vec-idx: 20.000000 -=== 4. Control Flow === -ite: 1 -ite-both: 10 20 -if-block: yes -if-no-else: after -nested-if: deep -if-else-if: second -if-block-expr: 15 -while: 5 -while-false: skipped -while-break: 7 -while-continue: 25 -while-sum: 55 -nested-while: 9 -nested-break: 2 2 -for: 10 20 30 40 -for-print: 10 20 30 40 -for-idx: 0 1 2 3 -for-2arg: 10@0 20@1 30@2 40@3 -for-break: 10 20 -for-continue: 10 30 40 -for-slice: 10 20 30 -for-slice-idx: 0:10 1:20 2:30 -for-nested: (0,0) (0,1) (1,0) (1,1) -for-break-idx: 2 -multi: 1 2 3 -=== 5. Functions === -const: 42 -typed-const: 3.140000 -default-init: 0 -implicit-ret: 42 -early-ret: 5 -early-ret2: 99 -void-return: ok -generic-s32: 42 -generic-f32: 1.500000 -generic-bool: true -generic-multi: 30 -lambda: 14 -lambda-ret: 5.000000 -local-fn: 7 -fn-nested: 26 -varargs: 15 -spread: 60 -fp: 7 -fp-reassign: 12 -fp-apply: 30 -=== 6. Scoping === -inner: 200 -outer: 100 -shadow-type: 42 -shadow-type: 3.140000 -nest3: 3 -nest2: 2 -nest1: 1 -scope-isolate: 100 -scope-reuse: 1 -scope-reuse: 2 -scope-reuse: 1 -defer-a -defer-b -defer-c -d4 -d3 -d2 -d1 -inner-defer -outer-defer -defer-in-if: body -defer-in-if: deferred -=== 7. Builtins === -out-ok -sqrt: 3.000000 -sqrt-f64: 4.000000 -sizeof-s32: 4 -sizeof-f64: 8 -sizeof-struct: 8 -typeof: int -typeof-float: float -typeof-string: string -typeof-bool: bool -typeof-struct: struct -typeof-enum: enum -typename: Point -fieldcount: 2 -fieldcount-enum: 3 -fieldname0: x -fieldname1: y -fieldname-enum0: red -fieldname-enum2: blue -fieldval0: 11 -fieldval1: 22 -fieldidx: 1 -fieldidx-tagged: 0 -fieldidx-tagged2: 2 -cast: 3 -cast-int-f64: 42.000000 -=== 8. Comptime === -run-const: 25 -run-expr: 42 -run-chain: 30 -ct-opt-coalesce: 141 -ct-opt-unwrap: 77 -ct-opt-guard: 10 -insert-ok -insert-gen: 42 -=== 9. Flags === -flags: .read | .write -has-read: yes -flags-neg: no-read -flags-single: .execute -flags-all: .read | .write | .execute -flags-raw: 3 -flags-explicit: .vsync | .resizable -flags-explicit-raw: 68 ---- swap --- -var swap: 20 10 -arr swap: 3 1 -3-way: 3 1 2 -=== 15. Foreign === -foreign-rename: 42 -=== 16. Compound Assign === -f64+=f32: 13.000000 -s64-=s32: 93 -=== 17. Slice Ptr === -sl-ptr[0]: 20 -sl-ptr[1]: 30 -=== 18. Array of Structs === -arr-struct-x: 3 -for-struct: Point{x: 1, y: 2} -for-struct: Point{x: 3, y: 4} -=== 19. Local Fn Return === -local-struct: 42 99 -local-enum: .circle(2.500000) -=== 20. UFCS Return Type === -direct: 7 -ufcs: 7 -=== 21. Type-Named Vars === -s2: 42 -s2+1: 43 -=== 22. If-Struct === -if-struct: 10 20 -else-struct: 30 40 -=== 23. Nested Arrays === -m[0][0]: 1 -m[0][2]: 3 -m[1][0]: 4 -m[1][2]: 6 -=== 24. String Comparison === -str-eq: true -str-neq: true -str-diff: false -empty-eq: true -=== 25. Array Loop Mutation === -loop-fill: 1 2 3 4 -compound: 13 -=== 26. #using === -using-x: 1 -using-y: 2 -using-z: 3 -pkt-id: 10 -pkt-ver: 42 -pkt-pay: 99 -sprite-px: 10 -sprite-r: 255 -sprite-scale: 1 -say: hello (len=5) -n=42 -=== Tuples === -40 -2 -10 -10 -42 -0 -0 -=== UFCS Aliases === -42 -42 -42 -42 -42 -3 -3 -3 -2 -1 -99 -=== Tuple Operators === -true -false -true -false -1 -2 -3 -4 -1 -2 -1 -2 -1 -2 -true -false -false -true -true -true -true -false ---- directory imports --- -7 -30 -hello from testpkg ---- pipe operator --- -7 -30 -14 -hello world -piped ok! -alloc len: 5 -alloc[0]: 10 -alloc[4]: 50 -bytes len: 3 ---- allocators --- -gpa allocs: 2 -gpa final: 0 -arena chunks: 1 -arena overflow: 2 -arena a1: 42 -arena a3: 99 -arena reset idx: 0 -arena reset gpa: 1 -arena deinit: 0 -buf pos: 48 -buf overflow: 0 -buf reset: 0 -1 == (1) -(1) == 1 -1 == 1 ---- optionals --- -opt x: 42 -opt y: null -unwrap: 10 -coalesce a: 42 -coalesce b: 99 -if-bind x: 7 -if-bind y: none -match some: 55 -match none: 0 -wrap pos: 5 -wrap neg: null -opt field default: null -opt field set: 42 -opt param a: 42 -opt param b: 0 -opt param 7: 7 -generic opt 1: 5 -generic opt 2: 7 -generic opt 3: null -chain some: 10 -chain none: 0 -chain print: 20 -chain null: null -deep chain 1: 99 -deep chain 2: 0 -narrow x: 42 -narrow y else: null -narrow else x: 42 -guard some: 42 -guard none: 0 -and both: 10 20 -and one null -or guard: 7 -or guard null: 0 -nested narrow: 10 20 -guard loop: 3 -block-lambda: 50 -block-lambda: 0 -block-lambda: 100 -hello block -named-fn-type: 7 -xx-fnptr: 142 -closure-type: fn_ptr-nonnull=true -closure-type: env-null=true -closure-call: 15 -auto-promote: 20 -auto-promote-var: 10 -closure-capture: 52 -closure-snapshot: 15 -closure-nocap: 14 -closure-multi: 33 -closure-block: 60 -closure-block: 0 -closure-block: 100 -[LOG] hello -closure-hof: 30 -closure-hof-bare: 20 -closure-f32: 10.000000 -closure-bool: hello -closure-2p: 107 -closure-3p: 61 -closure-mix: Alice is 35 -closure-rbool: false true -closure-reduce: 115 -closure-factory: 105 110 -closure-struct: 10 20 -closure-compose: 30 -closure-indep: 15 50 -opt-closure: none -opt-closure: 15 -opt-closure-btn: 1 99 -opt-closure-btn: null -closure-ptr: 3 -closure-enum: 2 -closure-rstr: [INFO] ok -closure-rstruct: 11 22 -closure-linear: 37 -closure-clamp: 0 100 255 -closure-compose2: 12 -closure-chain: 22 -closure-map: 3 6 9 12 15 -closure-filter: 3 [3 4 5] -closure-sort: 5 4 3 2 1 -closure-fe: item 0=10 -closure-fe: item 1=20 -closure-fe: item 2=30 -closure-find: 2 -closure-any: false true -closure-struct-field: -5 -closure-btn: 1 99 -closure-counter: 1 2 3 -closure-acc: 105 115 -closure-loop: 115 -closure-reassign: 11 -closure-reassign: 20 -closure-snapstruct: 15 -closure-cap-promoted: 11 -closure-iife: 15 -closure-toggle: none -closure-toggle: true -closure-panel: main 800x600 -closure-chain-call: true -closure-loop-0: 1 -closure-loop-1: 11 -closure-loop-4: 41 -closure-cond: 10 -closure-form: submitted -closure-form: no cancel -closure-null-env: true -closure-slice: 10 20 30 -closure-arena: 15 -closure-gpa: 17 allocs=0 -closure-opt: 42 -closure-ropt: 50 -closure-ropt: none -closure-mixed: 10 -closure-mixed: 15 -closure-mixed: 25 -closure-factory-indep: 20 30 40 -closure-deep-chain: 122 -closure-8cap: 36 -closure-4param: 10 -closure-shared-ptr: 7 -closure-f64: true -closure-zerocap: 49 true -closure-struct-method: 7 -closure-multi-factory: 10 -closure-multi-factory: 20 -closure-multi-factory: 30 -closure-bool-cap: true false -closure-as-arg: 142 -closure-strfmt: hello world -closure-ptr-before: 10 -closure-ptr-after: 42 -closure-neg: -70 -closure-proto-cap: true -closure-chain-factory: 37 -closure-while-cond: 3 -closure-infer: 7 -closure-infer-arg: 15 -closure-infer-block: 12 -closure-infer-cap: 105 -closure-infer-factory: 35 -closure-infer-compose: 11 -closure-infer-void: 42 -=== Protocols === -P1.1: 3 -P1.2: 30 -P2.1: 42 -P2.2: 150 -P2.3: 5 10 -P3.1: 5 -P3.2: 12 -hi hi -P4.1: 2 -yo yo -P4.2: 2 -P4.3: 6 2 -P5.1: true false -P5.2: 10 20 -P5.5: true false -P5.3: true false -P6.1: true false -P6.2: true false -P6.3: true false -P6.4: 40 -P6.5: 20 -P7.1: 30 -P7.2: 10 300 -P2.6: 5 10 -=== DONE === +/Volumes/Store/dev/swipelab/sx/examples/50-smoke.sx:5:48: error: cannot read import 'modules/std.sx' (not a file or directory) +/Volumes/Store/dev/swipelab/sx/examples/50-smoke.sx:3:1: error: cannot read import '/Volumes/Store/dev/swipelab/sx/examples/modules/testpkg' (not a file or directory)