1 /**
2 Copyright: Copyright (c) 2020, Joakim Brännström. All rights reserved.
3 License: MPL-2
4 Author: Joakim Brännström (joakim.brannstrom@gmx.com)
5 
6 This Source Code Form is subject to the terms of the Mozilla Public License,
7 v.2.0. If a copy of the MPL was not distributed with this file, You can obtain
8 one at http://mozilla.org/MPL/2.0/.
9 */
10 module dextool.plugin.mutate.backend.analyze.utility;
11 
12 import my.optional;
13 import my.path;
14 
15 import dextool.plugin.mutate.backend.analyze.ast : Interval;
16 import dextool.compilation_db : ParseFlags, SystemIncludePath;
17 
18 enum Direction {
19     bottomToTop,
20     topToBottom,
21 }
22 
23 struct Stack(T) {
24     import std.typecons : Tuple;
25     import my.container.vector;
26 
27     alias Element = Tuple!(T, "data", uint, "depth");
28     Vector!Element stack;
29 
30     alias stack this;
31 
32     // trusted: as long as arr do not escape the instance
33     void put(T a, uint depth) @trusted {
34         stack.put(Element(a, depth));
35     }
36 
37     // trusted: as long as arr do not escape the instance
38     void pop() @trusted {
39         stack.popBack;
40     }
41 
42     /**
43      * It is important that it removes up to and including the specified depth
44      * because the stack is used when doing depth first search. Each call to
45      * popUntil is expected to remove a layer. New nodes are then added to the
46      * parent which is the first one from the previous layer.
47      *
48      * Returns: the removed elements.
49      */
50     auto popUntil(uint depth) @trusted {
51         Vector!T rval;
52         while (!stack.empty && stack[$ - 1].depth >= depth) {
53             rval.put(stack[$ - 1].data);
54             stack.popBack;
55         }
56         return rval;
57     }
58 
59     T back() {
60         return stack.back.data;
61     }
62 
63     bool empty() @safe pure nothrow const @nogc {
64         return stack.empty;
65     }
66 
67     /** The depth of the parent that is furthest away or in other words closest
68      * to zero.
69      *
70      * Returns: the depth (1+) if any of the parent nodes is `k`, zero
71      * otherwise.
72      */
73     uint isParent(K...)(auto ref K k) {
74         import std.algorithm : among;
75 
76         return match!((a) {
77             if (a.data.among(k))
78                 return a.depth;
79             return 0;
80         })(stack, Direction.bottomToTop);
81     }
82 }
83 
84 /** Run until `pred` returns something that evaluates to true, in that case
85  * return the value.
86  *
87  * `pred` should take one parameter.
88  */
89 auto match(alias pred, T)(ref T stack, Direction d) {
90     auto rval = typeof(pred(stack[0])).init;
91 
92     if (stack.empty)
93         return rval;
94 
95     final switch (d) {
96     case Direction.bottomToTop:
97         foreach (i; 0 .. stack.length) {
98             rval = pred(stack[i]);
99             if (rval)
100                 break;
101         }
102         break;
103     case Direction.topToBottom:
104         for (long i = stack.length - 1; i > 0; --i) {
105             rval = pred(stack[i]);
106             if (rval)
107                 break;
108         }
109         break;
110     }
111 
112     return rval;
113 }
114 
115 /** An index that can be queries to see if an interval overlap any of those
116  * that are in it. Create an index of all intervals that then can be queried to
117  * see if a Cursor or Interval overlap a macro.
118  */
119 struct Index(KeyT) {
120     Interval[][KeyT] index;
121 
122     this(Interval[][KeyT] index) {
123         this.index = index;
124     }
125 
126     void put(KeyT k, Interval i) {
127         if (auto v = k in index) {
128             (*v) ~= i;
129         } else {
130             index[k] = [i];
131         }
132     }
133 
134     /// Check if `i` overlap any intervals for `key`.
135     bool intersect(const KeyT key, const Interval i) {
136         if (auto intervals = key in index) {
137             foreach (a; *intervals) {
138                 if (a.intersect(i)) {
139                     return true;
140                 }
141             }
142         }
143 
144         return false;
145     }
146 
147     /// Check if `i` overlap any intervals for `key`.
148     bool overlap(const KeyT key, const Interval i) {
149         if (auto intervals = key in index) {
150             foreach (a; *intervals) {
151                 if (a.overlap(i)) {
152                     return true;
153                 }
154             }
155         }
156 
157         return false;
158     }
159 }