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