Accessing result of a golang yacc generated parser
Asked Answered
H

2

8

I am trying to create a parser using golang's yacc tool. I found nex to simplify creating a lexer to give the parser. My problem now is that the generated parser has no method or field to give me access to the parsing result. I could just store the parse result in a global variable, but that seems wrong.

Currently I've added the following as an initial attempt to the top of my parser.y file:

type ResultParser interface {
  yyParser // Generated parser interface
  Result() s.Expr // s.Expr is an interface for the parsed result
}

func (p *yyParserImpl) Result() s.Expr {
  return p.stack[1].expr
}

func NewResultParser() ResultParser {
  return &yyParserImpl{}
}

Is there a recomended/better way of getting at the result from the parser?
(Since this feels like a bit of an abuse of the generator...)

Hawkeyed answered 24/4, 2016 at 11:49 Comment(4)
What do you mean by parse result?Allodium
When parsing the input I build a tree structure. It is stored in the $$.expr. I want to get the root of that tree. The above Result function seem to access the right result, but it feels a bit hacky. I'm not 100% sure that p.stack[1] will be where the root/parse result is always stored...Hawkeyed
Yeah, don't grovel in yacc's undocumented data structures, I suppose. I guess it's either "global variables" or "fields of the yyLexer interface's underlying type"; I use the latterAllodium
Thanks, that seems like the better approach. The above also breaks if I use another prefix so it's not very robust. I just wrote my own lexer anyway so, while it doesn't feel like the right place to store the result it's a lot better than a global variable. Just noticed that the parser also has a global yyErrorMessages variable :\ hmm..Hawkeyed
I
6

No, accessing stack[1] does not work reliably. It doesn’t contain any result as soon as the stack needs to grow beyond 16, its initial size. (See #16163.)

The if statement after the yystack label then creates a new stack and totally forgets about the one saved in yyParserImpl.


I did the following.

Add a result field to the lexer type:

type ShellLexer struct {
    /* … */
    result *ShellProgram
}

Extend the grammar by the following rule at the very beginning:

start : program {
    shyylex.(*ShellLexer).result = $$
}

(This depends on the parameter name of the Parse method (which can have a custom prefix), but I think that’s ok.)

Ibis answered 23/6, 2016 at 8:41 Comment(0)
T
1

Alternative solution: use sed to insert additional field into the generated parser. Then, in your grammar action, assign the value.

go tool yacc -o parser.go -p Filter parser.y
sed -i '/type FilterParserImpl struct/a tree *treeNode' parser.go

The generated parser:

type FilterParserImpl struct {
tree *treeNode
    lval  FilterSymType
    stack [FilterInitialStackSize]FilterSymType
    char  int
}

Grammar action:

filter { Filterrcvr.tree = $1 }
Ti answered 10/2, 2017 at 23:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.