Turbo Pascal Forever – Input/Output

Amigos, após um tempo sem posts, volto ao nosso curso de Pascal certo de que a fase inicial de fundamentação na linguagem já passou e que agora estamos aptos a avançar para tópicos de complexidade intermediária, ficando cada vez mais avançados.

Com base nisso vamos a uma das fundações mais importantes de qualquer linguagem  computacional e um dos pilares  da computação, que é a camada de I/O (Input/Output), ou em português, E/S (Entrada/Saída) de dados.

Tudo na computação envolve entrada e saída de dados, desde uma simples tecla digitada no teclado do computador, leitura/gravação de arquivos em um disco rígido, até a transferência de dados em grande escala através de dispositivos mais complexos de comunicação, como as placas de redes, seja com fio (Ethernet) ou sem fio (Wireless).

Por ser uma das principais fundações da computação, as linguagens computacionais implementam o conceito de E/S de forma bem abstrata na maioria dos casos, e principalmente contam com a possibilidade de futuras extensões a seu mecanismo de E/S padrão, visando o operações de E/S em novos dispositivos.

Feita a devida introdução, como de costume, vamos a….

 …E/S no Turbo Pascal. 

Os dispositivos de E/S mais comuns que utilizamos em  um computador, principalmente nos computadores antigos, é o teclado e o monitor de vídeo, sendo o teclado reconhecido com um dispositivo apenas de entrada de dados e o vídeo um dispositivo apenas de saída.

Os dispositivos de armazenamento (disquetes, hard disks, …) por sua vez, são dispositivos tanto de entrada quanto de saída, uma vez que é possível ler e escrever dados nos mesmos.

Como podemos ver, existem diversos dispositivos e se para cada um desses dispositivos padrão existisse uma forma proprietária para acessá-lo, teríamos uma verdadeira Torre de Babel na programação de novos dispositivos.

Felizmente os sistemas operacionais existem para abstrair uma série de dispositivos de tal forma que os mesmos sejam reconhecidos e acessados de maneira uniforme e transparente para o desenvolvedor, que por sua vez não precisa se preocupar com os detalhes internos do dispositivo em uso.

Felizmente tanto o MSX-DOS quanto o seu pai, o CP/M80, padronizaram alguns dispositivos de E/S, conhecidos como dispositivos lógicos. Abaixo segue a lista de dispositivos lógicos padronizados pelo CP/M e consequentemente o MSX-DOS:

CON: Console, ou a tela de texto do computador;
TRM: Terminal. Geralmente é redirecionado para o console ou CON;
LST: Dispositivo de listagem ou impressora;
AUX: Dispositivo de comunicação auxiliar. Também conhecido como COM:;
COM: Dispositivo de comunicação externo, comumente conhecido como COM1, ou dispositivo de comunicação externo, como uma porta serial RS-232;
USR: Dispositivo do usuário. Geralmente uma peça de software, conhecida como device driver, carregada pelo sistema operacional para responder às chamados ao dispositivo de hardware e/ou software a ser controlado. Podemos associa-lo aos modernos  device drivers, encontrados nos sistemas operacionais modernos, só que aqui em seu estado mais primitivo;
KBD: Dispositivo de teclado;

Cada um desses dispositivos lógicos está mapeado no Turbo Pascal a arquivos  pré-definidos, conforme descrito abaixo:

Input, associado ao dispositivo CON:
Output, associado ao dispositivo CON:
Con, associado ao dispositivo CON:
Trm, associado ao dispositivo TRM:
Kbd, associado ao dispositivo KBD:
Lst, associado ao dispositivo LST:
Aux, associado ao dispositivo AUX:
Usr, associado ao dispositivo USR:

Conhecendo agora os dispositivos padrão suportados pelo Turbo Pascal, vamos verificar a seguir como interagir com esses dispositivos, lendo ou escrevendo informações para os mesmos.

Rotinas padrão de E/S.

Como se trata de Entrada e Saída, o procedimento padrão implícito nessas operações é ler no caso de requisição de entrada de dados e escrever no caso de requisição de saída de dados. No Turbo Pascal essas operações são feitas ou para arquivos no FileSystem do sistema operacional, ou para qualquer um dos arquivos mapeados, conhecidos como file handlers, para os dispositivos, conforme descrito anteriormente.

Procedure Write e WriteLn

Para escrever uma informação em um determinado arquivo, ou dispositivo, utilizamos o comando Write e WriteLn, que já utilizamos bastante durante o tutorial. Segue abaixo a definição do mesmo:

Write( Var1, Var2, Var3, ...VarN );

Onde Var1, Var2, Var3, …VarN, são variáveis e/ou constantes a serem escritas no dispositivo padrão do Turbo Pascal, que no caso é o arquivo pré-definido, Con, conforme pode ser visto no código exemplo abaixo.

Program TestWrite;
Var     strText : String[50];

Begin
  strText := 'And this is a variable.';
  Write( 'This a constant for Write Procedure.', strText );
End.

Entretanto, como citamos anteriormente, é possível escrever em outros dispositivos diferentes do padrão (Con)  do comando Write, e para isso você deve especificar a variável do arquivo associada ao dispositivo desejado, como pode ser visto na definição abaixo:

Write( VarFile, Var1, Var2, Var3, ...VarN );

Onde VarFile é a variável associada ao arquivo/dispositivo em que se deseja escrever. Segue abaixo um exemplo onde ao invés de ter a saída direcionada para o dispositivo Con (Console), o mesmo é direcionado para a impressora conectada ao micro, lógicamente que só funcionará em computadores com a impressora conectada e senão estiver conectada, o seu programa ficará travado até que se pressione CTRL+STOP.

Program TestPrinter;
Var     strText : String[50];

Begin
  strText := 'And this is a variable.';
  Write( Lst, 'This a constant for Write Procedure.', strText );
End.

No caso acima, utilizamos o arquivo/dispositivo pré-mapeado do Turbo Pascal, Lst, que já está devidamente associada ao dispositivo LST: do sistema operacional MSX-DOS.

A procedure WriteLn tem sua sintaxe e uso idêntico a de Write, entretanto WriteLn, salta uma linha, inserindo um Carriage Return/LineFeed no final do dado escrito.

A procedure Read e ReadLn

Para ler dados de um arquivo/dispositivo temos os comandos Read e ReadLn, que utilizaremos bastante durante todo o tempo em que estivermos programando em Pascal e seus filhotes, como o Delphi. Segue abaixo a sintaxe do comando Read:

Read( Var1, Var2, ..., VarN );

Onde Var1, Var2, … VarN, são as variáveis que receberão os dados de entrada do arquivo/dispositivo em que se está fazendo a operação de leitura, no caso de Read, o dispositivo padrão é o Kdb, ou teclado (Keyboard).

O modo mais comum é utilizar Read/ReadLn para transferir dados do teclado, para uma variável em memória, como podemos ver no exemplo a seguir.

Program TestRead;
Var
      strData : String[255];

Begin
  Write( 'Please type a word' );
  Read( strData );
End;

No exemplo acima o programa aguarda uma entrada de uma string através do teclado, até que o usuário digite a tecla enter para finalizar a entrada.

Da mesma forma como nos comandos de escrita Write/WriteLn, os comandos Read/ReadLn aceitam uma entrada de um arquivo/dispositivo diferente do padrão estabelecido para o comando, conforme pode ser visto na definição abaixo:

Read( VarFile, Var1, Var2, Var3, ...VarN );

Onde VarFile é a variável associada ao arquivo/dispositivo em que se deseja realizar a leitura. Um arquivo/dispositivo padrão que pode ser utilizado para captura de dados é a porta AUX:, geralmente conhecida COM1: nos PC’s. Entretanto esse comando só retornará uma informação com sucesso, caso exista algum dispositivo conectado à porta auxiliar (COM) do computador.

Abaixo segue um exemplo utilizando o arquivo/dispositivo padrão, Kbd, que é o teclado, utilizado por padrão em Read/ReadLn, caso não seja especificado o arquivo/dispositivo a utilizar.

Program TestReadKbd;
Var
      strData : String[255];

Begin
  Write( 'Please type a word' );
  Read( Kbd, strData );
End;

Arquivos

Já que estamos falando de E/S e dispositivos, não podemos deixar de falar sobre E/S em  disco utilizando arquivos e as operações em arquivos consistem em 2 formas básicas. A primeira são os arquivos tipados e a segunda os arquivos sem tipo definido.

Para trabalhar com arquivos em Turbo Pascal, basta seguir a sequencia abaixo:

  1. Criar uma variável do tipo arquivo (file handle);
  2. Associar um  nome de arquivo a uma variável do tipo arquivo;
  3. Abrir o arquivo para escrita, leitura ou escrita/leitura;
  4. Ler dados do arquivo ou escrever dados para o arquivo, na sequencia desejada;
  5. Fechar o arquivo após o uso;

Primeiro de tudo devemos conhecer um tipo de dados importante na manipulação e gerenciamento de arquivos em Pascal, é o tipo File. Nos casos de arquivos tipados, podemos também definir um arquivo juntamente com o tipo de dados a ser manipulado nesse arquivo, conforme descrito abaixo:

Var
       fpIntegerFile : File Of Integer;
       fpRealFile    : File Of Real;
       fpStringFile  : File Of String[100];
       fpFileRecord  : File Of Record 
                                 strAddress : String[50];
                                 nNumber    : Integer;
                                 fSalary    : Real;
                               End;

Ao definir uma variável do tipo File, você está criando um file handle que será utilizado em todas as operações envolvendo arquivos e a primeira dessas operações é associar esse file handle a um arquivo no filesystem do sistema operacional, para isso utilizamos o comando Assign, cujo formato está descrito logo abaixo:

Assign( FileVar : File, strFileName : String );

Onde FileVar é uma variável do tipo File, podendo ser de qualquer um dos tipos especificados, conforme o exemplo anterior. strFileName é uma variável, ou constante do tipo String, que contém o nome do arquivo a ser aberto/criado para leitura/escrita;

Após associar o nome do arquivo à variável do tipo File, essa variável estará pronta para toda e qualquer operação subsequente a ser realizada com arquivos.

Abrindo arquivo para E/S.

A primeira operação que devemos fazer após associar um file handle a um arquivo no filesystem é abrir o arquivo para que possamos realizar qualquer operação de E/S no mesmo. Para isso Turbo Pascal conta com duas procedures que podem ser utilizadas para abrir um arquivo, que serão descritas a seguir.

Reset

A procedure Reset é utilizada para abrir um arquivo existente no filesystem e sua sintaxe é descrita abaixo:

Reset( FileVar : File );

Onde FileVar é a variável do tipo File, associada anteriormente a um arquivo através da procedure Assign, descrita anteriormente.

Rewrite

A procedure Rewrite é utilizada para abrir um arquivo independente se o mesmo existe no filesystem ou não. Caso o arquivo não exista o mesmo será criado.

Rewrite( FileVar : File );

Onde FileVar é a variável do tipo File, associada anteriormente a um arquivo através da procedure Assign, descrita anteriormente.

 Fechando arquivos

Bom, após todo esse processo de abertura de arquivos existe um processo importantíssimo a ser realizado ao final do uso do mesmo, que é o fechamento do arquivo previamente aberto, uma vez que as funções de E/S de Turbo Pascal, assim como as funções de E/S de ANSI C, são bufferizadas, ou seja, elas utilizam um buffer interno para cada leitura/escrita feita em um dispositivo, visando assim otimizar as operações de E/S, principalmente em dispositivos lentos.

Apesar do buffer interno proporcionar um ganho de performance nas operações de E/S, existe um problema, pois como tanto as operações de leitura quanto as de escrita são feitas em memória antes do buffer ser completado e despejado no dispositivo de armazenamento, no caso o disco rígido ou até mesmo um disquete.

Caso o desenvolvedor não instrua o programa a despejar esse buffer no disco, o Turbo Pascal irá fazê-lo “automagicamente“, quando o buffer for completado ou quando o arquivo for fechado.

Close

Fecha um arquivo especificado, despejando o buffer de E/S no dispositivo anteriormente aberto e invalidando o file handle para uso. Sua sintaxe é descrita abaixo:

Close( FileVar : File );

Onde FileVar é a variável do tipo File, associada anteriormente a um arquivo através da procedure Assign, descrita anteriormente.

Despejando o buffer de E/S.

Conforme descrito na procedure Close, as operações de E/S em arquivos de Turbo Pascal são bufferizadas e esse buffer é gerenciado “automagicamente” por Turbo Pascal.

