/
rope.pony
132 lines (113 loc) · 3.54 KB
/
rope.pony
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
class val Rope is (_RopeSegment & Stringable)
let _left: _RopeSegment
let _right: _RopeSegment
let _weight: USize
new val create(l: _RopeSegment = RopeNone, r: _RopeSegment = RopeNone) =>
_left = l
_right = r
_weight = l.size()
fun size(): USize =>
_weight + _right.size()
fun apply(i: USize): U8? =>
if _weight <= i
then _right(i - _weight)?
else _left(i)?
end
fun slice(i: USize, j: USize): Rope =>
if j <= i then return Rope(RopeNone) end
match ((_weight <= i), (_weight < j))
| (true, true) => _slice_segment(_right, i - _weight, j - _weight)
| (false, false) => _slice_segment(_left, i, j)
| (false, true) => Rope(_slice_segment(_left, i, _weight),
_slice_segment(_right, 0, j - _weight))
else Rope(RopeNone)
end
fun tag _slice_segment(seg': _RopeSegment, i: USize, j: USize): Rope =>
match seg'
| let seg: _RopeSegmentSlice => seg.slice(i, j)
| let seg: Rope => seg.slice(i, j)
| let seg: RopeNone => Rope(RopeNone)
else Rope(_RopeSegmentSlice(seg', i, j))
end
fun values(): Iterator[U8] =>
object is Iterator[U8]
let _left_values: Iterator[U8] = _left.values()
let _right_values: Iterator[U8] = _right.values()
fun ref has_next(): Bool => _left_values.has_next()
or _right_values.has_next()
fun ref next(): U8? => try _left_values.next()?
else _right_values.next()? end
end
fun val add(that: _RopeSegment): Rope =>
// TODO: balance the tree here when helpful
if _right is RopeNone then
if _left is RopeNone then
Rope(that)
else
Rope(_left, that)
end
else
Rope(this, that)
end
fun _copy_into_string(dest: String iso): String iso^ =>
_copy_seg_into_string(_right, _copy_seg_into_string(_left, consume dest))
fun tag _copy_seg_into_string(source: _RopeSegment, dest: String iso):
String iso^
=>
match source
| let s: Rope => s._copy_into_string(consume dest)
| let s: RopeNone => consume dest
else dest.append(source); consume dest
end
fun string(): String iso^ =>
let len = size()
let out = recover iso String(len) end
_copy_into_string(consume out)
fun find(sub: _RopeSegment): (Bool, USize) =>
"""
Try to find `sub` in this rope.
If found, returns a tuple with the first element being `true` and the second element
being the starting index of `sub` in this.
```pony
let rope = Rope + "abc" + "def"
match rope.find("cd")
| (true, let cd_index: USize) => "found"
| (false, _) => "not found"
end
```
"""
var i = USize(0)
let this_size = size()
let sub_size = sub.size()
try
while i < this_size do
var j = USize(0)
let same: Bool =
while (j < sub_size) do
if apply(i + j)? != sub.apply(j)? then
break false
end
j = j + 1
true
else
false
end
if same then
return (true, i)
end
i = i + 1
end
else
(false, USize(0))
end
(false, USize(0))
fun drop(amount: USize): Rope =>
"""
Drop the first `amount` elements and return the rest as a new Rope.
"""
slice(amount, size())
fun take(amount: USize): Rope =>
"""
Take the first `amount` elements and return them as a new Rope, dropping the rest.
"""
slice(0, amount)