Baixe o app para aproveitar ainda mais
Prévia do material em texto
Paradigmas de linguagens de programação Aula 2: Implementação e classi�cação das linguagens de programação Apresentação Todo programa escrito em uma linguagem de programação (LP) precisará ser convertido para a linguagem de máquina antes de ser executado. Nesta aula, demonstraremos os três diferentes métodos de implementação de linguagens de programação (LPs): compilação, interpretação pura e híbrido. Ao longo dos anos, várias LPs foram criadas com diferentes propósitos. Por isso, algumas classi�cações surgiram para associá-las sob diferentes perspectivas e critérios. Apresentaremos, portanto, as perspectivas de classi�cação e agrupamento dessas linguagens. Objetivos Apontar as formas de implementação de uma LP; Distinguir diferentes perspectivas para classi�car LPs. Formas de Implementação de uma linguagem de programação (LP) Todo programa, a menos que esteja escrito em linguagem de máquina – o que, hoje em dia, está totalmente fora dos propósitos –, precisará ser convertido para tal linguagem antes de ser executado. Essa conversão precisa de um programa que receba o código escrito na LP. Atenção O nome desse programa é código-fonte. Ele irá gerar o respectivo código em linguagem de máquina (compatível com o sistema operacional e o hardware em que o programa vai ser executado), fazendo a tradução do código-fonte em linguagem de máquina. Isso vai determinar como os programas na LP serão, de fato, implementados. No livro Conceitos de linguagem de programação, de Robert W. Sebesta (2011), são descritos os três métodos de implementação das LPs: tradução, interpretação e o sistema híbrido. Tradução Nesta alternativa, o programa escrito em uma linguagem de alto nível é traduzido para uma versão equivalente em linguagem de máquina antes de ser executado. O processo de tradução pode ser executado em várias fases conceituais cujas possiblidades ainda incluem múltiplas combinações e execução em simultaneidade. O processo de tradução é erroneamente chamado de compilação, que, na realidade, constitui apenas uma das fases do processo. O início da tradução se dá na leitura do programa-fonte (linguagem de alto nível); seu término, na geração do código executável (entendido pela máquina). Há quatro fases que compõem o tradutor: Compilação Montagem Carga Ligação A imagem a seguir ilustra os programas atuando no processo de tradução: Cada etapa desse processo de tradução está descrita abaixo: 1. O compilador analisa o código-fonte. Se tudo estiver bem, o converterá para um código Assembly (da máquina hospedeira). 2. O montador traduz o código Assembly para o código de máquina intermediário (código-objeto), que não pode ser lido nem pelo programador nem pelo computador. Este código pode ser: Relocável (carregável para execução em qualquer posição de memória), código mais comum e vantajoso; Absoluto (carregável a partir de um endereço absoluto de memória). 3. O carregador torna o código-objeto gerado pelo montador um código-objeto relocável, alterando os endereços necessários e colocando o código e os dados na localização de memória adequada. 4. O ligador (ou link editor) junta o código-objeto com as bibliotecas necessárias (outros objetos, rotinas do SO, dlls etc.) para gerar o programa executável. Compilador Clique no botão acima. Compilador O principal elemento do processo de tradução é, sem dívida, o compilador. Por isso, muitas vezes, o processo como um todo é erroneamente chamado de compilador, uma vez que o ambiente integrado das linguagens atuais já integra todos os componentes (montador, compilador, carregador e ligador). A importância do compilador está no fato de nele residir toda a ‘‘inteligência’’ (processamento) da linguagem. Esta �gura ilustra os componentes envolvidos no processo de compilação: 1. Análise léxica: Identi�ca e separa os tokens (elementos componentes da linguagem, eles são símbolos individuais da linguagem-átomo), desprezando comentários e espaços em branco. Monta a tabela de símbolos. 2. Análise sintática: Veri�ca se os tokens constituem estruturas sintáticas (exemplos: expressões e comandos). Aplica regras gramaticais da linguagem, reconhece a forma estrutural do programa e forma a árvore de derivação a partir da sequência de tokens. 3. Análise semântica: Veri�ca se as estruturas sintáticas possuem sentido. Exemplos: se um id de variável é usado como tal ou se há compatibilidade entre os operandos e os operadores. Averigua também se o programa possui erro de signi�cado e usa a árvore de derivação para formar uma estrutura interna do programa. 4. Gerador de código intermediário, otimizador de código e gerador de código: Irão, em distintas fases, gerar o programa alvo ou programa-objeto. Um gerador de código gera o código intermediário, que deve conter toda a informação para gerar o código-objeto. x := a * (b+ c) ⎫ ⎭ ⎬ ⎪ ⎪ (+, b, c, t1) (*,a, t1, t2) (:=, t2,x) ⎫ ⎭ ⎬ ⎪ ⎪ Load b Add c Sto t1 ⋯ O otimizador tem por objetivo eliminar redundâncias do código intermediário e tornar o objeto mais e�ciente. Existem otimizações dependentes de máquina e outras, não. 5. Tratador de erros: Em todas as fases, existem erros possíveis. Um bom compilador os apresenta de formas claras e objetivas. Erros de sintaxe e semântica são devidamente padronizados e identi�cados. Um gerenciador da tabela de símbolos mantém a tabela de símbolos (elementos ou tokens). As principais características dos compiladores são: Gerar código-objeto mais otimizado; Execução mais rápida; Traduz um mesmo comando apenas uma vez, mesmo que ele seja usado em várias partes do programa – tanto iterações como repetição de código; Processo de correção de erros e depuração é mais demorado; A programação �nal (código-objeto) é maior; O programa-objeto gerado é dependente de plataforma (processador + SO), necessitando de um compilador diferente para cada família de processadores/sistema operacional. A �gura abaixo ilustra tal dependência: Programas × Plataformas Atenção! Aqui existe uma videoaula, acesso pelo conteúdo online Interpretação pura A imagem a seguir mostra o processo de interpretação: No processo de implementação por interpretação, cada comando escrito na linguagem de alto nível (programa-fonte) é diretamente traduzido e executado (um a um) pela CPU sob comando do programa interpretador. Em geral, existe um procedimento, escrito em linguagem de máquina do computador, responsável pela execução de cada comando previsto na LP. Dessa forma, o interpretador traduz um comando de cada vez e chama uma rotina para completar a sua execução. Um interpretador, portanto, é um programa que executa repetidamente a seguinte sequência: 1 Obter a próxima instrução do programa-fonte na LP. 2 Determinar que ações devem ser executadas (conversão para comandos emlinguagem de máquina). 3 Executar as ações. Perceba que o procedimento acima descrito é bastante similar àquele realizado por computadores que implementarem a máquina de Non Neumann na execução de uma instrução: 1 Obter a próxima instrução, cujo endereço está no contador de instruções (CI). 2 Incrementar o CI, fazendo-o receber o endereço da próxima instrução. 3 Armazenar a instrução a ser executada no RI (registrador de instruções). 4 Decodi�car a instrução. 5 Executar a instrução. Tal semelhança mostra que a interpretação pode ser vista como a simulação em um computador hospedeiro de uma máquina especial cuja linguagem de máquina seja uma de mais alto nível. O processo de interpretação pode ser comparado ao processo em que duas pessoas conversam em línguas diferentes (português e inglês, por exemplo). Há a necessidade de uma terceira pessoa em cena: o intérprete. Ele escuta o que é dito em uma língua e imediatamente a traduz para outra. Entre as principais características do interpretador, podemos citar: 1. Não produz programa-objeto persistente. 2. Não traduz instruções que nunca são executadas. 3. O resultado da conversão é instantâneo: resulta da execução do comando ou da exibição de erro –interpretador puro. 4. Útil ao processo de depuração de código devido a mensagens de erros em tempo de execução (tanto análise sintática quanto semântica. 5. Execução mais lenta que em outros processos de tradução (compilação), pois, toda vez que o mesmo programa for executado, os mesmos passos de interpretação serão executados. 6. Consome menos memória. 7. O código-fonte é portátil. Não é gerado um código de máquina: Pode executar o comando em alto nível diretamente ou gerar um código intermediário, que, neste caso, é interpretado por uma máquina virtual (VM) – interpretador híbrido; Se a VM foi desenvolvida para diferentes plataformas, temos a portabilidade do código-fonte. É o caso da linguagem Java. Tradução × interpretação Vantagens Desvantagens Tradutores 1. Execução mais rápida. 2. Permite estruturas de programas mais complexas. 3. Permite a otimização de código. 1. Várias etapas de tradução. 2. Programação �nal é maior, precisando de mais memória para sua execução. 3. Processo de correção de erros e depuração é mais demorado. Interpretadores 1. Depuração mais simples. 2. Consome menos memória. 3. Resultado imediato do programa (ou parte dele). 1. Execução do programa é mais lenta. 2. Estruturas de dados demasiado simples. 3. É necessário fornecer a fonte ao utilizador. Sistemas de implementação híbridos Almejam obter o melhor de cada mundo: interpretação e tradução (incluindo a compilação). Por isso, o processo híbrido de implementação de LP combina a execução rápida dos tradutores (compiladores) com a portabilidade dos interpretadores. O segredo das implementações híbridas é a geração de um código intermediário, que, mesmo sendo mais facilmente interpretável, não �ca preso a uma plataforma (SO/hardware). O método híbrido constitui-se de duas etapas: Compilação para um código intermediário; Interpretação desse código. Esse código intermediário não é especí�co para uma plataforma, possibilitando aos programas já compilados para esse código serem portados em diferentes plataformas sem que eles sofram alteração nem façam mais nada. Para cada plataforma desejada, devemos ter um interpretador desse código. Como veremos adiante, a linguagem de sucesso que adotou o processo híbrido foi Java. Java, um caso de sucesso Clique no botão acima. Java, um caso de sucesso Trata-se de uma das linguagens interpretadas mais conhecidas atualmente. De alto nível, o Java foi desenvolvido pela Sum Microsystems. É orientado a objeto de maneira similar à do C++. Códigos-fonte em Java (de extensão .java) são traduzidos na forma de um código intermediário chamado bytecode, que pode ser executado por uma máquina virtual Java (JVM). Há várias implementações de JVMs para diversos sistemas operacionais, inclusive Unix, MAC, OS e Windows. Além disso, o bytecode pode ser convertido em instruções de linguagem de máquina pelo compilador JIT (em inglês, just-in-time compiler). Applets são aplicações menores de Java que podem ser baixadas de um servidor web e executadas em um navegador compatível com o Java, como o Internet Explorer. O Java possui muitas características que fazem dele uma linguagem interessante para uso no ambiente www. Comparação �nal O quadro abaixo apresenta um comparativo entre os três processos demonstrados nesta aula, destacando os principais atributos de cada um: Tradutores puros Interpretadores puros Processadores híbridos Geração de código executável Não gera código Geração de código intermediário Depende da plataforma Independe de plataforma Independe de plataforma Tradução lenta × execução rápida Execução lenta Tradução rápida × execução não muito rápida Atenção! Aqui existe uma videoaula, acesso pelo conteúdo online Classi�cação das linguagens de programação (LPs) Ao longo dos anos, alguns autores têm criado diferentes classi�cações para as LPs. Tendo em vista critérios diversos, eles as agrupavam sob diferentes perspectivas. Veremos a seguir algumas dessas classi�cações: Clique nos botões para ver as informações. São as linguagens que utilizam apenas instruções a serem executadas pelo processador. Sua primeira geração foi a linguagem de máquina: trata-se da única linguagem que um processador (CPU) pode executar nativamente – ou seja, em uma linguagem que a máquina compreende. A ilustração a seguir demonstra a representação de uma instrução em linguagem de máquina (ou binária) realizada por um processador especí�co. Ela tem palavras (unidade executada pelo processador) de 16 bits: 4 bits representam a instrução (código da instrução); 6 bits, cada operando. A segunda geração das linguagens de baixo nível trouxe a Assembly. Embora não seja uma linguagem nativa dos processadores, ela utiliza as instruções reais deles. Cada instrução na linguagem Assembly, portanto, será convertida para o código equivalente em linguagem de máquina. Apresentaremos a seguir três linhas de um código em Assembly que: Move dois para o registrado AX (linha 1); Move dois para o registrado BX (linha 2); Soma o conteúdo dos dois registradores (linha 3). Linguagens de baixo nível 0011 ↓ Código da instrução 001010 ↓ Endereço do 1º operando 001111 ↓ Endereço do 2º operando MOV AX,0002 MOV BX,0001 ADD AX,BX Linguagens cuja sintaxe das instruções está longe da linguagem de máquina e próximo da humana (em inglês, frisamos). Elas, portanto, são entendidas com mais facilidade pelo homem. Muito abstratas, as instruções das linguagens de alto nível não estão relacionadas à arquitetura do computador. As principais linguagens são: A seguir, apresentaremos o mesmo código expresso acima em linguagem Assembly, porém agora serão usadas variáveis de alto nível, como abstração do armazenamento e codi�cação na linguagem C: Linha 1: declara as variáveis A e B como de tipo inteiro; Linha 2: Indica o início do programa principal; Linha 3: Início do corpo do programa; Linha 4: Atribui o valor 2 à variável A; Linha 5: Atribui o valor 1 à variável B; Linha 6: Soma o conteúdo das variáveis A e B e armazena o resultado na variável A. Linguagens de alto nível ASP; C; C++; C#; Pascal; Delphi; Java; JavaScript; Lua; Matlab; PHP; Phyton; Ruby. int A,B; int main() { A=2; B=1; A=A+B; } Classi�cação por gerações Existem várias formas de classi�car as diferentes LPs existentes no mercado. Uma das maneiras mais amplamente difundidas é a classi�cação por gerações. Ela é realizada, defendem alguns autores, da 1a à 5a geração, sendo que, a cada geração, novos recursos facilitadores são embutidos nas respectivas linguagens. A imagem a seguir ilustra as classi�cações por nível (baixo e alto) e, dentro delas, a classi�cação por gerações: Figura: Classificação por nível e gerações. Vejamos a seguir um desdobramento sobre cada geração: Clique nos botões para ver as informações. Considera-se linguagem de 1ª geração aquela que lida diretamente com a arquitetura do computador e o conjunto de instruções em linguagem de máquina dos diferentes processadores. Como cada tipo de CPU possui um instruction set (conjunto de instruções) próprio, um programa em linguagem de máquina executável não será executado em um computador diferente daquele para o qual foi desenvolvido. O caso dos PCs é uma exceção a essa regra, já que eles possuem arquiteturas muito semelhantes e instruction sets compatíveis. A programação em linguagem de máquina é extremamente difícil e complexa, pois seu nível de abstração é zero. O programador precisa conhecer a sequência de bits necessária à execução de cada instrução desejada para uma CPU especí�ca. Por exemplo, a sequência 01000011 110010000 011000001 é uma instrução para ler um valor da memória para um registrador 01. Além disso, ele também precisa saber sobre detalhes de endereçamento de memória e de registradores. Linguagens de 1a geração Linguagens ainda de baixo nível (considerando a linguagem humana) que fazem uma instrução mnemônica corresponder a cada instrução Conjunto de instruções do processador. Denominadas Assembly, taislinguagens são traduzidas para a de máquina por um programa especial (montador) chamado de Assembler. Apesar de facilitarem o desenvolvimento dos programas, elas continuam, tal como as linguagens de máquina, a ser especí�cas para cada processador, mantendo, assim, suas consequentes desvantagens. Acompanhe este exemplo para uma CPU abstrata. Considere a seguinte sequência de instruções em linguagem Assembly: Move #8,A (lê um valor da posição de memória 8 para o registrador A); Move #8,B (lê um valor da posição de memória 9 para o registrador B); Add A,B (soma os valores armazenados nos registradores A e B). Em linguagem de máquina, depois de traduzidas pelo Assembler, essas instruções poderiam ser representadas pelas seguintes sequências de palavras binárias: 01000011 11001000 01100001 (Move #8,A); 01000011 11001001 01100010 (Move #9 B); 01010100 01100001 01100010 (Add A,B). Houve uma certa melhora no nível de abstração, mas a di�culdade permanece, pois o programador, além de ter de gravar os mnemônicos, teria de possuir conhecimento da arquitetura dos computadores (registradores, endereçamento de memória etc.). Linguagens de 2a geração Elas são as chamadas de alto nível, pois uma única instrução em uma linguagem inteligível pelo homem (próximo da humana) pode corresponder a muitas instruções em linguagem de máquina. Linguagens de 3ª geração são linguagens de aplicação geral que se caracterizam por suportarem: Variáveis; Tipos de dados simples (caractere, inteiro, real e lógico) e estruturados (matrizes, vetores, registros); Instruções condicionais e repetitivas; Programação modular (funções e procedimentos). O processo de tradução da linguagem de alto nível para a de máquina �cou mais complexo. Por isso, houve o surgimento de compiladores, interpretadores e tradutores para executarem a tarefa. Exemplos: Fortran, Basic, Pascal, COBOL e C. Embora tenha todas as propriedades e características de uma linguagem de 3ª geração, a linguagem C possui instruções que permitem acessar o hardware (memória, registradores e outros). Por causa dessa �exibilidade, ela foi usada para codi�car sistemas operacionais, compiladores e outros softwares básicos, tomando o lugar da linguagem Assembly, até então predominante sempre que era necessário o acesso ao hardware diretamente. Por essas características, C foi chamada de linguagem de médio nível. Esta geração de linguagens apresenta recursos para: Documentar os programas-fonte; Armazenar tipos de dados: Simples, estruturados e enumerados; Alocação dinâmica (ponteiros); Estruturas de controle: Sequencial, condicional, repetição e desvio incondicional; Programação modular; Conversão automática de dados; Operadores: Relacionais, lógicos e aritméticos. Linguagens de 3a geração Linguagens de alto nível (alguns autores as classi�cam como de altíssimo nível) que, ao contrário das LPs de terceira geração, possuem âmbitos concretos de aplicação e objetivos bem especí�cos. Trata-se de linguagens não procedimentais (novamente ao contrário das LPs de 3ª geração). Elas permitem, assim, que o programador especi�que o que deseja que seja feito, mas não como deve ser feito. Exemplo: a SQL (structured query language, em inglês) é utilizada para consultas à manipulação de banco de dados. PostScript e Matlab são dois outros exemplos de linguagens de 4ª geração. Linguagens de 4a geração Linguagens de especi�cação de problemas de forma declarativa e não algorítmica. Dois exemplos: Lisp e Prolog. As linguagens de 5ª geração são usadas para o desenvolvimento de sistemas especialistas (área da inteligência arti�cial) e de reconhecimento de voz. A imagem abaixo mostra as gerações e as respectivas linguagens: Linguagens de 5a geração Saiba mais Linguagens de 4ª geração, com frequência, são comparadas às de domínio especí�co e chamadas de linguagens aplicativas. Atenção! Aqui existe uma videoaula, acesso pelo conteúdo online Atividade 1. Avalie as assertivas sobre as formas de se implementar as LPs: I. Os códigos gerados pelas LPs implementadas por interpretação são independentes de plataforma e execução rápida. II. No processo de tradução (que inclui a compilação), o código gerado pelas linguagens é de execução rápida e dependente de plataforma. III. Java é uma linguagem que se adaptou bem ao processo híbrido de geração de código. IV. A tendência é o uso de processos híbridos que permitam aplicações mais portáveis. Com base em sua análise, marque a opção que apresenta apenas as assertivas corretas: a) I, II, III e IV b) II, III e IV c) II e III d) II e IV e) IV 2. Avalie as assertivas pertinentes à classi�cação de LPs e classi�que-as em V (verdadeira) ou F (falsa). a) QI. Ao grupo de linguagens de 1a geração, pertencem: linguagem de máquina ou binária e linguagem Assembly. b) II. As linguagens de alto nível ganham expressão nas décadas de 1980 e 1990, visando a gerar programas, tendo Phyton por sua representante. c) III. C++ é uma linguagem de alto nível e de segunda geração. d) IV. SQL é uma linguagem de 1a geração. 3. Ao classi�carmos as linguagens em níveis (baixo e alto) e em gerações (1ª a 5ª), podemos dizer que: I. As linguagens de 1ª a 3ª gerações compreendem as linguagens de baixo nível. II. As linguagens de baixo nível recebem esse nome porque utilizam apenas instruções que são executadas pelo processador. III. A linguagem de 1ª geração é o Assembly. IV. Ao total, desde a 1ª linguagem até os dias atuais, somam-se sete gerações de LPs. Marque a opção que apresenta apenas as assertivas verdadeiras: a) I e II b) I, II e III c) II d) II e III e) II e IV NotasReferências ___. Linguagem de programação de quarta geração. In: Wikipedia. Disponível em: https ://pt .wikipedia .org /wiki /Linguagem_de_programa %C3 %A7 %C3 %A3o_de_quarta_gera %C3 %A7 %C3 %A3o. Acesso em: 6 mai. 2019. SEBESTA, R. W. Conceitos de linguagem de programação. 9. ed. Porto Alegre: Bookman, 2011. VAREJÃO, F. M. Linguagem de programação: conceitos e técnicas. Rio de Janeiro: Elsevier, 2004. Próxima aula Três formas de classi�cação de LPs: domínio da aplicação, paradigma e década (ano de surgimento da LP); Principais propriedades das 10 LPs mais usadas atualmente. Explore mais javascript:void(0); Python; Introdução ao Objective-C; As 10 LPs mais usadas no mercado. javascript:void(0); javascript:void(0); javascript:void(0);
Compartilhar