O documento descreve o projeto de uma linguagem de programação, apresentando suas principais características e recursos. É definido que a linguagem terá como domínio a ciência, com ênfase em manipulação de grandes quantidades de dados numéricos. Detalham-se os tipos primitivos como inteiros, reais, caracteres e lógicos, além de tipos compostos como conjuntos, filas e estruturas. Explana-se sobre variáveis, expressões, controle de fluxo, subprogramas e sistema de tipos.
4. Cap´ıtulo 1
Defini¸c˜ao das diretrizes de uso e
caracter´ısticas principais da linguagem
de programa¸c˜ao
1.1 Dom´ınio de Aplica¸c˜ao
A linguagem proposta ´e baseada em C e FORTRAN. Seu dom´ınio de aplica¸c˜ao ´e cient´ıfico,
o que implica que al´em das estruturas de dados simples, ela tamb´em dispor´a de estruturas
que possibilitam a manipula¸c˜ao de grandes quantidades de dados e de alta precis˜ao num´erica
para as computa¸c˜oes com n´umeros reais.
A linguagem ter´a como p´ublico alvo matem´aticos e cientistas, que precisam trabalhar
com n´umeros muito grandes, altas precis˜oes e boas estruturas de controle.
1.2 Caracter´ısticas da linguagem
H´a algumas caracter´ısticas que determinam o qu˜ao boa ´e uma linguagem de programa¸c˜ao,
tais como:
Legibilidade: Um dos crit´erios mais importantes. Os programas desenvolvidos numa
determinada linguagem de programa¸c˜ao devem ser f´aceis de serem lidos e compreen-
didos. A linguagem a ser especificada ´e simples e disp˜oe de um pequeno n´umero de
componentes b´asicos, o que garante a simplicidade global. Os nomes dos tipos primi-
tivos e das outras palavras reservadas refletem bem suas finalidades tornando a leitura
mais natural.
Confiabilidade: Diz-se que uma linguagem ´e confi´avel se ela se comporta de acordo
com suas especifica¸c˜oes. A verifica¸c˜ao de tipos ´e de extrema importˆancia e nossa
linguagem fornece essa verifica¸c˜ao, o que n˜ao ocorre em linguagens n˜ao-fortemente
tipadas, e.g. Phyton. A linguagem oferece tamb´em a capacidade de tratamento de
exce¸c˜oes em tempo de execu¸c˜ao, o que existe em Java, C++ e C#, ao contr´ario de C.
3
5. CAP´ITULO 1. PRINCIPAIS CARACTER´ISTICAS DA LINGUAGEM 4
A linguagem C disp˜oe de apelidos. Um exemplo de seu uso ´e o union, que reserva a uma
mesma ´area de mem´oria para duas ou mais vari´aveis. Essa ferramente ´e amplamente
considerada perigosa e, portanto, n˜ao ser´a contemplada na linguagem a ser projetada.
Capacidade de escrita: A linguagem ´e de f´acil escrita e ter´a pouca ortogonalidade,
fazendo com que a codifica¸c˜ao de um programa nessa linguagem seja de escrita mais
objetiva. As restri¸c˜oes na combina¸c˜ao dos tipos de dados e estruturas b´asicas devido `a
pouca ortogonalidade evitar´a problemas de incompatibilidade de tipos que s˜ao dif´ıceis
de serem percebidas tanto pelo programadores quanto pelo compilador.
Assim como em Java, C e C++, a linguagem proposta ´e Case Sensitive1
. C n˜ao disp˜oe
de algumas estruturas de dados mais complexas como, por exemplo, conjuntos, listas, pilhas
e filas enquanto a nossa oferece estes tipos como primitivos al´em dos dispon´ıveis em C.
Outra novidade ´e o tipo primitivo bigInt que comporta um n´umero inteiro bem maior que
o convencional.
O fator que mais influencia na eficiˆencia dos programas escritos na linguagem proposta ´e a
flexibilidade de baixo n´ıvel herdada de C. E os fatores que mais influenciam na produtividade
do programador s˜ao a legibilidade, a capacidade de escrita e forma como o erro se apresenta
a ele durante os testes, permitindo a corre¸c˜ao mais r´apida dos erros.
Levando em conta o dom´ınio de aplica¸c˜ao da nossa linguagem, foi escolhido o sistema
de implementa¸c˜ao por compila¸c˜ao. A raz˜ao disso ´e que este sistema confere uma maior
eficiˆencia aos programas, requisito esse de alta relevˆancia no dom´ınio cient´ıfico em fun¸c˜ao da
necessidade de realizar computa¸c˜oes de alto custo.
Para que nossa linguagem atinja o sucesso e justifique sua cria¸c˜ao, ´e necess´aria uma
an´alise cuidadosa das necessidades de seu dom´ınio de aplica¸c˜ao. As linguagens COBOL e
FORTRAN, por exemplo, atendem suficientemente bem as necessidades de seus respectivos
dom´ınios. As linguagens posteriores n˜ao trouxeram modifica¸c˜oes realmente relevantes aos
recursos que estas j´a ofereciam. Deve-se tentar suprir todas estas necessidades, tentado fazer
um balanceamento entre os pontos fracos e fortes da linguagem proposta.
1
Case sensitive, do inglˆes “caixa alta”, significa que o sistema diferencia letras mai´usculas e min´usculas.
Por exemplo, a palavra “vari´avel” ´e diferente de “Vari´avel” ou “VARI´AVEL”, mesmo tendo a mesma escrita.
6. Cap´ıtulo 2
Valores, tipos e express˜oes
2.1 Tipos Primitivos
Tipos primitivos s˜ao tipos que n˜ao s˜ao derivados de nenhum outro; s˜ao, de certa forma,
estruturas atˆomicas da linguagem. Abaixo listamos os tipos primitivos oferecidos pela lin-
guagem, e na sess˜ao 2.5 os detalharemos.
char - caractere
short - inteiro curto
int representando o tipo inteiro
long - inteiro de faixas maiores (dobro do int)
bigInt - inteiro de maior faixa que long
bool
float - ponto flutuante
double - precis˜ao dupla para ponte flutuante
string - cadeia de caracteres
unsigned int - inteiro n˜ao sinalizado
unsigned long - long n˜ao sinalizado
unsigned bigInt - bigInt n˜ao sinalizado
unsigned short - short n˜ao sinalizado
unsigned float - float n˜ao sinalizado
unsigned double - double n˜ao sinalizado
5
7. CAP´ITULO 2. VALORES, TIPOS E EXPRESS ˜OES 6
complex - representando um n´umero complexo
< tipo > * - guarda o endere¸co de uma posi¸c˜ao de mem´oria;
2.2 Tipos Compostos
Tipos compostos s˜ao aqueles constru´ıdos a partir de tipos primitivos ou at´e mesmo de
outros tipos compostos. Como exemplo de tipos compostos na nossa linguagem temos:
Conjunto (set);
Fila (queue);
Pilha (stack);
Lista (list);
Vetor;
Struct;
Al´em desses, vale destacar um tipo composto bastante peculiar, que ´e o tipo recursivo.
Nessa categoria, se encaixam aqueles tipos que s˜ao compostos por elementos de mesmo tipo.
Um tipo recursivo cl´assico ´e a lista, onde cada elemento vai referenciar um pr´oximo elemento
da mesma natureza. Isso ´e poss´ıvel com a utiliza¸c˜ao de ponteiros. ´Arvores bin´arias e Grafos
tamb´em seguem a mesma linha de racioc´ınio, mas esses ´ultimos n˜ao s˜ao suportados pela
linguagem.
A constru¸c˜ao de um tipo recursivo ´e feita da seguinte forma, segundo a forma BNF:
<declara¸c~ao_tipo_recursivo> -> struct <nome_do_tipo> {
<conjunto_de_declara¸c~oes>
<nome_do_tipo> * <nome_do_atributo>;
};
Exemplo de uso:
struct ListaT{
int info;
ListaT * prox;
};
C´odigo 1: Estrutura de lista recursiva
No c´odigo 2.2 acima temos uma estrutura de lista recursiva. Neste caso, uma lista ´e
composta por um campo info, onde podemos armazenar e recuperar um dado do tipo inteiro,
e uma lista do mesmo tipo, ou seja, o interior da lista cont´em outra lista, que por sua vez
tem outra, e assim por diante, e dessa forma conseguimos fazer constru¸c˜oes recursivas na
linguagem.
8. CAP´ITULO 2. VALORES, TIPOS E EXPRESS ˜OES 7
2.3 Sistema de tipos
A linguagem proposta ser´a fortemente tipada. Diferentemente de C e C++, que n˜ao
fazem a verifica¸c˜ao de tipos de parˆametros de uma fun¸c˜ao. Destarte, n˜ao haver´a convers˜oes
impl´ıcitas de tipo (coer¸c˜ao). Quanto a compatibilidade, usaremos a combina¸c˜ao de dois
m´etodos: a compatibilidade de nomes e estruturas. Escolhemos esta abordagem por acharmos
mais segura e pr´atica, al´em de mais eficiente, pois, no melhor caso, n˜ao precisaremos comparar
as estruturas das vari´aveis, e no pior s´o verificamos os nomes.
Esse assunto ´e abordado de forma mais detalhada no cap´ıtulo 6.
2.4 Tipos definidos pelo usu´ario
Muitas vezes o usu´ario deseja criar seus pr´oprios tipos, e isso ´e poss´ıvel usando a primitiva
struct. ´E poss´ıvel, ainda, a defini¸c˜ao de estruturas gen´ericas, nas quais possamos declarar
uma vari´avel ora de um tipo, ora de outra, sem a necessidade de implementar mais de um
estrutura de tipo (ver exemplo do c´odigo 2.4 e 2.4).
template <typedef struct T>
typedef struct Par {
T a, b;
};
template <typedef struct T>
function getMax (Par <T> p ) : T {
return p.a > p.b ? p.a : p.b;
}
C´odigo 2: Defini¸c˜ao de tipos e fun¸c˜oes gen´ericas atrav´es do uso do templates
2.5 Representa¸c˜ao dos valores de cada tipo
Nesta sess˜ao apresentamos cada tipo e em seguida um exemplo do uso.
char: ´E representado internamente por 1 byte, ou seja, a faixa de valores que esse tipo
cobre ´e −128 a 127, e quando n˜ao sinalizado (unsigned char) compreende uma faixa de 0 a
255. Para a representa¸c˜ao de cada caractere usamos a tabela ASCII.
char c = ’a’;
unsigned d = ’b’;
short: ´E representado na base bin´aria por 16 bits = 2 bytes. Usamos a representa¸c˜ao de
complemento de 2 para permitirmos a representa¸c˜ao de n´umeros negativos. O intervalo de
cobertura desse ´e de −32768 a 32767, para o unsinged short temos de 0 a 65535.
9. CAP´ITULO 2. VALORES, TIPOS E EXPRESS ˜OES 8
import io;
import Par;
function main(): int {
Par<int> pi;
pi.a = 2;
pi.b = 7;
Par <float> pf;
pf.a = 2.5;
pf.b = 3.1415;
write("%dn", getMax(pi));
write("%dn", getMax(pf));
return 0;
}
C´odigo 3: Uso do tipo gen´erico Par para float
short a;
unsigned short b;
int: ´E representado da mesma forma que o short, mas compreende uma faixa maior que o
mesmo (4 bytes). O tipo int sinalizado compreende uma faixa de −2147483648 a 2147483647,
j´a o n˜ao sinalizado 0 a 4294967295.
int n = 50;
unsigned int m = 8;
long: Sua representa¸c˜ao ´e da mesma forma que o tipo int, por´em com uma faixa de
valores superior aos inteiros anteriores: 8 bytes. A faixa de representa¸c˜ao do tipo inteiro
longo ´e de −263
`a 263
− 1. 0 n˜ao sinalizado vai de 0 `a 264
− 1.
long l = 5000;
unsigned long l1 = 6000;
bigInt: Da mesma forma, o bigInt ´e representado de maneira similar, mas com o dobro
do tamanho do long: 16 bytes. Assim, a representa¸c˜ao do bigInt ´e a seguinte −2127
`a 2127
−1,
e n˜ao sinalizado vai de 0 `a 2128
− 1.
bigInt bi = 10000000;
unsigned bigInt b = 100000000;
bool: Pode assumir dois valores: true ou false e ´e representado por 1 byte. O true ser´a
representado por todos os 8 bits do byte em 1, caso contr´ario todos em 0.
10. CAP´ITULO 2. VALORES, TIPOS E EXPRESS ˜OES 9
bool a = true;
bool b = false;
float: O ponto flutuante ´e representado por 32 bits, sendo 1 reservado para o sinal, 8 de
expoente e 23 para a parte fracion´aria. Isso equivale a uma faixa de valores de ±3.4e±38.
float a = .2;
float b = 2.5;
double: O double ´e representado de forma semelhante ao float, mas com o dobro de
precis˜ao (8 bytes). A faixa de valores ´e de ±1.7e ± 308.
double a = 5.65
complex: O tipo complexo utilizar´a dois n´umeros reais, um para representar sua parte
real e o outro a parte imagin´aria e ter´a 8bytes e a faixa ser´a de −264
`a 264
− 1.
complex c(5.5, 7.8);
string: O tipo string ser´a uma lista encadeada de caracteres. Para esta lista, as opera¸c˜oes
de concatena¸c˜ao e convers˜ao para uma cadeia de caractere estar˜ao fundamentadas na lingua-
gem.
string s = "cplusminus.com";
vetor: Como um vetor aponta para um bloco de mem´oria, sua implementa¸c˜ao se d´a por
um ponteiro para apontar ao primeiro bloco de mem´oria alocado.
int v[10];
int vet[3] = {1, 2, 3};
int mat[2][2] = {1, 0;
0, 1};
set: Os conjuntos ser˜ao armazenados com uma cadeia de bits internamente. Quando em
1, o bit significa que o elemento est´a presente e, caso contr´ario, n˜ao estar´a. Consideremos o
seguinte conjunto:
[ a , . . . , p ]
Podemos utilizar 16 bits para representar esse conjunto. Desta forma, o conjunto [ a , b , f , j , p ]
seria representado como a seguir:
1100010001000001
Um conjunto ter´a um tamanho de 4 bytes, ou seja, ser´a poss´ıvel representar um conjunto
com no m´aximo 32 elementos.
11. CAP´ITULO 2. VALORES, TIPOS E EXPRESS ˜OES 10
import utility.set;
...
set <int> A;
set < set <int> > B;
B.add(A);
/**
* B ⊃ A
*/
list: A lista ´e definida recursivamente - uma lista ´e composta de outra lista.
import utility.list;
...
list <int> a;
list < list <int> > b;
ponteiros: Ponteiros e referˆencias s˜ao geralmente representados por valores ´unicos, arma-
zenados em c´elulas de mem´oria de 2 ou 4 bytes, dependendo da arquitetura do computador.
Na maioria dos computadores, os microprocessadores s˜ao baseados na arquitetura intel; as-
sim, os ponteiros e as referˆencias s˜ao representadas como pares de palavras de 16 bits, uma
para cada uma das duas partes de um endere¸co (segmento e deslocamento(offset)).
int * prt;
char * pt;
short * p;
2.5.1 Designadores dos tipos primitivos
Tabela 2.1: Designadores dos tipos primitivos da linguagem
Nome Descri¸c˜ao Tamanho Faixa
char Caractere 1byte −128 a 127 e unsigned: 0 a 255
short Inteiro curto 2bytes −32768 a 32767 e unsigned: 0 `a
65535
int Inteiro 4bytes −2147483648 a 2147483647 e un-
signed: 0 `a 4294967295
long Inteiro longo 8bytes −263
`a 263
− 1 e unsigned: 0 `a
264
− 1
bigInt Inteiro muito longo 16bytes −2127
`a 2127
− 1 e unsigned: 0 `a
2128
− 1
bool Valor booleano. Pode assumir dois va-
lores: true ou false
1byte true ou false
float Pronto flutuante 4bytes ±3.4e ± 38
double Precis˜ao dupla do ponto flutuante 8bytes ±1.7e ± 308
complex N´umeros complexos 8bytes −264
`a 264
− 1
12. CAP´ITULO 2. VALORES, TIPOS E EXPRESS ˜OES 11
2.5.2 Forma BNF de declara¸c˜ao de um tipo na Linguagem
<tipo_primitivo> -> char | short | int | long | bitInt | bool | float | double | complex | string
<tipo_composto> -> vetor | set | list
<tipo_vetor> -> <tipo_primitivo> | set
<tipo> -> <tipo_primitivo> | <tipo_composto>
<definicao_tamanho> -> [<tamanho>]
| <definicao_tamanho> <definicao_tamanho>
<declaracao_vetor> -> <tipo_vetor> <nome_variavel> <definicao_tamanho>;
<tipo_list> -> list < <tipo> >
<declaracao_list> -> <tipo_list> <nome_variavel>;
| list < <tipo_list> > <nome_variavel>;
<declaracao_set> -> <declaracao_set> <nome_variavel>;
| set < <declacao_set> >;
| set < <tipo> >
2.6 Express˜oes da linguagem
2.6.1 Operadores Aritm´eticos
Os cinco operadores se referem as seguintes, respectivas, opera¸c˜oes: adi¸c˜ao ( + ), sub-
tra¸c˜ao ( - ), multiplica¸c˜ao ( * ), divis˜ao ( / ) e mod ou resto ( % ). Estes operadores atuam
sobre dois operandos, por este motivos, s˜ao classificados como bin´arios.
2.6.2 Incremento e decremento
Os operadores (++) e (−−) respectivamente incremento, aumenta o valor da vari´avel em
1, e decremento, diminui o valor da vari´avel em 1.
2.6.3 Operadores Relacionais
Estes operadores representam as seguintes rela¸c˜oes:
Igualdade ( == ): A igualdade ´e uma opera¸c˜ao booleana que retorna o valor true,
caso dois operadores sejam considerados iguais, segundo um crit´erio de verifica¸c˜ao, e
false, caso contr´ario.
Diferen¸ca ( ! = ): A diferen¸ca retorna false, nos casos em que a igualdade retornaria
true, e retorna true, nos casos contr´arios.
Maior ( > ): A compara¸c˜ao maior retorna true, caso o primeiro operando seja maior
que o segundo, e false, quando o segundo operando ´e maior que o primeiro.
Menor (< ): A compara¸c˜ao menor retorna true, quando o primeiro operando ´e menor
que o segundo, e false, quando o contr´ario acontece.
13. CAP´ITULO 2. VALORES, TIPOS E EXPRESS ˜OES 12
Maior ou igual (>=): A compara¸c˜ao maior ou igual ´e a jun¸c˜ao de duas compara¸c˜oes,
esta compara¸c˜ao retorna true, sempre que pelo menos uma das duas return true, e false,
caso contr´arios.
Menor ou igual ( <= ): A compara¸c˜ao menor ou igual tem o mesmo funcionamento
da compara¸c˜ao maior ou igual, retornando true, quando o primeiro operando ´e menor
ou igual ao segundo.
2.6.4 Operadores l´ogicos
A seguir uma breve vincula¸c˜ao dos operadores com seus respectivos significados booleanos.
O operador “ ! ” representa a fun¸c˜ao l´ogica NOT.
O operador “ && ” representa a fun¸c˜ao l´ogica AND.
Por fim, o operador “||” representa a fun¸c˜ao l´ogica OR.
2.6.5 Operadores bit-a-bit
Estes operadores s˜ao caracterizados por fazer opera¸c˜oes com bit e bytes (palavras de
8 bit). Podemos reconhecer alguns operadores l´ogicos ou booleanos como o operador “ &
”, representando o AND bit a bit, o operador “|”, representando o OR bit a bit, o “ ˆ ”,
representando o XOR e por fim o “ ˜ ” que representa o NOT bit a bit. Os operadores de
deslocamento “>>” e “<<” de bits a direita e a esquerda respectivamente.
14. Cap´ıtulo 3
Vari´aveis e comandos
3.1 Modelo de armazenamento de valores
Visando permitir uma maior flexibilidade `a escrita dos programadores, ser˜ao permitidos
os seguintes modelos de armazenamento de valores na mem´oria:
(a) Est´atico: Em v´arias situa¸c˜oes, vari´aveis globalmente acess´ıveis s˜ao de grande utilidade.
O modelo de vincula¸c˜ao est´atica permite isso da forma mais eficiente (endere¸camento
direto). Este modelo v´ıncula `a vari´avel um determinado endere¸co de armazenamento
antes que o programa inicia, o qual permanece at´e o fim do programa.
(b) Dinˆamico na pilha: As vari´aveis dinˆamicas na pilha s˜ao aquelas em que o tipo ´e
vinculado estaticamente, mas a vincula¸c˜ao de armazenamento ´e feita no momento em
que o programa atinge a declara¸c˜ao da vari´avel, em tempo de execu¸c˜ao. Esse modelo ´e o
requisito b´asico para permitir subprogramas recursivos. Devido `a sobretaxa de aloca¸c˜ao
e desaloca¸c˜ao em tempo de execu¸c˜ao, este modelo perde em desempenho.
(c) Dinˆamico no heap expl´ıcitas: O uso de vari´aveis dinˆamicas no heap permite ao
programador interagir em mais baixo n´ıvel com blocos de mem´oria. A aloca¸c˜ao ´e feita
atrav´es de operadores ou fun¸c˜oes definidos(as) na linguagem e a manipula¸c˜ao ´e feita
atrav´es de ponteiros ou referˆencias. Esse modelo se aplica perfeitamente na defini¸c˜ao
de estruturas dinˆamicas, mas a manuten¸c˜ao dos blocos envolve riscos, que passam a ser
responsabilidade do programador.
3.2 Uso de vari´aveis na Linguagem
Na nossa linguagem, ao se declarar uma vari´avel, ´e vinculado um valor aleat´orio a ela (lixo
da mem´oria). Isso pode ser evitado realizando-se a inicializa¸c˜ao da vari´avel no momento de
sua declara¸c˜ao, tornando responsabilidade do programador a verifica¸c˜ao dessa necessidade.
Alem disso, ´e poss´ıvel vincular valores dinamicamente `a vari´avel atrav´es de opera¸c˜oes de
atribui¸c˜ao ou de leitura. Quanto `a atribui¸c˜ao de vari´aveis compostas, no caso dos arrays,
13
15. CAP´ITULO 3. VARI ´AVEIS E COMANDOS 14
dever´a ser especificado o ´ındice da posi¸c˜ao do array a ter seu valor atualizado. De modo que,
esse ´ındice denota o deslocamento do ponteiro na mem´oria em rela¸c˜ao ao endere¸co base do
array. Dessa forma, o programador tem uma forma mais intuitiva de manipular os arrays.
No caso dos tipos definidos atrav´es de structs, n˜ao ser´a poss´ıvel fazer uma atribui¸c˜ao direta
da forma < tipo composto >=< outro tipo composto >. Para este fim, a atribui¸c˜ao deve ser
feita campo `a campo.
Na forma BNF logo abaixo, apresentamos a forma geral da declara¸c˜ao de uma vari´avel,
e em seguida, alguns exemplos que segue a BNF descrita.
<declaracao_var> -> <tipo> <nome_var>;
| <tipo> <nome_var> = <valor>;
| <tipo> <lista_nomes_vars>;
<lista_nomes_vars> -> <nome>
| <nome>, <lista_nomes_vars>
int a, b, c, d;
string str = "CPlusMinus";
float f = .6;
3.3 Caracter´ısticas dos arrays da linguagem
Os elementos do array s˜ao referˆenciados por meio de seu endere¸co base, e de um ou mais
´ındices do tipo inteiro. A sintaxe da referˆencia consiste, basicamente, no nome da estrutura
seguido de uma lista de ´ındices, cada um colocado entre colchetes. Para evitar erros de faixa,
´e vinculado ao array uma faixa de valores (com limite inferior no zero) de modo a evitar o
acesso a posi¸c˜oes inv´alidas. Os arrays ser˜ao categorizados como:
(a) Est´atico: a faixa de valores do ´ındice e a aloca¸c˜ao de armazenamento s˜ao vinculados
estaticamente antes da execu¸c˜ao do programa (eficiente).
(b) Fixo dinˆamico na pilha: a faixa de valores do ´ındice ´e vinculada estaticamente, mas
a aloca¸c˜ao ´e feita no momento da elabora¸c˜ao da declara¸c˜ao (melhor uso do espa¸co de
armazenamento).
(c) Dinˆamico na pilha: a faixa de valores do ´ındice e o espa¸co de endere¸camento s˜ao
vinculados dinamicamente. Ocorrendo a vincula¸c˜ao, sua faixa de valores do ´ındice e seu
armazenamento alocado permanecem os mesmos at´e o fim de seu tempo de vida (maior
flexibilidade em rela¸c˜ao ao array fixo dinˆamico na pilha)
(d) Dinˆamico no heap: semelhante ao dinˆamico na pilha, exceto pela possibilidade da faixa
de valores do ´ındice e seu armazenamento alocado serem modificados qualquer n´umero
de vezes (ainda mais flex´ıvel).
Para fornecer maior poder de express˜ao ao programador, ´e permitido a defini¸c˜ao de uma
lista de ´ındices com tamanho sem imposi¸c˜ao de limites de tamanho para arrays est´aticos.
16. CAP´ITULO 3. VARI ´AVEIS E COMANDOS 15
Por outro lado, para evitar maiores problemas com eficiˆencia e manipula¸c˜ao de ponteiros, ´e
limitado para sete o tamanho da lista de ´ındices para arrays dinˆamicos de heap. Por ´ultimo
mas n˜ao menos importante, ´e permitida a inicializa¸c˜ao dos elementos do array no momento
de sua declara¸c˜ao se ele for est´atico, e, para facilitar a manipula¸c˜ao e melhorar o desempenho
de referenciamento, os arrays poder˜ao ser subdivididos em fatias, o que possibilita o uso de
menos express˜oes de ´ındice do que se fosse referˆenciado o array inteiro.
3.4 Escopo e tempo de vida das vari´aveis
A vari´avel global (est´atica) quando declarada estar´a acess´ıvel desde o in´ıcio do programa
at´e o final do mesmo. A vincula¸c˜ao dessa vari´avel a um espa¸co de mem´oria ´e feita antes da
execu¸c˜ao do programa. Uma vari´avel local estar´a acess´ıvel somente durante a execu¸c˜ao da
fun¸c˜ao onde ela foi declarada. Nesse caso, a v´ari´avel ´e vinculada a um espa¸co de mem´oria em
tempo de execu¸c˜ao, mas logo quando essa fun¸c˜ao ´e finalizada, esse espa¸co de mem´oria que
foi alocado para a vari´avel ´e desalocado. ´E importante n˜ao confundirmos a acessibilidade de
uma vari´avel com o seu escopo. Em geral, ´e muito comum pensarmos que o escopo de uma
vari´avel est´atica ´e somente durante a execu¸c˜ao do programa inteiro, mas isso n˜ao ´e verdade;
podemos declarar uma vari´avel est´atica dentro de uma fun¸c˜ao com o aux´ılio do modificador
static, e a mesma n˜ao estar acess´ıvel ao longo da execu¸c˜ao do programa inteiro. O que
acontece ´e que a vari´avel ´e est´atica e, mesmo ap´os o t´ermino da fun¸c˜ao, ela continua alocada,
mas n˜ao permanece acess´ıvel.
3.5 Comandos da linguagem
Nessa sess˜ao apresentaremos para cada comando sua forma BNF, e em seguida, um
exemplo de uso do comando em quest˜ao.
3.5.1 Entrada e Sa´ıda
3.5.1.1 Forma Normal
<cod_leitura> -> "%i"|"%d"|"%s"|"%f"|"%lf"|"%bi"|"%ld"
<cod_escrita> -> <string> | <cod_leitura>
|<string > <cod_escrita>
|<cod_leitura> <cod_escrita>
<lista_enderecos>-> &<variavel> | &<variavel>, <lista_enderecos>
<lista_vars> -> <variavel> | <variavel>, <lista_vars>
<comando_read> -> read(<cod_leitura>, <lista_enderecos>);
<comando_write> -> write (<cod_escrita>, <lista_vars>)
import io;
function main(): int {
int a, b;
read("%i %i", &a, &b);
int soma = a + b;
17. CAP´ITULO 3. VARI ´AVEIS E COMANDOS 16
write("%in", soma);
return 0;
}
3.5.2 Estruturas de Controle
3.5.2.1 Condicional
3.5.2.1.1 Comando if
Forma Normal
<comando_if> -> if ( <condicao> ) {
<comando>
} else if (<condicao2>) {
<lista_de_comandos>
} else {
<lista_de_comandos>
}
| if ( <condicao> ) {
<lista_de_comandos>
} else {
<lista_de_comandos>
}
| if ( <condicao> ) {
<lista_de_comandos>
}
| if ( <condicao> )
<comando>
import io;
function main(): int {
int n;
read("%d", &n);
if (n > 0) {
write("%d eh um numero positivon", n);
} else if(n < 0){
write("%d eh um numero negativon", n);
} else {
write("%d eh zeron", n);
}
return 0;
}
3.5.2.1.2 Comando switch-case
Forma Normal
<comando_switch-case> -> switch( <var_escolha> ) {
case <op1>:
<lista_de_comandos>
break;
case <op2>:
<lista_de_comandos>
18. CAP´ITULO 3. VARI ´AVEIS E COMANDOS 17
break;
...
default:
<lista_de_comandos>
break;
}
import io;
function main(): int {
string op;
read("%s", &op);
switch(op) {
case "um":
write("1n");
break;
case "dois":
write("2n");
break;
case "tres":
write("3n");
break;
default:
write("qwertyn");
break;
}
return 0;
}
3.5.2.2 Repeti¸c˜ao
3.5.2.2.1 Comando for
Forma Normal
<comando_for> -> for (<inicializacao>; <condicao_de_parada>; <incremento/decremento>) {
<lista_de_comandos>
<comando_for>
}
| for (<inicializacao>; <condicao_de_parada>; <incremento/decremento>)
<comando>
import io;
function main(): int {
int n;
read("%d", &n);
for (int i = 0; i < n; i++) {
write("%d ", 2 * (i + 1));
}
write("n");
return 0;
}
19. CAP´ITULO 3. VARI ´AVEIS E COMANDOS 18
3.5.2.3 Comando for-each
3.5.2.3.1 Forma Normal
<comando_foreach> -> foreach (<tipo> <nome_var> : <nome_list/nome_vetor>) {
<lista_de_comandos>
<comando_foreach>
}
| foreach (<tipo> <nome_var> : <nome_list/nome_vetor>)
<comando>
import io;
import utility.list;
function main(): int {
list <int> lst;
lst.add(0);
lst.add(2);
lst.add(3);
lst.add(4);
foreach (int a : lst) {
write("%d ", a);
}
write("n");
return 0;
}
3.5.2.4 Comando while
3.5.2.4.1 Forma Normal
<comando_while> -> while (<condicao_de_parada>) {
<lista_de_comandos>
<comando_while>
}
import io;
function main(): int {
int n;
read("%d", &n);
int i = 0;
while(i < n) {
++i;
write("%d ", 2 * i);
}
write("n");
return 0;
}
3.5.2.5 Comando do-while
3.5.2.5.1 Forma Normal
20. CAP´ITULO 3. VARI ´AVEIS E COMANDOS 19
<comando_do-while> -> do {
<lista_de_comandos>
} while ( <condicao_de_parada> );
import io;
function main(): int {
int i = 5;
do {
write("%d ", i);
i--;
} while (i != 0);
write("n");
return 0;
}
3.5.3 Comandos alternadores de fluxo (jumps)
Os tipos de jumps (alteradores de fluxo) da linguagem s˜ao descritos na subse¸c˜oes seguintes.
3.5.3.1 Comando break
No c´odigo 3.5.3.1 abaixo, que ilustra o uso do break, as linhas 4 e 5 sempre ser˜ao exe-
cutadas at´e que i = 10, quando a condi¸c˜ao da linha 5 ´e satisfeita e as linhas 6 e 7 ser˜ao
executadas. A sa´ıda do programa ser´a os n´umeros de 1 a 10 e uma quebra de linha. Na linha
7 o comando break ´e executado e o fluxo de execu¸c˜ao ´e desviado para a pr´oxima instru¸c˜ao,
fazendo com que o programa n˜ao entre no la¸co novamente.
int i = 0;
while(true) {
i++;
write("%d ", i);
if (i == 10) {
write("n");
break;
}
}
C´odigo 4: Uso do break
3.5.3.2 Comando continue
No c´odigo 3.5.3.2, que exibe um trecho de c´odigo com o emprego do comando continue,
enquanto i < 10 a linha 5 ser´a executada. O comando continue faz com que o programa
volte `a condi¸c˜ao do loop que o engloba, (linha 1) ignorando as linhas abaixo dele (linhas 6 e
7), ou seja, o programa entrar´a em loop mais uma vez. Quando i = 10 o programa escreve
uma quebra de linha e encerra o loop com o comando break.
21. CAP´ITULO 3. VARI ´AVEIS E COMANDOS 20
while(true) {
i++;
write("%i ", i);
if (i < 10)
continue;
write("n");
break;
}
C´odigo 5: Trecho de c´odigo com o uso do continue.
3.5.4 Primitivas de sa´ıdas dos blocos (escapes)
3.5.4.1 Comando return
O comando return serve para retornar o que uma fun¸c˜ao se propˆos a computar. Como
exemplo temos o trecho de c´odigo do C´odigo 3.5.4.1, onde ´e definida uma fun¸c˜ao par(i:int):
bool que retorna true se i for par e false caso contr´ario. Se i for par a linha 3 ´e executada e
o subprograma ´e finalizado. Caso contr´ario, a linha 4 ´e executada e o programa ´e finalizado.
Veja que uma fun¸c˜ao pode somente retornar um valor por execu¸c˜ao.
function par(i: int): bool {
if (i % 2 == 0)
return true;
return false;
}
C´odigo 6: Trecho de c´odigo com o uso do return.
3.5.4.2 Comando exit
O comando exit ´e usado para finalizar o programa como um todo. Vejamos o c´odigo
3.5.4.2, a sa´ıda ser´a:
msg de teste
Saindo ...
Na fun¸c˜ao f(), na linha 5 temos um comando exit(0), pontanto tudo que est´a ap´os a
chamada dessa fun¸c˜ao na main() n˜ao ser´a executado, pois o programa finaliza sua execu¸c˜ao
quando o comando exit(0) ´e usado. Se comentarmos a linha 11 do programa sua sa´ıda seria
a seguinte:
msg de teste
Saindo ...
msg de teste 2
22. CAP´ITULO 3. VARI ´AVEIS E COMANDOS 21
import io;
procedure f() {
write("Saindo ... n");
exit(0);
}
function main(): int {
write("msg de testen");
f();
write("msg de teste 2n");
return 0;
}
C´odigo 7: Trecho de c´odigo com o uso do exit.
3.5.5 Tratamento de Exce¸c˜oes
Nossa linguagem usar´a a estrutura de try-catch como forma de tratamento de exce¸c˜oes,
de forma semelhante a C++. As exce¸c˜oes ser˜ao definidas como os tipos presentes na lingua-
gem, podendo, assim, o pr´oprio usu´ario criar suas exce¸c˜oes. A seguir (c´odigo 3.5.5), tem-se
um exemplo de um c´odigo utilizando tratamento de exce¸c˜oes.
procedure g(){
int x;
write("Digite um numero positivo: n");
try{
read(x);
if(x < 0){
throw x;
}
}
catch(int erro){
write("Erro! numero negativo: %i n", erro);
}
}
C´odigo 8: Trecho de c´odigo com o uso do try-catch.
3.6 Aloca¸c˜ao de mem´oria das veri´aveis compostas
Para as vari´aveis compostas homogˆeneas, como os vetores, sua aloca¸c˜ao ser´a em blocos de
mem´oria sequenciais, e seu escopo ser´a como o de uma vari´avel comum. Seu referenciamento
ser´a em rela¸c˜ao ao primeiro bloco de mem´oria alocado por ele. Ou seja, o referenciamento
da pr´oxima vari´avel da sequˆencia ser´a apenas o incremento unit´ario da posi¸c˜ao da mem´oria.
Quanto a passagem destas vari´aveis por parˆametro, ser´a feito por referˆencia. As vari´aveis
compostas heterogˆeneas (registros) tamb´em ser˜ao alocadas sequencialmente, mas o acesso
23. CAP´ITULO 3. VARI ´AVEIS E COMANDOS 22
aos campos se d´a atrav´es de um deslocamento, que ´e guardado junto ao campo, j´a que o
tamanho de cada bloco n˜ao ´e necessariamente igual. Seu referenciamento ´e feito como o de
uma vari´avel comum, ou seja, copiando seus atributos. O mesmo vale para seu escopo - igual
ao de uma vari´avel comum.
24. Cap´ıtulo 4
Vincula¸c˜oes e regras de escopo
4.1 Bindings da linguagem
Quanto a vincula¸c˜ao de tipos, que esta associada a declara¸c˜ao de vari´aveis, ser´a, sem-
pre que poss´ıvel, feita est´aticamente devido ao sistema de implementa¸c˜ao escolhido, a com-
pila¸c˜ao, e a maior eficiˆencia dessa abordagem. Sendo assim, a vincula¸c˜ao de tipos ser´a
em tempo de compila¸c˜ao. Quanto a vincula¸c˜ao de armazenamento, temos como assunto,
vari´aveis est´aticas,stack-dinˆamicas e heap-dinˆamicas. As vari´aveis est´aticas tem seu espa¸co
de armazenamento vinculado em tempo de compila¸c˜ao. E as vari´aveis stack-dinˆamicas e
heap-dinˆamicas tem seus epa¸cos de armazenamento vinculados em tempo de execu¸c˜ao mas,
n˜ao da mesma forma. Vari´aveis stack-dinˆamicas s˜ao armazenadas temporariamente em uma
pilha e sua retirada ´e feita pela linguagem, dependendo de seu ambiente de referenciamento.
Vari´aveis heap-dinˆamica s˜ao armazenadas numa heap e sua retirada ´e responsabilidade do
programador. As caracter´ısticas mais b´asicas e elementares da linguagem, que est˜ao sendo
definidas durante esta disciplina, est˜ao sendo vinculadas em tempo de projeto.
4.2 Aliasing
Na linguagem haver´a dois casos do uso de aliasing, os quais s˜ao explicados abaixo. O
primeiro caso, ´e mais evidente em linguagens que d˜ao suporte a aliasing, ´e o uso de referˆencia.
Por exemplo, quando usamos passagem de parˆametros por referˆencia, passam a existir dois
nomes vinculados ao mesmo espa¸co de mem´oria, o que caracteriza o aliasing. O uso de
ponteiros pode ser tamb´em considerado aliasing em alguns casos. Caracterizamos seu uso
como aliasing quando dois ponteiros est˜ao referenciando um mesmo bloco de mem´oria. Este
tipo de apelido ´e indireto, pois os ponteiros em si n˜ao s˜ao aliasing, mas sua referˆencia o ´e.
4.3 Estrutura de blocos
Para definirmos os escopos das vari´aveis usamos blocos, que s˜ao, por sua vez, definidos
pelas chaves, ““ e “”. Segue abaixo a forma BNF.
23
25. CAP´ITULO 4. VINCULAC¸ ˜OES E REGRAS DE ESCOPO 24
<comando_da_linguagem> -> <comandos_basicos>
| <comandos_basicos> <comando_da_linguagem>
| <blocos>
| <blocos> <comando_da_linguagem>
<bloco> -> <comando_de_fluxo_ctrl> {
<comandos_da_linguagem>
}
| <decl_func> {
<comandos_da_linguagem>
}
Podemos usar a forma BNF acima descrita para definir blocos aninhados de forma re-
cursiva, visto que comando de fluxo de controle e declara¸c˜oes de fun¸c˜oes s˜ao comandos da
linguagem.
4.4 Regras de visibilidade e resolu¸c˜ao de escopo
Quanto ao escopo est´atico, as vari´aveis declaradas nos blocos ancestrais ser˜ao vis´ıveis a
todos os blocos internos a eles. J´a o contr´ario n˜ao ocorre: vari´aveis declaradas nos blocos mais
internos n˜ao s˜ao vis´ıveis a seus ancestrais. Quando o compilador encontra um identificador de
uma vari´avel, ´e necess´ario saber a qual vari´avel ele se refere. Para tanto, ´e preciso localizar a
sua instru¸c˜ao de declara¸c˜ao. Essa busca come¸ca no bloco em que foi encontrada a referˆencia `a
vari´avel. Caso a vari´avel n˜ao seja encontrada neste bloco, a busca se segue em seu pai est´atico
(bloco que engloba este bloco), e assim por diante. Caso n˜ao haja mais nenhum ancestral
est´atico para proceder com a busca o compilador ir´a retornar um erro de declara¸c˜ao. Quanto
ao escopo dinˆamico, essa busca se d´a de maneira diferente, uma vez que no escopo est´atico
a busca pela instru¸c˜ao de declara¸c˜ao da vari´avel ´e baseada na declara¸c˜ao dos blocos, e no
dinˆamico ´e baseado na ordem inversa de chamada de fun¸c˜oes. Consideremos a seguinte
situa¸c˜ao: a fun¸c˜ao main fm chama uma func˜ao f2, que por sua vez chama uma fun¸c˜ao
f1. Consideremos tamb´em que o compilador encontrou uma referˆencia a uma vari´avel x em
f1. Dessa forma a instru¸c˜ao de declara¸c˜ao de x ser´a buscada na ordem de chamada dessas
fun¸c˜oes. Primeiro o compilador verifica se a instru¸c˜ao de declara¸c˜ao se encontra na f1, caso
contr´ario, a busca continua em f2, e assim por diante. Caso n˜ao seja encontrada, o compilador
retornar´a um erro de declara¸c˜ao. Analizando essas duas formas de escopo, conclu´ımos que
nossa liguagem deve ser de escopo est´atico.
26. Cap´ıtulo 5
Subprogramas
Subprogramas s˜ao uma das formas de abstra¸c˜ao presente na linguagem, que nos for-
nece abstra¸c˜ao de processo, cujo conceito ´e de suma importˆancia para uma linguagem de
programa¸c˜ao, uma vez que aumenta a legibilidade. Duas categorias de subprogramas s˜ao
procedimentos e fun¸c˜oes, que ser˜ao abordadas na sess˜ao 5.1.
5.1 Procedimentos e fun¸c˜oes
Uma fun¸c˜ao ´e uma rela¸c˜ao que mapeia um dom´ınio de entrada em um dom´ınio de sa´ıda.
Ela recebe parˆametros e retorna um valor.
Um procedimento n˜ao retorna valor e ´e utilizado, normalmente, para executar um bloco
de comandos, e.g. para imprimir um texto. O c´odigo abaixo mostra a forma BNF que definem
a sintaxe das fun¸c˜oes e procedimentos.
Essa abordagem foi adotado pois torna mais evidente as diferen¸cas entre esses conceitos.
Em C/C++, por exemplo, os procedimentos s˜ao fundamentalmente fun¸c˜oes (que retornam o
tipo void), o que dificulta sua diferencia¸c˜ao. Usando essa abordagem torna-se mais natural
a associa¸c˜ao entre procedimentos e comandos e entre fun¸c˜oes e express˜oes.
<lista_de_parametros> -> <tipo> <nome_param>
| <lista_de_parametros>, <tipo> <nome_param>
<declaracao_funcao> -> function <nome_funcao> (<lista_de_parametros>): <tipo_retorno> {
<conjunto_instrucoes>
return <valor>;
}
<declaracao_procedimento> -> procedure <nome_procedimento> (<lista_de_parametros>) {
<conjunto_de_instrucoes>
}
5.2 Parˆametros
Os dados que poder˜ao ser colocados como argumentos em chamadas de fun¸c˜ao s˜ao tanto
de tipos da linguagem, listados e descritos nos problemas anteriores. Tamb´em ser´a poss´ıvel
ter nomes de subprogramas utilizados como parˆametro.
25
27. CAP´ITULO 5. SUBPROGRAMAS 26
Em sistemas de implementa¸c˜ao de linguagens de programa¸c˜ao ´e necess´ario incorporar
m´etodos de resolu¸c˜ao de nomes. As linguagens que utilizam resolu¸c˜ao de escopo est´atica
s˜ao chamadas de linguagens de escopo est´atico. As que utilizam resolu¸c˜ao escopo dinˆamico
s˜ao chamadas de linguagens de escopo dinˆamico. No caso de linguagens que suportam fun¸c˜oes
de alta ordem (aceitam fun¸c˜oes como parˆametros de fun¸c˜oes), temos trˆes formas de resolu¸c˜ao
de nomes. S˜ao elas vincula¸c˜ao rasa, vincula¸c˜ao profunda e ad hoc.
Introduziremos esses conceitos com base na explica¸c˜ao da Figura 5.1.
1. vincula¸c˜ao profunda: Ambiente da defini¸c˜ao do subprograma passado. Quando exe-
cutamos o programa da Figura 5.1, o subpprograma F1 quando ´e chamado, temos uma
express˜ao com a vari´avel A. O compilador busca a declara¸c˜ao de A, onde encontrar´a
informa¸c˜oes de sua vinculac˜ao. Assim, a busca se dar´a no subprograma F1. Caso n˜ao
seja encontrado, a busca continua no bloco que o envolve, onde finalmente a declara¸c˜ao
´e encontrada. Caso a busca n˜ao obtivesse sucesso, um erro de tipo seria lan¸cado. Esse
m´etodo de resoluc˜ao de nome ´e semelhante ao de vincula¸c˜ao est´atica.
2. vincula¸c˜ao rasa: Ambiente de defini¸c˜ao do subprograma que ordena o subprograma
passado. Usando mais um vez, o exemplo da Figura 5.1. Quando chamamos F3, que
chama F2 e por sua vez chama F1. O compilador ir´a fazer uma busca em F1 pelas
informa¸c˜oes de vincula¸c˜ao de A. Como n˜ao ´e encontrado ai, ele ir´a continuar a busca em
F2, e ai achar´a a declara¸c˜ao de A. Caso a busca em F2 falhasse a busca continuaria
em F3, e se mais uma vez n˜ao obtivesse sucesso, o compilador lan¸caria um erro de
declara¸c˜ao. Esse m´etodo se assemelha ao m´etodo de vincula¸c˜ao dinˆamica.
3. Vincula¸c˜ao ad hoc: Ambiente da instru¸c˜ao que passou o subpprograma como parˆametro
real. Por exemplo, quando executamos o c´odigo da Figura 5.1 temos a seguinte situa¸c˜ao:
quando F3 chama F2, passando como parˆametro F1, a declara¸c˜ao do A ´e encontrado
em F3.
Figura 5.1: C´odigo para representa¸c˜ao dos conceitos de vinculac˜ao profunda, rasa e ad hoc.
28. CAP´ITULO 5. SUBPROGRAMAS 27
5.3 Formas de passagem de parˆametros
Dentre as formas de passagem de parˆametro existentes, a nossa linguagem implementar´a
a passagem de parˆametro por valor e a passagem de parˆametro por referˆencia.
Passagem de parˆametro por valor: O valor do parˆametro real ´e usado para inici-
alizar o parˆametro formal correspondente, que, ent˜ao, age como uma vari´avel local no
subprograma.
A passagem por valor ´e normalmente implementada pela transferˆencia de dados reais
(um valor real ´e transmitido fisicamente para o chamador, para o chamado, ou ambos),
mas, ao inv´es disso, pode ser implementada transmitindo-se um caminho de acesso e,
neste caso, a c´elula de mem´oria que cont´em o valor deve estar protegida contra grava¸c˜ao
(read-only).
Na passagem por valor, ´e feita uma c´opia dos valores dos parˆametros reais no espa¸co
de mem´oria reservado para a fun¸c˜ao chamada ou chamadora, ou at´e mesmo fora delas.
A desvantagem deste m´etodo, se forem feitas transferˆencias f´ısicas, est´a no fato que
ser´a necess´ario armazenamento adicional para os parˆametros formais do subprograma
chamado, ou em alguma ´area fora do subprograma chamado ou chamador. Em adi¸c˜ao,
as opera¸c˜oes de armazenamento e transferˆencia podem ser custosas se o parˆametro for
grande, e.g. um vetor longo.
Passagem de parˆametro por referˆencia: Transmite um caminho de acesso, nor-
malmente apenas um endere¸co, para a fun¸c˜ao chamada. Isso possibilita o acesso `a
c´elula de mem´oria que armazena o parˆametro real.
Este m´etodo ´e mais eficiente que o descrito anteriormente, uma vez que n˜ao ´e necess´ario
espa¸co duplicado, nem qualquer atividade de c´opia.
Suas desvantagens s˜ao as seguintes:
– O acesso aos parˆametros se dar˜ao de forma mais lenta, pois mais um n´ıvel de
endere¸camento indireto ´e necess´ario.
– Mudan¸ca inadvertidas e errˆoneas poder˜ao ser feitas no parˆametro real, caso seja
exigido somente uma comunica¸c˜ao unidirecional com a fun¸c˜ao chamada.
– Cria¸c˜ao de apelidos. A passagem de referˆencia torna dispon´ıveis caminhos de
acesso aos subprogramas chamados, ampliando o acesso deles `a vari´aveis n˜ao lo-
cais. Neste m´etodo ´e poss´ıvel criar um apelido de diversas maneiras. Uma dessas
maneiras foi ilustrada nos problemas anteriores, quando definimos o que era ali-
sing.
5.4 Verifica¸c˜ao dos tipos de parˆametros
O m´etodo de prot´otipo ser´a utilizado, onde o tipo dos parˆametros formais s˜ao inclu´ıdos na
assinatura da fun¸c˜ao ou procedimento, e s˜ao verificados tanto a quantidade de parˆametros
29. CAP´ITULO 5. SUBPROGRAMAS 28
que foram passados quanto seus tipos. No caso particular de uma fun¸c˜ao requisitar um
n´umero real (float) e receber uma vari´avel inteira (int), a convers˜ao ser´a feita (alargamento)
e n˜ao haver´a problemas, enquanto que o caso contr´ario n˜ao ser´a permitido (estreitamento),
pois pode ocorrer a perda de informa¸c˜oes na convers˜ao de reais para inteiros.
5.5 Subprogramas sobrecarregados ou gen´ericos
A linguagem suportar´a tanto a produ¸c˜ao de subprogramas gen´ericos quanto a de
subprogramas polim´orficos. Este tipo de subprograma permite que n˜ao seja necess´ario
criar duas ou mais vers˜oes do mesmo para diferentes parˆametros. Al´em disso, a linguagem
tamb´em dar´a suporte a cria¸c˜ao de subprogramas sobrecarregados, tendo em vista que,
h´a casos em que ´e mais conveniente escrever duas ou mais fun¸c˜oes com parˆametros, retorno
ou quantidade de argumentos deferentes mas, com o mesmo nome.
5.6 Sistema de Implementa¸c˜ao
Algumas linguagens imperativas tem como passo primordial a compila¸c˜ao. Isso permite
que o programador desenvolva programas que executem diretamente no hardware. Esse tipo
de sistema de implementa¸c˜ao, na maioria dos casos, torna o programa eficiente. Por´em,
quando estamos desenvolvendo grandes sistemas, torna-se necess´ario compilar o programa
por completo toda vez que forem feitas altera¸c˜oes, o que pode ser muito custoso.
Nesse sentido, o sistema de implementa¸c˜ao utilizado na linguagem ´e a compila¸c˜ao se-
parada. Um dos fatores que justificam a escolha ´e a conveniˆencia de que, mesmo em um
sistema de m´edio e baixo porte, o programador tenha a possibilidade de compilar apenas os
m´odulos alterados recentemente.
30. Cap´ıtulo 6
Sistema de tipos da linguagem
O sistema de tipos de uma linguagem ´e um aspecto de grande importˆancia para a escolha
de uma linguagem para um determinado contexto. Ele quem define se uma linguagem faz
todas as verifica¸c˜oes de tipos, tanto em tempo de compila¸c˜ao quanto em tempo de execu¸c˜ao,
o que determina se a linguagem ´e ou n˜ao fortemente tipada.
6.1 Principais caracter´ısticas do sistema de tipos
A linguagem proposta ´e fortemente tipada, em vista do alto grau de confiabilidade re-
querido pelas aplica¸c˜oes de dom´ınio cient´ıfico, o qual essa caracter´ıstica provˆe. Desse modo,
qualquer erro de tipo ´e detectado durante a compila¸c˜ao ou em tempo de execu¸c˜ao.
Os tipos dos parˆametros de fun¸c˜oes s˜ao verificados durante o tempo de execu¸c˜ao.
6.2 Convers˜oes de Tipos
As convers˜oes de tipos ser˜ao permitidas s˜ao impl´ıcitas e expl´ıcitas, abaixo ambas s˜ao
descritas.
Impl´ıcitas: Somente convers˜oes de alargamento ser˜ao permitidas. Ou seja, o tipo sofrer´a
convers˜ao impl´ıcita somente quando o tipo para o qual esta sendo feita a convers˜ao contenha
a faixa de valores do tipo convertido.
Expl´ıcitas: As convers˜oes expl´ıcitas s˜ao oferecidas pela linguagem. Elas ser˜ao usadas
pelo usu´ario atrav´es de fun¸c˜oes. Como por exemplo a fun¸c˜ao stringToInt(a:string):int.
6.3 Regras de compatibilidade de tipos
As regras de compatibilidade de tipos adotadas pela linguagem s˜ao compatibilidade por
nome e compatibilidade por estrutura. A compatibilidade entre tipos anˆonimos (cujo tipo
n˜ao ´e expl´ıcito na declara¸c˜ao, por exemplo vetor) deve ser feitas atrav´es da compatibilidade
29
31. CAP´ITULO 6. SISTEMA DE TIPOS DA LINGUAGEM 30
por estruturas. J´a as estruturas de dados criadas pelo usu´ario a compatibilidade ´e verificada
por nome. Abaixo explicamos as regras descritas acima.
Compatibilidade por nome: Duas vari´aveis s˜ao compat´ıveis por nome se tiverem o
mesmo nome de tipo, ou estiverem na mesma declara¸c˜ao. Essa regra de compatibilidade ´e
de f´acil implementa¸c˜ao, embora seja altamente restritiva.
Compatibilidade por estrutura: Duas vari´aveis s˜ao compat´ıveis estruturalmente se
possu´ırem estruturas idˆenticas. Essa regra de compatibilidade ´e mais flex´ıvel que a primeira,
por´em ´e mais dif´ıcil de implementar.