Skip to content

Commit

Permalink
Use grammar via an interface
Browse files Browse the repository at this point in the history
  • Loading branch information
nihei9 committed Mar 23, 2022
1 parent 83bc2b1 commit ba524fa
Show file tree
Hide file tree
Showing 8 changed files with 173 additions and 49 deletions.
2 changes: 1 addition & 1 deletion cmd/vartan/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ func runParse(cmd *cobra.Command, args []string) (retErr error) {
}
}

p, err = driver.NewParser(cgram, src, opts...)
p, err = driver.NewParser(driver.NewGrammar(cgram), src, opts...)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion driver/conflict_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ assign: '=';
}

treeAct := NewSyntaxTreeActionSet(gram, false, true)
p, err := NewParser(gram, strings.NewReader(tt.src), SemanticAction(treeAct))
p, err := NewParser(NewGrammar(gram), strings.NewReader(tt.src), SemanticAction(treeAct))
if err != nil {
t.Fatal(err)
}
Expand Down
4 changes: 2 additions & 2 deletions driver/lac_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ d: 'd';
gram: gram,
}

p, err := NewParser(gram, strings.NewReader(src), SemanticAction(semAct))
p, err := NewParser(NewGrammar(gram), strings.NewReader(src), SemanticAction(semAct))
if err != nil {
t.Fatal(err)
}
Expand All @@ -92,7 +92,7 @@ d: 'd';
gram: gram,
}

p, err := NewParser(gram, strings.NewReader(src), SemanticAction(semAct), DisableLAC())
p, err := NewParser(NewGrammar(gram), strings.NewReader(src), SemanticAction(semAct), DisableLAC())
if err != nil {
t.Fatal(err)
}
Expand Down
124 changes: 82 additions & 42 deletions driver/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,61 @@ import (
"io"

mldriver "github.com/nihei9/maleeni/driver"
"github.com/nihei9/vartan/spec"
)

type Grammar interface {
// Class returns a class of grammar.
Class() string

// InitialState returns the initial state of a parser.
InitialState() int

// StartProduction returns the start production of grammar.
StartProduction() int

// Action returns an ACTION entry corresponding to a (state, terminal symbol) pair.
Action(state int, terminal int) int

// GoTo returns a GOTO entry corresponding to a (state, non-terminal symbol) pair.
GoTo(state int, lhs int) int

// ErrorTrapperState returns true when a state can shift the error symbol.
ErrorTrapperState(state int) bool

// LHS returns a LHS symbol of a production.
LHS(prod int) int

// AlternativeSymbolCount returns a symbol count of p production.
AlternativeSymbolCount(prod int) int

// RecoverProduction returns true when a production has the recover directive.
RecoverProduction(prod int) bool

// LexicalSpecification returns a lexical specification.
LexicalSpecification() mldriver.LexSpec

// TerminalCount returns a terminal symbol count of grammar.
TerminalCount() int

// EOF returns the EOF symbol.
EOF() int

// Error returns the error symbol.
Error() int

// Terminal retuns a string representaion of a terminal symbol.
Terminal(terminal int) string

// TerminalAlias returns an alias for a terminal.
TerminalAlias(terminal int) string

// Skip returns true when a terminal symbol must be skipped.
Skip(kind mldriver.KindID) bool

// LexicalKindToTerminal maps a lexical kind to a terminal symbol.
LexicalKindToTerminal(kind mldriver.KindID) int
}

