From 0bf0375200d28e558f036103b9a19976f8d45872 Mon Sep 17 00:00:00 2001 From: Julien Dessaux Date: Sat, 3 Dec 2022 14:44:50 +0100 Subject: Began writing a debugging tui --- .gitmodules | 3 ++ build.zig | 6 +++ lib/spoon | 1 + src/field.zig | 9 ++++ src/interpreter.zig | 16 ++++++ src/pointer.zig | 5 ++ src/tui.zig | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 178 insertions(+) create mode 160000 lib/spoon create mode 100644 src/tui.zig diff --git a/.gitmodules b/.gitmodules index bdabf44..a6eb613 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "spec"] path = spec url = https://github.com/catseye/Funge-98 +[submodule "lib/spoon"] + path = lib/spoon + url = https://git.sr.ht/~leon_plickat/zig-spoon diff --git a/build.zig b/build.zig index 799aebb..3ac3e80 100644 --- a/build.zig +++ b/build.zig @@ -22,6 +22,12 @@ pub fn build(b: *std.build.Builder) void { run_cmd.addArgs(args); } + const tui = b.addExecutable("zigfunge98-tui", "src/tui.zig"); + tui.addPackagePath("spoon", "lib/spoon/import.zig"); + tui.setTarget(target); + tui.setBuildMode(mode); + tui.install(); + const coverage = b.option(bool, "test-coverage", "Generate test coverage") orelse false; const run_step = b.step("run", "Run the app"); diff --git a/lib/spoon b/lib/spoon new file mode 160000 index 0000000..75dbab7 --- /dev/null +++ b/lib/spoon @@ -0,0 +1 @@ +Subproject commit 75dbab71771ca2ee5aadcd5ab7a4c503a9536031 diff --git a/src/field.zig b/src/field.zig index 0130848..03f9da3 100644 --- a/src/field.zig +++ b/src/field.zig @@ -69,6 +69,12 @@ const Line = struct { if (x >= l.x and x < l.x + @intCast(i64, l.len())) return l.data.items[@intCast(usize, x - @intCast(i64, l.x))]; return ' '; } + pub inline fn getData(l: *Line) std.ArrayList(i64) { + return l.data; + } + pub inline fn getX(l: *Line) i64 { + return l.x; + } fn init(allocator: std.mem.Allocator) !*Line { var l = try allocator.create(Line); l.allocator = allocator; @@ -244,6 +250,9 @@ pub const Field = struct { if (y >= f.y and y < f.y + @intCast(i64, f.lines.items.len)) return f.lines.items[@intCast(usize, y - @intCast(i64, f.y))].get(x); return ' '; } + pub fn getLine(f: *Field, y: usize) *Line { + return f.lines.items[y]; + } pub fn getSize(f: Field) [4]i64 { return [4]i64{ f.x, f.y, @intCast(i64, f.lx), @intCast(i64, f.lines.items.len) }; } diff --git a/src/interpreter.zig b/src/interpreter.zig index 760e008..51a542e 100644 --- a/src/interpreter.zig +++ b/src/interpreter.zig @@ -13,6 +13,12 @@ pub const Interpreter = struct { self.field.deinit(); self.allocator.destroy(self); } + pub inline fn getField(self: *Interpreter) *field.Field { + return self.field; + } + pub inline fn getPointer(self: *Interpreter) *pointer.Pointer { + return self.pointer; + } pub fn init(allocator: std.mem.Allocator, fileReader: anytype, timestamp: ?i64, args: []const []const u8, env: []const [*:0]const u8) !*Interpreter { var i = try allocator.create(Interpreter); errdefer allocator.destroy(i); @@ -34,6 +40,16 @@ pub const Interpreter = struct { } } } + pub fn step(self: *Interpreter, ioContext: anytype) !?i64 { + if (try self.pointer.exec(ioContext)) |ret| { + if (ret.code) |code| { + return code; + } else { + return 0; + } + } + return null; + } }; test "all" { diff --git a/src/pointer.zig b/src/pointer.zig index 0c516be..ce188f8 100644 --- a/src/pointer.zig +++ b/src/pointer.zig @@ -11,6 +11,8 @@ const pointerReturn = struct { code: ?i64 = null, }; +const PointerInfo = struct { x: i64, y: i64, dx: i64, dy: i64 }; + pub const Pointer = struct { allocator: std.mem.Allocator, field: *field.Field, @@ -350,6 +352,9 @@ pub const Pointer = struct { self.step(); return result; } + pub inline fn getInfo(self: *Pointer) PointerInfo { + return .{ .x = self.x, .y = self.y, .dx = self.dx, .dy = self.dy }; + } pub fn init(allocator: std.mem.Allocator, f: *field.Field, timestamp: ?i64, argv: []const []const u8, env: []const [*:0]const u8) !*Pointer { var p = try allocator.create(Pointer); errdefer allocator.destroy(p); diff --git a/src/tui.zig b/src/tui.zig new file mode 100644 index 0000000..2867a71 --- /dev/null +++ b/src/tui.zig @@ -0,0 +1,138 @@ +const std = @import("std"); +const interpreter = @import("interpreter.zig"); +const io = @import("io.zig"); + +const spoon = @import("spoon"); +var term: spoon.Term = undefined; +var args: [][:0]u8 = undefined; +var intp: *interpreter.Interpreter = undefined; + +pub fn main() anyerror!void { + //--- befunge initialization ---------------------------------------------- + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + defer std.debug.assert(!gpa.deinit()); + args = try std.process.argsAlloc(gpa.allocator()); + defer std.process.argsFree(gpa.allocator(), args); + if (args.len < 2) { + std.debug.print("Usage: {s} \n", .{args[0]}); + std.os.exit(1); + } + + var file = try std.fs.cwd().openFile(args[1], .{}); + defer file.close(); + + const env: []const [*:0]const u8 = std.os.environ; + intp = try interpreter.Interpreter.init(gpa.allocator(), file.reader(), null, args, env[0..]); + defer intp.deinit(); + + var ioContext = io.context(std.io.getStdIn().reader(), std.io.getStdOut().writer()); // TODO io functions for tui + + //--- Term initialization ------------------------------------------------- + try term.init(.{}); + defer term.deinit(); + + try std.os.sigaction(std.os.SIG.WINCH, &std.os.Sigaction{ + .handler = .{ .handler = handleSigWinch }, + .mask = std.os.empty_sigset, + .flags = 0, + }, null); + + var fds: [1]std.os.pollfd = undefined; + fds[0] = .{ + .fd = term.tty.handle, + .events = std.os.POLL.IN, + .revents = undefined, + }; + try term.uncook(.{}); + defer term.cook() catch {}; + + //--- Main loop ----------------------------------------------------------- + try term.fetchSize(); + try term.setWindowTitle("zigfunge98-tui", .{}); + try render(); + + var buf: [16]u8 = undefined; + var done = false; + while (!done) { + _ = try std.os.poll(&fds, -1); + + const read = try term.readInput(&buf); + var it = spoon.inputParser(buf[0..read]); + while (it.next()) |in| { + if (in.eqlDescription("escape") or in.eqlDescription("q")) { + done = true; + break; + } else if (in.eqlDescription("s")) { + if (try intp.step(&ioContext)) |code| { + std.os.exit(@intCast(u8, code)); + } + try render(); + } + } + } +} + +fn handleSigWinch(_: c_int) callconv(.C) void { + term.fetchSize() catch {}; + render() catch {}; +} + +fn render() !void { + var rc = try term.getRenderContext(); + defer rc.done() catch {}; + + try rc.clear(); + + if (term.width < 80 or term.height < 24) { + try rc.setAttribute(.{ .fg = .red, .bold = true }); + try rc.writeAllWrapping("Terminal too small!"); + return; + } + + const pointer = intp.getPointer().getInfo(); + + try rc.moveCursorTo(0, 0); + var filename = rc.restrictedPaddingWriter(term.width); + try filename.writer().print("{s} | steps:{d}", .{ args[1], 0 }); + + try rc.moveCursorTo(2, 0); + try rc.setAttribute(.{ .fg = .green, .reverse = true }); + var stack = rc.restrictedPaddingWriter(16); + try stack.writer().writeAll("---- Stack ----"); + + try rc.moveCursorTo(2, 18); + try rc.setAttribute(.{ .fg = .blue, .reverse = true }); + var fieldTitle = rc.restrictedPaddingWriter(term.width - 17); + const size = intp.getField().getSize(); + try fieldTitle.writer().print("Funge field | top left corner:({d},{d}) size:{d}x{d}", .{ size[0], size[1], size[2], size[3] }); + try fieldTitle.pad(); + try rc.setAttribute(.{ .fg = .blue, .reverse = false }); + var y: usize = 0; // TODO negative lines + while (y < @min(@intCast(usize, size[3]), term.height - 3)) : (y += 1) { + var field = rc.restrictedPaddingWriter(term.width - 17); + const line = intp.getField().getLine(y); + const data = line.getData(); + var x: usize = 0; + if (line.getX() >= 0) { + try rc.moveCursorTo(y + 3, 18 + @intCast(usize, line.getX())); + } else { + try rc.moveCursorTo(y + 3, 18); // TODO negative columns + } + while (x < @min(data.items.len, term.width - 18)) : (x += 1) { + if (x == pointer.x and y == pointer.y) { + try rc.setAttribute(.{ .fg = .magenta, .reverse = true }); + } + if (x == pointer.x + pointer.dx and y == pointer.y + pointer.dy) { // TODO optmize that? + try rc.setAttribute(.{ .fg = .magenta, .reverse = false }); + } + if (data.items[x] >= 32 and data.items[x] < 127) { + try field.writer().print("{c}", .{@intCast(u8, data.items[x])}); + } else { + try field.writer().writeAll("®"); + } + if (x == pointer.x and y == pointer.y or x == pointer.x + pointer.dx and y == pointer.y + pointer.dy) { + try rc.setAttribute(.{ .fg = .blue, .reverse = false }); + } + } + } +} -- cgit v1.2.3