commit 3d687835352c662dcd440d3542d3b59fb10e3167 Author: Marcel Guinhos Date: Tue Oct 6 22:09:39 2026 +0000 First commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7b9aa56 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +build/ +*.o +*.obj +*.exe +*.out +*.stackdump +.DS_Store +Thumbs.db +desktop.ini +*.swp +*~ diff --git a/INSTRUCOES_DE_IMPLEMENTACAO.md b/INSTRUCOES_DE_IMPLEMENTACAO.md new file mode 100644 index 0000000..28735c9 --- /dev/null +++ b/INSTRUCOES_DE_IMPLEMENTACAO.md @@ -0,0 +1,51 @@ +# Instruções de Implementação do Projeto Mini Pascal em C + +spawna um subagente para implementar isso em C usando a estrutura micro-pascal/src/ micro-pascal/include/ e /build e Makefile use estilo K&R use typedefs para nomes dos tipos, use enums para os TokenKinds. use também AstKind e KeywordKind (enums). + +prefiro que ponteiros se foram usados fiquem colados no nome do tipo, assim: Ponteiro* nome_da_variavel; + +nomes de variaveis precisam ser snake_case, nomes de funções camel case e nomes de tipos PascalCase. + +adicione tests/ também + +os artefatos de testes ficam em build//.tests/... + +e o build normal: + +build//bin + +o Makefile deve suportar windows e linux. + +O Makefile deve ter help. build, run, test. + +coloque os exemplos em examples/ + +lembrando que o src tem que estar bonitinho: + +src/main.c +src/micropascal-lexer/... +src/micropascal-parser/... +... + +o include tmb: + +include/micropascal-lexer/... +src/micropascal-parser/... + +.gitignore + +Quando terminar e estiver funcionando, remova todos os comentários e em dashes e utf-8 do código, dos readmes, dos .gitignores e .gitkeeps. + +Não se coloque como coautor, coloque a mim como autor: mguinhos@gmail.com (Marcel Guinhos; https://mguinhos.github.io/ ; https://github.com/mguinhos ; meu RA (Registro Academico) da UNICAP é 00000855041 meu email academico é marcel.00000855041@unicap.br + +A disciplina é 2026.2-MINF-0203-CONSTRUCAO DE COMPILADORES-MANHÃ (SEMESTRE-TURMA-NOME DA DISCIPLINA) o nome do professor é Robinson Lins. + +certifique-se de que todos os .h estão em include/ + +Dê um git init. nome do branch deve ser main + +Dê um git commit -m "First commit" + +Adicione LICENSE.md MIT +e Adicione README.md + diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..3a62d68 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Marcel Guinhos + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..837cd77 --- /dev/null +++ b/Makefile @@ -0,0 +1,73 @@ +ifeq ($(OS),Windows_NT) + DETECTED_OS := Windows + RM := del /Q + RMDIR := rmdir /S /Q + ARCH_RAW := $(PROCESSOR_ARCHITECTURE) + BIN_EXT := .exe + MKDIR_P = if not exist "$(1)" mkdir "$(1)" + RUN_PREFIX := +else + DETECTED_OS := $(shell uname -s) + RM := rm -f + RMDIR := rm -rf + ARCH_RAW := $(shell uname -m) + BIN_EXT := + MKDIR_P = mkdir -p "$(1)" + RUN_PREFIX := ./ +endif + +ifneq (,$(filter x86_64 amd64 AMD64 x86 i386 i486 i586 i686,$(ARCH_RAW))) + ARCH := x86 +else ifneq (,$(filter aarch64 arm64 ARM64 armv7l armv6l arm,$(ARCH_RAW))) + ARCH := arm +else ifneq (,$(filter ppc64 ppc64le powerpc,$(ARCH_RAW))) + ARCH := powerpc +else + ARCH := $(ARCH_RAW) +endif + +CC ?= cc +CFLAGS ?= -std=c99 -Wall -Wextra -Iinclude + +BUILD_DIR := build/$(ARCH) +BIN_DIR := $(BUILD_DIR)/bin +TEST_DIR := $(BUILD_DIR)/.tests +BIN := $(BIN_DIR)/micropascal$(BIN_EXT) +TEST_BIN := $(TEST_DIR)/micropascal-tests$(BIN_EXT) + +LIB_SRCS := $(wildcard src/micropascal-lexer/*.c) $(wildcard src/micropascal-parser/*.c) +APP_SRCS := src/main.c $(LIB_SRCS) +TEST_SRCS := tests/test_main.c tests/test_lexer.c tests/test_parser.c $(LIB_SRCS) + +ARGS ?= examples/testaparidade.pas + +.PHONY: all help build run test clean + +all: build + +help: + @echo "micro-Pascal - alvos disponiveis" + @echo " make help mostra esta ajuda" + @echo " make build compila o compilador em $(BIN)" + @echo " make run compila e executa em uma amostra (ARGS=arquivo)" + @echo " make test compila e executa a suite de testes" + @echo " make clean remove o diretorio build" + +build: $(BIN) + +$(BIN): $(APP_SRCS) + @$(call MKDIR_P,$(BIN_DIR)) + $(CC) $(CFLAGS) $(APP_SRCS) -o $(BIN) + +$(TEST_BIN): $(TEST_SRCS) + @$(call MKDIR_P,$(TEST_DIR)) + $(CC) $(CFLAGS) $(TEST_SRCS) -o $(TEST_BIN) + +run: build + $(RUN_PREFIX)$(BIN) $(ARGS) + +test: $(TEST_BIN) + $(RUN_PREFIX)$(TEST_BIN) + +clean: + $(RMDIR) build diff --git a/MicroPascal-parte1.pdf b/MicroPascal-parte1.pdf new file mode 100644 index 0000000..33ba8bd Binary files /dev/null and b/MicroPascal-parte1.pdf differ diff --git a/README.md b/README.md new file mode 100644 index 0000000..2a60e0f --- /dev/null +++ b/README.md @@ -0,0 +1,77 @@ +# micro-Pascal + +Analisadores lexico e sintatico de um compilador para a linguagem micro-Pascal, +uma versao simplificada de Pascal, implementados em C. + +## Contexto academico + +- Disciplina: 2026.2-MINF-0203-CONSTRUCAO DE COMPILADORES-MANHA +- Professor: Robinson Lins +- Universidade Catolica de Pernambuco (UNICAP) + +## Autor + +- Marcel Guinhos +- Site: https://mguinhos.github.io/ +- GitHub: https://github.com/mguinhos +- RA UNICAP: 00000855041 +- Email academico: marcel.00000855041@unicap.br + +## Estrutura do projeto + +``` +micro-pascal/ + include/ + micropascal-lexer/ cabecalhos do lexer (token.h, lexer.h) + micropascal-parser/ cabecalhos do parser (ast.h, parser.h) + micropascal-tests/ cabecalho auxiliar dos testes + src/ + main.c ponto de entrada + micropascal-lexer/ implementacao do lexer + micropascal-parser/ implementacao do parser e da AST + tests/ codigo dos testes automatizados + examples/ programas de exemplo em micro-Pascal + Makefile build para Linux e Windows +``` + +Todos os arquivos de cabecalho residem em include/. + +## Uso + +``` +make help mostra a ajuda +make build compila o compilador +make run compila e executa em examples/testaparidade.pas +make test compila e executa a suite de testes +make clean remove o diretorio build +``` + +Os binarios ficam em build//bin e os artefatos de teste em +build//.tests. + +Para usar outro programa de entrada: + +``` +make run ARGS=examples/somaimpares.pas +``` + +Para imprimir apenas a sequencia de tokens: + +``` +build/x86/bin/micropascal examples/testaparidade.pas --tokens +``` + +## Escopo + +- Analise lexica completa, com todos os tokens da especificacao. +- Analise sintatica por descida recursiva, com a precedencia e a + associatividade definidas na especificacao. + +Mensagens de erro: + +- Léxico: Erro léxico no caracter [x] +- Sintático: Erro de sintaxe no token [lexema] + +## Licenca + +MIT. Veja LICENSE.md. diff --git a/examples/contador.pas b/examples/contador.pas new file mode 100644 index 0000000..f70b107 --- /dev/null +++ b/examples/contador.pas @@ -0,0 +1,9 @@ +program Contador; +var + i : integer; +begin + i := 0; + repeat + i := i + 1; + until i >= 10; +end. diff --git a/examples/erro.pas b/examples/erro.pas new file mode 100644 index 0000000..47f50f8 --- /dev/null +++ b/examples/erro.pas @@ -0,0 +1,6 @@ +program Erro; +var + x : integer +begin + x := 1; +end. diff --git a/examples/logica.pas b/examples/logica.pas new file mode 100644 index 0000000..e5fa28e --- /dev/null +++ b/examples/logica.pas @@ -0,0 +1,14 @@ +program Logica; +var + a, b, c : integer; +begin + a := 1; + b := 2; + c := 3; + if not a = b and c <> a or a <= c then + write('o') + ; + while a < b do + a := a + 1 + ; +end. diff --git a/examples/somaimpares.pas b/examples/somaimpares.pas new file mode 100644 index 0000000..98115b2 --- /dev/null +++ b/examples/somaimpares.pas @@ -0,0 +1,17 @@ +program SomaImpares; +var + n : integer; + i, proximoImpar, soma : integer; +begin + n := 4; + i := 0; + soma := 0; + while i < n do + begin + proximoImpar := 2*i + 1; + soma := soma + proximoImpar; + i := i + 1; + end; + write(soma); + write('\n'); +end. diff --git a/examples/testaparidade.pas b/examples/testaparidade.pas new file mode 100644 index 0000000..478463b --- /dev/null +++ b/examples/testaparidade.pas @@ -0,0 +1,12 @@ +program TestaParidade; +var + n : integer; +begin + n := 2 * 13 + 5; + if n = (n div 2)*2 then + write('p') + ; + else + write('i') + ; +end. diff --git a/examples/tipos.pas b/examples/tipos.pas new file mode 100644 index 0000000..5ac6a40 --- /dev/null +++ b/examples/tipos.pas @@ -0,0 +1,15 @@ +program Tipos; +var + a, b : integer; + c : real; + d : char; +begin + a := 1; + b := 2; + c := 1.5 + 2.5 * 2.0; + d := 'x'; + write(a); + write(b); + write(c); + write(d); +end. diff --git a/include/micropascal-lexer/lexer.h b/include/micropascal-lexer/lexer.h new file mode 100644 index 0000000..b1a7063 --- /dev/null +++ b/include/micropascal-lexer/lexer.h @@ -0,0 +1,19 @@ +#ifndef MICROPASCAL_LEXER_LEXER_H +#define MICROPASCAL_LEXER_LEXER_H + +#include "micropascal-lexer/token.h" + +typedef struct Lexer { + const char* fonte; + size_t tamanho; + size_t posicao; + int linha; + int coluna; + int houve_erro; +} Lexer; + +void lexerInicializar(Lexer* lexer, const char* fonte); +Token lexerProximoToken(Lexer* lexer); +void tokenLiberar(Token* token); + +#endif diff --git a/include/micropascal-lexer/token.h b/include/micropascal-lexer/token.h new file mode 100644 index 0000000..90bd683 --- /dev/null +++ b/include/micropascal-lexer/token.h @@ -0,0 +1,60 @@ +#ifndef MICROPASCAL_LEXER_TOKEN_H +#define MICROPASCAL_LEXER_TOKEN_H + +#include + +typedef enum TokenKind { + TOKEN_EOF, + TOKEN_IDENTIFICADOR, + TOKEN_INTEIRO_LITERAL, + TOKEN_REAL_LITERAL, + TOKEN_CHAR_LITERAL, + TOKEN_MENOR, + TOKEN_MAIOR, + TOKEN_MENOR_IGUAL, + TOKEN_MAIOR_IGUAL, + TOKEN_IGUAL, + TOKEN_DIFERENTE, + TOKEN_MAIS, + TOKEN_MENOS, + TOKEN_MULTIPLICACAO, + TOKEN_DIVISAO, + TOKEN_DIV, + TOKEN_AND, + TOKEN_OR, + TOKEN_NOT, + TOKEN_ATRIBUICAO, + TOKEN_ABRE_PARENTESES, + TOKEN_FECHA_PARENTESES, + TOKEN_VIRGULA, + TOKEN_PONTO_VIRGULA, + TOKEN_PONTO, + TOKEN_DOIS_PONTOS, + TOKEN_PROGRAM, + TOKEN_IF, + TOKEN_THEN, + TOKEN_ELSE, + TOKEN_WHILE, + TOKEN_DO, + TOKEN_REPEAT, + TOKEN_UNTIL, + TOKEN_INTEGER, + TOKEN_REAL, + TOKEN_CHAR, + TOKEN_BEGIN, + TOKEN_END, + TOKEN_WRITE, + TOKEN_VAR, + TOKEN_ERRO +} TokenKind; + +typedef struct Token { + TokenKind kind; + char* lexema; + int linha; + int coluna; +} Token; + +const char* tokenKindNome(TokenKind kind); + +#endif diff --git a/include/micropascal-parser/ast.h b/include/micropascal-parser/ast.h new file mode 100644 index 0000000..e2b1b04 --- /dev/null +++ b/include/micropascal-parser/ast.h @@ -0,0 +1,31 @@ +#ifndef MICROPASCAL_PARSER_AST_H +#define MICROPASCAL_PARSER_AST_H + +typedef enum AstKind { + AST_PROGRAMA, + AST_SECAO_VAR, + AST_DECL_VAR, + AST_TIPO, + AST_BLOCO, + AST_LISTA_COMANDOS, + AST_ATRIBUICAO, + AST_ITERACAO, + AST_DECISAO, + AST_ESCRITA, + AST_EXPRESSAO_BINARIA, + AST_EXPRESSAO_UNARIA, + AST_EXPRESSAO_PARENTESES, + AST_EXPRESSAO_LITERAL, + AST_EXPRESSAO_IDENTIFICADOR +} AstKind; + +typedef struct AstNo { + AstKind kind; + char* valor; + struct AstNo* esquerda; + struct AstNo* direita; + struct AstNo* terceiro; + struct AstNo* proximo; +} AstNo; + +#endif diff --git a/include/micropascal-parser/parser.h b/include/micropascal-parser/parser.h new file mode 100644 index 0000000..9107739 --- /dev/null +++ b/include/micropascal-parser/parser.h @@ -0,0 +1,21 @@ +#ifndef MICROPASCAL_PARSER_PARSER_H +#define MICROPASCAL_PARSER_PARSER_H + +#include "micropascal-lexer/lexer.h" +#include "micropascal-parser/ast.h" + +typedef struct Parser { + Lexer* lexer; + Token atual; + int houve_erro; + int total_erros; + int silencioso; +} Parser; + +void parserInicializar(Parser* parser, Lexer* lexer); +void parserSilenciarErros(Parser* parser, int silenciar); +AstNo* parserAnalisarPrograma(Parser* parser); +void astLiberar(AstNo* no); +void astImprimir(const AstNo* no, int profundidade); + +#endif diff --git a/include/micropascal-tests/testes.h b/include/micropascal-tests/testes.h new file mode 100644 index 0000000..a59e15b --- /dev/null +++ b/include/micropascal-tests/testes.h @@ -0,0 +1,26 @@ +#ifndef MICROPASCAL_TESTS_TESTES_H +#define MICROPASCAL_TESTS_TESTES_H + +#include + +typedef struct ResultadoTeste { + int total; + int passaram; + int falharam; +} ResultadoTeste; + +void runLexerTests(ResultadoTeste* resultado_testes); +void runParserTests(ResultadoTeste* resultado_testes); + +#define TESTE_ASSERT(resultado_testes, condicao, descricao) \ + do { \ + (resultado_testes)->total++; \ + if (condicao) { \ + (resultado_testes)->passaram++; \ + } else { \ + (resultado_testes)->falharam++; \ + printf("FALHOU: %s (linha %d)\n", descricao, __LINE__); \ + } \ + } while (0) + +#endif diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..85de655 --- /dev/null +++ b/src/main.c @@ -0,0 +1,103 @@ +#include "micropascal-lexer/lexer.h" +#include "micropascal-parser/parser.h" + +#include +#include +#include + +static char* lerArquivo(const char* caminho, long* tamanho) +{ + FILE* arquivo = fopen(caminho, "rb"); + char* conteudo; + if (arquivo == NULL) { + return NULL; + } + fseek(arquivo, 0, SEEK_END); + *tamanho = ftell(arquivo); + fseek(arquivo, 0, SEEK_SET); + conteudo = (char*)malloc((size_t)(*tamanho) + 1); + if (conteudo == NULL) { + fclose(arquivo); + return NULL; + } + if (fread(conteudo, 1, (size_t)(*tamanho), arquivo) != (size_t)(*tamanho)) { + free(conteudo); + fclose(arquivo); + return NULL; + } + conteudo[*tamanho] = '\0'; + fclose(arquivo); + return conteudo; +} + +static void imprimirTokens(const char* fonte) +{ + Lexer lexer; + Token token; + lexerInicializar(&lexer, fonte); + do { + token = lexerProximoToken(&lexer); + if (token.kind == TOKEN_ERRO) { + tokenLiberar(&token); + break; + } + printf("%s [%s]\n", tokenKindNome(token.kind), token.lexema); + if (token.kind == TOKEN_EOF) { + tokenLiberar(&token); + break; + } + tokenLiberar(&token); + } while (1); +} + +int main(int argc, char** argv) +{ + const char* caminho; + const char* modo; + char* fonte; + long tamanho = 0; + + if (argc < 2) { + printf("Uso: micropascal [--tokens|--ast]\n"); + return 1; + } + + caminho = argv[1]; + modo = "--ast"; + if (argc >= 3) { + modo = argv[2]; + } + + fonte = lerArquivo(caminho, &tamanho); + if (fonte == NULL) { + printf("Nao foi possivel abrir o arquivo [%s]\n", caminho); + return 1; + } + + if (strcmp(modo, "--tokens") == 0) { + imprimirTokens(fonte); + free(fonte); + return 0; + } + + { + Lexer lexer; + Parser parser; + AstNo* programa; + lexerInicializar(&lexer, fonte); + parserInicializar(&parser, &lexer); + programa = parserAnalisarPrograma(&parser); + tokenLiberar(&parser.atual); + if (parser.houve_erro) { + astLiberar(programa); + free(fonte); + return 1; + } + printf("Analise sintatica concluida com sucesso.\n"); + astImprimir(programa, 0); + astLiberar(programa); + } + + free(fonte); + return 0; +} diff --git a/src/micropascal-lexer/lexer.c b/src/micropascal-lexer/lexer.c new file mode 100644 index 0000000..5b165b6 --- /dev/null +++ b/src/micropascal-lexer/lexer.c @@ -0,0 +1,333 @@ +#include "micropascal-lexer/lexer.h" + +#include +#include +#include +#include + +void lexerInicializar(Lexer* lexer, const char* fonte) +{ + lexer->fonte = fonte; + lexer->tamanho = strlen(fonte); + lexer->posicao = 0; + lexer->linha = 1; + lexer->coluna = 1; + lexer->houve_erro = 0; +} + +static int lexerAcabou(const Lexer* lexer) +{ + return lexer->posicao >= lexer->tamanho; +} + +static char lexerEspiar(const Lexer* lexer, size_t deslocamento) +{ + size_t indice = lexer->posicao + deslocamento; + if (indice >= lexer->tamanho) { + return '\0'; + } + return lexer->fonte[indice]; +} + +static char lexerAvancar(Lexer* lexer) +{ + char atual = lexer->fonte[lexer->posicao]; + lexer->posicao++; + if (atual == '\n') { + lexer->linha++; + lexer->coluna = 1; + } else { + lexer->coluna++; + } + return atual; +} + +static int ehLetra(char c) +{ + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; +} + +static int ehDigito(char c) +{ + return c >= '0' && c <= '9'; +} + +static char* lexerCopiarIntervalo(const Lexer* lexer, size_t inicio, size_t fim) +{ + size_t comprimento = fim - inicio; + char* resultado = (char*)malloc(comprimento + 1); + if (resultado == NULL) { + return NULL; + } + memcpy(resultado, lexer->fonte + inicio, comprimento); + resultado[comprimento] = '\0'; + return resultado; +} + +static Token lexerCriarToken(const Lexer* lexer, TokenKind kind, size_t inicio, int linha, int coluna) +{ + Token token; + token.kind = kind; + token.lexema = lexerCopiarIntervalo(lexer, inicio, lexer->posicao); + token.linha = linha; + token.coluna = coluna; + return token; +} + +static TokenKind lexerPalavraReservada(const char* lexema) +{ + if (strcmp(lexema, "program") == 0) { + return TOKEN_PROGRAM; + } + if (strcmp(lexema, "if") == 0) { + return TOKEN_IF; + } + if (strcmp(lexema, "then") == 0) { + return TOKEN_THEN; + } + if (strcmp(lexema, "else") == 0) { + return TOKEN_ELSE; + } + if (strcmp(lexema, "while") == 0) { + return TOKEN_WHILE; + } + if (strcmp(lexema, "do") == 0) { + return TOKEN_DO; + } + if (strcmp(lexema, "repeat") == 0) { + return TOKEN_REPEAT; + } + if (strcmp(lexema, "until") == 0) { + return TOKEN_UNTIL; + } + if (strcmp(lexema, "integer") == 0) { + return TOKEN_INTEGER; + } + if (strcmp(lexema, "real") == 0) { + return TOKEN_REAL; + } + if (strcmp(lexema, "char") == 0) { + return TOKEN_CHAR; + } + if (strcmp(lexema, "begin") == 0) { + return TOKEN_BEGIN; + } + if (strcmp(lexema, "end") == 0) { + return TOKEN_END; + } + if (strcmp(lexema, "write") == 0) { + return TOKEN_WRITE; + } + if (strcmp(lexema, "var") == 0) { + return TOKEN_VAR; + } + if (strcmp(lexema, "div") == 0) { + return TOKEN_DIV; + } + if (strcmp(lexema, "and") == 0) { + return TOKEN_AND; + } + if (strcmp(lexema, "or") == 0) { + return TOKEN_OR; + } + if (strcmp(lexema, "not") == 0) { + return TOKEN_NOT; + } + return TOKEN_IDENTIFICADOR; +} + +static Token lexerErro(Lexer* lexer, char caractere, int linha, int coluna) +{ + Token token; + lexer->houve_erro = 1; + token.kind = TOKEN_ERRO; + printf("Erro léxico no caracter [%c]\n", caractere); + token.lexema = lexerCopiarIntervalo(lexer, lexer->posicao, lexer->posicao); + token.linha = linha; + token.coluna = coluna; + return token; +} + +static Token lexerLerIdentificador(Lexer* lexer) +{ + size_t inicio = lexer->posicao; + int linha = lexer->linha; + int coluna = lexer->coluna; + Token token; + while (!lexerAcabou(lexer) && (ehLetra(lexerEspiar(lexer, 0)) || ehDigito(lexerEspiar(lexer, 0)))) { + lexerAvancar(lexer); + } + token = lexerCriarToken(lexer, TOKEN_IDENTIFICADOR, inicio, linha, coluna); + token.kind = lexerPalavraReservada(token.lexema); + return token; +} + +static Token lexerLerNumero(Lexer* lexer) +{ + size_t inicio = lexer->posicao; + int linha = lexer->linha; + int coluna = lexer->coluna; + TokenKind kind = TOKEN_INTEIRO_LITERAL; + if (lexerEspiar(lexer, 0) == '.') { + lexerAvancar(lexer); + while (!lexerAcabou(lexer) && ehDigito(lexerEspiar(lexer, 0))) { + lexerAvancar(lexer); + } + return lexerCriarToken(lexer, TOKEN_REAL_LITERAL, inicio, linha, coluna); + } + while (!lexerAcabou(lexer) && ehDigito(lexerEspiar(lexer, 0))) { + lexerAvancar(lexer); + } + if (lexerEspiar(lexer, 0) == '.' && ehDigito(lexerEspiar(lexer, 1))) { + kind = TOKEN_REAL_LITERAL; + lexerAvancar(lexer); + while (!lexerAcabou(lexer) && ehDigito(lexerEspiar(lexer, 0))) { + lexerAvancar(lexer); + } + } + return lexerCriarToken(lexer, kind, inicio, linha, coluna); +} + +static Token lexerLerChar(Lexer* lexer) +{ + size_t inicio = lexer->posicao; + int linha = lexer->linha; + int coluna = lexer->coluna; + char conteudo; + lexerAvancar(lexer); + if (lexerAcabou(lexer)) { + return lexerErro(lexer, '\'', linha, coluna); + } + conteudo = lexerEspiar(lexer, 0); + if (conteudo == '\\') { + char escapado = lexerEspiar(lexer, 1); + if (escapado == 'n' || escapado == 't') { + lexerAvancar(lexer); + lexerAvancar(lexer); + } else { + return lexerErro(lexer, conteudo, linha, coluna); + } + } else if (ehLetra(conteudo) || ehDigito(conteudo)) { + lexerAvancar(lexer); + } else { + return lexerErro(lexer, conteudo, linha, coluna); + } + if (lexerEspiar(lexer, 0) != '\'') { + return lexerErro(lexer, lexerEspiar(lexer, 0), linha, coluna); + } + lexerAvancar(lexer); + return lexerCriarToken(lexer, TOKEN_CHAR_LITERAL, inicio, linha, coluna); +} + +Token lexerProximoToken(Lexer* lexer) +{ + size_t inicio; + int linha; + int coluna; + char atual; + Token token; + + while (!lexerAcabou(lexer)) { + atual = lexerEspiar(lexer, 0); + if (atual == ' ' || atual == '\n' || atual == '\t' || atual == '\r') { + lexerAvancar(lexer); + continue; + } + break; + } + + if (lexerAcabou(lexer)) { + token.kind = TOKEN_EOF; + token.lexema = lexerCopiarIntervalo(lexer, lexer->posicao, lexer->posicao); + token.linha = lexer->linha; + token.coluna = lexer->coluna; + return token; + } + + inicio = lexer->posicao; + linha = lexer->linha; + coluna = lexer->coluna; + atual = lexerEspiar(lexer, 0); + + if (ehLetra(atual)) { + return lexerLerIdentificador(lexer); + } + if (ehDigito(atual)) { + return lexerLerNumero(lexer); + } + if (atual == '\'') { + return lexerLerChar(lexer); + } + if (atual == '.' && ehDigito(lexerEspiar(lexer, 1))) { + return lexerLerNumero(lexer); + } + + switch (atual) { + case '<': + lexerAvancar(lexer); + if (lexerEspiar(lexer, 0) == '=') { + lexerAvancar(lexer); + return lexerCriarToken(lexer, TOKEN_MENOR_IGUAL, inicio, linha, coluna); + } + if (lexerEspiar(lexer, 0) == '>') { + lexerAvancar(lexer); + return lexerCriarToken(lexer, TOKEN_DIFERENTE, inicio, linha, coluna); + } + return lexerCriarToken(lexer, TOKEN_MENOR, inicio, linha, coluna); + case '>': + lexerAvancar(lexer); + if (lexerEspiar(lexer, 0) == '=') { + lexerAvancar(lexer); + return lexerCriarToken(lexer, TOKEN_MAIOR_IGUAL, inicio, linha, coluna); + } + return lexerCriarToken(lexer, TOKEN_MAIOR, inicio, linha, coluna); + case '=': + lexerAvancar(lexer); + return lexerCriarToken(lexer, TOKEN_IGUAL, inicio, linha, coluna); + case '+': + lexerAvancar(lexer); + return lexerCriarToken(lexer, TOKEN_MAIS, inicio, linha, coluna); + case '-': + lexerAvancar(lexer); + return lexerCriarToken(lexer, TOKEN_MENOS, inicio, linha, coluna); + case '*': + lexerAvancar(lexer); + return lexerCriarToken(lexer, TOKEN_MULTIPLICACAO, inicio, linha, coluna); + case '/': + lexerAvancar(lexer); + return lexerCriarToken(lexer, TOKEN_DIVISAO, inicio, linha, coluna); + case ':': + lexerAvancar(lexer); + if (lexerEspiar(lexer, 0) == '=') { + lexerAvancar(lexer); + return lexerCriarToken(lexer, TOKEN_ATRIBUICAO, inicio, linha, coluna); + } + return lexerCriarToken(lexer, TOKEN_DOIS_PONTOS, inicio, linha, coluna); + case '(': + lexerAvancar(lexer); + return lexerCriarToken(lexer, TOKEN_ABRE_PARENTESES, inicio, linha, coluna); + case ')': + lexerAvancar(lexer); + return lexerCriarToken(lexer, TOKEN_FECHA_PARENTESES, inicio, linha, coluna); + case ',': + lexerAvancar(lexer); + return lexerCriarToken(lexer, TOKEN_VIRGULA, inicio, linha, coluna); + case ';': + lexerAvancar(lexer); + return lexerCriarToken(lexer, TOKEN_PONTO_VIRGULA, inicio, linha, coluna); + case '.': + lexerAvancar(lexer); + return lexerCriarToken(lexer, TOKEN_PONTO, inicio, linha, coluna); + default: + lexerAvancar(lexer); + return lexerErro(lexer, atual, linha, coluna); + } +} + +void tokenLiberar(Token* token) +{ + if (token->lexema != NULL) { + free(token->lexema); + token->lexema = NULL; + } +} diff --git a/src/micropascal-lexer/token.c b/src/micropascal-lexer/token.c new file mode 100644 index 0000000..d6c546c --- /dev/null +++ b/src/micropascal-lexer/token.c @@ -0,0 +1,93 @@ +#include "micropascal-lexer/token.h" + +const char* tokenKindNome(TokenKind kind) +{ + switch (kind) { + case TOKEN_EOF: + return "EOF"; + case TOKEN_IDENTIFICADOR: + return "IDENTIFICADOR"; + case TOKEN_INTEIRO_LITERAL: + return "INTEIRO_LITERAL"; + case TOKEN_REAL_LITERAL: + return "REAL_LITERAL"; + case TOKEN_CHAR_LITERAL: + return "CHAR_LITERAL"; + case TOKEN_MENOR: + return "MENOR"; + case TOKEN_MAIOR: + return "MAIOR"; + case TOKEN_MENOR_IGUAL: + return "MENOR_IGUAL"; + case TOKEN_MAIOR_IGUAL: + return "MAIOR_IGUAL"; + case TOKEN_IGUAL: + return "IGUAL"; + case TOKEN_DIFERENTE: + return "DIFERENTE"; + case TOKEN_MAIS: + return "MAIS"; + case TOKEN_MENOS: + return "MENOS"; + case TOKEN_MULTIPLICACAO: + return "MULTIPLICACAO"; + case TOKEN_DIVISAO: + return "DIVISAO"; + case TOKEN_DIV: + return "DIV"; + case TOKEN_AND: + return "AND"; + case TOKEN_OR: + return "OR"; + case TOKEN_NOT: + return "NOT"; + case TOKEN_ATRIBUICAO: + return "ATRIBUICAO"; + case TOKEN_ABRE_PARENTESES: + return "ABRE_PARENTESES"; + case TOKEN_FECHA_PARENTESES: + return "FECHA_PARENTESES"; + case TOKEN_VIRGULA: + return "VIRGULA"; + case TOKEN_PONTO_VIRGULA: + return "PONTO_VIRGULA"; + case TOKEN_PONTO: + return "PONTO"; + case TOKEN_DOIS_PONTOS: + return "DOIS_PONTOS"; + case TOKEN_PROGRAM: + return "PROGRAM"; + case TOKEN_IF: + return "IF"; + case TOKEN_THEN: + return "THEN"; + case TOKEN_ELSE: + return "ELSE"; + case TOKEN_WHILE: + return "WHILE"; + case TOKEN_DO: + return "DO"; + case TOKEN_REPEAT: + return "REPEAT"; + case TOKEN_UNTIL: + return "UNTIL"; + case TOKEN_INTEGER: + return "INTEGER"; + case TOKEN_REAL: + return "REAL"; + case TOKEN_CHAR: + return "CHAR"; + case TOKEN_BEGIN: + return "BEGIN"; + case TOKEN_END: + return "END"; + case TOKEN_WRITE: + return "WRITE"; + case TOKEN_VAR: + return "VAR"; + case TOKEN_ERRO: + return "ERRO"; + default: + return "DESCONHECIDO"; + } +} diff --git a/src/micropascal-parser/ast.c b/src/micropascal-parser/ast.c new file mode 100644 index 0000000..59c14a6 --- /dev/null +++ b/src/micropascal-parser/ast.c @@ -0,0 +1,85 @@ +#include "micropascal-parser/ast.h" + +#include +#include +#include +#include + +void astLiberar(AstNo* no); +void astImprimir(const AstNo* no, int profundidade); + +static const char* astKindNome(AstKind kind) +{ + switch (kind) { + case AST_PROGRAMA: + return "Programa"; + case AST_SECAO_VAR: + return "SecaoVar"; + case AST_DECL_VAR: + return "DeclVar"; + case AST_TIPO: + return "Tipo"; + case AST_BLOCO: + return "Bloco"; + case AST_LISTA_COMANDOS: + return "ListaComandos"; + case AST_ATRIBUICAO: + return "Atribuicao"; + case AST_ITERACAO: + return "Iteracao"; + case AST_DECISAO: + return "Decisao"; + case AST_ESCRITA: + return "Escrita"; + case AST_EXPRESSAO_BINARIA: + return "ExpressaoBinaria"; + case AST_EXPRESSAO_UNARIA: + return "ExpressaoUnaria"; + case AST_EXPRESSAO_PARENTESES: + return "ExpressaoParenteses"; + case AST_EXPRESSAO_LITERAL: + return "ExpressaoLiteral"; + case AST_EXPRESSAO_IDENTIFICADOR: + return "ExpressaoIdentificador"; + default: + return "Desconhecido"; + } +} + +void astLiberar(AstNo* no) +{ + if (no == NULL) { + return; + } + astLiberar(no->esquerda); + astLiberar(no->direita); + astLiberar(no->terceiro); + astLiberar(no->proximo); + if (no->valor != NULL) { + free(no->valor); + } + free(no); +} + +void astImprimir(const AstNo* no, int profundidade) +{ + int i; + const AstNo* atual; + if (no == NULL) { + return; + } + for (i = 0; i < profundidade; i++) { + printf(" "); + } + printf("%s", astKindNome(no->kind)); + if (no->valor != NULL) { + printf(" (%s)", no->valor); + } + printf("\n"); + astImprimir(no->esquerda, profundidade + 1); + astImprimir(no->direita, profundidade + 1); + astImprimir(no->terceiro, profundidade + 1); + for (atual = no->proximo; atual != NULL; atual = atual->proximo) { + astImprimir(atual, profundidade); + } +} diff --git a/src/micropascal-parser/parser.c b/src/micropascal-parser/parser.c new file mode 100644 index 0000000..9c7609b --- /dev/null +++ b/src/micropascal-parser/parser.c @@ -0,0 +1,428 @@ +#include "micropascal-parser/parser.h" + +#include +#include +#include + +static AstNo* parserCriarNo(AstKind kind, const char* valor) +{ + AstNo* no = (AstNo*)malloc(sizeof(AstNo)); + no->kind = kind; + if (valor != NULL) { + no->valor = (char*)malloc(strlen(valor) + 1); + strcpy(no->valor, valor); + } else { + no->valor = NULL; + } + no->esquerda = NULL; + no->direita = NULL; + no->terceiro = NULL; + no->proximo = NULL; + return no; +} + +static void parserAvancar(Parser* parser) +{ + tokenLiberar(&parser->atual); + parser->atual = lexerProximoToken(parser->lexer); +} + +void parserInicializar(Parser* parser, Lexer* lexer) +{ + parser->lexer = lexer; + parser->atual.kind = TOKEN_EOF; + parser->atual.lexema = NULL; + parser->atual.linha = 0; + parser->atual.coluna = 0; + parser->houve_erro = 0; + parser->total_erros = 0; + parser->silencioso = 0; + parserAvancar(parser); +} + +void parserSilenciarErros(Parser* parser, int silenciar) +{ + parser->silencioso = silenciar; +} + +static void parserErro(Parser* parser) +{ + const char* lexema = parser->atual.lexema; + if (lexema == NULL) { + lexema = ""; + } + parser->houve_erro = 1; + parser->total_erros++; + if (!parser->silencioso) { + printf("Erro de sintaxe no token [%s]\n", lexema); + } +} + +static int parserVerificar(Parser* parser, TokenKind kind) +{ + return parser->atual.kind == kind; +} + +static int parserConsumir(Parser* parser, TokenKind kind) +{ + if (parserVerificar(parser, kind)) { + parserAvancar(parser); + return 1; + } + return 0; +} + +static int parserEsperar(Parser* parser, TokenKind kind) +{ + if (parserConsumir(parser, kind)) { + return 1; + } + parserErro(parser); + return 0; +} + +static int parserEmInicioDeComando(Parser* parser) +{ + switch (parser->atual.kind) { + case TOKEN_BEGIN: + case TOKEN_IDENTIFICADOR: + case TOKEN_WHILE: + case TOKEN_REPEAT: + case TOKEN_IF: + case TOKEN_WRITE: + return 1; + default: + return 0; + } +} + +static AstNo* parserAnalisarBloco(Parser* parser); +static AstNo* parserAnalisarComando(Parser* parser); +static AstNo* parserAnalisarExpressao(Parser* parser); +static AstNo* parserAnalisarTipo(Parser* parser) +{ + switch (parser->atual.kind) { + case TOKEN_INTEGER: + parserAvancar(parser); + return parserCriarNo(AST_TIPO, "integer"); + case TOKEN_REAL: + parserAvancar(parser); + return parserCriarNo(AST_TIPO, "real"); + case TOKEN_CHAR: + parserAvancar(parser); + return parserCriarNo(AST_TIPO, "char"); + default: + parserErro(parser); + return NULL; + } +} + +static AstNo* parserAnalisarDeclVar(Parser* parser) +{ + AstNo* decl = parserCriarNo(AST_DECL_VAR, NULL); + AstNo* ultimo = NULL; + AstNo* identificador; + AstNo* tipo; + + if (!parserEsperar(parser, TOKEN_IDENTIFICADOR)) { + astLiberar(decl); + return NULL; + } + identificador = parserCriarNo(AST_EXPRESSAO_IDENTIFICADOR, NULL); + decl->esquerda = identificador; + ultimo = identificador; + + while (parserVerificar(parser, TOKEN_VIRGULA)) { + parserAvancar(parser); + if (!parserEsperar(parser, TOKEN_IDENTIFICADOR)) { + return decl; + } + ultimo->proximo = parserCriarNo(AST_EXPRESSAO_IDENTIFICADOR, NULL); + ultimo = ultimo->proximo; + } + + if (!parserEsperar(parser, TOKEN_DOIS_PONTOS)) { + return decl; + } + tipo = parserAnalisarTipo(parser); + decl->direita = tipo; + if (!parserEsperar(parser, TOKEN_PONTO_VIRGULA)) { + return decl; + } + return decl; +} + +static AstNo* parserAnalisarSecaoVar(Parser* parser) +{ + AstNo* secao = parserCriarNo(AST_SECAO_VAR, NULL); + AstNo* ultimo = NULL; + if (!parserEsperar(parser, TOKEN_VAR)) { + astLiberar(secao); + return NULL; + } + while (parserVerificar(parser, TOKEN_IDENTIFICADOR)) { + AstNo* decl = parserAnalisarDeclVar(parser); + if (decl == NULL) { + break; + } + if (ultimo == NULL) { + secao->esquerda = decl; + } else { + ultimo->proximo = decl; + } + ultimo = decl; + } + return secao; +} + +static AstNo* parserAnalisarExprBasica(Parser* parser) +{ + AstNo* no; + switch (parser->atual.kind) { + case TOKEN_ABRE_PARENTESES: + parserAvancar(parser); + no = parserCriarNo(AST_EXPRESSAO_PARENTESES, NULL); + no->esquerda = parserAnalisarExpressao(parser); + parserEsperar(parser, TOKEN_FECHA_PARENTESES); + return no; + case TOKEN_NOT: + parserAvancar(parser); + no = parserCriarNo(AST_EXPRESSAO_UNARIA, "not"); + no->esquerda = parserAnalisarExprBasica(parser); + return no; + case TOKEN_INTEIRO_LITERAL: + no = parserCriarNo(AST_EXPRESSAO_LITERAL, parser->atual.lexema); + parserAvancar(parser); + return no; + case TOKEN_REAL_LITERAL: + no = parserCriarNo(AST_EXPRESSAO_LITERAL, parser->atual.lexema); + parserAvancar(parser); + return no; + case TOKEN_CHAR_LITERAL: + no = parserCriarNo(AST_EXPRESSAO_LITERAL, parser->atual.lexema); + parserAvancar(parser); + return no; + case TOKEN_IDENTIFICADOR: + no = parserCriarNo(AST_EXPRESSAO_IDENTIFICADOR, parser->atual.lexema); + parserAvancar(parser); + return no; + default: + parserErro(parser); + return NULL; + } +} + +static AstNo* parserAnalisarExpMul(Parser* parser) +{ + AstNo* esquerda = parserAnalisarExprBasica(parser); + while (parser->atual.kind == TOKEN_MULTIPLICACAO || parser->atual.kind == TOKEN_DIVISAO || parser->atual.kind == TOKEN_DIV) { + char operador[8]; + AstNo* direita; + AstNo* novo; + strcpy(operador, parser->atual.lexema); + parserAvancar(parser); + direita = parserAnalisarExprBasica(parser); + novo = parserCriarNo(AST_EXPRESSAO_BINARIA, operador); + novo->esquerda = esquerda; + novo->direita = direita; + esquerda = novo; + } + return esquerda; +} + +static AstNo* parserAnalisarExpAd(Parser* parser) +{ + AstNo* esquerda = parserAnalisarExpMul(parser); + while (parser->atual.kind == TOKEN_MAIS || parser->atual.kind == TOKEN_MENOS) { + char operador[8]; + AstNo* direita; + AstNo* novo; + strcpy(operador, parser->atual.lexema); + parserAvancar(parser); + direita = parserAnalisarExpMul(parser); + novo = parserCriarNo(AST_EXPRESSAO_BINARIA, operador); + novo->esquerda = esquerda; + novo->direita = direita; + esquerda = novo; + } + return esquerda; +} + +static AstNo* parserAnalisarExpRel(Parser* parser) +{ + AstNo* esquerda = parserAnalisarExpAd(parser); + while (parser->atual.kind == TOKEN_IGUAL || parser->atual.kind == TOKEN_DIFERENTE || parser->atual.kind == TOKEN_MENOR || parser->atual.kind == TOKEN_MAIOR || parser->atual.kind == TOKEN_MENOR_IGUAL || parser->atual.kind == TOKEN_MAIOR_IGUAL) { + char operador[8]; + AstNo* direita; + AstNo* novo; + strcpy(operador, parser->atual.lexema); + parserAvancar(parser); + direita = parserAnalisarExpAd(parser); + novo = parserCriarNo(AST_EXPRESSAO_BINARIA, operador); + novo->esquerda = esquerda; + novo->direita = direita; + esquerda = novo; + } + return esquerda; +} + +static AstNo* parserAnalisarExpOrAnd(Parser* parser) +{ + AstNo* esquerda = parserAnalisarExpRel(parser); + while (parser->atual.kind == TOKEN_OR || parser->atual.kind == TOKEN_AND) { + char operador[8]; + AstNo* direita; + AstNo* novo; + strcpy(operador, parser->atual.lexema); + parserAvancar(parser); + direita = parserAnalisarExpRel(parser); + novo = parserCriarNo(AST_EXPRESSAO_BINARIA, operador); + novo->esquerda = esquerda; + novo->direita = direita; + esquerda = novo; + } + return esquerda; +} + +static AstNo* parserAnalisarExpressao(Parser* parser) +{ + return parserAnalisarExpOrAnd(parser); +} + +static AstNo* parserAnalisarAtribuicao(Parser* parser) +{ + AstNo* no = parserCriarNo(AST_ATRIBUICAO, parser->atual.lexema); + parserAvancar(parser); + if (!parserEsperar(parser, TOKEN_ATRIBUICAO)) { + return no; + } + no->esquerda = parserAnalisarExpressao(parser); + parserEsperar(parser, TOKEN_PONTO_VIRGULA); + return no; +} + +static AstNo* parserAnalisarIteracao(Parser* parser) +{ + AstNo* no; + if (parser->atual.kind == TOKEN_WHILE) { + parserAvancar(parser); + no = parserCriarNo(AST_ITERACAO, "while"); + no->esquerda = parserAnalisarExpressao(parser); + parserEsperar(parser, TOKEN_DO); + no->direita = parserAnalisarComando(parser); + return no; + } + parserAvancar(parser); + no = parserCriarNo(AST_ITERACAO, "repeat"); + no->esquerda = parserAnalisarComando(parser); + parserEsperar(parser, TOKEN_UNTIL); + no->direita = parserAnalisarExpressao(parser); + parserEsperar(parser, TOKEN_PONTO_VIRGULA); + return no; +} + +static AstNo* parserAnalisarDecisao(Parser* parser) +{ + AstNo* no; + parserAvancar(parser); + no = parserCriarNo(AST_DECISAO, NULL); + no->esquerda = parserAnalisarExpressao(parser); + parserEsperar(parser, TOKEN_THEN); + no->direita = parserAnalisarComando(parser); + if (parserVerificar(parser, TOKEN_ELSE)) { + parserAvancar(parser); + no->terceiro = parserAnalisarComando(parser); + } + return no; +} + +static AstNo* parserAnalisarEscrita(Parser* parser) +{ + AstNo* no; + parserAvancar(parser); + no = parserCriarNo(AST_ESCRITA, NULL); + parserEsperar(parser, TOKEN_ABRE_PARENTESES); + no->esquerda = parserAnalisarExpressao(parser); + parserEsperar(parser, TOKEN_FECHA_PARENTESES); + parserEsperar(parser, TOKEN_PONTO_VIRGULA); + return no; +} + +static AstNo* parserAnalisarComando(Parser* parser) +{ + switch (parser->atual.kind) { + case TOKEN_BEGIN: { + AstNo* bloco = parserAnalisarBloco(parser); + parserEsperar(parser, TOKEN_PONTO_VIRGULA); + return bloco; + } + case TOKEN_IDENTIFICADOR: + return parserAnalisarAtribuicao(parser); + case TOKEN_WHILE: + case TOKEN_REPEAT: + return parserAnalisarIteracao(parser); + case TOKEN_IF: + return parserAnalisarDecisao(parser); + case TOKEN_WRITE: + return parserAnalisarEscrita(parser); + default: + parserErro(parser); + return NULL; + } +} + +static AstNo* parserAnalisarBloco(Parser* parser) +{ + AstNo* bloco = parserCriarNo(AST_BLOCO, NULL); + AstNo* lista = parserCriarNo(AST_LISTA_COMANDOS, NULL); + AstNo* ultimo = NULL; + + if (!parserEsperar(parser, TOKEN_BEGIN)) { + astLiberar(bloco); + astLiberar(lista); + return NULL; + } + while (parserEmInicioDeComando(parser)) { + AstNo* comando = parserAnalisarComando(parser); + if (comando == NULL) { + break; + } + if (ultimo == NULL) { + lista->esquerda = comando; + } else { + ultimo->proximo = comando; + } + ultimo = comando; + } + parserEsperar(parser, TOKEN_END); + bloco->esquerda = lista; + return bloco; +} + +AstNo* parserAnalisarPrograma(Parser* parser) +{ + AstNo* programa = parserCriarNo(AST_PROGRAMA, NULL); + + if (!parserEsperar(parser, TOKEN_PROGRAM)) { + astLiberar(programa); + return NULL; + } + if (parserVerificar(parser, TOKEN_IDENTIFICADOR)) { + free(programa->valor); + programa->valor = (char*)malloc(strlen(parser->atual.lexema) + 1); + strcpy(programa->valor, parser->atual.lexema); + parserAvancar(parser); + } else { + parserErro(parser); + } + parserEsperar(parser, TOKEN_PONTO_VIRGULA); + if (parserVerificar(parser, TOKEN_VAR)) { + programa->esquerda = parserAnalisarSecaoVar(parser); + } + programa->direita = parserAnalisarBloco(parser); + parserEsperar(parser, TOKEN_PONTO); + if (!parserVerificar(parser, TOKEN_EOF)) { + parserErro(parser); + } + return programa; +} diff --git a/tests/test_lexer.c b/tests/test_lexer.c new file mode 100644 index 0000000..88535e5 --- /dev/null +++ b/tests/test_lexer.c @@ -0,0 +1,108 @@ +#include "micropascal-tests/testes.h" +#include "micropascal-lexer/lexer.h" + +#include + +static TokenKind tokenDe(const char* fonte) +{ + Lexer lexer; + Token token; + lexerInicializar(&lexer, fonte); + token = lexerProximoToken(&lexer); + tokenLiberar(&token); + return token.kind; +} + +static int lexemaDe(const char* fonte, const char* esperado) +{ + Lexer lexer; + Token token; + int igual; + lexerInicializar(&lexer, fonte); + token = lexerProximoToken(&lexer); + igual = token.lexema != NULL && strcmp(token.lexema, esperado) == 0; + tokenLiberar(&token); + return igual; +} + +static int contaTokens(const char* fonte) +{ + Lexer lexer; + Token token; + int contador = 0; + lexerInicializar(&lexer, fonte); + while (1) { + token = lexerProximoToken(&lexer); + contador++; + if (token.kind == TOKEN_EOF || token.kind == TOKEN_ERRO) { + tokenLiberar(&token); + break; + } + tokenLiberar(&token); + } + return contador; +} + +void runLexerTests(ResultadoTeste* resultado_testes) +{ + TESTE_ASSERT(resultado_testes, tokenDe("program") == TOKEN_PROGRAM, "reconhece program"); + TESTE_ASSERT(resultado_testes, tokenDe("if") == TOKEN_IF, "reconhece if"); + TESTE_ASSERT(resultado_testes, tokenDe("then") == TOKEN_THEN, "reconhece then"); + TESTE_ASSERT(resultado_testes, tokenDe("else") == TOKEN_ELSE, "reconhece else"); + TESTE_ASSERT(resultado_testes, tokenDe("while") == TOKEN_WHILE, "reconhece while"); + TESTE_ASSERT(resultado_testes, tokenDe("do") == TOKEN_DO, "reconhece do"); + TESTE_ASSERT(resultado_testes, tokenDe("repeat") == TOKEN_REPEAT, "reconhece repeat"); + TESTE_ASSERT(resultado_testes, tokenDe("until") == TOKEN_UNTIL, "reconhece until"); + TESTE_ASSERT(resultado_testes, tokenDe("integer") == TOKEN_INTEGER, "reconhece integer"); + TESTE_ASSERT(resultado_testes, tokenDe("real") == TOKEN_REAL, "reconhece real"); + TESTE_ASSERT(resultado_testes, tokenDe("char") == TOKEN_CHAR, "reconhece char"); + TESTE_ASSERT(resultado_testes, tokenDe("begin") == TOKEN_BEGIN, "reconhece begin"); + TESTE_ASSERT(resultado_testes, tokenDe("end") == TOKEN_END, "reconhece end"); + TESTE_ASSERT(resultado_testes, tokenDe("write") == TOKEN_WRITE, "reconhece write"); + TESTE_ASSERT(resultado_testes, tokenDe("var") == TOKEN_VAR, "reconhece var"); + TESTE_ASSERT(resultado_testes, tokenDe("div") == TOKEN_DIV, "reconhece div"); + TESTE_ASSERT(resultado_testes, tokenDe("and") == TOKEN_AND, "reconhece and"); + TESTE_ASSERT(resultado_testes, tokenDe("or") == TOKEN_OR, "reconhece or"); + TESTE_ASSERT(resultado_testes, tokenDe("not") == TOKEN_NOT, "reconhece not"); + + TESTE_ASSERT(resultado_testes, tokenDe("Begin") == TOKEN_IDENTIFICADOR, "Begin nao e palavra reservada"); + TESTE_ASSERT(resultado_testes, tokenDe("PROGRAM") == TOKEN_IDENTIFICADOR, "PROGRAM nao e palavra reservada"); + TESTE_ASSERT(resultado_testes, tokenDe("_contador") == TOKEN_IDENTIFICADOR, "identificador com underscore"); + TESTE_ASSERT(resultado_testes, tokenDe("abc123") == TOKEN_IDENTIFICADOR, "identificador alfanumerico"); + TESTE_ASSERT(resultado_testes, lexemaDe("xyz", "xyz"), "lexema do identificador preservado"); + + TESTE_ASSERT(resultado_testes, tokenDe("123") == TOKEN_INTEIRO_LITERAL, "inteiro literal"); + TESTE_ASSERT(resultado_testes, tokenDe("0") == TOKEN_INTEIRO_LITERAL, "inteiro literal zero"); + TESTE_ASSERT(resultado_testes, tokenDe("3.14") == TOKEN_REAL_LITERAL, "real literal"); + TESTE_ASSERT(resultado_testes, tokenDe("0.5") == TOKEN_REAL_LITERAL, "real literal com zero a esquerda"); + TESTE_ASSERT(resultado_testes, tokenDe(".5") == TOKEN_REAL_LITERAL, "real literal sem parte inteira"); + TESTE_ASSERT(resultado_testes, tokenDe("7.0") == TOKEN_REAL_LITERAL, "real literal inteiro"); + + TESTE_ASSERT(resultado_testes, tokenDe("'a'") == TOKEN_CHAR_LITERAL, "char literal letra"); + TESTE_ASSERT(resultado_testes, tokenDe("'0'") == TOKEN_CHAR_LITERAL, "char literal digito"); + TESTE_ASSERT(resultado_testes, tokenDe("'\\n'") == TOKEN_CHAR_LITERAL, "char literal quebra de linha"); + TESTE_ASSERT(resultado_testes, tokenDe("'\\t'") == TOKEN_CHAR_LITERAL, "char literal tabulacao"); + + TESTE_ASSERT(resultado_testes, tokenDe("<") == TOKEN_MENOR, "menor"); + TESTE_ASSERT(resultado_testes, tokenDe(">") == TOKEN_MAIOR, "maior"); + TESTE_ASSERT(resultado_testes, tokenDe("<=") == TOKEN_MENOR_IGUAL, "menor ou igual"); + TESTE_ASSERT(resultado_testes, tokenDe(">=") == TOKEN_MAIOR_IGUAL, "maior ou igual"); + TESTE_ASSERT(resultado_testes, tokenDe("=") == TOKEN_IGUAL, "igual"); + TESTE_ASSERT(resultado_testes, tokenDe("<>") == TOKEN_DIFERENTE, "diferente"); + TESTE_ASSERT(resultado_testes, tokenDe("+") == TOKEN_MAIS, "mais"); + TESTE_ASSERT(resultado_testes, tokenDe("-") == TOKEN_MENOS, "menos"); + TESTE_ASSERT(resultado_testes, tokenDe("*") == TOKEN_MULTIPLICACAO, "multiplicacao"); + TESTE_ASSERT(resultado_testes, tokenDe("/") == TOKEN_DIVISAO, "divisao de reais"); + TESTE_ASSERT(resultado_testes, tokenDe(":=") == TOKEN_ATRIBUICAO, "atribuicao"); + TESTE_ASSERT(resultado_testes, tokenDe("(") == TOKEN_ABRE_PARENTESES, "abre parenteses"); + TESTE_ASSERT(resultado_testes, tokenDe(")") == TOKEN_FECHA_PARENTESES, "fecha parenteses"); + TESTE_ASSERT(resultado_testes, tokenDe(",") == TOKEN_VIRGULA, "virgula"); + TESTE_ASSERT(resultado_testes, tokenDe(";") == TOKEN_PONTO_VIRGULA, "ponto e virgula"); + TESTE_ASSERT(resultado_testes, tokenDe(".") == TOKEN_PONTO, "ponto"); + TESTE_ASSERT(resultado_testes, tokenDe(":") == TOKEN_DOIS_PONTOS, "dois pontos"); + + TESTE_ASSERT(resultado_testes, contaTokens(" \n\t\r ") == 1, "brancos ignorados resultam em EOF"); + TESTE_ASSERT(resultado_testes, contaTokens("a b") == 3, "conta tokens simples"); + TESTE_ASSERT(resultado_testes, tokenDe("$") == TOKEN_ERRO, "caractere invalido gera erro"); + TESTE_ASSERT(resultado_testes, tokenDe("@") == TOKEN_ERRO, "arroba gera erro"); +} diff --git a/tests/test_main.c b/tests/test_main.c new file mode 100644 index 0000000..bb3c06a --- /dev/null +++ b/tests/test_main.c @@ -0,0 +1,27 @@ +#include "micropascal-tests/testes.h" + +#include + +int main(void) +{ + ResultadoTeste resultado_testes; + resultado_testes.total = 0; + resultado_testes.passaram = 0; + resultado_testes.falharam = 0; + + printf("Executando testes do lexer\n"); + runLexerTests(&resultado_testes); + + printf("Executando testes do parser\n"); + runParserTests(&resultado_testes); + + printf("\nTotal: %d | Passaram: %d | Falharam: %d\n", + resultado_testes.total, + resultado_testes.passaram, + resultado_testes.falharam); + + if (resultado_testes.falharam > 0) { + return 1; + } + return 0; +} diff --git a/tests/test_parser.c b/tests/test_parser.c new file mode 100644 index 0000000..1d4d11f --- /dev/null +++ b/tests/test_parser.c @@ -0,0 +1,111 @@ +#include "micropascal-tests/testes.h" +#include "micropascal-lexer/lexer.h" +#include "micropascal-parser/parser.h" + +#include + +static int programaValido(const char* fonte) +{ + Lexer lexer; + Parser parser; + AstNo* programa; + int valido; + lexerInicializar(&lexer, fonte); + parserInicializar(&parser, &lexer); + parserSilenciarErros(&parser, 1); + programa = parserAnalisarPrograma(&parser); + valido = !parser.houve_erro; + tokenLiberar(&parser.atual); + astLiberar(programa); + return valido; +} + +static const char* PROGRAMA_SIMPLES = + "program P;\n" + "var\n" + " x : integer;\n" + "begin\n" + " x := 1;\n" + "end.\n"; + +static const char* PROGRAMA_PARIDADE = + "program TestaParidade;\n" + "var\n" + " n : integer;\n" + "begin\n" + " n := 2 * 13 + 5;\n" + " if n = (n div 2)*2 then\n" + " write('p')\n" + " ;\n" + " else\n" + " write('i')\n" + " ;\n" + "end.\n"; + +static const char* PROGRAMA_IMPARES = + "program SomaImpares;\n" + "var\n" + " n : integer;\n" + " i, proximoImpar, soma : integer;\n" + "begin\n" + " n := 4;\n" + " i := 0;\n" + " soma := 0;\n" + " while i < n do\n" + " begin\n" + " proximoImpar := 2*i + 1;\n" + " soma := soma + proximoImpar;\n" + " i := i + 1;\n" + " end;\n" + " write(soma);\n" + " write('\\n');\n" + "end.\n"; + +static const char* PROGRAMA_REPEAT = + "program Contador;\n" + "var\n" + " i : integer;\n" + "begin\n" + " i := 0;\n" + " repeat\n" + " i := i + 1;\n" + " until i >= 10;\n" + "end.\n"; + +static const char* PROGRAMA_TIPOS = + "program Tipos;\n" + "var\n" + " a, b : integer;\n" + " c : real;\n" + " d : char;\n" + "begin\n" + " a := 1;\n" + " b := 2;\n" + " c := 1.5 + 2.5;\n" + " d := 'x';\n" + "end.\n"; + +void runParserTests(ResultadoTeste* resultado_testes) +{ + TESTE_ASSERT(resultado_testes, programaValido(PROGRAMA_SIMPLES), "programa simples valido"); + TESTE_ASSERT(resultado_testes, programaValido(PROGRAMA_PARIDADE), "programa TestaParidade valido"); + TESTE_ASSERT(resultado_testes, programaValido(PROGRAMA_IMPARES), "programa SomaImpares valido"); + TESTE_ASSERT(resultado_testes, programaValido(PROGRAMA_REPEAT), "programa com repeat until valido"); + TESTE_ASSERT(resultado_testes, programaValido(PROGRAMA_TIPOS), "programa com varios tipos valido"); + + TESTE_ASSERT(resultado_testes, programaValido("program P;\nbegin\n x := 1 + 2 * 3;\nend.\n"), "precedencia multiplicacao sobre soma"); + TESTE_ASSERT(resultado_testes, programaValido("program P;\nbegin\n x := (1 + 2) * 3;\nend.\n"), "parenteses"); + TESTE_ASSERT(resultado_testes, programaValido("program P;\nbegin\n x := not a and b or c;\nend.\n"), "operadores not and or"); + TESTE_ASSERT(resultado_testes, programaValido("program P;\nbegin\n x := a = b;\nend.\n"), "igualdade"); + TESTE_ASSERT(resultado_testes, programaValido("program P;\nbegin\n x := a <> b;\nend.\n"), "diferente"); + TESTE_ASSERT(resultado_testes, programaValido("program P;\nbegin\n x := a <= b;\nend.\n"), "menor ou igual"); + TESTE_ASSERT(resultado_testes, programaValido("program P;\nbegin\n if a then b := 1;\nend.\n"), "decisao sem else"); + TESTE_ASSERT(resultado_testes, programaValido("program P;\nbegin\n while a do b := 1;\nend.\n"), "iteracao while"); + + TESTE_ASSERT(resultado_testes, !programaValido("program P\nbegin end.\n"), "falta ponto e virgula apos nome"); + TESTE_ASSERT(resultado_testes, !programaValido("program P;\nbegin\n x := ;\nend.\n"), "expressao ausente"); + TESTE_ASSERT(resultado_testes, !programaValido("program P;\nbegin\n x := 1 +\nend.\n"), "operando ausente"); + TESTE_ASSERT(resultado_testes, !programaValido("program P;\nbegin\n if a then\nend.\n"), "comando ausente apos then"); + TESTE_ASSERT(resultado_testes, !programaValido("program P;\nvar x integer;\nbegin end.\n"), "falta dois pontos na declaracao"); + TESTE_ASSERT(resultado_testes, !programaValido("program P;\nbegin\n x := 1\nend.\n"), "falta ponto e virgula no comando"); +}