Compare commits
3 commits
5167397aa6
...
1f6cb4eb0c
Author | SHA1 | Date | |
---|---|---|---|
1f6cb4eb0c | |||
bd6e09bf50 | |||
5a38fd65c7 |
4 changed files with 217 additions and 98 deletions
|
@ -150,7 +150,12 @@ pub fn build(b: *std.Build) !void {
|
|||
});
|
||||
const options = b.addOptions();
|
||||
options.addOption(bool, "long_tests", long_tests);
|
||||
mod.addImport("build_options", options.createModule());
|
||||
|
||||
const git_describe = b.run(&.{ "git", "describe", "--always", "--dirty" });
|
||||
options.addOption([]const u8, "version", git_describe);
|
||||
|
||||
const options_module = options.createModule();
|
||||
mod.addImport("build_options", options_module);
|
||||
mod.linkLibrary(lib);
|
||||
mod.addIncludePath(upstream.path("include"));
|
||||
|
||||
|
@ -192,6 +197,7 @@ pub fn build(b: *std.Build) !void {
|
|||
// can be extremely useful in case of collisions (which can happen
|
||||
// importing modules from different packages).
|
||||
.{ .name = "pos", .module = mod },
|
||||
.{ .name = "build_options", .module = options_module },
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
|
16
replacements.json
Normal file
16
replacements.json
Normal 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": ""
|
||||
}
|
199
src/main.zig
199
src/main.zig
|
@ -1,21 +1,59 @@
|
|||
const builtin = @import("builtin");
|
||||
const std = @import("std");
|
||||
const pos = @import("pos");
|
||||
const build_options = @import("build_options");
|
||||
|
||||
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 {
|
||||
std.log.warn("No replacement configuration file found", .{});
|
||||
return replacements;
|
||||
};
|
||||
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 |_| {}
|
||||
|
||||
std.log.warn("No replacement configuration file found", .{});
|
||||
return replacements;
|
||||
}
|
||||
|
||||
const DeviceAction = enum {
|
||||
on,
|
||||
|
@ -201,7 +239,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 +275,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 "",
|
||||
|
@ -268,7 +306,27 @@ pub fn main() !u8 {
|
|||
sentence_parse_only = .sentence
|
||||
else if (std.mem.eql(u8, arg, "--command-parse-only"))
|
||||
sentence_parse_only = .command
|
||||
else if (sentence_arg == null)
|
||||
else if (std.mem.eql(u8, arg, "--help") or std.mem.eql(u8, arg, "-h")) {
|
||||
var stdout_writer = std.fs.File.stdout().writer(&.{});
|
||||
const stdout = &stdout_writer.interface;
|
||||
try stdout.print(
|
||||
\\pos version {s}
|
||||
\\
|
||||
\\Usage: {s} [OPTIONS] <sentence>
|
||||
\\
|
||||
\\Options:
|
||||
\\ --sentence-parse-only Parse sentence and show parse tree
|
||||
\\ --command-parse-only Parse as command with replacements
|
||||
\\ --help, -h Show this help message
|
||||
\\
|
||||
\\Configuration:
|
||||
\\ Replacements are loaded from:
|
||||
\\ 1. ./replacements.json (current directory)
|
||||
\\ 2. XDG_CONFIG_HOME/pos/replacements.json
|
||||
\\
|
||||
, .{ build_options.version, args[0] });
|
||||
return 0;
|
||||
} else if (sentence_arg == null)
|
||||
sentence_arg = arg;
|
||||
}
|
||||
|
||||
|
@ -302,16 +360,24 @@ 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});
|
||||
return 1;
|
||||
var tree = if (sentence_parse_only == .command) blk: {
|
||||
var replacements = try loadReplacements(allocator);
|
||||
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 +397,17 @@ pub fn main() !u8 {
|
|||
devices.deinit();
|
||||
}
|
||||
|
||||
processCommand(allocator, args[1], &parser, &devices) catch |err| {
|
||||
var replacements = try loadReplacements(allocator);
|
||||
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 +420,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 +455,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 +467,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 +478,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 +493,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 +507,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 +521,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 +535,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 +549,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 +561,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 +589,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 +615,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 +624,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 +637,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 +646,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 +659,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 +667,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 +680,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 +689,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 +701,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));
|
||||
}
|
||||
|
|
90
src/root.zig
90
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" {
|
||||
|
|
Loading…
Add table
Reference in a new issue