Schedule sibling inheritance by dependency readiness

This commit is contained in:
2026-10-19 17:25:27 +00:00
parent a56d4691ff
commit 6e40c166d6
5 changed files with 50 additions and 296 deletions

View File

@@ -25,84 +25,6 @@ type inheritanceGraph struct {
byName map[string]int
}
// depthFirstTemplateOrder preserves the currently wired parent-first order
// while the loading path adopts the graph/application boundary.
func (graph *inheritanceGraph) depthFirstTemplateOrder() ([]int, []Diagnostic) {
const (
unvisited = uint8(0)
active = uint8(1)
finished = uint8(2)
)
state := make([]uint8, len(graph.nodes))
order := make([]int, 0, len(graph.nodes))
for start := range graph.nodes {
if state[start] != unvisited {
continue
}
stack := []int{start}
for len(stack) != 0 {
index := stack[len(stack)-1]
if state[index] == unvisited {
state[index] = active
node := graph.nodes[index]
if node.hasParent {
parent := node.parent
switch state[parent] {
case active:
referencePath := node.path.Key(inheritanceKey)
location := publicLocation(node.directive.Value.Location)
return nil, []Diagnostic{inheritanceDiagnostic(CodeInheritanceCycle, referencePath, location,
[]Location{publicLocation(graph.nodes[parent].entry.Value.Location)}, "sibling inheritance contains a cycle")}
case unvisited:
stack = append(stack, parent)
continue
}
}
}
state[index] = finished
order = append(order, index)
stack = stack[:len(stack)-1]
}
}
return order, nil
}
func (graph *inheritanceGraph) depthFirstControls() []inheritanceControl {
visited := make([]bool, len(graph.nodes))
controls := make([]inheritanceControl, 0, len(graph.nodes))
type frame struct {
index int
entered bool
}
for start := range graph.nodes {
if visited[start] {
continue
}
stack := []frame{{index: start}}
for len(stack) != 0 {
current := &stack[len(stack)-1]
node := graph.nodes[current.index]
if !current.entered {
current.entered = true
visited[current.index] = true
if node.hasDirective {
controls = append(controls, inheritanceControl{
path: node.path.Key(inheritanceKey),
locations: []Location{publicLocation(node.directive.Value.Location)},
cancelled: node.cancelled,
})
}
if node.hasParent && !visited[node.parent] {
stack = append(stack, frame{index: node.parent})
continue
}
}
stack = stack[:len(stack)-1]
}
}
return controls
}
// analyzeInheritanceGraph retains only direct mapping siblings as graph nodes.
// It validates declarations separately from applying their templates, leaving
// nested scopes for the existing destination-local walk.
@@ -197,15 +119,11 @@ func expandSiblingDefinitionsWithOrigins(scope *document.Node, scopePath Path, l
if len(diagnostics) != 0 {
return nil, diagnostics
}
order, diagnostics := graph.depthFirstTemplateOrder()
order, diagnostics := graph.schedule()
if len(diagnostics) != 0 {
return nil, diagnostics
}
result, diagnostics := applyInheritanceTemplates(graph, order, limits, inputOrigins, budget...)
if len(diagnostics) == 0 {
result.controls = graph.depthFirstControls()
}
return result, diagnostics
return applyInheritanceTemplates(graph, order, limits, inputOrigins, budget...)
}
// applyInheritanceTemplates builds detached templates in the supplied