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 }