I need to design a hierarchy of AST-type classes for code introspection (such as the Java element API used during the annotation process). But I'm not sure how to make it easy to maintain and maintain.
My instinct is to model the language as close as possible and make the most of the type system. To do this, I would use inheritance to model the intuitive "is a" relationship between the syntax elements to get depletion controls in
change-expressions. That is to say.
Constructor ElementThey are
TypeElements and so on. This leads to a fairly deep class hierarchy.
However, shared behavior does not respect this hierarchy. For example:
InterfaceElements can enclose other elements. But
PackageElements only enclose others
TypeElements and only
ClassElements ever locked
Constructor Elements. Mixed interfaces in combination with delegates for implementation can be used to share behavior between classes. Even so, it becomes complex very quickly with deep class hierarchies and many mixing interfaces.
Should there be a unique interface for elements that can enclose other elements? Or an interface to be able to enclose packages, another interface to be able to enclose types, another to enclose functions, etc. Should there be separate properties for visibility and modality or simply one that contains all applicable modifiers?
The opposite approach (the one chosen by the Java Elements API) is to make the hierarchy flat with less specific interfaces: all classes implement the
getEnclosedElements: List function, with those elements that do not contain anything simply by returning an empty list. Similarly, there are no different types of classes and interfaces. They are only grouped under
TypeElement with an enum property that specifies what type it really is. Of course, that makes everything much simpler for me, but it's not obvious to the user just from the interface which classes can include what kind of other elements, so users have to read the API documentation with a lot of more often and your code is less secure.
Which approach is preferable here? I am afraid that deep class hierarchies will make the API invisible, difficult to maintain (because many mixins are necessary to share the behavior between similar classes) and annoying to use. For example: the user may want to write a function to print all the members of a
Interface. They can not use the
Encloses interface because a
PackageElement It also encloses elements and can not use the
TypeElement base class because that includes
TypeAliasElement that never encloses members. There is no interface or base class that includes only