First commit
This commit is contained in:
commit
dccc52bd7c
24 files changed
+1702
No files matched your search
+13
@@ -0,0 +1,13 @@
|
|||||||
|
build/
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
|
*.stackdump
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
desktop.ini
|
||||||
|
*.swp
|
||||||
|
*~
|
||||||
|
|
||||||
|
INSTRUCOES_DE_IMPLEMENTACAO.md
|
||||||
+21
@@ -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.
|
||||||
@@ -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
|
||||||
Binary file not shown.
@@ -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/<arquitetura>/bin e os artefatos de teste em
|
||||||
|
build/<arquitetura>/.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.
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
program Contador;
|
||||||
|
var
|
||||||
|
i : integer;
|
||||||
|
begin
|
||||||
|
i := 0;
|
||||||
|
repeat
|
||||||
|
i := i + 1;
|
||||||
|
until i >= 10;
|
||||||
|
end.
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
program Erro;
|
||||||
|
var
|
||||||
|
x : integer
|
||||||
|
begin
|
||||||
|
x := 1;
|
||||||
|
end.
|
||||||
@@ -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.
|
||||||
@@ -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.
|
||||||
@@ -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.
|
||||||
@@ -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.
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
#ifndef MICROPASCAL_LEXER_TOKEN_H
|
||||||
|
#define MICROPASCAL_LEXER_TOKEN_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
#ifndef MICROPASCAL_TESTS_TESTES_H
|
||||||
|
#define MICROPASCAL_TESTS_TESTES_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
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
|
||||||
+103
@@ -0,0 +1,103 @@
|
|||||||
|
#include "micropascal-lexer/lexer.h"
|
||||||
|
#include "micropascal-parser/parser.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
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 <arquivo> [--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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,333 @@
|
|||||||
|
#include "micropascal-lexer/lexer.h"
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
#include "micropascal-parser/ast.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,428 @@
|
|||||||
|
#include "micropascal-parser/parser.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
#include "micropascal-tests/testes.h"
|
||||||
|
#include "micropascal-lexer/lexer.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
#include "micropascal-tests/testes.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
#include "micropascal-tests/testes.h"
|
||||||
|
#include "micropascal-lexer/lexer.h"
|
||||||
|
#include "micropascal-parser/parser.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
Reference in new issue
Block a user