From 27f499dfbb2dd926611865babbab90e58a96dcb5 Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 30 May 2020 17:48:14 -0300 Subject: [PATCH 1/4] add basics of generic command parsing --- src/lang.zig | 63 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/src/lang.zig b/src/lang.zig index 88dcda1..2b3c003 100644 --- a/src/lang.zig +++ b/src/lang.zig @@ -318,32 +318,51 @@ pub const Lang = struct { self.doError("No command given", .{}); continue; } - var command = cmd_opt.?; + const command_string = cmd_opt.?; - var ctype_opt = self.getCommand(command); - var ctype: CommandType = undefined; - if (ctype_opt) |ctype_val| { - ctype = ctype_val; - } else { - self.doError("Unknown command '{}' ({})", .{ command, command.len }); + var found: bool = false; + + inline for (@typeInfo(NewCommand).Struct.decls) |cmd_struct_decl| { + 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); + } + } + + if (std.mem.eql(u8, &lowered_command_name, command_string)) { + found = true; + + // Based on the command struct fields, we need to decide + // how to actually get its arguments and creating the struct + // itself. + + const command_struct = cmd_struct_decl.data.Type; + var cmd: command_struct = undefined; + inline for (@typeInfo(command_struct).Struct.fields) |cmd_field| { + + // TODO: crash when no arguments are left but we still need + // arguments... + const argument = 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), + else => @panic("Invalid parameter type (" ++ @typeName(cmd_field.field_type) ++ ") left on command struct " ++ @typeName(command_struct) ++ "."), + }; + + @field(cmd, cmd_field.name) = argument_value; + } + } + } + + if (!found) { + self.doError("Unknown command '{}' ({})", .{ command_string, command_string.len }); continue; } - var args = ArgList.init(self.allocator); - errdefer args.deinit(); - - while (tok_it.next()) |arg| { - try args.append(arg); - } - - // construct final Command based on command - var cmd = Command{ .command = ctype, .args = args }; - self.validateCommand(cmd) catch |err| { - //self.doError("error validating command '{}': {}", command, err); - continue; - }; - - try cmds.append(cmd); + // try cmds.append(cmd); } if (self.has_error) return ParseError.ParseFail; From 154032f9b17c0a21cf8002134a61865e32b52e03 Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 30 May 2020 22:25:30 -0300 Subject: [PATCH 2/4] revamp command structure for tags and base types --- src/lang.zig | 155 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 129 insertions(+), 26 deletions(-) diff --git a/src/lang.zig b/src/lang.zig index 2b3c003..8a2a9ce 100644 --- a/src/lang.zig +++ b/src/lang.zig @@ -58,25 +58,91 @@ pub const Type = enum { }; pub const NewCommand = struct { - pub const Noop = struct {}; + tag: Tag, + + pub const Tag = enum { + noop, + load, + quicksave, + runqs, + + amp, + rflanger, + eq, + phaser, + mbeq, + chorus, + pitchscaler, + reverb, + highpass, + delay, + vinyl, + revdelay, + gate, + detune, + overdrive, + degrade, + repsycho, + talkbox, + dyncomp, + thruzero, + foverdrive, + gverb, + invert, + tapedelay, + moddelay, + multichorus, + saturator, + vintagedelay, + + noise, + wildnoise, + write, + embed, + + rotate, + }; + + pub fn cast(base: *@This(), comptime T: type) ?*T { + if (base.tag != T.base_tag) + return null; + + return @fieldParentPtr(T, "base", base); + } + + pub const Noop = struct { + pub const base_tag = Tag.noop; + base: NewCommand, + }; pub const Load = struct { + pub const base_tag = Tag.load; + base: NewCommand, path: []const u8, }; - pub const Quicksave = struct {}; + pub const Quicksave = struct { + pub const base_tag = Tag.quicksave; + base: NewCommand, + }; pub const RunQS = struct { + pub const base_tag = Tag.runqs; program: []const u8, + base: NewCommand, }; pub const Amp = struct { + pub const base_tag = Tag.amp; pub const base_type = Type.lv2_command; + base: NewCommand, gain: f64 }; pub const RFlanger = struct { + pub const base_tag = Tag.rflanger; pub const base_type = Type.lv2_command; + base: NewCommand, delay_depth_avg: f64, law_freq: f64, }; @@ -183,7 +249,7 @@ pub const Command = struct { } }; -pub const CommandList = std.ArrayList(Command); +pub const CommandList = std.ArrayList(NewCommand); pub const ArgList = std.ArrayList([]const u8); pub const KeywordMap = std.StringHashMap(CommandType); @@ -214,6 +280,14 @@ pub const Lang = struct { 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; @@ -297,9 +371,42 @@ pub const Lang = struct { self.has_error = true; } + fn parseCommandArguments( + self: *@This(), + comptime command_struct: type, + tok_it: *std.mem.TokenIterator, + commands: *CommandList, + ) !void { + // Based on the command struct fields, we can parse the arguments. + var cmd = try self.allocator.create(command_struct); + + inline for (@typeInfo(command_struct).Struct.fields) |cmd_field| { + comptime { + if (std.mem.eql(u8, cmd_field.name, "base")) { + continue; + } + } + + // 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 => arg, + else => @panic("Invalid parameter type (" ++ @typeName(cmd_field.field_type) ++ ") left on command struct " ++ @typeName(command_struct) ++ "."), + }; + + @field(cmd, cmd_field.name) = argument_value; + } + + try commands.append(cmd.base); + } + pub fn parse(self: *Lang, data: []const u8) ParseError!CommandList { var splitted_it = std.mem.split(data, ";"); - try self.fillKeywords(); + // try self.fillKeywords(); var cmds = CommandList.init(self.allocator); while (splitted_it.next()) |stmt_orig| { @@ -310,7 +417,7 @@ pub const Lang = struct { if (stmt.len == 0) continue; if (std.mem.startsWith(u8, stmt, "#")) continue; - // TODO better tokenizer instead of just tokenize(" "); + // TODO better tokenizer instead of just tokenize(" ")...maybe???? var tok_it = std.mem.tokenize(stmt, " "); var cmd_opt = tok_it.next(); @@ -323,6 +430,14 @@ pub const Lang = struct { var found: bool = false; 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 { @@ -331,29 +446,17 @@ pub const Lang = struct { } } + // if we have a match, we know the proper struct type + // to use. this actually works compared to storing command_struct + // in a variable because then that variable must be comptime. + + // the drawback of this approach is that our emitted code is basically linear + // because we don't use the hashmap anymore. maybe #5359 can help. + if (std.mem.eql(u8, &lowered_command_name, command_string)) { found = true; - - // Based on the command struct fields, we need to decide - // how to actually get its arguments and creating the struct - // itself. - - const command_struct = cmd_struct_decl.data.Type; - var cmd: command_struct = undefined; - inline for (@typeInfo(command_struct).Struct.fields) |cmd_field| { - - // TODO: crash when no arguments are left but we still need - // arguments... - const argument = 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), - else => @panic("Invalid parameter type (" ++ @typeName(cmd_field.field_type) ++ ") left on command struct " ++ @typeName(command_struct) ++ "."), - }; - - @field(cmd, cmd_field.name) = argument_value; - } + const cmd_struct_type = cmd_struct_decl.data.Type; + try self.parseCommandArguments(cmd_struct_type, &tok_it, &cmds); } } From f20079b807f5d0a015024f8a9242da21706787c1 Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 30 May 2020 22:25:43 -0300 Subject: [PATCH 3/4] disable codepaths not supporting new commands --- src/main.zig | 3 ++- src/runner.zig | 14 ++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/main.zig b/src/main.zig index 04bcf2d..9971f24 100644 --- a/src/main.zig +++ b/src/main.zig @@ -185,7 +185,8 @@ pub fn main() !void { const scri_path = try (args_it.next(allocator) orelse @panic("expected scri path or 'repl'")); if (std.mem.eql(u8, scri_path, "repl")) { - return try doRepl(allocator, &args_it); + @panic("TODO bring repl back"); + // return try doRepl(allocator, &args_it); } var file = try std.fs.cwd().openFile(scri_path, .{}); diff --git a/src/runner.zig b/src/runner.zig index 6276954..5496e8a 100644 --- a/src/runner.zig +++ b/src/runner.zig @@ -374,6 +374,8 @@ pub const Runner = struct { try image.runPlugin("http://calf.sourceforge.net/plugins/VintageDelay", pos, params); } + fn newRunCommand(self: *@This(), cmd: lang.NewCommand) !void {} + fn runCommand(self: *Runner, cmd: *lang.Command) !void { var params = ParamList.init(self.allocator); defer params.deinit(); @@ -775,14 +777,10 @@ pub const Runner = struct { cmds: lang.CommandList, debug_flag: bool, ) !void { - for (cmds.items) |const_cmd| { - if (debug_flag) const_cmd.print(); - - // copy the command so we own its memory - var cmd = try const_cmd.copy(self.allocator); - defer self.allocator.destroy(cmd); - - try self.runCommand(cmd); + for (cmds.items) |cmd| { + // TODO + // if (debug_flag) const_cmd.print(); + try self.newRunCommand(cmd); } } }; From c3b83c3e4b5d1a35ce7b740856b97596641303f7 Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 30 May 2020 22:29:13 -0300 Subject: [PATCH 4/4] replace f64 to f32 on some commands --- src/lang.zig | 8 ++++---- src/runner.zig | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/lang.zig b/src/lang.zig index 8a2a9ce..3b65c9d 100644 --- a/src/lang.zig +++ b/src/lang.zig @@ -136,15 +136,15 @@ pub const NewCommand = struct { pub const base_tag = Tag.amp; pub const base_type = Type.lv2_command; base: NewCommand, - gain: f64 + gain: f32 }; pub const RFlanger = struct { pub const base_tag = Tag.rflanger; pub const base_type = Type.lv2_command; base: NewCommand, - delay_depth_avg: f64, - law_freq: f64, + delay_depth_avg: f32, + law_freq: f32, }; }; @@ -404,7 +404,7 @@ pub const Lang = struct { try commands.append(cmd.base); } - pub fn parse(self: *Lang, data: []const u8) ParseError!CommandList { + pub fn parse(self: *Lang, data: []const u8) !CommandList { var splitted_it = std.mem.split(data, ";"); // try self.fillKeywords(); var cmds = CommandList.init(self.allocator); diff --git a/src/runner.zig b/src/runner.zig index 5496e8a..bde8eb9 100644 --- a/src/runner.zig +++ b/src/runner.zig @@ -778,7 +778,6 @@ pub const Runner = struct { debug_flag: bool, ) !void { for (cmds.items) |cmd| { - // TODO // if (debug_flag) const_cmd.print(); try self.newRunCommand(cmd); }