Commit 000ffb18 authored by Pascal J. Bourguignon's avatar Pascal J. Bourguignon

Added filter and pipe articles.

parent 068fc6cf
;;;; -*- mode:lisp;coding:utf-8 -*-
;;;;**************************************************************************
;;;;FILE: filter.lisp
;;;;LANGUAGE: Common-Lisp
;;;;SYSTEM: Common-Lisp
;;;;USER-INTERFACE: NONE
;;;;DESCRIPTION
;;;;
;;;; This package exports a grep I/O filter,
;;;; and a macro to build unix-like pipes, using
;;;; com.informatimago.clext.pipe.
;;;; Note com.informatimago.interactive.browser:{cat,more,less}
;;;; as any other expression reading *standard-input* and writing
;;;; *standard-output* can be used in a filter,
;;;;
;;;;AUTHORS
;;;; <PJB> Pascal J. Bourguignon <pjb@informatimago.com>
;;;;MODIFICATIONS
;;;; 2015-10-10 <PJB> Created.
;;;;BUGS
;;;;LEGAL
;;;; AGPL3
;;;;
;;;; Copyright Pascal J. Bourguignon 2015 - 2015
;;;;
;;;; This program is free software: you can redistribute it and/or modify
;;;; it under the terms of the GNU Affero General Public License as published by
;;;; the Free Software Foundation, either version 3 of the License, or
;;;; (at your option) any later version.
;;;;
;;;; This program 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 Affero General Public License for more details.
;;;;
;;;; You should have received a copy of the GNU Affero General Public License
;;;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;;;**************************************************************************
(defpackage "COM.INFORMATIMAGO.CLEXT.FILTER"
(:use "COMMON-LISP"
"BORDEAUX-THREADS"
"CL-PPCRE"
"COM.INFORMATIMAGO.COMMON-LISP.CESARUM.STREAM"
"COM.INFORMATIMAGO.CLEXT.PIPE")
(:export "FILTER" "IN" "GREP" "OUT")
(:documentation "
This package exports a grep I/O filter,
and a macro to build unix-like pipes, using
com.informatimago.clext.pipe.
Note com.informatimago.interactive.browser:{cat,more,less}
as any other expression reading *standard-input* and writing
*standard-output* can be used in a filter,
LEGAL
Copyright Pascal J. Bourguignon 2015 - 2015
AGPL3
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTYwithout even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"))
(in-package "COM.INFORMATIMAGO.CLEXT.FILTER")
(defun in (pathname &key (external-format :default) (if-does-not-exist :error))
"
DO: Reads the text file at PATHNAME and writes it out
to *STANDARD-OUTPUT*.
EXTERNAL-FORMAT: The external-format for the file.
IF-DOES-NOT-EXIST: (or (member :ERROR :END-OF-FILE NIL) string stream)
If the file doesn't exist, by default an error is
signaled. Alternatives are:
:END-OF-FILE closes the stream bound to
*STANDARD-OUTPUT* Note: you probably should don't
do that on the system standard output.
NIL: do nothing.
a STRING: writes the string to *STANDARD-OUTPUT*.
a STREAM: copy the stream to *STANDARD-OUTPUT*.
SEE ALSO: COM.INFORMATIMAGO.COMMON-LISP.INTERACTIVE.BROWSER:CAT
"
(with-open-file (input pathname
:direction :input
:external-format external-format
:if-does-not-exist (if (eq :error if-does-not-exist)
:error
nil))
(if input
(copy-stream input *standard-output*)
(etypecase if-does-not-exist
(null #|do nothing|#)
(string (write-string if-does-not-exist *standard-output*))
(stream (copy-stream if-does-not-exist *standard-output*))))))
(defun out (pathname &key (external-format :default) (if-exists :error))
"
DO: Writes the data from *STANDARD-INPUT* into the
text file at PATHNAME.
EXTERNAL-FORMAT: The external-format for the file.
IF-EXISTS: same as for OPEN.
"
(with-open-file (output pathname
:direction :output
:external-format external-format
:if-does-not-exist :create
:if-exists if-exists)
(when output
(copy-stream *standard-input* output))))
(defun grep (regexp &key case-insensitive (extended t) count line-number filename)
(loop
:with filename := (typecase filename
((or string pathname) filename)
(null nil)
(t (if (typep *standard-input* 'file-stream)
(pathname *standard-input*)
(princ-to-string *standard-input*))))
:with scanner := (create-scanner regexp
:case-insensitive-mode case-insensitive
:single-line-mode t
:extended-mode extended
:destructive nil)
:with counter := 0
:for line := (read-line *standard-input* nil nil)
:for linum :from 1
:while line
:when (scan scanner line)
:do (if count
(incf counter)
(if line-number
(if filename
(format t "~A:~D:~A~%" filename linum line)
(format t "~D:~A~%" linum line))
(if filename
(format t "~A:~A~%" filename line)
(write-line line))))
:finally (when count
(format t "~D line~:*~P match~:[~;es~]~%"
counter (= 1 counter)))))
(defvar *buffer-size* 4000)
(defmacro filter (&body filter-forms)
(let* ((vpipe (gensym "pipe")))
;; *standard-input* a pipe b pipe c *standard-output* main thread.
;; - -
(if (rest filter-forms)
`(let ((,vpipe (make-pipe :buffer-size *buffer-size*)))
(bt:make-thread (lambda ()
(unwind-protect
(filter ,@(butlast filter-forms))
(close *standard-output*)))
:initial-bindings (list (cons '*standard-output* (pipe-output-stream ,vpipe))
(cons '*standard-input* *standard-input*)))
(let ((*standard-input* (pipe-input-stream ,vpipe)))
,@(last filter-forms)))
(first filter-forms))))
;; (pprint (macroexpand-1 '(filter (in "/etc/passwd") (out "/tmp/p"))))
;; (filter (in "/etc/passwd") (out "/tmp/p"))
;;;; THE END ;;;;
.. comment: -*- mode:rst; coding:utf-8 -*-
Here is a new package, ``"COM.INFORMATIMAGO.CLEXT.PIPE"`` that implements
a unidirectional pipe using ``"BORDEAUX-THREADS"`` and ``"TRIVIAL-GRAY-STREAM"``.
The data written to the ``PIPE-OUTPUT-STREAM`` is queued (if a maximum
queue-size is specified for the stream, then the writing
thread may block if the buffer is full).
The data queued can be read from the ``PIPE-INPUT-STREAM``. If the
queue is empty, then the reading stream may block (unless it
used ``LISTEN``, ``READ-CHAR-NO-HANG``, etc).
When the stream is closed, one can still read from it until
the end of file is reached.
Multiple threads may read or write to the same pipe.
To demonstrate the use of pipes, the ``"COM.INFORMATIMAGO.CLEXT.FILTER"``
package exports a GREP function that processes ``*STANDARD-INPUT*`` and
writes to ``*STANDARD-OUTPUT*``, and a FILTER macro that builds a pipe
chain of any number of forms, each running in its own thread (but the
last one which runs in the calling thread).
It also provides a function IN that reads a text file and writes it to
``*STANDARD-OUTPUT*``, and a function OUT that reads ``*STANDARD-INPUT*`` and
writes it to a file. (They correspond to the unix redirections ``<`` ``>``
and ``>>``).
``COM.INFORMATIAMGO.COMMON-LISP.INTERACTIVE.BROWSER:MORE`` and ``CAT``
have been updated to read from ``*STANDARD-INPUT*`` when no file is
given, thus working as filters.
::
user1> (pprint (macroexpand-1 '(filter (in #P"~/tmp/misc/wang")
(grep "LISP" :line-number t)
(out "/tmp/wl.txt"))))
(let ((#1=#:|pipe77885| (com.informatimago.clext.pipe:make-pipe
:buffer-size com.informatimago.clext.filter::*buffer-size*)))
(bordeaux-threads:make-thread
(lambda nil
(unwind-protect
(filter (in #P"/Users/pjb/tmp/misc/wang") (grep "LISP" :line-number t))
(close *standard-output*)))
:initial-bindings
(list (cons '*standard-output* (com.informatimago.clext.pipe:pipe-output-stream #1#))
(cons '*standard-input* *standard-input*)))
(let ((*standard-input* (com.informatimago.clext.pipe:pipe-input-stream #1#)))
(out "/tmp/wl.txt")))
; No value
user1>
Example: ::
cl-user> (mkupack :use '("COMMON-LISP"
"COM.INFORMATIMAGO.COMMON-LISP.INTERACTIVE.BROWSER"
"COM.INFORMATIMAGO.CLEXT.FILTER"))
#<Package "USER1">
user1> (filter (in #P"~/tmp/misc/wang"))
Hao Wang, logicien americain.
L'algorithme en question a ete publie en 1960 dans l'IBM Journal,
article intitule "Toward Mechanical Mathematics", avec des variantes et
une extension au calcul des predicats. Il s'agit ici du "premier
programme" de Wang, systeme "P".
L'article a ete ecrit en 1958, et les experiences effectuees sur IBM 704
- machine a lampes, 32 k mots de 36 bits, celle-la meme qui vit naitre
LISP a la meme epoque. Le programme a ete ecrit en assembleur (Fortran
existait, mais il ne s'etait pas encore impose) et l'auteur estime que
"there is very little in the program that is not straightforward".
Il observe que les preuves engendrees sont "essentiellement des arbres",
et annonce que la machine a demontre 220 theoremes du calcul des
propositions (tautologies) en 3 minutes. Il en tire argument pour la
superiorite d'une approche algorithmique par rapport a une approche
heuristique comme celle du "Logic Theorist" de Newell, Shaw et Simon (a
partir de 1956 sur la machine JOHNNIAC de la Rand Corporation): un debat
qui dure encore...
Cet algorithme a ete popularise par J. McCarthy, comme exemple-fanion
d'application de LISP. Il figure dans le manuel de la premiere version
de LISP (LISP 1, sur IBM 704 justement, le manuel est date de Mars
1960), et il a ete repris dans le celebre "LISP 1.5 Programmer's Manual"
publie en 1962 par MIT Press, un des maitres-livres de l'Informatique.
nil
user1> (filter (in #P"~/tmp/misc/wang") (grep "LISP"))
LISP a la meme epoque. Le programme a ete ecrit en assembleur (Fortran
d'application de LISP. Il figure dans le manuel de la premiere version
de LISP (LISP 1, sur IBM 704 justement, le manuel est date de Mars
1960), et il a ete repris dans le celebre "LISP 1.5 Programmer's Manual"
nil
user1> (filter (in #P"~/tmp/misc/wang") (grep "program" :case-insensitive t))
programme" de Wang, systeme "P".
LISP a la meme epoque. Le programme a ete ecrit en assembleur (Fortran
"there is very little in the program that is not straightforward".
1960), et il a ete repris dans le celebre "LISP 1.5 Programmer's Manual"
nil
user1> (filter (in #P"~/tmp/misc/wang")
(grep " " :line-number t)
(let ((*terminal-height* 7)) (more)))
1:Hao Wang, logicien americain.
2:
3:L'algorithme en question a ete publie en 1960 dans l'IBM Journal,
4:article intitule "Toward Mechanical Mathematics", avec des variantes et
5:une extension au calcul des predicats. Il s'agit ici du "premier
6:programme" de Wang, systeme "P".
7:
8:L'article a ete ecrit en 1958, et les experiences effectuees sur IBM 704
Type RETURN for next page:
9:- machine a lampes, 32 k mots de 36 bits, celle-la meme qui vit naitre
10:LISP a la meme epoque. Le programme a ete ecrit en assembleur (Fortran
11:existait, mais il ne s'etait pas encore impose) et l'auteur estime que
12:"there is very little in the program that is not straightforward".
13:
14:Il observe que les preuves engendrees sont "essentiellement des arbres",
15:et annonce que la machine a demontre 220 theoremes du calcul des
Type RETURN for next page:
16:propositions (tautologies) en 3 minutes. Il en tire argument pour la
17:superiorite d'une approche algorithmique par rapport a une approche
18:heuristique comme celle du "Logic Theorist" de Newell, Shaw et Simon (a
19:partir de 1956 sur la machine JOHNNIAC de la Rand Corporation): un debat
20:qui dure encore...
21:
22:Cet algorithme a ete popularise par J. McCarthy, comme exemple-fanion
Type RETURN for next page:
23:d'application de LISP. Il figure dans le manuel de la premiere version
24:de LISP (LISP 1, sur IBM 704 justement, le manuel est date de Mars
25:1960), et il a ete repris dans le celebre "LISP 1.5 Programmer's Manual"
26:publie en 1962 par MIT Press, un des maitres-livres de l'Informatique.
27:
28:
29:
Type RETURN for next page:
; No value
user1> (filter (in #P"~/tmp/misc/wang") (grep "LISP" :line-number t) (out "/tmp/wl.txt"))
nil
user1> (cat "/tmp/wl.txt")
10:LISP a la meme epoque. Le programme a ete ecrit en assembleur (Fortran
23:d'application de LISP. Il figure dans le manuel de la premiere version
24:de LISP (LISP 1, sur IBM 704 justement, le manuel est date de Mars
25:1960), et il a ete repris dans le celebre "LISP 1.5 Programmer's Manual"
; No value
user1>
Voici un nouveau paquetage, ``"COM.INFORMATIMAGO.CLEXT.PIPE"`` qui
implémente un tube unidirectionel («pipe») basé sur
``"BORDEAUX-THREADS"`` et ``"TRIVIAL-GRAY-STREAM"``.
Les données écrites sur le flux de sortie ``PIPE-OUTPUT-STREAM`` sont
mise en queue (si une taille de queue maximale est spécifiée, alors le
fil qui écrit peut être bloqué lorsque le tampon est plein).
Les données dans la queue peuvent être lues du flux d'entrée
``PIPE-INPUT-STREAM``. Si la queue est vide, alors le fil lecteur
pourra être bloqué jusqu'à ce que des données soient disponibles
(à moins que ``LISTEN``, ``READ-CHAR-NO-HANG``, etc ne soient utilisés).
Quand le flux de sortie est fermé, on peut encore lire les données
dans la queue avec le flux d'entrée jusqu'à ce que la fin de fichier
soit atteinte.
Plusieurs fils peuvent lire ou écrire sur le même tuyau.
Pour démontrer l'utilisation de ces tuyaux, le paquetage
``"COM.INFORMATIMAGO.CLEXT.FILTER"`` exporte un fonction ``GREP`` qui
lit ``*STANDARD-INPUT*`` et écrit sur ``*STANDARD-OUTPUT*``, et une
macro ``FILTER`` qui construit une chaîne de tuyau contenant n'importe
quel nombre de formes, chacune tournant dans son fil propre (sauf la
dernière qui est évaluée dans le fil appelant)
Il fournit aussi une fonction ``IN`` qui lit un fichier texte et le
copie sur ``*STANDARD-OUTPUT*``, et une fonction ``OUT`` qui lit
``*STANDARD-INPUT*`` let le copie dans un fichier. (Elles
correspondent aux redirections `unix` : ``<`` ``>`` et ``>>``).
``COM.INFORMATIAMGO.COMMON-LISP.INTERACTIVE.BROWSER:MORE`` et ``CAT``
ont été mises à jours pour être capable de lire ``*STANDARD-INPUT*``
quand aucun fichier n'est donné, et fonctionnant ainsi comme filtres.
::
user1> (pprint (macroexpand-1 '(filter (in #P"~/tmp/misc/wang")
(grep "LISP" :line-number t)
(out "/tmp/wl.txt"))))
(let ((#1=#:|pipe77885| (com.informatimago.clext.pipe:make-pipe
:buffer-size com.informatimago.clext.filter::*buffer-size*)))
(bordeaux-threads:make-thread
(lambda nil
(unwind-protect
(filter (in #P"/Users/pjb/tmp/misc/wang") (grep "LISP" :line-number t))
(close *standard-output*)))
:initial-bindings
(list (cons '*standard-output* (com.informatimago.clext.pipe:pipe-output-stream #1#))
(cons '*standard-input* *standard-input*)))
(let ((*standard-input* (com.informatimago.clext.pipe:pipe-input-stream #1#)))
(out "/tmp/wl.txt")))
; No value
user1>
Exemple: ::
cl-user> (mkupack :use '("COMMON-LISP"
"COM.INFORMATIMAGO.COMMON-LISP.INTERACTIVE.BROWSER"
"COM.INFORMATIMAGO.CLEXT.FILTER"))
#<Package "USER1">
user1> (filter (in #P"~/tmp/misc/wang"))
Hao Wang, logicien americain.
L'algorithme en question a ete publie en 1960 dans l'IBM Journal,
article intitule "Toward Mechanical Mathematics", avec des variantes et
une extension au calcul des predicats. Il s'agit ici du "premier
programme" de Wang, systeme "P".
L'article a ete ecrit en 1958, et les experiences effectuees sur IBM 704
- machine a lampes, 32 k mots de 36 bits, celle-la meme qui vit naitre
LISP a la meme epoque. Le programme a ete ecrit en assembleur (Fortran
existait, mais il ne s'etait pas encore impose) et l'auteur estime que
"there is very little in the program that is not straightforward".
Il observe que les preuves engendrees sont "essentiellement des arbres",
et annonce que la machine a demontre 220 theoremes du calcul des
propositions (tautologies) en 3 minutes. Il en tire argument pour la
superiorite d'une approche algorithmique par rapport a une approche
heuristique comme celle du "Logic Theorist" de Newell, Shaw et Simon (a
partir de 1956 sur la machine JOHNNIAC de la Rand Corporation): un debat
qui dure encore...
Cet algorithme a ete popularise par J. McCarthy, comme exemple-fanion
d'application de LISP. Il figure dans le manuel de la premiere version
de LISP (LISP 1, sur IBM 704 justement, le manuel est date de Mars
1960), et il a ete repris dans le celebre "LISP 1.5 Programmer's Manual"
publie en 1962 par MIT Press, un des maitres-livres de l'Informatique.
nil
user1> (filter (in #P"~/tmp/misc/wang") (grep "LISP"))
LISP a la meme epoque. Le programme a ete ecrit en assembleur (Fortran
d'application de LISP. Il figure dans le manuel de la premiere version
de LISP (LISP 1, sur IBM 704 justement, le manuel est date de Mars
1960), et il a ete repris dans le celebre "LISP 1.5 Programmer's Manual"
nil
user1> (filter (in #P"~/tmp/misc/wang") (grep "program" :case-insensitive t))
programme" de Wang, systeme "P".
LISP a la meme epoque. Le programme a ete ecrit en assembleur (Fortran
"there is very little in the program that is not straightforward".
1960), et il a ete repris dans le celebre "LISP 1.5 Programmer's Manual"
nil
user1> (filter (in #P"~/tmp/misc/wang")
(grep " " :line-number t)
(let ((*terminal-height* 7)) (more)))
1:Hao Wang, logicien americain.
2:
3:L'algorithme en question a ete publie en 1960 dans l'IBM Journal,
4:article intitule "Toward Mechanical Mathematics", avec des variantes et
5:une extension au calcul des predicats. Il s'agit ici du "premier
6:programme" de Wang, systeme "P".
7:
8:L'article a ete ecrit en 1958, et les experiences effectuees sur IBM 704
Type RETURN for next page:
9:- machine a lampes, 32 k mots de 36 bits, celle-la meme qui vit naitre
10:LISP a la meme epoque. Le programme a ete ecrit en assembleur (Fortran
11:existait, mais il ne s'etait pas encore impose) et l'auteur estime que
12:"there is very little in the program that is not straightforward".
13:
14:Il observe que les preuves engendrees sont "essentiellement des arbres",
15:et annonce que la machine a demontre 220 theoremes du calcul des
Type RETURN for next page:
16:propositions (tautologies) en 3 minutes. Il en tire argument pour la
17:superiorite d'une approche algorithmique par rapport a une approche
18:heuristique comme celle du "Logic Theorist" de Newell, Shaw et Simon (a
19:partir de 1956 sur la machine JOHNNIAC de la Rand Corporation): un debat
20:qui dure encore...
21:
22:Cet algorithme a ete popularise par J. McCarthy, comme exemple-fanion
Type RETURN for next page:
23:d'application de LISP. Il figure dans le manuel de la premiere version
24:de LISP (LISP 1, sur IBM 704 justement, le manuel est date de Mars
25:1960), et il a ete repris dans le celebre "LISP 1.5 Programmer's Manual"
26:publie en 1962 par MIT Press, un des maitres-livres de l'Informatique.
27:
28:
29:
Type RETURN for next page:
; No value
user1> (filter (in #P"~/tmp/misc/wang") (grep "LISP" :line-number t) (out "/tmp/wl.txt"))
nil
user1> (cat "/tmp/wl.txt")
10:LISP a la meme epoque. Le programme a ete ecrit en assembleur (Fortran
23:d'application de LISP. Il figure dans le manuel de la premiere version
24:de LISP (LISP 1, sur IBM 704 justement, le manuel est date de Mars
25:1960), et il a ete repris dans le celebre "LISP 1.5 Programmer's Manual"
; No value
user1>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment