replace words in original sentence
This commit is contained in:
parent
47a6ffb1e3
commit
78a059e9ef
1 changed files with 143 additions and 51 deletions
194
src/main.zig
194
src/main.zig
|
@ -1,6 +1,11 @@
|
|||
const std = @import("std");
|
||||
const pos = @import("pos");
|
||||
|
||||
const word_replacements = std.StaticStringMap([]const u8).initComptime(.{
|
||||
.{ "lake", "light" },
|
||||
.{ "like", "light" },
|
||||
});
|
||||
|
||||
const DeviceAction = enum {
|
||||
on,
|
||||
off,
|
||||
|
@ -103,7 +108,7 @@ fn loadDeviceConfig(allocator: std.mem.Allocator, bin_dir: []const u8) !std.Stri
|
|||
const name_copy = try allocator.alloc(u8, name.len);
|
||||
_ = std.ascii.lowerString(name_copy, name);
|
||||
try devices.put(name_copy, try allocator.dupe(u8, url));
|
||||
std.log.debug("Loaded device: '{s}' -> '{s}'\n", .{ name, url });
|
||||
std.log.debug("Loaded device: '{s}' -> {s}", .{ name, url });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -175,6 +180,8 @@ fn extractDevice(allocator: std.mem.Allocator, object_words: [][]const u8, devic
|
|||
var allocating_writer = try std.Io.Writer.Allocating.initCapacity(allocator, total_size);
|
||||
defer allocating_writer.deinit();
|
||||
const writer = &allocating_writer.writer;
|
||||
|
||||
// Try original words first
|
||||
for (0..object_words.len) |removed_words| {
|
||||
defer allocating_writer.clearRetainingCapacity();
|
||||
const total_words = object_words.len - removed_words;
|
||||
|
@ -188,100 +195,162 @@ fn extractDevice(allocator: std.mem.Allocator, object_words: [][]const u8, devic
|
|||
});
|
||||
}
|
||||
|
||||
std.log.debug(
|
||||
"Attempting device match '{s}'",
|
||||
.{allocating_writer.written()},
|
||||
);
|
||||
std.log.debug("Attempting device match '{s}'", .{allocating_writer.written()});
|
||||
if (devices.getEntry(allocating_writer.written())) |entry| return entry;
|
||||
std.log.info(
|
||||
"Could not find device match '{s}'. Falling back to more general object",
|
||||
.{allocating_writer.written()},
|
||||
);
|
||||
}
|
||||
|
||||
// Try with word replacements
|
||||
for (0..object_words.len) |removed_words| {
|
||||
defer allocating_writer.clearRetainingCapacity();
|
||||
const total_words = object_words.len - removed_words;
|
||||
for (object_words, 1..) |word, i| {
|
||||
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;
|
||||
try writer.print("{s}{s}", .{
|
||||
final_word,
|
||||
if (i < object_words.len - removed_words) " " else "",
|
||||
});
|
||||
}
|
||||
|
||||
std.log.debug("Attempting device match with replacements '{s}'", .{allocating_writer.written()});
|
||||
if (devices.getEntry(allocating_writer.written())) |entry| return entry;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn main() !void {
|
||||
pub fn main() !u8 {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
defer _ = gpa.deinit();
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
const args = try std.process.argsAlloc(gpa.allocator());
|
||||
defer std.process.argsFree(gpa.allocator(), args);
|
||||
const args = try std.process.argsAlloc(allocator);
|
||||
defer std.process.argsFree(allocator, args);
|
||||
|
||||
var stdout_writer = std.fs.File.stdout().writer(&.{});
|
||||
const stdout = &stdout_writer.interface;
|
||||
_ = stdout;
|
||||
var stderr_writer = std.fs.File.stderr().writer(&.{});
|
||||
const stderr = &stderr_writer.interface;
|
||||
|
||||
if (args.len < 2) {
|
||||
stderr.print("Usage: {s} <sentence>\n", .{args[0]}) catch {};
|
||||
std.process.exit(1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
std.log.debug("loading device config", .{});
|
||||
// Get binary directory
|
||||
const bin_path = std.fs.selfExePathAlloc(gpa.allocator()) catch |err| {
|
||||
const bin_path = std.fs.selfExePathAlloc(allocator) catch |err| {
|
||||
stderr.print("Failed to get binary path: {}\n", .{err}) catch {};
|
||||
std.process.exit(1);
|
||||
return 1;
|
||||
};
|
||||
defer gpa.allocator().free(bin_path);
|
||||
defer allocator.free(bin_path);
|
||||
|
||||
const bin_dir = std.fs.path.dirname(bin_path) orelse ".";
|
||||
|
||||
var devices = loadDeviceConfig(gpa.allocator(), bin_dir) catch |err| {
|
||||
var devices = loadDeviceConfig(allocator, bin_dir) catch |err| {
|
||||
stderr.print("Failed to load device configuration: {}\n", .{err}) catch {};
|
||||
std.process.exit(1);
|
||||
return 1;
|
||||
};
|
||||
defer {
|
||||
var iterator = devices.iterator();
|
||||
while (iterator.next()) |entry| {
|
||||
gpa.allocator().free(entry.key_ptr.*);
|
||||
gpa.allocator().free(entry.value_ptr.*);
|
||||
allocator.free(entry.key_ptr.*);
|
||||
allocator.free(entry.value_ptr.*);
|
||||
}
|
||||
devices.deinit();
|
||||
}
|
||||
|
||||
std.log.debug("initializing parser", .{});
|
||||
var parser = pos.Parser.initWithDataDir(gpa.allocator(), bin_dir) catch |err| {
|
||||
var parser = pos.Parser.initWithDataDir(allocator, bin_dir) catch |err| {
|
||||
stderr.print("Failed to initialize parser: {}\n", .{err}) catch {};
|
||||
std.process.exit(1);
|
||||
return 1;
|
||||
};
|
||||
defer parser.deinit();
|
||||
|
||||
var tree = parser.parse(args[1]) catch |err| {
|
||||
stderr.print("Failed to parse sentence: {}\n", .{err}) catch {};
|
||||
std.process.exit(1);
|
||||
};
|
||||
defer tree.deinit();
|
||||
// Try original sentence first, then with word replacements
|
||||
const replacement_keys = word_replacements.keys();
|
||||
const replacement_values = word_replacements.values();
|
||||
|
||||
const action_words = tree.sentenceAction() catch |err| {
|
||||
stderr.print("Failed to extract action: {}\n", .{err}) catch {};
|
||||
std.process.exit(1);
|
||||
};
|
||||
defer gpa.allocator().free(action_words);
|
||||
var sentence_to_try: [:0]const u8 = args[1];
|
||||
var replaced_sentence: ?[:0]u8 = null;
|
||||
defer if (replaced_sentence) |s| allocator.free(s);
|
||||
|
||||
const object_words = tree.sentenceObject() catch |err| {
|
||||
stderr.print("Failed to extract object: {}\n", .{err}) catch {};
|
||||
std.process.exit(1);
|
||||
};
|
||||
defer gpa.allocator().free(object_words);
|
||||
|
||||
std.debug.print("Object words: ", .{});
|
||||
for (object_words) |word| {
|
||||
std.debug.print("'{s}' ", .{word});
|
||||
}
|
||||
std.debug.print("\n", .{});
|
||||
|
||||
if (parseAction(action_words)) |action| {
|
||||
if (try extractDevice(allocator, object_words, &devices)) |entry| {
|
||||
try sendWemoCommand(entry, action, gpa.allocator());
|
||||
} else {
|
||||
try stdout.print("Device not found in configuration\n", .{});
|
||||
var success = false;
|
||||
for (0..replacement_keys.len + 1) |attempt| {
|
||||
if (attempt > 0) {
|
||||
// Create sentence with replacements
|
||||
if (replaced_sentence) |s| allocator.free(s);
|
||||
const temp = try std.mem.replaceOwned(
|
||||
u8,
|
||||
allocator,
|
||||
sentence_to_try,
|
||||
replacement_keys[attempt - 1],
|
||||
replacement_values[attempt - 1],
|
||||
);
|
||||
replaced_sentence = try allocator.dupeZ(u8, temp);
|
||||
allocator.free(temp);
|
||||
if (std.mem.eql(u8, sentence_to_try, replaced_sentence.?)) continue;
|
||||
sentence_to_try = replaced_sentence.?;
|
||||
std.log.info("Replaced word '{s}' in sentence with replacement '{s}'. Trying sentence: {s}", .{
|
||||
replacement_keys[attempt - 1],
|
||||
replacement_values[attempt - 1],
|
||||
sentence_to_try,
|
||||
});
|
||||
}
|
||||
|
||||
var tree = parser.parse(sentence_to_try) catch |err| {
|
||||
std.log.err("Failed to parse sentence: {}\n", .{err});
|
||||
continue;
|
||||
};
|
||||
defer tree.deinit();
|
||||
|
||||
const action_words = tree.sentenceAction() catch |err| {
|
||||
std.log.err("Failed to extract action: {}\n", .{err});
|
||||
continue;
|
||||
};
|
||||
defer allocator.free(action_words);
|
||||
if (action_words.len == 0) {
|
||||
std.log.info("Failed to extract action from sentence", .{});
|
||||
continue;
|
||||
}
|
||||
|
||||
const object_words = tree.sentenceObject() catch |err| {
|
||||
std.log.err("Failed to extract object: {}\n", .{err});
|
||||
continue;
|
||||
};
|
||||
defer allocator.free(object_words);
|
||||
if (object_words.len == 0) {
|
||||
std.log.info("Failed to extract object from sentence", .{});
|
||||
continue;
|
||||
}
|
||||
|
||||
var aw = std.Io.Writer.Allocating.init(allocator);
|
||||
defer aw.deinit();
|
||||
const aw_writer = &aw.writer;
|
||||
try aw_writer.writeAll("Object words: [");
|
||||
var first = true;
|
||||
for (object_words) |word| {
|
||||
try aw_writer.print("{s}\"{s}\"", .{ if (!first) ", " else "", word });
|
||||
first = false;
|
||||
}
|
||||
std.log.debug("{s}]", .{aw.written()});
|
||||
|
||||
if (parseAction(action_words)) |action| {
|
||||
if (try extractDevice(allocator, object_words, &devices)) |entry| {
|
||||
try sendWemoCommand(entry, action, allocator);
|
||||
success = true;
|
||||
break; // Success, exit
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try stdout.print("Unrecognized sentence: {s}\n", .{args[1]});
|
||||
}
|
||||
if (!success) {
|
||||
try stderr.print("Unrecognized sentence: {s}\n", .{args[1]});
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
test "extractDevice single word exact match" {
|
||||
var devices = std.StringHashMap([]const u8).init(std.testing.allocator);
|
||||
|
@ -365,3 +434,26 @@ test "extractDevice fallback to shorter match" {
|
|||
|
||||
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();
|
||||
|
||||
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);
|
||||
|
||||
try std.testing.expectEqualStrings("192.168.1.100", result.?.value_ptr.*);
|
||||
}
|
||||
|
||||
test "extractDevice word replacement like to light" {
|
||||
var devices = std.StringHashMap([]const u8).init(std.testing.allocator);
|
||||
defer devices.deinit();
|
||||
|
||||
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);
|
||||
|
||||
try std.testing.expectEqualStrings("192.168.1.101", result.?.value_ptr.*);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue