libxexpr/xexpreval.c
author ali <ali@juiblex.co.uk>
Wed Oct 10 22:58:21 2012 +0100 (2012-10-10)
changeset 0 bc8c9a11cbfc
permissions -rw-r--r--
Initial version
ali@0
     1
#include <stdlib.h>
ali@0
     2
#include <string.h>
ali@0
     3
#include <math.h>
ali@0
     4
#include <xexpr.h>
ali@0
     5
#include "xexprprivate.h"
ali@0
     6
ali@0
     7
/**
ali@0
     8
 * SECTION:xexpreval
ali@0
     9
 * @short_description: An evaluator for the XEXPR language
ali@0
    10
 * @stability: Stable
ali@0
    11
 * @include: libxexpr/xexpr.h
ali@0
    12
 *
ali@0
    13
 * An evaluator for the
ali@0
    14
 * <ulink url="http://www.w3.org/TR/2000/NOTE-xexpr-20001121">XEXPR</ulink>
ali@0
    15
 * language.
ali@0
    16
 */
ali@0
    17
ali@0
    18
GSList *xexpr_extensions=NULL;
ali@0
    19
ali@0
    20
static XexprExtension libxexpr_extension={
ali@0
    21
    LIBXEXPR_NS,_xexpr_libxexpr_function_evaluate
ali@0
    22
};
ali@0
    23
ali@0
    24
static XexprConstant *xexpr_do_string(Xexpr *xexpr,GSList *bindings,
ali@0
    25
  GSList *args,GError **err);
ali@0
    26
static gboolean xexpr_defineable_id(const char *id,GError **err);
ali@0
    27
ali@0
    28
/**
ali@0
    29
 * xexpr_eval_error_quark:
ali@0
    30
 *
ali@0
    31
 * Registers an error quark for the libxexpr evaluator if necessary.
ali@0
    32
 *
ali@0
    33
 * Return value: The error quark used for libxexpr evaluator errors.
ali@0
    34
 */
ali@0
    35
GQuark xexpr_eval_error_quark(void)
ali@0
    36
{
ali@0
    37
    static GQuark quark;
ali@0
    38
    if (!quark)
ali@0
    39
	quark=g_quark_from_static_string("xexpr_eval_error");
ali@0
    40
    return quark;
ali@0
    41
}
ali@0
    42
ali@0
    43
/**
ali@0
    44
 * xexpr_register_extension:
ali@0
    45
 * @extension: (transfer none): an #XexprExtension
ali@0
    46
 *
ali@0
    47
 * Registers an extension to handle the evaluation of functions and retrieval
ali@0
    48
 * of variables in a new namespace.
ali@0
    49
 *
ali@0
    50
 * Return value: %TRUE if the extension was successfully registered
ali@0
    51
 */
ali@0
    52
gboolean xexpr_register_extension(XexprExtension *extension)
ali@0
    53
{
ali@0
    54
    GSList *lnk;
ali@0
    55
    XexprExtension *ext;
ali@0
    56
    static GStaticMutex mutex=G_STATIC_MUTEX_INIT;
ali@0
    57
    g_static_mutex_lock(&mutex);
ali@0
    58
    if (!xexpr_extensions)
ali@0
    59
	xexpr_extensions=g_slist_prepend(xexpr_extensions,&libxexpr_extension);
ali@0
    60
    if (!extension || !extension->ns || !*extension->ns ||
ali@0
    61
      !strcmp(extension->ns,XEXPR_NS))
ali@0
    62
    {
ali@0
    63
	g_static_mutex_unlock(&mutex);
ali@0
    64
	return FALSE;
ali@0
    65
    }
ali@0
    66
    for(lnk=xexpr_extensions;lnk;lnk=lnk->next)
ali@0
    67
    {
ali@0
    68
	ext=lnk->data;
ali@0
    69
	if (!strcmp(ext->ns,extension->ns))
ali@0
    70
	{
ali@0
    71
	    g_static_mutex_unlock(&mutex);
ali@0
    72
	    return FALSE;
ali@0
    73
	}
ali@0
    74
    }
ali@0
    75
    extension=g_memdup(extension,sizeof(*extension));
ali@0
    76
    extension->ns=g_strdup(extension->ns);
ali@0
    77
    xexpr_extensions=g_slist_prepend(xexpr_extensions,extension);
ali@0
    78
    g_static_mutex_unlock(&mutex);
ali@0
    79
    return TRUE;
ali@0
    80
}
ali@0
    81
ali@0
    82
/*
ali@0
    83
 * Returned constant should be freed iff evaluate is TRUE
ali@0
    84
 */
ali@0
    85
static XexprConstant *xexpr_get_argument(Xexpr *xexpr,GSList *bindings,
ali@0
    86
  GSList **args,const char *func,const char *id,gboolean evaluate,GError **err)
ali@0
    87
{
ali@0
    88
    XexprConstant *arg;
ali@0
    89
    arg=xexpr_bindings_get(bindings,id);
ali@0
    90
    if (!arg)
ali@0
    91
    {
ali@0
    92
	if (*args)
ali@0
    93
	{
ali@0
    94
	    arg=(*args)->data;
ali@0
    95
	    *args=(*args)->next;
ali@0
    96
	}
ali@0
    97
	else
ali@0
    98
	{
ali@0
    99
	    g_set_error(err,XEXPR_EVAL_ERROR,XEXPR_EVAL_ERROR_FAILED,
ali@0
   100
	      "Missing \"%s\" argument to \"%s\"",id,func);
ali@0
   101
	    return NULL;
ali@0
   102
	}
ali@0
   103
    }
ali@0
   104
    if (arg && evaluate)
ali@0
   105
	arg=xexpr_constant_evaluate(xexpr,arg,err);
ali@0
   106
    return arg;
ali@0
   107
}
ali@0
   108
ali@0
   109
static XexprConstant *xexpr_do_define(Xexpr *xexpr,GSList *bindings,
ali@0
   110
  GSList *args,GError **err)
ali@0
   111
{
ali@0
   112
    int i;
ali@0
   113
    gchar **vector=NULL;
ali@0
   114
    GSList *parameters=NULL;
ali@0
   115
    XexprConstant *name,*params,*value,*arg;
ali@0
   116
    XexprEnvironment *frame;
ali@0
   117
    name=xexpr_get_argument(xexpr,bindings,&args,"define","name",TRUE,err);
ali@0
   118
    if (!name)
ali@0
   119
	return NULL;
ali@0
   120
    if (name->type!=XEXPR_TYPE_STRING)
ali@0
   121
    {
ali@0
   122
	args=g_slist_prepend(NULL,name);
ali@0
   123
	arg=xexpr_do_string(xexpr,NULL,args,NULL);
ali@0
   124
	g_slist_free(args);
ali@0
   125
	if (arg)
ali@0
   126
	{
ali@0
   127
	    g_set_error(err,XEXPR_EVAL_ERROR,XEXPR_EVAL_ERROR_FAILED,
ali@0
   128
	      "Attempt to define a function with a non-string name \"%s\"",
ali@0
   129
	      arg->u.string);
ali@0
   130
	    xexpr_constant_free(arg);
ali@0
   131
	}
ali@0
   132
	else
ali@0
   133
	    g_set_error(err,XEXPR_EVAL_ERROR,XEXPR_EVAL_ERROR_FAILED,
ali@0
   134
	      "Attempt to define a function with a non-string name");
ali@0
   135
	xexpr_constant_free(name);
ali@0
   136
	return NULL;
ali@0
   137
    }
ali@0
   138
    else if (!_xexpr_validate_id(name->u.string,err) ||
ali@0
   139
      !xexpr_defineable_id(name->u.string,err))
ali@0
   140
    {
ali@0
   141
	xexpr_constant_free(name);
ali@0
   142
	return NULL;
ali@0
   143
    }
ali@0
   144
    params=xexpr_bindings_get(bindings,"args");
ali@0
   145
    if (params)
ali@0
   146
    {
ali@0
   147
	params=xexpr_constant_evaluate(xexpr,params,err);
ali@0
   148
	if (params->type!=XEXPR_TYPE_STRING)
ali@0
   149
	{
ali@0
   150
	    g_set_error(err,XEXPR_EVAL_ERROR,XEXPR_EVAL_ERROR_FAILED,
ali@0
   151
	      "Attempt to define a function with non-string argument names");
ali@0
   152
	    xexpr_constant_free(params);
ali@0
   153
	    xexpr_constant_free(name);
ali@0
   154
	    return NULL;
ali@0
   155
	}
ali@0
   156
	vector=g_strsplit_set(params->u.string," \t\n\r",-1);
ali@0
   157
	for(i=0;vector[i];i++)
ali@0
   158
	{
ali@0
   159
	    if (vector[i][0]>='0' && vector[i][0]<='9' ||
ali@0
   160
	      (vector[i][0]=='+' || vector[i][0]=='-') &&
ali@0
   161
	      vector[i][1]>='0' && vector[i][1]<='9')
ali@0
   162
	    {
ali@0
   163
		g_set_error(err,XEXPR_EVAL_ERROR,XEXPR_EVAL_ERROR_FAILED,
ali@0
   164
		 "Attempt to define a function with non-string argument names");
ali@0
   165
		xexpr_constant_free(params);
ali@0
   166
		xexpr_constant_free(name);
ali@0
   167
		g_strfreev(vector);
ali@0
   168
		g_slist_free(parameters);
ali@0
   169
		return NULL;
ali@0
   170
	    }
ali@0
   171
	    parameters=g_slist_prepend(parameters,vector[i]);
ali@0
   172
	}
ali@0
   173
	parameters=g_slist_reverse(parameters);
ali@0
   174
    }
ali@0
   175
    value=xexpr_new_function(parameters,args);
ali@0
   176
    g_slist_free(parameters);
ali@0
   177
    if (vector)
ali@0
   178
	g_strfreev(vector);
ali@0
   179
    frame=xexpr->environment;
ali@0
   180
    xexpr->environment=frame->outer;
ali@0
   181
    xexpr_var_new(xexpr,name->u.string,value);
ali@0
   182
    xexpr->environment=frame;
ali@0
   183
    xexpr_constant_free(name);
ali@0
   184
    return value;
ali@0
   185
}
ali@0
   186
ali@0
   187
static XexprConstant *xexpr_do_print(Xexpr *xexpr,GSList *bindings,GSList *args,
ali@0
   188
  GError **err)
