Skip to content

Commit

Permalink
Update cli
Browse files Browse the repository at this point in the history
  • Loading branch information
bgk- committed May 19, 2024
1 parent a75216a commit 5200b48
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 24 deletions.
2 changes: 1 addition & 1 deletion build.zig.zon
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.{
.name = "topiary",
.version = "0.12.1",
.version = "0.12.2",
.paths = .{""},
.dependencies = .{},
}
15 changes: 10 additions & 5 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,21 @@ To run the CLI:
```shell
topi - command line topiary processor
Usage:
topi [-v | --version] [-h | --help] <command> <file> [flags]
topi <command> <file> [flags]

Commands:
topi run <file> [start_bough] [--verbose]
topi auto <file> <count> [--verbose]
topi compile <file> <output_file|--dry|-d> [--verbose]
topi version
topi run <file> [start_bough] [--auto|-a] [--lang language_key] [--verbose]
topi test <file> <count> [--verbose]
topi compile <file> <output_file|--dry|-d> [--loc] [--verbose]
topi loc validate <file> [--verbose]
topi loc export <file> <output_file|--dry|-d> [--verbose]

Flags:
--version, -v: Output current version
--verbose: Output debug logs
--auto, -a: Automatically continue to next line
--lang: Localization language key
--loc: Include localization in compiled bytecode
--dry, -d: Compile only
```

