You are currently browsing LES’s articles.

Às vezes, ajuda muito poder examinar determinadas condições do sistema enquanto um programa/script é executado, mais ou menos como quem está voando e verifica as condições meteorológicas. Não só em procedimentos de depuração, embora eu ache que estes sejam os casos mais comuns.

Na dica anterior, eu falei de introspecção com o comando [info]. Agora, veja que informações importantes pode-se obter de determinadas variáveis. Assim como a linguagem Bash tem uma série de variáveis reservadas para informações de depuração, Tcl/Tk tem as suas.

  • env: contém um array com as variáveis de ambiente e seus respectivos valores. Tanto a forma quanto o conteúdo são muito parecidos com o que é retornado pelos comandos ‘env’ e ‘printenv’ em Bash. Para examinar um array, use o comando parray, e.g. [parray env].
  • tcl_platform: outro array, este contém informações sobre a plataforma. O item mais valioso costuma ser $tcl_platform(platform), que indica se o ambiente é Windows, Linux/Unix ou Mac, entre outros. Com esta informação, fica fácil adaptar um programa para que ele rode em diversas plataformas sem engasgar nem pedir ajuda ao usuário. Exemplos da minha máquina:
    tcl_platform(platform)    = unix
    tcl_platform(os)          = Linux
    tcl_platform(osVersion)   = 2.6.24-23-generic
    tcl_platform(machine)     = i686
    tcl_platform(byteOrder)   = littleEndian
  • tcl_patchLevel: é uma variável escalar que contém o número exato da versão de Tcl/Tk que está sendo usada para executar o programa. Por exemplo, 8.5.4. Serve para gerar mensagens de erro e admoestar o usuário caso ele esteja usando uma versão muito antiga de Tcl/Tk. Muita coisa importante só foi incorporada a partir da versão 8.4.0 (setembro de 2002).
  • errorInfo: variável escalar, contém o texto de erro do último erro registrado durante a execução do programa. A maioria das linguagens interrompe a execução do programa em caso de erro. Em Tcl/Tk, é muito fácil ajustar o programa para ignorar erros e seguir em frente. Neste caso, o último erro ocorrido fica registrado nesta variável.
  • argv: variável escalar, contém a lista dos argumentos com que o programa foi executado. Informação indispensável para a execução da maioria dos scripts. Se o programa não for executado com nenhum argumento, argv retorna vazia.
  • argc: variável escalar, contém o número de argumentos com que o programa foi executado. É o mesmo que [llength $argv]. Serve para confirmar se o programa foi executado com o número mínimo, máximo ou exato de argumentos que o programa exige. Se o programa não for executado com nenhum argumento, argc retorna o número 0.

Mais informações em http://www.tcl.tk/man/tcl8.5/TclCmd/tclvars.htm

introspecção:
[Do ingl. introspection.] S. f.
1. Observação da vida interior pelo próprio sujeito; exame que alguém faz dos próprios pensamentos e sentimentos.
Dicionário Aurélio – Século XXI

Às vezes, ajuda muito poder examinar TUDO que está acontecendo em um programa/script em um determinado momento. Não só em procedimentos de depuração, embora eu ache que estes sejam os casos mais comuns.

O comando da introspecção em Tcl/Tk é [info]. Sozinho, ele não faz nada. Precisa de pelo menos um argumento. Existem mais de 20 possíveis, mas os mais comuns são:

  • [info vars]: retorna os nomes de todas as variáveis já criadas no programa até o momento em que [info vars] for executado. Se [info vars] estiver no fim do programa, vai revelar todas as variáveis. Se [info vars] estiver no meio programa, não revela as variáveis que forem criadas depois dele.
  • [info exists "nome de variável"]: teste booleano que verifica se uma determinada variável existe, se já foi criada/declarada. É mais fácil fazer assim do que usar [info vars] e ficar procurando uma determinada variável na lista retornada.
  • [info globals]: retorna os nomes de todas as variáveis globais.
  • [info locals]: retorna os nomes de todas as variáveis locais, ou seja, que só existem no contexto de um procedimento (proc). Logo só faz sentido usar [info locals] dentro de um proc.
  • [info procs]: retorna os nomes de todos os procs já criados no programa até o momento em que [info procs] for executado.
  • [info body]: retorna todo o “corpo”, o código de um proc. É como uma radiografia do código fonte. Mostra tudo.
  • [info commands]: retorna os nomes de todos os comandos existentes no programa, inclusive os procs. Também inclui os “apelidos“. Isso inclui TODOS os comandos da linguagem Tcl/Tk, mas observe que alguns comandos podem ter sido apagados.

