From b1967f504002f0e05ad4913fe1364059412a8af0 Mon Sep 17 00:00:00 2001 From: keyuchang Date: Wed, 16 Feb 2022 14:10:12 +0800 Subject: [PATCH] Parser: use packed sparse matrix for state table #2 --- Builder/GoGenCode.go | 10 ++- Builder/GoGenPackTableCode.go | 112 ++++++++++++++++++++++++++++++++++ LALR/LALR.go | 21 +++++++ LALR/LALR_test.go | 8 +++ Parser/utils.go | 4 +- Utils/packtable.go | 93 ++++++++++++++++++++++++++++ Utils/packtable_test.go | 52 ++++++++++++++++ examples/ladd.y | 4 +- 8 files changed, 300 insertions(+), 4 deletions(-) create mode 100644 Builder/GoGenPackTableCode.go create mode 100644 Utils/packtable.go create mode 100644 Utils/packtable_test.go diff --git a/Builder/GoGenCode.go b/Builder/GoGenCode.go index d729e65..e180a79 100644 --- a/Builder/GoGenCode.go +++ b/Builder/GoGenCode.go @@ -247,8 +247,14 @@ func GoGenFromString(input string, file string) error { b := NewGoBuilder(w) b.buildConstPart() b.buildUionAndCode() - b.buildAnalyTable() - b.buildStateFunc() + if b.vnode.NeedPacked { + b.buildAnalyPackTable() + b.buildPackStateFunc() + } else { + b.buildAnalyTable() + b.buildStateFunc() + } + b.buildReduceFunc() b.buildTranslate() // Create file and write to it diff --git a/Builder/GoGenPackTableCode.go b/Builder/GoGenPackTableCode.go new file mode 100644 index 0000000..b2268cd --- /dev/null +++ b/Builder/GoGenPackTableCode.go @@ -0,0 +1,112 @@ +package builder + +import "fmt" + +// make AnalyTable +func (b *GoBuilder) buildAnalyPackTable() { + AnalyTable := ` +var StatePackAction = []int { + %s +} +var StatePackOffset = []int { + %s +} +var StackPackCheck = []int { + %s +} +` + saction := "" + soffset := "" + scheck := "" + for _, val := range b.vnode.ActionTable { + saction += fmt.Sprintf("%d,\t", val) + } + for _, val := range b.vnode.OffsetTable { + soffset += fmt.Sprintf("%d,\t", val) + } + for _, val := range b.vnode.CheckTable { + scheck += fmt.Sprintf("%d,\t", val) + } + b.AnalyTable = fmt.Sprintf(AnalyTable, saction, soffset, scheck) +} + +func (b *GoBuilder) buildPackStateFunc() { + b.StateFunc = ` +// Push StateSym +func PushStateSym(state *StateSym) { + if StackPointer >= len(StateSymStack) { + StateSymStack = append(StateSymStack, *state) + } else { + StateSymStack[StackPointer] = *state + } + StackPointer++ +} + +// Pop num StateSym +func PopStateSym(num int) { + StackPointer -= num +} + +func (s *StateSym) Action(a int) int { + if StatePackOffset[s.Yystate]+a >= len(StackPackCheck) || StackPackCheck[StatePackOffset[s.Yystate]+a] != s.Yystate { + return 0 + }else{ + return StatePackAction[StatePackOffset[s.Yystate]+a] + } +} + +func init() { + StateSymStack = []StateSym{ + { + Yystate: 0, + YySymIndex: 1, //$ + }, + } + StackPointer = 1 +} + +func Parser(input string) *ValType { + var currentPos int = 0 + var val ValType + lookAhead := fetchLookAhead(input, &val, ¤tPos) + for { + + if StackPointer == 0 { + break + } + if StackPointer > len(StateSymStack) { + break + } + s := &StateSymStack[StackPointer-1] + a := s.Action(lookAhead) + if a == ERROR_ACTION { + panic("Grammer error") + } else if a == ACCEPT_ACTION { + return &s.ValType + } else { + if a > 0 { + // shift + PushStateSym(&StateSym{ + Yystate: a, + YySymIndex: lookAhead, + ValType: val, + }) + lookAhead = fetchLookAhead(input, &val, ¤tPos) + } else { + reduceIndex := -a + SymTy := ReduceFunc(reduceIndex) + s := &StateSymStack[StackPointer-1] + gotoState := s.Action(SymTy.YySymIndex) + SymTy.Yystate = gotoState + PushStateSym(SymTy) + } + } + } + return nil +} +func fetchLookAhead(input string, val *ValType, pos *int) int { + token := GetToken(input, val, pos) + return translate(token) +} +` +} diff --git a/LALR/LALR.go b/LALR/LALR.go index 44fcbe0..2927268 100644 --- a/LALR/LALR.go +++ b/LALR/LALR.go @@ -9,6 +9,7 @@ import ( "sort" grammar "github.com/acekingke/yaccgo/Grammar" + utils "github.com/acekingke/yaccgo/Utils" ) @@ -21,6 +22,11 @@ type LALR1 struct { FollowSet, LookAheadSet map[int][]int NumOfStates int //States number + // The last is for pack table + NeedPacked bool + ActionTable []int + OffsetTable []int + CheckTable []int } // q --t--> p @@ -276,7 +282,22 @@ func ComputeLALR(g *grammar.Grammar) *LALR1 { panic(err.Error()) } else { lalr.GTable = tab + // try to pack the table + act, off, check := utils.PackTable(lalr.GTable) + lalr.NeedPacked = false lalr.NumOfStates = len(lalr.G.LR0.LR0Closure) + if len(act)+len(off)+len(check) > lalr.NumOfStates*len(lalr.G.Symbols) { + if utils.DebugFlags { + fmt.Println("The table is no need to pack") + } + } else { + if utils.DebugFlags { + fmt.Println("The table is packed") + } + lalr.NeedPacked = true + lalr.ActionTable, lalr.OffsetTable, lalr.CheckTable = act, off, check + } + } return lalr } diff --git a/LALR/LALR_test.go b/LALR/LALR_test.go index 961783b..c711ee3 100644 --- a/LALR/LALR_test.go +++ b/LALR/LALR_test.go @@ -12,6 +12,7 @@ import ( item "github.com/acekingke/yaccgo/Items" rule "github.com/acekingke/yaccgo/Rules" symbol "github.com/acekingke/yaccgo/Symbol" + utils "github.com/acekingke/yaccgo/Utils" ) func TestUnion(t *testing.T) { @@ -368,6 +369,13 @@ func TestLALR1_ambiguity(t *testing.T) { } fmt.Println("},") } + act, off, check := utils.PackTable(tab) + if len(act)+len(off)+len(check) > len(lalr.G.LR0.LR0Closure)*len(lalr.G.Symbols) { + fmt.Println("The table is no need to pack") + } else { + fmt.Println("The table is packed") + + } } } diff --git a/Parser/utils.go b/Parser/utils.go index bd7694d..1d9bf9c 100644 --- a/Parser/utils.go +++ b/Parser/utils.go @@ -4,7 +4,9 @@ Use of this source code is governed by MIT license that can be found in the LICE */ package parser -import "strings" +import ( + "strings" +) func genTempName(in string) string { return "$operator" + in diff --git a/Utils/packtable.go b/Utils/packtable.go new file mode 100644 index 0000000..f1ece0c --- /dev/null +++ b/Utils/packtable.go @@ -0,0 +1,93 @@ +package utils + +import "sort" + +type Pair struct { + a, b interface{} +} + +// Use Tarjan and Yao method compress the two-dimensional array +func PackTable(table [][]int) ( /*T*/ []int /*D*/, []int /*Check*/, []int) { + var row []int + entry := make([]bool, len(table)*len(table[0])) + //step 1 count every row non-zero element + rowCount := []Pair{} + for i := 0; i < len(table); i++ { + rowCount = append(rowCount, Pair{i, 0}) + for j := 0; j < len(table[i]); j++ { + if table[i][j] != 0 { + rowCount[i].b = rowCount[i].b.(int) + 1 + } + } + row = append(row, 0) + } + //step 2 fetch all non-zero element position + nonZeroPos := make(map[int][]int) // list(i) + for i := 0; i < len(table); i++ { + for j := 0; j < len(table[i]); j++ { + if table[i][j] != 0 { + nonZeroPos[i] = append(nonZeroPos[i], j) + } + } + } + //step 3 compress + //sort the count + sort.SliceStable(rowCount, func(i, j int) bool { + return rowCount[i].b.(int) > rowCount[j].b.(int) + }) + maxIndex := 0 + // from the largest to the smallest + for _, p := range rowCount { + i := p.a.(int) + row[i] = 0 + //check overlap + checkoverlap: + for _, j := range nonZeroPos[i] { + if entry[row[i]+j] { + row[i]++ + goto checkoverlap + } + } + for _, k := range nonZeroPos[i] { + entry[row[i]+k] = true + if maxIndex < row[i]+k { + maxIndex = row[i] + k + } + } + } + var ret []int = make([]int, maxIndex+1) + var check []int = make([]int, maxIndex+1) + //init check with -1 + for i := 0; i < maxIndex+1; i++ { + check[i] = -1 + } + //step 4 output + for i, js := range nonZeroPos { + for _, k := range js { + ret[row[i]+k] = table[i][k] + check[row[i]+k] = i + } + } + return ret, row, check +} + +func UnPackTable(rows int, cols int, T []int, D []int, C []int) [][]int { + var table [][]int = make([][]int, rows) + // step 1 find the maxIndex + + // step 2 allocate the table + for i := 0; i < len(table); i++ { + table[i] = make([]int, cols) + } + // step 3 fill the table + for i := 0; i < len(D); i++ { + for j := 0; j < cols; j++ { + if D[i]+j >= len(C) || C[D[i]+j] != i { + table[i][j] = 0 + } else { + table[i][j] = T[D[i]+j] + } + } + } + return table +} diff --git a/Utils/packtable_test.go b/Utils/packtable_test.go new file mode 100644 index 0000000..f820b75 --- /dev/null +++ b/Utils/packtable_test.go @@ -0,0 +1,52 @@ +/* +Copyright (c) 2021 Ke Yuchang(aceking.ke@gmail.com). All rights reserved. +Use of this source code is governed by MIT license that can be found in the LICENSE file. +*/ +package utils + +import ( + "fmt" + "reflect" + "testing" +) + +func TestPackTable1(t *testing.T) { + table := [][]int{ + {0, 0, 1, 2, 0, 0}, + {0, 0, 1, 2, 0, 0}, + {9, 0, 0, 0, 0, 8}, + {0, 10, 0, 0, 0, 0}, + {9, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 11, 0}, + {0, 0, 1, 2, 0, 0}, + {0, 0, 1, 2, 11, 0}, + {0, 10, 0, 0, 0, 0}, + } + fmt.Println(table) + T, D, C := PackTable(table) + fmt.Println(T) + fmt.Println(D) + fmt.Println(C) + R := UnPackTable(9, 6, T, D, C) + if !reflect.DeepEqual(table, R) { + t.Error("PackTable1 failed") + } + +} + +func TestPackTable2(t *testing.T) { + table := [][]int{ + {1, 0, 0}, + {0, 1, 0}, + {0, 0, 1}, + } + fmt.Println(table) + T, D, C := PackTable(table) + fmt.Println(T) + fmt.Println(D) + fmt.Println(C) + R := UnPackTable(3, 3, T, D, C) + if !reflect.DeepEqual(table, R) { + t.Error("PackTable2 failed") + } +} diff --git a/examples/ladd.y b/examples/ladd.y index 8702616..bdb14a3 100644 --- a/examples/ladd.y +++ b/examples/ladd.y @@ -25,7 +25,9 @@ %% PROG: /*empty*/ - | PROG E NL + | PROG E NL { + fmt.Println($2) + } E: E PLUS E { $$ = $1 + $3