1 module sily.meta;
2 package(sily) {
3     import std.array;
4     import std.string;
5     import std.algorithm;
6 
7 
8     // for my sanity or for sanity of anyone who's seeing this
9     // is(typeof(A + B)) is basically a check if two types
10     // are compatible together.
11     // so 0 + "A" will return false, but 0 + true will return true
12     bool isValidOp(string op, T, R, E = T)() pure {
13         mixin(`return is( typeof( mixin("T.init"` ~ op ~ `"R.init" ) ): E );`);
14     }
15 
16     /* -------------------------------------------------------------------------- */
17     /*                     LOTS OF STUFF IS PULLED FROM HERE:                     */
18     /*          HTTPS://GITHUB.COM/DEXSET/DESCORE/TREE/MASTER/IMPORT/DES          */
19     /*           TBH THIS WHOLE FILE IS FROM THIS REPO EXCEPT ISVALIDOP           */
20     /*          BUT IT WORKS AND I DON'T EVEN WANT TO TRY TO UNDERSTAND           */
21     /*                           WHAT IS GOING ON HERE                            */
22     /* -------------------------------------------------------------------------- */
23 
24         
25     // used as:
26     //
27     // static if (N == 2 || N == 3 || N == 4) {
28     //     static if (N == 2) enum AccessString = "x y|w h|u v";
29     //     else
30     //     static if (N == 3) enum AccessString = "x y z|w h d|u v t|r g b";
31     //     else
32     //     static if (N == 4) enum AccessString = "x y z w|r g b a";
33 
34     //     mixin accessByString!(N, T, "data", AccessString);
35     // }
36 
37     mixin template accessByString( T, size_t N, string data, string AS, string VVASES=" ", string VVASVS="|")
38         if( isCompatibleArrayAccessStrings(N,AS,VVASES,VVASVS) ) {
39         pure @property {
40             T opDispatch(string v)() const if( getIndex(AS,v,VVASES,VVASVS) != -1 ) { 
41                 mixin( format( "return this.%s[%d];", data, getIndex(AS,v,VVASES,VVASVS) ) ); 
42             }
43 
44             ref T opDispatch(string v)() if( getIndex(AS,v,VVASES,VVASVS) != -1 ) { 
45                 mixin( format( "return this.%s[%d];", data, getIndex(AS,v,VVASES,VVASVS) ) ); 
46             }
47 
48             static if( isOneSymbolPerFieldForAnyAccessString(AS,VVASES,VVASVS) ) {
49                 auto opDispatch(string v)() const if( v.length > 1 && oneOfAnyAccessAll(AS,v,VVASES,VVASVS) ) {
50                     static string gen() {
51                         string[] res;
52                         foreach( i, sym; v )
53                             res ~= format( "this.%s[%d]", data, getIndex( AS, ""~sym, VVASES, VVASVS ) );
54                         return res.join(",");
55                     }
56 
57                     mixin( `return Vector!(T, v.length)(` ~ gen() ~ `);` );
58                 }
59 
60                 auto opDispatch(string v,U)( in U b ) 
61                 if( v.length > 1 && oneOfAnyAccessAll(AS,v,VVASES,VVASVS) && 
62                     isCompatibleArrayAccessString(v.length,v) && 
63                 ( isSpecVector!(v.length,T,U) || ( isDynamicVector!U && is(typeof(T(U.datatype.init))) ) ) ) {
64                     static if( b.isDynamic ) enforce( v.length == b.length );
65 
66                     static string gen() {
67                         string[] res;
68                         foreach( i, sym; v )
69                             res ~= format( "this.%s[%d] = T( b[%d] );", data,
70                                         getIndex( AS, ""~sym, VVASES, VVASVS ), i );
71                         return res.join("\n");
72                     }
73 
74                     mixin( gen() );
75                     return b;
76                 }
77             }
78         }
79     }
80 
81     /// compatible for creating access dispatches
82     pure bool isCompatibleArrayAccessStrings( size_t N, string str, string sep1="", string sep2="|" )
83     in { assert( sep1 != sep2 ); } do {
84         auto strs = str.split(sep2);
85         foreach( s; strs )
86             if( !isCompatibleArrayAccessString(N,s,sep1) )
87                 return false;
88 
89         string[] fa;
90         foreach( s; strs )
91             fa ~= s.split(sep1);
92 
93         foreach( ref v; fa ) v = strip(v);
94 
95         foreach( i, a; fa )
96             foreach( j, b; fa )
97                 if( i != j && a == b ) return false;
98 
99         return true;
100     }
101 
102 
103     /// compatible for creating access dispatches
104     pure bool isCompatibleArrayAccessString( size_t N, string str, string sep="" ) { 
105         return N == getAccessFieldsCount(str,sep) && isArrayAccessString(str,sep); 
106     }
107 
108     ///
109     pure bool isArrayAccessString( in string as, in string sep="", bool allowDot=false ) {
110         if( as.length == 0 ) return false;
111         auto splt = as.split(sep);
112         foreach( i, val; splt )
113             if( !isValueAccessString(val,allowDot) || canFind(splt[0..i],val) )
114                 return false;
115         return true;
116     }
117 
118     ///
119     pure size_t getAccessFieldsCount( string str, string sep ) { return str.split(sep).length; }
120 
121     ///
122     pure ptrdiff_t getIndex( string as, string arg, string sep1="", string sep2="|" )
123     in { assert( sep1 != sep2 ); } do
124     {
125         foreach( str; as.split(sep2) )
126             foreach( i, v; str.split(sep1) )
127                 if( arg == v ) return i;
128         return -1;
129     }
130 
131     ///
132     pure bool oneOfAccess( string str, string arg, string sep="" ) {
133         auto splt = str.split(sep);
134         return canFind(splt,arg);
135     }
136 
137     ///
138     pure bool oneOfAccessAll( string str, string arg, string sep="" ) {
139         auto splt = arg.split("");
140         return all!(a=>oneOfAccess(str,a,sep))(splt);
141     }
142 
143     ///
144     pure bool oneOfAnyAccessAll( string str, string arg, string sep1="", string sep2="|" )
145     in { assert( sep1 != sep2 ); } do
146     {
147         foreach( s; str.split(sep2) )
148             if( oneOfAccessAll(s,arg,sep1) ) return true;
149         return false;
150     }
151 
152     /// check symbol count for access to field
153     pure bool isOneSymbolPerFieldForAnyAccessString( string str, string sep1="", string sep2="|" )
154     in { assert( sep1 != sep2 ); } do
155     {
156         foreach( s; str.split(sep2) )
157             if( isOneSymbolPerFieldAccessString(s,sep1) ) return true;
158         return false;
159     }
160 
161     /// check symbol count for access to field
162     pure bool isOneSymbolPerFieldAccessString( string str, string sep="" ) {
163         foreach( s; str.split(sep) )
164             if( s.length > 1 ) return false;
165         return true;
166     }
167 
168     pure
169     {
170 
171         bool isValueAccessString( in string as, bool allowDot=false ) {
172             return as.length > 0 &&
173             startsWithAllowedChars(as) &&
174             (allowDot?(all!(a=>isValueAccessString(a))(as.split("."))):allowedCharsOnly(as));
175         }
176 
177         bool startsWithAllowedChars( in string as ) {
178             switch(as[0]) {
179                 case 'a': .. case 'z': goto case;
180                 case 'A': .. case 'Z': goto case;
181                 case '_': return true;
182                 default: return false;
183             }
184         }
185 
186         bool allowedCharsOnly( in string as ) {
187             foreach( c; as ) if( !allowedChar(c) ) return false;
188             return true;
189         }
190 
191         bool allowedChar( in char c ) {
192             switch(c) {
193                 case 'a': .. case 'z': goto case;
194                 case 'A': .. case 'Z': goto case;
195                 case '0': .. case '9': goto case;
196                 case '_': return true;
197                 default: return false;
198             }
199         }
200 
201     }
202 }