diff --git a/src/lang.zig b/src/lang.zig index 3b65c9d..88dcda1 100644 --- a/src/lang.zig +++ b/src/lang.zig @@ -58,93 +58,27 @@ pub const Type = enum { }; pub const NewCommand = 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 Noop = struct {}; pub const Load = struct { - pub const base_tag = Tag.load; - base: NewCommand, path: []const u8, }; - pub const Quicksave = struct { - pub const base_tag = Tag.quicksave; - base: NewCommand, - }; + pub const Quicksave = struct {}; 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: f32 + gain: f64 }; pub const RFlanger = struct { - pub const base_tag = Tag.rflanger; pub const base_type = Type.lv2_command; - base: NewCommand, - delay_depth_avg: f32, - law_freq: f32, + delay_depth_avg: f64, + law_freq: f64, }; }; @@ -249,7 +183,7 @@ pub const Command = struct { } }; -pub const CommandList = std.ArrayList(NewCommand); +pub const CommandList = std.ArrayList(Command); pub const ArgList = std.ArrayList([]const u8); pub const KeywordMap = std.StringHashMap(CommandType); @@ -280,14 +214,6 @@ 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; @@ -371,42 +297,9 @@ 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) !CommandList { + 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| { @@ -417,7 +310,7 @@ pub const Lang = struct { if (stmt.len == 0) continue; if (std.mem.startsWith(u8, stmt, "#")) continue; - // TODO better tokenizer instead of just tokenize(" ")...maybe???? + // TODO better tokenizer instead of just tokenize(" "); var tok_it = std.mem.tokenize(stmt, " "); var cmd_opt = tok_it.next(); @@ -425,47 +318,32 @@ pub const Lang = struct { self.doError("No command given", .{}); continue; } - const command_string = cmd_opt.?; + var command = cmd_opt.?; - 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 { - for (struct_name) |c, i| { - lowered_command_name[i] = std.ascii.toLower(c); - } - } - - // 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; - const cmd_struct_type = cmd_struct_decl.data.Type; - try self.parseCommandArguments(cmd_struct_type, &tok_it, &cmds); - } - } - - if (!found) { - self.doError("Unknown command '{}' ({})", .{ command_string, command_string.len }); + 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 }); continue; } - // try cmds.append(cmd); + 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); } if (self.has_error) return ParseError.ParseFail; diff --git a/src/main.zig b/src/main.zig index 9971f24..04bcf2d 100644 --- a/src/main.zig +++ b/src/main.zig @@ -185,8 +185,7 @@ 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")) { - @panic("TODO bring repl back"); - // return try doRepl(allocator, &args_it); + 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 bde8eb9..6276954 100644 --- a/src/runner.zig +++ b/src/runner.zig @@ -374,8 +374,6 @@ 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(); @@ -777,9 +775,14 @@ pub const Runner = struct { cmds: lang.CommandList, debug_flag: bool, ) !void { - for (cmds.items) |cmd| { - // if (debug_flag) const_cmd.print(); - try self.newRunCommand(cmd); + 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); } } };