view dmdscript_tango/dstring.d @ 3:8363a4bf6a8f

rename package: dmdscript to dmdscript_tango
author saaadel
date Sun, 24 Jan 2010 18:33:05 +0200
parents 55c2951c07be
children
line wrap: on
line source


/* Digital Mars DMDScript source code.
 * Copyright (c) 2000-2002 by Chromium Communications
 * D version Copyright (c) 2004-2006 by Digital Mars
 * All Rights Reserved
 * written by Walter Bright
 * www.digitalmars.com
 * Use at your own risk. There is no warranty, express or implied.
 * License for redistribution is by the GNU General Public License in gpl.txt.
 *
 * A binary, non-exclusive license for commercial use can be
 * purchased from www.digitalmars.com/dscript/buy.html.
 *
 * DMDScript is implemented in the D Programming Language,
 * www.digitalmars.com/d/
 *
 * For a C++ implementation of DMDScript, including COM support,
 * see www.digitalmars.com/dscript/cppscript.html.
 */


module dmdscript_tango.dstring;

import std.regexp;
import std.utf;
import std.c.stdlib;
import std.c.string;

import dmdscript_tango.script;
import dmdscript_tango.dobject;
import dmdscript_tango.dregexp;
import dmdscript_tango.darray;
import dmdscript_tango.value;
import dmdscript_tango.threadcontext;
import dmdscript_tango.dfunction;
import dmdscript_tango.text;
import dmdscript_tango.property;
import dmdscript_tango.textgen.errmsgs;
import dmdscript_tango.dnative;

//alias script.tchar tchar;

/* ===================== Dstring_fromCharCode ==================== */

void* Dstring_fromCharCode(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    // ECMA 15.5.3.2
    d_string s;

    for (size_t i = 0; i < arglist.length; i++)
    {	Value* v;
	uint u;

	v = &arglist[i];
	u = v.toUint16();
	//writefln("string.fromCharCode(%x)", u);
	if (!std.utf.isValidDchar(u))
	{
	    ErrInfo errinfo;

	    ret.putVundefined();
	    return pthis.RuntimeError(&errinfo,
		    errmsgtbl[ERR_NOT_VALID_UTF],
		    "String", "fromCharCode()",
		    u);
	}
	std.utf.encode(s, u);
	//writefln("s[0] = %x, s = '%s'", s[0], s);
    }
    ret.putVstring(s);
    return null;
}

/* ===================== Dstring_constructor ==================== */

class Dstring_constructor : Dfunction
{
    this(ThreadContext *tc)
    {
	super(1, tc.Dfunction_prototype);
	name = "String";

	static NativeFunctionData nfd[] =
	[
	    {	&TEXT_fromCharCode, &Dstring_fromCharCode, 1 },
	];

	DnativeFunction.init(this, nfd, 0);
    }

    void *Construct(CallContext *cc, Value *ret, Value[] arglist)
    {
	// ECMA 15.5.2
	d_string s;
	Dobject o;

	s = (arglist.length) ? arglist[0].toString() : TEXT_;
	o = new Dstring(s);
	ret.putVobject(o);
	return null;
    }

