functions.c 10.4 KB
Newer Older
Jody Goldberg's avatar
Jody Goldberg committed
1
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
/*
jpekka's avatar
jpekka committed
3
 * fn-logical.c:  Built in logical functions and functions registration
4 5
 *
 * Authors:
jpekka's avatar
jpekka committed
6
 *   Miguel de Icaza (miguel@gnu.org)
Morten Welinder's avatar
Morten Welinder committed
7 8
 *   Jukka-Pekka Iivonen (iivonen@iki.fi)
 *   Morten Welinder (terra@diku.dk)
Jukka-Pekka Iivonen's avatar
Jukka-Pekka Iivonen committed
9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23
 */
Morten Welinder's avatar
Morten Welinder committed
24

25 26 27 28 29 30
#include <gnumeric-config.h>
#include <gnumeric.h>
#include <func.h>

#include <parse-util.h>
#include <cell.h>
31 32
#include <expr.h>
#include <value.h>
33
#include <auto-format.h>
34
#include <libgnome/gnome-i18n.h>
35

Morten Welinder's avatar
Morten Welinder committed
36 37 38 39 40 41
#include "plugin.h"
#include "plugin-util.h"
#include "module-plugin-defs.h"

GNUMERIC_MODULE_PLUGIN_INFO_DECL;

42 43
/***************************************************************************/

jpekka's avatar
jpekka committed
44 45 46
static const char *help_and = {
	N_("@FUNCTION=AND\n"
	   "@SYNTAX=AND(b1, b2, ...)\n"
47

48
	   "@DESCRIPTION="
jpekka's avatar
jpekka committed
49 50 51
	   "AND implements the logical AND function: the result is TRUE "
	   "if all of the expressions evaluate to TRUE, otherwise it returns "
	   "FALSE.\n"
52

jpekka's avatar
jpekka committed
53 54 55
	   "@b1, trough @bN are expressions that should evaluate to TRUE "
	   "or FALSE.  If an integer or floating point value is provided "
	   "zero is considered FALSE and anything else is TRUE.\n"
56

jpekka's avatar
jpekka committed
57 58 59 60
	   "If the values contain strings or empty cells those values are "
	   "ignored. "
	   "If no logical values are provided, then the error #VALUE! "
	   "is returned.\n"
61
	   "This function is Excel compatible. "
62 63
	   "\n"
	   "@EXAMPLES=\n"
jpekka's avatar
jpekka committed
64 65 66 67
	   "AND(TRUE,TRUE) equals TRUE.\n"
	   "AND(TRUE,FALSE) equals FALSE.\n\n"
	   "Let us assume that A1 holds number five and A2 number one.  Then\n"
	   "AND(A1>3,A2<2) equals TRUE.\n"
68
	   "\n"
jpekka's avatar
jpekka committed
69
	   "@SEEALSO=OR, NOT")
70 71 72
};

static Value *
jpekka's avatar
jpekka committed
73
callback_function_and (const EvalPos *ep, Value *value, void *closure)
74
{
jpekka's avatar
jpekka committed
75 76
	int *result = closure;
	gboolean err;
77

jpekka's avatar
jpekka committed
78 79 80
	*result = value_get_as_bool (value, &err) && *result;
	if (err)
		return value_new_error (ep, gnumeric_err_VALUE);
81

jpekka's avatar
jpekka committed
82
	return NULL;
83
}
84

85
static Value *
jpekka's avatar
jpekka committed
86
gnumeric_and (FunctionEvalInfo *ei, GnmExprList *nodes)
87
{
jpekka's avatar
jpekka committed
88
	int result = -1;
89

jpekka's avatar
jpekka committed
90 91 92 93 94 95 96
	/* Yes, AND is actually strict.  */
	Value *v = function_iterate_argument_values (ei->pos,
						     callback_function_and,
						     &result, nodes,
						     TRUE, TRUE);
	if (v != NULL)
		return v;
97

jpekka's avatar
jpekka committed
98 99
	/* See if there was any value worth using */
	if (result == -1)
100 101
		return value_new_error (ei->pos, gnumeric_err_VALUE);

jpekka's avatar
jpekka committed
102
	return value_new_bool (result);
103 104
}

105 106
/***************************************************************************/

