class SymbolTable:
def __init__(self):
# Pilha de escopos (o último é o escopo atual)
self.scopes = [{}]
def enter_scope(self):
# Cria um novo escopo
self.scopes.append({})
def exit_scope(self):
# Remove o escopo atual
if len(self.scopes) > 1:
self.scopes.pop()
def insert(self, name, type, line=None):
# Insere símbolo no escopo atual
if name in self.scopes[-1]:
return False # Erro: redeclaração
self.scopes[-1][name] = {'type': type, 'line': line}
return True
def lookup(self, name):
# Busca do escopo atual para o global
for scope in reversed(self.scopes):
if name in scope:
return scope[name]
return None # Não encontrado
int a = 5;
float b = 3.14;
string c = "texto";
a = b + c; // Erro: incompatibilidade de tipos
def semantic_analysis(ast, symbol_table):
"""Realiza análise semântica na AST"""
if ast is None:
return None
# Verifica o tipo do nó atual
if ast.node_type == 'BinaryOp':
# Analisa recursivamente os operandos
left_type = semantic_analysis(ast.left, symbol_table)
right_type = semantic_analysis(ast.right, symbol_table)
# Verifica compatibilidade de tipos
if ast.op in ['+', '-', '*', '/']:
if left_type == 'int' and right_type == 'int':
return 'int'
elif left_type in ['int', 'float'] and right_type in ['int', 'float']:
return 'float'
else:
raise SemanticError(f"Operador '{ast.op}' incompatível com tipos {left_type} e {right_type}", ast.line)
elif ast.node_type == 'VarDecl':
# Registra variável na tabela de símbolos
if not symbol_table.insert(ast.name, ast.type, ast.line):
raise SemanticError(f"Variável '{ast.name}' já declarada", ast.line)
return None
# Outros tipos de nós...
x = y + 5; // Erro se 'y' não foi declarado
int a = "texto"; // Erro: string não pode ser atribuída a int
int x = 5;
float x = 3.14; // Erro: 'x' já foi declarada
int soma(int a, int b) { return a + b; }
soma(1); // Erro: argumentos insuficientes
class SemanticError(Exception):
def __init__(self, message, line=None):
self.message = message
self.line = line
super().__init__(self.format_message())
def format_message(self):
if self.line:
return f"Erro semântico na linha {self.line}: {self.message}"
return f"Erro semântico: {self.message}"
def compile(source_code):
try:
# Análise léxica
tokens = lexer.tokenize(source_code)
# Análise sintática
ast = parser.parse(tokens)
# Análise semântica
symbol_table = SymbolTable()
semantic_analysis(ast, symbol_table)
# Geração de código
code = generate_code(ast, symbol_table)
return code
except SemanticError as e:
print(e)
return None
a = b * c + d * e
t1 = b * c
t2 = d * e
t3 = t1 + t2
a = t3
class TACGenerator:
def __init__(self):
self.instructions = []
self.temp_counter = 0
self.label_counter = 0
def new_temp(self):
"""Cria um novo nome de variável temporária"""
temp = f"t{self.temp_counter}"
self.temp_counter += 1
return temp
def new_label(self):
"""Cria um novo rótulo para saltos"""
label = f"L{self.label_counter}"
self.label_counter += 1
return label
def generate(self, ast):
"""Gera código TAC a partir da AST"""
return self._generate_node(ast)
def _generate_node(self, node):
if node is None:
return None
if node.node_type == 'BinaryOp':
# Gera código para os operandos
left_result = self._generate_node(node.left)
right_result = self._generate_node(node.right)
# Cria temporário para o resultado
result = self.new_temp()
# Adiciona instrução TAC
self.instructions.append(f"{result} = {left_result} {node.op} {right_result}")
return result
elif node.node_type == 'Num':
return str(node.value)
elif node.node_type == 'Var':
return node.name
# Implementação para outros tipos de nós...
if (a > b) {
x = 1;
} else {
x = 2;
}
if_temp = a > b
if_false if_temp goto L1
x = 1
goto L2
L1:
x = 2
L2:
while (i < 10) {
sum = sum + i;
i = i + 1;
}
L1:
temp = i < 10
if_false temp goto L2
sum = sum + i
i = i + 1
goto L1
L2:
x = 5
y = x + 2 → y = 5 + 2 → y = 7
if (false) {
x = 10; // código morto, nunca executado
}
x = 5 * 3 + 2 → x = 15 + 2 → x = 17
x = y * 1 → x = y
x = y + 0 → x = y
def optimize_tac(instructions):
"""Aplica otimizações básicas no código TAC"""
optimized = []
constants = {} # Mapeia variáveis para seus valores constantes
for instr in instructions:
# Analisa a instrução
if '=' in instr:
dest, expr = instr.split('=', 1)
dest = dest.strip()
expr = expr.strip()
# Propagação de constantes
for var, value in constants.items():
expr = expr.replace(var, value)
# Dobramento de constantes
try:
# Tenta avaliar a expressão se for constante
result = str(eval(expr))
constants[dest] = result
optimized.append(f"{dest} = {result}")
except:
# Se não for constante, mantém a instrução
optimized.append(f"{dest} = {expr}")
# Se não for uma expressão constante, remove do mapeamento
if dest in constants:
del constants[dest]
else:
# Instruções que não são atribuições
optimized.append(instr)
return optimized
int a = 5;
int b = 10;
int c = a + b;
int d = c * 2;
if (a > 0) {
int e = c + d;
print(e);
}
a = 5
b = 10
c = a + b
d = c * 2
temp1 = a > 0
if_false temp1 goto L1
e = c + d
print e
L1:
a = 5
b = 10
c = 15 // a + b calculado em tempo de compilação
d = 30 // c * 2 calculado em tempo de compilação
temp1 = 1 // 5 > 0 avaliado como verdadeiro (1)
// if_false removido pois condição é sempre verdadeira
e = 45 // c + d calculado em tempo de compilação
print 45 // valor de e propagado
// L1 removido pois é código morto
def emit_python(tac_instructions):
"""Converte código TAC para Python"""
python_code = []
python_code.append("# Código gerado automaticamente")
python_code.append("# por nosso compilador")
for instr in tac_instructions:
if '=' in instr and not ('==' in instr or '<=' in instr or '>=' in instr):
# Atribuição simples
python_code.append(instr)
elif instr.startswith('if_false'):
# Instrução condicional
parts = instr.split()
condition = parts[1]
label = parts[3]
python_code.append(f"if not {condition}:")
python_code.append(f" goto {label}")
elif instr.startswith('goto'):
# Instrução de salto
label = instr.split()[1]
python_code.append(f"# goto {label}")
elif instr.startswith('L'):
# Rótulo
python_code.append(f"# {instr}:")
elif instr.startswith('print'):
# Instrução print
var = instr.split()[1]
python_code.append(f"print({var})")
else:
# Outras instruções
python_code.append(f"# {instr}")
return "\n".join(python_code)
# lexer.py
def tokenize(source_code):
# implementação...
return tokens
# parser.py
def parse(tokens):
# implementação...
return ast
# semantic.py
def analyze(ast):
# implementação...
return symbol_table
# codegen.py
def generate(ast, symbol_table):
# implementação...
return code
# compiler.py
import lexer
import parser
import semantic
import codegen
import optimizer
import os
def compile_file(input_file, output_file):
"""Compila um arquivo fonte para o arquivo de saída"""
try:
# Lê o arquivo fonte
with open(input_file, 'r') as f:
source_code = f.read()
# Pipeline de compilação
print(f"Compilando {input_file}...")
# 1. Análise léxica
print("Realizando análise léxica...")
tokens = lexer.tokenize(source_code)
# 2. Análise sintática
print("Realizando análise sintática...")
ast = parser.parse(tokens)
# 3. Análise semântica
print("Realizando análise semântica...")
symbol_table = semantic.analyze(ast)
# 4. Geração de código TAC
print("Gerando código intermediário...")
tac_code = codegen.generate_tac(ast, symbol_table)
# 5. Otimização
print("Otimizando código...")
optimized_tac = optimizer.optimize(tac_code)
# 6. Emissão de código final
print("Gerando código final...")
final_code = codegen.emit_python(optimized_tac)
# Escreve o código no arquivo de saída
with open(output_file, 'w') as f:
f.write(final_code)
print(f"Compilação concluída. Código gerado em {output_file}")
return True
except Exception as e:
print(f"Erro durante a compilação: {e}")
return False
if __name__ == "__main__":
import sys
if len(sys.argv) != 3:
print("Uso: python compiler.py arquivo_entrada.src arquivo_saida.py")
else:
compile_file(sys.argv[1], sys.argv[2])
class CompilationError(Exception):
def __init__(self, phase, message, line=None, column=None):
self.phase = phase # 'lexical', 'syntax', 'semantic', 'codegen'
self.message = message
self.line = line
self.column = column
super().__init__(self.format_message())
def format_message(self):
location = ""
if self.line is not None:
location += f" na linha {self.line}"
if self.column is not None:
location += f", coluna {self.column}"
return f"Erro {self.phase}{location}: {self.message}"
# No pipeline de compilação:
try:
# Código de compilação
pass
except CompilationError as e:
print(e)
# Registra o erro em um log
with open('compile_errors.log', 'a') as log:
log.write(f"{datetime.now()}: {str(e)}\n")
programa {
int a = 5;
int b = 10;
// Calcula a soma
int soma = a + b;
// Verifica se é maior que 10
if (soma > 10) {
print("A soma é maior que 10");
print(soma);
} else {
print("A soma é menor ou igual a 10");
}
}
# Código gerado automaticamente
# por nosso compilador
a = 5
b = 10
soma = a + b
temp1 = soma > 10
if not temp1:
goto L1
print("A soma é maior que 10")
print(soma)
goto L2
# L1:
print("A soma é menor ou igual a 10")
# L2:
// Condicional if-else
if (condição) {
bloco1
} else {
bloco2
}
// Código TAC gerado
temp = condição
if_false temp goto L1
código_bloco1
goto L2
L1:
código_bloco2
L2:
// Loop while
while (condição) {
bloco
}
// Código TAC gerado
L1:
temp = condição
if_false temp goto L2
código_bloco
goto L1
L2:
import unittest
from compiler import lexer, parser, semantic, codegen
class TestLexer(unittest.TestCase):
def test_tokenize(self):
code = "int x = 5;"
tokens = lexer.tokenize(code)
self.assertEqual(len(tokens), 5)
self.assertEqual(tokens[0].type, "TYPE")
self.assertEqual(tokens[0].value, "int")
class TestParser(unittest.TestCase):
def test_parse_declaration(self):
tokens = [
Token("TYPE", "int", 1),
Token("ID", "x", 1),
Token("ASSIGN", "=", 1),
Token("NUMBER", "5", 1),
Token("SEMICOLON", ";", 1)
]
ast = parser.parse(tokens)
self.assertEqual(ast.node_type, "Program")
self.assertEqual(len(ast.statements), 1)
self.assertEqual(ast.statements[0].node_type, "VarDecl")
# Mais testes para semântica e geração de código...
if __name__ == "__main__":
unittest.main()
# No compilador principal
def compile_file(input_file, output_file, debug=False):
if debug:
print("Modo de depuração ativado")
# Durante a análise léxica
if debug:
print("Tokens gerados:")
for token in tokens:
print(f" {token}")
# Durante a análise sintática
if debug:
print("AST gerada:")
print_ast(ast, indent=2)
# E assim por diante...
programa {
// Função para calcular fatorial
int fatorial(int n) {
if (n <= 1) {
return 1;
} else {
return n * fatorial(n - 1);
}
}
// Função para verificar se um número é primo
bool ehPrimo(int num) {
if (num <= 1) {
return false;
}
int i = 2;
while (i * i <= num) {
if (num % i == 0) {
return false;
}
i = i + 1;
}
return true;
}
// Programa principal
int numero = 5;
print("Fatorial de " + numero + " é " + fatorial(numero));
if (ehPrimo(numero)) {
print(numero + " é primo");
} else {
print(numero + " não é primo");
}
}