*
* Creado: dom sep 14 13:03:49 ART 2003
*
- * $Id: parser_equation.c 50 2003-09-15 06:44:27Z luca $
+ * $Id$
*/
#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;
}