SlideShare uma empresa Scribd logo
1 de 31
Baixar para ler offline
Projeto de uma Linguagem de Programa¸c˜ao
Caio C´esar, Gabriel Vasiljevic, Jean Silva, Victor
20 de maio de 2013
Sum´ario
1 Principais caracter´ısticas da linguagem 2
1.1 Dom´ınio de Aplica¸c˜ao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.2 Caracter´ısticas da linguagem . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
2 Valores, tipos e express˜oes 4
2.1 Tipos Primitivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.2 Tipos Compostos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.3 Sistema de tipos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.4 Tipos definidos pelo usu´ario . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.5 Representa¸c˜ao dos valores de cada tipo . . . . . . . . . . . . . . . . . . . . . 6
2.5.1 Designadores dos tipos primitivos . . . . . . . . . . . . . . . . . . . . 8
2.5.2 Forma BNF de declara¸c˜ao de um tipo na Linguagem . . . . . . . . . 8
2.6 Express˜oes da linguagem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.6.1 Operadores Aritm´eticos . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.6.2 Incremento e decremento . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.6.3 Operadores Relacionais . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.6.4 Operadores l´ogicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.6.5 Operadores bit-a-bit . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3 Vari´aveis e comandos 11
3.1 Modelo de armazenamento de valores . . . . . . . . . . . . . . . . . . . . . . 11
3.2 Uso de vari´aveis na Linguagem . . . . . . . . . . . . . . . . . . . . . . . . . 11
3.3 Caracter´ısticas dos arrays da linguagem . . . . . . . . . . . . . . . . . . . . . 12
3.4 Escopo e tempo de vida das vari´aveis . . . . . . . . . . . . . . . . . . . . . . 13
3.5 Comandos da linguagem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.5.1 Entrada e Sa´ıda . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.5.2 Estruturas de Controle . . . . . . . . . . . . . . . . . . . . . . . . . . 14
3.5.3 Comandos alternadores de fluxo (jumps) . . . . . . . . . . . . . . . . 16
3.5.4 Primitivas de sa´ıdas dos blocos (escapes) . . . . . . . . . . . . . . . . 16
3.5.5 Tratamento de Exce¸c˜oes . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.6 Aloca¸c˜ao de mem´oria das veri´aveis compostas . . . . . . . . . . . . . . . . . 17
1
4 Vincula¸c˜oes e regras de escopo 18
4.1 Bindings da linguagem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
4.2 Aliasing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
4.3 Estrutura de blocos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
4.4 Regras de visibilidade e resolu¸c˜ao de escopo . . . . . . . . . . . . . . . . . . 19
5 Subprogramas 20
5.1 Procedimentos e fun¸c˜oes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
5.2 Parˆametros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
5.3 Formas de passagem de parˆametros . . . . . . . . . . . . . . . . . . . . . . . 22
5.4 Verifica¸c˜ao dos tipos de parˆametros . . . . . . . . . . . . . . . . . . . . . . . 22
5.5 Subprogramas sobrecarregados ou gen´ericos . . . . . . . . . . . . . . . . . . 23
5.6 Sistema de Implementa¸c˜ao . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
6 Sistema de tipos da linguagem 24
6.1 Principais caracter´ısticas do sistema de tipos . . . . . . . . . . . . . . . . . . 24
6.2 Convers˜oes de Tipos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
6.3 Regras de compatibilidade de tipos . . . . . . . . . . . . . . . . . . . . . . . 24
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
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.
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
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.
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.
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.
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.
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
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.
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.
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
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.
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;
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>
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;
 }
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
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.
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
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
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.
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
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.
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
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.
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
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.
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
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.

Mais conteúdo relacionado

Semelhante a Documentação de uma linguagem de progração

Semelhante a Documentação de uma linguagem de progração (20)

Latex
LatexLatex
Latex
 
Linguagem c
Linguagem cLinguagem c
Linguagem c
 
Estruturas dados
Estruturas dadosEstruturas dados
Estruturas dados
 
Estruturas dados
Estruturas dadosEstruturas dados
Estruturas dados
 
Poojava
PoojavaPoojava
Poojava
 
Latex tutorial
Latex tutorialLatex tutorial
Latex tutorial
 
Programação Orientada a Objetos com Java
Programação Orientada a Objetos com JavaProgramação Orientada a Objetos com Java
Programação Orientada a Objetos com Java
 
Apostila de latex
Apostila de latexApostila de latex
Apostila de latex
 
DissertacaoMScValterFinal20070216
DissertacaoMScValterFinal20070216DissertacaoMScValterFinal20070216
DissertacaoMScValterFinal20070216
 
Curso python
Curso pythonCurso python
Curso python
 
Apostila de poo em c++
Apostila de poo em c++Apostila de poo em c++
Apostila de poo em c++
 
Apostila de poo em c++
Apostila de poo em c++Apostila de poo em c++
Apostila de poo em c++
 
