#include #include #include #include #include #include #include #include "ss.h" #include "parse.h" int debug = 0, headers = 1, dir = BYROWS, depend = 1, evaluated = EVALUATED; FILE *out; /*** spreadsheet 2D array ***/ SS **ss; int SS_MAXROWS = 1000, SS_MAXCOLS = 702; /* default values */ // since the spreadsheet size can be specified at run-time, // used.start.{row,col} are initialized below in init_ss() // // Range used = { { 0, SS_MAXROWS, 0, SS_MAXCOLS }, { 0, -1, 0, -1 } }; // Range used = { { 0, 0, 0, 0 }, { 0, -1, 0, -1 } }; const char *global_format = "%.2f"; const char **row_format; const char **col_format; /* A0 reference cell constant */ Cell cell_00 = { 0, 0, 0, 0}; /* print range and format */ Range print_Range; int print_Format = A0; /* dynamically allocate space for the spreadsheet and format arrays */ void init_ss( void) { ss = malloc( SS_MAXROWS * sizeof(SS*)); if( ss) ss[0] = calloc( SS_MAXROWS * SS_MAXCOLS, sizeof(SS)); row_format = calloc( SS_MAXROWS, sizeof(char *)); col_format = calloc( SS_MAXCOLS, sizeof(char *)); if( ss == NULL || ss[0] == NULL || row_format == NULL || col_format == NULL) { yyerror( "init_ss: malloc failed"); exit(1); } for( int i = 1; i < SS_MAXROWS; ++i) ss[i] = ss[i-1] + SS_MAXCOLS; used.start.row = SS_MAXROWS; used.start.col = SS_MAXCOLS; } /* dynamically copy string or exit if malloc fails */ char *estrdup( const char *str) { char *s = malloc( strlen(str) + 1); if( s == NULL) { yyerror( "estrdup: malloc failed"); exit(1); } strcpy( s, str); return s; } /* dynamically concatenate strings or exit if realloc fails */ char *estrcat( char *str1, const char *str2) { char *s = realloc( str1, strlen(str1) + strlen(str2) + 1); if( s == NULL) { yyerror( "estrcat: realloc failed"); exit(1); } strcat( s, str2); return s; } /* convert Cell to Range */ Node *rangeify( Node *n) { Range r; r.start = r.end = n->u.c; n->u.r = r; if( n->type == CELL) n->type = RANGE; else /* n->type == RCCELL */ n->type = RCRANGE; return n; } void debug_cell( const Cell *c, const Cell *ref) { fprintf( out, "col = %d%s, row = %d%s, as string: ", c->col, c->col_fixed ? " (fixed)" : "", c->row, c->row_fixed ? " (fixed)" : ""); print_cell( c, ref); } /* check cell bounds and min/max */ int check_cell( const Cell *c) { if( c->row < 0 || c->row >= SS_MAXROWS || c->col < 0 || c->col >= SS_MAXCOLS) { fprintf( out, "error: check_cell: cell out of bounds: "); debug_cell( c, &cell_00); putc( '\n', out); return 1; } if( c->row < used.start.row) used.start.row = c->row; if( c->row > used.end.row) used.end.row = c->row; if( c->col < used.start.col) used.start.col = c->col; if( c->col > used.end.col) used.end.col = c->col; return 0; } /* print column using letters */ void print_col( int col) { /* adapted from gnumeric-1.10.15/src/parse-util.c col_name_internal() The column names are not a direct base-26 encoding since 'A' does not represent 0 except in the least-significant position. */ static int const steps[] = { 26, 26 * 26, 26 * 26 * 26, 26 * 26 * 26 * 26, 26 * 26 * 26 * 26 * 26, 26 * 26 * 26 * 26 * 26 * 26, INT_MAX }; int i; char *d, a[10]; for( i = 0; col >= steps[i]; ++i) col -= steps[i]; d = a + i + 1; *d = '\0'; while( i-- >= 0) { *--d = 'A' + col % 26; col /= 26; } fputs( a, out); } /* print cell, offset from reference cell */ void print_cell( const Cell *c, const Cell *ref) { if( c->col_fixed) { putc( '$', out); print_col( c->col); } else { print_col( c->col + ref->col); } if( c->row_fixed) { putc( '$', out); fprintf( out, "%d", c->row); } else { fprintf( out, "%d", c->row + ref->row); } } static void print_CR( int cr, int fixed, int val) { if( fixed) fprintf( out, "%c%d", cr, val); else if( val == 0) fprintf( out, "%c[]", cr); else fprintf( out, "%c[%+d]", cr, val); } /* print cell in RC format */ void print_cell_RC( const Cell *c) { print_CR( 'R', c->row_fixed, c->row); print_CR( 'C', c->col_fixed, c->col); } /* print cell in CR format */ void print_cell_CR( const Cell *c) { print_CR( 'C', c->col_fixed, c->col); print_CR( 'R', c->row_fixed, c->row); } /* print range, offset from reference cell */ void print_range( const Range *r, const Cell *ref) { print_cell( &r->start, ref); putc( ':', out); print_cell( &r->end, ref); } /* print range in RC format */ void print_range_RC( const Range *r) { print_cell_RC( &r->start); putc( ':', out); print_cell_RC( &r->end); } /* print range in CR format */ void print_range_CR( const Range *r) { print_cell_CR( &r->start); putc( ':', out); print_cell_CR( &r->end); } /* set actual cell location */ void adjust_cell( Cell *c, const Cell *ref_cell) { if( !c->row_fixed) c->row += ref_cell->row; if( !c->col_fixed) c->col += ref_cell->col; } /* set actual cell locations in a range */ void adjust_range( Range *r, const Cell *ref_cell) { adjust_cell( &r->start, ref_cell); adjust_cell( &r->end, ref_cell); } /*** install a cell ***/ static void install_cell_helper( const Cell *c, Node *n, int also_install_tree, int install_list) { if( check_cell( c)) return; if( debug) { fprintf( out, "install_cell: "); print_cell( c, &cell_00); putc( '\n', out); } SS *cp = &ss[c->row][c->col]; if( cp->used) { fprintf( out, "warning: overwriting cell "); print_cell( c, &cell_00); putc( '\n', out); } cp->used = 1; if( n->type == NUMBER) { cp->n = NULL; cp->val = n->u.val; cp->state = EVALUATED; /* free( n); can't free, n could be on an expr_list which wraps around and is traversed multiple times, i.e. see install_range() */ } else { cp->n = n; cp->val = 0; if( n->type == STRING) cp->state = EVALUATED; else cp->state = UNEVALUATED; if( also_install_tree) install_tree( n, c, install_list); } } void install_cell( const Node *cn, Node *n, int also_install_tree, int install_list) { install_cell_helper( &cn->u.c, n, also_install_tree, install_list); } /*** traversing a range ***/ typedef struct data { int first_col, last_col; SS *cp, *cp_from, *cp_to; SS **sspp; Cell c, from, to; Node *n1, *n2; Symbol *s; const char *str; struct tm *tm; int i[6]; double d[4]; double *dp, *dp2; double list_val; void (*tc)(struct data *); // used by ss_traverse_list() } Data; typedef void (*Traverse_Callback)(Data *); static void ss_traverse_range( const Cell *start, const Cell *end, Traverse_Callback tc, Data *d) { int row_inc, col_inc; if( debug) { fprintf( out, "traverse_range: %s", dir == BYROWS ? "byrows" : "bycols"); fprintf( out, " from "); print_cell( start, &cell_00); fprintf( out, " to "); print_cell( end, &cell_00); putc( '\n', out); } if( check_cell( start) || check_cell( end) ) return; d->c = *start; /* row/col increments could be +1 or -1 */ row_inc = (start->row <= end->row) ? 1 : -1; col_inc = (start->col <= end->col) ? 1 : -1; if( dir == BYROWS) { while(1) { while(1) { d->first_col = (d->c.col == start->col); d->last_col = (d->c.col == end->col); d->cp = &ss[d->c.row][d->c.col]; if( debug > 1) { fprintf( out, "traverse_range: cell "); print_cell( &d->c, &cell_00); putc( '\n', out); } tc( d); if( d->last_col) break; d->c.col += col_inc; } if( d->c.row == end->row) break; d->c.row += row_inc; d->c.col = start->col; } } else /* BYCOLS */ { while(1) { while(1) { d->first_col = (d->c.row == start->row); d->last_col = (d->c.row == end->row); d->cp = &ss[d->c.row][d->c.col]; if( debug > 1) { fprintf( out, "traverse_range: cell "); print_cell( &d->c, &cell_00); putc( '\n', out); } tc( d); if( d->last_col) break; d->c.row += row_inc; } if( d->c.col == end->col) break; d->c.col += col_inc; d->c.row = start->row; } } } /*** traversing a range with cell adjustment ***/ static void ss_traverse_range_adjust( const Node *n, const Cell *c, Traverse_Callback tc, Data *d, int direction) { Cell start, end; start = n->u.r.start; adjust_cell( &start, c); end = n->u.r.end; adjust_cell( &end, c); if( direction < 0) // traverse in reverse direction ss_traverse_range( &end, &start, tc, d); else ss_traverse_range( &start, &end, tc, d); } /*** traversing two ranges: destination (to) and source (from), as used by ss_copy ***/ static void ss_traverse_range2( const Range *rd, const Range *rs, Traverse_Callback tc, Data *d) { int from_row_inc, from_col_inc; int to_row_inc, to_col_inc; if( check_cell( &rd->start) || check_cell( &rd->end) || check_cell( &rs->start) || check_cell( &rs->end) ) return; d->from = rs->start; d->to = rd->start; d->cp_from = &ss[d->from.row][d->from.col]; d->cp_to = &ss[d->to.row][d->to.col]; d->from.row_fixed = d->from.col_fixed = 0; d->to.row_fixed = d->to.col_fixed = 0; /* row/col increments could be +1 or -1 */ from_row_inc = (d->from.row <= rs->end.row) ? 1 : -1; from_col_inc = (d->from.col <= rs->end.col) ? 1 : -1; to_row_inc = (d->to.row <= rd->end.row) ? 1 : -1; to_col_inc = (d->to.col <= rd->end.col) ? 1 : -1; if( debug) fprintf( out, "traverse_range2: %s\n", dir == BYROWS ? "byrows" : "bycols"); if( dir == BYROWS) { while(1) /* for each destination row */ { while(1) /* go across the destination columns */ { tc( d); if( d->from.col == rs->end.col) { d->from.col = rs->start.col; if( d->from.row == rs->end.row) d->from.row = rs->start.row; else d->from.row += from_row_inc; } else d->from.col += from_col_inc; d->cp_from = &ss[d->from.row][d->from.col]; if( d->to.col == rd->end.col) break; d->to.col += to_col_inc; d->cp_to = &ss[d->to.row][d->to.col]; } if( d->to.row == rd->end.row) break; d->to.row += to_row_inc; d->to.col = rd->start.col; d->cp_to = &ss[d->to.row][d->to.col]; } } else /* BYCOLS */ { while(1) /* for each destination column */ { while(1) /* go down (or up) the destination rows */ { tc( d); if( d->from.row == rs->end.row) { d->from.row = rs->start.row; if( d->from.col == rs->end.col) d->from.col = rs->start.col; else d->from.col += from_col_inc; } else d->from.row += from_row_inc; d->cp_from = &ss[d->from.row][d->from.col]; if( d->to.row == rd->end.row) break; d->to.row += to_row_inc; d->cp_to = &ss[d->to.row][d->to.col]; } if( d->to.col == rd->end.col) break; d->to.col += to_col_inc; d->to.row = rd->start.row; d->cp_to = &ss[d->to.row][d->to.col]; } } } /*** traversing two ranges with cell adjustment ***/ static void ss_traverse_range2_adjust( const Range *rd, const Range *rs, const Cell *c, Traverse_Callback tc, Data *d) { Range to = *rd, from = *rs; adjust_range( &to, c); adjust_range( &from, c); ss_traverse_range2( &to, &from, tc, d); } /*** traversing a list (to use the values) ***/ // // Symbols, expressions, and used cells in the list are evaluated // and the value is supplied to the callback via d->list_val // static void ss_traverse_list_callback( Data *d) { if( d->cp->used) { d->list_val = eval_cell( d->cp, &d->c); d->tc( d); } } static void ss_traverse_list( const Node *n, const Cell *c, Traverse_Callback tc, Data *d) { d->tc = tc; while( n) { switch( n->type) { // // ZZ - should only need case RANGE here? // case RANGE: case RCRANGE: case A0RCRANGE: case RCA0RANGE: if( debug) fprintf( out, "traverse_list: got range type %d\n", n->type); ss_traverse_range_adjust( n, c, ss_traverse_list_callback, d, 0); break; case ID: if( debug) fprintf( out, "traverse_list: got symbol %s\n", n->u.s->name); /* fall through */ default: d->list_val = eval_tree( n, c); tc( d); break; } n = n->next; } } /*** helper for lval_list assignment ***/ static void lval_helper( const char *name, const Node *r, const Cell *cp, double val) { Cell c; Symbol *s; switch( r->type) { case CELL: c = r->u.c; adjust_cell( &c, cp); if( !check_cell( &c) ) { if( ss[c.row][c.col].n) { fprintf( out, "%s: can not assign cell ", name); print_cell( &c, &cell_00); putc( '\n', out); } else { ss[c.row][c.col].used = 1; ss[c.row][c.col].val = val; ss[c.row][c.col].state = EVALUATED; } } break; case ID: s = r->u.s; if( s->n || s->constant) { fprintf( out, "%s: can not assign symbol %s\n", name, s->name); } else { s->val = val; s->state = EVALUATED; } break; default: /* shouldn't happen, checked at parse/compile time */ fprintf( out, "%s: lval must be cell or symbol\n", name); break; } } /* COPY command */ static void ss_copy_callback( Data *d) { if( debug) { fprintf( out, " to "); print_cell( &d->to, &cell_00); fprintf( out, " from "); print_cell( &d->from, &cell_00); putc('\n', out); } if( d->cp_to->used) { fprintf( out, "warning: overwriting cell "); print_cell( &d->to, &cell_00); putc( '\n', out); } *d->cp_to = *d->cp_from; d->cp_to->used = 1; } void ss_copy( const Node *rd, const Node *rs) { Data d; if( Rsize(rs) > Rsize(rd)) { yyerror( "copy: will not use all of the source range"); } ss_traverse_range2( &rd->u.r, &rs->u.r, ss_copy_callback, &d); } /* SORT command */ #define SORT_GET 0 #define SORT_PUT 1 #define SORT_X dp #define SORT_N i[0] #define SORT_MODE i[1] #define SORT_UNIQ i[2] #define SORT_UNIQ_N i[3] #define SORT_UNIQ_FIRST i[4] #define SORT_UNIQ_PREV d[0] static int ss_sort_compare( const void *v1, const void *v2) // for qsort() { const double *d1 = v1; const double *d2 = v2; if( *d1 < *d2) return -1; else if( *d2 < *d1) return +1; else return 0; } static void ss_sort_callback( Data *d) { // skip unused cells and strings // if( !d->cp->used || (d->cp->n && d->cp->n->type == STRING) ) return; // getting the original values // if( d->SORT_MODE == SORT_GET) { *d->SORT_X++ = d->cp->val; ++d->SORT_N; } else { // putting back the sorted values // if( d->SORT_UNIQ) // uniq option, no duplicates { while( !d->SORT_UNIQ_FIRST && d->SORT_N && d->SORT_UNIQ_PREV == *d->SORT_X) { --d->SORT_N; --d->SORT_UNIQ_N; ++d->SORT_X; // skip duplicates } d->SORT_UNIQ_FIRST = 0; if( d->SORT_N) // still have some values left { --d->SORT_N; d->SORT_UNIQ_PREV = d->cp->val = *d->SORT_X++; } else { d->cp->used = 0; // set the cell as unused } } else // no uniq option { d->cp->val = *d->SORT_X++; } } } void ss_sort( const Node *rn, int uniq, Node *c) { Data d; int n = Rsize(rn); if( n < 2) { if( c) lval_helper( "sort", c, &cell_00, n); return; } double *x = malloc( n*sizeof(double)); if( x == NULL) { yyerror( "ss_sort: malloc failed"); exit(1); } d.SORT_N = 0; d.SORT_X = x; d.SORT_MODE = SORT_GET; // get the values to be sorted // ss_traverse_range( &rn->u.r.start, &rn->u.r.end, ss_sort_callback, &d); if( debug) fprintf( out, "ss_sort: N = %d\n", d.SORT_N); if( d.SORT_N < 2) { if( c) lval_helper( "sort", c, &cell_00, d.SORT_N); free(x); return; } // sort the values // qsort( x, d.SORT_N, sizeof(double), ss_sort_compare); d.SORT_X = x; d.SORT_MODE = SORT_PUT; d.SORT_UNIQ = uniq; d.SORT_UNIQ_N = d.SORT_N; d.SORT_UNIQ_FIRST = 1; // put back the sorted values // ss_traverse_range( &rn->u.r.start, &rn->u.r.end, ss_sort_callback, &d); free(x); if( debug && uniq) fprintf( out, "ss_sort: SORT_UNIQ_N = %d\n", d.SORT_UNIQ_N); // set uniq count // if( c) lval_helper( "sort", c, &cell_00, d.SORT_UNIQ_N); } /*** install a range ***/ #define EXPR_LIST_START n1 #define EXPR_LIST_P n2 #define EXPR_LIST_TREE i[0] #define EXPR_LIST_RESTART i[1] static void install_range_callback( Data *d) { if( d->EXPR_LIST_P == NULL) { if( d->EXPR_LIST_RESTART) // reached end of list, restart { d->EXPR_LIST_P = d->EXPR_LIST_START; d->EXPR_LIST_TREE = 0; } else return; // when used by ss_fill_expr_list() } if( d->EXPR_LIST_P->type != EMPTY) install_cell_helper( &d->c, d->EXPR_LIST_P, d->EXPR_LIST_TREE, 0); d->EXPR_LIST_P = d->EXPR_LIST_P->next; } void install_range( const Node *rn, Expr_list *e, int restart) { Node *n, *t; Data d; if( debug) { fprintf( out, "install_range "); print_range( &rn->u.r, &cell_00); fprintf( out, "\n"); } n = e->head; d.EXPR_LIST_START = n; d.EXPR_LIST_P = n; d.EXPR_LIST_TREE = 1; d.EXPR_LIST_RESTART = restart; ss_traverse_range( &rn->u.r.start, &rn->u.r.end, install_range_callback, &d); if( d.EXPR_LIST_TREE == 1 && d.EXPR_LIST_P != NULL // ignore unused empty last element && (d.EXPR_LIST_P->type != EMPTY || d.EXPR_LIST_P->next != NULL) ) yyerror( "install_range: did not use all of expression list"); /* unlistify the list, otherwise the nodes will be printed as a list */ while( n) { t = n->next; n->next = NULL; n = t; } } /*** convert any ranges into lists ***/ #define RL_HEAD n1 #define RL_TAIL n2 #define RL_COUNT i[0] static void listify_ranges_callback( Data *d) { Node *n = add_cell( &d->c, CELL); ++d->RL_COUNT; if( d->RL_HEAD == NULL) { d->RL_HEAD = d->RL_TAIL = n; } else { d->RL_TAIL->next = n; d->RL_TAIL = n; } } Expr_list *listify_ranges( Expr_list *n) { Data d; Node *s, *t; d.RL_COUNT = 0; s = NULL; t = n->head; while( t) { switch( t->type) { case RANGE: case RCRANGE: case A0RCRANGE: case RCA0RANGE: d.RL_HEAD = d.RL_TAIL = NULL; ss_traverse_range( &t->u.r.start, &t->u.r.end, listify_ranges_callback, &d); if( s == NULL) n->head = d.RL_HEAD; else s->next = d.RL_HEAD; s = d.RL_TAIL; t = t->next; s->next = t; break; default: ++d.RL_COUNT; s = t; t = t->next; break; } } n->count = d.RL_COUNT; return n; } /*** installing formats ***/ void install_format( const char *fmt) { global_format = fmt; } void install_col_format( int col, const char *fmt) { if( col < 0 || col >= SS_MAXCOLS) { char buf[100]; sprintf( buf, "install_col_format: column %d out of range 0..%d", col, SS_MAXCOLS-1); yyerror( buf); } else { col_format[col] = fmt; } } void install_row_format( int row, const char *fmt) { if( row < 0 || row >= SS_MAXROWS) { char buf[100]; sprintf( buf, "install_row_format: row %d out of range 0..%d", row, SS_MAXROWS-1); yyerror( buf); } else { row_format[row] = fmt; } } void install_cell_format( const Node *cn, const char *fmt) { if( check_cell( &cn->u.c)) return; ss[cn->u.c.row][cn->u.c.col].format = fmt; } #define RANGE_FORMAT str static void install_range_format_callback( Data *d) { d->cp->format = d->RANGE_FORMAT; } void install_range_format( const Node *rn, const char *fmt) { Data d; d.RANGE_FORMAT = fmt; ss_traverse_range( &rn->u.r.start, &rn->u.r.end, install_range_format_callback, &d); } /* find the format for a cell */ static const char *get_format( const char *cell_format, int row, int col) { const char *format = global_format; if( cell_format) { format = cell_format; } else if( dir == BYROWS) { if( row_format[row]) format = row_format[row]; else if( col_format[col]) format = col_format[col]; } else /* dir == BYCOLS */ { if( col_format[col]) format = col_format[col]; else if( row_format[row]) format = row_format[row]; } return format; } /* FILL expr_list command */ void ss_fill_expr_list( Node *rn, Expr_list *e) { if( debug) { fprintf( out, "fill_expr_list: %d %d %d %d %d\n", rn->u.r.start.row, rn->u.r.start.col, rn->u.r.end.row, rn->u.r.end.col, e->count); } if( rn->u.r.start.row == rn->u.r.end.row) // filling a row { if( rn->u.r.start.col <= rn->u.r.end.col) rn->u.r.end.col = rn->u.r.start.col + e->count - 1; else rn->u.r.end.col = rn->u.r.start.col + 1 - e->count; } else if( rn->u.r.start.col == rn->u.r.end.col) // filling a column { if( rn->u.r.start.row <= rn->u.r.end.row) rn->u.r.end.row = rn->u.r.start.row + e->count - 1; else rn->u.r.end.row = rn->u.r.start.row + 1 - e->count; } else // filling a range { int rows, cols; if( dir == BYROWS) { cols = abs( rn->u.r.start.col - rn->u.r.end.col) + 1; rows = e->count/cols; if( rows*cols < e->count) ++rows; if( rn->u.r.start.row <= rn->u.r.end.row) rn->u.r.end.row = rn->u.r.start.row + rows - 1; else rn->u.r.end.row = rn->u.r.start.row + 1 - rows; } else // BYCOLS { rows = abs( rn->u.r.start.row - rn->u.r.end.row) + 1; cols = e->count/rows; if( rows*cols < e->count) ++cols; if( rn->u.r.start.col <= rn->u.r.end.col) rn->u.r.end.col = rn->u.r.start.col + cols - 1; else rn->u.r.end.col = rn->u.r.start.col + 1 - cols; } } if( debug) { fprintf( out, "fill_expr_list: %d %d %d %d %d\n", rn->u.r.start.row, rn->u.r.start.col, rn->u.r.end.row, rn->u.r.end.col, e->count); } install_range( rn, e, 0); } /* FILL date command */ #define DFILL_FMT str #define DFILL_YEAR_INC i[0] #define DFILL_MONTH_INC i[1] #define DFILL_DAY_INC i[2] #define DFILL_HOUR_INC i[3] #define DFILL_MIN_INC i[4] #define DFILL_SEC_INC i[5] #define DFILL_TM tm static void ss_dfill_callback( Data *d) { char s[100]; if( d->cp->used) { fprintf( out, "warning: overwriting cell "); print_cell( &d->c, &cell_00); putc( '\n', out); } if( mktime( d->DFILL_TM) == (time_t) -1) { yyerror( "fill: mktime() failed"); return; } if( strftime( s, 100, d->DFILL_FMT, d->DFILL_TM) == 0) yyerror( "fill: strftime() returned 0"); d->cp->used = 1; d->cp->val = 0; d->cp->state = EVALUATED; d->cp->n = add_string( estrdup( s)); d->DFILL_TM->tm_year += d->DFILL_YEAR_INC; d->DFILL_TM->tm_mon += d->DFILL_MONTH_INC; d->DFILL_TM->tm_mday += d->DFILL_DAY_INC; d->DFILL_TM->tm_hour += d->DFILL_HOUR_INC; d->DFILL_TM->tm_min += d->DFILL_MIN_INC; d->DFILL_TM->tm_sec += d->DFILL_SEC_INC; d->DFILL_TM->tm_isdst = -1; } void ss_dfill( const Node *rn, Expr_list *e) { Data d; struct tm tm; Node *fmt, *start, *year_inc, *month_inc; Node *day_inc = NULL, *hour_inc = NULL, *min_inc = NULL, *sec_inc = NULL; int year, month, day, hour, min, sec; // we know that e->count > 2 here // fmt = e->head; start = fmt->next; year_inc = start->next; month_inc = year_inc->next; if( month_inc) day_inc = month_inc->next; if( day_inc) hour_inc = day_inc->next; if( hour_inc) min_inc = hour_inc->next; if( min_inc) sec_inc = min_inc->next; if( fmt->type != STRING) { yyerror( "fill: format must be a string"); return; } if( start->type != STRING) { yyerror( "fill: start value must be a string"); return; } /* tm_year The number of years since 1900. tm_mon The number of months since January, in the range 0 to 11. tm_mday The day of the month, in the range 1 to 31. */ tm.tm_sec = tm.tm_min = 0; tm.tm_hour = 12; tm.tm_yday = tm.tm_wday = 0; tm.tm_isdst = -1; tm.tm_year = 100; tm.tm_mon = 0; tm.tm_mday = 1; if( sscanf( start->u.str, "%d-%d-%d:%d:%d:%d", &year, &month, &day, &hour, &min, &sec) == 6) { tm.tm_year = year - 1900; tm.tm_mon = month - 1; tm.tm_mday = day; tm.tm_hour = hour; tm.tm_min = min; tm.tm_sec = sec; } else if( sscanf( start->u.str, "%d-%d-%d", &year, &month, &day) == 3) { tm.tm_year = year - 1900; tm.tm_mon = month - 1; tm.tm_mday = day; } else if( sscanf( start->u.str, "%d:%d:%d", &hour, &min, &sec) == 3) { tm.tm_hour = hour; tm.tm_min = min; tm.tm_sec = sec; if( hour_inc != NULL) { yyerror( "fill: too many increment arguments"); return; } hour_inc = year_inc; // fix increment arguments min_inc = month_inc; sec_inc = day_inc; year_inc = month_inc = day_inc = NULL; } else { yyerror( "fill: bad start date/time"); return; } install_tree( year_inc, &cell_00, 1); d.tm = &tm; d.DFILL_FMT = fmt->u.str; d.DFILL_YEAR_INC = eval_tree( year_inc, &cell_00); d.DFILL_MONTH_INC = eval_tree( month_inc, &cell_00); d.DFILL_DAY_INC = eval_tree( day_inc, &cell_00); d.DFILL_HOUR_INC = eval_tree( hour_inc, &cell_00); d.DFILL_MIN_INC = eval_tree( min_inc, &cell_00); d.DFILL_SEC_INC = eval_tree( sec_inc, &cell_00); ss_traverse_range( &rn->u.r.start, &rn->u.r.end, ss_dfill_callback, &d); } /* parse string column, used by ss_Copy(), nf/cell(), and tree.c:add_cellref() * * if the column reference is invalid, returns -1 * if the column reference is a string, returns 1 * otherwise returns 0 */ int cell_parse_col( Node *r, Cell *x, const Cell *c) { SS *cp; Symbol *s; const char *col = NULL; switch( r->type) { case STRING: col = r->u.str; break; case CELL: *x = r->u.c; adjust_cell( x, c); if( check_cell( x)) return -1; cp = &ss[x->row][x->col]; if( cp->n && cp->n->type == STRING) col = cp->n->u.str; break; case ID: s = r->u.s; if( s->n && s->n->type == STRING) col = s->n->u.str; break; } if( !col) return 0; // note that parse_cell() from scan.so does not detect syntax errors // parse_cell( col, x); if( debug) { fprintf( out, "cell: parse_cell(\"%s\") = ", col); print_cell( x, &cell_00); putc( '\n', out); } return 1; } /* Copy...Cell() (originally fill...cell() command) */ // COPY_COL_VAL >= 0 means we have a constant column reference, // from cell() string, so callback does not evaluate the column formula // #define COPY_COL_VAL i[0] // formulas for the column and row // #define COPY_COL n1 #define COPY_ROW n2 static void ss_Copy_callback( Data *d) { Cell x; x.col_fixed = x.row_fixed = 0; x.col = d->COPY_COL_VAL; if( x.col < 0) x.col = eval_tree( d->COPY_COL, &d->c); x.row = eval_tree( d->COPY_ROW, &d->c); if( check_cell( &x)) return; SS *cp = &ss[x.row][x.col]; if( d->cp->used) { fprintf( out, "warning: overwriting cell "); print_cell( &d->c, &cell_00); putc( '\n', out); } d->cp->used = 1; d->cp->n = cp->n; if( cp->n == NULL) { d->cp->val = cp->val; d->cp->state = EVALUATED; } else if( cp->n->type == STRING) { d->cp->val = 0; d->cp->state = EVALUATED; } else { d->cp->val = 0; d->cp->state = UNEVALUATED; } } void ss_Copy( const Node *rn, Expr_list *e) { Cell x; Data d; if( e->count != 2) { yyerror( "Cell() takes two arguments"); return; } d.COPY_COL_VAL = -1; d.COPY_COL = e->head; d.COPY_ROW = d.COPY_COL->next; int col_is_string = cell_parse_col( d.COPY_COL, &x, &cell_00); if( col_is_string < 0) // error return; else if( col_is_string > 0) // col is a string d.COPY_COL_VAL = x.col; install_tree( d.COPY_COL, &rn->u.r.start, 1); if( debug) { fprintf( out, "ss_Copy: COL_VAL = %d; col,row = ", d.COPY_COL_VAL); print_tree( d.COPY_COL, &rn->u.r.start, 0); putc( '\n', out); } ss_traverse_range( &rn->u.r.start, &rn->u.r.end, ss_Copy_callback, &d); } /* FILL start, inc command */ #define FILL_START d[0] #define FILL_INC d[1] #define FILL_COUNT i[0] #define FILL_TYPE i[1] static void ss_fill_callback( Data *d) { double val; if( d->FILL_TYPE == LINEAR) val = d->FILL_START + d->FILL_INC*d->FILL_COUNT; else val = d->FILL_START * pow( d->FILL_INC, d->FILL_COUNT); if( d->cp->used) { fprintf( out, "warning: overwriting cell "); print_cell( &d->c, &cell_00); putc( '\n', out); } d->cp->used = 1; d->cp->val = val; d->cp->state = EVALUATED; d->cp->n = NULL; ++d->FILL_COUNT; } void ss_fill( const Node *rn, Expr_list *e, int filltype) { Data d; Node *start, *inc; if( e->count > 2) { ss_dfill( rn, e); return; } start = e->head; inc = start->next; // it's ok for inc to be NULL install_tree( start, &cell_00, 1); d.FILL_TYPE = filltype; d.FILL_START = eval_tree( start, &cell_00); if( inc) d.FILL_INC = eval_tree( inc, &cell_00); else if( filltype == LINEAR) d.FILL_INC = 0; else d.FILL_INC = 1; // GEOMETRIC d.FILL_COUNT = 0; ss_traverse_range( &rn->u.r.start, &rn->u.r.end, ss_fill_callback, &d); } /* TTFILL command */ #define TTFILL_VALUE i[0] #define TTFILL_OFFSET i[1] #define TTFILL_LENGTH i[2] static void ss_ttfill_callback( Data *d) { if( d->cp->used) { fprintf( out, "warning: overwriting cell "); print_cell( &d->c, &cell_00); putc( '\n', out); } d->cp->used = 1; d->cp->val = (d->TTFILL_VALUE >> d->TTFILL_OFFSET) & 1; d->cp->state = EVALUATED; d->cp->n = NULL; if( d->last_col) { ++d->TTFILL_VALUE; d->TTFILL_OFFSET = d->TTFILL_LENGTH; } else { --d->TTFILL_OFFSET; } } void ss_ttfill( const Node *rn) { Data d; if( dir == BYROWS) d.TTFILL_LENGTH = abs(rn->u.r.end.col - rn->u.r.start.col); else d.TTFILL_LENGTH = abs(rn->u.r.end.row - rn->u.r.start.row); d.TTFILL_VALUE = 0; d.TTFILL_OFFSET = d.TTFILL_LENGTH; ss_traverse_range( &rn->u.r.start, &rn->u.r.end, ss_ttfill_callback, &d); } /* srand */ void ss_srand( Node *n) { install_tree( n, &cell_00, 0); srand( eval_tree( n, &cell_00) ); } /* print the column headers */ static void ss_write_headers( void) { int row, col, inc; putc( '\t', out); if( dir == BYROWS) { col = print_Range.start.col; inc = (col <= print_Range.end.col) ? 1 : -1; while(1) { if( print_Format == A0) print_col( col); else fprintf( out, "%d", col); if( col == print_Range.end.col) break; putc( '\t', out); col += inc; } } else /* BYCOLS */ { row = print_Range.start.row; inc = (row <= print_Range.end.row) ? 1 : -1; while(1) { fprintf( out, "%d", row); if( row == print_Range.end.row) break; putc( '\t', out); row += inc; } } putc( '\n', out); } /* print callback */ #define PRINT_TYPE i[0] static void ss_print_callback( Data *d) { if( d->first_col && headers) { if( dir == BYROWS) fprintf( out, "%d", d->c.row); else { if( print_Format == A0) print_col( d->c.col); else fprintf( out, "%d", d->c.col); } putc( '\t', out); } if( d->PRINT_TYPE == FORMATS) fprintf( out, "%s", get_format( d->cp->format, d->c.row, d->c.col) ); else if( d->cp->used) { switch( d->PRINT_TYPE) { case FORMULAS: if( d->cp->n) print_tree( d->cp->n, &d->c, 0); else if( d->cp->depend) fprintf( out, "(%s)", d->cp->depend->name); else fprintf( out, "%g", d->cp->val); break; case VALUES: if( d->cp->n && d->cp->n->type == STRING) fprintf( out, "%s", d->cp->n->u.str); else fprintf( out, get_format( d->cp->format, d->c.row, d->c.col), d->cp->val); break; case POINTERS: fprintf( out, "%p", (void *) d->cp->n); break; case STATES: fprintf( out, "%d", d->cp->state); if( d->cp->depend) fprintf( out, " (%s)", d->cp->depend->name); break; } } if( d->last_col) putc( '\n', out); else putc( '\t', out); } void ss_print( int type) { Data d; switch( type) { case SYMBOLS: print_symbols(); return; case CONSTANTS: print_constants(); return; case FUNCTIONS: print_func(); return; case STATES: print_symbol_states(); break; } if( print_Range.end.row < 0 || print_Range.end.col < 0) /* empty spreadsheet */ return; if( headers) ss_write_headers(); else fprintf( out, "\t\n"); d.PRINT_TYPE = type; ss_traverse_range( &print_Range.start, &print_Range.end, ss_print_callback, &d); } /* plot2d callback */ static void ss_plot2d_callback( Data *d) { if( d->cp->used) fprintf( out, "%g", d->cp->val); if( d->last_col) putc( '\n', out); else putc( '\t', out); } void ss_plot2d( const Node *rn) { Data d; fprintf( out, "\t\n"); if( rn) ss_traverse_range( &rn->u.r.start, &rn->u.r.end, ss_plot2d_callback, &d); else ss_traverse_range( &used.start, &used.end, ss_plot2d_callback, &d); } /* plot3d callback */ static void ss_plot3d_callback( Data *d) { if( d->cp->used) fprintf( out, "%d %d %g\n", d->c.row, d->c.col, d->cp->val); } void ss_plot3d( const Node *rn) { Data d; fprintf( out, "\t\n"); if( rn) ss_traverse_range( &rn->u.r.start, &rn->u.r.end, ss_plot3d_callback, &d); else ss_traverse_range( &used.start, &used.end, ss_plot3d_callback, &d); } /*** evaluation routines ***/ #define EVAL_CHANGED i[0] static void ss_eval_callback( Data *d) { double oldval; if( debug) { fprintf( out, "eval: cell "); print_cell( &d->c, &cell_00); fprintf( out, ", state = %d", d->cp->state); if( !d->cp->used) fprintf( out, " - unused"); else if( d->cp->depend) fprintf( out, " - depends on %s", d->cp->depend->name); else if( !d->cp->n || d->cp->n->type == STRING) fprintf( out, " - constant"); putc( '\n', out); } if( d->cp->used) { if( depend) { eval_cell( d->cp, &d->c); // sets d->cp->val if necessary } else if( d->cp->n) { oldval = d->cp->val; d->cp->val = eval_tree( d->cp->n, &d->c); if( oldval != d->cp->val) d->EVAL_CHANGED = 1; } } } void ss_eval( const Node *rn, int n, int eval_order) { Data d; int i = 0; if( n == 0) return; depend = (n < 0); n = abs(n); d.EVAL_CHANGED = 1; /* flag for cell value changing */ while( i < n && d.EVAL_CHANGED) { ++i; d.EVAL_CHANGED = 0; // if evaluating in dependency order there is no need to // evaluate anything more than once switch( eval_order) { case EVAL_RANGE: ss_traverse_range( &rn->u.r.start, &rn->u.r.end, ss_eval_callback, &d); if( depend) return; break; case EVAL_SYMS: d.EVAL_CHANGED = eval_symbols(); if( depend) return; break; case EVAL_RANGE_SYMS: ss_traverse_range( &rn->u.r.start, &rn->u.r.end, ss_eval_callback, &d); d.EVAL_CHANGED += eval_symbols(); if( depend) return; break; case EVAL_SYMS_RANGE: d.EVAL_CHANGED = eval_symbols(); ss_traverse_range( &rn->u.r.start, &rn->u.r.end, ss_eval_callback, &d); if( depend) return; break; case EVAL_ALL: d.EVAL_CHANGED = eval_symbols(); if( used.end.row >= 0 && used.end.col >= 0) // non-empty spreadsheet { ss_traverse_range( &used.start, &used.end, ss_eval_callback, &d); if( depend) return; ss_traverse_range( &used.end, &used.start, ss_eval_callback, &d); } break; } } if( d.EVAL_CHANGED) fprintf( out, "eval: still changing after %d iteration%s\n", n, (n != 1) ? "s" : ""); else if( i < n) fprintf( out, "eval: converged after %d iteration%s\n", i, (i != 1) ? "s" : ""); depend = 1; } static void ss_eval_reset_callback( Data *d) { if( debug) { fprintf( out, "reset: cell "); print_cell( &d->c, &cell_00); putc( '\n', out); } if( d->cp->used && d->cp->n && d->cp->n->type != STRING) d->cp->state = UNEVALUATED; } void ss_eval_reset( const Node *rn, int eval_order) { Data d; // the order in which cells and symbols are reset doesn't matter, // but we follow eval_order for consistency with the ss_eval() code // switch( eval_order) { case EVAL_RANGE: ss_traverse_range( &rn->u.r.start, &rn->u.r.end, ss_eval_reset_callback, &d); break; case EVAL_SYMS: reset_symbols(); break; case EVAL_RANGE_SYMS: ss_traverse_range( &rn->u.r.start, &rn->u.r.end, ss_eval_reset_callback, &d); reset_symbols(); break; case EVAL_SYMS_RANGE: reset_symbols(); ss_traverse_range( &rn->u.r.start, &rn->u.r.end, ss_eval_reset_callback, &d); break; case EVAL_ALL: reset_symbols(); if( used.end.row >= 0 && used.end.col >= 0) // non-empty spreadsheet ss_traverse_range( &used.start, &used.end, ss_eval_reset_callback, &d); break; } } /************* functions *************/ double bad_func( const Node *n, const Cell *c) { fprintf( out, "\nss: bad function call\n"); return 0; } /************* operator functions *************/ double op_pp_mm( const Node *n, const Cell *cp) { double oldval = 0, newval = 0; int add, old; /* add or subtract, return old or new value */ Node *x; switch( n->type) { case X_MM: add = 0; old = 1; x = Left(n); break; case X_PP: add = 1; old = 1; x = Left(n); break; case MM_X: add = 0; old = 0; x = Right(n); break; case PP_X: add = 1; old = 0; x = Right(n); break; default: /* shouldn't happen... */ fprintf( out, "op_pp_mm: bad increment type %d\n", n->type); return 0; } if( x->type == CELL) { Cell c = x->u.c; adjust_cell( &c, cp); if( !check_cell( &c) ) { oldval = ss[c.row][c.col].val; if( ss[c.row][c.col].n) { fprintf( out, "error: can not perform operation %s on cell ", func[n->type]->name); print_cell( &c, &cell_00); putc( '\n', out); newval = oldval; } else { if( depend) { fprintf( out, "op_pp_mm: cell "); print_cell( &c, &cell_00); fprintf( out, " has a cyclic dependency\n"); ss[c.row][c.col].state = EVALUATED; } newval = add ? ++ss[c.row][c.col].val : --ss[c.row][c.col].val; ss[c.row][c.col].used = 1; } } } else if( x->type == ID) { Symbol *s = x->u.s; oldval = s->val; if( s->n) { fprintf( out, "error: can not perform operation %s on symbol %s\n", func[n->type]->name, s->name); newval = oldval; } else { if( depend) { fprintf( out, "op_pp_mm: symbol %s has a cyclic dependency\n", s->name); s->state = EVALUATED; } newval = add ? ++s->val : --s->val; } } else /* shouldn't happen, type checked at compile time */ { fprintf( out, "op_pp_mm: bad increment argument type %d\n", x->type); } return old ? oldval : newval; } double op_asgn( const Node *n, const Cell *cp) { double oldval, newval, rhs; Node *x = Left(n); Symbol *s = NULL; if( x) /* ID */ { s = x->u.s; oldval = s->val; if( depend) { fprintf( out, "op_asgn: symbol %s has a cyclic dependency\n", s->name); s->state = EVALUATED; } } else /* CELL */ { oldval = ss[cp->row][cp->col].val; if( depend) { fprintf( out, "op_asgn: cell "); print_cell( cp, &cell_00); fprintf( out, " has a cyclic dependency\n"); ss[cp->row][cp->col].state = EVALUATED; } } rhs = eval_tree( Right(n), cp); switch( n->type) { case MUL_ASGN: newval = oldval * rhs; break; case DIV_ASGN: newval = oldval / rhs; break; case MOD_ASGN: newval = fmod( oldval, rhs); break; case ADD_ASGN: newval = oldval + rhs; break; case SUB_ASGN: newval = oldval - rhs; break; case SHL_ASGN: newval = ldexp( oldval, rhs); break; case SHR_ASGN: newval = ldexp( oldval, -rhs); break; case BIT_AND_ASGN: newval = (long) oldval & (long) rhs; break; case BIT_XOR_ASGN: newval = (long) oldval ^ (long) rhs; break; case BIT_OR_ASGN: newval = (long) oldval | (long) rhs; break; case AND_ASGN: newval = oldval && rhs; break; case XOR_ASGN: newval = (oldval || 0) != (rhs || 0); break; case OR_ASGN: newval = oldval || rhs; break; default: /* shouldn't happen... */ fprintf( out, "op_asgn: bad operator assignment %d\n", n->type); return 0; } if( s) s->val = newval; else ss[cp->row][cp->col].val = newval; return newval; } double op_cast_int( const Node *n, const Cell *c) { return (int) eval_tree( Right(n), c); } double op_cast_long( const Node *n, const Cell *c) { return (long) eval_tree( Right(n), c); } double op_cast_double( const Node *n, const Cell *c) { return eval_tree( Right(n), c); } double op_pow( const Node *n, const Cell *c) { return pow( eval_tree( Left(n), c), eval_tree( Right(n), c) ); } double op_mul( const Node *n, const Cell *c) { return eval_tree( Left(n), c) * eval_tree( Right(n), c); } double op_div( const Node *n, const Cell *c) { return eval_tree( Left(n), c) / eval_tree( Right(n), c); } double op_mod( const Node *n, const Cell *c) { return fmod( eval_tree( Left(n), c), eval_tree( Right(n), c) ); } double op_add( const Node *n, const Cell *c) { return eval_tree( Left(n), c) + eval_tree( Right(n), c); } double op_sub( const Node *n, const Cell *c) { return eval_tree( Left(n), c) - eval_tree( Right(n), c); } double op_uminus( const Node *n, const Cell *c) { return -eval_tree( Right(n), c); } double op_bit_not( const Node *n, const Cell *c) { return ~ (long) eval_tree( Right(n), c); } double op_not( const Node *n, const Cell *c) { return !eval_tree( Right(n), c); } double op_shl( const Node *n, const Cell *c) { return ldexp( eval_tree( Left(n), c), eval_tree( Right(n), c) ); } double op_shr( const Node *n, const Cell *c) { return ldexp( eval_tree( Left(n), c), -eval_tree( Right(n), c) ); } double op_bit_and( const Node *n, const Cell *c) { return (long) eval_tree( Left(n), c) & (long) eval_tree( Right(n), c); } double op_bit_xor( const Node *n, const Cell *c) { return (long) eval_tree( Left(n), c) ^ (long) eval_tree( Right(n), c); } double op_bit_or( const Node *n, const Cell *c) { return (long) eval_tree( Left(n), c) | (long) eval_tree( Right(n), c); } double op_and( const Node *n, const Cell *c) { return eval_tree( Left(n), c) && eval_tree( Right(n), c); } double op_xor( const Node *n, const Cell *c) { /* x || 0 --> 0 or 1 */ return (eval_tree( Left(n), c) || 0) != (eval_tree( Right(n), c) || 0); } double op_or( const Node *n, const Cell *c) { return eval_tree( Left(n), c) || eval_tree( Right(n), c); } double op_cond( const Node *n, const Cell *c) { const Node *r = Right(n); return eval_tree( Left(n), c) ? eval_tree( Left(r), c) : eval_tree( Right(r), c); } double op_lt( const Node *n, const Cell *c) { return eval_tree( Left(n), c) < eval_tree( Right(n), c); } double op_le( const Node *n, const Cell *c) { return eval_tree( Left(n), c) <= eval_tree( Right(n), c); } double op_eq( const Node *n, const Cell *c) { return eval_tree( Left(n), c) == eval_tree( Right(n), c); } double op_ne( const Node *n, const Cell *c) { return eval_tree( Left(n), c) != eval_tree( Right(n), c); } double op_ge( const Node *n, const Cell *c) { return eval_tree( Left(n), c) >= eval_tree( Right(n), c); } double op_gt( const Node *n, const Cell *c) { return eval_tree( Left(n), c) > eval_tree( Right(n), c); } double op_comma( const Node *n, const Cell *c) { eval_tree( Left(n), c); return eval_tree( Right(n), c); } /************* helper for avg, var, stdev, stats *************/ #define STATS_AVG d[0] #define STATS_VAR d[1] #define STATS_MIN d[2] #define STATS_MAX d[3] #define STATS_COUNT i[0] static void stats_callback( Data *d) { double t; ++d->STATS_COUNT; t = d->list_val - d->STATS_AVG; d->STATS_AVG += t / d->STATS_COUNT; d->STATS_VAR += t * (d->list_val - d->STATS_AVG); if( d->list_val < d->STATS_MIN) d->STATS_MIN = d->list_val; if( d->list_val > d->STATS_MAX) d->STATS_MAX = d->list_val; } static void stats_helper( const Node *n, const Cell *c, double p[]) { Data d; d.STATS_AVG = d.STATS_VAR = d.STATS_COUNT = 0; d.STATS_MIN = HUGE_VAL; d.STATS_MAX = -HUGE_VAL; ss_traverse_list( n, c, stats_callback, &d); if( d.STATS_COUNT > 2) d.STATS_VAR /= (d.STATS_COUNT - 1); p[0] = d.STATS_AVG; p[1] = d.STATS_VAR; p[2] = d.STATS_MIN; p[3] = d.STATS_MAX; } /************* helper for feval, llsq, dot, etc. *************/ static int check_range( Node *n, const char *msg) { if( n && n->type == CELL) // change to RANGE { n->type = RANGE; n->u.r.end = n->u.r.start; } if( !n || n->type != RANGE) { fprintf( out, "%s must be a range\n", msg); return -1; } return 0; } /************* add dependency to a range *************/ static void add_range_depend_callback( Data *d) { #if 0 if( check_cell( &d->c)) return; #endif if( d->cp->n || d->cp->depend) { yyerror( "cell already has formula or dependency"); fprintf( out, "add_range_depend: cell "); print_cell( &d->c, &cell_00); putc( '\n', out); } d->cp->used = 1; d->cp->depend = d->s; d->cp->state = UNEVALUATED; } void add_range_depend( Node *n, Symbol *s) { Data d; d.s = s; ss_traverse_range( &n->u.r.start, &n->u.r.end, add_range_depend_callback, &d); } /************* numeric functions *************/ #include "tmp/nf.c" /************* range functions *************/ #include "tmp/rf.c"