Make 'read' handle input field splitting.
* gash/built-ins/read.scm (split-fields): New procedure. (main): Use it to split the input into fields and assign each field to its corresponding variable. * tests/read.org: New file. * Makefile.am (FULL_TESTS): Add it.
This commit is contained in:
parent
7a0f4fbae2
commit
5fed1b0d87
|
@ -142,6 +142,7 @@ FULL_TESTS = \
|
|||
tests/functions.org \
|
||||
tests/loops.org \
|
||||
tests/pipes-and-booleans.org \
|
||||
tests/read.org \
|
||||
tests/redirects.org \
|
||||
tests/signals.org \
|
||||
tests/temporary-assignments.org \
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
;;; Gash -- Guile As SHell
|
||||
;;; Copyright © 2018 Timothy Sample <samplet@ngyro.com>
|
||||
;;; Copyright © 2018, 2019 Timothy Sample <samplet@ngyro.com>
|
||||
;;;
|
||||
;;; This file is part of Gash.
|
||||
;;;
|
||||
|
@ -19,7 +19,8 @@
|
|||
(define-module (gash built-ins read)
|
||||
#:use-module (gash environment)
|
||||
#:use-module (ice-9 match)
|
||||
#:use-module (ice-9 rdelim))
|
||||
#:use-module (ice-9 rdelim)
|
||||
#:use-module (srfi srfi-1))
|
||||
|
||||
;;; Commentary:
|
||||
;;;
|
||||
|
@ -27,8 +28,56 @@
|
|||
;;;
|
||||
;;; Code:
|
||||
|
||||
;; The '(gash word)' module already has a 'split-fields' procedure.
|
||||
;; However, we need to be able to specify a maximum number of fields,
|
||||
;; which it cannot do. We could extend it, but it has to deal with
|
||||
;; quotes, which we do not here. It is simpler to write a specialized
|
||||
;; version that can deal with 'max' without quotes than it is to
|
||||
;; extend the more general version.
|
||||
(define* (split-fields str max hard-delims soft-delims
|
||||
#:optional (start 0) (end (string-length str)))
|
||||
"Split @var{str} into at most @var{max} fields. Each individual
|
||||
occurrence of a character in the set @var{hard-delims} delimits a
|
||||
field, while contiguous sequences of characters from the set
|
||||
@var{soft-delims} are treated as a single delimiter."
|
||||
(define non-soft-delims (char-set-complement soft-delims))
|
||||
(define all-delims (char-set-union hard-delims soft-delims))
|
||||
|
||||
(define* (field+next-index str i)
|
||||
(let* ((end* (or (string-index str all-delims i end) end))
|
||||
(start* (string-index str non-soft-delims end* end)))
|
||||
(values (substring str i end*)
|
||||
(if (and start*
|
||||
(char-set-contains? hard-delims
|
||||
(string-ref str start*)))
|
||||
(or (string-index str non-soft-delims (1+ start*) end) end)
|
||||
start*))))
|
||||
|
||||
(cond
|
||||
((string-index str non-soft-delims start end)
|
||||
=> (lambda (start*)
|
||||
(let loop ((i start*) (count 0) (acc '()))
|
||||
(if (>= count (1- max))
|
||||
(reverse! (cons (string-trim-right str soft-delims i end) acc))
|
||||
(call-with-values (lambda () (field+next-index str i))
|
||||
(lambda (field i*)
|
||||
(if i*
|
||||
(loop i* (1+ count) (cons field acc))
|
||||
(reverse! (cons field acc)))))))))
|
||||
(else '())))
|
||||
|
||||
(define (main . args)
|
||||
(match (read-line (current-input-port))
|
||||
((? eof-object?) 1)
|
||||
(str (setvar! (car args) str)
|
||||
0)))
|
||||
(str (let* ((limit (length args))
|
||||
(dflt (string #\space #\tab #\newline))
|
||||
(ifs (string->char-set (getvar "IFS" dflt)))
|
||||
(ifs/w (char-set-intersection ifs char-set:whitespace))
|
||||
(ifs/nw (char-set-difference ifs char-set:whitespace))
|
||||
(fields (split-fields str limit ifs/nw ifs/w)))
|
||||
(for-each (lambda (var field)
|
||||
;; XXX: Verify that VAR is a valid variable name.
|
||||
(setvar! var field))
|
||||
args
|
||||
(append fields (circular-list "")))
|
||||
0))))
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
;;; Gash -- Guile As SHell
|
||||
;;; Copyright © 2019 Timothy Sample <samplet@ngyro.com>
|
||||
;;;
|
||||
;;; This file is part of Gash.
|
||||
;;;
|
||||
;;; Gash is free software: you can redistribute it and/or modify
|
||||
;;; it under the terms of the GNU General Public License as published by
|
||||
;;; the Free Software Foundation, either version 3 of the License, or
|
||||
;;; (at your option) any later version.
|
||||
;;;
|
||||
;;; Gash is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with Gash. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
* Reads a single variable
|
||||
:script:
|
||||
#+begin_src sh
|
||||
echo foo | {
|
||||
read x
|
||||
echo $x
|
||||
}
|
||||
#+end_src
|
||||
:stdout:
|
||||
#+begin_example
|
||||
foo
|
||||
#+end_example
|
||||
|
||||
* Reads multiple variables
|
||||
:script:
|
||||
#+begin_src sh
|
||||
echo foo bar | {
|
||||
read x y
|
||||
echo $x
|
||||
echo $y
|
||||
}
|
||||
#+end_src
|
||||
:stdout:
|
||||
#+begin_example
|
||||
foo
|
||||
bar
|
||||
#+end_example
|
||||
|
||||
* Handles more variables than fields
|
||||
:script:
|
||||
#+begin_src sh
|
||||
echo foo bar baz | {
|
||||
read x y
|
||||
echo $x
|
||||
echo $y
|
||||
}
|
||||
#+end_src
|
||||
:stdout:
|
||||
#+begin_example
|
||||
foo
|
||||
bar baz
|
||||
#+end_example
|
||||
|
||||
* Handles more fields than variables
|
||||
:script:
|
||||
#+begin_src sh
|
||||
echo foo | {
|
||||
read x y
|
||||
echo $x
|
||||
echo $y
|
||||
}
|
||||
#+end_src
|
||||
:stdout:
|
||||
#+begin_example
|
||||
foo
|
||||
|
||||
#+end_example
|
||||
|
||||
* Trims whitespace in extra fields
|
||||
:script:
|
||||
#+begin_src sh
|
||||
echo foo bar 'baz ' | {
|
||||
read x y
|
||||
echo $x
|
||||
echo $y
|
||||
}
|
||||
#+end_src
|
||||
:stdout:
|
||||
#+begin_example
|
||||
foo
|
||||
bar baz
|
||||
#+end_example
|
Loading…
Reference in New Issue