Já usaste streams em channel-chatter e descartaste-os em boatswains-bilge. Este exercício é o teu primeiro stream personalizado: uma classe que implementa o protocolo, para que o mesmo código with-input-stream / read1 / stream-read1 que funcionava num <string-reader> funcione no teu.
Já conheces TUPLE: child < parent … de dragons-descendants: uma hierarquia de herança simples, em que os slots passam de cima para baixo. O Factor tem uma segunda forma de estender o grafo de classes: os mixins abertos. Um MIXIN: m declara uma classe sem slots, à qual outras classes se podem juntar mais tarde com INSTANCE: c m. A associação é muitos-para-muitos: uma classe pode ser instância de tantos mixins quantos quiser.
O protocolo dos streams usa dois mixins para assinalar que metade da interface uma classe implementa:
INSTANCE: my-stream input-stream ! readable
INSTANCE: my-stream output-stream ! writable
Um stream pode ser um, o outro, ou ambos. Os métodos genéricos de input-stream (ou output-stream) são então despachados para qualquer classe que se tenha juntado ao mixin. É assim que o read1, o stream-contents, o with-input-stream, etc. que tens à disposição encontram o caminho até à tua classe.
Os mixins são abertos: podes acrescentar um novo membro depois de o mixin ser declarado, até num vocabulário diferente. É este o contraste com TUPLE: < parent, em que a relação fica fixada quando a subclasse é declarada.
Os genéricos do protocolo estão em 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 )
O stream-read1 devolve o byte seguinte (ou f no fim do stream); o stream-write1 escreve um. O stream-read-unsafe é a leitura em lote de mais baixo nível que a maioria dos streams de entrada disponibiliza; o stream-read é construído sobre ele e trata da alocação. O stream-element-type indica se o stream trabalha com bytes (+byte+) ou com carateres (+character+); quando envolves outro stream, a implementação mais simples limita-se a delegar no stream subjacente.
destructors
Os streams são descartáveis: um stream personalizado estende disposable e implementa dispose*. É esse o framework de boatswains-bilge, aplicado a uma classe de stream:
TUPLE: my-stream < disposable underlying ;
: <my-stream> ( underlying -- s )
my-stream new-disposable swap >>underlying ;
M: my-stream dispose* underlying>> dispose ;
Depois de isso estar pronto, todos os combinadores with-… de channel-chatter funcionam automaticamente no teu stream.
És o telegrafista sem fios de um cargueiro. O sinal chega por uma linha ruidosa (a maquinaria do navio, o estático do tempo, uma conversa estrangeira ocasional), entremeado com o verdadeiro Morse que estás a tentar ouvir. Para apanhares a mensagem, fazes passar o fio por um dispositivo tape que elimina tudo exceto os símbolos de Morse válidos (., - e o espaço que separa as letras).
O teu trabalho é construir a tape como um fluxo de entrada personalizado do Factor.
Define um tuplo tape que estende disposable e tem uma única fenda, wire, para o fluxo de entrada subjacente.
TUPLE: tape < disposable wire ;
Depois escreve o construtor <tape> ( wire -- tape ). Usa new-disposable (e não new) para que o framework de destruição registe a tape, e guarda o fluxo subjacente na fenda wire.
Declara tape como um input-stream:
INSTANCE: tape input-stream
Isso faz com que os combinadores predefinidos do protocolo sejam encaminhados para a tua classe.
Implementa M: tape stream-read1 ( tape -- elt/f ). Lê bytes do fio subjacente até encontrares um de CHAR: ., CHAR: - ou CHAR: \s (espaço). Devolve esse byte. Se o fio devolver f (fim do fluxo), devolve f também; a tape chegou ao fim.
"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 no fio: a tape não altera o tipo de elementos que são lidos.
Implementa M: tape dispose* ( tape -- ). O framework chama o teu método exatamente uma vez por cada dispose; basta libertares o fio subjacente.
Com estas cinco peças no lugar, a tape funciona como um fluxo de entrada normal: with-input-stream, stream-read e as restantes palavras do protocolo são encaminhadas automaticamente para os teus métodos.
Inscreve-te no Exercism para aprenderes e dominares Factor com 47 conceitos163 exercícios, e mentoria humana real, tudo grátis.