ali@0
   189
{
ali@0
   190
    gboolean newline=FALSE;
ali@0
   191
    GSList *lnk;
ali@0
   192
    XexprConstant *arg;
ali@0
   193
    arg=xexpr_bindings_get(bindings,"newline");
ali@0
   194
    if (arg && arg->type==XEXPR_TYPE_STRING && !strcmp(arg->u.string,"true"))
ali@0
   195
	newline=TRUE;
ali@0
   196
    for(lnk=args;lnk;lnk=lnk->next)
ali@0
   197
    {
ali@0
   198
	arg=lnk->data;
ali@0
   199
	switch(arg->type)
ali@0
   200
	{
ali@0
   201
	    case XEXPR_TYPE_FUNCTION:
ali@0
   202
		/*
ali@0
   203
		 * http://www.w3.org/TR/xexpr/#id-0003 implies that
ali@0
   204
		 * function objects are ignored in <print>.
ali@0
   205
		 */
ali@0
   206
		break;
ali@0
   207
	    case XEXPR_TYPE_INVOCATION:
ali@0
   208
		printf("<%s/>",arg->u.invocation->function);
ali@0
   209
		break;
ali@0
   210
	    case XEXPR_TYPE_STRING:
ali@0
   211
		fputs(arg->u.string,stdout);
ali@0
   212
		break;
ali@0
   213
	    case XEXPR_TYPE_INTEGER:
ali@0
   214
		printf("%lld",arg->u.integer);
ali@0
   215
		break;
ali@0
   216
	    case XEXPR_TYPE_NUMBER:
ali@0
   217
		printf("%lg",arg->u.number);
ali@0
   218
		break;
ali@0
   219
	}
ali@0
   220
    }
ali@0
   221
    if (newline)
ali@0
   222
	putchar('\n');
ali@0
   223
    return xexpr_new_invocation(NULL,"true",NULL,NULL);
ali@0
   224
}
ali@0
   225
ali@0
   226
static XexprConstant *xexpr_do_println(Xexpr *xexpr,GSList *bindings,
ali@0
   227
  GSList *args,GError **err)
ali@0
   228
{
ali@0
   229
    putchar('\n');
ali@0
   230
    return xexpr_new_invocation(NULL,"true",NULL,NULL);
ali@0
   231
}
ali@0
   232
ali@0
   233
static XexprConstant *xexpr_do_get(Xexpr *xexpr,GSList *bindings,
ali@0
   234
  GSList *args,GError **err)
ali@0
   235
{
ali@0
   236
    XexprConstant *name,*value;
ali@0
   237
    name=xexpr_get_argument(xexpr,bindings,&args,"get","name",FALSE,err);
ali@0
   238
    if (!name)
ali@0
   239
	return NULL;
ali@0
   240
    if (name->type==XEXPR_TYPE_STRING)
ali@0
   241
	value=xexpr_var_get(xexpr,name->u.string);
ali@0
   242
    else
ali@0
   243
	value=NULL;
ali@0
   244
    if (value)
ali@0
   245
	value=xexpr_constant_dup(value);
ali@0
   246
    else
ali@0
   247
	value=xexpr_new_invocation(NULL,"nil",NULL,NULL);
ali@0
   248
    return value;
ali@0
   249
}
ali@0
   250
ali@0
   251
static XexprConstant *xexpr_do_set(Xexpr *xexpr,GSList *bindings,
ali@0
   252
  GSList *args,GError **err)
ali@0
   253
{
ali@0
   254
    XexprConstant *name,*value;
ali@0
   255
    name=xexpr_get_argument(xexpr,bindings,&args,"set","name",FALSE,err);
ali@0
   256
    if (!name)
ali@0
   257
	return NULL;
ali@0
   258
    if (name->type!=XEXPR_TYPE_STRING)
ali@0
   259
    {
ali@0
   260
	g_set_error(err,XEXPR_EVAL_ERROR,XEXPR_EVAL_ERROR_FAILED,
ali@0
   261
	  "Attempt to bind a value a non-string name");
ali@0
   262
	xexpr_constant_free(name);
ali@0
   263
	return NULL;
ali@0
   264
    }
ali@0
   265
    else if (!_xexpr_validate_id(name->u.string,err) ||
ali@0
   266
      !xexpr_defineable_id(name->u.string,err))
ali@0
   267
    {
ali@0
   268
	xexpr_constant_free(name);
ali@0
   269
	return NULL;
ali@0
   270
    }
ali@0
   271
    value=xexpr_get_argument(xexpr,bindings,&args,"set","value",FALSE,err);
ali@0
   272
    if (!value)
ali@0
   273
	return NULL;
ali@0
   274
    xexpr_var_set(xexpr,name->u.string,value);
ali@0
   275
    return xexpr_constant_dup(value);
ali@0
   276
}
ali@0
   277
ali@0
   278
static XexprConstant *xexpr_do_expr(Xexpr *xexpr,GSList *bindings,GSList *args,
ali@0
   279
  GError **err)
ali@0
   280
{
ali@0
   281
    if (!args)
ali@0
   282
	return xexpr_new_invocation(NULL,"nil",NULL,NULL);
ali@0
   283
    while(args->next)
ali@0
   284
	args=args->next;
ali@0
   285
    return xexpr_constant_dup(args->data);
ali@0
   286
}
ali@0
   287
ali@0
   288
static XexprConstant *xexpr_do_return(Xexpr *xexpr,GSList *bindings,
ali@0
   289
  GSList *args,GError **err)
ali@0
   290
{
ali@0
   291
    XexprConstant *retval;
ali@0
   292
    if (!args)
ali@0
   293
	retval=xexpr_new_invocation(NULL,"nil",NULL,NULL);
ali@0
   294
    while(args->next)
ali@0
   295
	args=args->next;
ali@0
   296
    retval=xexpr_constant_dup(args->data);
ali@0
   297
    return xexpr_new_invocation_take_ownership(NULL,g_strdup("return"),NULL,
ali@0
   298
      g_slist_prepend(NULL,retval));
ali@0
   299
}
ali@0
   300
ali@0
   301
static XexprConstant *xexpr_do_string(Xexpr *xexpr,GSList *bindings,
ali@0
   302
  GSList *args,GError **err)
ali@0
   303
{
ali@0
   304
    XexprConstant *arg;
ali@0
   305
    GString *str=g_string_new(NULL);
ali@0
   306
    while(args)
ali@0
   307
    {
ali@0
   308
	arg=args->data;
ali@0
   309
	switch(arg->type)
ali@0
   310
	{
ali@0
   311
	    case XEXPR_TYPE_FUNCTION:
ali@0
   312
		g_warn_if_reached();
ali@0
   313
		break;
ali@0
   314
	    case XEXPR_TYPE_INVOCATION:
ali@0
   315
		g_string_append_printf(str,"<%s/>",arg->u.invocation->function);
ali@0
   316
		break;
ali@0
   317
	    case XEXPR_TYPE_STRING:
ali@0
   318
		g_string_append(str,arg->u.string);
ali@0
   319
		break;
ali@0
   320
	    case XEXPR_TYPE_INTEGER:
ali@0
   321
		g_string_append_printf(str,"%lld",arg->u.integer);
ali@0
   322
		break;
ali@0
   323
	    case XEXPR_TYPE_NUMBER:
ali@0
   324
		g_string_append_printf(str,"%lg",arg->u.number);
ali@0
   325
		break;
ali@0
   326
	}
ali@0
   327
	args=args->next;
ali@0
   328
    }
ali@0
   329
    return xexpr_new_string_take_ownership(g_string_free(str,FALSE));
ali@0
   330
}
ali@0
   331
ali@0
   332
static XexprConstant *xexpr_do_integer(Xexpr *xexpr,GSList *bindings,
ali@0
   333
  GSList *args,GError **err)
ali@0
   334
{
ali@0
   335
    XexprConstant *arg;
ali@0
   336
    double d;
ali@0
   337
    long long int value=0;
ali@0
   338
    while(args)
ali@0
   339
    {
ali@0
   340
	arg=args->data;
ali@0
   341
	switch(arg->type)
ali@0
   342
	{
ali@0
   343
	    case XEXPR_TYPE_FUNCTION:
ali@0
   344
		g_warn_if_reached();
ali@0
   345
		break;
ali@0
   346
	    case XEXPR_TYPE_INVOCATION:
ali@0
   347
		g_warn_if_reached();
ali@0
   348
		break;
ali@0
   349
	    case XEXPR_TYPE_STRING:
ali@0
   350
		value=g_ascii_strtoll(arg->u.string,NULL,0);
ali@0
   351
		if (value==LLONG_MIN || value==LLONG_MAX)
ali@0
   352
		{
ali@0
   353
		    g_set_error(err,XEXPR_EVAL_ERROR,XEXPR_EVAL_ERROR_FAILED,
ali@0
   354
		      "Integer overflow on %s",arg->u.string);
ali@0
   355
		    return NULL;
ali@0
   356
		}
ali@0
   357
		break;
ali@0
   358
	    case XEXPR_TYPE_INTEGER:
ali@0
   359
		value=arg->u.integer;
ali@0
   360
		break;
ali@0
   361
	    case XEXPR_TYPE_NUMBER:
ali@0
   362
		if (!isfinite(arg->u.number))
ali@0
   363
		{
ali@0
   364
		    g_set_error(err,XEXPR_EVAL_ERROR,XEXPR_EVAL_ERROR_FAILED,
ali@0
   365
		      "%lg is not finite",arg->u.number);
ali@0
   366
		    return NULL;
ali@0
   367
		}
ali@0
   368
		d=nearbyint(arg->u.number);
ali@0
   369
		if (d<=LLONG_MIN || d>=LLONG_MAX)
ali@0
   370
		{
ali@0
   371
		    g_set_error(err,XEXPR_EVAL_ERROR,XEXPR_EVAL_ERROR_FAILED,
ali@0
   372
		      "Integer overflow on %lg",arg->u.number);
ali@0
   373
		    return NULL;
ali@0
   374
		}
ali@0
   375
		value=(long long int)d;
ali@0
   376
		break;
ali@0
   377
	}
ali@0
   378
	args=args->next;
ali@0
   379
    }
ali@0
   380
    return xexpr_new_integer(value);
ali@0
   381
}
ali@0
   382
ali@0
   383
static XexprConstant *xexpr_do_float(Xexpr *xexpr,GSList *bindings,
ali@0
   384
  GSList *args,GError **err)
ali@0
   385
{
ali@0
   386
    XexprConstant *arg;
ali@0
   387
    double value=0.0/0.0;
ali@0
   388
    while(args)
ali@0
   389
    {
ali@0
   390
	arg=args->data;
ali@0
   391
	switch(arg->type)
ali@0
   392
	{
ali@0
   393
	    case XEXPR_TYPE_FUNCTION:
ali@0
   394
		g_warn_if_reached();
ali@0
   395
		break;
ali@0
   396
	    case XEXPR_TYPE_INVOCATION:
ali@0
   397
		g_warn_if_reached();
ali@0
   398
		break;
ali@0
   399
	    case XEXPR_TYPE_STRING:
ali@0
   400
		value=g_ascii_strtod(arg->u.string,NULL);
ali@0
   401
		if (value==HUGE_VAL || value==-HUGE_VAL)
ali@0
   402
		{
ali@0
   403
		    g_set_error(err,XEXPR_EVAL_ERROR,XEXPR_EVAL_ERROR_FAILED,
ali@0
   404
		      "Numeric overflow on %s",arg->u.string);
ali@0
   405
		    return NULL;
ali@0
   406
		}
ali@0
   407
		break;
ali@0
   408
	    case XEXPR_TYPE_INTEGER:
ali@0
   409
		if (arg->u.integer<-DBL_MAX || arg->u.integer>DBL_MAX)
ali@0
   410
		{
ali@0
   411
		    g_set_error(err,XEXPR_EVAL_ERROR,XEXPR_EVAL_ERROR_FAILED,
ali@0
   412
		      "Numeric overflow on %lld",arg->u.integer);
ali@0
   413
		    return NULL;
ali@0
   414
		}
ali@0
   415
		value=(double)arg->u.integer;
ali@0
   416
		break;
ali@0
   417
	    case XEXPR_TYPE_NUMBER:
ali@0
   418
		value=arg->u.number;
ali@0
   419
		break;
ali@0
   420
	}
ali@0
   421
	args=args->next;
ali@0
   422
    }
ali@0
   423
    return xexpr_new_number(value);
ali@0
   424
}
ali@0
   425
