280 lines
8.0 KiB
Go
280 lines
8.0 KiB
Go
package dfa
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"gitea.xintech.co/zhouzhihong/lexmachine/frontend"
|
|
)
|
|
|
|
// LabeledAST is a post-order labeled version of the AST. The root the node will be Order[len(Order)-1].
|
|
type LabeledAST struct {
|
|
Root frontend.AST // root of the AST
|
|
Order []frontend.AST // post-order labeling of the all the nodes
|
|
Kids [][]int // a lookup table of location for each of the nodes children
|
|
Positions []int // a labeling of all the position nodes (Character/Range) in the AST.
|
|
posmap map[int]int // maps an order index to a pos index.
|
|
Matches []int // the index (in Positions) of all the EOS nodes
|
|
nullable []bool
|
|
first [][]int
|
|
last [][]int
|
|
follow []map[int]bool
|
|
}
|
|
|
|
// Label a tree and its "positions" (Character/Range nodes) in post-order
|
|
// notation.
|
|
func Label(ast frontend.AST) *LabeledAST {
|
|
type entry struct {
|
|
n frontend.AST
|
|
i int
|
|
kids []int
|
|
}
|
|
order := make([]frontend.AST, 0, 10)
|
|
children := make([][]int, 0, 10)
|
|
positions := make([]int, 0, 10)
|
|
matches := make([]int, 0, 10)
|
|
posmap := make(map[int]int)
|
|
stack := make([]entry, 0, 10)
|
|
stack = append(stack, entry{ast, 0, []int{}})
|
|
for len(stack) > 0 {
|
|
var c entry
|
|
stack, c = stack[:len(stack)-1], stack[len(stack)-1]
|
|
kids := c.n.Children()
|
|
for c.i < len(kids) {
|
|
kid := kids[c.i]
|
|
stack = append(stack, entry{c.n, c.i + 1, c.kids})
|
|
c = entry{kid, 0, []int{}}
|
|
kids = c.n.Children()
|
|
}
|
|
oid := len(order)
|
|
if len(stack) > 0 {
|
|
stack[len(stack)-1].kids = append(stack[len(stack)-1].kids, oid)
|
|
}
|
|
order = append(order, c.n)
|
|
children = append(children, c.kids)
|
|
switch c.n.(type) {
|
|
case *frontend.Character, *frontend.Range:
|
|
posmap[oid] = len(positions)
|
|
positions = append(positions, oid)
|
|
case *frontend.EOS:
|
|
pid := len(positions)
|
|
posmap[oid] = pid
|
|
positions = append(positions, oid)
|
|
matches = append(matches, pid)
|
|
}
|
|
}
|
|
return &LabeledAST{
|
|
Root: ast,
|
|
Order: order,
|
|
Kids: children,
|
|
Positions: positions,
|
|
posmap: posmap,
|
|
Matches: matches,
|
|
}
|
|
}
|
|
|
|
func (a *LabeledAST) pos(oid int) int {
|
|
if pid, has := a.posmap[oid]; !has {
|
|
panic("Passed a bad order id into Position (likely used a non-position node's id)")
|
|
} else {
|
|
return pid
|
|
}
|
|
}
|
|
|
|
// Follow computes look up tables for each Position (leaf node) in the tree
|
|
// which indicates what other Position could follow the current position in a
|
|
// matching string. It also computes what positions appear first in the tree.
|
|
func (a *LabeledAST) Follow() (firstOfTree []int, follow []map[int]bool) {
|
|
positions := a.Positions
|
|
nullable := a.MatchesEmptyString()
|
|
first := a.First()
|
|
last := a.Last()
|
|
|
|
// get the first of the whole ast by retrieving the first for the root (len(order)-1).
|
|
firstOfTree = make([]int, 0, len(first[len(a.Order)-1]))
|
|
for _, p := range first[len(a.Order)-1] {
|
|
firstOfTree = append(firstOfTree, p)
|
|
}
|
|
|
|
if a.follow != nil {
|
|
return firstOfTree, a.follow
|
|
}
|
|
|
|
follow = make([]map[int]bool, len(positions))
|
|
for i := range follow {
|
|
follow[i] = make(map[int]bool)
|
|
}
|
|
for i, node := range a.Order {
|
|
switch n := node.(type) {
|
|
case *frontend.Concat:
|
|
for x := 0; x < len(n.Items)-1; x++ {
|
|
j := a.Kids[i][x]
|
|
kFirst := make([]int, 0, 10)
|
|
for y := x + 1; y < len(n.Items); y++ {
|
|
k := a.Kids[i][y]
|
|
for _, p := range first[k] {
|
|
kFirst = append(kFirst, p)
|
|
}
|
|
if !nullable[k] {
|
|
break
|
|
}
|
|
}
|
|
for _, p := range last[j] {
|
|
for _, q := range kFirst {
|
|
follow[p][q] = true
|
|
}
|
|
}
|
|
}
|
|
case *frontend.Star, *frontend.Plus:
|
|
nFirst := make([]int, 0, 10)
|
|
for _, p := range first[i] {
|
|
nFirst = append(nFirst, p)
|
|
}
|
|
for _, p := range last[i] {
|
|
for _, q := range nFirst {
|
|
follow[p][q] = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
a.follow = follow
|
|
return firstOfTree, follow
|
|
}
|
|
|
|
// MatchesEmptyString computes a look up table for each node in the tree (in
|
|
// post order, matching the Order member) on whether or not the subtree rooted
|
|
// in each node matches the empty string.
|
|
func (a *LabeledAST) MatchesEmptyString() (nullable []bool) {
|
|
if a.nullable != nil {
|
|
return a.nullable
|
|
}
|
|
nullable = make([]bool, 0, len(a.Order))
|
|
for i, node := range a.Order {
|
|
switch n := node.(type) {
|
|
case *frontend.EOS:
|
|
nullable = append(nullable, true)
|
|
case *frontend.Character:
|
|
nullable = append(nullable, false)
|
|
case *frontend.Range:
|
|
nullable = append(nullable, false)
|
|
case *frontend.Maybe:
|
|
nullable = append(nullable, true)
|
|
case *frontend.Plus:
|
|
nullable = append(nullable, nullable[a.Kids[i][0]])
|
|
case *frontend.Star:
|
|
nullable = append(nullable, true)
|
|
case *frontend.Match:
|
|
nullable = append(nullable, nullable[a.Kids[i][0]])
|
|
case *frontend.Alternation:
|
|
nullable = append(nullable, nullable[a.Kids[i][0]] || nullable[a.Kids[i][1]])
|
|
case *frontend.AltMatch:
|
|
nullable = append(nullable, nullable[a.Kids[i][0]] || nullable[a.Kids[i][1]])
|
|
case *frontend.Concat:
|
|
epsilon := true
|
|
for _, j := range a.Kids[i] {
|
|
epsilon = epsilon && nullable[j]
|
|
}
|
|
nullable = append(nullable, epsilon)
|
|
default:
|
|
panic(fmt.Errorf("Unexpected type %T", n))
|
|
}
|
|
}
|
|
a.nullable = nullable
|
|
return nullable
|
|
}
|
|
|
|
// First computes a look up table for each node in the tree (in post order,
|
|
// matching the Order member) indicating for the subtree rooted at each node
|
|
// which positions (leaf nodes indexes in the Positions slice) will appear at
|
|
// the beginning of a string matching that subtree.
|
|
func (a *LabeledAST) First() (first [][]int) {
|
|
if a.first != nil {
|
|
return a.first
|
|
}
|
|
nullable := a.MatchesEmptyString()
|
|
first = make([][]int, 0, len(a.Order))
|
|
for i, node := range a.Order {
|
|
switch n := node.(type) {
|
|
case *frontend.EOS:
|
|
first = append(first, []int{a.pos(i)})
|
|
case *frontend.Character:
|
|
first = append(first, []int{a.pos(i)})
|
|
case *frontend.Range:
|
|
first = append(first, []int{a.pos(i)})
|
|
case *frontend.Maybe:
|
|
first = append(first, first[a.Kids[i][0]])
|
|
case *frontend.Plus:
|
|
first = append(first, first[a.Kids[i][0]])
|
|
case *frontend.Star:
|
|
first = append(first, first[a.Kids[i][0]])
|
|
case *frontend.Match:
|
|
first = append(first, first[a.Kids[i][0]])
|
|
case *frontend.Alternation:
|
|
first = append(first, append(first[a.Kids[i][0]], first[a.Kids[i][1]]...))
|
|
case *frontend.AltMatch:
|
|
first = append(first, append(first[a.Kids[i][0]], first[a.Kids[i][1]]...))
|
|
case *frontend.Concat:
|
|
f := make([]int, 0, len(n.Items))
|
|
for _, j := range a.Kids[i] {
|
|
f = append(f, first[j]...)
|
|
if !nullable[j] {
|
|
break
|
|
}
|
|
}
|
|
first = append(first, f)
|
|
default:
|
|
panic(fmt.Errorf("Unexpected type %T", n))
|
|
}
|
|
}
|
|
a.first = first
|
|
return first
|
|
}
|
|
|
|
// Last computes a look up table for each node in the tree (in post order,
|
|
// matching the Order member) indicating for the subtree rooted at each node
|
|
// which positions (leaf nodes indexes in the Positions slice) will appear at
|
|
// the end of a string matching that subtree.
|
|
func (a *LabeledAST) Last() (last [][]int) {
|
|
if a.last != nil {
|
|
return a.last
|
|
}
|
|
nullable := a.MatchesEmptyString()
|
|
last = make([][]int, 0, len(a.Order))
|
|
for i, node := range a.Order {
|
|
switch n := node.(type) {
|
|
case *frontend.EOS:
|
|
last = append(last, []int{a.pos(i)})
|
|
case *frontend.Character:
|
|
last = append(last, []int{a.pos(i)})
|
|
case *frontend.Range:
|
|
last = append(last, []int{a.pos(i)})
|
|
case *frontend.Maybe:
|
|
last = append(last, last[a.Kids[i][0]])
|
|
case *frontend.Plus:
|
|
last = append(last, last[a.Kids[i][0]])
|
|
case *frontend.Star:
|
|
last = append(last, last[a.Kids[i][0]])
|
|
case *frontend.Match:
|
|
last = append(last, last[a.Kids[i][0]])
|
|
case *frontend.Alternation:
|
|
last = append(last, append(last[a.Kids[i][0]], last[a.Kids[i][1]]...))
|
|
case *frontend.AltMatch:
|
|
last = append(last, append(last[a.Kids[i][0]], last[a.Kids[i][1]]...))
|
|
case *frontend.Concat:
|
|
l := make([]int, 0, len(n.Items))
|
|
for x := len(n.Items) - 1; x >= 0; x-- {
|
|
j := a.Kids[i][x]
|
|
l = append(l, last[j]...)
|
|
if !nullable[j] {
|
|
break
|
|
}
|
|
}
|
|
last = append(last, l)
|
|
default:
|
|
panic(fmt.Errorf("Unexpected type %T", n))
|
|
}
|
|
}
|
|
a.last = last
|
|
return last
|
|
}
|