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-compilercd fortall-compiler
python main.pyO 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 tdef 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
seeenquantoavaliam expressões lógicas (com0ou1)
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.