a + 10 * (b - c).ID a, PLUS, INT 10, TIMES, LPAREN, ID b, MINUS, ID c, RPAREN.a + 10 * (b - c)
ID(a), PLUS, INT(10), TIMES, LPAREN, ID(b), MINUS, ID(c), RPAREN
E → E + E | E * E | (E) | id | int
E. (+)
/ \
a (*)
/ \
10 (-)
/ \
b c
E → E + E | E * E | ( E ) | id
id + ( id ) por derivação (escolha esquerda ou direita) até obter a sentença.E → E + E | E * E | (E) | id
id + ( id )
E.E
id + ( id ).E + E.E → E + E
E + E
EE → id
id + E
EE → (E)
id + (E)
E dentro dos parêntesesE → id
id + (id)
(+)
/ \
id (E)
|
id
id + id * id.(id + id) * id.id + (id * id).E → E + E | E * E | (E) | id
id + id * id
E
E → E * E: E * E
E por E + E: (E + E) * E
E por id: (id + id) * id
(*)
/ \
(+) id
/ \
id id
E
E → E + E: E + E
E por E * E: E + (E * E)
E por id: id + (id * id)
(+)
/ \
id (*)
/ \
id id
E → E + T | T
T → T * F | F
F → ( E ) | id
* tem maior precedência pois está em nível mais “baixo” (T/F). + é associativo à esquerda pela recursão em E → E + T.id + id * id: faça uma derivação e mostre que o resultado força id + (id * id). Resultado esperado: visualizar como a hierarquia remove a ambiguidade e fixa precedência/associatividade.E → E + T | T
T → T * F | F
F → (E) | id
F = fator → o nível mais “baixo” (um identificador ou expressão entre parênteses).T = termo → operações de multiplicação sobre fatores.E = expressão → operações de soma sobre termos.* tem maior precedência porque aparece em nível mais próximo de F. 👉 O + é associativo à esquerda porque a regra E → E + T permite expandir pela esquerda.id + id * id
E
+, começamos por:E → E + T
E (à esquerda)E → T
T → F
F → id
id + T
Tid * id, então aplicamos:T → T * F
id + (T * F)
T e FT → F → id
F → id
id + (id * id)
(+)
/ \
id (*)
/ \
id id
id + (id * id)id + id + id → (id + id) + id).E → T E'
E' → + T E' | ε
T → F T'
T' → * F T' | ε
F → ( E ) | id
{E, E', T, T', F} para checar LL(1). Resultado esperado: enxergar a transformação estrutural que viabiliza parser LL(1).E → E + T | T
T → T * F | F
F → (E) | id
E → E + T, T → T * F), o que inviabiliza análise preditiva (LL(1)).A → A α | β, reescrevemos como:A → β A'
A' → α A' | ε
E:E → T E'
E' → + T E' | ε
T:T → F T'
T' → * F T' | ε
E → T E'
E' → + T E' | ε
T → F T'
T' → * F T' | ε
F → (E) | id
E' e T') para lidar com repetições (+ e *).FIRST(F) = { (, id }FIRST(T) = { (, id }FIRST(E) = { (, id }FIRST(E') = { +, ε }FIRST(T') = { *, ε }FOLLOW(E) = { ), $ }FOLLOW(E') = { ), $ }FOLLOW(T) = { +, ), $ }FOLLOW(T') = { +, ), $ }FOLLOW(F) = { *, +, ), $ }FIRST e FOLLOW nos casos de ε-produções, a gramática é LL(1) válida.id * id + id.(id * id) + id.{E,E',T,T'}. Resultado esperado: consolidar por contraste por que transformamos a gramática.E → E + E | E * E | (E) | id
(id * id) + id:E
→ E + E
→ (E * E) + E
→ (id * id) + id
id * (id + id):E
→ E * E
→ id * (E + E)
→ id * (id + id)
E → E + T | T
T → T * F | F
F → (E) | id
id * id + id:E → E + TE → T + TT → T * FT → F * FF → id (duas vezes) → (id * id) + id (+)
/ \
(*) id
/ \
id id
E → T E'
E' → + T E' | ε
T → F T'
T' → * F T' | ε
F → (E) | id
id * id + id:E → T E'T → F T'F → id → id T'T' → * F T' → * id T'T' → ε → id * idE' → + T E' → + id T' E'T' → ε, E' → εid * id + id(id * id) + id.S → if E then S else S | if E then S | other
S → if E then S X
X → else S | ε
id + id + id e mostre que, com a gramática hierarquizada, a associação é à esquerda: (id + id) + id.( id + id ) * id e valide a árvore respeitando precedência e parênteses. Resultado esperado: confirmar associatividade e precedência na prática.
Formalmente, uma gramática G é definida como uma quádrupla G = (V, Σ, R, S), onde:
G = ({E, T, F}, {id, +, *, (, )}, P, E)
Onde P contém as seguintes produções:
E → E + T | T
T → T * F | F
F → ( E ) | id
<expr> ::= <expr> "+" <term> | <expr> "-" <term> | <term>
<term> ::= <term> "*" <factor> | <term> "/" <factor> | <factor>
<factor> ::= "(" <expr> ")" | <number> | <id>
<number> ::= [0-9]+
<id> ::= [a-zA-Z][a-zA-Z0-9]*
Gramática:
E → E + T | T
T → T * F | F
F → ( E ) | id
Derivação à esquerda de "id + id * id":
E ⇒ E + T (usando E → E + T)
⇒ T + T (usando E → T)
⇒ F + T (usando T → F)
⇒ id + T (usando F → id)
⇒ id + T * F (usando T → T * F)
⇒ id + F * F (usando T → F)
⇒ id + id * F (usando F → id)
⇒ id + id * id (usando F → id)
Gramática:
E → E + T | T
T → T * F | F
F → ( E ) | id
Derivação à direita de "id + id * id":
E ⇒ E + T (usando E → E + T)
⇒ E + T * F (usando T → T * F)
⇒ E + T * id (usando F → id)
⇒ E + F * id (usando T → F)
⇒ E + id * id (usando F → id)
⇒ T + id * id (usando E → T)
⇒ F + id * id (usando T → F)
⇒ id + id * id (usando F → id)
E → E + T | T
T → T * F | F
F → id

