core.reflect becomes recursive
Stefan Koch
uplink.coder at googlemail.com
Sun Aug 8 18:50:31 UTC 2021
Good Afternoon everyone,
Just now I've added a nice tool to the core_reflect druntime
branch.
The transitive visitor.
If you have read my previous posts on core.reflect [1], then you
know that it works by giving you a simplified and stable version
of the AST.
Those trees are not that nice to work with using your bare hands
only.
You need to remember to descend the right leaves in your visitor.
And most of the time you are not interested in all nodes but only
in a certain subset.
For example maybe you just want to know about all function
declarations in your module.
In that case you would want to customize the behavior only for
`FunctionDeclaration` and have everything else just descend to
it's nodes.
Using the visitor pattern this can be done by implementing a
visitor that looks through all fields of a given AST Node, and if
a field is a subclass of the `Node` baseclass calls the accept
method on that field.
Of course you can write such a `TransitiveVisitor` manually.
But that is boring repetitive work and therefore one is likely to
get distracted and make mistakes.
Whereas if you could just iterate over all fields of all ASTNodes
and ask the question if the type of the field has the `Node`
baseclass you could have the code write itself.
You can do this today using templates but it somewhat tricky
since you need to use pattern-matching is-expressions.
Let's have a look at how we use core.reflect to do this.
First we need the `ClassDeclaration` of the visitor.
`static immutable ClassDeclaration visitor = cast(immutable
ClassDeclaration)nodeFromName("Visitor");`
The visitor will have overloads of the `visit` function for every
visit-able type.
So lets go through all the `FunctionDeclaration`s for `visit`.
```
foreach(m;visitor.members)
{
if (auto fd = cast(FunctionDeclaration) m)
{
```
The NodeType we are looking for should be the first and only
parameter.
And we need to get a class declaration from that type in order to
iterate over the fields on that Node-subclass.
```
assert(fd.name == "visit" && fd.type.paramterTypes.length ==
1);
auto nodeType = fd.type.parameterTypes[0].type;
auto cDecl = (cast(TypeClass) nodeType).sym; // this better
be a class type
```
To see if a field is a sub-class of `Node` we need two small
helper functions and one static variable
```
private static immutable ClassDeclaration node
= cast(immutable ClassDeclaration) nodeFromName("Node");
bool isBaseOf (const ClassDeclaration Base, const
ClassDeclaration C)
{
auto base = cast() C.base;
while (base)
{
if (base is Base)
{
return true;
}
base = cast() base.base;
}
return false;
}
static bool isNodeType(Type T)
{
if (auto CT = cast(TypeClass) T)
{
auto C = CT.sym;
return C is node || node.isBaseOf(C);
}
else return false;
}
```
And now we can iterate:
```
foreach(f; cDecl.fields)
{
if (isNodeType(f.type))
{
result ~= " if (node." ~ f.name ~ ")\n";
result ~= " node." ~ f.name ~ ".accept(this);\n";
}
}
```
And from here everything is just a mixin away.
The code actual non abbreviate code without explanations is here:
https://github.com/UplinkCoder/druntime/blob/core_reflect/src/core/reflect/transitiveVisitor.d
[0]
https://forum.dlang.org/thread/mmrkdmhymnojmjwvrrxg@forum.dlang.org
More information about the Digitalmars-d
mailing list