O mundo-da-linguagem-c
O mundo-da-linguagem-cO mundo-da-linguagem-c
O mundo-da-linguagem-c
 
O fantc3a1stico-mundo-da-linguagem-c
O fantc3a1stico-mundo-da-linguagem-cO fantc3a1stico-mundo-da-linguagem-c
O fantc3a1stico-mundo-da-linguagem-c
 
OWL QL e Regras Não Monótonas
OWL QL e Regras Não MonótonasOWL QL e Regras Não Monótonas
OWL QL e Regras Não Monótonas
 
Taxonomias
TaxonomiasTaxonomias
Taxonomias
 
Apostila de Funções em C
Apostila de Funções em CApostila de Funções em C
Apostila de Funções em C
 
Manual de portugol
Manual de portugolManual de portugol
Manual de portugol
 
Klavaro manual-pt-1.0.8
Klavaro manual-pt-1.0.8Klavaro manual-pt-1.0.8
Klavaro manual-pt-1.0.8
 
Apostila pascal
Apostila pascalApostila pascal
Apostila pascal
 

Documentação de uma linguagem de progração

  • 1. Projeto de uma Linguagem de Programa¸c˜ao Caio C´esar, Gabriel Vasiljevic, Jean Silva, Victor 20 de maio de 2013
  • 2. Sum´ario 1 Principais caracter´ısticas da linguagem 2 1.1 Dom´ınio de Aplica¸c˜ao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 1.2 Caracter´ısticas da linguagem . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 2 Valores, tipos e express˜oes 4 2.1 Tipos Primitivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 2.2 Tipos Compostos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 2.3 Sistema de tipos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 2.4 Tipos definidos pelo usu´ario . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 2.5 Representa¸c˜ao dos valores de cada tipo . . . . . . . . . . . . . . . . . . . . . 6 2.5.1 Designadores dos tipos primitivos . . . . . . . . . . . . . . . . . . . . 8 2.5.2 Forma BNF de declara¸c˜ao de um tipo na Linguagem . . . . . . . . . 8 2.6 Express˜oes da linguagem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 2.6.1 Operadores Aritm´eticos . . . . . . . . . . . . . . . . . . . . . . . . . 9 2.6.2 Incremento e decremento . . . . . . . . . . . . . . . . . . . . . . . . . 9 2.6.3 Operadores Relacionais . . . . . . . . . . . . . . . . . . . . . . . . . . 9 2.6.4 Operadores l´ogicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 2.6.5 Operadores bit-a-bit . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 3 Vari´aveis e comandos 11 3.1 Modelo de armazenamento de valores . . . . . . . . . . . . . . . . . . . . . . 11 3.2 Uso de vari´aveis na Linguagem . . . . . . . . . . . . . . . . . . . . . . . . . 11 3.3 Caracter´ısticas dos arrays da linguagem . . . . . . . . . . . . . . . . . . . . . 12 3.4 Escopo e tempo de vida das vari´aveis . . . . . . . . . . . . . . . . . . . . . . 13 3.5 Comandos da linguagem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 3.5.1 Entrada e Sa´ıda . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 3.5.2 Estruturas de Controle . . . . . . . . . . . . . . . . . . . . . . . . . . 14 3.5.3 Comandos alternadores de fluxo (jumps) . . . . . . . . . . . . . . . . 16 3.5.4 Primitivas de sa´ıdas dos blocos (escapes) . . . . . . . . . . . . . . . . 16 3.5.5 Tratamento de Exce¸c˜oes . . . . . . . . . . . . . . . . . . . . . . . . . 17 3.6 Aloca¸c˜ao de mem´oria das veri´aveis compostas . . . . . . . . . . . . . . . . . 17 1
  • 3. 4 Vincula¸c˜oes e regras de escopo 18 4.1 Bindings da linguagem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 4.2 Aliasing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 4.3 Estrutura de blocos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 4.4 Regras de visibilidade e resolu¸c˜ao de escopo . . . . . . . . . . . . . . . . . . 19 5 Subprogramas 20 5.1 Procedimentos e fun¸c˜oes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 5.2 Parˆametros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 5.3 Formas de passagem de parˆametros . . . . . . . . . . . . . . . . . . . . . . . 22 5.4 Verifica¸c˜ao dos tipos de parˆametros . . . . . . . . . . . . . . . . . . . . . . . 22 5.5 Subprogramas sobrecarregados ou gen´ericos . . . . . . . . . . . . . . . . . . 23 5.6 Sistema de Implementa¸c˜ao . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 6 Sistema de tipos da linguagem 24 6.1 Principais caracter´ısticas do sistema de tipos . . . . . . . . . . . . . . . . . . 24 6.2 Convers˜oes de Tipos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 6.3 Regras de compatibilidade de tipos . . . . . . . . . . . . . . . . . . . . . . . 24
  • 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.