Compare commits
No commits in common. "d09603c005794bd4e25e2edf130ec02f673a6512" and "9c6387973fc5f2d70762905abd45ce38aaef5b9d" have entirely different histories.
d09603c005
...
9c6387973f
2 changed files with 161 additions and 200 deletions
251
src/lang.zig
251
src/lang.zig
|
@ -51,29 +51,12 @@ pub const CommandType = enum {
|
||||||
Rotate,
|
Rotate,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const NewCommandType = enum {
|
pub const Type = enum {
|
||||||
/// "LV2 Commands" are commands that receive split, index, and then receive
|
/// "LV2 Commands" are commands that receive split, index, and then receive
|
||||||
/// any f64 arguments.
|
/// any f64 arguments.
|
||||||
lv2_command,
|
lv2_command,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn LV2Command(
|
|
||||||
comptime tag: NewCommand.Tag,
|
|
||||||
comptime plugin_url: []const u8,
|
|
||||||
comptime LV2Parameters: type,
|
|
||||||
) type {
|
|
||||||
return struct {
|
|
||||||
pub const base_tag = tag;
|
|
||||||
pub const command_type = NewCommandType.lv2_command;
|
|
||||||
pub const lv2_url = plugin_url;
|
|
||||||
|
|
||||||
base: NewCommand,
|
|
||||||
split: usize,
|
|
||||||
index: usize,
|
|
||||||
parameters: LV2Parameters,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const NewCommand = struct {
|
pub const NewCommand = struct {
|
||||||
tag: Tag,
|
tag: Tag,
|
||||||
|
|
||||||
|
@ -129,33 +112,6 @@ pub const NewCommand = struct {
|
||||||
|
|
||||||
.amp => Amp,
|
.amp => Amp,
|
||||||
.rflanger => RFlanger,
|
.rflanger => RFlanger,
|
||||||
.eq => Eq,
|
|
||||||
.phaser => Phaser,
|
|
||||||
// .mbeq => Mbeq,
|
|
||||||
// .chorus => Chorus,
|
|
||||||
// .pitchscaler => Pitchscaler,
|
|
||||||
// .reverb => Reverb,
|
|
||||||
// .highpass => Highpass,
|
|
||||||
// .delay => Delay,
|
|
||||||
// .vinyl => Vinyl,
|
|
||||||
// .revdelay => Revdelay,
|
|
||||||
// .gate => Gate,
|
|
||||||
// .detune => Detune,
|
|
||||||
// .overdrive => Overdrive,
|
|
||||||
// .degrade => Degrade,
|
|
||||||
// .repsycho => Repsycho,
|
|
||||||
// .talkbox => Talkbox,
|
|
||||||
// .dyncomp => Dyncomp,
|
|
||||||
// .thruzero => Thruzero,
|
|
||||||
// .foverdrive => Foverdrive,
|
|
||||||
// .gverb => Gverb,
|
|
||||||
// .invert => Invert,
|
|
||||||
// .tapedelay => Tapedelay,
|
|
||||||
// .moddelay => Moddelay,
|
|
||||||
// .multichorus => Multichorus,
|
|
||||||
// .saturator => Saturator,
|
|
||||||
// .vintagedelay => Vintagedelay,
|
|
||||||
|
|
||||||
else => @panic("TODO"),
|
else => @panic("TODO"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -193,37 +149,22 @@ pub const NewCommand = struct {
|
||||||
base: NewCommand,
|
base: NewCommand,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Amp = LV2Command(
|
pub const Amp = struct {
|
||||||
.amp,
|
pub const base_tag = Tag.amp;
|
||||||
"http://lv2plug.in/plugins/eg-amp",
|
base: NewCommand,
|
||||||
struct {
|
split: usize,
|
||||||
gain: f32
|
index: usize,
|
||||||
},
|
gain: f32
|
||||||
);
|
};
|
||||||
|
|
||||||
pub const RFlanger = LV2Command(
|
pub const RFlanger = struct {
|
||||||
.rflanger,
|
pub const base_tag = Tag.rflanger;
|
||||||
"http://plugin.org.uk/swh-plugins/retroFlange",
|
base: NewCommand,
|
||||||
struct {
|
split: usize,
|
||||||
delay_depth_avg: f32, law_freq: f32
|
index: usize,
|
||||||
},
|
delay_depth_avg: f32,
|
||||||
);
|
law_freq: f32,
|
||||||
|
};
|
||||||
pub const Eq = LV2Command(
|
|
||||||
.rflanger,
|
|
||||||
"http://plugin.org.uk/swh-plugins/dj_eq_mono",
|
|
||||||
struct {
|
|
||||||
lo: f32, mid: f32, hi: f32
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
pub const Phaser = LV2Command(
|
|
||||||
.rflanger,
|
|
||||||
"http://plugin.org.uk/swh-plugins/lfoPhaser",
|
|
||||||
struct {
|
|
||||||
lfo_rate: f32, lfo_depth: f32, fb: f32, spread: f32
|
|
||||||
},
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Command = struct {
|
pub const Command = struct {
|
||||||
|
@ -335,6 +276,7 @@ pub const KeywordMap = std.StringHashMap(CommandType);
|
||||||
/// A parser.
|
/// A parser.
|
||||||
pub const Lang = struct {
|
pub const Lang = struct {
|
||||||
allocator: *std.mem.Allocator,
|
allocator: *std.mem.Allocator,
|
||||||
|
keywords: KeywordMap,
|
||||||
|
|
||||||
has_error: bool = false,
|
has_error: bool = false,
|
||||||
line: usize = 0,
|
line: usize = 0,
|
||||||
|
@ -342,15 +284,105 @@ pub const Lang = struct {
|
||||||
pub fn init(allocator: *std.mem.Allocator) Lang {
|
pub fn init(allocator: *std.mem.Allocator) Lang {
|
||||||
return Lang{
|
return Lang{
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
|
.keywords = KeywordMap.init(allocator),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Lang) void {}
|
pub fn deinit(self: *Lang) void {
|
||||||
|
self.keywords.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn reset(self: *Lang) void {
|
pub fn reset(self: *Lang) void {
|
||||||
self.has_error = false;
|
self.has_error = false;
|
||||||
self.line = 0;
|
self.line = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fillKeywords(self: *Lang) !void {
|
||||||
|
inline for (@typeInfo(NewCommand).Struct.decls) |cmd_struct_decl| {
|
||||||
|
switch (cmd_struct_decl.data) {
|
||||||
|
.Type => |typ| switch (@typeInfo(typ)) {
|
||||||
|
.Struct => {},
|
||||||
|
else => continue,
|
||||||
|
},
|
||||||
|
else => continue,
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct_name = cmd_struct_decl.name;
|
||||||
|
|
||||||
|
comptime var lowered_command_name = [_]u8{0} ** struct_name.len;
|
||||||
|
comptime {
|
||||||
|
for (struct_name) |c, i| {
|
||||||
|
lowered_command_name[i] = std.ascii.toLower(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = try self.keywords.put(&lowered_command_name, @field(CommandType, struct_name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getCommand(self: *Lang, stmt: []const u8) ?CommandType {
|
||||||
|
var kv_opt = self.keywords.get(stmt);
|
||||||
|
|
||||||
|
if (kv_opt) |kv| {
|
||||||
|
return kv.value;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expectAny(self: *Lang, count: usize, args: ArgList) !void {
|
||||||
|
if (args.items.len != count) {
|
||||||
|
self.doError("expected {} arguments, found {}", .{ count, args.items.len });
|
||||||
|
return error.ArgRequired;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expectSingle(self: *Lang, args: ArgList) !void {
|
||||||
|
return try self.expectAny(1, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expectFloat(self: *Lang, count: usize, args: ArgList) !void {
|
||||||
|
var i: usize = 0;
|
||||||
|
|
||||||
|
if (args.items.len != count) {
|
||||||
|
self.doError("expected {} arguments, found {}", .{ count, args.items.len });
|
||||||
|
return error.ArgRequired;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (i < count) : (i += 1) {
|
||||||
|
var arg = args.items[i];
|
||||||
|
_ = std.fmt.parseFloat(f32, arg) catch |err| {
|
||||||
|
std.debug.warn("failed to parse f32: {}\n", .{err});
|
||||||
|
return error.FloatParseFail;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validateCommand(self: *Lang, cmd: Command) !void {
|
||||||
|
switch (cmd.command) {
|
||||||
|
.Quicksave, .Noop => {},
|
||||||
|
.Load, .RunQS => try self.expectSingle(cmd.args),
|
||||||
|
.Amp => try self.expectFloat(3, cmd.args),
|
||||||
|
.RFlanger => try self.expectFloat(4, cmd.args),
|
||||||
|
.Eq => try self.expectFloat(5, cmd.args),
|
||||||
|
.Phaser => try self.expectFloat(6, cmd.args),
|
||||||
|
.Mbeq => try self.expectFloat(17, cmd.args),
|
||||||
|
.Chorus => try self.expectFloat(8, cmd.args),
|
||||||
|
.PitchScaler => try self.expectFloat(3, cmd.args),
|
||||||
|
.Reverb => try self.expectFloat(12, cmd.args),
|
||||||
|
.Highpass => try self.expectFloat(5, cmd.args),
|
||||||
|
.Delay => try self.expectFloat(12, cmd.args),
|
||||||
|
.Vinyl => try self.expectFloat(7, cmd.args),
|
||||||
|
.RevDelay => try self.expectFloat(7, cmd.args),
|
||||||
|
.Noise => try self.expectFloat(4, cmd.args),
|
||||||
|
.WildNoise => try self.expectFloat(4, cmd.args),
|
||||||
|
.Write => try self.expectFloat(3, cmd.args),
|
||||||
|
.Rotate => try self.expectAny(2, cmd.args),
|
||||||
|
.Embed => try self.expectAny(3, cmd.args),
|
||||||
|
else => std.debug.warn("WARN unchecked command {}\n", .{cmd.command}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn doError(self: *Lang, comptime fmt: []const u8, args: var) void {
|
fn doError(self: *Lang, comptime fmt: []const u8, args: var) void {
|
||||||
std.debug.warn("error at line {}: ", .{self.line});
|
std.debug.warn("error at line {}: ", .{self.line});
|
||||||
std.debug.warn(fmt, args);
|
std.debug.warn(fmt, args);
|
||||||
|
@ -366,57 +398,28 @@ pub const Lang = struct {
|
||||||
) !void {
|
) !void {
|
||||||
// Based on the command struct fields, we can parse the arguments.
|
// Based on the command struct fields, we can parse the arguments.
|
||||||
var cmd = try self.allocator.create(command_struct);
|
var cmd = try self.allocator.create(command_struct);
|
||||||
const is_lv2_command = switch (command_struct.base_tag) {
|
|
||||||
.noop, .load, .quicksave, .runqs => false,
|
|
||||||
else => command_struct.command_type == .lv2_command,
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: crash when no arguments are left but we still need
|
inline for (@typeInfo(command_struct).Struct.fields) |cmd_field| {
|
||||||
// arguments...
|
comptime {
|
||||||
|
if (std.mem.eql(u8, cmd_field.name, "base")) {
|
||||||
if (is_lv2_command) {
|
continue;
|
||||||
cmd.split = try std.fmt.parseInt(usize, tok_it.next().?, 10);
|
|
||||||
cmd.index = try std.fmt.parseInt(usize, tok_it.next().?, 10);
|
|
||||||
|
|
||||||
inline for (@typeInfo(@TypeOf(cmd.parameters)).Struct.fields) |cmd_field| {
|
|
||||||
const arg = tok_it.next().?;
|
|
||||||
const argument_value = switch (cmd_field.field_type) {
|
|
||||||
f32 => try std.fmt.parseFloat(f32, arg),
|
|
||||||
else => @compileError("LV2 parameter struct can only have f32 fields"),
|
|
||||||
};
|
|
||||||
|
|
||||||
std.debug.warn("parsing {}, arg of type {} => {}\n", .{
|
|
||||||
@typeName(command_struct),
|
|
||||||
@typeName(@TypeOf(argument_value)),
|
|
||||||
argument_value,
|
|
||||||
});
|
|
||||||
|
|
||||||
@field(cmd.parameters, cmd_field.name) = argument_value;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
inline for (@typeInfo(command_struct).Struct.fields) |cmd_field| {
|
|
||||||
comptime {
|
|
||||||
if (std.mem.eql(u8, cmd_field.name, "base")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const arg = tok_it.next().?;
|
|
||||||
const argument_value = switch (cmd_field.field_type) {
|
|
||||||
usize => try std.fmt.parseInt(usize, arg, 10),
|
|
||||||
i32 => try std.fmt.parseInt(i32, arg, 10),
|
|
||||||
f32 => try std.fmt.parseFloat(f32, arg),
|
|
||||||
[]const u8 => try self.allocator.dupe(u8, arg),
|
|
||||||
else => @panic("Invalid parameter type (" ++ @typeName(cmd_field.field_type) ++ ") left on command struct " ++ @typeName(command_struct) ++ "."),
|
|
||||||
};
|
|
||||||
|
|
||||||
std.debug.warn("parsing {}, arg of type {} => {}\n", .{
|
|
||||||
@typeName(command_struct),
|
|
||||||
@typeName(@TypeOf(argument_value)),
|
|
||||||
argument_value,
|
|
||||||
});
|
|
||||||
|
|
||||||
@field(cmd, cmd_field.name) = argument_value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: crash when no arguments are left but we still need
|
||||||
|
// arguments...
|
||||||
|
const arg = tok_it.next().?;
|
||||||
|
const argument_value = switch (cmd_field.field_type) {
|
||||||
|
usize => try std.fmt.parseInt(usize, arg, 10),
|
||||||
|
i32 => try std.fmt.parseInt(i32, arg, 10),
|
||||||
|
f32 => try std.fmt.parseFloat(f32, arg),
|
||||||
|
[]const u8 => try self.allocator.dupe(u8, arg),
|
||||||
|
else => @panic("Invalid parameter type (" ++ @typeName(cmd_field.field_type) ++ ") left on command struct " ++ @typeName(command_struct) ++ "."),
|
||||||
|
};
|
||||||
|
|
||||||
|
std.debug.warn("parsing {}, arg of type {} => {}\n", .{ @typeName(command_struct), @typeName(@TypeOf(argument_value)), argument_value });
|
||||||
|
|
||||||
|
@field(cmd, cmd_field.name) = argument_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.base.tag = command_struct.base_tag;
|
cmd.base.tag = command_struct.base_tag;
|
||||||
|
|
110
src/runner.zig
110
src/runner.zig
|
@ -374,28 +374,6 @@ pub const Runner = struct {
|
||||||
try image.runPlugin("http://calf.sourceforge.net/plugins/VintageDelay", pos, params);
|
try image.runPlugin("http://calf.sourceforge.net/plugins/VintageDelay", pos, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn executeLV2Command(self: *@This(), command: var) !void {
|
|
||||||
const pos = plugin.Position{
|
|
||||||
.split = command.split,
|
|
||||||
.index = command.index,
|
|
||||||
};
|
|
||||||
|
|
||||||
var params = ParamList.init(self.allocator);
|
|
||||||
defer params.deinit();
|
|
||||||
|
|
||||||
const typ = @TypeOf(command);
|
|
||||||
|
|
||||||
inline for (@typeInfo(@TypeOf(command.parameters)).Struct.fields) |cmd_field| {
|
|
||||||
try params.append(plugin.Param{
|
|
||||||
.sym = cmd_field.name,
|
|
||||||
.value = @field(command.parameters, cmd_field.name),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var image = try self.getImage();
|
|
||||||
try image.runPlugin(typ.lv2_url, pos, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn newRunCommandSingle(
|
fn newRunCommandSingle(
|
||||||
self: *@This(),
|
self: *@This(),
|
||||||
cmd: lang.NewCommand,
|
cmd: lang.NewCommand,
|
||||||
|
@ -403,65 +381,15 @@ pub const Runner = struct {
|
||||||
) !void {
|
) !void {
|
||||||
comptime const typ = lang.NewCommand.tagToType(tag);
|
comptime const typ = lang.NewCommand.tagToType(tag);
|
||||||
const command = cmd.cast(typ).?;
|
const command = cmd.cast(typ).?;
|
||||||
inline for (@typeInfo(typ).Struct.decls) |decl| {
|
std.debug.warn("{} {}\n", .{ command.path.ptr, command.path.len });
|
||||||
comptime {
|
|
||||||
if (!std.mem.eql(u8, decl.name, "command_type")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const ctype = typ.command_type;
|
std.debug.warn("{}\n", .{command});
|
||||||
switch (ctype) {
|
|
||||||
.lv2_command => try self.executeLV2Command(command.*),
|
|
||||||
else => @panic("TODO support command type"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn newRunCommand(self: *@This(), cmd: lang.NewCommand) !void {
|
fn newRunCommand(self: *@This(), cmd: lang.NewCommand) !void {
|
||||||
// .load => try self.newRunCommandSingle(cmd, .load),
|
|
||||||
switch (cmd.tag) {
|
switch (cmd.tag) {
|
||||||
.load => {
|
.load => try self.newRunCommandSingle(cmd, .load),
|
||||||
const command = cmd.cast(lang.NewCommand.Load).?;
|
else => @panic("TODO"),
|
||||||
try self.loadCmd(command.path);
|
|
||||||
},
|
|
||||||
.quicksave => {
|
|
||||||
try self.quicksaveCmd();
|
|
||||||
},
|
|
||||||
.amp => try self.newRunCommandSingle(cmd, .amp),
|
|
||||||
|
|
||||||
.rflanger => try self.newRunCommandSingle(cmd, .rflanger),
|
|
||||||
.eq => try self.newRunCommandSingle(cmd, .eq),
|
|
||||||
.phaser => try self.newRunCommandSingle(cmd, .phaser),
|
|
||||||
// .mbeq => try self.newRunCommandSingle(cmd, .mbeq),
|
|
||||||
// .chorus => try self.newRunCommandSingle(cmd, .chorus),
|
|
||||||
// .pitchscaler => try self.newRunCommandSingle(cmd, .pitchscaler),
|
|
||||||
// .reverb => try self.newRunCommandSingle(cmd, .reverb),
|
|
||||||
// .highpass => try self.newRunCommandSingle(cmd, .highpass),
|
|
||||||
// .delay => try self.newRunCommandSingle(cmd, .delay),
|
|
||||||
// .vinyl => try self.newRunCommandSingle(cmd, .vinyl),
|
|
||||||
// .revdelay => try self.newRunCommandSingle(cmd, .revdelay),
|
|
||||||
// .gate => try self.newRunCommandSingle(cmd, .gate),
|
|
||||||
// .detune => try self.newRunCommandSingle(cmd, .detune),
|
|
||||||
// .overdrive => try self.newRunCommandSingle(cmd, .overdrive),
|
|
||||||
// .degrade => try self.newRunCommandSingle(cmd, .degrade),
|
|
||||||
// .repsycho => try self.newRunCommandSingle(cmd, .repsycho),
|
|
||||||
// .talkbox => try self.newRunCommandSingle(cmd, .talkbox),
|
|
||||||
// .dyncomp => try self.newRunCommandSingle(cmd, .dyncomp),
|
|
||||||
// .thruzero => try self.newRunCommandSingle(cmd, .thruzero),
|
|
||||||
// .foverdrive => try self.newRunCommandSingle(cmd, .foverdrive),
|
|
||||||
// .gverb => try self.newRunCommandSingle(cmd, .gverb),
|
|
||||||
// .invert => try self.newRunCommandSingle(cmd, .invert),
|
|
||||||
// .tapedelay => try self.newRunCommandSingle(cmd, .tapedelay),
|
|
||||||
// .moddelay => try self.newRunCommandSingle(cmd, .moddelay),
|
|
||||||
// .multichorus => try self.newRunCommandSingle(cmd, .multichorus),
|
|
||||||
// .saturator => try self.newRunCommandSingle(cmd, .saturator),
|
|
||||||
// .vintagedelay => try self.newRunCommandSingle(cmd, .vintagedelay),
|
|
||||||
|
|
||||||
else => {
|
|
||||||
std.debug.warn("TODO support {}\n", .{@tagName(cmd.tag)});
|
|
||||||
@panic("TODO support tag");
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -484,6 +412,28 @@ pub const Runner = struct {
|
||||||
.Quicksave => try self.quicksaveCmd(),
|
.Quicksave => try self.quicksaveCmd(),
|
||||||
.RunQS => try self.runQSCmd(cmd.args.items[0]),
|
.RunQS => try self.runQSCmd(cmd.args.items[0]),
|
||||||
|
|
||||||
|
.Amp => blk: {
|
||||||
|
const pos = try cmd.consumePosition();
|
||||||
|
try cmd.appendParam(¶ms, "gain");
|
||||||
|
try self.ampCmd(pos, params);
|
||||||
|
},
|
||||||
|
|
||||||
|
.RFlanger => blk: {
|
||||||
|
const pos = try cmd.consumePosition();
|
||||||
|
try cmd.appendParam(¶ms, "delay_depth_avg");
|
||||||
|
try cmd.appendParam(¶ms, "law_freq");
|
||||||
|
try self.rFlangerCmd(pos, params);
|
||||||
|
},
|
||||||
|
|
||||||
|
.Eq => blk: {
|
||||||
|
const pos = try cmd.consumePosition();
|
||||||
|
try cmd.appendParam(¶ms, "lo");
|
||||||
|
try cmd.appendParam(¶ms, "mid");
|
||||||
|
try cmd.appendParam(¶ms, "hi");
|
||||||
|
|
||||||
|
try self.eqCmd(pos, params);
|
||||||
|
},
|
||||||
|
|
||||||
.Phaser => blk: {
|
.Phaser => blk: {
|
||||||
const pos = try cmd.consumePosition();
|
const pos = try cmd.consumePosition();
|
||||||
|
|
||||||
|
@ -846,6 +796,14 @@ pub const Runner = struct {
|
||||||
) !void {
|
) !void {
|
||||||
for (cmds.items) |cmd| {
|
for (cmds.items) |cmd| {
|
||||||
cmd.print();
|
cmd.print();
|
||||||
|
|
||||||
|
switch (cmd.tag) {
|
||||||
|
.load => {
|
||||||
|
const proper_cmd = cmd.cast(lang.NewCommand.Load).?;
|
||||||
|
std.debug.warn("got load! {}\n", .{proper_cmd});
|
||||||
|
},
|
||||||
|
else => @panic("TODO"),
|
||||||
|
}
|
||||||
try self.newRunCommand(cmd.*);
|
try self.newRunCommand(cmd.*);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue