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); } }