ali@0
   426
static XexprConstant *xexpr_do_true(Xexpr *xexpr,GSList *bindings,GSList *args,
ali@0
   427
  GError **err)
ali@0
   428
{
ali@0
   429
    return xexpr_new_invocation(NULL,"true",NULL,NULL);
ali@0
   430
}
ali@0
   431
ali@0
   432
static XexprConstant *xexpr_do_false(Xexpr *xexpr,GSList *bindings,GSList *args,
ali@0
   433
  GError **err)
ali@0
   434
{
ali@0
   435
    return xexpr_new_invocation(NULL,"false",NULL,NULL);
ali@0
   436
}
ali@0
   437
ali@0
   438
static XexprConstant *xexpr_do_nil(Xexpr *xexpr,GSList *bindings,GSList *args,
ali@0
   439
  GError **err)
ali@0
   440
{
ali@0
   441
    return xexpr_new_invocation(NULL,"nil",NULL,NULL);
ali@0
   442
}
ali@0
   443
ali@0
   444
typedef struct xexpr_accumulator {
ali@0
   445
    gboolean first;
ali@0
   446
    Xexpr *xexpr;
ali@0
   447
    GSList *args;
ali@0
   448
    const char *result_binding;
ali@0
   449
    XexprConstant *arg,*result;
ali@0
   450
} XexprAccumulator;
ali@0
   451
ali@0
   452
static gboolean xexpr_accumulator_init(XexprAccumulator *accumulator,
ali@0
   453
  Xexpr *xexpr,GSList *args,gboolean bind_result,GError **err)
ali@0
   454
{
ali@0
   455
    XexprConstant *arg;
ali@0
   456
    accumulator->xexpr=xexpr;
ali@0
   457
    accumulator->args=args;
ali@0
   458
    accumulator->arg=NULL;
ali@0
   459
    accumulator->first=TRUE;
ali@0
   460
    if (!accumulator->args)
ali@0
   461
    {
ali@0
   462
	accumulator->result_binding=NULL;
ali@0
   463
	accumulator->result=xexpr_new_invocation(NULL,"nil",NULL,NULL);
ali@0
   464
	return TRUE;
ali@0
   465
    }
ali@0
   466
    arg=accumulator->args->data;
ali@0
   467
    /*
ali@0
   468
     * If arg invokes a function with no bindings and no arguments
ali@0
   469
     * then the result is accumulated in the function itself.
ali@0
   470
     * This allows constructs such as:
ali@0
   471
     *	<subtract><x/>1</subtract>
ali@0
   472
     * as a shorthand for:
ali@0
   473
     *	<set name="x"><subtract><get><x/></get>1</subtract></set>
ali@0
   474
     * This is implied by http://www.w3.org/TR/xexpr/#id-0045
ali@0
   475
     *
ali@0
   476
     * Note that since the value accumulates:
ali@0
   477
     *	<add x="1"><x/><x/><x/></add>
ali@0
   478
     * results in 4 rather than 3.
ali@0
   479
     */
ali@0
   480
    if (bind_result && arg->type==XEXPR_TYPE_INVOCATION &&
ali@0
   481
      !arg->u.invocation->bindings && !arg->u.invocation->constants)
ali@0
   482
	accumulator->result_binding=arg->u.invocation->function;
ali@0
   483
    else
ali@0
   484
	accumulator->result_binding=NULL;
ali@0
   485
    accumulator->result=xexpr_constant_evaluate(xexpr,arg,err);
ali@0
   486
    while(accumulator->result && accumulator->result->type==XEXPR_TYPE_FUNCTION)
ali@0
   487
    {
ali@0
   488
	arg=xexpr_constant_evaluate(xexpr,accumulator->result,err);
ali@0
   489
	xexpr_constant_free(accumulator->result);
ali@0
   490
	accumulator->result=arg;
ali@0
   491
    }
ali@0
   492
    return !!accumulator->result;
ali@0
   493
}
ali@0
   494
ali@0
   495
static gboolean xexpr_accumulator_next(XexprAccumulator *accumulator)
ali@0
   496
{
ali@0
   497
    if (accumulator->result_binding && !accumulator->first)
ali@0
   498
    {
ali@0
   499
	xexpr_var_set(accumulator->xexpr,accumulator->result_binding,
ali@0
   500
	  accumulator->result);
ali@0
   501
	if (xexpr_is_tracing(accumulator->xexpr,accumulator->result_binding))
ali@0
   502
	{
ali@0
   503
	    printf("\nChanged variable %s\n",accumulator->result_binding);
ali@0
   504
	    xexpr_stack_dump(accumulator->xexpr,stdout);
ali@0
   505
	}
ali@0
   506
    }
ali@0
   507
    accumulator->args=accumulator->args->next;
ali@0
   508
    accumulator->first=FALSE;
ali@0
   509
    return !!accumulator->args;
ali@0
   510
}
ali@0
   511
ali@0
   512
static XexprConstant *xexpr_accumulator_get(XexprAccumulator *accumulator,
ali@0
   513
  GError **err)
ali@0
   514
{
ali@0
   515
    if (accumulator->arg)
ali@0
   516
	xexpr_constant_free(accumulator->arg);
ali@0
   517
    accumulator->arg=xexpr_constant_evaluate(accumulator->xexpr,
ali@0
   518
      accumulator->args->data,err);
ali@0
   519
    return accumulator->arg;
ali@0
   520
}
ali@0
   521
ali@0
   522
static XexprConstant *xexpr_accumulator_finish(XexprAccumulator *accumulator)
ali@0
   523
{
ali@0
   524
    if (accumulator->arg)
ali@0
   525
	xexpr_constant_free(accumulator->arg);
ali@0
   526
    return accumulator->result;
ali@0
   527
}
ali@0
   528
ali@0
   529
static void xexpr_accumulator_abort(XexprAccumulator *accumulator)
ali@0
   530
{
ali@0
   531
    xexpr_constant_free(xexpr_accumulator_finish(accumulator));
ali@0
   532
}
ali@0
   533
ali@0
   534
static XexprConstant *xexpr_do_add(Xexpr *xexpr,GSList *bindings,GSList *args,
ali@0
   535
  GError **err)
ali@0
   536
{
ali@0
   537
    XexprConstant *arg;
ali@0
   538
    gchar *str;
ali@0
   539
    XexprAccumulator accumulator;
ali@0
   540
    if (!xexpr_accumulator_init(&accumulator,xexpr,args,TRUE,err))
ali@0
   541
	return NULL;
ali@0
   542
    while(xexpr_accumulator_next(&accumulator))
ali@0
   543
    {
ali@0
   544
	arg=xexpr_accumulator_get(&accumulator,err);
ali@0
   545
	if (!arg)
ali@0
   546
	{
ali@0
   547
	    xexpr_accumulator_abort(&accumulator);
ali@0
   548
	    return NULL;
ali@0
   549
	}
ali@0
   550
	switch(accumulator.result->type)
ali@0
   551
	{
ali@0
   552
	    case XEXPR_TYPE_INVOCATION:
ali@0
   553
	    case XEXPR_TYPE_FUNCTION:
ali@0
   554
		g_set_error(err,XEXPR_EVAL_ERROR,XEXPR_EVAL_ERROR_FAILED,
ali@0
   555
		  "Attempt to perform arithmetic op on a function");
ali@0
   556
		xexpr_accumulator_abort(&accumulator);
ali@0
   557
		return NULL;
ali@0
   558
		break;
ali@0
   559
	    case XEXPR_TYPE_STRING:
ali@0
   560
		switch(arg->type)
ali@0
   561
		{
ali@0
   562
		    case XEXPR_TYPE_FUNCTION:
ali@0
   563
		    case XEXPR_TYPE_INVOCATION:
ali@0
   564
			g_set_error(err,XEXPR_EVAL_ERROR,
ali@0
   565
			  XEXPR_EVAL_ERROR_FAILED,
ali@0
   566
			  "Attempt to perform arithmetic op on a function");
ali@0
   567
			xexpr_accumulator_abort(&accumulator);
ali@0
   568
			return NULL;
ali@0
   569
		    case XEXPR_TYPE_STRING:
ali@0
   570
			str=g_strconcat(accumulator.result->u.string,
ali@0
   571
			  arg->u.string,NULL);
ali@0
   572
			g_free(accumulator.result->u.string);
ali@0
   573
			accumulator.result->u.string=str;
ali@0
   574
			break;
ali@0
   575
		    case XEXPR_TYPE_INTEGER:
ali@0
   576
			str=g_strdup_printf("%s%lld",
ali@0
   577
			  accumulator.result->u.string,arg->u.integer);
ali@0
   578
			g_free(accumulator.result->u.string);
ali@0
   579
			accumulator.result->u.string=str;
ali@0
   580
			break;
ali@0
   581
		    case XEXPR_TYPE_NUMBER:
ali@0
   582
			str=g_strdup_printf("%s%lg",
ali@0
   583
			  accumulator.result->u.string,arg->u.number);
ali@0
   584
			g_free(accumulator.result->u.string);
ali@0
   585
			accumulator.result->u.string=str;
ali@0
   586
			break;
ali@0
   587
		}
ali@0
   588
		break;
ali@0
   589
	    case XEXPR_TYPE_INTEGER:
ali@0
   590
		if (arg->type==XEXPR_TYPE_INTEGER)
ali@0
   591
		{
ali@0
   592
		    accumulator.result->u.integer+=arg->u.integer;
ali@0
   593
		    break;
ali@0
   594
		}
ali@0
   595
		else if (arg->type==XEXPR_TYPE_NUMBER)
ali@0
   596
		{
ali@0
   597
		    accumulator.result->type=XEXPR_TYPE_NUMBER;
ali@0
   598
		    accumulator.result->u.number=accumulator.result->u.integer;
ali@0
   599
		}
ali@0
   600
		/* Fall through */
ali@0
   601
	    case XEXPR_TYPE_NUMBER:
ali@0
   602
		if (arg->type==XEXPR_TYPE_INTEGER)
ali@0
   603
		    accumulator.result->u.number+=arg->u.integer;
ali@0
   604
		else if (arg->type==XEXPR_TYPE_NUMBER)
ali@0
   605
		    accumulator.result->u.number+=arg->u.number;
ali@0
   606
		else
ali@0
   607
		{
ali@0
   608
		    g_set_error(err,XEXPR_EVAL_ERROR,XEXPR_EVAL_ERROR_FAILED,
ali@0
   609
		      "Attempt to add non-numeric to numeric quantity");
ali@0
   610
		    xexpr_accumulator_abort(&accumulator);
ali@0
   611
		    return NULL;
ali@0
   612
		}
ali@0
   613
		break;
ali@0
   614
	}
ali@0
   615
    }
ali@0
   616
    return xexpr_accumulator_finish(&accumulator);
ali@0
   617
}
ali@0
   618
ali@0
   619
static XexprConstant *xexpr_do_subtract(Xexpr *xexpr,GSList *bindings,
ali@0
   620
  GSList *args,GError **err)
