Você está na página 1de 15

LINGUAGENS E COMPILADORES

Código Binário x Linguagens de Programação

Inicialmente, os computadores foram criados para realizar cálculos matemáticos


de forma muito mais rápida do que seres humanos são capazes. Para que isso
fosse possível, era necessário que as devidas instruções matemáticas fossem
repassadas às máquinas. Nesse momento, surgiram as ​linguagens de
programação.

Em outras palavras, para que uma instrução seja “entendida” pelo computador,
é necessário que sejam escritas em ​linguagem de máquina​, ou seja, códigos
binários formados por sequências de 0 e 1.

Isso quer dizer que para aprender a programar, preciso aprender sobre
codificação binária​? Bem, esta é uma decisão sua, mas para te tranquilizar, a
resposta é NÃO!

Programar diretamente em linguagem de máquina é sim possível, porém, é um


processo lento e difícil, praticamente inviável nos dias de hoje devido a
complexidade dos sistemas modernos. Sendo assim, para que você não precise
codificar de forma binária, existem as ​linguagens de programação. Estas são
próximas as linguagens humanas e, portanto, mais fáceis de serem lidas e
compreendidas.

O que são linguagens de programação?

Resumindo: linguagens de programação são padrões de codificação binária,


com sintaxe e semânticas específicas. Desta forma, capazes de criar
instruções para máquinas. Graças a esses conjuntos de códigos e recursos, é
possível criar programas e sistemas para resolver os mais diversos
problemas do cotidiano.
Através dela, é possível programar de uma forma que um compilador traduza as
instruções para o computador (em binário).

TIPOS DE LINGUAGENS DE PROGRAMAÇÃO

Existem diversas categorias para classificar linguagens de programação. A seguir


descreveremos 3 tipos de classificação:

Paradigma de programação
Entende-se como paradigma uma forma de fazer algo. Ou seja, paradigma de
programação é o nome que se dá a maneira como se programa, a orientação
que seus códigos irão ter, a grosso modo, é a forma utilizada para resolver
um problema computacional. Linguagens podem suportar mais de um
paradigma ​(linguagens multiparadigma)​, este a ser escolhido conforme o
problema a ser resolvido.

● Paradigma Procedural ou Imperativo


Conceito de programação que define softwares como uma sequência de
comandos para o computador executar. O nome do paradigma,
Imperativo, está ligado ao tempo verbal, onde repassamos “ordens” ao
computador: faça isso, depois isso e depois aquilo.
Exemplos:
➢C
➢ Basic
➢ Algol
➢ Ada
➢ Lua
➢ Phyton
➢ Pascal
➢ Fortran
➢ Cobol

● Paradigma Orientado a Objetos


Paradigma mais popular atualmente, trata-se de um conceito de
programação baseado no uso de componentes individuais. Estes são
chamados objetos e fazem parte da composição do software.
Exemplos:
➢ Smalltalk
➢ Java
➢ C++

● Paradigma Declarativo
Programação declarativa é um paradigma de programação baseado em
programação funcional, programação lógica ou programação restritiva. Tal
termo é utilizado para discernir tais linguagens em relação à linguagens de
programação imperativa.

A linguagem declarativa é um tipo de linguagem de programação , onde


você descreve o que meta uma tarefa tem , mas sem escrever o código
para realizar a tarefa. Este tipo de linguagem tem várias vantagens em
comparação com outros tipos , principalmente linguagens imperativas,
onde você realmente escrever código que define como um programa deve
realizar tarefas.
Exemplos:
➢ Funcionais
○ LISP
○ Scheme
○ ML
○ Haskell
➢ Lógicas
○ Prolog (baseada em regra)
➢ HTML
➢ SQL

CLASSIFICAÇÃO DAS LINGUAGENS QUANTO AO SEU NÍVEL

As linguagens também são classificadas em níveis:


● Linguagem de máquina;
● Linguagens assembly;
● Linguagens de alto nível;

Linguagem de máquina

É uma linguagem “crua”, ou seja não muda seu estado natural. Essa linguagem é
formada de string de números, definindo a realização das operações em um
computador, sendo realizado uma tarefa de cada vez.

Características da linguagem de máquina

Consistem geralmente em strings de números;


Qualquer computador entende diretamente a sua própria linguagem de
máquina;
São dependentes de máquina (pode ser utilizada em um tipo de computador);
São complicadas para a leitura do código;
Ex: +655042223, +232121234, +777798878

Linguagem Assembly
Essa linguagem consiste de abreviações de expressões em inglês que são
operações elementares, onde se originou a base da linguagem Assembly. Os
assemblers como conhecidos são programas tradutores que convertem os
primeiros programas de linguagem assembly em linguagem de máquina a
velocidade do computador. Embora o código seja mais claro para seres
humanos, ele é incompreensível para computadores até ser traduzido em
linguagem de máquina.

