use replacements in a config file

This commit is contained in:
Emil Lerch 2025-10-15 10:57:43 -07:00
parent 5167397aa6
commit 5a38fd65c7
Signed by: lobo
GPG key ID: A7B62D657EF764F8
3 changed files with 189 additions and 95 deletions

16
replacements.json Normal file
View file

@ -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": ""
}

View file

@ -2,20 +2,53 @@ const builtin = @import("builtin");
const std = @import("std"); const std = @import("std");
const pos = @import("pos"); const pos = @import("pos");
const word_replacements = std.StaticStringMap([]const u8).initComptime(.{ fn loadReplacements(allocator: std.mem.Allocator) !std.StringHashMap([]const u8) {
.{ "late", "light" }, var replacements = std.StringHashMap([]const u8).init(allocator);
.{ "lake", "light" },
.{ "like", "light" }, // Try current directory first
.{ "life", "light" }, if (std.fs.cwd().openFile("replacements.json", .{})) |file| {
.{ "another", "on the" }, defer file.close();
.{ "better", "bedroom" }, const content = try file.readToEndAlloc(allocator, 1024 * 1024);
.{ "my", "light" }, defer allocator.free(content);
.{ "night", "light" }, const parsed = try std.json.parseFromSlice(std.json.Value, allocator, content, .{});
.{ "way", "light" }, defer parsed.deinit();
.{ "me all", "emil" }, const obj = parsed.value.object;
.{ "a meal", "emil" }, var it = obj.iterator();
.{ "her", "turn" }, 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 { const DeviceAction = enum {
on, 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(&.{}); var stderr_writer = std.fs.File.stderr().writer(&.{});
const stderr = &stderr_writer.interface; const stderr = &stderr_writer.interface;
try stderr.print( 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 \\devices.txt is available in the directory with this program
, .{}); , .{});
return error.ConfigurationNotAvailable; return error.ConfigurationNotAvailable;
@ -201,7 +234,7 @@ fn parseAction(action_words: [][]const u8) ?DeviceAction {
return null; 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; if (object_words.len == 0) return null;
var total_size: usize = 0; 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; if (i >= total_words + 1) break;
var buf: [256]u8 = undefined; var buf: [256]u8 = undefined;
const lower_string = std.ascii.lowerString(&buf, word); 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}", .{ try writer.print("{s}{s}", .{
final_word, final_word,
if (i < object_words.len - removed_words) " " else "", 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.?); const sentence_z = try allocator.dupeZ(u8, sentence_arg.?);
defer allocator.free(sentence_z); defer allocator.free(sentence_z);
var tree = if (sentence_parse_only == .command) var tree = if (sentence_parse_only == .command) blk: {
parser.adaptiveCommandParse(sentence_z, word_replacements) catch { var replacements = loadReplacements(allocator) catch |err| {
std.log.err("Failed to parse sentence: {s}", .{sentence_z}); stderr.print("Failed to load replacements configuration: {}\n", .{err}) catch {};
return 1; return 1;
};
defer {
var iterator = replacements.iterator();
while (iterator.next()) |entry| {
allocator.free(entry.key_ptr.*);
allocator.free(entry.value_ptr.*);
}
replacements.deinit();
} }
else break :blk parser.adaptiveCommandParse(sentence_z, &replacements) catch {
parser.parse(sentence_z) catch {
std.log.err("Failed to parse sentence: {s}", .{sentence_z}); std.log.err("Failed to parse sentence: {s}", .{sentence_z});
return 1; return 1;
}; };
} else parser.parse(sentence_z) catch {
std.log.err("Failed to parse sentence: {s}", .{sentence_z});
return 1;
};
defer tree.deinit(); defer tree.deinit();
try stdout.print("{f}", .{tree}); try stdout.print("{f}", .{tree});
@ -331,7 +375,20 @@ pub fn main() !u8 {
devices.deinit(); 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) { switch (err) {
error.UnrecognizedSentence => { error.UnrecognizedSentence => {
try stderr.print("Unrecognized sentence: {s}\n", .{args[1]}); try stderr.print("Unrecognized sentence: {s}\n", .{args[1]});
@ -344,9 +401,9 @@ pub fn main() !u8 {
return 0; return 0;
} }
fn processCommand(allocator: std.mem.Allocator, sentence: [:0]const u8, parser: *pos.Parser, devices: *std.StringHashMap([]const u8)) !void { 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, word_replacements) catch |err| { var tree = parser.adaptiveCommandParse(sentence, replacements) catch |err| {
std.log.err("Failed to parse sentence with all replacements: {}\n", .{err}); std.log.err("Failed to parse sentence: {}\n", .{err});
return error.UnrecognizedSentence; return error.UnrecognizedSentence;
}; };
defer tree.deinit(); defer tree.deinit();
@ -379,7 +436,7 @@ fn processCommand(allocator: std.mem.Allocator, sentence: [:0]const u8, parser:
std.log.debug("{s}]", .{aw.written()}); std.log.debug("{s}]", .{aw.written()});
if (parseAction(action_words)) |action| { 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) if (builtin.is_test)
testSendCommand(entry, action) testSendCommand(entry, action)
else else
@ -391,7 +448,9 @@ fn processCommand(allocator: std.mem.Allocator, sentence: [:0]const u8, parser:
return error.UnrecognizedSentence; 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; 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; var test_action: DeviceAction = undefined;
fn testSendCommand(device_entry: std.hash_map.StringHashMap([]const u8).Entry, action: DeviceAction) void { fn testSendCommand(device_entry: std.hash_map.StringHashMap([]const u8).Entry, action: DeviceAction) void {
test_device_entry = device_entry; 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" { test "extractDevice single word exact match" {
var devices = std.StringHashMap([]const u8).init(std.testing.allocator); var devices = std.StringHashMap([]const u8).init(std.testing.allocator);
defer devices.deinit(); 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("kitchen", "192.168.1.100");
try devices.put("bedroom", "192.168.1.101"); try devices.put("bedroom", "192.168.1.101");
var object_words = [_][]const u8{"kitchen"}; 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.*); 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" { test "extractDevice single word case insensitive" {
var devices = std.StringHashMap([]const u8).init(std.testing.allocator); var devices = std.StringHashMap([]const u8).init(std.testing.allocator);
defer devices.deinit(); 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("kitchen", "192.168.1.100");
var object_words = [_][]const u8{"Kitchen"}; 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.*); 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" { test "extractDevice two words exact match" {
var devices = std.StringHashMap([]const u8).init(std.testing.allocator); var devices = std.StringHashMap([]const u8).init(std.testing.allocator);
defer devices.deinit(); 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"); try devices.put("bedroom light", "192.168.1.100");
var object_words = [_][]const u8{ "bedroom", "light" }; 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.*); 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" { test "extractDevice two words case insensitive" {
var devices = std.StringHashMap([]const u8).init(std.testing.allocator); var devices = std.StringHashMap([]const u8).init(std.testing.allocator);
defer devices.deinit(); 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"); try devices.put("bedroom light", "192.168.1.100");
var object_words = [_][]const u8{ "Bedroom", "Light" }; 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.*); 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" { test "extractDevice not found" {
var devices = std.StringHashMap([]const u8).init(std.testing.allocator); var devices = std.StringHashMap([]const u8).init(std.testing.allocator);
defer devices.deinit(); 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("kitchen", "192.168.1.100");
var object_words = [_][]const u8{"bathroom"}; 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); try std.testing.expect(result == null);
} }
@ -461,9 +530,11 @@ test "extractDevice not found" {
test "extractDevice empty words" { test "extractDevice empty words" {
var devices = std.StringHashMap([]const u8).init(std.testing.allocator); var devices = std.StringHashMap([]const u8).init(std.testing.allocator);
defer devices.deinit(); defer devices.deinit();
var replacements = std.StringHashMap([]const u8).init(std.testing.allocator);
defer replacements.deinit();
var object_words = [_][]const u8{}; 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); try std.testing.expect(result == null);
} }
@ -471,22 +542,27 @@ test "extractDevice empty words" {
test "extractDevice fallback to shorter match" { test "extractDevice fallback to shorter match" {
var devices = std.StringHashMap([]const u8).init(std.testing.allocator); var devices = std.StringHashMap([]const u8).init(std.testing.allocator);
defer devices.deinit(); 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("kitchen", "192.168.1.100");
var object_words = [_][]const u8{ "kitchen", "ceiling", "light" }; 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.*); try std.testing.expectEqualStrings("192.168.1.100", result.?.value_ptr.*);
} }
test "extractDevice word replacement lake to light" { test "extractDevice word replacement lake to light" {
var devices = std.StringHashMap([]const u8).init(std.testing.allocator); var devices = std.StringHashMap([]const u8).init(std.testing.allocator);
defer devices.deinit(); 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"); try devices.put("bedroom light", "192.168.1.100");
var object_words = [_][]const u8{ "bedroom", "lake" }; 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.*); 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" { test "extractDevice word replacement like to light" {
var devices = std.StringHashMap([]const u8).init(std.testing.allocator); var devices = std.StringHashMap([]const u8).init(std.testing.allocator);
defer devices.deinit(); 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"); try devices.put("kitchen light", "192.168.1.101");
var object_words = [_][]const u8{ "kitchen", "like" }; 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.*); try std.testing.expectEqualStrings("192.168.1.101", result.?.value_ptr.*);
} }
test "processCommand successful match" { test "processCommand successful match" {
var devices = std.StringHashMap([]const u8).init(std.testing.allocator); var devices = std.StringHashMap([]const u8).init(std.testing.allocator);
defer devices.deinit(); 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("kitchen", "192.168.1.100");
@ -515,7 +596,7 @@ test "processCommand successful match" {
const sentence_z = try std.testing.allocator.dupeZ(u8, sentence); const sentence_z = try std.testing.allocator.dupeZ(u8, sentence);
defer std.testing.allocator.free(sentence_z); 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.expectEqualStrings("192.168.1.100", test_device_entry.value_ptr.*);
try std.testing.expectEqual(DeviceAction.on, test_action); try std.testing.expectEqual(DeviceAction.on, test_action);
@ -524,6 +605,9 @@ test "processCommand successful match" {
test "processCommand with word replacement lake to light" { test "processCommand with word replacement lake to light" {
var devices = std.StringHashMap([]const u8).init(std.testing.allocator); var devices = std.StringHashMap([]const u8).init(std.testing.allocator);
defer devices.deinit(); 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"); 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); const sentence_z = try std.testing.allocator.dupeZ(u8, sentence);
defer std.testing.allocator.free(sentence_z); 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.expectEqualStrings("192.168.1.100", test_device_entry.value_ptr.*);
try std.testing.expectEqual(DeviceAction.on, test_action); 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" { test "processCommand with word replacement like to light" {
var devices = std.StringHashMap([]const u8).init(std.testing.allocator); var devices = std.StringHashMap([]const u8).init(std.testing.allocator);
defer devices.deinit(); 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"); 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); const sentence_z = try std.testing.allocator.dupeZ(u8, sentence);
defer std.testing.allocator.free(sentence_z); 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.expectEqualStrings("192.168.1.101", test_device_entry.value_ptr.*);
try std.testing.expectEqual(DeviceAction.off, test_action); 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" { test "processCommand with word replacement like to light - three words" {
var devices = std.StringHashMap([]const u8).init(std.testing.allocator); var devices = std.StringHashMap([]const u8).init(std.testing.allocator);
defer devices.deinit(); 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"); 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); const sentence_z = try std.testing.allocator.dupeZ(u8, sentence);
defer std.testing.allocator.free(sentence_z); defer std.testing.allocator.free(sentence_z);
// const ll = std.testing.log_level; try processCommand(std.testing.allocator, sentence_z, &parser, &devices, &replacements);
// std.testing.log_level = .debug;
// defer std.testing.log_level = ll;
try processCommand(std.testing.allocator, sentence_z, &parser, &devices);
try std.testing.expectEqualStrings("192.168.1.101", test_device_entry.value_ptr.*); try std.testing.expectEqualStrings("192.168.1.101", test_device_entry.value_ptr.*);
try std.testing.expectEqual(DeviceAction.off, test_action); 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" { test "processCommand no match found" {
var devices = std.StringHashMap([]const u8).init(std.testing.allocator); var devices = std.StringHashMap([]const u8).init(std.testing.allocator);
defer devices.deinit(); 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("kitchen", "192.168.1.100");
@ -593,6 +682,5 @@ test "processCommand no match found" {
const sentence_z = try std.testing.allocator.dupeZ(u8, sentence); const sentence_z = try std.testing.allocator.dupeZ(u8, sentence);
defer std.testing.allocator.free(sentence_z); 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, &replacements));
try std.testing.expectError(error.UnrecognizedSentence, processCommand(std.testing.allocator, sentence_z, &parser, &devices));
} }

View file

@ -362,15 +362,14 @@ pub const Parser = struct {
_ = c.dictionary_delete(self.dict); _ = c.dictionary_delete(self.dict);
} }
fn applyReplacements(self: *Parser, sentence: []const u8, replacements: std.StaticStringMap([]const u8), final_buf: []u8) ![]const u8 { fn applyReplacements(self: *Parser, sentence: []const u8, replacements: *const std.StringHashMap([]const u8), final_buf: []u8) ![]const u8 {
_ = self; // we don't want to remove this completely, as there _ = self;
// could be a time when we need to re-parse after replacement
const replacement_keys = replacements.keys();
const replacement_values = replacements.values();
var altered = sentence; 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 k_buf: [256]u8 = undefined;
var v_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 /// sentence is found, it will be returned, with the guarantee that
/// sentenceObject and sentenceAction will return non-zero results. If that /// sentenceObject and sentenceAction will return non-zero results. If that
/// condition cannot be satisfied, error.NoValidParse will be returned /// 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 final_buf: [1024]u8 = undefined;
var altered = try self.applyReplacements(sentence, replacements, &final_buf); 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); var parser = try Parser.init(std.testing.allocator);
defer parser.deinit(); defer parser.deinit();
const replacements = std.StaticStringMap([]const u8).initComptime(.{ var replacements = std.StringHashMap([]const u8).init(std.testing.allocator);
.{ "lake", "light" }, defer replacements.deinit();
.{ "like", "light" }, try replacements.put("lake", "light");
}); try replacements.put("like", "light");
const sentence = "turn on the kitchen 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(); defer tree.deinit();
const action_words = try tree.sentenceAction(); const action_words = try tree.sentenceAction();
@ -814,14 +813,14 @@ test "adaptiveCommandParse with word replacement" {
var parser = try Parser.init(std.testing.allocator); var parser = try Parser.init(std.testing.allocator);
defer parser.deinit(); defer parser.deinit();
const replacements = std.StaticStringMap([]const u8).initComptime(.{ var replacements = std.StringHashMap([]const u8).init(std.testing.allocator);
.{ "lake", "light" }, defer replacements.deinit();
.{ "like", "light" }, try replacements.put("lake", "light");
}); try replacements.put("like", "light");
const sentence = "turn on the kitchen lake"; const sentence = "turn on the kitchen lake";
var tree = try parser.adaptiveCommandParse(sentence, replacements); var tree = try parser.adaptiveCommandParse(sentence, &replacements);
defer tree.deinit(); defer tree.deinit();
const action_words = try tree.sentenceAction(); const action_words = try tree.sentenceAction();
@ -841,22 +840,16 @@ test "adaptiveCommandParse no valid parse" {
var parser = try Parser.init(std.testing.allocator); var parser = try Parser.init(std.testing.allocator);
defer parser.deinit(); defer parser.deinit();
const replacements = std.StaticStringMap([]const u8).initComptime(.{ var replacements = std.StringHashMap([]const u8).init(std.testing.allocator);
.{ "lake", "light" }, defer replacements.deinit();
.{ "like", "light" }, try replacements.put("lake", "light");
}); try replacements.put("like", "light");
const sentence = "xyz abc def"; 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( try std.testing.expectError(
error.SentenceEmptyAfterNullRemoval, error.SentenceEmptyAfterNullRemoval,
parser.adaptiveCommandParse( parser.adaptiveCommandParse(sentence, &replacements),
sentence,
replacements,
),
); );
} }
@ -864,17 +857,14 @@ test "adaptiveCommandParse with word replacement and null removal" {
var parser = try Parser.init(std.testing.allocator); var parser = try Parser.init(std.testing.allocator);
defer parser.deinit(); defer parser.deinit();
const replacements = std.StaticStringMap([]const u8).initComptime(.{ var replacements = std.StringHashMap([]const u8).init(std.testing.allocator);
.{ "lake", "light" }, defer replacements.deinit();
.{ "like", "light" }, try replacements.put("lake", "light");
}); try replacements.put("like", "light");
const sentence = "alexa turn on the kitchen lake"; const sentence = "alexa turn on the kitchen lake";
// const ll = std.testing.log_level; var tree = try parser.adaptiveCommandParse(sentence, &replacements);
// defer std.testing.log_level = ll;
// std.testing.log_level = .debug;
var tree = try parser.adaptiveCommandParse(sentence, replacements);
defer tree.deinit(); defer tree.deinit();
const action_words = try tree.sentenceAction(); const action_words = try tree.sentenceAction();
@ -893,12 +883,12 @@ test "applyReplacements basic replacement" {
var parser = try Parser.init(std.testing.allocator); var parser = try Parser.init(std.testing.allocator);
defer parser.deinit(); defer parser.deinit();
const replacements = std.StaticStringMap([]const u8).initComptime(.{ var replacements = std.StringHashMap([]const u8).init(std.testing.allocator);
.{ "lake", "light" }, defer replacements.deinit();
}); try replacements.put("lake", "light");
var final_buf: [1024]u8 = undefined; 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); 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); var parser = try Parser.init(std.testing.allocator);
defer parser.deinit(); defer parser.deinit();
const replacements = std.StaticStringMap([]const u8).initComptime(.{ var replacements = std.StringHashMap([]const u8).init(std.testing.allocator);
.{ "lake", "light" }, defer replacements.deinit();
.{ "kitchen", "bedroom" }, try replacements.put("lake", "light");
}); try replacements.put("kitchen", "bedroom");
var final_buf: [1024]u8 = undefined; 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); 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); var parser = try Parser.init(std.testing.allocator);
defer parser.deinit(); defer parser.deinit();
const replacements = std.StaticStringMap([]const u8).initComptime(.{ var replacements = std.StringHashMap([]const u8).init(std.testing.allocator);
.{ "test", "" }, defer replacements.deinit();
}); try replacements.put("test", "");
var final_buf: [1024]u8 = undefined; 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" { test "removeNullWords no nulls" {