mirror of
https://github.com/open-gitagent/langship.sh.git
synced 2026-08-03 07:21:04 +02:00
110 lines
2.5 KiB
Go
110 lines
2.5 KiB
Go
package engine
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
)
|
|
|
|
// LoopBody holds the body node names in topological order for inline execution.
|
|
type LoopBody struct {
|
|
Order []string
|
|
Nodes map[string]bool
|
|
TerminalNodes []string
|
|
}
|
|
|
|
// GetLoopBody identifies the loop body nodes and returns them in topological
|
|
// order for inline execution within the parent ExecutionContext.
|
|
func GetLoopBody(dag *DAG, loopNodeName string) (*LoopBody, error) {
|
|
var entryNodes []string
|
|
for _, edge := range dag.Adjacency[loopNodeName] {
|
|
if edge.SourceOutputIndex == 0 {
|
|
entryNodes = append(entryNodes, edge.Target)
|
|
}
|
|
}
|
|
if len(entryNodes) == 0 {
|
|
return nil, fmt.Errorf("loop node %q has no body (no output 0 connections)", loopNodeName)
|
|
}
|
|
|
|
continuationNodes := make(map[string]bool)
|
|
for _, edge := range dag.Adjacency[loopNodeName] {
|
|
if edge.SourceOutputIndex == 1 {
|
|
continuationNodes[edge.Target] = true
|
|
}
|
|
}
|
|
|
|
bodyNodes := make(map[string]bool)
|
|
queue := make([]string, len(entryNodes))
|
|
copy(queue, entryNodes)
|
|
for _, n := range entryNodes {
|
|
bodyNodes[n] = true
|
|
}
|
|
for len(queue) > 0 {
|
|
current := queue[0]
|
|
queue = queue[1:]
|
|
for _, edge := range dag.Adjacency[current] {
|
|
next := edge.Target
|
|
if next == loopNodeName || continuationNodes[next] || bodyNodes[next] {
|
|
continue
|
|
}
|
|
bodyNodes[next] = true
|
|
queue = append(queue, next)
|
|
}
|
|
}
|
|
|
|
inDegree := make(map[string]int, len(bodyNodes))
|
|
for name := range bodyNodes {
|
|
inDegree[name] = 0
|
|
}
|
|
for name := range bodyNodes {
|
|
for _, edge := range dag.Adjacency[name] {
|
|
if bodyNodes[edge.Target] {
|
|
inDegree[edge.Target]++
|
|
}
|
|
}
|
|
}
|
|
|
|
var topoQueue []string
|
|
for name, deg := range inDegree {
|
|
if deg == 0 {
|
|
topoQueue = append(topoQueue, name)
|
|
}
|
|
}
|
|
sort.Strings(topoQueue)
|
|
|
|
var order []string
|
|
for len(topoQueue) > 0 {
|
|
current := topoQueue[0]
|
|
topoQueue = topoQueue[1:]
|
|
order = append(order, current)
|
|
for _, edge := range dag.Adjacency[current] {
|
|
if bodyNodes[edge.Target] {
|
|
inDegree[edge.Target]--
|
|
if inDegree[edge.Target] == 0 {
|
|
topoQueue = append(topoQueue, edge.Target)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var terminalNodes []string
|
|
for name := range bodyNodes {
|
|
isTerminal := true
|
|
for _, edge := range dag.Adjacency[name] {
|
|
if bodyNodes[edge.Target] {
|
|
isTerminal = false
|
|
break
|
|
}
|
|
}
|
|
if isTerminal {
|
|
terminalNodes = append(terminalNodes, name)
|
|
}
|
|
}
|
|
sort.Strings(terminalNodes)
|
|
|
|
return &LoopBody{
|
|
Order: order,
|
|
Nodes: bodyNodes,
|
|
TerminalNodes: terminalNodes,
|
|
}, nil
|
|
}
|