it is poggers now
This commit is contained in:
parent
7301699729
commit
d2e1b746e2
1 changed files with 139 additions and 79 deletions
218
bf.zig
218
bf.zig
|
@ -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.*) {
|
||||
.inc => |*ns| ns.* += n,
|
||||
else => {
|
||||
if (!ins.pointless()) try out.append(ins.*);
|
||||
cur = .{ .inc = n };
|
||||
},
|
||||
}
|
||||
} else {
|
||||
cur = .{ .inc = n };
|
||||
switch (cur) {
|
||||
.inc => |*ns| ns.* += n,
|
||||
else => {
|
||||
if (!cur.pointless()) try out.append(cur);
|
||||
cur = .{ .inc = n };
|
||||
},
|
||||
}
|
||||
},
|
||||
'>', '<' => {
|
||||
const n: i16 = if (b == '>') 1 else -1;
|
||||
if (cur) |*ins| {
|
||||
switch (ins.*) {
|
||||
.shift => |*ns| ns.* += n,
|
||||
else => {
|
||||
if (!ins.pointless()) try out.append(ins.*);
|
||||
cur = .{ .shift = n };
|
||||
},
|
||||
}
|
||||
} else {
|
||||
cur = .{ .shift = n };
|
||||
switch (cur) {
|
||||
.shift => |*ns| ns.* += n,
|
||||
else => {
|
||||
if (!cur.pointless()) try out.append(cur);
|
||||
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.*) {
|
||||
.in => |*ns| ns.* += 1,
|
||||
else => {
|
||||
if (!ins.pointless()) try out.append(ins.*);
|
||||
cur = .{ .in = 1 };
|
||||
},
|
||||
}
|
||||
} else {
|
||||
cur = .{ .in = 1 };
|
||||
switch (cur) {
|
||||
.in => |*ns| ns.* += 1,
|
||||
else => {
|
||||
if (!cur.pointless()) try out.append(cur);
|
||||
cur = .{ .in = 1 };
|
||||
},
|
||||
}
|
||||
},
|
||||
'.' => {
|
||||
if (cur) |*ins| {
|
||||
switch (ins.*) {
|
||||
.out => |*ns| ns.* += 1,
|
||||
else => {
|
||||
if (!ins.pointless()) try out.append(ins.*);
|
||||
cur = .{ .out = 1 };
|
||||
},
|
||||
}
|
||||
} else {
|
||||
cur = .{ .out = 1 };
|
||||
switch (cur) {
|
||||
.out => |*ns| ns.* += 1,
|
||||
else => {
|
||||
if (!cur.pointless()) try out.append(cur);
|
||||
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();
|
||||
|
|
Loading…
Reference in a new issue