Entretanto podemos forçar que o mesmo seja liberado no disco a qualquer momento e podemos fazer isso através da procedure Flush, cuja sintaxe está descrita abaixo:

Flush( FileVar : File );

Onde FileVar é a variável do tipo File, associada anteriormente a um arquivo através da procedure Assign, descrita anteriormente.

Abaixo um exemplo que demonstra o uso de todas as funções descritas acima:

(** Sample program to show the power 
  * of Turbo Pascal I/O procedures 
  * by PopolonY2k 2012.
  *)
Program Test_IO;
Var
       fpFile : File Of String[100];

Begin
  Assign( fpFile, 'temp.txt' ); { Assign  the file name to file variable }
  Rewrite( fpFile ); { Create a new file }
  WriteLn( fpFile, 'This is a Test' ); { Write text to file }
  Flush( fpFile ); { Force the buffer dump to disk }
  WriteLn( fpFile, 'Another text to file' );
  Close( fpFile ); { Close the file }
End.

Acesso aleatório ao arquivo.

Devemos conhecer que existe um ponteiro lógico que indica a posição de onde a próxima operação de leitura ou gravação será realizada, sendo que em cada operação de E/S realizada no arquivo, esse ponteiro lógico é movido ‘n’ bytes para frente, principalmente nos exemplos descritos até aqui, onde trabalhamos com arquivos sequenciais.

Sabendo disso, devemos conhecer também que existe uma forma, em Turbo Pascal, para manipulação desse ponteiro lógico, o que nos permite navegar pelo arquivo de maneira aleatória, não necessitando assim varrer um arquivo inteiro em busca de uma informação, e assim podemos trabalhar de maneira mais eficiente e veloz para os casos onde sabemos a posição do registro a ser lido ou escrito.

Para manipular a posição do ponteiro do arquivo, Turbo Pascal possui a procedure descrita abaixo.

Procedure Seek( fpFile : File; nRec : Integer );

A procedure Seek recebe um parâmetro do tipo File, que contém o file handle para o arquivo aberto através de Reset ou Rewrite e também aceita um valor inteiro que descreve  a nova posição do ponteiro no arquivo. Lembrando que a posição inicial do ponteiro no arquivo é sempre zero.

Essa posição não é o número de bytes a mover no arquivo e sim o número de registros do arquivo e sua posição em bytes varia de acordo com o tipo do arquivo. Abaixo um exemplo do uso de Seek.

(** This is a sample to show how to make a random access
  * to file, using Seek.
  * by PopolonY2k 2012.
  *)
Program Test_Seek;
Type TMyData = Record
  strName : String[50];
  nAge    : Integer;
End;

Var
       fpFile : File Of TMyData;
       rec    : TMyData;

Begin
  { The first step is write some data to file }
  Assign( fpFile, 'Data.dat' );  { Create the data file }
  Rewrite( fpFile );

  rec.strName := 'PopolonY2k';
  rec.nAge    := 60;
  Write( fpFile, rec );          { Write the first record }
  rec.strName := 'AphroditeY2k';
  rec.nAge    := 40;
  Write( fpFile, rec );          { Write the second record }
  rec.strName := 'HudnosY2k';
  rec.nAge    := 1000;
  Write( fpFile, rec );          { Write the third record }
  Close( fpFile );               { Close the file }

  { Now we start to read the file written previously }

  Assign( fpFile, 'Data.dat' );  { Open the data file }
  Reset( fpFile );

  { Our file has 3 record and the first record is in position 0 zero }
  { Then we start to read the mid record }
  Seek( fpFile, 1 );
  Read( fpFile, rec );
  WriteLn( 'Name -> ', rec.strName );
  WriteLn( 'Age  -> ', rec.nAge );

  { Now reading the last record }
  Seek( fpFile, 2 );
  Read( fpFile, rec );
  WriteLn( 'Name -> ', rec.strName );
  WriteLn( 'Age  -> ', rec.nAge );

  { Now reading the first record }
  Seek( fpFile, 0 );
  Read( fpFile, rec );
  WriteLn( 'Name -> ', rec.strName );
  WriteLn( 'Age  -> ', rec.nAge );
  Close( fpFile );
End.

Aquivos Texto.

Arquivos texto são arquivos cujo conteúdo é formado basicamente por caracteres imprimíveis, sendo compostos por diversas linhas de caracteres imprimíveis separados por um terminador de linha, que no caso do MSX-DOS é o caractere de controle carriage-return seguido de line-feed, sendo o final de arquivo delimitado por um caractere especial representado pelo código ASCII de número 26 (1A em hexadecimal), também representado pela combinação de teclas CTRL+Z.

Abaixo segue a descrição de alguns comandos importantes para a manipulação de arquivos em modo texto:

Function EOF( fpFile : File) : Boolean.

Essa função é especifica para o uso em arquivos no modo texto e sua finalidade é reconhecer se o final de arquivo foi alcançado ou não. O nome dessa função vem da abreviação de End Of File.

Conforme pode ser visto no protótipo da função acima, EOF recebe um parâmetro que é um file handle para um arquivo texto anteriormente aberto por Reset ou Rewrite. A função testa se arquivo chegou ao seu fim e retorna True caso o teste seja satisfeito e Falso caso contrário.

Function EOLn( fpFile : File ) : Boolean;

Assim como EOF, EOLn aceita um file handle, previamente aberto através de Reset ou Rewrite. A função testa se a ultima operação de leitura feita através da procedure Read, atingiu o final da linha delimitado por Carriage-return/Line-feed. O nome da função vem da abreviação de End Of Line.

Abaixo temos um exemplo demonstrando o uso das funções de manipulação de arquivo texto.

(** Sample code to show the Turbo Pascal text files related
  * procedures and functions;
  * This sample copy a complete input file to an output file specified
  * by the user;
  * by PopolonY2k 2012;
  *) 
Program Test_TextFiles;

Type TString = String[255];

Var
      fpFileIn,
      fpFileOut  : File Of TString;
      strLine,
      strFileIn,
      strFileOut : TString;

Begin
  WriteLn( 'Please type the input file name' );
  ReadLn( strFileIn );
  WriteLn( 'Please type the output file name' );
  ReadLn( strFileOut );

  Assign( fpFileIn, strFileIn );   { Assign the input and output   }
  Assign( fpFileOut, strFileOut ); { file names to the File handle }
  Reset( fpFileIn );               { variables                     }
  Rewrite( fpFileOut );

  {
    Now the we enter in loop reading the input file until reach the
    end of input file and writing the output to the output file
    previously opened for writing.
  }
  While( Not EOF( fpFileIn ) )  Do  { EOF function sample }
  Begin
    { 
      Here another loop to read the content for each line of input
      file, until the end of line.
    }
    While( Not EOLn( fpFileIn ) ) Do
    Begin
      Read( fpFileIn, strLine );    { Read content from input file }
      Write( fpFileOut, strLine );  { Write content to output file }
    End;
    WriteLn( fpFileOut ); { After read a complete line. We must to }
                          { add a CR/LF at the end of line for the }
                          { output file                            }
  End;
  Close( fpFileIn );      { Close the input and }
  Close( fpFileOut );     { the output files    }
End.

Arquivos sem tipo definido.

Já passeamos pelas funções de E/S de Turbo Pascal, para manipulação de arquivos tipados incluindo arquivos texto, entretanto não podemos deixar de citar que Turbo Pascal é uma linguagem poderosa no nível de que você tenha pleno controle da máquinam o que possibilita a criação de programas e bibliotecas de uso geral, incluindo a capacidadede manipulação de arquivos independente do tipo, ou seja, podemos manipular e gerenciar arquivos binários diversos com extrema facilidade devido a esse conjunto de funções  que veremos à seguir.

As procedures padrão para manipulação de arquivos não tipados são canais de E/S de baixo nível que usam acesso direto a arquivos no disco. Esse acesso é feito através de um registro, ou buffer, que tem um tamanho lógico de 128 bytes.

Esse tamanho virtual pode ser comparado aos setores de um arquivo de um sistema operacional, como no MSX-DOS que é de 512 bytes.

Vamos ao que interessa que são as rotinas de baixo nível para acesso direto a arquivos do Turbo Pascal, que descrevo abaixo.

Procedure BlockRead( fpFile : File; 
                     Var Buffer; 
                     nBytes : Integer );
{ Variation of the same procedure }
Procedure BlockRead( fpFile : File; 
                     Var Buffer; 
                     nBytes : Integer; 
                     Var nResult : Integer );

A procedure BlockRead é uma poderosa rotina para leitura de grandes blocos de dados de qualquer arquivo em qualquer formato. Como podemos ver, a rotina aceita um file handle que especifica o arquivo aberto anteriormente através de Reset ou Rewrite, uma variável buffer que irá receber o conteúdo da leitura feita por BlockRead, uma variável que especifica o número de bytes a ler do arquivo e também uma variável referência (vide passagem por referência no segundo post), que irá receber o número de registros lidos na ultima operação.

O buffer pode ter qualquer tamanho e apontar para qualquer área de memória que esteja disponível no computador. O parâmetro inteiro representado por nBytes, deve especificar o tamanho, em bytes, do buffer a ser carregado na operação de leitura e nResult irá retornar o número de registros de 128 bytes realmente lidos, ou seja se o seu buffer for de 256 bytes, nResult terá o valor 2 após a leitura de BlockRead.

Após a ultima operação de BlockRead, a posição do ponteiro do arquivo será  movida nBytes para frente no arquivo, ou até a posição final do arquivo, caso a mesma seja atingida.

Procedure BlockWrite( fpFile : File;
                      Var Buffer; 
                      nRecs : Integer );
{ Variation of the same procedure }
Procedure BlockWrite( fpFile : File;
                      Var Buffer; 
                      nRecs : Integer; 
                      Var nResult : Integer );

A procedure BlockWrite tem basicamente a mesma estrutura de BlockRead porém com função inversa, ou seja, ao invés de ler, a mesma grava dados de um buffer em memória para o disco. Da mesma forma que BlockRead, a rotina aceita um file handle que especifica o arquivo aberto anteriormente através de Reset ou Rewrite, uma variável buffer que contém a informação a ser escrita por BlockWrite, uma variável que especifica o número de registros a ser escrito e também uma variável referência, que irá receber o número de registros gravados na ultima operação de escrita.

buffer pode ter qualquer tamanho, ou apontar para qualquer área de memória, desde que estejam disponíveis para o computador. O parâmetro inteiro representado por nRecs, deve especificar o tamanho, em registros de 128 bytes, do buffer a ser gravado na operação de escrita e nResult irá retornar o número de registros de 128 bytes realmente escritos, ou seja se o seu buffer for de 256 bytesnResult terá o valor 2 após a operação de escrita feita por BlockWrite.

Após a ultima operação de BlockWrite, a posição do ponteiro do arquivo será  movida nBytes para frente no arquivo.

Abaixo segue um exemplo que demonstra o uso das rotinas de baixo nível, não tipadas, de Turbo Pascal.

(** Sample code to show the Turbo Pascal direct access low level
  * related procedures and functions;
  * This sample copy a complete input file to an output file specified
  * by the user;
  * by PopolonY2k 2012;
  *) 
Program Test_DirectIO;

Type TString = String[255];
Const ctMaxBuffer : Integer = 1024; { Uses 1024 bytes copy buffer }

Var
      fpFileIn,
      fpFileOut  : File;
      strLine,
      strFileIn,
      strFileOut : TString;
      nRecs     : Integer;
      aBuffer   : Array[0..ctMaxBuffer] Of Byte;

Begin
  WriteLn( 'Please type the input file name' );
  ReadLn( strFileIn );
  WriteLn( 'Please type the output file name' );
  ReadLn( strFileOut );

  Assign( fpFileIn, strFileIn );   { Assign the input and output   }
  Assign( fpFileOut, strFileOut ); { file names to the File handle }
  Reset( fpFileIn );               { variables                     }
  Rewrite( fpFileOut );

  { 
    Start reading the input file until reach the end of file, when
    the BlockRead return zero for the number of records loaded.
  }
  Repeat
    BlockRead( fpFileIn, aBuffer, SizeOf( aBuffer ), nRecs );
    BlockWrite( fpFileOut, aBuffer, nRecs );
  Until( nRecs = 0 );

  Close( fpFileIn );
  Close( fpFileOut );
End.

Error handling

Bom, até agora fizemos o caminho perfeito onde problemas não acontecem, discos não tem sua capacidade máxima atingida, erros de escrita e leitura não existem, proteção contra gravação é lenda, enfim, tudo é maravilhoso no mundo de Pokémon.