ali@0
   621
{
ali@0
   622
    XexprConstant *arg;
ali@0
   623
    XexprAccumulator accumulator;
ali@0
   624
    if (!xexpr_accumulator_init(&accumulator,xexpr,args,TRUE,err))
ali@0
   625
	return NULL;
ali@0
   626
    while(xexpr_accumulator_next(&accumulator))
ali@0
   627
    {
ali@0
   628
	arg=xexpr_accumulator_get(&accumulator,err);
ali@0
   629
	if (!arg)
ali@0
   630
	{
ali@0
   631
	    xexpr_accumulator_abort(&accumulator);
ali@0
   632
	    return NULL;
ali@0
   633
	}
ali@0
   634
	if (arg->type!=XEXPR_TYPE_INTEGER && arg->type!=XEXPR_TYPE_NUMBER)
ali@0
   635
	{
ali@0
   636
invalid_args:
ali@0
   637
	    g_set_error(err,XEXPR_EVAL_ERROR,XEXPR_EVAL_ERROR_FAILED,
ali@0
   638
	      "Attempt to perform subtraction on a non-numeric quantity");
ali@0
   639
	    xexpr_accumulator_abort(&accumulator);
ali@0
   640
	    return NULL;
ali@0
   641
	}
ali@0
   642
	switch(accumulator.result->type)
ali@0
   643
	{
ali@0
   644
	    case XEXPR_TYPE_INTEGER:
ali@0
   645
		if (arg->type==XEXPR_TYPE_INTEGER)
ali@0
   646
		{
ali@0
   647
		    accumulator.result->u.integer-=arg->u.integer;
ali@0
   648
		    break;
ali@0
   649
		}
ali@0
   650
		else
ali@0
   651
		{
ali@0
   652
		    accumulator.result->type=XEXPR_TYPE_NUMBER;
ali@0
   653
		    accumulator.result->u.number=accumulator.result->u.integer;
ali@0
   654
		}
ali@0
   655
		/* Fall through */
ali@0
   656
	    case XEXPR_TYPE_NUMBER:
ali@0
   657
		if (accumulator.arg->type==XEXPR_TYPE_INTEGER)
ali@0
   658
		    accumulator.result->u.number-=arg->u.integer;
ali@0
   659
		else
ali@0
   660
		    accumulator.result->u.number-=arg->u.number;
ali@0
   661
		break;
ali@0
   662
	    default:
ali@0
   663
		goto invalid_args;
ali@0
   664
	}
ali@0
   665
    }
ali@0
   666
    return xexpr_accumulator_finish(&accumulator);
ali@0
   667
}
ali@0
   668
ali@0
   669
static XexprConstant *xexpr_do_multiply(Xexpr *xexpr,GSList *bindings,
ali@0
   670
  GSList *args,GError **err)
ali@0
   671
{
ali@0
   672
    XexprConstant *arg;
ali@0
   673
    XexprAccumulator accumulator;
ali@0
   674
    if (!xexpr_accumulator_init(&accumulator,xexpr,args,FALSE,err))
ali@0
   675
	return NULL;
ali@0
   676
    while(xexpr_accumulator_next(&accumulator))
ali@0
   677
    {
ali@0
   678
	arg=xexpr_accumulator_get(&accumulator,err);
ali@0
   679
	if (!arg)
ali@0
   680
	{
ali@0
   681
	    xexpr_accumulator_abort(&accumulator);
ali@0
   682
	    return NULL;
ali@0
   683
	}
ali@0
   684
	if (arg->type!=XEXPR_TYPE_INTEGER && arg->type!=XEXPR_TYPE_NUMBER)
ali@0
   685
	{
ali@0
   686
	    GString *str=g_string_new(NULL);
ali@0
   687
invalid_args:
ali@0
   688
	    xexpr_constant_dump_string(arg,str);
ali@0
   689
	    g_set_error(err,XEXPR_EVAL_ERROR,XEXPR_EVAL_ERROR_FAILED,
ali@0
   690
	      "Attempt to perform multiply on a non-numeric quantity: %s",
ali@0
   691
	      str->str);
ali@0
   692
	    g_string_free(str,TRUE);
ali@0
   693
	    xexpr_accumulator_abort(&accumulator);
ali@0
   694
	    return NULL;
ali@0
   695
	}
ali@0
   696
	switch(accumulator.result->type)
ali@0
   697
	{
ali@0
   698
	    case XEXPR_TYPE_INTEGER:
ali@0
   699
		if (arg->type==XEXPR_TYPE_INTEGER)
ali@0
   700
		{
ali@0
   701
		    accumulator.result->u.integer*=arg->u.integer;
ali@0
   702
		    break;
ali@0
   703
		}
ali@0
   704
		else
ali@0
   705
		{
ali@0
   706
		    accumulator.result->type=XEXPR_TYPE_NUMBER;
ali@0
   707
		    accumulator.result->u.number=accumulator.result->u.integer;
ali@0
   708
		}
ali@0
   709
		/* Fall through */
ali@0
   710
	    case XEXPR_TYPE_NUMBER:
ali@0
   711
		if (arg->type==XEXPR_TYPE_INTEGER)
ali@0
   712
		    accumulator.result->u.number*=arg->u.integer;
ali@0
   713
		else
ali@0
   714
		    accumulator.result->u.number*=arg->u.number;
ali@0
   715
		break;
ali@0
   716
	    default:
ali@0
   717
		goto invalid_args;
ali@0
   718
	}
ali@0
   719
    }
ali@0
   720
    return xexpr_accumulator_finish(&accumulator);
ali@0
   721
}
ali@0
   722
ali@0
   723
static XexprConstant *xexpr_do_divide(Xexpr *xexpr,GSList *bindings,
ali@0
   724
  GSList *args,GError **err)
ali@0
   725
{
ali@0
   726
    XexprConstant *arg;
ali@0
   727
    long long int r;
ali@0
   728
    XexprAccumulator accumulator;
ali@0
   729
    if (!xexpr_accumulator_init(&accumulator,xexpr,args,FALSE,err))
ali@0
   730
	return NULL;
ali@0
   731
    while(xexpr_accumulator_next(&accumulator))
ali@0
   732
    {
ali@0
   733
	arg=xexpr_accumulator_get(&accumulator,err);
ali@0
   734
	if (!arg)
ali@0
   735
	{
ali@0
   736
	    xexpr_accumulator_abort(&accumulator);
ali@0
   737
	    return NULL;
ali@0
   738
	}
ali@0
   739
	if (arg->type!=XEXPR_TYPE_INTEGER && arg->type!=XEXPR_TYPE_NUMBER)
ali@0
   740
	{
ali@0
   741
invalid_args:
ali@0
   742
	    g_set_error(err,XEXPR_EVAL_ERROR,XEXPR_EVAL_ERROR_FAILED,
ali@0
   743
	      "Attempt to perform divide on a non-numeric quantity");
ali@0
   744
	    xexpr_accumulator_abort(&accumulator);
ali@0
   745
	    return NULL;
ali@0
   746
	}
ali@0
   747
	switch(accumulator.result->type)
ali@0
   748
	{
ali@0
   749
	    case XEXPR_TYPE_INTEGER:
ali@0
   750
		if (arg->type==XEXPR_TYPE_INTEGER)
ali@0
   751
		{
ali@0
   752
		    r=accumulator.result->u.integer/arg->u.integer;
ali@0
   753
		    if (r*arg->u.integer==accumulator.result->u.integer)
ali@0
   754
		    {
ali@0
   755
			accumulator.result->u.integer=r;
ali@0
   756
			break;
ali@0
   757
		    }
ali@0
   758
		}
ali@0
   759
		accumulator.result->type=XEXPR_TYPE_NUMBER;
ali@0
   760
		accumulator.result->u.number=accumulator.result->u.integer;
ali@0
   761
		/* Fall through */
ali@0
   762
	    case XEXPR_TYPE_NUMBER:
ali@0
   763
		if (arg->type==XEXPR_TYPE_INTEGER)
ali@0
   764
		    accumulator.result->u.number/=arg->u.integer;
ali@0
   765
		else
ali@0
   766
		    accumulator.result->u.number/=arg->u.number;
ali@0
   767
		break;
ali@0
   768
	    default:
ali@0
   769
		goto invalid_args;
ali@0
   770
	}
ali@0
   771
    }
ali@0
   772
    return xexpr_accumulator_finish(&accumulator);
ali@0
   773
}
ali@0
   774
ali@0
   775
/**
ali@0
   776
 * xexpr_constant_cast:
ali@0
   777
 * @constant: an #XexprConstant to cast
ali@0
   778
 * @to: type to cast @constant to
ali@0
   779
 *
ali@0
   780
 * Attempt to perform an implicit cast of @constant to the given type @to.
ali@0
   781
 * Implicit casting can only be performed between the two numeric types
ali@0
   782
 * and then only where the quantity can be represented without loss of
ali@0
   783
 * precision.
ali@0
   784
 *
ali@0
   785
 * Return value: %TRUE if @constant is now of type @to.
ali@0
   786
 */
ali@0
   787
gboolean xexpr_constant_cast(XexprConstant *constant,XexprType to)
ali@0
   788
{
ali@0
   789
    double dummy;
ali@0
   790
    if (constant->type==to)
ali@0
   791
	return TRUE;
ali@0
   792
    if (constant->type==XEXPR_TYPE_NUMBER && to==XEXPR_TYPE_INTEGER &&
ali@0
   793
      constant->u.number>=LLONG_MIN && constant->u.number<=LLONG_MAX &&
ali@0
   794
      !modf(constant->u.number,&dummy))
ali@0
   795
    {
ali@0
   796
	constant->type=XEXPR_TYPE_INTEGER;
ali@0
   797
	constant->u.integer=constant->u.number;
ali@0
   798
	return TRUE;
ali@0
   799
    }
ali@0
   800
    else if (constant->type==XEXPR_TYPE_INTEGER && to==XEXPR_TYPE_NUMBER &&
ali@0
   801
      constant->u.integer+1.0!=(double)constant->u.integer &&
ali@0
   802
      constant->u.integer-1.0!=(double)constant->u.integer)
ali@0
   803
    {
ali@0
   804
	constant->type=XEXPR_TYPE_NUMBER;
ali@0
   805
	constant->u.number=constant->u.integer;
ali@0
   806
	return TRUE;
ali@0
   807
    }
ali@0
   808
    else
ali@0
   809
	return FALSE;
ali@0
   810
}
ali@0
   811
ali@0
   812
static gboolean xexpr_constant_boolean(Xexpr *xexpr,XexprConstant *constant,
ali@0
   813
  GError **err)
