Skip to content

Commit

Permalink
Add define/alias, add usage for stdlib definitions, handle undefined …
Browse files Browse the repository at this point in the history
…commands without dying
  • Loading branch information
booniepepper committed Jul 11, 2023
1 parent 015c925 commit 121362b
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 71 deletions.
2 changes: 1 addition & 1 deletion old-rust-tests/tests/dt_args_basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use dt_test_utils::dt;

#[test]
fn status() {
assert_eq!("[ ]\n", dt(&["quote-all", "drop", "status"]).stdout);
assert_eq!("[ ]\n", dt(&["quote-all", "drop", "status"]).stderr);
}

#[test]
Expand Down
1 change: 1 addition & 0 deletions old-rust-tests/tests/dt_test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ pub fn dt_oneliner(source: &str) -> DtRunResult {

#[allow(dead_code)]
pub fn dt(args: &[&str]) -> DtRunResult {
println!("ARGS: {:?}", args);
Command::new(DT_PATH)
// .args(DEV_MODE_ARGS)
.args(args)
Expand Down
24 changes: 12 additions & 12 deletions src/builtins.zig
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ pub fn quit(dt: *DtMachine) !void {
try stderr.print("warning(quit): Exited with unused values: [ ", .{});

for (ctx.items) |item| {
try item.print(dt.alloc);
try item.print(stderr);
try stderr.print(" ", .{});
}
try stderr.print("] \n", .{});
Expand Down Expand Up @@ -429,19 +429,19 @@ pub fn rot(dt: *DtMachine) !void {

pub fn p(dt: *DtMachine) !void {
const val = try dt.pop();
try _p(val, dt.alloc, stdout);
try _p(val, stdout);
}

pub fn ep(dt: *DtMachine) !void {
const val = try dt.pop();
try _p(val, dt.alloc, stderr);
try _p(val, stderr);
}

fn _p(val: DtVal, allocator: Allocator, writer: std.fs.File.Writer) !void {
fn _p(val: DtVal, writer: std.fs.File.Writer) !void {
switch (val) {
// When printing strings, do not show " around a string.
.string => |s| try writer.print("{s}", .{s}),
else => try val.print(allocator),
else => try val.print(writer),
}
}

Expand All @@ -466,19 +466,19 @@ pub fn norm(dt: *DtMachine) !void {
}

pub fn @".s"(dt: *DtMachine) !void {
try stdout.print("[ ", .{});
try stderr.print("[ ", .{});

var top = dt.nest.first orelse {
try stdout.print("]", .{});
try stderr.print("]", .{});
return;
};

for (top.data.items) |val| {
try val.print(dt.alloc);
try stdout.print(" ", .{});
try val.print(stderr);
try stderr.print(" ", .{});
}

try stdout.print("]\n", .{});
try stderr.print("]\n", .{});
}

pub fn @"read-line"(dt: *DtMachine) !void {
Expand Down Expand Up @@ -979,7 +979,7 @@ pub fn @"do!"(dt: *DtMachine) !void {

const quote = try val.intoQuote(dt);

for (quote.items) |v| try dt.handle(v);
for (quote.items) |v| try dt.handleVal(v);
}

// Same as do! but does not uplevel any definitions
Expand All @@ -1000,7 +1000,7 @@ pub fn do(dt: *DtMachine) !void {

const quote = try val.intoQuote(dt);

for (quote.items) |v| try jail.handle(v);
for (quote.items) |v| try jail.handleVal(v);
dt.nest = jail.nest;
}

Expand Down
57 changes: 31 additions & 26 deletions src/interpret.zig
Original file line number Diff line number Diff line change
Expand Up @@ -104,14 +104,25 @@ pub const DtMachine = struct {
}
}

pub fn handle(self: *DtMachine, val: DtVal) anyerror!void {
pub fn handleVal(self: *DtMachine, val: DtVal) anyerror!void {
const log = std.log.scoped(.@"dt.handleVal");

switch (val) {
.command => |cmdName| try self.handleCmd(cmdName),
.command => |name| {
self.handleCmd(name) catch |e| {
if (e != error.CommandUndefined) return e;
try self.red();
log.warn("Undefined: {s}", .{name});
try self.norm();
};
},
else => try self.push(val),
}
}

pub fn handleCmd(self: *DtMachine, cmdName: String) !void {
const log = std.log.scoped(.@"dt.handleCmd");

if (self.depth > 0) {
try self.push(DtVal{ .command = cmdName });
return;
Expand All @@ -120,12 +131,11 @@ pub const DtMachine = struct {
if (self.defs.get(cmdName)) |cmd| {
try cmd.run(self);
return;
} else {
try self.red();
log.warn("Undefined: {s}", .{cmdName});
try self.norm();
}

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

pub fn loadFile(self: *DtMachine, code: []const u8) !void {
Expand Down Expand Up @@ -357,22 +367,22 @@ pub const DtVal = union(enum) {
};
}

pub fn print(self: DtVal, allocator: Allocator) !void {
pub fn print(self: DtVal, writer: std.fs.File.Writer) !void {
switch (self) {
.bool => |b| try stdout.print("{}", .{b}),
.int => |i| try stdout.print("{}", .{i}),
.float => |f| try stdout.print("{d}", .{f}),
.command => |cmd| try stdout.print("{s}", .{cmd}),
.deferred_command => |cmd| try stdout.print("\\{s}", .{cmd}),
.bool => |b| try writer.print("{}", .{b}),
.int => |i| try writer.print("{}", .{i}),
.float => |f| try writer.print("{d}", .{f}),
.command => |cmd| try writer.print("{s}", .{cmd}),
.deferred_command => |cmd| try writer.print("\\{s}", .{cmd}),
.quote => |q| {
try stdout.print("[ ", .{});
try writer.print("[ ", .{});
for (q.items) |val| {
try val.print(allocator);
try stdout.print(" ", .{});
try val.print(writer);
try writer.print(" ", .{});
}
try stdout.print("]", .{});
try writer.print("]", .{});
},
.string => |s| try stdout.print("\"{s}\"", .{s}),
.string => |s| try writer.print("\"{s}\"", .{s}),
}
}
};
Expand All @@ -395,7 +405,7 @@ pub const Command = struct {
again = false;

for (vals[0..lastIndex]) |val| {
try state.handle(val);
try state.handleVal(val);
}

const lastVal = vals[lastIndex];
Expand All @@ -404,12 +414,7 @@ pub const Command = struct {
// Tail calls optimized, yay!
.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 state.red();
try stderr.print("Undefined: {s}\n", .{cmdName});
try state.norm();
return DtError.CommandUndefined;
};
const cmd = state.defs.get(cmdName) orelse return DtError.CommandUndefined;
switch (cmd.action) {
.quote => |nextQuote| {
again = true;
Expand All @@ -419,7 +424,7 @@ pub const Command = struct {
.builtin => |b| return try b(state),
}
},
else => try state.handle(lastVal),
else => try state.handleVal(lastVal),
}
}
},
Expand Down
134 changes: 102 additions & 32 deletions src/stdlib.dt
Original file line number Diff line number Diff line change
@@ -1,82 +1,152 @@
### Binding ###

[ swap quote [ do ] concat swap def! ] \def def!
[ swap quote [ do ] concat swap def! ]
\def def!
\def "( <action> <name> -- ) Define a command." def-usage

[ : ] \-> def
[ def? not ] \undef? def
[ swap rot dup rot def swap def-usage ]
\define def!
\define "( <action> <description> <name> -- ) Define a command with a description." def-usage

[ dup rot swap dup rot [ do ] enq swap def! usage def-usage ]
\alias def!
\alias "( <prevname> <newname> -- ) Alias a new command from another, copying the description of the previous command." def-usage

[ def? not ] "( <name> -- <bool> ) Determine whether a command is undefined."
\undef? define


### Display ###

[ p nl ] \pl def
[ p ] \print def
[ pl ] \println def
[ \pl each ] \pls def
# Display: stdout

[ p nl ]
"( <a> -- ) Print the most recent value and a newline to standard output."
\pl define

[ \pl each ]
"( [<a>] -- ) Print the values of the most recent quote, each followed by a newline, to standard output."
\pls define

\p \print alias
\pl \println alias
\pls \printlns alias

# Display: stderr

[ ep enl ]
"( <a> -- ) Print the most recent value and a newline to standard error."
\epl define

[ \epl each ]
"( [<a>] -- ) Print the values of the most recent quote, each followed by a newline, to standard error."
\epls define

[ ep enl ] \epl def
[ ep ] \eprint def
[ epl ] \eprintln def
[ \epl each ] \epls def
\ep \eprint alias
\epl \eprintln alias
\epls \eprintlns alias

[ .s ] \status def
# Display: misc

\.s \status alias


### Filesystem and process things ###

[ cwd pl ] \pwd def
[ cwd pl ]
"( -- ) Print the current working directory to standard output."
\pwd define


### Math and such ###

[ 2 % 0 eq? ] \even? def
[ 2 % 1 eq? ] \odd? def
[ % 0 eq? ] \divisor? def
[ 2 % 0 eq? ] "( <a> -- <bool> ) Determine if a number is even." \even? define
[ 2 % 1 eq? ] "( <a> -- <bool> ) Determine if a number is odd." \odd? define
[ % 0 eq? ] "( <a> <b> -- <bool> ) Determine if a number a is evenly divisible by number b." \divisor? define


### Boolean operators ###

[ eq? not ] \neq? def
[ and not ] \nand def
[ or not ] \nor def
[ eq? not ]
"( <a> <b> -- <bool> ) Determine if two values are unequal."
\neq? define

[ and not ]
"( <a> <b> -- <bool> ) Determine if two values are not both truthy."
\nand define

[ or not ]
"( <a> <b> -- <bool> ) Determine if neither of two values are truthy."
\nor define


### String things ###

[ "" split ] \chars def
[ "" split ]
"( <string> -- [<substring>] ) Splits a string into individual characters, where a character is a single byte. (Not friendly to UTF-8 runes.)"
\chars define

# TODO: runes

[ " " split ] \words def
[ " " join ] \unwords def
[ " " split ]
"( <string> -- [<substring>] ) Splits a string on spaces."
\words define

[ "\n" split ] \lines def
[ "\n" join ] \unlines def
[ " " join ]
"( [<substring>] -- <string> ) Joins strings with spaces."
\unwords define

[ "\n" split ]
"( <string> -- [<substring>] ) Splits a string on newlines."
\lines define

[ "\n" join ]
"( [<substring>] -- <string> ) Joins strings with newlines."
\unlines define


### Quote manipulation ###

[ deq drop ] \first def
[ pop swap drop ] \last def
[ ... ] \unquote def
[ deq drop ]
"( [<a>] -- <b> ) The first element of a quote."
\first define

[ pop swap drop ]
"( [<a>] -- <b> ) The last element of a quote."
\last define

\... \unquote alias


### Control flow ###

[ [ cmd n ]:
n 0 gt? \continue :
[ cmd do cmd n 1 - times ] continue do?
] \times def
]
"( <action> <n> -- ? ) Perform a given action n times."
\times define

[ map drop ] \each def
[ map drop ]
"( [<a>] <action> -- ) Perform a given action with each value in a quote."
\each define

[ [ action cond ]:
[ action do action cond while ] cond do do?
] \while def
]
"( <action> <cond> -- ? ) Perform an action while the condition is truthy."
\while define


### Parsing ###

[ lines [ "," split ] map ] \parse-csv def
[ lines [ "," split ] map ]
"( <string> -- [[<cells>]] ) Naive conversion of a raw string in comma-separated value (CSV) format to a quote of lines of cells"
\parse-csv define


### Testing ###

[ [ cond msg ]: [ msg pl 1 exit ] cond not do? ] \assert-true def
[ [ cond msg ]: [ msg epl 1 exit ] cond not do? ]
"( <cond> <message> -- ) Naive assertion that requires a condition to be truthy. If falsy, it will print a message to standard error and fail with an exit code of 1."
\assert-true def

0 comments on commit 121362b

Please sign in to comment.