// $Id: sheet_calc.cc,v 1.7 1998/08/06 21:08:50 aml Exp $ #include "sheet.hh" #include "canvas.hh" void Sheet::manipulateReferences(Cell *cell, int operation, Oper_info *info) { int i; unsigned char *aux; int typ; double *pt_fp; Ref *pt_ref,ref; short *pt_int; char *pt_st; Range *pt_rg; Stack_elem *new_top; double x1,x2; int cnt; Formula *form ; Sheet *sheet; Cell *cell2; short r,c; Range_iter ri; Range rg; sheet = this; i = 0; form = cell->formula; aux = (unsigned char *)form->formula; while (aux - (unsigned char *) form->formula < form->size) { typ = *aux; aux++; switch(typ) { case FORM_FP: aux += FORM_FP_SIZE; break; case FORM_REF: memcpy(&ref,aux,sizeof(Ref)); c = cell_address(ref.col,cell->col); r = cell_address(ref.row,cell->row); if (CHECK_COL_BOUNDS(c) && CHECK_ROW_BOUNDS(r)) { cell2 = cellFind(c,r); if (operation == INSERT_REFERENCES) cell2->addDependence(cell); else if (operation == REMOVE_REFERENCES) cell2->removeDependence(cell); else if (operation == INSERT_ROW_OR_COLUMN) cell->changeCellReference(aux,info,c,r); } aux += FORM_REF_SIZE; break; case FORM_INT: aux += FORM_INT_SIZE; break; case FORM_RANGE: memcpy(&rg,aux,sizeof(Range)); ri.init(rg); cnt = 0; if (operation == INSERT_REFERENCES || operation == REMOVE_REFERENCES) for(ri.first(ref); !ri.last(); ri.next(ref)){ c = cell_address(ref.col,cell->col); r = cell_address(ref.row,cell->row); if (CHECK_COL_BOUNDS(c) && CHECK_ROW_BOUNDS(r)) { cell2 = cellFind(c,r); if (operation == INSERT_REFERENCES) cell2->addDependence(cell); else if (operation == REMOVE_REFERENCES) cell2->removeDependence(cell); } } else if (operation == INSERT_ROW_OR_COLUMN) { c = cell_address(rg.start.col,cell->col); r = cell_address(rg.start.row,cell->row); cell->changeCellReference(aux,info,c,r); c = cell_address(rg.end.col,cell->col); r = cell_address(rg.end.row,cell->row); cell->changeCellReference(aux+4,info,c,r); } aux += FORM_RANGE_SIZE; break; case FORM_RETURN: return; case FORM_PAR: break; case FORM_STRING: aux += strlen((char*)aux)+1; break; case FORM_UNMINUS: case FORM_UNARY_MINUS: break; /* All operators with a fixed number of arguments */ case FORM_PLUS: case FORM_SUB: case FORM_MULT: case FORM_DIV: case FORM_POWER: case FORM_ABS: case FORM_INTF: case FORM_SQRT: case FORM_LOG: case FORM_LN: case FORM_PI: case FORM_SIN: case FORM_COS: case FORM_TAN: case FORM_ATAN2: case FORM_ATAN: case FORM_ASIN: case FORM_ACOS: case FORM_EXP: case FORM_MONTH: case FORM_DAY: case FORM_SECOND: case FORM_HOUR: case FORM_MINUTE: case FORM_MOD: break; case FORM_GREATER: case FORM_SMALLER: case FORM_EQUAL: case FORM_GREATEREQ: case FORM_SMALLEREQ: case FORM_NEQUAL: case FORM_AND: case FORM_OR: break; case FORM_NOT: break; case FORM_ROUND: case FORM_UPPER: case FORM_LOWER: case FORM_PROPER: case FORM_VLOOKUP: case FORM_HLOOKUP: case FORM_IF: break; /* All functions with a variable number of arguments are similar */ case FORM_SUM: case FORM_SUMIF: case FORM_COUNT: case FORM_MIN: case FORM_MAX: aux++; break; default: cerr << "Unknown formula item code " << typ << "\n"; return; internal_error(); } } } Stack_elem *Sheet::evaluate(Cell *cell) { int i; unsigned char *aux; int typ; double *pt_fp; Ref *pt_ref,ref; short *pt_int; char *pt_st; Range *pt_rg; Stack_elem *new_top; double x1,x2; int i1,i2; int cnt; Formula *form ; Sheet *sheet; int nargs; sheet = this; i = 0; form = cell->formula; aux = (unsigned char *)form->formula; top = stack; while (aux - (unsigned char *) form->formula < form->size) { typ = *aux; aux++; top->type = typ; top->contents.error_code = NO_ERROR; switch(typ) { case FORM_FP: pt_fp = (double*)aux; memcpy(&(top->contents.fp_val),aux,sizeof(double)); top->type = FORM_FP; top++; aux += FORM_FP_SIZE; break; case FORM_REF: memcpy(&ref,aux,sizeof(Ref)); new_top = cellRef(cell_address(ref.col,cell->col), cell_address(ref.row,cell->row)); *top = *new_top; propagate_error(new_top,top); new_top->type = FORM_REF; top++; aux += FORM_REF_SIZE; break; case FORM_INT: memcpy(&(top->contents.int_val),aux,sizeof(short)); top->type = FORM_INT; top++; aux += FORM_INT_SIZE; break; case FORM_RANGE: /* pt_rg = (Range*) aux; top->contents.rg_val = *pt_rg; */ memcpy(&(top->contents.rg_val),aux,sizeof(Range)); top++; aux += FORM_RANGE_SIZE; top->type = FORM_RANGE; break; case FORM_RETURN: /* Formula ended */ return(top-1); break; case FORM_PAR: break; case FORM_STRING: cnt = strlen((char*)aux); memcpy(top->contents.string_val,aux,cnt+1); aux += cnt+1; top->type = FORM_STRING; top++; break; case FORM_UNMINUS: case FORM_UNARY_MINUS: new_top = top-1; if (!propagate_error(top-1,new_top)) { new_top->contents.fp_val = - stack_value(top-1); new_top->type = FORM_FP; } top = new_top+1; break; /* All operators with two arguments */ case FORM_PLUS: case FORM_SUB: case FORM_MULT: case FORM_DIV: case FORM_POWER: new_top = top-2; /* Tricky decision here. Any operation becomes a double result */ if (!propagate_error(top-1,new_top) && !propagate_error(top-2,new_top)) { x1 = stack_value(top-2); x2 = stack_value(top-1); SET_ERROR(new_top, NO_ERROR); new_top->type = FORM_FP; switch(typ) { case FORM_PLUS: new_top->contents.fp_val = x1+x2; break; case FORM_SUB: new_top->contents.fp_val = x1-x2; break; case FORM_MULT: new_top->contents.fp_val = x1*x2; break; case FORM_DIV: if (x2 == 0) { SET_ERROR(new_top,ERROR_DIV_BY_0); } else new_top->contents.fp_val = x1/x2; break; case FORM_POWER: if (x1 < 0) SET_ERROR(new_top,ERROR_BAD_VALUE) else { if (x1 > 0 && (log(x1)*x2 > UPPER_EXP_LIMIT || log(x1)*x2 < - UPPER_EXP_LIMIT)) SET_ERROR(new_top,ERROR_BAD_VALUE) else new_top->contents.fp_val = pow(x1,x2); } break; } } top = new_top+1; break; case FORM_GREATER: case FORM_SMALLER: case FORM_EQUAL: case FORM_GREATEREQ: case FORM_SMALLEREQ: case FORM_NEQUAL: case FORM_AND: case FORM_OR: new_top = top-2; /* Tricky decision here. Any operation becomes a double result */ if (!propagate_error(top-1,new_top) && !propagate_error(top-2,new_top)) { x1 = stack_value(top-2); x2 = stack_value(top-1); SET_ERROR(new_top, NO_ERROR); switch(typ) { case FORM_EQUAL: if (x1 == x2) new_top->contents.int_val = 1; else new_top->contents.int_val = 0; break; case FORM_GREATER: if (x1 > x2) new_top->contents.int_val = 1; else new_top->contents.int_val = 0; break; case FORM_SMALLER: if (x1 < x2) new_top->contents.int_val = 1; else new_top->contents.int_val = 0; break; case FORM_NEQUAL: if (x1 != x2) new_top->contents.int_val = 1; else new_top->contents.int_val = 0; break; case FORM_SMALLEREQ: if (x1 <= x2) new_top->contents.int_val = 1; else new_top->contents.int_val = 0; break; case FORM_GREATEREQ: if (x1 >= x2) new_top->contents.int_val = 1; else new_top->contents.int_val = 0; break; case FORM_AND: if (x1 && x2) new_top->contents.int_val = 1; else new_top->contents.int_val = 0; break; case FORM_OR: if (x1 || x2) new_top->contents.int_val = 1; else new_top->contents.int_val = 0; break; } } new_top->type = FORM_INT; top = new_top+1; break; /* Simple functions with a single argument */ case FORM_NOT: new_top = top-1; if (!propagate_error(top-1,new_top)) { x1 = stack_value(top-1); if (x1) new_top->contents.int_val = 0; else new_top->contents.int_val = 1; } new_top->type = FORM_INT; new_top = new_top+1; break; case FORM_ABS: new_top = top-1; if (!propagate_error(top-1,new_top)) { x1 = stack_value(top-1); new_top->contents.fp_val = fabs(x1); } new_top->type = FORM_FP; new_top = new_top+1; break; case FORM_INTF: new_top = top-1; if (!propagate_error(top-1,new_top)) { x1 = stack_value(top-1); new_top->contents.fp_val = floor(x1); } new_top->type = FORM_FP; new_top = new_top+1; break; case FORM_SQRT: new_top = top-1; if (!propagate_error(top-1,new_top)) { x1 = stack_value(top-1); if (x1 < 0) { SET_ERROR(new_top,ERROR_BAD_VALUE); } else new_top->contents.fp_val = sqrt(x1); } new_top->type = FORM_FP; new_top = new_top+1; break; case FORM_LOG: new_top = top-1; if (!propagate_error(top-1,new_top)) { x1 = stack_value(top-1); if (x1 < 0) { SET_ERROR(new_top,ERROR_BAD_VALUE); } else new_top->contents.fp_val = log10(x1); } new_top->type = FORM_FP; new_top = new_top+1; break; case FORM_LN: new_top = top-1; if (!propagate_error(top-1,new_top)) { x1 = stack_value(top-1); if (x1 < 0) { SET_ERROR(new_top,ERROR_BAD_VALUE); } else new_top->contents.fp_val = log(x1); } new_top->type = FORM_FP; new_top = new_top+1; break; case FORM_PI: new_top = top; new_top->contents.fp_val = M_PI; new_top->type = FORM_FP; top = new_top+1; break; case FORM_SIN: new_top = top-1; if (!propagate_error(top-1,new_top)) { x1 = stack_value(top-1); new_top->contents.fp_val = sin(x1); } new_top->type = FORM_FP; new_top = new_top+1; break; case FORM_COS: new_top = top-1; if (!propagate_error(top-1,new_top)) { x1 = stack_value(top-1); new_top->contents.fp_val = cos(x1); } new_top->type = FORM_FP; new_top = new_top+1; break; case FORM_TAN: new_top = top-1; if (!propagate_error(top-1,new_top)) { x1 = stack_value(top-1); new_top->contents.fp_val = tan(x1); } new_top->type = FORM_FP; new_top = new_top+1; break; case FORM_ATAN2: new_top = top-2; if (!propagate_error(top-1,new_top) && !propagate_error(top-2,new_top)) { double x; x1 = stack_value(top-2); x2 = stack_value(top-1); if (x2 == 0) SET_ERROR(new_top,ERROR_DIV_BY_0) else { x = x1/x2; if (x <= -M_PI/2.0 || x >= M_PI/2.0) SET_ERROR(new_top,ERROR_BAD_VALUE) else new_top->contents.fp_val = atan(x); } } new_top->type = FORM_FP; top = new_top+1; break; case FORM_ATAN: new_top = top-1; if (!propagate_error(top-1,new_top)) { x1 = stack_value(top-1); if (x1 <= -M_PI/2.0 || x1 >= M_PI/2.0) SET_ERROR(new_top,ERROR_BAD_VALUE) else new_top->contents.fp_val = tan(x1); } new_top->type = FORM_FP; new_top = new_top+1; break; case FORM_ASIN: new_top = top-1; if (!propagate_error(top-1,new_top)) { x1 = stack_value(top-1); if (x1 <= -1.0 || x1 >= 1.0) SET_ERROR(new_top,ERROR_BAD_VALUE) else new_top->contents.fp_val = asin(x1); } new_top->type = FORM_FP; new_top = new_top+1; break; case FORM_ACOS: new_top = top-1; if (!propagate_error(top-1,new_top)) { x1 = stack_value(top-1); if (x1 <= -1.0 || x1 >= 1.0) SET_ERROR(new_top,ERROR_BAD_VALUE) else new_top->contents.fp_val = acos(x1); } new_top->type = FORM_FP; new_top = new_top+1; break; case FORM_EXP: new_top = top-1; if (!propagate_error(top-1,new_top)) { x1 = stack_value(top-1); if (x1 > UPPER_EXP_LIMIT || x1 < - UPPER_EXP_LIMIT) SET_ERROR(new_top,ERROR_BAD_VALUE) else new_top->contents.fp_val = exp(x1); } new_top->type = FORM_FP; new_top = new_top+1; break; case FORM_DAY: new_top = top-1; if (!propagate_error(top-1,new_top)) { x1 = stack_value(top-1); new_top->contents.fp_val = day_function(x1); } new_top->type = FORM_FP; new_top = new_top+1; break; case FORM_MONTH: new_top = top-1; if (!propagate_error(top-1,new_top)) { x1 = stack_value(top-1); new_top->contents.fp_val = month_function(x1); } new_top->type = FORM_FP; new_top = new_top+1; break; case FORM_MINUTE: new_top = top-1; if (!propagate_error(top-1,new_top)) { x1 = stack_value(top-1); new_top->contents.fp_val = minute_function(x1); } new_top->type = FORM_FP; new_top = new_top+1; break; case FORM_HOUR: new_top = top-1; if (!propagate_error(top-1,new_top)) { x1 = stack_value(top-1); new_top->contents.fp_val = hour_function(x1); } new_top->type = FORM_FP; new_top = new_top+1; break; case FORM_SECOND: new_top = top-1; if (!propagate_error(top-1,new_top)) { x1 = stack_value(top-1); new_top->contents.fp_val = second_function(x1); } new_top->type = FORM_FP; new_top = new_top+1; break; case FORM_MOD: new_top = top-2; if (!propagate_error(top-2,new_top) && !propagate_error(top-1,new_top)) { i1 = (int) stack_value(top-2); i2 = (int) stack_value(top-1); if (i2 == 0) SET_ERROR(new_top,ERROR_DIV_BY_0) else new_top->contents.int_val = i1 % i2; } new_top->type = FORM_INT; top = new_top+1; break; /* All functions with a fixed number of arguments are similar */ case FORM_ROUND: nargs = 2; EVAL_FIXED_ARGS(Round,this,cell->col,cell->row,nargs); break; case FORM_UPPER: nargs = 1; EVAL_FIXED_ARGS(Upper,this,cell->col,cell->row,nargs); break; case FORM_LOWER: nargs = 1; EVAL_FIXED_ARGS(Lower,this,cell->col,cell->row,nargs); break; case FORM_PROPER: nargs = 1; EVAL_FIXED_ARGS(Proper,this,cell->col,cell->row,nargs); break; case FORM_VLOOKUP: nargs = 3; EVAL_FIXED_ARGS(Vlookup,this,cell->col,cell->row,nargs); break; case FORM_HLOOKUP: nargs = 3; EVAL_FIXED_ARGS(Hlookup,this,cell->col,cell->row,nargs); break; /* All functions with variable number of arguments are similar */ case FORM_IF: EVAL_VAR_NUM_ARGS(If,this,cell->col,cell->row); break; case FORM_SUM: EVAL_VAR_NUM_ARGS(Sum,this,cell->col,cell->row); break; case FORM_SUMIF: EVAL_VAR_NUM_ARGS(Sumif,this,cell->col,cell->row); break; case FORM_COUNT: EVAL_VAR_NUM_ARGS(Count,this,cell->col,cell->row); break; case FORM_MIN: EVAL_VAR_NUM_ARGS(Min,this,cell->col,cell->row); break; case FORM_MAX: EVAL_VAR_NUM_ARGS(Max,this,cell->col,cell->row); break; default: internal_error(); } } } Stack_elem *Sheet::formula(Cell *cell) { int i; unsigned char *aux; int typ; double *pt_fp,fp; Ref *pt_ref,ref; short *pt_int,sho; char *pt_st; Range *pt_rg,rg; Stack_elem *new_top; string s1,s2; char buf[256]; short c,r,c1,r1,c2,r2; Formula *form; int cnt; int nargs; i = 0; form = cell->formula; aux = (unsigned char *) form->formula; top = stack; while (aux - (unsigned char *)form->formula < form->size) { typ = *aux; aux++; switch(typ) { case FORM_FP: memcpy(&fp,aux,sizeof(double)); sprintf(buf,"%g",fp); top->st_val = buf; top->type = FORM_STRING; top++; aux += FORM_FP_SIZE; break; case FORM_REF: { short cc,rr; memcpy(&ref,aux,sizeof(Ref)); c = ref.col; r = ref.row; cc = cell_address(c,cell->col); rr = cell_address(r,cell->row); if (CHECK_COL_BOUNDS(cc) && CHECK_ROW_BOUNDS(rr)) { sprintf(buf,"%s%s%s%d", IS_RELATIVE(c) ? "" : "$", coltoa(cell_address(c,cell->col)), IS_RELATIVE(r) ? "" : "$", cell_address(r,cell->row)+1 ); } else sprintf(buf,"#REF"); top->st_val = buf; top->type = FORM_STRING; top++; aux += FORM_REF_SIZE; break; } case FORM_INT: memcpy(&sho,aux,sizeof(short)); sprintf(buf,"%d",sho); top->st_val = buf; top->type = FORM_STRING; top++; aux += FORM_INT_SIZE; break; case FORM_RANGE: { short cc1,cc2,rr1,rr2; memcpy(&rg,aux,sizeof(Range)); c1 = rg.start.col; r1 = rg.start.row; c2 = rg.end.col; r2 = rg.end.row; cc1 = cell_address(c1,cell->col); cc2 = cell_address(c2,cell->col); rr1 = cell_address(r1,cell->row); rr2 = cell_address(r2,cell->row); if (CHECK_COL_BOUNDS(cc1) && CHECK_ROW_BOUNDS(rr1)) { sprintf(buf,"%s%s%s%d", IS_RELATIVE(c1) ? "" : "$", coltoa(cc1), IS_RELATIVE(r1) ? "" : "$", rr1+1); } else sprintf(buf,"#REF"); top->st_val = buf; sprintf(buf,":"); top->st_val += buf; if (CHECK_COL_BOUNDS(cc2) && CHECK_ROW_BOUNDS(rr2)) { sprintf(buf,"%s%s%s%d", IS_RELATIVE(c2) ? "" : "$", coltoa(cc2), IS_RELATIVE(r2) ? "" : "$", rr2+1 ); } else sprintf(buf,"#REF"); top->st_val += buf; top->type = FORM_STRING; top++; aux += FORM_RANGE_SIZE; } break; case FORM_RETURN: /* Formula ended */ return(top-1); break; case FORM_PAR: new_top = top-1; s1 = new_top->st_val; new_top->st_val = "("; new_top->st_val += s1; new_top->st_val += ")"; new_top->type = FORM_STRING; break; case FORM_STRING: top->st_val = "\""; top->st_val += (char *)aux; top->st_val += "\""; top->type = FORM_STRING; aux += strlen((char*)aux)+1; top++; break; case FORM_UNMINUS: case FORM_UNARY_MINUS: new_top = top-1; s1 = (top-1)->st_val; new_top->st_val = "-"; new_top->st_val += s1; new_top->type = FORM_STRING; top = new_top+1; break; /* All operators with two arguments */ case FORM_PLUS: case FORM_SUB: case FORM_MULT: case FORM_DIV: case FORM_EQUAL: case FORM_GREATER: case FORM_SMALLER: case FORM_NEQUAL: case FORM_GREATEREQ: case FORM_SMALLEREQ: case FORM_POWER: new_top = top-2; s1 = (top-2)->st_val; s2 = (top-1)->st_val; new_top->st_val = s1; switch(typ) { case FORM_PLUS: new_top->st_val += "+"; break; case FORM_SUB: new_top->st_val += "-"; break; case FORM_MULT: new_top->st_val += "*"; break; case FORM_DIV: new_top->st_val += "/"; break; case FORM_POWER: new_top->st_val += "^"; break; case FORM_EQUAL: new_top->st_val += "="; break; case FORM_GREATER: new_top->st_val += ">"; break; case FORM_SMALLER: new_top->st_val += "<"; break; case FORM_NEQUAL: new_top->st_val += "<>"; break; case FORM_GREATEREQ: new_top->st_val += ">="; break; case FORM_SMALLEREQ: new_top->st_val += "<="; break; } new_top->st_val += s2; new_top->type = FORM_STRING; top = new_top+1; break; /* Operators with fixed number os arguments */ case FORM_NOT: nargs = 1; STRING_FIXED_ARGS("@NOT",nargs); break; case FORM_ABS: nargs = 1; STRING_FIXED_ARGS("@ABS",nargs); break; case FORM_INTF: nargs = 1; STRING_FIXED_ARGS("@INT",nargs); break; case FORM_SQRT: nargs = 1; STRING_FIXED_ARGS("@SQRT",nargs); break; case FORM_LOG: nargs = 1; STRING_FIXED_ARGS("@LOG",nargs); break; case FORM_LN: nargs = 1; STRING_FIXED_ARGS("@LN",nargs); break; case FORM_PI: new_top = top; new_top->st_val = "@PI()"; top = new_top+1; break; case FORM_SIN: nargs = 1; STRING_FIXED_ARGS("@SIN",nargs); break; case FORM_COS: nargs = 1; STRING_FIXED_ARGS("@COS",nargs); break; case FORM_TAN: nargs = 1; STRING_FIXED_ARGS("@TAN",nargs); break; case FORM_ATAN2: nargs = 2; STRING_FIXED_ARGS("@ATAN2",nargs); break; case FORM_ATAN: nargs = 1; STRING_FIXED_ARGS("@ATAN",nargs); break; case FORM_ASIN: nargs = 1; STRING_FIXED_ARGS("@ASIN",nargs); break; case FORM_ACOS: nargs = 1; STRING_FIXED_ARGS("@ACOS",nargs); break; case FORM_EXP: nargs = 1; STRING_FIXED_ARGS("@EXP",nargs); break; case FORM_DAY: nargs = 1; STRING_FIXED_ARGS("@DAY",nargs); break; case FORM_MONTH: nargs = 1; STRING_FIXED_ARGS("@MONTH",nargs); break; case FORM_MINUTE: nargs = 1; STRING_FIXED_ARGS("@MINUTE",nargs); break; case FORM_HOUR: nargs = 1; STRING_FIXED_ARGS("@HOUR",nargs); break; case FORM_SECOND: nargs = 1; STRING_FIXED_ARGS("@SECONDS",nargs); break; case FORM_MOD: nargs = 2; STRING_FIXED_ARGS("@MOD",nargs); break; case FORM_ROUND: nargs = 2; STRING_FIXED_ARGS("@ROUND",nargs); break; case FORM_UPPER: nargs = 1; STRING_FIXED_ARGS("@UPPER",nargs); break; case FORM_LOWER: nargs = 1; STRING_FIXED_ARGS("@LOWER",nargs); break; case FORM_PROPER: nargs = 1; STRING_FIXED_ARGS("@PROPER",nargs); break; case FORM_VLOOKUP: nargs = 3; STRING_FIXED_ARGS("@VLOOKUP",nargs); break; case FORM_HLOOKUP: nargs = 3; STRING_FIXED_ARGS("@HLOOKUP",nargs); break; case FORM_AND: nargs = 2; STRING_FIXED_ARGS("@AND",nargs); break; case FORM_OR: nargs = 2; STRING_FIXED_ARGS("@OR",nargs); break; /* Operators with variable numbers of arguments */ case FORM_IF: STRING_VAR_NUM_ARGS("@IF"); /* This is really a complex macro */ break; case FORM_SUM: STRING_VAR_NUM_ARGS("@SUM"); /* This is really a complex macro */ break; case FORM_SUMIF: STRING_VAR_NUM_ARGS("@SUMIF"); /* This is really a complex macro */ break; case FORM_AVG: STRING_VAR_NUM_ARGS("@AVERAGE");/*This is really a complex macro */ break; case FORM_COUNT: STRING_VAR_NUM_ARGS("@COUNT"); /* This is really a complex macro */ break; case FORM_MIN: STRING_VAR_NUM_ARGS("@MIN"); /* This is really a complex macro */ break; case FORM_MAX: STRING_VAR_NUM_ARGS("@MAX"); /* This is really a complex macro */ break; default: top->st_val = "UNKNOWN"; return(top); internal_error(); } } } char *Sheet::formula_string(Cell *cell) { Stack_elem *result; static string st; switch(cell->type) { case CODE_FORMULA: result = formula(cell); return(result->st_val); case CODE_NUMBER: sprintf(st,"%g",cell->value); return(st); case CODE_LABEL: return(cell->label); } } /* $Log: sheet_calc.cc,v $ * Revision 1.7 1998/08/06 21:08:50 aml * Released alpha version of Abacus. * // Revision 1.6 1997/03/27 10:00:47 aml // Started implementing graphs. // Fixed bug in tkCanvasPs.c // Created bindings for composite characters in Portuguese. // * Revision 1.5 1997/02/10 15:11:09 aml * Print settings now are saved to file. * Fixed buggy error message when loading sheets. * * Revision 1.4 1997/01/07 01:07:50 aml * Error propagation for formulas fixed. * Edit operations in place. * * Revision 1.3 1997/01/02 16:15:38 aml * Fixed unsufficient range of colunm width. * First cut of vlookup and hlookup functions. * Fixed bug in display routines. * * Revision 1.2 1996/12/11 21:40:09 aml * Sumif implemented. * Diverse time functions implemented. * Fixed needtoscroll2 to avoid out of control scroll. * * Revision 1.1 1996/10/06 19:21:53 aml * Initial revision * */