rework nodesAtLoc
This commit is contained in:
parent
397b5bc78f
commit
d5ac6b9734
113
src/ast.zig
113
src/ast.zig
@ -1584,41 +1584,92 @@ pub fn nodeChildrenRecursiveAlloc(allocator: std.mem.Allocator, tree: Ast, node:
|
|||||||
pub fn nodesAtLoc(allocator: std.mem.Allocator, tree: Ast, loc: offsets.Loc) error{OutOfMemory}![]Ast.Node.Index {
|
pub fn nodesAtLoc(allocator: std.mem.Allocator, tree: Ast, loc: offsets.Loc) error{OutOfMemory}![]Ast.Node.Index {
|
||||||
std.debug.assert(loc.start <= loc.end and loc.end <= tree.source.len);
|
std.debug.assert(loc.start <= loc.end and loc.end <= tree.source.len);
|
||||||
|
|
||||||
var nodes = std.ArrayListUnmanaged(Ast.Node.Index){};
|
const Context = struct {
|
||||||
errdefer nodes.deinit(allocator);
|
allocator: std.mem.Allocator,
|
||||||
|
nodes: std.ArrayListUnmanaged(Ast.Node.Index) = .{},
|
||||||
|
locs: std.ArrayListUnmanaged(offsets.Loc) = .{},
|
||||||
|
|
||||||
|
pub fn append(self: *@This(), ast: Ast, node: Ast.Node.Index) !void {
|
||||||
|
if (node == 0) return;
|
||||||
|
try self.nodes.append(self.allocator, node);
|
||||||
|
try self.locs.append(self.allocator, offsets.nodeToLoc(ast, node));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var context: Context = .{ .allocator = allocator };
|
||||||
|
defer context.nodes.deinit(allocator);
|
||||||
|
defer context.locs.deinit(allocator);
|
||||||
|
|
||||||
|
try context.nodes.ensureTotalCapacity(allocator, 32);
|
||||||
|
|
||||||
var parent: Ast.Node.Index = 0; // root node
|
var parent: Ast.Node.Index = 0; // root node
|
||||||
|
|
||||||
try nodes.ensureTotalCapacity(allocator, 32);
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const children = try nodeChildrenAlloc(allocator, tree, parent);
|
try iterateChildren(tree, parent, &context, error{OutOfMemory}, Context.append);
|
||||||
defer allocator.free(children);
|
|
||||||
|
|
||||||
var children_loc: ?offsets.Loc = null;
|
if (smallestEnclosingSubrange(context.locs.items, loc)) |subslice| {
|
||||||
for (children) |child_node| {
|
std.debug.assert(subslice.len != 0);
|
||||||
const child_loc = offsets.nodeToLoc(tree, child_node);
|
const nodes = context.nodes.items[subslice.start .. subslice.start + subslice.len];
|
||||||
|
if (nodes.len == 1) { // recurse over single child node
|
||||||
const merge_child = offsets.locIntersect(loc, child_loc) or offsets.locInside(child_loc, loc);
|
parent = nodes[0];
|
||||||
|
context.nodes.clearRetainingCapacity();
|
||||||
if (merge_child) {
|
context.locs.clearRetainingCapacity();
|
||||||
children_loc = if (children_loc) |l| offsets.locMerge(l, child_loc) else child_loc;
|
continue;
|
||||||
try nodes.append(allocator, child_node);
|
} else { // end-condition: found enclosing children
|
||||||
} else {
|
return try allocator.dupe(Ast.Node.Index, nodes);
|
||||||
if (nodes.items.len != 0) break;
|
|
||||||
}
|
}
|
||||||
}
|
} else { // the children cannot enclose the given source location
|
||||||
|
context.nodes.clearRetainingCapacity();
|
||||||
if (children_loc == null or !offsets.locInside(loc, children_loc.?)) {
|
context.nodes.appendAssumeCapacity(parent); // capacity is never 0
|
||||||
nodes.clearRetainingCapacity();
|
return try context.nodes.toOwnedSlice(allocator);
|
||||||
nodes.appendAssumeCapacity(parent); // capacity is never 0
|
|
||||||
return try nodes.toOwnedSlice(allocator);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nodes.items.len == 1) {
|
|
||||||
parent = nodes.items[0];
|
|
||||||
nodes.clearRetainingCapacity();
|
|
||||||
} else {
|
|
||||||
return try nodes.toOwnedSlice(allocator);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the following code can be described as the the following problem:
|
||||||
|
/// @param children a non-intersecting list of source ranges
|
||||||
|
/// @param loc be a source range
|
||||||
|
///
|
||||||
|
/// @return a slice of #children
|
||||||
|
///
|
||||||
|
/// Return the smallest possible subrange of #children whose
|
||||||
|
/// combined source range is inside #loc.
|
||||||
|
/// If #children cannot contain #loc i.e #loc is too large, return null.
|
||||||
|
/// @see tests/utility.ast.zig for usage examples
|
||||||
|
pub fn smallestEnclosingSubrange(children: []const offsets.Loc, loc: offsets.Loc) ?struct {
|
||||||
|
start: usize,
|
||||||
|
len: usize,
|
||||||
|
} {
|
||||||
|
switch (children.len) {
|
||||||
|
0 => return null,
|
||||||
|
1 => return if (offsets.locInside(loc, children[0])) .{ .start = 0, .len = 1 } else null,
|
||||||
|
else => {
|
||||||
|
for (children[0 .. children.len - 1], children[1..]) |previous_loc, current_loc| {
|
||||||
|
std.debug.assert(previous_loc.end <= current_loc.start); // must by sorted
|
||||||
|
std.debug.assert(!offsets.locIntersect(previous_loc, current_loc)); // must be non-intersecting
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var i: usize = 0;
|
||||||
|
const start: usize = while (i < children.len) : (i += 1) {
|
||||||
|
const child_loc = children[i];
|
||||||
|
if (child_loc.end < loc.start) continue;
|
||||||
|
|
||||||
|
if (child_loc.start <= loc.start) {
|
||||||
|
break i;
|
||||||
|
} else if (i != 0) {
|
||||||
|
break i - 1;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else return null;
|
||||||
|
|
||||||
|
const end = while (i < children.len) : (i += 1) {
|
||||||
|
const child_loc = children[i];
|
||||||
|
if (loc.end <= child_loc.end) break i + 1;
|
||||||
|
} else return null;
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.start = start,
|
||||||
|
.len = end - start,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user