Expand Down
14 changes: 14 additions & 0 deletions docs/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,20 @@ john.increaseAge(2)
print(john.age) // 27
```

Fields can also be indexed as well with `[]` notation.
This can be especially useful for extern function calls.
As an example with the above class:

```topi
const changeField = |instance, fieldName, newValue| {
instance[fieldName] = newValue
}
changeField(john, "firstName", "Johnny")
changeField(john, "age", 28)
print(john.fullName()) // "Johnny Doe"
```

## Multiple Files

Multiple files can be joined together to create a single story using `include "[PATH]"`.
Expand Down
2 changes: 1 addition & 1 deletion src/class.zig
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub const Class = struct {
pub fn getIndex(self: *const Class, field_name: []const u8) ?usize {
var i: usize = 0;
while (i < self.fields.len) : (i += 1) {
if (!std.mem.eql(u8, self.fields[i].name, field_name)) continue;
if (!std.mem.eql(u8, self.fields[i].name, std.mem.trim(u8, field_name, &[_]u8{0}))) continue;
return i;
}
return null;
Expand Down
37 changes: 26 additions & 11 deletions src/cli.zig
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,17 @@ fn usage(comptime msg: []const u8) !void {
try out.print("\n", .{});
try out.print("Commands:\n", .{});
try out.print(" topi version\n", .{});
try out.print(" topi run <file> [start_bough] [--loc language_key] [--verbose]\n", .{});
try out.print(" topi auto <file> <count> [--verbose]\n", .{});
try out.print(" topi compile <file> <output_file|--dry|-d> [--loc | --verbose]\n", .{});
try out.print(" topi run <file> [start_bough] [--auto|-a] [--lang language_key] [--verbose]\n", .{});
try out.print(" topi test <file> <count> [--verbose]\n", .{});
try out.print(" topi compile <file> <output_file|--dry|-d> [--loc] [--verbose]\n", .{});
try out.print(" topi loc validate <file> [--verbose]\n", .{});
try out.print(" topi loc export <file> <output_file|--dry|-d> [--verbose]\n", .{});
try out.print("\n", .{});
try out.print("Flags:\n", .{});
try out.print(" --verbose: Output debug logs\n", .{});
try out.print(" --auto, -a: Automatically continue to next line\n", .{});
try out.print(" --lang: Localization language key\n", .{});
try out.print(" --loc: Include localization in compiled bytecode\n", .{});
try out.print(" --dry, -d: Dry-run only\n", .{});
}

Expand Down Expand Up @@ -72,17 +75,18 @@ pub fn main() !void {
}

const is_run = std.mem.eql(u8, cmd, "run");
const is_auto = std.mem.eql(u8, cmd, "auto");
const is_test = std.mem.eql(u8, cmd, "auto");
const is_compile = std.mem.eql(u8, cmd, "compile");
const is_loc = std.mem.eql(u8, cmd, "loc");
if (!is_run and !is_auto and !is_compile and !is_loc) return usage("Unknown command");
if (!is_run and !is_test and !is_compile and !is_loc) return usage("Unknown command");

var bough_path: ?[]const u8 = null;
var out_path: ?[]const u8 = null;
var loc_key: ?[]const u8 = null;
var is_dry = false;
var is_export = false;
var is_validate = false;
var is_auto = false;

if (is_loc) {
const maybe_sub = args.next();
Expand Down Expand Up @@ -111,7 +115,11 @@ pub fn main() !void {
if (is_run) {
while (args.next()) |arg| {
if (std.mem.eql(u8, arg, "--verbose")) continue;
if (std.mem.eql(u8, arg, "--loc")) {
if (std.mem.eql(u8, arg, "--auto") or std.mem.eql(u8, arg, "-a")) {
is_auto = true;
continue;
}
if (std.mem.eql(u8, arg, "--lang")) {
loc_key = args.next();
continue;
}
Expand Down Expand Up @@ -193,7 +201,7 @@ pub fn main() !void {
}

const vm_alloc = arena.allocator();
if (is_auto) {
if (is_test) {
const maybe_count = args.next();
if (maybe_count == null) return usage("Auto requires a play count.");
auto_count = std.fmt.parseInt(u64, maybe_count.?, 10) catch {
Expand Down Expand Up @@ -228,7 +236,7 @@ pub fn main() !void {
}

if (is_run) {
var cli_runner = CliRunner.init();
var cli_runner = CliRunner.init(is_auto);
var vm = Vm.init(vm_alloc, bytecode, &cli_runner.runner) catch |err| {
try std.io.getStdErr().writeAll("Could not initialize Vm");
return checkVerbose(err);
Expand All @@ -254,9 +262,11 @@ fn getFilePath(allocator: std.mem.Allocator) !?[]const u8 {

const CliRunner = struct {
runner: Runner,
is_auto: bool,

pub fn init() CliRunner {
pub fn init(is_auto: bool) CliRunner {
return .{
.is_auto = is_auto,
.runner = .{
.onLineFn = onLine,
.onChoicesFn = onChoices,
Expand All @@ -280,9 +290,14 @@ const CliRunner = struct {
}
self.print(": ", .{});
self.print("{s}", .{dialogue.content});
var buf: [2]u8 = undefined;
if (stdin.readUntilDelimiterOrEof(&buf, '\n') catch &buf) |_| {
if (self.is_auto) {
self.print("\n", .{});
vm.selectContinue();
} else {
var buf: [2]u8 = undefined;
if (stdin.readUntilDelimiterOrEof(&buf, '\n') catch &buf) |_| {
vm.selectContinue();
}
}
}

Expand Down
14 changes: 8 additions & 6 deletions src/vm.zig
Original file line number Diff line number Diff line change
Expand Up @@ -505,10 +505,12 @@ pub const Vm = struct {
const field_name = field_value.obj.data.string;
if (inst.base.data.class.getIndex(field_name)) |idx| {
inst.fields[idx] = new_value;
} else return self.fail("Instance of {s} does not contain {s}", .{ inst.base.data.class.name, field_name });
} else {
return self.fail("Instance of {s} does not contain field '{s}'", .{ inst.base.data.class.name, field_name });
}
},
// todo add string indexing
else => return self.fail("Cannot index {s} into type {s}", .{ @tagName(field_value), @tagName(instance_value.obj.data) }),
else => return self.fail("Cannot index '{s}' into type {s}", .{ @tagName(field_value), @tagName(instance_value.obj.data) }),
}
},
.get_builtin => {
Expand Down Expand Up @@ -566,11 +568,11 @@ pub const Vm = struct {
switch (o.data) {
// remove final 0
.string => try writer.writeAll(o.data.string[0..(o.data.string.len - 1)]),
else => return self.fail("Unsupported interpolated type {s} for {s}", .{ val.typeName(), str }),
else => return self.fail("Unsupported interpolated type '{s}' for '{s}'", .{ val.typeName(), str }),
}
},
.visit => |v| try std.fmt.formatIntValue(v, "", .{}, list.writer()),
else => return self.fail("Unsupported interpolated type {s} for {s}", .{ val.typeName(), str }),
else => return self.fail("Unsupported interpolated type '{s}' for '{s}'", .{ val.typeName(), str }),
}
s = i;
}
Expand Down Expand Up @@ -773,9 +775,9 @@ pub const Vm = struct {
},
.instance => |i| {
if (index != .obj)
return self.fail("Can only query instance fields by string name, not {s}", .{@tagName(index)});
return self.fail("Can only query instance fields by string name, not '{s}'", .{@tagName(index)});
if (index.obj.data != .string)
return self.fail("Can only query instance fields by string name, not {s}", .{@tagName(index.obj.data)});
return self.fail("Can only query instance fields by string name, not '{s}'", .{@tagName(index.obj.data)});
if (i.base.data.class.getIndex(index.obj.data.string)) |idx| {
const field = i.fields[idx];
try self.push(field);
Expand Down

0 comments on commit 5200b48

Please sign in to comment.