quinta-feira, 18 de novembro de 2010

accents-substitute

Seguidamente tenho a necessidade de remover acentos de textos em português. Há umas semanas fiz uma extensão para Chicken chamada accents-substitute substituir caracteres acentuados pelos correspondentes sem acento (ou com entidades de HTML).

A extensão aceita textos em Latin1 (ISO-8859-1) ou UTF-8, havendo um módulo para cada codificação, ambos provendo o mesmo procedimento: accents-substitute (nome inspirado em string-substitute, da extensão regex).

Para facilitar o uso no dia-a-dia, fiz um pequeno script que usa o procedimento {{accents-substitute}}. Abaixo estão alguns exemplos de uso:
$ accents-substitute --help
Usage: accents-substitute [ --encoding= ] [ --mode= ] [ input file ]

Default values:
mode: ascii
encoding: utf8
$ cat lisp-br
Lisp-BR é um projeto que tem por objetivo divulgar linguagens de
programação da família Lisp no Brasil e formar uma comunidade de
usuários que tenham vontade de aprender não só uma nova forma de
programar, mas também uma nova maneira de pensar!
$ cat lisp-br | accents-substitute 
Lisp-BR e um projeto que tem por objetivo divulgar linguagens de
programacao da familia Lisp no Brasil e formar uma comunidade de
usuarios que tenham vontade de aprender nao so uma nova forma de
programar, mas tambem uma nova maneira de pensar!
$ accents-substitute --mode=html lisp-br
Lisp-BR é um projeto que tem por objetivo divulgar linguagens de
programação da família Lisp no Brasil e formar uma comunidade de
usuários que tenham vontade de aprender não só uma nova forma de
programar, mas também uma nova maneira de pensar!


O código da ferramenta de linha de comando está a seguir:
#!/bin/sh
#| -*- scheme -*-
exec csi -s $0 "$@"
|#

(use
(rename
accents-substitute-latin1
(accents-substitute accents-substitute-latin1))
(rename
accents-substitute-utf8
(accents-substitute accents-substitute-utf8)))

(use posix regex (srfi 1 13))

(define (command-line-argument option args)
;; Return the argument associated to the command line option OPTION
;; in ARGS or #f if OPTION is not found in ARGS or doesn't have any
;; argument.
(let ((val (any (cut string-match (string-append option "=(.*)") <>) args)))
(and val (cadr val))))

