Red + Ruby = Reby ou Rud ?

Como todos sabem, ou não, a próxima versão de Red traz #macros. No primeiro formato, é apenas um nome seguido de uma função e seus respectivos parâmetros. Neste caso, a macro irá avaliar a função e trocará o nome e seus parâmetros pelo resultado. No segundo formato, baseado em um padrão definido, o usuário poderá alterar o código para um novo formato (meio estilo Lisp). É interessante pois é uma outra forma de criação de dialetos em Red. Um usuário que gosta de Ruby e Python resolveu colocar o seguinte exemplo na lista:

Para repetir um trecho um determinado número de vezes em Red, utilizamos loop. Fica algo como:

loop 10 [
    print "Hello"
]

A macro acima permite que se crie um dialeto possibilitando a mesma construção  utilizada em Ruby, ou seja:

10 times [
    print "Hello"
]

Basicamente inseriu loop no início, incluiu a primeira parte do código start/1 que é o número 10, pulou a segunda parte start/2 que é o times e incluiu o bloco que é a terceira parte.

Possibilidades? Fica por conta da sua imaginação.  Como a linguagem ainda é beta, provavelmente #macros sofrerão algumas alterações no decorrer do desenvolvimento. Ainda não sei quando, como e onde utilizar #macros e parser para a criação de dialetos, isto é, qual a melhor situação para a utilização de um ou outro ou os dois simultaneamente. Muita coisa para a minha cabecinha. 😀

Red – um pouco de diversão

Como Red ainda está em desenvolvimento (e vai continuar por um bom tempo), um dos problemas que existem é a falta de documentação. O REPL é bastante prestativo permitindo que se digite o início da função, pressione-se Tab e uma lista das possíveis complementações é mostrada. Muitas vezes você sabe que existe algo mas não lembra exatamente o nome ou como inicia. Você pode digitar what e uma relação das funções disponíveis com uma breve descrição será mostrado. Mesmo assim pode ser possível que você não lembre exatamente como é utilizada. Mas você poderá utilizar Help ou ? função e verá maiores informações sobre a utilização dela.

Mas que tal facilitar um pouco a vida? Que tal uma janela com uma lista das funções onde, ao selecionar uma função, será mostrada sua utilização, descrição, argumentos e possíveis refinamentos? Bem, tipo a janela abaixo.

red-fun

Melhor ainda se for com apenas 27 linhas de código (poderia ser menos) como o que segue:

Como o suporte para GUI no Linux e OS X ainda não estão prontos, o programa funciona só no Windows (ou no wine para Linux).

Linhas 1 a 6 é apenas o cabeçalho.

Linha 8 só é necessária se o programa for compilado. O programa help.red encontra-se em environment/console da distribuição e, no meu caso, apenas copiei para o mesmo diretório do programa.

Linhas 12 a 16 varrem as funções existentes e as armazenas na variável fn

Linhas 18 a 27 se encarregam de criar a janela, a lista e a área de texto, preencher a lista com as funções encontradas e reagir ao clique na lista mostrando as informações na área de texto. Se você acha 10 linhas muito, é só colocar tudo que está depois do do na mesma linha que ficam apenas 5 linhas.

Existe espaço para melhorias como colocar exemplos e os respectivos resultados de cada função para facilitar ainda mais a vida do usuário ou especificar um filtro para a lista de funções.

Bem, por enquanto era isso.

Red – Parser

Introdução

É possível dizer que o objetivo do Parser no Red é o mesmo das Expressões Regulares (ER) em outras linguagens de programação. As diferenças básicas são:

  • Criado como uma dialeto (DSL) em Red.
  • Mais legível que uma ER (depende?).
  • Por ser criado em Red, possui uma grande integração com a linguagem.

O objetivo é, dada uma entrada qualquer (em Red é uma série como string, lista, etc.) e, baseando-se em determinadas regras, iremos verificar as ocorrências que casam na entrada para tomarmos diversas decisões. Em Red, podemos apenas verificar o casamento das regras retornando falso ou verdadeiro, coletar as partes onde a regra casa ou alterar os trechos onde as regras casam, etc..

Vejamos um caso prático e simples:

Desejamos verificar se o formato de um CPF é válido. Iremos aceitar valores no formato de três séries de três números, separados por ponto, um separador para o controle que poderá ser hífen ou barra e uma sequência de dois número para o controle. Ficaria algo como 999.999.999[-/]99. Uma das opções para o Parse em Red seria:

digitos: charset "0123456789"
centena: [3 digitos]
dezena: [2 digitos]
separador: charset "/-"
cpf: [centena dot centena dot centena separador dezena]

Diferente das ERs, Red não vem com a definição de dados numéricos (\d) para o Parser. Mas, como pode ser visto, é simples de definir. Também definimos centena como uma sequência de três dígitos e dezena como uma sequência de dois dígitos para facilitar a leitura da regra. Definimos os dois caracteres para o separador. O charset especifica que qualquer um dos caracteres irá casar com a regra. Poderíamos definir como [“/” | “-“], isto é, barra ou hífen. Se definíssemos apenas como “/-“, para casar, os dois caracteres deveriam estar presentes na ordem indicada. Alguns caracteres como dot, newline, cr, etc. já estão definidos na linguagem. Optei por usar o dot em vez de “.” para facilitar a leitura da regra. Finalmente criei a regra cpf (que é composta por várias outras regras) para validar se uma entrada é um CPF válido ou não (não estou calculando o controle, apenas o formato da entrada).