Porém no mundo real, todos os problemas acima e muitos outros mais, podem acontecer e irão acontecer pois Murphy nos acompanha a cada segundo e fará com que cada um dos problemas possíveis aconteça. Por isso devemos trabalhar nossos softwares buscando prever a maioria dos problemas ou pelo menos tratar a maioria das excessões possíveis de acontecer durante o ciclo de execução do software.

O Turbo Pascal possui um mecanismo de controle do comportamento de run-time conhecida como Diretivas de Compilação, que falaremos em outra oportunidade dedicada exclusivamente a Diretivas de Compilação. Entre os exemplos de diretivas de compilação está a que controla a checagem de acesso indevido a um índice de um array, outra que controla a checagem de compatibilidade entre parâmetros String, e entre as diversas diretivas existentes temos uma em especial para controle da checagem de erro de E/S que possibilita ao programador desabilitar a checagem automática de erros de E/S pelo Turbo Pascal, transferindo assim a responsabilidade para o programador sobre o tratamento de erros de E/S.

As diretivas de compilação são ativadas através de comentários (*$ *) ou {$ } sendo que a diretiva inicia com o caractere $ seguida pela representação da diretiva desejada. Algumas diretivas podem ser ativadas ou desativadas através do + (ativa) ou – (desativa) e tem escopo local ou global.

No caso da diretiva de checagem e erro de E/S, a diretiva é a {$I} (I/O checking) que tem seu estado default ativado {$I+}, ou seja, qualquer erro de E/S que aconteça em seu programa, o Turbo Pascal automagicamente irá adicionar um código de checagem de erro para cada operação de E/S e caso ocorra um erro de E/S, o sistema operacional será acionado informando o erro ocorrido, o que muitas vezes chega a ser um inconveniente pois as mensagens apresentadas pelo sistema operacional nem sempre são “esteticamente” conveninentes ao usuário final. Quem não se lembra das mensagens do MSDOS/MSX-DOS, (A)bort, (R)etry, (I)gnore ????

Pois bem, para contornar inconvenientes como esse e dar maior poder ao desenvolvedor para controlar as excessões de E/S, utilizamos a diretiva {$I}, que é uma diretiva local, ou seja, podemos ativar e desativá-la a qualquer momento durante o fluxo de execução do software, conforme pode ser visto no programa abaixo.

(** Sample code to show the Turbo Pascal directive for I/O control
  * by the programmer;
  * This sample copy a complete input file to an output file specified
  * by the user;
  * by PopolonY2k 2012;
  *) 
Program Test_IO_Compiler_Directive;

Type TString = String[255];
Const ctMaxBuffer : Integer = 1024; { Uses 1024 bytes copy buffer }

Var
      fpFileIn,
      fpFileOut  : File;
      strLine,
      strFileIn,
      strFileOut : TString;
      nRecs     : Integer;
      aBuffer   : Array[0..ctMaxBuffer] Of Byte;

Begin
  WriteLn( 'Please type the input file name' );
  ReadLn( strFileIn );
  WriteLn( 'Please type the output file name' );
  ReadLn( strFileOut );

  Assign( fpFileIn, strFileIn );   { Assign the input and output   }
  Assign( fpFileOut, strFileOut ); { file names to the File handle }
  {$I-}
  Reset( fpFileIn );               { variables                     }
  {$I+}

  If( IOResult <> 0 )  Then
  Begin
    WriteLn( 'Error to open the input file' );
    Halt;
  End
  Else
  Begin
    {$I-}
    Rewrite( fpFileOut );
    {$I+}
    If( IOResult <> 0 )  Then
    Begin
      WriteLn( 'Error to open the output file' );
      Halt;
    End;
  End;

  { 
    Start reading the input file until reach the end of file, when
    the BlockRead return zero for the number of records loaded.
  }
  Repeat
    BlockRead( fpFileIn, aBuffer, SizeOf( aBuffer ), nRecs );
    BlockWrite( fpFileOut, aBuffer, nRecs );
  Until( nRecs = 0 );

  Close( fpFileIn );
  Close( fpFileOut );
End.

No código acima podemos ver que {$I-} e {$I+} delimitam o escopo de onde inicia e onde termina a checagem de erro de E/S, sendo que após isso chamamos a função IOResult que nos retorna o ultimo erro ocorrido na ultima operação de E/S, sendo que qualquer valor diferente de zero sinaliza o código do ultimo erro ocorrido na ultima operação de E/S.

Dessa forma podemos fazer um controle minucioso sobre cada operação de E/S, retornando erros mais detalhados ou até permitindo que o software tome decisões sobre a melhor ação de contorno a ser executada para se resolver a excessão.

Finalizando o esse post percebemos que Turbo Pascal possui um rico conjunto de rotinas para manipulação de E/S, permitindo assim que softwares modernos e com alto grau de controle sejam construídos para plataformas que suportam esse poderoso compilador, o que é verdadeiro para a plataformas baseadas em CP/M80, como o caso do MSX.

Agradecimento

Aproveito para deixar aqui meu agradecimento a Nirvardo Cavalcante, membro atuante na comunidade de MSX nacional, pelo farto material sobre Turbo Pascal enviado a mim, incluindo código fonte.

Obrigado e, sim, em breve faremos aquele universal music player em Pascal. 🙂

[]’s
PopolonY2k

Referência na internet

Wireless network (Wikipedia)
http://en.wikipedia.org/wiki/Wireless_network

Ethernet (Wikipedia)
http://en.wikipedia.org/wiki/Ethernet

MSX-DOS (Wikipedia)
http://en.wikipedia.org/wiki/MSX-DOS

RS-232 (Wikipedia)
http://en.wikipedia.org/wiki/Rs232

Device Driver (Wikipedia)
http://en.wikipedia.org/wiki/Device_driver

Torre de Babel (Wikipedia)
http://pt.wikipedia.org/wiki/Torre_de_Babel

Caracteres imprimíveis (Wikipedia)
http://pt.wikipedia.org/wiki/ASCII#Caracteres_imprim.C3.ADveis

Caracteres não imprimíveis, ou caracteres de controle (Wikipedia)
http://pt.wikipedia.org/wiki/ASCII#Caracteres_n.C3.A3o_imprim.C3.ADveis

ANSI C (Wikipedia)
http://en.wikipedia.org/wiki/ANSI_C

Protótipo de função (Wikipedia)
http://pt.wikipedia.org/wiki/Prot%C3%B3tipo_de_fun%C3%A7%C3%A3o

Turbo Pascal Forever – Begin (PopolonY2k Rulezz)
http://www.popolony2k.com.br/?p=1694

Pokemón ( Wikipedia)
http://pt.wikipedia.org/wiki/Pok%C3%A9mon

Lei de Murphy (Wikipedia)
http://pt.wikipedia.org/wiki/Lei_de_Murphy

Turbo Pascal compiler directives (David I Embarcadero Blog)
http://blogs.embarcadero.com/davidi/2008/11/16/39118/

Print Friendly, PDF & Email

Turbo Pascal Forever – Statements

Antes de continuar o artigo sobre programação Pascal para plataformas old skool, primeiramente quero agradecer principalmente aos membros da comunidade nacional de MSX pelo feedback e apoio a essa série que está sendo desenvolvida e também à comunidade internacional de desenvolvedores Pascal, representados pelas novas gerações da linguagem, através do Delphi e Lazarus/FreePascal.

Below some individual thanks to community members by the support to this tutorial.

  • Werner Kai, active member of the brazilian and international MSX scenary, thanks for the Pascal documentation, source code and software compilation, sent to me, specially by the HiSoft Pascal information for MSX.
  • Embarcadero Development RAD Tools and Eliazer Kosciuk (Klax) on Twitter, thanks for the support to this tutorial, retwitting my Twitter messages.

Vamos ao que realmente interessa que é  mão-na-massa, hands on, ….

Statements

Um Statement em sua tradução livre é definido como uma declaração mas prefiro defini-lo tecnicamente da mesma forma como faz o manual original da versão 3 do Turbo Pascal de 1983~86 e que descrevo abaixo.

Tradução do manual original da Borland
Turbo Pascal – The Ultimate Pascal Development Environment – Borland 1983~1986

Um statement define a ação a ser executada pelo programa (ou sub-programa, ou sub-rotina) como uma sequencia de statements; cada uma especificando uma parte da ação.

Nesse sentido Pascal é uma linguagem de programação sequencial, ou seja, cada statement é executado sequencialmente um por vez, nunca simultâneamente. Um statement é encapsulado pelas palavras reservadas begin e end e dentro delas, statements são separadas por ponto e virgula (;).

Statements podem ser simples ou estruturados.

Claro que muita coisa mudou de lá pra cá, onde a tecnologia de hardware e a tecnologia de software evoluíram muito permitindo que tenhamos hoje um certo paralelismo, possibilitando a execução de statements simultaneamente, seja através de threads ou até mesmo paralelismo entre os diversos núcleos do processador, entretanto o conceito de execução sequencial de cada statement, em um módulo do programa, ou sub-programa, ainda é o mesmo até nas versões mais modernas de Pascal (Delphi, FreePascal, …), bem como na maioria das linguagens de alto e médio nível como Java e C.

Outro fator importante sobre os statements é que os mesmos podem ser categorizados como simples  e estruturados.

Statements simples.

Entre os statements simples podemos citar o de atribuição, procedure e o statement vazio.

Segue abaixo os casos de statements simples.

Statement de atribuição.

O statement de atribuição é um simples comando de atribuição a uma variável, conforme pode ser visto no código fonte do exemplo a seguir:

nValue   := 1000;  { Integer assignment statement }
strValue := 'This is a string assignment statement';
dValue   := 10.45; { Real assignment statement }
bValue   := ( 1 = 2 ); { Boolean assignment statement }
bEof     := EOF;{ Boolean assignment statement from function EOF }

Procedure statement

Uma procedure statement tem a função de executar uma sub-rotina definida pelo usuário ou até mesmo uma sub-rotina builtin do Turbo Pascal.

WriteLn( 'This is a builtin procedure call statement' );
ClrScr;  { Another builtin Turbo Pascal procedure statement }
ClrEOL;  { Another builtin Turbo Pascal procedure statement }

Statement vazio

Apesar de soar estranho, é possível ter um statement vazio, ou seja, que não executa nada. Segue abaixo alguns usos de statements vazios.

Begin End.        { Empty block }
While (True) Do;  { Infinite loop }
Repeat Until KeyPressed; { Wait for any key to be pressed }

Statements estruturados

Statements estruturados são construções compostas por outros statements, chamados de statements compostos, que podem ser executados em sequência, condicionalmente ou repetidamente.

Segue abaixo os statements estruturados.

Statements compostos

Um Statement composto é definido como um conjunto de outros statements simples ou compostos que estão sempre agrupados em blocos, no caso de Pascal um bloco é definido através das palavras reservadas begin e end, especificando assim que os statements serão executados na sequência em que eles foram escritos, sendo assim podemos afirmar que o próprio bloco principal de um programa em Pascal é um statement composto.

Abaixo alguns exemplos de statements compostos:

Program Test;
 
 Var bTest   : Boolean;
     strTemp : String[255];
 
{ Main block - In fact the main block is the major compound statement }
Begin
  If( bTest = True ) Then
    Begin { This is a compound statement }
      strTemp := 'This is a compound statement test';
      WriteLn( strTemp );
    End
  Else { This is another compound statement }
    Begin
      strTemp := 'Another compound statement test';
      WriteLn( strTemp );
    End;
End. { End of main block and the main compound statement }

Statements condicionais

É reconhecido que os computadores são máquinas extremamente lógicas e nada mais lógico que as linguagens de programação tenham ricos conjuntos de operações e possibilidades lógicas.

Turbo Pascal possui um conjunto moderno de statements dedicados a lógica condicional, possibilitando assim uma estruturação clara de grandes sistemas desenvolvidos na linguagem.

IF Statement

Assim como a maioria das linguagens de médio e alto nível, Pascal possui um statement dedicado a lógica condicional com duas opções de execução dependendo do resultado booleano de seu comparador.

A estrutura do statement IF é a seguinte:

IF boolean_expression THEN  { Statement executed for true condition }
ELSE  { Statement to be executed for false condition - optional };

Onde boolean_expression é uma expressão lógica, ou booleana, a ser executada e caso a mesma tenha um resultado verdadeiro (True) o statement após o THEN será executado, caso contrário será executado o statement após o ELSE.  Lembrando que o ELSE é opcional, ou seja, se o seu teste condicional não necessita executar nada para casos cuja expressão booleana testada não seja verdadeira (False), a condição do ELSE poderá ser suprimida do statement IF.

