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