mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-01-06 05:01:10 -05:00
Bumps [github.com/mna/pigeon](https://github.com/mna/pigeon) from 1.1.0 to 1.2.1. - [Release notes](https://github.com/mna/pigeon/releases) - [Changelog](https://github.com/mna/pigeon/blob/master/.goreleaser.yml) - [Commits](https://github.com/mna/pigeon/compare/v1.1.0...v1.2.1) --- updated-dependencies: - dependency-name: github.com/mna/pigeon dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
133 lines
3.4 KiB
Go
133 lines
3.4 KiB
Go
package builder
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/mna/pigeon/ast"
|
|
)
|
|
|
|
var (
|
|
// ErrNoLeader is no leader error.
|
|
ErrNoLeader = errors.New(
|
|
"SCC has no leadership candidate (no element is included in all cycles)")
|
|
// ErrHaveLeftRecursion is recursion error.
|
|
ErrHaveLeftRecursion = errors.New("grammar contains left recursion")
|
|
)
|
|
|
|
// PrepareGrammar evaluates parameters associated with left recursion.
|
|
func PrepareGrammar(grammar *ast.Grammar) (bool, error) {
|
|
mapRules := make(map[string]*ast.Rule, len(grammar.Rules))
|
|
for _, rule := range grammar.Rules {
|
|
mapRules[rule.Name.Val] = rule
|
|
}
|
|
ComputeNullables(mapRules)
|
|
haveLeftRecursion, err := ComputeLeftRecursives(mapRules)
|
|
if err != nil {
|
|
return false, fmt.Errorf("error compute left recursive: %w", err)
|
|
}
|
|
return haveLeftRecursion, nil
|
|
}
|
|
|
|
// ComputeNullables evaluates nullable nodes.
|
|
func ComputeNullables(rules map[string]*ast.Rule) {
|
|
// Compute which rules in a grammar are nullable
|
|
for _, rule := range rules {
|
|
rule.NullableVisit(rules)
|
|
}
|
|
}
|
|
|
|
func findLeader(
|
|
graph map[string]map[string]struct{}, scc map[string]struct{},
|
|
) (string, error) {
|
|
// Try to find a leader such that all cycles go through it.
|
|
leaders := make(map[string]struct{}, len(scc))
|
|
for k := range scc {
|
|
leaders[k] = struct{}{}
|
|
}
|
|
for start := range scc {
|
|
cycles, err := FindCyclesInSCC(graph, scc, start)
|
|
if err != nil {
|
|
return "", fmt.Errorf("error find cycles: %w", err)
|
|
}
|
|
for _, cycle := range cycles {
|
|
mapCycle := make(map[string]struct{}, len(cycle))
|
|
for _, k := range cycle {
|
|
mapCycle[k] = struct{}{}
|
|
}
|
|
for k := range scc {
|
|
if _, okCycle := mapCycle[k]; !okCycle {
|
|
delete(leaders, k)
|
|
}
|
|
}
|
|
if len(leaders) == 0 {
|
|
return "", ErrNoLeader
|
|
}
|
|
}
|
|
}
|
|
// Pick an arbitrary leader from the candidates.
|
|
var leader string
|
|
for k := range leaders {
|
|
leader = k // The only element.
|
|
break
|
|
}
|
|
return leader, nil
|
|
}
|
|
|
|
// ComputeLeftRecursives evaluates left recursion.
|
|
func ComputeLeftRecursives(rules map[string]*ast.Rule) (bool, error) {
|
|
graph := MakeFirstGraph(rules)
|
|
vertices := make([]string, 0, len(graph))
|
|
haveLeftRecursion := false
|
|
for k := range graph {
|
|
vertices = append(vertices, k)
|
|
}
|
|
sccs := StronglyConnectedComponents(vertices, graph)
|
|
for _, scc := range sccs {
|
|
if len(scc) > 1 {
|
|
for name := range scc {
|
|
rules[name].LeftRecursive = true
|
|
haveLeftRecursion = true
|
|
}
|
|
leader, err := findLeader(graph, scc)
|
|
if err != nil {
|
|
return false, fmt.Errorf("error find leader %v: %w", scc, err)
|
|
}
|
|
rules[leader].Leader = true
|
|
} else {
|
|
var name string
|
|
for k := range scc {
|
|
name = k // The only element.
|
|
break
|
|
}
|
|
if _, ok := graph[name][name]; ok {
|
|
rules[name].LeftRecursive = true
|
|
rules[name].Leader = true
|
|
haveLeftRecursion = true
|
|
}
|
|
}
|
|
}
|
|
return haveLeftRecursion, nil
|
|
}
|
|
|
|
// MakeFirstGraph compute the graph of left-invocations.
|
|
// There's an edge from A to B if A may invoke B at its initial position.
|
|
// Note that this requires the nullable flags to have been computed.
|
|
func MakeFirstGraph(rules map[string]*ast.Rule) map[string]map[string]struct{} {
|
|
graph := make(map[string]map[string]struct{})
|
|
vertices := make(map[string]struct{})
|
|
for rulename, rule := range rules {
|
|
names := rule.InitialNames()
|
|
graph[rulename] = names
|
|
for name := range names {
|
|
vertices[name] = struct{}{}
|
|
}
|
|
}
|
|
for vertex := range vertices {
|
|
if _, ok := graph[vertex]; !ok {
|
|
graph[vertex] = make(map[string]struct{})
|
|
}
|
|
}
|
|
return graph
|
|
}
|