1 /* Digital Mars DMDScript source code. 2 * Copyright (c) 2000-2002 by Chromium Communications 3 * D version Copyright (c) 2004-2010 by Digital Mars 4 * Distributed under the Boost Software License, Version 1.0. 5 * (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 * written by Walter Bright 7 * http://www.digitalmars.com 8 * 9 * D2 port by Dmitry Olshansky 10 * 11 * DMDScript is implemented in the D Programming Language, 12 * http://www.digitalmars.com/d/ 13 * 14 * For a C++ implementation of DMDScript, including COM support, see 15 * http://www.digitalmars.com/dscript/cppscript.html 16 */ 17 18 19 module dmdscript.dstring; 20 21 import undead.regexp; 22 import std.utf; 23 import core.stdc.stdlib; 24 import core.stdc..string; 25 import std.exception; 26 import std.algorithm; 27 import std.range; 28 import std.stdio; 29 30 import dmdscript.script; 31 import dmdscript.dobject; 32 import dmdscript.dregexp; 33 import dmdscript.darray; 34 import dmdscript.value; 35 import dmdscript.threadcontext; 36 import dmdscript.dfunction; 37 import dmdscript.text; 38 import dmdscript.property; 39 import dmdscript.errmsgs; 40 import dmdscript.dnative; 41 42 //alias script.tchar tchar; 43 44 /* ===================== Dstring_fromCharCode ==================== */ 45 46 void* Dstring_fromCharCode(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 47 { 48 static import dmdscript.utf; 49 50 // ECMA 15.5.3.2 51 d_string s; 52 53 for(size_t i = 0; i < arglist.length; i++) 54 { 55 Value* v; 56 uint u; 57 58 v = &arglist[i]; 59 u = v.toUint16(); 60 //writef("string.fromCharCode(%x)", u); 61 if(!std.utf.isValidDchar(u)) 62 { 63 ErrInfo errinfo; 64 65 ret.putVundefined(); 66 return pthis.RuntimeError(&errinfo, 67 errmsgtbl[ERR_NOT_VALID_UTF], 68 "String", "fromCharCode()", 69 u); 70 } 71 dmdscript.utf.encode(s, u); 72 //writefln("s[0] = %x, s = '%s'", s[0], s); 73 } 74 ret.putVstring(s); 75 return null; 76 } 77 78 /* ===================== Dstring_constructor ==================== */ 79 80 class DstringConstructor : Dfunction 81 { 82 this() 83 { 84 super(1, Dfunction_prototype); 85 name = "String"; 86 87 static enum NativeFunctionData[] nfd = 88 [ 89 { TEXT_fromCharCode, &Dstring_fromCharCode, 1 }, 90 ]; 91 92 DnativeFunction.initialize(this, nfd, 0); 93 } 94 95 override void *Construct(CallContext *cc, Value *ret, Value[] arglist) 96 { 97 // ECMA 15.5.2 98 d_string s; 99 Dobject o; 100 101 s = (arglist.length) ? arglist[0].toString() : TEXT_; 102 o = new Dstring(s); 103 ret.putVobject(o); 104 return null; 105 } 106 107 override void *Call(CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 108 { 109 // ECMA 15.5.1 110 d_string s; 111 112 s = (arglist.length) ? arglist[0].toString() : TEXT_; 113 ret.putVstring(s); 114 return null; 115 } 116 } 117 118 119 /* ===================== Dstring_prototype_toString =============== */ 120 121 void* Dstring_prototype_toString(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 122 { 123 //writef("Dstring.prototype.toString()\n"); 124 // othis must be a String 125 if(!othis.isClass(TEXT_String)) 126 { 127 ErrInfo errinfo; 128 129 ret.putVundefined(); 130 return pthis.RuntimeError(&errinfo, 131 errmsgtbl[ERR_FUNCTION_WANTS_STRING], 132 TEXT_toString, 133 othis.classname); 134 } 135 else 136 { 137 Value *v; 138 139 v = &(cast(Dstring)othis).value; 140 Value.copy(ret, v); 141 } 142 return null; 143 } 144 145 /* ===================== Dstring_prototype_valueOf =============== */ 146 147 void* Dstring_prototype_valueOf(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 148 { 149 // Does same thing as String.prototype.toString() 150 151 //writef("string.prototype.valueOf()\n"); 152 // othis must be a String 153 if(!othis.isClass(TEXT_String)) 154 { 155 ErrInfo errinfo; 156 157 ret.putVundefined(); 158 return pthis.RuntimeError(&errinfo, 159 errmsgtbl[ERR_FUNCTION_WANTS_STRING], 160 TEXT_valueOf, 161 othis.classname); 162 } 163 else 164 { 165 Value *v; 166 167 v = &(cast(Dstring)othis).value; 168 Value.copy(ret, v); 169 } 170 return null; 171 } 172 173 /* ===================== Dstring_prototype_charAt =============== */ 174 175 void* Dstring_prototype_charAt(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 176 { 177 // ECMA 15.5.4.4 178 179 Value *v; 180 int pos; // ECMA says pos should be a d_number, 181 // but int should behave the same 182 d_string s; 183 d_string result; 184 185 v = &othis.value; 186 s = v.toString(); 187 v = arglist.length ? &arglist[0] : &vundefined; 188 pos = cast(int)v.toInteger(); 189 190 result = TEXT_; 191 192 if(pos >= 0) 193 { 194 size_t idx; 195 196 while(1) 197 { 198 if(idx == s.length) 199 break; 200 if(pos == 0) 201 { 202 result = s[idx .. idx + std.utf.stride(s, idx)]; 203 break; 204 } 205 idx += std.utf.stride(s, idx); 206 pos--; 207 } 208 } 209 210 ret.putVstring(result); 211 return null; 212 } 213 214 /* ===================== Dstring_prototype_charCodeAt ============= */ 215 216 void* Dstring_prototype_charCodeAt(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 217 { 218 // ECMA 15.5.4.5 219 220 Value *v; 221 int pos; // ECMA says pos should be a d_number, 222 // but int should behave the same 223 d_string s; 224 uint len; 225 d_number result; 226 227 v = &othis.value; 228 s = v.toString(); 229 v = arglist.length ? &arglist[0] : &vundefined; 230 pos = cast(int)v.toInteger(); 231 232 result = d_number.nan; 233 234 if(pos >= 0) 235 { 236 size_t idx; 237 238 while(1) 239 { 240 assert(idx <= s.length); 241 if(idx == s.length) 242 break; 243 if(pos == 0) 244 { 245 result = std.utf.decode(s, idx); 246 break; 247 } 248 idx += std.utf.stride(s, idx); 249 pos--; 250 } 251 } 252 253 ret.putVnumber(result); 254 return null; 255 } 256 257 /* ===================== Dstring_prototype_concat ============= */ 258 259 void* Dstring_prototype_concat(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 260 { 261 // ECMA v3 15.5.4.6 262 d_string s; 263 264 //writefln("Dstring.prototype.concat()"); 265 266 s = othis.value.toString(); 267 for(size_t a = 0; a < arglist.length; a++) 268 s ~= arglist[a].toString(); 269 270 ret.putVstring(s); 271 return null; 272 } 273 274 /* ===================== Dstring_prototype_indexOf ============= */ 275 276 void* Dstring_prototype_indexOf(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 277 { 278 import std..string : indexOf; 279 import std.utf : toUCSindex, toUTFindex; 280 281 // ECMA 15.5.4.6 282 // String.prototype.indexOf(searchString, position) 283 284 Value* v1; 285 Value* v2; 286 ptrdiff_t pos; // ECMA says pos should be a d_number, 287 // but I can't find a reason. 288 d_string s; 289 size_t sUCSdim; 290 291 d_string searchString; 292 ptrdiff_t k; 293 294 Value xx; 295 xx.putVobject(othis); 296 s = xx.toString(); 297 sUCSdim = toUCSindex(s, s.length); 298 299 v1 = arglist.length ? &arglist[0] : &vundefined; 300 v2 = (arglist.length >= 2) ? &arglist[1] : &vundefined; 301 302 searchString = v1.toString(); 303 pos = cast(int)v2.toInteger(); 304 305 if(pos < 0) 306 pos = 0; 307 else if(pos > sUCSdim) 308 pos = sUCSdim; 309 310 if(searchString.length == 0) 311 k = pos; 312 else 313 { 314 pos = toUTFindex(s, pos); 315 k = indexOf(s[pos .. $], searchString); 316 if(k != -1) 317 k = toUCSindex(s, pos + k); 318 } 319 320 ret.putVnumber(k); 321 return null; 322 } 323 324 /* ===================== Dstring_prototype_lastIndexOf ============= */ 325 326 void* Dstring_prototype_lastIndexOf(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 327 { 328 import std.math : isNaN; 329 import std..string : lastIndexOf; 330 import std.utf : toUCSindex, toUTFindex; 331 332 // ECMA v3 15.5.4.8 333 // String.prototype.lastIndexOf(searchString, position) 334 335 Value *v1; 336 ptrdiff_t pos; // ECMA says pos should be a d_number, 337 // but I can't find a reason. 338 d_string s; 339 size_t sUCSdim; 340 d_string searchString; 341 ptrdiff_t k; 342 343 version(all) 344 { 345 { 346 // This is the 'transferable' version 347 Value *v; 348 void *a; 349 v = othis.Get(TEXT_toString); 350 a = v.Call(cc, othis, ret, null); 351 if(a) // if exception was thrown 352 return a; 353 s = ret.toString(); 354 } 355 } 356 else 357 { 358 // the 'builtin' version 359 s = othis.value.toString(); 360 } 361 sUCSdim = toUCSindex(s, s.length); 362 363 v1 = arglist.length ? &arglist[0] : &vundefined; 364 searchString = v1.toString(); 365 if(arglist.length >= 2) 366 { 367 d_number n; 368 Value *v = &arglist[1]; 369 370 n = v.toNumber(); 371 if(isNaN(n) || n > sUCSdim) 372 pos = sUCSdim; 373 else if(n < 0) 374 pos = 0; 375 else 376 pos = cast(int)n; 377 } 378 else 379 pos = sUCSdim; 380 381 //writef("len = %d, p = '%ls'\n", len, p); 382 //writef("pos = %d, sslen = %d, ssptr = '%ls'\n", pos, sslen, ssptr); 383 //writefln("s = '%s', pos = %s, searchString = '%s'", s, pos, searchString); 384 385 if(searchString.length == 0) 386 k = pos; 387 else 388 { 389 pos = toUTFindex(s, pos); 390 pos += searchString.length; 391 if(pos > s.length) 392 pos = s.length; 393 k = lastIndexOf(s[0 .. pos], searchString); 394 //writefln("s = '%s', pos = %s, searchString = '%s', k = %d", s, pos, searchString, k); 395 if(k != -1) 396 k = toUCSindex(s, k); 397 } 398 ret.putVnumber(k); 399 return null; 400 } 401 402 /* ===================== Dstring_prototype_localeCompare ============= */ 403 404 void* Dstring_prototype_localeCompare(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 405 { 406 // ECMA v3 15.5.4.9 407 d_string s1; 408 d_string s2; 409 d_number n; 410 Value *v; 411 412 v = &othis.value; 413 s1 = v.toString(); 414 s2 = arglist.length ? arglist[0].toString() : vundefined.toString(); 415 n = localeCompare(cc, s1, s2); 416 ret.putVnumber(n); 417 return null; 418 } 419 420 /* ===================== Dstring_prototype_match ============= */ 421 422 void* Dstring_prototype_match(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 423 { 424 // ECMA v3 15.5.4.10 425 Dregexp r; 426 Dobject o; 427 428 if(arglist.length && !arglist[0].isPrimitive() && 429 (o = arglist[0].toObject()).isDregexp()) 430 { 431 } 432 else 433 { 434 Value regret; 435 436 regret.putVobject(null); 437 Dregexp.getConstructor().Construct(cc, ®ret, arglist); 438 o = regret.object; 439 } 440 441 r = cast(Dregexp)o; 442 if(r.global.dbool) 443 { 444 Darray a = new Darray; 445 d_int32 n; 446 d_int32 i; 447 d_int32 lasti; 448 449 i = 0; 450 lasti = 0; 451 for(n = 0;; n++) 452 { 453 r.lastIndex.putVnumber(i); 454 Dregexp.exec(r, ret, (&othis.value)[0 .. 1], EXEC_STRING); 455 if(!ret..string) // if match failed 456 { 457 r.lastIndex.putVnumber(i); 458 break; 459 } 460 lasti = i; 461 i = cast(d_int32)r.lastIndex.toInt32(); 462 if(i == lasti) // if no source was consumed 463 i++; // consume a character 464 465 a.Put(n, ret, 0); // a[n] = ret; 466 } 467 ret.putVobject(a); 468 } 469 else 470 { 471 Dregexp.exec(r, ret, (&othis.value)[0 .. 1], EXEC_ARRAY); 472 } 473 return null; 474 } 475 476 /* ===================== Dstring_prototype_replace ============= */ 477 478 void* Dstring_prototype_replace(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 479 { 480 import std..string : indexOf; 481 // ECMA v3 15.5.4.11 482 // String.prototype.replace(searchValue, replaceValue) 483 484 d_string string; 485 d_string searchString; 486 d_string newstring; 487 Value *searchValue; 488 Value *replaceValue; 489 Dregexp r; 490 RegExp re; 491 d_string replacement; 492 d_string result; 493 int m; 494 int i; 495 int lasti; 496 regmatch_t[1] pmatch; 497 Dfunction f; 498 Value* v; 499 500 v = &othis.value; 501 string = v.toString(); 502 searchValue = (arglist.length >= 1) ? &arglist[0] : &vundefined; 503 replaceValue = (arglist.length >= 2) ? &arglist[1] : &vundefined; 504 r = Dregexp.isRegExp(searchValue); 505 f = Dfunction.isFunction(replaceValue); 506 if(r) 507 { 508 int offset = 0; 509 510 re = r.re; 511 i = 0; 512 result = string; 513 514 r.lastIndex.putVnumber(0); 515 for(;; ) 516 { 517 Dregexp.exec(r, ret, (&othis.value)[0 .. 1], EXEC_STRING); 518 if(!ret..string) // if match failed 519 break; 520 521 m = re.re_nsub; 522 if(f) 523 { 524 Value* alist; 525 526 alist = cast(Value *)alloca((m + 3) * Value.sizeof); 527 assert(alist); 528 alist[0].putVstring(ret..string); 529 for(i = 0; i < m; i++) 530 { 531 alist[1 + i].putVstring( 532 string[re.pmatch[1 + i].rm_so .. re.pmatch[1 + i].rm_eo]); 533 } 534 alist[m + 1].putVnumber(re.pmatch[0].rm_so); 535 alist[m + 2].putVstring(string); 536 f.Call(cc, f, ret, alist[0 .. m + 3]); 537 replacement = ret.toString(); 538 } 539 else 540 { 541 newstring = replaceValue.toString(); 542 replacement = re.replace(newstring); 543 } 544 ptrdiff_t starti = re.pmatch[0].rm_so + offset; 545 ptrdiff_t endi = re.pmatch[0].rm_eo + offset; 546 result = string[0 .. starti] ~ 547 replacement ~ 548 string[endi .. $]; 549 550 if(re.attributes & RegExp.REA.global) 551 { 552 offset += replacement.length - (endi - starti); 553 554 // If no source was consumed, consume a character 555 lasti = i; 556 i = cast(d_int32)r.lastIndex.toInt32(); 557 if(i == lasti) 558 { 559 i++; 560 r.lastIndex.putVnumber(i); 561 } 562 } 563 else 564 break; 565 } 566 } 567 else 568 { 569 searchString = searchValue.toString(); 570 ptrdiff_t match = indexOf(string, searchString); 571 if(match >= 0) 572 { 573 pmatch[0].rm_so = match; 574 pmatch[0].rm_eo = match + searchString.length; 575 if(f) 576 { 577 Value[3] alist; 578 579 alist[0].putVstring(searchString); 580 alist[1].putVnumber(pmatch[0].rm_so); 581 alist[2].putVstring(string); 582 f.Call(cc, f, ret, alist); 583 replacement = ret.toString(); 584 } 585 else 586 { 587 newstring = replaceValue.toString(); 588 replacement = RegExp.replace3(newstring, string, pmatch); 589 } 590 result = string[0 .. match] ~ 591 replacement ~ 592 string[match + searchString.length .. $]; 593 } 594 else 595 { 596 result = string; 597 } 598 } 599 600 ret.putVstring(result); 601 return null; 602 } 603 604 /* ===================== Dstring_prototype_search ============= */ 605 606 void* Dstring_prototype_search(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 607 { 608 // ECMA v3 15.5.4.12 609 Dregexp r; 610 Dobject o; 611 612 //writef("String.prototype.search()\n"); 613 if(arglist.length && !arglist[0].isPrimitive() && 614 (o = arglist[0].toObject()).isDregexp()) 615 { 616 } 617 else 618 { 619 Value regret; 620 621 regret.putVobject(null); 622 Dregexp.getConstructor().Construct(cc, ®ret, arglist); 623 o = regret.object; 624 } 625 626 r = cast(Dregexp)o; 627 Dregexp.exec(r, ret, (&othis.value)[0 .. 1], EXEC_INDEX); 628 return null; 629 } 630 631 /* ===================== Dstring_prototype_slice ============= */ 632 633 void* Dstring_prototype_slice(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 634 { 635 // ECMA v3 15.5.4.13 636 ptrdiff_t start; 637 ptrdiff_t end; 638 ptrdiff_t sUCSdim; 639 d_string s; 640 d_string r; 641 Value *v; 642 643 v = &othis.value; 644 s = v.toString(); 645 sUCSdim = std.utf.toUCSindex(s, s.length); 646 switch(arglist.length) 647 { 648 case 0: 649 start = 0; 650 end = sUCSdim; 651 break; 652 653 case 1: 654 start = arglist[0].toInt32(); 655 end = sUCSdim; 656 break; 657 658 default: 659 start = arglist[0].toInt32(); 660 end = arglist[1].toInt32(); 661 break; 662 } 663 664 if(start < 0) 665 { 666 start += sUCSdim; 667 if(start < 0) 668 start = 0; 669 } 670 else if(start >= sUCSdim) 671 start = sUCSdim; 672 673 if(end < 0) 674 { 675 end += sUCSdim; 676 if(end < 0) 677 end = 0; 678 } 679 else if(end >= sUCSdim) 680 end = sUCSdim; 681 682 if(start > end) 683 end = start; 684 685 start = toUTFindex(s, start); 686 end = toUTFindex(s, end); 687 r = s[start .. end]; 688 689 ret.putVstring(r); 690 return null; 691 } 692 693 694 /* ===================== Dstring_prototype_split ============= */ 695 696 void* Dstring_prototype_split(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 697 { 698 // ECMA v3 15.5.4.14 699 // String.prototype.split(separator, limit) 700 size_t lim; 701 size_t p; 702 size_t q; 703 size_t e; 704 Value* separator = &vundefined; 705 Value* limit = &vundefined; 706 Dregexp R; 707 RegExp re; 708 d_string rs; 709 d_string T; 710 d_string S; 711 Darray A; 712 int str; 713 714 //writefln("Dstring_prototype_split()"); 715 switch(arglist.length) 716 { 717 default: 718 limit = &arglist[1]; 719 goto case; 720 case 1: 721 separator = &arglist[0]; 722 goto case; 723 case 0: 724 break; 725 } 726 727 Value *v; 728 v = &othis.value; 729 S = v.toString(); 730 A = new Darray; 731 if(limit.isUndefined()) 732 lim = ~0u; 733 else 734 lim = limit.toUint32(); 735 p = 0; 736 R = Dregexp.isRegExp(separator); 737 if(R) // regular expression 738 { 739 re = R.re; 740 assert(re); 741 rs = null; 742 str = 0; 743 } 744 else // string 745 { 746 re = null; 747 rs = separator.toString(); 748 str = 1; 749 } 750 if(lim == 0) 751 goto Lret; 752 753 // ECMA v3 15.5.4.14 is specific: "If separator is undefined, then the 754 // result array contains just one string, which is the this value 755 // (converted to a string)." However, neither Javascript nor Jscript 756 // do that, they regard an undefined as being the string "undefined". 757 // We match Javascript/Jscript behavior here, not ECMA. 758 759 // Uncomment for ECMA compatibility 760 //if (!separator.isUndefined()) 761 { 762 //writefln("test1 S = '%s', rs = '%s'", S, rs); 763 if(S.length) 764 { 765 L10: 766 for(q = p; q != S.length; q++) 767 { 768 if(str) // string 769 { 770 if(q + rs.length <= S.length && !memcmp(S.ptr + q, rs.ptr, rs.length * tchar.sizeof)) 771 { 772 e = q + rs.length; 773 if(e != p) 774 { 775 T = S[p .. q]; 776 A.Put(cast(uint)A.length.number, T, 0); 777 if(A.length.number == lim) 778 goto Lret; 779 p = e; 780 goto L10; 781 } 782 } 783 } 784 else // regular expression 785 { 786 if(re.test(S, q)) 787 { 788 q = re.pmatch[0].rm_so; 789 e = re.pmatch[0].rm_eo; 790 if(e != p) 791 { 792 T = S[p .. q]; 793 //writefln("S = '%s', T = '%s', p = %d, q = %d, e = %d\n", S, T, p, q, e); 794 A.Put(cast(uint)A.length.number, T, 0); 795 if(A.length.number == lim) 796 goto Lret; 797 p = e; 798 for(uint i = 0; i < re.re_nsub; i++) 799 { 800 ptrdiff_t so = re.pmatch[1 + i].rm_so; 801 ptrdiff_t eo = re.pmatch[1 + i].rm_eo; 802 803 //writefln("i = %d, nsub = %s, so = %s, eo = %s, S.length = %s", i, re.re_nsub, so, eo, S.length); 804 if(so != -1 && eo != -1) 805 T = S[so .. eo]; 806 else 807 T = null; 808 A.Put(cast(uint)A.length.number, T, 0); 809 if(A.length.number == lim) 810 goto Lret; 811 } 812 goto L10; 813 } 814 } 815 } 816 } 817 T = S[p .. S.length]; 818 A.Put(cast(uint)A.length.number, T, 0); 819 goto Lret; 820 } 821 if(str) // string 822 { 823 if(rs.length <= S.length && S[0 .. rs.length] == rs[]) 824 goto Lret; 825 } 826 else // regular expression 827 { 828 if(re.test(S, 0)) 829 goto Lret; 830 } 831 } 832 833 A.Put(0u, S, 0); 834 Lret: 835 ret.putVobject(A); 836 return null; 837 } 838 839 840 /* ===================== Dstring_prototype_substr ============= */ 841 842 void *dstring_substring(d_string s, size_t sUCSdim, d_number start, d_number end, Value *ret) 843 { 844 import std.math : isNaN; 845 import std.utf : toUTFindex; 846 847 d_string sb; 848 d_int32 sb_len; 849 850 if(isNaN(start)) 851 start = 0; 852 else if(start > sUCSdim) 853 start = sUCSdim; 854 else if(start < 0) 855 start = 0; 856 857 if(isNaN(end)) 858 end = 0; 859 else if(end > sUCSdim) 860 end = sUCSdim; 861 else if(end < 0) 862 end = 0; 863 864 if(end < start) // swap 865 { 866 d_number t; 867 868 t = start; 869 start = end; 870 end = t; 871 } 872 873 size_t st = toUTFindex(s, cast(size_t)start); 874 size_t en = toUTFindex(s, cast(size_t)end); 875 sb = s[st .. en]; 876 877 ret.putVstring(sb); 878 return null; 879 } 880 881 void* Dstring_prototype_substr(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 882 { 883 import std.math : isNaN; 884 import std.utf : toUCSindex; 885 886 // Javascript: TDG pg. 689 887 // String.prototype.substr(start, length) 888 d_number start; 889 d_number length; 890 d_string s; 891 892 s = othis.value.toString(); 893 size_t sUCSdim = toUCSindex(s, s.length); 894 start = 0; 895 length = 0; 896 if(arglist.length >= 1) 897 { 898 start = arglist[0].toInteger(); 899 if(start < 0) 900 start = sUCSdim + start; 901 if(arglist.length >= 2) 902 { 903 length = arglist[1].toInteger(); 904 if(isNaN(length) || length < 0) 905 length = 0; 906 } 907 else 908 length = sUCSdim - start; 909 } 910 911 return dstring_substring(s, sUCSdim, start, start + length, ret); 912 } 913 914 /* ===================== Dstring_prototype_substring ============= */ 915 916 void* Dstring_prototype_substring(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 917 { 918 import std.utf : toUCSindex; 919 920 // ECMA 15.5.4.9 921 // String.prototype.substring(start) 922 // String.prototype.substring(start, end) 923 d_number start; 924 d_number end; 925 d_string s; 926 927 //writefln("String.prototype.substring()"); 928 s = othis.value.toString(); 929 size_t sUCSdim = toUCSindex(s, s.length); 930 start = 0; 931 end = sUCSdim; 932 if(arglist.length >= 1) 933 { 934 start = arglist[0].toInteger(); 935 if(arglist.length >= 2) 936 end = arglist[1].toInteger(); 937 //writef("s = '%ls', start = %d, end = %d\n", s, start, end); 938 } 939 940 void* p = dstring_substring(s, sUCSdim, start, end, ret); 941 return p; 942 } 943 944 /* ===================== Dstring_prototype_toLowerCase ============= */ 945 946 enum CASE 947 { 948 Lower, 949 Upper, 950 LocaleLower, 951 LocaleUpper 952 }; 953 954 void *tocase(Dobject othis, Value *ret, CASE caseflag) 955 { 956 import std..string : toLower, toUpper; 957 958 d_string s; 959 960 s = othis.value.toString(); 961 switch(caseflag) 962 { 963 case CASE.Lower: 964 s = toLower(s); 965 break; 966 case CASE.Upper: 967 s = toUpper(s); 968 break; 969 case CASE.LocaleLower: 970 s = toLower(s); 971 break; 972 case CASE.LocaleUpper: 973 s = toUpper(s); 974 break; 975 default: 976 assert(0); 977 } 978 979 ret.putVstring(s); 980 return null; 981 } 982 983 void* Dstring_prototype_toLowerCase(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 984 { 985 // ECMA 15.5.4.11 986 // String.prototype.toLowerCase() 987 988 //writef("Dstring_prototype_toLowerCase()\n"); 989 return tocase(othis, ret, CASE.Lower); 990 } 991 992 /* ===================== Dstring_prototype_toLocaleLowerCase ============= */ 993 994 void* Dstring_prototype_toLocaleLowerCase(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 995 { 996 // ECMA v3 15.5.4.17 997 998 //writef("Dstring_prototype_toLocaleLowerCase()\n"); 999 return tocase(othis, ret, CASE.LocaleLower); 1000 } 1001 1002 /* ===================== Dstring_prototype_toUpperCase ============= */ 1003 1004 void* Dstring_prototype_toUpperCase(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 1005 { 1006 // ECMA 15.5.4.12 1007 // String.prototype.toUpperCase() 1008 1009 return tocase(othis, ret, CASE.Upper); 1010 } 1011 1012 /* ===================== Dstring_prototype_toLocaleUpperCase ============= */ 1013 1014 void* Dstring_prototype_toLocaleUpperCase(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 1015 { 1016 // ECMA v3 15.5.4.18 1017 1018 return tocase(othis, ret, CASE.LocaleUpper); 1019 } 1020 1021 /* ===================== Dstring_prototype_anchor ============= */ 1022 1023 void *dstring_anchor(Dobject othis, Value* ret, d_string tag, d_string name, Value[] arglist) 1024 { 1025 // For example: 1026 // "foo".anchor("bar") 1027 // produces: 1028 // <tag name="bar">foo</tag> 1029 1030 d_string foo = othis.value.toString(); 1031 Value* va = arglist.length ? &arglist[0] : &vundefined; 1032 d_string bar = va.toString(); 1033 1034 d_string s; 1035 1036 s = "<" ~ 1037 tag ~ 1038 " " ~ 1039 name ~ 1040 "=\"" ~ 1041 bar ~ 1042 "\">" ~ 1043 foo ~ 1044 "</" ~ 1045 tag ~ 1046 ">"; 1047 1048 ret.putVstring(s); 1049 return null; 1050 } 1051 1052 1053 void* Dstring_prototype_anchor(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 1054 { 1055 // Non-standard extension 1056 // String.prototype.anchor(anchor) 1057 // For example: 1058 // "foo".anchor("bar") 1059 // produces: 1060 // <A NAME="bar">foo</A> 1061 1062 return dstring_anchor(othis, ret, "A", "NAME", arglist); 1063 } 1064 1065 void* Dstring_prototype_fontcolor(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 1066 { 1067 return dstring_anchor(othis, ret, "FONT", "COLOR", arglist); 1068 } 1069 1070 void* Dstring_prototype_fontsize(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 1071 { 1072 return dstring_anchor(othis, ret, "FONT", "SIZE", arglist); 1073 } 1074 1075 void* Dstring_prototype_link(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 1076 { 1077 return dstring_anchor(othis, ret, "A", "HREF", arglist); 1078 } 1079 1080 1081 /* ===================== Dstring_prototype bracketing ============= */ 1082 1083 /*************************** 1084 * Produce <tag>othis</tag> 1085 */ 1086 1087 void *dstring_bracket(Dobject othis, Value* ret, d_string tag) 1088 { 1089 d_string foo = othis.value.toString(); 1090 d_string s; 1091 1092 s = "<" ~ 1093 tag ~ 1094 ">" ~ 1095 foo ~ 1096 "</" ~ 1097 tag ~ 1098 ">"; 1099 1100 ret.putVstring(s); 1101 return null; 1102 } 1103 1104 void* Dstring_prototype_big(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 1105 { 1106 // Non-standard extension 1107 // String.prototype.big() 1108 // For example: 1109 // "foo".big() 1110 // produces: 1111 // <BIG>foo</BIG> 1112 1113 return dstring_bracket(othis, ret, "BIG"); 1114 } 1115 1116 void* Dstring_prototype_blink(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 1117 { 1118 return dstring_bracket(othis, ret, "BLINK"); 1119 } 1120 1121 void* Dstring_prototype_bold(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 1122 { 1123 return dstring_bracket(othis, ret, "B"); 1124 } 1125 1126 void* Dstring_prototype_fixed(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 1127 { 1128 return dstring_bracket(othis, ret, "TT"); 1129 } 1130 1131 void* Dstring_prototype_italics(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 1132 { 1133 return dstring_bracket(othis, ret, "I"); 1134 } 1135 1136 void* Dstring_prototype_small(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 1137 { 1138 return dstring_bracket(othis, ret, "SMALL"); 1139 } 1140 1141 void* Dstring_prototype_strike(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 1142 { 1143 return dstring_bracket(othis, ret, "STRIKE"); 1144 } 1145 1146 void* Dstring_prototype_sub(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 1147 { 1148 return dstring_bracket(othis, ret, "SUB"); 1149 } 1150 1151 void* Dstring_prototype_sup(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 1152 { 1153 return dstring_bracket(othis, ret, "SUP"); 1154 } 1155 1156 1157 1158 /* ===================== Dstring_prototype ==================== */ 1159 1160 class DstringPrototype : Dstring 1161 { 1162 this() 1163 { 1164 super(Dobject_prototype); 1165 1166 Put(TEXT_constructor, Dstring_constructor, DontEnum); 1167 1168 static enum NativeFunctionData[] nfd = 1169 [ 1170 { TEXT_toString, &Dstring_prototype_toString, 0 }, 1171 { TEXT_valueOf, &Dstring_prototype_valueOf, 0 }, 1172 { TEXT_charAt, &Dstring_prototype_charAt, 1 }, 1173 { TEXT_charCodeAt, &Dstring_prototype_charCodeAt, 1 }, 1174 { TEXT_concat, &Dstring_prototype_concat, 1 }, 1175 { TEXT_indexOf, &Dstring_prototype_indexOf, 1 }, 1176 { TEXT_lastIndexOf, &Dstring_prototype_lastIndexOf, 1 }, 1177 { TEXT_localeCompare, &Dstring_prototype_localeCompare, 1 }, 1178 { TEXT_match, &Dstring_prototype_match, 1 }, 1179 { TEXT_replace, &Dstring_prototype_replace, 2 }, 1180 { TEXT_search, &Dstring_prototype_search, 1 }, 1181 { TEXT_slice, &Dstring_prototype_slice, 2 }, 1182 { TEXT_split, &Dstring_prototype_split, 2 }, 1183 { TEXT_substr, &Dstring_prototype_substr, 2 }, 1184 { TEXT_substring, &Dstring_prototype_substring, 2 }, 1185 { TEXT_toLowerCase, &Dstring_prototype_toLowerCase, 0 }, 1186 { TEXT_toLocaleLowerCase, &Dstring_prototype_toLocaleLowerCase, 0 }, 1187 { TEXT_toUpperCase, &Dstring_prototype_toUpperCase, 0 }, 1188 { TEXT_toLocaleUpperCase, &Dstring_prototype_toLocaleUpperCase, 0 }, 1189 { TEXT_anchor, &Dstring_prototype_anchor, 1 }, 1190 { TEXT_fontcolor, &Dstring_prototype_fontcolor, 1 }, 1191 { TEXT_fontsize, &Dstring_prototype_fontsize, 1 }, 1192 { TEXT_link, &Dstring_prototype_link, 1 }, 1193 { TEXT_big, &Dstring_prototype_big, 0 }, 1194 { TEXT_blink, &Dstring_prototype_blink, 0 }, 1195 { TEXT_bold, &Dstring_prototype_bold, 0 }, 1196 { TEXT_fixed, &Dstring_prototype_fixed, 0 }, 1197 { TEXT_italics, &Dstring_prototype_italics, 0 }, 1198 { TEXT_small, &Dstring_prototype_small, 0 }, 1199 { TEXT_strike, &Dstring_prototype_strike, 0 }, 1200 { TEXT_sub, &Dstring_prototype_sub, 0 }, 1201 { TEXT_sup, &Dstring_prototype_sup, 0 }, 1202 ]; 1203 1204 DnativeFunction.initialize(this, nfd, DontEnum); 1205 } 1206 } 1207 1208 /* ===================== Dstring ==================== */ 1209 1210 class Dstring : Dobject 1211 { 1212 this(d_string s) 1213 { 1214 super(getPrototype()); 1215 classname = TEXT_String; 1216 1217 Put(TEXT_length, std.utf.toUCSindex(s, s.length), DontEnum | DontDelete | ReadOnly); 1218 value.putVstring(s); 1219 } 1220 1221 this(Dobject prototype) 1222 { 1223 super(prototype); 1224 1225 classname = TEXT_String; 1226 Put(TEXT_length, 0, DontEnum | DontDelete | ReadOnly); 1227 value.putVstring(null); 1228 } 1229 1230 static void initialize() 1231 { 1232 Dstring_constructor = new DstringConstructor(); 1233 Dstring_prototype = new DstringPrototype(); 1234 1235 Dstring_constructor.Put(TEXT_prototype, Dstring_prototype, DontEnum | DontDelete | ReadOnly); 1236 } 1237 1238 static Dfunction getConstructor() 1239 { 1240 return Dstring_constructor; 1241 } 1242 1243 static Dobject getPrototype() 1244 { 1245 return Dstring_prototype; 1246 } 1247 }