Skip to content

Commit

Permalink
Add inspire, make conditional do? instead of '?', other refactors
Browse files Browse the repository at this point in the history
  • Loading branch information
booniepepper committed Jul 7, 2023
1 parent 7e34fcd commit caa93fa
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 53 deletions.
6 changes: 3 additions & 3 deletions demos/fib.dt
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,16 @@ args first \script-name:
[usage [do eprint] each 1 exit] \usage-fail def

shebang-args pop to-int \n:
\usage-fail n not ?
\usage-fail n not do?

n 1 - \n:
\usage-fail 0 n lt? ?
\usage-fail 0 n lt? do?

0 pl

0 1

[ [a b]: b pl
a b + \c:
[1 exit] c not ?
[1 exit] c not do?
b c ] n times
100 changes: 61 additions & 39 deletions src/builtins.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const std = @import("std");
const Allocator = std.mem.Allocator;
const ArrayList = std.ArrayList;
const Stack = std.SinglyLinkedList;

const stdin = std.io.getStdIn().reader();
const stdout = std.io.getStdOut().writer();
const stderr = std.io.getStdErr().writer();
Expand All @@ -17,6 +18,8 @@ const Error = interpret.DtError;
const DtVal = interpret.DtVal;
const DtMachine = interpret.DtMachine;

const bangDescription = "If nested, any commands or variables defined will be available in the calling scope";

