summaryrefslogtreecommitdiff
path: root/src/nbc/stream.sml
blob: 00b60abb22f8826c0113467c494429c833dfb0e4 (plain)
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
signature STREAM = sig
	type 'element stream
	val create:
		(unit -> ('element * 'element stream) option)
		-> 'element stream
	val empty: unit -> 'element stream
	val cons: 'element * 'element stream -> 'element stream
	val unfold:
		('seed -> ('fruit * 'seed) option)
		-> 'seed
		-> 'fruit stream
	val getItem: 'element stream -> ('element * 'element stream) option
	val isEmpty: 'element stream -> bool
	val fold:
		('element * 'accumulation -> 'accumulation)
		-> 'accumulation
		-> 'element stream
		-> 'accumulation
	val length: 'element stream -> int
	val rev: 'element stream -> 'element stream
	val map: ('input -> 'output) -> 'input stream -> 'output stream
	val mapPartial:
		('input -> 'output option)
		-> 'input stream
		-> 'output stream
	val app: ('element -> unit) -> 'element stream -> unit
	val toList: 'element stream -> 'element list
	val fromList: 'element list -> 'element stream
	val toVector: 'element stream -> 'element vector
	val fromVector: 'element vector -> 'element stream
	val fromVectorSlice: 'element VectorSlice.slice -> 'element stream
	val toArray: 'element stream -> 'element array
	val fromArray: 'element array -> 'element stream
	val fromArraySlice: 'element ArraySlice.slice -> 'element stream
	val fromString: string -> char stream
	val fromSubstring: Substring.substring -> char stream
	val toString: char stream -> string
	val fromTextInstream: TextIO.instream -> char stream
	val append: 'element stream * 'element stream -> 'element stream
	val concat: 'element stream stream -> 'element stream
	val hd: 'element stream -> 'element
	val tl: 'element stream -> 'element stream
	val find: ('element -> bool) -> 'element stream -> 'element option
	val filter: ('element -> bool) -> 'element stream -> 'element stream
	val exists: ('element -> bool) -> 'element stream -> bool
	val all: ('element -> bool) -> 'element stream -> bool
	val partition:
		('element -> bool)
		-> 'element stream
		-> 'element stream * 'element stream
	val take: ('element -> bool) -> 'element stream -> 'element stream
	val drop: ('element -> bool) -> 'element stream -> 'element stream
	val split:
		('element -> bool)
		-> 'element stream
		-> 'element stream * 'element stream
	val trim: 'element stream * int -> 'element stream
	val tokens:
		('element -> bool)
		-> 'element stream
		-> 'element stream stream
	val fields:
		('element -> bool)
		-> 'element stream
		-> 'element stream stream
end

structure Stream :> STREAM = struct
	datatype 'element stream =
		T of unit -> ('element * 'element stream) option
	fun create function = T function
	fun empty () = create (fn () => NONE)
	fun cons headAndTail = create (fn () => SOME headAndTail)
	fun unfold harvest seed = create (fn () =>
		case harvest seed of
			NONE => NONE
			| SOME (fruit, seed) => SOME (
				fruit
				, unfold harvest seed
			)
	)
	fun getItem (T function) = function ()
	fun fromList list = unfold List.getItem list
	fun toList stream = case getItem stream of
		NONE => nil
		| SOME (head, tail) => head :: toList tail
	fun fold accumulate accumulation stream = case getItem stream of
		NONE => accumulation
		| SOME (head, tail) =>
			fold accumulate (accumulate (head, accumulation)) tail
	fun length stream = fold (fn (_, count) => count + 1) 0 stream
	fun rev stream = fromList (fold op :: nil stream)
	fun map transform stream = unfold (fn stream => 
		case getItem stream of
			NONE => NONE
			| SOME (head, tail) => SOME (
				transform head
				, tail
			)
	) stream
	fun app execute stream =
		fold (fn (element, ()) => execute element) () stream
	fun fromVectorSlice slice = unfold VectorSlice.getItem slice
	fun fromVector vector = fromVectorSlice (VectorSlice.full vector)
	fun fromArraySlice slice = unfold ArraySlice.getItem slice
	fun fromArray array = fromArraySlice (ArraySlice.full array)
	fun fromSubstring substring = unfold Substring.getc substring
	fun fromString string = fromSubstring (Substring.full string)
	local
		fun withTabulate tabulate stream =
			let
				val position = ref stream
			in
				tabulate (
					length stream
					, fn _ => case getItem (!position) of
						NONE => raise Fail "Stream"
						| SOME (head, tail) => (
							position := tail
							; head
						)
				)
			end
	in
		fun toVector stream = withTabulate Vector.tabulate stream
		fun toArray stream = withTabulate Array.tabulate stream
		fun toString stream = withTabulate CharVector.tabulate stream
	end
	fun fromTextInstream instream =
		unfold TextIO.StreamIO.input1 (TextIO.getInstream instream)
	fun append (first, second) = create (fn () =>
		case getItem first of
			NONE => getItem second
			| SOME (head, tail) => SOME (
				head
				, append (tail, second)
			)
	)
	fun concat streams = create (fn () =>
		case getItem streams of
			NONE => NONE
			| SOME (head, tail) =>
				getItem (append (head, concat tail))
	)
	fun hd stream = case getItem stream of
		NONE => raise Empty
		| SOME (head, _) => head
	fun tl stream = case getItem stream of
		NONE => raise Empty
		| SOME (_, tail) => tail
	fun last stream = hd (rev stream)
	fun drop (stream, count) =
		if count < 0 then raise Subscript
		else if count = 0 then stream
		else case getItem stream of
			NONE => raise Subscript
			| SOME (_, tail) => drop (tail, count - 1)
	fun nth streamAndOffset = case getItem (drop streamAndOffset) of
		NONE => raise Subscript
		| SOME (head, _) => head
	fun mapPartial transform stream = create (fn () =>
		case getItem stream of
			NONE => NONE
			| SOME (head, tail) => case transform head of
				NONE => getItem (mapPartial transform tail)
				| SOME element => SOME (
					element
					, mapPartial transform tail
				)
	)
	fun find test stream = case getItem stream of
		NONE => NONE
		| SOME (head, tail) =>
			if test head then SOME head
			else find test tail
	fun filter test stream = unfold (fn stream =>
		case getItem stream of
			NONE => NONE
			| someHeadAndTail as (SOME (head, tail)) =>
				if test head then someHeadAndTail
				else getItem (filter test tail)
	) stream
	fun exists test stream = case find test stream of
		NONE => false
		| SOME _ => true
	fun all test stream = not (exists (not o test) stream)
	fun partition test stream =
		let
			val withResult = map (fn element =>
				(test element, element)
			) stream
		in (
			mapPartial (fn (result, element) =>
				if result then SOME element
				else NONE
			) withResult
			, mapPartial (fn (result, element) =>
				if result then NONE
				else SOME element
			) withResult
		) end
	fun take test stream = create (fn () =>
		case getItem stream of
			NONE => NONE
			| SOME (head, tail) =>
				if test head then SOME (head, take test tail)
				else NONE
	)
	fun drop test stream = create (fn () =>
		case getItem stream of
			NONE => NONE
			| someHeadAndTail as (SOME (head, tail)) =>
				if test head then getItem (drop test tail)
				else someHeadAndTail
	)
	fun split test stream = (take test stream, drop test stream)
	fun trim (stream, count) =
		if count <= 0 then stream
		else create (fn () =>
			case getItem stream of
				NONE => NONE
				| SOME (_, tail) =>
					getItem (trim (tail, count - 1))
		)
	fun isEmpty stream = case getItem stream of
		NONE => true
		| SOME _ => false
	fun tokens isSeparator stream = unfold (fn stream =>
		let
			val skipped = drop isSeparator stream
		in
			if isEmpty skipped then NONE
			else SOME (split (not o isSeparator) skipped)
		end
	) stream
	fun fields isSeparator stream = unfold (fn stream =>
		if isEmpty stream then NONE
		else SOME (
			take (not o isSeparator) stream
			, trim (drop (not o isSeparator) stream, 1)
		)
	) stream
end