E → E + T | T
T → T * F | F
F → id
E → E + T | T
T → T * F | F
F → id
Derivação à esquerda:
E ⇒ E + T (usando E → E + T)
⇒ T + T (usando E → T)
⇒ F + T (usando T → F)
⇒ id + T (usando F → id, substituindo por "a")
⇒ id + T * F (usando T → T * F)
⇒ id + F * F (usando T → F)
⇒ id + id * F (usando F → id, substituindo por "b")
⇒ id + id * id (usando F → id, substituindo por "c")
E → E + T | T
T → T * F | F
F → id

E → E + T | T
T → T * F | F
F → ( E ) | id
E → E + E | E * E | ( E ) | id
E ⇒ E * E
⇒ E + E * E
⇒ id + E * E
⇒ id + id * E
⇒ id + id * id
E ⇒ E + E
⇒ id + E
⇒ id + E * E
⇒ id + id * E
⇒ id + id * id
E → E + T | T
E → T + E | T
E → E + E | E - E | T // Ambígua: não define associatividade
A → αβ₁ | αβ₂ | ... | αβₙ | γ₁ | γ₂ | ... | γₘ
A → αA' | γ₁ | γ₂ | ... | γₘ
A' → β₁ | β₂ | ... | βₙ
S → if E then S else S
| if E then S
| a
S → if E then S S'
| a
S' → else S
| ε
E → E + E | E * E | ( E ) | id
E → E + T | T
T → T * F | F
F → ( E ) | id
// Exemplo simplificado de parser recursivo em pseudocódigo
função parseE() {
parseT();
enquanto (próximoToken() == '+') {
consumirToken('+');
parseT();
}
}
função parseT() {
parseF();
enquanto (próximoToken() == '*') {
consumirToken('*');
parseF();
}
}
função parseF() {
se (próximoToken() == '(') {
consumirToken('(');
parseE();
consumirToken(')');
} senão se (próximoToken() == 'id') {
consumirToken('id');
} senão {
erro("Esperado '(' ou identificador");
}
}
E → E + T | T
função parseE() {
parseE(); // PROBLEMA: recursão infinita!
consumirToken('+');
parseT();
// OU
parseT();
}

A → A α | β
A → β A'
A' → α A' | ε
E → T E'
E' → + T E' | ε
Expr → Expr + Term | Expr - Term | Term
Term → Term * Factor | Term / Factor | Factor
Factor → (Expr) | ID
Expr → Expr + Term | ...
Term → Term * Factor | ...
Expr → Term Expr'
Expr' → + Term Expr' | - Term Expr' | ε
Term → Factor Term'
Term' → * Factor Term' | / Factor Term' | ε
Factor → (Expr) | ID
Expr → Term Expr'
Expr' → + Term Expr' | - Term Expr' | ε
Term → Factor Term'
Term' → * Factor Term' | / Factor Term' | ε
Factor → (Expr) | ID
import ply.yacc as yacc
import ply.lex as lex
# Definição do analisador léxico (simplificado)
tokens = ('ID', 'PLUS', 'MINUS', 'TIMES', 'DIVIDE', 'LPAREN', 'RPAREN')
t_PLUS = r'\+'
t_MINUS = r'-'
t_TIMES = r'\*'
t_DIVIDE = r'/'
t_LPAREN = r'\('
t_RPAREN = r'\)'
t_ID = r'[a-zA-Z][a-zA-Z0-9]*'
t_ignore = ' \t'
# Definição das regras de produção
def p_expr(p):
'''expr : term expr_prime'''
if p[2] is None: # Se expr_prime resultou em epsilon
p[0] = p[1]
else:
p[0] = p[2]
def p_expr_prime(p):
'''expr_prime : PLUS term expr_prime
| MINUS term expr_prime
| '''
if len(p) == 1: # Regra vazia (epsilon)
p[0] = None
elif p[1] == '+':
if p[3] is None: # Se o próximo expr_prime é epsilon
p[0] = ('add', p[-1], p[2])
else:
p[3][1] = ('add', p[-1], p[2])
p[0] = p[3]
# ... implementação similar para MINUS