    void *Call(CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
    {
	// ECMA 15.5.1
	d_string s;

	s = (arglist.length) ? arglist[0].toString() : TEXT_;
	ret.putVstring(s);
	return null;
    }
}


/* ===================== Dstring_prototype_toString =============== */

void* Dstring_prototype_toString(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    //writef("Dstring.prototype.toString()\n");
    // othis must be a String
    if (!othis.isClass(TEXT_String))
    {
	ErrInfo errinfo;

	ret.putVundefined();
	return pthis.RuntimeError(&errinfo,
		errmsgtbl[ERR_FUNCTION_WANTS_STRING],
		TEXT_toString,
		othis.classname);
    }
    else
    {	Value *v;

	v = &(cast(Dstring)othis).value;
	Value.copy(ret, v);
    }
    return null;
}

/* ===================== Dstring_prototype_valueOf =============== */

void* Dstring_prototype_valueOf(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    // Does same thing as String.prototype.toString()

    //writef("string.prototype.valueOf()\n");
    // othis must be a String
    if (!othis.isClass(TEXT_String))
    {
	ErrInfo errinfo;

	ret.putVundefined();
	return pthis.RuntimeError(&errinfo,
		errmsgtbl[ERR_FUNCTION_WANTS_STRING],
		TEXT_valueOf,
		othis.classname);
    }
    else
    {	Value *v;

	v = &(cast(Dstring)othis).value;
	Value.copy(ret, v);
    }
    return null;
}

/* ===================== Dstring_prototype_charAt =============== */

void* Dstring_prototype_charAt(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    // ECMA 15.5.4.4

    Value *v;
    int pos;		// ECMA says pos should be a d_number,
			// but int should behave the same
    d_string s;
    d_string result;

    v = &othis.value;
    s = v.toString();
    v = arglist.length ? &arglist[0] : &vundefined;
    pos = cast(int) v.toInteger();

    result = TEXT_;

    if (pos >= 0)
    {	size_t idx;

	while (1)
	{
	    if (idx == s.length)
		break;
	    if (pos == 0)
	    {
		result = s[idx .. idx + std.utf.stride(s, idx)];
		break;
	    }
	    idx += std.utf.stride(s, idx);
	    pos--;
	}
    }

    ret.putVstring(result);
    return null;
}

/* ===================== Dstring_prototype_charCodeAt ============= */

void* Dstring_prototype_charCodeAt(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    // ECMA 15.5.4.5

    Value *v;
    int pos;		// ECMA says pos should be a d_number,
			// but int should behave the same
    d_string s;
    uint len;
    d_number result;

    v = &othis.value;
    s = v.toString();
    v = arglist.length ? &arglist[0] : &vundefined;
    pos = cast(int) v.toInteger();

    result = d_number.nan;

    if (pos >= 0)
    {	size_t idx;

	while (1)
	{
	    assert(idx <= s.length);
	    if (idx == s.length)
		break;
	    if (pos == 0)
	    {
		result = std.utf.decode(s, idx);
		break;
	    }
	    idx += std.utf.stride(s, idx);
	    pos--;
	}
    }

    ret.putVnumber(result);
    return null;
}

/* ===================== Dstring_prototype_concat ============= */

void* Dstring_prototype_concat(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    // ECMA v3 15.5.4.6
    d_string s;

    //writefln("Dstring.prototype.concat()");

    s = othis.value.toString();
    for (size_t a = 0; a < arglist.length; a++)
	s ~= arglist[a].toString();

    ret.putVstring(s);
    return null;
}

/* ===================== Dstring_prototype_indexOf ============= */

void* Dstring_prototype_indexOf(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    // ECMA 15.5.4.6
    // String.prototype.indexOf(searchString, position)

    Value* v1;
    Value* v2;
    int pos;		// ECMA says pos should be a d_number,
			// but I can't find a reason.
    d_string s;
    size_t sUCSdim;

    d_string searchString;
    int k;

    Value xx;
    xx.putVobject(othis);
    s = xx.toString();
    sUCSdim = std.utf.toUCSindex(s, s.length);

    v1 = arglist.length ? &arglist[0] : &vundefined;
    v2 = (arglist.length >= 2) ? &arglist[1] : &vundefined;

    searchString = v1.toString();
    pos = cast(int) v2.toInteger();

    if (pos < 0)
	pos = 0;
    else if (pos > sUCSdim)
	pos = sUCSdim;

    if (searchString.length == 0)
	k = pos;
    else
    {	pos = std.utf.toUTFindex(s, pos);
	k = std.string.find(s[pos .. length], searchString);
	if (k != -1)
	    k = std.utf.toUCSindex(s, pos + k);
    }

    ret.putVnumber(k);
    return null;
}

/* ===================== Dstring_prototype_lastIndexOf ============= */

void* Dstring_prototype_lastIndexOf(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    // ECMA v3 15.5.4.8
    // String.prototype.lastIndexOf(searchString, position)

    Value *v1;
    int pos;		// ECMA says pos should be a d_number,
			// but I can't find a reason.
    d_string s;
    size_t sUCSdim;
    d_string searchString;
    int k;

version (all)
{
    {
    // This is the 'transferable' version
    Value *v;
    void *a;
    v = othis.Get(TEXT_toString);
    a = v.Call(cc, othis, ret, null);
    if (a)				// if exception was thrown
	return a;
    s = ret.toString();
    }
}
else
{
    // the 'builtin' version
    s = othis.value.toString();
}
    sUCSdim = std.utf.toUCSindex(s, s.length);

    v1 = arglist.length ? &arglist[0] : &vundefined;
    searchString = v1.toString();
    if (arglist.length >= 2)
    {	d_number n;
	Value *v = &arglist[1];

	n = v.toNumber();
	if (std.math.isnan(n) || n > sUCSdim)
	    pos = sUCSdim;
	else if (n < 0)
	    pos = 0;
	else
	    pos = cast(int) n;
    }
    else
	pos = sUCSdim;

    //writef("len = %d, p = '%ls'\n", len, p);
    //writef("pos = %d, sslen = %d, ssptr = '%ls'\n", pos, sslen, ssptr);
    //writefln("s = '%s', pos = %s, searchString = '%s'", s, pos, searchString);

    if (searchString.length == 0)
	k = pos;
    else
    {
	pos = std.utf.toUTFindex(s, pos);
	pos += searchString.length;
	if (pos > s.length)
	    pos = s.length;
	k = std.string.rfind(s[0 .. pos], searchString);
	//writefln("s = '%s', pos = %s, searchString = '%s', k = %d", s, pos, searchString, k);
	if (k != -1)
	    k = std.utf.toUCSindex(s, k);
    }
    ret.putVnumber(k);
    return null;
}

/* ===================== Dstring_prototype_localeCompare ============= */

void* Dstring_prototype_localeCompare(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    // ECMA v3 15.5.4.9
    d_string s1;
    d_string s2;
    d_number n;
    Value *v;

    v = &othis.value;
    s1 = v.toString();
    s2 = arglist.length ? arglist[0].toString() : vundefined.toString();
    n = localeCompare(cc, s1, s2);
    ret.putVnumber(n);
    return null;
}

/* ===================== Dstring_prototype_match ============= */

void* Dstring_prototype_match(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    // ECMA v3 15.5.4.10
    Dregexp r;
    Dobject o;

    if (arglist.length && !arglist[0].isPrimitive() &&
	(o = arglist[0].toObject()).isDregexp())
    {
	;
    }
    else
    {
	Value regret;

	regret.putVobject(null);
	Dregexp.getConstructor().Construct(cc, &regret, arglist);
	o = regret.object;
    }

    r = cast(Dregexp)o;
    if (r.global.dbool)
    {
	Darray a = new Darray;
	d_int32 n;
	d_int32 i;
	d_int32 lasti;

	i = 0;
	lasti = 0;
	for (n = 0; ; n++)
	{
	    r.lastIndex.putVnumber(i);
	    Dregexp.exec(r, ret, (&othis.value)[0 .. 1], EXEC_STRING);
	    if (!ret.string)		// if match failed
	    {
		r.lastIndex.putVnumber(i);
		break;
	    }
	    lasti = i;
	    i = cast(d_int32) r.lastIndex.toInt32();
	    if (i == lasti)		// if no source was consumed
		i++;			// consume a character

	    a.Put(n, ret, 0);		// a[n] = ret;
	}
	ret.putVobject(a);
    }
    else
    {
	Dregexp.exec(r, ret, (&othis.value)[0 .. 1], EXEC_ARRAY);
    }
    return null;
}

/* ===================== Dstring_prototype_replace ============= */

void* Dstring_prototype_replace(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    // ECMA v3 15.5.4.11
    // String.prototype.replace(searchValue, replaceValue)

    d_string string;
    d_string searchString;
    d_string newstring;
    Value *searchValue;
    Value *replaceValue;
    Dregexp r;
    RegExp re;
    tchar[] replacement;
    d_string result;
    int m;
    int i;
    int lasti;
    std.regexp.regmatch_t[1] pmatch;
    Dfunction f;
    Value* v;

    v = &othis.value;
    string = v.toString();
    searchValue  = (arglist.length >= 1) ? &arglist[0] : &vundefined;
    replaceValue = (arglist.length >= 2) ? &arglist[1] : &vundefined;
    r = Dregexp.isRegExp(searchValue);
    f = Dfunction.isFunction(replaceValue);
    if (r)
    {	int offset = 0;

	re = r.re;
	i = 0;
	result = string;

	r.lastIndex.putVnumber(0);
	for (;;)
	{
	    Dregexp.exec(r, ret, (&othis.value)[0 .. 1], EXEC_STRING);
	    if (!ret.string)		// if match failed
		break;

	    m = re.re_nsub;
	    if (f)
	    {
		Value* alist;

		alist = cast(Value *)alloca((m + 3) * Value.sizeof);
		assert(alist);
		alist[0].putVstring(ret.string);
		for (i = 0; i < m; i++)
		{
		    alist[1 + i].putVstring(
			string[re.pmatch[1 + i].rm_so .. re.pmatch[1 + i].rm_eo]);
		}
		alist[m + 1].putVnumber(re.pmatch[0].rm_so);
		alist[m + 2].putVstring(string);
		f.Call(cc, f, ret, alist[0 .. m + 3]);
		replacement = ret.toString();
	    }
	    else
	    {
		newstring = replaceValue.toString();
		replacement = re.replace(newstring);
	    }
	    int starti = re.pmatch[0].rm_so + offset;
	    int endi   = re.pmatch[0].rm_eo + offset;
	    result = string[0 .. starti] ~
		     replacement ~
		     string[endi .. length];

	    if (re.attributes & RegExp.REA.global)
	    {
		offset += replacement.length - (endi - starti);

		// If no source was consumed, consume a character
		lasti = i;
		i = cast(d_int32) r.lastIndex.toInt32();
		if (i == lasti)
		{   i++;
		    r.lastIndex.putVnumber(i);
		}
	    }
	    else
		break;
	}
    }
    else
    {	int match;

	searchString = searchValue.toString();
	match = std.string.find(string, searchString);
	if (match >= 0)
	{
	    pmatch[0].rm_so = match;
	    pmatch[0].rm_eo = match + searchString.length;
	    if (f)
	    {
		Value[3] alist;

		alist[0].putVstring(searchString);
		alist[1].putVnumber(pmatch[0].rm_so);
		alist[2].putVstring(string);
		f.Call(cc, f, ret, alist);
		replacement = ret.toString();
	    }
	    else
	    {
		newstring = replaceValue.toString();
		replacement = RegExp.replace3(newstring, string, pmatch);
	    }
	    result = string[0 .. match] ~
		     replacement ~
		     string[match + searchString.length .. length];
	}
	else
	{
	    result = string;
	}
    }

    ret.putVstring(result);
    return null;
}

/* ===================== Dstring_prototype_search ============= */

void* Dstring_prototype_search(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    // ECMA v3 15.5.4.12
    Dregexp r;
    Dobject o;

    //writef("String.prototype.search()\n");
    if (arglist.length && !arglist[0].isPrimitive() &&
	(o = arglist[0].toObject()).isDregexp())
    {
	;
    }
    else
    {	Value regret;

	regret.putVobject(null);
	Dregexp.getConstructor().Construct(cc, &regret, arglist);
	o = regret.object;
    }

    r = cast(Dregexp)o;
    Dregexp.exec(r, ret, (&othis.value)[0 .. 1], EXEC_INDEX);
    return null;
}

/* ===================== Dstring_prototype_slice ============= */

void* Dstring_prototype_slice(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    // ECMA v3 15.5.4.13
    d_int32 start;
    d_int32 end;
    d_int32 sUCSdim;
    d_string s;
    d_string r;
    Value *v;

    v = &othis.value;
    s = v.toString();
    sUCSdim = std.utf.toUCSindex(s, s.length);
    switch (arglist.length)
    {
	case 0:
	    start = 0;
	    end = sUCSdim;
	    break;

	case 1:
	    start = arglist[0].toInt32();
	    end = sUCSdim;
	    break;

	default:
	    start = arglist[0].toInt32();
	    end   = arglist[1].toInt32();
	    break;
    }

    if (start < 0)
    {
	start += sUCSdim;
	if (start < 0)
	    start = 0;
    }
    else if (start >= sUCSdim)
	start = sUCSdim;

    if (end < 0)
    {
	end += sUCSdim;
	if (end < 0)
	    end = 0;
    }
    else if (end >= sUCSdim)
	end = sUCSdim;

    if (start > end)
	end = start;

    start = toUTFindex(s, start);
    end   = toUTFindex(s, end);
    r = s[start .. end];

    ret.putVstring(r);
    return null;
}


/* ===================== Dstring_prototype_split ============= */

void* Dstring_prototype_split(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    // ECMA v3 15.5.4.14
    // String.prototype.split(separator, limit)
    d_uint32 lim;
    d_uint32 p;
    d_uint32 q;
    d_uint32 e;
    Value* separator = &vundefined;
    Value* limit = &vundefined;
    Dregexp R;
    RegExp re;
    d_string rs;
    d_string T;
    d_string S;
    Darray A;
    int str;

    //writefln("Dstring_prototype_split()");
    switch (arglist.length)
    {
	default:
	    limit = &arglist[1];
	case 1:
	    separator = &arglist[0];
	case 0:
	    break;
    }

    Value *v;
    v = &othis.value;
    S = v.toString();
    A = new Darray;
    if (limit.isUndefined())
	lim = ~0u;
    else
	lim = limit.toUint32();
    p = 0;
    R = Dregexp.isRegExp(separator);
    if (R)	// regular expression
    {	re = R.re;
	assert(re);
	rs = null;
	str = 0;
    }
    else	// string
    {	re = null;
	rs = separator.toString();
	str = 1;
    }
    if (lim == 0)
	goto Lret;

    // ECMA v3 15.5.4.14 is specific: "If separator is undefined, then the
    // result array contains just one string, which is the this value
    // (converted to a string)." However, neither Javascript nor Jscript
    // do that, they regard an undefined as being the string "undefined".
    // We match Javascript/Jscript behavior here, not ECMA.

    // Uncomment for ECMA compatibility
    //if (!separator.isUndefined())
    {
	//writefln("test1 S = '%s', rs = '%s'", S, rs);
	if (S.length)
	{
	L10:
	    for (q = p; q != S.length; q++)
	    {
		if (str)		// string
		{
		    if (q + rs.length <= S.length && !memcmp(S.ptr + q, rs.ptr, rs.length * tchar.sizeof))
		    {
			e = q + rs.length;
			if (e != p)
			{
			    T = S[p .. q];
			    A.Put(cast(uint) A.length.number, T, 0);
			    if (A.length.number == lim)
				goto Lret;
			    p = e;
			    goto L10;
			}
		    }
		}
		else		// regular expression
		{
		    if (re.test(S, q))
		    {	q = re.pmatch[0].rm_so;
			e = re.pmatch[0].rm_eo;
			if (e != p)
			{
			    T = S[p .. q];
			    //writefln("S = '%s', T = '%s', p = %d, q = %d, e = %d\n", S, T, p, q, e);
			    A.Put(cast(uint) A.length.number, T, 0);
			    if (A.length.number == lim)
				goto Lret;
			    p = e;
			    for (uint i = 0; i < re.re_nsub; i++)
			    {
				int so = re.pmatch[1 + i].rm_so;
				int eo = re.pmatch[1 + i].rm_eo;

				//writefln("i = %d, nsub = %s, so = %s, eo = %s, S.length = %s", i, re.re_nsub, so, eo, S.length);
				if (so != -1 && eo != -1)
				    T = S[so .. eo];
				else
				    T = null;
				A.Put(cast(uint) A.length.number, T, 0);
				if (A.length.number == lim)
				    goto Lret;
			    }
			    goto L10;
			}
		    }
		}
	    }
	    T = S[p .. S.length];
	    A.Put(cast(uint) A.length.number, T, 0);
	    goto Lret;
	}
	if (str)		// string
	{
	    if (rs.length <= S.length && S[0 .. rs.length] == rs[])
		goto Lret;
	}
	else		// regular expression
	{
	    if (re.test(S, 0))
		goto Lret;
	}
    }

    A.Put(0u, S, 0);
Lret:
    ret.putVobject(A);
    return null;
}


/* ===================== Dstring_prototype_substr ============= */

void *dstring_substring(d_string s, size_t sUCSdim, d_number start, d_number end, Value *ret)
{
    d_string sb;
    d_int32 sb_len;

    if (std.math.isnan(start))
	start = 0;
    else if (start > sUCSdim)
	start = sUCSdim;
    else if (start < 0)
	start = 0;

    if (std.math.isnan(end))
	end = 0;
    else if (end > sUCSdim)
	end = sUCSdim;
    else if (end < 0)
	end = 0;

    if (end < start)		// swap
    {	d_number t;

	t = start;
	start = end;
	end = t;
    }

    size_t st = std.utf.toUTFindex(s, cast(size_t)start);
    size_t en = std.utf.toUTFindex(s, cast(size_t)end);
    sb = s[st .. en];

    ret.putVstring(sb);
    return null;
}

void* Dstring_prototype_substr(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    // Javascript: TDG pg. 689
    // String.prototype.substr(start, length)
    d_number start;
    d_number length;
    d_string s;

    s = othis.value.toString();
    size_t sUCSdim = std.utf.toUCSindex(s, s.length);
    start = 0;
    length = 0;
    if (arglist.length >= 1)
    {
	start = arglist[0].toInteger();
	if (start < 0)
	    start = sUCSdim + start;
	if (arglist.length >= 2)
	{
	    length = arglist[1].toInteger();
	    if (std.math.isnan(length) || length < 0)
		length = 0;
	}
	else
	    length = sUCSdim - start;
    }

    return dstring_substring(s, sUCSdim, start, start + length, ret);
}

/* ===================== Dstring_prototype_substring ============= */

void* Dstring_prototype_substring(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    // ECMA 15.5.4.9
    // String.prototype.substring(start)
    // String.prototype.substring(start, end)
    d_number start;
    d_number end;
    d_string s;

    //writefln("String.prototype.substring()");
    s = othis.value.toString();
    size_t sUCSdim = std.utf.toUCSindex(s, s.length);
    start = 0;
    end = sUCSdim;
    if (arglist.length >= 1)
    {
	start = arglist[0].toInteger();
	if (arglist.length >= 2)
	    end = arglist[1].toInteger();
	//writef("s = '%ls', start = %d, end = %d\n", s, start, end);
    }

    void* p = dstring_substring(s, sUCSdim, start, end, ret);
    return p;
}

/* ===================== Dstring_prototype_toLowerCase ============= */

enum CASE
{
    Lower,
    Upper,
    LocaleLower,
    LocaleUpper
};

void *tocase(Dobject othis, Value *ret, CASE caseflag)
{
    d_string s;

    s = othis.value.toString();
    switch (caseflag)
    {
	case CASE.Lower:
	    s = std.string.tolower(s);
	    break;
	case CASE.Upper:
	    s = std.string.toupper(s);
	    break;
	case CASE.LocaleLower:
	    s = std.string.tolower(s);
	    break;
	case CASE.LocaleUpper:
	    s = std.string.toupper(s);
	    break;
	default:
	    assert(0);
    }

    ret.putVstring(s);
    return null;
}

void* Dstring_prototype_toLowerCase(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    // ECMA 15.5.4.11
    // String.prototype.toLowerCase()

    //writef("Dstring_prototype_toLowerCase()\n");
    return tocase(othis, ret, CASE.Lower);
}

/* ===================== Dstring_prototype_toLocaleLowerCase ============= */

void* Dstring_prototype_toLocaleLowerCase(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    // ECMA v3 15.5.4.17

    //writef("Dstring_prototype_toLocaleLowerCase()\n");
    return tocase(othis, ret, CASE.LocaleLower);
}

/* ===================== Dstring_prototype_toUpperCase ============= */

void* Dstring_prototype_toUpperCase(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    // ECMA 15.5.4.12
    // String.prototype.toUpperCase()

    return tocase(othis, ret, CASE.Upper);
}

/* ===================== Dstring_prototype_toLocaleUpperCase ============= */

void* Dstring_prototype_toLocaleUpperCase(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    // ECMA v3 15.5.4.18

    return tocase(othis, ret, CASE.LocaleUpper);
}

/* ===================== Dstring_prototype_anchor ============= */

void *dstring_anchor(Dobject othis, Value* ret, tchar[] tag, tchar[] name, Value[] arglist)
{
    // For example:
    //	"foo".anchor("bar")
    // produces:
    //	<tag name="bar">foo</tag>

    d_string foo = othis.value.toString();
    Value* va = arglist.length ? &arglist[0] : &vundefined;
    d_string bar = va.toString();

    d_string s;

    s = "<"	~
	tag	~
	" "	~
	name	~
	"=\""	~
	bar	~
	"\">"	~
	foo	~
	"</"	~
	tag	~
	">";

    ret.putVstring(s);
    return null;
}


void* Dstring_prototype_anchor(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    // Non-standard extension
    // String.prototype.anchor(anchor)
    // For example:
    //	"foo".anchor("bar")
    // produces:
    //	<A NAME="bar">foo</A>

    return dstring_anchor(othis, ret, "A", "NAME", arglist);
}

void* Dstring_prototype_fontcolor(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    return dstring_anchor(othis, ret, "FONT", "COLOR", arglist);
}

void* Dstring_prototype_fontsize(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    return dstring_anchor(othis, ret, "FONT", "SIZE", arglist);
}

void* Dstring_prototype_link(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    return dstring_anchor(othis, ret, "A", "HREF", arglist);
}


/* ===================== Dstring_prototype bracketing ============= */

/***************************
 * Produce <tag>othis</tag>
 */

void *dstring_bracket(Dobject othis, Value* ret, char[] tag)
{
    d_string foo = othis.value.toString();
    d_string s;

    s = "<"	~
	tag	~
	">"	~
	foo	~
	"</"	~
	tag	~
	">";

    ret.putVstring(s);
    return null;
}

void* Dstring_prototype_big(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    // Non-standard extension
    // String.prototype.big()
    // For example:
    //	"foo".big()
    // produces:
    //	<BIG>foo</BIG>

    return dstring_bracket(othis, ret, "BIG");
}

void* Dstring_prototype_blink(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    return dstring_bracket(othis, ret, "BLINK");
}

void* Dstring_prototype_bold(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    return dstring_bracket(othis, ret, "B");
}

void* Dstring_prototype_fixed(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    return dstring_bracket(othis, ret, "TT");
}

void* Dstring_prototype_italics(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    return dstring_bracket(othis, ret, "I");
}

void* Dstring_prototype_small(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    return dstring_bracket(othis, ret, "SMALL");
}

void* Dstring_prototype_strike(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    return dstring_bracket(othis, ret, "STRIKE");
}

void* Dstring_prototype_sub(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    return dstring_bracket(othis, ret, "SUB");
}

void* Dstring_prototype_sup(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    return dstring_bracket(othis, ret, "SUP");
}



/* ===================== Dstring_prototype ==================== */

class Dstring_prototype : Dstring
{
    this(ThreadContext *tc)
    {
	super(tc.Dobject_prototype);

	Put(TEXT_constructor, tc.Dstring_constructor, DontEnum);

	static NativeFunctionData nfd[] =
	[
	    {   &TEXT_toString, &Dstring_prototype_toString, 0 },
	    {   &TEXT_valueOf, &Dstring_prototype_valueOf, 0 },
	    {   &TEXT_charAt, &Dstring_prototype_charAt, 1 },
	    {   &TEXT_charCodeAt, &Dstring_prototype_charCodeAt, 1 },
	    {   &TEXT_concat, &Dstring_prototype_concat, 1 },
	    {   &TEXT_indexOf, &Dstring_prototype_indexOf, 1 },
	    {   &TEXT_lastIndexOf, &Dstring_prototype_lastIndexOf, 1 },
	    {   &TEXT_localeCompare, &Dstring_prototype_localeCompare, 1 },
	    {   &TEXT_match, &Dstring_prototype_match, 1 },
	    {   &TEXT_replace, &Dstring_prototype_replace, 2 },
	    {   &TEXT_search, &Dstring_prototype_search, 1 },
	    {   &TEXT_slice, &Dstring_prototype_slice, 2 },
	    {   &TEXT_split, &Dstring_prototype_split, 2 },
	    {   &TEXT_substr, &Dstring_prototype_substr, 2 },
	    {   &TEXT_substring, &Dstring_prototype_substring, 2 },
	    {   &TEXT_toLowerCase, &Dstring_prototype_toLowerCase, 0 },
	    {   &TEXT_toLocaleLowerCase, &Dstring_prototype_toLocaleLowerCase, 0 },
	    {   &TEXT_toUpperCase, &Dstring_prototype_toUpperCase, 0 },
	    {   &TEXT_toLocaleUpperCase, &Dstring_prototype_toLocaleUpperCase, 0 },
	    {   &TEXT_anchor, &Dstring_prototype_anchor, 1 },
	    {   &TEXT_fontcolor, &Dstring_prototype_fontcolor, 1 },
	    {   &TEXT_fontsize, &Dstring_prototype_fontsize, 1 },
	    {   &TEXT_link, &Dstring_prototype_link, 1 },
	    {   &TEXT_big, &Dstring_prototype_big, 0 },
	    {   &TEXT_blink, &Dstring_prototype_blink, 0 },
	    {   &TEXT_bold, &Dstring_prototype_bold, 0 },
	    {   &TEXT_fixed, &Dstring_prototype_fixed, 0 },
	    {   &TEXT_italics, &Dstring_prototype_italics, 0 },
	    {   &TEXT_small, &Dstring_prototype_small, 0 },
	    {   &TEXT_strike, &Dstring_prototype_strike, 0 },
	    {   &TEXT_sub, &Dstring_prototype_sub, 0 },
	    {   &TEXT_sup, &Dstring_prototype_sup, 0 },
	];

	DnativeFunction.init(this, nfd, DontEnum);
    }
}

/* ===================== Dstring ==================== */

class Dstring : Dobject
{
    this(d_string s)
    {
	super(getPrototype());
	classname = TEXT_String;

	Put(TEXT_length, std.utf.toUCSindex(s, s.length), DontEnum | DontDelete | ReadOnly);
	value.putVstring(s);
    }

    this(Dobject prototype)
    {
	super(prototype);

	classname = TEXT_String;
	Put(TEXT_length, 0, DontEnum | DontDelete | ReadOnly);
	value.putVstring(null);
    }

    static void init(ThreadContext *tc)
    {
	tc.Dstring_constructor = new Dstring_constructor(tc);
	tc.Dstring_prototype = new Dstring_prototype(tc);

	tc.Dstring_constructor.Put(TEXT_prototype, tc.Dstring_prototype, DontEnum | DontDelete | ReadOnly);
    }

    static Dfunction getConstructor()
    {
	ThreadContext *tc = ThreadContext.getThreadContext();
	assert(tc);
	return tc.Dstring_constructor;
    }

    static Dobject getPrototype()
    {
	ThreadContext *tc = ThreadContext.getThreadContext();
	assert(tc);
	return tc.Dstring_prototype;
    }
}