sexta-feira, 24 de abril de 2009

A linguagem ADA 95

Ada é uma linguagem de programação relativamente nova desenvolvida pelo Departamento de Defesa dos Estados Unidos em uma tentativa de resolver a confusão de software que existia na década de 70. Nesse período, sentia-se que as 2000 ou mais linguagens de programação em uso poderiam ser substituídas em grande parte por uma linguagem bem planejada para uso em sistemas de Tempo-Real embutidos (embedded).
Ada 95 é uma atualização ISO da linguagem de programação Ada para incorporar o mais recente conhecimento de desenvolvimento de software. A versão de 1983 da linguagem foi chamada de Ada durante anos. A versão mais nova (revisada em 1995) que substituiu a versão original também é chamada de Ada.
A linguagem ADA 95 é o resultado do maior esforço despendido para a definição de uma linguagem de programação. É uma linguagem de propósito geral, onde a qualidade de um programa é o seu forte. Esta linguagem está fortemente associada aos princípios da engenharia de software e estimula a construção de softwares voltados ao reuse.
O nome ADA foi escolhido em homenagem a Augusta Ada Byron, Condessa de Lovelace e filha do poeta Lord Byoron. Ada trabalhou com Charles Babbage na programação da Difference Engine. Por isso, Ada é considerada a primeira programadora.

Notação BNF da linguagem Ada 95

Veja a sessão de downloads.

Palavras reservadas da Linguagem ADA 95

A seguir estão as 69 palavras reservadas (keywords) da linguagem ADA 95 (Ada 83 só tinha 63 palavras reservadas):

abort abs abstract accept access
aliased all and array at
begin body case constant declare
delay delta digits do else
elsif end entry exception exit
for function generic goto if
in is limited loop mod
new not null of or
others out package pragma private
procedure protected raise range record
rem renames requeue return reverse
select separate subtype tagged task
terminate then type until use
when while with xor

Tipo de Dados Pré-Definidos da Linguagem ADA 95

A linguagem Ada dá muita importância aos tipos de dados e suas operações, sendo por isso chamada de fortemente tipada. Isto quer dizer que a cópia e a associação de objetos é apenas permitida quando os respectivos objetos têm tipos compatíveis. A seguir, estão os tipos pré-definidos mais populares da linguagem (tipos primitivos):
Tipo Integer
O tipo Integer permite o armazenamento de números inteiros dentro do intervalo fechado [-32767, 32967] , e faz parte dos tipos elementares escalares discretos da linguagem.
Tipo Natural
O tipo Natural restringe os valores do tipo Integer. Os objetos definidos com o tipo Natural podem conter valores dentro do intervalo fechado [0,32767].
Tipo Positive
O tipo Positive, assim como o tipo Natural, restringe os valores do tipo Integer. Os objetos do tipo Positive podem conter valores dentro do intervalo fechado [1,32767].
Tipo Character
O tipo Character permite o armazenamento de 256 caracteres. Ele engloba todos os literais caracteres
Tipo Boolean
O tipo Boolean permite o armazenamento dos valore boleanos true e false.
Tipo String
O tipo String permite o armazenamento de um conjunto de elementos do tipo Character.
Tipo Float
O tipo Float permite o armazenamento de números, incluindo os literais numéricos com valores significativos à direita do ponto decimal, mais especificamente denominados de números de ponto flutuante, dentro do intervalo fechado [-1.67E+306,1.67E+307] com até oito dígitos decimais, fazendo parte dos tipos elementares escalares da linguagem.

Coleções

Arrays
Arrays permitem a representação de coleções de dados homogêneos sob a forma de vetores, matrizes e estruturas com mais de 02 dimensões.
A sintaxe simplificada de definição de arrays é dada por
array ( {, }) of ;
Cada intervalo discreto especifica o primeiro e o último subscrito de cada dimensão. O Subtipo especifica o tipo de todos os objetos do array.
Vetores
Os arrays com apenas uma dimensão são apelidados de vetores. Abaixo está um exemplo de definição de vetor:
Tot_Dias : array (1 .. 12) of Integer;
Podemos definer ainda da seguinte forma:
Tot_Dias : array (1 .. 12) of Integer := (15,31,20,35,35,32,35,1,56,65,12,12);
Para acessarmos um elemento do vetor Tot_Dias basta apenas utilizarmos o comando Tot_Dias[<índice>], onde <índice> pode ser qualquer número entre 1 e 12 (quantidade de posições do vetor Tot_Dias).

Matrizes
Arrays com duas ou mais dimensões são apelidadas de matrizes. Logo abaixo está um exemplo de definição de matrizes:
Despesas : array ( 1 .. 31, 1 .. 3 ) of Float;
Para armazenar um valor numa determinada posição em uma matriz, podemos usar uma sintaxe semelhante a Despesas(<índice 1>, <índice 2>), onde os índices 1 e 2 são valores inteiros que estejam dentro do intervalo definido para a matriz (1 .. 31 e 1 .. 3).

Definição de Novos Tipos de Dados

Assim como existem os tipos de dados pré-definidos, pode-se também definir novos tipos.

A construção SubType

Podemos definir tipos de dados que herdam as propriedades de tipos anteriormente definidos. Também é possível modificar estar propriedades. Um meio de redefinir um tipo de dado é através da construção subtype. A sintaxe parcial para a construção sybtype é dada por:
Subtype is ;
Onde
::= []
Exemplos:

- Define um tipo composto por um string de comprimento 4
Subtype Chave_de_Pesquisa is String ( 1 .. 4 );

- Integer mas restringe a capacidade de armazenamento pela metade.
Subtype Integer_Pequeno is Integer
range Integer’First/2 .. Integer’Lasta/2;

-Simplesmente renomeia o tipo Integer
SubType Inteiro is Integer;

Tipos Enumeráveis

A definição de tipo enumeráveis possibilita a especificação de um conjunto de literais enumeráveis. Os tipos enumeráveis simplificam a declaração de constantes numéricas, uma vez que cada literal enumerável é associado a um valor numérico. Um literal enumerável também pode ser um identificador ou um literal caracter. A sintaxe de um tipo enumerável é dada por:
::= ( {,})

Cada especificação de literal enumerável está associada a uma posição dentro do conjunto. Por exemplo, o enumerável abaixo possui cinco literais enumeráveis com posições 0,1,2,3 e 4:

(Segunda, Terça, Quarta,Quinta,Sexta)

Isto e, o literal enumerável Segunda tem a posição 0, e Sexta tem a posição 4.

Veja um exemplo mais completo:
Subtype Dias is Integer range 1 .. 31;
Type Tipos_De_Despesas
Is (Alimentacao, Transporte, Moradia);
Os dois tipos supracitados nos permitem declarar uma matriz de forma mais robusta, e ao menos tempo, explicativa:

Despesas: array(Dias, Tipos_De_Despesas) of Float;

Expressões Relacionais

As expressões relacionais produzem valores do tipo boleano, isto é, true ou false. Veja o um exemplo de expressão relacional:
while R /= 0 loop
Abaixo estão os operadores relacionais da linguagem ADA 95:
OPERADOR
= Igual a
/= Diferente de (não igual a)
> Maior que
>= Maior ou igual
< Menor que
<= Menor ou igual a

O comando while

O comando while é utilizado para a especificação de iterações do tipo primeiro testa e depois executa, isto é, a sequência de comandos subordinada ao comando while é executada enquanto o resultado da avaliação de expressão situada entre as palavras reservadas while e loop for igual ao valor boleano true.
A sintaxe empregado pelo comando while é a seguinte:

while loop

end loop;

O comando for

A sintaxe simplificada do comando for é dada por
for
in [reverse] loop
end loop;

