1. Análise Léxica

A análise léxica converte o texto-fonte em uma sequência de tokens, eliminando comentários e espaços em branco. Cada token agrupa caracteres em lexemas com significado: identificadores, números, operadores, palavras-chave.

Diagrama mostrando Análise Léxica (tokenização) vs Análise Sintática (árvore de parse)

Diagrama mostrando Análise Léxica (tokenização) vs Análise Sintática (árvore de parse)

Exemplo em Python

import re
from collections import namedtuple

*# Definição de token*
Token = namedtuple('Token', ['tipo', 'valor'])

*# Expressões regulares para tokens*
token_specs = [
	('NUM',   r'\\d+(\\.\\d*)?'),
	('ID',    r'[A-Za-z_]\\w*'),
	('OP',    r'[+\\-*/=]'),   # corrigido aqui
	('LPAR',  r'\\('),
	('RPAR',  r'\\)'),
	('SKIP',  r'[ \\t]+'),
	('MISM',  r'.'),
]

*# Compila as regex*
tok_regex = '|'.join(f'(?P<{name}>{pattern})'
                     for name, pattern in token_specs)
get_token = re.compile(tok_regex).match

def lexer(text):
    pos = 0
    tokens = []
    mo = get_token(text, pos)
    while mo:
        tipo = mo.lastgroup
        valor = mo.group(tipo)
        if tipo == 'MISM':
            raise SyntaxError(f'Caractere inesperado {valor!r}')
        if tipo != 'SKIP':
            tokens.append(Token(tipo, valor))
        pos = mo.end()
        mo = get_token(text, pos)
    return tokens

*# Demonstração*
src = 'x = 42 + y'
print(lexer(src))
*# [Token(tipo='ID', valor='x'), Token(tipo='OP', valor='='), ...]*

2. Análise Sintática

A análise sintática (parsing) consome tokens e verifica se eles obedecem à gramática da linguagem, construindo uma árvore de análise (AST).

Gramática de Expressões Aritméticas

expr   → term ((‘+’|‘-’) term)*
term   → factor ((‘*’|‘/’) factor)*
factor → NUM | ID | ‘(’ expr ‘)’

Exemplo em Python: Parser Recursivo

*# Usa os tokens gerados pelo lexer anterior*
class Parser:
    def __init__(self, tokens):
        self.tokens = tokens
        self.pos = 0

    def peek(self):
        return self.tokens[self.pos] if self.pos < len(self.tokens) else None

    def consume(self, tipo=None):
        tok = self.peek()
        if tok and (tipo is None or tok.tipo == tipo):
            self.pos += 1
            return tok
        raise SyntaxError(f'Esperava {tipo} mas obteve {tok}')

    def parse(self):
        node = self.expr()
        if self.peek():
            raise SyntaxError('Tokens extras ao final')
        return node

    def expr(self):
        node = self.term()
        while self.peek() and self.peek().valor in ('+','-'):
            op = self.consume('OP')
            right = self.term()
            node = ('binop', op.valor, node, right)
        return node

    def term(self):
        node = self.factor()
        while self.peek() and self.peek().valor in ('*','/'):
            op = self.consume('OP')
            right = self.factor()
            node = ('binop', op.valor, node, right)
        return node

    def factor(self):
        tok = self.peek()
        if tok.tipo == 'NUM':
            return ('num', self.consume('NUM').valor)
        if tok.tipo == 'ID':
            return ('id', self.consume('ID').valor)
        if tok.tipo == 'LPAR':
            self.consume('LPAR')
            node = self.expr()
            self.consume('RPAR')
            return node
        raise SyntaxError(f'Fator inválido {tok}')

*# Demonstração*
src = '3 + 4 * (2 - x)'
tokens = lexer(src)
ast = Parser(tokens).parse()
print(ast)
*# ('binop','+',('num','3'),('binop','*',('num','4'),('binop','-',('num','2'),('id','x'))))*

3. Diferenças e Interação

Fluxo:

  1. Scanner gera tokens
  2. Parser consome tokens, constrói AST
  3. Próximas fases usam AST para análise semântica e geração de código.

Conclusão: dominar esses passos é essencial para entender compiladores e ferramentas de análise de código.