Este documento apresenta detalhes da implementação do trabalho final de Compiladores, como a gramática criada, os passos para instalação das ferramentas para a execução do código e detalhes na implementação realizada para a linguagem Fortall. A linguagem possui comandos de declaração, atribuição, leitura, escrita, estrutura condicional, laços de repetição e expressões aritméticas e lógicas. O compilador foi implementado em Python 3.9+, utilizando as bibliotecas PLY (Python Lex-Yacc) para análise léxica e sintática.
A linguagem Fortall implementada no trabalho segue a seguinte gramática, escrita na notação EBNF:
prog = "programa" id ";" [ declaracoes ] "inicio" lista_comandos "fim" "." ;
declaracoes = "var" lista_ids ":" tipo ";" { lista_ids ":" tipo ";" } ;
lista_ids = id { "," id } ;
tipo = "inteiro" | "logico" ;
lista_comandos = comando ";" { comando ";" } ;
comando = atribuicao
| leitura
| escrita
| composto
| condicional
| repeticao ;
atribuicao = id ":=" expr ;
leitura = "ler" "(" lista_ids ")" | "ler" [ "(" lista_ids ")" ] ;
escrita = "escrever" "(" stringvar { "," stringvar } ")" | "escrever" [ "(" stringvar { "," stringvar } ")" ] ;
composto = "inicio" lista_comandos "fim" ";" ;
condicional = "se" exprLogico "então" comando [ "senao" comando ] ;
repeticao = "enquanto" exprLogico "faca" comando ;
expr = fator expr_tail ;
expr_tail = "+" fator expr_tail
| "-" fator expr_tail
| "*" fator expr_tail
| "/" fator expr_tail
| ;
fator = "-" fator
| "(" expr ")"
| id
| num ;
exprLogico = expr "<" expr
| expr "<=" expr
| expr ">" expr
| expr ">=" expr
| expr "=" expr
| expr "<>" expr
| id ;
stringvar = str | expr ;
- Python 3.9+
- PLY (Python Lex-Yacc)
git clone https://github.com/dbseitenfus/fortall-compiler
cd fortall-compiler
python main.py
O projeto foi dividido em três arquivos principais: main, lexer e parser. A linguagem escolhida foi Python, devido à sua facilidade de implementação e pela existência de bibliotecas auxiliares que facilitaram o processo de desenvolvimento. Embora tenham sido separados os processos de análise sintática e análise semântica, as duas implementações estão no mesmo arquivo para facilitar o processo de criação de ambas. A análise léxica e sintática foram desenvolvidas com a utilização da biblioteca PLY.
O arquivo main.py
é responsável por chamar as funções responsáveis por todo processo. Ele lê o arquivo de entrada, chama a função parse()
que realiza a análise sintática (chamando o analisador léxico) e retorna uma AST. Posteriormente, ele chama a função executar()
passando como argumento a AST, onde ocorre a análise semântica.
O conjunto de tokens reconhecidos pela linguagem são:
- Identificadores e literais: ID, NUM, STR
- Operadores: MAIS (+), MENOS (-), MULT (*), DIV (/), ATRIB (:=)
- Delimitadores e símbolos: VIRG (,), PONTOEVIRG (;), DOISPONTOS (:), PONTO (.), LPAREN ((), RPAREN ()), LBRACK ([), RBRACK (])
- Operadores relacionais: LT (<), LE (<=), GT (>), GE (>=), EQ (=), NEQ (<>)
Os tokens retornados pelo lexer são instâncias da classe LexToken
, que possui os seguintes atributos:
type
: nome do tipo do token reconhecido (ex: ID, NUM)value
: conteúdo reconhecido (int, str etc.)lineno
: número da linhalexpos
: posição absoluta no texto
reserved = {
'programa': 'PROGRAMA',
'var': 'VAR',
'inteiro': 'INTEIRO',
...
}
Cada token é especificado com regex usando o prefixo t_
.
def t_ID(t):
r'[a-zA-Z_][a-zA-Z0-9_]*'
t.type = reserved.get(t.value, 'ID')
return t
def t_newline(t):
r'\n+'
t.lexer.lineno += len(t.value)
Comentários { ... }
são ignorados. Comentários entre tokens não são aceitos.
def t_error(t):
print(f"Caractere ilegal '{t.value[0]}' na linha {t.lineno}")
t.lexer.skip(1)
Verifica se o identificador está entre as palavras reservadas:
t.type = reserved.get(t.value, 'ID')
Utiliza o módulo YACC do PLY. Constrói a AST e utiliza análise LR (shift-reduce).
Exemplo:
def p_expression_plus(p):
'expressão: expressão MAIS termo'
p[0] = p[1] + p[3]
Com classes como Program
, Bloco
, Atrib
, etc.
def p_error(p):
print(f"Erro de sintaxe na linha {p.lineno}: token '{p.value}'")
- Verifica se variáveis foram declaradas
- Verifica compatibilidade de tipos nas atribuições
- Garante que operações aritméticas sejam entre inteiros
- Garante que comparações sejam entre tipos compatíveis
se
eenquanto
avaliam expressões lógicas (com0
ou1
)
symbol_table
: mapeia identificadores para tipos
mem
: armazena valores de variáveis
Erros são tratados com raise Exception()
e exibem mensagens explicativas.
O compilador Fortall implementa as etapas tradicionais de compilação:
- Análise Léxica
- Análise Sintática
- Construção de AST
- Análise Semântica
- Execução
Ele foi desenvolvido em Python com a biblioteca PLY e serve como base sólida para projetos didáticos de compiladores.