Skip to documentation
SLOP

tiny.smg.tree.runtime.stack

Reference tiny.smg tree runtime stack

Defined in tree.runtime.

API (15)

Actions

Public operations.

Types and contracts

Public types and contracts.

No direct callersNo direct callstree.runtimestack
Static calls · unresolved targets: unknown · external targets: unknown.

Source

Called byCallsNo direct callersprivate; no linktools.smg.src.tree.runtime.stackwalkPathstree.runtime.stack.BranchacceptedRoot
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callstest; no linktools.smg.src.tree.runtime.stacktest: graph stack merges parser heads...tree.runtime.stack.Branchequivalent
Static calls · unresolved targets: 0 · external targets: 3.
Called byCallstree.runtime.parserparseprivate; no linktools.smg.src.tree.runtime.parsertestVersiontest; no linktools.smg.src.tree.runtime.parsertest: focused active version compacti...test; no linktools.smg.src.tree.runtime.parsertest: focused equal-precedence active...test; no linktools.smg.src.tree.runtime.stacktest: graph stack merges parser heads...tree.runtime.scanner.Stateinittree.runtime.stack.Branchinit
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callersprivate; no linktools.smg.src.tree.runtime.stackdeinitReductionsprivate; no linktools.smg.src.tree.runtime.stackwalkPathstree.runtime.stack.Branchreduce
Static calls · unresolved targets: 1 · external targets: 2.
Called byCallsNo direct callsprivate; no linktools.smg.src.tree.runtime.parsertestVersiontest; no linktools.smg.src.tree.runtime.parsertest: focused active version compacti...test; no linktools.smg.src.tree.runtime.parsertest: focused equal-precedence active...tree.runtime.stack.Branchshift
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsNo direct callstree.runtime.parserparsetest; no linktools.smg.src.tree.runtime.parsertest: focused active version compacti...test; no linktools.smg.src.tree.runtime.parsertest: focused an allowed boundary mer...test; no linktools.smg.src.tree.runtime.parsertest: focused equal-precedence active...test; no linktools.smg.src.tree.runtime.parsertest: focused temporary overflow cann...+5 moretree.runtime.stack.Contextdeinit
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsNo direct callstree.runtime.parserparsetest; no linktools.smg.src.tree.runtime.parsertest: focused active version compacti...test; no linktools.smg.src.tree.runtime.parsertest: focused an allowed boundary mer...test; no linktools.smg.src.tree.runtime.parsertest: focused equal-precedence active...test; no linktools.smg.src.tree.runtime.parsertest: focused temporary overflow cann...+5 moretree.runtime.stack.Contextinit
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallstest; no linktools.smg.src.tree.runtime.stacktest: focused saturated graph stack i...private; no linktools.smg.src.tree.runtime.stack.ContextaddLinkprivate; no linktools.smg.src.tree.runtime.stackcanMergetree.runtime.stack.Contextmerge
Static calls · unresolved targets: 0 · external targets: 1.

Source: tools/smg/src/tree/runtime/root.zig:8

zig
pub const stack = @import("stack.zig");

Source: tools/smg/src/tree/runtime/stack.zig

