const std = @import("std"); const langs = @import("lang.zig"); const runners = @import("runner.zig"); const printer = @import("printer.zig"); test "scritcher" { _ = @import("lang.zig"); _ = @import("runner.zig"); } const readline = @cImport({ @cInclude("stdio.h"); @cInclude("stdlib.h"); @cInclude("readline/readline.h"); @cInclude("readline/history.h"); }); fn wrapInCmdList(allocator: *std.mem.Allocator, cmd: langs.Command) !langs.CommandList { var cmds = langs.CommandList.init(allocator); try cmds.append(cmd); return cmds; } pub fn doRepl(allocator: *std.mem.Allocator, args_it: var) !void { var stdout_file = try std.io.getStdOut(); const stdout = &stdout_file.outStream().stream; const scri_path = try (args_it.next(allocator) orelse @panic("expected scri path")); var file = try std.fs.File.openWrite(scri_path); defer file.close(); var out = file.outStream(); var stream = &out.stream; // the thing here is that 'load :0' would load the scri_path, since we // now have a 'repl' argument right before it. to counteract that, the // initial repl buffer contains 'load :1' instead. var loadargs = langs.ArgList.init(allocator); defer loadargs.deinit(); try loadargs.append(":1"); var cmds = try wrapInCmdList(allocator, langs.Command{ .command = .Load, .args = loadargs, }); defer cmds.deinit(); // we keep // - a CommandList with the full commands we have right now // - a Command with the current last typed successful command // - one runner that contains the current full state of the image // as if the current cmds was ran over it (TODO better wording) // - one runner that gets copied from the original on every new // command the user issues var lang = langs.Lang.init(allocator); defer lang.deinit(); var current: langs.Command = undefined; var runner = runners.Runner.init(allocator); defer runner.deinit(); // run the load command try runner.runCommands(cmds, true); var runqs_args = langs.ArgList.init(allocator); defer runqs_args.deinit(); // TODO change the runqs command to something given in an env var try runqs_args.append("ristretto"); while (true) { lang.reset(); var rd_line = readline.readline(c"> "); if (rd_line == null) break; readline.add_history(rd_line); defer std.heap.c_allocator.destroy(rd_line); var line = rd_line[0..std.mem.len(u8, rd_line)]; if (std.mem.eql(u8, line, "push")) { try cmds.append(current); // run the current added command to main cmds list // with the main parent runner var cmds_wrapped = try wrapInCmdList(allocator, current); defer cmds_wrapped.deinit(); try runner.runCommands(cmds_wrapped, true); continue; } else if (std.mem.eql(u8, line, "list")) { try printer.printList(cmds, stdout); continue; } else if (std.mem.eql(u8, line, "save")) { try file.seekTo(0); try printer.printList(cmds, stream); continue; } else if (std.mem.eql(u8, line, "quit") or std.mem.eql(u8, line, "q")) { std.debug.warn("leaving\n"); break; } var cmds_parsed = lang.parse(line) catch |err| { std.debug.warn("repl: error while parsing: {}\n", err); continue; }; current = cmds_parsed.at(0); // by cloning the parent runner, we can iteratively write // whatever command we want and only commit the good results // back to the parent runner var runner_clone = try runner.clone(); defer runner_clone.deinit(); try cmds_parsed.append(langs.Command{ .command = .RunQS, .args = runqs_args, }); try runner_clone.runCommands(cmds_parsed, true); try stdout.write("\n"); } } pub fn main() !void { const allocator = std.heap.direct_allocator; var lang = langs.Lang.init(allocator); defer lang.deinit(); var runner = runners.Runner.init(allocator); defer runner.deinit(); var args_it = std.process.args(); // TODO print help _ = try (args_it.next(allocator) orelse @panic("expected exe name")); const scri_path = try (args_it.next(allocator) orelse @panic("expected scri path")); if (std.mem.eql(u8, scri_path, "repl")) { return try doRepl(allocator, &args_it); } var file = try std.fs.File.openRead(scri_path); defer file.close(); // sadly, we read it all into memory. such is life const total_bytes = try file.getEndPos(); var data = try allocator.alloc(u8, total_bytes); defer allocator.free(data); _ = try file.read(data); var cmds = try lang.parse(data); defer cmds.deinit(); try runner.runCommands(cmds, true); }