Frank Coelho de Alcantara - 2021
Cabe ao Analisador Léxico percorrer cada símbolo usado na linguagem de programação e validar conjuntos destes símbolos, strings. A cada string válida na linguagem, damos o nome de lexema.
Cada lexema será representado na memória por meio de um token.
Um token é uma estrutura de dados que contém as informações básicas do lexema.
Cabe a equipe de desenvolvimento da linguagem definir o formato do Token.
No mínimo, o token deve conter a classe do lexema (palavra chave, identificador, inteiro, float, operador, etc.) e o valor do lexema ( for, +, etc.).
Não é raro encontrar o registro da linha e da coluna onde o token se encontra no código.
O Analisador Léxico precisa saber o que é palavra chave, e quais os lexemas são válidos.
E também irá precisar armazenar os tokens em uma estrutura de dados.
Tradicionalmente, estes dois tipos de dados são armazenados em uma estrutura chamada de Tabela de Símbolos.
Uma estrutura de dados que permeia todo o processo de troca de informações entre os módulos do processo de compilação.
Deve ser uma estrutura de dados muito eficiente para escrita, busca e leitura.
Neste momento a análise assintótica se torna relevante.
Uma solução é usar uma hash table cuja informação armazenada está em estruturas específicas.
Lexema | Token |
int | Keyword |
maior | Identifier |
( | Operator |
int | Keyword |
x | Identifier |
, | Operator |
int | Keyword |
Y | Identifier |
) | Operator |
{ | Operator |
If | Keyword |
Infelizmente Expressões Regulares apresentam algumas limitações para a definição de uma linguagem de programação.
As Regex são excelentes, ainda que lentas, para identificação de lexemas.
Muito difícil fazer uma expressão regular para garantir os pares de parênteses.
Muito difícil fazer uma expressão regular para funções e objetos garantindo os escopos.
A linguagem das linguagens: Uma gramática é uma lista de regras que determina como podem ser criadas e utilizadas todas as strings de uma linguagem;
Noam Chomsky é um dos pesquisadores mais importantes nesta área.
A nós, nesta disciplina, interessam apenas as gramáticas livres de contexto e as regulares. As gramáticas livres de contextos são um superconjunto das gramáticas regulares.
Linguagem | Gramática | Identificador |
---|---|---|
Recursivamente Enumeráreis | Gramáticas Irrestritas | Máquina de Touring |
Linguagens sensíveis ao contexto | Gramáticas sensíveis ao contexto | Maq. Turing com fita limitada |
Linguagens livres de contexto | Gramáticas livres de contexto | Autômato com pilha |
Linguagens Regulares | Gramáticas regulares | Autômato finito |
Uma Gramática Formal: 4-tupla: $$𝑮 := \{𝑵, \Sigma, 𝑷, 𝑺\}$$
Uma gramática livre de contexto é uma álgebra que descreve um conjunto de strings por meio da definição da estrutura das strings que formarão a linguagem.
Esta definição é recursiva, indicando que cada regra produção pode, ou não, ser utilizada para definir novas regras de produção
$$ STMT \rightarrow \space if \space EXPRESS \space STMT \space else \space STMT$$
Os símbolos que aparecem à esquerda da seta, são não terminais e representados por letras latinas maiúsculas $(𝐴, 𝐵, 𝑆, …)$.
O símbolo inicial é o que aparece do lado esquerdo da primeira regra, no nosso caso, na maior parte das vezes: $𝑆$.
Pode ser qualquer símbolo, usaremos o $𝑆$ por convenção.
Vamos produzir a string:“aabb”
Temos $S \rightarrow \emptyset$ e aplicamos 2 $𝑺 \rightarrow 𝑨𝑺𝑩$
Temos de $S \rightarrow ASB$ e aplicamos 3 $A \rightarrow a$
Temos de $S \rightarrow aSB$ e aplicamos 4 $B \rightarrow b$
Temos de $S \rightarrow aSb$ e aplicamos 1 $𝑺 \rightarrow AB$
Temos de $S \rightarrow aABb$ e aplicamos 3 $A \rightarrow a$
Temos de $S \rightarrow aaBb$ e aplicamos 4 $B \rightarrow b$
Conseguimos: $S \rightarrow aabb$
Que pode gerar / identificar:
outracoisa
if (0) outracoisa
if (1) outracoisa else if (0) outracoisa else outracoisa
Considere a gramática definida por:
Encontre uma sequência de substituições para encontrar: $aab$
Duas classes de regras de produção.
Lineares à Direita: as regras de produção obedecem: $$𝑨 \rightarrow 𝒘𝑩 \vee 𝑨\rightarrow 𝒘 (𝒘 \in \Sigma^*)$$
Lineares à esquerda: as regras de produção obedecem: $$𝑨 \rightarrow 𝑩𝒘 \vee 𝑨 \rightarrow 𝒘 (𝒘 \in \Sigma^*)$$
Em 1957 John Backus, trabalhando na IBM, criou uma linguagem para criar linguagens.
Representa uma gramática.
É mais simples para leitura que a álgebra.
É mais simples para escrita que a álgebra.
Finalmente virou um padrão ISO/IEC 14977 em 1996.
O criador da linguagem define como, e se usará este padrão.
Símbolo | Lêmos como |
---|---|
$::=$ | “é definido como” ou “pode ser substituído por” |
$|$ | seleção / ou |
$<>$ | não terminais |
Começou muito simples e com o tempo foi sendo adaptada as necessidades dos criadores de linguagens.
Funciona segundo os princípios das regras de produção. Uma sequência recursiva de substituição de regras permitindo a criação das sentenças da linguagem.
Considere a seguinte gramática definida em BNF
Derive o seguinte programa:
$\text{begin } a = b + \text{ const } \text{ end}$
Começamos do símbolo inicial, neste caso $ <program>$ e recorrentemente vamos procurando regras de substituição na grámática definida.
A palavra parser, que usaremos com frequência, tem o sentido de percorrer, varrer.
Nesta disciplina: técnicas de percorrer uma árvore sintática validando a gramática.
Na literatura, o conceito de parser se confunde com o conceitos de análise sintática. Não são raros os parsers que fazem análise léxica, sintática e semântica.
Considerando a gramática a seguir, apresente as derivações necessárias para encontrar “abbcde”, usando Bottom-up parser.
Regra 2: $abbcde \Rightarrow aAbcde$
Regra 2: $abbcde \Rightarrow aAde;$
Regra 3: $abbcde \Rightarrow aABe;$
Regra 1: $abbcde \Rightarrow S;$
A string “abbcde” é válida nesta gramática.