diff --git a/NEWS b/NEWS index f14e6c93c..3328a03cf 100644 --- a/NEWS +++ b/NEWS @@ -38,6 +38,13 @@ advanced argument handling such as optional and keyword arguments. The implementation fully supports (next-method) calls, also for keyword arguments. The new syntax is documented in the Guile Reference manual. +** New line or field iteration procedures in (ice-9 rdelim) + +(ice-9 rdelim) has three new procedures: for-rdelim-from-port, +for-delimited-from-port and for-line-in-file. Of these, for-line-in-file +is helpful in the common situation where you want a procedure applied to +every line in a file. + * Changes to the distribution * Bug fixes diff --git a/doc/ref/api-io.texi b/doc/ref/api-io.texi index 79bc9e9d6..0a9773f62 100644 --- a/doc/ref/api-io.texi +++ b/doc/ref/api-io.texi @@ -984,6 +984,28 @@ used. This procedure is equivalent to: @end lisp @end deffn +@deffn {Scheme Procedure} for-rdelim-from-port port proc rdelim-proc @ + [#:stop-pred=eof-object?] +For every unit provided by @code{(rdelim-proc port)}, provide +this unit(rdelim) to @var{proc} to be processed. This will continue throughout +@var{port} until @var{stop-pred} returns @code{#t}. +@var{stop-pred} is @code{eof-object?} by default. +@var{rdelim-proc} has to advance through @var{port} with every call made to it. +@end deffn + +@deffn {Scheme Procedure} for-delimited-from-port port proc @ + [#:delims=''\n''] [#:handle-delim='trim] +Call @var{proc} for every line delimited by @var{delims} from @var{port}. +@end deffn + +@deffn {Scheme Procedure} for-line-in-file file proc @ + [#:encoding=#f] [#:guess-encoding=#f] +Call @var{proc} for every line in @var{file}. +@var{file} must be a filename string. + +The line provided to @var{proc} is guaranteed to be a string. +@end deffn + @node Default Ports @subsection Default Ports for Input, Output and Errors @cindex Default ports diff --git a/module/ice-9/rdelim.scm b/module/ice-9/rdelim.scm index d2cd081d7..65d1932fc 100644 --- a/module/ice-9/rdelim.scm +++ b/module/ice-9/rdelim.scm @@ -23,7 +23,10 @@ ;;; similar to (scsh rdelim) but somewhat incompatible. (define-module (ice-9 rdelim) - #:export (read-line + #:export (for-delimited-from-port + for-line-in-file + for-rdelim-from-port + read-line read-line! read-delimited read-delimited! @@ -206,3 +209,33 @@ characters to read. By default, there is no limit." line) (else (error "unexpected handle-delim value: " handle-delim))))) + +(define* (for-rdelim-from-port port proc rdelim-proc + #:key (stop-pred eof-object?)) + "Call PROC for every (RDELIM-PROC PORT) from PORT until STOP-PRED returns #t. +RDELIM-PROC has to advance through PORT with every call." + (let loop ((rdelim (rdelim-proc port))) + (cond ((stop-pred rdelim) + (close-port port)) + (else + (proc rdelim) + (loop (rdelim-proc port)))))) + +(define* (for-delimited-from-port port proc + #:key (delims "\n") (handle-delim 'trim)) + "Call PROC for every delimited line from PORT until the eof-object is reached." + (for-rdelim-from-port port proc + (lambda (port) + (read-delimited delims port handle-delim)))) + +(define* (for-line-in-file file proc + #:key (encoding #f) (guess-encoding #f)) + "Call PROC for every line in FILE until the eof-object is reached. +FILE must be a filename string. + +The line provided to PROC is guaranteed to be a string." + (call-with-input-file + file + (lambda (port) + (for-delimited-from-port port proc)) + #:encoding encoding #:guess-encoding guess-encoding)) diff --git a/test-suite/tests/rdelim.test b/test-suite/tests/rdelim.test index 3aaa0b253..ad44278d2 100644 --- a/test-suite/tests/rdelim.test +++ b/test-suite/tests/rdelim.test @@ -20,7 +20,8 @@ (define-module (test-suite test-rdelim) #:use-module (ice-9 rdelim) #:use-module ((rnrs io ports) #:select (open-bytevector-input-port get-u8)) - #:use-module (test-suite lib)) + #:use-module (test-suite lib) + #:use-module (test-suite guile-test)) (with-test-prefix "read-line" @@ -247,6 +248,37 @@ (string=? (substring buf 0 len) s) (string=? (substring buf len) "."))))) + +(with-test-prefix "for-line-in-file" + + (define (test-file) + (data-file-name "ports-test.tmp")) + + (let ((lst '()) + (lines '()) + (string "line1\nline2\nline3") + (filename (test-file))) + (call-with-input-string + "A\0B\0C" + (lambda (port) + (pass-if "for-delimited-from-port parses stream correctly" + (for-delimited-from-port port + (lambda (entry) + (set! lst (cons entry lst))) + #:delims "\0") + (equal? lst '("C" "B" "A"))))) + (let ((port (open-output-file filename))) + (display string port) + (close-port port)) + (pass-if "for-line-in-file parses file correctly" + (for-line-in-file filename + (lambda (line) + (set! lines (cons line lines)))) + (equal? lines '("line3" "line2" "line1")))) + + (delete-file (test-file)) + ) + ;;; Local Variables: ;;; eval: (put 'with-test-prefix 'scheme-indent-function 1) ;;; eval: (put 'pass-if 'scheme-indent-function 1)