Last active
October 30, 2019 19:31
-
-
Save micaiahparker/e7f1f5e1190b2a2d41251bbc83a9de84 to your computer and use it in GitHub Desktop.
BF interpreter in Zig
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const std = @import("std"); | |
const io = std.io; | |
const File = std.fs.File; | |
const debug = std.debug; | |
const process = std.process; | |
const mem = std.mem; | |
const assert = debug.assert; | |
const warn = debug.warn; | |
const allocator = debug.global_allocator; | |
const logging = true; | |
const Token = enum { | |
Add, | |
Sub, | |
Get, | |
Put, | |
Next, | |
Prev, | |
LeftBracket, | |
RightBracket, | |
Invalid, | |
}; | |
pub fn main() anyerror!void { | |
var logger: File = File.openWrite("bfz.log") catch |err| { | |
warn("Failed to open 'log.bfz' {}\n", @errorName(err)); | |
return err; | |
}; | |
try log(logger, "Started program.\n"); | |
var args = process.args(); | |
const exe = args.next(allocator).? catch |err| { | |
warn("{}\n", @errorName(err)); | |
return err; | |
}; | |
const filename = args.next(allocator).? catch |err| { | |
warn("{}\n", err); | |
return err; | |
}; | |
var in = File.openRead(filename) catch |err| { | |
warn("Failed to open input: {}\n", filename); | |
return err; | |
}; | |
var ptr: u8 = 0; | |
var tape: []u8 = try allocator.alloc(u8, 1024 * 4); | |
for (tape) |*i| { | |
i.* = 0; | |
} | |
var program: []Token = try allocator.alloc(Token, 1024 * 4); | |
var p: u32 = 0; | |
var p_max: u32 = 0; | |
var stdin = try io.getStdIn(); | |
var buf: [1024 * 4]u8 = undefined; | |
while (true) { | |
if (p_max == program.len) { | |
warn("Buffer exceeded.\n"); | |
} | |
const bytes_read = in.read(buf[0..]) catch |err| { | |
warn("Unable to read from stdin {}\n", @errorName(err)); | |
return err; | |
}; | |
if (bytes_read == 0) { | |
break; | |
} | |
try log(logger, "Program:\n\n"); | |
for (buf[0..bytes_read]) |c, i| { | |
const token: Token = get_token(c); | |
if (token != Token.Invalid) { | |
program[p_max] = token; | |
try log(logger, buf[i..i+1]); | |
p_max += 1; | |
} | |
} | |
try log(logger, "\n\n"); | |
} | |
in.close(); | |
while (p < p_max) { | |
switch (program[p]) { | |
Token.LeftBracket => { | |
if (tape[ptr] == 0) { | |
p = nextBracket(program[0..], p); | |
} | |
}, | |
Token.RightBracket => { | |
if (tape[ptr] != 0) { | |
p = prevBracket(program[0..], p); | |
} | |
}, | |
Token.Add => { | |
tape[ptr] +%= 1; | |
}, | |
Token.Sub => { | |
tape[ptr] -%= 1; | |
}, | |
Token.Get => { | |
tape[ptr] = try get(); | |
}, | |
Token.Put => { | |
try put(tape[ptr .. ptr + 1]); | |
}, | |
Token.Next => { | |
ptr += 1; | |
}, | |
Token.Prev => { | |
ptr -= 1; | |
}, | |
else => unreachable, | |
} | |
var line_buf: [300]u8 = undefined; | |
const line = try std.fmt.bufPrint(line_buf[0..], "{} {} {} {}\n", p, @tagName(program[p]), ptr, tape[ptr]); | |
try log(logger, line); | |
p += 1; | |
} | |
allocator.free(tape); | |
try log(logger, "Free: tape\n"); | |
allocator.free(program); | |
try log(logger, "Free: program\n"); | |
logger.close(); | |
} | |
fn nextBracket(program: []Token, i: u32) u32 { | |
var tmp: u32 = i; | |
var stack: u32 = 0; | |
while (true) { | |
if (program[tmp] == Token.LeftBracket) { | |
stack += 1; | |
} | |
if (program[tmp] == Token.RightBracket) { | |
stack -= 1; | |
} | |
if (stack == 0 and program[tmp] == Token.RightBracket) { | |
return tmp; | |
} | |
tmp += 1; | |
} | |
} | |
fn prevBracket(program: []Token, i: u32) u32 { | |
var tmp: u32 = i; | |
var stack: u32 = 0; | |
while (true) { | |
if (program[tmp] == Token.RightBracket) { | |
stack += 1; | |
} | |
if (program[tmp] == Token.LeftBracket) { | |
stack -= 1; | |
} | |
if (stack == 0 and program[tmp] == Token.LeftBracket) { | |
return tmp; | |
} | |
tmp -= 1; | |
} | |
} | |
fn get_token(c: u8) Token { | |
return switch (c) { | |
'+' => Token.Add, | |
'-' => Token.Sub, | |
',' => Token.Get, | |
'.' => Token.Put, | |
'>' => Token.Next, | |
'<' => Token.Prev, | |
'[' => Token.LeftBracket, | |
']' => Token.RightBracket, | |
else => Token.Invalid, | |
}; | |
} | |
fn put(c: []u8) anyerror!void { | |
var stdout = io.getStdOut() catch |err| { | |
warn("Failed to get stdout. {}", @errorName(err)); | |
return err; | |
}; | |
stdout.write(c) catch |err| { | |
warn("Failed to write to stdout."); | |
return err; | |
}; | |
} | |
fn get() anyerror!u8 { | |
var buf: [1]u8 = undefined; | |
var stdin = try io.getStdIn(); | |
_ = try stdin.read(buf[0..1]); | |
return buf[0]; | |
} | |
fn log(file: File, msg: []const u8) anyerror!void { | |
if (logging) { | |
try file.write(msg); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment