├── .gitattributes ├── .github └── workflows │ └── main.yml ├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── build.zig └── src └── main.zig /.gitattributes: -------------------------------------------------------------------------------- 1 | *.zig text eol=lf 2 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: push 3 | jobs: 4 | test: 5 | strategy: 6 | matrix: 7 | os: [ubuntu-latest, macos-latest] 8 | runs-on: ${{matrix.os}} 9 | steps: 10 | - uses: actions/checkout@v1 11 | with: 12 | submodules: recursive 13 | - uses: goto-bus-stop/setup-zig@v1.0.0 14 | with: 15 | version: 0.6.0 16 | - run: zig build 17 | lint: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v1 21 | - uses: goto-bus-stop/setup-zig@v1.0.0 22 | with: 23 | version: 0.6.0 24 | - run: zig fmt --check . 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | zig-cache 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/zig-clap"] 2 | path = lib/zig-clap 3 | url = https://github.com/Hejsil/zig-clap.git 4 | branch = master 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 TM35-Metronome 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hacky-zig-repl 2 | 3 | Just a small wrapper program that provides a repl for the [Zig](https://ziglang.org/) programming language. 4 | -------------------------------------------------------------------------------- /build.zig: -------------------------------------------------------------------------------- 1 | const Builder = @import("std").build.Builder; 2 | 3 | pub fn build(b: *Builder) void { 4 | const mode = b.standardReleaseOptions(); 5 | const exe = b.addExecutable("hacky-zig-repl", "src/main.zig"); 6 | exe.addPackagePath("zig-clap", "lib/zig-clap/clap.zig"); 7 | exe.setBuildMode(mode); 8 | 9 | const run_cmd = exe.run(); 10 | const run_step = b.step("run", "Run the app"); 11 | run_step.dependOn(&run_cmd.step); 12 | 13 | b.default_step.dependOn(&exe.step); 14 | b.installArtifact(exe); 15 | } 16 | -------------------------------------------------------------------------------- /src/main.zig: -------------------------------------------------------------------------------- 1 | const clap = @import("zig-clap"); 2 | const std = @import("std"); 3 | 4 | const base64 = std.base64; 5 | const crypto = std.crypto; 6 | const debug = std.debug; 7 | const fmt = std.fmt; 8 | const fs = std.fs; 9 | const heap = std.heap; 10 | const io = std.io; 11 | const math = std.math; 12 | const mem = std.mem; 13 | const os = std.os; 14 | 15 | const Clap = clap.ComptimeClap(clap.Help, ¶ms); 16 | const Names = clap.Names; 17 | const Param = clap.Param(clap.Help); 18 | 19 | const params = [_]Param{ 20 | clap.parseParam("-h, --help display this help text and exit ") catch unreachable, 21 | clap.parseParam("-t, --tmp override the folder used to stored temporary files") catch unreachable, 22 | clap.parseParam("-v, --verbose print commands before executing them ") catch unreachable, 23 | clap.parseParam(" --zig override the path to the Zig executable ") catch unreachable, 24 | Param{ 25 | .takes_value = true, 26 | }, 27 | }; 28 | 29 | fn usage(stream: var) !void { 30 | try stream.writeAll( 31 | \\Usage: hacky-zig-repl [OPTION]... 32 | \\Allows repl like functionality for Zig. 33 | \\ 34 | \\Options: 35 | \\ 36 | ); 37 | try clap.help(stream, ¶ms); 38 | } 39 | 40 | const repl_template = 41 | \\usingnamespace @import("std"); 42 | \\pub fn main() !void {{ 43 | \\{} 44 | \\{} 45 | \\ try __repl_print_stdout(_{}); 46 | \\}} 47 | \\ 48 | \\fn __repl_print_stdout(v: var) !void {{ 49 | \\ const stdout = io.getStdOut().outStream(); 50 | \\ try stdout.writeAll("_{} = "); 51 | \\ try fmt.formatType( 52 | \\ v, 53 | \\ "", 54 | \\ fmt.FormatOptions{{}}, 55 | \\ stdout, 56 | \\ 3, 57 | \\ ); 58 | \\ try stdout.writeAll("\n"); 59 | \\}} 60 | \\ 61 | ; 62 | 63 | pub fn main() anyerror!void { 64 | @setEvalBranchQuota(10000); 65 | 66 | const stdin = io.getStdIn().inStream(); 67 | const stdout = io.getStdOut().outStream(); 68 | const stderr = io.getStdErr().outStream(); 69 | 70 | const pa = std.heap.page_allocator; 71 | var arg_iter = try clap.args.OsIterator.init(pa); 72 | var args = Clap.parse(pa, clap.args.OsIterator, &arg_iter) catch |err| { 73 | usage(stderr) catch {}; 74 | return err; 75 | }; 76 | 77 | if (args.flag("--help")) 78 | return try usage(stdout); 79 | 80 | const zig_path = args.option("--zig") orelse "zig"; 81 | const tmp_dir = args.option("--tmp") orelse "/tmp"; 82 | const verbose = args.flag("--verbose"); 83 | 84 | var last_run_buf = std.ArrayList(u8).init(pa); 85 | var line_buf = std.ArrayList(u8).init(pa); 86 | var i: usize = 0; 87 | while (true) : (line_buf.shrink(0)) { 88 | const last_run = last_run_buf.items; 89 | var arena = heap.ArenaAllocator.init(pa); 90 | defer arena.deinit(); 91 | 92 | const allocator = &arena.allocator; 93 | 94 | // TODO: proper readline prompt 95 | try stdout.writeAll(">> "); 96 | stdin.readUntilDelimiterArrayList(&line_buf, '\n', math.maxInt(usize)) catch |err| switch (err) { 97 | error.EndOfStream => break, 98 | else => |e| return e, 99 | }; 100 | 101 | const line = mem.trim(u8, line_buf.items, " \t"); 102 | if (line.len == 0) 103 | continue; 104 | 105 | const assignment = try fmt.allocPrint(allocator, "const _{} = {};\n", .{ i, line }); 106 | 107 | var crypt_src: [224 / 8]u8 = undefined; 108 | crypto.Blake2s224.hash(last_run, &crypt_src); 109 | 110 | var encoded_src: [base64.Base64Encoder.calcSize(crypt_src.len)]u8 = undefined; 111 | fs.base64_encoder.encode(&encoded_src, &crypt_src); 112 | 113 | const file_name = try fmt.allocPrint(allocator, "{}/{}.zig", .{ tmp_dir, &encoded_src }); 114 | if (verbose) 115 | debug.warn("writing source to '{}'\n", .{file_name}); 116 | 117 | const file = try std.fs.cwd().createFile(file_name, .{}); 118 | defer file.close(); 119 | try file.outStream().print(repl_template, .{ last_run, assignment, i, i }); 120 | 121 | if (verbose) 122 | debug.warn("running command '{} run {}'\n", .{ zig_path, file_name }); 123 | run(allocator, &[_][]const u8{ zig_path, "run", file_name }) catch |err| { 124 | debug.warn("error: {}\n", .{err}); 125 | continue; 126 | }; 127 | 128 | try last_run_buf.appendSlice(assignment); 129 | i += 1; 130 | } 131 | } 132 | 133 | fn run(allocator: *mem.Allocator, argv: []const []const u8) !void { 134 | const process = try std.ChildProcess.init(argv, allocator); 135 | defer process.deinit(); 136 | 137 | try process.spawn(); 138 | switch (try process.wait()) { 139 | std.ChildProcess.Term.Exited => |code| { 140 | if (code != 0) 141 | return error.ProcessFailed; 142 | }, 143 | else => return error.ProcessFailed, 144 | } 145 | } 146 | --------------------------------------------------------------------------------