Ferramentas do usuário

Ferramentas do site


c:compiladores

Compiladores

Um compilador é um programa que traduz linguagens de programação em linguagem do nível de máquina, com a finalidade de executar uma tarefa/comando. O nome “compilador” é usado principalmente para os programas que traduzem o código fonte de uma linguagem de programação de alto nível para uma linguagem de programação de baixo nível (por exemplo, Assembly ou código de máquina). Além disso, compiladores auxiliam o programador sinalizando alguns erros de sintaxe do código.

História do Compilador

O primeiro compilador foi criado por Grace Hopper, uma das primeiras e mais importantes programadoras da História, em 1952, para uma linguagem chamada A-0. Logo depois, em 1957, foi criado por um grupo da empresa de tecnologia IBM um compilador para o Fortran. Em 1960, a primeira linguagem que compilava para várias arquiteturas foi criada: o Cobol. Rapidamente a ideia de se utilizar um compilador foi se popularizando. Isso se deve ao fato da flexibilidade e eficácia que as linguagens de alto nível conseguiam entregar aos programadores.

Funcionamento

Geralmente, quando se utliza uma linguagem de alto nível, é notável que ela contém um certo nível de abstração dos comandos com finalidade de facilitar o entendimento humano. Sendo assim, o compilador serve para ler o código escrito pelo programador e converter para uma espécie de código objeto que contém instruções para a execução no microprocessador da máquina. Resumindo, o compilador consegue entender o código da sua linguagem escolhida e converte para uma linguagem que o computador consegue executar, ou seja, códigos binários.

Fases da Compilação

Um compilador pode ser dividido em três fases: front end, middle end e back end.

Front End

É no front end que ocorrem as análises lexicais, gramaticais e semânticas. Essa fase é responsável por verificar se o código está escrito da forma correta e condiz com a gramática da linguagem. Após as análises o front end produz uma IR( Intermediate Representation) que serve de entrada para o middle end. Essa também é a fase na qual é feita a checagem de tipos em linguagens tipadas como Typescript, Java, etc.

  • Analisador Léxico

Nessa primeira fase, ocorre a separação dos textos em pequenos trechos chamados 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, afim de refinar o código e deixar somente o essencial.

  • 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. A árvore de análise sintática ou apenas árvore sintática é 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, é o código avança para as próximas etapas, caso contrário, é apresentado uma mensagem de erro no console, informando a causa e, dependendo do compilador, a linha específica do erro.

  • Analisador Semântico

Na análise semântica, ocorre 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 que se está programando, nessa fase o analisador também é responsável por 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, o qual 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. Um código intermediário, também chamado de IR, é importante pois ele possibilita que, independente da linguagem original do código, ele seja transformado, ou executado em uma máquina virtual, em algum código de máquina específico.

Segue uma lista de linguagens que podem ser consideradas como código intermediário, visto 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).

Middle End

Um middle end não é necessário para que um compilador funcione, porém ele é de extrema importância, uma vez que ele deixa o código mais rápido e produz resultados melhores.

  • Otimizador de código

Nessa etapa, ocorre a examinação do código intermediário com o propósito de otimizá-lo. É importante destacar que 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.

Back End

O back end é responsável por transformar o código intermediário otimizado, ou a própria árvore sintática caso o compilador não tenha IR, em um código de máquina específico.

  • Gerador de código final

Na última fase do compilador é feita a geração do código para que a máquina consiga executá-lo. É uma das partes mais importantes, pois, dependendo da forma que é programada pode durar até o dobro do tempo de um código ineficiente.

Gramáticas

A gramática de uma linguagem é o conjunto de regras que define como um compilador deve realizar as análises léxicas, sintáticas e semânticas. Existem diversos tipos de gramáticas, normalmente as linguagens utilizam de gramáticas livres de contexto, pois são mais simples de implementar ou porque já existem ferramentas que gerem um scanner e um parser dada uma linguagem de livre contexto, por exemplo o LLVM, que é uma caixa de ferramentas para construção de compiladores ou interpretadores.

Gramática livre de contexto

Ela é um conjunto de regras que ditam como devem ser formadas e se relacionam o conjunto de caracteres que formam a linguagem. Funciona com base na atribuição de nomes e em que esses nomes significam. Exemplo:

  • Em língua portuguesa uma Oração Simples deve ser formada por um Sujeito e um Predicado, porém ela pode estar em ordem inversa e nesse caso o predicado vem antes que o sujeito, que nesse caso tem uma vírgula separando eles. Esse Predicado deve ser um Predicado Verbal ou um Predicado Nominal. Já uma frase é composta por uma ou mais Orações Simples, devendo ser separadas por um ponto final caso sejam mais de uma.

Para podermos representar a gramática de uma melhor forma usamos uma seta para indicar do que devem ser compostos cada identificador.

Esse formato é chamado de ABNF( Augmented Backus-Naur Form) e ele utiliza aspas duplas em volta do caracteres para representar que são palavras ou caracteres e não identificadores. O caractere | é utilizado para dizer ou um ou outro, pode ser de qualquer um dos identificadores.

Dessa forma podemos definir como será escrita e entendida a linguagem, pois partindo da gramática podemos dizer como cada expressão se relaciona com outra e o que elas significam em questões lógicas.

Referências

c/compiladores.txt · Última modificação: 2023/01/22 20:05 por Gabriel Bizio