Ex: load basepay


add overpay
store grosspay

Linguagem Alto Nível


São instruções únicas que podem ser escritas para realizar tarefas substanciais
onde a sintaxe se aproxima a uma linguagem humana, por isso entram na
categoria das linguagens de alto nível. Os programas tradutores são conhecidos
também pelo nome de ​compiladores ​(convertem os programas de linguagem
em alto nível em linguagem de máquina)​. Esse tipo de linguagem permite aos
programas escrever instruções que se pareçam com o inglês e contém notações
matemáticas comumente utilizadas. As linguagem em C, C++, .NET e o JAVA são
desse tipo de linguagem.
(alto ou baixo). Existem aquelas em que

CONCEITOS DE LINGUAGENS DE PROGRAMAÇÃO


● Aumento da capacidade de expressar ideias
○ Limitações de uma linguagem
■ Tipos de estrutura de dados
■ Tipos de estrutura de controle
■ Tipos de abstrações que podem ser utilizadas
● Assim, as formas de algoritmos também são limitadas
● Conhecimento de recursos de linguagens de programação reduz essas
limitações
● Programadores aumentam seu poder de expressão aprendendo novas
construções de linguagens

As duas partes mais importantes na abordagem de uma linguagem de


programação são:

● Sintática: Refere-se a como escrever um programa corretamente ou usar


a notação adequada. Uma linguagem de programação é uma notação
utilizada pelo programador para especificar ações a serem executadas por
um computador
● Semântica: Uma linguagem de programação compreende um conjunto de
conceitos que um programador usa para resolver problemas de
programação. Quais os conceitos e regras da linguagem.

A história das linguagens


Existem muitas linguagens de programação hoje no mercado, das quais são
destacadas: C, Java, C++ entre outras. São linguagens que oferecem um alto
nível de portabilidade e dão suporte a inúmeros recursos de baixo nível que
muitas outras linguagens não oferecem.

Portabilidade ​é um dos pontos chaves nesse processo de desenvolvimento,


acaba ajudando na capacidade de ser compilado ou executado em diferentes
arquiteturas seja de hardware ou de software. O termo pode ser usado também
para se referir a re-escrita de um código fonte para uma outra linguagem de
computador.

Por exemplo a ​linguagem C foi criada por Dennis Ritchie, na Bell Laboratories,
em 1972, nos Estados Unidos. Esse nome foi dado porque muitos de seus
recursos e características são derivados da linguagem B BCPL (Basic CPL). O B foi
desenvolvido por Ken Thompsom, também na Bell Laboratories, em 1969, e
contou com a ajuda de Dennis Ritchie. Na verdade o B é uma simplificação da
linguagem BCPL (Basic CPL). A BCPL foi criada em 1966, também na Inglaterra,
por Martin Richards, e foi desenvolvida a partir da linguagem CPL (Combined
Programming Language ou Linguagem de Programação Combinada), criada no
laboratório de matemática de uma universidade da Inglaterra.

Inicialmente o C foi criado para desenvolver o sistema operacional UNIX, que até
à época havia sido escrito em Assembly, que é uma linguagem de baixo nível e
uma das primeiras linguagens de programação a serem criadas.

Diversos sistemas operacionais já foram escritos em C, essa linguagem ajudou a


criar outros tipos e que deu origem a uma linguagem de programação muito
importante que é o C++. Muitos usuários ainda confundem o C e o C++ por
serem linguagens muito parecidas, mas a diferença básica entre elas é que o C++
oferece todos os recursos do C e mais a programação orientada a objetos.

Por que surgem linguagens de Programação diferentes?

Porque elas resolvem os problemas com custos computacionais diferentes.


Algumas resolvem melhor os problemas matemático-analíticos, outras resolvem
melhor os problemas científicos, outras, os problemas estatísticos.

Há ainda as que são mais indicadas para desenvolver aplicativos desktop, outras
desenvolvem melhor os aplicativos de nuvem, outras celular, outras front end, e
por aí vai.

As necessidades são diferentes, portanto exigem soluções diferentes e


ferramentas diferentes.

Há também a questão da evolução de hardwares (ex.: multicor) e as linguagens


disponíveis nem sempre acompanham estas evoluções.

Diferentes pontos de vistas:


● usuários: pensa normalmente numa interface gráfica e a possibilidade
de importar uma aplicação para diferentes ambientes;
● desenvolvedores: precisa de uma linguagem expressiva e deseja uma
interface que facilite a programação;
● aplicações que podem ser desenvolvidas: precisam ser simples e
expressivas.

COMPILADOR

Compilador: Um programa de computador que traduz código escrito em uma


