add ai-generated ASCII-art link diagram
This commit is contained in:
parent
a0a162632e
commit
e63f0024ce
1 changed files with 99 additions and 15 deletions
114
src/root.zig
114
src/root.zig
|
@ -54,6 +54,9 @@ pub const ParseTree = struct {
|
|||
}
|
||||
}
|
||||
pub fn format(self: ParseTree, writer: *std.io.Writer) std.io.Writer.Error!void {
|
||||
// Print ASCII link diagram
|
||||
try self.printLinkDiagram(writer);
|
||||
|
||||
try writer.writeAll("Words: ");
|
||||
for (self.words, 0..) |word, i| {
|
||||
try writer.print("{d}: '{s}' ", .{ i, word });
|
||||
|
@ -61,7 +64,7 @@ pub const ParseTree = struct {
|
|||
try writer.print("\n\nLinks ({} total):\n", .{self.links.len});
|
||||
|
||||
for (self.links, 0..) |link, i| {
|
||||
try writer.print(" [{d}] '{s}' --{s}--> '{s}'\n", .{ i, link.left_word, link.label, link.right_word });
|
||||
try writer.print(" [{d}] {s} --{s}--> {s}\n", .{ i, link.left_word, link.label, link.right_word });
|
||||
}
|
||||
|
||||
try writer.writeAll("\nConstituent Tree:\n");
|
||||
|
@ -71,6 +74,87 @@ pub const ParseTree = struct {
|
|||
try writer.writeAll(" (no constituent tree)\n");
|
||||
}
|
||||
}
|
||||
|
||||
fn printLinkDiagram(self: ParseTree, writer: *std.io.Writer) !void {
|
||||
// Simple ASCII diagram - just print links above words
|
||||
const max_width = 200;
|
||||
var line_buffer: [max_width]u8 = undefined;
|
||||
|
||||
// Calculate word positions
|
||||
var word_positions: [20]usize = undefined; // max 20 words
|
||||
var total_width: usize = 0;
|
||||
|
||||
for (self.words, 0..) |word, i| {
|
||||
if (i >= word_positions.len) break;
|
||||
word_positions[i] = total_width;
|
||||
total_width += word.len + 1;
|
||||
}
|
||||
|
||||
// Print a simple link line
|
||||
@memset(&line_buffer, ' ');
|
||||
|
||||
for (self.links) |link| {
|
||||
const left_idx = self.findWordIndex(link.left_word) orelse continue;
|
||||
const right_idx = self.findWordIndex(link.right_word) orelse continue;
|
||||
if (left_idx >= word_positions.len or right_idx >= word_positions.len) continue;
|
||||
|
||||
const left_pos = word_positions[left_idx] + link.left_word.len / 2;
|
||||
const right_pos = word_positions[right_idx] + link.right_word.len / 2;
|
||||
const start_pos = @min(left_pos, right_pos);
|
||||
const end_pos = @max(left_pos, right_pos);
|
||||
|
||||
if (end_pos < max_width) {
|
||||
if (start_pos < max_width) line_buffer[start_pos] = '+';
|
||||
if (end_pos < max_width) line_buffer[end_pos] = '+';
|
||||
for (start_pos + 1..end_pos) |i| {
|
||||
if (i < max_width) line_buffer[i] = '-';
|
||||
}
|
||||
|
||||
// Add label
|
||||
const label_start = (start_pos + end_pos) / 2;
|
||||
if (label_start >= link.label.len / 2 and label_start + link.label.len < max_width) {
|
||||
const label_pos = label_start - link.label.len / 2;
|
||||
for (link.label, 0..) |ch, i| {
|
||||
if (label_pos + i < max_width) {
|
||||
line_buffer[label_pos + i] = ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try writer.print("{s}\n", .{line_buffer[0..@min(total_width, max_width)]});
|
||||
|
||||
// Print connector line
|
||||
@memset(&line_buffer, ' ');
|
||||
for (self.links) |link| {
|
||||
const left_idx = self.findWordIndex(link.left_word) orelse continue;
|
||||
const right_idx = self.findWordIndex(link.right_word) orelse continue;
|
||||
if (left_idx >= word_positions.len or right_idx >= word_positions.len) continue;
|
||||
|
||||
const left_pos = word_positions[left_idx] + link.left_word.len / 2;
|
||||
const right_pos = word_positions[right_idx] + link.right_word.len / 2;
|
||||
|
||||
if (left_pos < max_width) line_buffer[left_pos] = '|';
|
||||
if (right_pos < max_width) line_buffer[right_pos] = '|';
|
||||
}
|
||||
|
||||
try writer.print("{s}\n", .{line_buffer[0..@min(total_width, max_width)]});
|
||||
|
||||
// Print words
|
||||
for (self.words, 0..) |word, i| {
|
||||
if (i > 0) try writer.writeAll(" ");
|
||||
try writer.print("{s}", .{word});
|
||||
}
|
||||
try writer.writeAll("\n\n");
|
||||
}
|
||||
|
||||
fn findWordIndex(self: ParseTree, word: []const u8) ?usize {
|
||||
for (self.words, 0..) |w, i| {
|
||||
if (std.mem.eql(u8, w, word)) return i;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
pub fn firstVerb(self: *ParseTree) ?[]const u8 {
|
||||
for (self.words) |word| {
|
||||
if (std.mem.endsWith(u8, word, ".v")) {
|
||||
|
@ -188,7 +272,7 @@ pub const ParseTree = struct {
|
|||
for (0..depth) |_| {
|
||||
try writer.writeAll(" ");
|
||||
}
|
||||
try writer.print("[{s}] ({}-{})\n", .{ node.label, node.start, node.end });
|
||||
try writer.print("{s} [{}-{}]\n", .{ node.label, node.start, node.end });
|
||||
if (node.child) |child| {
|
||||
try self.printConstituentNode(writer, child, depth + 1);
|
||||
}
|
||||
|
@ -536,23 +620,23 @@ test "real usage - jack" {
|
|||
test "adaptiveParse 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" },
|
||||
});
|
||||
|
||||
|
||||
const sentence = "turn on the kitchen light";
|
||||
const sentence_z = try std.testing.allocator.dupeZ(u8, sentence);
|
||||
defer std.testing.allocator.free(sentence_z);
|
||||
|
||||
|
||||
var tree = try parser.adaptiveParse(sentence_z, replacements);
|
||||
defer tree.deinit();
|
||||
|
||||
|
||||
const action_words = try tree.sentenceAction();
|
||||
defer std.testing.allocator.free(action_words);
|
||||
try std.testing.expect(action_words.len > 0);
|
||||
|
||||
|
||||
const object_words = try tree.sentenceObject();
|
||||
defer std.testing.allocator.free(object_words);
|
||||
try std.testing.expect(object_words.len > 0);
|
||||
|
@ -561,23 +645,23 @@ test "adaptiveParse successful without replacements" {
|
|||
test "adaptiveParse 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" },
|
||||
});
|
||||
|
||||
|
||||
const sentence = "turn on the kitchen lake";
|
||||
const sentence_z = try std.testing.allocator.dupeZ(u8, sentence);
|
||||
defer std.testing.allocator.free(sentence_z);
|
||||
|
||||
|
||||
var tree = try parser.adaptiveParse(sentence_z, replacements);
|
||||
defer tree.deinit();
|
||||
|
||||
|
||||
const action_words = try tree.sentenceAction();
|
||||
defer std.testing.allocator.free(action_words);
|
||||
try std.testing.expect(action_words.len > 0);
|
||||
|
||||
|
||||
const object_words = try tree.sentenceObject();
|
||||
defer std.testing.allocator.free(object_words);
|
||||
try std.testing.expect(object_words.len > 0);
|
||||
|
@ -586,15 +670,15 @@ test "adaptiveParse with word replacement" {
|
|||
test "adaptiveParse 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" },
|
||||
});
|
||||
|
||||
|
||||
const sentence = "xyz abc def";
|
||||
const sentence_z = try std.testing.allocator.dupeZ(u8, sentence);
|
||||
defer std.testing.allocator.free(sentence_z);
|
||||
|
||||
|
||||
try std.testing.expectError(error.NoValidParse, parser.adaptiveParse(sentence_z, replacements));
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue