From: Leandro Lucarella Date: Tue, 16 Sep 2003 06:59:30 +0000 (+0000) Subject: Andan (con pruebas moderadas) todos los parsers. Se separan algunas cosas. Se X-Git-Tag: svn_import~5 X-Git-Url: https://git.llucax.com/z.facultad/75.42/calculadora.git/commitdiff_plain/c57495a01a44c4401340183e8773df2db62401b4?ds=sidebyside Andan (con pruebas moderadas) todos los parsers. Se separan algunas cosas. Se corrigen bugs. --- diff --git a/Makefile b/Makefile index 9b31f15..f860561 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,8 @@ CFLAGS=-ansi -pedantic -Wall -g3 -DDEBUG CC=gcc-3.2 # Pruebas. -TESTS=dllist_test memdebug_test parser_equation_test parser_variable_test +TESTS=dllist_test memdebug_test parser_equation_test parser_variable_test \ + parser_expression_test # Programa a compilar. TARGETS=$(TESTS) @@ -30,15 +31,21 @@ tests: $(TESTS) ./dllist_test ./memdebug_test ./parser_equation_test 'a = 40' - ./parser_variable_test 'a = 40' + ./parser_variable_test 'a 40 -50 0.11' + ./parser_expression_test '(3 + (-a)) * 2' dllist_test: dllist.o memdebug_test: dllist.o meminfo.o memdebug.o -parser_equation_test: dllist.o parseerror.o equation.o strutil.o parser_equation.o meminfo.o memdebug.o +parser_equation_test: dllist.o strutil.o meminfo.o memdebug.o parseerror.o \ + parser_common.o equation.o parser_equation.o -parser_variable_test: dllist.o parseerror.o variable.o strutil.o parser_variable.o meminfo.o memdebug.o +parser_variable_test: dllist.o strutil.o meminfo.o memdebug.o parseerror.o \ + parser_common.o variable.o variable_list.o parser_variable.o + +parser_expression_test: dllist.o strutil.o meminfo.o memdebug.o parseerror.o \ + parser_common.o variable.o variable_list.o parser_expression.o tp1: $(TARGETS) diff --git a/memdebug.c b/memdebug.c index 30079ad..724f9cb 100644 --- a/memdebug.c +++ b/memdebug.c @@ -115,7 +115,7 @@ void memdebug_free(void* ptr) { DLList_remove_current(memdebug_list); #ifdef DEBUG } else { - fprintf(stderr, "%s: No se encontro el ptr 0x%X en la lista.\n", + fprintf(stderr, "%s: No se encontró el ptr 0x%X en la lista.\n", __FILE__, (size_t)ptr); #endif } diff --git a/parseerror.c b/parseerror.c index 2b71479..038314d 100644 --- a/parseerror.c +++ b/parseerror.c @@ -33,7 +33,9 @@ ParseError* ParseError_new(void) { void ParseError_delete(ParseError* pe) { if (pe) { - free(pe->message); + if (pe->message) { + free(pe->message); + } } free(pe); } @@ -83,10 +85,12 @@ size_t ParseError_print(ParseError* pe, FILE* fp) { if (!pe->pos) { /* Si hay mensaje. */ if (pe->message) { - return fprintf(fp, "Error: %s.\n", pe->message); + return fprintf(fp, "Error en la línea %u: %s.\n", pe->line, + pe->message); /* Si no hay mensaje. */ } else { - return fprintf(fp, "Error no especificado.\n"); + return fprintf(fp, "Error en la línea %u no especificado.\n", + pe->line); } /* Es un error de interpretación en una posición específica. */ } else { diff --git a/parser_common.c b/parser_common.c new file mode 100644 index 0000000..2c0f988 --- /dev/null +++ b/parser_common.c @@ -0,0 +1,35 @@ +/* vim: set et sts=4 sw=4 fdm=indent fdl=1 fdn=0 fo+=t tw=80: + * + * Taller de Programación (75.42). + * + * Ejercicio Número 2: + * Programa calculadora. + * + * Copyleft 2003 - Leandro Lucarella + * Puede copiar, modificar y distribuir este programa bajo los términos de + * la licencia GPL (http://www.gnu.org/). + * + * Creado: mar sep 16 00:59:12 ART 2003 + * + * $Id$ + */ + +#include "parser_common.h" +#include "bool.h" + +bool is_space(char c) { + return (c == ' ') || (c == '\t'); +} + +bool is_number(char c) { + return ('0' <= c) && (c <= '9'); +} + +bool is_alpha(char c) { + return (c == '_') || (('a' <= c) && (c <= 'z')) || (('A' <= c) && (c <= 'Z')); +} + +bool is_alpha_num(char c) { + return is_number(c) || is_alpha(c); +} + diff --git a/parser_common.h b/parser_common.h new file mode 100644 index 0000000..faed17b --- /dev/null +++ b/parser_common.h @@ -0,0 +1,30 @@ +/* vim: set et sts=4 sw=4 fdm=indent fdl=1 fdn=1 fo+=t tw=80: + * + * Taller de Programación (75.42). + * + * Ejercicio Número 2: + * Programa calculadora. + * + * Copyleft 2003 - Leandro Lucarella + * Puede copiar, modificar y distribuir este programa bajo los términos de + * la licencia GPL (http://www.gnu.org/). + * + * Creado: mar sep 16 01:00:39 ART 2003 + * + * $Id$ + */ + +#ifndef PARSER_COMMON_H +#define PARSER_COMMON_H + +#include "bool.h" + +bool is_space(char c); + +bool is_number(char c); + +bool is_alpha(char c); + +bool is_alpha_num(char c); + +#endif /* PARSER_COMMON_H */ diff --git a/parser_equation.c b/parser_equation.c index d76bd1d..ebf3698 100644 --- a/parser_equation.c +++ b/parser_equation.c @@ -16,27 +16,14 @@ #include "bool.h" #include "equation.h" +#include "parser_common.h" #include "parseerror.h" #include "dllist.h" #include #include #include -bool is_space(char c) { - return (c == ' ') || (c == '\t'); -} - -bool is_number(char c) { - return ('0' <= c) && (c <= '9'); -} - -bool is_alpha(char c) { - return (c == '_') || (('a' <= c) && (c <= 'z')) || (('A' <= c) && (c <= 'Z')); -} - -bool is_alpha_num(c) { - return is_number(c) || is_alpha(c); -} +#include "memdebug_debugger.h" size_t DLList_equation_print(DLList* l, FILE* fp) { size_t cant = 0; diff --git a/parser_expression.c b/parser_expression.c index 37a4df4..8ee6cc0 100644 --- a/parser_expression.c +++ b/parser_expression.c @@ -16,97 +16,151 @@ #include "bool.h" #include "parseerror.h" +#include "parser_common.h" +#include "variable.h" +#include "variable_list.h" #include "dllist.h" +#include "strutil.h" #include #include #include -bool is_space(char c) { - return (c == ' ') || (c == '\t'); -} - -bool is_number(char c) { - return ('0' <= c) && (c <= '9'); -} - -bool is_alpha(char c) { - return (c == '_') || (('a' <= c) && (c <= 'z')) || (('A' <= c) && (c <= 'Z')); -} - -bool is_alpha_num(c) { - return is_number(c) || is_alpha(c); -} - -size_t DLList_equation_print(DLList* l, FILE* fp) { - size_t cant = 0; - Equation* eq; - for (eq = DLList_begin(l); DLList_have_more(l); eq = DLList_next(l)) { - cant += Equation_print(eq, fp); - } - return cant; -} - -void DLList_equation_delete(DLList* l) { - if (l) { - while (!DLList_empty(l)) { - Equation_delete((Equation*)DLList_pop(l)); - } - } - DLList_delete(l); -} +#include "memdebug_debugger.h" #define PARSE_ERROR(str) ParseError_set_pos_message(error, str, i + 1, c) -bool parser_expression(const char* exp, size_t len, float* result, - ParseError* error) { - enum {SEARCH_VAR, VAR, SEARCH_EQUAL, EXP} state = SEARCH_VAR; - size_t var_start = 0; - size_t var_len = 0; +/** + * TODO: exp tiene que venir sin espacios. + * + * @bug Los números negativos hay que ponerlos entre paréntesis si hay algún + * operador de baja precedencia en el mismo nivel. + */ +bool parser_expression(const char* exp, size_t len, float* result, DLList* + var_list, ParseError* error) { + size_t level = 0; size_t i; char c; + char* exp_cuted; + char* err = NULL; + float result1; + float result2; + /* Si es vacío, devuelve cero. */ + if (!len) { + *result = 0.0; + return TRUE; + } /* Si es una expresión numérica, la devolvemos directamente. */ - *result = strntod(exp, len, &err); + if ((exp[0] == '(') && (exp[len-1] == ')')) { + /* Si está entre paréntesis, se los sacamos. */ + exp_cuted = strutil_copy_fragment(exp, 1, len - 2); + } else { + exp_cuted = strutil_copy_fragment(exp, 0, len); + } + /* No se pudo alocar la memoria. */ + if (!exp_cuted) { + ParseError_set_message(error, "No se pudo alocar memoria"); + return FALSE; + } + /* Trato de convertirlo a float. */ + *result = strtod(exp_cuted, &err); if (*err == '\0') { /* OK */ - return TRUE; /* La expresión es numérica (FIXME: puede ser numérica - entre paréntesis y esto falla). */ + free(exp_cuted); + return TRUE; } - /* No es una expresión numérica, busco operadores de alta precedencia. */ + /* No es un valor numérico, me fijo si está en la lista de variables. */ + if (DLList_variable_find(var_list, exp_cuted)) { + *result = ((Variable*)DLList_current(var_list))->value; + free(exp_cuted); + return TRUE; + } + free(exp_cuted); + /* No es una expresión numérica ni una variable, busco operadores de alta + * precedencia. */ for (i = 0; i < len; i++) { c = exp[i]; + /* Manejo los niveles de paréntesis. */ if ((c == '(')) { level++; } else if (c == ')') { level--; + /* Está en el nivel básico y hay un operador. */ } else if (((c == '+') || (c == '-')) && !level) { - /* FIXME: Es una operador => segundo operando. */ - if (!parser_expression(exp, i - 1, result1, error)) { + /* No hay segundo operando. */ + if (i == len - 1) { + ParseError_set_message(error, "Falta segundo operando"); return FALSE; } - /* FIXME: verificar que no este vacío el segundo operando. */ - if (!parser_expression(exp + i + 1, len - i - 1, result2, error)) { + /* Calcula la expresión del primer operando. */ + if (!parser_expression(exp, i, &result1, var_list, error)) { + /* Si hay error, devuelve FALSE (y "arrastra" el error. */ return FALSE; } - } else if (((c == '+') || (c == '-')) && !level) { - } else { /* es otra cosa => error */ - PARSE_ERROR("un espacio o una letra"); - return FALSE; + /* Calcula la expresión del segundo operando. */ + if (!parser_expression(exp + i + 1, len - i - 1, &result2, + var_list, error)) { + /* Si hay error, devuelve FALSE (y "arrastra" el error. */ + return FALSE; + } + *result = (c == '+') ? (result1 + result2) : (result1 - result2); + return TRUE; + } + } + /* No hay tampoco operadores de alta precendencia, busco operadores de baja + * precedencia. */ + for (i = 0; i < len; i++) { + c = exp[i]; + /* Manejo los niveles de paréntesis. */ + if ((c == '(')) { + level++; + } else if (c == ')') { + level--; + /* Está en el nivel básico y hay un operador. */ + } else if (((c == '*') || (c == '/')) && !level) { + /* No hay segundo operando. */ + if (i == len - 1) { + ParseError_set_message(error, "Falta segundo operando"); + return FALSE; + } + /* Calcula la expresión del primer operando. */ + if (!parser_expression(exp, i, &result1, var_list, error)) { + /* Si hay error, devuelve FALSE (y "arrastra" el error. */ + return FALSE; + } + /* Calcula la expresión del segundo operando. */ + if (!parser_expression(exp + i + 1, len - i - 1, &result2, + var_list, error)) { + /* Si hay error, devuelve FALSE (y "arrastra" el error. */ + return FALSE; + } + /* Si estamos dividiendo, chequeamos que el divisor no sea cero. */ + if (c == '/') { + /* Segundo operando es cero. */ + if (result2 == 0.0) { + ParseError_set_message(error, "División por cero"); + return FALSE; + } + *result = result1 / result2; + /* Si estamos multiplicando, no hay chequeos extra. */ + } else { + *result = result1 * result2; + } + return TRUE; } } - /* Error, no se llego a parsear todo bien. */ - error->pos = len; - switch (state) { - case SEARCH_VAR: - ParseError_set_message(error, - "No se encontró la definición de una variable"); - break; - case VAR: - case SEARCH_EQUAL: - ParseError_set_message(error, "No se encontró un igual"); - break; - case EXP: - ParseError_set_message(error, "No se encontró una expresión"); - break; + /* Si no tiene operadores ni valores numéricos ni es una variable, pero está + * entre paréntesis, pruebo de evaluar lo que está dentro de los paréntesis. + */ + if ((exp[0] == '(') && (exp[len-1] == ')')) { + if (!parser_expression(exp + 1, len - 2, result, var_list, error)) { + /* Si hay error, devuelve FALSE (y "arrastra" el error. */ + return FALSE; + } else { + return TRUE; + } } + /* Si no tiene operadores ni valores numéricos ni es una variable, es un + * error. */ + ParseError_set_message(error, "La expresión es incorrecta"); return FALSE; } diff --git a/parser_expression.h b/parser_expression.h index c144704..b09ac5e 100644 --- a/parser_expression.h +++ b/parser_expression.h @@ -9,39 +9,26 @@ * Puede copiar, modificar y distribuir este programa bajo los términos de * la licencia GPL (http://www.gnu.org/). * - * Creado: lun sep 15 01:26:35 ART 2003 + * Creado: mar sep 16 02:39:32 ART 2003 * * $Id$ */ -#ifndef PARSER_EQUATION_H -#define PARSER_EQUATION_H +#ifndef PARSER_EXPRESSION_H +#define PARSER_EXPRESSION_H #include "bool.h" #include "parseerror.h" #include "dllist.h" #include -size_t DLList_equation_print(DLList* l, FILE* fp); - -void DLList_equation_delete(DLList* l); - -/* TODO: - * - * opcion1: - * que devuelva la posición en donde empieza la exresión o cero si hubo - * error. En otro lugar debe dejar la variable que se parseo. - * - * opcion2: <---------------------------- Creo que es la mejor. - * Que devuelva una estructura con - * - variable. - * - expresion. - * - * opcion3: - * Que devuelva una estructura de nodo de variable o de ecuacion segun como - * se la llame. +/** + * TODO: exp tiene que venir sin espacios. + * + * @bug Los números negativos hay que ponerlos entre paréntesis si hay algún + * operador de baja precedencia en el mismo nivel. */ -bool parser_equation(const char* line, size_t len, DLList* equation_list, - ParseError* error); +bool parser_expression(const char* exp, size_t len, float* result, DLList* + var_list, ParseError* error); -#endif /* PARSER_EQUATION_H */ +#endif /* PARSER_EXPRESSION_H */ diff --git a/parser_expression_test.c b/parser_expression_test.c new file mode 100644 index 0000000..6f58f58 --- /dev/null +++ b/parser_expression_test.c @@ -0,0 +1,104 @@ +/* vim: set et sts=4 sw=4 fdm=indent fdl=1 fdn=0 fo+=t: + * + * Taller de Programación (75.42). + * + * Ejercicio Número 2: + * Programa calculadora. + * + * Copyleft 2003 - Leandro Lucarella + * Puede copiar, modificar y distribuir este programa bajo los términos de + * la licencia GPL (http://www.gnu.org/). + * + * Creado: mar sep 16 02:24:44 ART 2003 + * + * $Id$ + */ + +/** + * \file + * Hace varios chequeos para probar si anda bien la función + * parser_expression(). + */ + +#include +#include +#include + +#include "memdebug_debugger.h" +#include "parser_expression.h" +#include "variable.h" +#include "variable_list.h" +#include "strutil.h" + +/** + * Programa para probar parser_expression(). + * + * \return EXIT_SUCCESS si se realizó bien, EXIT_FAILURE si no. + */ +int main(int argc, char* argv[]) { + /* Declaración de variables. */ + int i; + float result; + DLList* lista; + Variable* var; + char* arg; + ParseError* error; + + if (argc < 2) { + fprintf(stderr, "Debe pasar expresiones a evaluar como parámetros.\n"); + fprintf(stderr, "Ejemplo: %s ' (50 + 10 ) *2/(-1)'\n", argv[0]); + return EXIT_FAILURE; + } + + lista = DLList_new(); + if (!lista) { + fprintf(stderr, "No se pudo crear la lista.\n"); + return EXIT_FAILURE; + } + + var = Variable_new("a", 0, 3); + if (!var) { + fprintf(stderr, "No se pudo crear la variable.\n"); + return EXIT_FAILURE; + } + Variable_set_value(var, "5", 0, 1); + + if (!DLList_push(lista, var)) { + fprintf(stderr, "No se pudo agregar variable a la lista.\n"); + return EXIT_FAILURE; + } + + DLList_variable_print(lista, stdout); + + error = ParseError_new(); + if (!error) { + fprintf(stderr, "No se pudo crear el error.\n"); + return EXIT_FAILURE; + } + + for (i = 1; i < argc; i++) { + arg = strutil_copy_stripspaces(argv[i], strlen(argv[i])); + if (!arg) { + fprintf(stderr, "No se pudo crear copia de argv[%u].\n", i); + return EXIT_FAILURE; + } + if (!parser_expression(arg, strlen(arg), &result, lista, + error)) { + error->line = i; + ParseError_print(error, stderr); + } else { + printf("Resultado %u: %f\n", i, result); + } + free(arg); + } + + DLList_variable_delete(lista); + + ParseError_delete(error); + + /* Veo si pierdo memoria. */ + memdebug_end(); + + return EXIT_SUCCESS; +} + diff --git a/parser_variable.c b/parser_variable.c index fadb965..cf9a026 100644 --- a/parser_variable.c +++ b/parser_variable.c @@ -14,58 +14,18 @@ * $Id$ */ +#include "parser_variable.h" #include "bool.h" #include "variable.h" -#include "parser_variable.h" +#include "variable_list.h" +#include "parser_common.h" #include "parseerror.h" #include "dllist.h" #include #include #include -bool is_space(char c) { - return (c == ' ') || (c == '\t'); -} - -bool is_number(char c) { - return ('0' <= c) && (c <= '9'); -} - -bool is_alpha(char c) { - return (c == '_') || (('a' <= c) && (c <= 'z')) || (('A' <= c) && (c <= 'Z')); -} - -bool is_alpha_num(c) { - return is_number(c) || is_alpha(c); -} - -bool DLList_variable_find(DLList* l, Variable* var) { - Variable* v; - for (v = DLList_begin(l); DLList_have_more(l); v = DLList_next(l)) { - if (!strcmp(v->variable, var->variable)) { - return TRUE; - } - } - return FALSE; -} - -size_t DLList_variable_print(DLList* l, FILE* fp) { - size_t cant = 0; - Variable* v; - for (v = DLList_begin(l); DLList_have_more(l); v = DLList_next(l)) { - cant += Variable_print(v, fp); - } - return cant; -} - -void DLList_variable_delete(DLList* l) { - if (l) { - while (!DLList_empty(l)) { - Variable_delete((Variable*)DLList_pop(l)); - } - } - DLList_delete(l); -} +#include "memdebug_debugger.h" #define PARSE_ERROR(str) ParseError_set_pos_message(error, str, i + 1, c) @@ -238,7 +198,7 @@ bool parser_variable(const char* line, size_t len, DLList* variable_list, return FALSE; } /* Ahora sí, la puedo agregar a la lista de variables. */ - if (DLList_variable_find(variable_list, var)) { + if (DLList_variable_find(variable_list, var->variable)) { /* Si ya había uno, lo borro. */ DLList_remove_current(variable_list); } diff --git a/parser_variable.h b/parser_variable.h index 91cf0fe..478b6a2 100644 --- a/parser_variable.h +++ b/parser_variable.h @@ -22,10 +22,6 @@ #include "dllist.h" #include -size_t DLList_variable_print(DLList* l, FILE* fp); - -void DLList_variable_delete(DLList* l); - bool parser_variable(const char* line, size_t len, DLList* variable_list, ParseError* error); diff --git a/parser_variable_test.c b/parser_variable_test.c index c682d93..f733fc6 100644 --- a/parser_variable_test.c +++ b/parser_variable_test.c @@ -25,6 +25,7 @@ #include #include "memdebug_debugger.h" +#include "variable_list.h" #include "parser_variable.h" /** diff --git a/strutil.c b/strutil.c index 48ec6f2..8a84d43 100644 --- a/strutil.c +++ b/strutil.c @@ -15,6 +15,7 @@ */ #include "strutil.h" +#include "parser_common.h" #include #include @@ -28,7 +29,24 @@ char* strutil_copy_fragment(const char* orig, size_t start, size_t len) { strncpy(new, orig + start, len); /* Termino la cadena. */ new[len] = '\0'; - /* Si no pude reservar la memoria, devuelvo NULL. */ + } + return new; +} + +char* strutil_copy_stripspaces(const char* orig, size_t len) { + size_t i = 0; + size_t j = 0; + /* Reservo el espacio (incluyendo el caracter nulo). */ + char* new = malloc(sizeof(char) * (len + 1)); + if (new) { + /* Copio caracteres que no sean espacios. */ + for (i = 0; i < len; i++) { + if (!is_space(orig[i])) { + new[j++] = orig[i]; + } + } + /* Termino la cadena. */ + new[j] = '\0'; } return new; } diff --git a/strutil.h b/strutil.h index 6da0a37..209308a 100644 --- a/strutil.h +++ b/strutil.h @@ -25,4 +25,9 @@ */ char* strutil_copy_fragment(const char* orig, size_t start, size_t len); +/** + * Copia un fragmento de una cadena eliminando los espacios. + */ +char* strutil_copy_stripspaces(const char* orig, size_t len); + #endif /* STRUTIL_H */ diff --git a/variable_list.c b/variable_list.c new file mode 100644 index 0000000..0919e7f --- /dev/null +++ b/variable_list.c @@ -0,0 +1,54 @@ +/* vim: set et sts=4 sw=4 fdm=indent fdl=1 fdn=0 fo+=t tw=80: + * + * Taller de Programación (75.42). + * + * Ejercicio Número 2: + * Programa calculadora. + * + * Copyleft 2003 - Leandro Lucarella + * Puede copiar, modificar y distribuir este programa bajo los términos de + * la licencia GPL (http://www.gnu.org/). + * + * Creado: mar sep 16 01:59:36 ART 2003 + * + * $Id$ + */ + +#include "bool.h" +#include "dllist.h" +#include "variable.h" +#include "variable_list.h" +#include +#include +#include + +#include "memdebug_debugger.h" + +bool DLList_variable_find(DLList* l, const char* var) { + Variable* v; + for (v = DLList_begin(l); DLList_have_more(l); v = DLList_next(l)) { + if (!strcmp(v->variable, var)) { + return TRUE; + } + } + return FALSE; +} + +size_t DLList_variable_print(DLList* l, FILE* fp) { + size_t cant = 0; + Variable* v; + for (v = DLList_begin(l); DLList_have_more(l); v = DLList_next(l)) { + cant += Variable_print(v, fp); + } + return cant; +} + +void DLList_variable_delete(DLList* l) { + if (l) { + while (!DLList_empty(l)) { + Variable_delete((Variable*)DLList_pop(l)); + } + } + DLList_delete(l); +} + diff --git a/variable_list.h b/variable_list.h new file mode 100644 index 0000000..6395d98 --- /dev/null +++ b/variable_list.h @@ -0,0 +1,31 @@ +/* vim: set et sts=4 sw=4 fdm=indent fdl=1 fdn=1 fo+=t tw=80: + * + * Taller de Programación (75.42). + * + * Ejercicio Número 2: + * Programa calculadora. + * + * Copyleft 2003 - Leandro Lucarella + * Puede copiar, modificar y distribuir este programa bajo los términos de + * la licencia GPL (http://www.gnu.org/). + * + * Creado: mar sep 16 02:01:55 ART 2003 + * + * $Id$ + */ + +#ifndef VARIABLE_LIST_H +#define VARIABLE_LIST_H + +#include "bool.h" +#include "dllist.h" +#include +#include + +bool DLList_variable_find(DLList* l, const char* var); + +size_t DLList_variable_print(DLList* l, FILE* fp); + +void DLList_variable_delete(DLList* l); + +#endif /* VARIABLE_LIST_H */