it is poggers now

This commit is contained in:
mehbark 2024-06-24 22:54:53 -04:00
parent 7301699729
commit d2e1b746e2

186
bf.zig
View file

@ -15,10 +15,16 @@ const Level1 = union(enum) {
// well, might as well use the extra space (union is bigger than its biggest member)
in: u16,
out: u16,
loop_start,
loop_end,
// u24s because i arbitrarily chose 2^24 as the limit
// maybe u32 would be more efficient idk this union should fit in a u32
// hm we can probably go way lower bc level1 is so much more compact than prog_unfiltered
// shouldn't be necessary
loop_start: u24,
loop_end: u24,
const Self = @This();
fn pointless(self: Self) bool {
return switch (self) {
.inc, .shift => |n| n == 0,
@ -26,127 +32,181 @@ const Level1 = union(enum) {
else => false,
};
}
};
const Level2 = union(enum) {
inc: i16,
shift: i16,
in: u16,
out: u16,
// only difference
// u24s because i arbitrarily chose 2^24 as the limit
// maybe u32 would be more efficient idk this union should fit in a u32
// hm we can probably go way lower bc level1 is so much more compact than prog_unfiltered
// shouldn't be necessary
loop_start: u24,
loop_end: u24,
fn nothing() Self {
return .{ .inc = 0 };
}
// the default fmt is human and machine readable..... but idc xd lol pog
pub fn format(
self: Self,
comptime fmt: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = fmt;
_ = options;
switch (self) {
.inc => |n| try writer.print("INC {d: >4}", .{n}),
.shift => |n| try writer.print("SHF {d: >4}", .{n}),
.in => |n| try writer.print("INP {d: >4}", .{n}),
.out => |n| try writer.print("OUT {d: >4}", .{n}),
.loop_start => |n| try writer.print("JEZ {d:0>4}", .{n}),
.loop_end => |n| try writer.print("JNZ {d:0>4}", .{n}),
}
}
};
// we'll do the discarding of comment chars here
fn compileLevel1(allocator: mem.Allocator, src: []const u8) !ArrayList(Level1) {
var cur: ?Level1 = null;
var cur = Level1.nothing();
var out = ArrayList(Level1).init(allocator);
var openers = ArrayList(u24).init(allocator);
errdefer {
out.deinit();
openers.deinit();
}
for (src) |b| {
switch (b) {
'+', '-' => {
const n: i16 = if (b == '+') 1 else -1;
if (cur) |*ins| {
switch (ins.*) {
switch (cur) {
.inc => |*ns| ns.* += n,
else => {
if (!ins.pointless()) try out.append(ins.*);
if (!cur.pointless()) try out.append(cur);
cur = .{ .inc = n };
},
}
} else {
cur = .{ .inc = n };
}
},
'>', '<' => {
const n: i16 = if (b == '>') 1 else -1;
if (cur) |*ins| {
switch (ins.*) {
switch (cur) {
.shift => |*ns| ns.* += n,
else => {
if (!ins.pointless()) try out.append(ins.*);
if (!cur.pointless()) try out.append(cur);
cur = .{ .shift = n };
},
}
} else {
cur = .{ .shift = n };
}
},
'[', ']' => {
if (cur) |ins| {
if (!ins.pointless()) try out.append(ins);
cur = null;
}
try out.append(if (b == '[') .loop_start else .loop_end);
'[' => {
if (!cur.pointless()) try out.append(cur);
cur = .{ .loop_start = 0 };
try openers.append(@as(u24, @truncate(out.items.len)));
},
']' => {
if (!cur.pointless()) try out.append(cur);
const opener_idx = openers.popOrNull() orelse return error.UnopenedCloser;
out.items[opener_idx].loop_start = @as(u24, @truncate(out.items.len));
cur = .{ .loop_end = opener_idx };
},
',' => {
if (cur) |*ins| {
switch (ins.*) {
switch (cur) {
.in => |*ns| ns.* += 1,
else => {
if (!ins.pointless()) try out.append(ins.*);
if (!cur.pointless()) try out.append(cur);
cur = .{ .in = 1 };
},
}
} else {
cur = .{ .in = 1 };
}
},
'.' => {
if (cur) |*ins| {
switch (ins.*) {
switch (cur) {
.out => |*ns| ns.* += 1,
else => {
if (!ins.pointless()) try out.append(ins.*);
if (!cur.pointless()) try out.append(cur);
cur = .{ .out = 1 };
},
}
} else {
cur = .{ .out = 1 };
}
},
else => {},
}
}
if (cur) |ins| {
if (!ins.pointless()) try out.append(ins);
}
if (!cur.pointless()) try out.append(cur);
const unclosed_openers = openers.items.len;
if (unclosed_openers > 0) return error.UnclosedOpener;
openers.deinit();
return out;
}
test "level 1" {
const src = "+++-- [ >><<< +- ] ...,,,";
fn testLevel1(src: []const u8, expected: []const Level1) !void {
const level1 = try compileLevel1(testing.allocator, src);
defer level1.deinit();
std.debug.print("{any}\n", .{level1.items});
errdefer {
std.debug.print("{s}\n", .{src});
std.debug.print(" OBTAINED EXPECTED\n", .{});
if (level1.items.len < expected.len) {
for (level1.items, expected[0..level1.items.len], 0..) |a, b, i| {
std.debug.print("{:0>4}: {} {}\n", .{ i, a, b });
}
for (expected[level1.items.len..], level1.items.len..) |b, i| {
std.debug.print("{:0>4}: -------- {}\n", .{ i, b });
}
} else {
for (level1.items[0..expected.len], expected, 0..) |a, b, i| {
std.debug.print("{:0>4}: {} {}\n", .{ i, a, b });
}
for (level1.items[expected.len..], expected.len..) |a, i| {
std.debug.print("{:0>4}: {} --------\n", .{ i, a });
}
}
}
const should_be = [_]Level1{
.{ .inc = 1 },
.loop_start,
.{ .shift = -1 },
.loop_end,
.{ .out = 3 },
.{ .in = 3 },
};
try testing.expectEqual(should_be.len, level1.items.len);
try testing.expectEqual(expected.len, level1.items.len);
for (
level1.items,
&should_be,
expected,
) |a, b| {
try testing.expectEqual(a, b);
}
}
test "level 1" {
try testLevel1(
"+++-- [ >><<< +- ] ...,,,",
&[_]Level1{
.{ .inc = 1 },
.{ .loop_start = 3 },
.{ .shift = -1 },
.{ .loop_end = 1 },
.{ .out = 3 },
.{ .in = 3 },
},
);
try testLevel1(
\\ 0 [
\\ 1 [
\\ 2 ]
\\ 3 [
\\ 4 [
\\ 5 ]
\\ 6 [
\\ 7 ]
\\ 8 ]
\\ 9 ]
,
&[_]Level1{
.{ .loop_start = 9 },
.{ .loop_start = 2 },
.{ .loop_end = 1 },
.{ .loop_start = 8 },
.{ .loop_start = 5 },
.{ .loop_end = 4 },
.{ .loop_start = 7 },
.{ .loop_end = 6 },
.{ .loop_end = 3 },
.{ .loop_end = 0 },
},
);
testLevel1("bla bla - [ ++ ", &[_]Level1{}) catch |err| try testing.expectEqual(err, error.UnclosedOpener);
testLevel1("wow wow += ]", &[_]Level1{}) catch |err| try testing.expectEqual(err, error.UnopenedCloser);
}
pub fn main() !void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
var allocator = arena.allocator();