diff options
-rw-r--r-- | grammar/grammar.go | 74 | ||||
-rw-r--r-- | grammar/slr.go | 20 | ||||
-rw-r--r-- | grammar/slr_test.go | 4 | ||||
-rw-r--r-- | grammar/symbol.go | 19 | ||||
-rw-r--r-- | grammar/symbol_test.go | 4 |
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) } |