From 5a38fd65c70e0c022324b6e1d8c4dbd26f48bce2 Mon Sep 17 00:00:00 2001 From: Emil Lerch Date: Wed, 15 Oct 2025 10:57:43 -0700 Subject: [PATCH] use replacements in a config file --- replacements.json | 16 +++++ src/main.zig | 178 ++++++++++++++++++++++++++++++++++------------ src/root.zig | 90 +++++++++++------------ 3 files changed, 189 insertions(+), 95 deletions(-) create mode 100644 replacements.json diff --git a/replacements.json b/replacements.json new file mode 100644 index 0000000..516ac29 --- /dev/null +++ b/replacements.json @@ -0,0 +1,16 @@ +{ + "late": "light", + "lake": "light", + "like": "light", + "life": "light", + "another": "on the", + "better": "bedroom", + "my": "light", + "night": "light", + "way": "light", + "me all": "emil", + "a meal": "emil", + "her": "turn", + "hearn": "turn", + "can't": "" +} diff --git a/src/main.zig b/src/main.zig index 22db5b2..30b5ae9 100644 --- a/src/main.zig +++ b/src/main.zig @@ -2,20 +2,53 @@ const builtin = @import("builtin"); const std = @import("std"); const pos = @import("pos"); -const word_replacements = std.StaticStringMap([]const u8).initComptime(.{ - .{ "late", "light" }, - .{ "lake", "light" }, - .{ "like", "light" }, - .{ "life", "light" }, - .{ "another", "on the" }, - .{ "better", "bedroom" }, - .{ "my", "light" }, - .{ "night", "light" }, - .{ "way", "light" }, - .{ "me all", "emil" }, - .{ "a meal", "emil" }, - .{ "her", "turn" }, -}); +fn loadReplacements(allocator: std.mem.Allocator) !std.StringHashMap([]const u8) { + var replacements = std.StringHashMap([]const u8).init(allocator); + + // Try current directory first + if (std.fs.cwd().openFile("replacements.json", .{})) |file| { + defer file.close(); + const content = try file.readToEndAlloc(allocator, 1024 * 1024); + defer allocator.free(content); + const parsed = try std.json.parseFromSlice(std.json.Value, allocator, content, .{}); + defer parsed.deinit(); + const obj = parsed.value.object; + var it = obj.iterator(); + while (it.next()) |entry| { + try replacements.put(try allocator.dupe(u8, entry.key_ptr.*), try allocator.dupe(u8, entry.value_ptr.*.string)); + } + return replacements; + } else |_| {} + + // Try XDG_CONFIG_HOME or HOME/.config + const config_dir = std.posix.getenv("XDG_CONFIG_HOME") orelse blk: { + const home = std.posix.getenv("HOME") orelse return error.NoConfigFound; + break :blk try std.fs.path.join(allocator, &.{ home, ".config" }); + }; + const config_path = if (std.posix.getenv("XDG_CONFIG_HOME") != null) + try std.fs.path.join(allocator, &.{ config_dir, "pos", "replacements.json" }) + else blk: { + defer allocator.free(config_dir); + break :blk try std.fs.path.join(allocator, &.{ config_dir, "pos", "replacements.json" }); + }; + defer allocator.free(config_path); + + if (std.fs.openFileAbsolute(config_path, .{})) |file| { + defer file.close(); + const content = try file.readToEndAlloc(allocator, 1024 * 1024); + defer allocator.free(content); + const parsed = try std.json.parseFromSlice(std.json.Value, allocator, content, .{}); + defer parsed.deinit(); + const obj = parsed.value.object; + var it = obj.iterator(); + while (it.next()) |entry| { + try replacements.put(try allocator.dupe(u8, entry.key_ptr.*), try allocator.dupe(u8, entry.value_ptr.*.string)); + } + return replacements; + } else |_| {} + + return error.NoConfigFound; +} const DeviceAction = enum { on, @@ -159,7 +192,7 @@ fn loadDevicesFromTxt(allocator: std.mem.Allocator, bin_dir: []const u8) !std.St var stderr_writer = std.fs.File.stderr().writer(&.{}); const stderr = &stderr_writer.interface; try stderr.print( - \\Error: could not load configuration. Please make sure controlData.json or + \\Error: could not load configuration. Please make sure controlData.json or \\devices.txt is available in the directory with this program , .{}); return error.ConfigurationNotAvailable; @@ -201,7 +234,7 @@ fn parseAction(action_words: [][]const u8) ?DeviceAction { return null; } -fn extractDevice(allocator: std.mem.Allocator, object_words: [][]const u8, devices: *std.StringHashMap([]const u8)) !?std.hash_map.StringHashMap([]const u8).Entry { +fn extractDevice(allocator: std.mem.Allocator, object_words: [][]const u8, devices: *std.StringHashMap([]const u8), replacements: *std.StringHashMap([]const u8)) !?std.hash_map.StringHashMap([]const u8).Entry { if (object_words.len == 0) return null; var total_size: usize = 0; @@ -237,7 +270,7 @@ fn extractDevice(allocator: std.mem.Allocator, object_words: [][]const u8, devic if (i >= total_words + 1) break; var buf: [256]u8 = undefined; const lower_string = std.ascii.lowerString(&buf, word); - const final_word = word_replacements.get(lower_string) orelse lower_string; + const final_word = replacements.get(lower_string) orelse lower_string; try writer.print("{s}{s}", .{ final_word, if (i < object_words.len - removed_words) " " else "", @@ -302,16 +335,27 @@ pub fn main() !u8 { const sentence_z = try allocator.dupeZ(u8, sentence_arg.?); defer allocator.free(sentence_z); - var tree = if (sentence_parse_only == .command) - parser.adaptiveCommandParse(sentence_z, word_replacements) catch { - std.log.err("Failed to parse sentence: {s}", .{sentence_z}); + var tree = if (sentence_parse_only == .command) blk: { + var replacements = loadReplacements(allocator) catch |err| { + stderr.print("Failed to load replacements configuration: {}\n", .{err}) catch {}; return 1; + }; + defer { + var iterator = replacements.iterator(); + while (iterator.next()) |entry| { + allocator.free(entry.key_ptr.*); + allocator.free(entry.value_ptr.*); + } + replacements.deinit(); } - else - parser.parse(sentence_z) catch { + break :blk parser.adaptiveCommandParse(sentence_z, &replacements) catch { std.log.err("Failed to parse sentence: {s}", .{sentence_z}); return 1; }; + } else parser.parse(sentence_z) catch { + std.log.err("Failed to parse sentence: {s}", .{sentence_z}); + return 1; + }; defer tree.deinit(); try stdout.print("{f}", .{tree}); @@ -331,7 +375,20 @@ pub fn main() !u8 { devices.deinit(); } - processCommand(allocator, args[1], &parser, &devices) catch |err| { + var replacements = loadReplacements(allocator) catch |err| { + stderr.print("Failed to load replacements configuration: {}\n", .{err}) catch {}; + return 1; + }; + defer { + var iterator = replacements.iterator(); + while (iterator.next()) |entry| { + allocator.free(entry.key_ptr.*); + allocator.free(entry.value_ptr.*); + } + replacements.deinit(); + } + + processCommand(allocator, args[1], &parser, &devices, &replacements) catch |err| { switch (err) { error.UnrecognizedSentence => { try stderr.print("Unrecognized sentence: {s}\n", .{args[1]}); @@ -344,9 +401,9 @@ pub fn main() !u8 { return 0; } -fn processCommand(allocator: std.mem.Allocator, sentence: [:0]const u8, parser: *pos.Parser, devices: *std.StringHashMap([]const u8)) !void { - var tree = parser.adaptiveCommandParse(sentence, word_replacements) catch |err| { - std.log.err("Failed to parse sentence with all replacements: {}\n", .{err}); +fn processCommand(allocator: std.mem.Allocator, sentence: [:0]const u8, parser: *pos.Parser, devices: *std.StringHashMap([]const u8), replacements: *std.StringHashMap([]const u8)) !void { + var tree = parser.adaptiveCommandParse(sentence, replacements) catch |err| { + std.log.err("Failed to parse sentence: {}\n", .{err}); return error.UnrecognizedSentence; }; defer tree.deinit(); @@ -379,7 +436,7 @@ fn processCommand(allocator: std.mem.Allocator, sentence: [:0]const u8, parser: std.log.debug("{s}]", .{aw.written()}); if (parseAction(action_words)) |action| { - if (try extractDevice(allocator, object_words, devices)) |entry| { + if (try extractDevice(allocator, object_words, devices, replacements)) |entry| { if (builtin.is_test) testSendCommand(entry, action) else @@ -391,7 +448,9 @@ fn processCommand(allocator: std.mem.Allocator, sentence: [:0]const u8, parser: return error.UnrecognizedSentence; } +// SAFETY: only used for testing, and in each case this is set before checking results var test_device_entry: std.hash_map.StringHashMap([]const u8).Entry = undefined; +// SAFETY: only used for testing, and in each case this is set before checking results var test_action: DeviceAction = undefined; fn testSendCommand(device_entry: std.hash_map.StringHashMap([]const u8).Entry, action: DeviceAction) void { test_device_entry = device_entry; @@ -400,12 +459,14 @@ fn testSendCommand(device_entry: std.hash_map.StringHashMap([]const u8).Entry, a test "extractDevice single word exact match" { var devices = std.StringHashMap([]const u8).init(std.testing.allocator); defer devices.deinit(); + var replacements = std.StringHashMap([]const u8).init(std.testing.allocator); + defer replacements.deinit(); try devices.put("kitchen", "192.168.1.100"); try devices.put("bedroom", "192.168.1.101"); var object_words = [_][]const u8{"kitchen"}; - const result = try extractDevice(std.testing.allocator, object_words[0..], &devices); + const result = try extractDevice(std.testing.allocator, object_words[0..], &devices, &replacements); try std.testing.expectEqualStrings("192.168.1.100", result.?.value_ptr.*); } @@ -413,11 +474,13 @@ test "extractDevice single word exact match" { test "extractDevice single word case insensitive" { var devices = std.StringHashMap([]const u8).init(std.testing.allocator); defer devices.deinit(); + var replacements = std.StringHashMap([]const u8).init(std.testing.allocator); + defer replacements.deinit(); try devices.put("kitchen", "192.168.1.100"); var object_words = [_][]const u8{"Kitchen"}; - const result = try extractDevice(std.testing.allocator, object_words[0..], &devices); + const result = try extractDevice(std.testing.allocator, object_words[0..], &devices, &replacements); try std.testing.expectEqualStrings("192.168.1.100", result.?.value_ptr.*); } @@ -425,11 +488,13 @@ test "extractDevice single word case insensitive" { test "extractDevice two words exact match" { var devices = std.StringHashMap([]const u8).init(std.testing.allocator); defer devices.deinit(); + var replacements = std.StringHashMap([]const u8).init(std.testing.allocator); + defer replacements.deinit(); try devices.put("bedroom light", "192.168.1.100"); var object_words = [_][]const u8{ "bedroom", "light" }; - const result = try extractDevice(std.testing.allocator, object_words[0..], &devices); + const result = try extractDevice(std.testing.allocator, object_words[0..], &devices, &replacements); try std.testing.expectEqualStrings("192.168.1.100", result.?.value_ptr.*); } @@ -437,11 +502,13 @@ test "extractDevice two words exact match" { test "extractDevice two words case insensitive" { var devices = std.StringHashMap([]const u8).init(std.testing.allocator); defer devices.deinit(); + var replacements = std.StringHashMap([]const u8).init(std.testing.allocator); + defer replacements.deinit(); try devices.put("bedroom light", "192.168.1.100"); var object_words = [_][]const u8{ "Bedroom", "Light" }; - const result = try extractDevice(std.testing.allocator, object_words[0..], &devices); + const result = try extractDevice(std.testing.allocator, object_words[0..], &devices, &replacements); try std.testing.expectEqualStrings("192.168.1.100", result.?.value_ptr.*); } @@ -449,11 +516,13 @@ test "extractDevice two words case insensitive" { test "extractDevice not found" { var devices = std.StringHashMap([]const u8).init(std.testing.allocator); defer devices.deinit(); + var replacements = std.StringHashMap([]const u8).init(std.testing.allocator); + defer replacements.deinit(); try devices.put("kitchen", "192.168.1.100"); var object_words = [_][]const u8{"bathroom"}; - const result = try extractDevice(std.testing.allocator, object_words[0..], &devices); + const result = try extractDevice(std.testing.allocator, object_words[0..], &devices, &replacements); try std.testing.expect(result == null); } @@ -461,9 +530,11 @@ test "extractDevice not found" { test "extractDevice empty words" { var devices = std.StringHashMap([]const u8).init(std.testing.allocator); defer devices.deinit(); + var replacements = std.StringHashMap([]const u8).init(std.testing.allocator); + defer replacements.deinit(); var object_words = [_][]const u8{}; - const result = try extractDevice(std.testing.allocator, object_words[0..], &devices); + const result = try extractDevice(std.testing.allocator, object_words[0..], &devices, &replacements); try std.testing.expect(result == null); } @@ -471,22 +542,27 @@ test "extractDevice empty words" { test "extractDevice fallback to shorter match" { var devices = std.StringHashMap([]const u8).init(std.testing.allocator); defer devices.deinit(); + var replacements = std.StringHashMap([]const u8).init(std.testing.allocator); + defer replacements.deinit(); try devices.put("kitchen", "192.168.1.100"); var object_words = [_][]const u8{ "kitchen", "ceiling", "light" }; - const result = try extractDevice(std.testing.allocator, object_words[0..], &devices); + const result = try extractDevice(std.testing.allocator, object_words[0..], &devices, &replacements); try std.testing.expectEqualStrings("192.168.1.100", result.?.value_ptr.*); } test "extractDevice word replacement lake to light" { var devices = std.StringHashMap([]const u8).init(std.testing.allocator); defer devices.deinit(); + var replacements = std.StringHashMap([]const u8).init(std.testing.allocator); + defer replacements.deinit(); + try replacements.put("lake", "light"); try devices.put("bedroom light", "192.168.1.100"); var object_words = [_][]const u8{ "bedroom", "lake" }; - const result = try extractDevice(std.testing.allocator, object_words[0..], &devices); + const result = try extractDevice(std.testing.allocator, object_words[0..], &devices, &replacements); try std.testing.expectEqualStrings("192.168.1.100", result.?.value_ptr.*); } @@ -494,17 +570,22 @@ test "extractDevice word replacement lake to light" { test "extractDevice word replacement like to light" { var devices = std.StringHashMap([]const u8).init(std.testing.allocator); defer devices.deinit(); + var replacements = std.StringHashMap([]const u8).init(std.testing.allocator); + defer replacements.deinit(); + try replacements.put("like", "light"); try devices.put("kitchen light", "192.168.1.101"); var object_words = [_][]const u8{ "kitchen", "like" }; - const result = try extractDevice(std.testing.allocator, object_words[0..], &devices); + const result = try extractDevice(std.testing.allocator, object_words[0..], &devices, &replacements); try std.testing.expectEqualStrings("192.168.1.101", result.?.value_ptr.*); } test "processCommand successful match" { var devices = std.StringHashMap([]const u8).init(std.testing.allocator); defer devices.deinit(); + var replacements = std.StringHashMap([]const u8).init(std.testing.allocator); + defer replacements.deinit(); try devices.put("kitchen", "192.168.1.100"); @@ -515,7 +596,7 @@ test "processCommand successful match" { const sentence_z = try std.testing.allocator.dupeZ(u8, sentence); defer std.testing.allocator.free(sentence_z); - try processCommand(std.testing.allocator, sentence_z, &parser, &devices); + try processCommand(std.testing.allocator, sentence_z, &parser, &devices, &replacements); try std.testing.expectEqualStrings("192.168.1.100", test_device_entry.value_ptr.*); try std.testing.expectEqual(DeviceAction.on, test_action); @@ -524,6 +605,9 @@ test "processCommand successful match" { test "processCommand with word replacement lake to light" { var devices = std.StringHashMap([]const u8).init(std.testing.allocator); defer devices.deinit(); + var replacements = std.StringHashMap([]const u8).init(std.testing.allocator); + defer replacements.deinit(); + try replacements.put("lake", "light"); try devices.put("kitchen light", "192.168.1.100"); @@ -534,7 +618,7 @@ test "processCommand with word replacement lake to light" { const sentence_z = try std.testing.allocator.dupeZ(u8, sentence); defer std.testing.allocator.free(sentence_z); - try processCommand(std.testing.allocator, sentence_z, &parser, &devices); + try processCommand(std.testing.allocator, sentence_z, &parser, &devices, &replacements); try std.testing.expectEqualStrings("192.168.1.100", test_device_entry.value_ptr.*); try std.testing.expectEqual(DeviceAction.on, test_action); @@ -543,6 +627,9 @@ test "processCommand with word replacement lake to light" { test "processCommand with word replacement like to light" { var devices = std.StringHashMap([]const u8).init(std.testing.allocator); defer devices.deinit(); + var replacements = std.StringHashMap([]const u8).init(std.testing.allocator); + defer replacements.deinit(); + try replacements.put("like", "light"); try devices.put("bedroom light", "192.168.1.101"); @@ -553,7 +640,7 @@ test "processCommand with word replacement like to light" { const sentence_z = try std.testing.allocator.dupeZ(u8, sentence); defer std.testing.allocator.free(sentence_z); - try processCommand(std.testing.allocator, sentence_z, &parser, &devices); + try processCommand(std.testing.allocator, sentence_z, &parser, &devices, &replacements); try std.testing.expectEqualStrings("192.168.1.101", test_device_entry.value_ptr.*); try std.testing.expectEqual(DeviceAction.off, test_action); @@ -561,6 +648,9 @@ test "processCommand with word replacement like to light" { test "processCommand with word replacement like to light - three words" { var devices = std.StringHashMap([]const u8).init(std.testing.allocator); defer devices.deinit(); + var replacements = std.StringHashMap([]const u8).init(std.testing.allocator); + defer replacements.deinit(); + try replacements.put("like", "light"); try devices.put("jack bedroom light", "192.168.1.101"); @@ -571,10 +661,7 @@ test "processCommand with word replacement like to light - three words" { const sentence_z = try std.testing.allocator.dupeZ(u8, sentence); defer std.testing.allocator.free(sentence_z); - // const ll = std.testing.log_level; - // std.testing.log_level = .debug; - // defer std.testing.log_level = ll; - try processCommand(std.testing.allocator, sentence_z, &parser, &devices); + try processCommand(std.testing.allocator, sentence_z, &parser, &devices, &replacements); try std.testing.expectEqualStrings("192.168.1.101", test_device_entry.value_ptr.*); try std.testing.expectEqual(DeviceAction.off, test_action); @@ -583,6 +670,8 @@ test "processCommand with word replacement like to light - three words" { test "processCommand no match found" { var devices = std.StringHashMap([]const u8).init(std.testing.allocator); defer devices.deinit(); + var replacements = std.StringHashMap([]const u8).init(std.testing.allocator); + defer replacements.deinit(); try devices.put("kitchen", "192.168.1.100"); @@ -593,6 +682,5 @@ test "processCommand no match found" { const sentence_z = try std.testing.allocator.dupeZ(u8, sentence); defer std.testing.allocator.free(sentence_z); - // Should return UnrecognizedSentence error - try std.testing.expectError(error.UnrecognizedSentence, processCommand(std.testing.allocator, sentence_z, &parser, &devices)); + try std.testing.expectError(error.UnrecognizedSentence, processCommand(std.testing.allocator, sentence_z, &parser, &devices, &replacements)); } diff --git a/src/root.zig b/src/root.zig index 79ca8ae..1ecaeca 100644 --- a/src/root.zig +++ b/src/root.zig @@ -362,15 +362,14 @@ pub const Parser = struct { _ = c.dictionary_delete(self.dict); } - fn applyReplacements(self: *Parser, sentence: []const u8, replacements: std.StaticStringMap([]const u8), final_buf: []u8) ![]const u8 { - _ = self; // we don't want to remove this completely, as there - // could be a time when we need to re-parse after replacement - const replacement_keys = replacements.keys(); - const replacement_values = replacements.values(); - + fn applyReplacements(self: *Parser, sentence: []const u8, replacements: *const std.StringHashMap([]const u8), final_buf: []u8) ![]const u8 { + _ = self; var altered = sentence; + var it = replacements.iterator(); - for (replacement_keys, replacement_values) |k, v| { + while (it.next()) |entry| { + const k = entry.key_ptr.*; + const v = entry.value_ptr.*; var k_buf: [256]u8 = undefined; var v_buf: [256]u8 = undefined; @@ -543,7 +542,7 @@ pub const Parser = struct { /// sentence is found, it will be returned, with the guarantee that /// sentenceObject and sentenceAction will return non-zero results. If that /// condition cannot be satisfied, error.NoValidParse will be returned - pub fn adaptiveCommandParse(self: *Parser, sentence: []const u8, replacements: std.StaticStringMap([]const u8)) !ParseTree { + pub fn adaptiveCommandParse(self: *Parser, sentence: []const u8, replacements: *const std.StringHashMap([]const u8)) !ParseTree { var final_buf: [1024]u8 = undefined; var altered = try self.applyReplacements(sentence, replacements, &final_buf); @@ -788,13 +787,13 @@ test "adaptiveCommandParse successful without replacements" { var parser = try Parser.init(std.testing.allocator); defer parser.deinit(); - const replacements = std.StaticStringMap([]const u8).initComptime(.{ - .{ "lake", "light" }, - .{ "like", "light" }, - }); + var replacements = std.StringHashMap([]const u8).init(std.testing.allocator); + defer replacements.deinit(); + try replacements.put("lake", "light"); + try replacements.put("like", "light"); const sentence = "turn on the kitchen light"; - var tree = try parser.adaptiveCommandParse(sentence, replacements); + var tree = try parser.adaptiveCommandParse(sentence, &replacements); defer tree.deinit(); const action_words = try tree.sentenceAction(); @@ -814,14 +813,14 @@ test "adaptiveCommandParse with word replacement" { var parser = try Parser.init(std.testing.allocator); defer parser.deinit(); - const replacements = std.StaticStringMap([]const u8).initComptime(.{ - .{ "lake", "light" }, - .{ "like", "light" }, - }); + var replacements = std.StringHashMap([]const u8).init(std.testing.allocator); + defer replacements.deinit(); + try replacements.put("lake", "light"); + try replacements.put("like", "light"); const sentence = "turn on the kitchen lake"; - var tree = try parser.adaptiveCommandParse(sentence, replacements); + var tree = try parser.adaptiveCommandParse(sentence, &replacements); defer tree.deinit(); const action_words = try tree.sentenceAction(); @@ -841,22 +840,16 @@ test "adaptiveCommandParse no valid parse" { var parser = try Parser.init(std.testing.allocator); defer parser.deinit(); - const replacements = std.StaticStringMap([]const u8).initComptime(.{ - .{ "lake", "light" }, - .{ "like", "light" }, - }); + var replacements = std.StringHashMap([]const u8).init(std.testing.allocator); + defer replacements.deinit(); + try replacements.put("lake", "light"); + try replacements.put("like", "light"); const sentence = "xyz abc def"; - // const ll = std.testing.log_level; - // defer std.testing.log_level = ll; - // std.testing.log_level = .debug; try std.testing.expectError( error.SentenceEmptyAfterNullRemoval, - parser.adaptiveCommandParse( - sentence, - replacements, - ), + parser.adaptiveCommandParse(sentence, &replacements), ); } @@ -864,17 +857,14 @@ test "adaptiveCommandParse with word replacement and null removal" { var parser = try Parser.init(std.testing.allocator); defer parser.deinit(); - const replacements = std.StaticStringMap([]const u8).initComptime(.{ - .{ "lake", "light" }, - .{ "like", "light" }, - }); + var replacements = std.StringHashMap([]const u8).init(std.testing.allocator); + defer replacements.deinit(); + try replacements.put("lake", "light"); + try replacements.put("like", "light"); const sentence = "alexa turn on the kitchen lake"; - // const ll = std.testing.log_level; - // defer std.testing.log_level = ll; - // std.testing.log_level = .debug; - var tree = try parser.adaptiveCommandParse(sentence, replacements); + var tree = try parser.adaptiveCommandParse(sentence, &replacements); defer tree.deinit(); const action_words = try tree.sentenceAction(); @@ -893,12 +883,12 @@ test "applyReplacements basic replacement" { var parser = try Parser.init(std.testing.allocator); defer parser.deinit(); - const replacements = std.StaticStringMap([]const u8).initComptime(.{ - .{ "lake", "light" }, - }); + var replacements = std.StringHashMap([]const u8).init(std.testing.allocator); + defer replacements.deinit(); + try replacements.put("lake", "light"); var final_buf: [1024]u8 = undefined; - const result = try parser.applyReplacements("turn on the kitchen lake", replacements, &final_buf); + const result = try parser.applyReplacements("turn on the kitchen lake", &replacements, &final_buf); try std.testing.expectEqualStrings("turn on the kitchen light", result); } @@ -906,13 +896,13 @@ test "applyReplacements multiple replacements" { var parser = try Parser.init(std.testing.allocator); defer parser.deinit(); - const replacements = std.StaticStringMap([]const u8).initComptime(.{ - .{ "lake", "light" }, - .{ "kitchen", "bedroom" }, - }); + var replacements = std.StringHashMap([]const u8).init(std.testing.allocator); + defer replacements.deinit(); + try replacements.put("lake", "light"); + try replacements.put("kitchen", "bedroom"); var final_buf: [1024]u8 = undefined; - const result = try parser.applyReplacements("turn on the kitchen lake", replacements, &final_buf); + const result = try parser.applyReplacements("turn on the kitchen lake", &replacements, &final_buf); try std.testing.expectEqualStrings("turn on the bedroom light", result); } @@ -920,12 +910,12 @@ test "applyReplacements empty after replacement" { var parser = try Parser.init(std.testing.allocator); defer parser.deinit(); - const replacements = std.StaticStringMap([]const u8).initComptime(.{ - .{ "test", "" }, - }); + var replacements = std.StringHashMap([]const u8).init(std.testing.allocator); + defer replacements.deinit(); + try replacements.put("test", ""); var final_buf: [1024]u8 = undefined; - try std.testing.expectError(error.SentenceEmptyAfterReplacements, parser.applyReplacements("test", replacements, &final_buf)); + try std.testing.expectError(error.SentenceEmptyAfterReplacements, parser.applyReplacements("test", &replacements, &final_buf)); } test "removeNullWords no nulls" {