I have autogenerated code. Simplified version:
package main
// begin of A
func main(){
ParseReader("x")
}
func parseInclude(fileName string) (interface{}, error) {
got, _ := ParseReader(fileName)
return got, nil
}
// end of A
type grammar struct {
pos int
run func(*parser) (interface{}, error)
}
var g = &grammar{
pos: 1,
run: (*parser).callonIncludeOp1,
}
type parser struct {
filename string
cur current
}
func (p *parser) callonIncludeOp1() (interface{}, error) {
return p.cur.onIncludeOp1("x")
}
func (p *parser) parse(g *grammar) (val interface{}, err error) {
return g.pos, nil
}
type current struct {
pos int
}
// B
func (c *current) onIncludeOp1(qfilename interface{}) (interface{}, error) {
got, _ := parseInclude("x")
return got, nil
}
func ParseReader(filename string) (interface{}, error) {
p := &parser{ filename: filename }
return p.parse(g)
}
I have error after compiling
./prog.go:19: initialization loop:
/home/gCDfp4/prog.go:19 g refers to
/home/gCDfp4/prog.go:25 (*parser).callonIncludeOp1 refers to
/home/gCDfp4/prog.go:36 (*current).onIncludeOp1 refers to
/home/gCDfp4/prog.go:7 parseInclude refers to
/home/gCDfp4/prog.go:41 ParseReader refers to
/home/gCDfp4/prog.go:19 g
I need do recursive call at grammar because i have preprocessor operator "#include" for parsing other file.
Because it is autogenerated code i can only modify code in block A or in function B.
How can i break the initialization cycle ?
This is the result of the package initialization where:
Dependency analysis does not rely on the actual values of the variables, only on lexical references to them in the source, analyzed transitively.
For instance, if a variable
x
's initialization expression refers to a function whose body refers to variabley
thenx
depends ony
.As in: "A reference to a variable or function is an identifier denoting that variable or function."
Your example in a playground returns something more direct:
tmp/sandbox395359317/main.go:21: initialization loop:
prog.go:21 g refers to
prog.go:28 (*parser).callonIncludeOp1 refers to
prog.go:21 g
There are techniques in Go for loose coupling, for instance interface.
As an example (not optimal, but at least breaks the initialization cycle), you can in //A
add:
type parseIncluder interface {
parseInclude(fileName string) (interface{}, error)
}
func (c *current) parseInclude(fileName string) (interface{}, error) {
return parseInclude(fileName)
}
And in //B
, the call to parseInclude()
becomes:
got, _ := c.cParseIncluder().parseInclude("x")
See Go plaground and click on Run
: no more initialization loop
.
The OP Red Skotina used a different approach with an package init()
function:
var gProxy grammar
func init() { gProxy = g }
func parseInclude(fileName string) (interface{}, error) {
got, _ := ParseReaderProxy(fileName)
return got, nil
}
func ParseReaderProxy(filename string) (interface{}, error) {
p := &parser{filename: filename}
return p.parse(gProxy)
}