1 ;; Copyright 2005 by Robert Dodier
3 ;; This program is free software; you can redistribute it and/or
4 ;; modify it under the terms of the GNU General Public License.
6 ;; This program has NO WARRANTY, not even the implied warranty of
7 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 ;; M: read_matrix (source, sep_ch_flag)
14 ;; read_matrix (source, M, sep_ch_flag)
15 ;; A : read_array (source, sep_ch_flag)
16 ;; read_array (source, A, sep_ch_flag)
17 ;; read_hashed_array (source, A, sep_ch_flag)
18 ;; L: read_nested_list (source, sep_ch_flag)
19 ;; L: read_list (source, sep_ch_flag)
20 ;; read_list (source, L, sep_ch_flag)
22 ;; read_binary_matrix (source, M)
23 ;; A : read_binary_array (source)
24 ;; read_binary_array (source, A)
25 ;; L: read_binary_list (source)
26 ;; read_binary_list (source, L)
28 ;; `source' is a file name or input stream.
32 ;; `sink' is a file name or output stream.
34 ;; write_data (X, sink, sep_ch_flag)
35 ;; write_binary_data (X, sink)
39 ;; byte_order_flag recognized values: msb, lsb
41 ;; assume_external_byte_order (byte_order_flag)
44 (defun $assume_external_byte_order
(x)
47 (define-external-byte-order :lsb
))
49 (define-external-byte-order :msb
))
51 (merror "assume_external_byte_order: unrecognized byte order flag: ~a" x
))))
53 (defun lisp-or-declared-maxima-array-p (x)
54 (or (arrayp x
) (mget x
'array
)))
56 ;; THESE FILE-OPENING FUNCTIONS WANT TO BE MOVED TO STRINGPROC (HOME OF OTHER SUCH FUNCTIONS) !!
58 (defun $openw_binary
(file)
60 #+sbcl
(sb-ext:native-namestring file
)
64 :element-type
'(unsigned-byte 8)
65 :if-does-not-exist
:create
))
67 (defun $opena_binary
(file)
69 #+sbcl
(sb-ext:native-namestring file
)
73 :element-type
'(unsigned-byte 8)
74 :if-does-not-exist
:create
))
76 (defun $openr_binary
(file) (open
77 #+sbcl
(sb-ext:native-namestring file
)
79 :element-type
'(unsigned-byte 8)))
81 ;; -------------------- read functions --------------------
83 ;; ---- functions to read a matrix
85 (defun $read_matrix
(stream-or-filename &rest args
)
86 (if ($matrixp
(car args
))
89 (sep-ch-flag (cadr args
))
90 (nrow (length (cdr M
)))
91 (ncol (if (> nrow
0) (length (cdadr M
)) 0))
92 (L ($read_list stream-or-filename sep-ch-flag
(* nrow ncol
))))
93 ;; COPYING DATA HERE !!
94 (fill-matrix-from-list L M nrow ncol
))
96 ((sep-ch-flag (car args
))
97 (rows-list (cdr ($read_nested_list stream-or-filename sep-ch-flag
)))
98 (rows-list-nonempty (remove-if #'(lambda (x) (= ($length x
) 0)) rows-list
)))
99 `(($matrix
) ,@rows-list-nonempty
))))
101 (defun $read_binary_matrix
(stream-or-filename M
)
104 ((nrow (length (cdr M
)))
105 (ncol (if (> nrow
0) (length (cdadr M
)) 0))
106 (L ($read_binary_list stream-or-filename
(* nrow ncol
))))
107 ;; COPYING DATA HERE !!
108 (fill-matrix-from-list L M nrow ncol
))
109 (merror "read_binary_matrix: expected a matrix, found ~a instead" (type-of M
))))
111 (defun fill-matrix-from-list (L M nrow ncol
)
114 (let ((row (nth (1+ i
) M
)))
116 (setf (nth (1+ j
) row
) (nth (1+ k
) L
))
120 ;; ---- functions to read a Lisp array or Maxima declared array
122 (defun $read_array
(stream-or-filename &rest args
)
123 (if (and args
(lisp-or-declared-maxima-array-p (car args
)))
126 (sep-ch-flag (and (cdr args
) (cadr args
)))
128 (read-into-existing-array stream-or-filename A sep-ch-flag mode
))
130 ((sep-ch-flag (and args
(car args
)))
132 (read-and-return-new-array stream-or-filename sep-ch-flag mode
))))
134 (defun $read_binary_array
(file-name &rest args
)
136 (read-into-existing-array file-name
(car args
) nil
'binary
)
137 (read-and-return-new-array file-name nil
'binary
)))
139 (defun read-into-existing-array (file-name A sep-ch-flag mode
)
141 (setq A
(get (mget A
'array
) 'array
)))
143 ((dimensions (array-dimensions A
))
144 (n (apply #'* dimensions
)))
145 (read-into-existing-array-size-known file-name A sep-ch-flag mode n
)
148 (defun read-into-existing-array-size-known (stream-or-filename A sep-ch-flag mode n
)
149 (if (streamp stream-or-filename
)
150 (read-into-existing-array-size-known-from-stream stream-or-filename A sep-ch-flag mode n
)
151 (let ((file-name (require-string stream-or-filename
)))
154 #+sbcl
(sb-ext:native-namestring file-name
)
156 :if-does-not-exist nil
157 :element-type
(if (eq mode
'text
) 'character
'(unsigned-byte 8)))
159 (read-into-existing-array-size-known-from-stream in A sep-ch-flag mode n
)
160 (merror "read_array: no such file `~a'" file-name
))))))
162 (defun read-into-existing-array-size-known-from-stream (in A sep-ch-flag mode n
)
163 (let (x (sep-ch (get-input-sep-ch sep-ch-flag in
)))
164 (if (eq mode
'text
) (reset-for-parse-next-element))
166 (if (eq (setq x
(if (eq mode
'text
) (parse-next-element in sep-ch
) (read-float-64 in
))) 'eof
)
168 (setf (row-major-aref A i
) x
))))
170 (defun read-into-existing-array-size-unknown-from-stream (in A sep-ch mode
)
172 (if (eq mode
'text
) (reset-for-parse-next-element))
174 (if (eq (setq x
(if (eq mode
'text
) (parse-next-element in sep-ch
) (read-float-64 in
))) 'eof
)
176 (vector-push-extend x A
))))
178 (defun read-and-return-new-array (stream-or-filename sep-ch-flag mode
)
179 (if (streamp stream-or-filename
)
180 (read-and-return-new-array-from-stream stream-or-filename sep-ch-flag mode
)
181 (let ((file-name (require-string stream-or-filename
)))
184 #+sbcl
(sb-ext:native-namestring file-name
)
186 :if-does-not-exist nil
187 :element-type
(if (eq mode
'text
) 'character
'(unsigned-byte 8)))
189 (read-and-return-new-array-from-stream in sep-ch-flag mode
)
190 (merror "read_array: no such file `~a'" file-name
))))))
192 (defun read-and-return-new-array-from-stream (in sep-ch-flag mode
)
193 (let ((A (make-array 0 :adjustable t
:fill-pointer t
))
194 (sep-ch (if (eq mode
'text
) (get-input-sep-ch sep-ch-flag in
))))
195 (read-into-existing-array-size-unknown-from-stream in A sep-ch mode
)))
197 ;; ---- functions to read a Maxima undeclared array
199 (defun $read_hashed_array
(stream-or-filename A
&optional sep-ch-flag
)
200 (if (streamp stream-or-filename
)
201 (read-hashed-array-from-stream stream-or-filename A sep-ch-flag
)
202 (let ((file-name (require-string stream-or-filename
)))
204 #+sbcl
(sb-ext:native-namestring file-name
)
206 :if-does-not-exist nil
)
208 (read-hashed-array-from-stream in A sep-ch-flag
)
209 (merror "read_hashed_array no such file `~a'" file-name
))))))
211 (defun read-hashed-array-from-stream (in A sep-ch-flag
)
212 (let (key L
(sep-ch (get-input-sep-ch sep-ch-flag in
)))
214 (setq L
(read-line in nil
'eof
))
215 (if (eq L
'eof
) (return))
216 (setq L
(make-mlist-from-string L sep-ch
))
219 (setq key
($first L
))
220 (if (= ($length L
) 1)
221 (arrstore (list (list A
'array
) key
) nil
)
222 (arrstore (list (list A
'array
) key
) ($rest L
)))))))
225 ;; ---- functions to read a list or nested list
227 (defun $read_nested_list
(stream-or-filename &optional sep-ch-flag
)
228 (if (streamp stream-or-filename
)
229 (read-nested-list-from-stream stream-or-filename sep-ch-flag
)
230 (let ((file-name (require-string stream-or-filename
)))
232 #+sbcl
(sb-ext:native-namestring file-name
)
234 :if-does-not-exist nil
)
236 (read-nested-list-from-stream in sep-ch-flag
)
237 (merror "read_nested_list: no such file `~a'" file-name
))))))
239 (defun read-nested-list-from-stream (in sep-ch-flag
)
240 (let (A L
(sep-ch (get-input-sep-ch sep-ch-flag in
)))
242 (setq L
(read-line in nil
'eof
))
244 (return (cons '(mlist simp
) (nreverse A
))))
245 (setq A
(cons (make-mlist-from-string L sep-ch
) A
)))))
247 (defun $read_list
(stream-or-filename &rest args
)
248 (if ($listp
(car args
))
251 (sep-ch-flag (cadr args
))
252 (n (or (caddr args
) ($length L
))))
253 (read-into-existing-list stream-or-filename L sep-ch-flag
'text n
))
254 (if (integerp (car args
))
255 (let ((n (car args
)))
256 (read-list stream-or-filename nil
'text n
))
257 (let ((sep-ch-flag (car args
)) (n (cadr args
)))
258 (read-list stream-or-filename sep-ch-flag
'text n
)))))
260 (defun read-into-existing-list (stream-or-filename L sep-ch-flag mode n
)
261 (if (streamp stream-or-filename
)
262 (read-into-existing-list-from-stream stream-or-filename L sep-ch-flag mode n
)
263 (let ((file-name (require-string stream-or-filename
)))
266 #+sbcl
(sb-ext:native-namestring file-name
)
268 :if-does-not-exist nil
269 :element-type
(if (eq mode
'text
) 'character
'(unsigned-byte 8)))
271 (read-into-existing-list-from-stream in L sep-ch-flag mode n
)
272 (merror "read_list: no such file `~a'" file-name
))))))
274 (defun read-into-existing-list-from-stream (in L sep-ch-flag mode n
)
275 (let (x (sep-ch (if (eq mode
'text
) (get-input-sep-ch sep-ch-flag in
))))
276 (if (eq mode
'text
) (reset-for-parse-next-element))
278 (if (eq (setq x
(if (eq mode
'text
) (parse-next-element in sep-ch
) (read-float-64 in
))) 'eof
)
280 (setf (nth (1+ i
) L
) x
))
283 (defun read-list (stream-or-filename sep-ch-flag mode n
)
284 (if (streamp stream-or-filename
)
285 (read-list-from-stream stream-or-filename sep-ch-flag mode n
)
286 (let ((file-name (require-string stream-or-filename
)))
289 #+sbcl
(sb-ext:native-namestring file-name
)
291 :if-does-not-exist nil
292 :element-type
(if (eq mode
'text
) 'character
'(unsigned-byte 8)))
294 (read-list-from-stream in sep-ch-flag mode n
)
295 (merror "read_list: no such file `~a'" file-name
))))))
297 (defun read-list-from-stream (in sep-ch-flag mode n
)
298 (let (A x
(sep-ch (if (eq mode
'text
) (get-input-sep-ch sep-ch-flag in
))))
299 (if (eq mode
'text
) (reset-for-parse-next-element))
304 (eq (setq x
(if (eq mode
'text
) (parse-next-element in sep-ch
) (read-float-64 in
)))
306 (return (cons '(mlist simp
) (nreverse A
))))
307 (setq A
(nconc (list x
) A
))
310 (defun $read_binary_list
(stream-or-filename &rest args
)
311 (if ($listp
(car args
))
314 (n (or (cadr args
) ($length L
))))
315 (read-into-existing-list stream-or-filename L nil
'binary n
))
316 (let ((n (car args
)))
317 (read-list stream-or-filename nil
'binary n
))))
319 (defun make-mlist-from-string (s sep-ch
)
320 ; scan-one-token-g isn't happy with symbol at end of string.
321 (setq s
(concatenate 'string s
" "))
323 (with-input-from-string (*parse-stream
* s
)
324 (let ((token) (L) (LL) (sign) (found-token) (found-sep))
326 (setq token
(scan-one-token-g t
'eof
))
331 (format t
"numericalio: trailing sign (~S) at end of line; strange, but just eat it.~%" sign
)))
333 ((eql sep-ch
#\space
)
334 (return (cons '(mlist) LL
)))
336 (if (or found-token found-sep
)
337 (return (cons '(mlist) (appropriate-append L LL
)))
338 ;; We reached EOF without encountering a token or a separator;
339 ;; this is an empty line.
340 (return '((mlist))))))))
342 ((or (eq token
'$-
) (eq token
'$
+))
343 (setq sign
(cond ((eq token
'$-
) -
1) (t 1))))
347 (setq token
(m* sign token
))
350 ((eql sep-ch
#\space
)
351 (setq found-token token
)
352 (setq LL
(append LL
(list token
))))
356 (setq found-sep token
)
357 (setq L
(appropriate-append L LL
))
360 (setq found-token token
)
361 (setq LL
(append LL
(list token
)))))))))))))
363 (defun appropriate-append (L LL
)
365 ((null LL
) (append L
'(nil)))
366 ((= (length LL
) 1) (append L LL
))
367 (t (append L
(list (append '((mlist)) LL
))))))
369 ;; ----- begin backwards compatibility stuff ... sigh -----
370 (defun $read_lisp_array
(file-name A
&optional sep-ch-flag
)
371 ($read_array file-name A sep-ch-flag
))
373 (defun $read_maxima_array
(file-name A
&optional sep-ch-flag
)
374 ($read_array file-name A sep-ch-flag
))
375 ;; ----- end backwards compatibility stuff ... sigh -----
377 ;; ---- read one element
379 (defvar newline-symbol
(intern (coerce '(#\$
#\newline
) 'string
)))
380 (defvar whitespace-sans-newline
(remove #\newline
*whitespace-chars
*))
382 (let (prev-token-sep-ch sign start-of-line
)
384 (defun reset-for-parse-next-element ()
385 (setq prev-token-sep-ch nil
)
387 (setq start-of-line t
))
389 (defun parse-next-element (in sep-ch
)
392 ;; Treat newline as a token, so leading/trailing separators can be detected,
393 ;; when separator is anything other than a space.
394 (*whitespace-chars
* (if (eql sep-ch
#\space
) *whitespace-chars
* whitespace-sans-newline
))
397 (setq token
(scan-one-token-g t
'eof
))
399 ((eq token newline-symbol
)
400 (setq start-of-line t
)
401 (when prev-token-sep-ch
402 (setq prev-token-sep-ch nil
)
405 (if prev-token-sep-ch
407 (setq prev-token-sep-ch nil
)
410 ((and (eql token sep-ch
) (not (eql sep-ch
#\space
))) ;; TEST FOR #\SPACE IS REDUNDANT
411 ;; We have a separator token.
412 ;; If the preceding token was also a separator,
413 ;; or we're at the start of a line, return NIL.
414 (if (or prev-token-sep-ch start-of-line
)
416 (setq start-of-line nil
)
418 (setq prev-token-sep-ch token
)))
419 ((prog nil
(setq start-of-line nil
)))
420 ((prog nil
(setq prev-token-sep-ch nil
)))
421 ((member token
'($- $
+))
422 (setq sign
(* sign
(if (eq token
'$-
) -
1 1))))
424 (let ((return-value (m* sign token
)))
426 (return return-value
))))))))
429 ;; -------------------- write functions -------------------
431 (defun open-file-appropriately (file-name mode
)
433 #+sbcl
(sb-ext:native-namestring file-name
)
436 :element-type
(if (eq mode
'text
) 'character
'(unsigned-byte 8))
437 :if-exists
(if (or (eq $file_output_append
'$true
) (eq $file_output_append t
)) :append
:supersede
)
438 :if-does-not-exist
:create
))
440 (defun $write_data
(X stream-or-filename
&optional sep-ch-flag
)
441 (write-data X stream-or-filename sep-ch-flag
'text
))
443 (defun $write_binary_data
(X stream-or-filename
)
444 (write-data X stream-or-filename nil
'binary
))
446 (defun write-data (X stream-or-filename sep-ch-flag mode
)
449 (if (streamp stream-or-filename
)
451 (open-file-appropriately (require-string stream-or-filename
) mode
))))
454 (write-matrix X out sep-ch-flag mode
))
456 (write-lisp-array X out sep-ch-flag mode
))
458 (write-maxima-array X out sep-ch-flag mode
))
460 (write-hashed-array X out sep-ch-flag mode
))
462 (write-list X out sep-ch-flag mode
))
463 (t (merror "write_data: don't know what to do with a ~M" (type-of X
))))
464 (if (streamp stream-or-filename
)
469 (defun write-matrix (M out sep-ch-flag mode
)
470 (let ((sep-ch (get-output-sep-ch sep-ch-flag out
)))
471 (mapcar #'(lambda (x) (write-list-lowlevel (cdr x
) out sep-ch mode
)) (cdr M
))))
473 (defun write-lisp-array (A out sep-ch-flag mode
)
474 (let ((sep-ch (get-output-sep-ch sep-ch-flag out
)) (d (array-dimensions A
)))
475 (write-lisp-array-helper A d
'() out sep-ch mode
)))
477 (defun write-lisp-array-helper (A d indices out sep-ch mode
)
478 (cond ((equalp (length d
) 1)
480 (loop for i from
0 to
(- (car d
) 1) do
481 (let ((x (apply 'aref
(append (list A
) (reverse (cons i indices
))))))
482 (setq L
(cons x L
))))
483 (write-list-lowlevel (reverse L
) out sep-ch mode
)))
485 (loop for i from
0 to
(- (car d
) 1) do
486 (write-lisp-array-helper A
(cdr d
) (cons i indices
) out sep-ch mode
)
487 (if (and (eq mode
'text
) (> (length d
) 2))
490 (defun write-maxima-array (A out sep-ch-flag mode
)
491 (write-lisp-array (symbol-array (mget A
'array
)) out sep-ch-flag mode
))
493 (defun write-hashed-array (A out sep-ch-flag mode
)
495 ((keys (cdddr (meval (list '($arrayinfo
) A
))))
496 (sep-ch (get-output-sep-ch sep-ch-flag out
))
499 (if (not keys
) (return))
500 (setq L
($arrayapply A
(car keys
)))
501 (cond ((listp L
) (pop L
))
502 (t (setq L
(list L
))))
503 (write-list-lowlevel (append (cdr (pop keys
)) L
) out sep-ch mode
))))
505 (defun write-list (L out sep-ch-flag mode
)
506 (let ((sep-ch (get-output-sep-ch sep-ch-flag out
)))
507 (write-list-lowlevel (cdr L
) out sep-ch mode
)))
509 (defun write-list-lowlevel (L out sep-ch mode
)
510 (setq sep-ch
(cond ((symbolp sep-ch
) (cadr (exploden sep-ch
))) (t sep-ch
)))
512 ((null L
) (terpri out
))
515 (if (not L
) (return))
518 (write-list-lowlevel (cdr e
) out sep-ch mode
))
524 (declare (special $lispdisp
))
527 ((null L
) (terpri out
))
528 (t (write-char sep-ch out
))))
531 (write-float ($float e
) out
)
532 (merror "write_data: encountered non-numeric data in binary output")))
534 (merror "write_data: unrecognized mode"))))))))))
536 (defun get-input-sep-ch (sep-ch-flag my-stream
)
538 ((eq sep-ch-flag
'$tab
)
539 (format t
"numericalio: separator flag ``tab'' not recognized for input; assume ``space'' instead.~%")
541 (t (get-output-sep-ch sep-ch-flag my-stream
))))
543 (defun get-output-sep-ch (sep-ch-flag my-stream
)
545 ((eq sep-ch-flag
'$space
) #\space
)
546 ((eq sep-ch-flag
'$tab
) #\tab
)
547 ((or (eq sep-ch-flag
'$comma
) (eq sep-ch-flag
'$csv
)) '$\
,) ; '$csv is backwards compatibility ... sigh
548 ((eq sep-ch-flag
'$pipe
) '$\|
)
549 ((eq sep-ch-flag
'$semicolon
) '$\
;)
551 ((stringp sep-ch-flag
)
552 (if (/= (length sep-ch-flag
) 1)
553 (progn (format t
"numericalio: unrecognized separator; assume ``space''.~%")
555 (let ((would-be-sep-ch (aref sep-ch-flag
0)))
557 ((eq would-be-sep-ch
#\space
) #\space
)
558 ((eq would-be-sep-ch
#\tab
) #\tab
)
559 ((eq would-be-sep-ch
#\
,) '$\
,)
560 ((eq would-be-sep-ch
#\|
) '$\|
)
561 ((eq would-be-sep-ch
#\
;) '$\;)
563 (format t
"numericalio: separator flag ~S not recognized; assume ``space''.~%" would-be-sep-ch
)
567 ((ignore-errors (equal (pathname-type (truename my-stream
)) "csv"))
571 (format t
"numericalio: separator flag ~S not recognized; assume ``space''.~%" (stripdollar sep-ch-flag
))
574 (defun require-string (s)
579 (merror "numericalio: expected a string, instead found a ~:M" (type-of s
)))))