Vejamos alguns exemplos práticos de utilização do statement IF.

Program Test_IF;
Var
      bValue1,
      bValue2   : Boolean;
      nValue    : Integer;
 
Begin
  bValue1 := True;
  bValue2 := False;
  nValue  := 100;
 
  { If statement checking boolean values }
  If( bValue1 = bValue2 )  Then
    WriteLn( 'The boolean values are equals' )
  Else
    WriteLn( 'The boolean values are different' );
 
  { If statement checking integer values }
  If( nValue = 1000 )  Then
    WriteLn( 'The integer value is equal the constant value' )
  Else
    WriteLn( 'The integer value is different of constant value' );
 
  { If statement with only one execution condition }
  If( nValue = 100 ) Then
    WriteLn( 'The integer value is equal the constant value' );
End.

Uma observação muito importante é quanto a utilização do ; (ponto-e-virgula) no final do statement IF. Nunca devemos nos esquecer de que o (ponto-e-virgula) é um finalizador de statements, ou seja, deve estar presente no final de cada statement, seja simples ou composto.

No caso do IF, ele poderá estar apenas no fim do statement completo, ou seja, caso tenhamos um IF com as condição do ELSE além do THEN implementada, o ; (ponto-e-virgula) só poderá estar ao final do ELSE, uma vez que ele finaliza o statement IF nesse caso. Mas caso tenhamos apenas a condição do THEN implementada o ; (ponto-e-virgula) deverá estar após o statement seguinte ao THEN.

Abaixo alguns exemplos do uso do (ponto-e-virgula) finalizando o statement IF.

Program TEST_IF_2;
Begin
  { If statement with optional ELSE }
  If( 1 = 2 )  Then
    WriteLn( 'Equals' );
 
  If( 1 = 1 )  Then
    WriteLn( 'Equals' )
  Else
    WriteLn( 'Not equals' );
End.

Outra observação é quanto ao uso combinado de um statement IF com statements compostos, como pode ser visto no exemplo abaixo.

Program TEST_IF_With_Compound_Statements;
Begin
  If( True )  Then
    Begin  { Start of the first compound statement }
      WriteLn( 'This is the first' );
      WriteLn( 'compound statement' );
    End
  Else
    Begin  { Start of second compound statement }
      WriteLn( 'This is the second' );
      WriteLn( 'compound statement' );
    End;
End.

CASE Statement

Outra estrutura condicional presente em Pascal, bem como na maioria das linguagens modernas, é o statement CASE que possibilita a comparação de uma expressão com diversas constantes de comparação, podendo ter assim especificadas diversas condições de execução de acordo com o valor da expressão de entrada.

A estrutura do statement CASE é a seguinte:

CASE expression OF
  constant1 : statement;
  constant2 : statament;
  .
  .
  .
  constantN : statament;
  ELSE statement { Optional };
END;

Onde expression é a expressão a ser comparada com cada constante especificada por  constant1, constant2, …., constantN sendo que caso uma dessas constantes satisfaça a expressão, o statement  associado à constante, após o : (dois pontos) será executado. Caso nenhuma das opções seja satisfeita, o statement após o ELSE será executado. Lembrando que assim como o ELSE do statement IF, o ELSE do statement CASE é opcional.

Abaixo temos um exemplo da utilização do CASE.

Program TEST_Case;
Var
      nValue : Integer;
 
Begin
  WriteLn( 'Digit a number between 0-3' );
  Read( nValue );
 
  Case ( nValue ) Of
    0 : WriteLn( 'The number is 0' );
    1 : WriteLn( 'The number is 1' );
    2 : WriteLn( 'The number is 2' );
    3 : WriteLn( 'The number is 3' );
    Else
      WriteLn( 'Number is not between 0-3' );
  End;
End.

Da mesma forma como no statement IF, podemos combinar o statement CASE com statements compostos, conforme pode ser visto no exemplo abaixo.

Program TEST_Case2;
Var
      nValue : Integer;
 
Begin
  WriteLn( 'Digit a number between 0-1' );
  Read( nValue );
 
  Case ( nValue ) Of
    0 : Begin   { The first compound statement in CASE statement }
          WriteLn( 'The number is 0' );
          ReadLn;
        End;
    1 : Begin   { The second compound statement in CASE statement }
          WriteLn( 'The number is 1' );
          ReadLn;
        End;
  End;
End.

Statements de repetição.

Uma das estruturas interessantes e poderosas de Pascal são os  statements de repetição que de fato são estruturas responsáveis pela execução cíclica de um ou mais statements até que uma condição booleana seja satisfeita, ou um contador tenha seu limite máximo atingido.

Em Pascal temos 3 statements de repetição distintos:

  • Statement FOR;
  • Statement WHILE;
  • Statement REPEAT UNTIL;

Statement FOR.

statement FOR é uma estrutura de repetição capaz de executar statements simples ou compostos por um determinado número de vezes tanto em ordem crescente quanto em ordem decrescente. É muito útil quando se conhece antecipadamente o limite máximo de repetições que se deseja executar.

A estrutura do statement FOR é a seguinte:

FOR count_variable := initial_value TO maximum_value DO execution_statement;

Onde count_variable é uma variável inteira a ser utilizada como contador e seu valor inicial será representado por initial_value, sendo incrementada em uma unidade, onde o statament representado por execution_statement será executado a cada iteração até que o limite de contagem, representado por maximum_value, seja atingido.

A segunda variação do statement FOR é a seguinte:

FOR count_variable := initial_value DOWNTO minimum_value DO execution_statement;

Sendo que a diferença entre essa última e a anterior é que ao invés de um contador crescente, temos um contador decrescente, representado pela palavra DOWNTO.

Segue abaixo alguns exemplos de repetição utilizando o statement FOR.

Program Test_Simple_FOR;
  Const   ctMaxCount : Integer = 2000; { Maximum for loop counter }
  Var     nCount     : Integer;
 
  Begin
    For nCount := 0 To ctMaxCount Do  { This is a simple FOR statement }
      WriteLn( 'Counting [', nCount, ']' );
  End.

Outro exemplo do statement FOR, executando um statement composto.

Program Test_Compound_FOR;
  Const   ctMaxCount : Integer = 2000; { Maximum for loop counter }
  Var     nCount     : Integer;
 
  Begin
    For nCount := 0 To ctMaxCount Do  { This is a compound FOR statement }
      Begin
        WriteLn( 'Counting [' );
        WriteLn( nCount );
        WriteLn( ']' );
      End;
  End.

Abaixo temos um exemplo do statement FOR com um contador decrescente.

Program Test_Decreasing_FOR;
  Const   ctMinCount : Integer = 10; { Minimum limit for the FOR loop counter }
  Var     nCount     : Integer;
 
  Begin
    { This is a decreasing FOR statament }
    For nCount := 1000 DownTo ctMinCount Do
      WriteLn( 'Counting [', nCount, ']' );
  End.

Statement WHILE.

Assim como o statement FOR, o statement WHILE é capaz de executar repetições de  statements simples ou compostos. Entretanto a diferença primordial entre o statement FOR e o statement WHILE é que esse último é capaz de executar a repetição até que uma condição booleana seja satisfeita, podendo ser essa simples ou mais complexa.

A estrutura do statement WHILE é a seguinte:

WHILE boolean_condition DO execution_statement;

Onde boolean_condition é a condição booleana a ser satisfeita e execution_statement o statement a ser executado até que a condição boolean_condition seja satisfeita.

Vamos a alguns exemplos do uso do statement de repetição, WHILE.

Program Test_Simple_WHILE;
Const
         ctMaxCount : Integer = 1000;
Var
         nCount     : Integer;
 
Begin
  nCount := 0;
 
  { Increase the counter variable, nCount, while nCount is ‹ 1000 }
  While( nCount ‹ ctMaxCount ) Do
    nCount := nCount + 1;
End.

Abaixo temos outro exemplo utilizando o statement WHILE, executando um statement composto onde a condição de parada é uma combinação booleana complexa.

Program Test_Complex_WHILE;
Const
         ctMaxCount : Integer = 1000;
Var
         nCount     : Integer;
         bExit      : Boolean;
 
Begin
  nCount := 0;
  bExit  := False;
 
  { This is a While with a more complex boolean checking exit }
  While( ( bExit = False ) And ( nCount ‹ ctMaxCount ) ) Do
    Begin
      If( nCount = 100 )  Then
        bExit := True;
 
      nCount := nCount + 1;
    End;
End.

Statement REPEAT UNTIL;

Por ultimo vamos descrever o statement de repetição, REPEAT UNTIL, que assim como os statements FOR e WHILE, é capaz de executar statements simples e compostos e assim como o statement WHILE, é capaz de executar a repetição até que uma condição booleana seja satisfeita.

A grande diferença do statement REPEAT UNTIL para o WHILE é que o REPEAT UNTIL tem a checagem da condição de parada do loop executada ao fim de uma primeira iteração no loop de execução do statement, ou seja, teremos pelo menos uma execução do statement ou conjunto de statements do REPEAT UNTIL.

A estrutura do statement REPEAT UNTIL é a seguinte:

REPEAT execution_statement UNTIL boolean_condition;

Onde execution_statement é o statement ou conjunto de statements a ser executados até que a condição boolean_condition seja satisfeita.

Segue abaixo um exemplo do statement REPEAT UNTIL em ação.

Program Test_REPEAT_Until;
Const    ctMaxCount = 1000;
Var      nCount : Integer;
         bExit  : Boolean;
Begin
  nCount := 0;
  bExit  := False;
 
  Repeat
    If( nCount = 100 )  Then
      bExit := True;
 
    nCount := nCount + 1;
  Until( ( bExit = True ) And ( nCount = ctMaxCount ) );
End.

Como podemos ver, a linguagem Pascal é rica de estruturas que facilitam e possibilitam o desenvolvimento de software de forma organizada e altamente estruturada, capacitando assim a construção de grandes sistemas escaláveis e de mais fácil manutenção.

Certamente daqui a 15 anos outro desenvolvedor poderá ler um código feito por você em Pascal e compreendê-lo com extrema facilidade devido a estrutura organizada, nativa de Pascal.

Por enquanto vou ficando por aqui deixando mais esse material para que possam se divertir até o post seguinte. 🙂

[]’s
PopolonY2k 

Referência na internet

Embarcadero Delphi
http://www.embarcadero.com/br/products/delphi

Lazarus
http://www.lazarus.freepascal.org/

FreePascal
http://www.freepascal.org/

Embarcadero RAD Tools Dev team (Twitter)
http://twitter.com/#!/RADtools

Eliazer Kosciuk – Klax (Twitter)
http://twitter.com/#!/eliazerk

Twitter
http://twitter.com/#!/RADtools

Print Friendly, PDF & Email

Turbo Pascal Forever – Begin

Quem acompanhou o ultimo post da série Turbo Pascal Forever, já teve um primeiro contato com os comandos do editor do Turbo, bem como já aprendeu como é a estrutura de um programa escrito em Pascal.

A partir desse post vamos começar a entrar em detalhes sobre as estruturas da linguagem Pascal e aos poucos vou introduzindo conceitos avançados sobre essa poderosa linguagem que torna possível o desenvolvimento de softwares básicos como sistemas operacionais, drivers de acesso a dispositivos e também jogos, que é o forte de máquinas como o MSX, por exemplo.

A minha meta é liberar durante o ano o código que tenho escrito para o MSX, sob a licença GPL V3, no meu repositório de Software Open Source para máquinas old skool, o Old Skool Tech, dentre esses códigos estão drivers de acesso a IDE do MSX (Sunrise-like), implementação de FAT16/32, Frameworks de testes unitários, bibliotecas de acesso a funções do MSX-DOS, MSX BIOS e também uma parte do software que eu estava desenvolvendo em 1995, o MSX Disk Doctor (MSXDD), já redesenhado para utilizar as novas tecnologias existentes, tudo isso escrito em Pascal, utilizando técnicas avançadas da linguagem, dentre elas a integração com hardware utilizando opcodes do Z80.

Mas antes disso o curso deverá estar em um estágio mais avançado do que estamos agora…..então vamos, sem muita “enrolação“, avançar. :).

Símbolos

O vocabulário básico aceito pelo Turbo Pascal consiste de símbolos básicos divididos entre letras, números e símbolos especiais.

  • Letras : A..Z, a..z e _ (underscore);
  • Digitos: 0..9;
  • Símbolos especiais: * – * / = ^ < > ( ) [ ] { } . , : ; ‘ # $

Lembrando que o Turbo Pascal não existe distinção entre maiúsculas e minúsculas, sendo reconhecida como uma linguagem case-insensitive, quanto ao uso de seus identificadores.

