From 46fd8f585cf4ce8cb4ae163e445bd44ce1e456b4 Mon Sep 17 00:00:00 2001 From: Emil Lerch Date: Wed, 24 Sep 2025 10:31:34 -0700 Subject: [PATCH] avoid memory corruption with multiple replacements, deinit fixes, clean up output --- src/root.zig | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/src/root.zig b/src/root.zig index 753df65..fd98a36 100644 --- a/src/root.zig +++ b/src/root.zig @@ -365,7 +365,7 @@ pub const Parser = struct { /// sentenceObject and sentenceAction will return non-zero results. If that /// condition cannot be satisfied, error.NoValidParse will be returned pub fn adaptiveParse(self: *Parser, sentence: [:0]const u8, replacements: std.StaticStringMap([]const u8)) !ParseTree { - var altered_buf: [1024]u8 = undefined; + var final_buf: [1024]u8 = undefined; const replacement_keys = replacements.keys(); const replacement_values = replacements.values(); @@ -374,6 +374,7 @@ pub const Parser = struct { // Step 1: Replacements for (replacement_keys, replacement_values) |key, value| { + var altered_buf: [1024]u8 = undefined; const altered_size = std.mem.replacementSize( u8, altered, @@ -394,7 +395,8 @@ pub const Parser = struct { ); altered_buf[altered_size] = 0; // add sentinel - altered = altered_buf[0..altered_size :0]; + @memcpy(final_buf[0 .. altered_size + 1], altered_buf[0 .. altered_size + 1]); + altered = final_buf[0..altered_size :0]; if (replacement_count > 0) // we have altered the deal. Pray we don't alter it further @@ -406,13 +408,19 @@ pub const Parser = struct { }); } var tree = self.parse(altered) catch |err| { - if (shouldLog()) - std.log.err("Failed to parse sentence: {}\n\t{s}", .{ err, altered }); + if (shouldLog()) { + if (altered.len > 0) + std.log.err("Failed to parse sentence: {}\n\t{s}", .{ err, altered }) + else + std.log.err("Sentence is empty: not parsing", .{}); + } // continue; return err; }; + var tree_ptr: ?*ParseTree = &tree; + errdefer if (tree_ptr) |p| p.deinit(); - std.log.debug("adaptiveParse (step 1 - replacements):\n\toriginal:\n\t\t{s}\n\taltered:\n\t\t{s}\n{f}", .{ + std.log.debug("adaptiveCommandParse (step 1 - replacements):\n\toriginal:\n\t\t{s}\n\taltered:\n\t\t{s}\n{f}", .{ sentence, altered, tree, @@ -424,6 +432,7 @@ pub const Parser = struct { nulls_removed = false; for (tree.words) |word| { if (std.mem.indexOf(u8, word, "[?]")) |i| { + var altered_buf: [1024]u8 = undefined; nulls_removed = true; // We need to alter this further const trimmed = word[0..i]; @@ -436,8 +445,13 @@ pub const Parser = struct { ); const len = altered.len - (removals * trimmed.len); altered_buf[len] = 0; - altered = altered_buf[0..len :0]; + @memcpy(final_buf[0 .. len + 1], altered_buf[0 .. len + 1]); + altered = final_buf[0..len :0]; + if (altered.len == 0) { + std.log.info("Removed null word '{s}' in sentence {d} time(s). Sentence now empty", .{ trimmed, removals }); + return error.SentenceEmptyAfterNullRemoval; + } std.log.info("Removed null word '{s}' in sentence {d} time(s). Sentence now:\n\t{s}", .{ trimmed, removals, @@ -446,11 +460,13 @@ pub const Parser = struct { // Retry parsing with the word removed tree.deinit(); tree = self.parse(altered) catch |err| { + tree_ptr = null; if (shouldLog()) std.log.err("Failed to parse altered sentence: {}\n\t{s}", .{ err, altered }); // continue; return err; }; + tree_ptr = &tree; break; // we will remove these words conservatively... } } @@ -475,9 +491,9 @@ pub const Parser = struct { // Validate that we can extract action and object before returning const action_words = tree.sentenceAction() catch |err| { - if (shouldLog()) - std.log.err("Failed to extract action: {}\n", .{err}); - tree.deinit(); + // This is the first catch, so we don't want to log here as it + // gets super noisy + std.log.debug("Failed to extract action: {}\n", .{err}); return err; // continue; }; @@ -485,7 +501,6 @@ pub const Parser = struct { if (action_words.len == 0) { std.log.info("Failed to extract action from sentence", .{}); - tree.deinit(); return error.SentenceActionNotFound; // continue; } @@ -493,7 +508,6 @@ pub const Parser = struct { const object_words = tree.sentenceObject() catch |err| { if (shouldLog()) std.log.err("Failed to extract object: {}\n", .{err}); - tree.deinit(); // continue; return err; }; @@ -501,7 +515,6 @@ pub const Parser = struct { if (object_words.len == 0) { std.log.info("Failed to extract object from sentence", .{}); - tree.deinit(); // continue; return error.SentenceObjectNotFound; }