determinada linguagem (código-fonte) em código escrito em outra linguagem
(linguagem-objeto).
Exemplos:
● Traduzir de C++ para C
● Traduzir de Java para JVM
● Traduzir de C para C (Por que?)

Um compilador é um programa de computador (ou um grupo de programas)


que, a partir de um ​código fonte escrito em uma linguagem compilada, cria um
programa semanticamente equivalente, porém escrito em outra linguagem,
código objeto​. Classicamente, um compilador traduz um programa de uma
linguagem textual facilmente entendida por um ser humano para uma
linguagem de máquina, específica para um processador e sistema operacional.
Atualmente, porém, são comuns compiladores que geram código para uma
máquina virtual que é, depois, interpretada por um interpretador. Ele é
chamado compilador por razões históricas; nos primeiros anos da programação
automática, existiam programas que percorriam bibliotecas de sub-rotinas e as
reunia, ou compilava, as subrotinas necessárias para executar uma determinada
tarefa.

Análise léxica:
é o processo de analisar a entrada de linhas de caracteres (tal como o
código-fonte de um programa de computador) e produzir uma sequência de
símbolos chamado "símbolos léxicos" (lexical tokens), ou somente
"símbolos" (tokens), que podem ser manipulados mais facilmente por um
parser (leitor de saída).
AULA 2. Conceitos de Análise Sintática e Análise Sintática
Descendente

1. Análise Sintática
● Tem a função de combinar a lista de tokens
● Criação de uma estrutura chamada Árvore Sintática

O que é que faz análise sintática?

A ideia de análise sintática é recebendo os tokens, tentar criar uma estrutura


que a gente vai chamar de ​árvore sintática.

Porque que a gente lida com árvore aqui?

Porque nossa linguagem tem uma tendência (linguagem de natural) a ser


dependente de contexto e em uma linguagem de programação nós temos que
evitar é ​dependência de contexto, então a única dependência de contexto que
nós admitimos em uma linguagem de programação é a ​dependência a partir dos
identificadores​ ​- dos elementos que o programador cria: variáveis, tipos…

Os identificadores ​(elementos que são criados pelo programador) é que vão dar
a dependência de contexto ou seja um nome que foi criado para uma variável
que vai ser usado posteriormente, ou nome de um método, de uma classe, de
uma função, etc.
Como iremos transformar isso de uma outra maneira, esse papel acaba sendo
exercido pela ​tabela de símbolos. ​Então ​vamos receber os ​tokens e tentar
montar essa estrutura de árvore, por que esta é completamente livre de
contexto.

A análise sintática também deve rejeitar tokens inválidos.


Reportar erros sintáticos

A análise sintática é mais complexa em natureza do que a análise léxica.

Precisamos de uma linguagem mais avançada


Hierarquia de Chomsky

Hierarquia de Chomsky: ​é a classificação de gramáticas formais descrita em


1959 pelo linguista ​Noam Chomsky​. Esta classificação possui 4 níveis, sendo
que os dois últimos níveis (os níveis 2 e 3) são amplamente utilizados na
descrição de linguagem de programação e na implementação de
interpretadores e compiladores. Mais especificamente, o nível 2 é utilizado
em análise sintática (computação) e o nível 3 em análise léxica.

● Linguagens Livre de Contexto


“Constituem um conjunto de linguagens que podem ser geradas por
gramáticas livres de contextos (GLC), reconhecidas por autômatos de
pilha”

● A maioria dos construtores das LP’s são expressos em GLC (Gramáticas


livre de contexto)
○ Linguagens são projetadas a partir de GLC

● É comum dividir os construtores em categorias sintáticas que englobam


algum conceito particular.
○ Expressões: usada no cálculo de valores
○ Statements (comandos): ações que ocorrem em um fluxo
○ Declarações: propriedades dos nomes usados em outras partes do
programa

● Definição baseada em derivação para uma linguagem gerada por uma


GLC
○ Dado uma GLC G com símbolo inicial S, símbolos terminais T e
produções P, a linguagem L(G) que G gera é definida para ser o
conjunto de todas as strings de símbolos terminais que podem ser
obtidas por derivação a partir de S usando as
produções P, ou seja, o conjunto:

Dado uma gramática qualquer iremos definir quais são os terminais, quais são as
regras de derivação ou de produção e a partir disso (terminais e não terminais)
conseguiremos definir a linguagem como sendo todas as cadeias (W) do alfabeto
da linguagem, de tal maneira que a partir de um não terminal inicial de um
ponto de partida conseguiremos gerar essa cadeia usando as regras (de
derivação, regras de produção) que estão definidos naquele conjunto P sociação
Atividade semana 6 - Linguagens e Compiladores