Operadores

  • Atribuição (:=) Operador básico de atribuição de valores a variáveis;
  • Inicialização de constantes (=) Operador de inicialização de constantes;
  • Lógicos (< <= > >= = <> and or xor not) Operadores utilizados para testes lógicos e condicionais. Lembrando que o operador =, além de inicialização de constantes é também utilizado como operador lógico de comparação de igualdade, sendo o seu inverso, o diferente, <>.
  • Aritméticos (* + – / div mod and or xor not) Operadores utilizados em operações matemáticas que vão desde somas até operações de divisão e multiplicação;
  • Unário (-) Operador unário utilizado em qualquer tipo de dado numérico, inteiro ou de ponto flutuante, para negação do operando;
  • Bit a bit (shl shr and or not xor) Operadores utilizados na manipulação de bits, envolvendo operações booleanas e deslocamento de bits;
  • Delimitador de faixa (..) O delimitador de faixa é utilizado basicamente para definir os limites de arrays e conjuntos;

Palavras reservadas

A linguagem C tem a fama de, com aproximadamente apenas 40 palavras reservadas, proporcionar ao programador escrever desde a mais simples biblioteca para exibir textos na tela, até o mais complexo sistema operacional. A linguagem Pascal, através do Turbo Pascal também possibilita ao programador, com as suas 48 palavras reservadas, fazer exatamente o mesmo que seria feito em C, por exemplo, o que a torna uma das mais poderosas linguagens de alto nível existentes, senão a mais poderosa, lembrando que C é considerada de médio nível.

Abaixo segue a lista de palavras reservadas de Turbo Pascal, até a versão 3.

*absolute, and, array, begin, case, const, div, do, downto, else, end, *external, file, forward, for, function, goto, *inline, if, in, label, mod, nil, not, *overlay, of, or, packed, procedure, program, record, repeat, set, *shl, *shr, *string, then, type, to, until, var, while, with, *xor.

(*) Não definidos no Pascal padrão, sendo extensões do Turbo Pascal e algumas, senão todas,  aceitas em outros compiladores Pascal mais modernos como o FreePascal e até o Delphi.

Delimitadores

Os elementos da linguagem Pascal podem ser separados por pelo menos um dos seguintes delimitadores.

  1. Espaço em branco;
  2. Final de linha;
  3. Comentário (* *) ou { };

TIP
Uma observação importante sobre as linhas de um programa no Turbo Pascal é quanto ao número máximo de caracteres aceito pelo editor do Turbo Pascal. O tamanho máximo aceito pelo editor de Turbo Pascal é 127 caracteres, sendo os caracteres subsequentes ignorados pelo compilador, entretanto se o programador escrever o seu código em outros editores que suportem linhas com mais de 127 caracteres, o mesmo será perfeitamente compilado no Turbo Pascal, sendo os caracteres excedentes aos 127 possíveis, automaticamente quebrados para a próxima linha no editor do Turbo Pascal.

Tipos escalares padrão

O Turbo Pascal 3 possui um conjunto bem poderoso de tipos built-in para uma linguagem do inicio da década de 80, onde o poder computacional e de memória das máquinas era bem limitado, onde esses recursos eram gerenciados praticamente a conta-gotas.

Abaixo descrevo os principais tipos escalares do Turbo Pascal 3:

  • Byte – Tipo de dado inteiro, ocupando 1 byte de memória e aceitando a faixa de valores entre 0 até 255.
  • Integer – Tipo de dado inteiro, ocupando 2 bytes na memória e aceitando a faixa de valores entre -32768 até 32767.
  • Real – Tipo de dado numérico capaz de aceitar valores em ponto flutuante, ocupando 6 bytes na memória e aceita valores na faixa entre 1E-38 até 1E+38 com a mantissa acima de 11 digitos significantes.
  • Boolean – Tipo de dado lógico que aceita valores lógicos para especificar estado de ligado/desligado ou verdadeiro/falso de uma determinada condição lógica. Os valores possíveis para o tipo booleano são especificados pelos identificadores padrão, true (verdadeiro) ou false (falso). O tipo Boolean ocupa 1 byte na memória.
  • Char – Tipo que representa um caractere da tabela ASCII da máquina. Os caracteres ASCII são ordenados de acordo com uma tabela especificada pela American Standard Code for Information Interchange, presente na BIOS/ROM da grande maioria dos computadores até os dias atuais. Os valores possíveis são quaisquer um da tabela ASCII (ex: ‘A’, ‘B’, …), sempre especificados entre ‘ ‘. O tipo Char ocupa 1 byte na memória.

Strings

Turbo Pascal implementou o conceito de strings, diferente do Pascal padrão que não possuía essa característica, sendo similar a linguagens como C padrão que tratam arrays de caracteres como strings.

A definição de string pode ser descrita como um conjunto de caracteres individuais concatenados entre ‘ ‘ (apóstrofos), conforme pode ser visto no exemplo abaixo.

‘Esse é um exemplo de String’

Algumas observações sobre strings em Turbo Pascal 3 são feitas a seguir:

  • Strings devem ter seu tamanho definido sempre no Turbo Pascal 3, ao contrário das Strings definidas a partir do Turbo Pascal 4, onde as mesmas já tem o tamanho de 255 caracteres caso não seja especificado seu tamanho;
  • Não é possível definir o tipo string explicitamente nos parâmetros de uma sub-rotina em Turbo Pascal 3, sendo necessário definir um tipo baseado em string com um tamanho especificado e especificar esse novo tipo no parâmetro da sub-rotina em que se deseja passar uma string por parâmetro;
  • Strings são compatíveis com arrays de char do mesmo tamanho;
  • Um caractere de uma string pode ser acessado através de seu índice na string. Lembrando que o indice 0 (zero) de qualquer string contém seu tamanho;
  • Constantes de strings são compatíveis com todos os tipos de strings (qualquer tamanho);
  • Strings também podem conter caracteres de controle concatenados (ex: #13#10 (Carriage Return\Line Feed), #$1B (Escape -Hexa), ^G (Control+G – Bell);

Let’s code.

Passados os detalhes, importantes porém na maioria das vezes chatos, vamos à parte que realmente interessa que é a mão-na-massa.

Podemos definir o programa em Pascal como sendo uma estrutura em camadas e que podem ser melhor compreendidas visualmente conforme a figura abaixo.

Estrutura de um programa em Pascal

Conforme já foi dito no post anterior, o cabeçalho ou Program Head é puramente opcional e geralmente é utilizado para identificar o programa desenvolvido;

Na sequência temos a área de declarações, que é onde definimos as variáveis globais do programa principal, novos tipos de dados, constantes e labels. Vamos a explicação de cada um desses itens.

Definição de labels.

Ao contrário de linguagens desestruturadas como MSX-BASIC, ou pouco estruturadas como Assembly, o Pascal possui um rico conjunto de estruturas de controle que praticamente eliminam a possibilidade de artifícios que tornam os programas pouco compreensíveis e ilegíveis, sendo possíveis de manutenção praticamente apenas pelo programador que fez o programa, como é o caso do famoso e combatido comando de salto incondicional, GOTO dos BASIC clássicos (incluindo o MSX-BASIC).

Apesar de conter toda estrutura de controle que torna o goto inválido e inútil, o Pascal inclui a possibilidade de saltos incondicionais através do comando goto presente no núcleo da linguagem.

Entretato apesar de incluir esse comando que de certa forma torna possível que os programas fiquem desestruturados, o Pascal o faz de uma maneira até que estruturada, através de labels que na realidade são marcadores  que identificam o ponto de salto incondicional. Abaixo um exemplo do uso de labels com o comando goto.

Program Test_Label;
 
Label never_use_goto;
 
Begin { Main Block }
  goto never_use_goto;
  WriteLn( 'This never will be executed' );
 
  never_use_goto:
  WriteLn( 'Tadaaaa !! It works like MSX-BASIC......very trash X.D' );
End.

Definição de Constantes

Em Pascal temos a possibilidade de definir constantes que, resumidamente, é a forma de associar valores a identificadores que não poderão ser mudados nem redefinidos no mesmo escopo do programa, entretanto dentro do escopo de outras sub-rotinas podemos definir constantes com o mesmo nome de identificadores existentes em outros escopos do programa.

Outra curiosidade sobre constantes em Pascal é que as mesmas podem ser definidas de maneira implicitamente e explicitamente tipadas. No caso de constantes implicitamente tipadas, a constante assume o tipo do r-value que está sendo associado ao identificador (l-value) (ex: ‘isso é uma string’ é considerado como uma constante do tipo String, o valor 1000 é considerado como uma constante do tipo Integer e 3.14 do tipo Real).

Segue abaixo um exemplo do uso de constantes.

Program Test_Const;
 
Const
  ctTestStr = 'This is a string constant'; { Implicit typed constant }
  ctTestStr2 : String[50] = 'This is another explicit typed string constant';
  ctInteger = 1000; { Implicit typed constant }
  ctInteger2 : Integer = 2000; { Explicit typed integer constant }
  ctPiReal = 3.14159265; { Implicit typed constant }
  ctRealPi : Real = 3.14159265; { Explicit typed real constant }
 
Begin   { Main Block }
  Writeln( ctTestStr );
  WriteLn( ctTestStr2 );
  WriteLn( ctInteger );
  WriteLn( ctInteger2 );
  WriteLn( ctPiReal );
  WriteLn( ctRealPi );
End.

Definição de tipos

Conforme já foi citado anteriormente, a linguagem Pascal é fortemente tipada, ou seja, considerando as devidas compatibilidades e capacidades dos tipos, a linguagem é extremamente rígida quanto a passagem de parâmetros e entre tipos de variáveis diferentes, por exemplo, caso seja feita a tentativa de atribuir uma variável do tipo Real para alguma do tipo inteiro (Integer ou Byte), ocorrerá um erro de compilação. Apesar disso ainda é possível fazer essa atribuição utilizando uma das funções de conversão entre tipos, existente na linguagem mas isso tem um custo de processamento e esses foi um dos motivos pelos quais a linguagem C se tornou mainstream na preferência dos programadores de sistemas de alta performance, pois a checagem de tipos de C é baixa e na maioria dos casos inexistente, o que pode trazer problemas para detectar bugs críticos mas por outro lado dá poder absoluto ao programador.

Entretanto a forte tipagem de Pascal a torna bastante segura uma vez que, para operações que não envolvem ponteiros e gerenciamento de memória, o programa compilado tem grandes chances de não ter bugs críticos uma vez que o compilador reclama para cada operação desconhecida e/ou suspeita.

Junto com a característica da forte tipagem de Pascal, a linguagem permite a definição de novos tipos baseados nos tipos built-in e também em outros tipos definidos pelo programador. A definição de novos tipos é muito importante e bem vinda em linguagens estruturadas e está presente na grande maioria das linguagens estruturadas modernas (não orientadas a objeto)…..bom, pelo menos nas duas melhores que são Pascal e C :). Um exemplo existente no compilador Turbo Pascal 3, é quanto ao uso do tipo String, pois até essa versão do compilador, principalmente pela baixa capacidade de memória das máquinas da época, uma variável do tipo String só poderia ser definida especificando um tamanho para a mesma.

Segue abaixo um exemplo de definição de uma variável do tipo String e também do uso de definição de tipos para suprir essa deficiencia.

Program Test_Type;
  Type TString = String[255]; { New string type defined with 255 chars size }
 
  Var st1 : String[13];       { String must be defined with size in TP3 }
      st2 : TString;          { String variable with no explicit defined size }
 
Begin  { Main Block }
  st1 := 'Test string 1';
  st2 := 'Test string 2';
End.

O exemplo acima serve para ilustrar o poder que Pascal tem para estender os tipos já existentes na linguagem, como no caso acima em que definimos um novo tipo TString com tamanho de 255 bytes. Mas como em qualquer área, é sempre bom utilizar as facilidades com muita parcimônia :). No exemplo acima foi atribuido uma String constante de 13 bytes (‘Test String 1’)  à variável st1, restando 242 bytes de memória, sem uso, ficando particamente perdidos.  Nesses casos vale o olhar clínico do programador, que com o tempo vai se tornando mais aguçado para remover esses desperdícios.

Lembrando também que o tipo String, a partir do Turbo Pascal 4, quando definido sem um tamanho específico assume a capacidade de 255 bytes, o que para o PC já não era muito em épocas passadas, mas nas plataformas de 8 bits com baixa capacidade de memória era algo considerável, talvez por isso tenha sido evitada a definição de tamanho padrão para o tipo String nos compiladores do Turbo Pascal para plataformas de 8 bits.

