diff --git a/src/image.zig b/src/image.zig index e031c5a..17a545d 100644 --- a/src/image.zig +++ b/src/image.zig @@ -193,7 +193,7 @@ pub const Image = struct { sseek(out_file, start); while (i <= end) : (i += buf.len) { - std.debug.warn("i={}, buf.len={}, end={}\n", i, buf.len, end); + std.debug.warn("\t\ti={}, buf.len={}, end={}\n", i, buf.len, end); sseek(self.sndfile, i); sseek(out_file, i); diff --git a/src/lang.zig b/src/lang.zig index cf5b1e6..79d38d0 100644 --- a/src/lang.zig +++ b/src/lang.zig @@ -6,6 +6,8 @@ pub const ParseError = error{ NoCommandGiven, UnknownCommand, ArgRequired, + FloatParseFail, + ParseFail, }; pub const CommandType = enum { @@ -232,10 +234,64 @@ pub const Lang = struct { return null; } + fn expectAny(self: *Lang, count: usize, args: ArgList) !void { + if (args.len != count) { + std.debug.warn("expected {} arguments, found {}\n", count, args.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.len != count) { + std.debug.warn("expected {} arguments, found {}\n", count, args.len); + return error.ArgRequired; + } + + while (i < count) : (i += 1) { + var arg = args.at(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(15, 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(5, 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), + else => std.debug.warn("WARN unchecked command {}\n", cmd.command), + } + } + pub fn parse(self: *Lang, data: []const u8) !CommandList { var splitted_it = std.mem.separate(data, ";"); try self.fillKeywords(); var cmds = CommandList.init(self.allocator); + var idx: usize = 0; while (splitted_it.next()) |stmt_orig| { var stmt = std.mem.trimRight(u8, stmt_orig, "\n"); @@ -261,15 +317,22 @@ pub const Lang = struct { } var args = ArgList.init(self.allocator); - while (tok_it.next()) |arg| { try args.append(arg); } // construct final Command based on command var cmd_ptr = try self.allocator.create(Command); + errdefer self.allocator.destroy(cmd_ptr); + cmd_ptr.* = Command{ .command = ctype, .args = args }; + self.validateCommand(cmd_ptr) catch |err| { + std.debug.warn("error at line {}: {}\n", idx, err); + return ParseError.ParseFail; + }; + try cmds.append(cmd_ptr); + idx += 1; } return cmds;