Mostrando postagens com marcador web-scheme. Mostrar todas as postagens
Mostrando postagens com marcador web-scheme. Mostrar todas as postagens

quinta-feira, 3 de setembro de 2009

web-scheme para Chicken 4

Vou aproveitar a transição de versões do sistema Chicken (versão 3 -> versão 4, com algumas incompatibilidades) para lançar uma versão nova de web-scheme, totalmente incompatível com a versão anterior (para Chicken 3). Eu mesmo provavelmente serei o maior prejudicado pelas incompatibilidades. Sem exagero, devo ter algumas dezenas de milhares de linhas de código usando web-scheme e Chicken 3. Mas o momento é esse.

Se bem me lembro, a extensão web-scheme foi lançada em 2005 e, desde então, venho pensando em algumas melhorias que só podem ser implementadas quebrando a compatibilidade com versões anteriores. Como Chicken 4 de certa forma faz isso, vou aproveitar a onda. :-)

As duas maiores modificações são sintáticas.

Uma das coisas que me incomodam em web-scheme para Chicken 3 é o mapeamento direto de nomes de tags para procedimentos. Inicialmente me pareceu uma boa idéia, e na maioria dos casos é, mas há situações que tornam esse aspecto inconveniente. Por exemplo, o procedimento equivalente à tag map, deveria, a rigor, ser map. Obviamente isso causa problemas em Scheme.

Casos semelhantes ocorrem com tags com nomes muito usuais como select (Chicken define uma macro com esse nome), title, i ou object.

Em Chicken 4 há a possibilidade de se importar um módulo prefixando símbolos do módulo importado. Então, por exemplo, seria possível importar o módulo web-scheme prefixando os procedimentos por ws:. Nos casos como o do procedimento map, não usar um prefixo seria suicídio. É difícil algum código em Scheme não fazer uso de map. web-scheme para Chicken 3 coloca "de fábrica" o prefixo ws: nos procedimentos map e select, para evitar conflito de nomes com os respectivos procedimento e macro correspondentes. Isso já é meio feio em Chicken 3, em que não há uma forma direta para se importar uma biblioteca de extensão com um prefixo -- em Chicken 4 seria muito feio, pois se o usuário decide prefixar os símbolos do módulo, ficarão dois prefixos para map e select!

Decidi, então, modificar o nome de todos os procedimentos correspondentes a tags HTML e usar uma notação semelhante à usada por Hop. Assim, em vez de (pre "texto"), tem-se (<pre> "texto").

Não estou totalmente satisfeito com esta notação, pois é conflitante com a convenção de nomes de classes quando se usa um sistema de objetos. Em Scheme não chega a ser um grande problema, pois o uso de classes não é muito usual. Em Common Lisp essa decisão seria mais problemática, pois CLOS é bastante usado. Optei por essa sitaxe porque claramente lembra a sintaxe de tags HTML e provavelmente desenvolvedores para a Web associarão <pre> à tag pre de HTML e não à classe pre de algum código parte de um sistema de objetos.

A outra modificação é com relação à sintaxe para atributos e valores de atributos de tags. Em web-scheme para Chicken 3 usa-se da seguinte forma:

