aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--grammar/grammar.go74
-rw-r--r--grammar/slr.go20
-rw-r--r--grammar/slr_test.go4
-rw-r--r--grammar/symbol.go19
-rw-r--r--grammar/symbol_test.go4
5 files changed, 78 insertions, 43 deletions
diff --git a/grammar/grammar.go b/grammar/grammar.go
index 3268902..70387e2 100644
--- a/grammar/grammar.go
+++ b/grammar/grammar.go
@@ -196,43 +196,13 @@ type symbolTableAndLexSpec struct {
}
func (b *GrammarBuilder) genSymbolTableAndLexSpec(root *spec.RootNode) (*symbolTableAndLexSpec, error) {
+ // Anonymous patterns take precedence over explicitly defined lexical specifications (named patterns).
+ // Thus anonymous patterns must be registered to `symTab` and `entries` before named patterns.
symTab := newSymbolTable()
- skipKinds := []mlspec.LexKind{}
- skipSyms := []string{}
entries := []*mlspec.LexEntry{}
- for _, prod := range root.LexProductions {
- if _, exist := symTab.toSymbol(prod.LHS); exist {
- b.errs = append(b.errs, &verr.SpecError{
- Cause: semErrDuplicateTerminal,
- Detail: prod.LHS,
- Row: prod.Pos.Row,
- })
- continue
- }
-
- _, err := symTab.registerTerminalSymbol(prod.LHS)
- if err != nil {
- return nil, err
- }
-
- entry, skip, specErr, err := genLexEntry(prod)
- if err != nil {
- return nil, err
- }
- if specErr != nil {
- b.errs = append(b.errs, specErr)
- continue
- }
- if skip {
- skipKinds = append(skipKinds, mlspec.LexKind(prod.LHS))
- skipSyms = append(skipSyms, prod.LHS)
- }
- entries = append(entries, entry)
- }
anonPat2Sym := map[string]symbol{}
sym2AnonPat := map[symbol]string{}
- var anonEntries []*mlspec.LexEntry
{
anonPats := []string{}
for _, prod := range root.Productions {
@@ -263,18 +233,48 @@ func (b *GrammarBuilder) genSymbolTableAndLexSpec(root *spec.RootNode) (*symbolT
if err != nil {
return nil, err
}
+
anonPat2Sym[p] = sym
sym2AnonPat[sym] = p
- anonEntries = append(anonEntries, &mlspec.LexEntry{
+ entries = append(entries, &mlspec.LexEntry{
Kind: mlspec.LexKind(kind),
Pattern: mlspec.LexPattern(p),
})
}
}
- // Anonymous patterns take precedence over explicitly defined lexical specifications.
- entries = append(anonEntries, entries...)
+ skipKinds := []mlspec.LexKind{}
+ skipSyms := []string{}
+ for _, prod := range root.LexProductions {
+ if _, exist := symTab.toSymbol(prod.LHS); exist {
+ b.errs = append(b.errs, &verr.SpecError{
+ Cause: semErrDuplicateTerminal,
+ Detail: prod.LHS,
+ Row: prod.Pos.Row,
+ })
+ continue
+ }
+
+ _, err := symTab.registerTerminalSymbol(prod.LHS)
+ if err != nil {
+ return nil, err
+ }
+
+ entry, skip, specErr, err := genLexEntry(prod)
+ if err != nil {
+ return nil, err
+ }
+ if specErr != nil {
+ b.errs = append(b.errs, specErr)
+ continue
+ }
+ if skip {
+ skipKinds = append(skipKinds, mlspec.LexKind(prod.LHS))
+ skipSyms = append(skipSyms, prod.LHS)
+ }
+ entries = append(entries, entry)
+ }
checkedFragments := map[string]struct{}{}
for _, fragment := range root.Fragments {
@@ -662,12 +662,12 @@ func Compile(gram *Grammar, opts ...compileOption) (*spec.CompiledGrammar, error
skip[modeNum] = skipRec
}
- terms, err := gram.symbolTable.getTerminalTexts()
+ terms, err := gram.symbolTable.terminalTexts()
if err != nil {
return nil, err
}
- nonTerms, err := gram.symbolTable.getNonTerminalTexts()
+ nonTerms, err := gram.symbolTable.nonTerminalTexts()
if err != nil {
return nil, err
}
diff --git a/grammar/slr.go b/grammar/slr.go
index 870e84f..eb74622 100644
--- a/grammar/slr.go
+++ b/grammar/slr.go
@@ -266,6 +266,26 @@ func (b *slrTableBuilder) writeDescription(w io.Writer) {
fmt.Fprintf(w, "no conflicts\n\n")
}
+ fmt.Fprintf(w, "# Terminals\n\n")
+
+ termSyms := b.symTab.terminalSymbols()
+
+ fmt.Fprintf(w, "%v symbols:\n\n", len(termSyms))
+
+ for _, sym := range termSyms {
+ text, ok := b.symTab.toText(sym)
+ if !ok {
+ text = fmt.Sprintf("<symbol not found: %v>", sym)
+ }
+ if strings.HasPrefix(text, "_") {
+ fmt.Fprintf(w, "%4v %v: \"%v\"\n", sym.num(), text, b.sym2AnonPat[sym])
+ } else {
+ fmt.Fprintf(w, "%4v %v\n", sym.num(), text)
+ }
+ }
+
+ fmt.Fprintf(w, "\n")
+
fmt.Fprintf(w, "# Productions\n\n")
fmt.Fprintf(w, "%v productions:\n\n", len(b.prods.getAllProductions()))
diff --git a/grammar/slr_test.go b/grammar/slr_test.go
index 9723a0d..41be03f 100644
--- a/grammar/slr_test.go
+++ b/grammar/slr_test.go
@@ -59,11 +59,11 @@ id: "[A-Za-z_][0-9A-Za-z_]*";
t.Fatal(err)
}
- nonTermTexts, err := gram.symbolTable.getNonTerminalTexts()
+ nonTermTexts, err := gram.symbolTable.nonTerminalTexts()
if err != nil {
t.Fatal(err)
}
- termTexts, err := gram.symbolTable.getTerminalTexts()
+ termTexts, err := gram.symbolTable.terminalTexts()
if err != nil {
t.Fatal(err)
}
diff --git a/grammar/symbol.go b/grammar/symbol.go
index ae6000c..9c0e9bd 100644
--- a/grammar/symbol.go
+++ b/grammar/symbol.go
@@ -2,6 +2,7 @@ package grammar
import (
"fmt"
+ "sort"
)
type symbolKind string
@@ -224,14 +225,28 @@ func (t *symbolTable) toText(sym symbol) (string, bool) {
return text, ok
}
-func (t *symbolTable) getTerminalTexts() ([]string, error) {
+func (t *symbolTable) terminalSymbols() []symbol {
+ syms := make([]symbol, 0, t.termNum.Int()-terminalNumMin.Int())
+ for sym := range t.sym2Text {
+ if !sym.isTerminal() || sym.isNil() || sym.isEOF() {
+ continue
+ }
+ syms = append(syms, sym)
+ }
+ sort.Slice(syms, func(i, j int) bool {
+ return syms[i] < syms[j]
+ })
+ return syms
+}
+
+func (t *symbolTable) terminalTexts() ([]string, error) {
if t.termNum == terminalNumMin {
return nil, fmt.Errorf("symbol table has no terminals")
}
return t.termTexts, nil
}
-func (t *symbolTable) getNonTerminalTexts() ([]string, error) {
+func (t *symbolTable) nonTerminalTexts() ([]string, error) {
if t.nonTermNum == nonTerminalNumMin || t.nonTermTexts[symbolStart.num().Int()] == "" {
return nil, fmt.Errorf("symbol table has no terminals or no start symbol")
}
diff --git a/grammar/symbol_test.go b/grammar/symbol_test.go
index 52e5452..fda3ddf 100644
--- a/grammar/symbol_test.go
+++ b/grammar/symbol_test.go
@@ -104,7 +104,7 @@ func TestSymbol(t *testing.T) {
})
t.Run("texts of non-terminals", func(t *testing.T) {
- ts, err := tab.getNonTerminalTexts()
+ ts, err := tab.nonTerminalTexts()
if err != nil {
t.Fatal(err)
}
@@ -119,7 +119,7 @@ func TestSymbol(t *testing.T) {
})
t.Run("texts of terminals", func(t *testing.T) {
- ts, err := tab.getTerminalTexts()
+ ts, err := tab.terminalTexts()
if err != nil {
t.Fatal(err)
}