ali@0
   814
{
ali@0
   815
    gboolean retval;
ali@0
   816
    XexprConstant *result;
ali@0
   817
    switch (constant->type)
ali@0
   818
    {
ali@0
   819
	case XEXPR_TYPE_NUMBER:
ali@0
   820
	    return !(constant->u.number==0);
ali@0
   821
	case XEXPR_TYPE_INTEGER:
ali@0
   822
	    return !!constant->u.integer;
ali@0
   823
	case XEXPR_TYPE_STRING:
ali@0
   824
	    return !!*constant->u.string;
ali@0
   825
	case XEXPR_TYPE_INVOCATION:
ali@0
   826
	    return strcmp(constant->u.invocation->function,"false") &&
ali@0
   827
	      strcmp(constant->u.invocation->function,"nil");
ali@0
   828
	case XEXPR_TYPE_FUNCTION:
ali@0
   829
	    /*
ali@0
   830
	     * See http://www.w3.org/TR/xexpr/#id-0038
ali@0
   831
	     */
ali@0
   832
	    result=xexpr_constant_evaluate(xexpr,constant,err);
ali@0
   833
	    if (!result)
ali@0
   834
		return FALSE;
ali@0
   835
	    retval=xexpr_constant_boolean(xexpr,result,err);
ali@0
   836
	    xexpr_constant_free(result);
ali@0
   837
	    return retval;
ali@0
   838
    }
ali@0
   839
    return FALSE;
ali@0
   840
}
ali@0
   841
ali@0
   842
/*
ali@0
   843
 * Returns: 0 if equal, <0 if value1 < value2, >0 if value1 > value2,
ali@0
   844
 *          NaN if value1 is not comparible with value2.
ali@0
   845
 */
ali@0
   846
static double xexpr_constant_compare(Xexpr *xexpr,XexprConstant *v1,
ali@0
   847
  XexprConstant *v2)
ali@0
   848
{
ali@0
   849
    double retval=0.0/0.0;
ali@0
   850
    XexprConstant *value1,*value2;
ali@0
   851
    value1=xexpr_constant_evaluate(xexpr,v1,NULL);
ali@0
   852
    if (!value1)
ali@0
   853
	value1=xexpr_constant_dup(v1);
ali@0
   854
    while(value1->type==XEXPR_TYPE_FUNCTION)
ali@0
   855
    {
ali@0
   856
	v1=xexpr_constant_evaluate(xexpr,value1,NULL);
ali@0
   857
	if (v1)
ali@0
   858
	{
ali@0
   859
	    xexpr_constant_free(value1);
ali@0
   860
	    value1=v1;
ali@0
   861
	}
ali@0
   862
	else
ali@0
   863
	    break;
ali@0
   864
    }
ali@0
   865
    value2=xexpr_constant_evaluate(xexpr,v2,NULL);
ali@0
   866
    if (!value2)
ali@0
   867
	value2=xexpr_constant_dup(v2);
ali@0
   868
    while(value2->type==XEXPR_TYPE_FUNCTION)
ali@0
   869
    {
ali@0
   870
	v2=xexpr_constant_evaluate(xexpr,value2,NULL);
ali@0
   871
	if (v2)
ali@0
   872
	{
ali@0
   873
	    xexpr_constant_free(value2);
ali@0
   874
	    value2=v2;
ali@0
   875
	}
ali@0
   876
	else
ali@0
   877
	    break;
ali@0
   878
    }
ali@0
   879
    if (value1->type!=value2->type)
ali@0
   880
    {
ali@0
   881
	if (!xexpr_constant_cast(value1,value2->type) &&
ali@0
   882
	  !xexpr_constant_cast(value2,value1->type))
ali@0
   883
	{
ali@0
   884
	    xexpr_constant_free(value1);
ali@0
   885
	    xexpr_constant_free(value2);
ali@0
   886
	    return 0.0/0.0;
ali@0
   887
	}
ali@0
   888
    }
ali@0
   889
    switch(value1->type)
ali@0
   890
    {
ali@0
   891
	case XEXPR_TYPE_FUNCTION:
ali@0
   892
	    g_warn_if_reached();
ali@0
   893
	    break;
ali@0
   894
	case XEXPR_TYPE_INVOCATION:
ali@0
   895
	    g_warn_if_fail(value1->u.invocation->bindings==NULL);
ali@0
   896
	    g_warn_if_fail(value2->u.invocation->bindings==NULL);
ali@0
   897
	    g_warn_if_fail(value1->u.invocation->constants==NULL);
ali@0
   898
	    g_warn_if_fail(value2->u.invocation->constants==NULL);
ali@0
   899
	    retval=strcmp(value1->u.invocation->function,
ali@0
   900
	      value2->u.invocation->function);
ali@0
   901
	    break;
ali@0
   902
	case XEXPR_TYPE_STRING:
ali@0
   903
	    retval=strcmp(value1->u.string,value2->u.string);
ali@0
   904
	    break;
ali@0
   905
	case XEXPR_TYPE_INTEGER:
ali@0
   906
	    retval=value1->u.integer-value2->u.integer;
ali@0
   907
	    break;
ali@0
   908
	case XEXPR_TYPE_NUMBER:
ali@0
   909
	    retval=value1->u.number-value2->u.number;
ali@0
   910
	    break;
ali@0
   911
    }
ali@0
   912
    xexpr_constant_free(value1);
ali@0
   913
    xexpr_constant_free(value2);
ali@0
   914
    return retval;
ali@0
   915
}
ali@0
   916
ali@0
   917
static XexprConstant *xexpr_do_eq(Xexpr *xexpr,GSList *bindings,GSList *args,
ali@0
   918
  GError **err)
ali@0
   919
{
ali@0
   920
    XexprConstant *arg,*standard=NULL;
ali@0
   921
    while(args)
ali@0
   922
    {
ali@0
   923
	arg=args->data;
ali@0
   924
	if (!standard)
ali@0
   925
	    standard=arg;
ali@0
   926
	else if (xexpr_constant_compare(xexpr,standard,arg))
ali@0
   927
	    return xexpr_new_invocation(NULL,"false",NULL,NULL);
ali@0
   928
	args=args->next;
ali@0
   929
    }
ali@0
   930
    return xexpr_new_invocation(NULL,"true",NULL,NULL);
ali@0
   931
}
ali@0
   932
ali@0
   933
static XexprConstant *xexpr_do_neq(Xexpr *xexpr,GSList *bindings,GSList *args,
ali@0
   934
  GError **err)
ali@0
   935
{
ali@0
   936
    XexprConstant *arg;
ali@0
   937
    GSList *lnk;
ali@0
   938
    while(args)
ali@0
   939
    {
ali@0
   940
	arg=args->data;
ali@0
   941
	for(lnk=args->next;lnk;lnk=lnk->next)
ali@0
   942
	    if (!xexpr_constant_compare(xexpr,arg,lnk->data))
ali@0
   943
		return xexpr_new_invocation(NULL,"false",NULL,NULL);
ali@0
   944
	args=args->next;
ali@0
   945
    }
ali@0
   946
    return xexpr_new_invocation(NULL,"true",NULL,NULL);
ali@0
   947
}
ali@0
   948
ali@0
   949
static XexprConstant *xexpr_do_leq(Xexpr *xexpr,GSList *bindings,GSList *args,
ali@0
   950
  GError **err)
ali@0
   951
{
ali@0
   952
    XexprConstant *arg,*standard=NULL;
ali@0
   953
    while(args)
ali@0
   954
    {
ali@0
   955
	arg=args->data;
ali@0
   956
	if (standard && !(xexpr_constant_compare(xexpr,standard,arg)<=0))
ali@0
   957
	    return xexpr_new_invocation(NULL,"false",NULL,NULL);
ali@0
   958
	standard=arg;
ali@0
   959
	args=args->next;
ali@0
   960
    }
ali@0
   961
    return xexpr_new_invocation(NULL,"true",NULL,NULL);
ali@0
   962
}
ali@0
   963
ali@0
   964
static XexprConstant *xexpr_do_geq(Xexpr *xexpr,GSList *bindings,GSList *args,
ali@0
   965
  GError **err)
ali@0
   966
{
ali@0
   967
    XexprConstant *arg,*standard=NULL;
ali@0
   968
    while(args)
ali@0
   969
    {
ali@0
   970
	arg=args->data;
ali@0
   971
	if (standard && !(xexpr_constant_compare(xexpr,standard,arg)>=0))
ali@0
   972
	    return xexpr_new_invocation(NULL,"false",NULL,NULL);
ali@0
   973
	standard=arg;
ali@0
   974
	args=args->next;
ali@0
   975
    }
ali@0
   976
    return xexpr_new_invocation(NULL,"true",NULL,NULL);
ali@0
   977
}
ali@0
   978
ali@0
   979
static XexprConstant *xexpr_do_lt(Xexpr *xexpr,GSList *bindings,GSList *args,
ali@0
   980
  GError **err)
ali@0
   981
{
ali@0
   982
    XexprConstant *arg,*standard=NULL;
ali@0
   983
    while(args)
ali@0
   984
    {
ali@0
   985
	arg=args->data;
ali@0
   986
	if (standard && !(xexpr_constant_compare(xexpr,standard,arg)<0))
ali@0
   987
	    return xexpr_new_invocation(NULL,"false",NULL,NULL);
ali@0
   988
	standard=arg;
ali@0
   989
	args=args->next;
ali@0
   990
    }
ali@0
   991
    return xexpr_new_invocation(NULL,"true",NULL,NULL);
ali@0
   992
}
ali@0
   993
ali@0
   994
static XexprConstant *xexpr_do_gt(Xexpr *xexpr,GSList *bindings,GSList *args,
ali@0
   995
  GError **err)
ali@0
   996
{
ali@0
   997
    XexprConstant *arg,*standard=NULL;
ali@0
   998
    while(args)
ali@0
   999
    {
ali@0
  1000
	arg=args->data;
ali@0
  1001
	if (standard && !(xexpr_constant_compare(xexpr,standard,arg)>0))
ali@0
  1002
	    return xexpr_new_invocation(NULL,"false",NULL,NULL);
ali@0
  1003
	standard=arg;
ali@0
  1004
	args=args->next;
ali@0
  1005
    }
ali@0
  1006
    return xexpr_new_invocation(NULL,"true",NULL,NULL);
ali@0
  1007
}
ali@0
  1008
ali@0
  1009
static XexprConstant *xexpr_do_and(Xexpr *xexpr,GSList *bindings,GSList *args,
ali@0
  1010
  GError **err)
ali@0
  1011
{
ali@0
  1012
    GError *tmp_err=NULL;
ali@0
  1013
    XexprConstant *arg;
ali@0
  1014
    XexprAccumulator accumulator;
ali@0
  1015
    if (!args)
ali@0
  1016
	return xexpr_new_invocation(NULL,"true",NULL,NULL);
ali@0
  1017
    if (!xexpr_accumulator_init(&accumulator,xexpr,args,FALSE,err))
ali@0
  1018
	return NULL;
ali@0
  1019
    if (!xexpr_constant_boolean(xexpr,accumulator.result,&tmp_err))
ali@0
  1020
    {
ali@0
  1021
	xexpr_accumulator_abort(&accumulator);
ali@0
  1022
	if (tmp_err)
ali@0
  1023
	{
ali@0
  1024
	    g_propagate_error(err,tmp_err);
ali@0
  1025
	    return NULL;
ali@0
  1026
	}
ali@0
  1027
	else
ali@0
  1028
	    return xexpr_new_invocation(NULL,"false",NULL,NULL);
ali@0
  1029
    }
ali@0
  1030
    while(xexpr_accumulator_next(&accumulator))
ali@0
  1031
    {
ali@0
  1032
	arg=xexpr_accumulator_get(&accumulator,err);
ali@0
  1033
	if (!arg)
ali@0
  1034
	{
ali@0
  1035
	    xexpr_accumulator_abort(&accumulator);
ali@0
  1036
	    return NULL;
ali@0
  1037
	}
ali@0
  1038
	if (!xexpr_constant_boolean(xexpr,arg,&tmp_err))
ali@0
  1039
	{
ali@0
  1040
	    xexpr_accumulator_abort(&accumulator);
ali@0
  1041
	    if (tmp_err)
ali@0
  1042
	    {
ali@0
  1043
		g_propagate_error(err,tmp_err);
ali@0
  1044
		return NULL;
ali@0
  1045
	    }
ali@0
  1046
	    else
ali@0
  1047
		return xexpr_new_invocation(NULL,"false",NULL,NULL);
ali@0
  1048
	}
ali@0
  1049
    }
ali@0
  1050
    xexpr_accumulator_abort(&accumulator);
ali@0
  1051
    return xexpr_new_invocation(NULL,"true",NULL,NULL);
ali@0
  1052
}
ali@0
  1053