zig
const std = @import("std");const runtime = @import("root.zig");const limits_mod = @import("../../limits/root.zig");const abi = runtime.abi;const language = runtime.language;const lexer = runtime.lexer;const scanner = runtime.scanner;const subtree = runtime.subtree;const testing_limits = @import("../../root.zig").default_limits.parser;const Link = struct {    previous: *Node,    tree: *const subtree.Subtree,};const Node = struct {    state: abi.State,    position: lexer.Position,    links: []Link,    link_count: usize = 0,    precedence: i64,    fn linkSlice(self: *const Node) []const Link {        return self.links[0..self.link_count];    }};pub const Budget = struct {    operations: *usize,    limit: usize,    fn charge(self: Budget) !void {        if (self.operations.* >= self.limit) return error.OperationLimitExceeded;        self.operations.* += 1;    }};pub const Context = struct {    arena: std.heap.ArenaAllocator,    base: *Node,    limits: limits_mod.Parser,    pub fn init(allocator: std.mem.Allocator, limits: limits_mod.Parser) !Context {        var self = Context{            .arena = std.heap.ArenaAllocator.init(allocator),            .base = undefined,            .limits = limits,        };        errdefer self.arena.deinit();        self.base = try self.arena.allocator().create(Node);        self.base.* = .{            .state = 1,            .position = .{ .byte = 0, .point = .{ .row = 0, .column = 0 } },            .links = try self.arena.allocator().alloc(Link, limits.link_count),            .precedence = 0,        };        return self;    }    pub fn deinit(self: *Context) void {        self.arena.deinit();        self.* = undefined;    }    fn push(self: *Context, previous: *Node, tree: *const subtree.Subtree, state: abi.State) !*Node {        const allocator = self.arena.allocator();        const result = try allocator.create(Node);        result.* = .{            .state = state,            .position = tree.end,            .links = try allocator.alloc(Link, self.limits.link_count),            .link_count = 1,            .precedence = previous.precedence + tree.dynamic_precedence,        };        result.links[0] = .{ .previous = previous, .tree = tree };        return result;    }    pub fn merge(self: *Context, left: *Node, right: *Node) !bool {        if (!canMerge(left, right)) return error.IncompatibleStackHeads;        if (left == right) return false;        var changed = false;        for (right.linkSlice()) |link| changed = self.addLink(left, link) or changed;        return changed;    }    fn addLink(self: *Context, node: *Node, candidate: Link) bool {        if (candidate.previous == node) return false;        for (node.links[0..node.link_count]) |*existing| {            if (!linksEquivalent(existing.*, candidate)) continue;            if (existing.previous == candidate.previous) {                if (candidate.tree.dynamic_precedence <= existing.tree.dynamic_precedence) return false;                existing.tree = candidate.tree;                node.precedence = candidate.previous.precedence + candidate.tree.dynamic_precedence;                return true;            }            if (canMerge(existing.previous, candidate.previous)) {                var changed = false;                for (candidate.previous.linkSlice()) |link| changed = self.addLink(existing.previous, link) or changed;                const candidate_precedence = candidate.previous.precedence + candidate.tree.dynamic_precedence;                if (candidate_precedence > node.precedence) {                    node.precedence = candidate_precedence;                    changed = true;                }                return changed;            }        }        if (node.link_count == node.links.len) return false;        node.links[node.link_count] = candidate;        node.link_count += 1;        node.precedence = @max(node.precedence, candidate.previous.precedence + candidate.tree.dynamic_precedence);        return true;    }};fn canMerge(left: *const Node, right: *const Node) bool {    return left.state == right.state and left.position.byte == right.position.byte;}fn linksEquivalent(left: Link, right: Link) bool {    if (left.tree == right.tree) return true;    return left.tree.symbol == right.tree.symbol and        left.tree.start.byte -| left.previous.position.byte == right.tree.start.byte -| right.previous.position.byte and        left.tree.end.byte -| left.tree.start.byte == right.tree.end.byte -| right.tree.start.byte and        left.tree.children.len == right.tree.children.len and        left.tree.extra == right.tree.extra and        scannerStatesEqual(left.tree.scanner_state, right.tree.scanner_state);}fn scannerStatesEqual(left: ?scanner.State, right: ?scanner.State) bool {    if (left) |left_state| {        if (right) |right_state| return left_state.eql(right_state);        return left_state.isEmpty();    }    return right == null or right.?.isEmpty();}pub const Branch = struct {    head: *Node,    position: lexer.Position,    scanner_state: scanner.State,    pub fn init(context: *Context, kind: scanner.Kind) Branch {        return .{            .head = context.base,            .position = .{ .byte = 0, .point = .{ .row = 0, .column = 0 } },            .scanner_state = scanner.State.init(kind),        };    }    pub fn deinit(self: *Branch, _: std.mem.Allocator) void {        self.* = undefined;    }    pub fn clone(self: Branch, _: std.mem.Allocator) !Branch {        return self;    }    pub fn state(self: Branch) abi.State {        return self.head.state;    }    pub fn shift(        self: *Branch,        context: *Context,        tree: *const subtree.Subtree,        state_after: abi.State,        position: lexer.Position,        scanner_after: scanner.State,    ) !void {        self.head = try context.push(self.head, tree, state_after);        self.position = position;        self.scanner_state = scanner_after;    }    pub fn reduce(        self: Branch,        allocator: std.mem.Allocator,        context: *Context,        tree: *subtree.Tree,        lang: language.Language,        action: abi.Reduce,        output: *std.ArrayList(Branch),        budget: Budget,    ) !void {        var candidates: std.ArrayList(Reduction) = .empty;        defer deinitReductions(allocator, &candidates);        var visit = ReductionVisit{            .allocator = allocator,            .tree = tree,            .action = action,            .position = self.position,            .candidates = &candidates,        };        try walkPaths(            allocator,            self.head,            action.child_count,            context.limits.iterator_count,            budget,            &visit,            visitReduction,        );        for (candidates.items) |candidate| {            const next_state = lang.nextState(candidate.predecessor.state, action.symbol);            if (next_state == 0) continue;            var branch = Branch{                .head = candidate.predecessor,                .position = self.position,                .scanner_state = self.scanner_state,            };            branch.head = try context.push(branch.head, candidate.parent, next_state);            for (candidate.extras.items) |extra| branch.head = try context.push(branch.head, extra, next_state);            try output.append(allocator, branch);        }    }    pub fn acceptedRoot(        self: Branch,        allocator: std.mem.Allocator,        context: *Context,        tree: *subtree.Tree,        budget: Budget,    ) !?*const subtree.Subtree {        var visit = RootVisit{ .allocator = allocator, .tree = tree };        try walkPaths(            allocator,            self.head,            null,            context.limits.iterator_count,            budget,            &visit,            visitRoot,        );        return visit.selected;    }    pub fn precedence(self: Branch) i64 {        return self.head.precedence;    }    pub fn equivalent(left: Branch, right: Branch) bool {        return left.state() == right.state() and            std.meta.eql(left.position, right.position) and            left.scanner_state.eql(right.scanner_state);    }};const PathIterator = struct {    node: *Node,    structural_count: usize,    path: std.ArrayList(*const subtree.Subtree) = .empty,};fn walkPaths(    allocator: std.mem.Allocator,    head: *Node,    goal: ?usize,    iterator_count: usize,    budget: Budget,    context: anytype,    comptime visit: anytype,) !void {    var iterators: std.ArrayList(PathIterator) = .empty;    defer {        for (iterators.items) |*iterator| iterator.path.deinit(allocator);        iterators.deinit(allocator);    }    try iterators.append(allocator, .{ .node = head, .structural_count = 0 });    while (iterators.items.len > 0) {        var index: usize = 0;        var round_count = iterators.items.len;        while (index < round_count) {            try budget.charge();            const node = iterators.items[index].node;            const complete = if (goal) |count| iterators.items[index].structural_count == count else node.link_count == 0;            if (complete or node.link_count == 0) {                if (complete) try visit(context, node, iterators.items[index].path.items);                var finished = iterators.orderedRemove(index);                finished.path.deinit(allocator);                round_count -= 1;                continue;            }            const alternative_count = @min(                @as(usize, node.link_count - 1),                iterator_count -| iterators.items.len,            );            try iterators.ensureUnusedCapacity(allocator, alternative_count);            for (node.links[1 .. alternative_count + 1]) |link| {                var alternative = PathIterator{                    .node = link.previous,                    .structural_count = iterators.items[index].structural_count + @intFromBool(!link.tree.extra),                };                errdefer alternative.path.deinit(allocator);                try alternative.path.appendSlice(allocator, iterators.items[index].path.items);                try alternative.path.append(allocator, link.tree);                iterators.appendAssumeCapacity(alternative);            }            const first = node.links[0];            iterators.items[index].node = first.previous;            iterators.items[index].structural_count += @intFromBool(!first.tree.extra);            try iterators.items[index].path.append(allocator, first.tree);            index += 1;        }    }}const ReductionVisit = struct {    allocator: std.mem.Allocator,    tree: *subtree.Tree,    action: abi.Reduce,    position: lexer.Position,    candidates: *std.ArrayList(Reduction),};fn visitReduction(visit: *ReductionVisit, predecessor: *Node, reverse_path: []const *const subtree.Subtree) !void {    try addReduction(visit.allocator, visit.tree, visit.action, visit.position, predecessor, reverse_path, visit.candidates);}const RootVisit = struct {    allocator: std.mem.Allocator,    tree: *subtree.Tree,    selected: ?*const subtree.Subtree = null,};fn visitRoot(visit: *RootVisit, _: *Node, reverse_path: []const *const subtree.Subtree) !void {    const candidate = try rootForPath(visit.allocator, visit.tree, reverse_path) orelse return;    if (visit.selected == null or try preferred(visit.allocator, visit.selected.?, candidate) == candidate) visit.selected = candidate;}const Reduction = struct {    predecessor: *Node,    parent: *const subtree.Subtree,    extras: std.ArrayList(*const subtree.Subtree),};fn addReduction(    allocator: std.mem.Allocator,    tree: *subtree.Tree,    action: abi.Reduce,    position: lexer.Position,    predecessor: *Node,    reverse_path: []const *const subtree.Subtree,    candidates: *std.ArrayList(Reduction),) !void {    var children: std.ArrayList(*const subtree.Subtree) = .empty;    defer children.deinit(allocator);    try children.ensureTotalCapacity(allocator, reverse_path.len);    var index = reverse_path.len;    while (index > 0) {        index -= 1;        children.appendAssumeCapacity(reverse_path[index]);    }    var children_end = children.items.len;    while (children_end > 0 and children.items[children_end - 1].extra) children_end -= 1;    const parent = try tree.node(        action.symbol,        children.items[0..children_end],        action.production_id,        predecessor.state,        action.dynamic_precedence,        position,        false,    );    var extras: std.ArrayList(*const subtree.Subtree) = .empty;    errdefer extras.deinit(allocator);    try extras.appendSlice(allocator, children.items[children_end..]);    for (candidates.items) |*candidate| {        if (candidate.predecessor != predecessor) continue;        const winner = try preferred(allocator, candidate.parent, parent);        if (winner == parent) {            candidate.parent = parent;            candidate.extras.deinit(allocator);            candidate.extras = extras;        } else {            extras.deinit(allocator);        }        return;    }    try candidates.append(allocator, .{        .predecessor = predecessor,        .parent = parent,        .extras = extras,    });}fn preferred(allocator: std.mem.Allocator, left: *const subtree.Subtree, right: *const subtree.Subtree) !*const subtree.Subtree {    if (left.dynamic_precedence > right.dynamic_precedence) return left;    if (right.dynamic_precedence > left.dynamic_precedence) return right;    return if (try subtree.order(allocator, left, right) == .gt) right else left;}fn rootForPath(    allocator: std.mem.Allocator,    tree: *subtree.Tree,    reverse_path: []const *const subtree.Subtree,) !?*const subtree.Subtree {    var entries: std.ArrayList(*const subtree.Subtree) = .empty;    defer entries.deinit(allocator);    try entries.ensureTotalCapacity(allocator, reverse_path.len);    var reverse_index = reverse_path.len;    while (reverse_index > 0) {        reverse_index -= 1;        entries.appendAssumeCapacity(reverse_path[reverse_index]);    }    var root_index = entries.items.len;    while (root_index > 0) {        root_index -= 1;        if (!entries.items[root_index].extra) break;    } else return null;    const base = entries.items[root_index];    if (root_index == 0 and entries.items.len == 1) return base;    var children: std.ArrayList(*const subtree.Subtree) = .empty;    defer children.deinit(allocator);    try children.ensureTotalCapacity(allocator, entries.items.len - 1 + base.children.len);    for (entries.items[0..root_index]) |entry| children.appendAssumeCapacity(entry);    for (base.children) |child| children.appendAssumeCapacity(child);    for (entries.items[root_index + 1 ..]) |entry| children.appendAssumeCapacity(entry);    var child_precedence: i32 = 0;    for (base.children) |child| child_precedence += child.dynamic_precedence;    return try tree.node(        base.symbol,        children.items,        base.production_id,        base.parse_state,        base.dynamic_precedence - child_precedence,        base.start,        base.extra,    );}fn deinitReductions(allocator: std.mem.Allocator, reductions: *std.ArrayList(Reduction)) void {    for (reductions.items) |*reduction| reduction.extras.deinit(allocator);    reductions.deinit(allocator);}test "graph stack merges parser heads without merging scanner states" {    var context = try Context.init(std.testing.allocator, testing_limits);    defer context.deinit();    const left = Branch.init(&context, .cpp);    var right = Branch.init(&context, .cpp);    try std.testing.expect(left.equivalent(right));    right.scanner_state.cpp.delimiter_length = 1;    try std.testing.expect(!left.equivalent(right));}test "focused graph links compare serialized external scanner state" {    var context = try Context.init(std.testing.allocator, testing_limits);    defer context.deinit();    var left_tree = subtree.Subtree{        .symbol = 1,        .children = &.{},        .start = .{ .byte = 0, .point = .{ .row = 0, .column = 0 } },        .end = .{ .byte = 1, .point = .{ .row = 0, .column = 1 } },        .production_id = 0,        .dynamic_precedence = 0,        .parse_state = 1,        .visible_descendant_count = 0,        .named_descendant_count = 0,        .visible = true,        .named = true,        .extra = false,        .scanner_state = null,    };    var right_tree = left_tree;    var empty_state = scanner.State.init(.cpp);    left_tree.scanner_state = empty_state;    try std.testing.expect(linksEquivalent(        .{ .previous = context.base, .tree = &left_tree },        .{ .previous = context.base, .tree = &right_tree },    ));    empty_state.cpp.delimiter_length = 1;    right_tree.scanner_state = empty_state;    try std.testing.expect(!linksEquivalent(        .{ .previous = context.base, .tree = &left_tree },        .{ .previous = context.base, .tree = &right_tree },    ));}test "focused saturated graph stack ignores precedence from a dropped ninth link" {    var context = try Context.init(std.testing.allocator, testing_limits);    defer context.deinit();    var trees: [9]subtree.Subtree = undefined;    for (&trees, 0..) |*tree, index| tree.* = .{        .symbol = @intCast(index + 1),        .children = &.{},        .start = .{ .byte = 0, .point = .{ .row = 0, .column = 0 } },        .end = .{ .byte = 1, .point = .{ .row = 0, .column = 1 } },        .production_id = 0,        .dynamic_precedence = if (index == 8) 100 else 0,        .parse_state = 1,        .visible_descendant_count = 0,        .named_descendant_count = 0,        .visible = true,        .named = true,        .extra = false,        .scanner_state = null,    };    var left_links: [testing_limits.link_count]Link = undefined;    var left = Node{        .state = 2,        .position = trees[0].end,        .links = &left_links,        .link_count = testing_limits.link_count,        .precedence = 0,    };    for (left.links[0..testing_limits.link_count], 0..) |*link, index| link.* = .{ .previous = context.base, .tree = &trees[index] };    var right_links: [testing_limits.link_count]Link = undefined;    var right = Node{        .state = 2,        .position = trees[8].end,        .links = &right_links,        .link_count = 1,        .precedence = 100,    };    right.links[0] = .{ .previous = context.base, .tree = &trees[8] };    try std.testing.expect(!try context.merge(&left, &right));    try std.testing.expectEqual(testing_limits.link_count, left.link_count);    try std.testing.expectEqual(@as(i64, 0), left.precedence);}const WalkWitness = struct {    count: usize = 0,    expected_bottom: *const subtree.Subtree,};fn visitWalkWitness(witness: *WalkWitness, _: *Node, path: []const *const subtree.Subtree) !void {    try std.testing.expectEqual(@as(usize, 3), path.len);    try std.testing.expectEqual(witness.expected_bottom, path[2]);    witness.count += 1;}test "focused saturated pop-count and pop-all retain the pinned 64-path frontier" {    var context = try Context.init(std.testing.allocator, testing_limits);    defer context.deinit();    var trees: [24]subtree.Subtree = undefined;    for (&trees, 0..) |*tree, index| tree.* = .{        .symbol = @intCast(index + 1),        .children = &.{},        .start = .{ .byte = @intCast(index), .point = .{ .row = 0, .column = @intCast(index) } },        .end = .{ .byte = @intCast(index + 1), .point = .{ .row = 0, .column = @intCast(index + 1) } },        .production_id = 0,        .dynamic_precedence = 0,        .parse_state = 1,        .visible_descendant_count = 0,        .named_descendant_count = 0,        .visible = true,        .named = true,        .extra = false,        .scanner_state = null,    };    var nodes: [3]Node = undefined;    var node_links: [3][testing_limits.link_count]Link = undefined;    for (&nodes, 0..) |*node, level| {        node.* = .{            .state = @intCast(level + 2),            .position = trees[level * testing_limits.link_count].end,            .links = &node_links[level],            .link_count = testing_limits.link_count,            .precedence = 0,        };        const previous = if (level == 0) context.base else &nodes[level - 1];        for (node.links[0..testing_limits.link_count], 0..) |*link, index| {            link.* = .{ .previous = previous, .tree = &trees[level * testing_limits.link_count + index] };        }    }    for ([_]?usize{ 3, null }) |goal| {        var operations: usize = 0;        var witness = WalkWitness{ .expected_bottom = &trees[0] };        try walkPaths(            std.testing.allocator,            &nodes[2],            goal,            testing_limits.iterator_count,            .{ .operations = &operations, .limit = 137 },            &witness,            visitWalkWitness,        );        try std.testing.expectEqual(@as(usize, 64), witness.count);        try std.testing.expectEqual(@as(usize, 137), operations);    }    var operations: usize = 0;    var witness = WalkWitness{ .expected_bottom = &trees[0] };    try std.testing.expectError(        error.OperationLimitExceeded,        walkPaths(            std.testing.allocator,            &nodes[2],            3,            testing_limits.iterator_count,            .{ .operations = &operations, .limit = 136 },            &witness,            visitWalkWitness,        ),    );}

Complete caller list for tree.runtime.stack.Context.deinit

10 direct callers.

Complete caller list for tree.runtime.stack.Context.init

10 direct callers.

Audit

Definitions16
Public names16
Members8
Version26.7.0
Revisiondaab053ee433