Prévia do material em texto
2
Análise Léxica
Nesta semana, exploraremos a análise léxica em compiladores, um dos primeiros passos cruciais na tradução
de código-fonte para código executável. Ao compreender os fundamentos da análise léxica, você estará mais
próximo de dominar o funcionamento dos compiladores. As habilidades e competências a serem
desenvolvidas incluem a capacidade de identificar tokens, entender a importância da análise léxica na
compilação e aplicar esse conhecimento em casos práticos.
Objetivos de aprendizagem
Ao final dessa semana, você deverá ser capaz de:
• Compreender o que é análise léxica e por que ela é importante em compiladores;
• Identificar tokens em um código-fonte;
• Entender como a análise léxica é o primeiro passo na compilação de programas.
Desafio
Imagine que você está desenvolvendo um compilador para uma nova linguagem de programação. Sua tarefa
é analisar um trecho de código-fonte em C presente na ferramenta Lexer assim que ela é executada
no browser e identificar os tokens presentes. Acesse a ferramenta on-line denominada Lexer.
Revisitando Conhecimentos
Para melhor entendimento desta semana, é importante possuir conhecimentos prévios em:
• Material de apoio: Introdução à programação do zero | O Universo da Programação
• Material de apoio: Estrutura de Dados - Aula 1 - Apresentação da disciplina | Univesp
Vídeo sobre linguagens de programação.
Orientação de estudos
Aprofunde-se nos conceitos da seguinte forma:
1. Assista às videoaulas na sequência para entender o que é a análise léxica e como este é um passo
importante no processo de compilação de um programa.
2. Ao terminar de assistir às videoaulas, faça a leitura dos textos-base indicados.
3. Escolha um dos materiais de apoio como sugestão para ampliar mais seus conhecimentos.
4. Leia o Desafio e o escopo do Fórum Temático e procure discutir com seus colegas o assunto.
5. Teste seus conhecimentos sobre o assunto fazendo os Exercícios de Apoio propostos.
6. Em seguida, faça a Atividade Avaliativa da semana.
Bons estudos!
Análise Léxica – Parte 1
• Objetivos
• O que são tokens
• Expressões Regulares
• Gramática
• Operações
• Notações
https://wgrape.github.io/lexer/?lang=c
https://ava.univesp.br/bbcswebdav/pid-1552370-dt-content-rid-17802296_1/courses/COM590-2024S1B2-T001/COM590/S2.html#tab-1
https://ava.univesp.br/bbcswebdav/pid-1552370-dt-content-rid-17802296_1/courses/COM590-2024S1B2-T001/COM590/S2.html#tab-2
OBJETIVOS
● A primeira fase da compilação é a análise léxica.
● O analisador léxico utiliza o código e o divide em tokens, removendo os comentários e espaços em branco extras
usados no código.
● Ele também tem a função de verificar os tokens inválidos após criá-los.
● Se houver algum token inválido, o analisador léxico para de analisar o código e apresenta um erro.
● Tarefas principais
● Ler o código-fonte e armazená-lo em buffer para análise
● Tokenizar o código identificando e categorizando tokens
● Eliminar espaços em branco e comentários
● Gerar mensagens de erro significativas ao encontrar erros de sintaxe
● Construir uma tabela de símbolos para estágios posteriores do processo de compilação
● Fornecer uma saída bem estruturada para o analisador
O QUE SÃO TOKENS
● A sequência de caracteres em um token é conhecida como lexemas.
● Um lexema deve seguir certas regras predefinidas para ser considerado um token válido.
● Essas regras são definidas usando um padrão. Esses padrões representam o token e as expressões regulares
definem os padrões.
● Exemplos
● Palavras-chave, números, pontuação, strings e identificadores podem ser considerados tokens em qualquer
linguagem de programação.
● Especificação dos tokens
● Os tokens são especificados em conjuntos diferentes. A teoria da linguagem considera alguns conjuntos como
token específico e outros como diferentes
● Alfabetos
● Todos os números e alfabetos são considerados alfabetos hexadecimais por idioma. {az, AZ,0,1,2,3….}
● Strings
● A coleção de diferentes alfabetos que ocorre continuamente é conhecida como string.
O comprimento da string pode ser definido como o número de caracteres ou de alfabetos que ocorrem juntos.
● Símbolos Especiais
● A maioria das linguagens de alto nível contém alguns símbolos especiais
EXPRESSÕES REGULARES
● O analisador léxico somente precisa identificar ou varrer alguma parte ou um conjunto finito de tokens/strings
válidos pertencentes à linguagem. Ele procura o padrão seguindo as regras
● Uma expressão regular define o padrão finito na linguagem que contém um conjunto finito de símbolos.
● Gramática regular é a gramática definida por expressões regulares, e linguagem regular é a linguagem definida pela
gramática regular.
GRAMÁTICA
● Uma gramática é:
● Um sistema gerador de linguagens
● Um sistema de reescrita
● Uma maneira finita de representar uma linguagem
● Objetivo da Gramática
● Definir um subconjunto que forma uma determinada linguagem
● Exemplo de uma gramática
<sentença> :: = <sujeito> <verbo> <objeto>
<sujeito> :: = <substantivo> |<artigo> <substantivo> |<artigo> <adjetivo> <substantivo>
<substantivo> :: = compilador | tradutor | codigo | interpretador | programa | resultado
<artigo> :: = o | um
<adjetivo> :: = eficiente | bom | melhor | ruim :: = gera | traduz | compila | interpreta
<verbo> :: = gera| traduz | compila | interpreta
<objeto> :: = <substantivo> | <artigo> <substantivo> | <artigo> <adjetivo> <substantivo> | ε (* vazio *)
OPERAÇÕES
● Um operador é um símbolo, ou uma sequência de símbolos que representa uma operação ou cálculo específico a
ser executado em um, ou mais operandos
● Os operadores fazem parte da análise léxica, que é o processo de dividir o código-fonte em uma sequência de
tokens
● Os operadores podem ser classificados em diferentes categorias
● Operadores aritméticos: +, -, *, /
● Operadores relacionais: , <=, >=, == , !=
● Operadores lógicos: &&, ||, !
● Operadores bit a bit: &, |, ^
● Operadores de atribuição: = , +=, -= , *= , /=
NOTAÇÕES
● São símbolos ou caracteres utilizados para representar um token específico no código-fonte e tambem para
descrever a estrutura da linguagem
● São definidas por meio de expressões regulares.
● Como funciona?
1. O analisador léxico verifica o código-fonte
2. Ele combina a sequência de caracteres de entrada com a notação correspondente para gerar um token
3. Os tokens gerados pelo analisador léxico são então passados para o analisador de sintaxe, para processamento
posterior
Análise Léxica – Parte 2
ROTEIRO
• Lexema no desenho do compilador
● Visão geral
● Tipos
● Calculando lexemas
• Introdução ao Lex
LEXEMA NO DESENHO DO COMPILADOR
● Um lexema é uma sequência de caracteres de um programa, agrupados como uma única unidade.
● Quando um compilador ou interpretador lê o código-fonte de um programa, o compilador o divide em unidades
menores chamadas lexemas.
● Esses lexemas ajudarão o compilador a analisar e processar o programa com eficiência.
● Os lexemas são atribuídos a um tipo de token, que é um código numérico que representa o tipo de lexemas
● Esses tokens podem ser passados para o analisador, que pode ser usado para construir a árvore de análise com
vários métodos de análise
● Exemplo: Considere uma instrução 'a = b / 2;' na linguagem de programação C. Esta declaração será dividida em:
● Identificador: 'a'
● Operador: '='
● Identificador: 'b'
● Operador: '/'
● Literal: '2'
● Pontuação: ';'
● Tipos
● Operadores: São os símbolos que podem ser usados para realizar operações matemáticas ou lógicas como adição
(+), subtração (-), multiplicação (*), divisão (/), etc.
● Identificadores: são os nomes de variáveis, métodos e funções. Por exemplo, se houver uma instrução 'int a = 10;'
em C. Aqui 'a' é um identificador
● Pontuação: Sãoos símbolos usados para separar e agrupar diferentes partes do programa. Por exemplo, ponto e
vírgula (;), vírgula (,), colchetes ({}), etc.
● Palavras-chave: são usadas para identificar construções de programação, como loops, condicionais e funções. Por
exemplo, 'if', 'else', 'while', etc.
● Literais: são os valores que podem ser usados no código�fonte. Por exemplo, se houver uma instrução 'int a = 10;'
em C. Aqui '10' é literal. Literais podem ser de qualquer tipo de dados
● Quantos lexemas conseguimos identificar no código anterior?
● include <iostream> // 3 lexemas: "include", "", r ">"
● using namespace std; // 4 lexemas: "using", "namespace", "std", e ";"
● int main() { // 3 lexemas: "int", "main", e "()"
● int x = 10; // 5 lexemas: "int", "x", "=", "10", e ";"
● cout << "O valor de x é: " << x << endl; // 11 lexemas: "cout", "<<", ""O valor de x é: "", "<<", "x", "<<", "endl", e ";"
● return 0; // 3 lexemas: "return", "0", e ";"
● }
● Cálculo de lexemas em um programa
● Ainda que o cálculo dos lexemas em um programa seja feito pelos lexers, vamos entender melhor os lexemas, com
um exemplo simples:
● Considere o seguinte programa C++:
● #include
● using namespace std;
● int main() {
● int x = 30;
● cout << "O valor de x é: " << x << endl;
● return 0;
● }
INTRODUÇÃO AO LEX
● O que é um Lexer?
● Lexer (ou analisador léxico) é o primeiro componente no processo de compilação que produz o lexema
● O analisador léxico lê o código-fonte, caractere por caractere, e os agrupa como uma única unidade
● Na sequência, esses lexemas podem ser passados ao analisador para construir a árvore de análise
● Lex no desenho do compilador é um programa usado para gerar scanners ou analisadores léxicos, também
chamados de tokenizadores
● Esses tokenizadores identificam o padrão léxico no programa de entrada e convertem o texto de entrada na
sequência de tokens
● O código do Lex, destinado a sistemas baseados em Unix e que hoje também está presente no Linux foi
originalmente por Eric Schmidt e Mike Lesk
● Funções do Lex
● Criação do Analisador Léxico: O processo começa com a criação de um programa chamado lex.1 usando a
linguagem Lex. Este programa define as regras e padrões para reconhecer tokens no código-fonte
● Execução do compilador Lex: O programa lex.1 é então executado usando o compilador Lex. Esta etapa gera um
programa C chamado lex.yy.c
● Execução do compilador C: O compilador C é então usado para compilar o programa lex.yy.c gerado. O resultado é
um programa objeto conhecido como a.out
● Análise Léxica: O programa objeto a.out é essencialmente um analisador léxico. Quando este programa é
executado, ele pega um fluxo de entrada de código-fonte e o transforma em uma sequência de tokens com base nas
regras definidas no programa lex.1 original
Programa fonte Lex
● Um programa fonte LEX é uma coleção de instruções e padrões que são escritos na linguagem de programação LEX
● O programa fonte LEX define como esses tokens são reconhecidos e processados
● Esses tokens podem representar palavras-chave, identificadores, operadores e outras construções de linguagem
● Componentes principais
● Definições Auxiliares: Geralmente estão localizadas na "Seção de Definições" do programa fonte lex. Estas são
macros ou variáveis definidas pelo usuário que simplificam a expressão de expressões regulares e ações no programa
● Regras de tradução: são comumente encontradas na "Seção de regras" do programa fonte lex. Eles estabelecem o
mapeamento entre padrões e ações. Cada regra de tradução consiste em um padrão de expressão regular seguido
pela ação a ser executada quando esse padrão for correspondido no código-fonte de entrada
● Formato do arquivo lex
● Qualquer arquivo lex consiste nas três partes, a seguir:
● Definições: Contém declarações de definições constantes, variáveis e regulares
● Regras: Define as regras na forma de expressões regulares e ações correspondentes que o lexer deve realizar para
identificar tokens no fluxo de entrada
● Sub-rotinas do usuário: Contém as funções definidas pelo usuário que podem ser usadas pelo bloco de código de
ação na seção de regras do arquivo lex
● O arquivo lex formal tem o seguinte formato
● { definicoes }
● %%
● { regras }
● %%
● { subrotinas de usuario }
● Implementando um analisador léxico no Linux ]
● Considere um programa que conta o número de palavras digitadas pelo usuário em linha de comando
● Na seção de definição do programa, incluímos a biblioteca padrão para operações de entrada-saída e operações de
string e uma contagem variável para manter a contagem do número de palavras
● Na seção de regras, especificamos a expressão regular ([a-zA-Z0-9])*, que corresponde a qualquer string formada
por alfabetos e dígitos numéricos, como “ABCD008”, “XXX001”, etc.
● Também há uma regra para uma nova linha Quando um caractere de nova linha é encontrado, a contagem atual de
palavras é impressa e o contador é definido como zero
● Implementando um analisador léxico no Linux
%{
#include <stdio.h>
#include <string.h>
int conta = 0;
%}
/* secao de regras*/
%%
([a-zA-Z0-9])* {conta++;} /* regra para contar o numero de palavras*/
"\n" {printf("Numero total de palavras : %d\n", conta); conta = 0;}
%%
int yywrap(void){}
int main()
{ // funcao que inicia a analise
yylex();
return 0;
}
● Como executar?
● Salve o conteúdo anterior em um arquivo chamado arquivo.lex
● Na sequência digite na linha de comando: lex arquivo.lex
● Depois, também na linha de comando: gcc lex.yy.c
● Para finalizar digite:
● ./a.out
● Faça um teste e digite 3 palavras, por exemplo, com espaços entre elas e veja o resultado!
● Para finalizar o programa digite: Ctrl + C
● → Curiosidade: Olhem o conteúdo do arquivo lex.yy.c
● Para o funcionamento do lex no Linux é necessário realizar a instalação de dois pacotes com o comando a seguir:
● sudo apt-get install bison flex
● flex
● Ferramenta usada para gerar analisadores léxicos (scanners ou lexers) escrito por Vern Paxson em C por volta de
1987.
● É utilizado em conjunto com o gerador de analisador Berkeley Yacc ou o gerador de analisador GNU Bison . Flex e
Bison são mais flexíveis que Lex e Yacc e produzem código mais rápido.
Aprofundando o tema
Texto de apoio 1 - Análise léxica | José R. Malaquias
Material sobre análise léxica em compiladores e atividades práticas.
Texto de apoio 2 - Análise léxica | ICMC-USP
Introdução à análise léxica em compiladores.
Vídeo de apoio: Linguagens e compiladores: conceitos de linguagens de programação e análise léxica
| Ricardo Luís de Azevedo da Rocha
Semana 2 - Fórum Temático
https://ava.univesp.br/bbcswebdav/pid-1552374-dt-content-rid-17802290_1/courses/COM590-2024S1B2-T001/COM590/pdf/S2-aprofundando-An%C3%A1lise%20l%C3%A9xica.pdf
https://ava.univesp.br/bbcswebdav/pid-1552374-dt-content-rid-17802290_1/courses/COM590-2024S1B2-T001/COM590/pdf/S2-aprofundando-An%C3%A1lise%20l%C3%A9xica%20-%20ICMC-USP.pdf
A Importância da Análise Léxica em Compiladores
Como você vê a importância da análise léxica na construção de compiladores? Compartilhe exemplos da sua vida
profissional ou acadêmica que destacam a relevância da análise léxica. Discuta também como erros nessa fase
podem afetar a compilação. Além disso, discuta com seus colegas os tokens identificados quando acessou a
ferramenta Lexer, apresentando quantos tokens foram encontrados.
Exercícios de apoio
1. Qual é a principal função da análise léxica em um compilador?
a. Verificar a correção gramatical do código-fonte.
b. Gerar código de máquina a partir do código-fonte.
c. Identificar erros de lógica no código-fonte.
d. Converter o código-fonte em árvores de análise sintática.
e. Identificar e categorizar os tokens no código-fonte.
A principal função da análise léxica é identificar e categorizar os tokensno código-fonte. A análise
léxica lida com a segmentação do código-fonte em unidades léxicas significativas, como palavras-
chave, identificadores, números e símbolos. As assertivas incorretas referem-se a funções de outras
fases do processo de compilação, como análise sintática e geração de código de máquina. Consulte o
livro Compiladores: princípios e práticas, p. 31-33.
2. Qual dos seguintes símbolos é geralmente identificado pela análise léxica como um
token separado em linguagens de programação?
a. +
b. ||
c. %
d. =
e. []
O símbolo "+" é geralmente identificado como um token separado pela análise léxica em linguagens
de programação. Ele representa uma operação de adição e é um exemplo de um operador aritmético
que é tratado como um token distinto. As opções incorretas são frequentemente parte de tokens
compostos ou podem não ser tratadas como tokens separados pela análise léxica. Por exemplo, "||"
geralmente representa um operador lógico OR em vez de dois tokens separados. Consulte o
livro Compiladores: princípios, técnicas e ferramentas, p. 70-74.
ATIVIDADE AVALIATIVA
PERGUNTA 1
Analise o seguinte trecho de código em uma linguagem de programação fictícia e responda corretamente à
pergunta:
if (x > 5) {
y = 10;
} else {
y = 20;
}
Qual é o token gerado pelo analisador léxico para o operador "else"?
a. Palavra-chave
b. Delimitador.
c. Identificador.
d. Operador relacional.
e. Operador aritmético.
O token gerado pelo analisador léxico para a palavra "else" é uma palavra-chave, pois indica uma estrutura de
controle condicional em muitas linguagens de programação.
PERGUNTA 2
Dado o seguinte código-fonte em uma linguagem de programação:
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
Quantos tokens de identificador são gerados pelo analisador léxico nesse código?
a. 0
b. 1
c. 2
d. 3
e. 4
No código fornecido, há dois identificadores: "i" e "System". "i" é usado como uma variável, enquanto "System" é uma
referência a uma classe ou objeto.
PERGUNTA 3
Considere o seguinte trecho de código em uma linguagem de programação fictícia:
int x = 42
Qual é o token gerado pelo analisador léxico para a palavra-chave "int"?
a. Identificador.
b. Número.
c. Operador de atribuição.
d. Tipo de dado.
e. Delimitador.
O token gerado para a palavra-chave "int" é o tipo de dado, que representa o tipo de variável que está sendo
declarado.