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