jpekka's avatar
jpekka committed
107 108 109
static const char *help_not = {
	N_("@FUNCTION=NOT\n"
	   "@SYNTAX=NOT(number)\n"
110

111
	   "@DESCRIPTION="
jpekka's avatar
jpekka committed
112 113
	   "NOT implements the logical NOT function: the result is TRUE "
	   "if the @number is zero;  otherwise the result is FALSE.\n"
114 115 116
	   "This function is Excel compatible. "
	   "\n"
	   "@EXAMPLES=\n"
jpekka's avatar
jpekka committed
117 118
	   "NOT(0) equals TRUE.\n"
	   "NOT(TRUE) equals FALSE.\n"
119
	   "\n"
jpekka's avatar
jpekka committed
120
	   "@SEEALSO=AND, OR")
121 122 123
};

static Value *
jpekka's avatar
jpekka committed
124
gnumeric_not (FunctionEvalInfo *ei, Value **argv)
125
{
jpekka's avatar
jpekka committed
126 127 128 129
	gboolean err, val = value_get_as_bool (argv [0], &err);
	if (err)
		return value_new_error (ei->pos, _("Type Mismatch"));
	return value_new_bool (!val);
130 131
}

132 133
/***************************************************************************/

jpekka's avatar
jpekka committed
134 135 136
static const char *help_or = {
	N_("@FUNCTION=OR\n"
	   "@SYNTAX=OR(b1, b2, ...)\n"
Morten Welinder's avatar
Morten Welinder committed
137 138

	   "@DESCRIPTION="
jpekka's avatar
jpekka committed
139 140 141 142 143 144 145 146
	   "OR implements the logical OR function: the result is TRUE if "
	   "any of the values evaluated to TRUE.\n"
	   "@b1, trough @bN are expressions that should evaluate to TRUE "
	   "or FALSE. If an integer or floating point value is provided "
	   "zero is considered FALSE and anything else is TRUE.\n"
	   "If the values contain strings or empty cells those values are "
	   "ignored.  If no logical values are provided, then the error "
	   "#VALUE! is returned.\n"
147 148
	   "This function is Excel compatible. "
	   "\n"
Morten Welinder's avatar
Morten Welinder committed
149
	   "@EXAMPLES=\n"
jpekka's avatar
jpekka committed
150 151
	   "OR(TRUE,FALSE) equals TRUE.\n"
	   "OR(3>4,4<3) equals FALSE.\n"
152
	   "\n"
jpekka's avatar
jpekka committed
153
	   "@SEEALSO=AND, NOT")
154 155 156
};

static Value *
jpekka's avatar
jpekka committed
157
callback_function_or (const EvalPos *ep, Value *value, void *closure)
158
{
jpekka's avatar
jpekka committed
159 160
	int *result = closure;
	gboolean err;
161

jpekka's avatar
jpekka committed
162 163 164
	*result = value_get_as_bool (value, &err) || *result == 1;
	if (err)
		return value_new_error (ep, gnumeric_err_VALUE);
165

jpekka's avatar
jpekka committed
166
	return NULL;
167 168 169
}

static Value *
jpekka's avatar
jpekka committed
170
gnumeric_or (FunctionEvalInfo *ei, GnmExprList *nodes)
171
{
jpekka's avatar
jpekka committed
172
	int result = -1;
173

jpekka's avatar
jpekka committed
174 175 176 177 178 179 180
	/* Yes, OR is actually strict.  */
	Value *v = function_iterate_argument_values (ei->pos,
						     callback_function_or,
						     &result, nodes,
						     TRUE, TRUE);
	if (v != NULL)
		return v;
181

jpekka's avatar
jpekka committed
182 183
	/* See if there was any value worth using */
	if (result == -1)
184 185
		return value_new_error (ei->pos, gnumeric_err_VALUE);

jpekka's avatar
jpekka committed
186
	return value_new_bool (result);
187 188 189 190
}

/***************************************************************************/

jpekka's avatar
jpekka committed
191 192 193
static const char *help_xor = {
	N_("@FUNCTION=XOR\n"
	   "@SYNTAX=XOR(b1, b2, ...)\n"
194 195

	   "@DESCRIPTION="
jpekka's avatar
jpekka committed
196 197 198 199 200 201 202 203
	   "XOR implements the logical exclusive OR function: the result is TRUE if "
	   "an odd number of the values evaluated to TRUE.\n"
	   "@b1, trough @bN are expressions that should evaluate to TRUE "
	   "or FALSE. If an integer or floating point value is provided "
	   "zero is considered FALSE and anything else is TRUE.\n"
	   "If the values contain strings or empty cells those values are "
	   "ignored.  If no logical values are provided, then the error "
	   "#VALUE! is returned.\n"
204
	   "@EXAMPLES=\n"
jpekka's avatar
jpekka committed
205 206
	   "XOR(TRUE,FALSE) equals TRUE.\n"
	   "XOR(3>4,4<3) equals FALSE.\n"
207
	   "\n"
jpekka's avatar
jpekka committed
208
	   "@SEEALSO=OR, AND, NOT")
209 210 211
};

