1 module util.visitor;
2 
3 auto dispatch(alias unhandled = function void(t) {
4 	throw new Exception(typeid(t).toString() ~ " is not supported.");
5 	// XXX: Buggy for some reason.
6 	// throw new Exception(typeid(t).toString() ~ " is not supported by visitor " ~ typeid(V).toString() ~ " .");
7 }, V, T, Args...)(ref V visitor, Args args, T t)
8 		if (is(V == struct) && (is(T == class) || is(T == interface))) {
9 	return dispatchImpl!(unhandled)(visitor, args, t);
10 }
11 
12 auto dispatch(alias unhandled = function void(t) {
13 	throw new Exception(typeid(t).toString() ~ " is not supported.");
14 	// XXX: Buggy for some reason.
15 	// throw new Exception(typeid(t).toString() ~ " is not supported by visitor " ~ typeid(V).toString() ~ " .");
16 }, V, T, Args...)(V visitor, Args args, T t)
17 		if ((is(V == class) || is(V == interface))
18 			    && (is(T == class) || is(T == interface))) {
19 	return dispatchImpl!(unhandled)(visitor, args, t);
20 }
21 
22 // XXX: is @trusted if visitor.visit is @safe .
23 private auto dispatchImpl(alias unhandled, V, T, Args...)(auto ref V visitor,
24                                                           Args args, T t) in {
25 	assert(t, "You can't dispatch null");
26 } do {
27 	static if (is(T == class)) {
28 		alias o = t;
29 	} else {
30 		auto o = cast(Object) t;
31 	}
32 
33 	auto tid = typeid(o);
34 
35 	import std.traits;
36 	static if (is(V == struct)) {
37 		import std.typetuple;
38 		alias Members = TypeTuple!(__traits(getOverloads, V, "visit"));
39 	} else {
40 		alias Members = MemberFunctionsTuple!(V, "visit");
41 	}
42 
43 	foreach (visit; Members) {
44 		alias parameters = ParameterTypeTuple!visit;
45 
46 		static if (parameters.length == args.length + 1) {
47 			alias parameter = parameters[args.length];
48 
49 			// FIXME: ensure call is correctly done when args exists.
50 			static if (is(parameter == class)
51 				           && !__traits(isAbstractClass, parameter)
52 				           && is(parameter : T)) {
53 				if (tid is typeid(parameter)) {
54 					return visitor.visit(args, () @trusted {
55 						// Fast cast can be trusted in this case, we already did the check.
56 						import util.fastcast;
57 						return fastCast!parameter(o);
58 					}());
59 				}
60 			}
61 		}
62 	}
63 
64 	// Dispatch isn't possible.
65 	enum returnVoid = is(typeof(return) == void);
66 	static if (returnVoid || is(typeof(unhandled(t)) == void)) {
67 		unhandled(t);
68 		assert(returnVoid);
69 	} else {
70 		return unhandled(t);
71 	}
72 }
73 
74 auto accept(T, V)(T t, ref V visitor)
75 		if (is(V == struct) && (is(T == class) || is(T == interface))) {
76 	return acceptImpl(t, visitor);
77 }
78 
79 auto accept(T, V)(T t, V visitor)
80 		if ((is(V == class) || is(V == interface))
81 			    && (is(T == class) || is(T == interface))) {
82 	return acceptImpl(t, visitor);
83 }
84 
85 private auto acceptImpl(T, V)(T t, auto ref V visitor) {
86 	static if (is(typeof(visitor.visit(t)))) {
87 		return visitor.visit(t);
88 	} else {
89 		visitor.dispatch(t);
90 	}
91 }