Definição de variáveis

Podemos definir variáveis exatamente da forma como escrevemos, ou seja, são áreas de memória onde seu conteúdo pode ser modificado mediante a uma instrução explicita do programa para que a mesma seja alterada. Variáveis, não importa o tipo, são representadas por bytes na memória da máquina sendo que para alguns tipos de dados a quantidade necessária de bytes para armazenar uma informação é maior e para outros menor.

Lembrando ainda sobre o conceito de tipagem de Pascal, ao contrário de linguagens onde uma variável criada pode assumir qualquer tipo de dados, variáveis em Pascal devem ter um tipo definido e além disso, devem ser definidas antes de seu uso em uma área dedicada do escopo de utilização. O fato de ser forçada a definição da variável antes de seu uso evita uma série de problemas futuros, a maioria deles relacionados a escopo de utilização das mesmas. Quem já tentou escrever grandes softwares utilizando linguagens com tipagem fraca, como o MSX-BASIC ou PHP, sabe do que estou falando pois a chance de conflito entre variáveis com mesmo nome, mas definidos em escopos diferentes é muito grande e termina, na maioria das vezes, levando a um aumento considerável de tempo nos casos de manutenção do código.

Em Pascal,  forma de se definir uma variável é através da palavra reservada Var, seguida do nome do identificador da variável : Tipo desejado;

Abaixo podemos ver exemplos de definições de variáveis e suas variações.

Program Test_Var_1;
 Var
      nInt, nInt2 : Integer;
      nByte       : Byte;
      fReal       : Real;
      bBoolean    : Boolean;
      strString   : String[20];
 
Begin  { Main Block }
  nInt  := 10;
  nInt2 := 20;
  nByte := 255;
  fReal := 1255.543;
  bBoolean  := True;
  strString := 'This is a string';
 
  WriteLn( nInt );
  WriteLn( nInt2 );
  WriteLn( nByte );
  WriteLn( fReal );
  WriteLn( bBoolean );
  WriteLn( strString );
End.

O código acima é um exemplo clássico de definição de variáveis simples, onde na área de variáveis do bloco de programa principal são declarados os identificadores das variáveis e seus tipos correspondentes, e no programa principal atribuimos valores a essas variáveis através do operador :=, conforme já foi explicado um pouco mais acima nesse mesmo post. Variáveis do mesmo tipo, podem ser separadas por virgula (,) no momento de sua definição.

Variáveis do tipo Array

Além da riqueza de tipos builtin e da possibilidade de definição de novos tipos, o Pascal tem também a possibilidade de construção e manipulação de arrays, uni e multi-dimensionais, baseado nos tipos nativos e também nos tipos definidos pelo usuário. Segue abaixo exemplos do uso de arrays em Pascal.

Program Test_Var_Array;
 Type TString = String[20];
 
 Var
      aInt    : Array[0..2] Of Integer;            { Unidimensional array }
      aString : Array[1..2] Of TString;            { Unidimensional array }
      aByte   : Array[0..1, 0..1] Of Byte;         { Multidimensional array }
      aReal   : Array[0..2] Of Array[0..2] Of Real;{ Multidimensional array }
 
Begin  { Main Block }
  { Multiples ways to access a array item }
  aInt[0] := 10;
  aInt[1] := 1000;
  aString[1] := 'Test string 1';
  aString[2] := 'Test string 2';
  aByte[0,0] := 11;
  aByte[0,1] := 12;
  aByte[1,0] := 13;
  aByte[1,1] := 14;
  aReal[0][0] := 1.1;
  aReal[0][1] := 2.1;
  aReal[1][0] := 1.1;
  aReal[1][1] := 2.1;
 
  WriteLn( aInt[0], aInt[1] );
  WriteLn( aString[1], aString[2] );
  WriteLn( aByte[0,0], aByte[0,0], aByte[0,1], aByte[1,0], aByte[1,1] );
  WriteLn( aReal[0,0], aReal[0,0], aReal[0,1], aReal[1,0], aReal[1,1] );
End.

O programa acima demonstra as diversas formas de se definir um variável do tipo array e também as formas de acessar seu conteúdo, sendo que vale a pena analisar as duas formas de definição de variáveis array multi-dimensionais.

      aByte   : Array[0..1, 0..1] Of Byte;         { Multidimensional array }
      aReal   : Array[0..2] Of Array[0..2] Of Real;{ Multidimensional array }
 A primeira mais comum em outras linguagens estruturadas e a segunda mais específica de Pascal. No código de acesso ao conteúdo dos itens de um array também é possível fazê-lo de duas formas, conforme pode ser visto no programa acima e que deixo destacado abaixo.
  aByte[1,1] := 14;
  aReal[0][0] := 1.1;

Quanto a definição de variáveis e manipulação de arrays podemos escrever um post inteiro maior do que esse inclusive mas acredito que já cobrimos bastante sobre o conceito de variáveis e seus modificadores. É claro que vou ampliar essa discussão no decorrer do tutorial, nos próximos posts.

Variáveis com tipos complexos ou compostos.

Conforme descrito no primeiro post da série, Pascal foi criado para ajudar no ensino de boas práticas estruturadas de programação e por isso reconhecemos um conjunto rico de estruturas presente na linguagem. Posso até afirmar que a linguagem Pascal na sua forma padrão é tão poderosa e eficiente que, desde o seu lançamento, teve poucas mudanças substanciais exceto no conceito de orientação a objetos que foi introduzido a partir do Turbo Pascal 5.5, mas se considerarmos o núcleo da linguagem, podemos afirmar que Niklaus Wirth foi tão genial quanto  Dennis Ritchie em sua criação.

Um exemplo da força das estruturas de dados em Pascal pode ser vista quando citamos a possibilidade de definição de tipos complexos ou tipos compostos, através de uma estrutura presente na linguagem, conhecida por Record ou registro, similar a struct, presente na linguagem C. Através de Record podemos agrupar variáveis de diversos tipos builtin e até outros tipos definidos pelo usuário, conforme pode ser visto no exemplo abaixo.

Program Test_Var_Complex_Types;
 Type TString = String[20];
 Type TRecordSample = Record
        f1 : Byte;      { Record field with builtin type }
        f2 : Real;
        f3 : TString;   { Record field with user defined type }
      End;
 
 Var
      Rec1      : Record
                    f1 : Byte;      { Record field with builtin type }
                    f2 : Real;
                    f3 : TString;   { Record field with user defined type }
                  End;
      Rec2      : TRecordSample;
 
Begin  { Main Block }
  Rec1.f1 := 1;
  Rec1.f2 := 1.1;
  Rec1.f3 := 'Test string record 1';
 
  Rec2.f1 := 1;
  Rec2.f2 := 2.1;
  Rec2.f3 := 'Test string record 2';
 
  WriteLn( Rec1.f1, Rec1.f2, Rec1.f3 );
  WriteLn( Rec2.f1, Rec2.f2, Rec2.f3 );
End.

No programa acima podem ser vistas as duas formas de definição de registros sendo a primeira, e mais indicada, a definição de um novo tipo que representa a estrutura a ser manipulada e o segundo modo definindo uma variável registro diretamente na declaração de tipo da própria variável, conforme pode ser visto no trecho de código retirado do programa acima, que reproduzo abaixo.

 Var
      Rec1      : Record
                    f1 : Byte;      { Record field with builtin type }
                    f2 : Real;
                    f3 : TString;   { Record field with user defined type }
                  End;

Essa abordagem pode ser uma opção mais complicada se considerarmos a manutenção de código no futuro, uma vez que pode ser necessária a alteração da estrutura e caso a mesma tenha sido replicada no decorrer do programa, com certeza o trabalho de manutenção será ampliado, sem contar que é uma prática pouco elegante.

Definição de procedures e funções

Nos primórdios da computação e engenharia de software tudo era mais arcaico, os conceitos de encapsulamento, hoje amplamente difundidos nas mais variadas tecnologias existentes, praticamente não existiam, tudo era global, tudo era visível e ao mesmo tempo invisível, podemos dizer que era o caos na terra :).

As linguagens estruturadas vieram para colocar ordem na casa e redefinir os conceitos de engenharia de software para os que conhecemos hoje. Pascal assume essa posição com maestria, principalmente quando percebemos que seu conceito de encapsulamento permitiu uma fácil transição para o paradigma da programação orientada a objetos.

Entre os conceitos de encapsulamento vamos citar primeiramente as procedures ou procedimentos. Procedures, são nada mais nada menos do que sub-rotinas cujo o conceito, no mundo de engenharia de software, é o mesmo que ter um sub-programa ou uma rotina menor dentro de um programa maior e que na maioria das vezes pode funcionar de maneira independente do programa maior, dessa forma, ao criar um programa e dividi-lo em sub-rotinas menores, diminuimos a complexidade do mesmo, encapsulando a resolução de problemas complexos em pequenas peças de software que inclusive poderão ser reaproveitadas no futuro.

Procedures tem a mesma estrutura do programa principal em Pascal, logo podemos ter áreas de declaração de variáveis, labels, constantes, tipos, e inclusive novas procedures e funções locais à procedure que está sendo criada. Então o mesmo diagrama de estrutura do programa principal, vale para procedures e functions, com algumas pequenas adequações de nomenclatura.

Estrutura de sub-rotinas em Pascal

Procedures, assim como o programa principal, pode receber parâmetros do módulo principal que o chamou, então podemos ter um conjunto de procedures e funções que recebem, ou não, parâmetros necessários para resolver o problema ao qual se propõe a resolver. Abaixo segue alguns exemplos de definições e chamadas de procedures.

Program Test_Procedure;
 
 Const ctTest : Integer = 100;
 Type  TString = String[20];
 
 Var
      nValue  : Integer;
 
 { Subroutine head - not optional }
 Procedure ThisIsATestProcedure( nGlobalConstValue : Integer );
 Const ctTest : Integer = 200;{ Local definition of same name global constant }
 Type  TMyType = Integer;
 
 Var
       nValue : TMyType;
 
 Begin       { Subroutine main block }
   nValue := ctTest;
   WriteLn( 'Global ctTest constant value', nGlobalConstValue );
   WriteLn( 'Local ctTest constant value', nValue );
 End;
 
Begin  { Main Block }
  nValue := ctTest;
  ThisIsATestProcedure( nValue );
End.

O exemplo acima ilustra bem o conceito de encapsulamento presente em Pascal, uma vez que a procedure define elementos com mesmo nome de identificadores presentes no escopo global e ainda assim cada variável e constante com nomes comuns tiveram seus valores devidamente preservados em seus respectivos contextos.

Abaixo segue um outro exemplo que demonstra uma procedure que contém outra procedure interna, ou seja, podemos definir diversos níveis de encapsulamento inclusive dentro de sub-rotinas, sejam procedures ou functions.

Program Test_Procedure2;
 
 Const ctTest : Integer = 100;
 Type  TString = String[20];
 
 Var
      nValue  : Integer;
 
 { Subroutine head - not optional }
 Procedure ThisIsATestProcedure( nGlobalConstValue : Integer );
 Const ctTest : Integer = 200;{ Local definition of same name global constant }
 Type  TMyType = Byte;
 
 Var
       nValue : TMyType;
 
  { child Subroutine head - not optional }
  Procedure ChildProcedure( nGlobalConstValue : Integer );
  Const ctTest : Integer = 300;{ Local definition of same name global constant }
  Type  TMyType = Integer;
 
  Var
        nValue : TMyType;
 
  Begin       { Subroutine-child main block }
    nValue := ctTest;
    WriteLn( 'Subroutine ctTest constant value', nGlobalConstValue );
    WriteLn( 'Local ctTest constant value', nValue );
  End;
 
Begin { Subroutine main block }
   nValue := ctTest;
   WriteLn( 'Global ctTest constant value', nGlobalConstValue );
   WriteLn( 'Local ctTest constant value', nValue );
   ChildProcedure( nValue );
 End;
 
Begin  { Main Block }
  nValue := ctTest;
  ThisIsATestProcedure( nValue );
End.

Nos exemplos anteriores todas as procedures recebem pelo menos 1 parâmetro sendo que para passar mais de 1 parâmetro basta seguir as seguintes regras:

  1. Identificadores de mesmo tipo podem ser separados por virgula(,);
  2. Identificadores de tipos diferentes devem, ser separados por ponto-e-virgula(;) logo após a definição do ultimo tipo;
