diff options
-rw-r--r-- | cmd/vartan/parse.go | 2 | ||||
-rw-r--r-- | driver/parser.go | 158 | ||||
-rw-r--r-- | driver/parser_test.go | 2 |
3 files changed, 101 insertions, 61 deletions
diff --git a/cmd/vartan/parse.go b/cmd/vartan/parse.go index ef22ff3..95b61b2 100644 --- a/cmd/vartan/parse.go +++ b/cmd/vartan/parse.go @@ -62,7 +62,7 @@ func runParse(cmd *cobra.Command, args []string) (retErr error) { defer f.Close() src = f } - p, err = driver.NewParser(cgram, src) + p, err = driver.NewParser(cgram, src, driver.MakeAST()) if err != nil { return err } diff --git a/driver/parser.go b/driver/parser.go index 10c7162..3cc8a9a 100644 --- a/driver/parser.go +++ b/driver/parser.go @@ -51,6 +51,22 @@ func printTree(w io.Writer, node *Node, ruledLine string, childRuledLinePrefix s } } +type ParserOption func(p *Parser) error + +func MakeAST() ParserOption { + return func(p *Parser) error { + p.makeAST = true + return nil + } +} + +func MakeCST() ParserOption { + return func(p *Parser) error { + p.makeCST = true + return nil + } +} + type semanticFrame struct { cst *Node ast *Node @@ -63,20 +79,31 @@ type Parser struct { semStack []*semanticFrame cst *Node ast *Node + makeAST bool + makeCST bool } -func NewParser(gram *spec.CompiledGrammar, src io.Reader) (*Parser, error) { +func NewParser(gram *spec.CompiledGrammar, src io.Reader, opts ...ParserOption) (*Parser, error) { lex, err := mldriver.NewLexer(gram.LexicalSpecification.Maleeni.Spec, src) if err != nil { return nil, err } - return &Parser{ + p := &Parser{ gram: gram, lex: lex, stateStack: []int{}, semStack: []*semanticFrame{}, - }, nil + } + + for _, opt := range opts { + err := opt(p) + if err != nil { + return nil, err + } + } + + return p, nil } func (p *Parser) Parse() error { @@ -103,16 +130,27 @@ func (p *Parser) Parse() error { } // semantic action - p.semStack = append(p.semStack, &semanticFrame{ - cst: &Node{ - KindName: p.gram.ParsingTable.Terminals[tsym], - Text: tokText, - }, - ast: &Node{ - KindName: p.gram.ParsingTable.Terminals[tsym], - Text: tokText, - }, - }) + { + var ast *Node + var cst *Node + if p.makeAST { + ast = &Node{ + KindName: p.gram.ParsingTable.Terminals[tsym], + Text: tokText, + } + } + if p.makeCST { + cst = &Node{ + KindName: p.gram.ParsingTable.Terminals[tsym], + Text: tokText, + } + } + + p.semStack = append(p.semStack, &semanticFrame{ + cst: cst, + ast: ast, + }) + } case act > 0: // Reduce accepted := p.reduce(act) if accepted { @@ -123,60 +161,62 @@ func (p *Parser) Parse() error { } // semantic action - - prodNum := act - lhs := p.gram.ParsingTable.LHSSymbols[prodNum] - - // When an alternative is empty, `n` will be 0, and `handle` will be empty slice. - n := p.gram.ParsingTable.AlternativeSymbolCounts[prodNum] - handle := p.semStack[len(p.semStack)-n:] - - var cst *Node - { - children := make([]*Node, len(handle)) - for i, f := range handle { - children[i] = f.cst - } - cst = &Node{ - KindName: p.gram.ParsingTable.NonTerminals[lhs], - Children: children, - } - } - - var ast *Node { - act := p.gram.ASTAction.Entries[prodNum] - children := []*Node{} - if act != nil { - for _, e := range act { - if e > 0 { - offset := e - 1 - children = append(children, handle[offset].ast) - } else { - offset := e*-1 - 1 - for _, c := range handle[offset].ast.Children { - children = append(children, c) + prodNum := act + lhs := p.gram.ParsingTable.LHSSymbols[prodNum] + + // When an alternative is empty, `n` will be 0, and `handle` will be empty slice. + n := p.gram.ParsingTable.AlternativeSymbolCounts[prodNum] + handle := p.semStack[len(p.semStack)-n:] + + var ast *Node + var cst *Node + if p.makeAST { + act := p.gram.ASTAction.Entries[prodNum] + children := []*Node{} + if act != nil { + for _, e := range act { + if e > 0 { + offset := e - 1 + children = append(children, handle[offset].ast) + } else { + offset := e*-1 - 1 + for _, c := range handle[offset].ast.Children { + children = append(children, c) + } } } + } else { + // If an alternative has no AST action, a driver generates + // a node with the same structure as a CST. + for _, f := range handle { + children = append(children, f.ast) + } } - } else { - // If an alternative has no AST action, a driver generates - // a node with the same structure as a CST. - for _, f := range handle { - children = append(children, f.ast) + + ast = &Node{ + KindName: p.gram.ParsingTable.NonTerminals[lhs], + Children: children, } } - ast = &Node{ - KindName: p.gram.ParsingTable.NonTerminals[lhs], - Children: children, + if p.makeCST { + children := make([]*Node, len(handle)) + for i, f := range handle { + children[i] = f.cst + } + + cst = &Node{ + KindName: p.gram.ParsingTable.NonTerminals[lhs], + Children: children, + } } - } - p.semStack = p.semStack[:len(p.semStack)-n] - p.semStack = append(p.semStack, &semanticFrame{ - cst: cst, - ast: ast, - }) + p.semStack = p.semStack[:len(p.semStack)-n] + p.semStack = append(p.semStack, &semanticFrame{ + cst: cst, + ast: ast, + }) + } default: var tokText string if tok.EOF { diff --git a/driver/parser_test.go b/driver/parser_test.go index 5763d63..a46de9b 100644 --- a/driver/parser_test.go +++ b/driver/parser_test.go @@ -424,7 +424,7 @@ b: "a"; t.Fatal(err) } - p, err := NewParser(gram, strings.NewReader(tt.src)) + p, err := NewParser(gram, strings.NewReader(tt.src), MakeAST(), MakeCST()) if err != nil { t.Fatal(err) } |