Linguagens Formais e Automatos
Video da aula estara disponivel em breve
Linguagens Formais
Uma linguagem formal e um conjunto (potencialmente infinito) de strings formadas a partir de um alfabeto (conjunto finito de simbolos). As linguagens de programacao sao linguagens formais — cada programa valido e uma string que pertence a linguagem definida pela gramatica da linguagem.
Formalmente: dado um alfabeto Σ, uma linguagem L e um subconjunto de Σ* (todas as strings possiveis sobre Σ).
# Exemplo: linguagem dos identificadores validos em Python
# Alfabeto: letras + digitos + underscore
# Regra: comeca com letra ou _, seguido de letras, digitos ou _
import re
# Expressao regular que define a linguagem dos identificadores
pattern = r'^[a-zA-Z_][a-zA-Z0-9_]*$'
# Estas strings pertencem a linguagem:
assert re.match(pattern, "resultado")
assert re.match(pattern, "_privado")
assert re.match(pattern, "var2")
# Estas NAO pertencem:
assert not re.match(pattern, "2var") # comeca com digito
assert not re.match(pattern, "minha-var") # contem hifen
Hierarquia de Chomsky
Noam Chomsky classificou as linguagens formais em quatro tipos, cada um mais poderoso (e mais custoso de reconhecer) que o anterior:
+---------------------------------------------------+
| Tipo 0: Recursivamente Enumeraveis |
| Reconhecedor: Maquina de Turing |
| +---------------------------------------------+ |
| | Tipo 1: Sensiveis ao Contexto | |
| | Reconhecedor: Automato Linearmente Limitado | |
| | +---------------------------------------+ | |
| | | Tipo 2: Livres de Contexto | | |
| | | Reconhecedor: Automato de Pilha (PDA) | | |
| | | +--------------------------------+ | | |
| | | | Tipo 3: Regulares | | | |
| | | | Reconhecedor: Automato Finito | | | |
| | | | (DFA / NFA) | | | |
| | | +--------------------------------+ | | |
| | +---------------------------------------+ | |
| +---------------------------------------------+ |
+---------------------------------------------------+
Para compiladores, os dois tipos mais importantes sao:
- Tipo 3 (Regulares): usadas na analise LEXICA (tokens)
- Tipo 2 (Livres de Contexto): usadas na analise SINTATICA (estrutura)
Automatos Finitos Deterministicos (DFA)
Um DFA e uma maquina com um numero finito de estados que lê uma string simbolo por simbolo e decide se ela pertence a linguagem. Cada estado tem exatamente uma transicao para cada simbolo possivel.
DFA que reconhece numeros inteiros (um ou mais digitos):
digito digito
+----------+ +----------+
| v | v
---> ( q0 ) ---digito---> (( q1 )) --+
inicio aceita
q0 = estado inicial (nenhum digito lido)
q1 = estado de aceitacao (pelo menos um digito lido)
Transicoes:
q0 --[0-9]--> q1
q1 --[0-9]--> q1
q0 --[outro]--> ERRO
q1 --[outro]--> ERRO (para o DFA; na pratica, retorna o token)
# Implementacao de um DFA em Python
class DFA:
def __init__(self, states, alphabet, transitions, start, accept):
self.states = states
self.alphabet = alphabet
self.transitions = transitions # dict: (estado, simbolo) -> estado
self.start = start
self.accept = accept
def accepts(self, input_string):
current = self.start
for symbol in input_string:
if symbol not in self.alphabet:
return False
key = (current, symbol)
if key not in self.transitions:
return False
current = self.transitions[key]
return current in self.accept
# DFA para numeros inteiros
digits = set('0123456789')
transitions = {}
for d in digits:
transitions[('q0', d)] = 'q1'
transitions[('q1', d)] = 'q1'
dfa = DFA(
states={'q0', 'q1'},
alphabet=digits,
transitions=transitions,
start='q0',
accept={'q1'}
)
print(dfa.accepts("42")) # True
print(dfa.accepts("0")) # True
print(dfa.accepts("")) # False (nenhum digito)
print(dfa.accepts("12a3")) # False ('a' nao esta no alfabeto)
Automatos Finitos Nao-Deterministicos (NFA)
Um NFA e como um DFA, mas permite multiplas transicoes para o mesmo simbolo e transicoes vazias (epsilon, ε). Um NFA aceita se algum caminho possivel leva a um estado de aceitacao.
NFA que reconhece strings terminando em "ab":
a,b a b
+----------+ +----------+ +----------+
| v | v | v
---> ( q0 ) ----a----> ( q1 ) ---b----> (( q2 ))
inicio aceita
q0 pode ir para q0 (com 'a' ou 'b') OU para q1 (com 'a')
Isso e o nao-determinismo: dois caminhos possiveis com 'a' em q0
flex convertem regex para NFA, depois para DFA, para gerar lexers eficientes.
De Regex para Automatos
Expressoes regulares e automatos finitos sao equivalentes em poder: toda regex pode ser convertida para um NFA (construcao de Thompson), e todo NFA pode ser convertido para um DFA (construcao de subconjuntos).
Regex ----Thompson----> NFA ----Subconjuntos----> DFA ----Minimizacao----> DFA minimo
Exemplo: regex a(b|c)*
Passo 1 - NFA (Thompson):
+--b--+
| |
---> (q0) --a--> (q1) --+--> (q1) [loop em b|c]
| |
+--c--+
(q1) e de aceitacao
Passo 2 - DFA equivalente:
b b
+---+ +---+
v | v |
---> (q0) --a--> (q1) ----+
aceita c c
+---+ +---+
v |
(q1)
No harness.os
O formato CLAUDE.md e os arquivos de regras do harness.os sao definidos por gramaticas implicitas. Os triggers de regras usam padroes que seguem expressoes regulares:
# Exemplo de regra harness.os com trigger baseado em padroes
title: Commit Hygiene
context: git-commit
trigger: "quando o usuario pede um commit"
content: |
Nao adicionar Co-Authored-By: Claude
Usar mensagens concisas e descritivas
# O frontmatter YAML tem uma gramatica definida:
# regra ::= frontmatter corpo
# frontmatter ::= '---' NEWLINE campos '---' NEWLINE
# campos ::= campo (NEWLINE campo)*
# campo ::= CHAVE ':' ESPACO VALOR
# corpo ::= TEXTO_LIVRE
A gramatica do frontmatter YAML e regular (pode ser reconhecida por um automato finito), mas o corpo do arquivo CLAUDE.md usa Markdown, que contem estruturas livres de contexto (como listas aninhadas e blocos de codigo). Entender a hierarquia de Chomsky ajuda a escolher a ferramenta certa para parsear cada parte.
Resumo
- Linguagens formais sao conjuntos de strings definidos por regras precisas
- A hierarquia de Chomsky classifica linguagens em 4 tipos (regular, livre de contexto, sensivel ao contexto, recursivamente enumeravel)
- Compiladores usam linguagens regulares (lexica) e livres de contexto (sintatica)
- DFAs sao maquinas de estados deterministicas; NFAs permitem nao-determinismo
- Regex, NFA e DFA sao equivalentes em poder expressivo
Exercicio
Escreva a gramatica (em notacao BNF) para o frontmatter de um arquivo de regra do harness.os. A gramatica deve aceitar os campos title, context, trigger, e content, delimitados por ---. Depois, desenhe o DFA que reconhece a delimitacao do frontmatter (a sequencia --- seguida de newline).
Verifique seu entendimento
Na hierarquia de Chomsky, qual tipo de linguagem e reconhecido por automatos finitos (DFA/NFA)?