quinta-feira, 22 de abril de 2010

Parrot VM

Parrot Parrot VM é a máquina virtual criada com base nas linguagens de programação Python e Perl que proporciona o ambiente para a criação, a implementação e o uso de linguagens de programa. As linguagens suportadas pela máquina virtual estão listadas na página do projeto, com destaque para a curiosa e elegante linguagem tutorial Squaak.


Um dos exemplos de Squaak é factorial.sq:
# Copyright (C) 2008, Parrot Foundation.
# $Id: factorial.sq 36833 2009-02-17 20:09:26Z allison $

print("Please enter number: ")
x = read()

sub factorial(n)
## test for < 2 because all numbers are represented by
## floating-point numbers, and equality checks on those
## do not work (yet?)
if n < 2 then
return 1
else
return n * factorial(n - 1)
end
end

print("factorial of ", x, " is: ", factorial(x))


Voltando à Parrot VM, ela possui uma linguagem própria, um assembly.

Seguem aqui dois exemplos de seu assembly. Ambos gravam o que é digitado pelo usuário em um arquivo (test.text) e depois exibem o conteúdo do arquivo. Simples.

O objetivo de dois códigos é mostrar como pode ser feita a escrita e leitura em arquivo usando descritor de arquivo, de modo totalmente procedimental (como em C), e usando manipulador de arquivo, lançando mão da orientação a objetos.

Descritor de arquivo (procedimental)


.sub 'main' :main
.local string filename
.local string line
.local pmc STDIN
.local pmc fd

filename = 'test.text'
STDIN = getstdin

# Writing operation
fd = open filename, 'w'
print "> "
line = readline STDIN
print fd, line
close fd

# Reading operation
fd = open filename, 'r'
line = read fd, 10240
print line
close fd
.end


Manipulador de arquivo (OO)


.sub 'main' :main
.local string filename
.local string line
.local pmc STDIN
.local pmc file_handle

filename = 'test.text'
STDIN = getstdin
file_handle = new 'FileHandle'

# Writing operation
file_handle.'open'(filename, 'w')
file_handle.'encoding'('utf-8')
print "> "
line = readline STDIN
file_handle.'print'(line)
file_handle.'close'()

# Reading operation
file_handle.'open'(filename, 'r')
line = file_handle.'readall'()
print line
file_handle.'close'()
.end


[]’s
Cacilhας, La Batalema

domingo, 11 de abril de 2010

Coleções com Smalltalk

Smalltalk Dando seguimento a uma série de artigos sobre GNU Smalltalk, este artigo demonstra alguns recursos de coleção (arrays e dicionários).

A classe base para coleções é Collection, filha de Iterable. As demais classes, como Array, Dictionary, Bag e Set, são herdeiras diretas ou indiretas de Collection.

Para criar um instância vazia de qualquer subclasse de Collection, você pode usar a mensagem #new.

Array


Há duas sintaxes para instanciação de Array, uma literal e outra programável.

A sintaxe literal suporta apenas dados literais: inicia-se com #(, os elementos são separados por blanks (espaços, tabulações, mudanças de linha) e é fechado com ).

Por exemplo:
#(1 2 3)


A limitação dessa sintaxe é que ela só suporta valores literais, não suporta qualquer forma de código (bloco, variável, construtor, etc…). Portanto, não é recomendável.

A sintaxe recomendada para criação de array com código é {}, pois a sintaxe literal suporta apenas literais. A sintaxe com suporte a código se inicia com {, os elementos são separados por ponto (.) seguido de blank e é fechado por }.

Por exemplo:
| anArray b |

b := 2.
anArray := {
1.
b.
[3] value.
}.


O principal seletor para leitura de elementos em um array é #at:ifAbsent:, que recebe como primeiro parâmetro o índice e como segundo um bloco que retorna o valor em caso se ausência.

Por exemplo:
anArray at: 2 ifAbsent: [0]


Retorna o elemento de índice 2 ou, em caso de não existir, retorna zero ([0] value).

Dictionary


Essa classe representa um dicionário ou hash, ou seja, uma coleção de pares chave-valor.

Por exemplo:
| aDict |

aDict := Dictionary from: {
#a -> 1.
#b -> 2.
#c -> 3.
}.


Se usar Squeak ou Pharo, a mensagem de criação é #newFrom: em vez de #from:.

Também aceita a mensagem #at:ifAbsent:, que recebe como primeiro parâmetro a chave e como segundo um bloco que retorna o valor em caso de ausência. Ainda responde à mensagem #at:, que recebe apenas a chave e, em caso de ausência, levanta uma exceção NotFound.

A mensagem para inserir um novo par é #add::
aDict add: (#d -> 4).


Para alterar o valor de uma chave use #at:put::
aDict at: #a put: 5.


Também funciona para adicionar novo par.

Para remover um par pela chave basta usar a mensagem #removeKey::
aDict removeKey: #a.


À mensagem #keys responde uma lista – uma instância de Set, de fato – das chaves dos pares, já o seletor #keysAndValuesDo: executa um bloco para cada par:
aDict keysAndValuesDo: [ :key :value |
Transcript
show: key printString;
tab;
show: value printString;
cr.
].


Set


Set é um array onde cada elemento é único, ou seja, não se repete. Para criar uma instância de Set:
| aSet |

aSet := #(1 2 3 4 5) asSet.


Para remover um elemento, use a mensagem #remove: e para adicionar #add:. Se o elemento adicionado já existir, nada acontece.

Para listar o conteúdo há a mensagem #do::
aSet do: [ :e |
Transcript show: e printString; cr.
].


Bag


Bag é exatamente o que o nome diz: um saco de elementos. Você pode ir jogando o que quiser lá dentro e os elementos vão sendo armazenados desordenadamente.
| aBag |

aBag := #(1 2 3 4 5) asBag.


Os principais seletores são #add:, #remove: e #do:, que funcionam de modo similar aos seletores de Set, mas permite duplicação de elementos.

Conclusão


Há uma série de outras classes filhas de Collection, como OrderedCollection, SortedCollection e LinkedList, e muitos outros seletores úteis.

Para mais informações, execute gst-blox, acesse SmalltalkClass Hierarchy Browser. Na nova janela, clique em ClassSearch e faça uma busca por Collection.

De classe em classe, navegue pelos métodos e veja como cada classe responde a cada mensagem.

[]’s
Cacilhας, La Batalema


PS: Referência: M206 Computing: An Object-oriented Approach

domingo, 4 de abril de 2010

Quem ama BLOXeia!

Smalltalk GNU Smalltalk é a implementação Smalltalk da Free Software Foundation para o sistema operacional GNU.

Para interface gráfica, ele oferece o módulo BLOX. Sua IDE pode ser iniciada pelo comando:
bash$ gst-blox


Caso queira navegar pelas classes de sua própria imagem – dev.im por exemplo –, use:
bash$ gst-blox -I dev.im


Este artigo apresentará um exemplo extremamente simples de uso programático do BLOX para criar interface gráfica com o usuário, baseado no código encontrado no tutorial oficial.

Primeiro vamos criar a imagem contendo o módulo BLOX:
bash$ gst-load -iI blox-sample.im Blox


Crie um arquivo fonte blox-sample.st onde vamos trabalhar.

Declare as variáveis que usaremos:
| win con lab bt |


Agora crie a janela principal com o título Example e seu container:
" Main window "
win := BLOX.BWindow new: 'Example'.
con := BLOX.BContainer new: win.


Para definar as dimensões da janela e a callback do botão de fechamento:
" Main window's geometry and close button "
win
width: 200 height: 50;

callback: [
BLOX.Blox terminateMainLoop.
true.
] message: #value.


A callback encerrará o ciclo principal. Callbacks de janelas precisam ter a última saída true (quando a janela deve ser fechada) ou false (quando a janela não deve ser fechada).

Podemos então configurar a geometria padrão para widgets filhos:
" Geometry management "
con
setVerticalLayout: true;
defaultHeight: 50;
defaultWidth: 200.


Agora, para criar um rótulo (label) e um botão:
lab := BLOX.BLabel new: con label: 'Waiting...'.
bt := BLOX.BButton new: con label: 'Click me'.


O rótulo exibirá seu texto centralizado, com fundo azul e cor de texto branco:
" Label setup "
lab
alignment: #TopCenter;
backgroundColor: #blue;
foregroundColor: #white.


A callback que responde ao evento clique no botão:
" Button callback "
bt
callback: [
lab label: 'HELLO WORLD!'.
] message: #value.


Para exibir a janela:
" Show window "
win map.


Por último devemos iniciar o ciclo principal (mainloop):
" Start mainloop "
BLOX.Blox dispatchEvents: win.


Se quiser, é possível exibir uma mensagem indicando o fim do programa:
Transcript
show: 'Finished';
cr.


Resta apenas executar a aplicação:
bash$ gst -I blox-sample.im -f blox-sample.st


Outra opção ao BLOX é Gtk+.

[]’s
Cacilhας, La Batalema

sábado, 3 de abril de 2010

SUnit

Smalltalk Smalltalk possui um ótimo arcabouço de teste unitário chamado SUnit – Smalltalk Unit Test Suite. Porém sua documentação é um tanto fraca.

Segue aqui um pequeno tutorial de uso do SUnit. Veja que não é necessariamente a forma correta de usá-lo. Usaremos GNU Smalltalk.

Imagine que queremos implementar um método em Integer que retorna se o número é primo ou não. Podemos primeiro criar um test case para verificar seu funcionamento.

Para isso, crie um arquivo test-is-prime.st e defina a seguinte classe:
TestCase subclass: #TestIsPrime
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Unit Test'
!


A classe TestIsPrime é filha de TestCase e, para nosso fim atual, não precisará de atributos.

Precisaremos de um método de classe que execute os testes, para que não precisemos criar uma TestSuite:
!TestIsPrime class methodsFor: 'running'!

run
(self testSelectors) do: [ :test |
self run: test.
].
!
!


Podemos agora criar a categoria accessing para os testes:
!TestIsPrime methodsFor: 'accessing'!


O primeiro teste pode ser para determinar se números negativos retornam um erro quando se tenta determinar sua primalidade:
testNegative
self should: [ -1 isPrime ] raise: ArithmeticError.
!


Não esqueça de indentar!
[update 2010-04-04]
A indentação aqui tem objetivo exclusivo de manter a clareza do código.
[/update]


O método #should:raise: executa um bloco e asserta que uma exceção será levantada, neste caso ArithmeticError.

Podemos fazer o mesmo para zero:
testZero
self should: [ 0 isPrime ] raise: ArithmeticError.
!


Já para o número um, é preciso assertar que ele não é primo, ou seja, retorne falso:
testOne
self shouldnt: [ 1 isPrime ].
!


O método #shouldnt: executa um bloco esperando pelo retorno falso.

Mas quando chegarmos ao dois, ele é primo:
testTwo
self should: [ 2 isPrime ].
!


O método #should: executa um bloco esperando pelo retorno verdadeiro.

Você pode fazer isso indefinidamente se quiser:
testSomeOtherValues
self should: [ 3 isPrime ].
self shouldnt: [ 4 isPrime ].
self should: [ 5 isPrime ].
self shouldnt: [ 6 isPrime ].
self should: [ 7 isPrime ].
self shouldnt: [ 8 isPrime ].
self shouldnt: [ 9 isPrime ].
self shouldnt: [ 10 isPrime ].
self should: [ 11 isPrime ].
!


Quando cançar, encerre a lista de métodos da categoria com a linha:
!


Ou, se preferir ser mais explícito:
!!


No final, adicione uma linha para executar os testes:
TestIsPrime run.


Para rodar os testes, copie a imagem contendo seu código para, por exemplo, test.im e acrescente o SUnit com o seguinte comando:
bash$ gst-load -I test.im SUnit


Agora, com a imagem preparada, você pode executar os testes:
bash$ gst-sunit -I test.im -f test-is-prime.st


Se tudo correr bem, nada acontecerá, caso contrário você poderá receber uma mensagem do tipo:
TestIsPrime>>#testSomeOtherValues ..
FAILURE: Assertion failed


Onde TestIsPrime>>#testSomeOtherValues significa que falhou o teste contido no método #testSomeOtherValues da classe TestIsPrime.

[]’s
Cacilhας, La Batalema