E → T E'
E' → + T E' | ε
T → F T'
T' → * F T' | ε
F → ( E ) | num
+, *, parênteses e números).→ significa “pode ser substituído por”.| significa “ou” (escolha de alternativa).ε significa vazio, ou seja, não tem nada (pode parar por aí).E').E' representa repetições de + T.+, usamos ε (vazio) e paramos.T').*, paramos.num), ou(E)).num + ( num * num )E (expressão). E
E → T E': T E'
T → F T': F T' E'
F → num (porque o primeiro símbolo da entrada é num): num T' E'
num vem + e não *, usamos T' → ε: num E'
E' pode ser + T E', porque vem um +: num + T E'
T, a entrada começa com (, então em F → ( E ): num + ( E ) T' E'
E): num + ( T E' ) T' E'
T vira F T', e F → num: num + ( num T' E' ) T' E'
*, usamos T' → * F T':num + ( num * F T' E' ) T' E'
F seguinte também vira num:num + ( num * num T' E' ) T' E'
* nem + dentro, então os T' e E' viram ε:num + ( num * num )
E
/ \
T E'
| + T
F |
| F
num (E)
|
T E'
|
F T'
| \
num * F
|
num
num + ( num * num ) é válido,E
├── T
│ ├── F
│ │ └── num
│ └── T' (ε)
└── E'
├── +
├── T
│ ├── F
│ │ ├── "("
│ │ ├── E
│ │ │ ├── T
│ │ │ │ ├── F
│ │ │ │ │ └── num
│ │ │ │ └── T'
│ │ │ │ ├── *
│ │ │ │ ├── F
│ │ │ │ │ └── num
│ │ │ │ └── T' (ε)
│ │ │ └── E' (ε)
│ │ └── ")"
│ └── T' (ε)
└── E' (ε)
(+)
/ \
num (*)
/ \
num num
E → T E'E' → + T E' | εT → F T'T' → * F T' | εF → ( E ) | numnum + ( num * num ) $ ($ = fim da entrada)Regras de decisão (LL(1), por lookahead):
Em F:num→F→num;(→F→(E).
Em T':*→T'→* F T';+ou)ou$→T'→ε.
Em E':+→E'→+ T E';)ou$→E'→ε.
num + ( num * num ) é aceita. Observe como:E' implementa “repetições de + T” (soma iterativa).T' implementa “repetições de * F” (multiplicação iterativa).F decide entre número ou parênteses, permitindo precedência correta: o * é resolvido dentro dos parênteses antes do +.ET E'F T' E'num T' E'num E'num + T E'num + F T' E'num + ( E ) T' E'num + ( T E' ) T' E'num + ( F T' E' ) T' E'num + ( num T' E' ) T' E'num + ( num * F T' E' ) T' E'num + ( num * num T' E' ) T' E'num + ( num * num E' ) T' E'num + ( num * num ) T' E'num + ( num * num ) E'num + ( num * num )num * num + num $ ($ = fim) Gramática:E → T E'E' → + T E' | εT → F T'T' → * F T' | εF → ( E ) | numnum → F→num; ( → F→(E).* → T'→* F T'; + ) $ → T'→ε.+ → E'→+ T E'; ) $ → E'→ε.* após o primeiro num, o parser escolhe T'→* F T' antes de permitir qualquer + em E'. Assim, ele fecha a multiplicação primeiro e só depois processa a soma — exatamente a precedência desejada: (num * num) + num
ET E'F T' E'num T' E'num * F T' E'num * num T' E'num * num E'num * num + T E'num * num + F T' E'num * num + num T' E'num * num + num E'num * num + numnum * num + num, usando a mesma gramática LL:E
├── T
│ ├── F
│ │ └── num
│ └── T'
│ ├── *
│ ├── F
│ │ └── num
│ └── T' (ε)
└── E'
├── +
├── T
│ ├── F
│ │ └── num
│ └── T' (ε)
└── E' (ε)
(+)
/ \
(*) num
/ \
num num
num + num $E → T E'
E' → + T E' | ε
T → F T'
T' → * F T' | ε
F → ( E ) | num
E
├── T → F → num
└── E'
├── +
├── T → F → num
└── E' → ε
(+)
/ \
num num
num * num $E
├── T
│ ├── F → num
│ └── T'
│ ├── *
│ └── F → num
└── E' → ε
(*)
/ \
num num
num + num * num $num como T.+ → entra em + T E'.num * num, então escolhe T' → * F T'.(num * num) dentro do lado direito do +. (+)
/ \
num (*)
/ \
num num
( num + num ) * num $num + num.num. (*)
/ \
(+) num
/ \
num num
num * num * num $T'.num encadeados por *.( num * num ) + num $+ na raiz, (*) no lado esquerdo.num + num + num $+.+ encadeados (associatividade à esquerda).-. Sugestão: alterar apenas E':E' → (+|-) T E' | ε
num - num + num. Construa a árvore de derivação e a AST./ na gramática, modificando T':T' → (*|/) F T' | ε
num / num * num.lark ou uma função manual). Vou usar lark porque é leve e próxima da ideia de "fly parsing":from lark import Lark, Tree, Token
# Gramática LL para expressões simples
grammar = r"""
?start: expr
?expr: term expr_rest
?expr_rest: "+" term expr_rest
| "-" term expr_rest
| -> empty
?term: factor term_rest
?term_rest: "*" factor term_rest
| "/" factor term_rest
| -> empty
?factor: NUMBER
| "(" expr ")"
NUMBER: /[0-9]+/
%ignore " "
"""
parser = Lark(grammar, start="start")
entrada = "2 + 3 * 4"
tree = parser.parse(entrada)
print(tree.pretty())
"5 * 6 + 7" e compare a árvore.entrada = "(2 + 3) * 4"
tree = parser.parse(entrada)
print(tree.pretty())
try:
entrada = "2 + * 3"
tree = parser.parse(entrada)
print(tree.pretty())
except Exception as e:
print("Erro de parsing:", e)
"* 2 + 3", "2 +" e veja o erro.- em expr_rest. Teste:entrada = "10 - 3 + 2"
tree = parser.parse(entrada)
print(tree.pretty())
"10 - 3 - 2" e interprete./. Teste:entrada = "12 / 3 * 2"
tree = parser.parse(entrada)
print(tree.pretty())
"12 / (3 * 2)".Transformer do Lark:from lark import Transformer
class EvalExpr(Transformer):
def NUMBER(self, token):
return int(token)
def expr(self, items):
val = items[0]
for op, nxt in zip(items[1::2], items[2::2]):
if op == '+': val += nxt
if op == '-': val -= nxt
return val
def expr_rest(self, items):
return items
def term(self, items):
val = items[0]
for op, nxt in zip(items[1::2], items[2::2]):
if op == '*': val *= nxt
if op == '/': val //= nxt
return val
def term_rest(self, items):
return items
entrada = "2 + 3 * (4 - 1)"
tree = parser.parse(entrada)
calc = EvalExpr().transform(tree)
print("Resultado:", calc)
float em vez de int./) em vez de inteira (//)."10 / 4"."7 * (2 + 5)"."2 ++ 3".^) na gramática com maior precedência que * e /."2 + 3 * 4" vira 2 + (3 * 4)).

E → E + T | TE → T E'
E' → + T E' | εE → T E'
E' → + T E' | ε
T → F T'
T' → * F T' | ε
F → ( E ) | id
E → T E'
E' → + T E' | ε
T → F T'
T' → * F T' | ε
F → ( E ) | id
class Parser:
def __init__(self, tokens):
self.tokens = tokens
self.pos = 0
self.lookahead = tokens[0]
def match(self, expected):
if self.lookahead == expected:
self.pos += 1
if self.pos < len(self.tokens):
self.lookahead = self.tokens[self.pos]
else:
self.error(f"Esperado {expected}")
def error(self, message):
raise Exception(message)
def parse(self):
self.expr()
if self.pos < len(self.tokens):
self.error("Tokens extras após fim da expressão")
# Para a gramática:
# E → T E'
# E' → + T E' | ε
# T → F T'
# T' → * F T' | ε
# F → ( E ) | id
def expr(self):
self.term()
self.expr_prime()
def expr_prime(self):
if self.lookahead == '+':
self.match('+')
self.term()
self.expr_prime()
# ε caso (não precisa fazer nada)
def term(self):
self.factor()
self.term_prime()
def term_prime(self):
if self.lookahead == '*':
self.match('*')
self.factor()
self.term_prime()
# ε caso
def factor(self):
if self.lookahead == '(':
self.match('(')
self.expr()
self.match(')')
elif self.lookahead == 'id':
self.match('id')
else:
self.error("Esperado '(' ou 'id'")
expr → term { ('+' | '-') term }
term → factor { ('*' | '/') factor }
factor → number | '(' expr ')'
def expr(self):
# Primeiro reconhece um term
self.term()
# Depois processa sequência de + ou - seguidos de term
while self.lookahead in ('+', '-'):
operator = self.lookahead
self.match(operator)
self.term()
# Aqui poderia adicionar código para
# construir a árvore ou avaliar a expressão
self.lookahead = self.tokens[self.pos]
def match(self, expected):
if self.lookahead == expected:
self.pos += 1
if self.pos < len(self.tokens):
self.lookahead = self.tokens[self.pos]
else:
self.lookahead = '$' # Fim da entrada
else:
self.error(f"Esperado {expected}, encontrado {self.lookahead}")
def tokenize(text):
tokens = []
i = 0
while i < len(text):
# Ignora espaços em branco
if text[i].isspace():
i += 1
continue
# Operadores e parênteses
if text[i] in "+-*/()":
tokens.append(text[i])
i += 1
continue
# Números
if text[i].isdigit():
start = i
while i < len(text) and text[i].isdigit():
i += 1
tokens.append(int(text[start:i]))
continue
# Caractere desconhecido
raise Exception(f"Caractere inválido: {text[i]}")
return tokens
def error(self, message):
line = self.get_current_line()
column = self.get_current_column()
error_message = f"Erro sintático na linha {line}, coluna {column}: {message}"
# Exibir o contexto do erro
line_content = self.get_line_content(line)
pointer = ' ' * (column - 1) + '^'
print(error_message)
print(line_content)
print(pointer)
raise SyntaxError(error_message)

def expr(self):
# Primeiro reconhece um term
left = self.term()
# Depois processa sequência de + ou - seguidos de term
while self.lookahead in ('+', '-'):
operator = self.lookahead
self.match(operator)
right = self.term()
# Cria um nó para a operação binária
left = Node('BinOp', operator, [left, right])
return left
expr → term { '+' term }
term → factor { '*' factor }
factor → number | '(' expr ')'
class Parser:
def __init__(self, tokens):
self.tokens = tokens
self.pos = 0
self.lookahead = tokens[0]
def match(self, expected):
# Implementar
def expr(self):
# Implementar
def term(self):
# Implementar
def factor(self):
# Implementar
expr → term { ('+' | '-') term }
term → factor { ('*' | '/') factor }
factor → number | '(' expr ')'
stmt → if_stmt | other_stmt
if_stmt → 'if' '(' expr ')' stmt ['else' stmt]
expr → 'true' | 'false'
other_stmt → 'A' | 'B'

# Problema: recursão à esquerda
expr → expr + term | term
# Solução
expr → term expr'
expr' → + term expr' | ε
# Problema: ambiguidade no if-else
stmt → if expr then stmt | if expr then stmt else stmt
# Solução
stmt → matched_stmt | unmatched_stmt
matched_stmt → if expr then matched_stmt else matched_stmt | other_stmt
unmatched_stmt → if expr then stmt | if expr then matched_stmt else unmatched_stmt