Se eu estiver no REPL do Red, uma sessão típica poderia ser:

red>> parse "100.010.001-00" cpf
== true

red>> parse "000.000.000/00" cpf
== true

red>> parse "999.111.555/77" cpf
== true

red>> parse "192.168.45" cpf
== false

red>> parse "192.168.45-77" cpf
== false

Outra possibilidade para a utilização da regra CPF seria para extrair todos os CPFs de um texto. Supondo o seguinte texto:

NOME: João
IP: 192.45.77.44
CPF: 111.111.111/11
NOME: Antônio
IP: 194.58.58.9
CPF: 123.456.789-00
NOME: Empresa Ltda.
IP: 201.195.45.44
CGC: 44.555.666/0001-11

No REPL e supondo-se que o texto estivesse sendo referenciado por s, teríamos algo como:

red>> parse s [collect some [keep cpf | skip]]
== ["111.111.111/11" "123.456.789-00"]

Aqui o collect irá colocar os CPFs da entrada em uma lista e o keep informa o que deverá ser armazenado na lista. No caso, keep cpf ou salta para a próxima posição na entrada.

Entre as outras utilizações para a regra, a possibilidade de padronizar o formato do CPF tipo trocar a barra pelo hífen.

Como falei antes “integrado com a linguagem Red”, é possível passar um código para a regra e ele será interpretado quando a regra é satisfeita. Por exemplo (considerando o texto acima):

parse s [some [copy v cpf (print ["Encontrei:" v]) | skip ]
Encontrei: 111.111.111/11
Encontrei: 123.456.789-00
== true

Quando a regra casar com a entrada, v conterá uma referência ao valor encontrado e o código que estiver entre parênteses será executado.

Sei que você não vai dizer, mas caso diga algo como: “Ah mas com ER eu escrevo menos. Seria algo como \d{3}\.\d{3}\.\d{3}-\d{2}”

Se o problema fosse só quantos caracteres você digita, certamente você iria programar em J ou K. Se você não programa nessas linguagens, você não está sendo 100% sincero. O ideal seria a gente pensar, pressionar enter e …. o programa se materializar no disco. Bem, também é possível diminuir a verbosidade no Red. Poderia ter algo como:

D: charset "0123456789"</pre>
<pre>cpf: [3 D dot 3 D dot 3 D "-" 2 D]

Não ficou tão ruim né? Pessoalmente, até acho um pouco mais legível que a ER para um leigo.

Por curiosidade, se você quiser mostrar o título de uma página html, o código ficaria assim:

page: read http://www.red-lang.org/
parse page [thru &lt;title&gt; copy titulo to &lt;/title&gt;]
print titulo

e será apresentado “Red Programming Language”.

Recursividade

As regras podem ser recursivas. Se você deseja validar se uma entrada é uma expressão matemática válida, você pode aplicar algo como o código abaixo. Pense em algo como BNF.

expr:    [term ["+" | "-"] expr | term]
term:    [factor ["*" | "/"] term | factor]
factor:  [primary "**" factor | primary]
primary: [some digit | "(" expr ")"]
digit:   charset "0123456789"

Para as regras acima você usou, basicamente, apenas o que já foi mostrado neste artigo. O único acréscimo foi de some que significa 1 ou mais ocorrências, isto é, no mínimo um dígito. Tem o any que significa zero ou mais. Se você conhece ER irá associar imediatamente os dois meta-caracteres para as duas funções.

Então parse “1+2*(3-2)/4” expr irá retornar true (é uma expressão válida para a regra) ao passo que parse “1-(3/)+2” expr irá retornar false (a expressão é inválida conforme a regra).

Então é isso. Ficar por aqui para não escrever um livro (a única forma de abranger detalhadamente tudo).

 

Red -convertendo imagens para tons de cinza

Resolvi fazer um programa em Red (lembrando que ainda está em desenvolvimento e na versão 0.6) para converter uma imagem colorida em preto e branco (tonalidades de cinza). Basta que as cores R, G e B possuam o mesmo valor para que a imagem seja mostrada com tonalidades de cinza. Basicamente temos seis formas de conversão, cada qual apresentando resultados característicos.

Claridade

O nível do cinza será calculado como
Claridade = (max(R, G, B) + min(R, G, B)) / 2

Luminosidade

O nível do cinza será calculado como
Luminosidade = 0.21 × R + 0.72 × G + 0.07 × B

Brilho Médio

O nível do cinza será calculado como
Brilho médio = (R + G + B) / 3

Canais

Escolhemos um dos canais (R, G, B) e replicamos e seu valor nos outros dois canais. Esta técnica simula uma foto feita com um filme preto e branco com a utilização de um filtro com uma das cores dos canais. O resultado é que as cores complementares serão bloqueadas deixando passar apenas a cor do filtro. Utilizando um filtro vermelho em uma foto onde aparece o céu azul e com nuvens brancas, o céu ficará bem mais escuro entregando uma maior dramaticidade para a foto.

O efeito obtido por cada uma das técnicas pode ser visto nas imagens abaixo.
Como a parte gráfica para Mac e Linux ainda estão em desenvolvimento (a do Windows também mas está mais próxima do resultado final), o programa só roda no Windows. Futuramente deverá rodar no Mac, Linux, etc..

Casualmente o programa ficou com 100 linhas como pode ser visto abaixo.

Resultado das diferentes conversões para visualizar a diferença entre elas:

O programa ainda sofrerá alterações mas, por enquanto, era isto.