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 struct Stack(T) {
15     import std.typecons : Tuple;
16     import automem;
17 
18     alias Element = Tuple!(T, "data", uint, "depth");
19     Vector!(Element) stack;
20 
21     alias stack this;
22 
23     // trusted: as long as arr do not escape the instance
24     void put(T a, uint depth) @trusted {
25         stack.put(Element(a, depth));
26     }
27 
28     // trusted: as long as arr do not escape the instance
29     void pop() @trusted {
30         stack.popBack;
31     }
32 
33     /**
34      * It is important that it removes up to and including the specified depth
35      * because the stack is used when doing depth first search. Each call to
36      * popUntil is expected to remove a layer. New nodes are then added to the
37      * parent which is the first one from the previous layer.
38      *
39      * Returns: the removed elements.
40      */
41     auto popUntil(uint depth) @trusted {
42         Vector!T rval;
43         while (!stack.empty && stack[$ - 1].depth >= depth) {
44             rval.put(stack[$ - 1].data);
45             stack.popBack;
46         }
47         return rval;
48     }
49 
50     T back() {
51         return stack[$ - 1].data;
52     }
53 
54     bool empty() @safe pure nothrow const @nogc {
55         return stack.empty;
56     }
57 
58     /// Returns: the depth (1+) if any of the parent nodes is `k`, zero otherwise.
59     uint isInside(K)(K k) {
60         import std.algorithm : filter;
61 
62         foreach (a; stack.range.filter!(a => a.data.kind == k)) {
63             return a.depth;
64         }
65         return 0;
66     }
67 }
68 
69 /** An index that can be queries to see if an interval overlap any of those
70  * that are in it. Create an index of all intervals that then can be queried to
71  * see if a Cursor or Interval overlap a macro.
72  */
73 struct Index(KeyT) {
74     Interval[][KeyT] index;
75 
76     this(Interval[][KeyT] index) {
77         this.index = index;
78     }
79 
80     void put(KeyT k, Interval i) {
81         if (auto v = k in index) {
82             (*v) ~= i;
83         } else {
84             index[k] = [i];
85         }
86     }
87 
88     /** Check if `i` is inside any of the intervals for `key`.
89      *
90      * Returns: true if `i` is inside a macro interval.
91      */
92     bool inside(const KeyT key, const Interval i) {
93         static bool test(Interval i, uint p) {
94             return p >= i.begin && p <= i.end;
95         }
96 
97         if (auto intervals = key in index) {
98             foreach (a; *intervals) {
99                 if (test(a, i.begin) || test(a, i.end)) {
100                     return true;
101                 }
102             }
103         }
104 
105         return false;
106     }
107 }