1 /** 2 Package containings meta utilities line swizzling or mixins. 3 Not accesible from outside packages 4 */ 5 module sily.meta.swizzle; 6 import std.array; 7 import std.string; 8 import std.algorithm; 9 10 // for my sanity or for sanity of anyone who's seeing this 11 // is(typeof(A + B)) is basically a check if two types 12 // are compatible together. 13 // so 0 + "A" will return false, but 0 + true will return true 14 /** 15 Returns true if op between type T and type R is compatible and returns type convertable to E (T) 16 Example: 17 --- 18 // Assuming "this" is a vector with type "T" 19 auto opBinary(string op, R)(in Vector!(R, N) b) const 20 if ( isValidOp(op, T, R) ) { 21 // op 22 } 23 // If we want for return value to be float 24 auto opBinary(string op, R)(in Vector!(R, N) b) const 25 if ( isValidOp(op, T, R, float) ) { 26 // op 27 } 28 --- 29 */ 30 bool isValidOp(string op, T, R, E = T)() pure { 31 mixin(`return is( typeof( mixin("T.init"` ~ op ~ `"R.init" ) ): E );`); 32 } 33 34 /* --------------------------------- Swizzle -------------------------------- */ 35 36 /** 37 Allows swizzling for vector types "Vector!(Type, Size)". Creates opDispatch 38 containing available swizzle 39 Usage: 40 --- 41 // Params: T, size_t size, string dataArrayName, string accessString, string dataSep, string accessSep 42 43 // Setting up separators automatically 44 mixin accessByString!(T, 4, "data", "x y z w"); 45 46 // Data separator can be anything 47 mixin accessByString!(int, 3, "data", "x,y,z", ","); 48 49 // Same for access separator 50 mixin accessByString!(float, 3, "data", "x,y,z/r,g,b", ",", "/"); 51 52 // What sily.vector uses: 53 static if (N == 2 || N == 3 || N == 4) { 54 static if (N == 2) private enum AS = "x y|w h|u v"; 55 else 56 static if (N == 3) private enum AS = "x y z|w h d|u v t|r g b"; 57 else 58 static if (N == 4) private enum AS = "x y z w|r g b a"; 59 mixin accessByString!(T, N, "data", AS); 60 } 61 --- 62 */ 63 mixin template accessByString( T, size_t N, string data, string accessString, string dataSep=" ", string accessSep="|") 64 if( isCompatibleArrayAccessStrings(N,accessString,dataSep,accessSep) ) { 65 pure @property { 66 T opDispatch(string v)() const if( getIndex(accessString,v,dataSep,accessSep) != -1 ) { 67 mixin( format( "return this.%s[%d];", data, getIndex(accessString,v,dataSep,accessSep) ) ); 68 } 69 70 ref T opDispatch(string v)() if( getIndex(accessString,v,dataSep,accessSep) != -1 ) { 71 mixin( format( "return this.%s[%d];", data, getIndex(accessString,v,dataSep,accessSep) ) ); 72 } 73 74 static if( isOneSymbolPerFieldForAnyAccessString(accessString,dataSep,accessSep) ) { 75 auto opDispatch(string v)() const 76 if( v.length > 1 && oneOfAnyAccessAll(accessString,v,dataSep,accessSep) ) { 77 static string gen() { 78 string[] res; 79 foreach( i, sym; v ) 80 res ~= format( "this.%s[%d]", data, getIndex( accessString, ""~sym, dataSep, accessSep ) ); 81 return res.join(","); 82 } 83 84 mixin( `return Vector!(T, v.length)(` ~ gen() ~ `);` ); 85 } 86 87 auto opDispatch(string v,U)( in U b ) 88 if( v.length > 1 && oneOfAnyAccessAll(accessString,v,dataSep,accessSep) && 89 isCompatibleArrayAccessString(v.length,v) && 90 ( isSpecVector!(v.length,T,U) || ( isDynamicVector!U && is(typeof(T(U.datatype.init))) ) ) ) { 91 static if( b.isDynamic ) enforce( v.length == b.length ); 92 93 static string gen() { 94 string[] res; 95 foreach( i, sym; v ) 96 res ~= format( "this.%s[%d] = T( b[%d] );", data, 97 getIndex( accessString, ""~sym, dataSep, accessSep ), i ); 98 return res.join("\n"); 99 } 100 101 mixin( gen() ); 102 return b; 103 } 104 } 105 } 106 } 107 108 /// compatible for creating access dispatches 109 pure bool isCompatibleArrayAccessStrings( size_t N, string str, string sep1="", string sep2="|" ) 110 in { assert( sep1 != sep2 ); } do { 111 auto strs = str.split(sep2); 112 foreach( s; strs ) 113 if( !isCompatibleArrayAccessString(N,s,sep1) ) 114 return false; 115 116 string[] fa; 117 foreach( s; strs ) 118 fa ~= s.split(sep1); 119 120 foreach( ref v; fa ) v = strip(v); 121 122 foreach( i, a; fa ) 123 foreach( j, b; fa ) 124 if( i != j && a == b ) return false; 125 126 return true; 127 } 128 129 130 /// compatible for creating access dispatches 131 pure bool isCompatibleArrayAccessString( size_t N, string str, string sep="" ) { 132 return N == getAccessFieldsCount(str,sep) && isArrayAccessString(str,sep); 133 } 134 135 /// 136 pure bool isArrayAccessString( in string as, in string sep="", bool allowDot=false ) { 137 if( as.length == 0 ) return false; 138 auto splt = as.split(sep); 139 foreach( i, val; splt ) 140 if( !isValueAccessString(val,allowDot) || canFind(splt[0..i],val) ) 141 return false; 142 return true; 143 } 144 145 /// 146 pure size_t getAccessFieldsCount( string str, string sep ) { return str.split(sep).length; } 147 148 /// 149 pure ptrdiff_t getIndex( string as, string arg, string sep1="", string sep2="|" ) 150 in { assert( sep1 != sep2 ); } do { 151 foreach( str; as.split(sep2) ) 152 foreach( i, v; str.split(sep1) ) 153 if( arg == v ) return i; 154 return -1; 155 } 156 157 /// 158 pure bool oneOfAccess( string str, string arg, string sep="" ) { 159 auto splt = str.split(sep); 160 return canFind(splt,arg); 161 } 162 163 /// 164 pure bool oneOfAccessAll( string str, string arg, string sep="" ) { 165 auto splt = arg.split(""); 166 return all!(a=>oneOfAccess(str,a,sep))(splt); 167 } 168 169 /// 170 pure bool oneOfAnyAccessAll( string str, string arg, string sep1="", string sep2="|" ) 171 in { assert( sep1 != sep2 ); } do { 172 foreach( s; str.split(sep2) ) 173 if( oneOfAccessAll(s,arg,sep1) ) return true; 174 return false; 175 } 176 177 /// check symbol count for access to field 178 pure bool isOneSymbolPerFieldForAnyAccessString( string str, string sep1="", string sep2="|" ) 179 in { assert( sep1 != sep2 ); } do { 180 foreach( s; str.split(sep2) ) 181 if( isOneSymbolPerFieldAccessString(s,sep1) ) return true; 182 return false; 183 } 184 185 /// check symbol count for access to field 186 pure bool isOneSymbolPerFieldAccessString( string str, string sep="" ) { 187 foreach( s; str.split(sep) ) 188 if( s.length > 1 ) return false; 189 return true; 190 } 191 192 pure { 193 194 bool isValueAccessString( in string as, bool allowDot=false ) { 195 return as.length > 0 && 196 startsWithAllowedChars(as) && 197 (allowDot?(all!(a=>isValueAccessString(a))(as.split("."))):allowedCharsOnly(as)); 198 } 199 200 bool startsWithAllowedChars( in string as ) { 201 switch(as[0]) { 202 case 'a': .. case 'z': goto case; 203 case 'A': .. case 'Z': goto case; 204 case '_': return true; 205 default: return false; 206 } 207 } 208 209 bool allowedCharsOnly( in string as ) { 210 foreach( c; as ) if( !allowedChar(c) ) return false; 211 return true; 212 } 213 214 bool allowedChar( in char c ) { 215 switch(c) { 216 case 'a': .. case 'z': goto case; 217 case 'A': .. case 'Z': goto case; 218 case '0': .. case '9': goto case; 219 case '_': return true; 220 default: return false; 221 } 222 } 223 224 }