; .EnTete "Programmer en Ceyx" "" "Annexe V: Les Flux Line'aires"
; .Annexe V "Les Flux Line'aires"
; .Auteur "Jean-Marie Hullot"
; .i
; Ce chapitre est un exemple d'utilisation de classes auto-type'es
; c'est a` dire de types de'finis par deftclass. On y trouvera
; aussi des exemples d'utilisation de la construction defrule.
; On trouvera ici des exemples tre`s caracte'ristiques de
; l'utilisation de la construction send.
; 
; Toutes les fonctions de'crites ici sont utilisables sous Ceyx a`
; condition d'avoir charge' le fichier stream.ll de la bibliothe`que
; Ceyx, qui n'est d'ailleurs pas autre chose que ce chapitre:
; .r
; 
; .DebLL 1
; ? (ceyx-load stream)
; = stream.ll
; .FinLL
; .pp
; Le premier but suivi avec l'introduction des flux est
; la prise de contro↑le sur les entre'es sorties de Lisp.
; Le principe de la manoeuvre est d'intercaler
; entre le clavier, sur lequel Le←Lisp prend son input et l'e'cran, sur
; lequel il visualise son output, autant de machines virtuelles
; interme'diaires qu'il est ne'cessaire pour de'composer le traitement
; d'une application donne'e.

(ceyx-load defrule)

; .Section "Les Flux"
; Nous de'finissons une classe ge'ne'rique \fBStream\fR
; posse'dant deux champs:
;     - \fIsource\fR, qui pointe sur un objet fournissant de l'input a` la
; stream, d'une manie`re qui reste a` de'terminer,
;     - \fIdestination\fR, qui pointe sur un objet vers lequel la stream
; dirige son output, d'un manie`re qui reste elle-aussi a` de'terminer.

(deftclass Stream
                source
                destination)

(defmake {Stream} Stream ())

; .ps -2
; .TS
; center box tab (|);
; c s
; c c.
; T{
; .ps +1
; \fBLe Type: #:Tclass:Stream\fR
; .ps -1
; T}
;  | 
; ←
; ←
; .T&
; c s.
; T{
; \fBAbre'viation: \fRStream.
; T}
; ←
; .T&
; c s
; l l.
; \fBFonction de Cre'ation\fR
; Stream|()
; ←
; .T&
; c s
; l l.
; \fBChamps\fR
; class-attributes|(Vector ...)
; source|*
; destination|*
; ←
; .T&
; c s
; l l.
; \fBProprie'te's Se'mantiques\fR
; close|(stream)
; connect|(stream1 stream2)
; destination|obj
; open|(stream)
; source|obj
; ←
; .T&
; c s
; l s.
; \fBSous Mode`le\fR
; LinearStream
; .TE
; .ps +2


; Connecter stream1 avec stream2 consiste a` faire pointer la destination
; de stream1 sur stream2 et la source de stream2 sur stream1:

(de {Stream}:connect (stream1 stream2)
    ({Stream}:destination stream1 stream2)
    ({Stream}:source stream2 stream1)
    t)

;  Les proprie'te's par de'faut d'ouverture et de fermeture de flux:

(de {Stream}:open (stream))
(de {Stream}:close (stream))

; \fBExemples:\fR
; 
; .DebLL 1
; ? (setq stream1 (Stream))
; = #(#:Tclass:Stream . #[() ()])
; ? (setq stream2 (Stream))
; = #(#:Tclass:Stream . #[() ()])
; ? (sendq connect stream1 stream2)
; = t
; ? (sendq source stream1)
; = ()
; ? (eq stream1 (sendq source stream2))
; = t
; ? (eq stream2 (sendq destination stream1))
; = t
; ? (sendq destination stream2)
; = ()
; .FinLL
; .pp
; Pour connecter temporairement une source ou une destination:

(defmacro with-source (stream source . body)
     `(let ((st ,stream))
           (let ((osource ({Stream}:source st)))
                (protect
                  (progn
                     ({Stream}:source st ,source)
                     ,@body)
                  ({Stream}:source st osource)))))

(defmacro with-destination (stream destination . body)
     `(let ((st ,stream))
           (let ((odestination ({Stream}:destination st)))
                (protect
                  (progn
                     ({Stream}:destination st ,destination)
                     ,@body)
                  ({Stream}:destination st odestination)))))


