Skip to content

Commit

Permalink
Generate an AST and a CST only when they are necessary
Browse files Browse the repository at this point in the history
  • Loading branch information
nihei9 committed Aug 4, 2021
1 parent bb878f9 commit c14ae41
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 61 deletions.
2 changes: 1 addition & 1 deletion cmd/vartan/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
158 changes: 99 additions & 59 deletions driver/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Expand All @@ -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 {
Expand All @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion driver/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down

0 comments on commit c14ae41

Please sign in to comment.