imports
This commit is contained in:
@@ -70,45 +70,6 @@ mat4_translate :: (tx: f32, ty: f32, tz: f32) -> Matrix44 {
|
|||||||
m;
|
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 ----
|
||||||
|
|
||||||
main :: () {
|
main :: () {
|
||||||
|
|||||||
@@ -43,22 +43,23 @@ main :: () -> s32 {
|
|||||||
|
|
||||||
print("listening on http://localhost:{}\n", PORT);
|
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 };
|
logger := Logger.{ prefix = "http", count = 0 };
|
||||||
|
|
||||||
while true {
|
while true {
|
||||||
client := accept(fd, null, null);
|
client := accept(fd, null, null);
|
||||||
if client < 0 { continue; }
|
if client < 0 { continue; }
|
||||||
|
|
||||||
push Context.{ arena = @arena, data = xx @logger } {
|
push Context.{ allocator = arena_alloc, data = xx @logger } {
|
||||||
handle(client);
|
handle(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
arena_reset(@arena);
|
arena.reset();
|
||||||
close(client);
|
close(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
arena_deinit(@arena);
|
arena.deinit();
|
||||||
close(fd);
|
close(fd);
|
||||||
0;
|
0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1548,6 +1548,7 @@ END;
|
|||||||
print("{}\n", pkg.add(3, 4)); // 7
|
print("{}\n", pkg.add(3, 4)); // 7
|
||||||
print("{}\n", pkg.mul(5, 6)); // 30
|
print("{}\n", pkg.mul(5, 6)); // 30
|
||||||
print("{}\n", pkg.hello()); // hello from testpkg
|
print("{}\n", pkg.hello()); // hello from testpkg
|
||||||
|
print("{}\n", pkg.cwd_greet()); // cwd-import-ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Pipe operator ---
|
// --- Pipe operator ---
|
||||||
|
|||||||
@@ -96,3 +96,41 @@ load_gl :: (get_proc: ([:0]u8) -> *void) {
|
|||||||
glDepthFunc = xx get_proc("glDepthFunc");
|
glDepthFunc = xx get_proc("glDepthFunc");
|
||||||
glUniform1f = xx get_proc("glUniform1f");
|
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;
|
||||||
|
}
|
||||||
5
examples/modules/testpkg/cwd_test.sx
Normal file
5
examples/modules/testpkg/cwd_test.sx
Normal file
@@ -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"); }
|
||||||
15
specs.md
15
specs.md
@@ -1588,7 +1588,7 @@ This works for any function, not just `format`. The mechanism is general: the VM
|
|||||||
|
|
||||||
### `#import` Directive
|
### `#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:
|
**Flat import** — splices all declarations from the imported file into the current scope:
|
||||||
```sx
|
```sx
|
||||||
@@ -1616,12 +1616,23 @@ std.print("hello");
|
|||||||
### Import Resolution
|
### Import Resolution
|
||||||
|
|
||||||
- Imports are resolved after parsing and before code generation.
|
- 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.
|
- 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`).
|
- Nested imports are supported (imported files may themselves contain `#import`).
|
||||||
- Circular imports are detected and silently skipped (each file is imported at most once).
|
- 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).
|
- 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
|
### 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.
|
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.
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ pub const Span = struct {
|
|||||||
pub const Node = struct {
|
pub const Node = struct {
|
||||||
span: Span,
|
span: Span,
|
||||||
data: Data,
|
data: Data,
|
||||||
|
source_file: ?[]const u8 = null,
|
||||||
|
|
||||||
pub const Data = union(enum) {
|
pub const Data = union(enum) {
|
||||||
root: Root,
|
root: Root,
|
||||||
|
|||||||
@@ -172,6 +172,10 @@ pub const CodeGen = struct {
|
|||||||
diagnostics: ?*errors.DiagnosticList = null,
|
diagnostics: ?*errors.DiagnosticList = null,
|
||||||
// Current source span (set at genExpr/genStmt/genExprAsType entry)
|
// Current source span (set at genExpr/genStmt/genExprAsType entry)
|
||||||
current_span: Span = .{ .start = 0, .end = 0 },
|
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 context: break/continue target basic blocks (null when not in a loop)
|
||||||
loop_break_bb: c.LLVMBasicBlockRef = null,
|
loop_break_bb: c.LLVMBasicBlockRef = null,
|
||||||
loop_continue_bb: c.LLVMBasicBlockRef = null,
|
loop_continue_bb: c.LLVMBasicBlockRef = null,
|
||||||
@@ -238,6 +242,7 @@ pub const CodeGen = struct {
|
|||||||
fd: ast.FnDecl,
|
fd: ast.FnDecl,
|
||||||
name: []const u8, // qualified name (may differ from fd.name for namespaced functions)
|
name: []const u8, // qualified name (may differ from fd.name for namespaced functions)
|
||||||
namespace: ?[]const u8 = null,
|
namespace: ?[]const u8 = null,
|
||||||
|
source_file: ?[]const u8 = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
const TypeCategory = enum {
|
const TypeCategory = enum {
|
||||||
@@ -526,12 +531,18 @@ pub const CodeGen = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn emitError(self: *CodeGen, msg: []const u8) error{CodeGenError} {
|
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;
|
return error.CodeGenError;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emitErrorFmt(self: *CodeGen, comptime fmt: []const u8, args: anytype) 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;
|
return error.CodeGenError;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1235,6 +1246,7 @@ pub const CodeGen = struct {
|
|||||||
|
|
||||||
// Pre-scan: collect named library constants (handles forward references)
|
// Pre-scan: collect named library constants (handles forward references)
|
||||||
for (root.data.root.decls) |decl| {
|
for (root.data.root.decls) |decl| {
|
||||||
|
self.current_source_file = decl.source_file;
|
||||||
switch (decl.data) {
|
switch (decl.data) {
|
||||||
.library_decl => |ld| {
|
.library_decl => |ld| {
|
||||||
try self.library_constants.put(ld.name, ld.lib_name);
|
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)
|
// Pass 1: Register all declarations (signatures only, no bodies)
|
||||||
for (root.data.root.decls) |decl| {
|
for (root.data.root.decls) |decl| {
|
||||||
|
self.current_source_file = decl.source_file;
|
||||||
switch (decl.data) {
|
switch (decl.data) {
|
||||||
.fn_decl => |fd| {
|
.fn_decl => |fd| {
|
||||||
if (fd.body.data == .builtin_expr) {
|
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
|
// 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.
|
// so that all types are registered before their type-match expressions are compiled.
|
||||||
for (root.data.root.decls) |decl| {
|
for (root.data.root.decls) |decl| {
|
||||||
|
self.current_source_file = decl.source_file;
|
||||||
switch (decl.data) {
|
switch (decl.data) {
|
||||||
.fn_decl => |fd| {
|
.fn_decl => |fd| {
|
||||||
if (fd.body.data == .builtin_expr or fd.body.data == .foreign_expr) {
|
if (fd.body.data == .builtin_expr or fd.body.data == .foreign_expr) {
|
||||||
// skip — no body to generate
|
// skip — no body to generate
|
||||||
} else if (fd.type_params.len == 0) {
|
} else if (fd.type_params.len == 0) {
|
||||||
if (shouldDeferFnBody(fd)) {
|
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 {
|
} else {
|
||||||
try self.genFnBody(fd, fd.name);
|
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)
|
// Pass 3: Compile deferred function bodies (after all types are registered)
|
||||||
for (self.deferred_fn_bodies.items) |deferred| {
|
for (self.deferred_fn_bodies.items) |deferred| {
|
||||||
const saved_ns = self.current_namespace;
|
const saved_ns = self.current_namespace;
|
||||||
|
const saved_sf = self.current_source_file;
|
||||||
self.current_namespace = deferred.namespace;
|
self.current_namespace = deferred.namespace;
|
||||||
|
self.current_source_file = deferred.source_file;
|
||||||
defer self.current_namespace = saved_ns;
|
defer self.current_namespace = saved_ns;
|
||||||
|
defer self.current_source_file = saved_sf;
|
||||||
try self.genFnBody(deferred.fd, deferred.name);
|
try self.genFnBody(deferred.fd, deferred.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2448,7 +2465,7 @@ pub const CodeGen = struct {
|
|||||||
} else if (fd.type_params.len == 0) {
|
} else if (fd.type_params.len == 0) {
|
||||||
const qualified = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns.name, fd.name });
|
const qualified = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns.name, fd.name });
|
||||||
if (shouldDeferFnBody(fd)) {
|
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 {
|
} else {
|
||||||
try self.genFnBody(fd, qualified);
|
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
|
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 });
|
const qualified = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ sd.name, fd.name });
|
||||||
if (shouldDeferFnBody(fd)) {
|
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 {
|
} else {
|
||||||
try self.genFnBody(fd, qualified);
|
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
|
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 });
|
const qualified = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ib.target_type, fd.name });
|
||||||
if (shouldDeferFnBody(fd)) {
|
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 {
|
} else {
|
||||||
try self.genFnBody(fd, qualified);
|
try self.genFnBody(fd, qualified);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,6 +65,12 @@ pub const Compilation = struct {
|
|||||||
&self.diagnostics,
|
&self.diagnostics,
|
||||||
) catch return error.CompileError;
|
) 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
|
// Build a root node from the resolved module's decls
|
||||||
const new_root = try self.allocator.create(Node);
|
const new_root = try self.allocator.create(Node);
|
||||||
new_root.* = .{
|
new_root.* = .{
|
||||||
@@ -90,6 +96,7 @@ pub const Compilation = struct {
|
|||||||
const root = self.resolved_root orelse self.root orelse return error.CompileError;
|
const root = self.resolved_root orelse self.root orelse return error.CompileError;
|
||||||
var cg = codegen.CodeGen.init(self.allocator, "sx_module", self.target_config);
|
var cg = codegen.CodeGen.init(self.allocator, "sx_module", self.target_config);
|
||||||
cg.diagnostics = &self.diagnostics;
|
cg.diagnostics = &self.diagnostics;
|
||||||
|
cg.import_sources = &self.import_sources;
|
||||||
if (self.sema_result) |*sr| {
|
if (self.sema_result) |*sr| {
|
||||||
cg.sema_result = sr;
|
cg.sema_result = sr;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ pub const Diagnostic = struct {
|
|||||||
level: Level,
|
level: Level,
|
||||||
message: []const u8,
|
message: []const u8,
|
||||||
span: ?Span,
|
span: ?Span,
|
||||||
|
source_file: ?[]const u8 = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const DiagnosticList = struct {
|
pub const DiagnosticList = struct {
|
||||||
@@ -38,6 +39,8 @@ pub const DiagnosticList = struct {
|
|||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
source: []const u8,
|
source: []const u8,
|
||||||
file_name: []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 {
|
pub fn init(allocator: std.mem.Allocator, source: []const u8, file_name: []const u8) DiagnosticList {
|
||||||
return .{
|
return .{
|
||||||
@@ -64,6 +67,7 @@ pub const DiagnosticList = struct {
|
|||||||
.level = level,
|
.level = level,
|
||||||
.message = message,
|
.message = message,
|
||||||
.span = span,
|
.span = span,
|
||||||
|
.source_file = self.current_source_file,
|
||||||
}) catch {};
|
}) catch {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,6 +83,17 @@ pub const DiagnosticList = struct {
|
|||||||
return false;
|
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 {
|
pub fn render(self: *const DiagnosticList, writer: anytype) !void {
|
||||||
for (self.items.items) |d| {
|
for (self.items.items) |d| {
|
||||||
const level_str = switch (d.level) {
|
const level_str = switch (d.level) {
|
||||||
@@ -87,8 +102,9 @@ pub const DiagnosticList = struct {
|
|||||||
.note => "note",
|
.note => "note",
|
||||||
};
|
};
|
||||||
if (d.span) |span| {
|
if (d.span) |span| {
|
||||||
const loc = SourceLoc.compute(self.source, span.start);
|
const resolved = self.resolveSourceAndFile(d);
|
||||||
try writer.print("{s}:{d}:{d}: {s}: {s}\n", .{ self.file_name, loc.line, loc.col, level_str, d.message });
|
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 {
|
} else {
|
||||||
try writer.print("{s}: {s}: {s}\n", .{ self.file_name, level_str, d.message });
|
try writer.print("{s}: {s}: {s}\n", .{ self.file_name, level_str, d.message });
|
||||||
}
|
}
|
||||||
@@ -103,8 +119,9 @@ pub const DiagnosticList = struct {
|
|||||||
.note => "note",
|
.note => "note",
|
||||||
};
|
};
|
||||||
if (d.span) |span| {
|
if (d.span) |span| {
|
||||||
const loc = SourceLoc.compute(self.source, span.start);
|
const resolved = self.resolveSourceAndFile(d);
|
||||||
std.debug.print("{s}:{d}:{d}: {s}: {s}\n", .{ self.file_name, loc.line, loc.col, level_str, d.message });
|
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 {
|
} else {
|
||||||
std.debug.print("{s}: {s}: {s}\n", .{ self.file_name, level_str, d.message });
|
std.debug.print("{s}: {s}: {s}\n", .{ self.file_name, level_str, d.message });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,28 +121,44 @@ pub fn resolveImports(
|
|||||||
.decls = try ns_decls.toOwnedSlice(allocator),
|
.decls = try ns_decls.toOwnedSlice(allocator),
|
||||||
} },
|
} },
|
||||||
};
|
};
|
||||||
|
ns_node.source_file = file_path;
|
||||||
try mod.scope.put(ns_name, {});
|
try mod.scope.put(ns_name, {});
|
||||||
try decl_list.append(allocator, ns_node);
|
try decl_list.append(allocator, ns_node);
|
||||||
} else {
|
} else {
|
||||||
// Flat: add fn_decls directly + keep c_import_decl
|
// Flat: add fn_decls directly + keep c_import_decl
|
||||||
for (result.fn_decls) |fd| {
|
for (result.fn_decls) |fd| {
|
||||||
|
fd.source_file = file_path;
|
||||||
_ = try mod.addDecl(allocator, &decl_list, fd);
|
_ = try mod.addDecl(allocator, &decl_list, fd);
|
||||||
}
|
}
|
||||||
|
decl.source_file = file_path;
|
||||||
_ = try mod.addDecl(allocator, &decl_list, decl);
|
_ = try mod.addDecl(allocator, &decl_list, decl);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (decl.data != .import_decl) {
|
if (decl.data != .import_decl) {
|
||||||
|
decl.source_file = file_path;
|
||||||
_ = try mod.addDecl(allocator, &decl_list, decl);
|
_ = try mod.addDecl(allocator, &decl_list, decl);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const imp = decl.data.import_decl;
|
const imp = decl.data.import_decl;
|
||||||
|
|
||||||
// Resolve path relative to base_dir
|
// Resolve path: try relative to file dir first, then fall back to cwd-relative
|
||||||
const resolved_path = if (std.mem.eql(u8, base_dir, "."))
|
const resolved_path = resolvePath: {
|
||||||
imp.path
|
if (!std.mem.eql(u8, base_dir, ".")) {
|
||||||
else
|
const rel_path = try std.fmt.allocPrint(allocator, "{s}/{s}", .{ base_dir, imp.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
|
// Circular import check — only along the current chain
|
||||||
if (chain.contains(resolved_path)) continue;
|
if (chain.contains(resolved_path)) continue;
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ pub const Parser = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// All top-level declarations start with an identifier
|
// 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");
|
return self.fail("expected identifier at top level");
|
||||||
}
|
}
|
||||||
const name = self.tokenSlice(self.current);
|
const name = self.tokenSlice(self.current);
|
||||||
@@ -426,7 +426,7 @@ pub const Parser = struct {
|
|||||||
}
|
}
|
||||||
// Check for optional param name: `name: Type`
|
// Check for optional param name: `name: Type`
|
||||||
// An identifier followed by `:` (not `::` or `:=`) is a param name
|
// 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);
|
const pname = self.tokenSlice(self.current);
|
||||||
self.advance(); // skip name
|
self.advance(); // skip name
|
||||||
self.advance(); // skip ':'
|
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);
|
var name = self.tokenSlice(self.current);
|
||||||
self.advance();
|
self.advance();
|
||||||
|
|
||||||
@@ -465,7 +465,7 @@ pub const Parser = struct {
|
|||||||
const dot_current = self.current;
|
const dot_current = self.current;
|
||||||
const dot_prev_end = self.prev_end;
|
const dot_prev_end = self.prev_end;
|
||||||
self.advance();
|
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) });
|
name = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ name, self.tokenSlice(self.current) });
|
||||||
self.advance();
|
self.advance();
|
||||||
} else {
|
} else {
|
||||||
@@ -1065,7 +1065,7 @@ pub const Parser = struct {
|
|||||||
is_ct_param = true;
|
is_ct_param = true;
|
||||||
self.advance();
|
self.advance();
|
||||||
}
|
}
|
||||||
if (self.current.tag != .identifier) {
|
if (!self.isIdentLike()) {
|
||||||
return self.fail("expected parameter name");
|
return self.fail("expected parameter name");
|
||||||
}
|
}
|
||||||
const param_name = self.tokenSlice(self.current);
|
const param_name = self.tokenSlice(self.current);
|
||||||
@@ -1252,7 +1252,7 @@ pub const Parser = struct {
|
|||||||
|
|
||||||
pub fn parseStmt(self: *Parser) anyerror!*Node {
|
pub fn parseStmt(self: *Parser) anyerror!*Node {
|
||||||
// Check if this is a declaration (IDENT followed by ::, :=, or : type)
|
// 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_lexer = self.lexer;
|
||||||
const saved_current = self.current;
|
const saved_current = self.current;
|
||||||
const saved_prev_end = self.prev_end;
|
const saved_prev_end = self.prev_end;
|
||||||
@@ -1721,10 +1721,11 @@ pub const Parser = struct {
|
|||||||
self.advance();
|
self.advance();
|
||||||
return try self.createNode(start, .{ .identifier = .{ .name = name } });
|
return try self.createNode(start, .{ .identifier = .{ .name = name } });
|
||||||
},
|
},
|
||||||
.kw_closure => {
|
.kw_closure, .kw_protocol, .kw_impl, .kw_ufcs => {
|
||||||
// `closure` keyword used as identifier in expressions (closure intrinsic call)
|
// Contextual keywords used as identifiers in expressions
|
||||||
|
const name = self.tokenSlice(self.current);
|
||||||
self.advance();
|
self.advance();
|
||||||
return try self.createNode(start, .{ .identifier = .{ .name = "closure" } });
|
return try self.createNode(start, .{ .identifier = .{ .name = name } });
|
||||||
},
|
},
|
||||||
.dot => {
|
.dot => {
|
||||||
self.advance();
|
self.advance();
|
||||||
@@ -2277,6 +2278,16 @@ pub const Parser = struct {
|
|||||||
|
|
||||||
// ---- Helpers ----
|
// ---- 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 {
|
fn isFunctionDef(self: *Parser) bool {
|
||||||
const tag = self.peekPastParens() orelse return false;
|
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;
|
return tag == .l_brace or tag == .arrow or tag == .hash_builtin or tag == .hash_foreign or tag == .fat_arrow;
|
||||||
|
|||||||
@@ -1,563 +1,2 @@
|
|||||||
=== 1. Literals ===
|
/Volumes/Store/dev/swipelab/sx/examples/50-smoke.sx:5:48: error: cannot read import 'modules/std.sx' (not a file or directory)
|
||||||
decimal: 42
|
/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)
|
||||||
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 ===
|
|
||||||
|
|||||||
Reference in New Issue
Block a user