ali@0
  1054
static XexprConstant *xexpr_do_or(Xexpr *xexpr,GSList *bindings,GSList *args,
ali@0
  1055
  GError **err)
ali@0
  1056
{
ali@0
  1057
    GError *tmp_err=NULL;
ali@0
  1058
    XexprConstant *arg;
ali@0
  1059
    XexprAccumulator accumulator;
ali@0
  1060
    if (!args)
ali@0
  1061
	return xexpr_new_invocation(NULL,"true",NULL,NULL);
ali@0
  1062
    if (!xexpr_accumulator_init(&accumulator,xexpr,args,FALSE,err))
ali@0
  1063
	return NULL;
ali@0
  1064
    if (xexpr_constant_boolean(xexpr,accumulator.result,&tmp_err))
ali@0
  1065
    {
ali@0
  1066
	xexpr_accumulator_abort(&accumulator);
ali@0
  1067
	return xexpr_new_invocation(NULL,"true",NULL,NULL);
ali@0
  1068
    }
ali@0
  1069
    else if (tmp_err)
ali@0
  1070
    {
ali@0
  1071
	xexpr_accumulator_abort(&accumulator);
ali@0
  1072
	g_propagate_error(err,tmp_err);
ali@0
  1073
	return NULL;
ali@0
  1074
    }
ali@0
  1075
    while(xexpr_accumulator_next(&accumulator))
ali@0
  1076
    {
ali@0
  1077
	arg=xexpr_accumulator_get(&accumulator,err);
ali@0
  1078
	if (!arg)
ali@0
  1079
	{
ali@0
  1080
	    xexpr_accumulator_abort(&accumulator);
ali@0
  1081
	    return NULL;
ali@0
  1082
	}
ali@0
  1083
	if (xexpr_constant_boolean(xexpr,arg,&tmp_err))
ali@0
  1084
	{
ali@0
  1085
	    xexpr_accumulator_abort(&accumulator);
ali@0
  1086
	    return xexpr_new_invocation(NULL,"true",NULL,NULL);
ali@0
  1087
	}
ali@0
  1088
	else if (tmp_err)
ali@0
  1089
	{
ali@0
  1090
	    xexpr_accumulator_abort(&accumulator);
ali@0
  1091
	    g_propagate_error(err,tmp_err);
ali@0
  1092
	    return NULL;
ali@0
  1093
	}
ali@0
  1094
    }
ali@0
  1095
    xexpr_accumulator_abort(&accumulator);
ali@0
  1096
    return xexpr_new_invocation(NULL,"false",NULL,NULL);
ali@0
  1097
}
ali@0
  1098
ali@0
  1099
/*
ali@0
  1100
 * <not> is a misnomer, http://www.w3.org/TR/xexpr/#id-0040
ali@0
  1101
 * makes it clear that, except in the trivial case of no arguments,
ali@0
  1102
 * <not> is simply the negation of <and>.
ali@0
  1103
 */
ali@0
  1104
static XexprConstant *xexpr_do_not(Xexpr *xexpr,GSList *bindings,GSList *args,
ali@0
  1105
  GError **err)
ali@0
  1106
{
ali@0
  1107
    gboolean test;
ali@0
  1108
    XexprConstant *result;
ali@0
  1109
    if (!args)
ali@0
  1110
	return xexpr_new_invocation(NULL,"true",NULL,NULL);
ali@0
  1111
    result=xexpr_do_and(xexpr,bindings,args,err);
ali@0
  1112
    if (!result)
ali@0
  1113
	return NULL;
ali@0
  1114
    test=xexpr_constant_boolean(xexpr,result,NULL);
ali@0
  1115
    xexpr_constant_free(result);
ali@0
  1116
    return xexpr_new_invocation(NULL,test?"false":"true",NULL,NULL);
ali@0
  1117
}
ali@0
  1118
ali@0
  1119
static XexprConstant *xexpr_do_if(Xexpr *xexpr,GSList *bindings,GSList *args,
ali@0
  1120
  GError **err)
ali@0
  1121
{
ali@0
  1122
    GError *tmp_err=NULL;
ali@0
  1123
    gboolean test;
ali@0
  1124
    XexprConstant *arg;
ali@0
  1125
    if (g_slist_length(args)<2)
ali@0
  1126
    {
ali@0
  1127
	g_set_error(err,XEXPR_EVAL_ERROR,XEXPR_EVAL_ERROR_FAILED,
ali@0
  1128
	  "<if> requires at least 2 arguments to be passed");
ali@0
  1129
	return NULL;
ali@0
  1130
    }
ali@0
  1131
    arg=xexpr_constant_evaluate(xexpr,args->data,err);
ali@0
  1132
    if (!arg)
ali@0
  1133
	return NULL;
ali@0
  1134
    test=xexpr_constant_boolean(xexpr,arg,&tmp_err);
ali@0
  1135
    if (tmp_err)
ali@0
  1136
    {
ali@0
  1137
	g_propagate_error(err,tmp_err);
ali@0
  1138
	return NULL;
ali@0
  1139
    }
ali@0
  1140
    xexpr_constant_free(arg);
ali@0
  1141
    if (test)
ali@0
  1142
	return xexpr_constant_evaluate(xexpr,args->next->data,err);
ali@0
  1143
    else if (args->next->next)
ali@0
  1144
	return xexpr_constant_evaluate(xexpr,args->next->next->data,err);
ali@0
  1145
    else
ali@0
  1146
	return xexpr_new_invocation(NULL,"nil",NULL,NULL);
ali@0
  1147
}
ali@0
  1148
ali@0
  1149
static XexprConstant *xexpr_do_switch(Xexpr *xexpr,GSList *bindings,
ali@0
  1150
  GSList *args,GError **err)
ali@0
  1151
{
ali@0
  1152
    GError *tmp_err=NULL;
ali@0
  1153
    gboolean test;
ali@0
  1154
    XexprConstant *case_arg,*arg;
ali@0
  1155
    GSList *lnk;
ali@0
  1156
    while(args)
ali@0
  1157
    {
ali@0
  1158
	case_arg=args->data;
ali@0
  1159
	if (case_arg->type!=XEXPR_TYPE_INVOCATION ||
ali@0
  1160
	  strcmp(case_arg->u.invocation->function,"case"))
ali@0
  1161
	{
ali@0
  1162
	    g_set_error(err,XEXPR_EVAL_ERROR,XEXPR_EVAL_ERROR_FAILED,
ali@0
  1163
	      "<switch> requires a list of <case> expressions");
ali@0
  1164
	    return NULL;
ali@0
  1165
	}
ali@0
  1166
	lnk=case_arg->u.invocation->constants;
ali@0
  1167
	if (!lnk)
ali@0
  1168
	{
ali@0
  1169
	    g_set_error(err,XEXPR_EVAL_ERROR,XEXPR_EVAL_ERROR_FAILED,
ali@0
  1170
	      "<case> requires a test expression");
ali@0
  1171
	    return NULL;
ali@0
  1172
	}
ali@0
  1173
	arg=xexpr_constant_evaluate(xexpr,lnk->data,err);
ali@0
  1174
	if (!arg)
ali@0
  1175
	    return NULL;
ali@0
  1176
	test=xexpr_constant_boolean(xexpr,arg,&tmp_err);
ali@0
  1177
	if (tmp_err)
ali@0
  1178
	{
ali@0
  1179
	    g_propagate_error(err,tmp_err);
ali@0
  1180
	    return NULL;
ali@0
  1181
	}
ali@0
  1182
	xexpr_constant_free(arg);
ali@0
  1183
	if (test)
ali@0
  1184
	{
ali@0
  1185
	    arg=NULL;
ali@0
  1186
	    while(lnk)
ali@0
  1187
	    {
ali@0
  1188
		if (arg)
ali@0
  1189
		    xexpr_constant_free(arg);
ali@0
  1190
		arg=xexpr_constant_evaluate(xexpr,lnk->data,err);
ali@0
  1191
		if (!arg)
ali@0
  1192
		    return NULL;
ali@0
  1193
		lnk=lnk->next;
ali@0
  1194
	    }
ali@0
  1195
	    if (!arg)
ali@0
  1196
	       arg=xexpr_new_invocation(NULL,"nil",NULL,NULL);
ali@0
  1197
	    return arg;
ali@0
  1198
	}
ali@0
  1199
	args=args->next;
ali@0
  1200
    }
ali@0
  1201
    return xexpr_new_invocation(NULL,"nil",NULL,NULL);
ali@0
  1202
}
ali@0
  1203
ali@0
  1204
static XexprConstant *xexpr_do_while(Xexpr *xexpr,GSList *bindings,GSList *args,
ali@0
  1205
  GError **err)
ali@0
  1206
{
ali@0
  1207
    GError *tmp_err=NULL;
ali@0
  1208
    gboolean test;
ali@0
  1209
    XexprConstant *arg,*result;
ali@0
  1210
    if (g_slist_length(args)!=2)
ali@0
  1211
    {
ali@0
  1212
	g_set_error(err,XEXPR_EVAL_ERROR,XEXPR_EVAL_ERROR_FAILED,
ali@0
  1213
	  "<while> takes 2 arguments");
ali@0
  1214
	return NULL;
ali@0
  1215
    }
ali@0
  1216
    result=xexpr_new_invocation(NULL,"nil",NULL,NULL);
ali@0
  1217
    do
ali@0
  1218
    {
ali@0
  1219
	arg=xexpr_constant_evaluate(xexpr,args->data,err);
ali@0
  1220
	if (!arg)
ali@0
  1221
	{
ali@0
  1222
	    xexpr_constant_free(result);
ali@0
  1223
	    return NULL;
ali@0
  1224
	}
ali@0
  1225
	test=xexpr_constant_boolean(xexpr,arg,&tmp_err);
ali@0
  1226
	if (tmp_err)
ali@0
  1227
	{
ali@0
  1228
	    g_propagate_error(err,tmp_err);
ali@0
  1229
	    return NULL;
ali@0
  1230
	}
ali@0
  1231
	xexpr_constant_free(arg);
ali@0
  1232
	if (test)
ali@0
  1233
	{
ali@0
  1234
	    xexpr_constant_free(result);
ali@0
  1235
	    result=xexpr_constant_evaluate(xexpr,args->next->data,err);
ali@0
  1236
	    if (!result)
ali@0
  1237
		return NULL;
ali@0
  1238
	}
ali@0
  1239
    } while(test);
ali@0
  1240
    return result;
ali@0
  1241
}
ali@0
  1242
