pub fn main() !void { clear(); var gpa = std.heap.GeneralPurposeAllocator(.{}).init; defer _ = gpa.deinit(); const allocator = gpa.allocator(); var ln = Linenoise.init(allocator); defer ln.deinit(); var prng = Random.DefaultPrng.init(blk: { var seed: u64 = undefined; try std.posix.getrandom(std.mem.asBytes(&seed)); break :blk seed; }); const rand = prng.random(); const p = try getPrompt(allocator); defer allocator.free(p); while (try ln.linenoise(p)) |input| { defer allocator.free(input); if (input.len > 0) { try ln.history.add(input); const command = try tokenizeCommand(input, allocator); defer allocator.free(command); if (mathTest(rand, 100)) { _ = runCommand(command, allocator) catch |err| switch (err) { error.FileNotFound => print("mash: {s}: command not found\n", .{command[0]}), error.AccessDenied => print("mash: {s}: Permission denied\n", .{command[0]}), else => print("Unkown error: {}\n", .{err}), }; } else { print( \\\n \\How can you expect to execute commands without knowing any math???\n \\\n , .{}); } } } } const Operators = enum { add, subtract, multiply, divide, const Self = @This(); fn toString(s: Self) []const u8 { return switch (s) { .add => "+", .subtract => "-", .multiply => "*", .divide => "/", }; } fn apply(s: Self, a: i32, b: i32) i32 { return switch (s) { .add => a + b, .subtract => a - b, .multiply => a * b, .divide => @divTrunc(a, b), }; } }; /// return true if the user passed, false if the user did not. fn mathTest(rand: Random, limit: i32) bool { // const operators = comptime .{ // .{ "+", add }, // .{ "-", subtract }, // .{ "*", multiply }, // .{ "/", divide }, // }; // const ops = StaticStringMap(BinaryOperator).initComptime(operators); // _ = ops; const op = rand.enumValue(Operators); const num1 = rand.intRangeAtMost(i32, 1, limit); const num2 = rand.intRangeAtMost(i32, 1, limit); const answer = op.apply(num1, num2); print("{d} {s} {d} = {d}\n", .{ num1, op.toString(), num2, answer }); return true; } fn runCommand(command: [][]const u8, allocator: Allocator) !u8 { var child = process.Child.init(command, allocator); const result = try child.spawnAndWait(); return result.Exited; } fn getPrompt(allocator: Allocator) ![]const u8 { const script = @embedFile("./get_prompt.bash"); const result = try process.Child.run(.{ .allocator = allocator, .argv = &[_][]const u8{ "bash", "-i", "-c", script }, }); allocator.free(result.stderr); return result.stdout; } fn tokenizeCommand(command: []const u8, allocator: Allocator) ![][]const u8 { var argv_array_list = ArrayList([]const u8).init(allocator); defer argv_array_list.deinit(); var tokens = tokenizeAny(u8, command, " "); while (tokens.next()) |token| { try argv_array_list.append(token); } return argv_array_list.toOwnedSlice(); } fn clear() void { print("\u{001b}[H\u{001b}[J", .{}); } fn print(comptime fmt: []const u8, args: anytype) void { stdout.print(fmt, args) catch unreachable; } const std = @import("std"); const stdout = std.io.getStdOut().writer(); const tokenizeAny = std.mem.tokenizeAny; const process = std.process; const Allocator = std.mem.Allocator; const ArrayList = std.ArrayList; const StaticStringMap = std.static_string_map.StaticStringMap; const Random = std.Random; const Linenoise = @import("linenoise").Linenoise;