Commit c5c51af8 authored by Morten Welinder's avatar Morten Welinder

Arrays: code cleanup.

This moves us closer to making array corners and elements part of GnmExprTop
as opposed to the current state where they are in GnmExpr, but are only
allowed at top level.

Specifically, with this commit, we now query attributes of array
corners and elements mostly with a texpr.  Mostly.

A nice side effect is that many expr-impl.h includes can be removed.  We
have too many of those.
parent 53efa64b
2017-12-30 Morten Welinder <terra@gnome.org>
* src/expr.c (gnm_expr_top_get_array_corner): Remove. All callers
changed.
(gnm_expr_top_get_array_size, gnm_expr_top_get_array_value)
(gnm_expr_top_get_array_expr): New functions.
* src/cell.c (gnm_cell_is_array_corner): Remove. All callers
changed.
2017-12-29 Morten Welinder <terra@gnome.org>
* src/ssdiff.c (diff_names): Fix checks for inserted or removed
......
......@@ -3,6 +3,7 @@ Gnumeric 1.12.39
Morten:
* Fix ssdiff problem. [#792038]
* Fix ISREF.
* Some internal array formula code cleanups.
--------------------------------------------------------------------------
Gnumeric 1.12.38
......
......@@ -3507,7 +3507,6 @@ excel_write_FORMULA (ExcelWriteState *ewb, ExcelWriteSheet *esheet, GnmCell cons
gint col, row;
GnmValue *v;
GnmExprTop const *texpr;
GnmExprArrayCorner const *corner;
g_return_if_fail (ewb);
g_return_if_fail (cell);
......@@ -3575,16 +3574,19 @@ excel_write_FORMULA (ExcelWriteState *ewb, ExcelWriteSheet *esheet, GnmCell cons
ms_biff_put_commit (ewb->bp);
corner = gnm_expr_top_get_array_corner (texpr);
if (corner) {
if (gnm_expr_top_is_array_corner (texpr)) {
GnmCellPos c_in, r_in;
if (gnm_expr_is_data_table (corner->expr, &c_in, &r_in)) {
int cols, rows;
gnm_expr_top_get_array_size (texpr, &cols, &rows);
if (gnm_expr_is_data_table (gnm_expr_top_get_array_expr (texpr), &c_in, &r_in)) {
guint16 flags = 0;
guint8 *data = ms_biff_put_len_next (ewb->bp, BIFF_TABLE_v2, 16);
GSF_LE_SET_GUINT16 (data + 0, cell->pos.row);
GSF_LE_SET_GUINT16 (data + 2, cell->pos.row + corner->rows-1);
GSF_LE_SET_GUINT16 (data + 2, cell->pos.row + rows-1);
GSF_LE_SET_GUINT16 (data + 4, cell->pos.col);
GSF_LE_SET_GUINT16 (data + 5, cell->pos.col + corner->cols-1);
GSF_LE_SET_GUINT16 (data + 5, cell->pos.col + cols-1);
if ((c_in.col != 0 || c_in.row != 0) &&
(r_in.col != 0 || r_in.row != 0)) {
......@@ -3614,14 +3616,14 @@ excel_write_FORMULA (ExcelWriteState *ewb, ExcelWriteSheet *esheet, GnmCell cons
} else {
ms_biff_put_var_next (ewb->bp, BIFF_ARRAY_v2);
GSF_LE_SET_GUINT16 (data+0, cell->pos.row);
GSF_LE_SET_GUINT16 (data+2, cell->pos.row + corner->rows-1);
GSF_LE_SET_GUINT16 (data+2, cell->pos.row + rows-1);
GSF_LE_SET_GUINT16 (data+4, cell->pos.col);
GSF_LE_SET_GUINT16 (data+5, cell->pos.col + corner->cols-1);
GSF_LE_SET_GUINT16 (data+5, cell->pos.col + cols-1);
GSF_LE_SET_GUINT16 (data+6, 0x0); /* alwaysCalc & calcOnLoad */
GSF_LE_SET_GUINT32 (data+8, 0);
GSF_LE_SET_GUINT16 (data+12, 0); /* bogus len, fill in later */
ms_biff_put_var_write (ewb->bp, data, 14);
len = excel_write_array_formula (ewb, corner,
len = excel_write_array_formula (ewb, gnm_expr_top_get_array_expr (texpr),
esheet->gnum_sheet, col, row);
ms_biff_put_var_seekto (ewb->bp, 12);
......
......@@ -871,10 +871,11 @@ write_node (PolishData *pd, GnmExpr const *expr, int paren_level,
ptg = FORMULA_PTG_EXPR;
if (pd->sheet != NULL) {
GnmExprArrayCorner const *corner = gnm_cell_is_array_corner (
sheet_cell_get (pd->sheet, pd->col - x, pd->row - y));
if (NULL != corner &&
gnm_expr_is_data_table (corner->expr, NULL, NULL))
GnmCell const *ccell =
sheet_cell_get (pd->sheet, pd->col - x, pd->row - y);
if (ccell &&
gnm_expr_top_is_array_corner (ccell->base.texpr) &&
gnm_expr_is_data_table (gnm_expr_top_get_array_expr (ccell->base.texpr), NULL, NULL))
ptg = FORMULA_PTG_TBL;
}
......@@ -980,7 +981,7 @@ write_arrays (PolishData *pd)
guint32
excel_write_array_formula (ExcelWriteState *ewb,
GnmExprArrayCorner const *array,
GnmExpr const *array_expr,
Sheet *sheet, int fn_col, int fn_row)
{
PolishData pd;
......@@ -988,7 +989,7 @@ excel_write_array_formula (ExcelWriteState *ewb,
guint32 len;
g_return_val_if_fail (ewb, 0);
g_return_val_if_fail (array, 0);
g_return_val_if_fail (array_expr, 0);
pd.col = fn_col;
pd.row = fn_row;
......@@ -1000,7 +1001,7 @@ excel_write_array_formula (ExcelWriteState *ewb,
pd.allow_sheetless_ref = TRUE;
start = ewb->bp->curpos;
write_node (&pd, array->expr, 0, XL_ROOT);
write_node (&pd, array_expr, 0, XL_ROOT);
len = ewb->bp->curpos - start;
write_arrays (&pd);
......
......@@ -29,7 +29,7 @@ guint32 excel_write_formula (ExcelWriteState *ewb,
Sheet *sheet, int fn_col, int fn_row,
ExcelFuncContext context);
guint32 excel_write_array_formula (ExcelWriteState *ewb,
GnmExprArrayCorner const *array,
GnmExpr const *array_expr,
Sheet *sheet, int fn_col, int fn_row);
void excel_write_prep_expressions (ExcelWriteState *ewb);
......
......@@ -32,12 +32,12 @@
#include "workbook.h"
#include "sheet.h"
#include "func.h"
#include <expr-impl.h>
#include "gnm-format.h"
#include <goffice/goffice.h>
#include <glib-object.h>
#include <string.h>
#include <expr.h>
#include <expr-impl.h>
#include <value.h>
typedef struct {
......@@ -170,7 +170,8 @@ static void
xlsx_func_map_out (GnmConventionsOut *out, GnmExprFunction const *func)
{
XLSXExprConventions const *xconv = (XLSXExprConventions const *)(out->convs);
char const *name = gnm_func_get_name (func->func, FALSE);
GnmFunc const *gfunc = gnm_expr_get_func_def ((GnmExpr *)func);
char const *name = gnm_func_get_name (gfunc, FALSE);
gboolean (*handler) (GnmConventionsOut *out, GnmExprFunction const *func);
handler = g_hash_table_lookup (xconv->xlfn_handler_map, name);
......@@ -182,7 +183,7 @@ xlsx_func_map_out (GnmConventionsOut *out, GnmExprFunction const *func)
if (new_name == NULL) {
char *new_u_name;
new_u_name = g_ascii_strup (name, -1);
if (func->func->impl_status ==
if (gfunc->impl_status ==
GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC)
g_string_append (target, "_xlfngnumeric.");
/* LO & friends use _xlfnodf */
......
......@@ -41,7 +41,6 @@
#include "value.h"
#include "cell.h"
#include "expr.h"
#include "expr-impl.h"
#include "func.h"
#include "style-color.h"
#include "validation.h"
......@@ -1540,7 +1539,6 @@ xlsx_write_cells (XLSXWriteState *state, GsfXMLOut *xml,
char *content;
GnmParsePos pp;
GnmExprTop const *texpr;
GnmExprArrayCorner const *array;
char *cheesy_span = g_strdup_printf ("%d:%d", extent->start.col+1, extent->end.col+1);
Sheet *sheet = (Sheet *)state->sheet;
GPtrArray *all_cells = sheet_cells (sheet, extent);
......@@ -1693,11 +1691,13 @@ xlsx_write_cells (XLSXWriteState *state, GsfXMLOut *xml,
if (!gnm_expr_top_is_array_elem (texpr, NULL, NULL)) {
gsf_xml_out_start_element (xml, "f");
array = gnm_expr_top_get_array_corner (texpr);
if (NULL != array) {
if (gnm_expr_top_is_array_corner (texpr)) {
GnmRange r;
int cols, rows;
gnm_expr_top_get_array_size (texpr, &cols, &rows);
range_init_cellpos_size (&r, &cell->pos,
array->cols, array->rows);
cols, rows);
gsf_xml_out_add_cstr_unchecked (xml, "t", "array");
xlsx_add_range (xml, "ref", &r);
}
......
......@@ -40,7 +40,6 @@
#include <workbook.h>
#include <sheet.h>
#include <cell.h>
#include <expr-impl.h>
#include <string.h>
#ifdef WIN32
......@@ -490,17 +489,17 @@ genericXLLFunction (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
GnmValue *g = NULL;
guint i,m;
const XLLFunctionInfo*info = NULL;
GnmFunc const *gfunc = gnm_eval_info_get_func (ei);
g_assert (NULL != xll_function_info_map);
info=g_tree_lookup (xll_function_info_map,ei->func_call->func->name);
info=g_tree_lookup (xll_function_info_map, gfunc->name);
g_assert (NULL != info);
m=ei->func_call->argc;
if ( m > MAXIMUM_NUMBER_OF_EXCEL_FUNCTION_ARGUMENTS )
m = MAXIMUM_NUMBER_OF_EXCEL_FUNCTION_ARGUMENTS;
m = gnm_eval_info_get_arg_count (ei);
m = MAX (m, MAXIMUM_NUMBER_OF_EXCEL_FUNCTION_ARGUMENTS);
for (i = 0; i < m; ++i)
copy_construct_xloper_from_gnm_value (x+i,argv[i],ei);
m = info->number_of_arguments;
if ( m > MAXIMUM_NUMBER_OF_EXCEL_FUNCTION_ARGUMENTS )
m = MAXIMUM_NUMBER_OF_EXCEL_FUNCTION_ARGUMENTS;
m = MAX (m, MAXIMUM_NUMBER_OF_EXCEL_FUNCTION_ARGUMENTS);
for (; i < m; ++i)
x[i].xltype=xltypeMissing;
func = (XLLFunctionWithVarArgs)info->xll_function;
......
......@@ -1470,7 +1470,7 @@ gnumeric_column (GnmFuncEvalInfo *ei, GnmValue const * const *args)
if (ref == NULL) {
col = ei->pos->eval.col + 1; /* user visible counts from 0 */
if (eval_pos_is_array_context (ei->pos))
width = ei->pos->array->cols;
gnm_expr_top_get_array_size (ei->pos->array_texpr, &width, NULL);
else
return value_new_int (col);
} else if (VALUE_IS_CELLRANGE (ref)) {
......@@ -1620,8 +1620,8 @@ gnumeric_row (GnmFuncEvalInfo *ei, GnmValue const * const *args)
if (ref == NULL) {
row = ei->pos->eval.row + 1; /* user visible counts from 0 */
if (ei->pos->array != NULL)
n = ei->pos->array->rows;
if (eval_pos_is_array_context (ei->pos))
gnm_expr_top_get_array_size (ei->pos->array_texpr, NULL, &n);
else
return value_new_int (row);
} else if (VALUE_IS_CELLRANGE (ref)) {
......
......@@ -35,7 +35,6 @@
#include <goffice/goffice.h>
#include <gnm-plugin.h>
#include "expr.h"
#include "expr-impl.h"
#include "gutils.h"
#include "func.h"
#include "cell.h"
......@@ -144,10 +143,8 @@ func_marshal_func (GnmFuncEvalInfo *ei, GnmValue *argv[])
int i, min, max;
g_return_val_if_fail (ei != NULL, NULL);
g_return_val_if_fail (ei->func_call != NULL, NULL);
g_return_val_if_fail (ei->func_call->func != NULL, NULL);
fndef = ei->func_call->func;
fndef = gnm_eval_info_get_func (ei);
function_def_count_args (fndef, &min, &max);
function = (SCM) gnm_func_get_user_data (fndef);
......
......@@ -3505,16 +3505,16 @@ odf_write_cell (GnmOOExport *state, GnmCell *cell, GnmRange const *merge_range,
GnmParsePos pp;
if (gnm_cell_is_array (cell)) {
GnmExprArrayCorner const *ac;
if (gnm_expr_top_is_array_corner (cell->base.texpr)) {
int cols, rows;
ac = gnm_expr_top_get_array_corner (cell->base.texpr);
if (ac != NULL) {
gnm_expr_top_get_array_size (cell->base.texpr, &cols, &rows);
gsf_xml_out_add_uint (state->xml,
TABLE "number-matrix-columns-spanned",
(unsigned int)(ac->cols));
(unsigned int)cols);
gsf_xml_out_add_uint (state->xml,
TABLE "number-matrix-rows-spanned",
(unsigned int)(ac->rows));
(unsigned int)rows);
}
}
......
......@@ -27,7 +27,6 @@
#include "sheet.h"
#include "sheet-style.h"
#include "expr.h"
#include "expr-impl.h"
#include "value.h"
#include "cell.h"
#include "mstyle.h"
......@@ -119,7 +118,6 @@ cb_sylk_write_cell (GnmCellIter const *iter, SylkWriter *state)
{
GnmValue const *v;
GnmExprTop const *texpr;
GnmExprArrayCorner const *array;
if (iter->pp.eval.row != state->cur_row)
gsf_output_printf (state->output, "C;Y%d;X%d",
......@@ -129,7 +127,8 @@ cb_sylk_write_cell (GnmCellIter const *iter, SylkWriter *state)
gsf_output_printf (state->output, "C;X%d",
iter->pp.eval.col + 1);
if (NULL != (v = iter->cell->value)) {
v = iter->cell->value;
if (v) {
if (VALUE_IS_STRING (v)) {
gsf_output_write (state->output, 3, ";K\"");
sylk_write (state, v->v_str.val->str);
......@@ -143,11 +142,15 @@ cb_sylk_write_cell (GnmCellIter const *iter, SylkWriter *state)
} /* ignore the rest */
}
if (NULL != (texpr = iter->cell->base.texpr)) {
if (NULL != (array = gnm_expr_top_get_array_corner (texpr))) {
texpr = iter->cell->base.texpr;
if (texpr) {
if (gnm_expr_top_is_array_corner (texpr)) {
int cols, rows;
gnm_expr_top_get_array_size (texpr, &cols, &rows);
gsf_output_printf (state->output, ";R%d;C%d;M",
iter->pp.eval.row + array->rows,
iter->pp.eval.col + array->cols);
iter->pp.eval.row + rows,
iter->pp.eval.col + cols);
} else if (gnm_expr_top_is_array_elem (texpr, NULL, NULL)) {
gsf_output_write (state->output, 2, ";I");
texpr = NULL;
......
......@@ -15,7 +15,6 @@
#include "workbook.h"
#include "sheet.h"
#include "expr.h"
#include "expr-impl.h"
#include "rendered-value.h"
#include "value.h"
#include "style.h"
......@@ -461,8 +460,8 @@ gboolean
gnm_cell_array_bound (GnmCell const *cell, GnmRange *res)
{
GnmExprTop const *texpr;
GnmExprArrayCorner const *array;
int x, y;
int cols, rows;
if (NULL == cell || !gnm_cell_has_expr (cell))
return FALSE;
......@@ -479,24 +478,17 @@ gnm_cell_array_bound (GnmCell const *cell, GnmRange *res)
texpr = cell->base.texpr;
}
array = gnm_expr_top_get_array_corner (texpr);
if (!array)
if (!gnm_expr_top_is_array_corner (texpr))
return FALSE;
gnm_expr_top_get_array_size (texpr, &cols, &rows);
range_init (res, cell->pos.col, cell->pos.row,
cell->pos.col + array->cols - 1,
cell->pos.row + array->rows - 1);
cell->pos.col + cols - 1,
cell->pos.row + rows - 1);
return TRUE;
}
GnmExprArrayCorner const *
gnm_cell_is_array_corner (GnmCell const *cell)
{
return cell && gnm_cell_has_expr (cell)
? gnm_expr_top_get_array_corner (cell->base.texpr)
: NULL;
}
/**
* gnm_cell_is_array:
* @cell: #GnmCell const *
......@@ -520,15 +512,18 @@ gnm_cell_is_array (GnmCell const *cell)
gboolean
gnm_cell_is_nonsingleton_array (GnmCell const *cell)
{
GnmExprArrayCorner const *corner;
int cols, rows;
if ((cell == NULL) || !gnm_cell_has_expr (cell))
return FALSE;
if (gnm_expr_top_is_array_elem (cell->base.texpr, NULL, NULL))
return TRUE;
corner = gnm_expr_top_get_array_corner (cell->base.texpr);
return corner && (corner->cols > 1 || corner->rows > 1);
if (!gnm_expr_top_is_array_corner (cell->base.texpr))
return FALSE;
gnm_expr_top_get_array_size (cell->base.texpr, &cols, &rows);
return cols > 1 || rows > 1;
}
/***************************************************************************/
......@@ -1021,7 +1016,7 @@ cb_set_array_value (GnmCellIter const *iter, gpointer user)
void
gnm_cell_convert_expr_to_value (GnmCell *cell)
{
GnmExprArrayCorner const *array;
GnmExprTop const *texpr;
g_return_if_fail (cell != NULL);
g_return_if_fail (gnm_cell_has_expr (cell));
......@@ -1030,19 +1025,23 @@ gnm_cell_convert_expr_to_value (GnmCell *cell)
if (gnm_cell_expr_is_linked (cell))
dependent_unlink (GNM_CELL_TO_DEP (cell));
array = gnm_expr_top_get_array_corner (cell->base.texpr);
if (array) {
texpr = cell->base.texpr;
if (gnm_expr_top_is_array_corner (texpr)) {
int rows, cols;
gnm_expr_top_get_array_size (texpr, &cols, &rows);
sheet_foreach_cell_in_range (cell->base.sheet, CELL_ITER_ALL,
cell->pos.col, cell->pos.row,
cell->pos.col + array->cols - 1,
cell->pos.row + array->rows - 1,
cell->pos.col + cols - 1,
cell->pos.row + rows - 1,
cb_set_array_value,
array->value);
gnm_expr_top_get_array_value (texpr));
} else {
g_return_if_fail (!gnm_cell_is_array (cell));
}
gnm_expr_top_unref (cell->base.texpr);
gnm_expr_top_unref (texpr);
cell->base.texpr = NULL;
}
......
......@@ -46,8 +46,6 @@ gboolean gnm_cell_is_zero (GnmCell const *cell);
gboolean gnm_cell_is_array (GnmCell const *cell);
gboolean gnm_cell_is_nonsingleton_array (GnmCell const *cell);
GnmExprArrayCorner const *
gnm_cell_is_array_corner (GnmCell const *cell);
gboolean gnm_cell_array_bound (GnmCell const *cell, GnmRange *res);
/*
......
......@@ -32,7 +32,6 @@
#include "sheet-view.h"
#include "cell.h"
#include "expr.h"
#include "expr-impl.h"
#include "dependent.h"
#include "selection.h"
#include "parse-util.h"
......
......@@ -1140,9 +1140,8 @@ link_unlink_expr_dep (GnmEvalPos *ep, GnmExpr const *tree, gboolean qlink)
/* Non-corner cells depend on the corner */
GnmCellRef a;
GnmCellPos const *pos = dependent_pos (ep->dep);
/* We cannot support array expressions unless
* we have a position.
*/
// We cannot support array expressions unless we have
// a position.
g_return_val_if_fail (pos != NULL, DEPENDENT_NO_FLAG);
a.col_relative = a.row_relative = FALSE;
......@@ -1154,8 +1153,11 @@ link_unlink_expr_dep (GnmEvalPos *ep, GnmExpr const *tree, gboolean qlink)
}
case GNM_EXPR_OP_ARRAY_CORNER: {
// It's mildly unclean to do this here. We need the texpr, so get the cell.
GnmCellPos const *cpos = dependent_pos (ep->dep);
GnmCell const *cell = sheet_cell_get (ep->dep->sheet, cpos->col, cpos->row);
GnmEvalPos pos = *ep;
pos.array = &tree->array_corner;
pos.array_texpr = cell->base.texpr;
/* Corner cell depends on the contents of the expr */
return link_unlink_expr_dep (&pos, tree->array_corner.expr, qlink);
}
......@@ -2039,15 +2041,16 @@ dependents_unrelocate (GSList *info)
* is not really relevant. eg when undoing a pasted
* cut that was relocated but also saved to a buffer */
if (cell != NULL) {
GnmExprArrayCorner const *corner =
gnm_expr_top_get_array_corner (tmp->oldtree);
if (corner) {
if (gnm_expr_top_is_array_corner (tmp->oldtree)) {
int cols, rows;
gnm_expr_top_get_array_size (tmp->oldtree, &cols, &rows);
gnm_cell_set_array_formula (tmp->u.pos.sheet,
tmp->u.pos.eval.col,
tmp->u.pos.eval.row,
tmp->u.pos.eval.col + corner->cols - 1,
tmp->u.pos.eval.row + corner->rows - 1,
gnm_expr_top_new (gnm_expr_copy (corner->expr)));
tmp->u.pos.eval.col + cols - 1,
tmp->u.pos.eval.row + rows - 1,
gnm_expr_top_new (gnm_expr_copy (gnm_expr_top_get_array_expr (tmp->oldtree))));
cell_queue_recalc (cell);
sheet_flag_status_update_cell (cell);
} else
......
......@@ -506,7 +506,10 @@ gnm_expr_free (GnmExpr const *expr)
case GNM_EXPR_OP_ARRAY_CORNER:
value_release (expr->array_corner.value);
gnm_expr_free (expr->array_corner.expr);
// A proper corner will not have NULL here, but we explicitly allow it
// during construction, so allow it here too.
if (expr->array_corner.expr)
gnm_expr_free (expr->array_corner.expr);
CHUNK_FREE (expression_pool_big, (gpointer)expr);
break;
......@@ -1518,67 +1521,11 @@ gnm_expr_eval (GnmExpr const *expr, GnmEvalPos const *pos,
}
return handle_empty (res, flags);
case GNM_EXPR_OP_ARRAY_CORNER: {
GnmEvalPos range_pos = *pos;
range_pos.array = &expr->array_corner;
a = gnm_expr_eval (expr->array_corner.expr, &range_pos,
flags | GNM_EXPR_EVAL_PERMIT_NON_SCALAR);
value_release (expr->array_corner.value);
/* Store real result (cast away const)*/
((GnmExpr*)expr)->array_corner.value = a;
if (a != NULL &&
(VALUE_IS_CELLRANGE (a) || VALUE_IS_ARRAY (a))) {
if (value_area_get_width (a, pos) <= 0 ||
value_area_get_height (a, pos) <= 0)
return value_new_error_NA (pos);
a = (GnmValue *)value_area_get_x_y (a, 0, 0, pos);
}
return handle_empty ((a != NULL) ? value_dup (a) : NULL, flags);
}
case GNM_EXPR_OP_ARRAY_ELEM: {
/* The upper left corner manages the recalc of the expr */
GnmCell *corner = array_elem_get_corner (&expr->array_elem,
pos->sheet, &pos->eval);
if (!corner ||
!gnm_expr_top_is_array_corner (corner->base.texpr)) {
g_warning ("Funky array setup.");
return handle_empty (NULL, flags);
}
gnm_cell_eval (corner);
a = corner->base.texpr->expr->array_corner.value;
if (a == NULL)
return handle_empty (NULL, flags);
if ((VALUE_IS_CELLRANGE (a) || VALUE_IS_ARRAY (a))) {
int const num_x = value_area_get_width (a, pos);
int const num_y = value_area_get_height (a, pos);
int x = expr->array_elem.x;
int y = expr->array_elem.y;
/* Evaluate relative to the upper left corner */
GnmEvalPos tmp_ep = *pos;
tmp_ep.eval.col -= x;
tmp_ep.eval.row -= y;
/* If the src array is 1 element wide or tall we wrap */
if (x >= 1 && num_x == 1)
x = 0;
if (y >= 1 && num_y == 1)
y = 0;
if (x >= num_x || y >= num_y)
return value_new_error_NA (pos);
a = (GnmValue *)value_area_get_x_y (a, x, y, &tmp_ep);
}
case GNM_EXPR_OP_ARRAY_CORNER:
case GNM_EXPR_OP_ARRAY_ELEM:
g_warning ("Unexpected array expressions encountered");
return value_new_error_VALUE (pos);
return handle_empty ((a != NULL) ? value_dup (a) : NULL, flags);
}
case GNM_EXPR_OP_SET:
if (flags & GNM_EXPR_EVAL_PERMIT_NON_SCALAR) {
int i;
......@@ -1892,6 +1839,21 @@ gnm_expr_get_constant (GnmExpr const *expr)
return expr->constant.value;
}
/**
* gnm_expr_get_cellref:
* @expr:
*
* If this expression consists of just a cell reference, return it.
*/
GnmCellRef const *
gnm_expr_get_cellref (GnmExpr const *expr)
{
if (GNM_EXPR_GET_OPER (expr) != GNM_EXPR_OP_CELLREF)
return NULL;
return &expr->cellref.ref;
}
typedef struct {
GnmExprRelocateInfo const *details;
......@@ -3110,28 +3072,109 @@ gnm_expr_top_is_volatile (GnmExprTop const *texpr)
}
static GnmValue *
gnm_expr_top_eval_array_corner (GnmExprTop const *texpr,
GnmEvalPos const *pos,
GnmExprEvalFlags flags)
{
GnmExpr const *expr = texpr->expr;
GnmEvalPos pos2;
GnmValue *a;
pos2 = *pos;
pos2.array_texpr = texpr;
a = gnm_expr_eval (expr->array_corner.expr, &pos2,
flags | GNM_EXPR_EVAL_PERMIT_NON_SCALAR);
value_release (expr->array_corner.value);
/* Store real result (cast away const)*/
((GnmExpr*)expr)->array_corner.value = a;
if (a != NULL &&
(VALUE_IS_CELLRANGE (a) || VALUE_IS_ARRAY (a))) {
if (value_area_get_width (a, pos) <= 0 ||
value_area_get_height (a, pos) <= 0)
return value_new_error_NA (pos);
a = (GnmValue *)value_area_get_x_y (a, 0, 0, pos);
}
return handle_empty ((a != NULL) ? value_dup (a) : NULL, flags);
}
static GnmValue *
gnm_expr_top_eval_array_elem (GnmExprTop const *texpr,
GnmEvalPos const *pos,
GnmExprEvalFlags flags)
{
GnmExpr const *expr = texpr->expr;
/* The upper left corner manages the recalc of the expr */
GnmCell *corner = array_elem_get_corner (&expr->array_elem,
pos->sheet, &pos->eval);
GnmValue *a;
if (!corner ||
!gnm_expr_top_is_array_corner (corner->base.texpr)) {
g_warning ("Funky array setup.");
return handle_empty (NULL, flags);
}
gnm_cell_eval (corner);
a = gnm_expr_top_get_array_value (corner->base.texpr);
if (a == NULL)
return handle_empty (NULL, flags);
if ((VALUE_IS_CELLRANGE (a) || VALUE_IS_ARRAY (a))) {
int const num_x = value_area_get_width (a, pos);
int const num_y = value_area_get_height (a, pos);
int x = expr->array_elem.x;
int y = expr->array_elem.y;
/* Evaluate relative to the upper left corner */
GnmEvalPos tmp_ep = *pos;
tmp_ep.eval.col -= x;
tmp_ep.eval.row -= y;
/* If the src array is 1 element wide or tall we wrap */
if (x >= 1 && num_x == 1)
x = 0;
if (y >= 1 && num_y == 1)
y = 0;
if (x >= num_x || y >= num_y)
return value_new_error_NA (pos);
a = (GnmValue *)value_area_get_x_y (a, x, y, &tmp_ep);
}
return handle_empty ((a != NULL) ? value_dup (a) : NULL, flags);
}
GnmValue *
gnm_expr_top_eval (GnmExprTop const *texpr,
GnmEvalPos const *pos,
GnmExprEvalFlags flags)
{
GnmValue *res;
GnmEvalPos ep;
GnmExprArrayCorner array;
g_return_val_if_fail (GNM_IS_EXPR_TOP (texpr), NULL);
gnm_app_recalc_start ();
if ((flags & GNM_EXPR_EVAL_ARRAY_CONTEXT) && !pos->array) {
array.oper = GNM_EXPR_OP_ARRAY_CORNER;
array.cols = array.rows = 1;
array.value = NULL;
array.expr = texpr->expr;
ep = *pos;
ep.array = &array;
pos = &ep;
}
res = gnm_expr_eval (texpr->expr, pos, flags);
if (gnm_expr_top_is_array_corner (texpr))
res = gnm_expr_top_eval_array_corner (texpr, pos, flags);
else if (gnm_expr_top_is_array_elem (texpr, NULL, NULL))
res = gnm_expr_top_eval_array_elem (texpr, pos, flags);
<