From 6b3557c9098d27daf4c056a6593acbca2b6c8fe2 Mon Sep 17 00:00:00 2001 From: booniepepper Date: Mon, 10 Jul 2023 11:57:08 -0700 Subject: [PATCH] Many internal refactors, split stdlib from dt-only things --- demos/shell.dt | 8 +++++ src/builtins.zig | 11 ++----- src/dt.dt | 52 +++++++++++++++++++++++++++++++++ src/interpret.zig | 10 ++++--- src/main.zig | 74 ++++++++++++++++++++--------------------------- src/stdlib.dt | 36 ----------------------- src/string.zig | 9 ++---- 7 files changed, 104 insertions(+), 96 deletions(-) create mode 100755 demos/shell.dt create mode 100644 src/dt.dt diff --git a/demos/shell.dt b/demos/shell.dt new file mode 100755 index 0000000..44564bf --- /dev/null +++ b/demos/shell.dt @@ -0,0 +1,8 @@ +#!/usr/bin/env dt + +# TODO: Try making a very simple repl-based shell + +ls .s + +repl + diff --git a/src/builtins.zig b/src/builtins.zig index df016f2..74ca0ce 100644 --- a/src/builtins.zig +++ b/src/builtins.zig @@ -376,14 +376,9 @@ pub fn norm(dt: *DtMachine) !void { fn _p(val: DtVal, allocator: Allocator, writer: std.fs.File.Writer) !void { switch (val) { - .string => |s| { - var unescaped = try std.mem.replaceOwned(u8, allocator, s, "\\n", "\n"); - unescaped = try std.mem.replaceOwned(u8, allocator, unescaped, "\\\"", "\""); - try writer.print("{s}", .{unescaped}); - }, - else => { - try val.print(allocator); - }, + // When printing strings, do not show " around a string. + .string => |s| try writer.print("{s}", .{s}), + else => try val.print(allocator), } } diff --git a/src/dt.dt b/src/dt.dt new file mode 100644 index 0000000..57295b3 --- /dev/null +++ b/src/dt.dt @@ -0,0 +1,52 @@ +### Flags and such ### + +[ [ action flag ]: + args [ flag eq? ] any? + action swap do? +] \handle-flag def + +[ "dt " p version pl ] \print-version def + +[ [ print-version 0 exit ] "--version" handle-flag ] \--version def + +[ print-version + [ "USAGE: dt FLAGS CODE..." + "dt is \"duct tape\" for your unix pipes." + "" + "dt can be used in pipes to transform data with general purpose programming" + "operations. It can also excel in short shebang scripts, or be explored as" + "an interactive REPL (read-eval-print loop)." + "" + "More info at https://dt.plumbing" + ] pls + 0 exit +] \print-help def + +[ [ print-help 0 exit ] "--help" handle-flag ] \--help def + +[ --help --version ] \handle-flags def + +[ handle-flags args unwords eval ] \run-args def! + + +### PIPE things ### + +[ read-lines run-args ] \pipe-thru-args def! + + +### Shebang things ### + +[ args deq swap drop ] \shebang-args def! + + +### REPL things ### + +[ red "dt " p version pl norm inspire pl ] \repl-greet def + +[ \.q \quit def + \.q \exit def +] \repl-prelude def + +[ handle-flags repl-greet repl-prelude repl ] \main-repl def! + +[ "» " p read-line eval repl ] \repl def! diff --git a/src/interpret.zig b/src/interpret.zig index 3fa9610..e45edf0 100644 --- a/src/interpret.zig +++ b/src/interpret.zig @@ -132,6 +132,11 @@ pub const DtMachine = struct { return DtError.CommandUndefined; } + pub fn loadFile(self: *DtMachine, code: []const u8) !void { + var toks = Token.parse(self.alloc, code); + while (try toks.next()) |token| try self.interpret(token); + } + pub fn red(self: *DtMachine) !void { try self.norm(); try self.stdoutConfig.setColor(stdout, Color.red); @@ -374,10 +379,7 @@ pub const DtVal = union(enum) { } try stdout.print("]", .{}); }, - .string => |s| { - const escaped = try string.escape(allocator, s); - try stdout.print("\"{s}\"", .{escaped}); - }, + .string => |s| try stdout.print("\"{s}\"", .{s}), .err => |e| try stdout.print("~{s}~", .{e}), } } diff --git a/src/main.zig b/src/main.zig index b0a833a..fe3ff6a 100644 --- a/src/main.zig +++ b/src/main.zig @@ -20,6 +20,7 @@ const builtins = @import("builtins.zig"); pub const version = "0.11.1"; // Update in build.zig.zon as well. TODO: Change to @import when it's supported for zon const stdlib = @embedFile("stdlib.dt"); +const dtlib = @embedFile("dt.dt"); pub fn main() !void { var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); @@ -28,73 +29,62 @@ pub fn main() !void { var machine = try DtMachine.init(arena.allocator()); try builtins.defineAll(&machine); + try machine.loadFile(stdlib); + try machine.loadFile(dtlib); - try loadStdlib(arena.allocator(), &machine); + const stdinPiped = !std.io.getStdIn().isTty(); + const stdoutPiped = !std.io.getStdOut().isTty(); if (try readShebangFile(arena.allocator())) |fileContents| { - var toks = Token.parse(arena.allocator(), fileContents); - return while (try toks.next()) |token| try machine.interpret(token); - } else if (!std.io.getStdIn().isTty()) { + return machine.loadFile(fileContents) catch |e| return doneOrDie(&machine, e); + } else if (stdinPiped) { return handlePipedStdin(&machine); - } else if (!std.io.getStdOut().isTty()) { + } else if (stdoutPiped) { return handlePipedStdoutOnly(&machine); } return readEvalPrintLoop(&machine); } -// TODO: Can this be done at comptime somehow? -fn loadStdlib(allocator: Allocator, machine: *DtMachine) !void { - var toks = Token.parse(allocator, stdlib); - while (try toks.next()) |token| try machine.interpret(token); +fn handlePipedStdin(dt: *DtMachine) !void { + dt.handleCmd("pipe-thru-args") catch |e| return doneOrDie(dt, e); } -fn handlePipedStdin(machine: *DtMachine) !void { - machine.interpret(.{ .term = "pipe-thru-args" }) catch |e| { - if (e == error.BrokenPipe) return; - - try machine.red(); - try stderr.print("RIP: {any}\n", .{e}); - try machine.norm(); - - std.os.exit(1); - }; -} - -fn handlePipedStdoutOnly(machine: *DtMachine) !void { - machine.interpret(.{ .term = "run-args" }) catch |e| { - if (e == error.BrokenPipe) return; - - try machine.red(); - try stderr.print("RIP: {any}\n", .{e}); - try machine.norm(); - - std.os.exit(1); - }; +fn handlePipedStdoutOnly(dt: *DtMachine) !void { + dt.handleCmd("run-args") catch |e| return doneOrDie(dt, e); } -fn readEvalPrintLoop(machine: *DtMachine) !void { - machine.interpret(.{ .term = "run-args" }) catch |e| { - try machine.red(); - try stderr.print("RIP: {any}\n", .{e}); - try machine.norm(); +fn readEvalPrintLoop(dt: *DtMachine) !void { + dt.handleCmd("run-args") catch |e| return doneOrDie(dt, e); - std.os.exit(1); - }; - - while (true) machine.interpret(.{ .term = "main-repl" }) catch |e| switch (e) { + while (true) dt.handleCmd("main-repl") catch |e| switch (e) { error.EndOfStream => { try stderr.print("\n", .{}); return; }, else => { - try machine.red(); + try dt.red(); try stderr.print("Recovering from: {any}\n", .{e}); - try machine.norm(); + try dt.norm(); }, }; } +fn doneOrDie(dt: *DtMachine, reason: anyerror) !void { + try stderr.print("\n", .{}); + switch (reason) { + error.EndOfStream => return, + error.BrokenPipe => return, + else => { + try dt.red(); + try stderr.print("RIP: {any}\n", .{reason}); + try dt.norm(); + + std.os.exit(1); + }, + } +} + fn readShebangFile(allocator: Allocator) !?[]const u8 { var args = std.process.args(); _ = args.skip(); diff --git a/src/stdlib.dt b/src/stdlib.dt index d02b5db..73975b5 100644 --- a/src/stdlib.dt +++ b/src/stdlib.dt @@ -6,42 +6,6 @@ [ def? not ] \undef? def -### Flags and such ### - -[ [ action flag ]: - args [ flag eq? ] any? - action swap do? -] \handle-flag def - -[ "dt " p version pl ] \print-version def -[ [ print-version 0 exit ] "--version" handle-flag ] \--version def - -[ "Good luck, this help hasn't been finished yet." pl 0 exit ] \print-help def -[ [ print-help 0 exit ] "--help" handle-flag ] \--help def - -[ --help --version ] \handle-flags def - -[ handle-flags args unwords eval ] \run-args def - - -### PIPE things ### - -[ read-lines run-args ] \pipe-thru-args def - - -### Shebang things ### - -[ args deq swap drop ] \shebang-args def - - -### REPL things ### - -[ red "dt " p version pl norm inspire pl ] \repl-greet def -[ handle-flags repl-greet repl ] \main-repl def -[ "» " p read-line eval repl ] \repl def -[ .q ] \quit def - - ### Display ### [ p nl ] \pl def diff --git a/src/string.zig b/src/string.zig index ffeefb4..aa10f0c 100644 --- a/src/string.zig +++ b/src/string.zig @@ -27,14 +27,11 @@ pub fn escape(allocator: Allocator, unescaped: String) !String { } pub fn unescape(allocator: Allocator, unescaped: String) !String { - var buf: []u8 = undefined; + var result = try allocator.dupe(u8, unescaped); for (escapes) |esc| { - buf = std.mem.replaceOwned(u8, allocator, unescaped, esc.to, esc.from) catch |e| { - allocator.free(buf); - return e; - }; + result = try std.mem.replaceOwned(u8, allocator, result, esc.to, esc.from); } - return buf; + return result; }