/
Visitor.d
158 lines (141 loc) · 3.9 KB
/
Visitor.d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/// Author: Aziz Köksal
/// License: GPL3
/// $(Maturity very high)
module dil.ast.Visitor;
import dil.ast.Node,
dil.ast.Declarations,
dil.ast.Expressions,
dil.ast.Statements,
dil.ast.Types,
dil.ast.Parameters;
/// Generate visit methods.
///
/// E.g.:
/// ---
/// Declaration visit(ClassDeclaration){return null;};
/// Expression visit(CommaExpression){return null;};
/// ---
char[] generateVisitMethods()
{
char[] text;
foreach (className; g_classNames)
text ~= "returnType!(\""~className~"\") visit("~className~" node){return node;}\n";
return text;
}
// pragma(msg, generateAbstractVisitMethods());
/// Gets the appropriate return type for the provided class.
template returnType(char[] className)
{
static if (is(typeof(mixin(className)) : Declaration))
alias Declaration returnType;
else
static if (is(typeof(mixin(className)) : Statement))
alias Statement returnType;
else
static if (is(typeof(mixin(className)) : Expression))
alias Expression returnType;
else
static if (is(typeof(mixin(className)) : TypeNode))
alias TypeNode returnType;
else
alias Node returnType;
}
/// Generate functions which do the second dispatch.
///
/// E.g.:
/// ---
/// Expression visitCommaExpression(Visitor visitor, CommaExpression c)
/// { visitor.visit(c); /* Second dispatch. */ }
/// ---
/// The equivalent in the traditional visitor pattern would be:
/// ---
/// class CommaExpression : Expression
/// {
/// void accept(Visitor visitor)
/// { visitor.visit(this); }
/// }
/// ---
char[] generateDispatchFunctions()
{
char[] text;
foreach (className; g_classNames)
text ~= "returnType!(\""~className~"\") visit"~className~"(Visitor visitor, "~className~" c)\n"
"{ return visitor.visit(c); }\n";
return text;
}
// pragma(msg, generateDispatchFunctions());
/++
Generates an array of function pointers.
---
[
cast(void*)&visitCommaExpression,
// etc.
]
---
+/
char[] generateVTable()
{
char[] text = "[";
foreach (className; g_classNames)
text ~= "cast(void*)&visit"~className~",\n";
return text[0..$-2]~"]"; // slice away last ",\n"
}
// pragma(msg, generateVTable());
/// Implements a variation of the visitor pattern.
///
/// Inherited by classes that need to traverse a D syntax tree
/// and do computations, transformations and other things on it.
abstract class Visitor
{
mixin(generateVisitMethods());
static
mixin(generateDispatchFunctions());
// This is necessary so that the compiler puts
// the array into the static data segment.
mixin("private const _dispatch_vtable = " ~ generateVTable() ~ ";");
/// The table holding function pointers to the second dispatch functions.
static const dispatch_vtable = _dispatch_vtable;
static assert(dispatch_vtable.length == g_classNames.length,
"vtable length doesn't match number of classes");
/// Looks up the second dispatch function for n and returns that.
Node function(Visitor, Node) getDispatchFunction()(Node n)
{
return cast(Node function(Visitor, Node))dispatch_vtable[n.kind];
}
/// The main and first dispatch function.
Node dispatch(Node n)
{ // Second dispatch is done in the called function.
return getDispatchFunction(n)(this, n);
}
final:
Declaration visit(Declaration n)
{ return visitD(n); }
Statement visit(Statement n)
{ return visitS(n); }
Expression visit(Expression n)
{ return visitE(n); }
TypeNode visit(TypeNode n)
{ return visitT(n); }
Node visit(Node n)
{ return visitN(n); }
Declaration visitD(Declaration n)
{
return cast(Declaration)cast(void*)dispatch(n);
}
Statement visitS(Statement n)
{
return cast(Statement)cast(void*)dispatch(n);
}
Expression visitE(Expression n)
{
return cast(Expression)cast(void*)dispatch(n);
}
TypeNode visitT(TypeNode n)
{
return cast(TypeNode)cast(void*)dispatch(n);
}
Node visitN(Node n)
{
return dispatch(n);
}
}