é um identificador declarado automaticamente pelo comando for.
é especificado através de um nome de tipo de dado discreto, ou por um intervalo, cujo tipo de dado é utilizado na declaração automática do .
A semântica do comando for estabelece a obtenção do primeiro valor de intervalo e sua atribuição ao , que também é chamado de parâmetro de interação. A é executa se o valor do parâmetro de iteração é menor ou igual ao último valor do intervalo. Cada vez que termina sua execução, o parâmetro de controle recebe o próximo valor. Abaixo está um exemplo prático da utilização do comando for:
For I in 0 .. 3 loop
X(I) := X(I) + 1;
End loop;
A opção reverse do commando for especifica que o identificador de definição receberá, em cada interação, os valores em ordem inversa ao do intervalo, isto é, do maior ao menor. Veja um exemplo da semântica do comando for utilizando reverse
For I in reverse 0 .. 3 loop
X(I) := X(I) + 1;
End loop;

O comando If

O comando if permite a especificação da execução condicional de comandos. A sintaxe do comando if, adaptada do Ada 95 Reference Manual é exibida a seguir:
::= if then

{ elseif then
}
[else
]
::=

Cada comando IF deve terminar por um end Ii;. A construção elseif está envolva pelos símbolos {}, isto quer dizer que ela pode ocorrer zero, 1, 2,...,n vezes após a cláusula if inicial.
A construção else está envolta pelos símbolos [ ], e isto quer dizer que ela é opcional.
A semântica do comando if estabelece que apenas uma seqüencia de comandos seja executada.

O comando Goto

O comando goto permite o desvio do fluxo de execução do programa. Veja o exemplo abaixo :

<>
Ada.Text_IO.Put(“Especifique o primeiro valor”);
Ada.Integer_Text_IO.Get(A);
Ada.Text_IO.Put(“Especifique último valor”);
Ada.Integer_Text_IO.Get(B);

If A <> B then
Goto De_Novo;
End if;

O comando Goto De_Novo; especifica que o próximo comando a ser executado será aquele que segue a definição do rótulo De_Novo;

O trecho de código acima poderia ser substituído pelo código abaixo:

loop

Ada.Text_IO.Put(“Especifique o primeiro valor”);
Ada.Integer_Text_IO.Get(A);
Ada.Text_IO.Put(“Especifique último valor”);
Ada.Integer_Text_IO.Get(B);

exit when not (A <> B);

Funções

Funções são subprogramas cuja execução das seqüências de comandos culminam na elaboração de um valor. A execução de uma função sempre resulta em um valor.
Abaixo está um exemplo que demonstra a codificação de uma função que encontra o maior de três valores inteiros:

function Maior_De_Tres(A ,B ,C : in Integer) return Integer is
begin
return Integer’Max(A,Integer’Max(B,C));
end Maior_De_Tres;


Procedimentos

Os procedimentos são subprogramas que implementam subalgoritmos . Quando um algoritmo não se ajusta aos requisitos de uma função, o procedimento ou procedure é a escolha para sua implementação. O código abaixo demonstra um exemplo de procedimento:

procedure Maior_De_Tres(A ,B ,C : in Intege; R: out Integer) is
begin
R := Integer’Max(A,Integer’Max(B,C));
end Maior_De_Tres;

Perceba que a transmissão do resultado da expressão que encontra o maior valor é feita para o parâmetro R.

Escopo em Sub Programas