(a 'href "http://minha-url.com" "Minha URL")


Em web-scheme para Chicken 4 decidi usar parâmetros por palavra-chave para representar atributos. Fica, então, assim:

(<a> href: "http://minha-url.com" "Minha URL")


As razões para esta mudaça são puramente técnicas. No esquema usado em web-scheme para Chicken 3, precisei implementar um parser para extrair atributos e seus respectivos valores. Usando parâmetros por palavra-chave, ganho o parser "de brinde" de Chicken, melhor e mais rápido.

O esquema de atributos usado por web-scheme para Chicken 3 tem uma limitação chata quando espera-se poder usar ou não atributos em função do fluxo de execução do código. Exemplo: no caso de um procedimento text-input como um wrapper para o elemento input de tipo texto:

(define (text-input text #!key maxlength)
(if maxlength
(input 'type "text" 'maxlength maxlength 'value text)
(input 'type "text" 'value text)))


Em web-scheme para Chicken 3 não há uma forma "fácil" (i.e., sem eval ou macros) de fazer isso sem duplicar código. Em web-scheme para Chicken 4 bastará:

(define (text-input text #!key maxlength)
(<input> type: "text" maxlength: maxlength value: text)


O uso de parâmetros por palavra-chave dá essa flexibilidade, o que é tratado internamente pelo código que gera o HTML. Se o valor de um parâmetro for #f o par atributo/valor não é gerado. Em web-scheme para Chicken 3 geraria uma certa complicação nos casos de atributos que não requerem valor, como selected em elementos select.

Essas serão as modificações mais radicais. Haverá também modificações nos procedimentos extras de web-scheme, os quais não estão diretamente relacionados ao mapeamento tags->procedimentos. Mas essa parte ainda está em estudo.

terça-feira, 6 de maio de 2008

Código formatado no Blogger

A minha primeira tentiva de escrita de código no Blogger foi frustrante: sem destaque de sintaxe (syntax highlight) e um editor com poucos recursos.

Vi que a marcação do texto era feita com HTML (surpresa!) e decidi fazer um script para gerar texto formatado para o Blogger. Faço a marcação do texto com web-scheme e o programa se encarrega de gerar o HTML. Depois só copio e colo o código HTML na caixa de edição do Blogger.

O programa para geração de HTML está a seguir (em Chicken Scheme). O "embelezamento" do código é feito com o programa Enscript.

(use posix utils web-scheme (srfi 1))

(define (run . cmd)
(let* ((p (open-input-pipe (sprintf "~A 2>&1" (apply conc cmd)))))
(read-all p)))

(define (run-enscript code lang #!optional pretty)
(let ((tmp (create-temporary-file)))
(with-output-to-file tmp (lambda ()
(if pretty
(pp code)
(print code))))
(let ((out (run "enscript -q --color --highlight="
lang " -o - -whtml " tmp)))
(delete-file tmp)
out)))

(define (htmlize code lang #!optional no-pre)
(let* ((out (run-enscript code lang (not (string? code))))
(lines (with-input-from-string out read-lines)))
(string-append
((if no-pre ;; pre nao quebra linhas
tt
pre)
(string-intersperse
;; retira cabecalho e rodape colocados pelo enscript
(drop-right (take-right lines (- (length lines) 11)) 5)
"\n"))
(if no-pre "\n" ""))))

(define ->
(let ((bookmarks
'((Chicken . "http://www.call-with-current-continuation.org")
(web-scheme . "http://chicken.wiki.br/web-scheme")
(eggs . "http://chicken.wiki.br/eggs"))))
(lambda (url #!optional text)
(let ((bookmarked (alist-ref url bookmarks)))
(if (and (symbol? url) bookmarked)
(a 'href bookmarked (symbol->string url))
(a 'href url (or text url)))))))

(define (p . text) ;; paragrafo do blogger
(string-append (apply conc text) "\n\n"))

(define (usage #!optional exit-code)
(print (program-name) " <input-file>")
(when exit-code (exit exit-code)))

(let ((args (command-line-arguments)))
(if (null? args)
(usage 1)
(print
(string-intersperse
(map eval (with-input-from-file (car args) read-file))))))

A função -> é especialmente interessante: admite como argumentos uma URL e um texto, somente uma URL, ou uma palavra-chave para acesso aos bookmarks.

O código fonte deste artigo inteiro é:

(p "A minha primeira tentiva de escrita de código no Blogger foi frustrante: sem destaque de sintaxe (" (i "syntax highlight") ") e um editor com poucos recursos.")

(p "Vi que a marcação do texto era feita com HTML (surpresa!) e decidi fazer um script para gerar texto formatado para o Blogger. Faço a marcação do texto com " (-> 'web-scheme) " e o programa se encarrega de gerar o HTML. Depois só copio e colo o código HTML na caixa de edição do Blogger.")

(p "O programa para geração de HTML está a seguir (em " (-> 'Chicken) " Scheme). O \"embelezamento\" do código é feito com o programa " (-> "http://www.gnu.org/software/enscript/" "Enscript") ".")

(htmlize (read-all "./bin/blog.scm") 'scheme)

(p "A função " (code "->") " é especialmente interessante: admite como argumentos uma URL e um texto, somente uma URL, ou uma palavra-chave para acesso aos " (i "bookmarks") ".")

(p "O código fonte " (b "deste artigo inteiro") " é:")

(htmlize (read-all "./call-hc/codigo-formatado-no-blogger.scm") 'scheme 'no-pre)

segunda-feira, 5 de maio de 2008

web-scheme for dummies

web-scheme é uma extensão do sistema do sistema Chicken que implementa uma linguagem de programação baseada em Scheme para a Web.

web-scheme disponibiliza funções para a geração de código HTML a partir de código Scheme. Exemplo:

(div 'id "caixa" "Texto")

produz a string

<div  id="caixa">Texto</div>

Todas as tags de HTML são representadas como funções em Scheme de mesmo nome, exceto nomes conflitantes, como, por exemplo, map e select (nomeadas ws:map e ws:select, respectivamente).

Além da representação de tags HTML como funções em Scheme, web-scheme também disponibiliza algumas funções que podem facilitar a geração de código HTML.

Um exemplo é a função ws:page, para gerar páginas HTML de forma desburocratizada. Exemplo:

(ws:page "Oi")

gera a seguinte string com código HTML:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html><head><meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title></title>
</head>
<body>Oi</body>
</html>

É possível alterar o código HTML gerado por ws:page através do uso de parâmetros. Por exemplo, o conjunto de caracteres da página pode ser alterado com o parâmetro charset:

(ws:page "Oi" charset: "utf-8")

produz

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title></title>
</head>
<body>Oi</body>
</html>

Outro exemplo é a função ws:make-table, que usa como argumento uma lista de listas representando uma tabela:

(ws:make-table '((1 2 3) (4 5 6)))

produz

<table><tr><td>1</td>
<td>2</td>
<td>3</td>
</tr>
<tr><td>4</td>
<td>5</td>
<td>6</td>
</tr>
</table>

Além dessas, há várias outras funções, macros e parâmetros com documentação no site da extensão.