SlideShare una empresa de Scribd logo
1 de 31
Descargar para leer sin conexión
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.

Más contenido relacionado

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

Similar 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
 

Último

Assessement Boas Praticas em Kubernetes.pdf
Assessement Boas Praticas em Kubernetes.pdfAssessement Boas Praticas em Kubernetes.pdf
Assessement Boas Praticas em Kubernetes.pdfNatalia Granato
 
ATIVIDADE 1 - CUSTOS DE PRODUÇÃO - 52_2024.docx
ATIVIDADE 1 - CUSTOS DE PRODUÇÃO - 52_2024.docxATIVIDADE 1 - CUSTOS DE PRODUÇÃO - 52_2024.docx
ATIVIDADE 1 - CUSTOS DE PRODUÇÃO - 52_2024.docx2m Assessoria
 
ATIVIDADE 1 - GCOM - GESTÃO DA INFORMAÇÃO - 54_2024.docx
ATIVIDADE 1 - GCOM - GESTÃO DA INFORMAÇÃO - 54_2024.docxATIVIDADE 1 - GCOM - GESTÃO DA INFORMAÇÃO - 54_2024.docx
ATIVIDADE 1 - GCOM - GESTÃO DA INFORMAÇÃO - 54_2024.docx2m Assessoria
 
Padrões de Projeto: Proxy e Command com exemplo
Padrões de Projeto: Proxy e Command com exemploPadrões de Projeto: Proxy e Command com exemplo
Padrões de Projeto: Proxy e Command com exemploDanilo Pinotti
 
Boas práticas de programação com Object Calisthenics
Boas práticas de programação com Object CalisthenicsBoas práticas de programação com Object Calisthenics
Boas práticas de programação com Object CalisthenicsDanilo Pinotti
 
ATIVIDADE 1 - LOGÍSTICA EMPRESARIAL - 52_2024.docx
ATIVIDADE 1 - LOGÍSTICA EMPRESARIAL - 52_2024.docxATIVIDADE 1 - LOGÍSTICA EMPRESARIAL - 52_2024.docx
ATIVIDADE 1 - LOGÍSTICA EMPRESARIAL - 52_2024.docx2m Assessoria
 

Último (6)

Assessement Boas Praticas em Kubernetes.pdf
Assessement Boas Praticas em Kubernetes.pdfAssessement Boas Praticas em Kubernetes.pdf
Assessement Boas Praticas em Kubernetes.pdf
 
ATIVIDADE 1 - CUSTOS DE PRODUÇÃO - 52_2024.docx
ATIVIDADE 1 - CUSTOS DE PRODUÇÃO - 52_2024.docxATIVIDADE 1 - CUSTOS DE PRODUÇÃO - 52_2024.docx
ATIVIDADE 1 - CUSTOS DE PRODUÇÃO - 52_2024.docx
 
ATIVIDADE 1 - GCOM - GESTÃO DA INFORMAÇÃO - 54_2024.docx
ATIVIDADE 1 - GCOM - GESTÃO DA INFORMAÇÃO - 54_2024.docxATIVIDADE 1 - GCOM - GESTÃO DA INFORMAÇÃO - 54_2024.docx
ATIVIDADE 1 - GCOM - GESTÃO DA INFORMAÇÃO - 54_2024.docx
 
Padrões de Projeto: Proxy e Command com exemplo
Padrões de Projeto: Proxy e Command com exemploPadrões de Projeto: Proxy e Command com exemplo
Padrões de Projeto: Proxy e Command com exemplo
 
Boas práticas de programação com Object Calisthenics
Boas práticas de programação com Object CalisthenicsBoas práticas de programação com Object Calisthenics
Boas práticas de programação com Object Calisthenics
 
ATIVIDADE 1 - LOGÍSTICA EMPRESARIAL - 52_2024.docx
ATIVIDADE 1 - LOGÍSTICA EMPRESARIAL - 52_2024.docxATIVIDADE 1 - LOGÍSTICA EMPRESARIAL - 52_2024.docx
ATIVIDADE 1 - LOGÍSTICA EMPRESARIAL - 52_2024.docx
 

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.