Muitas linguagens de programação utilizam a compilação no seu fluxo de desenvolvimento, isso se deve ao fato dessas linguagens terem uma sintaxe muito rígida e pouco flexível. Um exemplo é a linguagem C, que necessita de um compilador para a gerar um código a partir dos seus arquivos.
Nos dias atuais, utilizamos compiladores sem que nós saibamos. Um exemplo disso é o uso do VS Code, que é uma IDE que consegue se adaptar à sua linguagem e auxiliar no desenvolvimento do seu programa.
Vamos então mostrar nesse artigo tudo sobre o compilador, desde o papel dele nas linguagens de alto nível, até a estruturação das suas etapas de processamento. Continue com a gente!
- O que é um compilador?
- Conheça a história do compilador?
- Como funciona um compilador? Para que serve?
- Quais os tipos de compiladores?
- Entenda como funcionam as fases do compilador!
- Quais as ferramentas de construção dos compiladores?
- Quais as diferenças entre compilador e interpretador?
- Qual usar, afinal? Compilador ou interpretador?
- Compiladores e interpretadores podem ser usados juntos?
- E os Scripts, podem ser compilados?
- Quais os 4 compiladores mais usados?
O que é um compilador?
Um compilador é um tradutor de linguagens de programação casuais para linguagens de programação do nível da máquina. Isso é, um programa que a partir do código de uma linguagem qualquer, realiza algumas etapas como a validação e, por fim, gera um ou mais arquivo(s) que na maioria das vezes é binário.
Além disso, os compiladores também indicam se houve algum tipo de erro na sintaxe do código, ou seja, se você escreveu o código de maneira incorreta, o compilador vai exibir um erro sobre isso.
Muitas linguagens de programação utilizam compiladores, Java, C, C++, C# e Lisp são exemplos disso. Mesmo utilizando compiladores diferentes, a ideia principal é igual: traduzir o código da linguagem para a linguagem Assembly.
Conheça a história do compilador
O primeiro compilador foi criado por Grace Hopper em 1952, para uma linguagem chamada A-0. Logo depois, em 1957, foi criado por um grupo da IBM um compilador para o saudoso Fortran. Em 1960, a primeira linguagem que compilava para várias arquiteturas foi criada: o Cobol.
Muito rapidamente a ideia de se utilizar um compilador foi ganhando muitos aliados. Isso se deve ao fato da flexibilidade que as linguagens de alto nível conseguiam entregar. Aliás, os primeiros compiladores foram todos escritos na linguagem Assembly.
Como funciona um compilador? Para que serve?
Geralmente, quando trabalhamos com alguma linguagem de alto nível, percebemos que ela contém um certo nível de abstração dos comandos para que facilite o entendimento humano. Sendo assim, o compilador existe para ler o código e converter para uma espécie de código objeto que contém instruções para a execução no microprocessador.
Resumindo, o compilador consegue entender o código da sua linguagem preferida e converte para uma linguagem que o computador consegue executar. Ao decorrer do texto, nos aprofundaremos mais em como exatamente funciona esse processo e quais são as etapas exatamente.
Quais os tipos de compiladores?
Temos também alguns tipos de compiladores, vamos mostrar para você quais são os principais:
Compilador cruzado
É um compilador que consegue criar códigos executáveis para plataformas diferentes da que está sendo executado. Um exemplo disso seria a criação de um APK (formato de arquivo padrão do android) a partir de um programa no Windows 10. Existem também outros pontos positivos desse método:
- criação de programas para sistemas embutidos, já que eles não tem muitos recursos. Um exemplo disso seria um microondas que contém um computador com pouco processamento e contém uma série de equipamentos como um touchpad, sensor na porta, um alto falante, uma tela digital para mostrar o tempo e o controlador da máquina de esquentar a comida. Tudo isso deve ser controlado por um pequeno computador que não é muito poderoso para conter todo um ambiente de desenvolvimento, logo, é mais simples utilizar a compilação cruzada nesses casos;
- compilação para várias máquinas. Por exemplo, para uma empresa que precisa de suporte para várias versões de um sistema operacional ou até mesmo vários sistemas operacionais.
Compilador Source-to-source
Um método de tradução de uma linguagem de alto nível para alguma outra linguagem de alto nível. Nesse processo, é aferido todos os mesmos processos de compilação e temos como resultado final o código de alguma outra linguagem. Um exemplo disso é o famoso TypeScript, que basicamente converte todo o seu código, de maneira automatizada, para o JavaScript.
Temos aqui uma lista de programas que fazem uso desse padrão de compilação:
Compilador Just-in-time (JIT)
Possivelmente, é um dos compiladores mais famosos. Você já deve ter ouvido falar nele enquanto estudava Java, .NET ou até Python. É um método de compilação que converte o código dessas linguagens para um código de meio termo chamado bytecode. Nesse caso, o código não é entendido pela máquina em si e é preciso contar com uma máquina virtual para a execução desse código. Também pode ser conhecido como um interpretador.
Com isso, temos o programa rodando logo após o início da compilação e é comum que o código gerado anteriormente contribua para a compilação final. Como o código será compilado várias vezes, esse método não é muito bom em fazer otimizações, já que se perde muito tempo nesse processo.
Entenda como funcionam as fases do compilador!
Vamos agora destrinchar todos os passos de um compilador:
Analisador Léxico
Nessa primeira fase, temos a separação dos textos em pequenos pedaços chamados de tokens léxicos. Isso é feito por meio de um analisador chamado scanner. Toda a leitura é feita caractere por caractere tendo como objetivo a separação e identificação de cada componente do programa.
Também é de responsabilidade dessa etapa a eliminação de espaços vazios, formatações e comentários do código. Existem alguns programas famosos que servem só para atuar na parte de análise léxica.
Analisador de Sintaxe
Logo após a análise léxica, é feita a verificação sintática das cadeias dos tokens para que seja confirmado se as regras e a estrutura seguem a gramática formal. Isso é, verifica-se se não existe nenhum erro na sintaxe do programa.
Também é conhecido como árvore de análise sintática ou apenas árvore sintática. Essa árvore é desenvolvida para definir a gramática da linguagem criada. Com isso, as regras são embutidas nesse processo e, se o programa não apresentar nenhum erro, é continuado o fluxo do compilador, se não, é apresentado uma mensagem de erro.
Analisador Semântico
Na análise semântica, temos a verificação de algumas outras regras, como o tipo de cada variável. Nesse passo é feita a validação da lógica do código da linguagem de programação em si, também é papel do analisador checar se todas as variáveis foram iniciadas.
Gerador de código intermediário
Tem o propósito de criar um código de objeto que ainda não foi concluído, porém, já é possível ser executado através de máquinas virtuais. Existem diversos tipos de códigos intermediários, sendo o mais famoso o código de três endereços. Também temos as triplas e quadruplas para representar esse nível de código.
Temos uma lista de linguagens que podem ser consideradas como código intermediário, já que são rodadas em máquinas virtuais:
- Java – Bytecode;
- TIMI – Usado em compiladores da plataforma IBM i;
- Matlab;
- O-code – Feito pelo BCPL (Linguagem de Programação Básica Combinada).
Otimizador de código
Nessa etapa, temos a examinação do código intermediário com o propósito de otimizá-lo, utilizando várias técnicas. Mesmo o mais moderno dos compiladores não consegue otimizar totalmente o código. Isso porque não é possível agradar todos os diferentes casos de design. Mesmo assim, essa parte é fundamental para a geração do código binário.
Entre as técnicas de substituição de códigos padrões, temos a eliminação de subexpressões excessivas, substituição das operações matemáticas e a repartição de laços, já que eles são estruturas que cabem uma certa otimização.
Gerador de código final
A última fase do compilador, sem nenhum segredo, é feita a geração do código para que a máquina consiga executá-lo. É uma das partes mais importantes na hora de se programar, pois, dependendo da forma que é programada pode durar até o dobro do tempo de um código ineficiente.
Nessa fase temos algumas etapas que o gerador deve passar:
- seleção da instrução – É feita a definição de cada instrução do nível da máquina para cada lógica do programa,
- ordem da instrução – Em que ordem colocar as instruções para a execução. É um dos principais pontos de otimização;
- alocação do registro – Etapa onde é feita a alocação das variáveis no registrador do processador;
- data depurada – Opcional, porém se necessário é gerado o código para a depuração.
Quais as ferramentas de construção dos compiladores?
Existem diversos tipos de ferramentas nos compiladores, todos introduzidos em tecnologias relacionadas ao computador. Esses instrumentos são criados a partir de uma linguagem específica ou feitos a partir de um algoritmo e acabam implementando algum componente do compilador.
Abaixo temos alguns exemplos:
Scanner generators
Serve para gerar analisadores léxicos a partir da entrada de dados, que são as descrições de várias expressões regulares baseadas nos tokens da linguagem. Em resumo, ele gera um autômato finito para reconhecer a expressão regular. Um exemplo disso é o programa Lex.
Mecanismos de tradução de sintaxe
É uma ferramenta para gerar códigos intermediários com três formatos de endereço na sua entrada, que contém uma árvore de análise. Esses mecanismos têm rotinas que percorrem a árvore e, em seguida, produzem o código intermediário. Sendo nesse caso, cada nó é associado a uma ou mais traduções.
Geradores de analisador
Ele produz analisadores de sintaxe (parsers) a partir da entrada que é baseada em uma descrição gramatical de linguagem de programação, até mesmo uma gramática livre de contexto. É muito útil, pois a fase de análise de sintaxe é muito complexa e consomem mais tempo manual e de compilação. Alguns desses programas são o PIC e o EQM.
Geradores de código automáticos
O código de máquina é gerado a partir de uma linguagem intermediária, sendo que cada operação deve obedecer a um conjunto de regras e, se não ocorrer erros, é gerado o código final. Nesse processo se utiliza uma comparação de templates entre as linguagens para que ocorra a substituição das instruções do código intermediário para o código de máquina.
Mecanismos de fluxo de dados
Usado na otimização do código. Aqui, as informações são coletadas a partir do usuário e do código intermediário. Logo após, é feita a análise do fluxo de dados para que seja mostrado como os valores são transmitidos de um lugar do código para outro.
Quais as diferenças entre compilador e interpretador?
Um compilador é um programa que traduz o código escrito em uma linguagem de alto nível para código de máquina. Em resumo, traduz o código legível para os seres humanos e converte para a linguagem binária que o processador entende. Além disso, o compilador deve obedecer as regras de sintaxe da linguagem, no caso, se ocorrer algum erro você deve corrigi-los.
Alguns exemplos de linguagens que utilizam compiladores:
- Java;
- C++;
- Switch;
- C#;
- Rust.
O interpretador também é um programa, porém, ele não tem o processo de traduzir todo o programa em um arquivo para conseguir rodar, e sim, ele inclui cada instrução da sua linguagem de alto nível no código de máquina. Com isso, o papel de tradução é feito de uma maneira muito mais dinâmica e que consegue incluir alguns tipos de código, tais como, scripts, códigos pré-compilados e o próprio código fonte.
Alguns exemplos de linguagens que utilizam interpretadores:
- PHP;
- JavaScript;
- Lisp;
- Matlab;
- Perl.
Vantagens e desvantagens dos compiladores
Vantagens dos compiladores:
- o código já é traduzido para a linguagem de máquina, com isso, se obtém um tempo menor de execução;
- a confiabilidade do código final, já que os processos do compilador são bem severos e conseguem validar e otimizar muito bem o código.
Desvantagens dos compiladores:
- você não pode mudar o código sem ter que recompilar todo o programa;
- mesmo com diversas otimizações, o compilador acaba sofrendo com o seu tempo de finalização do código;
- outro problema que podemos ter é no suporte de várias versões de um programa que é gerado a partir do mesmo compilador.
Vantagens e desvantagens dos interpretadores
Vantagens dos interpretadores:
- são fáceis de usar, especialmente para iniciantes;
- a execução é linha por linha, sendo assim fica mais fácil de encontrar o erro;
- nenhum código intermediário, por tanto, utiliza a memória de maneira mais inteligente.
Desvantagens dos interpretadores:
- o tempo de execução é muito grande, devido a forma de que se é traduzido;
- para ser executado deve-se ter um programa correspondente ao interpretador;
- menos seguro.
Qual usar, afinal? Compilador ou interpretador?
Para responder essa pergunta você deve entender qual o problema que a sua linguagem tenta resolver, já que, em alguns casos, faz sentido o programa ser compilado e em outros, interpretado.
Podemos pegar o exemplo do JavaScript, que é uma linguagem mais utilizada com interpretadores. Podemos então pensar que isso acontece porque os sites precisam carregar os seus conteúdos de uma maneira simples e prática por meio de pequenos ou até grandes scripts. Se algum erro for cometido no meio tempo, o programa acaba não se importando muito e continua a executar as outras tarefas.
Já em programas que não precisam ser carregados de diversas formas ou formatos, é mais prático a utilização de um compilador, um exemplo é a linguagem C++. Nela podemos escrever um programa com o objetivo de apenas resolver a tarefa que nos foi dada. Sendo assim, conseguimos gerar um código mais compacto e com poucas alternativas de execução, consequentemente, a segurança e a otimização do código compilado é superiormente mais eficiente do que o interpretado.
Compiladores e interpretadores podem ser usados juntos?
A resposta é bem simples: Podem! E uma das linguagens mais famosas que faz uso dessa estratégia é o Java. Por utilizar a compilação no seu processo de desenvolvimento e na fase do código intermediário que é gerado o bytecode, a JVM (Máquina virtual do Java) interpreta o código.
Simplificando, o bytecode é o código de máquina da máquina virtual do Java e, com isso, ela consegue algumas vantagens dos dois mundos, sendo a principal delas a portabilidade de código, já que só é necessário uma JVM em cada computador para que ocorra a execução do código.
E os Scripts, podem ser compilados?
Resumidamente, sim. No entanto, deve-se ver a necessidade do seu projeto para que faça sentido ele ser compilado. Atualmente, tanto linguagens como o JavaScript, PHP e Python podem ser compiladas com o auxílio de programas, tais como o Brython, PyDev, Kite, Komodo e WebStorm.
Porém, em alguns casos não é possível utilizar códigos compilados. Em scripts web, a execução do código é feita a partir de um interpretador do navegador e, com isso, infelizmente não pode ser diferente (pelo menos atualmente). Em outros casos também não é possível executar um script web diretamente da área de trabalho.
Quais os 4 compiladores mais usados?
Vamos ver agora os 4 compiladores mais utilizados com a linguagem C:
Borland Turbo C
Turbo C é um dos mais básicos e populares compiladores para a linguagem C. Foi introduzido em 1987. Foi muito famoso pelo seu pequeno tamanho, velocidade de compilação e baixo preço. Quando o Turbo C++ foi lançado, em 1990, ambos os compiladores foram unidos e o nome Turbo C foi descontinuado. Em 2006, ele foi relançado como um software de graça.
Tiny C Compiler
Esse compilador foi desenhado para funcionar em computadores lentos com pouco espaço e também é um compilador da linguagem C. Foi em 2005, com suporte para o Windows e algumas das suas vantagens estão listadas abaixo:
- o tamanho do arquivo é muito pequeno e de acordo com o seu criador (Fabrice Bellard) ele roda 9 vezes mais rápido que o GCC;
- inclui ferramentas que possibilitam o aumento opcional das memórias, verificador de limites e uma melhora na estabilidade do código;
- permite também a execução automática de programas durante o tempo de compilação usando apenas argumentos na linha de comando. Dessa forma, os programas são executados no UNIX, usando scripts do shell.
Portable C Compiler (PCC)
Esse outro compilador foi estabelecido muito cedo como o compilador do C em meados de 1970. Teve uma longa vida útil e foi muito famoso na época que a maioria dos compiladores eram baseados nele.
As principais vantagens do PCC dependiam da sua capacidade e previsão de probabilidade. Além disso, foi feito de uma forma que os arquivos de origem dependessem da máquina, mas nem todos, apenas alguns. Também consegue detectar erros de sintaxe e realizar validações perfeitas. A última versão lançada até o momento dessa postagem é do dia 10 de dezembro de 2014.
GCC
GNU Compiler Collection é o compilador criado pelo projeto GNU que suporta várias linguagens de programação e tem como principal objetivo ser totalmente gratuito e de código aberto. Foi lançado em 1987 com o suporte de apenas a linguagem C. Logo depois foi se expandindo lentamente para C++, Java, Android e IOS. Aqui, cada compilador de cada linguagem tem a sua própria lógica e processo para a geração dos programas.
Algumas das vantagens do GCC são:
- consegue eliminar pedaços de código morto;
- consegue remover redundâncias de código;
- melhora a otimização de Arrays.
Vimos que o compilador exerce um grande papel na vida dos desenvolvedores e que também ocorre um grande trabalho e esforço para que os códigos possam ser executados de uma maneira segura e otimizada. Sem o compilador, precisaríamos escrever o código todo em linguagem de máquina Assembly e isso causaria uma série de problemas na estrutura e padronização dos códigos.
Por isso, existem várias linguagens e vários tipos de linguagens, algumas até não utilizam o compilador e sim o interpretador, como no caso de sites que acessamos pelo navegador. Em outros casos podemos também compilar um código interpretado. Em suma, percebemos que o compilador e o interpretador exercem um grande papel no fluxo de desenvolvimento de novas aplicações e que é interessante entender o funcionamento dessas etapas.
Caso tenha ficado interessado por linguagens compiladas, recomendo a que veja mais sobre a Linguagem C: o que é e quais os principais fundamentos!