Commit d2c3b2ba authored by Arturo Espinosa's avatar Arturo Espinosa

It autocalcs!



It autocalcs!

My technique is incredibly cool!  The engine should support
most excel features for recursive computations (we just need
the dialog boxes now).

It still has a wierdo crash from a mismatched string ref/unref
but that is going to be trivial to fix.

Next step:  Functions in Perl.
parent 76c29c52
1998-08-07 Miguel de Icaza <miguel@nuclecu.unam.mx> 1998-08-07 Miguel de Icaza <miguel@nuclecu.unam.mx>
* src/gnumeric-sheet.c: Renamed all references to (GnumericSheet
*) called "sheet" to "gsheet".
* src/sheet.h: Renamed parent_workbook in to be workbook.
* src/cell.h: Include a Sheet * in the cell. Make routines that
required a Sheet parameter only use the Cell parameter now.
* src/eval.c (add_tree_deps, cell_add_dependencies, * src/eval.c (add_tree_deps, cell_add_dependencies,
add_value_deps, dependency_hash_init, dependency_hash, add_value_deps, dependency_hash_init, dependency_hash,
dependency_equal): New functions to maintain the dependency_equal): New functions to maintain the
......
1998-08-07 Miguel de Icaza <miguel@nuclecu.unam.mx> 1998-08-07 Miguel de Icaza <miguel@nuclecu.unam.mx>
* src/gnumeric-sheet.c: Renamed all references to (GnumericSheet
*) called "sheet" to "gsheet".
* src/sheet.h: Renamed parent_workbook in to be workbook.
* src/cell.h: Include a Sheet * in the cell. Make routines that
required a Sheet parameter only use the Cell parameter now.
* src/eval.c (add_tree_deps, cell_add_dependencies, * src/eval.c (add_tree_deps, cell_add_dependencies,
add_value_deps, dependency_hash_init, dependency_hash, add_value_deps, dependency_hash_init, dependency_hash,
dependency_equal): New functions to maintain the dependency_equal): New functions to maintain the
......
1998-08-07 Miguel de Icaza <miguel@nuclecu.unam.mx> 1998-08-07 Miguel de Icaza <miguel@nuclecu.unam.mx>
* src/gnumeric-sheet.c: Renamed all references to (GnumericSheet
*) called "sheet" to "gsheet".
* src/sheet.h: Renamed parent_workbook in to be workbook.
* src/cell.h: Include a Sheet * in the cell. Make routines that
required a Sheet parameter only use the Cell parameter now.
* src/eval.c (add_tree_deps, cell_add_dependencies, * src/eval.c (add_tree_deps, cell_add_dependencies,
add_value_deps, dependency_hash_init, dependency_hash, add_value_deps, dependency_hash_init, dependency_hash,
dependency_equal): New functions to maintain the dependency_equal): New functions to maintain the
......
1998-08-07 Miguel de Icaza <miguel@nuclecu.unam.mx> 1998-08-07 Miguel de Icaza <miguel@nuclecu.unam.mx>
* src/gnumeric-sheet.c: Renamed all references to (GnumericSheet
*) called "sheet" to "gsheet".
* src/sheet.h: Renamed parent_workbook in to be workbook.
* src/cell.h: Include a Sheet * in the cell. Make routines that
required a Sheet parameter only use the Cell parameter now.
* src/eval.c (add_tree_deps, cell_add_dependencies, * src/eval.c (add_tree_deps, cell_add_dependencies,
add_value_deps, dependency_hash_init, dependency_hash, add_value_deps, dependency_hash_init, dependency_hash,
dependency_equal): New functions to maintain the dependency_equal): New functions to maintain the
......
...@@ -14,6 +14,7 @@ GNOME_XML_LIB = -lxml ...@@ -14,6 +14,7 @@ GNOME_XML_LIB = -lxml
GNUMERIC_BASE_SOURCES = \ GNUMERIC_BASE_SOURCES = \
cell.h \ cell.h \
cell.c \
eval.c \ eval.c \
expr.c \ expr.c \
expr.h \ expr.h \
......
#include <config.h> #include <config.h>
#include <gnome.h> #include <gnome.h>
#include <gnumeric.h> #include "gnumeric.h"
#include "eval.h"
void static void
sheet_cell_foreach_range (Sheet *sheet, cell_formula_link (Cell *cell)
int start_col, int start_row,
int end_col, int end_row,
CellCallback callback,
void *closure)
{ {
GList *col = sheet->cols_info; Sheet *sheet = cell->sheet;
for (; col; col = col->next){ sheet->formula_cell_list = g_list_prepend (sheet->formula_cell_list, cell);
ColRowInfo *ci = l->data; }
if (ci->pos < start_col) static void
continue; cell_formula_unlink (Cell *cell)
if (ci->pos > end_col) {
break; Sheet *sheet = cell->sheet;
sheet->formula_cell_list = g_list_remove (sheet->formula_cell_list, cell);
}
for (row = (GList *) col->data; row; row = row->data){ void
ColRowInfo *ri = l->data; cell_set_formula (Cell *cell, char *text)
{
char *error_msg = NULL;
if (ri->pos < start_row) g_return_if_fail (cell != NULL);
continue; g_return_if_fail (text != NULL);
cell->parsed_node = expr_parse_string (&text [1],
cell->col->pos,
cell->row->pos,
&error_msg);
if (cell->parsed_node == NULL){
if (cell->text)
string_unref_ptr (&cell->text);
cell->text = string_get (error_msg);
return;
}
if (ri->pos > end_row) cell_formula_link (cell);
break; cell_add_dependencies (cell);
cell_queue_recalc (cell);
}
v = (*callback)(sheet, (Cell *) ri->data); void
cell_set_text (Cell *cell, char *text)
{
GdkFont *font;
char *rendered_text;
GList *deps;
g_return_if_fail (cell != NULL);
g_return_if_fail (text != NULL);
/* If the callback wishes to stop, return */ /* The value entered */
if (v) if (cell->entered_text)
return; string_unref_ptr (&cell->entered_text);
} cell->entered_text = string_get (text);
if (cell->parsed_node){
cell_drop_dependencies (cell);
expr_tree_unref (cell->parsed_node);
} }
}
if (text [0] == '='){
cell_set_formula (cell, text);
} else {
Value *v = g_new (Value, 1);
int is_text = 0, is_float = 0;
char *p;
if (cell->text)
string_unref_ptr (&cell->text);
Cell * cell->text = string_get (text);
sheet_cell_new (Sheet *sheet, int col, int row)
{ for (p = text; *p && !is_text; p++){
Cell *cell = g_new0 (cell, 1); switch (*p){
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
break;
case 'E': case 'e': case '+': case ':': case '.':
is_float = 1;
break;
default:
is_text = 1;
}
}
if (is_text){
v->type = VALUE_STRING;
v->v.str = string_get (text);
} else {
if (is_float){
v->type = VALUE_FLOAT;
float_get_from_range (text, text+strlen(text),
&v->v.v_float);
} else {
v->type = VALUE_INTEGER;
int_get_from_range (text, text+strlen (text),
&v->v.v_int);
}
/* FIXME: */
/* In this case we need to format the text */
}
cell->value = v;
}
cell->col = sheet_col_find (sheet, col); /* Queue all of the dependencies for this cell */
cell->row = sheet_row_find (sheet, row); deps = cell_get_dependencies (cell->sheet,
cell->col->pos,
cell->row->pos);
if (deps)
cell_queue_recalc_list (deps);
cell->style = sheet_compute_style (sheet, col, row);
return cell; /* Finish setting the values for this cell */
} cell->flags = 0;
Cell * rendered_text = CELL_TEXT_GET (cell);
sheet_cell_new_with_text (Sheet *sheet, int col, int row, char *text)
{
Cell *cell;
GdkFont *font;
cell = sheet_cell_new (sheet, col, row, text);
cell->text = g_strdup (text);
font = cell->style->font->font; font = cell->style->font->font;
cell->width = gdk_text_width (font, text, strlen (text));
cell->height = font->ascent + font->descent;
return cell; cell->width = cell->col->margin_a + cell->col->margin_b +
gdk_text_width (font, rendered_text, strlen (rendered_text));
cell->height = font->ascent + font->descent;
} }
...@@ -29,6 +29,7 @@ typedef enum { ...@@ -29,6 +29,7 @@ typedef enum {
} CellFlags; } CellFlags;
typedef struct { typedef struct {
void *sheet;
ColRowInfo *col; ColRowInfo *col;
ColRowInfo *row; ColRowInfo *row;
...@@ -47,10 +48,14 @@ typedef struct { ...@@ -47,10 +48,14 @@ typedef struct {
int height; /* Height of text */ int height; /* Height of text */
int flags; int flags;
int iteration;
} Cell; } Cell;
#define CELL_TEXT_GET(cell) ((cell)->text ? cell->text->str : cell->entered_text->str) #define CELL_TEXT_GET(cell) ((cell)->text ? cell->text->str : cell->entered_text->str)
#define CELL_IS_FORMULA(cell) (cell->entered_text->str [0] == '=') #define CELL_IS_FORMULA(cell) (cell->entered_text->str [0] == '=')
#define MAX_ITERATIONS(cell) 1
void cell_set_text (Cell *cell, char *text);
void cell_set_formula (Cell *cell, char *text);
#endif /* GNUMERIC_CELL_H */ #endif /* GNUMERIC_CELL_H */
...@@ -13,22 +13,25 @@ ...@@ -13,22 +13,25 @@
#include "utils.h" #include "utils.h"
#include "eval.h" #include "eval.h"
static GList *dependency_hash; static GHashTable *dependency_hash;
void void
sheet_compute_cell (Sheet *sheet, Cell *cell) cell_eval (Cell *cell)
{ {
char *error_msg; char *error_msg;
Value *v; Value *v;
g_return_if_fail (sheet != NULL);
g_return_if_fail (IS_SHEET (sheet));
g_return_if_fail (cell != NULL); g_return_if_fail (cell != NULL);
if (cell->iteration == MAX_ITERATIONS(cell))
return;
cell->iteration++;
if (cell->text) if (cell->text)
string_unref_ptr (&cell->text); string_unref_ptr (&cell->text);
v = eval_expr (sheet, cell->parsed_node, v = eval_expr (cell->sheet, cell->parsed_node,
cell->col->pos, cell->col->pos,
cell->row->pos, cell->row->pos,
&error_msg); &error_msg);
...@@ -47,19 +50,11 @@ sheet_compute_cell (Sheet *sheet, Cell *cell) ...@@ -47,19 +50,11 @@ sheet_compute_cell (Sheet *sheet, Cell *cell)
cell->text = string_get (str); cell->text = string_get (str);
g_free (str); g_free (str);
} }
}
static void sheet_redraw_cell_region (cell->sheet,
sheet_recompute_one_cell (Sheet *sheet, int col, int row, Cell *cell)
{
if (cell->parsed_node == NULL)
return;
printf ("recomputing %d %d\n", col, row);
sheet_compute_cell (sheet, cell);
sheet_redraw_cell_region (sheet,
cell->col->pos, cell->row->pos, cell->col->pos, cell->row->pos,
cell->col->pos, cell->row->pos); cell->col->pos, cell->row->pos);
cell->iteration--;
} }
/* /*
...@@ -89,11 +84,11 @@ dependency_equal (gconstpointer v, gconstpointer v2) ...@@ -89,11 +84,11 @@ dependency_equal (gconstpointer v, gconstpointer v2)
* Hash function for DependencyRange structures * Hash function for DependencyRange structures
*/ */
static guint static guint
dependency_hash (gconstpointer v) dependency_hash_func (gconstpointer v)
{ {
DependencyRange *r = v; const DependencyRange *r = v;
return (((r->start_row << 8) + (r->end_row) << 8) + return ((((r->start_row << 8) + r->end_row) << 8) +
(r->start_col << 8) + (r->end_col)); (r->start_col << 8) + (r->end_col));
} }
...@@ -103,7 +98,7 @@ dependency_hash (gconstpointer v) ...@@ -103,7 +98,7 @@ dependency_hash (gconstpointer v)
static void static void
dependency_hash_init (void) dependency_hash_init (void)
{ {
dependency_hash = g_hash_table_new (dependency_hash, dependency_equal); dependency_hash = g_hash_table_new (dependency_hash_func, dependency_equal);
} }
/* /*
...@@ -113,7 +108,7 @@ dependency_hash_init (void) ...@@ -113,7 +108,7 @@ dependency_hash_init (void)
* We compute the location from cell->row->pos and cell->col->pos * We compute the location from cell->row->pos and cell->col->pos
*/ */
static void static void
add_cell_range_deps (Sheet *sheet, Cell *cell, CellRef *a, CellRef *b) add_cell_range_deps (Cell *cell, CellRef *a, CellRef *b)
{ {
DependencyRange range, *result; DependencyRange range, *result;
int col = cell->col->pos; int col = cell->col->pos;
...@@ -122,19 +117,20 @@ add_cell_range_deps (Sheet *sheet, Cell *cell, CellRef *a, CellRef *b) ...@@ -122,19 +117,20 @@ add_cell_range_deps (Sheet *sheet, Cell *cell, CellRef *a, CellRef *b)
/* Convert to absolute cordinates */ /* Convert to absolute cordinates */
cell_get_abs_col_row (a, col, row, &range.start_col, &range.start_row); cell_get_abs_col_row (a, col, row, &range.start_col, &range.start_row);
cell_get_abs_col_row (b, col, row, &range.end_col, &range.end_row); cell_get_abs_col_row (b, col, row, &range.end_col, &range.end_row);
range.sheet = sheet; range.ref_count = 0;
range.sheet = cell->sheet;
/* Look it up */ /* Look it up */
result = g_hash_table_lookup (dependency_hash, &range); result = g_hash_table_lookup (dependency_hash, &range);
if (result){ if (result){
Cell *cell; GList *cl;
result->ref_count++; result->ref_count++;
/* Is the cell already listed? */ /* Is the cell already listed? */
cell = g_list_find (result->cell_list, cell); cl = g_list_find (result->cell_list, cell);
if (cell) if (cl)
return; return;
/* It was not: add it */ /* It was not: add it */
...@@ -145,8 +141,8 @@ add_cell_range_deps (Sheet *sheet, Cell *cell, CellRef *a, CellRef *b) ...@@ -145,8 +141,8 @@ add_cell_range_deps (Sheet *sheet, Cell *cell, CellRef *a, CellRef *b)
/* Create a new DependencyRange structure */ /* Create a new DependencyRange structure */
result = g_new (DependencyRange, 1); result = g_new (DependencyRange, 1);
*result = range; *result = range;
range.ref_count = 1; result->ref_count = 1;
range.cell_list = g_list_insert (NULL, cell); result->cell_list = g_list_prepend (NULL, cell);
g_hash_table_insert (dependency_hash, result, result); g_hash_table_insert (dependency_hash, result, result);
} }
...@@ -155,11 +151,11 @@ add_cell_range_deps (Sheet *sheet, Cell *cell, CellRef *a, CellRef *b) ...@@ -155,11 +151,11 @@ add_cell_range_deps (Sheet *sheet, Cell *cell, CellRef *a, CellRef *b)
* Adds the dependencies for a Value * Adds the dependencies for a Value
*/ */
static void static void
add_value_deps (Sheet *sheet, Cell *cell, Value *value) add_value_deps (Cell *cell, Value *value)
{ {
GList *l; GList *l;
switch (v->type){ switch (value->type){
case VALUE_STRING: case VALUE_STRING:
case VALUE_INTEGER: case VALUE_INTEGER:
case VALUE_FLOAT: case VALUE_FLOAT:
...@@ -169,14 +165,14 @@ add_value_deps (Sheet *sheet, Cell *cell, Value *value) ...@@ -169,14 +165,14 @@ add_value_deps (Sheet *sheet, Cell *cell, Value *value)
/* Check every element of the array */ /* Check every element of the array */
case VALUE_ARRAY: case VALUE_ARRAY:
for (l = value->v.array; l; l = l->next) for (l = value->v.array; l; l = l->next)
add_value_deps (sheet, cell, l->data); add_value_deps (cell, l->data);
break; break;
case VALUE_CELLRANGE: case VALUE_CELLRANGE:
add_cell_range_deps ( add_cell_range_deps (
sheet, cell, cell,
v->cell_range.cell_a, &value->v.cell_range.cell_a,
v->cell_range.cell_b); &value->v.cell_range.cell_b);
break; break;
} }
} }
...@@ -186,8 +182,10 @@ add_value_deps (Sheet *sheet, Cell *cell, Value *value) ...@@ -186,8 +182,10 @@ add_value_deps (Sheet *sheet, Cell *cell, Value *value)
* and cell range references. * and cell range references.
*/ */
static void static void
add_tree_deps (Sheet *sheet, Cell *cell, ExprTree *tree) add_tree_deps (Cell *cell, ExprTree *tree)
{ {
GList *l;
switch (tree->oper){ switch (tree->oper){
case OP_EQUAL: case OP_EQUAL:
case OP_NOT_EQUAL: case OP_NOT_EQUAL:
...@@ -201,28 +199,28 @@ add_tree_deps (Sheet *sheet, Cell *cell, ExprTree *tree) ...@@ -201,28 +199,28 @@ add_tree_deps (Sheet *sheet, Cell *cell, ExprTree *tree)
case OP_DIV: case OP_DIV:
case OP_EXP: case OP_EXP:
case OP_CONCAT: case OP_CONCAT:
add_tree_deps (sheet, cell, tree->u.binary.value_a); add_tree_deps (cell, tree->u.binary.value_a);
add_tree_deps (sheet, cell, tree->u.binary.value_b); add_tree_deps (cell, tree->u.binary.value_b);
return; return;
case OP_CONSTANT: case OP_CONSTANT:
add_value_deps (sheet, cell, tree->u.constant); add_value_deps (cell, tree->u.constant);
return; return;
case OP_FUNCALL: case OP_FUNCALL:
for (l = tree->u.function.arg_list; l; l = l->next) for (l = tree->u.function.arg_list; l; l = l->next)
add_tree_deps (sheet, cell, l->data); add_tree_deps (cell, l->data);
return; return;
case OP_VAR: case OP_VAR:
add_cell_range_deps ( add_cell_range_deps (
sheet, cell, cell,
cell->u.constant->v.cell, &tree->u.constant->v.cell,
cell->u.constant->v.cell); &tree->u.constant->v.cell);
return; return;
case OP_NEG: case OP_NEG:
add_value_deps (sheet, cell, tree->u.value); add_tree_deps (cell, tree->u.value);
return; return;
} /* switch */ } /* switch */
...@@ -234,17 +232,15 @@ add_tree_deps (Sheet *sheet, Cell *cell, ExprTree *tree) ...@@ -234,17 +232,15 @@ add_tree_deps (Sheet *sheet, Cell *cell, ExprTree *tree)
* parsed expression. * parsed expression.
*/ */
void void
cell_add_dependencies (Cell *cell, Sheet *sheet) cell_add_dependencies (Cell *cell)
{ {
g_return_if_fail (cell != NULL); g_return_if_fail (cell != NULL);
g_return_if_fail (sheet != NULL);
g_return_if_fail (cell->parsed_node != NULL); g_return_if_fail (cell->parsed_node != NULL);
g_return_if_fail (IS_SHEET (sheet));
if (!dependency_hash) if (!dependency_hash)
dependency_hash_init (); dependency_hash_init ();
add_tree_deps (sheet, cell, cell->parsed_node); add_tree_deps (cell, cell->parsed_node);
} }
/* /*
...@@ -264,8 +260,9 @@ dependency_remove_cell (gpointer key, gpointer value, gpointer the_cell) ...@@ -264,8 +260,9 @@ dependency_remove_cell (gpointer key, gpointer value, gpointer the_cell)
list = g_list_find (range->cell_list, the_cell); list = g_list_find (range->cell_list, the_cell);
if (!list) if (!list)
return; return;
range->cell_list = g_list_remove_link (range->cell_list, list); range->cell_list = g_list_remove_link (range->cell_list, list);
range->ref_count--; range->ref_count--;
if (range->ref_count == 0) if (range->ref_count == 0)
...@@ -285,12 +282,14 @@ cell_drop_dependencies (Cell *cell) ...@@ -285,12 +282,14 @@ cell_drop_dependencies (Cell *cell)
g_hash_table_foreach (dependency_hash, dependency_remove_cell, cell); g_hash_table_foreach (dependency_hash, dependency_remove_cell, cell);
/* Drop any unused DependencyRanges (because their ref_count reached zero */ /* Drop any unused DependencyRanges (because their ref_count reached zero) */
if (remove_list){ if (remove_list){
GList *l = remove_list; GList *l = remove_list;
for (; l ; l = l->next) for (; l ; l = l->next){
g_hash_table_remove (dependency_hash, l->data);
g_free (l->data); g_free (l->data);
}
g_list_free (remove_list);