CC=gcc-3.2
# Pruebas.
-TESTS=dllist_test memdebug_test parser_equation_test
+TESTS=dllist_test memdebug_test parser_equation_test parser_variable_test
# Programa a compilar.
TARGETS=$(TESTS)
tests: $(TESTS)
./dllist_test
./memdebug_test
+ ./parser_equation_test 'a = 40'
+ ./parser_variable_test 'a = 40'
dllist_test: dllist.o
memdebug_test: dllist.o meminfo.o memdebug.o
-parser_equation_test: dllist.o parseerror.o equation.o parser_equation.o meminfo.o memdebug.o
+parser_equation_test: dllist.o parseerror.o equation.o strutil.o parser_equation.o meminfo.o memdebug.o
+
+parser_variable_test: dllist.o parseerror.o variable.o strutil.o parser_variable.o meminfo.o memdebug.o
tp1: $(TARGETS)
--- /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: dom sep 14 13:03:49 ART 2003
+ *
+ * $Id$
+ */
+
+#include "bool.h"
+#include "variable.h"
+#include "parser_variable.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);
+}
+
+#define PARSE_ERROR(str) ParseError_set_pos_message(error, str, i + 1, c)
+
+bool parser_variable(const char* line, size_t len, DLList* variable_list,
+ ParseError* error) {
+ enum {SEARCH_VAR, VAR, SEARCH_VAL, VAL, SEARCH_MIN, MIN, SEARCH_MAX, MAX}
+ state = SEARCH_VAR;
+ size_t var_start = 0;
+ size_t var_len = 0;
+ size_t val_start = 0;
+ size_t val_len = 0;
+ size_t min_start = 0;
+ size_t min_len = 0;
+ size_t max_start = 0;
+ size_t max_len = 0;
+ size_t i;
+ char c;
+ Variable* var;
+ for (i = 0; i < len; i++) {
+ c = line[i];
+ switch (state) {
+ case SEARCH_VAR: /* Busca el comienzo de una variable. */
+ if (!is_space(c)) {
+ if (is_alpha(c)) {
+ /* Es una letra => es el comienzo de la variable. */
+ state = VAR;
+ var_start = i;
+ } else { /* es otra cosa => error */
+ PARSE_ERROR("un espacio o una letra");
+ return FALSE;
+ }
+ }
+ /* Si es espacio, no hace nada, deja que siga. */
+ break;
+ case VAR: /* Se fija cual es el nombre de la variable. */
+ if (!is_alpha_num(c)) {
+ if (is_space(c)) {
+ /* Es un espacio, entonces hay que buscar un igual. */
+ state = SEARCH_VAL;
+ /* Calculo la longitud de la variable. */
+ var_len = i - var_start;
+ } else { /* es otra cosa */
+ PARSE_ERROR(
+ "un espacio, una letra, un número o un igual");
+ return FALSE;
+ }
+ }
+ /* Si es alfanumérico, no hace nada, deja que siga. */
+ break;
+ case SEARCH_VAL: /* Busca el valor. */
+ if (!is_space(c)) {
+ /* Pasamos a interpretar el valor. */
+ state = VAL;
+ /* Pone el inicio del valor. */
+ val_start = i;
+ }
+ /* Si es espacio, no hace nada, deja que siga. */
+ break;
+ case VAL: /* Se fija hasta donde va el valor actual. */
+ if (is_space(c)) { /* Si es un espacio, terminó el valor. */
+ /* Buscamos otro valor. */
+ state = SEARCH_MIN;
+ /* Pone la longitud de este valor. */
+ val_len = i - val_start;
+ }
+ /* Si es alfanumérico, no hace nada, deja que siga. */
+ break;
+ case SEARCH_MIN: /* Busca el mímino. */
+ if (!is_space(c)) {
+ /* Pasamos a interpretar el mínimo. */
+ state = MIN;
+ /* Pone el inicio del mínimo. */
+ min_start = i;
+ }
+ /* Si es espacio, no hace nada, deja que siga. */
+ break;
+ case MIN: /* Se fija hasta donde va el mínimo. */
+ if (is_space(c)) { /* Si es un espacio, terminó el mínimo. */
+ /* Buscamos el máximo. */
+ state = SEARCH_MAX;
+ /* Pone la longitud del mínimo. */
+ min_len = i - min_start;
+ }
+ /* Si es alfanumérico, no hace nada, deja que siga. */
+ break;
+ case SEARCH_MAX: /* Busca el máximo. */
+ if (!is_space(c)) {
+ /* Pasamos al estado de máximo encontrado. */
+ state = MAX;
+ /* Pone el inicio del máximo. */
+ max_start = i;
+ /* Pone la longitud del máximo hasta el final (ya no hay más
+ * valores a buscar). */
+ max_len = i - len;
+ }
+ /* Si es espacio, no hace nada, deja que siga. */
+ break;
+ }
+ /* Si status es max, no se busca mas nada, terminamo el loop for. */
+ if (state == MAX) {
+ break;
+ }
+ }
+ /* Veo como terminó la interpretación. */
+ error->pos = len;
+ /* No se pudo interpretar una variable. */
+ if (state < VAL) {
+ switch (state) {
+ case SEARCH_VAR:
+ /* No tiene variable. */
+ ParseError_set_message(error,
+ "No se encontró la definición de una variable");
+ return FALSE;
+ case VAR:
+ case SEARCH_VAL:
+ /* No tiene valor de la variable. */
+ ParseError_set_message(error,
+ "No se encontró el valor de la variable");
+ return FALSE;
+ }
+ }
+ /* Al menos la variable con su valor ya la tenemos. */
+ var = Variable_new(line, var_start, var_len);
+ if (!var) {
+ ParseError_set_message(error, "No se pudo alocar memoria de variable");
+ return FALSE;
+ }
+ switch (state) {
+ case MAX:
+ /* Tiene máximo. */
+ if (!Variable_set_max(var, line, max_start, max_len)) {
+ /* No se pudo setear el máximo. */
+ Variable_delete(var);
+ ParseError_set_message(error,
+ "No se pudo establecer el máximo");
+ return FALSE;
+ }
+ /* Continúo, tiene mínimo y valor también. */
+ case MIN:
+ case SEARCH_MAX:
+ /* Tiene míninimo. */
+ if (!Variable_set_min(var, line, min_start, min_len)) {
+ /* No se pudo setear el mínimo. */
+ Variable_delete(var);
+ ParseError_set_message(error,
+ "No se pudo establecer el mínimo");
+ return FALSE;
+ }
+ /* Continúo, tiene valor también. */
+ case VAL:
+ case SEARCH_MIN:
+ /* Tiene solo valor. */
+ if (!Variable_set_value(var, line, val_start, val_len)) {
+ /* No se pudo setear el valor. */
+ Variable_delete(var);
+ ParseError_set_message(error,
+ "No se pudo establecer el valor");
+ return FALSE;
+ }
+ }
+ /* Está todo ok, tengo la variable cargada, verifico max y min. */
+ if (!Variable_check_min_max(var)) {
+ Variable_delete(var);
+ ParseError_set_message(error,
+ "El mínimo es mayor que el máximo");
+ return FALSE;
+ }
+ /* Ahora sí, la puedo agregar a la lista de variables. */
+ if (DLList_variable_find(variable_list, var)) {
+ /* Si ya había uno, lo borro. */
+ DLList_remove_current(variable_list);
+ }
+ if (!DLList_push(variable_list, var)) {
+ ParseError_set_message(error,
+ "No se pudo agregar variable a la lista");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+#undef PARSE_ERROR /* Sólo queremos usarlo en este fragmento */
+
--- /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: lun sep 15 20:22:23 ART 2003
+ *
+ * $Id$
+ */
+
+#ifndef PARSER_VARIABLE_H
+#define PARSER_VARIABLE_H
+
+#include "bool.h"
+#include "parseerror.h"
+#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);
+
+#endif /* PARSER_VARIABLE_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: sáb ago 30 18:24:31 ART 2003
+ *
+ * $Id$
+ */
+
+/**
+ * \file
+ * Hace varios chequeos para probar si anda bien la función
+ * parser_parser_variable().
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "memdebug_debugger.h"
+#include "parser_variable.h"
+
+/**
+ * Programa para probar parser_variable().
+ *
+ * \return EXIT_SUCCESS si se realizó bien, EXIT_FAILURE si no.
+ */
+int main(int argc, char* argv[]) {
+ /* Declaración de variables. */
+ int i;
+ DLList* lista;
+ ParseError* error;
+
+ if (argc < 2) {
+ fprintf(stderr, "Debe pasar variables a evaluar como parámetros.\n");
+ fprintf(stderr, "Ejemplo: %s 'b = 50' 'a = 1.7\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ lista = DLList_new();
+ if (!lista) {
+ fprintf(stderr, "No se pudo crear la lista.\n");
+ return EXIT_FAILURE;
+ }
+
+ error = ParseError_new();
+ if (!error) {
+ fprintf(stderr, "No se pudo crear el error.\n");
+ return EXIT_FAILURE;
+ }
+
+ for (i = 1; i < argc; i++) {
+ if (!parser_variable(argv[i], strlen(argv[i]), lista, error)) {
+ error->line = i;
+ ParseError_print(error, stderr);
+ }
+ }
+
+ DLList_variable_print(lista, stdout);
+
+ DLList_variable_delete(lista);
+
+ ParseError_delete(error);
+
+ /* Veo si pierdo memoria. */
+ memdebug_end();
+
+ return EXIT_SUCCESS;
+}
+
--- /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: lun sep 15 18:12:35 ART 2003
+ *
+ * $Id$
+ */
+
+#include "strutil.h"
+#include <stdlib.h>
+#include <string.h>
+
+#include "memdebug_debugger.h"
+
+char* strutil_copy_fragment(const char* orig, size_t start, size_t len) {
+ /* Reservo el espacio (incluyendo el caracter nulo). */
+ char* new = malloc(sizeof(char) * (len + 1));
+ if (new) {
+ /* Copio el fragmento. */
+ strncpy(new, orig + start, len);
+ /* Termino la cadena. */
+ new[len] = '\0';
+ /* Si no pude reservar la memoria, devuelvo NULL. */
+ }
+ return new;
+}
+
--- /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: lun sep 15 18:14:36 ART 2003
+ *
+ * $Id$
+ */
+
+#ifndef STRUTIL_H
+#define STRUTIL_H
+
+#include <stdlib.h>
+#include <stdio.h>
+
+/**
+ * Copia un fragmento de una cadena devolviendo el puntero a la copia.
+ */
+char* strutil_copy_fragment(const char* orig, size_t start, 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: lun sep 15 01:10:28 ART 2003
+ *
+ * $Id$
+ */
+
+#include "variable.h"
+#include "strutil.h"
+#include <stdlib.h>
+#include <string.h>
+
+#include "memdebug_debugger.h"
+
+Variable* Variable_new(const char* line, size_t var_start, size_t var_len) {
+ Variable* var = malloc(sizeof(Variable));
+ if (var) {
+ /* Copio el fragmento de la variable. */
+ var->variable = strutil_copy_fragment(line, var_start, var_len);
+ /* Si no hay más memoria, libero el resto y devuelvo NULL. */
+ if (!var->variable) {
+ free(var);
+ return NULL;
+ }
+ var->have_min = FALSE;
+ var->have_max = FALSE;
+ }
+ return var;
+}
+
+void Variable_delete(Variable* var) {
+ if (var) {
+ free(var->variable);
+ }
+ free(var);
+}
+
+/* simplifico el manejo de errores, false si hubo error. */
+bool Variable_set_value(Variable* var, const char* line, size_t start,
+ size_t len) {
+ char* err = NULL;
+ char* str = strutil_copy_fragment(line, start, len);
+ if (!str) {
+ return FALSE;
+ }
+ var->value = strtod(str, &err);
+ /* Si el caracter donde apunta error no es 0 es porque hay error. */
+ if (*err != '\0') {
+ /* Libero memoria reservada. */
+ free(str);
+ return FALSE;
+ }
+ free(str);
+ return TRUE;
+}
+
+/* simplifico el manejo de errores, false si hubo error. */
+bool Variable_set_min(Variable* var, const char* line, size_t start,
+ size_t len) {
+ char* err = NULL;
+ char* str = strutil_copy_fragment(line, start, len);
+ if (!str) {
+ return FALSE;
+ }
+ var->min = strtod(str, &err);
+ /* Si el caracter donde apunta error no es 0 es porque hay error. */
+ if (*err != '\0') {
+ /* Libero memoria reservada. */
+ free(str);
+ return FALSE;
+ }
+ free(str);
+ var->have_min = TRUE;
+ return TRUE;
+}
+
+/* simplifico el manejo de errores, false si hubo error. */
+bool Variable_set_max(Variable* var, const char* line, size_t start,
+ size_t len) {
+ char* err = NULL;
+ char* str = strutil_copy_fragment(line, start, len);
+ if (!str) {
+ return FALSE;
+ }
+ var->max = strtod(str, &err);
+ /* Si el caracter donde apunta error no es 0 es porque hay error. */
+ if (*err != '\0') {
+ /* Libero memoria reservada. */
+ free(str);
+ return FALSE;
+ }
+ free(str);
+ var->have_max = TRUE;
+ return TRUE;
+}
+
+/* simplifico el manejo de errores, false si hubo error. */
+bool Variable_check_min_max(Variable* var) {
+ return !var->have_min
+ || !var->have_max
+ || (var->min <= var->max);
+}
+
+size_t Variable_print(Variable* var, FILE* fp) {
+ size_t cant = 0;
+ cant += fprintf(fp, "%s = %f", var->variable, var->value);
+ if (var->have_min || var->have_max) {
+ cant += fprintf(fp, "(");
+ }
+ if (var->have_min) {
+ cant += fprintf(fp, "min = %f", var->min);
+ }
+ if (var->have_min && var->have_max) {
+ cant += fprintf(fp, ", ");
+ }
+ if (var->have_max) {
+ cant += fprintf(fp, "max = %f", var->max);
+ }
+ if (var->have_min || var->have_max) {
+ cant += fprintf(fp, ")");
+ }
+ cant += fprintf(fp, "\n");
+ return cant;
+}
+
--- /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: lun sep 15 18:55:54 ART 2003
+ *
+ * $Id$
+ */
+
+#ifndef VARIABLE_H
+#define VARIABLE_H
+
+#include "bool.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+typedef struct {
+ char* variable;
+ float value;
+ float min;
+ float max;
+ bool have_min;
+ bool have_max;
+} Variable;
+
+Variable* Variable_new(const char* line, size_t var_start, size_t var_len);
+
+void Variable_delete(Variable* var);
+
+/* simplifico el manejo de errores, false si hubo error. */
+bool Variable_set_value(Variable* var, const char* line, size_t start,
+ size_t len);
+
+/* simplifico el manejo de errores, false si hubo error. */
+bool Variable_set_min(Variable* var, const char* line, size_t start,
+ size_t len);
+
+/* simplifico el manejo de errores, false si hubo error. */
+bool Variable_set_max(Variable* var, const char* line, size_t start,
+ size_t len);
+
+/* simplifico el manejo de errores, false si hubo error. */
+bool Variable_check_min_max(Variable* var);
+
+size_t Variable_print(Variable* var, FILE* fp);
+
+#endif /* VARIABLE_H */