string, wstring are now bidirectional (not random) ranges

std.algorithm: defined move with one argument; levenshtein distance generalized to with all forward ranges; take now has swapped arguments
std.array: empty for arrays is now a @property; front and back for a string and wstring automatically decodes the first/last character; popFront, popBack for string and wstring obey the UTF stride
std.conv: changed the default array formatting from "[a, b, c]" to "a b c"
std.range: swapped order of arguments in take
std.stdio: added readln template
std.variant: now works with statically-sized arrays and const data
std.traits: added isNarrowString
This commit is contained in:
Andrei Alexandrescu 2010-02-22 15:52:31 +00:00
parent 35e5e25943
commit 2a9a6e336c
16 changed files with 790 additions and 400 deletions

View file

@ -138,6 +138,7 @@ struct Map(alias fun, Range) if (isInputRange!(Range))
unittest unittest
{ {
scope(failure) writeln("Unittest failed at line ", __LINE__);
int[] arr1 = [ 1, 2, 3, 4 ]; int[] arr1 = [ 1, 2, 3, 4 ];
int[] arr2 = [ 5, 6 ]; int[] arr2 = [ 5, 6 ];
auto squares = map!("a * a")(arr1); auto squares = map!("a * a")(arr1);
@ -319,7 +320,7 @@ unittest
a = [ 1, 2, 3, 4, 5 ]; a = [ 1, 2, 3, 4, 5 ];
// Stringize with commas // Stringize with commas
string rep = reduce!("a ~ `, ` ~ to!(string)(b)")("", a); string rep = reduce!("a ~ `, ` ~ to!(string)(b)")("", a);
assert(rep[2 .. $] == "1, 2, 3, 4, 5"); assert(rep[2 .. $] == "1, 2, 3, 4, 5", "["~rep[2 .. $]~"]");
} }
unittest unittest
@ -529,6 +530,14 @@ unittest
assert(s21.a == 1 && s21.b == null && s22.a == 10 && s22.b != null); assert(s21.a == 1 && s21.b == null && s22.a == 10 && s22.b != null);
} }
/// Ditto
T move(T)(ref T src)
{
T result;
move(src, result);
return result;
}
// moveAll // moveAll
/** /**
For each element $(D a) in $(D src) and each element $(D b) in $(D For each element $(D a) in $(D src) and each element $(D b) in $(D
@ -667,6 +676,7 @@ assert(i == 3);
---- ----
*/ */
struct Splitter(Range, Separator) struct Splitter(Range, Separator)
if (!is(typeof(ElementType!Range.init == Separator.init)))
{ {
private: private:
Range _input; Range _input;
@ -675,17 +685,8 @@ private:
size_t _frontLength = size_t.max; size_t _frontLength = size_t.max;
static if (isBidirectionalRange!Range) static if (isBidirectionalRange!Range)
size_t _backLength = size_t.max; size_t _backLength = size_t.max;
enum bool separatorIsRange =
!is(typeof(ElementType!Range.init == _separator));
static if (separatorIsRange) size_t separatorLength() { return _separator.length; }
{
size_t separatorLength() { return _separator.length; }
}
else
{
enum size_t separatorLength = 1;
}
void ensureFrontLength() void ensureFrontLength()
{ {
@ -693,24 +694,21 @@ private:
assert(!_input.empty); assert(!_input.empty);
// compute front length // compute front length
_frontLength = _input.length - find(_input, _separator).length; _frontLength = _input.length - find(_input, _separator).length;
if (_frontLength == _input.length) _backLength = _frontLength; static if (isBidirectionalRange!Range)
if (_frontLength == _input.length) _backLength = _frontLength;
} }
void ensureBackLength() void ensureBackLength()
{ {
if (_backLength != _backLength.max) return; static if (isBidirectionalRange!Range)
if (_backLength != _backLength.max) return;
assert(!_input.empty); assert(!_input.empty);
// compute back length // compute back length
static if (separatorIsRange) static if (isBidirectionalRange!Range)
{ {
_backLength = _input.length - _backLength = _input.length -
find(retro(_input), retro(_separator)).length; find(retro(_input), retro(_separator)).length;
} }
else
{
_backLength = _input.length -
find(retro(_input), _separator).length;
}
} }
public: public:
@ -741,7 +739,8 @@ public:
// done, there's no separator in sight // done, there's no separator in sight
_input = _input[_frontLength .. _frontLength]; _input = _input[_frontLength .. _frontLength];
_frontLength = _frontLength.max; _frontLength = _frontLength.max;
_backLength = _backLength.max; static if (isBidirectionalRange!Range)
_backLength = _backLength.max;
return; return;
} }
if (_frontLength + separatorLength == _input.length) if (_frontLength + separatorLength == _input.length)
@ -750,7 +749,8 @@ public:
// an empty item right after this. // an empty item right after this.
_input = _input[_input.length .. _input.length]; _input = _input[_input.length .. _input.length];
_frontLength = 0; _frontLength = 0;
_backLength = 0; static if (isBidirectionalRange!Range)
_backLength = 0;
return; return;
} }
// Normal case, pop one item and the separator, get ready for // Normal case, pop one item and the separator, get ready for
@ -761,36 +761,124 @@ public:
} }
// Bidirectional functionality as suggested by Brad Roberts. // Bidirectional functionality as suggested by Brad Roberts.
static if (isBidirectionalRange!Range)
{
Range back()
{
ensureBackLength;
return _input[_input.length - _backLength .. _input.length];
}
void popBack()
{
ensureBackLength;
if (_backLength == _input.length)
{
// done
_input = _input[0 .. 0];
_frontLength = _frontLength.max;
_backLength = _backLength.max;
return;
}
if (_backLength + separatorLength == _input.length)
{
// Special case: popping the first-to-first item; there is
// an empty item right before this. Leave the separator in.
_input = _input[0 .. 0];
_frontLength = 0;
_backLength = 0;
return;
}
// Normal case
_input = _input[0 .. _input.length - _backLength - separatorLength];
_backLength = _backLength.max;
}
}
}
struct Splitter(Range, Separator)
if (is(typeof(ElementType!Range.init == Separator.init)))
{
Range _input;
Separator _separator;
size_t _frontLength = size_t.max;
size_t _backLength = size_t.max;
this(Range input, Separator separator)
{
_input = input;
_separator = separator;
computeFront();
computeBack();
}
bool empty()
{
return _input.empty;
}
Range front()
{
if (_frontLength == _frontLength.max)
{
// This is not the first iteration, clean up separators
while (!_input.empty && _input.front == _separator)
_input.popFront();
computeFront();
}
return _input[0 .. _frontLength];
}
void popFront()
{
computeFront();
_input = _input[_frontLength .. $];
_frontLength = _frontLength.max;
}
Range back() Range back()
{ {
ensureBackLength; if (_backLength == _backLength.max)
return _input[_input.length - _backLength .. _input.length]; {
while (!_input.empty && _input.back == _separator) _input.popBack();
computeBack();
}
assert(_backLength <= _input.length, text(_backLength));
return _input[$ - _backLength .. $];
} }
void popBack() void popBack()
{ {
ensureBackLength; computeBack();
if (_backLength == _input.length) enforce(_backLength <= _input.length);
{ _input = _input[0 .. $ - _backLength];
// done
_input = _input[0 .. 0];
_frontLength = _frontLength.max;
_backLength = _backLength.max;
return;
}
if (_backLength + separatorLength == _input.length)
{
// Special case: popping the first-to-first item; there is
// an empty item right before this. Leave the separator in.
_input = _input[0 .. 0];
_frontLength = 0;
_backLength = 0;
return;
}
// Normal case
_input = _input[0 .. _input.length - _backLength - separatorLength];
_backLength = _backLength.max; _backLength = _backLength.max;
} }
private:
void computeFront()
{
if (_frontLength == _frontLength.max)
{
_frontLength = _input.length - _input.find(_separator).length;
}
}
void computeBack()
{
if (_backLength == _backLength.max)
{
//_backLength = find(retro(_input), _separator).length;
_backLength = 0;
auto i = _input;
while (!i.empty)
{
if (i.back == _separator) break;
++_backLength;
i.popBack();
}
}
assert(_backLength <= _input.length);
}
} }
/// Ditto /// Ditto
@ -824,8 +912,9 @@ unittest
} }
assert(i == w.length); assert(i == w.length);
// Now go back // Now go back
//auto s = splitter(a, 0); auto s2 = splitter(a, 0);
foreach_reverse (e; splitter(a, 0))
foreach_reverse (e; s2)
{ {
assert(i > 0); assert(i > 0);
assert(equal(e, w[--i]), text(e)); assert(equal(e, w[--i]), text(e));
@ -1415,7 +1504,7 @@ is ignored.
&& needle[virtual_begin - 1] == needle[$ - portion - 1]) && needle[virtual_begin - 1] == needle[$ - portion - 1])
return 0; return 0;
invariant delta = portion - ignore; immutable delta = portion - ignore;
return equal(needle[needle.length - delta .. needle.length], return equal(needle[needle.length - delta .. needle.length],
needle[virtual_begin .. virtual_begin + delta]); needle[virtual_begin .. virtual_begin + delta]);
} }
@ -1475,7 +1564,7 @@ public:
/// Ditto /// Ditto
BoyerMooreFinder!(binaryFun!(pred), Range) boyerMooreFinder BoyerMooreFinder!(binaryFun!(pred), Range) boyerMooreFinder
(alias pred = "a == b", Range) (alias pred = "a == b", Range)
(Range needle) if (isRandomAccessRange!(Range)) (Range needle) if (isRandomAccessRange!(Range) || isSomeString!Range)
{ {
return typeof(return)(needle); return typeof(return)(needle);
} }
@ -2405,13 +2494,18 @@ struct Levenshtein(Range, alias equals, CostType = size_t)
CostType distance(Range s, Range t) CostType distance(Range s, Range t)
{ {
AllocMatrix(s.length + 1, t.length + 1); auto slen = walkLength(s), tlen = walkLength(t);
AllocMatrix(slen + 1, tlen + 1);
foreach (i; 1 .. rows) foreach (i; 1 .. rows)
{ {
auto sfront = s.front;
s.popFront();
auto tt = t;
foreach (j; 1 .. cols) foreach (j; 1 .. cols)
{ {
auto cSub = _matrix[i - 1][j - 1] auto cSub = _matrix[i - 1][j - 1]
+ (equals(s[i - 1], t[j - 1]) ? 0 : _substitutionIncrement); + (equals(sfront, tt.front) ? 0 : _substitutionIncrement);
tt.popFront();
auto cIns = _matrix[i][j - 1] + _insertionIncrement; auto cIns = _matrix[i][j - 1] + _insertionIncrement;
auto cDel = _matrix[i - 1][j] + _deletionIncrement; auto cDel = _matrix[i - 1][j] + _deletionIncrement;
switch (min_index(cSub, cIns, cDel)) { switch (min_index(cSub, cIns, cDel)) {
@ -2427,7 +2521,7 @@ struct Levenshtein(Range, alias equals, CostType = size_t)
} }
} }
} }
return _matrix[s.length][t.length]; return _matrix[slen][tlen];
} }
EditOp[] path(Range s, Range t) EditOp[] path(Range s, Range t)
@ -2528,7 +2622,7 @@ assert(levenshteinDistance!("toupper(a) == toupper(b)")
*/ */
size_t levenshteinDistance(alias equals = "a == b", Range1, Range2) size_t levenshteinDistance(alias equals = "a == b", Range1, Range2)
(Range1 s, Range2 t) (Range1 s, Range2 t)
if (isRandomAccessRange!(Range1) && isRandomAccessRange!(Range2)) if (isForwardRange!(Range1) && isForwardRange!(Range2))
{ {
Levenshtein!(Range1, binaryFun!(equals), uint) lev; Levenshtein!(Range1, binaryFun!(equals), uint) lev;
return lev.distance(s, t); return lev.distance(s, t);
@ -2549,7 +2643,7 @@ assert(equals(p.field[1], "nrrnsnnn"));
Tuple!(size_t, EditOp[]) Tuple!(size_t, EditOp[])
levenshteinDistanceAndPath(alias equals = "a == b", Range1, Range2) levenshteinDistanceAndPath(alias equals = "a == b", Range1, Range2)
(Range1 s, Range2 t) (Range1 s, Range2 t)
if (isRandomAccessRange!(Range1) && isRandomAccessRange!(Range2)) if (isForwardRange!(Range1) && isForwardRange!(Range2))
{ {
Levenshtein!(Range, binaryFun!(equals)) lev; Levenshtein!(Range, binaryFun!(equals)) lev;
auto d = lev.distance(s, t); auto d = lev.distance(s, t);
@ -2603,7 +2697,7 @@ auto d = copy(a, b);
---- ----
To copy at most $(D n) elements from range $(D a) to range $(D b), you To copy at most $(D n) elements from range $(D a) to range $(D b), you
may want to use $(D copy(take(n, a), b)). To copy those elements from may want to use $(D copy(take(a, n), b)). To copy those elements from
range $(D a) that satisfy predicate $(D pred) to range $(D b), you may range $(D a) that satisfy predicate $(D pred) to range $(D b), you may
want to use $(D copy(filter!(pred)(a), b)). want to use $(D copy(filter!(pred)(a), b)).
@ -2699,7 +2793,7 @@ assert(arr == [ 3, 2, 1 ]);
---- ----
*/ */
void reverse(Range)(Range r) void reverse(Range)(Range r)
//if (isBidirectionalRange!(Range) && hasSwappableElements!(Range)) if (isBidirectionalRange!(Range) && hasSwappableElements!(Range))
{ {
while (!r.empty) while (!r.empty)
{ {

View file

@ -15,7 +15,7 @@ module std.array;
import std.c.stdio; import std.c.stdio;
import core.memory; import core.memory;
import std.algorithm, std.contracts, std.conv, std.encoding, std.range, import std.algorithm, std.contracts, std.conv, std.encoding, std.range,
std.string, std.traits, std.typecons; std.string, std.traits, std.typecons, std.utf;
version(unittest) private import std.stdio; version(unittest) private import std.stdio;
/** /**
@ -160,7 +160,7 @@ void main()
---- ----
*/ */
bool empty(T)(in T[] a) { return !a.length; } @property bool empty(T)(in T[] a) { return !a.length; }
unittest unittest
{ {
@ -187,7 +187,7 @@ void main()
---- ----
*/ */
void popFront(T)(ref T[] a) void popFront(T)(ref T[] a) if (!is(Unqual!T == char) && !is(Unqual!T == wchar))
{ {
assert(a.length, "Attempting to popFront() past the end of an array of " assert(a.length, "Attempting to popFront() past the end of an array of "
~ T.stringof); ~ T.stringof);
@ -203,6 +203,25 @@ unittest
assert(a == [ 2, 3 ]); assert(a == [ 2, 3 ]);
} }
void popFront(T)(ref T[] a) if (is(Unqual!T == char) || is(Unqual!T == wchar))
{
assert(a.length, "Attempting to popFront() past the end of an array of "
~ T.stringof);
a = a[std.utf.stride(a, 0) .. $];
}
unittest
{
string s1 = "\xC2\xA9hello";
s1.popFront();
assert(s1 == "hello");
wstring s2 = "\xC2\xA9hello";
s2.popFront();
assert(s2 == "hello");
string s3 = "\u20AC100";
//write(s3, '\n');
}
/** /**
Implements the range interface primitive $(D popBack) for built-in Implements the range interface primitive $(D popBack) for built-in
arrays. Due to the fact that nonmember functions can be called with arrays. Due to the fact that nonmember functions can be called with
@ -221,7 +240,11 @@ void main()
---- ----
*/ */
void popBack(T)(ref T[] a) { assert(a.length); a = a[0 .. $ - 1]; } void popBack(T)(ref T[] a) if (!is(Unqual!T == char) && !is(Unqual!T == wchar))
{
assert(a.length);
a = a[0 .. $ - 1];
}
unittest unittest
{ {
@ -232,6 +255,63 @@ unittest
assert(a == [ 1, 2 ]); assert(a == [ 1, 2 ]);
} }
void popBack(T)(ref T[] a) if (is(Unqual!T == char))
{
immutable n = a.length;
const p = a.ptr + n;
if (n >= 1 && (p[-1] & 0b1100_0000) != 0b1000_0000)
{
a = a[0 .. n - 1];
}
else if (n >= 2 && (p[-2] & 0b1100_0000) != 0b1000_0000)
{
a = a[0 .. n - 2];
}
else if (n >= 3 && (p[-3] & 0b1100_0000) != 0b1000_0000)
{
a = a[0 .. n - 3];
}
else if (n >= 4 && (p[-4] & 0b1100_0000) != 0b1000_0000)
{
a = a[0 .. n - 4];
}
else
{
assert(false, "Invalid UTF character at end of string");
}
}
unittest
{
string s = "hello\xE2\x89\xA0";
s.popBack();
assert(s == "hello", s);
string s3 = "\xE2\x89\xA0";
auto c = decodeBack(s3);
assert(c == cast(dchar)'\u2260');
assert(s3 == "");
}
void popBack(T)(ref T[] a) if (is(Unqual!T == wchar))
{
assert(a.length);
if (a.length == 1)
{
a = a[0 .. 0];
return;
}
invariant c = a[$ - 2];
a = a[0 .. $ - 1 - (c >= 0xD800 && c <= 0xDBFF)];
}
unittest
{
wstring s = "hello\xE2\x89\xA0";
s.popBack();
assert(s == "hello");
}
/** /**
Implements the range interface primitive $(D front) for built-in Implements the range interface primitive $(D front) for built-in
arrays. Due to the fact that nonmember functions can be called with arrays. Due to the fact that nonmember functions can be called with
@ -248,14 +328,24 @@ void main()
} }
---- ----
*/ */
ref typeof(A[0]) front(A)(A a) if (is(typeof(A[0]))) ref typeof(A[0]) front(A)(A a) if (is(typeof(A[0])) && !isNarrowString!A)
{ {
assert(a.length, "Attempting to fetch the front of an empty array"); assert(a.length, "Attempting to fetch the front of an empty array");
return a[0]; return a[0];
} }
dchar front(A)(A a) if (is(typeof(A[0])) && isNarrowString!A)
{
assert(a.length, "Attempting to fetch the front of an empty array");
size_t i = 0;
return decode(a, i);
}
/// Ditto /// Ditto
void front(T)(T[] a, T v) { assert(a.length); a[0] = v; } void front(T)(T[] a, T v) if (!isNarrowString!A)
{
assert(a.length); a[0] = v;
}
/** /**
Implements the range interface primitive $(D back) for built-in Implements the range interface primitive $(D back) for built-in
@ -272,7 +362,45 @@ void main()
} }
---- ----
*/ */
ref T back(T)(T[] a) { assert(a.length); return a[a.length - 1]; } ref typeof(A.init[0]) back(A)(A a) if (is(typeof(A.init[0]))
&& !isNarrowString!A)
{
enforce(a.length, "Attempting to fetch the back of an empty array");
return a[$ - 1];
}
dchar back(A)(A a)
if (is(typeof(A.init[0])) && isNarrowString!A && a[0].sizeof < 4)
{
assert(a.length, "Attempting to fetch the back of an empty array");
auto n = a.length;
const p = a.ptr + n;
if (n >= 1 && (p[-1] & 0b1100_0000) != 0b1000_0000)
{
--n;
return std.utf.decode(a, n);
}
else if (n >= 2 && (p[-2] & 0b1100_0000) != 0b1000_0000)
{
n -= 2;
return decode(a, n);
}
else if (n >= 3 && (p[-3] & 0b1100_0000) != 0b1000_0000)
{
n -= 3;
return decode(a, n);
}
else if (n >= 4 && (p[-4] & 0b1100_0000) != 0b1000_0000)
{
n -= 4;
return decode(a, n);
}
else
{
throw new UtfException("Invalid UTF character at end of string");
}
}
/** /**
Implements the range interface primitive $(D put) for built-in Implements the range interface primitive $(D put) for built-in
@ -521,21 +649,24 @@ managed array can accommodate before triggering a reallocation).
Appends one item to the managed array. Appends one item to the managed array.
*/ */
void put(U)(U item) if (isImplicitlyConvertible!(U, T) || void put(U)(U item) if (isImplicitlyConvertible!(U, T) ||
isSomeString!(T[]) && isSomeString!(U[])) isSomeChar!T && isSomeChar!U)
{ {
static if (isSomeString!(T[]) && T.sizeof != U.sizeof) static if (isSomeChar!T && isSomeChar!U && T.sizeof < U.sizeof)
{ {
// must do some transcoding around here // must do some transcoding around here
encode!(T)(item, this); Unqual!T[T.sizeof == 1 ? 4 : 2] encoded;
auto len = std.utf.encode(encoded, item);
put(encoded[0 .. len]);
} }
else else
{ {
if (!pArray) pArray = (new typeof(*pArray)[1]).ptr; if (!pArray) pArray = (new typeof(*pArray)[1]).ptr;
if (pArray.length < _capacity) immutable len = pArray.length;
if (len < _capacity)
{ {
// Should do in-place construction here // Should do in-place construction here
pArray.ptr[pArray.length] = item; pArray.ptr[len] = item;
*pArray = pArray.ptr[0 .. pArray.length + 1]; *pArray = pArray.ptr[0 .. len + 1];
} }
else else
{ {
@ -550,34 +681,21 @@ Appends one item to the managed array.
Appends an entire range to the managed array. Appends an entire range to the managed array.
*/ */
void put(Range)(Range items) if (isForwardRange!Range void put(Range)(Range items) if (isForwardRange!Range
&& is(typeof(Appender.init.put(ElementType!(Range).init)))) && is(typeof(Appender.init.put(items.front))))
{ {
// @@@ UNCOMMENT WHEN BUG 2912 IS FIXED @@@ static if (is(typeof(*pArray ~= items)))
// static if (is(typeof(*cast(T[]*) pArray ~= items)))
// {
// if (!pArray) pArray = (new typeof(*pArray)[1]).ptr;
// *pArray ~= items;
// }
// else
// {
// // Generic input range
// for (; !items.empty; items.popFront)
// {
// put(items.front());
// }
// }
// @@@ Doctored version taking BUG 2912 into account @@@
static if (is(typeof(*cast(T[]*) pArray ~= items)) &&
T.sizeof == ElementType!Range.sizeof)
{ {
if (!pArray) pArray = (new typeof(*pArray)[1]).ptr; if (!pArray) pArray = (new typeof(*pArray)[1]).ptr;
*pArray ~= items; *pArray ~= items;
} }
else else
{ {
//pragma(msg, Range.stringof);
// Generic input range // Generic input range
foreach (e; items) put(e); for (; !items.empty; items.popFront)
{
put(items.front());
}
} }
} }

View file

@ -63,7 +63,6 @@ class Base64CharException: Base64Exception
immutable array = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; immutable array = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/** /**
* Returns the number of bytes needed to encode a string of length slen. * Returns the number of bytes needed to encode a string of length slen.
*/ */
@ -163,7 +162,6 @@ unittest
assert(encode("all your base64 are belong to foo") == "YWxsIHlvdXIgYmFzZTY0IGFyZSBiZWxvbmcgdG8gZm9v"); assert(encode("all your base64 are belong to foo") == "YWxsIHlvdXIgYmFzZTY0IGFyZSBiZWxvbmcgdG8gZm9v");
} }
/** /**
* Returns the number of bytes needed to decode an encoded string of this * Returns the number of bytes needed to decode an encoded string of this
* length. * length.
@ -173,7 +171,6 @@ uint decodeLength(uint elen)
return elen / 4 * 3; return elen / 4 * 3;
} }
/** /**
* Decodes str[] and places the result in buf[]. * Decodes str[] and places the result in buf[].
* Params: * Params:
@ -203,7 +200,8 @@ body
uint arrayIndex(char ch) uint arrayIndex(char ch)
out(result) out(result)
{ {
assert(ch == array[result]); //@@BUG@@@ http://d.puremagic.com/issues/show_bug.cgi?id=3667");
//assert(ch == array[result]);
} }
body body
{ {
@ -221,7 +219,6 @@ body
assert(0); assert(0);
} }
if(!estr.length) if(!estr.length)
return buf[0 .. 0]; return buf[0 .. 0];

View file

@ -710,7 +710,7 @@ string decimal(Big b)
b = t.q; b = t.q;
result ~= cast(char)(t.r + '0'); result ~= cast(char)(t.r + '0');
} }
reverse(result); reverse(cast(ubyte[]) result);
return assumeUnique(result); return assumeUnique(result);
} }

View file

@ -336,7 +336,8 @@ invariant(T[U]) assumeUnique(T, U)(ref T[U] array)
return result; return result;
} }
unittest // @@@BUG@@@
version(none) unittest
{ {
int[string] arr = ["a":1]; int[string] arr = ["a":1];
auto arr1 = assumeUnique(arr); auto arr1 = assumeUnique(arr);

View file

@ -152,11 +152,11 @@ Converts array (other than strings) to string. The left bracket,
separator, and right bracket are configurable. Each element is separator, and right bracket are configurable. Each element is
converted by calling $(D to!T). converted by calling $(D to!T).
*/ */
T to(T, S)(S s, in T leftBracket = "[", in T separator = ", ", T to(T, S)(S s, in T leftBracket = "", in T separator = " ",
in T rightBracket = "]") in T rightBracket = "")
if (isSomeString!(T) && !isSomeString!(S) && isArray!(S)) if (isSomeString!(T) && !isSomeString!(S) && isArray!(S))
{ {
alias Unqual!(ElementType!(T)) Char; alias Unqual!(typeof(T.init[0])) Char;
// array-to-string conversion // array-to-string conversion
static if (is(S == void[]) static if (is(S == void[])
|| is(S == const(void)[]) || is(S == invariant(void)[])) { || is(S == const(void)[]) || is(S == invariant(void)[])) {
@ -171,7 +171,7 @@ if (isSomeString!(T) && !isSomeString!(S) && isArray!(S))
result.put(leftBracket); result.put(leftBracket);
foreach (i, e; s) { foreach (i, e; s) {
if (i) result.put(separator); if (i) result.put(separator);
result.put(to!(T)(e)); result.put(to!T(e));
} }
result.put(rightBracket); result.put(rightBracket);
return cast(T) result.data; return cast(T) result.data;
@ -180,11 +180,13 @@ if (isSomeString!(T) && !isSomeString!(S) && isArray!(S))
unittest unittest
{ {
long[] b = [ 1, 3, 5 ];
auto s = to!string(b);
//printf("%d, |%*s|\n", s.length, s.length, s.ptr);
assert(to!string(b) == "1 3 5", s);
double[2] a = [ 1.5, 2.5 ]; double[2] a = [ 1.5, 2.5 ];
//writeln(to!string(a)); //writeln(to!string(a));
assert(to!string(a) == "[1.5, 2.5]"); assert(to!string(a) == "1.5 2.5");
short[] b = [ 1, 3, 5 ];
assert(to!string(b) == "[1, 3, 5]");
} }
/** /**
@ -196,7 +198,7 @@ T to(T, S)(S s, in T leftBracket = "[", in T keyval = ":",
in T separator = ", ", in T rightBracket = "]") in T separator = ", ", in T rightBracket = "]")
if (isAssociativeArray!(S) && isSomeString!(T)) if (isAssociativeArray!(S) && isSomeString!(T))
{ {
alias Unqual!(ElementType!(T)) Char; alias Unqual!(typeof(T.init[0])) Char;
Appender!(Char[]) result; Appender!(Char[]) result;
// hash-to-string conversion // hash-to-string conversion
result.put(leftBracket); result.put(leftBracket);
@ -261,7 +263,7 @@ if (is(S == struct) && isSomeString!(T) && !is(typeof(&S.init.toString)))
{ {
// ok, attempt to forge the tuple // ok, attempt to forge the tuple
t = cast(typeof(t)) &s; t = cast(typeof(t)) &s;
alias Unqual!(ElementType!(T)) Char; alias Unqual!(typeof(T.init[0])) Char;
Appender!(Char[]) app; Appender!(Char[]) app;
app.put(left); app.put(left);
foreach (i, e; t.field) foreach (i, e; t.field)
@ -874,7 +876,7 @@ unittest {
// test array to string conversion // test array to string conversion
foreach (T ; AllNumerics) { foreach (T ; AllNumerics) {
auto a = [to!(T)(1), 2, 3]; auto a = [to!(T)(1), 2, 3];
assert(to!(string)(a) == "[1, 2, 3]"); assert(to!(string)(a) == "1 2 3");
} }
// test enum to int conversion // test enum to int conversion
// enum Testing { Test1, Test2 }; // enum Testing { Test1, Test2 };
@ -1096,13 +1098,8 @@ if (!isSomeString!Source && isFloatingPoint!Target)
Target parse(Target, Source)(ref Source s) Target parse(Target, Source)(ref Source s)
if (isSomeString!Source && isFloatingPoint!Target) if (isSomeString!Source && isFloatingPoint!Target)
{ {
//writefln("toFloat('%s')", s);
auto sz = toStringz(to!(const char[])(s));
if (std.ctype.isspace(*sz))
goto Lerr;
// issue 1589 // issue 1589
version (Windows) //version (Windows)
{ {
if (icmp(s, "nan") == 0) if (icmp(s, "nan") == 0)
{ {
@ -1111,27 +1108,52 @@ if (isSomeString!Source && isFloatingPoint!Target)
} }
} }
// BUG: should set __locale_decpoint to "." for DMC auto t = s;
if (t.startsWith('+', '-')) t.popFront();
munch(t, "0-9"); // integral part
if (t.startsWith('.'))
{
t.popFront(); // decimal point
munch(t, "0-9"); // fractionary part
}
if (t.startsWith('E', 'e'))
{
t.popFront();
if (t.startsWith('+', '-')) t.popFront();
munch(t, "0-9"); // exponent
}
auto len = s.length - t.length;
char sz[80] = void;
enforce(len < sz.length);
foreach (i; 0 .. len) sz[i] = s[i];
sz[len] = 0;
// //writefln("toFloat('%s')", s);
// auto sz = toStringz(to!(const char[])(s));
// if (std.ctype.isspace(*sz))
// goto Lerr;
// // BUG: should set __locale_decpoint to "." for DMC
setErrno(0); setErrno(0);
char* endptr; char* endptr;
static if (is(Target == float)) static if (is(Target == float))
auto f = strtof(sz, &endptr); auto f = strtof(sz.ptr, &endptr);
else static if (is(Target == double)) else static if (is(Target == double))
auto f = strtod(sz, &endptr); auto f = strtod(sz.ptr, &endptr);
else static if (is(Target == real)) else static if (is(Target == real))
auto f = strtold(sz, &endptr); auto f = strtold(sz.ptr, &endptr);
else else
static assert(false); static assert(false);
if (getErrno() == ERANGE) if (getErrno() == ERANGE)
goto Lerr; goto Lerr;
assert(endptr); assert(endptr);
if (endptr == sz) if (endptr == sz.ptr)
{ {
// no progress // no progress
goto Lerr; goto Lerr;
} }
s = s[endptr - sz .. $]; s = s[endptr - sz.ptr .. $];
return f; return f;
Lerr: Lerr:
conv_error!(Source, Target)(s); conv_error!(Source, Target)(s);
@ -1573,7 +1595,6 @@ unittest
try try
{ {
i = to!short(errors[j]); i = to!short(errors[j]);
printf("i = %d\n", i);
} }
catch (Error e) catch (Error e)
{ {
@ -1637,7 +1658,7 @@ unittest
try try
{ {
i = to!ushort(errors[j]); i = to!ushort(errors[j]);
printf("i = %d\n", i); debug(conv) printf("i = %d\n", i);
} }
catch (Error e) catch (Error e)
{ {
@ -1706,7 +1727,7 @@ unittest
try try
{ {
i = to!byte(errors[j]); i = to!byte(errors[j]);
printf("i = %d\n", i); debug(conv) printf("i = %d\n", i);
} }
catch (Error e) catch (Error e)
{ {
@ -1770,7 +1791,7 @@ unittest
try try
{ {
i = to!ubyte(errors[j]); i = to!ubyte(errors[j]);
printf("i = %d\n", i); debug(conv) printf("i = %d\n", i);
} }
catch (Error e) catch (Error e)
{ {
@ -2506,8 +2527,9 @@ T to(T, S)(S input)
if (std.typetuple.staticIndexOf!(Unqual!S, uint, ulong) >= 0 && isSomeString!T) if (std.typetuple.staticIndexOf!(Unqual!S, uint, ulong) >= 0 && isSomeString!T)
{ {
Unqual!S value = input; Unqual!S value = input;
alias Unqual!(ElementType!T) Char; alias Unqual!(typeof(T.init[0])) Char;
static if (is(ElementType!T == const) || is(ElementType!T == immutable)) static if (is(typeof(T.init[0]) == const) ||
is(typeof(T.init[0]) == immutable))
{ {
if (value < 10) if (value < 10)
{ {
@ -2522,21 +2544,23 @@ if (std.typetuple.staticIndexOf!(Unqual!S, uint, ulong) >= 0 && isSomeString!T)
else else
auto maxlength = (value > uint.max ? S.sizeof : uint.sizeof) * 3; auto maxlength = (value > uint.max ? S.sizeof : uint.sizeof) * 3;
Char [] result; Char[] result;
if (__ctfe) result = new Char[maxlength]; if (__ctfe)
result = new Char[maxlength];
else else
result = cast(Char[]) result = cast(Char[])
GC.malloc(Char.sizeof * maxlength, GC.BlkAttr.NO_SCAN) GC.malloc(Char.sizeof * maxlength, GC.BlkAttr.NO_SCAN)
[0 .. Char.sizeof * maxlength]; [0 .. Char.sizeof * maxlength];
uint ndigits = 0; uint ndigits = 0;
while (value) do
{ {
const c = cast(Char) ((value % 10) + '0'); const c = cast(Char) ((value % 10) + '0');
value /= 10; value /= 10;
ndigits++; ndigits++;
result[$ - ndigits] = c; result[$ - ndigits] = c;
} }
while (value);
return cast(T) result[$ - ndigits .. $]; return cast(T) result[$ - ndigits .. $];
} }
@ -2544,20 +2568,22 @@ unittest
{ {
assert(wtext(int.max) == "2147483647"w); assert(wtext(int.max) == "2147483647"w);
assert(wtext(int.min) == "-2147483648"w); assert(wtext(int.min) == "-2147483648"w);
assert(to!string(0L) == "0");
} }
/// $(D char), $(D wchar), $(D dchar) to a string type. /// $(D char), $(D wchar), $(D dchar) to a string type.
T to(T, S)(S c) if (staticIndexOf!(Unqual!S, char, wchar, dchar) >= 0 T to(T, S)(S c) if (staticIndexOf!(Unqual!S, char, wchar, dchar) >= 0
&& isSomeString!(T)) && isSomeString!(T))
{ {
static if (ElementType!T.sizeof >= S.sizeof) alias typeof(T.init[0]) Char;
static if (Char.sizeof >= S.sizeof)
{ {
return [ c ]; return [ c ];
} }
else else
{ {
Unqual!(ElementType!T)[] result; Unqual!Char[] result;
encode(result, cast(dchar) c); encode(result, c);
return cast(T) result; return cast(T) result;
} }
} }
@ -2588,7 +2614,7 @@ if (staticIndexOf!(Unqual!S, int, long) >= 0 && isSomeString!T)
{ {
if (value >= 0) if (value >= 0)
return to!T(cast(Unsigned!(S)) value); return to!T(cast(Unsigned!(S)) value);
alias Unqual!(ElementType!T) Char; alias Unqual!(typeof(T.init[0])) Char;
// Cache read-only data only for const and immutable - mutable // Cache read-only data only for const and immutable - mutable
// data is supposed to use allocation in all cases // data is supposed to use allocation in all cases

View file

@ -1749,7 +1749,7 @@ body
// EncoderInstance!(E).encode(c,buffer); // EncoderInstance!(E).encode(c,buffer);
// } // }
/** /*
Encodes $(D c) in units of type $(D E) and writes the result to the Encodes $(D c) in units of type $(D E) and writes the result to the
output range $(D R). Returns the number of $(D E)s written. output range $(D R). Returns the number of $(D E)s written.
*/ */

View file

@ -1566,7 +1566,7 @@ struct FormatInfo
bool, "flHash", 1, bool, "flHash", 1,
ubyte, "", 3)); ubyte, "", 3));
/* For arrays only: the trailing */ /* For arrays only: the trailing */
const(char)[] innerTrailing; const(char)[] innerTrailing, trailing;
/* /*
* Given a string format specification fmt, parses a format * Given a string format specification fmt, parses a format
@ -1577,6 +1577,7 @@ struct FormatInfo
{ {
FormatInfo result; FormatInfo result;
if (fmt.empty) return result; if (fmt.empty) return result;
scope(success) result.trailing = to!(const(char)[])(fmt);
size_t i = 0; size_t i = 0;
for (;;) for (;;)
switch (fmt[i]) switch (fmt[i])
@ -2867,7 +2868,8 @@ void skipData(Range)(ref Range input, FormatInfo spec)
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
T unformat(T, Range)(ref Range input, FormatInfo spec) if (isArray!T) T unformat(T, Range)(ref Range input, FormatInfo spec)
if (isArray!T && !isSomeString!T)
{ {
Appender!(T) app; Appender!(T) app;
for (;;) for (;;)
@ -2882,6 +2884,42 @@ T unformat(T, Range)(ref Range input, FormatInfo spec) if (isArray!T)
return app.data; return app.data;
} }
//------------------------------------------------------------------------------
T unformat(T, Range)(ref Range input, FormatInfo spec) if (isSomeString!T)
{
Appender!(T) app;
if (spec.trailing.empty)
{
for (; !input.empty; input.popFront())
{
app.put(input.front);
}
}
else
{
for (; !input.empty && !input.startsWith(spec.trailing.front);
input.popFront())
{
app.put(input.front);
}
}
//input = input[spec.innerTrailing.length .. $];
return app.data;
}
unittest
{
string s1, s2;
char[] line = "hello, world".dup;
formattedRead(line, "%s", &s1);
assert(s1 == "hello, world", s1);
line = "hello, world;yah".dup;
formattedRead(line, "%s;%s", &s1, &s2);
assert(s1 == "hello, world", s1);
assert(s2 == "yah", s2);
}
private template acceptedSpecs(T) private template acceptedSpecs(T)
{ {
static if (isIntegral!T) enum acceptedSpecs = "sdu";// + "coxX" (todo) static if (isIntegral!T) enum acceptedSpecs = "sdu";// + "coxX" (todo)
@ -2908,17 +2946,25 @@ T unformat(T, Range)(ref Range input, FormatInfo spec) if (isFloatingPoint!T)
{ {
// raw read // raw read
enforce(input.length >= T.sizeof); enforce(input.length >= T.sizeof);
enforce(ElementType!(Range).sizeof == 1); enforce(isSomeString!Range || ElementType!(Range).sizeof == 1);
union X union X
{ {
char[T.sizeof] raw; ubyte[T.sizeof] raw;
T typed; T typed;
} }
X x; X x;
foreach (i; 0 .. T.sizeof) foreach (i; 0 .. T.sizeof)
{ {
x.raw[i] = input.front; static if (isSomeString!Range)
input.popFront; {
x.raw[i] = input[0];
input = input[1 .. $];
}
else
{
x.raw[i] = input.front;
input.popFront();
}
} }
return x.typed; return x.typed;
} }

View file

@ -64,14 +64,14 @@ JSONValue parseJSON(T)(in T json, int maxDepth = -1) if(isInputRange!T) {
if(json.empty()) return root; if(json.empty()) return root;
int depth = -1; int depth = -1;
char next = 0; dchar next = 0;
int line = 1, pos = 1; int line = 1, pos = 1;
void error(string msg) { void error(string msg) {
throw new JSONException(msg, line, pos); throw new JSONException(msg, line, pos);
} }
char peekChar() { dchar peekChar() {
if(!next) { if(!next) {
if(json.empty()) return '\0'; if(json.empty()) return '\0';
next = json.front(); next = json.front();
@ -84,10 +84,10 @@ JSONValue parseJSON(T)(in T json, int maxDepth = -1) if(isInputRange!T) {
while(isspace(peekChar())) next = 0; while(isspace(peekChar())) next = 0;
} }
char getChar(bool SkipWhitespace = false)() { dchar getChar(bool SkipWhitespace = false)() {
static if(SkipWhitespace) skipWhitespace(); static if(SkipWhitespace) skipWhitespace();
char c = void; dchar c = void;
if(next) { if(next) {
c = next; c = next;
next = 0; next = 0;
@ -111,16 +111,17 @@ JSONValue parseJSON(T)(in T json, int maxDepth = -1) if(isInputRange!T) {
void checkChar(bool SkipWhitespace = true, bool CaseSensitive = true)(char c) { void checkChar(bool SkipWhitespace = true, bool CaseSensitive = true)(char c) {
static if(SkipWhitespace) skipWhitespace(); static if(SkipWhitespace) skipWhitespace();
char c2 = getChar(); auto c2 = getChar();
static if(!CaseSensitive) c2 = cast(char)tolower(c2); static if(!CaseSensitive) c2 = tolower(c2);
if(c2 != c) error(text("Found '", c2, "' when expecting '", c, "'.")); if(c2 != c) error(text("Found '", c2, "' when expecting '", c, "'."));
} }
bool testChar(bool SkipWhitespace = true, bool CaseSensitive = true)(char c) { bool testChar(bool SkipWhitespace = true, bool CaseSensitive = true)(char c)
{
static if(SkipWhitespace) skipWhitespace(); static if(SkipWhitespace) skipWhitespace();
char c2 = peekChar(); auto c2 = peekChar();
static if(!CaseSensitive) c2 = cast(char)tolower(c2); static if (!CaseSensitive) c2 = tolower(c2);
if(c2 != c) return false; if(c2 != c) return false;
@ -139,7 +140,7 @@ JSONValue parseJSON(T)(in T json, int maxDepth = -1) if(isInputRange!T) {
case '\\': case '\\':
getChar(); getChar();
char c = getChar(); auto c = getChar();
switch(c) { switch(c) {
case '"': str.put('"'); break; case '"': str.put('"'); break;
case '\\': str.put('\\'); break; case '\\': str.put('\\'); break;
@ -152,7 +153,7 @@ JSONValue parseJSON(T)(in T json, int maxDepth = -1) if(isInputRange!T) {
case 'u': case 'u':
dchar val = 0; dchar val = 0;
foreach_reverse(i; 0 .. 4) { foreach_reverse(i; 0 .. 4) {
char hex = cast(char)toupper(getChar()); auto hex = toupper(getChar());
if(!isxdigit(hex)) error("Expecting hex character"); if(!isxdigit(hex)) error("Expecting hex character");
val += (isdigit(hex) ? hex - '0' : hex - 'A') << (4 * i); val += (isdigit(hex) ? hex - '0' : hex - 'A') << (4 * i);
} }
@ -166,8 +167,8 @@ JSONValue parseJSON(T)(in T json, int maxDepth = -1) if(isInputRange!T) {
goto Next; goto Next;
default: default:
char c = getChar(); auto c = getChar();
appendJSONChar(&str, c, getChar(), &error); appendJSONChar(&str, c, &error);
goto Next; goto Next;
} }
@ -179,7 +180,7 @@ JSONValue parseJSON(T)(in T json, int maxDepth = -1) if(isInputRange!T) {
if(maxDepth != -1 && depth > maxDepth) error("Nesting too deep."); if(maxDepth != -1 && depth > maxDepth) error("Nesting too deep.");
char c = getChar!true(); auto c = getChar!true();
switch(c) { switch(c) {
case '{': case '{':
@ -314,8 +315,8 @@ string toJSON(in JSONValue* root) {
void toString(string str) { void toString(string str) {
json.put('"'); json.put('"');
for(int i; i != str.length; i++) { foreach (dchar c; str) {
switch(str[i]) { switch(c) {
case '"': json.put("\\\""); break; case '"': json.put("\\\""); break;
case '\\': json.put("\\\\"); break; case '\\': json.put("\\\\"); break;
case '/': json.put("\\/"); break; case '/': json.put("\\/"); break;
@ -325,7 +326,7 @@ string toJSON(in JSONValue* root) {
case '\r': json.put("\\r"); break; case '\r': json.put("\\r"); break;
case '\t': json.put("\\t"); break; case '\t': json.put("\\t"); break;
default: default:
appendJSONChar(&json, str[i], str[++i], appendJSONChar(&json, c,
(string msg){throw new JSONException(msg);}); (string msg){throw new JSONException(msg);});
} }
} }
@ -388,23 +389,25 @@ string toJSON(in JSONValue* root) {
return json.data; return json.data;
} }
private void appendJSONChar(Appender!string* dst, char c, lazy char next, private void appendJSONChar(Appender!string* dst, dchar c,
scope void delegate(string) error) scope void delegate(string) error)
{ {
int stride = UTFStride((&c)[0 .. 1], 0); if(iscntrl(c)) error("Illegal control character.");
if(stride == 1) { dst.put(c);
if(iscntrl(c)) error("Illegal control character."); // int stride = UTFStride((&c)[0 .. 1], 0);
dst.put(c); // if(stride == 1) {
} // if(iscntrl(c)) error("Illegal control character.");
else { // dst.put(c);
char[6] utf = void; // }
utf[0] = c; // else {
foreach(i; 1 .. stride) utf[i] = next; // char[6] utf = void;
size_t index = 0; // utf[0] = c;
if(iscntrl(toUnicode(utf[0 .. stride], index))) // foreach(i; 1 .. stride) utf[i] = next;
error("Illegal control character"); // size_t index = 0;
dst.put(utf[0 .. stride]); // if(iscntrl(toUnicode(utf[0 .. stride], index)))
} // error("Illegal control character");
// dst.put(utf[0 .. stride]);
// }
} }
/** /**

View file

@ -235,11 +235,8 @@ template isRandomAccessRange(R)
{ {
enum bool isRandomAccessRange = enum bool isRandomAccessRange =
(isBidirectionalRange!(R) || isInfinite!(R)) (isBidirectionalRange!(R) || isInfinite!(R))
&& is(typeof( && is(typeof(R.init[1]))
{ && !isNarrowString!R;
R r;
auto e = r[1]; // can index
}()));
} }
unittest unittest
@ -295,9 +292,9 @@ unittest
{ {
enum XYZ : string { a = "foo" }; enum XYZ : string { a = "foo" };
auto x = front(XYZ.a); auto x = front(XYZ.a);
static assert(is(ElementType!(XYZ) : char)); static assert(is(ElementType!(XYZ) : dchar));
immutable char[3] a = "abc"; immutable char[3] a = "abc";
static assert(is(ElementType!(typeof(a)) : char)); static assert(is(ElementType!(typeof(a)) : dchar));
int[] i; int[] i;
static assert(is(ElementType!(typeof(i)) : int)); static assert(is(ElementType!(typeof(i)) : int));
} }
@ -328,7 +325,7 @@ unittest
static assert(!hasSwappableElements!(const int[])); static assert(!hasSwappableElements!(const int[]));
static assert(!hasSwappableElements!(const(int)[])); static assert(!hasSwappableElements!(const(int)[]));
static assert(hasSwappableElements!(int[])); static assert(hasSwappableElements!(int[]));
static assert(hasSwappableElements!(char[])); //static assert(hasSwappableElements!(char[]));
} }
/** /**
@ -371,7 +368,8 @@ other ranges may be infinite.
*/ */
template hasLength(R) template hasLength(R)
{ {
enum bool hasLength = is(typeof(R.init.length) : ulong); enum bool hasLength = is(typeof(R.init.length) : ulong) &&
!isNarrowString!R;
} }
unittest unittest
@ -458,7 +456,7 @@ upTo) steps have been taken and returns $(D upTo).
size_t walkLength(Range)(Range range, size_t upTo = size_t.max) size_t walkLength(Range)(Range range, size_t upTo = size_t.max)
if (isInputRange!(Range)) if (isInputRange!(Range))
{ {
static if (hasLength!(Range)) static if (isRandomAccessRange!Range && hasLength!Range)
{ {
return range.length; return range.length;
} }
@ -558,7 +556,7 @@ Forwards to $(D _input[_input.length - n + 1]). Defined only if $(D R)
is a random access range and if $(D R) defines $(D R.length). is a random access range and if $(D R) defines $(D R.length).
*/ */
static if (isRandomAccessRange!(R) && hasLength!(R)) static if (isRandomAccessRange!(R) && hasLength!(R))
ref ElementType!(R) opIndex(uint n) ref ElementType!R opIndex(uint n)
{ {
return _input[_input.length - n - 1]; return _input[_input.length - n - 1];
} }
@ -568,7 +566,7 @@ Range primitive operation that returns the length of the
range. Forwards to $(D _input.length) and is defined only if $(D range. Forwards to $(D _input.length) and is defined only if $(D
hasLength!(R)). hasLength!(R)).
*/ */
static if (hasLength!(R)) static if (hasLength!R || isNarrowString!R)
size_t length() size_t length()
{ {
return _input.length; return _input.length;
@ -1180,7 +1178,7 @@ offers random access and $(D length), $(D Take) offers them as well.
Example: Example:
---- ----
int[] arr1 = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; int[] arr1 = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
auto s = take(5, arr1); auto s = take(arr1, 5);
assert(s.length == 5); assert(s.length == 5);
assert(s[4] == 5); assert(s[4] == 5);
assert(equal(s, [ 1, 2, 3, 4, 5 ][])); assert(equal(s, [ 1, 2, 3, 4, 5 ][]));
@ -1190,8 +1188,8 @@ assert(equal(s, [ 1, 2, 3, 4, 5 ][]));
struct Take(R) if (isInputRange!(R)) struct Take(R) if (isInputRange!(R))
{ {
private: private:
size_t _maxAvailable;
R _input; R _input;
size_t _maxAvailable;
enum bool byRef = is(typeof(&(R.init[0]))); enum bool byRef = is(typeof(&(R.init[0])));
public: public:
@ -1293,20 +1291,18 @@ public:
} }
}); });
} }
Take opSlice() { return this; }
} }
/// Ditto /// Ditto
Take!(R) take(R)(size_t n, R input) if (isInputRange!(R)) Take!(R) take(R)(R input, size_t n) if (isInputRange!(R))
{ {
return Take!(R)(n, input); return Take!(R)(input, n);
} }
unittest unittest
{ {
int[] arr1 = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; int[] arr1 = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
auto s = take(5, arr1); auto s = take(arr1, 5);
assert(s.length == 5); assert(s.length == 5);
assert(s[4] == 5); assert(s[4] == 5);
assert(equal(s, [ 1, 2, 3, 4, 5 ][])); assert(equal(s, [ 1, 2, 3, 4, 5 ][]));
@ -1394,7 +1390,7 @@ unittest
/** /**
Repeats one value forever. Example: Repeats one value forever. Example:
---- ----
enforce(equal(take(4, repeat(5)), [ 5, 5, 5, 5 ][])); enforce(equal(take(repeat(5), 4), [ 5, 5, 5, 5 ][]));
---- ----
*/ */
@ -1418,21 +1414,21 @@ Repeat!(T) repeat(T)(T value) { return Repeat!(T)(value); }
unittest unittest
{ {
enforce(equal(take(4, repeat(5)), [ 5, 5, 5, 5 ][])); enforce(equal(take(repeat(5), 4), [ 5, 5, 5, 5 ][]));
} }
/** /**
Replicates $(D value) exactly $(D n) times. Equivalent to $(D take(n, Replicates $(D value) exactly $(D n) times. Equivalent to $(D
repeat(value))). take(repeat(value), n)).
*/ */
Take!(Repeat!(T)) replicate(T)(size_t n, T value) Take!(Repeat!(T)) replicate(T)(T value, size_t n)
{ {
return take(n, repeat(value)); return take(repeat(value), n);
} }
unittest unittest
{ {
enforce(equal(replicate(4, 5), [ 5, 5, 5, 5 ][])); enforce(equal(replicate(5, 4), [ 5, 5, 5, 5 ][]));
} }
/** /**
@ -1446,7 +1442,7 @@ mostly for performance reasons.
Example: Example:
---- ----
assert(equal(take(5, cycle([1, 2][])), [ 1, 2, 1, 2, 1 ][])); assert(equal(take(cycle([1, 2][]), 5), [ 1, 2, 1, 2, 1 ][]));
---- ----
Tip: This is a great way to implement simple circular buffers. Tip: This is a great way to implement simple circular buffers.
@ -1543,12 +1539,12 @@ Cycle!(R) cycle(R)(ref R input, size_t index = 0) if (isStaticArray!R)
unittest unittest
{ {
assert(equal(take(5, cycle([1, 2][])), [ 1, 2, 1, 2, 1 ][])); assert(equal(take(cycle([1, 2][]), 5), [ 1, 2, 1, 2, 1 ][]));
int[3] a = [ 1, 2, 3 ]; int[3] a = [ 1, 2, 3 ];
static assert(isStaticArray!(typeof(a))); static assert(isStaticArray!(typeof(a)));
auto c = cycle(a); auto c = cycle(a);
assert(a.ptr == c._ptr); assert(a.ptr == c._ptr);
assert(equal(take(5, cycle(a)), [ 1, 2, 3, 1, 2 ][])); assert(equal(take(cycle(a), 5), [ 1, 2, 3, 1, 2 ][]));
} }
/** /**
@ -1978,9 +1974,9 @@ Example:
// a[0] = 1, a[1] = 1, and compute a[n+1] = a[n-1] + a[n] // a[0] = 1, a[1] = 1, and compute a[n+1] = a[n-1] + a[n]
auto fib = recurrence!("a[n-1] + a[n-2]")(1, 1); auto fib = recurrence!("a[n-1] + a[n-2]")(1, 1);
// print the first 10 Fibonacci numbers // print the first 10 Fibonacci numbers
foreach (e; take(10, fib)) { writeln(e); } foreach (e; take(fib, 10)) { writeln(e); }
// print the first 10 factorials // print the first 10 factorials
foreach (e; take(10, recurrence!("a[n-1] * n")(1))) { writeln(e); } foreach (e; take(recurrence!("a[n-1] * n")(1), 10)) { writeln(e); }
---- ----
*/ */
struct Recurrence(alias fun, StateType, size_t stateSize) struct Recurrence(alias fun, StateType, size_t stateSize)
@ -2021,15 +2017,15 @@ version(none) unittest
{ {
auto fib = recurrence!("a[n-1] + a[n-2]")(1, 1); auto fib = recurrence!("a[n-1] + a[n-2]")(1, 1);
int[] witness = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55 ]; int[] witness = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55 ];
//foreach (e; take(10, fib)) writeln(e); //foreach (e; take(fib, 10)) writeln(e);
assert(equal(take(10, fib), witness)); assert(equal(take(fib, 10), witness));
foreach (e; take(10, fib)) {}//writeln(e); foreach (e; take(fib, 10)) {}//writeln(e);
//writeln(s.front); //writeln(s.front);
auto fact = recurrence!("n * a[n-1]")(1); auto fact = recurrence!("n * a[n-1]")(1);
assert( equal(take(10, fact), [1, 1, 2, 2*3, 2*3*4, 2*3*4*5, 2*3*4*5*6, assert( equal(take(fact, 10), [1, 1, 2, 2*3, 2*3*4, 2*3*4*5, 2*3*4*5*6,
2*3*4*5*6*7, 2*3*4*5*6*7*8, 2*3*4*5*6*7*8*9][]) ); 2*3*4*5*6*7, 2*3*4*5*6*7*8, 2*3*4*5*6*7*8*9][]) );
auto piapprox = recurrence!("a[n] + (n & 1 ? 4. : -4.) / (2 * n + 3)")(4.); auto piapprox = recurrence!("a[n] + (n & 1 ? 4. : -4.) / (2 * n + 3)")(4.);
foreach (e; take(20, piapprox)) {}//writeln(e); foreach (e; take(piapprox, 20)) {}//writeln(e);
} }
/** /**
@ -2098,14 +2094,14 @@ unittest
// alias Sequence!("a.field[0] += a.field[1]", // alias Sequence!("a.field[0] += a.field[1]",
// Tuple!(int, int)) Gen; // Tuple!(int, int)) Gen;
// Gen x = Gen(tuple(0, 5)); // Gen x = Gen(tuple(0, 5));
// foreach (e; take(15, x)) // foreach (e; take(x, 15))
// {}//writeln(e); // {}//writeln(e);
auto y = Sequence!("a.field[0] + n * a.field[1]", Tuple!(int, int)) auto y = Sequence!("a.field[0] + n * a.field[1]", Tuple!(int, int))
(tuple(0, 4)); (tuple(0, 4));
//@@BUG //@@BUG
//auto y = sequence!("a.field[0] + n * a.field[1]")(0, 4); //auto y = sequence!("a.field[0] + n * a.field[1]")(0, 4);
//foreach (e; take(15, y)) //foreach (e; take(y, 15))
{}//writeln(e); {}//writeln(e);
} }
@ -2163,9 +2159,8 @@ if (is(typeof((E.init - B.init) + 1 * S.init)))
"; count=", count)); "; count=", count));
assert(!myless(count * step, end - begin), text("begin=", begin, assert(!myless(count * step, end - begin), text("begin=", begin,
"; end=", end, "; step=", step, "; count=", count)); "; end=", end, "; step=", step, "; count=", count));
return typeof(return)(count, return typeof(return)(typeof(return).Source(
typeof(return).Source( Tuple!(CommonType!(B, E), S)(begin, step), 0u), count);
Tuple!(CommonType!(B, E), S)(begin, step), 0u));
} }
/// Ditto /// Ditto

View file

@ -1516,7 +1516,7 @@ Returns the number of parenthesized captures
} }
/// Ditto /// Ditto
Regex!(Unqual!(ElementType!(String))) regex(String) Regex!(Unqual!(typeof(String.init[0]))) regex(String)
(String pattern, string flags = null) (String pattern, string flags = null)
{ {
return typeof(return)(pattern, flags); return typeof(return)(pattern, flags);
@ -1528,9 +1528,9 @@ stores the matching state and can be inspected and iterated.
*/ */
struct RegexMatch(Range = string) struct RegexMatch(Range = string)
{ {
alias .ElementType!(Range) E; alias typeof(Range.init[0]) E;
// Engine // Engine
alias .Regex!(Unqual!(.ElementType!(Range))) Regex; alias .Regex!(Unqual!E) Regex;
private alias Regex.regmatch_t regmatch_t; private alias Regex.regmatch_t regmatch_t;
/** /**
@ -1623,34 +1623,35 @@ void main()
unittest unittest
{ {
uint i; // @@@BUG@@@ This doesn't work if a client module uses -unittest
foreach(m; match(to!(Range)("abcabcabab"), regex(to!(Range)("ab")))) // uint i;
{ // foreach (m; match(to!(Range)("abcabcabab"), regex(to!(Range)("ab"))))
++i; // {
assert(m.hit == "ab"); // ++i;
//writefln("%s[%s]%s", m.pre, m.hit, m.post); // assert(m.hit == "ab");
} // //writefln("%s[%s]%s", m.pre, m.hit, m.post);
assert(i == 4); // }
// assert(i == 4);
} }
unittest unittest
{ {
// @@@ // @@@BUG@@@ This doesn't work if a client module uses -unittest
debug(regex) printf("regex.search.unittest()\n"); // debug(regex) printf("regex.search.unittest()\n");
int i; // int i;
//foreach(m; RegexMatch("ab").search("abcabcabab")) // //foreach(m; RegexMatch("ab").search("abcabcabab"))
foreach(m; .match("abcabcabab", regex("ab"))) // foreach(m; .match("abcabcabab", regex("ab")))
{ // {
auto s = std.string.format("%s[%s]%s", m.pre, m.hit, m.post); // auto s = std.string.format("%s[%s]%s", m.pre, m.hit, m.post);
if (i == 0) assert(s == "[ab]cabcabab"); // if (i == 0) assert(s == "[ab]cabcabab");
else if (i == 1) assert(s == "abc[ab]cabab"); // else if (i == 1) assert(s == "abc[ab]cabab");
else if (i == 2) assert(s == "abcabc[ab]ab"); // else if (i == 2) assert(s == "abcabc[ab]ab");
else if (i == 3) assert(s == "abcabcab[ab]"); // else if (i == 3) assert(s == "abcabcab[ab]");
else assert(0); // else assert(0);
i++; // i++;
} // }
assert(i == 4); // assert(i == 4);
} }
struct Captures struct Captures
@ -1716,14 +1717,15 @@ foreach (m; match("abracadabra", "(.)a(.)"))
unittest unittest
{ {
Appender!(char[]) app; // @@@BUG@@@ This doesn't work if a client module uses -unittest
foreach (m; match("abracadabra", "(.)a(.)")) // Appender!(char[]) app;
{ // foreach (m; match("abracadabra", "(.)a(.)"))
assert(m.captures.length == 3); // {
foreach (c; m.captures) // assert(m.captures.length == 3);
app.put(c), app.put(';'); // foreach (c; m.captures)
} // app.put(c), app.put(';');
assert(app.data == "rac;r;c;dab;d;b;"); // }
// assert(app.data == "rac;r;c;dab;d;b;");
} }
/******************* /*******************
@ -1821,26 +1823,6 @@ Returns $(D hit) (converted to $(D string) if necessary).
return result; return result;
} }
unittest
{
//@@@
// debug(regex) printf("regex.replace.unittest()\n");
auto r = match("1ab2ac3", regex("a[bc]", "g"));
auto result = r.replaceAll("x$&y");
auto i = std.string.cmp(result, "1xaby2xacy3");
assert(i == 0);
r = match("1ab2ac3", regex("ab", "g"));
result = r.replaceAll("xy");
i = std.string.cmp(result, "1xy2ac3");
assert(i == 0);
r = match("wyda", regex("(giba)"));
assert(r.captures.length == 0);
}
/* /*
* Test s[] starting at startindex against regular expression. * Test s[] starting at startindex against regular expression.
* Returns: 0 for no match, !=0 for match * Returns: 0 for no match, !=0 for match
@ -1863,7 +1845,7 @@ Returns $(D hit) (converted to $(D string) if necessary).
pmatch[0].endIdx = 0; pmatch[0].endIdx = 0;
// First character optimization // First character optimization
Unqual!(ElementType!(Range)) firstc = 0; Unqual!(typeof(Range.init[0])) firstc = 0;
if (engine.program[0] == engine.REchar) if (engine.program[0] == engine.REchar)
{ {
firstc = engine.program[1]; firstc = engine.program[1];
@ -1922,13 +1904,6 @@ Returns $(D hit) (converted to $(D string) if necessary).
*/ */
//alias test opEquals; //alias test opEquals;
unittest
{
//@@@
assert(!match("abc", regex(".b.")).empty);
assert(match("abc", regex(".b..")).empty);
}
private bool chr(ref uint si, E c) private bool chr(ref uint si, E c)
{ {
for (; si < input.length; si++) for (; si < input.length; si++)
@ -2697,6 +2672,30 @@ and, using the format string, generate and return a new string.
} }
} // end of class RegexMatch } // end of class RegexMatch
unittest
{
debug(regex) printf("regex.replace.unittest()\n");
auto r = match("1ab2ac3", regex("a[bc]", "g"));
auto result = r.replaceAll("x$&y");
auto i = std.string.cmp(result, "1xaby2xacy3");
assert(i == 0);
r = match("1ab2ac3", regex("ab", "g"));
result = r.replaceAll("xy");
i = std.string.cmp(result, "1xy2ac3");
assert(i == 0);
r = match("wyda", regex("(giba)"));
assert(r.captures.length == 0);
}
unittest
{
//@@@
assert(!match("abc", regex(".b.")).empty);
assert(match("abc", regex(".b..")).empty);
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/** /**
@ -2707,7 +2706,7 @@ inspection or for iterating over all matches (if the regular
expression was built with the "g" option). expression was built with the "g" option).
*/ */
RegexMatch!(Range) match(Range, Engine)(Range r, Engine engine) RegexMatch!(Range) match(Range, Engine)(Range r, Engine engine)
if (is(Engine == Regex!(Unqual!(ElementType!(Range))))) if (is(Engine == Regex!(Unqual!(typeof(Range.init[0])))))
{ {
return typeof(return)(engine, r); return typeof(return)(engine, r);
} }
@ -2789,7 +2788,7 @@ assert(replace("noon", regex("^n"), "[$&]") == "[n]oon");
*/ */
Range replace(Range, Engine, String)(Range input, Engine regex, String format) Range replace(Range, Engine, String)(Range input, Engine regex, String format)
if (is(Engine == Regex!(Unqual!(ElementType!(Range))))) if (is(Engine == Regex!(Unqual!(typeof(Range.init[0])))))
{ {
return RegexMatch!(Range)(regex, input).replaceAll(format); return RegexMatch!(Range)(regex, input).replaceAll(format);
} }
@ -2915,7 +2914,7 @@ struct Splitter(Range)
{ {
Range _input; Range _input;
size_t _offset; size_t _offset;
alias Regex!(Unqual!(ElementType!(Range))) Rx; alias Regex!(Unqual!(typeof(Range.init[0]))) Rx;
// Rx _rx; // Rx _rx;
RegexMatch!(Range) _match; RegexMatch!(Range) _match;

View file

@ -20,9 +20,9 @@ Distributed under the Boost Software License, Version 1.0.
module std.stdio; module std.stdio;
public import core.stdc.stdio; public import core.stdc.stdio;
private import std.stdiobase;
private import core.memory, core.stdc.errno, core.stdc.stddef, private import core.memory, core.stdc.errno, core.stdc.stddef,
core.stdc.stdlib, core.stdc.string, core.stdc.wchar_; core.stdc.stdlib, core.stdc.string, core.stdc.wchar_;
private import std.stdiobase;
private import std.algorithm, std.array, std.contracts, std.conv, std.file, std.format, private import std.algorithm, std.array, std.contracts, std.conv, std.file, std.format,
/*std.metastrings,*/ std.range, std.string, std.traits, std.typecons, /*std.metastrings,*/ std.range, std.string, std.traits, std.typecons,
std.typetuple, std.utf; std.typetuple, std.utf;
@ -618,47 +618,88 @@ This method is more efficient than the one in the previous example
because $(D stdin.readln(buf)) reuses (if possible) memory allocated because $(D stdin.readln(buf)) reuses (if possible) memory allocated
by $(D buf), whereas $(D buf = stdin.readln()) makes a new memory allocation by $(D buf), whereas $(D buf = stdin.readln()) makes a new memory allocation
with every line. */ with every line. */
S readln(S = string)(dchar terminator = '\n')
size_t readln(ref char[] buf, dchar terminator = '\n')
{ {
enforce(p && p.handle, "Attempt to read from an unopened file."); Unqual!(typeof(S.init[0]))[] buf;
return readlnImpl(p.handle, buf, terminator);
}
/** ditto */
string readln(dchar terminator = '\n')
{
char[] buf;
readln(buf, terminator); readln(buf, terminator);
return assumeUnique(buf); return assumeUnique(buf);
} }
/** ditto */ unittest
// TODO: optimize this
size_t readln(ref wchar[] buf, dchar terminator = '\n')
{ {
string s = readln(terminator); std.file.write("deleteme", "hello\nworld\n");
if (!s.length) return 0; scope(exit) std.file.remove("deleteme");
buf.length = 0; foreach (C; Tuple!(char, wchar, dchar).Types)
foreach (wchar c; s)
{ {
buf ~= c; auto witness = [ "hello\n", "world\n" ];
auto f = File("deleteme");
uint i = 0;
immutable(C)[] buf;
while ((buf = f.readln!(typeof(buf))()).length)
{
assert(i < witness.length);
assert(equal(buf, witness[i++]));
}
assert(i == witness.length);
}
}
/** ditto */
size_t readln(C)(ref C[] buf, dchar terminator = '\n') if (isSomeChar!C)
{
static if (is(C == char))
{
enforce(p && p.handle, "Attempt to read from an unopened file.");
return readlnImpl(p.handle, buf, terminator);
}
else
{
// TODO: optimize this
string s = readln(terminator);
if (!s.length) return 0;
buf.length = 0;
foreach (wchar c; s)
{
buf ~= c;
}
return buf.length;
}
}
/** ditto */
size_t readln(C, R)(ref C[] buf, R terminator)
if (isBidirectionalRange!R && is(typeof(terminator.front == buf[0])))
{
auto last = terminator.back();
C[] buf2;
swap(buf, buf2);
for (;;) {
if (!readln(buf2, last) || endsWith(buf2, terminator)) {
if (buf.empty) {
buf = buf2;
} else {
buf ~= buf2;
}
break;
}
buf ~= buf2;
} }
return buf.length; return buf.length;
} }
/** ditto */ unittest
// TODO: fold this together with wchar
size_t readln(ref dchar[] buf, dchar terminator = '\n')
{ {
string s = readln(terminator); std.file.write("deleteme", "hello\n\rworld\nhow\n\rare ya");
if (!s.length) return 0; auto witness = [ "hello\n\r", "world\nhow\n\r", "are ya" ];
buf.length = 0; scope(exit) std.file.remove("deleteme");
foreach (dchar c; s) auto f = File("deleteme");
uint i = 0;
char[] buf;
while (f.readln(buf, "\n\r"))
{ {
buf ~= c; assert(i < witness.length);
assert(buf == witness[i++]);
} }
return buf.length;
} }
size_t readf(Data...)(in char[] format, Data data) size_t readf(Data...)(in char[] format, Data data)
@ -882,7 +923,10 @@ $(D Range) that locks the file and allows fast writing to it.
/// Range primitive implementations. /// Range primitive implementations.
void put(A)(A writeme) if (is(ElementType!A : const(dchar))) void put(A)(A writeme) if (is(ElementType!A : const(dchar)))
{ {
alias ElementType!A C; static if (isSomeString!A)
alias typeof(writeme[0]) C;
else
alias ElementType!A C;
static assert(!is(C == void)); static assert(!is(C == void));
// writeln("typeof(A.init[0]) = ", typeof(A.init[0]), // writeln("typeof(A.init[0]) = ", typeof(A.init[0]),
// ", ElementType!A = ", ElementType!A); // ", ElementType!A = ", ElementType!A);
@ -1053,7 +1097,8 @@ $(D Range) that locks the file and allows fast writing to it.
void popFront() void popFront()
{ {
enforce(_f.p && _f.p.handle, "Attempt to read from an unopened file."); enforce(_f.p && _f.p.handle,
"Attempt to read from an unopened file.");
assert(!empty); assert(!empty);
_crt = getc(cast(FILE*) _f.p.handle); _crt = getc(cast(FILE*) _f.p.handle);
} }
@ -1176,16 +1221,16 @@ unittest
scope(failure) printf("Failed test at line %d\n", __LINE__); scope(failure) printf("Failed test at line %d\n", __LINE__);
void[] buf; void[] buf;
write(buf); write(buf);
// // test write // test write
string file = "dmd-build-test.deleteme.txt"; string file = "dmd-build-test.deleteme.txt";
auto f = File(file, "w"); auto f = File(file, "w");
scope(exit) { std.file.remove(file); } // scope(exit) { std.file.remove(file); }
f.write("Hello, ", "world number ", 42, "!"); f.write("Hello, ", "world number ", 42, "!");
f.close; f.close;
assert(cast(char[]) std.file.read(file) == "Hello, world number 42!"); assert(cast(char[]) std.file.read(file) == "Hello, world number 42!");
// // test write on stdout // // test write on stdout
auto saveStdout = stdout; //auto saveStdout = stdout;
scope(exit) stdout = saveStdout; //scope(exit) stdout = saveStdout;
//stdout.open(file, "w"); //stdout.open(file, "w");
Object obj; Object obj;
//write("Hello, ", "world number ", 42, "! ", obj); //write("Hello, ", "world number ", 42, "! ", obj);
@ -1837,8 +1882,6 @@ Initialize with a message and an error code. */
extern(C) void std_stdio_static_this() extern(C) void std_stdio_static_this()
{ {
//printf("std_stdio_static_this()\n");
//Bind stdin, stdout, stderr //Bind stdin, stdout, stderr
__gshared File.Impl stdinImpl; __gshared File.Impl stdinImpl;
stdinImpl.handle = core.stdc.stdio.stdin; stdinImpl.handle = core.stdc.stdio.stdin;

View file

@ -742,8 +742,9 @@ unittest
S tolower(S)(S s) if (isSomeString!S) S tolower(S)(S s) if (isSomeString!S)
{ {
alias typeof(s[0]) Char;
int changed; int changed;
Unqual!(ElementType!S)[] r; Unqual!(Char)[] r;
for (size_t i = 0; i < s.length; i++) for (size_t i = 0; i < s.length; i++)
{ {
@ -755,7 +756,7 @@ S tolower(S)(S s) if (isSomeString!S)
r = s.dup; r = s.dup;
changed = 1; changed = 1;
} }
r[i] = cast(Unqual!(ElementType!S)) (c + ('a' - 'A')); r[i] = cast(Unqual!Char) (c + ('a' - 'A'));
} }
else if (c > 0x7F) else if (c > 0x7F)
{ {
@ -866,8 +867,9 @@ unittest
S toupper(S)(S s) if (isSomeString!S) S toupper(S)(S s) if (isSomeString!S)
{ {
alias typeof(s[0]) Char;
int changed; int changed;
Unqual!(ElementType!S)[] r; Unqual!(Char)[] r;
foreach (i; 0 .. s.length) foreach (i; 0 .. s.length)
{ {
@ -879,7 +881,7 @@ S toupper(S)(S s) if (isSomeString!S)
r = to!(typeof(r))(s); r = to!(typeof(r))(s);
changed = 1; changed = 1;
} }
r[i] = cast(Unqual!(ElementType!S)) (c - ('a' - 'A')); r[i] = cast(Unqual!(Char)) (c - ('a' - 'A'));
} }
else if (c > 0x7F) else if (c > 0x7F)
{ {

View file

@ -952,8 +952,7 @@ Detect whether T is one of the built-in string types
template isSomeString(T) template isSomeString(T)
{ {
enum isSomeString = is(T : const(char[])) enum isSomeString = isNarrowString!T || is(T : const(dchar[]));
|| is(T : const(wchar[])) || is(T : const(dchar[]));
} }
unittest unittest
@ -969,6 +968,24 @@ unittest
static assert(isSomeString!(char[4])); static assert(isSomeString!(char[4]));
} }
template isNarrowString(T)
{
enum isNarrowString = is(T : const(char[])) || is(T : const(wchar[]));
}
unittest
{
static assert(!isNarrowString!(int));
static assert(!isNarrowString!(int[]));
static assert(!isNarrowString!(byte[]));
static assert(isNarrowString!(char[]));
static assert(!isNarrowString!(dchar[]));
static assert(isNarrowString!(string));
static assert(isNarrowString!(wstring));
static assert(!isNarrowString!(dstring));
static assert(isNarrowString!(char[4]));
}
/** /**
Detect whether T is one of the built-in character types Detect whether T is one of the built-in character types
*/ */

View file

@ -129,7 +129,9 @@ private invariant ubyte[256] UTF8stride =
uint stride(in char[] s, size_t i) uint stride(in char[] s, size_t i)
{ {
return UTF8stride[s[i]]; invariant result = UTF8stride[s[i]];
assert(result > 0 && result <= 6);
return result;
} }
/** /**
@ -469,7 +471,7 @@ body
// Decodes one dchar from input range $(D r). Returns the decoded // Decodes one dchar from input range $(D r). Returns the decoded
// character and the shortened range. // character and the shortened range.
dchar decodeFront(Range)(ref Range r) dchar decodeFront(Range)(ref Range r) if (!isSomeString!Range)
out (result) out (result)
{ {
assert(isValidDchar(result)); assert(isValidDchar(result));
@ -528,6 +530,7 @@ body
* 11111000 10000xxx (10xxxxxx 10xxxxxx 10xxxxxx) * 11111000 10000xxx (10xxxxxx 10xxxxxx 10xxxxxx)
* 11111100 100000xx (10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx) * 11111100 100000xx (10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx)
*/ */
assert(!r.empty);
auto u2 = r.front; auto u2 = r.front;
enforce(!((u & 0xFE) == 0xC0 || enforce(!((u & 0xFE) == 0xC0 ||
(u == 0xE0 && (u2 & 0xE0) == 0x80) || (u == 0xE0 && (u2 & 0xE0) == 0x80) ||
@ -597,7 +600,7 @@ unittest
// Decodes one dchar from input range $(D r). Returns the decoded // Decodes one dchar from input range $(D r). Returns the decoded
// character and the shortened range. // character and the shortened range.
dchar decodeBack(Range)(ref Range r) dchar decodeBack(Range)(ref Range r) if (!isSomeString!Range)
{ {
enforce(!r.empty); enforce(!r.empty);
Unqual!(ElementType!Range)[4] chars; Unqual!(ElementType!Range)[4] chars;
@ -627,13 +630,28 @@ dchar decodeBack(Range)(ref Range r)
return decoded; return decoded;
} }
dchar decodeFront(Range)(ref Range r) if (isSomeString!Range)
{
auto result = r.front;
r.popFront();
return result;
}
dchar decodeBack(Range)(ref Range r) if (isSomeString!Range)
{
auto result = r.back;
r.popBack();
return result;
}
unittest unittest
{ {
debug(utf) printf("utf.decodeBack.unittest\n"); debug(utf) printf("utf.decodeBack.unittest\n");
static string s1 = "abcd"; string s1 = "abcd";
auto c = decodeBack(s1); auto c = decodeBack(s1);
assert(c == cast(dchar)'d'); assert(c == cast(dchar)'d');
assert(s1 == "abc"); assert(s1 == "abc");
c = decodeBack(s1); c = decodeBack(s1);
assert(c == cast(dchar)'c'); assert(c == cast(dchar)'c');
@ -644,7 +662,7 @@ unittest
assert(c == cast(dchar)'\u00A9'); assert(c == cast(dchar)'\u00A9');
assert(s2 == ""); assert(s2 == "");
static string s3 = "\xE2\x89\xA0"; string s3 = "\xE2\x89\xA0";
c = decodeBack(s3); c = decodeBack(s3);
assert(c == cast(dchar)'\u2260'); assert(c == cast(dchar)'\u2260');
assert(s3 == ""); assert(s3 == "");
@ -1178,7 +1196,6 @@ unittest
assert(w == "hello"); assert(w == "hello");
d = toUTF32(c); d = toUTF32(c);
assert(d == "hello"); assert(d == "hello");
c = toUTF8(w); c = toUTF8(w);
assert(c == "hello"); assert(c == "hello");
d = toUTF32(w); d = toUTF32(w);
@ -1238,10 +1255,16 @@ Standards: Unicode 5.0, ASCII, ISO-8859-1, WINDOWS-1252
Params: Params:
s = the string to be counted s = the string to be counted
*/ */
uint count(E)(const(E)[] s) size_t count(E)(const(E)[] s) if (isSomeChar!E && E.sizeof < 4)
{ {
//assert(isValid(s)); return walkLength(s);
return walkLength(byDchar(s)); // size_t result = 0;
// while (!s.empty)
// {
// ++result;
// s.popFront();
// }
// return result;
} }
unittest unittest

View file

@ -66,8 +66,10 @@
module std.variant; module std.variant;
import std.traits, std.c.string, std.typetuple, std.conv; import std.traits, std.c.string, std.typetuple, std.conv;
import std.stdio; // for testing only version(unittest)
import std.contracts; // for testing only {
import std.contracts, std.stdio;
}
private template maxSize(T...) private template maxSize(T...)
{ {
@ -149,9 +151,9 @@ template This2Variant(V, T...)
struct VariantN(size_t maxDataSize, AllowedTypesX...) struct VariantN(size_t maxDataSize, AllowedTypesX...)
{ {
private:
alias This2Variant!(VariantN, AllowedTypesX) AllowedTypes; alias This2Variant!(VariantN, AllowedTypesX) AllowedTypes;
private:
// Compute the largest practical size from maxDataSize // Compute the largest practical size from maxDataSize
struct SizeChecker struct SizeChecker
{ {
@ -253,7 +255,21 @@ private:
alias TypeTuple!(A, ImplicitConversionTargets!(A)) AllTypes; alias TypeTuple!(A, ImplicitConversionTargets!(A)) AllTypes;
foreach (T ; AllTypes) foreach (T ; AllTypes)
{ {
if (targetType != typeid(T)) continue; if (targetType != typeid(T) &&
targetType != typeid(const(T)))
{
static if (isImplicitlyConvertible!(T, immutable(T)))
{
if (targetType != typeid(immutable(T)))
{
continue;
}
}
else
{
continue;
}
}
// found!!! // found!!!
static if (is(typeof(*cast(T*) target = *src))) static if (is(typeof(*cast(T*) target = *src)))
{ {
@ -363,7 +379,11 @@ private:
case OpID.index: case OpID.index:
// Added allowed!(...) prompted by a bug report by Chris // Added allowed!(...) prompted by a bug report by Chris
// Nicholson-Sauls. // Nicholson-Sauls.
static if (isArray!(A) && allowed!(typeof(A.init[0]))) static if (isStaticArray!(A) && allowed!(typeof(A.init)))
{
enforce(0, "Not implemented");
}
static if (isDynamicArray!(A) && allowed!(typeof(A.init[0])))
{ {
// array type; input and output are the same VariantN // array type; input and output are the same VariantN
auto result = cast(VariantN*) parm; auto result = cast(VariantN*) parm;
@ -422,7 +442,7 @@ private:
// append a whole array to the array // append a whole array to the array
(*zis) ~= arg[0].get!(A); (*zis) ~= arg[0].get!(A);
} }
break; break;
} }
else else
{ {
@ -466,44 +486,35 @@ public:
VariantN opAssign(T)(T rhs) VariantN opAssign(T)(T rhs)
{ {
assert(&this !is null); // weird bug in hashtables //writeln(typeid(rhs));
static assert(allowed!(T), "Cannot store a " ~ T.stringof static assert(allowed!(T), "Cannot store a " ~ T.stringof
~ " in a " ~ VariantN.stringof ~ ". Valid types are " ~ " in a " ~ VariantN.stringof ~ ". Valid types are "
~ AllowedTypes.stringof); ~ AllowedTypes.stringof);
static if (isStaticArray!(T)) static if (is(T : VariantN))
{ {
// Fix for Brad's bug rhs.fptr(OpID.copyOut, &rhs.store, &this);
auto temp = to!(DecayStaticToDynamicArray!(T))(rhs); }
return opAssign(temp); else static if (is(T : const(VariantN)))
{
static assert(false,
"Assigning Variant objects from const Variant"
" objects is currently not supported.");
} }
else else
{ {
static if (is(T : VariantN)) static if (T.sizeof <= size)
{ {
rhs.fptr(OpID.copyOut, &rhs.store, &this); memcpy(&store, &rhs, rhs.sizeof);
}
else static if (is(T : const(VariantN)))
{
static assert(false,
"Assigning Variant objects from const Variant"
" objects is currently not supported.");
} }
else else
{ {
static if (T.sizeof <= size) auto p = new T;
{ *p = rhs;
memcpy(&store, &rhs, rhs.sizeof); memcpy(&store, &p, p.sizeof);
}
else
{
auto p = new T;
*p = rhs;
memcpy(&store, &p, p.sizeof);
}
fptr = &handler!(T);
} }
return this; fptr = &handler!(T);
} }
return this;
} }
/** Returns true if and only if the $(D_PARAM VariantN) object /** Returns true if and only if the $(D_PARAM VariantN) object
@ -575,31 +586,31 @@ public:
return fptr(OpID.testConversion, null, &info) == 0; return fptr(OpID.testConversion, null, &info) == 0;
} }
private T[] testing123(T)(T*); // private T[] testing123(T)(T*);
/** // /**
* A workaround for the fact that functions cannot return // * A workaround for the fact that functions cannot return
* statically-sized arrays by value. Essentially $(D_PARAM // * statically-sized arrays by value. Essentially $(D_PARAM
* DecayStaticToDynamicArray!(T[N])) is an alias for $(D_PARAM // * DecayStaticToDynamicArray!(T[N])) is an alias for $(D_PARAM
* T[]) and $(D_PARAM DecayStaticToDynamicArray!(T)) is an alias // * T[]) and $(D_PARAM DecayStaticToDynamicArray!(T)) is an alias
* for $(D_PARAM T). // * for $(D_PARAM T).
*/ // */
template DecayStaticToDynamicArray(T) // template DecayStaticToDynamicArray(T)
{ // {
static if (isStaticArray!(T)) // static if (isStaticArray!(T))
{ // {
alias typeof(testing123(&T[0])) DecayStaticToDynamicArray; // alias typeof(testing123(&T[0])) DecayStaticToDynamicArray;
} // }
else // else
{ // {
alias T DecayStaticToDynamicArray; // alias T DecayStaticToDynamicArray;
} // }
} // }
static assert(is(DecayStaticToDynamicArray!(invariant(char)[21]) == // static assert(is(DecayStaticToDynamicArray!(invariant(char)[21]) ==
invariant(char)[]), // invariant(char)[]),
DecayStaticToDynamicArray!(invariant(char)[21]).stringof); // DecayStaticToDynamicArray!(invariant(char)[21]).stringof);
/** /**
* Returns the value stored in the $(D_PARAM VariantN) object, * Returns the value stored in the $(D_PARAM VariantN) object,
@ -609,15 +620,15 @@ public:
* VariantException). * VariantException).
*/ */
DecayStaticToDynamicArray!(T) get(T)() if (!is(T == const)) T get(T)() if (!is(T == const))
{ {
union Buf union Buf
{ {
TypeInfo info; TypeInfo info;
DecayStaticToDynamicArray!(T) result; T result;
}; };
auto p = *cast(T**) &store; auto p = *cast(T**) &store;
Buf buf = { typeid(DecayStaticToDynamicArray!(T)) }; Buf buf = { typeid(T) };
if (fptr(OpID.get, &store, &buf)) if (fptr(OpID.get, &store, &buf))
{ {
throw new VariantException(type, typeid(T)); throw new VariantException(type, typeid(T));
@ -625,16 +636,16 @@ public:
return buf.result; return buf.result;
} }
DecayStaticToDynamicArray!(T) get(T)() const if (is(T == const)) T get(T)() const if (is(T == const))
{ {
union Buf union Buf
{ {
TypeInfo info; TypeInfo info;
DecayStaticToDynamicArray!(Unqual!T) result; Unqual!T result;
}; };
auto p = *cast(T**) &store; auto p = *cast(T**) &store;
Buf buf = { typeid(DecayStaticToDynamicArray!(T)) }; Buf buf = { typeid(T) };
if (fptr(OpID.get, cast(Unqual!(typeof(&store))) &store, &buf)) if (fptr(OpID.get, cast(typeof(&store)) &store, &buf))
{ {
throw new VariantException(type, typeid(T)); throw new VariantException(type, typeid(T));
} }
@ -1103,30 +1114,31 @@ static class VariantException : Exception
unittest unittest
{ {
// alias This2Variant!(char, int, This[int]) W1; alias This2Variant!(char, int, This[int]) W1;
// alias TypeTuple!(int, char[int]) W2; alias TypeTuple!(int, char[int]) W2;
// static assert(is(W1 == W2)); static assert(is(W1 == W2));
// alias Algebraic!(void, string) var_t; alias Algebraic!(void, string) var_t;
// var_t foo = "quux"; var_t foo = "quux";
} }
unittest unittest
{ {
// @@@BUG@@@
// alias Algebraic!(real, This[], This[int], This[This]) A; // alias Algebraic!(real, This[], This[int], This[This]) A;
// A v1, v2, v3; // A v1, v2, v3;
// v2 = 5.0L; // v2 = 5.0L;
// v3 = 42.0L; // v3 = 42.0L;
// //v1 = [ v2 ][]; // //v1 = [ v2 ][];
// auto v = v1.peek!(A[]); // auto v = v1.peek!(A[]);
//writeln(v[0]); // //writeln(v[0]);
//v1 = [ 9 : v3 ]; // v1 = [ 9 : v3 ];
// //writeln(v1); // //writeln(v1);
// v1 = [ v3 : v3 ]; // v1 = [ v3 : v3 ];
//writeln(v1); // //writeln(v1);
} }
version(none) unittest unittest
{ {
// try it with an oddly small size // try it with an oddly small size
VariantN!(1) test; VariantN!(1) test;
@ -1224,8 +1236,15 @@ unittest
assert( v.get!(string) == "Hello, World!" ); assert( v.get!(string) == "Hello, World!" );
v = [1,2,3,4,5]; v = [1,2,3,4,5];
assert( v.peek!(int[]) ); assert( v.peek!(int[5]) );
assert( v.get!(int[]) == [1,2,3,4,5] ); assert( v.get!(int[5]) == [1,2,3,4,5] );
{
// @@@BUG@@@: array literals should have type T[], not T[5] (I guess)
// v = [1,2,3,4,5];
// assert( v.peek!(int[]) );
// assert( v.get!(int[]) == [1,2,3,4,5] );
}
v = 3.1413; v = 3.1413;
assert( v.peek!(double) ); assert( v.peek!(double) );
@ -1303,6 +1322,9 @@ unittest
assert( hash[v2] == 1 ); assert( hash[v2] == 1 );
assert( hash[v3] == 2 ); assert( hash[v3] == 2 );
} }
/+
// @@@BUG@@@
// dmd: mtype.c:3886: StructDeclaration* TypeAArray::getImpl(): Assertion `impl' failed.
{ {
int[char[]] hash; int[char[]] hash;
hash["a"] = 1; hash["a"] = 1;
@ -1314,6 +1336,7 @@ unittest
assert( vhash.get!(int[char[]])["b"] == 2 ); assert( vhash.get!(int[char[]])["b"] == 2 );
assert( vhash.get!(int[char[]])["c"] == 3 ); assert( vhash.get!(int[char[]])["c"] == 3 );
} }
+/
} }
unittest unittest
@ -1359,7 +1382,9 @@ unittest
unittest unittest
{ {
const x = Variant(42); const x = Variant(42);
auto y = x.get!(const int)(); auto y1 = x.get!(const int)();
// @@@BUG@@@
//auto y2 = x.get!(immutable int)();
} }
// test iteration // test iteration
@ -1371,4 +1396,5 @@ unittest
{ {
assert(i == ++j); assert(i == ++j);
} }
assert(j == 4);
} }