Commit 25bec6fa authored by Michael Meeks's avatar Michael Meeks

Many fixes in ms-formula.c, proper bracketing / precedence from the reverse

Polish.
parent 5ba230e2
......@@ -15,19 +15,22 @@
#include "utils.h"
#include "ms-formula.h"
#define NO_PRECEDENCE 256
/**
* Various bits of data for operators
* see S59E2B.HTM for formula_ptg values
* formula PTG, prefix, middle, suffix, precedence
**/
FORMULA_OP_DATA formula_op_data[] = {
{ 0x03, 0, "+", 0, 1 },
{ 0x04, 0, "-", 0, 1 },
{ 0x05, 0, "*", 0, 3 },
{ 0x06, 0, "/", 0, 2 }
{ 0x03, 0, "+", 0, 8 },
{ 0x04, 0, "-", 0, 8 },
{ 0x05, 0, "*", 0, 32 },
{ 0x06, 0, "/", 0, 16 }
} ;
#define FORMULA_OP_DATA_LEN (sizeof(formula_op_data)/sizeof(FORMULA_OP_DATA))
/* FIXME: the standard function indexes need to be found from xlcall.h */
/**
* Various bits of data for functions
* function index, prefix, middle, suffix, multiple args?, precedence
......@@ -56,7 +59,7 @@ static CellRef *getRefV7(BYTE col, WORD gbitrw, int curcol, int currow)
cr->row-= currow ;
if (cr->col_relative)
cr->col-= curcol ;
printf ("7Out : %d, %d at %d, %d\n", cr->col, cr->row, curcol, currow) ;
/* printf ("7Out : %d, %d at %d, %d\n", cr->col, cr->row, curcol, currow) ; */
return cr ;
}
/**
......@@ -74,10 +77,148 @@ static CellRef *getRefV8(WORD row, WORD gbitcl, int curcol, int currow)
cr->row-= currow ;
if (cr->col_relative)
cr->col-= curcol ;
printf ("8Out : %d, %d at %d, %d\n", cr->col, cr->row, curcol, currow) ;
/* printf ("8Out : %d, %d at %d, %d\n", cr->col, cr->row, curcol, currow) ; */
return cr ;
}
typedef struct _PARSE_DATA
{
char *name ;
int precedence ;
} PARSE_DATA ;
static PARSE_DATA *parse_data_new (char *buffer, int precedence)
{
PARSE_DATA *ans = (PARSE_DATA *)malloc(sizeof(PARSE_DATA)) ;
ans->name = buffer ;
ans->precedence = precedence ;
return ans ;
}
static void parse_data_free (PARSE_DATA *ptr)
{
if (ptr->name)
free (ptr->name) ;
free (ptr) ;
}
typedef struct _PARSE_LIST
{
GList *data ;
int length ;
} PARSE_LIST ;
static PARSE_LIST *parse_list_new ()
{
PARSE_LIST *ans = (PARSE_LIST *)malloc (sizeof(PARSE_LIST)) ;
ans->data = 0 ;
ans->length = 0 ;
return ans ;
}
static void parse_list_push (PARSE_LIST *list, PARSE_DATA *pd)
{
list->data = g_list_append (list->data, pd) ;
list->length++ ;
}
static void parse_list_push_raw (PARSE_LIST *list, char *buffer, int precedence)
{
parse_list_push(list, parse_data_new (buffer, precedence)) ;
}
static PARSE_DATA *parse_list_pop (PARSE_LIST *list)
{
GList *tmp ;
PARSE_DATA *ans ;
tmp = g_list_last (list->data) ;
if (tmp == 0)
printf ("Warning not enough arguments on stack\n") ;
list->data = g_list_remove_link (list->data, tmp) ;
ans = tmp->data ;
g_list_free (tmp) ;
list->length-- ;
return ans ;
}
static void parse_list_free (PARSE_LIST *list)
{
while (list->data)
parse_data_free (parse_list_pop(list)) ;
}
static PARSE_DATA *parse_list_pop_front (PARSE_LIST *list)
{
PARSE_DATA *ans ;
GList *tmp ;
tmp = g_list_first (list->data) ;
if (tmp == 0)
printf ("Warning not enough arguments on stack\n") ;
list->data = g_list_remove_link (list->data, tmp) ;
ans = tmp->data ;
g_list_free (tmp) ;
list->length-- ;
return ans ;
}
/**
* This pops a load of arguments off the stack,
* comman delimits them sticking result in 'into'
* frees the stack space.
**/
static void parse_list_comma_delimit_n (PARSE_LIST *list, char *into, int n)
{
char *buffer = into ;
int lp, start ;
GList *ptr ;
start = list->length - n ;
ptr = g_list_nth (list->data, start) ;
for (lp=0;lp<n;lp++)
{
char *str = &buffer[strlen(buffer)] ;
PARSE_DATA *dat = ptr->data ;
char *appnd = dat->name ;
ptr = ptr->next ;
sprintf (str, "%s%c", appnd, ptr?',':' ') ;
}
for (lp=0;lp<n;lp++)
{
PARSE_DATA *dat = parse_list_pop (list) ;
parse_data_free (dat) ;
}
}
/**
* Prepends an '=' and handles nasty cases
**/
static char *parse_list_to_equation (PARSE_LIST *list)
{
if (list->length > 0 && list->data)
{
PARSE_DATA *pd ;
char *formula ;
pd = g_list_first (list->data)->data ;
if (!pd)
return "Stack too short" ;
if (!pd->name)
return "No data in stack entry" ;
formula = (char *)malloc(strlen(pd->name)+2) ;
if (!formula)
return "Out of memory" ;
strcpy (formula, "=") ;
strcat (formula, pd->name) ;
printf ("The answer is : '%s'\n", formula) ;
return formula ;
}
else
return "Nothing on stack" ;
}
/**
* Parse that RP Excel formula, see S59E2B.HTM
* Sadly has to be parsed to text and back !
......@@ -87,8 +228,9 @@ void ms_excel_parse_formula (MS_EXCEL_SHEET *sheet, BIFF_QUERY *q)
Cell *cell ;
BYTE *cur ;
int length, fn_row, fn_col ;
GList *stack = 0 ; /* A whole load of Text arguments */
PARSE_LIST *stack ;
int error = 0 ;
char *ans ;
g_assert (q->ls_op == BIFF_FORMULA) ;
fn_col = BIFF_GETCOL(q) ;
......@@ -101,6 +243,7 @@ void ms_excel_parse_formula (MS_EXCEL_SHEET *sheet, BIFF_QUERY *q)
/* NB. the effective '-1' here is so that the offsets and lengths
are identical to those in the documentation */
cur = q->data + 23 ;
stack = parse_list_new() ;
while (length>0 && !error)
{
int ptg_length = 0 ;
......@@ -125,7 +268,7 @@ void ms_excel_parse_formula (MS_EXCEL_SHEET *sheet, BIFF_QUERY *q)
ptg_length = 3 ;
}
buffer = cellref_name (ref, fn_col, fn_row) ;
stack = g_list_append (stack, strdup (buffer)) ;
parse_list_push_raw(stack, strdup (buffer), NO_PRECEDENCE) ;
printf ("%s\n", buffer) ;
free (ref) ;
}
......@@ -149,13 +292,12 @@ void ms_excel_parse_formula (MS_EXCEL_SHEET *sheet, BIFF_QUERY *q)
strcpy (buffer, cellref_name (first, fn_col, fn_row)) ;
strcat (buffer, ":") ;
strcat (buffer, cellref_name (last, fn_col, fn_row)) ;
stack = g_list_append (stack, strdup(buffer)) ;
parse_list_push_raw(stack, strdup (buffer), NO_PRECEDENCE) ;
printf ("%s\n", buffer) ;
free (first) ;
free (last) ;
}
break ;
/* FIXME: the standard function indexes need to be found from xlcall.h */
case FORMULA_PTG_FUNC_VAR:
{
int numargs = (BIFF_GETBYTE( cur ) & 0x7f) ;
......@@ -166,51 +308,25 @@ void ms_excel_parse_formula (MS_EXCEL_SHEET *sheet, BIFF_QUERY *q)
int lp ;
char buffer[4096] ; /* Nasty ! */
printf ("Found formula %d with %d args\n", iftab, numargs) ;
/* Whack those arguments on an arg list */
for (lp=0;lp<numargs;lp++)
{
tmp = g_list_last (stack) ;
if (tmp == 0)
printf ("Warning not enough arguments on stack for fn %d: %d\n", iftab, numargs), error=1 ;
stack = g_list_remove_link (stack, tmp) ;
args = g_list_append(args, tmp->data) ;
g_list_free (tmp) ;
}
printf ("Search %d records\n", FORMULA_FUNC_DATA_LEN) ;
for (lp=0;lp<FORMULA_FUNC_DATA_LEN;lp++)
{
if (formula_func_data[lp].function_idx == iftab && formula_func_data[lp].multi_arg)
{
FORMULA_FUNC_DATA *fd = &formula_func_data[lp] ;
const FORMULA_FUNC_DATA *fd = &formula_func_data[lp] ;
GList *ptr ;
strcpy (buffer, (fd->prefix)?fd->prefix:"") ;
ptr = g_list_first (args) ;
while (ptr)
{
char *str = &buffer[strlen(buffer)] ;
char *appnd = ptr->data ;
ptr = ptr->next ;
sprintf (str, "%s%c", appnd, ptr?',':' ') ;
}
parse_list_comma_delimit_n (stack, &buffer[strlen(buffer)], numargs) ;
strcat (buffer, fd->suffix?fd->suffix:"") ;
stack = g_list_append (stack, strdup(buffer)) ;
parse_list_push_raw(stack, strdup (buffer), fd->precedence) ;
break ;
}
}
if (lp==FORMULA_FUNC_DATA_LEN)
printf ("FIXME, unimplemented vararg fn %d, with %d args\n", iftab, numargs), error=1 ;
/* Free args : should be unused by now */
tmp = g_list_first (args) ;
while (tmp)
{
free (tmp->data) ;
tmp=tmp->next ;
}
g_list_free (args) ;
ptg_length = 3 ;
}
break ;
......@@ -243,32 +359,37 @@ void ms_excel_parse_formula (MS_EXCEL_SHEET *sheet, BIFF_QUERY *q)
{
if (ptgbase == formula_op_data[lp].formula_ptg)
{
char *arg2, *arg1 ;
PARSE_DATA *arg1, *arg2 ;
GList *tmp ;
char buffer[2048] ;
FORMULA_OP_DATA *fd = &formula_op_data[lp] ;
int bracket_arg2 ;
int bracket_arg1 ;
tmp = g_list_last (stack) ;
if (tmp == 0)
printf ("Warning not enough arguments on stack for op %d", ptgbase), error=1 ;
stack = g_list_remove_link (stack, tmp) ;
arg2 = tmp->data ;
g_list_free (tmp) ;
tmp = g_list_last (stack) ;
if (tmp == 0)
printf ("Warning not enough arguments on stack for op %d", ptgbase), error=1 ;
stack = g_list_remove_link (stack, tmp) ;
arg1 = tmp->data ;
g_list_free (tmp) ;
arg2 = parse_list_pop (stack) ;
arg1 = parse_list_pop (stack) ;
bracket_arg2 = arg2->precedence<fd->precedence ;
bracket_arg1 = arg1->precedence<fd->precedence ;
strcpy (buffer, fd->prefix?fd->prefix:"") ;
strcat (buffer, arg1) ;
if (bracket_arg1)
strcat (buffer, "(") ;
strcat (buffer, arg1->name) ;
if (bracket_arg1)
strcat (buffer, ")") ;
strcat (buffer, fd->mid?fd->mid:"") ;
strcat (buffer, arg2) ;
if (bracket_arg2)
strcat (buffer, "(") ;
strcat (buffer, arg2->name) ;
if (bracket_arg2)
strcat (buffer, ")") ;
strcat (buffer, fd->suffix?fd->suffix:"") ;
printf ("Op : '%s'\n", buffer) ;
stack = g_list_append (stack, strdup(buffer)) ;
parse_list_push_raw(stack, strdup (buffer), fd->precedence) ;
parse_data_free (arg1) ;
parse_data_free (arg2) ;
break ;
}
}
......@@ -285,23 +406,18 @@ void ms_excel_parse_formula (MS_EXCEL_SHEET *sheet, BIFF_QUERY *q)
return ;
}
printf ("--------- Found valid formula !---------\n") ;
cell = sheet_cell_fetch (sheet->gnum_sheet, BIFF_GETCOL(q), BIFF_GETROW(q)) ;
if (stack)
ans = parse_list_to_equation(stack) ;
if (ans)
{
char *init = g_list_first (stack)->data ;
char *ptr = (char *)malloc(strlen(init)+2) ;
if (!init)
{
ms_excel_sheet_insert (sheet, BIFF_GETXF(q), BIFF_GETCOL(q), BIFF_GETROW(q), "Bad formula") ;
return ;
}
strcpy (ptr, "=") ;
strcat (ptr, init) ;
printf ("The answer is : '%s'\n", ptr) ;
cell = sheet_cell_fetch (sheet->gnum_sheet, BIFF_GETCOL(q), BIFF_GETROW(q)) ;
/* FIXME: this _should_ be a set_formula with the formula, and a
set_text_simple with the current value */
cell_set_text(cell, ptr) ;
/* Set up cell stuff */
cell_set_text (cell, ans) ;
ms_excel_set_cell_xf (sheet, cell, BIFF_GETXF(q)) ;
}
parse_list_free (stack) ;
}
......@@ -29,7 +29,7 @@ typedef struct _FORMULA_OP_DATA
char *prefix ;
char *mid ;
char *suffix ;
int precedance ;
int precedence ;
} FORMULA_OP_DATA ;
typedef struct _FORMULA_FUNC_DATA
......@@ -39,7 +39,7 @@ typedef struct _FORMULA_FUNC_DATA
char *mid ;
char *suffix ;
int multi_arg ;
int precedance ;
int precedence ;
} FORMULA_FUNC_DATA ;
#endif
......@@ -15,19 +15,22 @@
#include "utils.h"
#include "ms-formula.h"
#define NO_PRECEDENCE 256
/**
* Various bits of data for operators
* see S59E2B.HTM for formula_ptg values
* formula PTG, prefix, middle, suffix, precedence
**/
FORMULA_OP_DATA formula_op_data[] = {
{ 0x03, 0, "+", 0, 1 },
{ 0x04, 0, "-", 0, 1 },
{ 0x05, 0, "*", 0, 3 },
{ 0x06, 0, "/", 0, 2 }
{ 0x03, 0, "+", 0, 8 },
{ 0x04, 0, "-", 0, 8 },
{ 0x05, 0, "*", 0, 32 },
{ 0x06, 0, "/", 0, 16 }
} ;
#define FORMULA_OP_DATA_LEN (sizeof(formula_op_data)/sizeof(FORMULA_OP_DATA))
/* FIXME: the standard function indexes need to be found from xlcall.h */
/**
* Various bits of data for functions
* function index, prefix, middle, suffix, multiple args?, precedence
......@@ -56,7 +59,7 @@ static CellRef *getRefV7(BYTE col, WORD gbitrw, int curcol, int currow)
cr->row-= currow ;
if (cr->col_relative)
cr->col-= curcol ;
printf ("7Out : %d, %d at %d, %d\n", cr->col, cr->row, curcol, currow) ;
/* printf ("7Out : %d, %d at %d, %d\n", cr->col, cr->row, curcol, currow) ; */
return cr ;
}
/**
......@@ -74,10 +77,148 @@ static CellRef *getRefV8(WORD row, WORD gbitcl, int curcol, int currow)
cr->row-= currow ;
if (cr->col_relative)
cr->col-= curcol ;
printf ("8Out : %d, %d at %d, %d\n", cr->col, cr->row, curcol, currow) ;
/* printf ("8Out : %d, %d at %d, %d\n", cr->col, cr->row, curcol, currow) ; */
return cr ;
}
typedef struct _PARSE_DATA
{
char *name ;
int precedence ;
} PARSE_DATA ;
static PARSE_DATA *parse_data_new (char *buffer, int precedence)
{
PARSE_DATA *ans = (PARSE_DATA *)malloc(sizeof(PARSE_DATA)) ;
ans->name = buffer ;
ans->precedence = precedence ;
return ans ;
}
static void parse_data_free (PARSE_DATA *ptr)
{
if (ptr->name)
free (ptr->name) ;
free (ptr) ;
}
typedef struct _PARSE_LIST
{
GList *data ;
int length ;
} PARSE_LIST ;
static PARSE_LIST *parse_list_new ()
{
PARSE_LIST *ans = (PARSE_LIST *)malloc (sizeof(PARSE_LIST)) ;
ans->data = 0 ;
ans->length = 0 ;
return ans ;
}
static void parse_list_push (PARSE_LIST *list, PARSE_DATA *pd)
{
list->data = g_list_append (list->data, pd) ;
list->length++ ;
}
static void parse_list_push_raw (PARSE_LIST *list, char *buffer, int precedence)
{
parse_list_push(list, parse_data_new (buffer, precedence)) ;
}
static PARSE_DATA *parse_list_pop (PARSE_LIST *list)
{
GList *tmp ;
PARSE_DATA *ans ;
tmp = g_list_last (list->data) ;
if (tmp == 0)
printf ("Warning not enough arguments on stack\n") ;
list->data = g_list_remove_link (list->data, tmp) ;
ans = tmp->data ;
g_list_free (tmp) ;
list->length-- ;
return ans ;
}
static void parse_list_free (PARSE_LIST *list)
{
while (list->data)
parse_data_free (parse_list_pop(list)) ;
}
static PARSE_DATA *parse_list_pop_front (PARSE_LIST *list)
{
PARSE_DATA *ans ;
GList *tmp ;
tmp = g_list_first (list->data) ;
if (tmp == 0)
printf ("Warning not enough arguments on stack\n") ;
list->data = g_list_remove_link (list->data, tmp) ;
ans = tmp->data ;
g_list_free (tmp) ;
list->length-- ;
return ans ;
}
/**
* This pops a load of arguments off the stack,
* comman delimits them sticking result in 'into'
* frees the stack space.
**/
static void parse_list_comma_delimit_n (PARSE_LIST *list, char *into, int n)
{
char *buffer = into ;
int lp, start ;
GList *ptr ;
start = list->length - n ;
ptr = g_list_nth (list->data, start) ;
for (lp=0;lp<n;lp++)
{
char *str = &buffer[strlen(buffer)] ;
PARSE_DATA *dat = ptr->data ;
char *appnd = dat->name ;
ptr = ptr->next ;
sprintf (str, "%s%c", appnd, ptr?',':' ') ;
}
for (lp=0;lp<n;lp++)
{
PARSE_DATA *dat = parse_list_pop (list) ;
parse_data_free (dat) ;
}
}
/**
* Prepends an '=' and handles nasty cases
**/
static char *parse_list_to_equation (PARSE_LIST *list)
{
if (list->length > 0 && list->data)
{
PARSE_DATA *pd ;
char *formula ;
pd = g_list_first (list->data)->data ;
if (!pd)
return "Stack too short" ;
if (!pd->name)
return "No data in stack entry" ;
formula = (char *)malloc(strlen(pd->name)+2) ;
if (!formula)
return "Out of memory" ;
strcpy (formula, "=") ;
strcat (formula, pd->name) ;
printf ("The answer is : '%s'\n", formula) ;
return formula ;
}
else
return "Nothing on stack" ;
}
/**
* Parse that RP Excel formula, see S59E2B.HTM
* Sadly has to be parsed to text and back !
......@@ -87,8 +228,9 @@ void ms_excel_parse_formula (MS_EXCEL_SHEET *sheet, BIFF_QUERY *q)
Cell *cell ;
BYTE *cur ;
int length, fn_row, fn_col ;
GList *stack = 0 ; /* A whole load of Text arguments */
PARSE_LIST *stack ;
int error = 0 ;
char *ans ;
g_assert (q->ls_op == BIFF_FORMULA) ;
fn_col = BIFF_GETCOL(q) ;
......@@ -101,6 +243,7 @@ void ms_excel_parse_formula (MS_EXCEL_SHEET *sheet, BIFF_QUERY *q)
/* NB. the effective '-1' here is so that the offsets and lengths
are identical to those in the documentation */
cur = q->data + 23 ;
stack = parse_list_new() ;
while (length>0 && !error)
{
int ptg_length = 0 ;
......@@ -125,7 +268,7 @@ void ms_excel_parse_formula (MS_EXCEL_SHEET *sheet, BIFF_QUERY *q)
ptg_length = 3 ;
}
buffer = cellref_name (ref, fn_col, fn_row) ;
stack = g_list_append (stack, strdup (buffer)) ;
parse_list_push_raw(stack, strdup (buffer), NO_PRECEDENCE) ;
printf ("%s\n", buffer) ;
free (ref) ;
}
......@@ -149,13 +292,12 @@ void ms_excel_parse_formula (MS_EXCEL_SHEET *sheet, BIFF_QUERY *q)
strcpy (buffer, cellref_name (first, fn_col, fn_row)) ;
strcat (buffer, ":") ;
strcat (buffer, cellref_name (last, fn_col, fn_row)) ;
stack = g_list_append (stack, strdup(buffer)) ;
parse_list_push_raw(stack, strdup (buffer), NO_PRECEDENCE) ;
printf ("%s\n", buffer) ;
free (first) ;
free (last) ;
}
break ;
/* FIXME: the standard function indexes need to be found from xlcall.h */
case FORMULA_PTG_FUNC_VAR:
{
int numargs = (BIFF_GETBYTE( cur ) & 0x7f) ;
......@@ -166,51 +308,25 @@ void ms_excel_parse_formula (MS_EXCEL_SHEET *sheet, BIFF_QUERY *q)
int lp ;
char buffer[4096] ; /* Nasty ! */
printf ("Found formula %d with %d args\n", iftab, numargs) ;
/* Whack those arguments on an arg list */
for (lp=0;lp<numargs;lp++)
{
tmp = g_list_last (stack) ;
if (tmp == 0)
printf ("Warning not enough arguments on stack for fn %d: %d\n", iftab, numargs), error=1 ;
stack = g_list_remove_link (stack, tmp) ;
args = g_list_append(args, tmp->data) ;
g_list_free (tmp) ;
}
printf ("Search %d records\n", FORMULA_FUNC_DATA_LEN) ;
for (lp=0;lp<FORMULA_FUNC_DATA_LEN;lp++)
{
if (formula_func_data[lp].function_idx == iftab && formula_func_data[lp].multi_arg)
{
FORMULA_FUNC_DATA *fd = &formula_func_data[lp] ;
const FORMULA_FUNC_DATA *fd = &formula_func_data[lp] ;
GList *ptr ;