É sempre mais interessante testar estes comandos no console Tkcon, mas é claro que eles podem ser testados em qualquer programa ou script em Tcl/Tk.

Não é muito interessante testar introspecção em scripts muito pequenos. Quanto maior for o programa, maior será a quantidade de informação a ser garimpada.

Podemos obter o conteúdo (texto) da área de transferência, mais conhecida por aí como “clipboard”, a mesma que é usada nas operações de copiar-e-colar do ambiente gráfico.

Antes, é preciso carregar o pacote Tk. Você pode rodar estes testes com o executável “wish”, que já tem Tk, ou apenas executar “tclsh” e carregar o Tk como um pacote qualquer:

% package require Tk

Pronto! Copie algum texto de um programa qualquer. A seguinte função/procedimento mostra o conteúdo da área de transferência:

% proc clipLeitura {} {
       catch { set varclip [ selection get -selection CLIPBOARD ] } Texto
       return $Texto
}

% clipLeitura
Texto copiado

Só é possível copiar texto. Se o conteúdo não for texto, a função retorna erro, por isso eu sempre uso o comando [catch] na primeira linha. Assim, o procedimento retorna uma string vazia, mas não faz o programa/script/comando morrer.

Não podemos só ler, também podemos escrever:

% proc clipEscrita { Texto } {
       clipboard clear -displayof .
       clipboard append -displayof . $Texto
}

% clipEscrita "Um texto qualquer"

Este comando coloca “Um texto qualquer” na área de transferência. Essa string estará pronta para ser “colada” em qualquer campo ou janela de qualquer programa do ambiente gráfico.

Este procedimento funciona bem com um script que crie pelo menos uma janela Tk e esteja devidamente preparado para lidar com interações do usuário. A maneira mais fácil é testar dentro do console Tkcon, uma espécie de console para ambiente gráfico que, em vez de shell, interpreta Tcl/Tk.

A primeira coisa a se fazer é carregar o pacote Snack:

% package require sound
2.2

Se o número da versão do pacote for retornado, significa que ele está presente e foi carregado. Se não, é preciso verificar a instalação do seu sistema antes de prosseguir.

Em seguida, selecione o arquivo:

% snack::sound s -file "/caminho/do/arquivo.wav"

Daí, é só mandar tocar:

% s play -block 1

Exemplo de função:

proc SomNaCaixa { arquivoDeSom } {
       package require sound
       snack::sound s -file "$arquivoDeSom"
       s play -block 1
}

SomNaCaixa "/caminho/do/arquivo.mp3"

O Snack é multiplataforma e suporta os formatos WAV, AU, AIFF, MP3, CSL, SD, SMP e NIST/Sphere. Também reproduz streaming.

Mais informações:

O comando [expr] é um dos que mais desagradam quem está começando a programar em Tcl, por causa da sintaxe:

% puts "2 + 2 = [ expr {2+2} ]"
2 + 2 = 4

O comando [unknown], super versátil, permite-nos chutar o [expr] pra escanteio na grande maioria das situações. Normalmente, [unknown] é o comando acionado automaticamente pela linguagem em caso de erro de digitação ou comando desconhecido. Tudo que ele faz é retornar uma mensagem de erro. Exemplo:

% 6*8+3*2+42
invalid command name "6*8+3*2+42"

Mas ele pode ser alterado, como quase tudo em Tcl.

Antes, é mais prudente reservar o comando [unknown] original dando-lhe outro nome:

% rename unknown MsgErro

Agora, ele se chama [MsgErro]. Em seguida, criamos um novo comando [unknown]:

% proc  unknown  args  {
if      { [ regexp  {^[-+/\*\.0-9 ()]+$}  $args ] }     {
return  [ expr $args ]
}

uplevel 1 MsgErro $args
}

