1 module dcl.util; 2 3 import std..string; 4 import std.range; 5 import std.exception; 6 import std.algorithm; 7 8 version(unittest) 9 { 10 import std.stdio; 11 import dcl.error; 12 } 13 14 package: 15 16 /// generate propertyes for subject 17 string infoMixin( string subject, string enumname, in string[] list, string[] ids=null ) 18 { 19 string[] ret; 20 21 foreach( ln; list ) 22 { 23 auto tpl = splitInfoLine( ln ); 24 25 ret ~= fformat( 26 q{%D_TYPE% %PROP_NAME%() @property { 27 28 if( %IDSISNULL% ) 29 throw new CLException( "id is null" ); 30 31 import std.traits; 32 import std.algorithm; 33 import std.array; 34 import std.conv; 35 36 alias T=%CL_TYPE%; 37 alias R=%D_TYPE%; 38 39 // TODO: it's for strings 40 static if( isDynamicArray!T ) 41 { 42 size_t len; 43 checkCall!clGet%CC_SUBJ%Info( %IDS%, %CL_PARAM_NAME%, 0, null, &len ); 44 alias ueT = Unqual!(ElementEncodingType!T); 45 auto buf = new ueT[]( len ); 46 if( len == 0 ) return null; 47 checkCall!clGet%CC_SUBJ%Info( %IDS%, 48 %CL_PARAM_NAME%, len * ueT.sizeof, buf.ptr, &len ); 49 50 return to!R(buf[0 .. len/ueT.sizeof - cast(size_t)isSomeString!T ]); 51 } 52 else 53 { 54 T val; 55 56 checkCall!clGet%CC_SUBJ%Info( %IDS%, 57 %CL_PARAM_NAME%, typeof(val).sizeof, &val, null ); 58 59 static if( is( R == class ) ) return R.getFromID( val ); 60 else static if( is( R == enum ) ) return cast(R)val; 61 else return to!R(val); 62 } 63 }}, 64 65 [ 66 "CC_SUBJ": toCamelCase( subject ), 67 "CL_TYPE": tpl.cl_type, 68 "D_TYPE": tpl.d_type, 69 "CL_PARAM_NAME": paramEnumName( "CL" ~ "_" ~ enumname.toUpper, tpl.cl_param_name ), 70 "PROP_NAME": tpl.prop_name, 71 "IDS": ids is null ? "id" : ids.map!(a=>a ~ ".id").array.join(", "), 72 "IDSISNULL": ids is null ? "id is null" : ids.map!(a=>a ~ ".id is null").array.join(" || ") 73 ] 74 ); 75 } 76 77 return ret.join("\n"); 78 } 79 80 /// ditto 81 string infoMixin( string subject, in string[] list ) 82 { 83 return infoMixin( subject, subject, list ); 84 } 85 86 unittest 87 { 88 import std.stdio; 89 auto info_list = 90 [ 91 "cl_command_type:Command command_type:command", 92 "cl_int:Status command_execution_status:status", 93 "uint reference_count:ref_count", 94 "uint max_block" 95 ]; 96 //writeln( infoMixin( "event", info_list ) ); 97 } 98 99 /++ split info line 100 + 101 + Rules: 102 + --- 103 + property: 104 + type name 105 + 106 + type: 107 + d_type 108 + cl_type:d_type 109 + 110 + name: 111 + prop_name 112 + cl_param_name:prop_name 113 + --- 114 +/ 115 auto splitInfoLine( string ln ) 116 { 117 static struct Result { string d_type, cl_type, prop_name, cl_param_name; } 118 119 auto splt = ln.strip.split(" "); 120 enforce( splt.length == 2, 121 format( "bad info format '%s', need one space between type and name", ln.strip ) ); 122 123 auto types = splt[0].split(":").cycle; 124 125 Result ret; 126 127 ret.d_type = types[1]; 128 ret.cl_type = types[0]; 129 130 auto names = splt[1].split(":").cycle; 131 132 ret.prop_name = names[1]; 133 ret.cl_param_name = names[0]; 134 135 return ret; 136 } 137 138 /// 139 unittest 140 { 141 auto r = splitInfoLine( "uint param" ); 142 assertEq( r.d_type, "uint" ); 143 assertEq( r.cl_type, "uint" ); 144 assertEq( r.prop_name, "param" ); 145 assertEq( r.cl_param_name, "param" ); 146 } 147 148 /// 149 unittest 150 { 151 auto r = splitInfoLine( "cl_uint:MyEnum param" ); 152 assertEq( r.d_type, "MyEnum" ); 153 assertEq( r.cl_type, "cl_uint" ); 154 assertEq( r.prop_name, "param" ); 155 assertEq( r.cl_param_name, "param" ); 156 } 157 158 /// 159 unittest 160 { 161 auto r = splitInfoLine( "uint param:prop" ); 162 assertEq( r.d_type, "uint" ); 163 assertEq( r.cl_type, "uint" ); 164 assertEq( r.prop_name, "prop" ); 165 assertEq( r.cl_param_name, "param" ); 166 } 167 168 /// 169 unittest 170 { 171 auto r = splitInfoLine( "cl_uint:MyEnum param:prop" ); 172 assertEq( r.d_type, "MyEnum" ); 173 assertEq( r.cl_type, "cl_uint" ); 174 assertEq( r.prop_name, "prop" ); 175 assertEq( r.cl_param_name, "param" ); 176 } 177 178 string paramEnumName( string prefix, string name ) 179 { 180 if( name.startsWith("!") ) return "CL_" ~ name[1..$].toUpper; 181 return prefix ~ "_" ~ name.toUpper; 182 } 183 184 unittest 185 { 186 assertEq( paramEnumName( "CL_PLATFORM", "name" ), "CL_PLATFORM_NAME" ); 187 assertEq( paramEnumName( "CL_PLATFORM", "nAmE" ), "CL_PLATFORM_NAME" ); 188 assertEq( paramEnumName( "CL_DEVICE", "max_work_item_dimensions" ), 189 "CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS" ); 190 assertEq( paramEnumName( "CL_DEVICE", "!driver_version" ), "CL_DRIVER_VERSION" ); 191 } 192 193 string fformat( string input, string[string] dict ) 194 { 195 //string rplc( Captures!string m ) 196 //{ return dict[ m.hit[1..$-1] ]; } 197 //return replaceAll!(rplc)( input, ctRegex!( r"%\w*%" ) ); 198 199 string rplc( string m ) 200 { 201 //import std.stdio; 202 //stderr.writeln( m ); 203 return dict[m[1..$-1]]; 204 } 205 return replaceWords!(rplc)( input ); 206 } 207 208 209 unittest 210 { 211 auto input = 212 q{ hello %NAME% 213 i have %SUBJ% for you }; 214 215 auto expect = 216 q{ hello Ivan 217 i have question for you }; 218 219 auto result = fformat( input, [ "NAME": "Ivan", "SUBJ": "question" ] ); 220 221 assertEq( result, expect ); 222 } 223 224 unittest 225 { 226 auto input = q{ hello %NAME% i have %SUBJ% for you}; 227 auto expect = q{ hello Ivan i have question for you}; 228 auto result = fformat( input, [ "NAME": "Ivan", "SUBJ": "question" ] ); 229 assertEq( result, expect ); 230 } 231 232 unittest 233 { 234 auto input = q{ hello %NAME% i have %SUBJ% for you %s}; 235 auto expect = q{ hello Ivan i have question for you %s}; 236 auto result = fformat( input, [ "NAME": "Ivan", "SUBJ": "question" ] ); 237 assertEq( result, expect ); 238 } 239 240 unittest 241 { 242 auto input = q{ hello %NAME% i have %SUBJ% for you %s }; 243 auto expect = q{ hello Ivan i have question for you %s }; 244 auto result = fformat( input, [ "NAME": "Ivan", "SUBJ": "question" ] ); 245 assertEq( result, expect ); 246 } 247 248 unittest 249 { 250 auto input = q{%NAME% i have %SUBJ% for you}; 251 auto expect = q{Ivan i have question for you}; 252 auto result = fformat( input, [ "NAME": "Ivan", "SUBJ": "question" ] ); 253 assertEq( result, expect ); 254 } 255 256 unittest 257 { 258 auto input = q{%NAME% i %% have %SUBJ% for you}; 259 auto expect = q{Ivan i %% have question for you}; 260 auto result = fformat( input, [ "NAME": "Ivan", "SUBJ": "question" ] ); 261 assertEq( result, expect ); 262 } 263 264 unittest 265 { 266 auto input = q{%%%NAME% i have %SUBJ% for you}; 267 auto expect = q{%%Ivan i have question for you}; 268 auto result = fformat( input, [ "NAME": "Ivan", "SUBJ": "question" ] ); 269 assertEq( result, expect ); 270 } 271 272 unittest 273 { 274 auto input = q{%NAME% i have %SUBJ% for you%%}; 275 auto expect = q{Ivan i have question for you%%}; 276 auto result = fformat( input, [ "NAME": "Ivan", "SUBJ": "question" ] ); 277 assertEq( result, expect ); 278 } 279 280 unittest 281 { 282 auto input = q{%NAME% i have%%%}; 283 auto expect = q{Ivan i have%%%}; 284 auto result = fformat( input, [ "NAME": "Ivan", "SUBJ": "question" ] ); 285 assertEq( result, expect ); 286 } 287 288 unittest 289 { 290 auto input = q{%NAME %i have %SUBJ%%}; 291 auto expect = q{%NAME %i have question%}; 292 auto result = fformat( input, [ "NAME": "Ivan", "SUBJ": "question" ] ); 293 assertEq( result, expect ); 294 } 295 296 /// 297 string toSnakeCase( in string str, bool ignore_first=true ) @property pure @trusted 298 { 299 string[] buf; 300 buf ~= ""; 301 foreach( i, ch; str ) 302 { 303 if( [ch].toUpper == [ch] ) buf ~= ""; 304 buf[$-1] ~= [ch].toLower; 305 } 306 if( buf[0].length == 0 && ignore_first ) 307 buf = buf[1..$]; 308 return buf.join("_"); 309 } 310 311 /// 312 unittest 313 { 314 assertEq( "SomeVar".toSnakeCase, "some_var" ); 315 assertEq( "SomeVar".toSnakeCase(false), "_some_var" ); 316 317 assertEq( "someVar".toSnakeCase, "some_var" ); 318 assertEq( "someVar".toSnakeCase(false), "some_var" ); 319 320 assertEq( "ARB".toSnakeCase, "a_r_b" ); 321 assertEq( "ARB".toSnakeCase(false), "_a_r_b" ); 322 323 // not alphabetic chars in upper case looks like lower, func separate by them 324 assertEq( "A.B.r.A".toSnakeCase, "a_._b_.r_._a" ); 325 assertEq( "A_B_r_A".toSnakeCase, "a___b__r___a" ); 326 } 327 328 /// 329 string toCamelCaseBySep( in string str, string sep="_", bool first_capitalize=true ) pure @trusted 330 { 331 auto arr = str.split(sep).filter!(a=>a.length>0).array; 332 string[] ret; 333 foreach( i, v; arr ) 334 { 335 auto bb = v.capitalize; 336 if( i == 0 && !first_capitalize ) 337 bb = v.toLower; 338 ret ~= bb; 339 } 340 return ret.join(""); 341 } 342 343 /// 344 unittest 345 { 346 assertEq( toCamelCaseBySep( "single-precision-constant", "-", false ), "singlePrecisionConstant" ); 347 assertEq( toCamelCaseBySep( "one.two.three", ".", true ), "OneTwoThree" ); 348 assertEq( toCamelCaseBySep( "one..three", ".", true ), "OneThree" ); 349 assertEq( toCamelCaseBySep( "one/three", "/" ), "OneThree" ); 350 assertEq( toCamelCaseBySep( "one_.three", ".", false ), "one_Three" ); 351 352 // `_` in upper case looks equals as lower case 353 assertEq( toCamelCaseBySep( "one._three", ".", true ), "One_three" ); 354 } 355 356 /// 357 string toCamelCase( in string str, bool first_capitalize=true ) @property pure @trusted 358 { return toCamelCaseBySep( str, "_", first_capitalize ); } 359 360 /// 361 unittest 362 { 363 assertEq( "some_class".toCamelCase, "SomeClass" ); 364 assertEq( "_some_class".toCamelCase, "SomeClass" ); 365 assertEq( "some_func".toCamelCase(false), "someFunc" ); 366 assertEq( "_some_func".toCamelCase(false), "someFunc" ); 367 assertEq( "a_r_b".toCamelCase, "ARB" ); 368 assertEq( toCamelCase( "program_build" ), "ProgramBuild" ); 369 assertEq( toCamelCase( "program__build" ), "ProgramBuild" ); 370 371 assertEq( toCamelCase( "program__build", false ), toCamelCaseBySep( "program__build", "_", false ) ); 372 } 373 374 private: 375 376 string replaceWords(alias fun)( string s ) 377 { 378 string ret; 379 size_t p0 = 0, p1 = 0; 380 381 void inc() { p1++; } 382 void dump() { ret ~= s[min(p0,$)..min(p1,$)]; p0 = p1; } 383 void dumpfun() { ret ~= fun( s[p0..p1] ); p0 = p1; } 384 385 m:while( p1 < s.length ) 386 { 387 if( s[p1] == '%' ) 388 { 389 dump; inc; 390 while( p1 < s.length ) 391 { 392 if( s[p1] == '%' ) 393 { 394 inc; 395 if( p1-2 == p0 ) // if no symbol between %% 396 continue m; 397 else 398 dumpfun; 399 break; 400 } 401 else if( !identitySymbol(s[p1]) ) { inc; break; } 402 inc; 403 } 404 } 405 inc; 406 if( p1 >= s.length ) dump(); 407 } 408 if( p0 != p1 ) ret ~= s[p0..$]; // in case endWith("%%") 409 return ret; 410 } 411 412 bool identitySymbol( char c ) 413 { 414 switch(c) 415 { 416 case 'a': .. case 'z': case 'A': .. case 'Z': 417 case '_': case '0': .. case '9': return true; 418 default: return false; 419 } 420 }