aboutsummaryrefslogtreecommitdiff
path: root/grammar/follow.go
diff options
context:
space:
mode:
Diffstat (limited to 'grammar/follow.go')
-rw-r--r--grammar/follow.go197
1 files changed, 197 insertions, 0 deletions
diff --git a/grammar/follow.go b/grammar/follow.go
new file mode 100644
index 0000000..f835bba
--- /dev/null
+++ b/grammar/follow.go
@@ -0,0 +1,197 @@
+package grammar
+
+import (
+ "fmt"
+)
+
+type followEntry struct {
+ symbols map[symbol]struct{}
+ eof bool
+}
+
+func newFollowEntry() *followEntry {
+ return &followEntry{
+ symbols: map[symbol]struct{}{},
+ eof: false,
+ }
+}
+
+func (e *followEntry) add(sym symbol) bool {
+ if _, ok := e.symbols[sym]; ok {
+ return false
+ }
+ e.symbols[sym] = struct{}{}
+ return true
+}
+
+func (e *followEntry) addEOF() bool {
+ if !e.eof {
+ e.eof = true
+ return true
+ }
+ return false
+}
+
+func (e *followEntry) merge(fst *firstEntry, flw *followEntry) bool {
+ changed := false
+
+ if fst != nil {
+ for sym := range fst.symbols {
+ added := e.add(sym)
+ if added {
+ changed = true
+ }
+ }
+ }
+
+ if flw != nil {
+ for sym := range flw.symbols {
+ added := e.add(sym)
+ if added {
+ changed = true
+ }
+ }
+ if flw.eof {
+ added := e.addEOF()
+ if added {
+ changed = true
+ }
+ }
+ }
+
+ return changed
+}
+
+type followSet struct {
+ set map[symbol]*followEntry
+}
+
+func newFollow(prods *productionSet) *followSet {
+ flw := &followSet{
+ set: map[symbol]*followEntry{},
+ }
+ for _, prod := range prods.getAllProductions() {
+ if _, ok := flw.set[prod.lhs]; ok {
+ continue
+ }
+ flw.set[prod.lhs] = newFollowEntry()
+ }
+ return flw
+}
+
+func (flw *followSet) find(sym symbol) (*followEntry, error) {
+ e, ok := flw.set[sym]
+ if !ok {
+ return nil, fmt.Errorf("an entry of FOLLOW was not found; symbol: %s", sym)
+ }
+ return e, nil
+}
+
+type followComContext struct {
+ prods *productionSet
+ first *firstSet
+ follow *followSet
+}
+
+func newFollowComContext(prods *productionSet, first *firstSet) *followComContext {
+ return &followComContext{
+ prods: prods,
+ first: first,
+ follow: newFollow(prods),
+ }
+}
+
+func genFollowSet(prods *productionSet, first *firstSet) (*followSet, error) {
+ ntsyms := map[symbol]struct{}{}
+ for _, prod := range prods.getAllProductions() {
+ if _, ok := ntsyms[prod.lhs]; ok {
+ continue
+ }
+ ntsyms[prod.lhs] = struct{}{}
+ }
+
+ cc := newFollowComContext(prods, first)
+ for {
+ more := false
+ for ntsym := range ntsyms {
+ e, err := cc.follow.find(ntsym)
+ if err != nil {
+ return nil, err
+ }
+ if ntsym.isStart() {
+ changed := e.addEOF()
+ if changed {
+ more = true
+ }
+ }
+ for _, prod := range prods.getAllProductions() {
+ for i, sym := range prod.rhs {
+ if sym != ntsym {
+ continue
+ }
+ fst, err := first.find(prod, i+1)
+ if err != nil {
+ return nil, err
+ }
+ changed := e.merge(fst, nil)
+ if changed {
+ more = true
+ }
+ if fst.empty {
+ flw, err := cc.follow.find(prod.lhs)
+ if err != nil {
+ return nil, err
+ }
+ changed := e.merge(nil, flw)
+ if changed {
+ more = true
+ }
+ }
+ }
+ }
+ }
+ if !more {
+ break
+ }
+ }
+
+ return cc.follow, nil
+}
+
+func genFollowEntry(cc *followComContext, acc *followEntry, ntsym symbol) (bool, error) {
+ changed := false
+
+ if ntsym.isStart() {
+ added := acc.addEOF()
+ if added {
+ changed = true
+ }
+ }
+ for _, prod := range cc.prods.getAllProductions() {
+ for i, sym := range prod.rhs {
+ if sym != ntsym {
+ continue
+ }
+ fst, err := cc.first.find(prod, i+1)
+ if err != nil {
+ return false, err
+ }
+ added := acc.merge(fst, nil)
+ if added {
+ changed = true
+ }
+ if fst.empty {
+ flw, err := cc.follow.find(prod.lhs)
+ if err != nil {
+ return false, err
+ }
+ added := acc.merge(nil, flw)
+ if added {
+ changed = true
+ }
+ }
+ }
+ }
+
+ return changed, nil
+}