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 }