type SyntaxError struct {
Row int
Col int
Expand All @@ -34,7 +86,7 @@ func SemanticAction(semAct SemanticActionSet) ParserOption {
}

type Parser struct {
gram *spec.CompiledGrammar
gram Grammar
lex *mldriver.Lexer
stateStack *stateStack
semAct SemanticActionSet
Expand All @@ -44,8 +96,8 @@ type Parser struct {
synErrs []*SyntaxError
}

func NewParser(gram *spec.CompiledGrammar, src io.Reader, opts ...ParserOption) (*Parser, error) {
lex, err := mldriver.NewLexer(mldriver.NewLexSpec(gram.LexicalSpecification.Maleeni.Spec), src)
func NewParser(gram Grammar, src io.Reader, opts ...ParserOption) (*Parser, error) {
lex, err := mldriver.NewLexer(gram.LexicalSpecification(), src)
if err != nil {
return nil, err
}
Expand All @@ -56,7 +108,7 @@ func NewParser(gram *spec.CompiledGrammar, src io.Reader, opts ...ParserOption)
stateStack: &stateStack{},
}

if p.gram.ParsingTable.Class != "lalr" {
if p.gram.Class() != "lalr" {
p.disableLAC = true
}

Expand All @@ -71,7 +123,7 @@ func NewParser(gram *spec.CompiledGrammar, src io.Reader, opts ...ParserOption)
}

func (p *Parser) Parse() error {
p.stateStack.push(p.gram.ParsingTable.InitialState)
p.stateStack.push(p.gram.InitialState())
tok, err := p.nextToken()
if err != nil {
return err
Expand Down Expand Up @@ -111,7 +163,7 @@ ACTION_LOOP:
prodNum := act

recovered := false
if p.onError && p.gram.ParsingTable.RecoverProductions[prodNum] != 0 {
if p.onError && p.gram.RecoverProduction(prodNum) {
p.onError = false
p.shiftCount = 0
recovered = true
Expand Down Expand Up @@ -186,24 +238,22 @@ func (p *Parser) validateLookahead(term int) bool {
p.stateStack.enableExploratoryMode()
defer p.stateStack.disableExploratoryMode()

tab := p.gram.ParsingTable

for {
act := tab.Action[p.stateStack.topExploratorily()*tab.TerminalCount+term]
act := p.gram.Action(p.stateStack.topExploratorily(), term)

switch {
case act < 0: // Shift
return true
case act > 0: // Reduce
prodNum := act

lhs := tab.LHSSymbols[prodNum]
if lhs == tab.LHSSymbols[tab.StartProduction] {
lhs := p.gram.LHS(prodNum)
if lhs == p.gram.LHS(p.gram.StartProduction()) {
return true
}
n := tab.AlternativeSymbolCounts[prodNum]
n := p.gram.AlternativeSymbolCount(prodNum)
p.stateStack.popExploratorily(n)
state := tab.GoTo[p.stateStack.topExploratorily()*tab.NonTerminalCount+lhs]
state := p.gram.GoTo(p.stateStack.topExploratorily(), lhs)
p.stateStack.pushExploratorily(state)
default: // Error
return false
Expand All @@ -212,7 +262,6 @@ func (p *Parser) validateLookahead(term int) bool {
}

func (p *Parser) nextToken() (*mldriver.Token, error) {
skip := p.gram.LexicalSpecification.Maleeni.Skip
for {
// We don't have to check whether the token is invalid because the kind ID of the invalid token is 0,
// and the parsing table doesn't have an entry corresponding to the kind ID 0. Thus we can detect
Expand All @@ -222,7 +271,7 @@ func (p *Parser) nextToken() (*mldriver.Token, error) {
return nil, err
}

if skip[tok.KindID] > 0 {
if p.gram.Skip(tok.KindID) {
continue
}

Expand All @@ -232,10 +281,10 @@ func (p *Parser) nextToken() (*mldriver.Token, error) {

func (p *Parser) tokenToTerminal(tok *mldriver.Token) int {
if tok.EOF {
return p.gram.ParsingTable.EOFSymbol
return p.gram.EOF()
}

return p.gram.LexicalSpecification.Maleeni.KindToTerminal[tok.KindID]
return p.gram.LexicalKindToTerminal(tok.KindID)
}

func (p *Parser) lookupAction(tok *mldriver.Token) int {
Expand All @@ -246,17 +295,13 @@ func (p *Parser) lookupAction(tok *mldriver.Token) int {
}
}

termCount := p.gram.ParsingTable.TerminalCount
term := p.tokenToTerminal(tok)
return p.gram.ParsingTable.Action[p.stateStack.top()*termCount+term]
return p.gram.Action(p.stateStack.top(), p.tokenToTerminal(tok))
}

func (p *Parser) lookupActionOnError() (int, error) {
termCount := p.gram.ParsingTable.TerminalCount
errSym := p.gram.ParsingTable.ErrorSymbol
act := p.gram.ParsingTable.Action[p.stateStack.top()*termCount+errSym]
act := p.gram.Action(p.stateStack.top(), p.gram.Error())
if act >= 0 {
return 0, fmt.Errorf("an entry must be a shift action by the error symbol; entry: %v, state: %v, symbol: %v", act, p.stateStack.top(), p.gram.ParsingTable.Terminals[errSym])
return 0, fmt.Errorf("an entry must be a shift action by the error symbol; entry: %v, state: %v, symbol: %v", act, p.stateStack.top(), p.gram.Terminal(p.gram.Error()))
}

return act, nil
Expand All @@ -267,26 +312,25 @@ func (p *Parser) shift(nextState int) {
}

func (p *Parser) reduce(prodNum int) bool {
tab := p.gram.ParsingTable
lhs := tab.LHSSymbols[prodNum]
if lhs == tab.LHSSymbols[tab.StartProduction] {
lhs := p.gram.LHS(prodNum)
if lhs == p.gram.LHS(p.gram.StartProduction()) {
return true
}
n := tab.AlternativeSymbolCounts[prodNum]
n := p.gram.AlternativeSymbolCount(prodNum)
p.stateStack.pop(n)
nextState := tab.GoTo[p.stateStack.top()*tab.NonTerminalCount+lhs]
nextState := p.gram.GoTo(p.stateStack.top(), lhs)
p.stateStack.push(nextState)
return false
}

func (p *Parser) trapError() (int, bool) {
count := 0
for {
if p.gram.ParsingTable.ErrorTrapperStates[p.stateStack.top()] != 0 {
if p.gram.ErrorTrapperState(p.stateStack.top()) {
return count, true
}

if p.stateStack.top() != p.gram.ParsingTable.InitialState {
if p.stateStack.top() != p.gram.InitialState() {
p.stateStack.pop(1)
count++
} else {
Expand All @@ -301,14 +345,10 @@ func (p *Parser) SyntaxErrors() []*SyntaxError {

func (p *Parser) searchLookahead(state int) []string {
kinds := []string{}
term2Kind := p.gram.LexicalSpecification.Maleeni.TerminalToKind
kindNames := p.gram.LexicalSpecification.Maleeni.Spec.KindNames
aliases := p.gram.LexicalSpecification.Maleeni.KindAliases
termCount := p.gram.ParsingTable.TerminalCount
base := p.stateStack.top() * termCount
termCount := p.gram.TerminalCount()
for term := 0; term < termCount; term++ {
if p.disableLAC {
if p.gram.ParsingTable.Action[base+term] == 0 {
if p.gram.Action(p.stateStack.top(), term) == 0 {
continue
}
} else {
Expand All @@ -319,19 +359,19 @@ func (p *Parser) searchLookahead(state int) []string {

// We don't add the error symbol to the look-ahead symbols because users cannot input the error symbol
// intentionally.
if term == p.gram.ParsingTable.ErrorSymbol {
if term == p.gram.Error() {
continue
}

if term == p.gram.ParsingTable.EOFSymbol {
if term == p.gram.EOF() {
kinds = append(kinds, "<eof>")
continue
}

if alias := aliases[term]; alias != "" {
if alias := p.gram.TerminalAlias(term); alias != "" {
kinds = append(kinds, alias)
} else {
kinds = append(kinds, kindNames[term2Kind[term]].String())
kinds = append(kinds, p.gram.Terminal(term))
}
}

Expand Down
2 changes: 1 addition & 1 deletion driver/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -768,7 +768,7 @@ error: 'error' #skip;
}

treeAct := NewSyntaxTreeActionSet(gram, true, true)
p, err := NewParser(gram, strings.NewReader(tt.src), SemanticAction(treeAct))
p, err := NewParser(NewGrammar(gram), strings.NewReader(tt.src), SemanticAction(treeAct))
if err != nil {
t.Fatal(err)
}
Expand Down
2 changes: 1 addition & 1 deletion driver/semantic_action_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ char: "[a-z]";
semAct := &testSemAct{
gram: gram,
}
p, err := NewParser(gram, strings.NewReader(tt.src), SemanticAction(semAct))
p, err := NewParser(NewGrammar(gram), strings.NewReader(tt.src), SemanticAction(semAct))
if err != nil {
t.Fatal(err)
}
Expand Down
Loading

0 comments on commit ba524fa

Please sign in to comment.