ali@0
  1243
static XexprConstant *xexpr_do_do(Xexpr *xexpr,GSList *bindings,GSList *args,
ali@0
  1244
  GError **err)
ali@0
  1245
{
ali@0
  1246
    GError *tmp_err=NULL;
ali@0
  1247
    gboolean test;
ali@0
  1248
    XexprConstant *arg,*result;
ali@0
  1249
    if (g_slist_length(args)!=2)
ali@0
  1250
    {
ali@0
  1251
	g_set_error(err,XEXPR_EVAL_ERROR,XEXPR_EVAL_ERROR_FAILED,
ali@0
  1252
	  "<do> takes 2 arguments");
ali@0
  1253
	return NULL;
ali@0
  1254
    }
ali@0
  1255
    result=NULL;
ali@0
  1256
    do
ali@0
  1257
    {
ali@0
  1258
	if (result)
ali@0
  1259
	    xexpr_constant_free(result);
ali@0
  1260
	result=xexpr_constant_evaluate(xexpr,args->data,err);
ali@0
  1261
	if (!result)
ali@0
  1262
	    return NULL;
ali@0
  1263
	arg=xexpr_constant_evaluate(xexpr,args->next->data,err);
ali@0
  1264
	if (!arg)
ali@0
  1265
	{
ali@0
  1266
	    xexpr_constant_free(result);
ali@0
  1267
	    return NULL;
ali@0
  1268
	}
ali@0
  1269
	test=xexpr_constant_boolean(xexpr,arg,&tmp_err);
ali@0
  1270
	if (tmp_err)
ali@0
  1271
	{
ali@0
  1272
	    g_propagate_error(err,tmp_err);
ali@0
  1273
	    return NULL;
ali@0
  1274
	}
ali@0
  1275
	xexpr_constant_free(arg);
ali@0
  1276
    } while(test);
ali@0
  1277
    return result;
ali@0
  1278
}
ali@0
  1279
ali@0
  1280
static struct xexpr_builtin {
ali@0
  1281
    char *id;
ali@0
  1282
    gboolean evaluate_args;
ali@0
  1283
    XexprConstant *(*func)(Xexpr *xexpr,GSList *bindings,GSList *args,
ali@0
  1284
      GError **err);
ali@0
  1285
} xexpr_builtins[]={
ali@0
  1286
    { "define",		FALSE,	xexpr_do_define },
ali@0
  1287
    { "print",		TRUE,	xexpr_do_print },
ali@0
  1288
    { "println",	TRUE,	xexpr_do_println },
ali@0
  1289
    { "get",		TRUE,	xexpr_do_get },
ali@0
  1290
    { "set",		TRUE,	xexpr_do_set },
ali@0
  1291
    { "expr",		TRUE,	xexpr_do_expr },
ali@0
  1292
    { "xexpr",		TRUE,	xexpr_do_expr },
ali@0
  1293
    { "return",		TRUE,	xexpr_do_return },
ali@0
  1294
    { "string",		TRUE,	xexpr_do_string },
ali@0
  1295
    { "integer",	TRUE,	xexpr_do_integer },
ali@0
  1296
    { "float",		TRUE,	xexpr_do_float },
ali@0
  1297
    { "true",		TRUE,	xexpr_do_true },
ali@0
  1298
    { "false",		TRUE,	xexpr_do_false },
ali@0
  1299
    { "nil",		TRUE,	xexpr_do_nil },
ali@0
  1300
    { "add",		FALSE,	xexpr_do_add },
ali@0
  1301
    { "subtract",	FALSE,	xexpr_do_subtract },
ali@0
  1302
    { "multiply",	FALSE,	xexpr_do_multiply },
ali@0
  1303
    { "divide",		FALSE,	xexpr_do_divide },
ali@0
  1304
    { "eq",		TRUE,	xexpr_do_eq },
ali@0
  1305
    { "neq",		TRUE,	xexpr_do_neq },
ali@0
  1306
    { "leq",		TRUE,	xexpr_do_leq },
ali@0
  1307
    { "geq",		TRUE,	xexpr_do_geq },
ali@0
  1308
    { "lt",		TRUE,	xexpr_do_lt },
ali@0
  1309
    { "gt",		TRUE,	xexpr_do_gt },
ali@0
  1310
    { "and",		FALSE,	xexpr_do_and },
ali@0
  1311
    { "or",		FALSE,	xexpr_do_or },
ali@0
  1312
    { "not",		FALSE,	xexpr_do_not },
ali@0
  1313
    { "if",		FALSE,	xexpr_do_if },
ali@0
  1314
    { "switch",		FALSE,	xexpr_do_switch },
ali@0
  1315
    { "while",		FALSE,	xexpr_do_while },
ali@0
  1316
    { "do",		FALSE,	xexpr_do_do },
ali@0
  1317
};
ali@0
  1318
ali@0
  1319
static gboolean xexpr_defineable_id(const char *id,GError **err)
ali@0
  1320
{
ali@0
  1321
    int i;
ali@0
  1322
    for(i=0;i<G_N_ELEMENTS(xexpr_builtins);i++)
ali@0
  1323
	if (!strcmp(xexpr_builtins[i].id,id))
ali@0
  1324
	{
ali@0
  1325
	    g_set_error(err,XEXPR_PARSE_ERROR,XEXPR_EVAL_ERROR_FAILED,
ali@0
  1326
	      "Can't define reserved XEXPR ID \"%s\"",id);
ali@0
  1327
	    return FALSE;
ali@0
  1328
	}
ali@0
  1329
    return TRUE;
ali@0
  1330
}
ali@0
  1331
ali@0
  1332
static XexprConstant *xexpr_closure(Xexpr *xexpr,XexprConstant *constant,
ali@0
  1333
  GError **err)
ali@0
  1334
{
ali@0
  1335
    GSList *lnk;
ali@0
  1336
    XexprConstant *result,*tmp;
ali@0
  1337
    g_return_val_if_fail(constant->type==XEXPR_TYPE_FUNCTION,NULL);
ali@0
  1338
    result=NULL;
ali@0
  1339
    for(lnk=constant->u.function->constants;lnk;lnk=lnk->next)
ali@0
  1340
    {
ali@0
  1341
	if (result)
ali@0
  1342
	    xexpr_constant_free(result);
ali@0
  1343
	result=xexpr_constant_evaluate(xexpr,lnk->data,err);
ali@0
  1344
	if (!result)
ali@0
  1345
	    return NULL;
ali@0
  1346
	if (result->type==XEXPR_TYPE_INVOCATION &&
ali@0
  1347
	  !strcmp(result->u.invocation->function,"return"))
ali@0
  1348
	{
ali@0
  1349
	    lnk=g_slist_last(result->u.invocation->constants);
ali@0
  1350
	    tmp=xexpr_constant_dup(lnk->data);
ali@0
  1351
	    xexpr_constant_free(result);
ali@0
  1352
	    result=tmp;
ali@0
  1353
	    break;
ali@0
  1354
	}
ali@0
  1355
    }
ali@0
  1356
    if (!result)
ali@0
  1357
       result=xexpr_new_invocation(NULL,"nil",NULL,NULL);
ali@0
  1358
    return result;
ali@0
  1359
}
ali@0
  1360
ali@0
  1361
static XexprConstant *xexpr_function_evaluate(Xexpr *xexpr,const char *ns,
ali@0
  1362
  const char *id,GSList *bindings,GSList *args,GError **err)
ali@0
  1363
{
ali@0
  1364
    int builtin;
ali@0
  1365
    gboolean evaluate_args;
ali@0
  1366
    XexprBinding *binding;
ali@0
  1367
    XexprExtension *ext;
ali@0
  1368
    XexprConstant *value,*function_def,*result;
ali@0
  1369
    const char *unbound_parameter;
ali@0
  1370
    GSList *unbound_args=args,*lnk,*actual,*unbound_parameters;
ali@0
  1371
    if (ns)
ali@0
  1372
    {
ali@0
  1373
	for(lnk=xexpr_extensions;lnk;lnk=lnk->next)
ali@0
  1374
	{
ali@0
  1375
	    ext=lnk->data;
ali@0
  1376
	    if (!strcmp(ext->ns,ns))
ali@0
  1377
		return ext->function_evaluate(xexpr,ns,id,bindings,args,err);
ali@0
  1378
	}
ali@0
  1379
	g_set_error(err,XEXPR_EVAL_ERROR,XEXPR_EVAL_ERROR_FAILED,
ali@0
  1380
	  "Unknown namespace \"%s\"",ns);
ali@0
  1381
	return NULL;
ali@0
  1382
    }
ali@0
  1383
    for(builtin=0;builtin<G_N_ELEMENTS(xexpr_builtins);builtin++)
ali@0
  1384
	if (!strcmp(xexpr_builtins[builtin].id,id))
ali@0
  1385
	{
ali@0
  1386
	    evaluate_args=xexpr_builtins[builtin].evaluate_args;
ali@0
  1387
	    break;
ali@0
  1388
	}
ali@0
  1389
    if (builtin==G_N_ELEMENTS(xexpr_builtins))
ali@0
  1390
    {
ali@0
  1391
	function_def=xexpr_var_get(xexpr,id);
ali@0
  1392
	if (!function_def)
ali@0
  1393
	{
ali@0
  1394
	    g_set_error(err,XEXPR_EVAL_ERROR,XEXPR_EVAL_ERROR_FAILED,
ali@0
  1395
	      "Undefined function: \"%s\"",id);
ali@0
  1396
	    return NULL;
ali@0
  1397
	}
ali@0
  1398
	else if (function_def->type!=XEXPR_TYPE_FUNCTION)
ali@0
  1399
	    return xexpr_constant_evaluate(xexpr,function_def,err);
ali@0
  1400
	unbound_parameters=g_slist_copy(function_def->u.function->args);
ali@0
  1401
	evaluate_args=TRUE;
ali@0
  1402
    }
ali@0
  1403
    else
ali@0
  1404
    {
ali@0
  1405
	function_def=NULL;
ali@0
  1406
	unbound_parameters=NULL;
ali@0
  1407
    }
ali@0
  1408
    for(lnk=bindings;lnk;lnk=lnk->next)
ali@0
  1409
    {
ali@0
  1410
	binding=lnk->data;
ali@0
  1411
	actual=g_slist_find_custom(unbound_parameters,binding->id,
ali@0
  1412
	  (GCompareFunc)strcmp);
ali@0
  1413
	if (actual)
ali@0
  1414
	    unbound_parameters=g_slist_delete_link(unbound_parameters,actual);
ali@0
  1415
	xexpr_var_new(xexpr,binding->id,binding->value);
ali@0
  1416
    }
ali@0
  1417
    if (evaluate_args)
ali@0
  1418
    {
ali@0
  1419
	for(lnk=unbound_parameters;lnk;lnk=lnk->next)
ali@0
  1420
	{
ali@0
  1421
	    unbound_parameter=lnk->data;
ali@0
  1422
	    value=xexpr_var_get(xexpr,unbound_parameter);
ali@0
  1423
	    if (value)
ali@0
  1424
		xexpr_var_new(xexpr,unbound_parameter,value);
ali@0
  1425
	    else
ali@0
  1426
	    {
ali@0
  1427
		value=xexpr_new_invocation(NULL,"nil",NULL,NULL);
ali@0
  1428
		xexpr_var_new(xexpr,unbound_parameter,value);
ali@0
  1429
		xexpr_constant_free(value);
ali@0
  1430
	    }
ali@0
  1431
	}
ali@0
  1432
    }
ali@0
  1433
    while(unbound_parameters)
ali@0
  1434
    {
ali@0
  1435
	unbound_parameter=unbound_parameters->data;
ali@0
  1436
	if (unbound_args)
ali@0
  1437
	{
ali@0
  1438
	    if (evaluate_args)
ali@0
  1439
	    {
ali@0
  1440
		value=xexpr_constant_evaluate(xexpr,unbound_args->data,err);
ali@0
  1441
		if (!value)
ali@0
  1442
		{
ali@0
  1443
		    g_slist_free(unbound_parameters);
ali@0
  1444
		    return NULL;
ali@0
  1445
		}
ali@0
  1446
		xexpr_var_set(xexpr,unbound_parameter,value);
ali@0
  1447
		xexpr_constant_free(value);
ali@0
  1448
	    }
ali@0
  1449
	    else
ali@0
  1450
	    {
ali@0
  1451
		value=unbound_args->data;
ali@0
  1452
		xexpr_var_new(xexpr,unbound_parameter,value);
ali@0
  1453
	    }
ali@0
  1454
	    unbound_args=unbound_args->next;
ali@0
  1455
	    unbound_parameters=
ali@0
  1456
	      g_slist_delete_link(unbound_parameters,unbound_parameters);
ali@0
  1457
	}
ali@0
  1458
	else
ali@0
  1459
	{
ali@0
  1460
	    g_set_error(err,XEXPR_EVAL_ERROR,XEXPR_EVAL_ERROR_FAILED,
ali@0
  1461
	      "No actual value to bind to parameter %s of function %s",
ali@0
  1462
	      unbound_parameter,id);
ali@0
  1463
	    g_slist_free(unbound_parameters);
ali@0
  1464
	    return NULL;
ali@0
  1465
	}
ali@0
  1466
    }
ali@0
  1467
    if (evaluate_args)
ali@0
  1468
    {
ali@0
  1469
	args=NULL;
ali@0
  1470
	while(unbound_args)
ali@0
  1471
	{
ali@0
  1472
	    value=xexpr_constant_evaluate(xexpr,unbound_args->data,err);
ali@0
  1473
	    if (!value)
ali@0
  1474
		return NULL;
ali@0
  1475
	    args=g_slist_prepend(args,value);
ali@0
  1476
	    unbound_args=unbound_args->next;
ali@0
  1477
	}
ali@0
  1478
	args=g_slist_reverse(args);
ali@0
  1479
    }
ali@0
  1480
    if (function_def)
ali@0
  1481
	result=xexpr_closure(xexpr,function_def,err);
ali@0
  1482
    else
ali@0
  1483
	result=xexpr_builtins[builtin].func(xexpr,bindings,args,err);
ali@0
  1484
    if (evaluate_args)
ali@0
  1485
    {
ali@0
  1486
	g_slist_foreach(args,(GFunc)xexpr_constant_free,NULL);
ali@0
  1487
	g_slist_free(args);
ali@0
  1488
    }
ali@0
  1489
    return result;
ali@0
  1490
}
ali@0
  1491