Os objetos declarados dentro de subprogramas podem ter o mesmo nome que objetos declarados fora deste. Podemos ter também subprogramas declarados dentro de subprogramas. Chamamos isto de aninhamento de subprogramas. A normal que define qual objeto é utilizado em dada circunstância é o escopo. Daí surgem os objetos globais e objetos locais.
Objetos Globais
Quando tempos um programa principal, M, e um subprograma, S, declarado dentro de M, diz-se que S pode acessar qualquer construção de M. Essas construções podem ser tipos, objetos, nomes de subprogramas e outras unidades.
Objetos Locais
Dentro de S podemos ter também objetos declarados. Diz-se que estes objetos são locais a S. S, por seu turno, pode ter um subprograma, F, dentro de sua região declarativa. Neste caso, os objetos declarados dentro de F são locais a F, isto é, S não tem acesso a eles, da mesma forma que M não tem acesso aos objetos de S. Temos ainda o caso de um objeto, digamos A, se declarado em M e F. Quando a é referencia após sua declaração em F, o objeto utilizado é o que pertence a F e não a M ou S. Objetos locais têm prioridade sobre objetos globais. Se A for referenciado dentro de S, o objeto utilizado será o declarado em M.
Nível
Para descrever a posição de um subprograma dentro de outro, utiliza-se o termo nível. O programa principal tem nível 0; um ou mais subprogramas codificados na região declarativa do programa principal tem nível 1, e assim por diante. Subprogramas declarados em uma mesma região declarativa têm o mesmo nível. Quando maior for o nível de um subprograma, maior será o grau de aninhamento do programa.

Overloading

O Ada permite sobrecarga de funções e procedimentos, isto é, permite que vários procedimentos (ou funções) sejam declarados com o mesmo nomefazendo distinção entre eles pelos tipos incluídos na lista de parâmetros e o tipo do retorno; e a eficiência não é degradada pelos nomes sobrecarregados.
Um dos problemas que pode ocorrer, é que em alguns caso uma simples confusão na hora de declarar as variáveis da função pode fazer o sistema usar a função errada o que geraria dados inválidos. Um problema até pior poderia ser achado se nós tivemos uma função para a que utiliza um INTEGER e um FLOAT para produção, porque só um erro pequeno pode causar resultados errôneos. Por causa disto, seria vantagem usar subprogramas diferentes com nomes diferentes para operações diferentes.

Package

Embora Ada95 permita a compilação separada(um arquivo compilado de cada vez), ela mamtem sua característica checagem de tipos. Diferentemente de FORTRAN, Ada95 usa compilação separada, não compilação independente. Em FORTRAN os arquivos são compilados sem tomarem conhecimento do conteúdo dos outros arquivos. Em Ada95 são feitas chacegens de definição de tipos, passagem de parâmetros etc. O que possibilita que Ada95 apresente essa modularidade são os pacotes.
Um pacote refere-se a uma coleção de entidades relacionadas, sendo essas procedimentos, funções, tipos, subtipos, constantes, variáveis e até mesmo outros pacotes.

Especificação de um Pacote

Um pacote deve ter uma especificação e um corpo. Na especificação são declarados os tipos, subtipos, variáveis, constantes e os cabeçalhos de funções e procedimentos. No corpo do pacote são definidas as funções e procedimentos. Uma especificação de pacote contem a palavra-chave “package”, o nome do pacote e a palavra-chave “is”.Por exemplo:

-- Interface de AdderPkg
package AdderPkg is
type MY_ARRAY is array(INTEGER range <>) of FLOAT;
procedure Add_Em_Up(In_Dat : in MY_ARRAY;
Sum : out FLOAT);
end AdderPkg;

A especificação do pacote é também chamada de interface porque é a parte do pacote à qual o usuário terá acesso. A declaração de funções e procedimentos deve ser completa para que o compilador cheque se o programa cliente está passando de forma correta os parâmetros e atribuindo a variáveis de tipo correto os valores retornados pelas funções.

Corpo de um Pacote

O corpo de um pacote contem a parte de implementação das funções e procedimentos declarados na interface(definição do pacote). Os cabeçalhos das funções e procedimentos devem ser repetidos aqui seguidos por sua completa definição. Agora é a hora de dizer o que cada função faz e como. Essa é a parte que não interessa ao programa cliente, que só quer saber como usar o que está disponível. Seguindo o exemplo anterior:

