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 dextool.plugin.mutate.backend.analyze.ast : Interval;
13 
14 enum Direction {
15     bottomToTop,
16     topToBottom,
17 }
18 
19 struct Stack(T) {
20     import std.typecons : Tuple;
21     import automem;
22 
23     alias Element = Tuple!(T, "data", uint, "depth");
24     Vector!(Element) stack;
25 
26     alias stack this;
27 
28     // trusted: as long as arr do not escape the instance
29     void put(T a, uint depth) @trusted {
30         stack.put(Element(a, depth));
31     }
32 
33     // trusted: as long as arr do not escape the instance
34     void pop() @trusted {
35         stack.popBack;
36     }
37 
38     /**
39      * It is important that it removes up to and including the specified depth
40      * because the stack is used when doing depth first search. Each call to
41      * popUntil is expected to remove a layer. New nodes are then added to the
42      * parent which is the first one from the previous layer.
43      *
44      * Returns: the removed elements.
45      */
46     auto popUntil(uint depth) @trusted {
47         Vector!T rval;
48         while (!stack.empty && stack[$ - 1].depth >= depth) {
49             rval.put(stack[$ - 1].data);
50             stack.popBack;
51         }
52         return rval;
53     }
54 
55     T back() {
56         return stack[$ - 1].data;
57     }
58 
59     bool empty() @safe pure nothrow const @nogc {
60         return stack.empty;
61     }
62 
63     /** The depth of the parent that is furthest away or in other words closest
64      * to zero.
65      *
66      * Returns: the depth (1+) if any of the parent nodes is `k`, zero
67      * otherwise.
68      */
69     uint isParent(K)(K k) {
70         return match!((a) {
71             if (a[0].data.kind == k)
72                 return a[0].depth;
73             return 0;
74         })(stack, Direction.bottomToTop);
75     }
76 }
77 
78 /** Run until `pred` returns something that evaluates to true, in that case
79  * return the value.
80  *
81  * `pred` should take one parameter.
82  */
83 auto match(alias pred, T)(ref T stack, Direction d) {
84     auto rval = typeof(pred(stack[0 .. $])).init;
85 
86     if (stack.empty)
87         return rval;
88 
89     auto safeSlice(size_t i) @trusted {
90         return stack[i .. $];
91     }
92 
93     final switch (d) {
94     case Direction.bottomToTop:
95         foreach (i; 0 .. stack.length) {
96             rval = pred(safeSlice(i));
97             if (rval)
98                 break;
99         }
100         break;
101     case Direction.topToBottom:
102         for (long i = stack.length - 1; i > 0; --i) {
103         }
104         foreach (i; 0 .. stack.length) {
105             rval = pred(safeSlice(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` is inside any of the intervals for `key`.
135      *
136      * Returns: true if `i` is inside a macro interval.
137      */
138     bool inside(const KeyT key, const Interval i) {
139         static bool test(Interval i, uint p) {
140             return p >= i.begin && p <= i.end;
141         }
142 
143         if (auto intervals = key in index) {
144             foreach (a; *intervals) {
145                 if (test(a, i.begin) || test(a, i.end)) {
146                     return true;
147                 }
148             }
149         }
150 
151         return false;
152     }
153 }