static Value *
jpekka's avatar
jpekka committed
212
callback_function_xor (const EvalPos *ep, Value *value, void *closure)
213
{
jpekka's avatar
jpekka committed
214 215
	int *result = closure;
	gboolean err;
216

jpekka's avatar
jpekka committed
217 218 219
	*result = value_get_as_bool (value, &err) ^ (*result == 1);
	if (err)
		return value_new_error (ep, gnumeric_err_VALUE);
220

jpekka's avatar
jpekka committed
221
	return NULL;
222 223 224
}

static Value *
jpekka's avatar
jpekka committed
225
gnumeric_xor (FunctionEvalInfo *ei, GnmExprList *nodes)
226
{
jpekka's avatar
jpekka committed
227
	int result = -1;
228

jpekka's avatar
jpekka committed
229 230 231 232 233 234
	Value *v = function_iterate_argument_values (ei->pos,
						     callback_function_xor,
						     &result, nodes,
						     TRUE, TRUE);
	if (v != NULL)
		return v;
235

jpekka's avatar
jpekka committed
236 237
	/* See if there was any value worth using */
	if (result == -1)
238 239
		return value_new_error (ei->pos, gnumeric_err_VALUE);

jpekka's avatar
jpekka committed
240
	return value_new_bool (result);
241 242 243 244
}

/***************************************************************************/

jpekka's avatar
jpekka committed
245 246 247
static const char *help_if = {
	N_("@FUNCTION=IF\n"
	   "@SYNTAX=IF(condition[,if-true,if-false])\n"
248 249

	   "@DESCRIPTION="
jpekka's avatar
jpekka committed
250 251 252 253 254 255
	   "Use the IF statement to evaluate conditionally other expressions "
	   "IF evaluates @condition.  If @condition returns a non-zero value "
	   "the result of the IF expression is the @if-true expression, "
	   "otherwise IF evaluates to the value of @if-false. "
	   "If ommitted @if-true defaults to TRUE and @if-false to FALSE.\n"
	   "This function is Excel compatible. "
256 257
	   "\n"
	   "@EXAMPLES=\n"
jpekka's avatar
jpekka committed
258
	   "IF(FALSE,TRUE,FALSE) equals FALSE.\n"
259
	   "\n"
jpekka's avatar
jpekka committed
260
	   "@SEEALSO=")
261 262 263
};

static Value *
jpekka's avatar
jpekka committed
264
gnumeric_if (FunctionEvalInfo *ei, GnmExprList *expr_node_list)
265
{
jpekka's avatar
jpekka committed
266 267 268 269
	GnmExpr *expr;
	Value *value;
	int ret, args;
	gboolean err;
270

jpekka's avatar
jpekka committed
271 272 273 274 275
	/* Type checking */
	args = gnm_expr_list_length (expr_node_list);
	if (args < 1 || args > 3)
		return value_new_error (ei->pos,
					_("Invalid number of arguments"));
276

jpekka's avatar
jpekka committed
277 278 279 280
	/* Compute the if part */
	value = gnm_expr_eval (expr_node_list->data, ei->pos, GNM_EXPR_EVAL_STRICT);
	if (VALUE_IS_EMPTY_OR_ERROR(value))
		return value;
281

jpekka's avatar
jpekka committed
282 283 284 285 286
	/* Choose which expression we will evaluate */
	ret = value_get_as_bool (value, &err);
	value_release (value);
	if (err)
		/* FIXME: please verify error code.  */
287 288
		return value_new_error (ei->pos, gnumeric_err_VALUE);

jpekka's avatar
jpekka committed
289 290 291 292 293 294 295 296 297 298 299 300
	if (ret){
		if (expr_node_list->next)
			expr = (GnmExpr *) expr_node_list->next->data;
		else
			return value_new_bool (TRUE);
	} else {
		if (expr_node_list->next &&
		    expr_node_list->next->next)
			expr = (GnmExpr *) expr_node_list->next->next->data;
		else
			return value_new_bool (FALSE);
	}
301

jpekka's avatar
jpekka committed
302 303
	/* Return the result */
	return gnm_expr_eval (expr, ei->pos, GNM_EXPR_EVAL_PERMIT_NON_SCALAR);
304 305 306 307
}

