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
{
scope(failure) writeln("Unittest failed at line ", __LINE__);
int[] arr1 = [ 1, 2, 3, 4 ];
int[] arr2 = [ 5, 6 ];
auto squares = map!("a * a")(arr1);
@ -319,7 +320,7 @@ unittest
a = [ 1, 2, 3, 4, 5 ];
// Stringize with commas
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
@ -529,6 +530,14 @@ unittest
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
/**
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)
if (!is(typeof(ElementType!Range.init == Separator.init)))
{
private:
Range _input;
@ -675,17 +685,8 @@ private:
size_t _frontLength = size_t.max;
static if (isBidirectionalRange!Range)
size_t _backLength = size_t.max;
enum bool separatorIsRange =
!is(typeof(ElementType!Range.init == _separator));
static if (separatorIsRange)
{
size_t separatorLength() { return _separator.length; }
}
else
{
enum size_t separatorLength = 1;
}
size_t separatorLength() { return _separator.length; }
void ensureFrontLength()
{
@ -693,24 +694,21 @@ private:
assert(!_input.empty);
// compute front 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()
{
if (_backLength != _backLength.max) return;
static if (isBidirectionalRange!Range)
if (_backLength != _backLength.max) return;
assert(!_input.empty);
// compute back length
static if (separatorIsRange)
static if (isBidirectionalRange!Range)
{
_backLength = _input.length -
find(retro(_input), retro(_separator)).length;
}
else
{
_backLength = _input.length -
find(retro(_input), _separator).length;
}
}
public:
@ -741,7 +739,8 @@ public:
// done, there's no separator in sight
_input = _input[_frontLength .. _frontLength];
_frontLength = _frontLength.max;
_backLength = _backLength.max;
static if (isBidirectionalRange!Range)
_backLength = _backLength.max;
return;
}
if (_frontLength + separatorLength == _input.length)
@ -750,7 +749,8 @@ public:
// an empty item right after this.
_input = _input[_input.length .. _input.length];
_frontLength = 0;
_backLength = 0;
static if (isBidirectionalRange!Range)
_backLength = 0;
return;
}
// Normal case, pop one item and the separator, get ready for
@ -761,36 +761,124 @@ public:
}
// 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()
{
ensureBackLength;
return _input[_input.length - _backLength .. _input.length];
if (_backLength == _backLength.max)
{
while (!_input.empty && _input.back == _separator) _input.popBack();
computeBack();
}
assert(_backLength <= _input.length, text(_backLength));
return _input[$ - _backLength .. $];
}
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];
computeBack();
enforce(_backLength <= _input.length);
_input = _input[0 .. $ - _backLength];
_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
@ -824,8 +912,9 @@ unittest
}
assert(i == w.length);
// Now go back
//auto s = splitter(a, 0);
foreach_reverse (e; splitter(a, 0))
auto s2 = splitter(a, 0);
foreach_reverse (e; s2)
{
assert(i > 0);
assert(equal(e, w[--i]), text(e));
@ -1415,7 +1504,7 @@ is ignored.
&& needle[virtual_begin - 1] == needle[$ - portion - 1])
return 0;
invariant delta = portion - ignore;
immutable delta = portion - ignore;
return equal(needle[needle.length - delta .. needle.length],
needle[virtual_begin .. virtual_begin + delta]);
}
@ -1475,7 +1564,7 @@ public:
/// Ditto
BoyerMooreFinder!(binaryFun!(pred), Range) boyerMooreFinder
(alias pred = "a == b", Range)
(Range needle) if (isRandomAccessRange!(Range))
(Range needle) if (isRandomAccessRange!(Range) || isSomeString!Range)
{
return typeof(return)(needle);
}
@ -2405,13 +2494,18 @@ struct Levenshtein(Range, alias equals, CostType = size_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)
{
auto sfront = s.front;
s.popFront();
auto tt = t;
foreach (j; 1 .. cols)
{
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 cDel = _matrix[i - 1][j] + _deletionIncrement;
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)
@ -2528,7 +2622,7 @@ assert(levenshteinDistance!("toupper(a) == toupper(b)")
*/
size_t levenshteinDistance(alias equals = "a == b", Range1, Range2)
(Range1 s, Range2 t)
if (isRandomAccessRange!(Range1) && isRandomAccessRange!(Range2))
if (isForwardRange!(Range1) && isForwardRange!(Range2))
{
Levenshtein!(Range1, binaryFun!(equals), uint) lev;
return lev.distance(s, t);
@ -2549,7 +2643,7 @@ assert(equals(p.field[1], "nrrnsnnn"));
Tuple!(size_t, EditOp[])
levenshteinDistanceAndPath(alias equals = "a == b", Range1, Range2)
(Range1 s, Range2 t)
if (isRandomAccessRange!(Range1) && isRandomAccessRange!(Range2))
if (isForwardRange!(Range1) && isForwardRange!(Range2))
{
Levenshtein!(Range, binaryFun!(equals)) lev;
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
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
want to use $(D copy(filter!(pred)(a), b)).
@ -2699,7 +2793,7 @@ assert(arr == [ 3, 2, 1 ]);
----
*/
void reverse(Range)(Range r)
//if (isBidirectionalRange!(Range) && hasSwappableElements!(Range))
if (isBidirectionalRange!(Range) && hasSwappableElements!(Range))
{
while (!r.empty)
{

View file

@ -15,7 +15,7 @@ module std.array;
import std.c.stdio;
import core.memory;
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;
/**
@ -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
{
@ -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 "
~ T.stringof);
@ -203,6 +203,25 @@ unittest
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
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
{
@ -232,6 +255,63 @@ unittest
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
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");
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
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
@ -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
@ -521,21 +649,24 @@ managed array can accommodate before triggering a reallocation).
Appends one item to the managed array.
*/
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
encode!(T)(item, this);
Unqual!T[T.sizeof == 1 ? 4 : 2] encoded;
auto len = std.utf.encode(encoded, item);
put(encoded[0 .. len]);
}
else
{
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
pArray.ptr[pArray.length] = item;
*pArray = pArray.ptr[0 .. pArray.length + 1];
pArray.ptr[len] = item;
*pArray = pArray.ptr[0 .. len + 1];
}
else
{
@ -550,34 +681,21 @@ Appends one item to the managed array.
Appends an entire range to the managed array.
*/
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(*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)
static if (is(typeof(*pArray ~= items)))
{
if (!pArray) pArray = (new typeof(*pArray)[1]).ptr;
*pArray ~= items;
}
else
{
//pragma(msg, Range.stringof);
// 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+/";
/**
* 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");
}
/**
* Returns the number of bytes needed to decode an encoded string of this
* length.
@ -173,7 +171,6 @@ uint decodeLength(uint elen)
return elen / 4 * 3;
}
/**
* Decodes str[] and places the result in buf[].
* Params:
@ -203,7 +200,8 @@ body
uint arrayIndex(char ch)
out(result)
{
assert(ch == array[result]);
//@@BUG@@@ http://d.puremagic.com/issues/show_bug.cgi?id=3667");
//assert(ch == array[result]);
}
body
{
@ -221,7 +219,6 @@ body
assert(0);
}
if(!estr.length)
return buf[0 .. 0];

View file

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

View file

@ -336,7 +336,8 @@ invariant(T[U]) assumeUnique(T, U)(ref T[U] array)
return result;
}
unittest
// @@@BUG@@@
version(none) unittest
{
int[string] arr = ["a":1];
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
converted by calling $(D to!T).
*/
T to(T, S)(S s, in T leftBracket = "[", in T separator = ", ",
in T rightBracket = "]")
T to(T, S)(S s, in T leftBracket = "", in T separator = " ",
in T rightBracket = "")
if (isSomeString!(T) && !isSomeString!(S) && isArray!(S))
{
alias Unqual!(ElementType!(T)) Char;
alias Unqual!(typeof(T.init[0])) Char;
// array-to-string conversion
static if (is(S == void[])
|| is(S == const(void)[]) || is(S == invariant(void)[])) {
@ -171,7 +171,7 @@ if (isSomeString!(T) && !isSomeString!(S) && isArray!(S))
result.put(leftBracket);
foreach (i, e; s) {
if (i) result.put(separator);
result.put(to!(T)(e));
result.put(to!T(e));
}
result.put(rightBracket);
return cast(T) result.data;
@ -180,11 +180,13 @@ if (isSomeString!(T) && !isSomeString!(S) && isArray!(S))
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 ];
//writeln(to!string(a));
assert(to!string(a) == "[1.5, 2.5]");
short[] b = [ 1, 3, 5 ];
assert(to!string(b) == "[1, 3, 5]");
assert(to!string(a) == "1.5 2.5");
}
/**
@ -196,7 +198,7 @@ T to(T, S)(S s, in T leftBracket = "[", in T keyval = ":",
in T separator = ", ", in T rightBracket = "]")
if (isAssociativeArray!(S) && isSomeString!(T))
{
alias Unqual!(ElementType!(T)) Char;
alias Unqual!(typeof(T.init[0])) Char;
Appender!(Char[]) result;
// hash-to-string conversion
result.put(leftBracket);
@ -261,7 +263,7 @@ if (is(S == struct) && isSomeString!(T) && !is(typeof(&S.init.toString)))
{
// ok, attempt to forge the tuple
t = cast(typeof(t)) &s;
alias Unqual!(ElementType!(T)) Char;
alias Unqual!(typeof(T.init[0])) Char;
Appender!(Char[]) app;
app.put(left);
foreach (i, e; t.field)
@ -874,7 +876,7 @@ unittest {
// test array to string conversion
foreach (T ; AllNumerics) {
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
// enum Testing { Test1, Test2 };
@ -1096,13 +1098,8 @@ if (!isSomeString!Source && isFloatingPoint!Target)
Target parse(Target, Source)(ref Source s)
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
version (Windows)
//version (Windows)
{
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);
char* endptr;
static if (is(Target == float))
auto f = strtof(sz, &endptr);
auto f = strtof(sz.ptr, &endptr);
else static if (is(Target == double))
auto f = strtod(sz, &endptr);
auto f = strtod(sz.ptr, &endptr);
else static if (is(Target == real))
auto f = strtold(sz, &endptr);
auto f = strtold(sz.ptr, &endptr);
else
static assert(false);
if (getErrno() == ERANGE)
goto Lerr;
assert(endptr);
if (endptr == sz)
if (endptr == sz.ptr)
{
// no progress
goto Lerr;
}
s = s[endptr - sz .. $];
s = s[endptr - sz.ptr .. $];
return f;
Lerr:
conv_error!(Source, Target)(s);
@ -1573,7 +1595,6 @@ unittest
try
{
i = to!short(errors[j]);
printf("i = %d\n", i);
}
catch (Error e)
{
@ -1637,7 +1658,7 @@ unittest
try
{
i = to!ushort(errors[j]);
printf("i = %d\n", i);
debug(conv) printf("i = %d\n", i);
}
catch (Error e)
{
@ -1706,7 +1727,7 @@ unittest
try
{
i = to!byte(errors[j]);
printf("i = %d\n", i);
debug(conv) printf("i = %d\n", i);
}
catch (Error e)
{
@ -1770,7 +1791,7 @@ unittest
try
{
i = to!ubyte(errors[j]);
printf("i = %d\n", i);
debug(conv) printf("i = %d\n", i);
}
catch (Error e)
{
@ -2506,8 +2527,9 @@ T to(T, S)(S input)
if (std.typetuple.staticIndexOf!(Unqual!S, uint, ulong) >= 0 && isSomeString!T)
{
Unqual!S value = input;
alias Unqual!(ElementType!T) Char;
static if (is(ElementType!T == const) || is(ElementType!T == immutable))
alias Unqual!(typeof(T.init[0])) Char;
static if (is(typeof(T.init[0]) == const) ||
is(typeof(T.init[0]) == immutable))
{
if (value < 10)
{
@ -2522,21 +2544,23 @@ if (std.typetuple.staticIndexOf!(Unqual!S, uint, ulong) >= 0 && isSomeString!T)
else
auto maxlength = (value > uint.max ? S.sizeof : uint.sizeof) * 3;
Char [] result;
if (__ctfe) result = new Char[maxlength];
Char[] result;
if (__ctfe)
result = new Char[maxlength];
else
result = cast(Char[])
GC.malloc(Char.sizeof * maxlength, GC.BlkAttr.NO_SCAN)
[0 .. Char.sizeof * maxlength];
uint ndigits = 0;
while (value)
do
{
const c = cast(Char) ((value % 10) + '0');
value /= 10;
ndigits++;
result[$ - ndigits] = c;
}
while (value);
return cast(T) result[$ - ndigits .. $];
}
@ -2544,20 +2568,22 @@ unittest
{
assert(wtext(int.max) == "2147483647"w);
assert(wtext(int.min) == "-2147483648"w);
assert(to!string(0L) == "0");
}
/// $(D char), $(D wchar), $(D dchar) to a string type.
T to(T, S)(S c) if (staticIndexOf!(Unqual!S, char, wchar, dchar) >= 0
&& isSomeString!(T))
{
static if (ElementType!T.sizeof >= S.sizeof)
alias typeof(T.init[0]) Char;
static if (Char.sizeof >= S.sizeof)
{
return [ c ];
}
else
{
Unqual!(ElementType!T)[] result;
encode(result, cast(dchar) c);
Unqual!Char[] result;
encode(result, c);
return cast(T) result;
}
}
@ -2588,7 +2614,7 @@ if (staticIndexOf!(Unqual!S, int, long) >= 0 && isSomeString!T)
{
if (value >= 0)
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
// data is supposed to use allocation in all cases

View file

@ -1749,7 +1749,7 @@ body
// EncoderInstance!(E).encode(c,buffer);
// }
/**
/*
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.
*/

View file

@ -1566,7 +1566,7 @@ struct FormatInfo
bool, "flHash", 1,
ubyte, "", 3));
/* For arrays only: the trailing */
const(char)[] innerTrailing;
const(char)[] innerTrailing, trailing;
/*
* Given a string format specification fmt, parses a format
@ -1577,6 +1577,7 @@ struct FormatInfo
{
FormatInfo result;
if (fmt.empty) return result;
scope(success) result.trailing = to!(const(char)[])(fmt);
size_t i = 0;
for (;;)
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;
for (;;)
@ -2882,6 +2884,42 @@ T unformat(T, Range)(ref Range input, FormatInfo spec) if (isArray!T)
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)
{
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
enforce(input.length >= T.sizeof);
enforce(ElementType!(Range).sizeof == 1);
enforce(isSomeString!Range || ElementType!(Range).sizeof == 1);
union X
{
char[T.sizeof] raw;
ubyte[T.sizeof] raw;
T typed;
}
X x;
foreach (i; 0 .. T.sizeof)
{
x.raw[i] = input.front;
input.popFront;
static if (isSomeString!Range)
{
x.raw[i] = input[0];
input = input[1 .. $];
}
else
{
x.raw[i] = input.front;
input.popFront();
}
}
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;
int depth = -1;
char next = 0;
dchar next = 0;
int line = 1, pos = 1;
void error(string msg) {
throw new JSONException(msg, line, pos);
}
char peekChar() {
dchar peekChar() {
if(!next) {
if(json.empty()) return '\0';
next = json.front();
@ -84,10 +84,10 @@ JSONValue parseJSON(T)(in T json, int maxDepth = -1) if(isInputRange!T) {
while(isspace(peekChar())) next = 0;
}
char getChar(bool SkipWhitespace = false)() {
dchar getChar(bool SkipWhitespace = false)() {
static if(SkipWhitespace) skipWhitespace();
char c = void;
dchar c = void;
if(next) {
c = next;
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) {
static if(SkipWhitespace) skipWhitespace();
char c2 = getChar();
static if(!CaseSensitive) c2 = cast(char)tolower(c2);
auto c2 = getChar();
static if(!CaseSensitive) c2 = tolower(c2);
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();
char c2 = peekChar();
static if(!CaseSensitive) c2 = cast(char)tolower(c2);
auto c2 = peekChar();
static if (!CaseSensitive) c2 = tolower(c2);
if(c2 != c) return false;
@ -139,7 +140,7 @@ JSONValue parseJSON(T)(in T json, int maxDepth = -1) if(isInputRange!T) {
case '\\':
getChar();
char c = getChar();
auto c = getChar();
switch(c) {
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':
dchar val = 0;
foreach_reverse(i; 0 .. 4) {
char hex = cast(char)toupper(getChar());
auto hex = toupper(getChar());
if(!isxdigit(hex)) error("Expecting hex character");
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;
default:
char c = getChar();
appendJSONChar(&str, c, getChar(), &error);
auto c = getChar();
appendJSONChar(&str, c, &error);
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.");
char c = getChar!true();
auto c = getChar!true();
switch(c) {
case '{':
@ -314,8 +315,8 @@ string toJSON(in JSONValue* root) {
void toString(string str) {
json.put('"');
for(int i; i != str.length; i++) {
switch(str[i]) {
foreach (dchar c; str) {
switch(c) {
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 '\t': json.put("\\t"); break;
default:
appendJSONChar(&json, str[i], str[++i],
appendJSONChar(&json, c,
(string msg){throw new JSONException(msg);});
}
}
@ -388,23 +389,25 @@ string toJSON(in JSONValue* root) {
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)
{
int stride = UTFStride((&c)[0 .. 1], 0);
if(stride == 1) {
if(iscntrl(c)) error("Illegal control character.");
dst.put(c);
}
else {
char[6] utf = void;
utf[0] = c;
foreach(i; 1 .. stride) utf[i] = next;
size_t index = 0;
if(iscntrl(toUnicode(utf[0 .. stride], index)))
error("Illegal control character");
dst.put(utf[0 .. stride]);
}
if(iscntrl(c)) error("Illegal control character.");
dst.put(c);
// int stride = UTFStride((&c)[0 .. 1], 0);
// if(stride == 1) {
// if(iscntrl(c)) error("Illegal control character.");
// dst.put(c);
// }
// else {
// char[6] utf = void;
// utf[0] = c;
// foreach(i; 1 .. stride) utf[i] = next;
// size_t index = 0;
// 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 =
(isBidirectionalRange!(R) || isInfinite!(R))
&& is(typeof(
{
R r;
auto e = r[1]; // can index
}()));
&& is(typeof(R.init[1]))
&& !isNarrowString!R;
}
unittest
@ -295,9 +292,9 @@ unittest
{
enum XYZ : string { a = "foo" };
auto x = front(XYZ.a);
static assert(is(ElementType!(XYZ) : char));
static assert(is(ElementType!(XYZ) : dchar));
immutable char[3] a = "abc";
static assert(is(ElementType!(typeof(a)) : char));
static assert(is(ElementType!(typeof(a)) : dchar));
int[] i;
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!(int[]));
static assert(hasSwappableElements!(char[]));
//static assert(hasSwappableElements!(char[]));
}
/**
@ -371,7 +368,8 @@ other ranges may be infinite.
*/
template hasLength(R)
{
enum bool hasLength = is(typeof(R.init.length) : ulong);
enum bool hasLength = is(typeof(R.init.length) : ulong) &&
!isNarrowString!R;
}
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)
if (isInputRange!(Range))
{
static if (hasLength!(Range))
static if (isRandomAccessRange!Range && hasLength!Range)
{
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).
*/
static if (isRandomAccessRange!(R) && hasLength!(R))
ref ElementType!(R) opIndex(uint n)
ref ElementType!R opIndex(uint n)
{
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
hasLength!(R)).
*/
static if (hasLength!(R))
static if (hasLength!R || isNarrowString!R)
size_t length()
{
return _input.length;
@ -1180,7 +1178,7 @@ offers random access and $(D length), $(D Take) offers them as well.
Example:
----
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[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))
{
private:
size_t _maxAvailable;
R _input;
size_t _maxAvailable;
enum bool byRef = is(typeof(&(R.init[0])));
public:
@ -1293,20 +1291,18 @@ public:
}
});
}
Take opSlice() { return this; }
}
/// 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
{
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[4] == 5);
assert(equal(s, [ 1, 2, 3, 4, 5 ][]));
@ -1394,7 +1390,7 @@ unittest
/**
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
{
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,
repeat(value))).
Replicates $(D value) exactly $(D n) times. Equivalent to $(D
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
{
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:
----
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.
@ -1543,12 +1539,12 @@ Cycle!(R) cycle(R)(ref R input, size_t index = 0) if (isStaticArray!R)
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 ];
static assert(isStaticArray!(typeof(a)));
auto c = cycle(a);
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]
auto fib = recurrence!("a[n-1] + a[n-2]")(1, 1);
// 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
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)
@ -2021,15 +2017,15 @@ version(none) unittest
{
auto fib = recurrence!("a[n-1] + a[n-2]")(1, 1);
int[] witness = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55 ];
//foreach (e; take(10, fib)) writeln(e);
assert(equal(take(10, fib), witness));
foreach (e; take(10, fib)) {}//writeln(e);
//foreach (e; take(fib, 10)) writeln(e);
assert(equal(take(fib, 10), witness));
foreach (e; take(fib, 10)) {}//writeln(e);
//writeln(s.front);
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][]) );
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]",
// Tuple!(int, int)) Gen;
// Gen x = Gen(tuple(0, 5));
// foreach (e; take(15, x))
// foreach (e; take(x, 15))
// {}//writeln(e);
auto y = Sequence!("a.field[0] + n * a.field[1]", Tuple!(int, int))
(tuple(0, 4));
//@@BUG
//auto y = sequence!("a.field[0] + n * a.field[1]")(0, 4);
//foreach (e; take(15, y))
//foreach (e; take(y, 15))
{}//writeln(e);
}
@ -2163,9 +2159,8 @@ if (is(typeof((E.init - B.init) + 1 * S.init)))
"; count=", count));
assert(!myless(count * step, end - begin), text("begin=", begin,
"; end=", end, "; step=", step, "; count=", count));
return typeof(return)(count,
typeof(return).Source(
Tuple!(CommonType!(B, E), S)(begin, step), 0u));
return typeof(return)(typeof(return).Source(
Tuple!(CommonType!(B, E), S)(begin, step), 0u), count);
}
/// Ditto

View file

@ -1516,7 +1516,7 @@ Returns the number of parenthesized captures
}
/// Ditto
Regex!(Unqual!(ElementType!(String))) regex(String)
Regex!(Unqual!(typeof(String.init[0]))) regex(String)
(String pattern, string flags = null)
{
return typeof(return)(pattern, flags);
@ -1528,9 +1528,9 @@ stores the matching state and can be inspected and iterated.
*/
struct RegexMatch(Range = string)
{
alias .ElementType!(Range) E;
alias typeof(Range.init[0]) E;
// Engine
alias .Regex!(Unqual!(.ElementType!(Range))) Regex;
alias .Regex!(Unqual!E) Regex;
private alias Regex.regmatch_t regmatch_t;
/**
@ -1623,34 +1623,35 @@ void main()
unittest
{
uint i;
foreach(m; match(to!(Range)("abcabcabab"), regex(to!(Range)("ab"))))
{
++i;
assert(m.hit == "ab");
//writefln("%s[%s]%s", m.pre, m.hit, m.post);
}
assert(i == 4);
// @@@BUG@@@ This doesn't work if a client module uses -unittest
// uint i;
// foreach (m; match(to!(Range)("abcabcabab"), regex(to!(Range)("ab"))))
// {
// ++i;
// assert(m.hit == "ab");
// //writefln("%s[%s]%s", m.pre, m.hit, m.post);
// }
// assert(i == 4);
}
unittest
{
// @@@
debug(regex) printf("regex.search.unittest()\n");
// @@@BUG@@@ This doesn't work if a client module uses -unittest
// debug(regex) printf("regex.search.unittest()\n");
int i;
//foreach(m; RegexMatch("ab").search("abcabcabab"))
foreach(m; .match("abcabcabab", regex("ab")))
{
auto s = std.string.format("%s[%s]%s", m.pre, m.hit, m.post);
if (i == 0) assert(s == "[ab]cabcabab");
else if (i == 1) assert(s == "abc[ab]cabab");
else if (i == 2) assert(s == "abcabc[ab]ab");
else if (i == 3) assert(s == "abcabcab[ab]");
else assert(0);
i++;
}
assert(i == 4);
// int i;
// //foreach(m; RegexMatch("ab").search("abcabcabab"))
// foreach(m; .match("abcabcabab", regex("ab")))
// {
// auto s = std.string.format("%s[%s]%s", m.pre, m.hit, m.post);
// if (i == 0) assert(s == "[ab]cabcabab");
// else if (i == 1) assert(s == "abc[ab]cabab");
// else if (i == 2) assert(s == "abcabc[ab]ab");
// else if (i == 3) assert(s == "abcabcab[ab]");
// else assert(0);
// i++;
// }
// assert(i == 4);
}
struct Captures
@ -1716,14 +1717,15 @@ foreach (m; match("abracadabra", "(.)a(.)"))
unittest
{
Appender!(char[]) app;
foreach (m; match("abracadabra", "(.)a(.)"))
{
assert(m.captures.length == 3);
foreach (c; m.captures)
app.put(c), app.put(';');
}
assert(app.data == "rac;r;c;dab;d;b;");
// @@@BUG@@@ This doesn't work if a client module uses -unittest
// Appender!(char[]) app;
// foreach (m; match("abracadabra", "(.)a(.)"))
// {
// assert(m.captures.length == 3);
// foreach (c; m.captures)
// app.put(c), app.put(';');
// }
// assert(app.data == "rac;r;c;dab;d;b;");
}
/*******************
@ -1821,26 +1823,6 @@ Returns $(D hit) (converted to $(D string) if necessary).
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.
* 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;
// First character optimization
Unqual!(ElementType!(Range)) firstc = 0;
Unqual!(typeof(Range.init[0])) firstc = 0;
if (engine.program[0] == engine.REchar)
{
firstc = engine.program[1];
@ -1922,13 +1904,6 @@ Returns $(D hit) (converted to $(D string) if necessary).
*/
//alias test opEquals;
unittest
{
//@@@
assert(!match("abc", regex(".b.")).empty);
assert(match("abc", regex(".b..")).empty);
}
private bool chr(ref uint si, E c)
{
for (; si < input.length; si++)
@ -2697,6 +2672,30 @@ and, using the format string, generate and return a new string.
}
} // 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).
*/
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);
}
@ -2789,7 +2788,7 @@ assert(replace("noon", regex("^n"), "[$&]") == "[n]oon");
*/
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);
}
@ -2915,7 +2914,7 @@ struct Splitter(Range)
{
Range _input;
size_t _offset;
alias Regex!(Unqual!(ElementType!(Range))) Rx;
alias Regex!(Unqual!(typeof(Range.init[0]))) Rx;
// Rx _rx;
RegexMatch!(Range) _match;

View file

@ -20,9 +20,9 @@ Distributed under the Boost Software License, Version 1.0.
module std.stdio;
public import core.stdc.stdio;
private import std.stdiobase;
private import core.memory, core.stdc.errno, core.stdc.stddef,
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,
/*std.metastrings,*/ std.range, std.string, std.traits, std.typecons,
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
by $(D buf), whereas $(D buf = stdin.readln()) makes a new memory allocation
with every line. */
size_t readln(ref char[] buf, dchar terminator = '\n')
S readln(S = string)(dchar terminator = '\n')
{
enforce(p && p.handle, "Attempt to read from an unopened file.");
return readlnImpl(p.handle, buf, terminator);
}
/** ditto */
string readln(dchar terminator = '\n')
{
char[] buf;
Unqual!(typeof(S.init[0]))[] buf;
readln(buf, terminator);
return assumeUnique(buf);
}
/** ditto */
// TODO: optimize this
size_t readln(ref wchar[] buf, dchar terminator = '\n')
unittest
{
string s = readln(terminator);
if (!s.length) return 0;
buf.length = 0;
foreach (wchar c; s)
std.file.write("deleteme", "hello\nworld\n");
scope(exit) std.file.remove("deleteme");
foreach (C; Tuple!(char, wchar, dchar).Types)
{
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;
}
/** ditto */
// TODO: fold this together with wchar
size_t readln(ref dchar[] buf, dchar terminator = '\n')
unittest
{
string s = readln(terminator);
if (!s.length) return 0;
buf.length = 0;
foreach (dchar c; s)
std.file.write("deleteme", "hello\n\rworld\nhow\n\rare ya");
auto witness = [ "hello\n\r", "world\nhow\n\r", "are ya" ];
scope(exit) std.file.remove("deleteme");
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)
@ -882,7 +923,10 @@ $(D Range) that locks the file and allows fast writing to it.
/// Range primitive implementations.
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));
// writeln("typeof(A.init[0]) = ", typeof(A.init[0]),
// ", ElementType!A = ", ElementType!A);
@ -1053,7 +1097,8 @@ $(D Range) that locks the file and allows fast writing to it.
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);
_crt = getc(cast(FILE*) _f.p.handle);
}
@ -1176,16 +1221,16 @@ unittest
scope(failure) printf("Failed test at line %d\n", __LINE__);
void[] buf;
write(buf);
// // test write
// test write
string file = "dmd-build-test.deleteme.txt";
auto f = File(file, "w");
scope(exit) { std.file.remove(file); }
f.write("Hello, ", "world number ", 42, "!");
f.close;
assert(cast(char[]) std.file.read(file) == "Hello, world number 42!");
// scope(exit) { std.file.remove(file); }
f.write("Hello, ", "world number ", 42, "!");
f.close;
assert(cast(char[]) std.file.read(file) == "Hello, world number 42!");
// // test write on stdout
auto saveStdout = stdout;
scope(exit) stdout = saveStdout;
//auto saveStdout = stdout;
//scope(exit) stdout = saveStdout;
//stdout.open(file, "w");
Object 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()
{
//printf("std_stdio_static_this()\n");
//Bind stdin, stdout, stderr
__gshared File.Impl stdinImpl;
stdinImpl.handle = core.stdc.stdio.stdin;

View file

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

View file

@ -952,8 +952,7 @@ Detect whether T is one of the built-in string types
template isSomeString(T)
{
enum isSomeString = is(T : const(char[]))
|| is(T : const(wchar[])) || is(T : const(dchar[]));
enum isSomeString = isNarrowString!T || is(T : const(dchar[]));
}
unittest
@ -969,6 +968,24 @@ unittest
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
*/

View file

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

View file

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