Gramaticas Livres de Contexto
Video da aula estara disponivel em breve
Por que Gramaticas?
Na fase lexica, transformamos caracteres em tokens. Agora, na fase sintatica, precisamos verificar se os tokens estao organizados numa estrutura valida. Para isso, usamos gramaticas livres de contexto (Context-Free Grammars, CFGs).
Uma CFG consiste em quatro elementos:
- Terminais — os tokens produzidos pelo lexer (NUMBER, ID, PLUS, etc.)
- Nao-terminais — simbolos abstratos que representam estruturas da linguagem (expr, stmt, program)
- Producoes — regras que definem como nao-terminais podem ser expandidos
- Simbolo inicial — o nao-terminal que representa um programa completo
Notacao BNF
A Backus-Naur Form (BNF) e a notacao padrao para escrever CFGs:
# Gramatica para expressoes aritmeticas simples
# Terminais: NUMBER, ID, +, -, *, /, (, )
expr ::= expr '+' term
| expr '-' term
| term
term ::= term '*' factor
| term '/' factor
| factor
factor ::= '(' expr ')'
| NUMBER
| ID
# Esta gramatica codifica precedencia:
# * e / tem precedencia maior que + e -
# Parenteses tem precedencia maxima
Derivacoes
Uma derivacao mostra como uma string pode ser gerada a partir do simbolo inicial, aplicando producoes passo a passo:
Derivar "x + 2 * y" (derivacao mais a esquerda):
expr
=> expr '+' term (regra: expr -> expr '+' term)
=> term '+' term (regra: expr -> term)
=> factor '+' term (regra: term -> factor)
=> ID '+' term (regra: factor -> ID)
=> ID '+' term '*' factor (regra: term -> term '*' factor)
=> ID '+' factor '*' factor (regra: term -> factor)
=> ID '+' NUMBER '*' factor (regra: factor -> NUMBER)
=> ID '+' NUMBER '*' ID (regra: factor -> ID)
=> x + 2 * y
Arvore de derivacao (parse tree):
expr
/ | \
expr '+' term
| / | \
term term '*' factor
| | |
factor factor ID(y)
| |
ID(x) NUMBER(2)
Ambiguidade
Uma gramatica e ambigua se existe alguma string que pode ser derivada de duas formas diferentes (gerando duas arvores de derivacao distintas). Isso e um problema serio para compiladores.
# Gramatica AMBIGUA para expressoes:
expr ::= expr '+' expr
| expr '*' expr
| NUMBER
# A string "2 + 3 * 4" tem DUAS arvores:
# Arvore 1: (2 + 3) * 4 = 20 Arvore 2: 2 + (3 * 4) = 14
# expr expr
# / | \ / | \
# expr '*' expr expr '+' expr
# / | \ | | / | \
# expr '+' expr NUMBER(4) NUMBER(2) expr '*' expr
# | | | |
# NUMBER(2) NUMBER(3) NUMBER(3) NUMBER(4)
# SOLUCAO: usar a gramatica com precedencia (expr/term/factor)
# que garante uma unica arvore de derivacao
Eliminando Ambiguidade
Tecnicas comuns para eliminar ambiguidade:
# 1. Precedencia via niveis de producao (expr > term > factor)
# Ja vimos acima.
# 2. Associatividade via recursao
# Recursao a esquerda = associativo a esquerda
expr ::= expr '+' term # a + b + c = (a + b) + c
# Recursao a direita = associativo a direita
assign ::= ID '=' assign # a = b = c = a = (b = c)
# 3. Dangling else: "if a then if b then s1 else s2"
# Pertence ao if interno ou externo?
# Ambigua:
stmt ::= 'if' expr 'then' stmt
| 'if' expr 'then' stmt 'else' stmt
# Desambiguada (cada else casa com o if mais proximo):
stmt ::= matched | unmatched
matched ::= 'if' expr 'then' matched 'else' matched
| other_stmt
unmatched ::= 'if' expr 'then' stmt
| 'if' expr 'then' matched 'else' unmatched
No harness.os
A estrutura de uma definicao de workflow no harness.os pode ser descrita por uma CFG:
# Gramatica para definicoes de workflow do harness.os
workflow ::= header steps
header ::= 'slug' ':' IDENTIFIER NEWLINE
'title' ':' STRING NEWLINE
'description' ':' STRING NEWLINE
steps ::= step
| steps step
step ::= STEP_NUM '.' action NEWLINE
action ::= 'Call' tool_call result_clause
| 'Run' tool_call result_clause
| 'Check' condition result_clause
| 'Log' tool_call result_clause
tool_call ::= IDENTIFIER '(' arg_list ')'
arg_list ::= IDENTIFIER
| arg_list ',' IDENTIFIER
| /* vazio */
result_clause ::= '->' DESCRIPTION
| /* vazio */
condition ::= IDENTIFIER comparator VALUE
comparator ::= '==' | '!=' | '>' | '<'
Essa CFG define exatamente quais sequencias de tokens formam um workflow valido. Um parser baseado nessa gramatica pode validar workflows automaticamente e reportar erros precisos.
Resumo
- CFGs definem a estrutura sintatica usando terminais, nao-terminais, producoes e simbolo inicial
- BNF e a notacao padrao para CFGs
- Derivacoes mostram como strings sao geradas; parse trees visualizam a estrutura
- Ambiguidade ocorre quando uma string tem duas arvores de derivacao
- Precedencia e associatividade resolvem ambiguidade em expressoes
Exercicio
Escreva uma CFG completa para definicoes de workflow do harness.os. A gramatica deve cobrir: cabecalho (slug, title, description), steps numerados, acoes (Call, Run, Check, Log), tool calls com argumentos, e clausulas de resultado. Teste derivando pelo menos 2 workflows completos.
Verifique seu entendimento
Uma gramatica e ambigua quando: