Began writing a debugging tui
This commit is contained in:
parent
860ebc4fd4
commit
0bf0375200
7 changed files with 178 additions and 0 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
|
1
lib/spoon
Submodule
1
lib/spoon
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 75dbab71771ca2ee5aadcd5ab7a4c503a9536031
|
|
@ -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) };
|
||||
}
|
||||
|
|
|
@ -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" {
|
||||
|
|
|
@ -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);
|
||||
|
|
138
src/tui.zig
Normal file
138
src/tui.zig
Normal file
|
@ -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} <b98_file_to_run>\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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue