Add initial files

This commit is contained in:
Santiago Lo Coco 2022-04-02 07:40:09 -03:00
commit bbbc46e81b
18 changed files with 574 additions and 0 deletions

57
CMakeLists.txt Normal file
View File

@ -0,0 +1,57 @@
# Versión mínima requerida de CMake:
cmake_minimum_required(VERSION 3.22)
# Nombre del proyecto, y lenguaje usado (C, en este caso):
project(Compiler C)
# Compilar el parser con Bison:
add_custom_command(
OUTPUT ../src/frontend/syntactic-analysis/bison-parser.c ../src/frontend/syntactic-analysis/bison-parser.h
COMMAND bison -d ../src/frontend/syntactic-analysis/bison-grammar.y -o ../src/frontend/syntactic-analysis/bison-parser.c)
# Seleccionar estrategia según el compilador de C disponible en el sistema:
if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
message(NOTICE "El compilador de C es GCC.")
# Opciones para GCC:
add_compile_options(-static-libgcc)
add_compile_options(-std=gnu99)
add_compile_options(-O3)
# Compilar el scanner con Flex:
add_custom_command(
OUTPUT ../src/frontend/lexical-analysis/flex-scanner.c
COMMAND flex -o ../src/frontend/lexical-analysis/flex-scanner.c ../src/frontend/lexical-analysis/flex-patterns.l
DEPENDS ../src/frontend/syntactic-analysis/bison-parser.c ../src/frontend/syntactic-analysis/bison-parser.h)
elseif (CMAKE_C_COMPILER_ID STREQUAL "MSVC")
message(NOTICE "El compilador de C es Microsoft Visual Studio.")
# Opciones para MSVC:
# add_compile_options(...)
# Compilar el scanner con Flex, usando compatibilidad con Microsoft Windows:
add_custom_command(
OUTPUT ../src/frontend/lexical-analysis/flex-scanner.c
COMMAND flex --wincompat -o ../src/frontend/lexical-analysis/flex-scanner.c ../src/frontend/lexical-analysis/flex-patterns.l
DEPENDS ../src/frontend/syntactic-analysis/bison-parser.c ../src/frontend/syntactic-analysis/bison-parser.h)
else ()
message(NOTICE "El compilador de C es desconocido.")
message(NOTICE "No se generará el scanner con Flex.")
endif ()
# Especificar punto de entrada del proyecto y códigos fuente (extensión *.c):
add_executable(Compiler
src/main.c
src/backend/code-generation/generator.c
src/backend/domain-specific/calculator.c
src/backend/support/logger.c
src/frontend/lexical-analysis/flex-actions.c
src/frontend/lexical-analysis/flex-scanner.c
src/frontend/syntactic-analysis/bison-actions.c
src/frontend/syntactic-analysis/bison-parser.c)
# Linkear el proyecto y sus librerías:
target_link_libraries(Compiler)

12
LICENSE.md Normal file
View File

@ -0,0 +1,12 @@
MIT License
Copyright (c) 2022 Agustín Golmar \
Copyright (c) 2022 Santiago Lo Coco \
Copyright (c) 2022 Juan Martín Barmasch \
Copyright (c) 2022 Ezequiel Bellver
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.

45
README.md Normal file
View File

@ -0,0 +1,45 @@
[![✗](https://img.shields.io/badge/Release-v0.1.0-ffb600.svg?style=for-the-badge)](https://github.com/agustin-golmar/Flex-Bison-Compiler/releases)
# Compilador Flex/Bison
Un compilador vacío construido con Flex y Bison.
## Requerimientos
Para construir el compilador, se requieren las siguientes dependencias:
* [Bison v3.8.2](https://www.gnu.org/software/bison/)
* [CMake v3.22.2](https://cmake.org/)
* [Flex v2.6.4](https://github.com/westes/flex)
* [GCC v11.1.0](https://gcc.gnu.org/)
* [Make v4.3](https://www.gnu.org/software/make/)
Si en lugar de trabajar con un entorno _Linux_, se está construyendo el proyecto sobre un entorno _Microsoft Windows_, se debe instalar _Microsoft Visual Studio 2022_ con las extensiones para desarrollar aplicaciones en _C/C++_, así como también las herramientas requeridas, con excepción del compilador _GCC_ y la herramienta _Make_.
## Construcción
Para construir el proyecto por completo, ejecute en la raíz del repositorio el siguiente comando:
```bash
user@machine:path/ $ cmake -S . -B bin
user@machine:path/ $ cd bin
user@machine:path/ $ make
```
En un entorno _Microsoft Windows_, en lugar de ejecutar el comando `make`, se deberá abrir la solución generada `bin/Compiler.sln` con el IDE _Microsoft Visual Studio 2022_. Los ejecutables que este sistema construye se depositan dentro del directorio `bin/Debug` y `bin/Release` según corresponda.
## Ejecución
Para compilar un programa, primero cree un archivo vacío denominado `program` con el siguiente contenido:
```
123123 + 123 - 2 * (454 + 890 / 89)
```
Luego, ejecute el compilador desde el directorio raíz del proyecto, inyectando el programa desde la entrada estándard:
```bash
user@machine:path/ $ cat program | bin/Compiler
```
Deberia obtener el resultado correcto de evaluar el programa anterior: `122318`.

View File

@ -0,0 +1,10 @@
#include "../support/logger.h"
#include "generator.h"
/**
* Implementación de "generator.h".
*/
void Generator(int result) {
LogInfo("El resultado de la expresion computada es: '%d'.", result);
}

View File

@ -0,0 +1,6 @@
#ifndef GENERATOR_HEADER
#define GENERATOR_HEADER
void Generator(int result);
#endif

View File

@ -0,0 +1,21 @@
#include "calculator.h"
/**
* Implementación de "calculator.h".
*/
int Add(const int leftAddend, const int rightAddend) {
return leftAddend + rightAddend;
}
int Subtract(const int minuend, const int subtract) {
return minuend - subtract;
}
int Multiply(const int multiplicand, const int multiplier) {
return multiplicand * multiplier;
}
int Divide(const int dividend, const int divisor) {
return dividend / divisor;
}

View File

@ -0,0 +1,12 @@
#ifndef CALCULATOR_HEADER
#define CALCULATOR_HEADER
int Add(const int leftAddend, const int rightAddend);
int Subtract(const int minuend, const int subtract);
int Multiply(const int multiplicand, const int multiplier);
int Divide(const int dividend, const int divisor);
#endif

View File

@ -0,0 +1,7 @@
# Analizador Semántico
¿Qué se debería implementar en esta parte del proyecto?
El analizador semántico debe ser el encargado de analizar el árbol de sintaxis abstracta construido desde el analizador sintáctico (_i.e._, Bison), aplicando todas las reglas del dominio específico que el lenguaje resuelve, garantizando que el significado (la semántica) del programa, es correcto. Esto implica verificar que las operaciones estén bien formadas, que los parámetros y tipos sean correctos, que la definición de las variables (en caso de existir dicho concepto), sean consistentes, entre muchos otros detalles asociados a la especificación del lenguaje.
Si el analizador semántico logra validar el programa completo, entonces se procede a la última fase: la __generación de código__. Nótese que la generación de código no necesariamente implica emitir un nuevo programa (por ejemplo, un ejecutable, o código en _assembler_ listo para compilar). Para un programa que permita procesar datos, la generación de código puede implicar emitir un set de datos, o un simple resultado, como es el caso de este proyecto base (que computa el resultado de un cálculo aritmético, y lo muestra por consola).

View File

@ -0,0 +1,41 @@
#include "logger.h"
#include <stdarg.h>
#include <stdio.h>
/**
* Implementación de "logger.h".
*/
void Log(FILE * const stream, const char * prefix, const char * const format, const char * suffix, va_list arguments) {
fprintf(stream, "%s", prefix);
vfprintf(stream, format, arguments);
fprintf(stream, "%s", suffix);
}
void LogDebug(const char * const format, ...) {
va_list arguments;
va_start(arguments, format);
Log(stdout, "[DEBUG] ", format, "\n", arguments);
va_end(arguments);
}
void LogError(const char * const format, ...) {
va_list arguments;
va_start(arguments, format);
Log(stderr, "[ERROR] ", format, "\n", arguments);
va_end(arguments);
}
void LogErrorRaw(const char * const format, ...) {
va_list arguments;
va_start(arguments, format);
Log(stderr, "", format, "", arguments);
va_end(arguments);
}
void LogInfo(const char * const format, ...) {
va_list arguments;
va_start(arguments, format);
Log(stdout, "[INFO ] ", format, "\n", arguments);
va_end(arguments);
}

View File

@ -0,0 +1,16 @@
#ifndef LOGGER_HEADER
#define LOGGER_HEADER
#include <stdio.h>
void Log(FILE * const stream, const char * prefix, const char * const format, const char * suffix, va_list arguments);
void LogDebug(const char * const format, ...);
void LogError(const char * const format, ...);
void LogErrorRaw(const char * const format, ...);
void LogInfo(const char * const format, ...);
#endif

View File

@ -0,0 +1,55 @@
#ifndef SHARED_HEADER
#define SHARED_HEADER
#include <stdio.h>
// Descriptor del archivo de entrada que utiliza Bison.
extern FILE * yyin;
// Descriptor del archivo de salida que utiliza Bison.
extern FILE * yyout;
// Variable global que contiene el número escaneado.
extern int yylval;
// Variable global que contiene el número de la línea analizada.
extern int yylineno;
// Token actual en el tope de la pila del analizador Bison.
extern char * yytext;
// Función global de manejo de errores en Bison.
extern void yyerror(const char * string);
// Función global del analizador léxico Flex.
extern int yylex(void);
// Función global del analizador sintáctico Bison.
extern int yyparse(void);
// Emular tipo "boolean".
typedef enum {
false = 0,
true = 1
} boolean;
// Estado global de toda la aplicación.
typedef struct {
// Indica si la compilación tuvo problemas hasta el momento:
boolean succeed;
// Indica el resultado de la compilación:
int result;
// Agregar una pila para almacenar tokens/nodos.
// Agregar un nodo hacia la raíz del árbol de sintaxis abstracta.
// Agregar una tabla de símbolos.
// ...
} CompilerState;
// El estado se define e inicializa en el archivo "main.c":
extern CompilerState state;
#endif

View File

@ -0,0 +1,22 @@
#include "../../backend/support/logger.h"
#include "flex-actions.h"
#include <stdlib.h>
/**
* Implementación de "flex-rules.h".
*/
TokenID IntegerPatternAction(const char * lexeme) {
LogDebug("IntegerPatternAction: '%s'.", lexeme);
yylval = atoi(lexeme);
return INTEGER;
}
void IgnoredPatternAction(const char * lexeme) {
LogDebug("IgnoredPatternAction: '%s'.", lexeme);
}
TokenID UnknownPatternAction(const char * lexeme) {
LogDebug("UnknownPatternAction: '%s'.", lexeme);
return YYUNDEF;
}

View File

@ -0,0 +1,48 @@
#ifndef FLEX_ACTIONS_HEADER
#define FLEX_ACTIONS_HEADER
#include "../../backend/support/shared.h"
/**
* Se definen los diferentes IDs de cada token disponible para el scanner Flex.
*/
typedef enum TokenID {
// Por defecto, el valor "0" hace fallar el analizador sintáctico.
UNKNOWN = 0,
// Código de error de Bison, que permite abortar el escaneo de lexemas cuando
// se presente un patrón desconocido. El número "257" coincide con el valor
// que Bison le otorga por defecto, pero además permite que el resto de
// tokens continúen desde el valor "258" lo que permite proteger los IDs
// internos que Bison reserva para crear "tokens literales":
YYUNDEF = 257,
// Operadores aritméticos.
ADD,
SUB,
MUL,
DIV,
// Paréntesis.
OPEN_PARENTHESIS,
CLOSE_PARENTHESIS,
// Tipos de dato.
INTEGER
} TokenID;
/**
* Se definen las acciones a ejecutar sobre cada patrón hallado mediante el
* analizador léxico Flex. Este analizador solo puede identificar
* construcciones regulares, ya que utiliza un autómata finito determinístico
* (a.k.a. DFA), como mecanismo de escaneo y reconocimiento.
*/
TokenID IntegerPatternAction(const char * lexeme);
void IgnoredPatternAction(const char * lexeme);
TokenID UnknownPatternAction(const char * lexeme);
#endif

View File

@ -0,0 +1,38 @@
%{
#include "flex-actions.h"
%}
/* Obliga a crear una regla explícita para manejar lexemas desconocidos. */
%option nodefault
/* Escanea un único archivo y se detiene. */
%option noyywrap
/* Dispone una variable global "yylineno" con el número de línea actual. */
%option yylineno
/* Patrones reutilizables. */
crlf \r\n
digit [0-9]
endline \n
whitespace [ \f\n\r\t\v]
%%
"+" { return ADD; }
"-" { return SUB; }
"*" { return MUL; }
"/" { return DIV; }
"(" { return OPEN_PARENTHESIS; }
")" { return CLOSE_PARENTHESIS; }
{digit}+ { return IntegerPatternAction(yytext); }
{whitespace} { IgnoredPatternAction(yytext); }
. { return UnknownPatternAction(yytext); }
%%

View File

@ -0,0 +1,67 @@
#include "../../backend/domain-specific/calculator.h"
#include "../../backend/support/logger.h"
#include "bison-actions.h"
#include <stdio.h>
#include <string.h>
/**
* Implementación de "bison-grammar.h".
*/
void yyerror(const char * string) {
LogError("Mensaje: '%s' debido a '%s' (linea %d).", string, yytext, yylineno);
LogError("En ASCII es:");
LogErrorRaw("\t");
const int length = strlen(yytext);
for (int i = 0; i < length; ++i) {
LogErrorRaw("[%d]", yytext[i]);
}
LogErrorRaw("\n\n");
}
int ProgramGrammarAction(const int value) {
LogDebug("ProgramGrammarAction(%d)", value);
state.succeed = true;
state.result = value;
return value;
}
int AdditionExpressionGrammarAction(const int leftValue, const int rightValue) {
LogDebug("AdditionExpressionGrammarAction(%d, %d)", leftValue, rightValue);
return Add(leftValue, rightValue);
}
int SubtractionExpressionGrammarAction(const int leftValue, const int rightValue) {
LogDebug("SubtractionExpressionGrammarAction(%d, %d)", leftValue, rightValue);
return Subtract(leftValue, rightValue);
}
int MultiplicationExpressionGrammarAction(const int leftValue, const int rightValue) {
LogDebug("MultiplicationExpressionGrammarAction(%d, %d)", leftValue, rightValue);
return Multiply(leftValue, rightValue);
}
int DivisionExpressionGrammarAction(const int leftValue, const int rightValue) {
LogDebug("DivisionExpressionGrammarAction(%d, %d)", leftValue, rightValue);
return Divide(leftValue, rightValue);
}
int FactorExpressionGrammarAction(const int value) {
LogDebug("FactorExpressionGrammarAction(%d)", value);
return value;
}
int ExpressionFactorGrammarAction(const int value) {
LogDebug("ExpressionFactorGrammarAction(%d)", value);
return value;
}
int ConstantFactorGrammarAction(const int value) {
LogDebug("ConstantFactorGrammarAction(%d)", value);
return value;
}
int IntegerConstantGrammarAction(const int value) {
LogDebug("IntegerConstantGrammarAction(%d)", value);
return value;
}

View File

@ -0,0 +1,30 @@
#ifndef BISON_ACTIONS_HEADER
#define BISON_ACTIONS_HEADER
#include "../../backend/support/shared.h"
/**
* Se definen las acciones a ejecutar sobre cada regla de producción de la
* gramática. El objetivo de cada acción debe ser el de construir el nodo
* adecuado que almacene la información requerida en el árbol de sintaxis
* abstracta (i.e., el AST).
*/
// Programa.
int ProgramGrammarAction(const int value);
// Expresión.
int AdditionExpressionGrammarAction(const int leftValue, const int rightValue);
int SubtractionExpressionGrammarAction(const int leftValue, const int rightValue);
int MultiplicationExpressionGrammarAction(const int leftValue, const int rightValue);
int DivisionExpressionGrammarAction(const int leftValue, const int rightValue);
int FactorExpressionGrammarAction(const int value);
// Factores.
int ExpressionFactorGrammarAction(const int value);
int ConstantFactorGrammarAction(const int value);
// Constantes.
int IntegerConstantGrammarAction(const int value);
#endif

View File

@ -0,0 +1,41 @@
%{
#include "bison-actions.h"
%}
// IDs de los tokens generados desde Flex:
%token ADD
%token SUB
%token MUL
%token DIV
%token OPEN_PARENTHESIS
%token CLOSE_PARENTHESIS
%token INTEGER
// Reglas de asociatividad y precedencia (de menor a mayor):
%left ADD SUB
%left MUL DIV
%%
program: expression { $$ = ProgramGrammarAction($1); }
;
expression: expression ADD expression { $$ = AdditionExpressionGrammarAction($1, $3); }
| expression SUB expression { $$ = SubtractionExpressionGrammarAction($1, $3); }
| expression MUL expression { $$ = MultiplicationExpressionGrammarAction($1, $3); }
| expression DIV expression { $$ = DivisionExpressionGrammarAction($1, $3); }
| factor { $$ = FactorExpressionGrammarAction($1); }
;
factor: OPEN_PARENTHESIS expression CLOSE_PARENTHESIS { $$ = ExpressionFactorGrammarAction($2); }
| constant { $$ = ConstantFactorGrammarAction($1); }
;
constant: INTEGER { $$ = IntegerConstantGrammarAction($1); }
;
%%

46
src/main.c Normal file
View File

@ -0,0 +1,46 @@
#include "backend/code-generation/generator.h"
#include "backend/support/logger.h"
#include "backend/support/shared.h"
#include "frontend/syntactic-analysis/bison-parser.h"
#include <stdio.h>
//Estado de la aplicación.
CompilerState state;
// Punto de entrada principal del compilador.
const int main(const int argumentCount, const char ** arguments) {
// Inicializar estado de la aplicación.
state.result = 0;
state.succeed = false;
// Mostrar parámetros recibidos por consola.
for (int i = 0; i < argumentCount; ++i) {
LogInfo("Argumento %d: '%s'", i, arguments[i]);
}
// Compilar el programa de entrada.
LogInfo("Compilando...\n");
const int result = yyparse();
switch (result) {
case 0:
if (state.succeed) {
LogInfo("La compilacion fue exitosa.");
Generator(state.result);
}
else {
LogError("Se produjo un error en la aplicacion.");
return -1;
}
break;
case 1:
LogError("Bison finalizo debido a un error de sintaxis.");
break;
case 2:
LogError("Bison finalizo abruptamente debido a que ya no hay memoria disponible.");
break;
default:
LogError("Error desconocido mientras se ejecutaba el analizador Bison (codigo %d).", result);
}
LogInfo("Fin.");
return result;
}