Program Test_Procedure3;
 
 Const ctTest : Integer = 100;
 Type  TString = String[20];
 
 Var
      nValue  : Integer;
 
 { Subroutine head - not optional }
 Procedure ThisIsATestProcedure( nGlobalConstValue, nNumber : Integer; fValue : Real );
 Const ctTest : Integer = 200;{ Local definition of same name global constant }
 Type  TMyType = Integer;
 
 Var
       nValue : TMyType;
 
 Begin       { Subroutine main block }
   nValue := ctTest;
   WriteLn( 'Global ctTest constant value', nGlobalConstValue );
   WriteLn( 'Local ctTest constant value', nValue );
   WriteLn( 'Global number value', nNumber );
   WriteLn( 'Global Real value', fValue );
 End;
 
Begin  { Main Block }
  nValue := ctTest;
  ThisIsATestProcedure( nValue, 10, 10.59 );
End.

Outro conceito interessante de Pascal e de muitas outras linguagens modernas é quanto a passagem de parâmetros. Existem duas formas de passagem de parâmetros para sub-rotinas em Pascal, a primeira é a passagem de parâmetros por valor, onde o conteúdo da variável passada por parâmetro não é alterado caso a sub-rotina altere essa variável internamente. A segunda é passagem de parâmetro por referência, que por sua vez possibilita a alteração do conteúdo da variável passada por parâmetro para uma sub-rotina. Abaixo podemos ver um exemplo que demonstra os dois casos.

Program Test_Procedure4;
 
 Const ctTest : Integer = 100;
 
 Var
      nValue, nValue2  : Integer;
 
 { Subroutine head - not optional }
 Procedure ValuePassingTest( nGlobalConstValue : Integer; Var nValue2 : Integer );
 Const ctTest : Integer = 200;{ Local definition of same name global constant }
 
 Begin       { Subroutine main block }
   nGlobalConstValue := ctTest;
   nValue2 := 2000;
 End;
 
Begin  { Main Block }
  nValue  := ctTest;
  nValue2 := 0;
  WriteLn( nValue2 );
  ValuePassingTest( nValue, nValue2 );
  WriteLn( nValue2 );
End.

O detalhe da passagem de parâmetro por referência está no uso da palavra Var, na definição da sub-rotina, como pode ser visto no código acima. O uso de var na especificação do parâmetro de entrada do protótipo da função, especifica que aquele parâmeto é possível de alteração pela sub-rotina.

Por ultimo vamos falar sobre as Functions, ou funções, que em Pascal seguem as mesmas regras de escopo e passagem de parâmetros das procedures sendo a única diferença que uma Function retorna valor à rotina chamadora. Abaixo o exemplo simples de implementação e chamada de uma Function.

Program Test_Function;
 
 Const ctTest : Integer = 100;
 Type  TString = String[20];
 
 Var
      nValue, nRes  : Integer;
 
 { Subroutine head - not optional }
 Function SumConstants( nValue1 : Integer ) : Integer;
 Const ctTest : Integer = 200;{ Local definition of same name global constant }
 Type  TMyType = Integer;
 
 Var
       nValue : TMyType;
 
 Begin       { Subroutine main block }
   nValue := ctTest;
   SumConstants := ( nValue + nValue1 );
 End;
 
Begin  { Main Block }
  nValue := ctTest;
  nRes := SumConstants( nValue, 10, 10.59 );
  WriteLn( nRes );
End.

Ao contrário de outras linguagens, o Turbo Pascal 3 implementa uma forma peculiar de retorno de valores para a função chamadora, que é atribuir o valor ao nome da função definida, como podemos ver no código destacado abaixo.

 { Subroutine head - not optional }
 Function SumConstants( nValue1 : Integer ) ; Integer;
 Const ctTest : Integer = 200;{ Local definition of same name global constant }
 Type  TMyType = Integer;
 
 Var
       nValue : TMyType;
 
 Begin       { Subroutine main block }
   nValue := ctTest;
   SumConstants := ( nValue + nValue1 );
 End;

A função SumConstants assume o valor da operação matemática entre os dois operandos nValue e nValue1. Essa forma peculiar de retorno de valores de funções perdurou até o Turbo Pascal 7.0, sendo que a primeira versão do Delphi , padronizou a palavra reservada Result, para retornar valores de função, sendo que até hoje é possível retornar valores de função da mesma forma como faziam Turbo Pascal 3 e o Pascal padrão, até em compiladores Delphi e FreePascal mais modernos.

Uma observação é quanto a declaração do tipo a ser retornado pela função. Conforme pode ser visto no código acima, o tipo do retorno é especificado da mesma forma como se estivessemos definindo uma variável, ou seja, Function Nome_da_função : tipo a ser retornado.

Amigos vou fechando esse post, que ficou bem extenso porém como se trata de questões fundamentais da linguagem não poderia ser diferente, mas a partir do próximo post vou tratar de assuntos que podem ser encapsulados em sub-rotinas menores :), então por enquanto divirtam-se.

Até a próxima.

[]’s
PopolonY2k

Referência na internet

MSX-DOS (Wikipedia)
http://en.wikipedia.org/wiki/MSX-DOS

MSX BIOS (MSX Assembly pages)
http://map.grauw.nl/resources/msxbios.php

FAT (File allocation table)
http://en.wikipedia.org/wiki/File_Allocation_Table

GPL v3 (Wikipedia)
http://en.wikipedia.org/wiki/GPL#Version_3

Opcodes (Wikipedia)
http://en.wikipedia.org/wiki/Opcode

Z80 (Wikipedia)
http://en.wikipedia.org/wiki/Z80

Open Source Software (Wikipedia)
http://en.wikipedia.org/wiki/Open-source_software

PlanetaMessenger.org’s Old Skool Tech (Sourceforge.net)
http://sourceforge.net/projects/oldskooltech/

Anuncio do lançamento do Old Skool Tech (PopolonY2k Rulezz)
http://www.popolony2k.com.br/?p=377

Free Pascal (Official website)
http://www.freepascal.org/

Embarcadero Delphi
http://www.embarcadero.com/br/products/delphi

ASCII (Wikipedia)
http://en.wikipedia.org/wiki/ASCII

MSX-BASIC (Wikipedia)
http://en.wikipedia.org/wiki/MSX_BASIC

Assembly language (Wikipedia)
http://en.wikipedia.org/wiki/Assembly_language

GOTO Statement (Wikipedia)
http://en.wikipedia.org/wiki/Goto

Program scope (Wikipedia)
http://en.wikipedia.org/wiki/Scope_%28computer_science%29

Values r-value and l-value  (Wikipedia)
http://en.wikipedia.org/wiki/Value_%28computer_science%29

Object oriented programming (Wikipedia)
http://en.wikipedia.org/wiki/Object-oriented_programming

PHP (Wikipedia)
http://en.wikipedia.org/wiki/PHP

Turbo Pascal Forver – Introdução – PopolonY2k Rulezz
http://www.popolony2k.com.br/?p=1058

Encapsulation (Wikipedia)
http://en.wikipedia.org/wiki/Encapsulation_(object-oriented_programming)

Sub-routine (Wikipedia)
http://en.wikipedia.org/wiki/Subroutine

Evaluation strategy (Wikipedia)
http://en.wikipedia.org/wiki/Evaluation_strategy

Print Friendly, PDF & Email

Turbo Pascal Forever – Start your engines.

Após uma bela sequência de posts sobre jogos e afins, volto a escrever sobre o que eu gosto de fazer que é programar coisas 🙂 e principalmente para enfim dar continuidade a minha primeira série sobre programação para micros clássicos, que é o curso de Turbo Pascal 3.0, iniciado nesse post aqui, que por sua vez foi e é muito conhecido e utilizado em computadores baseados e/ou compatíveis com o sistema operacional CP/M, isso inclui o MSX e talvez seja linguagem mais utilizada nessa arquitetura, depois do MSX-BASIC.

Bom, primeiramente vamos responder às perguntas deixadas no ultimo post, que reproduzo a seguir.

PERGUNTA 1 (utilizem a área de comentários do post para responder)

Qual o nome do arquiteto que deixou a Borland para na Microsoft criar uma das linguagens mais conhecidas da empresa na atualidade ?

Resposta: Anders Hejlsberg, que foi um dos principais arquitetos da Borland International desde a época do Turbo Pascal, sendo também o homem forte do Delphi até aproximadamente 1996 (versão 3.0), depois disso a Microsoft fez uma grande investida nos principais nomes da equipe de compiladores da Borland, levando-os para Redmond onde  posteriormente trabalharam, sob a tutela de Anders, no projeto do Framework, cópia da arquitetura Java, conhecido como Microsoft .Net, incluindo a linguagem C# que é a mais conhecida nessa plataforma. Na verdade Anders Hejlsberg criou um compilador Pascal em sua empresa, situada na Dinamarca e conhecida como Poly Data, tendo a Borland licenciado o núcleo de sua implementação da linguagem Pascal, posteriormente adicionando um editor integrado, criando assim o Turbo Pascal. Não só isso, antes de vender o seu Pascal (PolyPascal) para a Borland, Anders era representante da Microsoft na Dinamarca, o que o colocava em desacordo com a Borland. Posteriormente a Borland adquiriu 100% dos direitos do compilador e contratou Anders como um dos principais arquitetos do Turbo Pascal (Fonte: Anders Hejlsberg Wikipedia e Antique Software: Turbo Pascal 1.0 – Embarcadero).

PERGUNTA 2(utilizem a área de comentários do post para responder)

Que grande empresa, reconhecida pelos seus gadgets e PC‘s, desenvolveu a extensão de Pascal, denominada Object Pascal ?

Resposta: Apple Inc. Muita gente sabe da fixação da Apple em ser incompatível e tentar amarrar seus produtos tornando-os uma caixa preta incomunicável com outras plataformas, pois bem, não é de hoje que a Apple opta por tecnologias não mainstream, como é o Objective-C que é a tecnologia base  de desenvolvimento para os principais produtos da empresa, como IPod, IPad, IPhone e MacOSX. No passado a mesma Apple optou por manter a base de software de seu MacOS, toda escrita em Pascal, em um momento em que a linguagem C se tornava o mainstream como tecnologia de base para sistemas operacionais, uma vez que a maioria dos sistemas existentes optavam pela linguagem de Dennis Ritchie, principalmente pelas obvias características de portabilidade, poder de abstração, facilidade de interfacear alto nível com baixo nível e principalmente velocidade.

Apple I - Detalhe do "incompatible" no quadro ao fundo. Desde aquela época já era assim. :)
Apple I – Detalhe do “incompatible” no quadro ao fundo…….desde aquela época já era assim. 🙂

Pois bem, a empresa de Steve Jobs basicamente desenvolveu a extensão do Pascal orientado a objetos, conhecida como Object Pascal, sob a tutela de Niklaus Wirth, e que mais tarde serviria como base para versões futuras do Turbo Pascal 5.5 da Borland até as versões atuais do Delphi.

Integrated Development Editor (aka IDE)

O Turbo Pascal 1.0 foi reconhecidamente o primeiro compilador a integrar um ambiente de desenvolvimento (IDE), juntamente com o mais rápido compilador (1 passo) da época e com isso se tornou um sucesso absoluto no meio comercial e principalmente no meio acadêmico, sendo utilizado até os dias de hoje em diversas instituições de ensino, através de compiladores modernos e compatíveis como o FreePascal.

A IDE das primeiras 3 versões do Turbo Pascal, apesar de simples e até arcaica para os padrões atuais, representou um grande avanço no meio de desenvolvimento de software, principalmente por combinar um poderoso editor de textos, compatível em comandos com o “padrão” dominante na época, o “padrão” WordStar, e que se perpetuou para a maioria dos processadores, editores de textos e IDE‘s, até os dias de hoje.

É sobre esses comandos do editor e também sobre as opções do menu do Turbo Pascal que descrevo abaixo.

Tela de abertura do Turbo Pascal 1.0. Foi assim até a versão 3.0

A imagem acima demonstra a tela de menu das primeiras versões do Turbo Pascal onde o usuário tem a possibilidade de, através dos caracteres que estão em maiúsculo (também colorido na versão x86, ou PC), selecionar as opções desejadas no ambiente integrado.

