corrigen bugs.
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)
./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)
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
}
void ParseError_delete(ParseError* pe) {
if (pe) {
- free(pe->message);
+ if (pe->message) {
+ free(pe->message);
+ }
}
free(pe);
}
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 {
--- /dev/null
+/* 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 <llucare@fi.uba.ar>
+ * 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);
+}
+
--- /dev/null
+/* 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 <llucare@fi.uba.ar>
+ * 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 */
#include "bool.h"
#include "equation.h"
+#include "parser_common.h"
#include "parseerror.h"
#include "dllist.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.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(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;
#include "bool.h"
#include "parseerror.h"
+#include "parser_common.h"
+#include "variable.h"
+#include "variable_list.h"
#include "dllist.h"
+#include "strutil.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.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(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;
}
* 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 <stdlib.h>
-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 */
--- /dev/null
+/* 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 <llucare@fi.uba.ar>
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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;
+}
+
* $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 <stdlib.h>
#include <stdio.h>
#include <string.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(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)
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);
}
#include "dllist.h"
#include <stdlib.h>
-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);
#include <string.h>
#include "memdebug_debugger.h"
+#include "variable_list.h"
#include "parser_variable.h"
/**
*/
#include "strutil.h"
+#include "parser_common.h"
#include <stdlib.h>
#include <string.h>
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;
}
*/
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 */
--- /dev/null
+/* 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 <llucare@fi.uba.ar>
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#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);
+}
+
--- /dev/null
+/* 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 <llucare@fi.uba.ar>
+ * 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 <stdlib.h>
+#include <stdio.h>
+
+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 */