-- Implementação de AdderPkg
package body AdderPkg is
procedure Add_Em_Up(In_Dat : in MY_ARRAY;
Sum : out FLOAT) is
Total : FLOAT;
begin
Total := 0.0;
for Index in In_Dat'FIRST..In_Dat'LAST loop
Total := Total + In_Dat(Index);
end loop;
Sum := Total;
end Add_Em_Up;
end AdderPkg;

O começo da parte de implementação do pacote acrescenta a palavra-chave “body”, informando que ali começa o corpo do pacote.
O pacote composto por essas duas partes pode ser compilado mas não executado porque não é um programa inteiro. Ele foi definido para ser apenas usado por outros programas.

Alguns Pacotes Padrão

pacakge standard - e´a raiz de todos os pacotes em Ada95 e é onde coisas como integer, float, boolean, entre outras coisas estão definidas. Este pacote é automaticamente WITHed em toda unidade de compilação Ada.
package Ada.Numerics.Elementary_Functions - esse pacote fornece as funções trigonométricas e um operador de exponenciação de ponto flutuante.
package Ada.Characters.Latin_1 - esse pacote substitui o pacote ASCII que estava disponível na linguagem Ada83 e está agora obsoleta. Esse novo pacote contem as definições do conjunto de caracteres ASCII.

Bibliografia

Lopes, Arthur Vargas. Introdução à Programação com Ada 95 / Artur Vargas Lopes. – Canoas: Ed. ULBRA, 1997. 424p.
http://guaiba.ulbra.tche.br/zeve/Aula.rtf.
http://www.infres.enst.fr/~pautet/Ada95/a95list.htm

Programa de Exemplo em ADA

--EXEMPLO
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;

procedure IntAttrs is

type BUG_RANGE is range -13..34;

Rat : INTEGER;
Dog : NATURAL;
Cat : POSITIVE;
Bug : BUG_RANGE;

begin

Rat := 12;
Bug := -11;

Put("The type INTEGER uses ");
Put(INTEGER'SIZE);
Put(" bits of memory,");
New_Line;
Put(" and has a range from ");
Put(INTEGER'FIRST);
Put(" to ");
Put(INTEGER'LAST);
New_Line;
Put(" Rat has a present value of ");
Put(Rat);
New_Line(2);


Put("The type BUG_RANGE uses ");
Put(INTEGER(BUG_RANGE'SIZE));
Put(" bits of memory,");
New_Line;
Put(" and has a range from ");
Put(INTEGER(BUG_RANGE'FIRST));
Put(" to ");
Put(INTEGER(BUG_RANGE'LAST));
New_Line;
Put(" Bug has a present value of ");
Put_Line(INTEGER(Bug));
New_Line(1);

end IntAttrs;


-- Resultado da execução

-- The type INTEGER uses 32 bits of memory,
-- and has a range from -2147483648 to 2147483647
-- Rat has a present value of 12


-- The type BUG_RANGE uses 7 bits of memory,
-- and has a range from -13 to 34
-- Bug has a present value of -11

Programa de Exemplo em ADA - II

with Ada.Text_IO;
use Ada.Text_IO;

procedure MixTypes is

PI : constant := 3.1416;
TWO_PI : constant := 2 * PI;

type MY_FLOAT is digits 7;
Size : MY_FLOAT;

type MY_FIXED is delta 0.1 range -40.0..120.0;
Temperature : MY_FIXED;

Index : INTEGER;

begin
Size := TWO_PI;
Temperature := 2 * TWO_PI;
Temperature := 3 * Temperature;
Temperature := MY_FIXED(12.0 * TWO_PI);

Size := MY_FLOAT(Temperature) + PI;
Size := MY_FLOAT(Temperature + PI);

Index := INTEGER(Size + MY_FLOAT(Temperature) + PI);
Index := INTEGER(Size) + INTEGER(Temperature) + INTEGER(PI);
Index := INTEGER(Size + PI) + INTEGER(Temperature);

end MixTypes