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