Segue abaixo a descrição das opções do menu principal e a medida em que avançarmos no curso, vou descrevendo as outras opções dos sub-menus de alguns desses itens.

  • Logged drive – Mostra e possibilita a alteração do drive corrente. Ao se teclar L, aparecerá uma nova mensagem “New drive“, solicitando a nova unidade que será a corrente.
  • Work file – No Turbo Pascal existe o conceito de Arquivo Principal (Main file) e Arquivo de Trabalho (Work file). De fato o arquivo de trabalho é o principal, pois quando não é especificado um arquivo principal, o Arquivo de trabalho é assumido como o mesmo. Era uma forma arcaica de se trabalhar com 2 arquivos simultâneamente, de forma que o principal só é carregado no momento da compilação e o Work file no momento da edição. Isso é ótimo quando se está trabalhando no desenvolvimento de arquivos de include (que veremos mais a frente) e se deseja compilar e executar com um outro arquivo principal e que utiliza esse include.
  • Main file – Vide opção acima.
  • Edit – Entra no editor de programas do Turbo Pascal, carregando o Work file, ou Main file que estiver especificado.
  • Compile – Compila o programa para a memória, disco (.COM) ou para o formato Chain (.CHN), de acordo com a seleção feita na opção compiler Options, descrita mais abaixo. Quando compilado em disco, o Turbo Pascal gera um executável compatível com o MSXDOS e CP/M, no formato .COM, podendo ser executado nesses sistemas operacionais. Até a versão 3.0 do Turbo Pascal, existia a possibilidade de que algumas sub-rotinas estivessem em outros arquivos executáveis especialmente gerenciados pelo runtime do Turbo Pascal, que são os arquivos Chain (corrente), em um formato especifico do Turbo Pascal, com a extensão .CHN. Podemos traçar um paralelo desses arquivos .CHN com as técnicas modernas de Dynamic loading possíveis através dos Shared Objects ou Dynamic Load Libraries presentes nos sistemas operacionais modernos, como UNIX (Linux, Solaris, …), através dos módulos .so e no Windows através das famosas .dll‘s, entretanto no Turbo Pascal há muitas limitações do uso de arquivos .CHN.
  • Run – Compila e executa um programa, de acordo com a seleção feita na opção compiler Options.
  • Save – Salva um Work file ou Main file, carregado no editor.
  • Dir – Mostra o conteúdo do drive e diretório corrente.
  • Quit – Sai do Turbo Pascal.
  • compiler Options – Possibilita a alteração do modo de compilação, podendo ser compilação em memória, arquivo .COM, ou .CHN. Existe também a opção de configuração para depuração de erros de runtime.

Comandos básicos do editor.

Ao contrário dos dias atuais onde tudo se resolve com um clique do mouse ou até mesmo com um toque dos dedos, nos primeiros dias da micro-informática nem tudo era repleto de menus com fácil acesso e principalmente com padronizações bem definidas quanto aos atalhos comuns entre os softwares, coisa que hoje é definida grande parte pelo próprio sistema operacional hospedeiro. No Turbo Pascal 3.0 não é diferente, até mesmo porque é um software de 1983, onde estávamos bem longe de tal padronização. Entretanto existiam alguns “padrões” de mercado, na época e que, principalmente, eram ditados pelos softwares que estavam “bombando” naquele momento.

No caso do Turbo Pascal, um outro software que influenciou muito na interface de edição dos programas, foi o WordStar, que foi o primeiro processador de textos de grande sucesso do mundo, na época. Como a grande maioria dos usuários já estavam habituados aos padrões de comandos de edição do WordStar, todo software similar daquela época procurava imitar o líder de mercado, comportamento esse que é seguido até hoje em todos os softwares atuais.

Tela principal do WordStar
Tela principal do WordStar

Apenas para deixar registrado visualmente, abaixo podemos observar o conceito de WYSIWYG da época, sob o ponto de vista do software líder de mercado, o WordStar.

WordStar e os primórdios do WYSIWYG
WordStar e os primórdios do WYSIWYG

Não vou descrever todos os comandos e atalhos do editor do Turbo Pascal mas os principais e mais importantes com certeza estarão aqui. Os atalhos sempre eram ativados no Turbo Pascal com a combinação CTRL+K, seguida de outra tecla que especificava o comando a ser executado. Segue abaixo a lista dos principais:

  • CTRL+K D – Essa combinação de teclas quando digitada de dentro do editor, faz com que o menu principal seja ativado. Geralmente quando você está editando um fonte e na sequência deseja iniciar a compilação do mesmo, primeiro deverá sair do editor e retornar ao menu principal, uma vez que nessa versão do Turbo Pascal não tinhamos ainda o conceito de menus DropDown.
  • CTRL+K B – Combinação de teclas que especifica a marcação de um inicio de bloco a ser copiado para o Clipboard, ou área de transferência, como conhecemos em português.
  • CTRL+K K – Combinação de teclas que especifica a marcação do fim de um bloco a ser copiado para o Clipboard, ou área de transferência. É similar a marcação de texto feito pelo mouse em sistemas de processamento de textos atuais.
  • CTRL+K C – Copia um texto previamente marcado através da combinação anterior CTRL+K B/CTRL+K K, para a posição corrente do cursor.
  • CTRL+K V – Move um texto previamente marcado através da combinação anterior CTRL+K B/CTRL+K K, para a posição corrente do cursor.
  • CTRL+K W – Grava para arquivo, o texto previamente marcado através da combinação anterior CTRL+K B/CTRL+K K. Ao executar essa combinação, aparecerá, no topo da tela do editor, a mensagem “Write block to file:“, solicitando o nome do arquivo a ser gravado.
  • CTRL+K R – Carrega, na posição corrente do cursor, o texto gravado em um arquivo do disco. Ao executar essa combinação, aparecerá, no topo da tela do editor, a mensagem “Read block from file:“, solicitando o nome do arquivo a ser carregado.

.

A hora do código.

Bom, chega de enrolar e vamos colocar a mão na massa, passando primeiramente pelos conceitos mais básicos da linguagem, então nesse post vamos nos concentrar apenas na estrutura básica de um programa Pascal e a partir do próximo post, sim, vamos entrar nos detalhes mais avançados da linguagem, incluindo sua integração com recursos específicos de nossa plataforma preferida, o MSX :).

Lembrando que o intuito desse curso não é ensinar estrutura de dados e técnicas de programação, portanto irei considerar isso um pré-requisito.

Vamos conhecer a estrutura básica de um programa em Pascal.

Program Hello_World;
(*
  ********************************************************
  * Área de variáveis globais;
  * Definição de constantes;
  * Definição de novos tipos de dados (user defined types);
  * Definição de registros (estruturas de dados);
  * Definição de funções do usuário;
  * Definição de procedimentos do usuário;
  ********************************************************
*)
Begin  { Entry point principal do programa ou método principal }
  (* Área de código do programa principal *)
  WriteLn( 'Hello World' );
End.

Vamos começar do inicio :). Primeiramente o que o novo programador de Pascal precisa saber é que a linguagem não é case-sensitive, ou seja, comandos, variáveis, constantes, novos tipos ou registros, se definidos diferenciando-se apenas os caracteres entre maiúsculos e minúsculos, não serão distintos para o compilador Pascal. Ex: PROGRAM, Program, PrOgRaM, para o compilador Pascal é considerado a mesma palavra.

Sabendo isso vamos começar analisando a primeira linha de nosso programa exemplo, começando pela palavra reservada Program nome_programa, que especifica o nome do programa em questão, mas tem efeito apenas cosmético, sendo mais utilizado para identificar o programa principal, por isso, caso o programador não queira utilizar não é empecilho para se escrever um programa em Pascal. Caso se utilize um nome de programa não se esqueça de que o nome não pode ter espaços.

Logo abaixo temos um comentário de múltiplas linhas que se inicia com  (* e termina com *), podendo também ser definido um comentário de múltiplas linhas através de { terminando com }. Infelizmente até a primeira versão do Delphi, não tivemos no Pascal da Borland nenhum equivalente aos comentários de linha única, como o //.

(*
  ********************************************************
  * Área de variáveis globais;
  * Deinição de constantes;
  * Definição de novos tipos de dados (user defined types);
  * Definição de registros (estruturas de dados);
  * Definição de funções do usuário;
  * Definição de procedimentos do usuário;
  ********************************************************
*)

Podemos, e vamos, utilizar no futuro a área onde está o comentário acima justamente da forma descrita no próprio comentário no código, ou seja, área de variáveis globais, constantes, definições de novos tipos e estruturas, funções e procedimentos definidos pelo usuário, que veremos a partir do próximo post.

Por último temos o bloco do programa principal, main entry-point ou função principal para quem gosta de C/C++.

Como Pascal é uma linguagem estruturada onde cada função tem seu próprio escopo de código e dados, para se definir o início desse escopo, utilizamos a palavra reservada Begin, finalizando-o através da palavra End.

Lembrando que em Pascal o separador de comandos é sempre o ; (ponto e virgula) mas no caso do bloco principal a palavra End sempre terminará com . (ponto), então o seu programa principal sempre será conforme a estrutura abaixo.

Begin  { Inicio  do bloco do programa principal }
  (* Seu programa entra aqui *)
End.   { Término do bloco principal }

Por último vou falar, resumidamente, do statement WriteLn, que é utilizado como rotina de saída (output) para os dispositivos suportados pelo Turbo Pascal e nesse caso específico é utilizado para exibir um conjunto de caracteres (string) na saída padrão do computador, que é a tela do console, então o nosso programa de exemplo….

Begin
  WriteLn( 'Hello World' );
End.

…deverá gerar a seguinte saída…

.
Hello World
.

Não vou me aprofundar, por enquanto, sobre o statement WriteLn uma vez que nos próximos posts vou voltar a falar em profundidade sobre esse comando e também sobre as demais rotinas de I/O de Turbo Pascal.

Bom, vou ficar por aqui, entretanto você pode estar se perguntando o porque deu eu estar bem devagar nesse principio do curso e eu te respondo que eu conheço os meus visitantes assíduos e sei que muitos dos que estão esperando “ansiosamente” pelo curso de Pascal, seguido pelo de C, são não programadores, logo, terei que deixar essa fundação bem sólida. 😉 e apesar de eu já ter dito que não irei focar em estruturas de dados, nesse caso, vou manter algo mais light no inicio….mas a pauleira começa a partir do próximo post. 🙂

[]’s
PopolonY2k

Referência na internet

Turbo Pascal Forever – Introdução
http://www.popolony2k.com.br/?p=1058

CP/M operating system (Wikipedia)
http://en.wikipedia.org/wiki/CP/M

Anders Hejlsberg (Wikipedia)
http://en.wikipedia.org/wiki/Anders_Hejlsberg

Antique Software: Turbo Pascal 1.0 – Embarcadero
http://edn.embarcadero.com/article/20693

Java – Linguagem de programação (Wikipedia)
http://pt.wikipedia.org/wiki/Java_%28linguagem_de_programa%C3%A7%C3%A3o%29

Microsoft .Net (Wikipedia)
http://pt.wikipedia.org/wiki/Microsoft_.NET

C# (Wikipedia)
http://en.wikipedia.org/wiki/C_Sharp_%28programming_language%29

Conspiracy Theory: Microsoft’s .Net IS Borland’s Product
http://delphi.about.com/od/delphifornet/a/conspiracydnet.htm

Apple Inc. (Wikipedia)
http://en.wikipedia.org/wiki/Apple_Inc.

Objective-C (Wikipedia)
http://en.wikipedia.org/wiki/Objective-C

IPod (Wikipedia)
http://en.wikipedia.org/wiki/Ipod

IPad (Wikipedia)
http://en.wikipedia.org/wiki/Ipad

IPhone (Wikipedia)
http://en.wikipedia.org/wiki/Iphone

MacOS (Wikipedia)
http://en.wikipedia.org/wiki/Macos

MacOSX (Wikipedia)
http://en.wikipedia.org/wiki/Macosx

Linguagem C (Wikipedia)
http://en.wikipedia.org/wiki/C_language

Dennis Ritchie RIP++
http://www.popolony2k.com.br/?p=1204

Object Pascal (Wikipedia)
http://en.wikipedia.org/wiki/Object_Pascal

Embarcadero Delphi (Wikipédia)
http://en.wikipedia.org/wiki/Borland_Delphi

WordStar (Wikipedia)
http://en.wikipedia.org/wiki/WordStar

Dynamic loading (Wikipedia)
http://en.wikipedia.org/wiki/Dynamic_loading

FreePascal (Wikipedia)
http://en.wikipedia.org/wiki/Freepascal

Menu (Computing – Wikipedia)
http://en.wikipedia.org/wiki/Menu_%28computing%29

Clipboard (Wikipedia)
http://en.wikipedia.org/wiki/Clipboard_%28software%29

WYSIWYG (Wikipedia)
http://en.wikipedia.org/wiki/WYSIWYG

Case-sensitive (Wikipedia)
http://pt.wikipedia.org/wiki/Case_sensitive

Print Friendly, PDF & Email