From a63bec226ca9367c282de1fa729db767b41c4a70 Mon Sep 17 00:00:00 2001 From: Piotr Fila Date: Thu, 25 Jun 2026 09:17:21 +0200 Subject: [PATCH 01/11] Add anyverz module --- modules/anyverz/build.zig | 119 ++++++++++++++++++++++++++++++++++ modules/anyverz/build.zig.zon | 10 +++ tools/regz/src/xml_c.zig | 7 ++ 3 files changed, 136 insertions(+) create mode 100644 modules/anyverz/build.zig create mode 100644 modules/anyverz/build.zig.zon create mode 100644 tools/regz/src/xml_c.zig diff --git a/modules/anyverz/build.zig b/modules/anyverz/build.zig new file mode 100644 index 000000000..14a1621de --- /dev/null +++ b/modules/anyverz/build.zig @@ -0,0 +1,119 @@ +const builtin = @import("builtin"); +const std = @import("std"); + +const zigVersionNotSupported = + @compileError("Zig version " ++ + builtin.zig_version_string ++ + " is not supported by anyverz"); + +pub const ZigVersion = enum { @"0.16", @"0.17" }; + +pub const zig_version: ZigVersion = switch (builtin.zig_version.minor) { + 16 => .@"0.16", + 17 => .@"0.17", + else => zigVersionNotSupported, +}; + +comptime { + std.testing.expectEqual(0, builtin.zig_version.major) catch zigVersionNotSupported; + _ = zig_version; +} + +// fields + +pub const StructFieldAttributes = switch (zig_version) { + .@"0.16" => std.builtin.Type.StructField.Attributes, + .@"0.17" => std.builtin.Type.Struct.FieldAttributes, +}; + +pub fn fieldsLen(T: type) comptime_int { + @setEvalBranchQuota(10000); + return std.meta.fieldNames(T).len; +} + +pub const fieldNames = std.meta.fieldNames; + +pub fn fieldTypes(T: type) [fieldsLen(T)]type { + @setEvalBranchQuota(10000); + comptime switch (zig_version) { + .@"0.16" => { + const fieldInfos = std.meta.fields(T); + var types: [fieldInfos.len]type = undefined; + for (fieldInfos, 0..) |field, i| types[i] = field.type; + return types; + }, + .@"0.17" => return std.meta.fieldTypes(T)[0..fieldsLen(T)].*, + }; +} + +pub fn fieldAttrs(T: type) [fieldsLen(T)]StructFieldAttributes { + @setEvalBranchQuota(10000); + comptime switch (zig_version) { + .@"0.16" => { + const fieldInfos = std.meta.fields(T); + var attrs: [fieldInfos.len]StructFieldAttributes = undefined; + for (fieldInfos, 0..) |field, i| attrs[i] = .{ + .@"comptime" = field.is_comptime, + .@"align" = field.alignment, + .default_value_ptr = field.default_value_ptr, + }; + return attrs; + }, + .@"0.17" => switch (@typeInfo(T)) { + inline .@"struct", .@"union" => |info| return info.field_attrs, + else => @compileError("Expected struct or union type, found '" ++ @typeName(T) ++ "'"), + }, + }; +} + +// declarations + +pub fn declsLen(T: type) usize { + @setEvalBranchQuota(10000); + return comptime std.meta.declarations(T).len; +} + +pub fn declNames(comptime T: type) [declsLen(T)][:0]const u8 { + @setEvalBranchQuota(10000); + comptime switch (zig_version) { + .@"0.16" => { + const declInfos = std.meta.declarations(T); + var names: [declInfos.len][:0]const u8 = undefined; + for (declInfos, 0..) |decl, i| names[i] = decl.name; + return names; + }, + .@"0.17" => return std.meta.declarations(T)[0..declsLen(T)].*, + }; +} + +// build utilities + +pub fn addPassthruArgs(b: *std.Build, run_step: *std.Build.Step.Run) void { + switch (zig_version) { + .@"0.16" => if (b.args) |args| run_step.addArgs(args), + .@"0.17" => run_step.addPassthruArgs(), + } +} + +pub fn buildRoot(b: *std.Build) std.Io.Dir { + return switch (zig_version) { + .@"0.16" => b.build_root.handle, + .@"0.17" => b.root.root_dir.handle, + }; +} + +pub fn findProgramLazy(b: *std.Build, names: []const [:0]const u8) std.Build.LazyPath { + return switch (zig_version) { + .@"0.16" => .{ .cwd_relative = b.findProgram(names, &.{}) catch + std.debug.panic("Could not find program {s}", .{names[0]}) }, + .@"0.17" => b.findProgramLazy(.{ .names = names }), + }; +} + +pub fn build(b: *std.Build) void { + _ = b.addModule("anyverz", .{ + .root_source_file = b.path("build.zig"), + .target = b.standardTargetOptions(.{}), + .optimize = b.standardOptimizeOption(.{}), + }); +} diff --git a/modules/anyverz/build.zig.zon b/modules/anyverz/build.zig.zon new file mode 100644 index 000000000..d18bd7638 --- /dev/null +++ b/modules/anyverz/build.zig.zon @@ -0,0 +1,10 @@ +.{ + .name = .anyverz, + .version = "0.0.1", + .minimum_zig_version = "0.16.0", + .paths = .{ + "build.zig", + "build.zig.zon", + }, + .fingerprint = 0xdb00016624a5ee02, +} diff --git a/tools/regz/src/xml_c.zig b/tools/regz/src/xml_c.zig new file mode 100644 index 000000000..3be03ce83 --- /dev/null +++ b/tools/regz/src/xml_c.zig @@ -0,0 +1,7 @@ +// Only used when compiling with zig 0.16 +pub const c = @cImport({ + @cDefine("LIBXML_TREE_ENABLED", {}); + @cDefine("LIBXML_SCHEMAS_ENABLED", {}); + @cDefine("LIBXML_READER_ENABLED", {}); + @cInclude("libxml/xmlreader.h"); +}); From 8a1b46fc761b98e079601c5c2a576972dfae4e46 Mon Sep 17 00:00:00 2001 From: Piotr Fila Date: Thu, 25 Jun 2026 11:20:39 +0200 Subject: [PATCH 02/11] Make the build system work on both zig 0.16 and 0.17 --- build-internals/build.zig | 4 +++- build-internals/build.zig.zon | 1 + build.zig | 13 +++++++------ build.zig.zon | 1 + port/raspberrypi/rp2xxx/build.zig | 6 +++++- port/raspberrypi/rp2xxx/build.zig.zon | 1 + sim/aviron/build.zig | 16 ++++++++-------- sim/aviron/build.zig.zon | 1 + tools/regz/build.zig | 4 +++- tools/regz/build.zig.zon | 9 +++++---- 10 files changed, 35 insertions(+), 21 deletions(-) diff --git a/build-internals/build.zig b/build-internals/build.zig index 12d254889..5d5d70835 100644 --- a/build-internals/build.zig +++ b/build-internals/build.zig @@ -3,6 +3,8 @@ const Build = std.Build; const LazyPath = Build.LazyPath; const Module = Build.Module; +const anyverz = @import("anyverz"); + const regz = @import("regz"); pub const Patch = regz.patch.Patch; const uf2 = @import("uf2"); @@ -103,7 +105,7 @@ pub const Target = struct { const ret = from.dep.builder.allocator.create(Target) catch @panic("out of memory"); ret.* = from.*; - inline for (@typeInfo(DeriveOptions).@"struct".field_names) |field_name| { + inline for (comptime anyverz.fieldNames(DeriveOptions)) |field_name| { const value = @field(options, field_name); if (value) |val| @field(ret, field_name) = val; } diff --git a/build-internals/build.zig.zon b/build-internals/build.zig.zon index df15de78f..bef461023 100644 --- a/build-internals/build.zig.zon +++ b/build-internals/build.zig.zon @@ -7,6 +7,7 @@ .uf2 = .{ .path = "../tools/uf2" }, .@"esp-image" = .{ .path = "../tools/esp-image" }, .dfu = .{ .path = "../tools/dfu" }, + .anyverz = .{ .path = "../modules/anyverz" }, }, .paths = .{ "build.zig", diff --git a/build.zig b/build.zig index 9c518c7b9..7590469be 100644 --- a/build.zig +++ b/build.zig @@ -3,6 +3,8 @@ const Build = std.Build; const LazyPath = Build.LazyPath; const assert = std.debug.assert; +const anyverz = @import("anyverz"); + const internals = @import("build-internals"); pub const Target = internals.Target; pub const Cpu = internals.Cpu; @@ -74,7 +76,7 @@ pub const PortSelect = struct { pub const all: PortSelect = blk: { var ret: PortSelect = undefined; - for (@typeInfo(PortSelect).@"struct".field_names) |field_name| { + for (anyverz.fieldNames(PortSelect)) |field_name| { @field(ret, field_name) = true; } @@ -82,9 +84,8 @@ pub const PortSelect = struct { }; comptime { - const info = @typeInfo(PortSelect).@"struct"; // assumes fields are in the same order as the port list - for (port_list, info.field_names, info.field_attrs) |port_entry, field_name, field_attr| { + for (port_list, anyverz.fieldNames(PortSelect), anyverz.fieldAttrs(PortSelect)) |port_entry, field_name, field_attr| { assert(std.mem.eql(u8, port_entry.name, field_name)); const default_value_ptr: *const bool = @ptrCast(field_attr.default_value_ptr); assert(false == default_value_ptr.*); @@ -144,7 +145,7 @@ pub fn MicroBuild(port_select: PortSelect) type { var field_names: [count][]const u8 = undefined; var field_types: [count]type = undefined; - var field_attrs: [count]std.builtin.Type.Struct.FieldAttributes = undefined; + var field_attrs: [count]anyverz.StructFieldAttributes = undefined; if (count > 0) { var i: usize = 0; @@ -776,7 +777,7 @@ pub inline fn custom_lazy_import( const deps = build_runner.dependencies; const pkg_hash = custom_find_import_pkg_hash_or_fatal(dep_name); - inline for (@typeInfo(deps.packages).@"struct".decl_names) |decl_name| { + inline for (comptime anyverz.declNames(deps.packages)) |decl_name| { if (comptime std.mem.eql(u8, decl_name, pkg_hash)) { const pkg = @field(deps.packages, decl_name); const available = !@hasDecl(pkg, "available") or pkg.available; @@ -798,7 +799,7 @@ inline fn custom_find_import_pkg_hash_or_fatal(comptime dep_name: []const u8) [] const build_runner = @import("root"); const deps = build_runner.dependencies; - const pkg_deps = comptime for (@typeInfo(deps.packages).@"struct".decl_names) |decl_name| { + const pkg_deps = comptime for (anyverz.declNames(deps.packages)) |decl_name| { const pkg_hash = decl_name; const pkg = @field(deps.packages, pkg_hash); if (@hasDecl(pkg, "build_zig") and pkg.build_zig == @This()) break pkg.deps; diff --git a/build.zig.zon b/build.zig.zon index ae37a1652..45ebe7c5c 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -24,6 +24,7 @@ .@"modules/network" = .{ .path = "modules/network" }, .@"modules/riscv32-common" = .{ .path = "modules/riscv32-common" }, .@"modules/rtt" = .{ .path = "modules/rtt" }, + .anyverz = .{ .path = "modules/anyverz" }, // simulators .@"sim/aviron" = .{ .path = "sim/aviron", .lazy = true }, diff --git a/port/raspberrypi/rp2xxx/build.zig b/port/raspberrypi/rp2xxx/build.zig index 02a0ffef0..b7980244a 100644 --- a/port/raspberrypi/rp2xxx/build.zig +++ b/port/raspberrypi/rp2xxx/build.zig @@ -1,5 +1,6 @@ const std = @import("std"); const microzig = @import("microzig/build-internals"); +const anyverz = @import("anyverz"); const Self = @This(); @@ -365,7 +366,10 @@ fn get_bootrom(b: *std.Build, target: *const microzig.Target, rom: BootROM) std. const rom_objcopy = b.addObjCopy(rom_exe.getEmittedBin(), .{ .basename = b.fmt("{s}.bin", .{@tagName(rom)}), - .format = .binary, + .format = switch (anyverz.zig_version) { + .@"0.17" => .binary, + .@"0.16" => .bin, + }, }); return rom_objcopy.getOutput(); diff --git a/port/raspberrypi/rp2xxx/build.zig.zon b/port/raspberrypi/rp2xxx/build.zig.zon index f69ad2304..9e296b902 100644 --- a/port/raspberrypi/rp2xxx/build.zig.zon +++ b/port/raspberrypi/rp2xxx/build.zig.zon @@ -11,6 +11,7 @@ .lazy = true, }, .@"bounded-array" = .{ .path = "../../../modules/bounded-array" }, + .anyverz = .{ .path = "../../../modules/anyverz" }, }, .paths = .{ "README.md", diff --git a/sim/aviron/build.zig b/sim/aviron/build.zig index 3ad8f5f99..cda4d067b 100644 --- a/sim/aviron/build.zig +++ b/sim/aviron/build.zig @@ -3,6 +3,8 @@ const Build = std.Build; const LazyPath = Build.LazyPath; const ResolvedTarget = Build.ResolvedTarget; +const anyverz = @import("anyverz"); + const TestSuiteConfig = @import("src/testconfig.zig").TestSuiteConfig; const samples = [_][]const u8{ @@ -73,7 +75,7 @@ pub fn build(b: *Build) !void { const run_cmd = b.addRunArtifact(aviron_exe); run_cmd.step.dependOn(b.getInstallStep()); - run_cmd.addPassthruArgs(); + anyverz.addPassthruArgs(b, run_cmd); run_step.dependOn(&run_cmd.step); const avr_target = b.resolveTargetQuery(avr_target_query); @@ -156,7 +158,7 @@ fn add_test_suite( // Scan the testsuite directory for files. Based on the extension, either load or compile them. // Files in testsuite.avr-gcc will be compiled with avr-gcc and have the output copied to // this directory. - var walkdir = try b.root.root_dir.handle.openDir(io, "testsuite", .{ .iterate = true }); + var walkdir = try anyverz.buildRoot(b).openDir(io, "testsuite", .{ .iterate = true }); defer walkdir.close(io); var walker = try walkdir.walk(b.allocator); @@ -192,7 +194,7 @@ fn add_test_suite( }; const ext = std.fs.path.extension(entry.basename); - const action: FileAction = inline for (@typeInfo(@TypeOf(extension_to_action)).@"struct".field_names) |field_name| { + const action: FileAction = inline for (comptime anyverz.fieldNames(@TypeOf(extension_to_action))) |field_name| { const action: FileAction = @field(extension_to_action, field_name); if (std.mem.eql(u8, ext, "." ++ field_name)) @@ -331,12 +333,10 @@ fn add_test_suite_update( b: *Build, invoke_step: *Build.Step, ) !void { - const avr_gcc = b.findProgramLazy(.{ - .names = &.{"avr-gcc"}, - }); + const avr_gcc = anyverz.findProgramLazy(b, &.{"avr-gcc"}); const io = b.graph.io; - var walkdir = try b.root.root_dir.handle.openDir(io, "testsuite.avr-gcc", .{ .iterate = true }); + var walkdir = try anyverz.buildRoot(b).openDir(io, "testsuite.avr-gcc", .{ .iterate = true }); defer walkdir.close(io); var walker = try walkdir.walk(b.allocator); @@ -364,7 +364,7 @@ fn add_test_suite_update( }; const ext = std.fs.path.extension(entry.basename); - const action: FileAction = inline for (@typeInfo(@TypeOf(extension_to_action)).@"struct".field_names) |field_name| { + const action: FileAction = inline for (comptime anyverz.fieldNames(@TypeOf(extension_to_action))) |field_name| { const action: FileAction = @field(extension_to_action, field_name); if (std.mem.eql(u8, ext, "." ++ field_name)) break action; diff --git a/sim/aviron/build.zig.zon b/sim/aviron/build.zig.zon index 35f0f6440..43643f01d 100644 --- a/sim/aviron/build.zig.zon +++ b/sim/aviron/build.zig.zon @@ -4,6 +4,7 @@ .fingerprint = 0xfc536f957aeaeb54, .dependencies = .{ .@"bounded-array" = .{ .path = "../../modules/bounded-array" }, + .anyverz = .{ .path = "../../modules/anyverz" }, .flags = .{ .path = "../../tools/flags" }, .ihex = .{ .url = "git+https://github.com/mattnite/zig-ihex#9deb291104ecd7dcaab0db27bf05252764600947", diff --git a/tools/regz/build.zig b/tools/regz/build.zig index ab9f02247..0f0afacff 100644 --- a/tools/regz/build.zig +++ b/tools/regz/build.zig @@ -1,6 +1,8 @@ const std = @import("std"); const Build = std.Build; +const anyverz = @import("anyverz"); + pub const patch = @import("src/patch.zig"); pub fn build(b: *Build) !void { @@ -67,7 +69,7 @@ pub fn build(b: *Build) !void { b.installArtifact(regz); const run_cmd = b.addRunArtifact(regz); - run_cmd.addPassthruArgs(); + anyverz.addPassthruArgs(b, run_cmd); run_cmd.step.dependOn(b.getInstallStep()); const run_step = b.step("run", "Run the app"); diff --git a/tools/regz/build.zig.zon b/tools/regz/build.zig.zon index 98393d793..4f2574ac1 100644 --- a/tools/regz/build.zig.zon +++ b/tools/regz/build.zig.zon @@ -10,12 +10,13 @@ }, .dependencies = .{ .libxml2 = .{ - .url = "git+https://github.com/allyourcodebase/libxml2.git#215ef60bb2a82386b2d90631142ecaf5069d9793", - .hash = "libxml2-2.15.1-2-qHdjhjJXAAD98vpy0IF6EsCNQUd8BBloVE-kCqbk1njK", + .url = "git+https://github.com/piotrfila/libxml2.git#6e7036d5e52aef1dd1ff99ba7083df16754e94af", + .hash = "libxml2-2.15.1-2-qHdjhtlVAAASX3jLnB19epDXk1zTG0OnIMvY799BTg6b", }, .zqlite = .{ - .url = "git+https://github.com/karlseguin/zqlite.zig#3cbe6a9473872efde0c7e0bb996e5595c76ab282", - .hash = "zqlite-0.0.1-RWLaY9UynABmwfHlRcRhjzL8NzgXqkADoqKtlfaGpZB2", + .url = "git+https://github.com/karlseguin/zqlite.zig#5ac4cabe95a6d7faa7baf0da8057a8e6e8a227a1", + .hash = "zqlite-0.0.1-RWLaY64xnAAxsD2YNT8QqPMJUuaFv2GOzFRR4aGypJv3", }, + .anyverz = .{ .path = "../../modules/anyverz" }, }, } From a76419c1725faf5d33fc57c3d64f4f459b2fd110 Mon Sep 17 00:00:00 2001 From: Piotr Fila Date: Thu, 25 Jun 2026 11:24:15 +0200 Subject: [PATCH 03/11] regz: Simpler VirtualFilesysem Io creation --- tools/regz/src/VirtualFilesystem.zig | 137 +++------------------------ 1 file changed, 12 insertions(+), 125 deletions(-) diff --git a/tools/regz/src/VirtualFilesystem.zig b/tools/regz/src/VirtualFilesystem.zig index 4b8cffa2b..0555a11cb 100644 --- a/tools/regz/src/VirtualFilesystem.zig +++ b/tools/regz/src/VirtualFilesystem.zig @@ -108,7 +108,7 @@ fn operate(userdata: ?*anyopaque, op: std.Io.Operation) std.Io.Cancelable!std.Io .file_read_streaming => unreachable, .device_io_control => unreachable, .net_receive => unreachable, - .net_read => unreachable, + // .net_read => unreachable, }; } @@ -261,132 +261,19 @@ fn get_child(fs: *VirtualFilesystem, parent: ID, component: []const u8) ?ID { } else null; } +const vtable = blk: { + var ret = std.Io.failing.vtable.*; + ret.operate = operate; + ret.dirClose = Dir.close; + ret.dirCreateDirPathOpen = Dir.create_dir_path_open; + ret.dirCreateFile = Dir.create_file; + ret.fileClose = File.close; + break :blk ret; +}; + pub fn io(vfs: *VirtualFilesystem) std.Io { return .{ .userdata = vfs, - .vtable = &.{ - .dirCreateFile = Dir.create_file, - .operate = operate, - - // Default/failing/unimplemented handlers - .crashHandler = std.Io.noCrashHandler, - .async = std.Io.noAsync, - .concurrent = std.Io.failingConcurrent, - .await = std.Io.unreachableAwait, - .cancel = std.Io.unreachableCancel, - .groupAsync = std.Io.noGroupAsync, - .groupConcurrent = std.Io.failingGroupConcurrent, - .groupAwait = std.Io.unreachableGroupAwait, - .groupCancel = std.Io.unreachableGroupCancel, - - .recancel = std.Io.unreachableRecancel, - .swapCancelProtection = std.Io.unreachableSwapCancelProtection, - .checkCancel = std.Io.unreachableCheckCancel, - - .futexWait = std.Io.noFutexWait, - .futexWaitUncancelable = std.Io.noFutexWaitUncancelable, - .futexWake = std.Io.noFutexWake, - - .batchAwaitAsync = std.Io.unreachableBatchAwaitAsync, - .batchAwaitConcurrent = std.Io.unreachableBatchAwaitConcurrent, - .batchCancel = std.Io.unreachableBatchCancel, - - .dirCreateDir = std.Io.failingDirCreateDir, - .dirCreateDirPath = std.Io.failingDirCreateDirPath, - .dirCreateDirPathOpen = Dir.create_dir_path_open, - .dirOpenDir = std.Io.failingDirOpenDir, - .dirStat = std.Io.failingDirStat, - .dirStatFile = std.Io.failingDirStatFile, - .dirAccess = std.Io.failingDirAccess, - .dirCreateFileAtomic = std.Io.failingDirCreateFileAtomic, - .dirOpenFile = std.Io.failingDirOpenFile, - .dirClose = Dir.close, - .dirRead = std.Io.noDirRead, - .dirRealPath = std.Io.failingDirRealPath, - .dirRealPathFile = std.Io.failingDirRealPathFile, - .dirDeleteFile = std.Io.failingDirDeleteFile, - .dirDeleteDir = std.Io.failingDirDeleteDir, - .dirRename = std.Io.failingDirRename, - .dirRenamePreserve = std.Io.failingDirRenamePreserve, - .dirSymLink = std.Io.failingDirSymLink, - .dirReadLink = std.Io.failingDirReadLink, - .dirSetOwner = std.Io.failingDirSetOwner, - .dirSetFileOwner = std.Io.failingDirSetFileOwner, - .dirSetPermissions = std.Io.failingDirSetPermissions, - .dirSetFilePermissions = std.Io.failingDirSetFilePermissions, - .dirSetTimestamps = std.Io.noDirSetTimestamps, - .dirHardLink = std.Io.failingDirHardLink, - - .fileStat = std.Io.failingFileStat, - .fileLength = std.Io.failingFileLength, - .fileClose = File.close, - .fileWritePositional = std.Io.failingFileWritePositional, - .fileWriteFileStreaming = std.Io.noFileWriteFileStreaming, - .fileWriteFilePositional = std.Io.noFileWriteFilePositional, - .fileReadPositional = std.Io.failingFileReadPositional, - .fileSeekBy = std.Io.failingFileSeekBy, - .fileSeekTo = std.Io.failingFileSeekTo, - .fileSync = std.Io.failingFileSync, - .fileIsTty = std.Io.unreachableFileIsTty, - .fileEnableAnsiEscapeCodes = std.Io.unreachableFileEnableAnsiEscapeCodes, - .fileSupportsAnsiEscapeCodes = std.Io.unreachableFileSupportsAnsiEscapeCodes, - .fileSetLength = std.Io.failingFileSetLength, - .fileSetOwner = std.Io.failingFileSetOwner, - .fileSetPermissions = std.Io.failingFileSetPermissions, - .fileSetTimestamps = std.Io.noFileSetTimestamps, - .fileLock = std.Io.failingFileLock, - .fileTryLock = std.Io.failingFileTryLock, - .fileUnlock = std.Io.unreachableFileUnlock, - .fileDowngradeLock = std.Io.failingFileDowngradeLock, - .fileRealPath = std.Io.failingFileRealPath, - .fileHardLink = std.Io.failingFileHardLink, - - .fileMemoryMapCreate = std.Io.failingFileMemoryMapCreate, - .fileMemoryMapDestroy = std.Io.unreachableFileMemoryMapDestroy, - .fileMemoryMapSetLength = std.Io.unreachableFileMemoryMapSetLength, - .fileMemoryMapRead = std.Io.unreachableFileMemoryMapRead, - .fileMemoryMapWrite = std.Io.unreachableFileMemoryMapWrite, - - .processExecutableOpen = std.Io.failingProcessExecutableOpen, - .processExecutablePath = std.Io.failingProcessExecutablePath, - .lockStderr = std.Io.unreachableLockStderr, - .tryLockStderr = std.Io.noTryLockStderr, - .unlockStderr = std.Io.unreachableUnlockStderr, - .processCurrentPath = std.Io.failingProcessCurrentPath, - .processSetCurrentDir = std.Io.failingProcessSetCurrentDir, - .processSetCurrentPath = std.Io.failingProcessSetCurrentPath, - .processReplace = std.Io.failingProcessReplace, - .processReplacePath = std.Io.failingProcessReplacePath, - .processSpawn = std.Io.failingProcessSpawn, - .processSpawnPath = std.Io.failingProcessSpawnPath, - .childWait = std.Io.unreachableChildWait, - .childKill = std.Io.unreachableChildKill, - - .progressParentFile = std.Io.failingProgressParentFile, - - .now = std.Io.noNow, - .clockResolution = std.Io.failingClockResolution, - .sleep = std.Io.noSleep, - - .random = std.Io.noRandom, - .randomSecure = std.Io.failingRandomSecure, - - .netListenIp = std.Io.failingNetListenIp, - .netAccept = std.Io.failingNetAccept, - .netBindIp = std.Io.failingNetBindIp, - .netConnectIp = std.Io.failingNetConnectIp, - .netListenUnix = std.Io.failingNetListenUnix, - .netConnectUnix = std.Io.failingNetConnectUnix, - .netSocketCreatePair = std.Io.failingNetSocketCreatePair, - .netSend = std.Io.failingNetSend, - - .netWrite = std.Io.failingNetWrite, - .netWriteFile = std.Io.failingNetWriteFile, - .netClose = std.Io.unreachableNetClose, - .netShutdown = std.Io.failingNetShutdown, - .netInterfaceNameResolve = std.Io.failingNetInterfaceNameResolve, - .netInterfaceName = std.Io.unreachableNetInterfaceName, - .netLookup = std.Io.failingNetLookup, - }, + .vtable = &vtable, }; } From 6988bd9346aa9e97e8e2e534b1a3d3839c86ccb6 Mon Sep 17 00:00:00 2001 From: Piotr Fila Date: Thu, 25 Jun 2026 11:24:26 +0200 Subject: [PATCH 04/11] regz: Don't iterate over type fields where not needed --- tools/regz/src/Database.zig | 5 +---- tools/regz/src/arch.zig | 5 +---- tools/regz/src/targetdb.zig | 7 ++----- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/tools/regz/src/Database.zig b/tools/regz/src/Database.zig index 0f6cd2d2a..8e3925c73 100644 --- a/tools/regz/src/Database.zig +++ b/tools/regz/src/Database.zig @@ -364,10 +364,7 @@ pub const Access = enum { pub const default = .read_write; pub fn to_string(access: Access) []const u8 { - return inline for (@typeInfo(Access).@"enum".field_names) |field_name| { - if (@field(Access, field_name) == access) - break field_name; - } else unreachable; + return @tagName(access); } }; diff --git a/tools/regz/src/arch.zig b/tools/regz/src/arch.zig index d644f101e..99163a5fa 100644 --- a/tools/regz/src/arch.zig +++ b/tools/regz/src/arch.zig @@ -52,10 +52,7 @@ pub const Arch = enum { pub const default = .unknown; pub fn to_string(arch: Arch) []const u8 { - return inline for (@typeInfo(Arch).@"enum".field_names) |field_name| { - if (@field(Arch, field_name) == arch) - break field_name; - } else unreachable; + return @tagName(arch); } pub fn is_arm(arch: Arch) bool { diff --git a/tools/regz/src/targetdb.zig b/tools/regz/src/targetdb.zig index 6ec7e05a4..01d618008 100644 --- a/tools/regz/src/targetdb.zig +++ b/tools/regz/src/targetdb.zig @@ -24,11 +24,8 @@ fn parse_isa_to_arch(isa: []const u8) Arch { const lower = std.ascii.lowerString(&buf, isa); // Try to match against all Arch enum values - inline for (@typeInfo(Arch).@"enum".field_names) |field_name| { - if (std.mem.eql(u8, lower, field_name)) { - return @field(Arch, field_name); - } - } + if (std.meta.stringToEnum(Arch, lower)) |ret| + return ret; log.warn("Unknown ISA: {s}, defaulting to unknown arch", .{isa}); return .unknown; From 19c3b5d0bd2847d6c189ed41ee1ab2a252e177cf Mon Sep 17 00:00:00 2001 From: Piotr Fila Date: Thu, 25 Jun 2026 11:28:11 +0200 Subject: [PATCH 05/11] regz: Buildable on both 0.16 and 0.17 --- tools/regz/build.zig | 29 +++++++++++------------------ tools/regz/src/Database.zig | 18 +++++++++++------- tools/regz/src/arch/arm.zig | 4 +++- tools/regz/src/svd.zig | 3 ++- 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/tools/regz/build.zig b/tools/regz/build.zig index 0f0afacff..5c6fd30a4 100644 --- a/tools/regz/build.zig +++ b/tools/regz/build.zig @@ -9,6 +9,11 @@ pub fn build(b: *Build) !void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); + const anyverz_dep = b.dependency("anyverz", .{ + .target = target, + .optimize = optimize, + }); + const libxml2_dep = b.dependency("libxml2", .{ .target = target, .optimize = .ReleaseSafe, @@ -19,8 +24,6 @@ pub fn build(b: *Build) !void { .optimize = optimize, }); - const zqlite = zqlite_dep.module("zqlite"); - const xml_module = b.createModule(.{ .root_source_file = b.path("src/xml.zig"), .target = target, @@ -35,14 +38,9 @@ pub fn build(b: *Build) !void { .target = target, .optimize = optimize, .imports = &.{ - .{ - .name = "zqlite", - .module = zqlite, - }, - .{ - .name = "xml", - .module = xml_module, - }, + .{ .name = "zqlite", .module = zqlite_dep.module("zqlite") }, + .{ .name = "xml", .module = xml_module }, + .{ .name = "anyverz", .module = anyverz_dep.module("anyverz") }, }, }); regz_module.linkLibrary(libxml2_dep.artifact("xml")); @@ -54,14 +52,9 @@ pub fn build(b: *Build) !void { .target = target, .optimize = optimize, .imports = &.{ - .{ - .name = "regz", - .module = regz_module, - }, - .{ - .name = "xml", - .module = xml_module, - }, + .{ .name = "regz", .module = regz_module }, + .{ .name = "xml", .module = xml_module }, + .{ .name = "anyverz", .module = anyverz_dep.module("anyverz") }, }, }), .use_llvm = true, diff --git a/tools/regz/src/Database.zig b/tools/regz/src/Database.zig index 8e3925c73..7966db729 100644 --- a/tools/regz/src/Database.zig +++ b/tools/regz/src/Database.zig @@ -6,6 +6,8 @@ const std = @import("std"); const Allocator = std.mem.Allocator; const assert = std.debug.assert; +const anyverz = @import("anyverz"); + const zqlite = @import("zqlite"); const c = zqlite.c; @@ -380,7 +382,7 @@ fn gen_field_list(comptime T: type, opts: struct { prefix: ?[]const u8 = null }) var buf: [4096]u8 = undefined; var fbs: std.Io.Writer = .fixed(&buf); - inline for (@typeInfo(T).@"struct".field_names, 0..) |field_name, i| { + inline for (comptime anyverz.fieldNames(T), 0..) |field_name, i| { if (i != 0) fbs.writeAll(", ") catch unreachable; @@ -419,8 +421,7 @@ fn gen_sql_table_impl(comptime name: []const u8, comptime T: type) ![:0]const u8 // check that primary key and foreign keys exist var primary_key_found = T.sql_opts.primary_key == null; - const info = @typeInfo(T); - inline for (info.@"struct".field_names) |field_name| { + inline for (comptime anyverz.fieldNames(T)) |field_name| { if (T.sql_opts.primary_key) |primary_key| { if (std.mem.eql(u8, primary_key.name, field_name)) primary_key_found = true; @@ -431,7 +432,7 @@ fn gen_sql_table_impl(comptime name: []const u8, comptime T: type) ![:0]const u8 try fbs.print("CREATE TABLE {s} (\n", .{name}); var first = true; - inline for (info.@"struct".field_names, info.@"struct".field_types) |field_name, field_type| { + inline for (comptime anyverz.fieldNames(T), anyverz.fieldTypes(T)) |field_name, field_type| { if (first) { first = false; } else { @@ -471,7 +472,7 @@ fn gen_sql_table_impl(comptime name: []const u8, comptime T: type) ![:0]const u8 for (T.sql_opts.foreign_keys) |foreign_key| { try fbs.writeAll(",\n"); - const field_type = for (@typeInfo(T).@"struct".field_names, @typeInfo(T).@"struct".field_types) |field_name, field_type| { + const field_type = for (comptime anyverz.fieldNames(T), anyverz.fieldTypes(T)) |field_name, field_type| { if (std.mem.eql(u8, field_name, foreign_key.name)) break field_type; } else unreachable; @@ -772,8 +773,11 @@ pub fn get_device_id_by_name(db: *Database, name: []const u8) !?DeviceID { fn scan_row(comptime T: type, allocator: Allocator, row: zqlite.Row) !T { var entry: T = undefined; - const info = @typeInfo(T).@"struct"; - inline for (info.field_names, info.field_types, 0..) |field_name, field_type, i| { + inline for ( + comptime anyverz.fieldNames(T), + anyverz.fieldTypes(T), + 0.., + ) |field_name, field_type, i| { if (@typeInfo(field_type) == .@"enum") { if (@hasDecl(field_type, "to_string")) @field(entry, field_name) = std.meta.stringToEnum(field_type, row.text(i)) orelse return error.Unknown diff --git a/tools/regz/src/arch/arm.zig b/tools/regz/src/arch/arm.zig index f1f52dcf0..a601a9c46 100644 --- a/tools/regz/src/arch/arm.zig +++ b/tools/regz/src/arch/arm.zig @@ -3,6 +3,8 @@ const std = @import("std"); const assert = std.debug.assert; const Allocator = std.mem.Allocator; +const anyverz = @import("anyverz"); + const Database = @import("../Database.zig"); const Arch = @import("../arch.zig").Arch; const DeviceID = Database.DeviceID; @@ -53,7 +55,7 @@ const system_interrupts = struct { pub fn load_system_interrupts(db: *Database, device_id: DeviceID, arch: Arch) !void { assert(arch.is_arm()); - inline for (@typeInfo(Arch).@"enum".field_names) |field_name| { + inline for (comptime anyverz.fieldNames(Arch)) |field_name| { if (arch == @field(Arch, field_name)) { if (@hasDecl(system_interrupts, field_name)) { for (@field(system_interrupts, field_name)) |interrupt| { diff --git a/tools/regz/src/svd.zig b/tools/regz/src/svd.zig index da6cfc0a6..f779cd602 100644 --- a/tools/regz/src/svd.zig +++ b/tools/regz/src/svd.zig @@ -2,6 +2,7 @@ const std = @import("std"); const ArenaAllocator = std.heap.ArenaAllocator; const Allocator = std.mem.Allocator; +const anyverz = @import("anyverz"); const xml = @import("xml"); const Arch = @import("arch.zig").Arch; @@ -33,7 +34,7 @@ const Context = struct { ) !RegisterProperties { const register_props = try RegisterProperties.parse(node); var base_register_props = ctx.register_props.get(from) orelse unreachable; - inline for (@typeInfo(RegisterProperties).@"struct".field_names) |field_name| { + inline for (comptime anyverz.fieldNames(RegisterProperties)) |field_name| { if (@field(register_props, field_name)) |value| @field(base_register_props, field_name) = value; } From c6aef019391527cdc3ccd9f698a73586707a43fb Mon Sep 17 00:00:00 2001 From: Piotr Fila Date: Thu, 25 Jun 2026 11:35:36 +0200 Subject: [PATCH 06/11] sorcerer: Use managed containers --- tools/sorcerer/build.zig | 30 +++++++++++++++--------------- tools/sorcerer/src/RegzWindow.zig | 30 +++++++++++++++--------------- tools/sorcerer/src/cli.zig | 2 +- tools/sorcerer/src/main.zig | 4 ++-- tools/sorcerer/src/test_diff.zig | 12 ++++++------ 5 files changed, 39 insertions(+), 39 deletions(-) diff --git a/tools/sorcerer/build.zig b/tools/sorcerer/build.zig index 6f4980879..db34ae604 100644 --- a/tools/sorcerer/build.zig +++ b/tools/sorcerer/build.zig @@ -284,7 +284,7 @@ fn find_target_location(b: *std.Build, lazy_path: LazyPath) RegisterSchemaUsage. } fn convert_patch_files(b: *std.Build, patch_files: []const LazyPath) ![]const RegisterSchemaUsage.PatchFile { - var result: std.ArrayList(RegisterSchemaUsage.PatchFile) = .{}; + var result: std.ArrayList(RegisterSchemaUsage.PatchFile) = .empty; for (patch_files) |patch_file| { const converted: RegisterSchemaUsage.PatchFile = switch (patch_file) { .src_path => |src_path| .{ @@ -365,29 +365,29 @@ const LazyPathContext = struct { }; fn LazyPathHashMap(comptime Value: type) type { - return std.ArrayHashMap(std.Build.LazyPath, Value, LazyPathContext, true); + return std.ArrayHashMapUnmanaged(std.Build.LazyPath, Value, LazyPathContext, true); } fn get_register_schemas(b: *std.Build, mb: *MicroBuild) ![]const RegisterSchemaUsage { const targets = get_targets(mb); - var deduped_targets: LazyPathHashMap(RegisterSchemaUsage.Format) = .init(b.allocator); - var chips: LazyPathHashMap(std.ArrayList(RegisterSchemaUsage.Chip)) = .init(b.allocator); - var boards: LazyPathHashMap(std.ArrayList(RegisterSchemaUsage.Board)) = .init(b.allocator); - var locations: LazyPathHashMap(RegisterSchemaUsage.Location) = .init(b.allocator); + var deduped_targets: LazyPathHashMap(RegisterSchemaUsage.Format) = .empty; + var chips: LazyPathHashMap(std.ArrayList(RegisterSchemaUsage.Chip)) = .empty; + var boards: LazyPathHashMap(std.ArrayList(RegisterSchemaUsage.Board)) = .empty; + var locations: LazyPathHashMap(RegisterSchemaUsage.Location) = .empty; for (targets) |twp| { const t = twp.target; const lazy_path = switch (t.chip.register_definition) { .targetdb => |targetdb| blk: { - try deduped_targets.put(targetdb.path, .targetdb); + try deduped_targets.put(b.allocator, targetdb.path, .targetdb); break :blk targetdb.path; }, .embassy => |embassy| blk: { - try deduped_targets.put(embassy.path, .embassy); + try deduped_targets.put(b.allocator, embassy.path, .embassy); break :blk embassy.path; }, inline else => |lazy_path| blk: { - try deduped_targets.put(lazy_path, switch (t.chip.register_definition) { + try deduped_targets.put(b.allocator, lazy_path, switch (t.chip.register_definition) { .svd => .svd, .atdf => .atdf, .embassy, .targetdb => unreachable, @@ -407,13 +407,13 @@ fn get_register_schemas(b: *std.Build, mb: *MicroBuild) ![]const RegisterSchemaU .patch_files = patch_files, }); } else { - var chip_list: std.ArrayList(RegisterSchemaUsage.Chip) = .{}; + var chip_list: std.ArrayList(RegisterSchemaUsage.Chip) = .empty; try chip_list.append(b.allocator, .{ .name = t.chip.name, .target_name = twp.path, .patch_files = patch_files, }); - try chips.put(lazy_path, chip_list); + try chips.put(b.allocator, lazy_path, chip_list); } if (t.board) |board| if (boards.getEntry(lazy_path)) |entry| { @@ -428,20 +428,20 @@ fn get_register_schemas(b: *std.Build, mb: *MicroBuild) ![]const RegisterSchemaU }); } } else { - var board_list: std.ArrayList(RegisterSchemaUsage.Board) = .{}; + var board_list: std.ArrayList(RegisterSchemaUsage.Board) = .empty; try board_list.append(b.allocator, .{ .name = board.name, }); - try boards.put(lazy_path, board_list); + try boards.put(b.allocator, lazy_path, board_list); }; } for (deduped_targets.keys()) |lazy_path| { const location = find_target_location(b, lazy_path); - try locations.put(lazy_path, location); + try locations.put(b.allocator, lazy_path, location); } - var ret: std.ArrayList(RegisterSchemaUsage) = .{}; + var ret: std.ArrayList(RegisterSchemaUsage) = .empty; for (deduped_targets.keys(), deduped_targets.values()) |lazy_path, format| { var chip_list = chips.get(lazy_path).?; var board_list = boards.get(lazy_path); diff --git a/tools/sorcerer/src/RegzWindow.zig b/tools/sorcerer/src/RegzWindow.zig index 8e9897aa2..5d0433a63 100644 --- a/tools/sorcerer/src/RegzWindow.zig +++ b/tools/sorcerer/src/RegzWindow.zig @@ -10,7 +10,7 @@ selected_file: ?VirtualFilesystem.ID = null, displayed_file: ?VirtualFilesystem.ID = null, active_view: View = .code_generation, chip_info: ?ChipInfo = null, -loaded_patches: std.StringArrayHashMapUnmanaged(LoadedPatchFile) = .{}, +loaded_patches: std.StringArrayHashMapUnmanaged(LoadedPatchFile) = .empty, selected_patch: ?SelectedPatch = null, patches_loaded: bool = false, format: regz.Database.Format, @@ -902,8 +902,8 @@ fn load_patch_files(w: *RegzWindow) void { w.loaded_patches.put(w.gpa, owned_path, .{ .path = owned_path, .patches = null, - .pending_patches = .{}, - .deleted_patch_indices = .{}, + .pending_patches = .empty, + .deleted_patch_indices = .empty, .parse_error = error_msg, .is_editable = is_editable, }) catch {}; @@ -917,8 +917,8 @@ fn load_patch_files(w: *RegzWindow) void { w.loaded_patches.put(w.gpa, owned_path, .{ .path = owned_path, .patches = null, - .pending_patches = .{}, - .deleted_patch_indices = .{}, + .pending_patches = .empty, + .deleted_patch_indices = .empty, .parse_error = error_msg, .is_editable = is_editable, }) catch {}; @@ -931,8 +931,8 @@ fn load_patch_files(w: *RegzWindow) void { w.loaded_patches.put(w.gpa, owned_path, .{ .path = owned_path, .patches = null, - .pending_patches = .{}, - .deleted_patch_indices = .{}, + .pending_patches = .empty, + .deleted_patch_indices = .empty, .parse_error = error_msg, .is_editable = is_editable, }) catch {}; @@ -948,8 +948,8 @@ fn load_patch_files(w: *RegzWindow) void { w.loaded_patches.put(w.gpa, owned_path, .{ .path = owned_path, .patches = patches, - .pending_patches = .{}, - .deleted_patch_indices = .{}, + .pending_patches = .empty, + .deleted_patch_indices = .empty, .is_editable = is_editable, }) catch {}; } @@ -1249,7 +1249,7 @@ fn display_diff(w: *RegzWindow, file_diffs: []const FileDiff, sel: SelectedPatch } // Build unified diff format text - var diff_text: std.ArrayList(u8) = .{}; + var diff_text: std.ArrayList(u8) = .empty; defer diff_text.deinit(w.gpa); for (file_diffs) |fd| { @@ -1475,7 +1475,7 @@ fn compute_patch_diff(w: *RegzWindow, temp_arena: Allocator, sel: SelectedPatch, }; // Compare files and build diffs (before vs after) - var file_diffs: std.ArrayList(FileDiff) = .{}; + var file_diffs: std.ArrayList(FileDiff) = .empty; // Recursively compare all files compare_vfs_files(arena, &before_vfs, &after_vfs, &file_diffs, .root, "") catch { @@ -1553,11 +1553,11 @@ fn compare_vfs_files( } fn compute_line_diff(arena: Allocator, old_content: []const u8, new_content: []const u8) ![]const DiffLine { - var result: std.ArrayList(DiffLine) = .{}; + var result: std.ArrayList(DiffLine) = .empty; // Split content into lines - var old_lines: std.ArrayList([]const u8) = .{}; - var new_lines: std.ArrayList([]const u8) = .{}; + var old_lines: std.ArrayList([]const u8) = .empty; + var new_lines: std.ArrayList([]const u8) = .empty; var old_iter = std.mem.splitScalar(u8, old_content, '\n'); while (old_iter.next()) |line| { @@ -1669,7 +1669,7 @@ fn compute_lcs(arena: Allocator, old_lines: []const []const u8, new_lines: []con } // Backtrack - var lcs_result: std.ArrayList(LCS_Entry) = .{}; + var lcs_result: std.ArrayList(LCS_Entry) = .empty; var i = m; var j = n; while (i > 0 and j > 0) { diff --git a/tools/sorcerer/src/cli.zig b/tools/sorcerer/src/cli.zig index 88fae6225..99d2fbca0 100644 --- a/tools/sorcerer/src/cli.zig +++ b/tools/sorcerer/src/cli.zig @@ -201,7 +201,7 @@ fn handle_write_error(err: anyerror) error{Explained} { } fn print_list_json(allocator: Allocator, port_filter: ?[]const u8) !void { - var entries: std.ArrayList(JsonEntry) = .{}; + var entries: std.ArrayList(JsonEntry) = .empty; defer entries.deinit(allocator); // Track seen chip names to deduplicate diff --git a/tools/sorcerer/src/main.zig b/tools/sorcerer/src/main.zig index fa55a591d..a711cd89a 100644 --- a/tools/sorcerer/src/main.zig +++ b/tools/sorcerer/src/main.zig @@ -36,7 +36,7 @@ pub const std_options: std.Options = .{ .log_level = .info, }; -var gpa_instance = std.heap.GeneralPurposeAllocator(.{}){}; +var gpa_instance: std.heap.DebugAllocator(.{}) = .init; const gpa = gpa_instance.allocator(); var arena: std.heap.ArenaAllocator = .init(gpa); @@ -44,7 +44,7 @@ var state: State = .{}; const State = struct { orig_content_scale: f32 = 1.0, - regz_windows: std.StringArrayHashMapUnmanaged(*RegzWindow) = .{}, + regz_windows: std.StringArrayHashMapUnmanaged(*RegzWindow) = .empty, show_from_microzig_window: bool = false, show_stats_window: bool = true, show_search_chips_window: bool = false, diff --git a/tools/sorcerer/src/test_diff.zig b/tools/sorcerer/src/test_diff.zig index 1a1ecc5b2..602547240 100644 --- a/tools/sorcerer/src/test_diff.zig +++ b/tools/sorcerer/src/test_diff.zig @@ -10,11 +10,11 @@ const DiffLine = struct { fn compute_line_diff(arena: std.mem.Allocator, old_content: []const u8, new_content: []const u8) ![]const DiffLine { const Kind = DiffLine.Kind; - var result: std.ArrayList(DiffLine) = .{}; + var result: std.ArrayList(DiffLine) = .empty; // Split content into lines - var old_lines: std.ArrayList([]const u8) = .{}; - var new_lines: std.ArrayList([]const u8) = .{}; + var old_lines: std.ArrayList([]const u8) = .empty; + var new_lines: std.ArrayList([]const u8) = .empty; var old_iter = std.mem.splitScalar(u8, old_content, '\n'); while (old_iter.next()) |line| { @@ -28,11 +28,11 @@ fn compute_line_diff(arena: std.mem.Allocator, old_content: []const u8, new_cont // Encode lines as single characters for diffz (line-mode diffing) var line_to_char: std.StringHashMap(u8) = .init(arena); - var char_to_line: std.ArrayList([]const u8) = .{}; + var char_to_line: std.ArrayList([]const u8) = .empty; var next_char: u8 = 1; - var old_chars: std.ArrayList(u8) = .{}; - var new_chars: std.ArrayList(u8) = .{}; + var old_chars: std.ArrayList(u8) = .empty; + var new_chars: std.ArrayList(u8) = .empty; // Encode old lines for (old_lines.items) |line| { From 85a8598382d6f50b00210c556443c701acd89546 Mon Sep 17 00:00:00 2001 From: Piotr Fila Date: Thu, 25 Jun 2026 12:29:01 +0200 Subject: [PATCH 07/11] sorcerer: Rename window handles from w to wnd --- tools/sorcerer/src/RegzWindow.zig | 516 +++++++++++++++--------------- 1 file changed, 258 insertions(+), 258 deletions(-) diff --git a/tools/sorcerer/src/RegzWindow.zig b/tools/sorcerer/src/RegzWindow.zig index 5d0433a63..343bb73de 100644 --- a/tools/sorcerer/src/RegzWindow.zig +++ b/tools/sorcerer/src/RegzWindow.zig @@ -275,26 +275,26 @@ pub fn create( return window; } -pub fn destroy(w: *RegzWindow) void { +pub fn destroy(wnd: *RegzWindow) void { // Clean up pending_patches and deleted_patch_indices ArrayLists - for (w.loaded_patches.values()) |*loaded| { - loaded.pending_patches.deinit(w.gpa); - loaded.deleted_patch_indices.deinit(w.gpa); + for (wnd.loaded_patches.values()) |*loaded| { + loaded.pending_patches.deinit(wnd.gpa); + loaded.deleted_patch_indices.deinit(wnd.gpa); } - // Clean up loaded patches hashmap (strings are in w.arena, freed below) - w.loaded_patches.deinit(w.gpa); + // Clean up loaded patches hashmap (strings are in wnd.arena, freed below) + wnd.loaded_patches.deinit(wnd.gpa); - w.vfs.deinit(); - w.arena.deinit(); - w.db.destroy(); + wnd.vfs.deinit(); + wnd.arena.deinit(); + wnd.db.destroy(); } -pub fn show(w: *RegzWindow) !void { - if (!w.show_window) +pub fn show(wnd: *RegzWindow) !void { + if (!wnd.show_window) return; - var arena: std.heap.ArenaAllocator = .init(w.gpa); + var arena: std.heap.ArenaAllocator = .init(wnd.gpa); defer arena.deinit(); // Use a local flag for the window header close button @@ -304,19 +304,19 @@ pub fn show(w: *RegzWindow) !void { var float = dvui.floatingWindow(@src(), .{}, .{ .min_size_content = .{ .w = 400, .h = 400 }, .max_size_content = .width(400), - .id_extra = w.id_extra, + .id_extra = wnd.id_extra, }); defer float.deinit(); - float.dragAreaSet(dvui.windowHeader("Regz", w.title, &header_close_flag)); + float.dragAreaSet(dvui.windowHeader("Regz", wnd.title, &header_close_flag)); // Check if user clicked the X button if (!header_close_flag) { - if (w.has_unsaved_patches) { - w.show_unsaved_warning = true; + if (wnd.has_unsaved_patches) { + wnd.show_unsaved_warning = true; // Don't actually close yet } else { - w.show_window = false; + wnd.show_window = false; } } @@ -340,7 +340,7 @@ pub fn show(w: *RegzWindow) !void { if (dvui.dialogNativeFolderSelect(dvui.currentWindow().arena(), .{ .title = "Save Generated Code To...", }) catch null) |folder_path| { - w.save_to_directory(folder_path) catch |err| { + wnd.save_to_directory(folder_path) catch |err| { std.log.err("Failed to save: {}", .{err}); }; } @@ -349,20 +349,20 @@ pub fn show(w: *RegzWindow) !void { // Save Patches - only enabled when has_unsaved_patches if (dvui.menuItemLabel(@src(), "Save Patches", .{}, .{ .expand = .horizontal, - .color_text = if (!w.has_unsaved_patches) dvui.Color.fromHex("918175") else null, - }) != null and w.has_unsaved_patches) { - w.save_all_patches(arena.allocator()) catch |err| { - w.validation_error_message = std.fmt.allocPrint(w.arena.allocator(), "Failed to save patches: {s}", .{@errorName(err)}) catch "Save failed"; - w.show_validation_error = true; + .color_text = if (!wnd.has_unsaved_patches) dvui.Color.fromHex("918175") else null, + }) != null and wnd.has_unsaved_patches) { + wnd.save_all_patches(arena.allocator()) catch |err| { + wnd.validation_error_message = std.fmt.allocPrint(wnd.arena.allocator(), "Failed to save patches: {s}", .{@errorName(err)}) catch "Save failed"; + wnd.show_validation_error = true; }; m.close(); } if (dvui.menuItemLabel(@src(), "Close", .{}, .{ .expand = .horizontal }) != null) { - if (w.has_unsaved_patches) { - w.show_unsaved_warning = true; + if (wnd.has_unsaved_patches) { + wnd.show_unsaved_warning = true; } else { - w.show_window = false; + wnd.show_window = false; } m.close(); } @@ -373,35 +373,35 @@ pub fn show(w: *RegzWindow) !void { defer fw.deinit(); if (dvui.menuItemLabel(@src(), "Code Generation", .{}, .{ .expand = .horizontal }) != null) { - w.active_view = .code_generation; + wnd.active_view = .code_generation; m.close(); } if (dvui.menuItemLabel(@src(), "Patches", .{}, .{ .expand = .horizontal }) != null) { - w.active_view = .patches; + wnd.active_view = .patches; m.close(); } if (dvui.menuItemLabel(@src(), "Analysis", .{}, .{ .expand = .horizontal }) != null) { - w.active_view = .analysis; + wnd.active_view = .analysis; m.close(); } } } - switch (w.active_view) { - .code_generation => w.show_code_generation(arena.allocator()), - .patches => w.show_patches(arena.allocator()), - .analysis => w.show_analysis(arena.allocator()), + switch (wnd.active_view) { + .code_generation => wnd.show_code_generation(arena.allocator()), + .patches => wnd.show_patches(arena.allocator()), + .analysis => wnd.show_analysis(arena.allocator()), } // Render dialogs - w.show_create_patch_dialog_ui(arena.allocator()); - w.show_unsaved_warning_dialog(); - w.show_validation_error_dialog(); + wnd.show_create_patch_dialog_ui(arena.allocator()); + wnd.show_unsaved_warning_dialog(); + wnd.show_validation_error_dialog(); } -fn show_code_generation(w: *RegzWindow, arena: Allocator) void { +fn show_code_generation(wnd: *RegzWindow, arena: Allocator) void { var hbox = dvui.box(@src(), .{ .dir = .horizontal }, .{ .expand = .both }); defer hbox.deinit(); @@ -409,7 +409,7 @@ fn show_code_generation(w: *RegzWindow, arena: Allocator) void { const scroll_arena = dvui.scrollArea(@src(), .{}, .{}); defer scroll_arena.deinit(); - w.show_file_tree( + wnd.show_file_tree( arena, @src(), .{}, @@ -450,12 +450,12 @@ fn show_code_generation(w: *RegzWindow, arena: Allocator) void { }, .{ .expand = .both }); defer te.deinit(); - if (w.selected_file) |id| { + if (wnd.selected_file) |id| { // Update text when file selection changes - if (w.displayed_file != id or dvui.firstFrame(te.data().id)) { - te.textSet(w.vfs.get_content(id), false); + if (wnd.displayed_file != id or dvui.firstFrame(te.data().id)) { + te.textSet(wnd.vfs.get_content(id), false); te.textLayout.selection.moveCursor(0, false); - w.displayed_file = id; + wnd.displayed_file = id; } } @@ -470,16 +470,16 @@ fn show_code_generation(w: *RegzWindow, arena: Allocator) void { var tl = dvui.textLayout(@src(), .{}, .{ .expand = .horizontal }); defer tl.deinit(); - if (w.selected_file) |id| - tl.addText(w.vfs.get_content(id), .{}); + if (wnd.selected_file) |id| + tl.addText(wnd.vfs.get_content(id), .{}); } } -fn show_patches(w: *RegzWindow, arena: Allocator) void { +fn show_patches(wnd: *RegzWindow, arena: Allocator) void { // Load patches on first view - if (!w.patches_loaded) { - w.load_patch_files(); - w.patches_loaded = true; + if (!wnd.patches_loaded) { + wnd.load_patch_files(); + wnd.patches_loaded = true; } var hbox = dvui.box(@src(), .{ .dir = .horizontal }, .{ .expand = .both }); @@ -498,7 +498,7 @@ fn show_patches(w: *RegzWindow, arena: Allocator) void { var scroll = dvui.scrollArea(@src(), .{}, .{ .expand = .vertical }); defer scroll.deinit(); - w.show_patch_tree(arena); + wnd.show_patch_tree(arena); } // Right panel: Patch details @@ -506,11 +506,11 @@ fn show_patches(w: *RegzWindow, arena: Allocator) void { var scroll = dvui.scrollArea(@src(), .{}, .{ .expand = .both }); defer scroll.deinit(); - w.show_patch_details(arena); + wnd.show_patch_details(arena); } } -fn show_analysis(w: *RegzWindow, arena: Allocator) void { +fn show_analysis(wnd: *RegzWindow, arena: Allocator) void { var hbox = dvui.box(@src(), .{ .dir = .horizontal }, .{ .expand = .both }); defer hbox.deinit(); @@ -527,7 +527,7 @@ fn show_analysis(w: *RegzWindow, arena: Allocator) void { var scroll = dvui.scrollArea(@src(), .{}, .{ .expand = .vertical }); defer scroll.deinit(); - w.show_analysis_tree(arena); + wnd.show_analysis_tree(arena); } // Right panel: Equivalence group details @@ -535,13 +535,13 @@ fn show_analysis(w: *RegzWindow, arena: Allocator) void { var scroll = dvui.scrollArea(@src(), .{}, .{ .expand = .both }); defer scroll.deinit(); - w.show_analysis_details(arena); + wnd.show_analysis_details(arena); } } -fn show_analysis_tree(w: *RegzWindow, arena: Allocator) void { +fn show_analysis_tree(wnd: *RegzWindow, arena: Allocator) void { // Run analysis on all peripherals if not cached - const cached = w.get_or_run_full_analysis() orelse { + const cached = wnd.get_or_run_full_analysis() orelse { _ = dvui.label(@src(), "Error running analysis", .{}, .{ .color_text = dvui.Color.fromHex("EF2F27"), }); @@ -575,7 +575,7 @@ fn show_analysis_tree(w: *RegzWindow, arena: Allocator) void { defer tree.deinit(); for (cached.peripheral_results, 0..) |periph_result, periph_idx| { - const is_selected = w.selected_analysis_peripheral != null and w.selected_analysis_peripheral.? == periph_idx; + const is_selected = wnd.selected_analysis_peripheral != null and wnd.selected_analysis_peripheral.? == periph_idx; var branch = tree.branch(@src(), .{ .expanded = is_selected }, .{ .id_extra = periph_idx }); defer branch.deinit(); @@ -598,8 +598,8 @@ fn show_analysis_tree(w: *RegzWindow, arena: Allocator) void { // Handle click on peripheral to select if (branch.button.clicked()) { - w.selected_analysis_peripheral = periph_idx; - w.selected_equivalence_group = null; + wnd.selected_analysis_peripheral = periph_idx; + wnd.selected_equivalence_group = null; } if (branch.expander(@src(), .{ .indent = 14 }, .{ .margin = .{ .x = 14 } })) { @@ -611,8 +611,8 @@ fn show_analysis_tree(w: *RegzWindow, arena: Allocator) void { defer group_branch.deinit(); const is_group_selected = is_selected and - w.selected_equivalence_group != null and - w.selected_equivalence_group.? == group_idx; + wnd.selected_equivalence_group != null and + wnd.selected_equivalence_group.? == group_idx; // Icon for group dvui.icon(@src(), "GroupIcon", dvui.entypo.flow_tree, .{}, .{ .gravity_y = 0.5 }); @@ -626,8 +626,8 @@ fn show_analysis_tree(w: *RegzWindow, arena: Allocator) void { }); if (group_branch.button.clicked()) { - w.selected_analysis_peripheral = periph_idx; - w.selected_equivalence_group = group_idx; + wnd.selected_analysis_peripheral = periph_idx; + wnd.selected_equivalence_group = group_idx; } } @@ -645,9 +645,9 @@ fn show_analysis_tree(w: *RegzWindow, arena: Allocator) void { _ = arena; } -fn show_analysis_details(w: *RegzWindow, arena: Allocator) void { +fn show_analysis_details(wnd: *RegzWindow, arena: Allocator) void { // Check if a peripheral and group are selected - const periph_idx = w.selected_analysis_peripheral orelse { + const periph_idx = wnd.selected_analysis_peripheral orelse { var vbox = dvui.box(@src(), .{ .dir = .vertical }, .{ .expand = .both, .padding = dvui.Rect.all(16), @@ -658,7 +658,7 @@ fn show_analysis_details(w: *RegzWindow, arena: Allocator) void { return; }; - const group_idx = w.selected_equivalence_group orelse { + const group_idx = wnd.selected_equivalence_group orelse { var vbox = dvui.box(@src(), .{ .dir = .vertical }, .{ .expand = .both, .padding = dvui.Rect.all(16), @@ -670,7 +670,7 @@ fn show_analysis_details(w: *RegzWindow, arena: Allocator) void { }; // Get cached analysis result - const cached = w.cached_analysis orelse { + const cached = wnd.cached_analysis orelse { _ = dvui.label(@src(), "No analysis data available", .{}, .{}); return; }; @@ -724,7 +724,7 @@ fn show_analysis_details(w: *RegzWindow, arena: Allocator) void { _ = dvui.spacer(@src(), .{ .min_size_content = .{ .h = 12 } }); // Create Patch from Group button - const has_editable_files = w.has_editable_patch_files(); + const has_editable_files = wnd.has_editable_patch_files(); if (has_editable_files) { if (dvui.button(@src(), "Create Patch from Group", .{}, .{ .color_fill = dvui.Color.fromHex("98BC37"), @@ -732,11 +732,11 @@ fn show_analysis_details(w: *RegzWindow, arena: Allocator) void { .color_text = dvui.Color.fromHex("1C1B19"), })) { // Initialize pending patch creation - w.pending_patch_creation = .{ + wnd.pending_patch_creation = .{ .peripheral_name = periph_result.peripheral_name, .group_idx = group_idx, }; - w.show_create_patch_dialog = true; + wnd.show_create_patch_dialog = true; } } else { _ = dvui.label(@src(), "(No editable patch files)", .{}, .{ @@ -760,7 +760,7 @@ fn show_analysis_details(w: *RegzWindow, arena: Allocator) void { }, }; - var grid = dvui.grid(@src(), .{ .col_widths = &w.analysis_col_widths }, .{}, .{ + var grid = dvui.grid(@src(), .{ .col_widths = &wnd.analysis_col_widths }, .{}, .{ .expand = .both, .background = true, .padding = dvui.Rect.all(4), @@ -769,19 +769,19 @@ fn show_analysis_details(w: *RegzWindow, arena: Allocator) void { // Headers with resize handles dvui.gridHeading(@src(), grid, 0, "Name", .{ - .sizes = &w.analysis_col_widths, + .sizes = &wnd.analysis_col_widths, .num = 0, .min_size = 60, .max_size = 300, }, header_style); dvui.gridHeading(@src(), grid, 1, "Value", .{ - .sizes = &w.analysis_col_widths, + .sizes = &wnd.analysis_col_widths, .num = 1, .min_size = 40, .max_size = 150, }, header_style); dvui.gridHeading(@src(), grid, 2, "Description", .{ - .sizes = &w.analysis_col_widths, + .sizes = &wnd.analysis_col_widths, .num = 2, .min_size = 100, .max_size = 500, @@ -836,27 +836,27 @@ fn show_analysis_details(w: *RegzWindow, arena: Allocator) void { } } -fn get_or_run_full_analysis(w: *RegzWindow) ?*const CachedAnalysis { +fn get_or_run_full_analysis(wnd: *RegzWindow) ?*const CachedAnalysis { // Ensure patches are loaded and applied before running analysis - if (!w.patches_loaded) { - w.load_patch_files(); - w.patches_loaded = true; + if (!wnd.patches_loaded) { + wnd.load_patch_files(); + wnd.patches_loaded = true; } // Return cached if available - if (w.cached_analysis != null) { - return &w.cached_analysis.?; + if (wnd.cached_analysis != null) { + return &wnd.cached_analysis.?; } // Run analysis on all peripherals - const alloc = w.arena.allocator(); + const alloc = wnd.arena.allocator(); - const peripherals = w.db.get_peripherals(alloc) catch { + const peripherals = wnd.db.get_peripherals(alloc) catch { return null; }; var results: std.ArrayList(PeripheralAnalysisResult) = .empty; - var analysis = regz.Analysis.init(w.db); + var analysis = regz.Analysis.init(wnd.db); for (peripherals) |peripheral| { const result = analysis.find_equivalent_enums(alloc, peripheral.id) catch { @@ -873,18 +873,18 @@ fn get_or_run_full_analysis(w: *RegzWindow) ?*const CachedAnalysis { } // Cache result in window's arena (persists across frames) - w.cached_analysis = .{ + wnd.cached_analysis = .{ .peripheral_results = results.toOwnedSlice(alloc) catch &.{}, }; - return &w.cached_analysis.?; + return &wnd.cached_analysis.?; } -fn load_patch_files(w: *RegzWindow) void { - const chip = w.chip_info orelse return; +fn load_patch_files(wnd: *RegzWindow) void { + const chip = wnd.chip_info orelse return; // Use window's arena for persistent storage across frames - const alloc = w.arena.allocator(); + const alloc = wnd.arena.allocator(); for (chip.patch_files) |pf| { const path_result = construct_patch_path(alloc, pf); @@ -899,7 +899,7 @@ fn load_patch_files(w: *RegzWindow) void { const file = std.fs.cwd().openFile(path, .{}) catch |err| { const owned_path = alloc.dupe(u8, path) catch continue; const error_msg = std.fmt.allocPrint(alloc, "Failed to open file: {s}", .{@errorName(err)}) catch continue; - w.loaded_patches.put(w.gpa, owned_path, .{ + wnd.loaded_patches.put(wnd.gpa, owned_path, .{ .path = owned_path, .patches = null, .pending_patches = .empty, @@ -914,7 +914,7 @@ fn load_patch_files(w: *RegzWindow) void { const content = file.readToEndAllocOptions(alloc, 10 * 1024 * 1024, null, .of(u8), 0) catch |err| { const owned_path = alloc.dupe(u8, path) catch continue; const error_msg = std.fmt.allocPrint(alloc, "Failed to read file: {s}", .{@errorName(err)}) catch continue; - w.loaded_patches.put(w.gpa, owned_path, .{ + wnd.loaded_patches.put(wnd.gpa, owned_path, .{ .path = owned_path, .patches = null, .pending_patches = .empty, @@ -928,7 +928,7 @@ fn load_patch_files(w: *RegzWindow) void { const patches = std.zon.parse.fromSlice([]const regz.Patch, alloc, content, null, .{}) catch |err| { const owned_path = alloc.dupe(u8, path) catch continue; const error_msg = std.fmt.allocPrint(alloc, "Failed to parse ZON: {s}", .{@errorName(err)}) catch continue; - w.loaded_patches.put(w.gpa, owned_path, .{ + wnd.loaded_patches.put(wnd.gpa, owned_path, .{ .path = owned_path, .patches = null, .pending_patches = .empty, @@ -941,11 +941,11 @@ fn load_patch_files(w: *RegzWindow) void { // Apply patches to the database so analysis reflects them for (patches) |patch| { - apply_single_patch(w.db, alloc, patch) catch continue; + apply_single_patch(wnd.db, alloc, patch) catch continue; } const owned_path = alloc.dupe(u8, path) catch continue; - w.loaded_patches.put(w.gpa, owned_path, .{ + wnd.loaded_patches.put(wnd.gpa, owned_path, .{ .path = owned_path, .patches = patches, .pending_patches = .empty, @@ -955,7 +955,7 @@ fn load_patch_files(w: *RegzWindow) void { } // Regenerate VFS and invalidate caches since patches were applied - w.on_database_changed(); + wnd.on_database_changed(); } fn construct_patch_path(arena: Allocator, pf: RegisterSchemaUsage.PatchFile) ?[]const u8 { @@ -965,8 +965,8 @@ fn construct_patch_path(arena: Allocator, pf: RegisterSchemaUsage.PatchFile) ?[] }; } -fn show_patch_tree(w: *RegzWindow, arena: Allocator) void { - if (w.loaded_patches.count() == 0) { +fn show_patch_tree(wnd: *RegzWindow, arena: Allocator) void { + if (wnd.loaded_patches.count() == 0) { _ = dvui.label(@src(), "No patch files", .{}, .{}); return; } @@ -978,7 +978,7 @@ fn show_patch_tree(w: *RegzWindow, arena: Allocator) void { defer tree.deinit(); var file_idx: usize = 0; - for (w.loaded_patches.keys(), w.loaded_patches.values()) |path, loaded| { + for (wnd.loaded_patches.keys(), wnd.loaded_patches.values()) |path, loaded| { defer file_idx += 1; var branch = tree.branch(@src(), .{ .expanded = true }, .{ .id_extra = file_idx }); @@ -1027,7 +1027,7 @@ fn show_patch_tree(w: *RegzWindow, arena: Allocator) void { } if (op_branch.button.clicked() and !is_deleted) { - w.selected_patch = .{ .file_index = file_idx, .patch_index = patch_idx }; + wnd.selected_patch = .{ .file_index = file_idx, .patch_index = patch_idx }; } } } @@ -1046,7 +1046,7 @@ fn show_patch_tree(w: *RegzWindow, arena: Allocator) void { }); if (op_branch.button.clicked()) { - w.selected_patch = .{ .file_index = file_idx, .patch_index = full_idx }; + wnd.selected_patch = .{ .file_index = file_idx, .patch_index = full_idx }; } } } @@ -1065,9 +1065,9 @@ fn get_patch_label(patch: regz.Patch, arena: Allocator) []const u8 { }; } -fn show_patch_details(w: *RegzWindow, arena: Allocator) void { +fn show_patch_details(wnd: *RegzWindow, arena: Allocator) void { // Empty state - no patches available - if (w.loaded_patches.count() == 0) { + if (wnd.loaded_patches.count() == 0) { var vbox = dvui.box(@src(), .{ .dir = .vertical }, .{ .expand = .both, .padding = dvui.Rect.all(16), @@ -1079,7 +1079,7 @@ fn show_patch_details(w: *RegzWindow, arena: Allocator) void { return; } - const sel = w.selected_patch orelse { + const sel = wnd.selected_patch orelse { var vbox = dvui.box(@src(), .{ .dir = .vertical }, .{ .expand = .both, .padding = dvui.Rect.all(16), @@ -1090,11 +1090,11 @@ fn show_patch_details(w: *RegzWindow, arena: Allocator) void { return; }; - const keys = w.loaded_patches.keys(); + const keys = wnd.loaded_patches.keys(); if (sel.file_index >= keys.len) return; const path = keys[sel.file_index]; - const loaded = w.loaded_patches.get(path) orelse return; + const loaded = wnd.loaded_patches.get(path) orelse return; if (loaded.parse_error != null) { _ = dvui.label(@src(), "Cannot display details: file has parse errors", .{}, .{ @@ -1118,9 +1118,9 @@ fn show_patch_details(w: *RegzWindow, arena: Allocator) void { }; // Invalidate cache if selected patch changed - if (w.cached_diff) |cached| { + if (wnd.cached_diff) |cached| { if (cached.file_index != sel.file_index or cached.patch_index != sel.patch_index) { - w.cached_diff = null; + wnd.cached_diff = null; } } @@ -1154,7 +1154,7 @@ fn show_patch_details(w: *RegzWindow, arena: Allocator) void { .color_fill = dvui.Color.fromHex("EF2F27"), .color_text = dvui.Color.fromHex("FFFFFF"), })) { - w.delete_patch(sel.file_index, sel.patch_index, is_pending); + wnd.delete_patch(sel.file_index, sel.patch_index, is_pending); } } } @@ -1169,30 +1169,30 @@ fn show_patch_details(w: *RegzWindow, arena: Allocator) void { defer tab_bar.deinit(); // Fields tab button - const fields_selected = w.patch_detail_tab == .fields; + const fields_selected = wnd.patch_detail_tab == .fields; if (dvui.button(@src(), "Fields", .{}, .{ .background = fields_selected, .border = if (fields_selected) dvui.Rect.all(1) else dvui.Rect.all(0), .padding = dvui.Rect.all(4), })) { - w.patch_detail_tab = .fields; + wnd.patch_detail_tab = .fields; } // Diff tab button - const diff_selected = w.patch_detail_tab == .diff; + const diff_selected = wnd.patch_detail_tab == .diff; if (dvui.button(@src(), "Diff", .{}, .{ .background = diff_selected, .border = if (diff_selected) dvui.Rect.all(1) else dvui.Rect.all(0), .padding = dvui.Rect.all(4), })) { - w.patch_detail_tab = .diff; + wnd.patch_detail_tab = .diff; } } _ = dvui.spacer(@src(), .{ .min_size_content = .{ .h = 8 } }); // Tab content - switch (w.patch_detail_tab) { + switch (wnd.patch_detail_tab) { .fields => { switch (patch) { .override_arch => |p| show_override_arch_widget(p), @@ -1204,14 +1204,14 @@ fn show_patch_details(w: *RegzWindow, arena: Allocator) void { } }, .diff => { - w.show_patch_diff(arena, sel, patch); + wnd.show_patch_diff(arena, sel, patch); }, } } -fn show_patch_diff(w: *RegzWindow, arena: Allocator, sel: SelectedPatch, patch: regz.Patch) void { +fn show_patch_diff(wnd: *RegzWindow, arena: Allocator, sel: SelectedPatch, patch: regz.Patch) void { // Check if cache is valid - if (w.cached_diff) |cached| { + if (wnd.cached_diff) |cached| { if (cached.file_index == sel.file_index and cached.patch_index == sel.patch_index) { // Display cached diff if (cached.error_message) |err| { @@ -1221,16 +1221,16 @@ fn show_patch_diff(w: *RegzWindow, arena: Allocator, sel: SelectedPatch, patch: return; } - w.display_diff(cached.file_diffs, sel); + wnd.display_diff(cached.file_diffs, sel); return; } } // Compute new diff - w.compute_patch_diff(arena, sel, patch); + wnd.compute_patch_diff(arena, sel, patch); // Display the newly computed diff - if (w.cached_diff) |cached| { + if (wnd.cached_diff) |cached| { if (cached.error_message) |err| { _ = dvui.label(@src(), "Error computing diff: {s}", .{err}, .{ .color_text = dvui.Color.fromHex("EF2F27"), @@ -1238,11 +1238,11 @@ fn show_patch_diff(w: *RegzWindow, arena: Allocator, sel: SelectedPatch, patch: return; } - w.display_diff(cached.file_diffs, sel); + wnd.display_diff(cached.file_diffs, sel); } } -fn display_diff(w: *RegzWindow, file_diffs: []const FileDiff, sel: SelectedPatch) void { +fn display_diff(wnd: *RegzWindow, file_diffs: []const FileDiff, sel: SelectedPatch) void { if (file_diffs.len == 0) { _ = dvui.label(@src(), "No changes detected", .{}, .{}); return; @@ -1250,19 +1250,19 @@ fn display_diff(w: *RegzWindow, file_diffs: []const FileDiff, sel: SelectedPatch // Build unified diff format text var diff_text: std.ArrayList(u8) = .empty; - defer diff_text.deinit(w.gpa); + defer diff_text.deinit(wnd.gpa); for (file_diffs) |fd| { // Unified diff file headers - diff_text.appendSlice(w.gpa, "--- a/") catch continue; - diff_text.appendSlice(w.gpa, fd.filename) catch continue; - diff_text.append(w.gpa, '\n') catch continue; - diff_text.appendSlice(w.gpa, "+++ b/") catch continue; - diff_text.appendSlice(w.gpa, fd.filename) catch continue; - diff_text.append(w.gpa, '\n') catch continue; + diff_text.appendSlice(wnd.gpa, "--- a/") catch continue; + diff_text.appendSlice(wnd.gpa, fd.filename) catch continue; + diff_text.append(wnd.gpa, '\n') catch continue; + diff_text.appendSlice(wnd.gpa, "+++ b/") catch continue; + diff_text.appendSlice(wnd.gpa, fd.filename) catch continue; + diff_text.append(wnd.gpa, '\n') catch continue; // Hunk header (simplified - just use @@ -1 +1 @@) - diff_text.appendSlice(w.gpa, "@@ -1 +1 @@\n") catch continue; + diff_text.appendSlice(wnd.gpa, "@@ -1 +1 @@\n") catch continue; // Diff lines for (fd.lines) |line| { @@ -1271,11 +1271,11 @@ fn display_diff(w: *RegzWindow, file_diffs: []const FileDiff, sel: SelectedPatch .added => '+', .removed => '-', }; - diff_text.append(w.gpa, prefix) catch continue; - diff_text.appendSlice(w.gpa, line.text) catch continue; - diff_text.append(w.gpa, '\n') catch continue; + diff_text.append(wnd.gpa, prefix) catch continue; + diff_text.appendSlice(wnd.gpa, line.text) catch continue; + diff_text.append(wnd.gpa, '\n') catch continue; } - diff_text.append(w.gpa, '\n') catch continue; + diff_text.append(wnd.gpa, '\n') catch continue; } // Copy button at the top @@ -1343,18 +1343,18 @@ fn display_diff(w: *RegzWindow, file_diffs: []const FileDiff, sel: SelectedPatch } } -fn compute_patch_diff(w: *RegzWindow, temp_arena: Allocator, sel: SelectedPatch, patch: regz.Patch) void { +fn compute_patch_diff(wnd: *RegzWindow, temp_arena: Allocator, sel: SelectedPatch, patch: regz.Patch) void { _ = patch; // We'll get the patch from the loaded patches instead // Use window's persistent arena for cached data - const arena = w.arena.allocator(); + const arena = wnd.arena.allocator(); // Create TWO fresh databases: // 1. before_db - with all patches BEFORE the selected one applied // 2. after_db - with all patches UP TO AND INCLUDING the selected one applied // Create before database - var before_db = regz.Database.create_from_path(w.gpa, w.format, w.path, w.device) catch |err| { - w.cached_diff = .{ + var before_db = regz.Database.create_from_path(wnd.gpa, wnd.format, wnd.path, wnd.device) catch |err| { + wnd.cached_diff = .{ .file_index = sel.file_index, .patch_index = sel.patch_index, .file_diffs = &.{}, @@ -1365,8 +1365,8 @@ fn compute_patch_diff(w: *RegzWindow, temp_arena: Allocator, sel: SelectedPatch, defer before_db.destroy(); // Create after database - var after_db = regz.Database.create_from_path(w.gpa, w.format, w.path, w.device) catch |err| { - w.cached_diff = .{ + var after_db = regz.Database.create_from_path(wnd.gpa, wnd.format, wnd.path, wnd.device) catch |err| { + wnd.cached_diff = .{ .file_index = sel.file_index, .patch_index = sel.patch_index, .file_diffs = &.{}, @@ -1379,8 +1379,8 @@ fn compute_patch_diff(w: *RegzWindow, temp_arena: Allocator, sel: SelectedPatch, // Apply patches to both databases // For before_db: apply all patches from files [0..sel.file_index) and patches [0..sel.patch_index) from sel.file_index // For after_db: apply all patches from files [0..sel.file_index] and patches [0..sel.patch_index] from sel.file_index - const keys = w.loaded_patches.keys(); - const values = w.loaded_patches.values(); + const keys = wnd.loaded_patches.keys(); + const values = wnd.loaded_patches.values(); for (keys, values, 0..) |_, loaded, file_idx| { const orig_count = if (loaded.patches) |p| p.len else 0; @@ -1447,11 +1447,11 @@ fn compute_patch_diff(w: *RegzWindow, temp_arena: Allocator, sel: SelectedPatch, } // Generate code for before state - var before_vfs: VirtualFilesystem = .init(w.gpa); + var before_vfs: VirtualFilesystem = .init(wnd.gpa); defer before_vfs.deinit(); before_db.to_zig(before_vfs.dir(), .{}) catch |err| { - w.cached_diff = .{ + wnd.cached_diff = .{ .file_index = sel.file_index, .patch_index = sel.patch_index, .file_diffs = &.{}, @@ -1461,11 +1461,11 @@ fn compute_patch_diff(w: *RegzWindow, temp_arena: Allocator, sel: SelectedPatch, }; // Generate code for after state - var after_vfs: VirtualFilesystem = .init(w.gpa); + var after_vfs: VirtualFilesystem = .init(wnd.gpa); defer after_vfs.deinit(); after_db.to_zig(after_vfs.dir(), .{}) catch |err| { - w.cached_diff = .{ + wnd.cached_diff = .{ .file_index = sel.file_index, .patch_index = sel.patch_index, .file_diffs = &.{}, @@ -1479,7 +1479,7 @@ fn compute_patch_diff(w: *RegzWindow, temp_arena: Allocator, sel: SelectedPatch, // Recursively compare all files compare_vfs_files(arena, &before_vfs, &after_vfs, &file_diffs, .root, "") catch { - w.cached_diff = .{ + wnd.cached_diff = .{ .file_index = sel.file_index, .patch_index = sel.patch_index, .file_diffs = &.{}, @@ -1488,7 +1488,7 @@ fn compute_patch_diff(w: *RegzWindow, temp_arena: Allocator, sel: SelectedPatch, return; }; - w.cached_diff = .{ + wnd.cached_diff = .{ .file_index = sel.file_index, .patch_index = sel.patch_index, .file_diffs = file_diffs.toOwnedSlice(arena) catch &.{}, @@ -1826,27 +1826,27 @@ fn labeled_field(label_text: []const u8, value: []const u8) void { _ = dvui.label(@src(), " {s}", .{value}, .{}); } -fn save_to_directory(w: *RegzWindow, folder_path: []const u8) !void { +fn save_to_directory(wnd: *RegzWindow, folder_path: []const u8) !void { var output_dir = try std.fs.cwd().makeOpenPath(folder_path, .{}); defer output_dir.close(); - try w.save_vfs_recursive(output_dir, .root); + try wnd.save_vfs_recursive(output_dir, .root); } -fn save_vfs_recursive(w: *RegzWindow, output_dir: std.fs.Dir, parent_id: VirtualFilesystem.ID) !void { - const children = try w.vfs.get_children(w.gpa, parent_id); - defer w.gpa.free(children); +fn save_vfs_recursive(wnd: *RegzWindow, output_dir: std.fs.Dir, parent_id: VirtualFilesystem.ID) !void { + const children = try wnd.vfs.get_children(wnd.gpa, parent_id); + defer wnd.gpa.free(children); for (children) |entry| { - const name = w.vfs.get_name(entry.id); + const name = wnd.vfs.get_name(entry.id); switch (entry.kind) { .directory => { var subdir = try output_dir.makeOpenPath(name, .{}); defer subdir.close(); - try w.save_vfs_recursive(subdir, entry.id); + try wnd.save_vfs_recursive(subdir, entry.id); }, .file => { - const content = w.vfs.get_content(entry.id); + const content = wnd.vfs.get_content(entry.id); const file = try output_dir.createFile(name, .{}); defer file.close(); try file.writeAll(content); @@ -1856,7 +1856,7 @@ fn save_vfs_recursive(w: *RegzWindow, output_dir: std.fs.Dir, parent_id: Virtual } fn show_file_tree( - w: *RegzWindow, + wnd: *RegzWindow, arena: Allocator, src: std.builtin.SourceLocation, tree_init_options: dvui.TreeWidget.InitOptions, @@ -1869,12 +1869,12 @@ fn show_file_tree( var tree = dvui.TreeWidget.tree(src, tree_init_options, tree_options); defer tree.deinit(); - const children = try w.vfs.get_children(arena, .root); - try w.show_file_tree_recursive(arena, .root, children, tree, unique_id, branch_options, expander_options); + const children = try wnd.vfs.get_children(arena, .root); + try wnd.show_file_tree_recursive(arena, .root, children, tree, unique_id, branch_options, expander_options); } fn show_file_tree_recursive( - w: *RegzWindow, + wnd: *RegzWindow, arena: Allocator, directory: VirtualFilesystem.ID, children: []const VirtualFilesystem.Entry, @@ -1885,8 +1885,8 @@ fn show_file_tree_recursive( ) !void { var id_extra: usize = 0; for (children, 0..) |child_entry, i| { - if (directory == .root and w.selected_file == null and i == 0) { - w.selected_file = child_entry.id; + if (directory == .root and wnd.selected_file == null and i == 0) { + wnd.selected_file = child_entry.id; } id_extra += 1; var branch_opts_override = dvui.Options{ @@ -1894,7 +1894,7 @@ fn show_file_tree_recursive( .expand = .horizontal, }; const expanded = true; - //oconst branch_id = tree.data().id.update(w.vfs.get_name(directory)); + //oconst branch_id = tree.data().id.update(wnd.vfs.get_name(directory)); const branch = tree.branch(@src(), .{ .expanded = expanded }, branch_opts_override.override(branch_options)); defer branch.deinit(); @@ -1910,7 +1910,7 @@ fn show_file_tree_recursive( }, ); - const name = w.vfs.get_name(child_entry.id); + const name = wnd.vfs.get_name(child_entry.id); _ = dvui.label(@src(), "{s}", .{name}, .{}); dvui.icon( @@ -1947,19 +1947,19 @@ fn show_file_tree_recursive( defer box.deinit(); } - const grandchildren = try w.vfs.get_children(arena, child_entry.id); - try w.show_file_tree_recursive(arena, child_entry.id, grandchildren, tree, unique_id, branch_options, expander_options); + const grandchildren = try wnd.vfs.get_children(arena, child_entry.id); + try wnd.show_file_tree_recursive(arena, child_entry.id, grandchildren, tree, unique_id, branch_options, expander_options); }, .file => { dvui.icon(@src(), "FileIcon", dvui.entypo.text_document, .{}, .{ .gravity_y = 0.5, }); - const name = w.vfs.get_name(child_entry.id); + const name = wnd.vfs.get_name(child_entry.id); _ = dvui.label(@src(), "{s}", .{name}, .{}); if (branch.button.clicked()) { - w.selected_file = child_entry.id; + wnd.selected_file = child_entry.id; std.log.info("Clicked: {s}", .{name}); } }, @@ -2027,14 +2027,14 @@ fn process_read_only_events(te: *dvui.TextEntryWidget) void { } /// Check if there are any editable patch files loaded -fn has_editable_patch_files(w: *RegzWindow) bool { +fn has_editable_patch_files(wnd: *RegzWindow) bool { // Ensure patches are loaded first - if (!w.patches_loaded) { - w.load_patch_files(); - w.patches_loaded = true; + if (!wnd.patches_loaded) { + wnd.load_patch_files(); + wnd.patches_loaded = true; } - for (w.loaded_patches.values()) |loaded| { + for (wnd.loaded_patches.values()) |loaded| { if (loaded.is_editable and loaded.parse_error == null) { return true; } @@ -2043,18 +2043,18 @@ fn has_editable_patch_files(w: *RegzWindow) bool { } /// Show the create patch dialog UI -fn show_create_patch_dialog_ui(w: *RegzWindow, arena: Allocator) void { - if (!w.show_create_patch_dialog) return; +fn show_create_patch_dialog_ui(wnd: *RegzWindow, arena: Allocator) void { + if (!wnd.show_create_patch_dialog) return; - const pending = &(w.pending_patch_creation orelse return); + const pending = &(wnd.pending_patch_creation orelse return); - var float = dvui.floatingWindow(@src(), .{ .open_flag = &w.show_create_patch_dialog }, .{ + var float = dvui.floatingWindow(@src(), .{ .open_flag = &wnd.show_create_patch_dialog }, .{ .min_size_content = .{ .w = 350, .h = 250 }, .tag = "create_patch_dialog", }); defer float.deinit(); - float.dragAreaSet(dvui.windowHeader("Create Patch", "", &w.show_create_patch_dialog)); + float.dragAreaSet(dvui.windowHeader("Create Patch", "", &wnd.show_create_patch_dialog)); var vbox = dvui.box(@src(), .{ .dir = .vertical }, .{ .expand = .both, @@ -2084,7 +2084,7 @@ fn show_create_patch_dialog_ui(w: *RegzWindow, arena: Allocator) void { // List of patch files var file_idx: usize = 0; - for (w.loaded_patches.keys(), w.loaded_patches.values()) |path, loaded| { + for (wnd.loaded_patches.keys(), wnd.loaded_patches.values()) |path, loaded| { defer file_idx += 1; const basename = std.fs.path.basename(path); @@ -2118,8 +2118,8 @@ fn show_create_patch_dialog_ui(w: *RegzWindow, arena: Allocator) void { // Cancel button if (dvui.button(@src(), "Cancel", .{}, .{})) { - w.show_create_patch_dialog = false; - w.pending_patch_creation = null; + wnd.show_create_patch_dialog = false; + wnd.pending_patch_creation = null; } _ = dvui.spacer(@src(), .{ .min_size_content = .{ .w = 8 } }); @@ -2133,20 +2133,20 @@ fn show_create_patch_dialog_ui(w: *RegzWindow, arena: Allocator) void { .color_text = if (can_create) dvui.Color.fromHex("1C1B19") else dvui.Color.fromHex("918175"), }) and can_create) { // Create the patch - w.create_patch_from_group(arena, pending.*) catch |err| { - w.validation_error_message = std.fmt.allocPrint(w.arena.allocator(), "Failed to create patch: {s}", .{@errorName(err)}) catch "Failed to create patch"; - w.show_validation_error = true; + wnd.create_patch_from_group(arena, pending.*) catch |err| { + wnd.validation_error_message = std.fmt.allocPrint(wnd.arena.allocator(), "Failed to create patch: {s}", .{@errorName(err)}) catch "Failed to create patch"; + wnd.show_validation_error = true; }; - w.show_create_patch_dialog = false; - w.pending_patch_creation = null; + wnd.show_create_patch_dialog = false; + wnd.pending_patch_creation = null; } } } /// Create a patch from an equivalence group -fn create_patch_from_group(w: *RegzWindow, arena: Allocator, pending: PendingPatchCreation) !void { +fn create_patch_from_group(wnd: *RegzWindow, arena: Allocator, pending: PendingPatchCreation) !void { // Get the cached analysis result - const cached = w.cached_analysis orelse return error.NoAnalysis; + const cached = wnd.cached_analysis orelse return error.NoAnalysis; // Find the peripheral result const periph_idx = blk: { @@ -2171,7 +2171,7 @@ fn create_patch_from_group(w: *RegzWindow, arena: Allocator, pending: PendingPat // Create the add_enum_and_apply patch const patch = try create_add_enum_and_apply_patch( - w.arena.allocator(), + wnd.arena.allocator(), pending.peripheral_name, enum_name, group, @@ -2179,30 +2179,30 @@ fn create_patch_from_group(w: *RegzWindow, arena: Allocator, pending: PendingPat // Add to the selected patch file const file_index = pending.selected_file_index orelse return error.NoFileSelected; - const keys = w.loaded_patches.keys(); + const keys = wnd.loaded_patches.keys(); if (file_index >= keys.len) return error.InvalidFileIndex; const path = keys[file_index]; - const loaded = w.loaded_patches.getPtr(path) orelse return error.FileNotFound; + const loaded = wnd.loaded_patches.getPtr(path) orelse return error.FileNotFound; - try loaded.pending_patches.append(w.gpa, patch); + try loaded.pending_patches.append(wnd.gpa, patch); loaded.is_dirty = true; - w.has_unsaved_patches = true; + wnd.has_unsaved_patches = true; // Apply the patch to the database so analysis reflects the change - try apply_single_patch(w.db, arena, patch); + try apply_single_patch(wnd.db, arena, patch); // Refresh all views that depend on the database - w.on_database_changed(); + wnd.on_database_changed(); } /// Delete a patch from a patch file -fn delete_patch(w: *RegzWindow, file_idx: usize, patch_idx: usize, is_pending: bool) void { - const keys = w.loaded_patches.keys(); +fn delete_patch(wnd: *RegzWindow, file_idx: usize, patch_idx: usize, is_pending: bool) void { + const keys = wnd.loaded_patches.keys(); if (file_idx >= keys.len) return; const path = keys[file_idx]; - const loaded = w.loaded_patches.getPtr(path) orelse return; + const loaded = wnd.loaded_patches.getPtr(path) orelse return; if (!loaded.is_editable) return; @@ -2217,76 +2217,76 @@ fn delete_patch(w: *RegzWindow, file_idx: usize, patch_idx: usize, is_pending: b } else { // Mark original patch as deleted if (patch_idx < orig_count) { - loaded.deleted_patch_indices.append(w.gpa, patch_idx) catch return; + loaded.deleted_patch_indices.append(wnd.gpa, patch_idx) catch return; } } loaded.is_dirty = true; - w.has_unsaved_patches = true; + wnd.has_unsaved_patches = true; // Clear selected patch if it was the deleted one - if (w.selected_patch) |sel| { + if (wnd.selected_patch) |sel| { if (sel.file_index == file_idx and sel.patch_index == patch_idx) { - w.selected_patch = null; + wnd.selected_patch = null; } } // Rebuild database and reapply remaining patches - w.rebuild_database_with_patches(); + wnd.rebuild_database_with_patches(); } /// Rebuild the database from scratch and reapply all non-deleted patches -fn rebuild_database_with_patches(w: *RegzWindow) void { +fn rebuild_database_with_patches(wnd: *RegzWindow) void { // Destroy current database - w.db.destroy(); + wnd.db.destroy(); // Recreate database from source - w.db = regz.Database.create_from_path(w.gpa, w.format, w.path, w.device) catch |err| { + wnd.db = regz.Database.create_from_path(wnd.gpa, wnd.format, wnd.path, wnd.device) catch |err| { std.log.err("Failed to recreate database: {}", .{err}); return; }; - const alloc = w.arena.allocator(); + const alloc = wnd.arena.allocator(); // Reapply all non-deleted patches from all files - for (w.loaded_patches.values()) |loaded| { + for (wnd.loaded_patches.values()) |loaded| { if (loaded.patches) |patches| { for (patches, 0..) |patch, idx| { if (!loaded.is_patch_deleted(idx)) { - apply_single_patch(w.db, alloc, patch) catch continue; + apply_single_patch(wnd.db, alloc, patch) catch continue; } } } // Reapply pending patches for (loaded.pending_patches.items) |patch| { - apply_single_patch(w.db, alloc, patch) catch continue; + apply_single_patch(wnd.db, alloc, patch) catch continue; } } // Refresh all views that depend on the database - w.on_database_changed(); + wnd.on_database_changed(); } /// Called when the database changes (patches added/deleted) /// Regenerates VFS and invalidates all cached views -fn on_database_changed(w: *RegzWindow) void { +fn on_database_changed(wnd: *RegzWindow) void { // Regenerate the virtual file system with new code // Deinit old VFS and create new one - w.vfs.deinit(); - w.vfs = .init(w.gpa); - w.db.to_zig(w.vfs.dir(), .{}) catch |err| { + wnd.vfs.deinit(); + wnd.vfs = .init(wnd.gpa); + wnd.db.to_zig(wnd.vfs.dir(), .{}) catch |err| { std.log.err("Failed to regenerate code: {}", .{err}); }; // Reset displayed file to force refresh in code view - w.displayed_file = null; - w.selected_file = null; + wnd.displayed_file = null; + wnd.selected_file = null; // Invalidate cached analysis - w.cached_analysis = null; + wnd.cached_analysis = null; // Invalidate cached diff - w.cached_diff = null; + wnd.cached_diff = null; } /// Create an add_enum_and_apply patch from an equivalence group @@ -2339,16 +2339,16 @@ fn create_add_enum_and_apply_patch( } /// Show the unsaved warning dialog -fn show_unsaved_warning_dialog(w: *RegzWindow) void { - if (!w.show_unsaved_warning) return; +fn show_unsaved_warning_dialog(wnd: *RegzWindow) void { + if (!wnd.show_unsaved_warning) return; - var float = dvui.floatingWindow(@src(), .{ .open_flag = &w.show_unsaved_warning }, .{ + var float = dvui.floatingWindow(@src(), .{ .open_flag = &wnd.show_unsaved_warning }, .{ .min_size_content = .{ .w = 300, .h = 120 }, .tag = "unsaved_warning_dialog", }); defer float.deinit(); - float.dragAreaSet(dvui.windowHeader("Unsaved Changes", "", &w.show_unsaved_warning)); + float.dragAreaSet(dvui.windowHeader("Unsaved Changes", "", &wnd.show_unsaved_warning)); var vbox = dvui.box(@src(), .{ .dir = .vertical }, .{ .expand = .both, @@ -2376,17 +2376,17 @@ fn show_unsaved_warning_dialog(w: *RegzWindow) void { .color_text = dvui.Color.fromHex("1C1B19"), })) { // Try to save - var temp_arena: std.heap.ArenaAllocator = .init(w.gpa); + var temp_arena: std.heap.ArenaAllocator = .init(wnd.gpa); defer temp_arena.deinit(); - w.save_all_patches(temp_arena.allocator()) catch |err| { - w.validation_error_message = std.fmt.allocPrint(w.arena.allocator(), "Failed to save patches: {s}", .{@errorName(err)}) catch "Save failed"; - w.show_validation_error = true; - w.show_unsaved_warning = false; + wnd.save_all_patches(temp_arena.allocator()) catch |err| { + wnd.validation_error_message = std.fmt.allocPrint(wnd.arena.allocator(), "Failed to save patches: {s}", .{@errorName(err)}) catch "Save failed"; + wnd.show_validation_error = true; + wnd.show_unsaved_warning = false; return; }; - w.show_unsaved_warning = false; - w.show_window = false; + wnd.show_unsaved_warning = false; + wnd.show_window = false; } _ = dvui.spacer(@src(), .{ .min_size_content = .{ .w = 8 } }); @@ -2396,30 +2396,30 @@ fn show_unsaved_warning_dialog(w: *RegzWindow) void { .color_fill = dvui.Color.fromHex("EF2F27"), .color_text = dvui.Color.fromHex("FCE8C3"), })) { - w.show_unsaved_warning = false; - w.show_window = false; + wnd.show_unsaved_warning = false; + wnd.show_window = false; } _ = dvui.spacer(@src(), .{ .min_size_content = .{ .w = 8 } }); // Cancel button if (dvui.button(@src(), "Cancel", .{}, .{})) { - w.show_unsaved_warning = false; + wnd.show_unsaved_warning = false; } } } /// Show the validation error dialog -fn show_validation_error_dialog(w: *RegzWindow) void { - if (!w.show_validation_error) return; +fn show_validation_error_dialog(wnd: *RegzWindow) void { + if (!wnd.show_validation_error) return; - var float = dvui.floatingWindow(@src(), .{ .open_flag = &w.show_validation_error }, .{ + var float = dvui.floatingWindow(@src(), .{ .open_flag = &wnd.show_validation_error }, .{ .min_size_content = .{ .w = 350, .h = 100 }, .tag = "validation_error_dialog", }); defer float.deinit(); - float.dragAreaSet(dvui.windowHeader("Error", "", &w.show_validation_error)); + float.dragAreaSet(dvui.windowHeader("Error", "", &wnd.show_validation_error)); var vbox = dvui.box(@src(), .{ .dir = .vertical }, .{ .expand = .both, @@ -2427,7 +2427,7 @@ fn show_validation_error_dialog(w: *RegzWindow) void { }); defer vbox.deinit(); - if (w.validation_error_message) |msg| { + if (wnd.validation_error_message) |msg| { _ = dvui.label(@src(), "{s}", .{msg}, .{ .color_text = dvui.Color.fromHex("EF2F27"), }); @@ -2445,38 +2445,38 @@ fn show_validation_error_dialog(w: *RegzWindow) void { _ = dvui.spacer(@src(), .{ .expand = .horizontal }); if (dvui.button(@src(), "OK", .{}, .{})) { - w.show_validation_error = false; - w.validation_error_message = null; + wnd.show_validation_error = false; + wnd.validation_error_message = null; } } } /// Save all dirty patch files -fn save_all_patches(w: *RegzWindow, arena: Allocator) !void { +fn save_all_patches(wnd: *RegzWindow, arena: Allocator) !void { // First, validate all patches against targets - for (w.loaded_patches.keys(), w.loaded_patches.values()) |path, loaded| { + for (wnd.loaded_patches.keys(), wnd.loaded_patches.values()) |path, loaded| { if (!loaded.is_dirty) continue; // Get targets using this patch file - const targets = w.get_targets_using_patch_file(arena, path); + const targets = wnd.get_targets_using_patch_file(arena, path); // Validate against each target for (targets) |target| { - w.validate_patch_file(arena, path, target) catch |err| { - w.validation_error_message = std.fmt.allocPrint(w.arena.allocator(), "Validation failed for target '{s}': {s}", .{ target.name, @errorName(err) }) catch "Validation failed"; - w.show_validation_error = true; + wnd.validate_patch_file(arena, path, target) catch |err| { + wnd.validation_error_message = std.fmt.allocPrint(wnd.arena.allocator(), "Validation failed for target '{s}': {s}", .{ target.name, @errorName(err) }) catch "Validation failed"; + wnd.show_validation_error = true; return err; }; } } // All validations passed, write files and reload - const alloc = w.arena.allocator(); - for (w.loaded_patches.keys()) |path| { - const loaded = w.loaded_patches.getPtr(path) orelse continue; + const alloc = wnd.arena.allocator(); + for (wnd.loaded_patches.keys()) |path| { + const loaded = wnd.loaded_patches.getPtr(path) orelse continue; if (!loaded.is_dirty) continue; - try w.write_patch_file(path, loaded.*); + try wnd.write_patch_file(path, loaded.*); // Reload patches from the saved file to update loaded.patches const file = try std.fs.cwd().openFile(path, .{}); @@ -2491,7 +2491,7 @@ fn save_all_patches(w: *RegzWindow, arena: Allocator) !void { loaded.deleted_patch_indices.clearRetainingCapacity(); } - w.has_unsaved_patches = false; + wnd.has_unsaved_patches = false; } const TargetInfo = struct { @@ -2501,8 +2501,8 @@ const TargetInfo = struct { }; /// Get all targets that use a specific patch file -fn get_targets_using_patch_file(w: *RegzWindow, arena: Allocator, patch_path: []const u8) []const TargetInfo { - const rsus = w.register_schema_usages orelse return &.{}; +fn get_targets_using_patch_file(wnd: *RegzWindow, arena: Allocator, patch_path: []const u8) []const TargetInfo { + const rsus = wnd.register_schema_usages orelse return &.{}; var targets: std.ArrayList(TargetInfo) = .empty; @@ -2526,8 +2526,8 @@ fn get_targets_using_patch_file(w: *RegzWindow, arena: Allocator, patch_path: [] } /// Validate a patch file against a target by attempting to apply all patches -fn validate_patch_file(w: *RegzWindow, arena: Allocator, patch_path: []const u8, target: TargetInfo) !void { - const rsus = w.register_schema_usages orelse return error.NoSchemaUsages; +fn validate_patch_file(wnd: *RegzWindow, arena: Allocator, patch_path: []const u8, target: TargetInfo) !void { + const rsus = wnd.register_schema_usages orelse return error.NoSchemaUsages; if (target.rsu_idx >= rsus.len) return error.InvalidTarget; const rsu = rsus[target.rsu_idx]; @@ -2550,11 +2550,11 @@ fn validate_patch_file(w: *RegzWindow, arena: Allocator, patch_path: []const u8, const chip_name = if (target.chip_idx < rsu.chips.len) rsu.chips[target.chip_idx].name else null; - const db = try regz.Database.create_from_path(w.gpa, format, schema_path, chip_name); + const db = try regz.Database.create_from_path(wnd.gpa, format, schema_path, chip_name); defer db.destroy(); // Get the loaded patch data - const loaded = w.loaded_patches.get(patch_path) orelse return error.PatchFileNotFound; + const loaded = wnd.loaded_patches.get(patch_path) orelse return error.PatchFileNotFound; // Apply original patches (excluding deleted ones) if (loaded.patches) |patches| { @@ -2583,7 +2583,7 @@ fn apply_single_patch(db: *regz.Database, arena: Allocator, patch: regz.Patch) ! } /// Write a patch file combining original (non-deleted) and pending patches -fn write_patch_file(w: *RegzWindow, path: []const u8, loaded: LoadedPatchFile) !void { +fn write_patch_file(wnd: *RegzWindow, path: []const u8, loaded: LoadedPatchFile) !void { // Count non-deleted original patches var non_deleted_count: usize = 0; if (loaded.patches) |patches| { @@ -2596,8 +2596,8 @@ fn write_patch_file(w: *RegzWindow, path: []const u8, loaded: LoadedPatchFile) ! const total_len = non_deleted_count + loaded.pending_patches.items.len; - var all_patches = try w.gpa.alloc(regz.Patch, total_len); - defer w.gpa.free(all_patches); + var all_patches = try wnd.gpa.alloc(regz.Patch, total_len); + defer wnd.gpa.free(all_patches); var idx: usize = 0; if (loaded.patches) |patches| { @@ -2614,7 +2614,7 @@ fn write_patch_file(w: *RegzWindow, path: []const u8, loaded: LoadedPatchFile) ! } // Serialize to ZON - var zon_buf: std.Io.Writer.Allocating = .init(w.arena.allocator()); + var zon_buf: std.Io.Writer.Allocating = .init(wnd.arena.allocator()); try std.zon.stringify.serialize(all_patches, .{ .emit_default_optional_fields = false, }, &zon_buf.writer); From c1ea9b508f7cdea161ae8607d4a62a4626e38497 Mon Sep 17 00:00:00 2001 From: Piotr Fila Date: Thu, 25 Jun 2026 12:49:12 +0200 Subject: [PATCH 08/11] Use Io.Writer instead of File.Writer and ArrayList.Writer --- tools/sorcerer/build.zig | 82 +++++++++--------- tools/sorcerer/src/RegzWindow.zig | 78 ++++++++--------- tools/sorcerer/src/cli.zig | 138 +++++++++++------------------- 3 files changed, 129 insertions(+), 169 deletions(-) diff --git a/tools/sorcerer/build.zig b/tools/sorcerer/build.zig index db34ae604..24b2b1e38 100644 --- a/tools/sorcerer/build.zig +++ b/tools/sorcerer/build.zig @@ -477,8 +477,8 @@ fn get_port_name(path: []const u8) []const u8 { /// Generate a Zig source file containing the register schemas as compile-time constants. fn generate_zig_schema_literal(allocator: std.mem.Allocator, schemas: []const RegisterSchemaUsage) ![]const u8 { - var buf: std.ArrayList(u8) = .{}; - const writer = buf.writer(allocator); + var writer: std.Io.Writer.Allocating = .init(allocator); + var w = &writer.writer; // Helper to normalize paths (convert backslashes to forward slashes for Windows compatibility) const normalize_path = struct { @@ -491,7 +491,7 @@ fn generate_zig_schema_literal(allocator: std.mem.Allocator, schemas: []const Re } }.call; - try writer.writeAll( + try w.writeAll( \\// Auto-generated file - do not edit manually. \\// Generated by tools/sorcerer/build.zig \\ @@ -502,86 +502,86 @@ fn generate_zig_schema_literal(allocator: std.mem.Allocator, schemas: []const Re ); for (schemas) |schema| { - try writer.writeAll(" .{\n"); + try w.writeAll(" .{\n"); // Format - try writer.print(" .format = .{s},\n", .{@tagName(schema.format)}); + try w.print(" .format = .{s},\n", .{@tagName(schema.format)}); // Chips - try writer.writeAll(" .chips = &.{\n"); + try w.writeAll(" .chips = &.{\n"); for (schema.chips) |chip| { - try writer.writeAll(" .{\n"); - try writer.print(" .name = \"{s}\",\n", .{chip.name}); - try writer.print(" .target_name = \"{s}\",\n", .{chip.target_name}); + try w.writeAll(" .{\n"); + try w.print(" .name = \"{s}\",\n", .{chip.name}); + try w.print(" .target_name = \"{s}\",\n", .{chip.target_name}); // Patch files if (chip.patch_files.len > 0) { - try writer.writeAll(" .patch_files = &.{\n"); + try w.writeAll(" .patch_files = &.{\n"); for (chip.patch_files) |patch_file| { switch (patch_file) { .src_path => |src| { const sub_path = try normalize_path(allocator, src.sub_path); const build_root = try normalize_path(allocator, src.build_root); - try writer.writeAll(" .{ .src_path = .{\n"); - try writer.print(" .sub_path = \"{s}\",\n", .{sub_path}); - try writer.print(" .build_root = \"{s}\",\n", .{build_root}); - try writer.writeAll(" } },\n"); + try w.writeAll(" .{ .src_path = .{\n"); + try w.print(" .sub_path = \"{s}\",\n", .{sub_path}); + try w.print(" .build_root = \"{s}\",\n", .{build_root}); + try w.writeAll(" } },\n"); }, .dependency => |dep| { const sub_path = try normalize_path(allocator, dep.sub_path); const build_root = try normalize_path(allocator, dep.build_root); - try writer.writeAll(" .{ .dependency = .{\n"); - try writer.print(" .sub_path = \"{s}\",\n", .{sub_path}); - try writer.print(" .build_root = \"{s}\",\n", .{build_root}); - try writer.print(" .dep_name = \"{s}\",\n", .{dep.dep_name}); - try writer.writeAll(" } },\n"); + try w.writeAll(" .{ .dependency = .{\n"); + try w.print(" .sub_path = \"{s}\",\n", .{sub_path}); + try w.print(" .build_root = \"{s}\",\n", .{build_root}); + try w.print(" .dep_name = \"{s}\",\n", .{dep.dep_name}); + try w.writeAll(" } },\n"); }, } } - try writer.writeAll(" },\n"); + try w.writeAll(" },\n"); } - try writer.writeAll(" },\n"); + try w.writeAll(" },\n"); } - try writer.writeAll(" },\n"); + try w.writeAll(" },\n"); // Boards - try writer.writeAll(" .boards = &.{"); + try w.writeAll(" .boards = &.{"); for (schema.boards, 0..) |board, i| { - if (i > 0) try writer.writeAll(", "); - try writer.print(".{{ .name = \"{s}\" }}", .{board.name}); + if (i > 0) try w.writeAll(", "); + try w.print(".{{ .name = \"{s}\" }}", .{board.name}); } - try writer.writeAll("},\n"); + try w.writeAll("},\n"); // Location - try writer.writeAll(" .location = "); + try w.writeAll(" .location = "); switch (schema.location) { .src_path => |src| { const sub_path = try normalize_path(allocator, src.sub_path); const build_root = try normalize_path(allocator, src.build_root); - try writer.writeAll(".{ .src_path = .{\n"); - try writer.print(" .port_name = \"{s}\",\n", .{src.port_name}); - try writer.print(" .sub_path = \"{s}\",\n", .{sub_path}); - try writer.print(" .build_root = \"{s}\",\n", .{build_root}); - try writer.writeAll(" } },\n"); + try w.writeAll(".{ .src_path = .{\n"); + try w.print(" .port_name = \"{s}\",\n", .{src.port_name}); + try w.print(" .sub_path = \"{s}\",\n", .{sub_path}); + try w.print(" .build_root = \"{s}\",\n", .{build_root}); + try w.writeAll(" } },\n"); }, .dependency => |dep| { const sub_path = try normalize_path(allocator, dep.sub_path); const build_root = try normalize_path(allocator, dep.build_root); - try writer.writeAll(".{ .dependency = .{\n"); - try writer.print(" .sub_path = \"{s}\",\n", .{sub_path}); - try writer.print(" .build_root = \"{s}\",\n", .{build_root}); - try writer.print(" .dep_name = \"{s}\",\n", .{dep.dep_name}); - try writer.print(" .port_name = \"{s}\",\n", .{dep.port_name}); - try writer.writeAll(" } },\n"); + try w.writeAll(".{ .dependency = .{\n"); + try w.print(" .sub_path = \"{s}\",\n", .{sub_path}); + try w.print(" .build_root = \"{s}\",\n", .{build_root}); + try w.print(" .dep_name = \"{s}\",\n", .{dep.dep_name}); + try w.print(" .port_name = \"{s}\",\n", .{dep.port_name}); + try w.writeAll(" } },\n"); }, } - try writer.writeAll(" },\n"); + try w.writeAll(" },\n"); } - try writer.writeAll( + try w.writeAll( \\}; \\ ); - return buf.toOwnedSlice(allocator); + return writer.toOwnedSlice(); } diff --git a/tools/sorcerer/src/RegzWindow.zig b/tools/sorcerer/src/RegzWindow.zig index 343bb73de..f4993f219 100644 --- a/tools/sorcerer/src/RegzWindow.zig +++ b/tools/sorcerer/src/RegzWindow.zig @@ -896,7 +896,7 @@ fn load_patch_files(wnd: *RegzWindow) void { }; // Read and parse ZON file - const file = std.fs.cwd().openFile(path, .{}) catch |err| { + const reader = (std.fs.cwd().openFile(path, .{}) catch |err| { const owned_path = alloc.dupe(u8, path) catch continue; const error_msg = std.fmt.allocPrint(alloc, "Failed to open file: {s}", .{@errorName(err)}) catch continue; wnd.loaded_patches.put(wnd.gpa, owned_path, .{ @@ -908,10 +908,10 @@ fn load_patch_files(wnd: *RegzWindow) void { .is_editable = is_editable, }) catch {}; continue; - }; - defer file.close(); + }).reader(""); + defer reader.file.close(); - const content = file.readToEndAllocOptions(alloc, 10 * 1024 * 1024, null, .of(u8), 0) catch |err| { + const content = reader.file.readToEndAllocOptions(alloc, 10 * 1024 * 1024, null, .of(u8), 0) catch |err| { const owned_path = alloc.dupe(u8, path) catch continue; const error_msg = std.fmt.allocPrint(alloc, "Failed to read file: {s}", .{@errorName(err)}) catch continue; wnd.loaded_patches.put(wnd.gpa, owned_path, .{ @@ -1247,22 +1247,17 @@ fn display_diff(wnd: *RegzWindow, file_diffs: []const FileDiff, sel: SelectedPat _ = dvui.label(@src(), "No changes detected", .{}, .{}); return; } - - // Build unified diff format text - var diff_text: std.ArrayList(u8) = .empty; - defer diff_text.deinit(wnd.gpa); + var writer: std.Io.Writer.Allocating = .init(wnd.gpa); + var w = &writer.writer; for (file_diffs) |fd| { - // Unified diff file headers - diff_text.appendSlice(wnd.gpa, "--- a/") catch continue; - diff_text.appendSlice(wnd.gpa, fd.filename) catch continue; - diff_text.append(wnd.gpa, '\n') catch continue; - diff_text.appendSlice(wnd.gpa, "+++ b/") catch continue; - diff_text.appendSlice(wnd.gpa, fd.filename) catch continue; - diff_text.append(wnd.gpa, '\n') catch continue; - - // Hunk header (simplified - just use @@ -1 +1 @@) - diff_text.appendSlice(wnd.gpa, "@@ -1 +1 @@\n") catch continue; + // Unified diff file headers wunk header (simplified - just use @@ -1 +1 @@) + w.print( + \\--- a {s} + \\+++ b {s} + \\@@ -1 +1 @@ + \\ + , .{ fd.filename, fd.filename }) catch continue; // Diff lines for (fd.lines) |line| { @@ -1271,16 +1266,16 @@ fn display_diff(wnd: *RegzWindow, file_diffs: []const FileDiff, sel: SelectedPat .added => '+', .removed => '-', }; - diff_text.append(wnd.gpa, prefix) catch continue; - diff_text.appendSlice(wnd.gpa, line.text) catch continue; - diff_text.append(wnd.gpa, '\n') catch continue; + w.writeByte(prefix) catch continue; + w.writeAll(line.text) catch continue; + w.writeByte('\n') catch continue; } - diff_text.append(wnd.gpa, '\n') catch continue; + w.writeByte('\n') catch continue; } // Copy button at the top if (dvui.button(@src(), "Copy Diff", .{}, .{})) { - dvui.clipboardTextSet(diff_text.items); + dvui.clipboardTextSet(writer.written()); } _ = dvui.spacer(@src(), .{ .min_size_content = .{ .h = 8 } }); @@ -1305,7 +1300,7 @@ fn display_diff(wnd: *RegzWindow, file_diffs: []const FileDiff, sel: SelectedPat // Always set text content on first frame of this widget (unique per patch) if (dvui.firstFrame(te.data().id)) { - te.textSet(diff_text.items, false); + te.textSet(writer.written(), false); te.textLayout.selection.moveCursor(0, false); } @@ -1847,9 +1842,10 @@ fn save_vfs_recursive(wnd: *RegzWindow, output_dir: std.fs.Dir, parent_id: Virtu }, .file => { const content = wnd.vfs.get_content(entry.id); - const file = try output_dir.createFile(name, .{}); - defer file.close(); - try file.writeAll(content); + var writer = (try output_dir.createFile(name, .{})) + .writer(""); + defer writer.file.close(); + try writer.interface.writeAll(content); }, } } @@ -2479,9 +2475,9 @@ fn save_all_patches(wnd: *RegzWindow, arena: Allocator) !void { try wnd.write_patch_file(path, loaded.*); // Reload patches from the saved file to update loaded.patches - const file = try std.fs.cwd().openFile(path, .{}); - defer file.close(); - const content = try file.readToEndAllocOptions(alloc, 10 * 1024 * 1024, null, .of(u8), 0); + const reader = (try std.fs.cwd().openFile(path, .{})).reader(""); + defer reader.file.close(); + const content = try reader.file.readToEndAllocOptions(alloc, 10 * 1024 * 1024, null, .of(u8), 0); const new_patches = std.zon.parse.fromSlice([]const regz.Patch, alloc, content, null, .{}) catch null; // Update the loaded state @@ -2614,16 +2610,18 @@ fn write_patch_file(wnd: *RegzWindow, path: []const u8, loaded: LoadedPatchFile) } // Serialize to ZON - var zon_buf: std.Io.Writer.Allocating = .init(wnd.arena.allocator()); - try std.zon.stringify.serialize(all_patches, .{ - .emit_default_optional_fields = false, - }, &zon_buf.writer); - - // Write to file - const file = try std.fs.cwd().createFile(path, .{}); - defer file.close(); - try file.writeAll(zon_buf.written()); - try file.writeAll("\n"); + var writer = (try std.fs.cwd().createFile(path, .{})) + .writer(try wnd.arena.allocator().alloc(u8, 1024)); + defer writer.file.close(); + const w = &writer.interface; + + try std.zon.stringify.serialize( + all_patches, + .{ .emit_default_optional_fields = false }, + w, + ); + try w.writeByte('\n'); + try w.flush(); } // Unit tests for line diff computation diff --git a/tools/sorcerer/src/cli.zig b/tools/sorcerer/src/cli.zig index 99d2fbca0..5db82bcb2 100644 --- a/tools/sorcerer/src/cli.zig +++ b/tools/sorcerer/src/cli.zig @@ -14,31 +14,18 @@ const RegisterSchemaUsage = @import("RegisterSchemaUsage"); const Allocator = std.mem.Allocator; -const StdoutWriter = struct { - buf: [4096]u8 = undefined, - file_writer: ?std.fs.File.Writer = null, +var w_stdout: *std.Io.Writer = undefined; +var w_stderr: *std.Io.Writer = undefined; - fn writer(self: *StdoutWriter) *std.Io.Writer { - if (self.file_writer == null) { - self.file_writer = std.fs.File.stdout().writer(&self.buf); - } - return &self.file_writer.?.interface; - } -}; +pub fn main() !void { + var buf_stdout: [4096]u8 = undefined; + var writer_stdout = std.fs.File.stdout().writer(&buf_stdout); + w_stdout = &writer_stdout.interface; -const StderrWriter = struct { - buf: [4096]u8 = undefined, - file_writer: ?std.fs.File.Writer = null, + var buf_stderr: [4096]u8 = undefined; + var writer_stderr = std.fs.File.stderr().writer(&buf_stderr); + w_stderr = &writer_stderr.interface; - fn writer(self: *StderrWriter) *std.Io.Writer { - if (self.file_writer == null) { - self.file_writer = std.fs.File.stderr().writer(&self.buf); - } - return &self.file_writer.?.interface; - } -}; - -pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); const allocator = gpa.allocator(); @@ -69,19 +56,15 @@ fn run(allocator: Allocator) !void { } else if (std.mem.eql(u8, command, "-h") or std.mem.eql(u8, command, "--help")) { try print_usage(); } else { - var stderr_writer: StderrWriter = .{}; - const stderr = stderr_writer.writer(); - try stderr.print("Unknown command: {s}\n\n", .{command}); - try stderr.flush(); + try w_stderr.print("Unknown command: {s}\n\n", .{command}); + try w_stderr.flush(); try print_usage(); return error.Explained; } } fn print_usage() !void { - var stdout_writer: StdoutWriter = .{}; - const stdout = stdout_writer.writer(); - try stdout.writeAll( + try w_stdout.writeAll( \\sorcerer-cli - MicroZig Register Definition Tool \\ \\Usage: @@ -108,7 +91,7 @@ fn print_usage() !void { \\ sorcerer-cli generate RP2040 -o ./my-regs/ \\ ); - try stdout.flush(); + try w_stdout.flush(); } // ───────────────────────────────────────────────────────────────────────────── @@ -125,10 +108,8 @@ fn run_list(allocator: Allocator, args: []const []const u8) !void { if (std.mem.eql(u8, arg, "--port")) { i += 1; if (i >= args.len) { - var stderr_writer: StderrWriter = .{}; - const stderr = stderr_writer.writer(); - try stderr.writeAll("Error: --port requires a value\n"); - try stderr.flush(); + try w_stderr.writeAll("Error: --port requires a value\n"); + try w_stderr.flush(); return error.Explained; } port_filter = args[i]; @@ -138,10 +119,8 @@ fn run_list(allocator: Allocator, args: []const []const u8) !void { try print_usage(); return; } else { - var stderr_writer: StderrWriter = .{}; - const stderr = stderr_writer.writer(); - try stderr.print("Unknown option: {s}\n", .{arg}); - try stderr.flush(); + try w_stderr.print("Unknown option: {s}\n", .{arg}); + try w_stderr.flush(); return error.Explained; } } @@ -154,16 +133,13 @@ fn run_list(allocator: Allocator, args: []const []const u8) !void { } fn print_list_table(allocator: Allocator, port_filter: ?[]const u8) !void { - var stdout_writer: StdoutWriter = .{}; - const stdout = stdout_writer.writer(); - // Track seen chip names to deduplicate display var seen_chips = std.StringHashMap(void).init(allocator); defer seen_chips.deinit(); // Print header - stdout.print("{s:<24} {s}\n", .{ "CHIP", "PORT" }) catch |err| return handle_write_error(err); - stdout.print("{s:-<24} {s:-<24}\n", .{ "", "" }) catch |err| return handle_write_error(err); + w_stdout.print("{s:<24} {s}\n", .{ "CHIP", "PORT" }) catch |err| return handle_write_error(err); + w_stdout.print("{s:-<24} {s:-<24}\n", .{ "", "" }) catch |err| return handle_write_error(err); // Print entries (one line per unique chip) for (schemas.schemas) |schema| { @@ -183,10 +159,10 @@ fn print_list_table(allocator: Allocator, port_filter: ?[]const u8) !void { } seen_chips.put(chip.name, {}) catch {}; - stdout.print("{s:<24} {s}\n", .{ chip.name, port_name }) catch |err| return handle_write_error(err); + w_stdout.print("{s:<24} {s}\n", .{ chip.name, port_name }) catch |err| return handle_write_error(err); } } - stdout.flush() catch |err| return handle_write_error(err); + w_stdout.flush() catch |err| return handle_write_error(err); } /// Handle write errors - exit silently on BrokenPipe so that we can e.g. pipe to `more`. @@ -237,11 +213,9 @@ fn print_list_json(allocator: Allocator, port_filter: ?[]const u8) !void { const json_str = try std.json.Stringify.valueAlloc(allocator, entries.items, .{ .whitespace = .indent_2 }); defer allocator.free(json_str); - var stdout_writer: StdoutWriter = .{}; - const stdout = stdout_writer.writer(); - stdout.writeAll(json_str) catch |err| return handle_write_error(err); - stdout.writeByte('\n') catch |err| return handle_write_error(err); - stdout.flush() catch |err| return handle_write_error(err); + w_stdout.writeAll(json_str) catch |err| return handle_write_error(err); + w_stdout.writeByte('\n') catch |err| return handle_write_error(err); + w_stdout.flush() catch |err| return handle_write_error(err); } const JsonEntry = struct { @@ -271,10 +245,8 @@ fn run_generate(allocator: Allocator, args: []const []const u8) !void { if (std.mem.eql(u8, arg, "-o") or std.mem.eql(u8, arg, "--output")) { i += 1; if (i >= args.len) { - var stderr_writer: StderrWriter = .{}; - const stderr = stderr_writer.writer(); - try stderr.writeAll("Error: --output requires a value\n"); - try stderr.flush(); + try w_stderr.writeAll("Error: --output requires a value\n"); + try w_stderr.flush(); return error.Explained; } output_path = args[i]; @@ -284,30 +256,24 @@ fn run_generate(allocator: Allocator, args: []const []const u8) !void { } else if (!std.mem.startsWith(u8, arg, "-")) { chip_name = arg; } else { - var stderr_writer: StderrWriter = .{}; - const stderr = stderr_writer.writer(); - try stderr.print("Unknown option: {s}\n", .{arg}); - try stderr.flush(); + try w_stderr.print("Unknown option: {s}\n", .{arg}); + try w_stderr.flush(); return error.Explained; } } const chip = chip_name orelse { - var stderr_writer: StderrWriter = .{}; - const stderr = stderr_writer.writer(); - try stderr.writeAll("Error: chip name is required\n"); - try stderr.writeAll("Usage: sorcerer-cli generate [-o ]\n"); - try stderr.flush(); + try w_stderr.writeAll("Error: chip name is required\n"); + try w_stderr.writeAll("Usage: sorcerer-cli generate [-o ]\n"); + try w_stderr.flush(); return error.Explained; }; // Find matching schema const schema = find_schema(chip) orelse { - var stderr_writer: StderrWriter = .{}; - const stderr = stderr_writer.writer(); - try stderr.print("Error: chip '{s}' not found\n", .{chip}); - try stderr.writeAll("Use 'sorcerer-cli list' to see available chips\n"); - try stderr.flush(); + try w_stderr.print("Error: chip '{s}' not found\n", .{chip}); + try w_stderr.writeAll("Use 'sorcerer-cli list' to see available chips\n"); + try w_stderr.flush(); return error.Explained; }; @@ -331,19 +297,14 @@ fn generate_code( chip_name: []const u8, output_path: []const u8, ) !void { - var stderr_writer: StderrWriter = .{}; - const stderr = stderr_writer.writer(); - var stdout_writer: StdoutWriter = .{}; - const stdout = stdout_writer.writer(); - // Get full path to register definition file const input_path = try get_full_path(allocator, schema.location); defer allocator.free(input_path); - try stdout.print("Generating register definitions for {s}...\n", .{chip_name}); - try stdout.print(" Input: {s}\n", .{input_path}); - try stdout.print(" Output: {s}/\n", .{output_path}); - try stdout.flush(); + try w_stdout.print("Generating register definitions for {s}...\n", .{chip_name}); + try w_stdout.print(" Input: {s}\n", .{input_path}); + try w_stdout.print(" Output: {s}/\n", .{output_path}); + try w_stdout.flush(); // Map format const format: regz.Database.Format = switch (schema.format) { @@ -355,8 +316,8 @@ fn generate_code( // Create database from register definition file var db = regz.Database.create_from_path(allocator, format, input_path, chip_name) catch |err| { - try stderr.print("Error loading register definition: {}\n", .{err}); - try stderr.flush(); + try w_stderr.print("Error loading register definition: {}\n", .{err}); + try w_stderr.flush(); return error.Explained; }; defer db.destroy(); @@ -366,23 +327,23 @@ fn generate_code( defer vfs.deinit(); db.to_zig(vfs.dir(), .{}) catch |err| { - try stderr.print("Error generating Zig code: {}\n", .{err}); - try stderr.flush(); + try w_stderr.print("Error generating Zig code: {}\n", .{err}); + try w_stderr.flush(); return error.Explained; }; // Write virtual filesystem contents to actual directory var output_dir = std.fs.cwd().makeOpenPath(output_path, .{}) catch |err| { - try stderr.print("Error creating output directory: {}\n", .{err}); - try stderr.flush(); + try w_stderr.print("Error creating output directory: {}\n", .{err}); + try w_stderr.flush(); return error.Explained; }; defer output_dir.close(); const files_written = try write_vfs_to_dir(allocator, &vfs, output_dir, .root, ""); - try stdout.print("Generated {d} file(s)\n", .{files_written}); - try stdout.flush(); + try w_stdout.print("Generated {d} file(s)\n", .{files_written}); + try w_stdout.flush(); } fn get_full_path(allocator: Allocator, location: RegisterSchemaUsage.Location) ![]const u8 { @@ -421,9 +382,10 @@ fn write_vfs_to_dir( try output_dir.makePath(dirname); } - const file = try output_dir.createFile(full_path, .{}); - defer file.close(); - try file.writeAll(content); + var writer = (try output_dir.createFile(full_path, .{})).writer(""); + defer writer.file.close(); + try writer.interface.writeAll(content); + try writer.interface.flush(); files_written += 1; }, From f355054cc98183edff046211c3014d8beef1d800 Mon Sep 17 00:00:00 2001 From: Piotr Fila Date: Thu, 25 Jun 2026 19:08:54 +0200 Subject: [PATCH 09/11] regz: Export VirtualFilesystem and make patch's enum type --- tools/regz/src/module.zig | 2 ++ tools/regz/src/patch.zig | 22 ++++++++++------------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tools/regz/src/module.zig b/tools/regz/src/module.zig index 21c70d4f3..040e82928 100644 --- a/tools/regz/src/module.zig +++ b/tools/regz/src/module.zig @@ -4,6 +4,7 @@ pub const arm = @import("arch/arm.zig"); pub const Patch = @import("patch.zig").Patch; pub const Arch = @import("arch.zig").Arch; pub const embassy = @import("embassy.zig"); +pub const VirtualFilesystem = @import("VirtualFilesystem.zig"); test { _ = Database; @@ -12,4 +13,5 @@ test { _ = Patch; _ = Arch; _ = embassy; + _ = VirtualFilesystem; } diff --git a/tools/regz/src/patch.zig b/tools/regz/src/patch.zig index a95a4926f..7831da8d6 100644 --- a/tools/regz/src/patch.zig +++ b/tools/regz/src/patch.zig @@ -3,22 +3,20 @@ const Allocator = std.mem.Allocator; const Database = @import("Database.zig"); const Arch = @import("arch.zig").Arch; -pub const Type = struct { - pub const EnumField = struct { - name: []const u8, - description: ?[]const u8 = null, - value: u32, - }; - +pub const Patch = union(enum) { pub const Enum = struct { + pub const Field = struct { + name: []const u8, + description: ?[]const u8 = null, + value: u32, + }; + name: []const u8, description: ?[]const u8 = null, bitsize: u8, - fields: []const EnumField = &.{}, + fields: []const Field = &.{}, }; -}; -pub const Patch = union(enum) { override_arch: struct { device_name: []const u8, arch: Arch, @@ -31,7 +29,7 @@ pub const Patch = union(enum) { }, add_enum: struct { parent: []const u8, - @"enum": Type.Enum, + @"enum": Enum, }, /// The replaced type MUST be the same size. Bit or Byte size depends on the /// context @@ -50,7 +48,7 @@ pub const Patch = union(enum) { /// combines `add_enum` with multiple `set_enum_type` operations. add_enum_and_apply: struct { parent: []const u8, - @"enum": Type.Enum, + @"enum": Enum, apply_to: []const []const u8, }, From eb38c375c584f948e3e950e42c62b8ba5169c15a Mon Sep 17 00:00:00 2001 From: Piotr Fila Date: Thu, 25 Jun 2026 19:18:39 +0200 Subject: [PATCH 10/11] sorcerer: Update to zig 0.16 --- tools/sorcerer/build.zig | 10 ++--- tools/sorcerer/build.zig.zon | 12 +++--- tools/sorcerer/src/RegzWindow.zig | 66 +++++++++++++++---------------- tools/sorcerer/src/cli.zig | 52 ++++++++++++------------ tools/sorcerer/src/main.zig | 2 +- 5 files changed, 67 insertions(+), 75 deletions(-) diff --git a/tools/sorcerer/build.zig b/tools/sorcerer/build.zig index 24b2b1e38..a1d2cc0b8 100644 --- a/tools/sorcerer/build.zig +++ b/tools/sorcerer/build.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const anyverz = @import("anyverz"); const LazyPath = std.Build.LazyPath; const microzig = @import("microzig"); const RegisterSchemaUsage = @import("src/RegisterSchemaUsage.zig"); @@ -63,7 +64,7 @@ pub fn build(b: *std.Build) void { b.installArtifact(cli_exe); const run_cli_cmd = b.addRunArtifact(cli_exe); - run_cli_cmd.addPassthruArgs(); + anyverz.addPassthruArgs(b, run_cli_cmd); run_cli_cmd.step.dependOn(b.getInstallStep()); const run_cli_step = b.step("run-cli", "Run the CLI tool"); @@ -119,11 +120,6 @@ pub fn build(b: *std.Build) void { }); if (tree_sitter_zig_dep) |tsd| { exe_mod.addIncludePath(tsd.path("src")); - exe_mod.addCSourceFiles(.{ - .root = tsd.path(""), - .files = &.{"src/parser.c"}, - .flags = &.{"-std=c11"}, - }); } const tree_sitter_diff_dep = b.lazyDependency("tree_sitter_diff", .{ @@ -152,7 +148,7 @@ pub fn build(b: *std.Build) void { b.installArtifact(exe); const run_cmd = b.addRunArtifact(exe); - run_cmd.addPassthruArgs(); + anyverz.addPassthruArgs(b, run_cmd); // I only want the path to the register schema file, not the lazy path, // because I want to be able to refresh it with `zig build` while sorcerer diff --git a/tools/sorcerer/build.zig.zon b/tools/sorcerer/build.zig.zon index 49229fc89..5b73e4854 100644 --- a/tools/sorcerer/build.zig.zon +++ b/tools/sorcerer/build.zig.zon @@ -6,16 +6,17 @@ .path = "../..", }, .dvui = .{ - .url = "git+https://github.com/david-vanderson/dvui#0ca4d5c7a7915ee53a7f69c7facb8b48b7807f40", - .hash = "dvui-0.4.0-dev-AQFJmc3h2gBHoPtLm2KO1NEWuqV-kPm1cusWeY6OkHKf", + .url = "git+https://github.com/david-vanderson/dvui#134905dc9923e1e91fdce56c99b534060b6e77f6", + .hash = "dvui-0.5.0-dev-AQFJmTc_9wCyJsvAY3TqdGxWDcfbFbd9kDCCVqrl4Vy7", }, .serial = .{ .url = "git+https://github.com/ZigEmbeddedGroup/serial#fbd7389ff8bbc9fa362aa74081588755b5d028a0", .hash = "serial-0.0.1-PoeRzF60AAAN8Iu0yXTIX-t3DVzsnmN7vWHKM2HA2Zbq", }, .tree_sitter_zig = .{ - .url = "git+https://github.com/tree-sitter-grammars/tree-sitter-zig#6479aa13f32f701c383083d8b28360ebd682fb7d", - .hash = "tree_sitter_zig-1.1.2-AAAAAJ57XACsD_XBHC4i6G6rnCsZ1dZj7CLRTC09toEB", + // Switch back to main repo when it gets updated + .url = "git+https://github.com/Axiomatic-Mind/tree-sitter-zig#a86528e98ea85ee4a03b445bc581f520f58de15e", + .hash = "tree_sitter_zig-1.1.2-7Hcpfz-EXAAyx6X1nCueM1Gm9TXwQYBmanFE3A2sJXkH", }, .tree_sitter_diff = .{ .url = "git+https://github.com/tree-sitter-grammars/tree-sitter-diff#2520c3f934b3179bb540d23e0ef45f75304b5fed", @@ -25,8 +26,9 @@ .url = "git+https://github.com/ziglibs/diffz#a20dd1f11b10819a6f570f98b42e1c91e3704357", .hash = "diffz-0.0.1-G2tlIQrOAQCfH15jdyaLyrMgV8eGPouFhkCeYFTmJaLk", }, + .anyverz = .{ .path = "../../modules/anyverz" }, }, - .minimum_zig_version = "0.15.2", + .minimum_zig_version = "0.16.0", .fingerprint = 0x5f77e0bc82473a35, .paths = .{ "README.md", diff --git a/tools/sorcerer/src/RegzWindow.zig b/tools/sorcerer/src/RegzWindow.zig index f4993f219..1027ccefe 100644 --- a/tools/sorcerer/src/RegzWindow.zig +++ b/tools/sorcerer/src/RegzWindow.zig @@ -238,7 +238,7 @@ pub fn create( const window = try gpa.create(RegzWindow); errdefer gpa.destroy(window); - var db = try regz.Database.create_from_path(gpa, format, path, device); + var db = try regz.Database.create_from_path(gpa, dvui.io, format, path, device); errdefer db.destroy(); var arena: std.heap.ArenaAllocator = .init(gpa); @@ -268,7 +268,7 @@ pub fn create( .register_schema_usages = register_schema_usages, }; - try db.to_zig(window.vfs.dir(), .{}); + try db.to_zig(window.vfs.io(), window.vfs.root_dir(), .{}); count += 1; @@ -896,7 +896,7 @@ fn load_patch_files(wnd: *RegzWindow) void { }; // Read and parse ZON file - const reader = (std.fs.cwd().openFile(path, .{}) catch |err| { + var reader = (std.Io.Dir.cwd().openFile(dvui.io, path, .{}) catch |err| { const owned_path = alloc.dupe(u8, path) catch continue; const error_msg = std.fmt.allocPrint(alloc, "Failed to open file: {s}", .{@errorName(err)}) catch continue; wnd.loaded_patches.put(wnd.gpa, owned_path, .{ @@ -908,10 +908,10 @@ fn load_patch_files(wnd: *RegzWindow) void { .is_editable = is_editable, }) catch {}; continue; - }).reader(""); - defer reader.file.close(); + }).reader(dvui.io, ""); + defer reader.file.close(dvui.io); - const content = reader.file.readToEndAllocOptions(alloc, 10 * 1024 * 1024, null, .of(u8), 0) catch |err| { + const content = reader.interface.allocRemainingAlignedSentinel(alloc, .limited(10 * 1024 * 1024), .of(u8), 0) catch |err| { const owned_path = alloc.dupe(u8, path) catch continue; const error_msg = std.fmt.allocPrint(alloc, "Failed to read file: {s}", .{@errorName(err)}) catch continue; wnd.loaded_patches.put(wnd.gpa, owned_path, .{ @@ -925,7 +925,7 @@ fn load_patch_files(wnd: *RegzWindow) void { continue; }; - const patches = std.zon.parse.fromSlice([]const regz.Patch, alloc, content, null, .{}) catch |err| { + const patches = std.zon.parse.fromSliceAlloc([]const regz.Patch, alloc, content, null, .{}) catch |err| { const owned_path = alloc.dupe(u8, path) catch continue; const error_msg = std.fmt.allocPrint(alloc, "Failed to parse ZON: {s}", .{@errorName(err)}) catch continue; wnd.loaded_patches.put(wnd.gpa, owned_path, .{ @@ -1348,7 +1348,7 @@ fn compute_patch_diff(wnd: *RegzWindow, temp_arena: Allocator, sel: SelectedPatc // 2. after_db - with all patches UP TO AND INCLUDING the selected one applied // Create before database - var before_db = regz.Database.create_from_path(wnd.gpa, wnd.format, wnd.path, wnd.device) catch |err| { + var before_db = regz.Database.create_from_path(wnd.gpa, dvui.io, wnd.format, wnd.path, wnd.device) catch |err| { wnd.cached_diff = .{ .file_index = sel.file_index, .patch_index = sel.patch_index, @@ -1360,7 +1360,7 @@ fn compute_patch_diff(wnd: *RegzWindow, temp_arena: Allocator, sel: SelectedPatc defer before_db.destroy(); // Create after database - var after_db = regz.Database.create_from_path(wnd.gpa, wnd.format, wnd.path, wnd.device) catch |err| { + var after_db = regz.Database.create_from_path(wnd.gpa, dvui.io, wnd.format, wnd.path, wnd.device) catch |err| { wnd.cached_diff = .{ .file_index = sel.file_index, .patch_index = sel.patch_index, @@ -1445,7 +1445,7 @@ fn compute_patch_diff(wnd: *RegzWindow, temp_arena: Allocator, sel: SelectedPatc var before_vfs: VirtualFilesystem = .init(wnd.gpa); defer before_vfs.deinit(); - before_db.to_zig(before_vfs.dir(), .{}) catch |err| { + before_db.to_zig(wnd.vfs.io(), before_vfs.root_dir(), .{}) catch |err| { wnd.cached_diff = .{ .file_index = sel.file_index, .patch_index = sel.patch_index, @@ -1459,7 +1459,7 @@ fn compute_patch_diff(wnd: *RegzWindow, temp_arena: Allocator, sel: SelectedPatc var after_vfs: VirtualFilesystem = .init(wnd.gpa); defer after_vfs.deinit(); - after_db.to_zig(after_vfs.dir(), .{}) catch |err| { + after_db.to_zig(wnd.vfs.io(), after_vfs.root_dir(), .{}) catch |err| { wnd.cached_diff = .{ .file_index = sel.file_index, .patch_index = sel.patch_index, @@ -1822,13 +1822,13 @@ fn labeled_field(label_text: []const u8, value: []const u8) void { } fn save_to_directory(wnd: *RegzWindow, folder_path: []const u8) !void { - var output_dir = try std.fs.cwd().makeOpenPath(folder_path, .{}); - defer output_dir.close(); + var output_dir = try std.Io.Dir.cwd().createDirPathOpen(dvui.io, folder_path, .{}); + defer output_dir.close(dvui.io); try wnd.save_vfs_recursive(output_dir, .root); } -fn save_vfs_recursive(wnd: *RegzWindow, output_dir: std.fs.Dir, parent_id: VirtualFilesystem.ID) !void { +fn save_vfs_recursive(wnd: *RegzWindow, output_dir: std.Io.Dir, parent_id: VirtualFilesystem.ID) !void { const children = try wnd.vfs.get_children(wnd.gpa, parent_id); defer wnd.gpa.free(children); @@ -1836,15 +1836,15 @@ fn save_vfs_recursive(wnd: *RegzWindow, output_dir: std.fs.Dir, parent_id: Virtu const name = wnd.vfs.get_name(entry.id); switch (entry.kind) { .directory => { - var subdir = try output_dir.makeOpenPath(name, .{}); - defer subdir.close(); + var subdir = try output_dir.createDirPathOpen(dvui.io, name, .{}); + defer subdir.close(dvui.io); try wnd.save_vfs_recursive(subdir, entry.id); }, .file => { const content = wnd.vfs.get_content(entry.id); - var writer = (try output_dir.createFile(name, .{})) - .writer(""); - defer writer.file.close(); + var writer = (try output_dir.createFile(dvui.io, name, .{})) + .writer(dvui.io, ""); + defer writer.file.close(dvui.io); try writer.interface.writeAll(content); }, } @@ -2237,7 +2237,7 @@ fn rebuild_database_with_patches(wnd: *RegzWindow) void { wnd.db.destroy(); // Recreate database from source - wnd.db = regz.Database.create_from_path(wnd.gpa, wnd.format, wnd.path, wnd.device) catch |err| { + wnd.db = regz.Database.create_from_path(wnd.gpa, dvui.io, wnd.format, wnd.path, wnd.device) catch |err| { std.log.err("Failed to recreate database: {}", .{err}); return; }; @@ -2270,7 +2270,7 @@ fn on_database_changed(wnd: *RegzWindow) void { // Deinit old VFS and create new one wnd.vfs.deinit(); wnd.vfs = .init(wnd.gpa); - wnd.db.to_zig(wnd.vfs.dir(), .{}) catch |err| { + wnd.db.to_zig(wnd.vfs.io(), wnd.vfs.root_dir(), .{}) catch |err| { std.log.err("Failed to regenerate code: {}", .{err}); }; @@ -2295,13 +2295,8 @@ fn create_add_enum_and_apply_patch( // Build parent path: "types.peripherals.{peripheral_name}" const parent = try std.fmt.allocPrint(alloc, "types.peripherals.{s}", .{peripheral_name}); - // Get the EnumField type from the Patch type using type introspection - const AddEnumAndApply = std.meta.TagPayload(regz.Patch, .add_enum_and_apply); - const EnumType = @TypeOf(@as(AddEnumAndApply, undefined).@"enum"); - const EnumFieldType = std.meta.Child(@TypeOf(@as(EnumType, undefined).fields)); - // Convert fields (note: Database.EnumField.value is u64, Patch.EnumField.value is u32) - var fields = try alloc.alloc(EnumFieldType, group.fields.len); + var fields = try alloc.alloc(regz.Patch.Enum.Field, group.fields.len); for (group.fields, 0..) |field, i| { fields[i] = .{ .name = try alloc.dupe(u8, field.name), @@ -2475,10 +2470,11 @@ fn save_all_patches(wnd: *RegzWindow, arena: Allocator) !void { try wnd.write_patch_file(path, loaded.*); // Reload patches from the saved file to update loaded.patches - const reader = (try std.fs.cwd().openFile(path, .{})).reader(""); - defer reader.file.close(); - const content = try reader.file.readToEndAllocOptions(alloc, 10 * 1024 * 1024, null, .of(u8), 0); - const new_patches = std.zon.parse.fromSlice([]const regz.Patch, alloc, content, null, .{}) catch null; + var reader = (try std.Io.Dir.cwd().openFile(dvui.io, path, .{})) + .reader(dvui.io, ""); + defer reader.file.close(dvui.io); + const content = try reader.interface.allocRemainingAlignedSentinel(alloc, .limited(10 * 1024 * 1024), .of(u8), 0); + const new_patches = std.zon.parse.fromSliceAlloc([]const regz.Patch, alloc, content, null, .{}) catch null; // Update the loaded state loaded.patches = new_patches; @@ -2546,7 +2542,7 @@ fn validate_patch_file(wnd: *RegzWindow, arena: Allocator, patch_path: []const u const chip_name = if (target.chip_idx < rsu.chips.len) rsu.chips[target.chip_idx].name else null; - const db = try regz.Database.create_from_path(wnd.gpa, format, schema_path, chip_name); + const db = try regz.Database.create_from_path(wnd.gpa, dvui.io, format, schema_path, chip_name); defer db.destroy(); // Get the loaded patch data @@ -2610,9 +2606,9 @@ fn write_patch_file(wnd: *RegzWindow, path: []const u8, loaded: LoadedPatchFile) } // Serialize to ZON - var writer = (try std.fs.cwd().createFile(path, .{})) - .writer(try wnd.arena.allocator().alloc(u8, 1024)); - defer writer.file.close(); + var writer = (try std.Io.Dir.cwd().createFile(dvui.io, path, .{})) + .writer(dvui.io, try wnd.arena.allocator().alloc(u8, 1024)); + defer writer.file.close(dvui.io); const w = &writer.interface; try std.zon.stringify.serialize( diff --git a/tools/sorcerer/src/cli.zig b/tools/sorcerer/src/cli.zig index 5db82bcb2..16fdc905f 100644 --- a/tools/sorcerer/src/cli.zig +++ b/tools/sorcerer/src/cli.zig @@ -17,20 +17,16 @@ const Allocator = std.mem.Allocator; var w_stdout: *std.Io.Writer = undefined; var w_stderr: *std.Io.Writer = undefined; -pub fn main() !void { - var buf_stdout: [4096]u8 = undefined; - var writer_stdout = std.fs.File.stdout().writer(&buf_stdout); +pub fn main(init: std.process.Init) !void { + var writer_stdout = std.Io.File.stdout() + .writer(init.io, init.arena.allocator().alloc(u8, 4 * 1024)); w_stdout = &writer_stdout.interface; - var buf_stderr: [4096]u8 = undefined; - var writer_stderr = std.fs.File.stderr().writer(&buf_stderr); + var writer_stderr = std.Io.File.stderr() + .writer(init.io, init.arena.allocator().alloc(u8, 4 * 1024)); w_stderr = &writer_stderr.interface; - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - defer _ = gpa.deinit(); - const allocator = gpa.allocator(); - - run(allocator) catch |err| { + run(init) catch |err| { switch (err) { error.Explained => std.process.exit(1), else => return err, @@ -38,9 +34,8 @@ pub fn main() !void { }; } -fn run(allocator: Allocator) !void { - const args = try std.process.argsAlloc(allocator); - defer std.process.argsFree(allocator, args); +fn run(init: std.process.Init) !void { + const args = try init.minimal.args.toSlice(init.arena.allocator()); if (args.len < 2) { try print_usage(); @@ -50,9 +45,9 @@ fn run(allocator: Allocator) !void { const command = args[1]; if (std.mem.eql(u8, command, "list")) { - try run_list(allocator, args[2..]); + try run_list(init.gpa, args[2..]); } else if (std.mem.eql(u8, command, "generate")) { - try run_generate(allocator, args[2..]); + try run_generate(init.gpa, init.io, args[2..]); } else if (std.mem.eql(u8, command, "-h") or std.mem.eql(u8, command, "--help")) { try print_usage(); } else { @@ -235,7 +230,7 @@ fn get_port_name(location: RegisterSchemaUsage.Location) []const u8 { // Generate command // ───────────────────────────────────────────────────────────────────────────── -fn run_generate(allocator: Allocator, args: []const []const u8) !void { +fn run_generate(allocator: Allocator, io: std.Io, args: []const []const u8) !void { var chip_name: ?[]const u8 = null; var output_path: []const u8 = "./zig-out"; @@ -277,7 +272,7 @@ fn run_generate(allocator: Allocator, args: []const []const u8) !void { return error.Explained; }; - try generate_code(allocator, schema, chip, output_path); + try generate_code(allocator, io, schema, chip, output_path); } fn find_schema(chip_name: []const u8) ?RegisterSchemaUsage { @@ -293,6 +288,7 @@ fn find_schema(chip_name: []const u8) ?RegisterSchemaUsage { fn generate_code( allocator: Allocator, + io: std.Io, schema: RegisterSchemaUsage, chip_name: []const u8, output_path: []const u8, @@ -315,7 +311,7 @@ fn generate_code( }; // Create database from register definition file - var db = regz.Database.create_from_path(allocator, format, input_path, chip_name) catch |err| { + var db = regz.Database.create_from_path(allocator, io, format, input_path, chip_name) catch |err| { try w_stderr.print("Error loading register definition: {}\n", .{err}); try w_stderr.flush(); return error.Explained; @@ -326,21 +322,21 @@ fn generate_code( var vfs = regz.VirtualFilesystem.init(allocator); defer vfs.deinit(); - db.to_zig(vfs.dir(), .{}) catch |err| { + db.to_zig(io, vfs.root_dir(), .{}) catch |err| { try w_stderr.print("Error generating Zig code: {}\n", .{err}); try w_stderr.flush(); return error.Explained; }; // Write virtual filesystem contents to actual directory - var output_dir = std.fs.cwd().makeOpenPath(output_path, .{}) catch |err| { + var output_dir = std.Io.Dir.cwd().createDirPathOpen(io, output_path, .{}) catch |err| { try w_stderr.print("Error creating output directory: {}\n", .{err}); try w_stderr.flush(); return error.Explained; }; - defer output_dir.close(); + defer output_dir.close(io); - const files_written = try write_vfs_to_dir(allocator, &vfs, output_dir, .root, ""); + const files_written = try write_vfs_to_dir(allocator, io, &vfs, output_dir, .root, ""); try w_stdout.print("Generated {d} file(s)\n", .{files_written}); try w_stdout.flush(); @@ -355,8 +351,9 @@ fn get_full_path(allocator: Allocator, location: RegisterSchemaUsage.Location) ! fn write_vfs_to_dir( allocator: Allocator, + io: std.Io, vfs: *regz.VirtualFilesystem, - output_dir: std.fs.Dir, + output_dir: std.Io.Dir, parent_id: regz.VirtualFilesystem.ID, parent_path: []const u8, ) !usize { @@ -379,18 +376,19 @@ fn write_vfs_to_dir( // Create subdirectory if needed if (std.fs.path.dirname(full_path)) |dirname| { - try output_dir.makePath(dirname); + try output_dir.createDirPath(io, dirname); } - var writer = (try output_dir.createFile(full_path, .{})).writer(""); - defer writer.file.close(); + var writer = (try output_dir.createFile(io, full_path, .{})) + .writer(io, ""); + defer writer.file.close(io); try writer.interface.writeAll(content); try writer.interface.flush(); files_written += 1; }, .directory => { - files_written += try write_vfs_to_dir(allocator, vfs, output_dir, child.id, full_path); + files_written += try write_vfs_to_dir(allocator, io, vfs, output_dir, child.id, full_path); }, } } diff --git a/tools/sorcerer/src/main.zig b/tools/sorcerer/src/main.zig index a711cd89a..a5efb547f 100644 --- a/tools/sorcerer/src/main.zig +++ b/tools/sorcerer/src/main.zig @@ -127,7 +127,7 @@ pub fn frame() !dvui.App.Result { // Stats floating window show_stats_window(); - dvui.Examples.demo(); + dvui.Examples.demo(.lite); from_microzig_menu(); search_chips_window(); From 69910d5bc06d4b850a356c28684df85c9bce617e Mon Sep 17 00:00:00 2001 From: Piotr Fila Date: Sat, 27 Jun 2026 21:33:36 +0200 Subject: [PATCH 11/11] Fix anyverz.fieldAttrs on zig 0.17 --- modules/anyverz/build.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/anyverz/build.zig b/modules/anyverz/build.zig index 14a1621de..abaed9db7 100644 --- a/modules/anyverz/build.zig +++ b/modules/anyverz/build.zig @@ -60,7 +60,7 @@ pub fn fieldAttrs(T: type) [fieldsLen(T)]StructFieldAttributes { return attrs; }, .@"0.17" => switch (@typeInfo(T)) { - inline .@"struct", .@"union" => |info| return info.field_attrs, + inline .@"struct", .@"union" => |info| return info.field_attrs[0..fieldsLen(T)].*, else => @compileError("Expected struct or union type, found '" ++ @typeName(T) ++ "'"), }, };