Traduzindo: a cláusula “IF” usa Expressões Regulares para detectar se a linha de comando “incorreta” é uma expressão aritmética, inclusive parênteses. Assim, também podemos agrupar expressões. Se a cláusula “IF” não casar, o novo comando [unknown] retorna aquela função de erro original que foi transferida para o comando [MsgErro]. Não precisamos mais da sintaxe chata do [expr]:

% 6*8+3*2+42
96

% 45+2*3+6-1
56

% (45+2)*(3+6)-1
422

% puts "Um dia tem 24 horas ou [24*60] minutos"
Um dia tem 24 horas ou 1440 minutos

Além de renomear comandos em Tcl/Tk, também é possível criar “apelidos” para os comandos com o [interp alias]. Por exemplo, o comando [file exists] verifica se um determinado arquivo existe:

% if { [file exists "/caminho/do/arquivo.txt"] == 1 } {
       puts "O arquivo existe"
}

Se você achar que “file exists” é muito longo e/ou tiver saudade do Bash, pode criar o apelido “-e”:

% interp alias {} -e {} file exists

% if { [-e "/caminho/do/arquivo.txt"] == 1 } {
       puts "O arquivo existe."
}

Todos os outros comandos -[letra] do Bash podem ser recriados e carregados no início do programa:

interp alias {} -e {} file exists
interp alias {} -f {} file isfile
interp alias {} -d {} file isdirectory
interp alias {} -r {} file readable
interp alias {} -w {} file writable
interp alias {} -x {} file executable
interp alias {} -o {} file owned

Apelido “@” para o foreach, se você sentir saudade de Perl:

% interp alias {} @ {} foreach

% @ x "1 2 3" { puts $x }
1
2
3

Para quem gosta muito de PHP:

% interp alias {} strtolower {} string tolower

% puts [strtolower "GRITARIA"]
gritaria

Tcl/Tk tem propriedades camaleônicas. Praticamente tudo pode ser modificado, o que pode resultar na criação de uma linguagem quase inteiramente nova. Por exemplo, se você não gostar do comando nativo [puts] porque está muito acostumado com ‘echo’, pode mudar o nome do comando:

% puts "Alô, mamãe!"
Alô, mamãe!

% rename puts echo

% puts "Alô, mamãe!"
invalid command name "puts"

% echo "Alô, mamãe!"
Alô, mamãe!

Se o segundo argumento de [rename] for a string vazia em vez de um nome novo para o comando a ser renomeado, o comando é apagado!

% rename echo {}

% echo "Alô, mamãe!"
invalid command name "echo"

Você pode querer “matar” alguns comandos em programas/scripts que aceitem dados digitados pelo usuário. Sem determinados comandos, o programa fica mais seguro.

Não existe comando nativo em Tcl/Tk que retorne o valor de Pi. Mas o valor de Pi está disponível em muitos lugares, logo é fácil registrar seu valor numa variável ou num procedimento e usá-lo sempre que quiser.

Numa variável:

set pi "3.141592653589793238462643383279502884197169399375105820
9749445923078164062862089986280348253421170679821480865132823066
4709384460955058223172535940812848111745028410270193852110555964
46229489549303820"

Num procedimento:

proc Pi {}  {
return 3.1415926535897932384626433832795028841971693993751058209
7494459230781640628620899862803482534211706798214808651328230664
7093844609550582231725359408128481117450284102701938521105559644
6229489549303820
}

Como usar o valor de Pi em um cálculo:

set raio 5
expr {2 * $pi * $raio}
;# retorna 31.41592653589793

ou

set raio 5
expr {2 * [Pi] * $raio}
;# retorna 31.41592653589793

O uso do procedimento pode ser mais útil que o uso da variável, pois o procedimento pode conter instruções adicionais que ajustem o número de casas decimais antes ou depois de se retornar o valor.

Em Tcl/Tk, um procedimento tem praticamente o mesmo valor de um comando nativo. Podemos até modificar completamente a função de um comando nativo. Neste caso, seria criado o procedimento Pi, que passaria a ter o valor e o comportamento de um comando nativo novo. Falarei mais disso na próxima semana.

Add to Technorati Favorites

Assuntos