Initial QSfera import
This commit is contained in:
+132
@@ -0,0 +1,132 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user