(define (usage #!optional exit-code)
(print "Usage: " (pathname-strip-directory (program-name))
" [ --encoding=<utf8|latin1> ] [ --mode=<ascii|html> ] [ input file ]")
(print "\nDefault values:\n"
" mode: ascii\n"
" encoding: utf8")
(when exit-code (exit exit-code)))

(let* ((args (command-line-arguments))
(mode (command-line-argument "--mode" args))
(encoding (command-line-argument "--encoding" args))
(paramless-args (remove (cut string-prefix? "--" <>) args))
(accents-substitute accents-substitute-utf8))

(when (or (member "-h" args) (member "--help" args))
(usage 0))

(when (and encoding (not (member encoding '("utf8" "latin1"))))
(print "'" encoding "' is not a valid encoding.")
(exit 1))

(when (and mode (not (member mode '("ascii" "html"))))
(print "'" mode "' is not a valid mode.")
(exit 1))

(when (equal? encoding "latin1")
(set! accents-substitute accents-substitute-latin1))

(let ((port (if (null? paramless-args)
(current-input-port)
(open-input-file (car paramless-args)))))
(let loop ()
(let ((line (read-line port)))
(unless (eof-object? line)
(print (accents-substitute line mode: (and mode (string->symbol mode))))
(loop))))
(unless (null? paramless-args)
(close-input-port port))))


Adicionei este exemplo de aplicação à seção Examples da documentação de accents-substitute.

terça-feira, 5 de outubro de 2010

Awful no Windows ("That's too awful")

Com a correção da extensão sendfile (por David Krentzlin) para Windows, awful passa a funcionar também no sistema das telas azuis.



As correções ainda não foram oficialmente lançadas (i.e., ainda não estão disponíveis para serem instaladas via chicken-install), mas podem ser obtidas em http://code.call-cc.org/svn/chicken-eggs/release/4/sendfile/trunk/ (usuário: anonymous, sem senha).

segunda-feira, 13 de setembro de 2010

Chicken 4.6.0

Foi lançada hoje a versão 4.6.0 de Chicken. O download da nova versão pode ser feito de http://code.call-cc.org. As novidades estão listadas no arquivo NEWS.

sábado, 11 de setembro de 2010

Migração de site estático para awful: tratando requisições por index.html

Até pouco tempo atrás a minha página pessoal era totalmente estática. Depois que comecei a desenvolver awful achei que seria meio vergonhoso se a minha própria página não usasse o software que produzo. Então resolvi refazê-la usando awful (eu poderia simplesmente jogar os arquivos estáticos para o servidor web disponibilizá-los, mas não teria graça).

Um problema que ocorreu na migração do site estático para o com use de awful foi a ausência de arquivos index.html. Várias páginas eram disponibilizadas e referenciadas através um arquivo index.html, e esses arquivos não existem mais na nova estrutura com awful. A solução que adotei foi redirecionar os acessos aos arquivos index.html para o nome do "diretório" que os contém (na verdade não são diretórios, mas URIs associadas a procedimentos em Scheme).

Por exemplo, antes havia http://parenteses.org/mario/index.html. Hoje há um procedimento associado a http://parenteses.org/mario.

Com a possibilidade de associar procedimentos a URIs que casa com uma expressão regular, fazer com que http://parenteses.org/mario responda por http://parenteses.org/mario/index.html se torna bem fácil:
(use awful)

(define-page (regexp "/mario/.*index.html")
(lambda (path)
(redirect-to (string-chomp path "index.html"))))

Assim, todas as requisições por recursos /index.html disponíveis no caminho /mario/ serão redirecionadas para /.

quinta-feira, 9 de setembro de 2010

Substituindo ferrugem por bolhas

Neste domingo recebi um inesperado telefonema do Douglas, guitarrista da Crossfire, convidando para reunirmos a banda e fazer um som. Não tocávamos juntos desde o final de 2008 e eu havia parado de tocar no ano passado.

Depois de um ano sem tocar baixo, decidi arriscar para ver no que daria. Surpreendentemente, tocamos durante duas horas como se estivéssemos tocando regularmente. Fiquei impressionado.

Impressionante também foram as seqüelas. Esta é a foto dos meus dedos logo após o término do ensaio:



Esse é o estado dos meus dedos hoje:



Fica a dica: se fores tocar depois de um ano parado(a), use palheta. :-)

Chicken Gazette


Há algumas semanas foi lançada a Chicken Gazette, uma publicação semanal de notícias sobre o sistema Chicken. Até agora já foram publicadas duas edições, as quais tratam de vários assuntos relacionados a Chicken, desde aspectos técnicos a questões sociais, de infraestrutura, últimos acontecimentos no desenvolvimento e curiosidades em geral.

A elaboração das edições é rotativa, ou seja, a cada semana há um editor diferente. A primeira edição ficou por conta de Christian Kellerman, a segunda por Moritz Heidkamp e a terceira está sendo elaborada por Alaric Snell-Pym.

As edições da Chicken Gazette podem ser acompanhadas através de feeds atom.

sábado, 28 de agosto de 2010

Awful 0.26

Há pouco lancei a versão 0.26 de awful. A principal novidade é a integração de um editor em javascript para o web REPL (o texto anterior é sobre isso).


Houve algumas modificações para tornar o web REPL mais útil para desenvolvimento interativo, como a possibilidade de redefinir e eliminar definições de páginas em tempo de execução.

Coloquei em http://wiki.call-cc.org/video/awful-guess.ogv um vídeo mostrando o desenvolvimento de um simples jogo de adivinhação de números, com o uso de ajax e do web REPL.

Em http://parenteses.org/jogos há versões mais elaboradas de jogos no mesmo estilo.

sexta-feira, 20 de agosto de 2010

Awful fancy web REPL

Estou pensando em adicionar um editor de código em Javascript ao web REPL de awful. A idéia é facilitar a prototipação rápida, execução de testes, depuração e correção de problemas de forma fácil e com o servidor em execução.

A versão de desenvolvimento de awful contém um código inicial para isso. Abaixo está um screencast do que está implementado até agora.



O arquivo do vídeo, com uma qualidade significativamente melhor do que o mostrado acima, pode ser obtido em http://parenteses.org/mario/misc/fancy-web-repl.ogv (~6.1MB).

O editor em Javascript que estou utilizando é o Codemirror.

Emacs rápido

Uma dica para usuários de Emacs que apelam para editores mais limitados como zile, nano e vi (risada sarcástica aqui) para edição rápida de arquivos (e.g., arquivos de configuração):
emacs -Q -nw

Um rápido e impreciso benchmark na máquina que estou usando agora mostra os seguintes resultados para abrir e fechar o Emacs 10 vezes:

$ time for i in `seq 10`; do \
> emacs -Q -nw --eval '(save-buffers-kill-emacs)'; \
> done
real 0m0.753s
user 0m0.596s
sys 0m0.140s

Menos de 1 segundo para abrir e fechar o Emacs 10 vezes.

Habilitar modos que tornam o uso do Emacs mais confortável, como transient-mark-mode, global-font-lock-mode e show-paren-mode não custa caro:

$ time for i in `seq 10`; do\
> emacs -Q -nw --eval \
> '(progn (global-font-lock-mode t)
> (transient-mark-mode t)
> (show-paren-mode t)
> (save-buffers-kill-emacs))'; \
> done
real 0m0.800s
user 0m0.616s
sys 0m0.168s

Novamente, menos de um segundo para abrir e fechar o Emacs 10 vezes, com coloração de sintaxe e tudo (inclusive um interpretador de Lisp). :-)

terça-feira, 17 de agosto de 2010

Awful 0.25

Há pouco foi lançada a versão 0.25 de awful. A seguir são descritas as novidades.

O servidor awful admite novos parâmetros de linha de comando: --development-mode, --ip-address e --port. Os parâmetros --development-mode e --port servem, respectivamente, para fazer com que o servidor escute nos dados endereço IP e porta.

O parâmetro --development-mode ativa o modo de desenvolvimento de awful. Neste modo, funcionalidades como o Web REPL e o Session inspector são automaticamente ativadas. O caminho /reload também é automaticamente criado para que as aplicações possam ser recarregadas sem que seja necessário reiniciar o servidor. No modo de desenvolvimento, mensagens de erro das aplicações são exibidas no cliente (e.g., navegador). Abaixo está um exemplo (erro.scm):
#!/usr/bin/awful

(use awful)

(define-page (main-page-path)
(lambda ()
(++ "Oops " 'erro)))


Para executar no modo de desenvolvimento:
$ awful --development-mode erro.scm

Ao acessar http://localhost:8080/ a mensagem de erro do screenshot abaixo é exibida no navegador, seguida de um link para o Web REPL.



Quando executado em modo de desenvolvimento, o parâmetro development-mode? tem como valor #t.

Outra novidade é o parâmetro awful-response-headers o qual permite ajustar os campos do cabeçalho de resposta para o cliente. Abaixo está um exemplo de um procedimento para associar URIs a respostas JSON:
#!/usr/bin/awful

(use awful)

(define (define-json path body)
(define-page path
(lambda ()
(awful-response-headers '((content-type "text/json")))
(body))
no-template: #t))

(define-json (main-page-path)
(lambda ()
"{a: 1}"))


A menos que explicitamente fornecido, awful calcula automaticamente o tamanho da resposta e o atribui ao campo Content-Length.

Hoje também disponibilizei um pequeno texto com instruções sobre como instalar awful em sistemas Unix[-like].

quarta-feira, 11 de agosto de 2010

Operações em caminhos para arquivos em buffers do Emacs

Seguidamente sinto a falta de funções no Emacs para ver o caminho completo para o arquivo aberto no buffer que estou editando (poder copiar o caminho completo seria um plus). A seguir estão duas pequenas funções para fazer isso.

show-buffer-file-name exibe na echo area do Emacs o caminho completo para o arquivo. copy-buffer-file-name coloca o caminho completo do para o arquivo no topo do kill ring do Emacs (no X Window, também copia para a área de seleção primária, o que pode ser útil para quem, como eu, usa bastante a combinacao Emacs + emulador de terminal).

(defun show-buffer-file-name ()
(interactive)
(message buffer-file-name))


(defun copy-buffer-file-name ()
(interactive)
(if buffer-file-name
(kill-new buffer-file-name)))

terça-feira, 20 de julho de 2010

Awful 0.24

Há pouco foi lançada a versão 0.24 de awful. As novidades que mais se destacam, além de algumas pequenas melhorias e da correção de alguns bugs que ocorriam em casos especiais, há o suporte inicial a armazenamento de identificadores de sessão em cookies (via spiffy-cookies) e o suporte a redirecionamentos de forma fácil (procedimento redirect-to).

A seguir está um exemplo de definição de um redirecionador:


(define-page "/mario"
(lambda ()
(redirect-to "http://parenteses.org/mario")))


A definição do redirecionador pode ser encurtada com o uso de cut:


(define-page "/mario" (cut redirect-to "http://parenteses.org/mario"))


Também aproveitei para dar uma embelezada no web REPL e no inspetor de sessão (screenshots abaixo).



10 anos de Chicken!

Há dez anos foi publicado o anúncio da primeira versão pública de Chicken: http://groups.google.com/group/comp.lang.scheme/msg/edfb2da16fd89fae

Em http://web.archive.org/web/20000824034645/http://www.anu.ie/felix/chicken.html está a página do projeto, como era na época.

segunda-feira, 17 de maio de 2010

Chicken 4.5.0

Alguns minutos atrás foi anunciado o lançamento da versão 4.5.0 de Chicken. A lista de novidades está em http://chicken.wiki.br/releases/4.5.0/NEWS.

quarta-feira, 12 de maio de 2010

domingo, 9 de maio de 2010

Awful: expressões regulares para dispatching de URLs

Há pouco fiz o commit da mais nova versão de awful (0.18) . A novidade mais significativa desta versão é o suporte a dispatching de URLs através de expressões regulares. Em outras palavras, agora é possível associar uma expressão regular a um procedimento a ser executado. Assim, se o caminho da URL da requisição bate com a expressão regular, o procedimento é invocado com o caminho como argumento.

Abaixo está um exemplo simples que ilustra o que a nova versão pode fazer:
(use awful srfi-1 regex)

(define-page (regexp "/add/.*")
(lambda (path)
(let ((numbers (filter-map string->number (string-split path "/"))))
(number->string (apply + numbers)))))


Se acessarmos o servidor executando o código acima através da URL http://host:port/add/1/2/3 o resultado exibido será 6.

Org mode e links para mensagens no Gnus

Esses dias passei os olhos sobre um guia compacto sobre o Org mode e
achei uma coisa super útil: a possibilidade de fazer links do
documento em Org para mensagens do Gnus.

A sintaxe é a seguinte:

[[gnus:<mailbox>#<mensagem id>][<texto>]]


Onde:


  • <mailbox> é a mailbox aonde está armazenada a mensagem

  • <mensagem id> é o número da mensagem na mailbox

  • <texto> é o texto que aparecerá no link



Exemplo:

[[gnus:lists.chicken-users#2045 [Coisa para ler sobre Chicken]]


Algumas combinações de teclas:


  • C-c C-l: cria um link interativamente

  • C-C C-o: segue o link sob o cursor

segunda-feira, 26 de abril de 2010

Awful scripts

Uma dica rápida para executar aplicações web em awful como scripts em sistemas Unix[-compatíveis]: usando a linha com shebang.

Dessa forma, basta executar o arquivo com a aplicação web como se fosse um arquivo executável qualquer:
$ cat hello-world.scm
#! /usr/bin/awful

(use awful)

(define-page (main-page-path)
(lambda ()
"Hello, world!"))

$ ./hello-word.scm

sexta-feira, 16 de abril de 2010

Sintaxe para definição de "curried procedures"

A definição de curried procedures é bastante usual em Scheme. Consiste em fazer com que o valor produzido por um procedimento seja outro procedimento.

Abaixo está um exemplo de um criador de objetos que respondem a mensagens:
(define (make-obj)
(lambda (message)
(case message
((hello) "Hello!")
((bye) "Bye bye!")
(else (error "Unknown message.")))))


O código acima já está com uma sintaxe simplificada para definições de procedimentos. A forma mais longa seria:
(define make-obj
(lambda ()
(lambda (message)
(case message
((hello) "Hello!")
((bye) "Bye bye!")
(else (error "Unknown message."))))))


Não está na especificação da linguagem, mas algumas implementações oferecem a possibilidade de usar uma sintaxe mais simplificada para a definição de curried procedures:
(define ((make-obj) message)
(case message
((hello) "Hello!")
((bye) "Bye bye!")
(else (error "Unknown message."))))


Das implementações em que testei, Chicken, Guile, MIT Scheme e PLT admitem essa sintaxe para definição de curried procedures. Ypsilon e Gambit não admitem.

quinta-feira, 15 de abril de 2010

Documentação de Chicken com chicken-doc

chicken-doc é uma extensão do sistema Chicken para pesquisa e navegação na documentação do compilador e das extensões através da linha de comando.

É bem parecida com uma extensão que eu havia feito há uns anos: man. Na época da extensão man, a documentação de Chicken era no formato textinfo. man, então, fazia o parsing do arquivo texinfo e gerava um arquivo com expressões simbólicas de fácil pesquisa em Scheme. Depois da adoção de wiki para a documentação do compilador, a extensão man caiu em desuso.

chicken-doc, diferentemente da extensão man, faz o parsing de arquivos fonte do wiki, incluindo a documentação das extensões, R5RS e algumas SRFIs! Hoje foi lançada a versão 0.3.2 com a funcionalidade de pesquisa na documentação.

O uso de chicken-doc depende da geração da base de dados onde serão feitas as pesquisas. Para gerar a base de dados, é necessária a extensão chicken-doc-admin. Em http://chicken.wiki.br/eggref/4/chicken-doc-admin#quick-start há um passo-a-passo sobre como gerar a base.

Após gerada a base, pode-se usar a ferramenta de linha de comando chicken-doc:


$ chicken-doc
usage: chicken-doc -s|-c|-i path
chicken-doc -f id
chicken-doc -m re
chicken-doc id | path
-s path Show signature
-c path Show table of contents (child IDs)
-i path Show documentation
-f id Show all matching paths for ID
where ID is a single identifier and PATH is zero or
more IDs comprising a path from the documentation root,
separated by spaces or the # character.

-m re Show all matching paths for RE
where RE is a POSIX regular expression. Similar to -f.

When no option is given, guess the user's intent. With
a single ID, find the ID (as with -f) and show its
documentation (as with -i) or show all matching paths
if multiple matches exist. If more than one ID is
provided, show documentation on the path (as with -i).

Examples:
-f open/rdonly # Show matches for open/rdonly
-s posix open/rdonly # Show signature of open/rdonly in Unit posix
-s 9p open/rdonly # Show signature of open/rdonly in 9p egg
-i 9p#open/rdonly # Show documentation for same
-c posix # Show TOC for Unit posix
-c # Show toplevel TOC
-m call- # Show identifiers containing call-
-m -file$ # Show identifiers ending in -file
use # Show doc for "use" in chicken core
posix # Show doc for Unit posix
open/rdonly # Show matches for open/rdonly
posix open/rdonly # Show doc for open/rdonly in Unit posix


Alguns exemplos de uso:


$ chicken-doc reverse
path: (scheme reverse)

-- procedure: (reverse list)

Returns a newly allocated list consisting of the elements of list in
reverse order.

(reverse '(a b c)) ===> (c b a)
(reverse '(a (b c) d (e (f))))
===> ((e (f)) d (b c) a)


A primeira linha indica o "caminho" para o símbolo procurado. O primeiro símbolo do caminho pode ser scheme (indicando que é o símbolo é um componente da especificação da linguagem), o nome de uma unit de Chicken, uma SRFI ou uma extensão. É possível que ele possa ser encontrado em mais de um módulo. Neste caso, chicken-doc apresenta uma lista de opções:


$ chicken-doc enable-db
Found 3 matches:
(awful-sql-de-lite enable-db) (enable-db)
(awful-sqlite3 enable-db) (enable-db)
(awful-postgresql enable-db) (enable-db)


$ chicken-doc awful-postgresql enable-db

-- procedure: (enable-db)

Enable Postgresql support for awful
(http://chicken.wiki.br/eggref/4/awful). This procedure basically sets
up awful to use the connection, disconnection, query and query escaping
procedures for Postgresql databases.


Com a versão lançada hoje, é possivel pesquisar na base de identificadores de procedimentos, macros e parâmetros. Exemplos:


$ chicken-doc -m '^string->'
(library string->blob) (string->blob STRING)
(byte-blob string->byte-blob) (string->byte-blob STRING) => BYTE-BLOB
(sxml-transforms string->goodHTML) (string->goodHTML html)
(library string->keyword) (string->keyword STRING)
(scheme string->list) (string->list string)
(scheme string->number) (string->number string radix)
(scheme string->symbol) (string->symbol string)
(posix string->time) (string->time TIME [FORMAT])
(library string->uninterned-symbol) (string->uninterned-symbol STRING)


O argumento para o parâmetro -m é uma expressão regular.

Algumas das funcionalidades de chicken-doc podem também ser usadas no REPL:


$ csi

CHICKEN
(c)2008-2010 The Chicken Team
(c)2000-2007 Felix L. Winkelmann
Version 4.4.4
linux-unix-gnu-x86 [ manyargs dload ptables ]
compiled 2010-03-30 on dellito (Linux)

; loading ./.csirc ...
csi> (use chicken-doc)
csi> ,doc string-intersperse
path: (data-structures string-intersperse)

-- procedure: (string-intersperse LIST [STRING])

Returns a string that contains all strings in `LIST` concatenated together.
`STRING` is placed between each concatenated string and defaults to `" "`.

(string-intersperse '("one" "two") "three")

is equivalent to

(apply string-append (intersperse '("one" "two") "three"))

csi> ,wtf concatenate
(srfi-1 concatenate) (concatenate list-of-lists) -> value
(srfi-1 concatenate!) (concatenate! list-of-lists) -> value
(ports make-concatenated-port) (make-concatenated-port PORT1 PORT2 ...)


O comando ,doc mostra a documentação do símbolo dado como argumento. O comando ,wtf (de where to find ;-)) é equivalente à opção de linha de comando -m de chicken-doc.

Uma opção interessante para uso de chicken-doc é associar uma tecla de atalho para executá-lo no emacs. Em http://chicken.wiki.br/eggref/4/chicken-doc há dicas sobre como fazer isso.

terça-feira, 30 de março de 2010

Lies, damn lies and benchmarks

Esses dias me deparei com um texto que mostra um benchmark simplificado (bastante ingênuo, na verdade) cujo título é "Holy Shmoly, Ruby 1.9 smokes Python away!". O benchmark baseia-se na execução do cálculo da série de Fibonacci. A implementação é recursiva e exercita, basicamente, chamadas de função e aritmética básica. São comparadas as implementações de Python e Ruby.

Embora seja muito simplificado, este benchmark desencadeou várias surpresas.

Surpresa 1 -- A implementação de Ruby está ficando mais rápida



Esta surpresa, na realidade, foi dupla para mim. Eu tinha idéia de que a implementação de Ruby era "meio lenta", mas não sabia que era tão lenta. A parte boa da surpresa é que a versão 1.9 está consideravelmente mais rápida.

Como referência para as próximas medições, abaixo estão os tempos de execução que obtive na minha máquina com as implementações em Ruby para Fibonacci de 0 a 39 (Ruby 1.8 e 1.9):
def fib(n)
if n == 0 || n == 1
n
else
fib(n-1) + fib(n-2)
end
end

40.times do |i|
puts "n=#{i} => #{fib(i)}"
end
Ruby 1.8Ruby 1.9
real    9m26.377s
user 8m11.039s
sys 1m15.273s
real    1m14.576s
user 1m14.565s
sys 0m0.020s

Surpresa 2 -- O desempenho de Python



Ao longo deste texto vou mostrar algumas extensões que fiz ao benchmark original, o que acabou gerando mais surpresas. A primeira dela foi medir o desempenho de Python usando o JIT Psyco. Abaixo estão o código utilizado e os resultados obtidos na minha máquina:

Python puro (sem Psyco)


#! /usr/bin/env python
def fib(n):
if n == 0 or n == 1:
return n
else:
return fib(n-1) + fib(n-2)


for i in xrange(40):
print "n=%d => %d" % (i, fib(i))
time ./fib.py
n=0 => 0
n=1 => 1
...
n=39 => 63245986

real 2m21.908s
user 2m21.349s
sys 0m0.016s


Python com Psyco


#! /usr/bin/env python
import psyco
psyco.full()

def fib(n):
if n == 0 or n == 1:
return n
else:
return fib(n-1) + fib(n-2)


for i in xrange(40):
print "n=%d => %d" % (i, fib(i))
time ./fib.py
n=0 => 0
n=1 => 1
...
n=39 => 63245986

real 0m6.998s
user 0m6.948s
sys 0m0.008s

Com o uso de Psyco, o tempo de execução foi de ~2m22s para ~7s. Impressionante.


Surpresa 3 -- O desempenho de Chicken



Decidi estender o benchmark um pouco mais e implementar o algoritmo em Scheme, com Chicken:
(define (fib n)
(if (or (= n 0) (= n 1))
n
(+ (fib (- n 1)) (fib (- n 2)))))

(let loop ((n 0))
(when (< n 40)
(print "n=" n " => " (fib n))
(loop (+ n 1))))


O desempenho de Chicken, quando interpretando código, é violentamente lento (e esperado, pelo menos por mim):
time csi -s fib.scm
n=0 => 0
n=1 => 1
...
n=39 => 63245986

real 5m21.635s
user 5m20.696s
sys 0m0.916s

Quando o código é compilado com Chicken, há uma melhora significativa no desempenho:
$ csc fib.scm
time ./fib
n=0 => 0
n=1 => 1
...
n=39 => 63245986

real 0m29.996s
user 0m29.938s
sys 0m0.060s

Usando um pouco de agressividade na otimização:
$ csc -O5 fib.scm
time ./fib
n=0 => 0
n=1 => 1
...
n=39 => 63245986

real 0m21.506s
user 0m21.405s
sys 0m0.012s

Com a compilação do código em Chicken, foi possível baixar o tempo de execução de mais de 5 minutos para pouco mais de 20 segundos.

Por curiosidade, resolvi ver o tempo que uma implementação em C, nos mesmos moldes, levaria:
#include <stdio.h>

int fib(int n) {
if (n == 0 || n == 1) {
return n;
} else {
return fib(n - 1) + fib(n - 2);
}
}


int main() {
int n;
for (n=0; n<40; n++) {
printf("n=%d => %d\n", n, fib(n));
}
}
$ gcc -O3 -ffast-math -fomit-frame-pointer -o fib-c fib-c.c
$ time ./fib-c
n=0 => 0
n=1 => 1
...
n=39 => 63245986

real 0m1.374s
user 0m1.372s
sys 0m0.004s


Até aqui, temos C, Python + Psyco e Chicken (compilado) nas três primeiras colocações em tempo de execução. Porém, há um detalhe na implementação em Chicken que faz uma certa diferença: o operadores numéricos (e.g., +, =) aceitam um número arbitrário de argumentos, diferentemente dos operadores numéricos das outras linguagens, que são binários. Obviamente, há um custo para isso. Entretanto, Chicken dispõe de procedimentos binários para operações numéricas com inteiros (com prefixo fx):
(define (fib n)
(if (or (fx= n 0) (fx= n 1))
n
(fx+ (fib (fx- n 1)) (fib (fx- n 2)))))

(let loop ((n 0))
(when (fx< n 40)
(print "n=" n " => " (fib n))
(loop (fx+ n 1))))


compilando e executando este código, temos:
$ csc -O5 fibfx.scm
$ time ./fibfx
n=0 => 0
n=1 => 1
...
n=39 => 63245986

real 0m1.088s
user 0m1.088s
sys 0m0.000s

A surpresa aqui é que o tempo de execução de uma implementação em Scheme é menor do que o de uma implementação em C. E, mais intrigante ainda, é que Chicken compila para C. :-) Porém, a estratégia de compilação usada por Chicken gera código C mais otimizado do que um humano programando.

No final das contas, temos os seguintes tempos, ordenados por ordem decrescente de tempo de execução:
Ruby 1.89m26.377s
Chicken (interpretado)5m21.635s
Python puro2m21.908s
Ruby 1.91m14.576s
Python + Psyco0m6.998s
C0m1.374s
Chicken (compilado)0m1.088s


As seguintes versões de interpretadores e compiladores foram usadas:
Python2.6.4
Psyco1.6
Ruby1.8 e 1.9
Chicken4.4.4
GCC4.4.1

As medições foram feitas em um processador de 2.4GHz com sistema operacional Ubuntu Linux.

Vale ressaltar, novamente, que esse benchmark, embora tenha gerado algumas surpresas, representa muito pouco para avaliação de desempenho de implementações de linguagens de programação.

segunda-feira, 29 de março de 2010

Publicação dinâmica de arquivos estáticos com awful

Hoje um usuário no canal #chicken (freenode.net) levantou a questão de como servir conteúdo dinâmico a partir de arquivos estáticos, mas sem publicar os arquivos estáticos diretamente. É, parece confuso. Um exemplo seria ter o diretório de documentos do servidor vazio e gerar conteúdo dinâmico a partir de arquivos estáticos e código executado no servidor. Isto é exatamente o que awful faz (via define-page). Porém, o usuário queria disponibilizar conteúdo dinâmico em vários formatos, não apenas em [X]HTML. Por exemplo, servir uma imagem através da URL http://servidor/imagem, onde imagem não é um arquivo no diretório de documentos do servidor. Além disso, ele queria que http://servidor/imagem retornasse a própria imagem, não uma página em HTML com <img src="imagem">.

Confesso que isso não foi algo que cogitei quando implementei define-page. Pensei no uso de define-page para a definição de páginas em [X]HTML ou de texto, não para imagens, vídeos ou formatos arbitrários.

Para a minha surpresa, a solução é bastante simples com o uso do procedimento send-static-file, de Spiffy:
(define-page "img"

(lambda ()
(begin
(send-static-file "/home/mario/img.png")
"")))

O código acima faz com que send-static-file ajuste os cabeçalhos HTTP para indicar que a resposta é um arquivo de imagem (com base na extensão) e envie o conteúdo do arquivo dado como argumento.

Porém, send-static-file considera que o caminho do arquivo passado como argumento está a partir do diretório de documentos do servidor web (i.e., o caminho passado como argumento será concatenado com o diretório de documentos do servidor web). Tentativas de passar um caminho absoluto, obviamente, não funcionam, pois o caminho absoluto será concatenado ao diretório de documentos do servidor, possivelmente apontando para um arquivo que não existe ou, com certeza, apontando para o arquivo errado.

A solução para este problema, no entanto, também é fácil: basta temporariamente mudar o valor do parâmetro que indica o diretório de documentos do servidor (root-path):
(define-page "img"

(lambda ()
(begin
(parameterize ((root-path "/"))
(send-static-file "/home/mario/img.png"))
"")))

No exemplo acima, configurei o diretório de documentos do servidor web para ser /, de forma que eu possa referenciar arquivos através do caminho absoluto. O uso de parameterize garante que a definição temporária será válida apenas durante a execução do seu corpo (send-static-file, no exemplo).

Obviamente, a definição de URLs para envio de objetos estáticos de forma dinâmica pode ser simplificada se definirmos um procedimento para isso:
(define (define-static-file url-path abs-path)
(define-page url-path
(lambda ()
(begin

(parameterize ((root-path "/"))
(send-static-file abs-path))
""))))

Estando define-static-file definido, publicar um arquivo estático de forma dinâmica, basta:
(define-static-file "img" "/home/mario/img.png")

quarta-feira, 24 de março de 2010

Awful em 35 segundos

Hoje vi na lista de usuários brasileiros de Python uma mensagem em que era apontado um vídeo sobre o framework Bottle. O vídeo mostra o desenvolvimento de um "hello world" no framework em 36 segundos. Resolvi fazer o mesmo com awful e consegui (oh!) em 35 segundos.



Típica medição inútil, mas aí está. :-)

Não sei se é possível visualizar o vídeo em uma resolução maior com esse tocador de vídeos do blogger. Na dúvida, coloquei umas cópias em http://parenteses.org/mario/misc/awful.avi e http://parenteses.org/mario/misc/awful.ogv.

segunda-feira, 8 de março de 2010

Scheme sem parênteses

Para aqueles que não usam Scheme só por causa dos parênteses: seus problemas acabaram! Basta substituir os parênteses por colchetes.

[define [fatorial n]
[if [< n 2]
1
[* n [fatorial [- n 1]]]]]

[display [fatorial 5]]
[newline]


Exemplos com algumas implementações:

Chicken:
$ csi -s fatorial.scm 
120


Gambit:
$ gsi fatorial.scm 
120


PLT:
$ mzscheme -f fatorial.scm 
120


Ypsilon:
$ ypsilon fatorial.scm 
120


Embora [ e ] sejam caracteres reservados em R5RS, muitas implementações os utilizam como sinônimos para parênteses. É comum a prática de uso de colchetes para associações entre variáveis e valores em formas let. Exemplo:

(let ([a 3]
[b 4]
[c 5])
(+ a b c))


Eu, particularmente, não costumo usar colchetes como sinônimo de parênteses, mas já vi muito código que usa.

sexta-feira, 26 de fevereiro de 2010

try/catch em Scheme

Para quem não gosta da sintaxe para tratamento de exceções de handle-exceptions (SRFI-12), a seguir está uma pequena macro para transformar a sintaxe de handle-exceptions em uma com try/catch:

(define-syntax try
(syntax-rules (catch)
((_ attempt (catch exn handler ...))
(handle-exceptions exn
(begin handler ...)
attempt))))


Exemplos de uso:

(define (try-car l #!optional default)
(try (car l)
(catch exn default)))


csi> (try-car '(1 2 3 4))
1
csi> (try-car '())
#f
csi> (try-car '() '())
()

terça-feira, 23 de fevereiro de 2010

Arc challenge em awful

Há dois anos, Paul Graham propôs o seguinte desafio (arc challenge):

Write a program that causes the url said (e.g. http://localhost:port/said) to produce a page with an input field and a submit button. When the submit button is pressed, that should produce a second page with a single link saying "click here." When that is clicked it should lead to a third page that says "you said: ..." where ... is whatever the user typed in the original input field. The third page must only show what the user actually typed. I.e. the value entered in the input field must not be passed in the url, or it would be possible to change the behavior of the final page by editing the url.


Abaixo está uma implementação para o arc challenge em awful:

(use awful html-utils spiffy-request-vars)

(define-session-page "said"
(lambda ()
(with-request-vars $ (said)
(cond (said
($session-set! 'said said)
(link "said" "click here"))
(($session 'said)
=> (lambda (said)
(++ "You said: " said)))
(else (form (++ (text-input 'said)
(submit-input))
action: "said"
method: 'post))))))


Para testar o programa, basta executar:

$ awful arc-challenge.scm


E acessar http://localhost:8080/said

terça-feira, 16 de fevereiro de 2010

doctests em Scheme

No ano passado, o Luciano Ramalho criou um grupo para estudo do livro Structure and Interpretation of Computer Programs. O grupo ainda existe mas está com as atividades paradas atualmente. Boa parte dos componentes do grupo são usuários de Python. O Luciano é um usuário de Python e mencionou a falta, em Scheme, de uma facilidade que existe em Python: a possibilidade de de especificar testes em docstrings.

A minha argumentação com relação a esta funcionalidade em Scheme é de que não deve ser feita da mesma forma como em Python, pois em Scheme, diferentemente de Python, docstrings são ambíguas. O equivalente a docstrings em Scheme só poderia ser feito através de comentários (doccomments?).

Em Python, a ambigüidade não existe porque funções exigem um comando return para indicar que a função terminará e desviará o fluxo de execução para o ponto imediatamente seguinte ao de onde foi invocada, opcionalmente, devolvendo resultados. Em Scheme, não há um comando return explícito (a menos que seja definido um procedimento de escape de uma continuação, mas este é um caso bem específico e não muito usual). O valor produzido por um procedimento é o valor resultante da última expressão avaliada. No caso de procedimentos em que a única expressão a ser avaliada é uma string, tem-se a ambigüidade. Exemplo:

(define (proc)
"Isso é uma docstring ou uma string legítima?")


A ambigüidade que ocorre com docstrings é a mesma que ocorre com doctests. Se a única expressão a ser avaliada por um procedimento for uma string, como saber a string deve ser o resultado da avaliação do procedimento ou simplesmente uma docstring? Em Scheme, é uma ambigüidade. Em Python a ambigüidade é resolvida com o comando return.

Ignorando a ambigüidade que pode ocorrer em algumas definições, implementei em Scheme um esquema semelhante a doctests em Python. A implementação não é de uso geral, mas serve como " prova de conceito" (ainda que o conceito não esteja totalmente correto :-)).

A sintaxe das strings de teste é a seguinte:

  • a expressão sob teste, que seria digitada no REPL, deve ser precedida por >

  • o resultado esperado deve ser precedido por :



O parser das strings de teste é bastante limitado. Não são admitidas múltiplas linhas para expressões de teste nem de resultado. O parser ignora linhas não iniciadas por > ou :.

A única forma sintática para declaração de procedimentos admitida é:

(define (proc args)
body)


Outras formas, como as abaixo, não são suportadas:

(define proc
(lambda (args)
body))

(define proc #f)
(set! proc (lambda (args) body))

(define proc
(let ()
(lambda (args)
body)))


Teste de procedimentos resultantes da expansão de macros também não é suportado.

A implementação consiste, basicamente, de um procedimento doctest que lê todas as formas (forms) do arquivo em que é invocado e procura por definições com o padrão

(define (proc args)
"doctest"
body)


Então a string com os testes é extraída da definição e é passada para o parser de strings de teste, o qual avalia as expressões e os resultados esperados e imprime o resultado.

Um aspecto interessante da implementação é a forma como o parsing do código é feito: com manipulação de listas (estrutura de dados usada para representar código em Scheme).

O código da implementação, em Chicken Scheme, está abaixo:

(use posix (srfi 1 13))

(define (doctest)

(define (pick-teststrings forms)
;; Retorna uma alist '((procname1 . test-string1) (proname2 . test-string2) ...)
(filter-map
(lambda (form)
(and (list? form)
(eq? (car form) 'define)
(not (null? (cddr form))) ;; (define sym)
(and (pair? (cadr form)) ;; (define (proc ...))
(string? (caddr form))
(cons (caadr form) ;; procname
(caddr form))))) ;; test-string
forms))

(define (check test result)
(let* ((error-test #f)
(error-result #f)
(err-msg (lambda (e)
(with-output-to-string (cut print-error-message e))))
(pass
(equal? (handle-exceptions e
(begin
(set! error-test (err-msg e))
#f)
(eval test))
(handle-exceptions e
(begin
(set! error-result (err-msg e))
#t)
(eval result)))))
(if (or error-test error-result)
(display
(string-append
"Erro executando "
(->string (if error-test test result))
" --> " (or error-test error-result)))
(if pass
(print test " = " result " [ok]")
(print test " != " result " [fail]")
))))

(define (parse-teststring teststring)
(define (parse-line line prefix)
(and (string-prefix? (->string prefix) line)
(with-input-from-string
(string-trim line (lambda (c) (char=? c prefix)))
read)))
(let ((tests '())
(results '()))
(for-each (lambda (line)
(set! line (string-trim-both line))
(cond ((parse-line line #\>)
=> (lambda (expr)
(set! tests (cons expr tests))))
((parse-line line #\:)
=> (lambda (expr)
(set! results (cons expr results))))))
(with-input-from-string teststring read-lines))
(values tests results)))

(let ((forms (with-input-from-file (program-name) read-file)))
(for-each (lambda (procname/teststring)
(let ((procname (car procname/teststring))
(teststring (cdr procname/teststring)))
(print "===== " procname " =====")
(let-values (((tests results) (parse-teststring teststring)))
(for-each (lambda (test result)
(check test result))
(reverse tests)
(reverse results)))
(print "")))
(pick-teststrings forms))))


Abaixo estão exemplos de uso de doctests e, em seguinda, a saída da execução dos testes:

#!/usr/bin/csi -script

(load "doctest.scm")

(define (plus a b)
" Aqui estao os testes
> (plus 3 4)
: 7
> (plus 4 5 0)
: 8
> (plus 4 5)
: 8
"

(+ a b))


(define (minus a b)
"
> (minus (plus 3 4) 3)
: 4
> (minus 4 5 0)
: 8
> (minus 4 5)
: 8
"

(- a b))

(define (sort-list-string l)
"
> (sort-list-string '(\"b\" \"o\" \"p\" \"h\" \"v\"))
: '(\"b\" \"h\" \"o\" \"p\" \"v\")
"

(sort l (cut string-ci<? <> <>)))

(define (fatorial n)
"
> (fatorial 1)
: 1
> (fatorial 2)
: 2
> (fatorial 3)
: 6
> (fatorial 5)
: 120
> (fatorial 10)
: 3
> (plus (fatorial 0) (fatorial 3))
: 7
"

(if (< n 2)
1
(* n (fatorial (sub1 n)))))

(doctest)


A saída da execução dos doctests é mostrada abaixo:

===== plus =====
(plus 3 4) = 7 [ok]
Erro executando (plus 4 5 0) --> Error: bad argument count - received 3 but expected 2: #
(plus 4 5) != 8 [fail]

===== minus =====
(minus (plus 3 4) 3) = 4 [ok]
Erro executando (minus 4 5 0) --> Error: bad argument count - received 3 but expected 2: #
(minus 4 5) != 8 [fail]

===== sort-list-string =====
(sort-list-string (quote (b o p h v))) = (quote (b h o p v)) [ok]

===== fatorial =====
(fatorial 1) = 1 [ok]
(fatorial 2) = 2 [ok]
(fatorial 3) = 6 [ok]
(fatorial 5) = 120 [ok]
(fatorial 10) != 3 [fail]
(plus (fatorial 0) (fatorial 3)) = 7 [ok]

markerpen.el

markerpen.el é uma extensão interessante para destacar regiões quaisquer de buffers do Emacs. Analogamente, é como destacar o texto de um documento com uma caneta marca-textos (daí o nome markerpen).



Para destacar uma região, basta selecionar a região desejada e executar M-x markerpen-mark-region. Para eliminar o destaque, deve-se selecionar a região e executar M-x markerpen-clear-region. Obviamente, é possível definir atalhos de teclas para esses comandos.

As cores podem ser selecionadas a partir de perfis disponibilizados pela extensão. A seleção é feita através dos comandos M-x markerpenn, onde n é um número de 1 a 10 (há 10 perfis pré-programados).

segunda-feira, 8 de fevereiro de 2010

Cálculo de fatorial através de redirecionamentos de páginas

Abaixo está uma aplicação absurdamente inútil e sem sentido usando awful: cálculo de fatorial através de redirecionamentos de páginas.

Para calcular o fatorial de 5, por exemplo, basta executar o servidor com a aplicação:

$ awful awful-fatorial.scm

e acessar http://localhost:8080/?n=5 (a entrada de dados é feita através da variável n).

O código está a seguir:

(use awful html-tags html-utils)

(root-path ".")

(define-page (main-page-path)
(lambda ()
(let* ((n ($ 'n 0 string->number))
(current ($ 'current n string->number))
(accum ($ 'accum 1 string->number))
(finished? (< current 2)))
(html-page
(if finished?
(<h1> n "! = " accum)
"")
headers: (if finished?
""
(<meta> http-equiv: "refresh"
content: (conc "0;url=?n=" n
"&accum=" (* current accum)
"&current=" (sub1 current)))))))
no-template: #t)

sábado, 6 de fevereiro de 2010

Suporte a múltiplos bancos de dados para Awful

A partir da versão 0.10, awful passa a oferecer suporte a múltiplos bancos de dados. Atualmente há suporte para PostgreSQL (através da extensão postgresql) e SQLite3 (através da extensão sqlite3 ou sql-de-lite).

Há uma extensão para cada tipo de base de dados: awful-postgresql, awful-sqlite3 e awful-sql-de-lite. No caso de SQLite3, pode-se optar por usar tanto a extensão sqlite3 ou sql-de-lite (duas extensões diferentes para acesso a bases SQLite3).

A forma de acesso a bases de dados continua praticamente a mesma: através do procedimento $db, o qual faz uso das facilidades oferecidas por awful (conexão transparente, fechamento automático). A única diferença é que agora é necessário carregar a extensão relativa ao tipo de base de dados que pretende-se acessar (e.g., (use awful awful-postgresql)) e usar enable-db como um procedimento sem argumentos, não um parâmetro com argumento booleano, como antes.

Abaixo está um exemplo completo de uma aplicação que gera uma página HTML com uma tabela contendo dados de nome e endereço da tabela users de uma base de dados PostgreSQL:

(use awful awful-sql-de-lite html-utils)

(enable-db)

(db-credentials "db.db")

(define-page (main-page-path)
(lambda ()
(tabularize ($db "select name, address from users"))))


Para publicar esta página, basta executar (supondo que o código acima está no arquivo users.scm):

$ awful users.scm


(ficará disponível em http://localhost:8080).

Se um dia for necessário migrar essa base de dados para PostgreSQL, basta usar (use awful-postgresl) em vez de (use awful-sql-de-lite) e ajustar a configuração de credenciais para acesso ao banco (no caso de SQLite3, a credencial é o caminho para o arquivo com a base de dados). Se a estrutura da base de dados não for modificada, e se forem usadas consultas SQL portáveis, o código Scheme não precisará ser alterado.

Opcionalmente, a página pode ser compilada:

$ csc -s users.scm
$ awful users.so



Simples assim. :-)

terça-feira, 26 de janeiro de 2010

Woof em Chicken

Há uns dias li sobre o woof, um programa para compartilhar arquivos via HTTP. A descrição do site é mais detalhada:


I guess everybody with a laptop has experienced this problem at some
point: You plug into a network and just want to exchange files with
other participants. It always is a pain until you can exchange
files with the person vis-a-vis.

Of course there are a lot of tools to tackle this problem. For large
scale communities there are dozens of filesharing networks. However,
they don't work for small local networks. Of course you could put your
stuff to exchange on a local web server, but who really wants to
maintain this? Tools like the ingenious
npush/npoll are
extremely helpful, provided that both parties have it installed,

SAFT/sendfile
also aims to solve this problem, but needs a permanently running daemon...

Woof (Web Offer One File) tries a different approach. It
assumes that everybody has a web-browser or a commandline web-client
installed. Woof is a small simple stupid webserver that can
easily be invoked on a single file. Your partner can access the file
with tools he trusts (e.g. wget). No need to enter
passwords on keyboards where you don't know about keyboard sniffers, no
need to start a huge lot of infrastructure, just do a

$ woof filename

and tell the recipient the URL woof spits out. When he got that
file, woof will quit and everything is done.


Abaixo está uma implementação em setenta e poucas linhas de código em Chicken Scheme, usando o servidor web Spiffy.

#! /usr/bin/csi -s
(use posix srfi-13 spiffy)

(define (woof file count ip-addr port)
(when port (server-port port))
(root-path (or (pathname-directory file) (current-directory)))
(let ((current-count 0)
(ip-addresses (and ip-addr (string-split ip-addr ",")))
(file (pathname-strip-directory file)))
(handle-file
(lambda (path)
(if (equal? (pathname-strip-directory path) file)
(if (or (not ip-addr) (member (remote-address) ip-addresses))
(let* ((ext (pathname-extension path))
(handler (alist-ref ext (file-extension-handlers)
string-ci=? send-static-file)))
(set! current-count (add1 current-count))
(print "Serving file " file
" to " (remote-address)
" (count=" current-count ").")
(handler path)
(when (>= current-count count)
(exit 0)))
(begin

(print "Denying access to host " (remote-address) ".")
(send-status 403 "Forbidden")))
(begin
(print "Denying access to file " path ".")
(send-status 403 "Forbidden")))))
(print "Sharing '" file "' with "

(if ip-addresses
(string-intersperse ip-addresses ", ")
"anybody")
".")
(print "URL: http://" (get-host-name) ":" port "/" file)
(start-server)))

(define (usage #!optional exit-code)
(display
(string-append
(pathname-strip-directory (program-name))
" [ --port=<port> ] [ --ip-addr=<ip address> ] [ --count=<count> ] file\n")
(if exit-code (current-error-port) (current-output-port)))
(when exit-code (exit exit-code)))

(define (cl-option-value option args #!optional (converter identity))
(let loop ((args args))
(and (not (null? args))
(let ((arg (car args)))
(if (string-prefix? option arg)
(cond ((string-match (string-append option "=(.*)") arg)
=> (lambda (m)
(converter (cadr m))))
(else (loop (cdr args))))
(loop (cdr args)))))))

(let ((args (command-line-arguments)))
(when (null? args)
(usage 1))
(when (or (member "-h" args) (member "--help" args))
(usage 0))
(let ((count (cl-option-value "--count" args string->number))
(ip-addr (cl-option-value "--ip-addr" args))
(port (cl-option-value "--port" args string->number))
(file (car (reverse args))))
(if (file-exists? file)
(woof file (or count 1) ip-addr port)
(begin

(display (string-append file " does not exist.\n")
(current-error-port))
(exit 1)))))


Nem todas as funcionalidades do woof original estão presentes, mas é possível especificar:

  • o arquivo a ser compartilhado

  • a porta a ser usada pelo servidor

  • o número IP da máquina que poderá pegar o arquivo (pode-se liberar o acesso para mais de uma máquina separando os números IP por vírgula)

  • o número de vezes que o arquivo pode ser acessado



Abaixo está a sintaxe para utilização do woof em Chicken na linha de comando:
$ woof --help                                  
woof [ --port=<port> ] [ --ip-addr=<ip address> ] [ --count=<count> ] file

sábado, 23 de janeiro de 2010

jsmin para Chicken Scheme

Há pouco fiz o commit no repositório de extensões do sistema Chicken de uma tradução para Scheme do código C do jsmin, de Douglas Crockford.

A extensão fornece apenas dois procedimentos: jsmin-string e jsmin-file, as quais compactam o código javascript considerando como entrada, respectivamente, uma string e um arquivo.

O código Scheme traduzido de código C (sem uso da FFI) não é dos mais bonitos :-), pois a tradução foi feita praticamente linha-a-linha (inclusive mantendo os returns, implementados através de call/cc).

Programação para a Web com Awful

Há uns dias comecei a desenvolver mais uma tentativa de tornar a programação para a Web mais "fácil". O resultado é uma extensão para o sistema Chicken (versão 4) que fornece as seguintes funcionalidades:

  • Interfaces simples para o uso de bases de dados Postgresql

  • Suporte a Ajax através da biblioteca JQuery

  • Acesso fácil às variáveis de requisições HTTP (tanto da query string (método GET) quando do corpo da requisição (e.g., método POST)

  • Flexibilidade: há vários parâmetros de configuração

  • A compilação de páginas para código nativo para a plataforma alvo é fácil

  • Disponibilidade de um visualizador de sessão

  • Disponibilidade de um REPL que pode ser acessado através do navegador (via HTTP) para a depuração de aplicações para a web



Tentei fazer uma documentação razoável, que cobrisse boa parte da implementação. Ela está em http://chicken.wiki.br/eggref/4/awful. Na documentação podem ser vistos exemplos de aplicações simples que demonstram algumas das funcionalidades.

Sobre o nome: é só um nome. :-) Em http://chicken.wiki.br/eggref/4/awful#the-name há algumas possíveis interpretações caso AWFUL fosse uma sigla.