/***************************************************************************/

jpekka's avatar
jpekka committed
308 309 310
static const char *help_true = {
	N_("@FUNCTION=TRUE\n"
	   "@SYNTAX=TRUE()\n"
Jukka-Pekka Iivonen's avatar
Jukka-Pekka Iivonen committed
311

312
	   "@DESCRIPTION="
jpekka's avatar
jpekka committed
313 314
	   "TRUE returns boolean value true.\n"
	   "This function is Excel compatible. "
315 316
	   "\n"
	   "@EXAMPLES=\n"
jpekka's avatar
jpekka committed
317
	   "TRUE() equals TRUE.\n"
318
	   "\n"
jpekka's avatar
jpekka committed
319
	   "@SEEALSO=FALSE")
320 321 322
};

static Value *
jpekka's avatar
jpekka committed
323
gnumeric_true (FunctionEvalInfo *ei, Value **args)
324
{
jpekka's avatar
jpekka committed
325
	return value_new_bool (TRUE);
326 327 328 329
}

/***************************************************************************/

jpekka's avatar
jpekka committed
330 331 332
static const char *help_false = {
	N_("@FUNCTION=FALSE\n"
	   "@SYNTAX=FALSE()\n"
333 334

	   "@DESCRIPTION="
jpekka's avatar
jpekka committed
335
	   "FALSE returns boolean value false.\n"
336 337 338
	   "This function is Excel compatible."
	   "\n"
	   "@EXAMPLES=\n"
jpekka's avatar
jpekka committed
339
	   "FALSE() equals FALSE.\n"
340
	   "\n"
jpekka's avatar
jpekka committed
341
	   "@SEEALSO=TRUE")
342 343 344
};

static Value *
jpekka's avatar
jpekka committed
345
gnumeric_false (FunctionEvalInfo *ei, Value **args)
346
{
jpekka's avatar
jpekka committed
347
	return value_new_bool (FALSE);
Jukka-Pekka Iivonen's avatar
Jukka-Pekka Iivonen committed
348 349 350 351
}

/***************************************************************************/

jpekka's avatar
jpekka committed
352 353 354 355 356 357 358 359
const ModulePluginFunctionInfo logical_functions[] = {
	{ "and", 0, N_("number,number,"), &help_and, NULL, gnumeric_and, NULL, NULL },
	{ "or", 0, N_("number,number,"), &help_or, NULL, gnumeric_or, NULL, NULL },
	{ "xor", 0, N_("number,number,"), &help_xor, NULL, gnumeric_xor, NULL, NULL },
	{ "not", "f", N_("number"), &help_not, gnumeric_not, NULL, NULL, NULL },
	{ "if", 0, N_("condition,if true,if false"), &help_if, NULL, gnumeric_if, NULL, NULL },
	{ "true", "", "", &help_true, gnumeric_true, NULL, NULL, NULL },
	{ "false", "", "", &help_false, gnumeric_false, NULL, NULL, NULL },
Morten Welinder's avatar
Morten Welinder committed
360 361 362 363 364 365 366 367
        {NULL}
};

/* FIXME: Should be merged into the above.  */
static const struct {
	const char *func;
	AutoFormatTypes typ;
} af_info[] = {
jpekka's avatar
jpekka committed
368
	{ "if", AF_FIRST_ARG_FORMAT2 },
Morten Welinder's avatar
Morten Welinder committed
369 370 371 372 373 374 375 376
	{ NULL, AF_UNKNOWN }
};

void
plugin_init (void)
{
	int i;
	for (i = 0; af_info[i].func; i++)
jpekka's avatar
jpekka committed
377
		auto_format_function_result_by_name (af_info[i].func, af_info[i].typ);
Morten Welinder's avatar
Morten Welinder committed
378 379
}

Morten Welinder's avatar
Morten Welinder committed
380
void
Morten Welinder's avatar
Morten Welinder committed
381
plugin_cleanup (void)
382
{
Morten Welinder's avatar
Morten Welinder committed
383 384 385
	int i;
	for (i = 0; af_info[i].func; i++)
		auto_format_function_result_remove (af_info[i].func);
386
}