pub fn defineAll(machine: *DtMachine) !void {
try machine.define(".q", "quit, printing a warning if there are any values left on stack", .{ .builtin = quit });
try machine.define("exit", "exit with the specified exit code", .{ .builtin = exit });
Expand All @@ -31,16 +34,17 @@ pub fn defineAll(machine: *DtMachine) !void {

try machine.define("exec", "execute a child process. When successful, returns stdout as a string. When unsuccessful, prints the child's stderr to stderr, and returns boolean false", .{ .builtin = exec });

try machine.define("def!", "define a new command", .{ .builtin = defBang });
try machine.define("def!", "define a new command. " ++ bangDescription, .{ .builtin = defBang });
try machine.define("defs", "produce a quote of all definition names", .{ .builtin = defs });
try machine.define("def?", "return true if a name is defined", .{ .builtin = isDef });
try machine.define("usage", "print the usage notes of a given command", .{ .builtin = cmdUsage });
try machine.define(":", "bind variables", .{ .builtin = colon });

try machine.define("do!", "execute a command or quote", .{ .builtin = doBang });
try machine.define("do!", "execute a command or quote. " ++ bangDescription, .{ .builtin = doBang });
try machine.define("do", "execute a command or quote", .{ .builtin = do });
try machine.define("doin", "execute a command or quote in a previous quote", .{ .builtin = doin });
try machine.define("?", "consumes a command/quote and a value and performs it if the value is truthy", .{ .builtin = opt });
try machine.define("do!?", "consume a boolean and either do or drop a command or quote. " ++ bangDescription, .{ .builtin = doBangMaybe });
try machine.define("do?", "consume a boolean and either do or drop a command or quote", .{ .builtin = doMaybe });

try machine.define("dup", "duplicate the top value", .{ .builtin = dup });
try machine.define("drop", "drop the top value", .{ .builtin = drop });
Expand All @@ -51,6 +55,9 @@ pub fn defineAll(machine: *DtMachine) !void {
try machine.define("ep", "print a value to stderr", .{ .builtin = ep });
try machine.define("nl", "print a newline to stdout", .{ .builtin = nl });
try machine.define("enl", "print a newline to stderr", .{ .builtin = enl });
try machine.define("red", "make stdout/stderr red", .{ .builtin = red });
try machine.define("green", "make stdout/stderr green and bold (for the colorblind)", .{ .builtin = green });
try machine.define("norm", "reset stdout/stderr colors", .{ .builtin = norm });
try machine.define(".s", "print the stack", .{ .builtin = dotS });

try machine.define("read-line", "get a line from standard input (until newline)", .{ .builtin = readLine });
Expand All @@ -66,6 +73,7 @@ pub fn defineAll(machine: *DtMachine) !void {
try machine.define("/", "divide two numeric values", .{ .builtin = divide });
try machine.define("%", "modulo two numeric values", .{ .builtin = modulo });
try machine.define("abs", "consume a number and produce its absolute value", .{ .builtin = abs });
try machine.define("rand", "produces a random integer", .{ .builtin = rand });

try machine.define("eq?", "consume two values and return true if they are equal", .{ .builtin = eq });
try machine.define("gt?", "consume two numbers and return true if the most recent is greater", .{ .builtin = greaterThan });
Expand Down Expand Up @@ -108,18 +116,23 @@ pub fn defineAll(machine: *DtMachine) !void {
try machine.define("to-def", "coerce value to a deferred command", .{ .builtin = toDef });
try machine.define("to-quote", "coerce value to quote", .{ .builtin = toQuote });
try machine.define("to-error", "coerce value to an error", .{ .builtin = toError });

try machine.define("inspire", "get inspired", .{ .builtin = inspire });
}

pub fn quit(dt: *DtMachine) !void {
const ctx = try dt.popContext();

if (ctx.items.len > 0) {
try dt.red();
try stderr.print("Warning: Exited with unused values: [ ", .{});

for (ctx.items) |item| {
try item.print(dt.alloc);
try stderr.print(" ", .{});
}
try stderr.print("] \n", .{});
try dt.norm();
}

std.os.exit(0);
Expand Down Expand Up @@ -156,10 +169,7 @@ pub fn cd(dt: *DtMachine) !void {
path = try std.process.getEnvVarOwned(dt.alloc, "HOME");
}

std.os.chdir(path) catch |e| {
try stderr.print("Unable to change directory: {any}\n", .{e});
try dt.push(val);
};
std.os.chdir(path) catch |e| return dt.rewind(val, e);
}

pub fn ls(dt: *DtMachine) !void {
Expand Down Expand Up @@ -226,7 +236,10 @@ pub fn exec(dt: *DtMachine) !void {
return;
},
else => {
try dt.red();
try stderr.print("{s}", .{result.stderr});
try dt.norm();

try dt.push(.{ .bool = false });
},
}
Expand Down Expand Up @@ -349,6 +362,18 @@ pub fn ep(dt: *DtMachine) !void {
try _p(val, dt.alloc, stderr);
}

pub fn red(dt: *DtMachine) !void {
try dt.red();
}

pub fn green(dt: *DtMachine) !void {
try dt.green();
}

pub fn norm(dt: *DtMachine) !void {
try dt.norm();
}

fn _p(val: DtVal, allocator: Allocator, writer: std.fs.File.Writer) !void {
switch (val) {
.string => |s| {
Expand Down Expand Up @@ -447,11 +472,7 @@ pub fn add(dt: *DtMachine) !void {

const res = @addWithOverflow(a, b);

if (res[1] == 1) {
try dt.pushN(2, vals);
try stderr.print("ERROR: Adding {} and {} would overflow.\n", .{ a, b });
return Error.IntegerOverflow;
}
if (res[1] == 1) return dt.rewindN(2, vals, Error.IntegerOverflow);

try dt.push(.{ .int = res[0] });
return;
Expand All @@ -472,11 +493,7 @@ pub fn subtract(dt: *DtMachine) !void {

const res = @subWithOverflow(a, b);

if (res[1] == 1) {
try dt.pushN(2, vals);
try stderr.print("ERROR: Subtracting {} from {} would overflow.\n", .{ b, a });
return Error.IntegerOverflow;
}
if (res[1] == 1) return dt.rewindN(2, vals, Error.IntegerUnderflow);

try dt.push(.{ .int = res[0] });
return;
Expand All @@ -497,11 +514,7 @@ pub fn multiply(dt: *DtMachine) !void {

const res = @mulWithOverflow(a, b);

if (res[1] == 1) {
try dt.pushN(2, vals);
try stderr.print("ERROR: Multiplying {} by {} would overflow.\n", .{ a, b });
return Error.IntegerOverflow;
}
if (res[1] == 1) return dt.rewindN(2, vals, Error.IntegerOverflow);

try dt.push(.{ .int = res[0] });
return;
Expand All @@ -521,11 +534,7 @@ pub fn divide(dt: *DtMachine) !void {
const a = try vals[0].intoInt();
const b = try vals[1].intoInt();

if (b == 0) {
try dt.pushN(2, vals);
try stderr.print("ERROR: Cannot divide {} by zero.\n", .{a});
return Error.DivisionByZero;
}
if (b == 0) return dt.rewindN(2, vals, Error.DivisionByZero);

try dt.push(.{ .int = @divTrunc(a, b) });
return;
Expand All @@ -534,11 +543,7 @@ pub fn divide(dt: *DtMachine) !void {
const a = vals[0].intoFloat() catch |e| return dt.rewindN(2, vals, e);
const b = vals[1].intoFloat() catch |e| return dt.rewindN(2, vals, e);

if (b == 0) {
try dt.pushN(2, vals);
try stderr.print("ERROR: Cannot divide {} by zero.\n", .{a});
return Error.DivisionByZero;
}
if (b == 0) return dt.rewindN(2, vals, Error.DivisionByZero);

try dt.push(.{ .float = a / b });
}
Expand Down Expand Up @@ -576,6 +581,11 @@ pub fn abs(dt: *DtMachine) !void {
try dt.push(.{ .float = std.math.fabs(a) });
}

pub fn rand(dt: *DtMachine) !void {
const n = std.crypto.random.int(i64);
try dt.push(.{ .int = n });
}

pub fn eq(dt: *DtMachine) !void {
const vals = try dt.popN(2);

Expand Down Expand Up @@ -827,13 +837,6 @@ pub fn contains(dt: *DtMachine) !void {
try dt.push(.{ .bool = std.mem.containsAtLeast(u8, str, 1, substr) });
}

pub fn opt(dt: *DtMachine) !void {
var val = try dt.pop();
const cond = val.intoBool(dt);

try if (cond) do(dt) else drop(dt) catch |e| return dt.rewind(val, e);
}

pub fn doBang(dt: *DtMachine) !void {
var val = try dt.pop();

Expand Down Expand Up @@ -871,6 +874,20 @@ pub fn do(dt: *DtMachine) !void {
dt.nest = jail.nest;
}

pub fn doBangMaybe(dt: *DtMachine) !void {
var val = try dt.pop();
const cond = val.intoBool(dt);

try if (cond) doBang(dt) else drop(dt) catch |e| return dt.rewind(val, e);
}

pub fn doMaybe(dt: *DtMachine) !void {
var val = try dt.pop();
const cond = val.intoBool(dt);

try if (cond) do(dt) else drop(dt) catch |e| return dt.rewind(val, e);
}

pub fn doin(dt: *DtMachine) !void {
const vals = try dt.popN(2);

Expand Down Expand Up @@ -1163,3 +1180,8 @@ pub fn toError(dt: *DtMachine) !void {

try dt.push(.{ .err = errName });
}

pub fn inspire(dt: *DtMachine) !void {
const i = std.crypto.random.uintLessThan(usize, dt.inspiration.len);
try dt.push(.{ .string = dt.inspiration[i] });
}
11 changes: 11 additions & 0 deletions src/inspiration
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Be generous with the duct tape, you know; spare the duct tape, spoil the job.
If it ain't broke, you're not trying.
If the women don't find you handsome, they should at least find you handy.
Remember, you may have to grow old, but you don't have to mature.
Remember, I'm pulling for ya. We're all in this together!
When the going gets tough, switch to power tools.
Quando omni flunkus, moritadi.
Learn from my mistakes - someone should.
You're only limited by your own imagination, and the laws in your area.
Now, this is only temporary... unless it works.
Keep your stick on the ice.
49 changes: 45 additions & 4 deletions src/interpret.zig
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@ const StringHashMap = std.StringHashMap;
const stdin = std.io.getStdIn().reader();
const stdout = std.io.getStdOut().writer();
const stderr = std.io.getStdErr().writer();
const Color = std.io.tty.Color;

const tokens = @import("tokens.zig");
const Token = tokens.Token;

const string = @import("string.zig");
const String = string.String;

const inspiration = @embedFile("inspiration");

pub const Dictionary = StringHashMap(Command);
pub const Quote = ArrayList(DtVal);

Expand All @@ -38,21 +41,37 @@ pub const DtError = error{

pub const DtMachine = struct {
alloc: Allocator,

nest: Stack(ArrayList(DtVal)),
defs: Dictionary,
depth: u8,

defs: Dictionary,

stdoutConfig: std.io.tty.Config,
stderrConfig: std.io.tty.Config,

inspiration: []String,

pub fn init(alloc: Allocator) !DtMachine {
var nest = Stack(Quote){};
var mainNode = try alloc.create(Stack(Quote).Node);
mainNode.* = Stack(Quote).Node{ .data = Quote.init(alloc) };
nest.prepend(mainNode);

var inspirations = ArrayList(String).init(alloc);
var lines = std.mem.tokenizeScalar(u8, inspiration, '\n');
while (lines.next()) |line| {
try inspirations.append(line);
}

return .{
.alloc = alloc,
.nest = nest,
.defs = Dictionary.init(alloc),
.depth = 0,
.defs = Dictionary.init(alloc),
.stdoutConfig = std.io.tty.detectConfig(std.io.getStdOut()),
.stderrConfig = std.io.tty.detectConfig(std.io.getStdErr()),
.inspiration = inspirations.items,
};
}

Expand Down Expand Up @@ -97,18 +116,38 @@ pub const DtMachine = struct {
}

if (self.defs.get(cmdName)) |cmd| {
// try stderr.print("Running command: {s}\n", .{cmd.name});
cmd.run(self) catch |e| {
if (e == error.EndOfStream) return e;
try self.push(DtVal{ .err = @errorName(e) });
};
return;
}

try self.red();
try stderr.print("Undefined: {s}\n", .{cmdName});
try self.norm();
return DtError.CommandUndefined;
}

pub fn red(self: *DtMachine) !void {
try self.norm();
try self.stdoutConfig.setColor(stdout, Color.red);
try self.stderrConfig.setColor(stderr, Color.red);
}

pub fn green(self: *DtMachine) !void {
try self.stdoutConfig.setColor(stdout, Color.green);
try self.stdoutConfig.setColor(stdout, Color.bold);

try self.stderrConfig.setColor(stderr, Color.green);
try self.stdoutConfig.setColor(stdout, Color.bold);
}

pub fn norm(self: *DtMachine) !void {
try self.stdoutConfig.setColor(stdout, Color.reset);
try self.stderrConfig.setColor(stderr, Color.reset);
}

pub fn child(self: *DtMachine) !DtMachine {
var newMachine = try DtMachine.init(self.alloc);

Expand Down Expand Up @@ -369,7 +408,9 @@ pub const Command = struct {
.command => |cmdName| {
// Even if this is the same command name, we should re-fetch in case it's been redefined
const cmd = state.defs.get(cmdName) orelse {
try stderr.print("Command undefined: {s}\n", .{cmdName});
try state.red();
try stderr.print("Undefined: {s}\n", .{cmdName});
try state.norm();
return DtError.CommandUndefined;
};
switch (cmd.action) {
Expand Down
Loading

0 comments on commit caa93fa

Please sign in to comment.