1 /**
2 Copyright: Copyright (c) 2016, 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 Various utilities used by generators.
11 TODO change the name of this module to location_filter.
12 */
13 module cpptooling.generator.utility;
14 
15 import std.functional : unaryFun;
16 import std.traits;
17 import std.range : ElementType;
18 
19 version (unittest) {
20     import unit_threaded : shouldEqual;
21 }
22 
23 /** Filter according to location existence and predicate.
24  *
25  * A false result is:
26  *  - neither a location for a declaration or definition exist.
27  *  - fails the predicate.
28  *
29  * The predicate is only called when a location exist.
30  */
31 template filterAnyLocation(alias predicate) {
32     alias predFun = unaryFun!predicate;
33 
34     auto filterAnyLocation(Range, LookupT)(Range range, LookupT lookup) {
35         import std.algorithm : filter, map;
36         import std.typecons : tuple;
37         import std.range : ElementType;
38         import cpptooling.data.type : LocationTag, Location;
39 
40         struct LocElem {
41             LocationTag location;
42             ElementType!Range value;
43         }
44 
45         // dfmt off
46         return range
47             // get the location associated with each item
48             .map!(a => tuple(lookup(a.usr), a))
49             // remove those that don't have a location
50             .filter!(a => a[0].length != 0)
51             // unpack the location. a declaration or definition, doesn't matter
52             .map!(a => tuple(a[0].front.any, a[1]))
53             // pack data in a struct that make it easier to use with named
54             // fields
55             .map!(a => LocElem(a[0].front, a[1]))
56             .filter!(a => predicate(a));
57         // dfmt on
58     }
59 }
60 
61 @("shall only let USRs with a location pass")
62 unittest {
63     import std.algorithm : joiner;
64     import std.array : array;
65     import std.range : only, dropOne;
66     import std.typecons : Nullable;
67     import cpptooling.data.symbol.types : USRType;
68     import cpptooling.data.type : LocationTag, Location;
69 
70     auto valid_loc = LocationTag(Location("valid.h", 1, 2));
71 
72     static struct ImplAny {
73         Nullable!LocationTag data;
74 
75         static auto makeNull() {
76             auto rval = ImplAny(LocationTag.init);
77             rval.data.nullify;
78             return rval;
79         }
80 
81         this(LocationTag data) {
82             this.data = data;
83         }
84 
85         auto any() {
86             return data.isNull ? only(LocationTag.init).dropOne : only(data.get);
87         }
88     }
89 
90     static struct ImplUSR {
91         USRType usr;
92         int id; /// only for test purpose to ensure the data has passed through
93     }
94 
95     // dfmt off
96     // predicate says "yes", expecting meaning of life
97     filterAnyLocation!(a => true)(
98         only(ImplUSR(USRType("fool"), 42)), (USRType a) { return only(ImplAny(valid_loc)); } )
99         .front.value.id.shouldEqual(42);
100 
101     // lookup says "no location for that USR", expecting filter to catch and remove the nothingness
102     filterAnyLocation!(a => true)(
103         only(ImplUSR(USRType("fool"), 42)), (USRType a) { return only(ImplAny.makeNull).dropOne; } )
104         .array()
105         .length.shouldEqual(0);
106 
107     // predicate says "no", expecting filter to remove the input
108     filterAnyLocation!(a => false)(
109         only(ImplUSR(USRType("fool"), 42)), (USRType a) { return only(ImplAny(valid_loc)); } )
110         .array()
111         .length.shouldEqual(0);
112 
113     // dfmt on
114 }