File: file-iterators.lisp

package info (click to toggle)
cl-containers 20140211-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 1,076 kB
  • ctags: 1,386
  • sloc: lisp: 8,330; makefile: 14
file content (301 lines) | stat: -rw-r--r-- 9,656 bytes parent folder | download | duplicates (4)
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
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
(in-package #:containers)

#|
- file lines
file forms
string as characters
string as words
string as, e.g., paragraphs

stemming
|#

(defclass* basic-stream-iterator (forward-iterator)
  ((stream nil i :reader iterator-stream)
   (close? nil r)))

(defmethod initialize-instance :after
    ((object basic-stream-iterator) &key container)
  (setf (values (slot-value object 'stream)
                (slot-value object 'close?))
        (open-file-for-iterator object container))
  
  (advance object)
  ;; if garbage collected close the stream
  (funcall-if-exists 'care-when-finalized 'mopu object))

(defmethod finish ((iterator basic-stream-iterator))
  (when (and (close? iterator)
             (streamp (iterator-stream iterator))
             (open-stream-p (iterator-stream iterator)))
    (close (iterator-stream iterator))
    (setf (slot-value iterator 'close?) nil)))

(defmethod open-file-for-iterator
    ((object basic-stream-iterator) (filename string))
  (values (open filename :if-does-not-exist :error
                :direction :input)
          t))

(defmethod open-file-for-iterator
    ((object basic-stream-iterator) (filename pathname))
  (open-file-for-iterator object (namestring filename)))

(defmethod open-file-for-iterator
    ((object basic-stream-iterator) (filename stream))
  (values filename nil))


;;; file-iterator
;;;
;;;?? assume that someone else is handling buffering for now...

(defclass* file-iterator (basic-stream-iterator)
  ((current-char nil r)))

(defmethod base-class-for-iteratee ((container pathname))
  'file-iterator)

(defmethod move ((iterator file-iterator) (direction (eql :forward)))
  (advance iterator))

(defmethod advance ((iterator file-iterator))
  (setf (slot-value iterator 'current-char) 
        (read-char (iterator-stream iterator) nil :eof)))

(defmethod current-element ((iterator file-iterator))
  (current-char iterator))

(defmethod current-element-p ((iterator file-iterator))
  (and (call-next-method)
       (not (eq (current-char iterator) :eof))))

(defmethod move-p ((iterator file-iterator) (direction (eql :forward)))
  (not (eq (current-char iterator) :eof)))


;;; file-line-iterator

(defclass* file-line-iterator (basic-stream-iterator)
  ((current-line nil r)))

(defmethod move ((iterator file-line-iterator) (direction (eql :forward)))
  (advance iterator))

(defmethod advance ((iterator file-line-iterator))
  (setf (slot-value iterator 'current-line) 
        (read-line (iterator-stream iterator) nil :eof)))

(defmethod current-element ((iterator file-line-iterator))
  (current-line iterator))

(defmethod current-element-p ((iterator file-line-iterator))
  (and (call-next-method)
       (not (eq (current-line iterator) :eof))))

(defmethod move-p ((iterator file-line-iterator) (direction (eql :forward)))
  (not (eq (current-line iterator) :eof)))

(defmethod class-for-contents-as ((contents pathname) (as (eql :lines)))
  'file-line-iterator)

;;; file-form-iterator

(defclass* file-form-iterator (basic-stream-iterator)
  ((current-form nil r)))


(defmethod move ((iterator file-form-iterator) (direction (eql :forward)))
  (advance iterator))


(defmethod advance ((iterator file-form-iterator))
  (setf (slot-value iterator 'current-form) 
        (read (iterator-stream iterator) nil :eof)))


(defmethod current-element ((iterator file-form-iterator))
  (current-form iterator))


(defmethod current-element-p ((iterator file-form-iterator))
  (and (call-next-method)
       (not (eq (current-form iterator) :eof))))


(defmethod move-p ((iterator file-form-iterator) (direction (eql :forward)))
  (not (eq (current-form iterator) :eof)))


(defmethod class-for-contents-as ((contents pathname) (as (eql :forms)))
  'file-form-iterator)


;;; delimited-iterator

(defclass* delimited-iterator (forward-iterator)
  ((cache (make-array 20 :element-type 'character
		      :fill-pointer 0 :adjustable t) r)
   (current-chunk nil r)
   (internal-iterator nil r)
   (element-characterizer 'metatilities:whitespacep ia)
   (skip-empty-chunks? t ia)
   (starting-element nil a)))


(defclass* internal-iterator-mixin ()
  ((iterator nil ir)))


(defmethod initialize-instance :after ((object delimited-iterator) &key container
                                       &allow-other-keys)
  (setf (slot-value object 'internal-iterator) 
        (make-internal-iterator object container))
  (when (move-forward-p (internal-iterator object))
    (move-forward (internal-iterator object)))
  (advance object))


(defmethod make-internal-iterator ((object delimited-iterator) container)
  (make-iterator container 
                 :iterator-class 'internal-iterator-mixin
                 :iterator object))


(defgeneric characterize-element (iterator element)
  (:documentation "Examines element in the context of iterator and returns a value describing how to treat it. This can be one of:

* nil or :appended - append element to the current chunk
* t or :delimiter  - complete the current chunk and start a new one \(ignore element\)
* :ignored         - act as if this element was never seen
* :start-new       - complete the current chunk and start a new one with this element
"))

(defmethod characterize-element ((iterator delimited-iterator) (thing t))
  (funcall (element-characterizer iterator) thing))


(defmethod move ((iterator delimited-iterator) (direction (eql :forward)))
  (advance iterator))


(defmethod move-internal ((iterator delimited-iterator) (direction (eql :forward)))
  (move-forward (internal-iterator iterator)))


(defmethod advance ((iterator delimited-iterator))
  (let ((internal (internal-iterator iterator)))
    (setf (fill-pointer (cache iterator)) 0)
    (when (starting-element iterator)
      (vector-push-extend (starting-element iterator) (cache iterator))
      (setf (starting-element iterator) nil))
    (loop while (move-forward-p internal) do
          (let ((element-is (characterize-element iterator (current-element internal))))
            ;(format t "~%~A ~A" (current-element internal) element-is)
            (case element-is
              ((nil :appended) 
               (vector-push-extend (current-element internal) (cache iterator)))
              ((t :delimiter)
               (if (skip-empty-chunks? iterator)
                 (loop while (and (move-forward-p internal)
                                  (member (characterize-element
                                           iterator (current-element internal))
                                          '(:ignored :delimiter t))) do
                       ;(format t "~%  '~A'" (current-element internal))
                       (move-internal iterator :forward))
                 
                 (move-internal iterator :forward))
               ;; leave loop
               (return))
              (:ignored nil)
              (:start-new 
               (setf (starting-element iterator) (current-element internal))
               (move-internal iterator :forward)
               ;; leave loop
               (return))
              (t 
               (warn "Don't know how to ~S ~S" element-is (current-element internal)))))
      
          (move-forward internal))
    (setf (slot-value iterator 'current-chunk)
          (combine-elements iterator))))


(defmethod combine-elements ((iterator delimited-iterator)) 
  (format nil "~A" (coerce (cache iterator) 'string)))

#+Experimental
;;?? trying to guarentee single calls to characterize-element (this doesn't work)
(defmethod advance ((iterator delimited-iterator))
  (let ((internal (internal-iterator iterator))
        (first? t))
    (setf (fill-pointer (cache iterator)) 0) 
    (loop while (move-forward-p internal) do
          (when (characterize-element iterator (current-element internal))
            (if (skip-empty-chunks? iterator)
              (loop while (or first?
                              (and (move-forward-p internal)
                                   (characterize-element iterator (current-element internal)))) do
                    (setf first? nil)
                    (move-forward internal))
              
              (move-forward internal))
            (return))
          (vector-push-extend (current-element internal) (cache iterator))
          (move-forward internal))
    (setf (slot-value iterator 'current-chunk)
          (coerce (cache iterator) 'string))))


(defmethod current-element ((iterator delimited-iterator))
  (current-chunk iterator))


(defmethod current-element-p ((iterator delimited-iterator))
  (and (call-next-method)
       (or (not (skip-empty-chunks? iterator))
           (plusp (fill-pointer (cache iterator))))))

(defmethod move-p ((iterator delimited-iterator) (direction (eql :forward)))
  (or (move-p (internal-iterator iterator) direction)
      (plusp (size (cache iterator)))))

(defclass* word-iterator (delimited-iterator)
  ()
  (:default-initargs
    :element-characterizer 'metatilities:whitespacep))

(defclass* line-iterator (delimited-iterator)
  ()
  (:default-initargs
    :element-characterizer (lambda (ch) (or (eq ch #\linefeed)
                                            (eq ch #\newline)
                                            (eq ch #\return)))))

(defmethod class-for-contents-as ((contents t) (as (eql :lines)))
  'line-iterator)

(defmethod class-for-contents-as ((contents t) (as (eql :words)))
  'word-iterator)



#|
(collect-elements (make-iterator "this is
paragraph number one.

this is paragraph number two.




and this
is
paragraph number
three." :treat-contents-as :lines))

(collect-elements (make-iterator #P"user-home:qt.lisp" :treat-contents-as :lines))
|#