; .Section "Les Flux Line'aires"
; .SSection "De'finition"

; Jusqu'a` une pe'riode re'cente, les entre'es sorties ont essentiellement
; e'te' oriente'es caracte`res. Pour traiter ce type d'entre'es sorties
; Ceyx propose la notion de flux line'aire.

(deftclass {Stream}:LinearStream)

; Nous allons de'finir par la suite plusieurs mode`les de flux line'aires
; qui vont tous suivre une me↑me ide'e de construction, l'imple'mentation
; e'tant elle particulie`re a` chaque cas. L'ide'e conductrice est la
; suivante: une \fBLinearStream\fR posse`de un buffer de stockage de dimension
; fixe ou illimite'e selon les cas et maintient sur ce buffer deux pointeurs:
; 
;     - un pointeur dit de lecture \fIinpos\fR,
;     - un pointeur dit d'e'criture \fIoutpos\fR.
; 
; Nous repre'sentons picturalement le buffer et ses deux pointeurs, dans le
; cas d'un flux de caracte`res de taille 10 par:
; 
; .ps -2
; .TS
; tab (|);
; | c | c c c c c c c c c 
;  | c | c | c | c | c | c | c | c | c | c|
; | c | c c c c c c c c c .
; W| | | | | | | | | 
; ←
;  | | | | | | | | | 
; ←
; R| | | | | | | | | 
; .TE
; .ps +2
; 
; ou` R de'signe le pointeur de lecture et W le pointeur d'e'criture.
; Pour e'crire un caracte`re dans ce flux, il suffit de lui envoyer le
; message \fInew\fR avec le code ascii du caracte`re comme argument.
; Ceci a  a pour effet d'introduire ce caracte`re dans le flux a` la
; position outpos et de de'caler outpos d'un cran vers la droite
; 
; .DebLL 1
; ? (sendq new stream #/a)
; = 97
; .FinLL
; .pp
; 
; .ps -2
; .TS
; tab (|);
; c | c | c c c c c c c c 
;  | c | c | c | c | c | c | c | c | c | c|
; | c | c c c c c c c c c .
;  |W| | | | | | | | 
; ←
; a| | | | | | | | | 
; ←
; R| | | | | | | | | 
; .TE
; .ps +2
; 
; Ecrivons un nouveau caracte`re dans ce flux:
; 
; .DebLL 1
; ? (sendq new stream #/b)
; = 98
; .FinLL
; .pp
; 
; .ps -2
; .TS
; tab (|);
; c c | c | c c c c c c c 
;  | c | c | c | c | c | c | c | c | c | c|
; | c | c c c c c c c c c .
;  | |W| | | | | | | 
; ←
; a|b| | | | | | | | 
; ←
; R| | | | | | | | | 
; .TE
; .ps +2
; 
; Pour lire un caracte`re dans le flux il suffit de lui envoyer le message
; \fInext\fR sans arguments. Le code ascii du caracte`re a` la place inpos
; dans le flux est alors renvoye' en valeur et le pointeur de lecture est
; avance' d'un cran:
; 
; .DebLL 1
; ? (sendq next stream)
; = 97
; .FinLL
; .pp
; 
; .ps -2
; .TS
; tab (|);
; c c | c | c c c c c c c 
;  | c | c | c | c | c | c | c | c | c | c|
; c | c | c c c c c c c c .
;  | |W| | | | | | | 
; ←
; a|b| | | | | | | | 
; ←
;  |R| | | | | | | | 
; .TE
; .ps +2
; 
; Un nouvel appel a` \fInext\fR nous met dans la situation:
; 
; .DebLL 1
; ? (sendq next stream)
; = 98
; .FinLL
; .pp
; .ps -2
; .TS
; tab (|);
; c c | c | c c c c c c c 
;  | c | c | c | c | c | c | c | c | c | c|
; c c | c | c c c c c c c .
;  | |W| | | | | | | 
; ←
; a|b| | | | | | | | 
; ←
;  | |R| | | | | | | 
; .TE
; .ps +2
; 
; Le pointeur de lecture est alors e'gal au pointeur d'e'criture.
; Nous dirons que le flux est vide, dans le sens ou il n'y a plus
; rien a` lire.
; 
; .DebLL 1
; ? (sendq null stream)
; = 2
; .FinLL
; .pp
; 
; Comme il n'y a plus rien a` lire, le flux va re'agir au message \fInext\fR
; en relayant ce message sur  sa source. Nous introduisons pour ce
; faire un second flux stream1, tel que la source de stream vaille
; stream1. Et nous e'crivons les caracte`res c et d dans ce flux:
; 
; .bp
; STREAM1
; 
; .ps -2
; .TS
; tab (|);
; c c | c | c c 
;  | c | c | c | c | c|
; | c | c c c c .
;  | |W| | 
; ←
; c|d| | | 
; ←
; R| | | | 
; .TE
; .ps +2
; 
; 
; STREAM
; 
; .ps -2
; .TS
; tab (|);
; c c | c | c c c c c c c 
;  | c | c | c | c | c | c | c | c | c | c|
; c c | c | c c c c c c c .
;  | |W| | | | | | | 
; ←
; a|b| | | | | | | | 
; ←
;  | |R| | | | | | | 
; .TE
; .ps +2
; 
; Si nous envoyons maintenant \fInext\fR a` stream, ce message est relaye'
; sur sa source stream1, et le re'sultat est e'crit dans stream puis lu.
; 
; .DebLL 1
; ? (sendq next stream)
; = 99
; .FinLL
; 
; STREAM1
; 
; .ps -2
; .TS
; tab (|);
; c c | c | c c 
;  | c | c | c | c | c|
; c | c | c c c .
;  | |W| | 
; ←
; c|d| | | 
; ←
;  |R| | | 
; .TE
; .ps +2
; 
; STREAM
; 
; .ps -2
; .TS
; tab (|);
; c c c | c | c c c c c c 
;  | c | c | c | c | c | c | c | c | c | c|
; c c c | c | c c c c c c .
;  | | |W| | | | | | 
; ←
; a|b|c| | | | | | | 
; ←
;  | | |R| | | | | | 
; .TE
; .ps +2
; 
; Une autre ope'ration inte'ressante sur les flux line'aires, consiste
; a` tranfe'rer l'information contenue dans un flux dans un autre flux.
; Cette ope'ration est re'alise'e en envoyant le message \fIbltstream\fR
; aux deux flux stream1 et stream. L'ope'ration \fIbltstream\fR est
; imple'mente'e comme une re`gle Ceyx, de manie`re que la fac/on 
; dont s'effectue le transfert de'pende a` la foix du type de
; stream et de stream1, permettant ainsi une imple'mentation
; optimale pour chaque situation du transfert. Par de'faut, le
; tranfert s'effectue au caracte`re par caracte`re.
; 
; .DebLL 1
; ? (sendq new stream1 #/e)
; = 101
; ? (sendq new stream1 #/f)
; = 102
; .FinLL
; 
; STREAM1
; 
; .ps -2
; .TS
; tab (|);
; c c c c | c | 
;  | c | c | c | c | c|
; c | c | c c c .
;  | | | |W
; ←
; c|d|e|f| 
; ←
;  |R| | | 
; .TE
; .ps +2
; 
; 
; STREAM
; 
; .ps -2
; .TS
; tab (|);
; c c c | c | c c c c c c 
;  | c | c | c | c | c | c | c | c | c | c|
; c c c | c | c c c c c c .
;  | | |W| | | | | | 
; ←
; a|b|c| | | | | | | 
; ←
;  | | |R| | | | | | 
; .TE
; .ps +2
; 
; Nous transfe'rons le contenu de stream1 dans stream:
; 
; .DebLL 1
; ? (sendq bltstream stream1 stream)
; = 4
; .FinLL
; 
; STREAM1
; 
; .ps -2
; .TS
; tab (|);
; c c c c | c | 
;  | c | c | c | c | c|
; c c c c | c | .
;  | | | |W
; ←
; c|d|e|f| 
; ←
;  | | | |R
; .TE
; .ps +2
; 
; 
; STREAM
; 
; .ps -2
; .TS
; tab (|);
; c c c c c c | c | c c c 
;  | c | c | c | c | c | c | c | c | c | c|
; c c c | c | c c c c c c .
;  | | | | | |W| | | 
; ←
; a|b|c|d|e|f| | | | 
; ←
;  | | |R| | | | | | 
; .TE
; .ps +2
; 
; 
; Pour nettoyer un flux, c'est a`dire pour remettre a` ze'ro les
; pointeurs de lecture et d'e'criture, on lui envoie le message
; \fIclear\fR:
; 
; .DebLL 1
; ? (sendq clear stream1)
; = 0
; .FinLL
; 
; STREAM1
; 
; .ps -2
; .TS
; tab (|);
; | c | c c c c 
;  | c | c | c | c | c|
; | c | c c c c .
; W| | | | 
; ←
;  | | | | 
; ←
; R| | | | 
; .TE
; .ps +2
; 
; 
; STREAM
; 
; .ps -2
; .TS
; tab (|);
; c c c c c c | c | c c c 
;  | c | c | c | c | c | c | c | c | c | c|
; c c c | c | c c c c c c .
;  | | | | | |W| | | 
; ←
; a|b|c|d|e|f| | | | 
; ←
;  | | |R| | | | | | 
; .TE
; .ps +2
; 
; 
; Nous de'finissons un certain nombre d'autres ope'rations sur les flux
; line'aires:
; 
; \(bu \fIflush\fR qui consiste a`
; transfe'rer le contenu d'un flux dans sa destination, puis a`
; remettre a` ze'ro son buffer en lui envoyant le message clear;
; 
; \(bu \fInext?\fR qui permet de tester s'il y a quelque chose a` lire
; dans le flux ou dans un des flus chai↑ne's en source et, si oui, de
; le lire.
; 
; \(bu \fIpeek\fR qui fait un \fInext\fR sans de'placer le pointeur de 
; lecture.
; 
; \(bu \fIreturn\fR qui remat a` ze'ro le pointeur de lecture et permet ainsi
; de recommencer une lecture.

; .ps -2
; .TS
; center box tab (|);
; c s
; c c.
; T{
; .ps +1
; \fBLe Type: #:Tclass:Stream:LinearStream\fR
; .ps -1
; T}
;  | 
; ←
; ←
; .T&
; c s.
; T{
; \fBAbre'viation: \fRLinearStream.
; T}
; ←
; .T&
; c s
; l l.
; \fBChamps\fR
; class-attributes|(Vector ...)
; source|*
; destination|*
; ←
; .T&
; c s
; l l.
; \fBProprie'te's Se'mantiques\fR
; bltstream|(x y)
; bltstream!1|(x y)
; bltstream!2|(x y)
; bltstream!3|(x y)
; bltstream!4|(stream dest)
; bltstream!5|(stream dest)
; destination|obj
; eos|(stream)
; flush|(stream)
; next?|(stream)
; class-attributes|obj
; source|obj
; ←
; .T&
; c s
; l s.
; \fBSous Mode`les\fR
; CharStream
; InChannel
; InputBuffer
; ListStream
; OutChannel
; OutputBuffer
; .TE
; .ps +2
; 
; Les bltstream!n sont des se'mantiqes interme'diares engendre'es par le
; defrule.

(de {LinearStream}:flush (stream)
    (sendq bltstream stream ({Stream}:destination stream))
    (sendq clear stream))

; En cas de de'dordement, on fait par de'faut un flush.

(de {LinearStream}:eos (stream)
    (sendq flush stream))

(defrule bltstream (x~{LinearStream} y~{LinearStream})
    (until (sendq null x)
           (sendq new y (sendq next x))))


; Pour voir s'il y a quelque chose a` lire et si oui le lire:

(de {LinearStream}:next? (stream)
    (if (sendq null stream)
        (sendq next? ({Stream}:source stream))
        (sendq next stream)))

; .SSection "Les Cas Limites"
; Les objets qu'on retrouve souvent aux extre↑mite's de chai↑nes
; de flux line'aires:
; 
; \(bu () joue le ro↑le de /dev/null

(de {null}:new (null val) ())

(de {null}:next (null)
    (syserror '{null}:next "Nothing in nil" ()))

(de {null}:flush (null))

(defrule bltstream (x~null y~{LinearStream})
    nil)

; \(bu le canal d'entre'e, tyi, tys dans le jargon des flux.

; .ps -2
; .TS
; center box tab (|);
; c s
; c c.
; T{
; .ps +1
; \fBLe Type: #:Tclass:Stream:LinearStream:InChannel\fR
; .ps -1
; T}
;  | 
; ←
; ←
; .T&
; c s.
; T{
; \fBAbre'viation: \fRInChannel.
; T}
; ←
; .T&
; c s
; l l.
; \fBFonction de Cre'ation\fR
; InChannel|()
; ←
; .T&
; c s
; l l.
; \fBChamps\fR
; class-attributes|(Vector ...)
; source|*
; destination|*
; ←
; .T&
; c s
; l l.
; \fBProprie'te's Se'mantiques\fR
; bltstream|(x y)
; destination|obj
; flush|(channel)
; next|(channel)
; next?|(channel)
; class-attributes|obj
; source|obj
; .TE
; .ps +2

(deftclass {LinearStream}:InChannel)
(defmake {InChannel} InChannel ())
(de {InChannel}:next (channel) (tyi))
(de {InChannel}:next? (channel) (tys))
(de {InChannel}:flush (channel) ())

(defrule bltstream (x~{InChannel} y~{LinearStream})
         nil)

; \(bu le canal de sortie, tyo, tyflush dans le jargon des flux.

; .ps -2
; .TS
; center box tab (|);
; c s
; c c.
; T{
; .ps +1
; \fBLe Type: #:Tclass:Stream:LinearStream:OutChannel\fR
; .ps -1
; T}
;  | 
; ←
; ←
; .T&
; c s.
; T{
; \fBAbre'viation: \fROutChannel.
; T}
; ←
; .T&
; c s
; l l.
; \fBFonction de Cre'ation\fR
; OutChannel|()
; ←
; .T&
; c s
; l l.
; \fBChamps\fR
; class-attributes|(Vector ...)
; source|*
; destination|*
; ←
; .T&
; c s
; l l.
; \fBProprie'te's Se'mantiques\fR
; destination|obj
; flush|(channel)
; new|(channel val)
; class-attributes|obj
; source|obj
; .TE
; .ps +2


(deftclass {LinearStream}:OutChannel)
(defmake {OutChannel} OutChannel ())
(de {OutChannel}:new (channel val) (tyo val))
(de {OutChannel}:flush (channel) (tyflush))
(de {OutChannel}:newline (channel)
    (when #:system:real-terminal-flag (tyo #\return))
    (tyo #\lf)
    (tyflush))

; .SSection "Les Flux de Caracte`res"
; Il s'agit de flux dont le buffer est imple'mente' comme un chai↑ne
; de caracte`res.

; .ps -2
; .TS
; center box tab (|);
; c s
; c c.
; T{
; .ps +1
; \fBLe Type: #:Tclass:Stream:LinearStream:CharStream\fR
; .ps -1
; T}
;  | 
; ←
; ←
; .T&
; c s.
; T{
; \fBAbre'viation: \fRCharStream.
; T}
; ←
; .T&
; c s
; l l.
; \fBFonction de Cre'ation\fR
; CharStream|(size)
; ←
; .T&
; c s
; l l.
; \fBChamps\fR
; class-attributes|(Vector ...)
; source|*
; destination|*
; string|*
; size|fix
; inpos|fix
; outpos|fix
; ←
; .T&
; c s
; l l.
; \fBProprie'te's Se'mantiques\fR
; clear|(stream)
; current|(stream)
; init|(stream size)
; inpos|obj
; new|(stream val)
; next|(stream)
; null|(stream)
; outpos|obj
; peek|(stream)
; return|(stream)
; size|obj
; string|obj
; .TE
; .ps +2


(deftclass {LinearStream}:CharStream
                string
                size~fix
                (inpos~fix 0)
                (outpos~fix 0))

(de CharStream (size)
    (sendq init (omakeq CharStream) size))

(defmake {CharStream} CharStream)

(de {CharStream}:init (stream size)
    (ochangeq CharStream stream
                string (makestring size #\sp)
                size size))


(de {CharStream}:clear (stream) 
     (fillstring ({CharStream}:string stream)
                 0
                 #\sp
                 ({CharStream}:size stream))
     ({CharStream}:inpos stream 0)
     ({CharStream}:outpos stream 0))

(de {CharStream}:null (stream)
    (= ({CharStream}:inpos stream) ({CharStream}:outpos stream)))

(de {CharStream}:current (stream)
    (chrnth ({CharStream}:inpos stream) ({CharStream}:string stream)))

(de {CharStream}:next (stream)
    (ifn (= ({CharStream}:inpos stream)
            ({CharStream}:outpos stream))
         (prog1
            ({CharStream}:current stream)
            ({CharStream}:inpos stream (1+ ({CharStream}:inpos stream))))
         ({CharStream}:new stream
                      (sendq next ({Stream}:source stream)))
         ({CharStream}:next stream)))

(de {CharStream}:peek (stream)
    (prog1
      ({CharStream}:next stream)
      ({CharStream}:inpos stream (1- ({CharStream}:inpos stream)))))

(de {CharStream}:return (stream)
    ({CharStream}:inpos stream 0))

(de {CharStream}:new (stream val)
    (cond
      ((= ({CharStream}:outpos stream)
          ({CharStream}:size stream))
       (sendq eos stream)
       ({CharStream}:new stream val))
      (t (chrset ({CharStream}:outpos stream)
                 ({CharStream}:string stream)
                 val)
         ({CharStream}:outpos stream (1+ ({CharStream}:outpos stream)))
         val)))


; .bp
; .SSection "Les Flux sous Forme de Liste"
; Ici le buffer est imple'mente' sous forme de liste. Ces flux 
; peuvent donc stocker un nombre arbitraire d'objets. De plus
; il peuvent stocker des objets quelconques et pas seulement des
; caracte`res.

; .ps -2
; .TS
; center box tab (|);
; c s
; c c.
; T{
; .ps +1
; \fBLe Type: #:Tclass:Stream:LinearStream:ListStream\fR
; .ps -1
; T}
;  | 
; ←
; ←
; .T&
; c s.
; T{
; \fBAbre'viation: \fRListStream.
; T}
; ←
; .T&
; c s
; l l.
; \fBFonction de Cre'ation\fR
; ListStream|()
; ←
; .T&
; c s
; l l.
; \fBChamps\fR
; class-attributes|(Vector ...)
; source|*
; destination|*
; list|*
; inpos|*
; outpos|*
; ←
; .T&
; c s
; l l.
; \fBProprie'te's Se'mantiques\fR
; clear|(stream)
; current|(stream)
; init|(stream)
; inpos|obj
; list|obj
; new|(stream val)
; next|(stream)
; null|(stream)
; outpos|obj
; peek|(stream)
; return|(stream)
; .TE
; .ps +2

(deftclass {LinearStream}:ListStream
                list
                inpos
                outpos)

(de ListStream ()
    (sendq init (omakeq {ListStream})))

(defmake {ListStream} ListStream)

(de {ListStream}:init (stream)
    (let ((buffer (list ())))
         (ochangeq {ListStream} stream
                list buffer
                inpos buffer
                outpos buffer)))

(demethod {ListStream}:clear (stream) (list)
    (rplacd list ())
    ({ListStream}:outpos stream list)
    ({ListStream}:inpos stream list))

(de {ListStream}:null (stream)
    (eq ({ListStream}:inpos stream)
        ({ListStream}:outpos stream)))

(de {ListStream}:current (stream)
    (car ({ListStream}:inpos stream)))

(de {ListStream}:next (stream)
    (ifn (eq ({ListStream}:inpos stream)
             ({ListStream}:outpos stream))
         (prog1
            (car ({ListStream}:inpos stream))
            ({ListStream}:inpos stream (cdr ({ListStream}:inpos stream))))
         ({ListStream}:new stream 
                           (sendq next ({Stream}:source stream)))
         ({ListStream}:next stream)))

(demethod {ListStream}:peek (stream) (inpos)
   (prog1
    ({ListStream}:next stream)
    ({ListStream}:inpos stream inpos)))

(de {ListStream}:return (stream)
    ({ListStream}:inpos stream ({ListStream}:list stream)))

(de {ListStream}:new (stream val)
    (rplaca ({ListStream}:outpos stream) val)
    (rplacd ({ListStream}:outpos stream) (list ()))
    ({ListStream}:outpos stream (cdr ({ListStream}:outpos stream))))

; .bp
; .SSection "Le Tampon d'Entre'e Le←Lisp"
; Pour manipuler le tampon d'entre'e Le←Lisp comme un flux line'aire:

; .ps -2
; .TS
; center box tab (|);
; c s
; c c.
; T{
; .ps +1
; \fBLe Type: #:Tclass:Stream:LinearStream:InputBuffer\fR
; .ps -1
; T}
;  | 
; ←
; ←
; .T&
; c s.
; T{
; \fBAbre'viation: \fRInputBuffer.
; T}
; ←
; .T&
; c s
; l l.
; \fBFonction de Cre'ation\fR
; InputBuffer|()
; ←
; .T&
; c s
; l l.
; \fBChamps\fR
; class-attributes|(Vector ...)
; source|*
; destination|*
; inpos|fix
; inmax|fix
; ←
; .T&
; c s
; l l.
; \fBProprie'te's Se'mantiques\fR
; bltstream|(stream dest)
; bol|(stream)
; clear|(stream)
; close|(stream)
; inmax|obj
; inpos|obj
; new|(stream val)
; newl|(stream l)
; next|(stream)
; null|(stream)
; open|(stream)
; peek|(stream)
; return|(stream)
; .TE
; .ps +2

(deftclass {LinearStream}:InputBuffer
                (inpos~fix 0)
                (inmax~fix 0))

(defmake {InputBuffer} InputBuffer)

(de InputBuffer () (omakeq {InputBuffer} source (InChannel)))

(de {InputBuffer}:open (stream)
    (inpos 0) (inmax 0))

(de {InputBuffer}:close (stream)
    (inpos 0) (inmax 0))

(de {InputBuffer}:clear (stream)
    ({InputBuffer}:inpos stream (inpos 0))
    ({InputBuffer}:inmax stream (inmax 0)))
    
(de {InputBuffer}:null (stream)
    (= (inmax) (inpos)))

(de {InputBuffer}:next (stream)
    (ifn (= (inpos) (inmax))
         (prog1 (inbuf (inpos))
                (inpos (1+ (inpos))))
         ({InputBuffer}:new stream (sendq next ({Stream}:source stream)))
         ({InputBuffer}:next stream)))

(de {InputBuffer}:peek (stream)
    (prog1
      ({InputBuffer}:next stream)
      (inpos (1- (inpos)))))

(de {InputBuffer}:return (stream)
    (inpos 0))

(de {InputBuffer}:new (stream val)
    (chrset (inmax) (inbuf) val)
    (inmax (1+ (inmax))))
    
; Pour faire l'ouput d'une liste de codes ascii dans le tampon d'entre'e:

(de {InputBuffer}:newl (stream l)
    (while l ({InputBuffer}:new stream (nextl l))))

(defrule bltstream (stream~InputBuffer dest~{LinearStream})
    (let ((n (inpos)))
      (until (= n (inmax))
           (sendq new dest (inbuf n))
           (inpos (inmax))
           (incr n))))

(de {InputBuffer}:bol (stream)
    ({InputBuffer}:peek stream))

; .bp
; .SSection "Le Tampon de Sortie Le←Lisp"
; Pour manipuler le tampon de sortie Le←Lisp comme un flux line'aire:

; .ps -2
; .TS
; center box tab (|);
; c s
; c c.
; T{
; .ps +1
; \fBLe Type: #:Tclass:Stream:LinearStream:OutputBuffer\fR
; .ps -1
; T}
;  | 
; ←
; ←
; .T&
; c s.
; T{
; \fBAbre'viation: \fROutputBuffer.
; T}
; ←
; .T&
; c s
; l l.
; \fBFonction de Cre'ation\fR
; OutputBuffer|()
; ←
; .T&
; c s
; l l.
; \fBChamps\fR
; class-attributes|(Vector ...)
; source|*
; destination|*
; outpos|fix
; lmargin|fix
; rmargin|fix
; ←
; .T&
; c s
; l l.
; \fBProprie'te's Se'mantiques\fR
; bltstream|(stream dest)
; clear|(stream)
; close|(stream)
; eol|(stream)
; getl|(obuf)
; lmargin|obj
; new|(stream val)
; null|(stream)
; open|(stream)
; outpos|obj
; rmargin|obj
; .TE
; .ps +2

(deftclass {LinearStream}:OutputBuffer
                (outpos~fix 0)
                (lmargin~fix 0)
                (rmargin~fix 79))

(defmake {OutputBuffer} OutputBuffer)

(de OutputBuffer () (omakeq {OutputBuffer} destination (OutChannel)))

(de {OutputBuffer}:open (stream)
    (lmargin ({OutputBuffer}:lmargin stream))
    (rmargin ({OutputBuffer}:rmargin stream))
    (clrbufout))

(de clrbufout ()
    ; pourquoi le fillstring merde-t-il sur outbuf?
    (let ((n 0)) (repeat (rmargin) (outbuf n #\sp) (incr n)))
    (outpos (lmargin)))


(de {OutputBuffer}:close (stream)
    ({OutputBuffer}:lmargin stream (lmargin))
    ({OutputBuffer}:rmargin stream (rmargin)))

(de {OutputBuffer}:clear (stream)
    (clrbufout))

(de {OutputBuffer}:null (stream)
    (= (outpos) 0))

(de {OutputBuffer}:new (stream val)
    (cond
       ((= (outpos) (rmargin))
        (eol)
        ({OutputBuffer}:new stream val))
       (t (outbuf (outpos) val)
          (outpos (1+ (outpos))))))

(defrule bltstream (stream~OutputBuffer dest~LinearStream)
    (let ((n 0))
         (until (= n (outpos))
                (sendq new dest (outbuf n))
                (incr n))))

; Pour re'cupe'rer le contenu du buffer de sortie comme une liste:

(de {OutputBuffer}:getl (obuf)
    (let ((l ())
          (n 0))
         (until (= n (outpos))
                (newl l (outbuf n))
                (incr n))
         (nreverse l)))

(de {OutputBuffer}:eol (stream)
    (sendq flush stream)
    (sendq newline ({Stream}:destination stream)))


; .Section "Flux d'Entre'e, de Sortie, d'Erreur"

(defvar {Lisp}:OutputBuffer (OutputBuffer))
(defvar {Lisp}:InputBuffer  (InputBuffer))

; Nous conservons dans des variables globales un flux d'entre'e courant,
; un flux de sortie courant, et un flux d'erreur courant. Ils sont
; initialise's avec le buffer d'entre'e Le←Lisp pour le premier et
; avec le buffer de sortie LeLisp pour les deux autres.

(defvar {CeyxSys}:instream {Lisp}:InputBuffer)
(defvar {CeyxSys}:outstream {Lisp}:OutputBuffer)
(defvar {CeyxSys}:errorstream {Lisp}:OutputBuffer)

(de {Ceyx}:bol () (sendq bol (instream)))
(de {Ceyx}:eol () (sendq eol (outstream)))

; Pour modifier le flux d'entre'e courant, on utilisera la fonction instream
; qui, appele'e avec un argument qui doit e↑tre une stream:
;   - ferme l'ancien flux courant,
;   - ouvre le flux passe' en argument,
;   - et le met dans la valeur de la variable {CeyxSys}:instream.
; 
; Appele'e sans argument, cette fonction rame`ne en valeur le flux
; d'entre'e courant, c'est a` dire la valeur de la variable 
; {CeyxSys}:instream.

(de instream arg
    (ifn arg {CeyxSys}:instream
             (sendq close {CeyxSys}:instream)
             (setq {CeyxSys}:instream (car arg))
             (sendq open {CeyxSys}:instream)
             {CeyxSys}:instream))


(de outstream arg
    (ifn arg {CeyxSys}:outstream
             (sendq close {CeyxSys}:outstream)
             (setq {CeyxSys}:outstream (car arg))
             (sendq open {CeyxSys}:outstream)
             {CeyxSys}:outstream))

(de errorstream arg
    (ifn arg {CeyxSys}:errorstream
             (sendq close {CeyxSys}:errorstream)
             (setq {CeyxSys}:errorstream (car arg))
             (sendq open {CeyxSys}:errorstream)
             {CeyxSys}:errorstream))