Ya usaste flujos en channel-chatter y te deshiciste de ellos en boatswains-bilge. Este ejercicio es tu primer flujo personalizado: una clase que implementa el protocolo, para que el mismo código de with-input-stream / read1 / stream-read1 que funcionaba con un <string-reader> funcione también con el tuyo.
Ya conoces TUPLE: child < parent … de dragons-descendants: una jerarquía de un solo padre en la que los slots descienden. Factor tiene una segunda forma de extender el grafo de clases: los mixins abiertos. Un MIXIN: m declara una clase sin slots a la que otras clases se pueden unir más tarde con INSTANCE: c m. La pertenencia es de muchos a muchos: una clase puede ser instancia de tantos mixins como quiera.
El protocolo de flujos usa dos mixins para marcar cuál de las dos mitades de la interfaz implementa una clase:
INSTANCE: my-stream input-stream ! readable
INSTANCE: my-stream output-stream ! writable
Un flujo puede ser una, la otra, o ambas. Los métodos genéricos de input-stream (o output-stream) despachan entonces a cualquier clase que se haya unido al mixin: así es como los read1, stream-contents, with-input-stream, etc. del entorno encuentran el camino hacia tu clase.
Los mixins son abiertos: puedes agregar un nuevo miembro después de que se declara el mixin, incluso en un vocabulario distinto. Ese es el contraste con TUPLE: < parent, donde la relación queda fija al declarar la clase hija.
Los genéricos del protocolo viven en io:
stream-read1 ( stream -- elt/f )
stream-read ( n stream -- seq/f )
stream-read-unsafe ( n buf stream -- count )
stream-write1 ( elt stream -- )
stream-write ( seq stream -- )
stream-flush ( stream -- )
stream-element-type ( stream -- type )
stream-read1 devuelve el siguiente byte (o f al llegar al final del flujo); stream-write1 escribe uno. stream-read-unsafe es la lectura por lotes de más bajo nivel que ofrecen la mayoría de los flujos de entrada; stream-read se construye sobre ella y se encarga de la asignación. stream-element-type indica si el flujo trabaja con bytes (+byte+) o con caracteres (+character+); cuando envuelves otro flujo, la implementación más sencilla simplemente delega en el flujo subyacente.
destructors
Los flujos se pueden desechar: un flujo personalizado extiende disposable e implementa dispose*. Ese es el framework de boatswains-bilge, aplicado a una clase de flujo:
TUPLE: my-stream < disposable underlying ;
: <my-stream> ( underlying -- s )
my-stream new-disposable swap >>underlying ;
M: my-stream dispose* underlying>> dispose ;
Una vez que eso está en su lugar, todos los combinadores with-… de channel-chatter funcionan en tu flujo automáticamente.
Eres el telegrafista inalámbrico de un carguero. La señal llega por una línea ruidosa (la maquinaria del barco, la estática del clima, algún parloteo extranjero ocasional), intercalada con el Morse que de verdad estás escuchando. Para captar el mensaje, pasas el cable por un dispositivo de cinta que elimina todo excepto los símbolos Morse válidos (., - y el espacio que separa las letras).
Tu trabajo es construir la cinta como un flujo de entrada personalizado de Factor.
Define un tuple tape que extiende disposable y guarda un slot, wire, para el flujo de entrada subyacente.
TUPLE: tape < disposable wire ;
Luego escribe el constructor <tape> ( wire -- tape ). Usa new-disposable (no new) para que el framework del destructor registre la cinta, y guarda el flujo subyacente en el slot wire.
Declara que tape es un input-stream:
INSTANCE: tape input-stream
Eso hace que los combinadores predeterminados del protocolo despachen a tu clase.
Implementa M: tape stream-read1 ( tape -- elt/f ). Lee bytes del cable subyacente hasta que encuentres uno de CHAR: ., CHAR: - o CHAR: \s (espacio). Devuelve ese byte. Si el cable devuelve f (fin del flujo), devuelve f también: la cinta terminó.
"x.y -ab cz" <string-reader> <tape>
[ stream-read1 ] [ stream-read1 ] [ stream-read1 ] tri
! => CHAR: . CHAR: - CHAR: \s
Implementa M: tape stream-element-type ( tape -- type ) delegando en el cable: la cinta no cambia el tipo de elementos que se leen.
Implementa M: tape dispose* ( tape -- ). El framework llama a tu método exactamente una vez por cada dispose; solo libera el cable subyacente.
Con esas cinco piezas en su lugar, la cinta funciona como un flujo de entrada normal: with-input-stream, stream-read y las demás palabras del protocolo despachan a tus métodos automáticamente.
Regístrate en Exercism para aprender y dominar Factor con 47 conceptos163 ejercicios y mentoría humana real, todo gratis.