1 module anansi.container.stack;
2 
3 import std.range, std.traits;
4 
5 /**
6  * Implements a 
7  */
8 struct Stack(T) {
9     public this(Stuff)(Stuff stuff) 
10         if (isInputRange!Stuff && 
11             isImplicitlyConvertible!(ElementType!Stuff, T)) {
12         _payload = array(stuff);
13     } 
14 
15     this(this) {
16         _payload = _payload.dup;
17     }
18 
19     void push(T value) {
20         _payload ~= value;
21     }
22 
23     void pop() 
24     in {
25         assert (_payload.length > 0, "Stack must not be empty to pop");
26     }
27     body {
28         _payload = _payload[0 .. $-1];
29         _payload.assumeSafeAppend();
30     }
31 
32     @property ref inout(T) front() inout 
33     in { 
34         assert (_payload.length > 0, "Stack must not be empty to read front");
35     }
36     body {
37         return _payload[$-1];
38     }
39 
40     @property bool empty() const {
41         return _payload.empty;
42     }
43 
44     @property size_t length() const {
45         return _payload.length;
46     }
47 
48     private T[] _payload;
49 }
50 
51 version (unittest) {
52     import std.conv, std.stdio;
53 }
54 
55 unittest {
56     writeln("Stack: Default construction must yield an empty stack.");
57     auto s = Stack!int();
58     assert(s.empty, "A default-constructed stack should be empty");
59     assert(s.length == 0, "A default-constructed stack should have a length of 0");
60 }
61 
62 unittest {
63     writeln("Stack: Construction from a range should yield a non-empty stack.");
64     auto s = Stack!int([1, 2, 3, 4, 5, 6, 7, 8, 9]);
65     assert (!s.empty, "Range-constructed stack should not be empty");
66     assert (s.length == 9, "Range-constructed stack should have length 9.");
67 }
68 
69 unittest {
70     writeln("Stack: Enumerating a stack should yield items in reverse order.");
71     auto s = Stack!int([1, 2, 3, 4, 5, 6, 7, 8, 9]);
72     int expected = 9;
73     while (!s.empty) {
74         assert (s.front == expected, 
75             "Expected " ~ to!string(expected) ~ 
76             ", got " ~ to!string(s.front));
77         s.pop();
78         expected--;
79     }
80 }
81 
82 unittest {
83     writeln("Stack: Adding an item to a stack should increase its length.");
84     auto s = Stack!int();
85     s.push(42);
86     assert (!s.empty, "Stack should not be empty after push.");
87     assert (s.length == 1, "Stack should not be empty after push.");
88 }
89 
90 unittest {
91     writeln("Stack: Popping an item from a stack should decrease its length.");
92     auto s = Stack!int([1, 2, 3, 4, 5]);
93     auto n = s.length;
94     s.pop();
95     assert (s.length == (n-1), "Stack length should be reduced by 1 after a pop.");
96 
97 }