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
E
E → id
id + E
E
E → (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
T
id * id
, então aplicamos:T → T * F
id + (T * F)
T
e F
T → 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 + T
E → T + T
T → T * F
T → F * F
F → 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 * id
E' → + 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