ali@0
  1492
/**
ali@0
  1493
 * xexpr_constant_evaluate:
ali@0
  1494
 * @xexpr: an #Xexpr
ali@0
  1495
 * @constant: the #XexprConstant to evaluate
ali@0
  1496
 * @err: (allow-none): location to store error, or %NULL
ali@0
  1497
 *
ali@0
  1498
 * Evaluate an XEXPR expression. Typically, an expression will evaluate to
ali@0
  1499
 * one of the basic types: string, integer or float. However, the XEXPR
ali@0
  1500
 * language specification states that &lt;define&gt; returns a function
ali@0
  1501
 * object (ie., an #XexprConstant with type %XEXPR_TYPE_FUNCTION).
ali@0
  1502
 * If such a function object is passed to xexpr_constant_evaluate(), then
ali@0
  1503
 * the function definition will be evaluated. If the function takes
ali@0
  1504
 * arguments, then these will be taken as &lt;nil&gt;.
ali@0
  1505
 *
ali@0
  1506
 * The returned expresion should be freed with xexpr_constant_free()
ali@0
  1507
 * when no longer needed.
ali@0
  1508
 *
ali@0
  1509
 * Return value: (transfer full): The evaluated result, or %NULL on error
ali@0
  1510
 */
ali@0
  1511
XexprConstant *xexpr_constant_evaluate(Xexpr *xexpr,XexprConstant *constant,
ali@0
  1512
  GError **err)
ali@0
  1513
{
ali@0
  1514
    GSList *lnk;
ali@0
  1515
    XexprConstant *result,*tmp;
ali@0
  1516
    switch(constant->type)
ali@0
  1517
    {
ali@0
  1518
	case XEXPR_TYPE_FUNCTION:
ali@0
  1519
	    /*
ali@0
  1520
	     * It's not clear how function objects should be evaluated.
ali@0
  1521
	     * We choose to invoke them with all arguments as <nil/>.
ali@0
  1522
	     * Note that this means that <get>x</get> will not error
ali@0
  1523
	     * out if x is defined as taking arguments whereas <x/>
ali@0
  1524
	     * will. http://www.w3.org/TR/xexpr/#id-0014 states that
ali@0
  1525
	     * these have the same effect in _most_ cases which implies
ali@0
  1526
	     * that there are differences in some cases.
ali@0
  1527
	     */
ali@0
  1528
	    _xexpr_push_environment(xexpr,NULL);
ali@0
  1529
	    tmp=xexpr_new_invocation(NULL,"nil",NULL,NULL);
ali@0
  1530
	    for(lnk=constant->u.function->args;lnk;lnk=lnk->next)
ali@0
  1531
		xexpr_var_new(xexpr,lnk->data,tmp);
ali@0
  1532
	    xexpr_constant_free(tmp);
ali@0
  1533
	    result=xexpr_closure(xexpr,constant,err);
ali@0
  1534
	    _xexpr_pop_environment(xexpr);
ali@0
  1535
	    return result;
ali@0
  1536
	    break;
ali@0
  1537
	case XEXPR_TYPE_INVOCATION:
ali@0
  1538
	    _xexpr_push_environment(xexpr,constant->u.invocation->function);
ali@0
  1539
	    result=xexpr_function_evaluate(xexpr,
ali@0
  1540
	      constant->u.invocation->ns,
ali@0
  1541
	      constant->u.invocation->function,
ali@0
  1542
	      constant->u.invocation->bindings,
ali@0
  1543
	      constant->u.invocation->constants,err);
ali@0
  1544
	    _xexpr_pop_environment(xexpr);
ali@0
  1545
	    return result;
ali@0
  1546
	    break;
ali@0
  1547
	case XEXPR_TYPE_STRING:
ali@0
  1548
	    return xexpr_new_string(constant->u.string,-1);
ali@0
  1549
	    break;
ali@0
  1550
	case XEXPR_TYPE_INTEGER:
ali@0
  1551
	    return xexpr_new_integer(constant->u.integer);
ali@0
  1552
	    break;
ali@0
  1553
	case XEXPR_TYPE_NUMBER:
ali@0
  1554
	    return xexpr_new_number(constant->u.number);
ali@0
  1555
	    break;
ali@0
  1556
    }
ali@0
  1557
    return NULL;
ali@0
  1558
}
ali@0
  1559
ali@0
  1560
/**
ali@0
  1561
 * xexpr_evaluate:
ali@0
  1562
 * @xexpr: an #Xexpr to evaluate
ali@0
  1563
 * @err: (allow-none): location to store error, or %NULL
ali@0
  1564
 *
ali@0
  1565
 * Evaluate an XEXPR expression. Typically, an expression will evaluate to
ali@0
  1566
 * one of the basic types: string, integer or float. However, the XEXPR
ali@0
  1567
 * language specification states that &lt;define&gt; returns a function
ali@0
  1568
 * object (ie., an #XexprConstant with type %XEXPR_TYPE_FUNCTION).
ali@0
  1569
 * If such a function object is passed to xexpr_evaluate(), then
ali@0
  1570
 * the function definition will be evaluated. If the function takes
ali@0
  1571
 * arguments, then these will be taken as &lt;nil&gt;.
ali@0
  1572
 *
ali@0
  1573
 * The returned expresion should be freed with xexpr_free()
ali@0
  1574
 * when no longer needed.
ali@0
  1575
 *
ali@0
  1576
 * Return value: (transfer full): The evaluated result, or %NULL on error
ali@0
  1577
 */
ali@0
  1578
Xexpr *xexpr_evaluate(Xexpr *xexpr,GError **err)
ali@0
  1579
{
ali@0
  1580
    Xexpr *results;
ali@0
  1581
    XexprConstant *constant,*result;
ali@0
  1582
    GSList *lnk;
ali@0
  1583
    results=xexpr_sub(xexpr);
ali@0
  1584
    for(lnk=xexpr->constants;lnk;lnk=lnk->next)
ali@0
  1585
    {
ali@0
  1586
	constant=lnk->data;
ali@0
  1587
	result=xexpr_constant_evaluate(xexpr,constant,err);
ali@0
  1588
	if (!result)
ali@0
  1589
	{
ali@0
  1590
	    xexpr_free(results);
ali@0
  1591
	    return NULL;
ali@0
  1592
	}
ali@0
  1593
	else
ali@0
  1594
	    results->constants=g_slist_prepend(results->constants,result);
ali@0
  1595
    }
ali@0
  1596
    results->constants=g_slist_reverse(results->constants);
ali@0
  1597
    return results;
ali@0
  1598
}
ali@0
  1599
ali@0
  1600
/**
ali@0
  1601
 * xexpr_test:
ali@0
  1602
 * @xexpr: an #Xexpr to test
ali@0
  1603
 * @err: (allow-none): location to store error, or %NULL
ali@0
  1604
 *
ali@0
  1605
 * Test an expression by looking at the last constant in the expression.
ali@0
  1606
 * Numerical constants are treated as &lt;true&gt; if they are non-zero,
ali@0
  1607
 * string constants are treated as &lt;true&gt; if they are non-empty,
ali@0
  1608
 * function invocations are treated as &lt;true&gt; unless they are
ali@0
  1609
 * &lt;false&gt; or &lt;nil&gt;, function definitions are recursively
ali@0
  1610
 * evaluated before they are tested.
ali@0
  1611
 *
ali@0
  1612
 * Return value: %TRUE if the expression is equivalent to &lt;true&gt;.
ali@0
  1613
 */
ali@0
  1614
gboolean xexpr_test(Xexpr *xexpr,GError **err)
ali@0
  1615
{
ali@0
  1616
    GSList *lnk;
ali@0
  1617
    XexprConstant *constant;
ali@0
  1618
    lnk=g_slist_last(xexpr->constants);
ali@0
  1619
    constant=lnk->data;
ali@0
  1620
    return xexpr_constant_boolean(xexpr,constant,err);
ali@0
  1621
}