Utilize o esquema de tradução incompleto (P é o símbolo gerador, a raiz da


gramática), o código de programa que usa a linguagem fonte definida e as
tabelas de símbolos ilustradas a seguir:
P→MD {defTam(top (tabPtr), top (desloc));

pop( TabPtr ); pop(desloc)}

M→e {t = geraTab( nil ); push( t, tabPtr ); push( 0, desloc )}

D→D;D

D → id : T {adSimb( top( tabPtr ), id.nome, T.tipo, top(desloc));

top(desloc) = top(desloc) + T.tam}

D → proc id ; N D ; {t = top( tabPtr ); defTam(t, top(desloc)); pop(tabPtr);

pop(desloc); adProc( top( tabPtr ), id.nome, t)}

M→e { INCOMPLETO }

T → int {T.tipo = int; T.tam := 4}

T → real { INCOMPLETO }

T → array [num] of T1 {T.tipo = matriz( num.val, T1.tipo ); T.tam := num.val*T1.tam)}

T → ^T1 {T.tipo = ponteiro( T1.tipo ); T.tam := 4}

x: real;

y: int;

proc P1;

A: real;

proc P2;

B: int;

C: ^real;

...

proc P3;

D: array [5] of real;

E: int;

...

end P3;

...

end P2;

...

end P1;
Pergunta 1 2 pts
Para que as tabelas de símbolos estejam corretas, quais ações devem ser
tomadas no esquema para a opção “T → real”? Qual código deve completar a
linha correspondente?
❏ T.tipo = real; T.tam := 16
❏ T.tipo = inteiro; T.tam := 8
❏ T.tipo = ponteiro(real); T.tam := 4
❏ T.tipo = ponteiro(real); T.tam := 8
❏ T.tipo = real; T.tam := 8

Pergunta 2 2 pts
Qual código completaria o esquema de tradução para gerar uma tabela de
símbolos corretamente a partir de um procedimento?
❏ t = geraTab( top( tabPtr ) ); push( 0, desloc )
❏ t = geraTab( nil ); push( t, tabPtr )
❏ t = geraTab( nil ); push( 0, desloc )
❏ t = geraTab( top( tabPtr ) ); push( t, tabPtr ); push( 0, desloc )
❏ t = geraTab( nil ); push( t, tabPtr ); push( 0, desloc )

Pergunta 3 2 pts
Se o código em questão definisse outro procedimento P4 a partir de P3, quem
seria o ancestral de P4 e qual referência deveria ser colocada na tabela de
símbolos? Considere a figura das tabelas de símbolos da questão anterior e a
complete para responder a essa questão.
❏ ​O ancestral de P4 é P3, com isso a referência de ancestralidade na
tabela (e na figura) é P3.
❏ O ancestral de P3 é P2, com isso a referência de ancestralidade na tabela
(e na figura) é P2.
❏ O ancestral de P3 é P4, com isso a referência de ancestralidade na tabela
(e na figura) é P4.
❏ O ancestral de P4 é P2, com isso a referência de ancestralidade na tabela
(e na figura) é P2.
❏ O ancestral de P4 é P1, com isso a referência de ancestralidade na tabela
(e na figura) é P1.

Pergunta 4 2 pts
Por que a variável “E” de P3 foi alocada na posição 40?
❏ Porque essa é uma variável inteira e deve ficar separada de 10 posições de
uma variável do tipo array para evitar colisões.
❏ Porque as variáveis são alocadas em posições múltiplas de 10 e é a quarta
variável.
❏ Porque cada variável é alocada na posição subsequente à variável
anterior, contando-se a partir do programa principal.
❏ Porque o tamanho de ponteiro é 4, e há 10 ponteiros.
❏ Porque a variável é a segunda na lista de variáveis e a variável anterior é
alocada em 0, portanto, basta adicionar o tamanho dessa variável (array
de cinco reais), ou seja 40 (5*8).

Pergunta 5 2 pts
Considerando a estrutura das tabelas de símbolos, o código fonte passado
permite ao procedimento P3 visualizar variáveis de P1? Como?

❏ Sim, permite. O processo é complicado em tempo de execução, mas, em


tempo de compilação, todas as variáveis são acessíveis em uma linguagem
estruturada por blocos.
❏ Não permite. Isso é impossível nessa linguagem porque o esquema prevê
modelo estático e não de blocos.
❏ Não permite, não é possível porque P3 não é ancestral direto de P1.
❏ Sim, permite. Basta acessar localmente, porque a tabela de símbolos é
única.
❏ Sim, permite. Basta realizar uma busca local, se o resultado for falso,
procure no ancestral e proceda dessa maneira até o último ancestral. Se
algum deles encontrar a variável buscada, pode-se gerar código porque o
endereço é válido.