I am writing an interpreter in Go and I am looking for the idiomatic way to store the AST. I read the Go compiler source code and it seems they used interfaces with an empty method to represent the AST. For example, we have the following hierarchy,
Object
--Immovable
----Building
----Mountain
--Movable
----Car
----Bike
This is how the above hierarchy is implemented in the "empty method" way.
type Object interface {
object()
}
type Immovable interface {
Object
immovable()
}
type Building struct {
...
}
type Mountain struct {
...
}
type Movable interface {
Object
movable()
}
type Car struct {
...
}
type Mountain struct {
...
}
func (*Building) object() {}
func (*Mountain) object() {}
func (*Car) object() {}
func (*Bike) object() {}
func (*Building) immovable() {}
func (*Mountain) immovable() {}
func (*Car) movable() {}
func (*Bike) movable() {}
The above code is a contrived example and this is how the Go compiler implemented the AST with dozens of empty methods. But WHY? Note how many empty methods are defined. It may get very complicated with the increase of the depth of the hierarchy.
It is stated in the comments that the empty methods disallow the assignment of incompatible types. In our example, a *Car
can't be assigned to a *Immovable
for instance.
This is so easy in other languages like C++ that supports inheritance. I can't think of any other way of representing the AST.
The way how the Go compiler AST is implemented may be idiomatic but isn't it less straight forward?
See Question&Answers more detail:
os 与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…