1 /**
2  * A depth-first visitor for expressions.
3  *
4  * Copyright:   Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved
5  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
6  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/apply.d, _apply.d)
8  * Documentation:  https://dlang.org/phobos/dmd_apply.html
9  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/apply.d
10  */
11 
12 module dmd.apply;
13 
14 import dmd.arraytypes;
15 import dmd.dsymbol;
16 import dmd.dsymbolsem;
17 import dmd.dtemplate;
18 import dmd.expression;
19 import dmd.visitor;
20 
21 /**************************************
22  * An Expression tree walker that will visit each Expression e in the tree,
23  * in depth-first evaluation order, and call fp(e,param) on it.
24  * fp() signals whether the walking continues with its return value:
25  * Returns:
26  *      0       continue
27  *      1       done
28  * It's a bit slower than using virtual functions, but more encapsulated and less brittle.
29  * Creating an iterator for this would be much more complex.
30  */
31 private extern (C++) final class PostorderExpressionVisitor : StoppableVisitor
32 {
33     alias visit = typeof(super).visit;
34 public:
35     StoppableVisitor v;
36 
37     extern (D) this(StoppableVisitor v)
38     {
39         this.v = v;
40     }
41 
42     bool doCond(Expression e)
43     {
44         if (!stop && e)
45             e.accept(this);
46         return stop;
47     }
48 
49     bool doCond(Expressions* e)
50     {
51         if (!e)
52             return false;
53         for (size_t i = 0; i < e.dim && !stop; i++)
54             doCond((*e)[i]);
55         return stop;
56     }
57 
58     bool applyTo(Expression e)
59     {
60         e.accept(v);
61         stop = v.stop;
62         return true;
63     }
64 
65     override void visit(Expression e)
66     {
67         applyTo(e);
68     }
69 
70     override void visit(NewExp e)
71     {
72         //printf("NewExp::apply(): %s\n", toChars());
73         doCond(e.thisexp) || doCond(e.newargs) || doCond(e.arguments) || applyTo(e);
74     }
75 
76     override void visit(NewAnonClassExp e)
77     {
78         //printf("NewAnonClassExp::apply(): %s\n", toChars());
79         doCond(e.thisexp) || doCond(e.newargs) || doCond(e.arguments) || applyTo(e);
80     }
81 
82     override void visit(TypeidExp e)
83     {
84         doCond(isExpression(e.obj)) || applyTo(e);
85     }
86 
87     override void visit(UnaExp e)
88     {
89         doCond(e.e1) || applyTo(e);
90     }
91 
92     override void visit(BinExp e)
93     {
94         doCond(e.e1) || doCond(e.e2) || applyTo(e);
95     }
96 
97     override void visit(AssertExp e)
98     {
99         //printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars());
100         doCond(e.e1) || doCond(e.msg) || applyTo(e);
101     }
102 
103     override void visit(CallExp e)
104     {
105         //printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars());
106         doCond(e.e1) || doCond(e.arguments) || applyTo(e);
107     }
108 
109     override void visit(ArrayExp e)
110     {
111         //printf("ArrayExp::apply(apply_fp_t fp, void *param): %s\n", toChars());
112         doCond(e.e1) || doCond(e.arguments) || applyTo(e);
113     }
114 
115     override void visit(SliceExp e)
116     {
117         doCond(e.e1) || doCond(e.lwr) || doCond(e.upr) || applyTo(e);
118     }
119 
120     override void visit(ArrayLiteralExp e)
121     {
122         doCond(e.basis) || doCond(e.elements) || applyTo(e);
123     }
124 
125     override void visit(AssocArrayLiteralExp e)
126     {
127         doCond(e.keys) || doCond(e.values) || applyTo(e);
128     }
129 
130     override void visit(StructLiteralExp e)
131     {
132         if (e.stageflags & stageApply)
133             return;
134         int old = e.stageflags;
135         e.stageflags |= stageApply;
136         doCond(e.elements) || applyTo(e);
137         e.stageflags = old;
138     }
139 
140     override void visit(TupleExp e)
141     {
142         doCond(e.e0) || doCond(e.exps) || applyTo(e);
143     }
144 
145     override void visit(CondExp e)
146     {
147         doCond(e.econd) || doCond(e.e1) || doCond(e.e2) || applyTo(e);
148     }
149 }
150 
151 bool walkPostorder(Expression e, StoppableVisitor v)
152 {
153     scope PostorderExpressionVisitor pv = new PostorderExpressionVisitor(v);
154     e.accept(pv);
155     return v.stop;
156 }
157 
158 /*********************************
159  * Iterate this dsymbol or members of this scoped dsymbol, then
160  * call `fp` with the found symbol and `params`.
161  * Params:
162  *  symbol = the dsymbol or parent of members to call fp on
163  *  fp = function pointer to process the iterated symbol.
164  *       If it returns nonzero, the iteration will be aborted.
165  *  params = any parameters passed to fp.
166  * Returns:
167  *  nonzero if the iteration is aborted by the return value of fp,
168  *  or 0 if it's completed.
169  */
170 int apply(FP, Params...)(Dsymbol symbol, FP fp, Params params)
171 {
172     if (auto nd = symbol.isNspace())
173     {
174         return nd.members.foreachDsymbol( (s) { return s && s.apply(fp, params); } );
175     }
176     if (auto ad = symbol.isAttribDeclaration())
177     {
178         return ad.include(ad._scope).foreachDsymbol( (s) { return s && s.apply(fp, params); } );
179     }
180     if (auto tm = symbol.isTemplateMixin())
181     {
182         if (tm._scope) // if fwd reference
183             dsymbolSemantic(tm, null); // try to resolve it
184 
185         return tm.members.foreachDsymbol( (s) { return s && s.apply(fp, params); } );
186     }
187 
188     return fp(symbol, params);
189 }