move replacement processing to root.zig
This commit is contained in:
parent
febc14be32
commit
bde4a1fe3d
2 changed files with 131 additions and 101 deletions
138
src/main.zig
138
src/main.zig
|
@ -284,114 +284,50 @@ pub fn main() !u8 {
|
||||||
}
|
}
|
||||||
|
|
||||||
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)) !void {
|
||||||
// Try original sentence first, then with word replacements
|
var tree = parser.adaptiveParse(sentence, word_replacements) catch |err| {
|
||||||
const replacement_keys = word_replacements.keys();
|
std.log.err("Failed to parse sentence with all replacements: {}\n", .{err});
|
||||||
const replacement_values = word_replacements.values();
|
return error.UnrecognizedSentence;
|
||||||
|
};
|
||||||
|
defer tree.deinit();
|
||||||
|
|
||||||
var sentence_to_try: [:0]const u8 = sentence;
|
const action_words = try tree.sentenceAction();
|
||||||
var replaced_sentence: ?[:0]u8 = null;
|
defer allocator.free(action_words);
|
||||||
defer if (replaced_sentence) |s| allocator.free(s);
|
|
||||||
|
|
||||||
var success = false;
|
const object_words = try tree.sentenceObject();
|
||||||
for (0..replacement_keys.len + 1) |attempt| {
|
defer allocator.free(object_words);
|
||||||
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.debug("{f}", .{tree});
|
||||||
std.log.err("Failed to parse sentence: {}\n", .{err});
|
var aw = std.Io.Writer.Allocating.init(allocator);
|
||||||
continue;
|
defer aw.deinit();
|
||||||
};
|
const aw_writer = &aw.writer;
|
||||||
defer tree.deinit();
|
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()});
|
||||||
|
|
||||||
// Bracketed words are "null"
|
aw.clearRetainingCapacity();
|
||||||
// words with [?] are "unknown"
|
try aw_writer.writeAll("Action words: [");
|
||||||
// If we have unknowns, I think we want to replace (or if no replacement
|
first = true;
|
||||||
// is available, strip) them. Then re-parse immediately, because we're
|
for (action_words) |word| {
|
||||||
// in a bad enough state that we might screw something else up
|
try aw_writer.print("{s}\"{s}\"", .{ if (!first) ", " else "", word });
|
||||||
//
|
first = false;
|
||||||
// If there are nulls, then we should walk those nulls and look for
|
}
|
||||||
// replacement values. If any replacements have been performed, then
|
std.log.debug("{s}]", .{aw.written()});
|
||||||
// try re-parsing at that point.
|
|
||||||
//
|
|
||||||
// This might all be best done in the library itself. Pass in the
|
|
||||||
// map of replacement words and let it churn.
|
|
||||||
//
|
|
||||||
// For null words, I think we can use this replacement loop
|
|
||||||
// if (tree.hasUnknowns()) // then what?
|
|
||||||
// {}
|
|
||||||
|
|
||||||
const action_words = tree.sentenceAction() catch |err| {
|
if (parseAction(action_words)) |action| {
|
||||||
if (!builtin.is_test)
|
if (try extractDevice(allocator, object_words, devices)) |entry| {
|
||||||
std.log.err("Failed to extract action: {}\nParse tree: {f}", .{ err, tree });
|
if (builtin.is_test)
|
||||||
continue;
|
testSendCommand(entry, action)
|
||||||
};
|
else
|
||||||
defer allocator.free(action_words);
|
try sendWemoCommand(allocator, entry, action);
|
||||||
if (action_words.len == 0) {
|
return; // Success
|
||||||
std.log.info("Failed to extract action from sentence", .{});
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const object_words = tree.sentenceObject() catch |err| {
|
|
||||||
std.log.err("Failed to extract object: {}\nParse tree: {f}", .{ err, tree });
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
defer allocator.free(object_words);
|
|
||||||
if (object_words.len == 0) {
|
|
||||||
std.log.info("Failed to extract object from sentence", .{});
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
std.log.debug("{f}", .{tree});
|
|
||||||
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()});
|
|
||||||
|
|
||||||
aw.clearRetainingCapacity();
|
|
||||||
try aw_writer.writeAll("Action words: [");
|
|
||||||
first = true;
|
|
||||||
for (action_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| {
|
|
||||||
if (builtin.is_test)
|
|
||||||
testSendCommand(entry, action)
|
|
||||||
else
|
|
||||||
try sendWemoCommand(allocator, entry, action);
|
|
||||||
success = true;
|
|
||||||
break; // Success, exit
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!success) return error.UnrecognizedSentence;
|
|
||||||
|
return error.UnrecognizedSentence;
|
||||||
}
|
}
|
||||||
|
|
||||||
var test_device_entry: std.hash_map.StringHashMap([]const u8).Entry = undefined;
|
var test_device_entry: std.hash_map.StringHashMap([]const u8).Entry = undefined;
|
||||||
|
|
94
src/root.zig
94
src/root.zig
|
@ -1,3 +1,4 @@
|
||||||
|
const builtin = @import("builtin");
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const c = @cImport({
|
const c = @cImport({
|
||||||
@cInclude("link-includes.h");
|
@cInclude("link-includes.h");
|
||||||
|
@ -275,6 +276,99 @@ pub const Parser = struct {
|
||||||
_ = c.dictionary_delete(self.dict);
|
_ = c.dictionary_delete(self.dict);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parses a sentence with an attempt to "fix" the sentence. If a valid
|
||||||
|
/// 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 adaptiveParse(self: *Parser, sentence: [:0]const u8, replacements: std.StaticStringMap([]const u8)) !ParseTree {
|
||||||
|
var sentence_to_try: [:0]const u8 = sentence;
|
||||||
|
var replaced_sentence: ?[:0]u8 = null;
|
||||||
|
defer if (replaced_sentence) |s| self.allocator.free(s);
|
||||||
|
|
||||||
|
const replacement_keys = replacements.keys();
|
||||||
|
const replacement_values = replacements.values();
|
||||||
|
|
||||||
|
for (0..replacement_keys.len + 1) |attempt| {
|
||||||
|
if (attempt > 0) {
|
||||||
|
// Create sentence with replacements
|
||||||
|
if (replaced_sentence) |s| self.allocator.free(s);
|
||||||
|
const temp = try std.mem.replaceOwned(
|
||||||
|
u8,
|
||||||
|
self.allocator,
|
||||||
|
sentence_to_try,
|
||||||
|
replacement_keys[attempt - 1],
|
||||||
|
replacement_values[attempt - 1],
|
||||||
|
);
|
||||||
|
replaced_sentence = try self.allocator.dupeZ(u8, temp);
|
||||||
|
self.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 = self.parse(sentence_to_try) catch |err| {
|
||||||
|
std.log.err("Failed to parse sentence: {}\n", .{err});
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Bracketed words are "null"
|
||||||
|
// words with [?] are "unknown"
|
||||||
|
// If we have unknowns, I think we want to replace (or if no replacement
|
||||||
|
// is available, strip) them. Then re-parse immediately, because we're
|
||||||
|
// in a bad enough state that we might screw something else up
|
||||||
|
//
|
||||||
|
// If there are nulls, then we should walk those nulls and look for
|
||||||
|
// replacement values. If any replacements have been performed, then
|
||||||
|
// try re-parsing at that point.
|
||||||
|
//
|
||||||
|
// This might all be best done in the library itself. Pass in the
|
||||||
|
// map of replacement words and let it churn.
|
||||||
|
//
|
||||||
|
// For null words, I think we can use this replacement loop
|
||||||
|
// if (tree.hasUnknowns()) // then what?
|
||||||
|
// {}
|
||||||
|
|
||||||
|
// Validate that we can extract action and object before returning
|
||||||
|
const action_words = tree.sentenceAction() catch |err| {
|
||||||
|
if (!builtin.is_test)
|
||||||
|
std.log.err("Failed to extract action: {}\n", .{err});
|
||||||
|
tree.deinit();
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
defer self.allocator.free(action_words);
|
||||||
|
|
||||||
|
if (action_words.len == 0) {
|
||||||
|
if (!builtin.is_test)
|
||||||
|
std.log.info("Failed to extract action from sentence", .{});
|
||||||
|
tree.deinit();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const object_words = tree.sentenceObject() catch |err| {
|
||||||
|
if (!builtin.is_test)
|
||||||
|
std.log.err("Failed to extract object: {}\n", .{err});
|
||||||
|
tree.deinit();
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
defer self.allocator.free(object_words);
|
||||||
|
|
||||||
|
if (object_words.len == 0) {
|
||||||
|
if (!builtin.is_test)
|
||||||
|
std.log.info("Failed to extract object from sentence", .{});
|
||||||
|
tree.deinit();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
return error.NoValidParse;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse(self: *Parser, input: []const u8) !ParseTree {
|
pub fn parse(self: *Parser, input: []const u8) !ParseTree {
|
||||||
const c_input = try self.allocator.dupeZ(u8, input);
|
const c_input = try self.allocator.dupeZ(u8, input);
|
||||||
defer self.allocator.free(c_input);
|
defer self.allocator.free(c_input);
|
||||||
|
|
Loading…
Add table
Reference in a new issue