/* -*- Mode: c; c-basic-offset: 2 -*- * * rasqal_expr.c - Rasqal general expression support * * $Id: rasqal_expr.c 11551 2006-10-29 21:12:27Z dajobe $ * * Copyright (C) 2003-2006, David Beckett http://purl.org/net/dajobe/ * Copyright (C) 2003-2005, University of Bristol, UK http://www.bristol.ac.uk/ * * This package is Free Software and part of Redland http://librdf.org/ * * It is licensed under the following three licenses as alternatives: * 1. GNU Lesser General Public License (LGPL) V2.1 or any newer version * 2. GNU General Public License (GPL) V2 or any newer version * 3. Apache License, V2.0 or any newer version * * You may not use this file except in compliance with at least one of * the above three licenses. * * See LICENSE.html or LICENSE.txt at the top of this package for the * complete terms and further detail along with the license texts for * the licenses in COPYING.LIB, COPYING and LICENSE-2.0.txt respectively. * * */ #ifdef HAVE_CONFIG_H #include #endif #ifdef WIN32 #include #endif #include #include #ifdef HAVE_STDLIB_H #include #endif #include #ifdef RASQAL_REGEX_PCRE #include #endif #ifdef RASQAL_REGEX_POSIX #include #include #endif #include "rasqal.h" #include "rasqal_internal.h" #ifndef STANDALONE static RASQAL_INLINE int rasqal_expression_as_boolean(rasqal_expression* e, int *error); static RASQAL_INLINE int rasqal_expression_as_integer(rasqal_expression* e, int *error); static RASQAL_INLINE int rasqal_expression_compare(rasqal_expression* e1, rasqal_expression* e2, int flags, int *error); /** * rasqal_new_data_graph: * @uri: source URI * @name_uri: name of graph (or NULL) * @flags: %RASQAL_DATA_GRAPH_NAMED or %RASQAL_DATA_GRAPH_BACKGROUND * * Constructor - create a new #rasqal_data_graph. * * The name_uri is only used when the flags are %RASQAL_DATA_GRAPH_NAMED. * * Return value: a new #rasqal_data_graph or NULL on failure. **/ rasqal_data_graph* rasqal_new_data_graph(raptor_uri* uri, raptor_uri* name_uri, int flags) { rasqal_data_graph* dg=(rasqal_data_graph*)RASQAL_CALLOC(rasqal_data_graph, 1, sizeof(rasqal_data_graph)); dg->uri=raptor_uri_copy(uri); if(name_uri) dg->name_uri=raptor_uri_copy(name_uri); dg->flags=flags; return dg; } /** * rasqal_free_data_graph: * @dg: #rasqal_data_graph object * * Destructor - destroy a #rasqal_data_graph object. * **/ void rasqal_free_data_graph(rasqal_data_graph* dg) { if(dg->uri) raptor_free_uri(dg->uri); if(dg->name_uri) raptor_free_uri(dg->name_uri); RASQAL_FREE(rasqal_data_graph, dg); } /** * rasqal_data_graph_print: * @dg: #rasqal_data_graph object * @fh: the #FILE* handle to print to * * Print a Rasqal data graph in a debug format. * * The print debug format may change in any release. **/ void rasqal_data_graph_print(rasqal_data_graph* dg, FILE* fh) { if(dg->name_uri) fprintf(fh, "data graph(%s named as %s flags %d)", raptor_uri_as_string(dg->uri), raptor_uri_as_string(dg->name_uri), dg->flags); else fprintf(fh, "data graph(%s, flags %d)", raptor_uri_as_string(dg->uri), dg->flags); } /** * rasqal_new_variable_typed: * @rq: #rasqal_query to associate the variable with * @type: variable type defined by enumeration rasqal_variable_type * @name: variable name * @value: variable #rasqal_literal value (or NULL) * * Constructor - Create a new typed Rasqal variable. * * The variable must be associated with a query, since variable * names are only significant with a single query. * * The @name and @value become owned by the rasqal_variable structure * * Return value: a new #rasqal_variable or NULL on failure. **/ rasqal_variable* rasqal_new_variable_typed(rasqal_query* rq, rasqal_variable_type type, unsigned char *name, rasqal_literal *value) { int i; rasqal_variable* v; raptor_sequence* seq; int* count_p; switch(type) { case RASQAL_VARIABLE_TYPE_ANONYMOUS: seq=rq->anon_variables_sequence; count_p=&rq->anon_variables_count; break; case RASQAL_VARIABLE_TYPE_NORMAL: seq=rq->variables_sequence; count_p=&rq->variables_count; break; case RASQAL_VARIABLE_TYPE_UNKNOWN: default: RASQAL_DEBUG2("Unknown variable type %d", type); return NULL; } for(i=0; i< raptor_sequence_size(seq); i++) { v=(rasqal_variable*)raptor_sequence_get_at(seq, i); if(!strcmp((const char*)v->name, (const char*)name)) { /* name already present, do not need a copy */ RASQAL_FREE(cstring, name); return v; } } v=(rasqal_variable*)RASQAL_CALLOC(rasqal_variable, 1, sizeof(rasqal_variable)); v->type= type; v->name= name; v->value= value; v->offset= (*count_p)++; raptor_sequence_push(seq, v); return v; } /** * rasqal_new_variable: * @rq: #rasqal_query to associate the variable with * @name: variable name * @value: variable #rasqal_literal value (or NULL) * * Constructor - Create a new Rasqal normal variable. * * The variable must be associated with a query, since variable * names are only significant with a single query. * * This creates a regular variable that can be returned of type * RASQAL_VARIABLE_TYPE_NORMAL. Use rasqal_new_variable_typed * to create other variables. * * The @name and @value become owned by the rasqal_variable structure * * Return value: a new #rasqal_variable or NULL on failure. **/ rasqal_variable* rasqal_new_variable(rasqal_query* rq, unsigned char *name, rasqal_literal *value) { return rasqal_new_variable_typed(rq, RASQAL_VARIABLE_TYPE_NORMAL, name, value); } /** * rasqal_free_variable: * @v: #rasqal_variable object * * Destructor - Destroy a Rasqal variable object. * **/ void rasqal_free_variable(rasqal_variable* v) { if(v->name) RASQAL_FREE(cstring, (void*)v->name); if(v->value) rasqal_free_literal(v->value); RASQAL_FREE(rasqal_variable, v); } /** * rasqal_variable_print: * @v: the #rasqal_variable object * @fh: the #FILE* handle to print to * * Print a Rasqal variable in a debug format. * * The print debug format may change in any release. * **/ void rasqal_variable_print(rasqal_variable* v, FILE* fh) { if(v->type == RASQAL_VARIABLE_TYPE_ANONYMOUS) fprintf(fh, "anon-variable(%s", v->name); else fprintf(fh, "variable(%s", v->name); if(v->value) { fputc('=', fh); rasqal_literal_print(v->value, fh); } fputc(')', fh); } /** * rasqal_variable_set_value: * @v: the #rasqal_variable object * @l: the #rasqal_literal value to set (or NULL) * * Set the value of a Rasqal variable. * * The variable value is an input parameter and is copied in, not shared. * If the variable value is NULL, any existing value is deleted. * **/ void rasqal_variable_set_value(rasqal_variable* v, rasqal_literal* l) { if(v->value) rasqal_free_literal(v->value); v->value=l; #ifdef RASQAL_DEBUG if(!v->name) RASQAL_FATAL1("variable has no name"); RASQAL_DEBUG2("setting variable %s to value ", v->name); if(v->value) rasqal_literal_print(v->value, stderr); else fputs("(NULL)", stderr); fputc('\n', stderr); #endif } /** * rasqal_new_prefix: * @prefix: Short prefix string to stand for URI or NULL. * @uri: Name #raptor_uri. * * Constructor - create a new #rasqal_prefix. * * Return value: a new #rasqal_prefix or NULL on failure. **/ rasqal_prefix* rasqal_new_prefix(const unsigned char *prefix, raptor_uri* uri) { rasqal_prefix* p=(rasqal_prefix*)RASQAL_CALLOC(rasqal_prefix, 1, sizeof(rasqal_prefix)); p->prefix=prefix; p->uri=uri; return p; } /** * rasqal_free_prefix: * @p: #rasqal_prefix object. * * Destructor - destroy a #rasqal_prefix object. **/ void rasqal_free_prefix(rasqal_prefix* p) { if(p->prefix) RASQAL_FREE(cstring, (void*)p->prefix); raptor_free_uri(p->uri); RASQAL_FREE(rasqal_prefix, p); } /** * rasqal_prefix_print: * @p: #rasqal_prefix object. * @fh: The #FILE* handle to print to. * * Print a Rasqal prefix in a debug format. * * The print debug format may change in any release. **/ void rasqal_prefix_print(rasqal_prefix* p, FILE* fh) { fprintf(fh, "prefix(%s as %s)", (p->prefix ? (const char*)p->prefix : "(default)"), raptor_uri_as_string(p->uri)); } /** * rasqal_new_triple: * @subject: Triple subject. * @predicate: Triple predicate. * @object: Triple object. * * Constructor - create a new #rasqal_triple triple or triple pattern. * * The triple origin can be set with rasqal_triple_set_origin(). * * Return value: a new #rasqal_triple or NULL on failure. **/ rasqal_triple* rasqal_new_triple(rasqal_literal* subject, rasqal_literal* predicate, rasqal_literal* object) { rasqal_triple* t=(rasqal_triple*)RASQAL_CALLOC(rasqal_triple, 1, sizeof(rasqal_triple)); t->subject=subject; t->predicate=predicate; t->object=object; return t; } /** * rasqal_new_triple_from_triple: * @t: Triple to copy. * * Copy constructor - create a new #rasqal_triple from an existing one. * * Return value: a new #rasqal_triple or NULL on failure. **/ rasqal_triple* rasqal_new_triple_from_triple(rasqal_triple* t) { rasqal_triple* newt=(rasqal_triple*)RASQAL_CALLOC(rasqal_triple, 1, sizeof(rasqal_triple)); newt->subject=rasqal_new_literal_from_literal(t->subject); newt->predicate=rasqal_new_literal_from_literal(t->predicate); newt->object=rasqal_new_literal_from_literal(t->object); return newt; } /** * rasqal_free_triple: * @t: #rasqal_triple object. * * Destructor - destroy a #rasqal_triple object. **/ void rasqal_free_triple(rasqal_triple* t) { if(t->subject) rasqal_free_literal(t->subject); if(t->predicate) rasqal_free_literal(t->predicate); if(t->object) rasqal_free_literal(t->object); if(t->origin) rasqal_free_literal(t->origin); RASQAL_FREE(rasqal_triple, t); } /** * rasqal_triple_print: * @t: #rasqal_triple object. * @fh: The #FILE* handle to print to. * * Print a Rasqal triple in a debug format. * * The print debug format may change in any release. **/ void rasqal_triple_print(rasqal_triple* t, FILE* fh) { fputs("triple(", fh); rasqal_literal_print(t->subject, fh); fputs(", ", fh); rasqal_literal_print(t->predicate, fh); fputs(", ", fh); rasqal_literal_print(t->object, fh); fputc(')', fh); if(t->origin) { fputs(" with origin(", fh); rasqal_literal_print(t->origin, fh); fputc(')', fh); } } /** * rasqal_triple_set_origin: * @t: The triple object. * @l: The #rasqal_literal object to set as origin. * * Set the origin field of a #rasqal_triple. **/ void rasqal_triple_set_origin(rasqal_triple* t, rasqal_literal* l) { t->origin=l; } /** * rasqal_triple_get_origin: * @t: The triple object. * * Get the origin field of a #rasqal_triple. * * Return value: The triple origin or NULL. **/ rasqal_literal* rasqal_triple_get_origin(rasqal_triple* t) { return t->origin; } /** * rasqal_new_1op_expression: * @op: Expression operator * @arg: Operand 1 * * Constructor - create a new 1-operand expression. * * The operators are: * @RASQAL_EXPR_TILDE @RASQAL_EXPR_BANG @RASQAL_EXPR_UMINUS * @RASQAL_EXPR_BOUND @RASQAL_EXPR_STR @RASQAL_EXPR_LANG * @RASQAL_EXPR_LANGMATCHES * @RASQAL_EXPR_DATATYPE @RASQAL_EXPR_ISURI @RASQAL_EXPR_ISBLANK * @RASQAL_EXPR_ISLITERAL @RASQAL_EXPR_ORDER_COND_ASC * @RASQAL_EXPR_ORDER_COND_DESC * * @RASQAL_EXPR_BANG and @RASQAL_EXPR_UMINUS are used by RDQL and * SPARQL. @RASQAL_EXPR_TILDE by RDQL only. The rest by SPARQL * only. * * Return value: a new #rasqal_expression object or NULL on failure **/ rasqal_expression* rasqal_new_1op_expression(rasqal_op op, rasqal_expression* arg) { rasqal_expression* e=(rasqal_expression*)RASQAL_CALLOC(rasqal_expression, 1, sizeof(rasqal_expression)); e->usage=1; e->op=op; e->arg1=arg; return e; } /** * rasqal_new_2op_expression: * @op: Expression operator * @arg1: Operand 1 * @arg2: Operand 2 * * Constructor - create a new 2-operand expression. * * The operators are: * @RASQAL_EXPR_AND @RASQAL_EXPR_OR @RASQAL_EXPR_EQ * @RASQAL_EXPR_NEQ @RASQAL_EXPR_LT @RASQAL_EXPR_GT @RASQAL_EXPR_LE * @RASQAL_EXPR_GE @RASQAL_EXPR_PLUS @RASQAL_EXPR_MINUS * @RASQAL_EXPR_STAR @RASQAL_EXPR_SLASH @RASQAL_EXPR_REM * @RASQAL_EXPR_STR_EQ @RASQAL_EXPR_STR_NEQ * * @RASQAL_EXPR_REM @RASQAL_EXPR_STR_EQ and @RASQAL_EXPR_STR_NEQ are * not used by SPARQL. @RASQAL_EXPR_REM is used by RDQL. * * Return value: a new #rasqal_expression object or NULL on failure **/ rasqal_expression* rasqal_new_2op_expression(rasqal_op op, rasqal_expression* arg1, rasqal_expression* arg2) { rasqal_expression* e=(rasqal_expression*)RASQAL_CALLOC(rasqal_expression, 1, sizeof(rasqal_expression)); e->usage=1; e->op=op; e->arg1=arg1; e->arg2=arg2; return e; } /** * rasqal_new_3op_expression: * @op: Expression operator * @arg1: Operand 1 * @arg2: Operand 2 * @arg3: Operand 3 (may be NULL) * * Constructor - create a new 3-operand expression. * * The only operator is: * @RASQAL_EXPR_REGEX * * Return value: a new #rasqal_expression object or NULL on failure **/ rasqal_expression* rasqal_new_3op_expression(rasqal_op op, rasqal_expression* arg1, rasqal_expression* arg2, rasqal_expression* arg3) { rasqal_expression* e=(rasqal_expression*)RASQAL_CALLOC(rasqal_expression, 1, sizeof(rasqal_expression)); e->usage=1; e->op=op; e->arg1=arg1; e->arg2=arg2; e->arg3=arg3; return e; } /** * rasqal_new_string_op_expression: * @op: Expression operator * @arg1: Operand 1 * @literal: Literal operand 2 * * Constructor - create a new expression with one expression and one string operand. * * The operators are: * @RASQAL_EXPR_STR_MATCH (RDQL, SPARQL) and * @RASQAL_EXPR_STR_NMATCH (RDQL) * * Return value: a new #rasqal_expression object or NULL on failure **/ rasqal_expression* rasqal_new_string_op_expression(rasqal_op op, rasqal_expression* arg1, rasqal_literal* literal) { rasqal_expression* e=(rasqal_expression*)RASQAL_CALLOC(rasqal_expression, 1, sizeof(rasqal_expression)); e->usage=1; e->op=op; e->arg1=arg1; e->literal=literal; return e; } /** * rasqal_new_literal_expression: * @literal: Literal operand 1 * * Constructor - create a new expression for a #rasqal_literal * * Return value: a new #rasqal_expression object or NULL on failure **/ rasqal_expression* rasqal_new_literal_expression(rasqal_literal *literal) { rasqal_expression* e=(rasqal_expression*)RASQAL_CALLOC(rasqal_expression, 1, sizeof(rasqal_expression)); e->usage=1; e->op=RASQAL_EXPR_LITERAL; e->literal=literal; return e; } /** * rasqal_new_function_expression: * @name: function name * @args: sequence of #rasqal_expression function arguments * * Constructor - create a new expression for a function with expression arguments * * Return value: a new #rasqal_expression object or NULL on failure **/ rasqal_expression* rasqal_new_function_expression(raptor_uri* name, raptor_sequence* args) { rasqal_expression* e=(rasqal_expression*)RASQAL_CALLOC(rasqal_expression, 1, sizeof(rasqal_expression)); e->usage=1; e->op=RASQAL_EXPR_FUNCTION; e->name=name; e->args=args; return e; } /** * rasqal_new_cast_expression: * @name: cast datatype URI * @value: expression value to cast to @datatype type * * Constructor - create a new expression for casting and expression to a datatype * * Return value: a new #rasqal_expression object or NULL on failure **/ rasqal_expression* rasqal_new_cast_expression(raptor_uri* name, rasqal_expression *value) { rasqal_expression* e=(rasqal_expression*)RASQAL_CALLOC(rasqal_expression, 1, sizeof(rasqal_expression)); e->usage=1; e->op=RASQAL_EXPR_CAST; e->name=name; e->arg1=value; return e; } /** * rasqal_expression_clear: * @e: expression * * Empty an expression of contained content. * * Intended to be used to deallocate resources from a statically * declared #rasqal_expression such as on a stack. **/ void rasqal_expression_clear(rasqal_expression* e) { switch(e->op) { case RASQAL_EXPR_AND: case RASQAL_EXPR_OR: case RASQAL_EXPR_EQ: case RASQAL_EXPR_NEQ: case RASQAL_EXPR_LT: case RASQAL_EXPR_GT: case RASQAL_EXPR_LE: case RASQAL_EXPR_GE: case RASQAL_EXPR_PLUS: case RASQAL_EXPR_MINUS: case RASQAL_EXPR_STAR: case RASQAL_EXPR_SLASH: case RASQAL_EXPR_REM: case RASQAL_EXPR_STR_EQ: case RASQAL_EXPR_STR_NEQ: case RASQAL_EXPR_LANGMATCHES: rasqal_free_expression(e->arg1); rasqal_free_expression(e->arg2); break; case RASQAL_EXPR_REGEX: rasqal_free_expression(e->arg1); rasqal_free_expression(e->arg2); if(e->arg3) rasqal_free_expression(e->arg3); break; case RASQAL_EXPR_TILDE: case RASQAL_EXPR_BANG: case RASQAL_EXPR_UMINUS: case RASQAL_EXPR_BOUND: case RASQAL_EXPR_STR: case RASQAL_EXPR_LANG: case RASQAL_EXPR_DATATYPE: case RASQAL_EXPR_ISURI: case RASQAL_EXPR_ISBLANK: case RASQAL_EXPR_ISLITERAL: case RASQAL_EXPR_ORDER_COND_ASC: case RASQAL_EXPR_ORDER_COND_DESC: rasqal_free_expression(e->arg1); break; case RASQAL_EXPR_STR_MATCH: case RASQAL_EXPR_STR_NMATCH: rasqal_free_expression(e->arg1); /* FALLTHROUGH */ case RASQAL_EXPR_LITERAL: rasqal_free_literal(e->literal); break; case RASQAL_EXPR_FUNCTION: raptor_free_uri(e->name); raptor_free_sequence(e->args); break; case RASQAL_EXPR_CAST: raptor_free_uri(e->name); rasqal_free_expression(e->arg1); break; case RASQAL_EXPR_UNKNOWN: default: RASQAL_FATAL2("Unknown operation %d", e->op); } } /** * rasqal_new_expression_from_expression: * @e: #rasqal_expression object to copy * * Copy Constructor - create a new #rasqal_expression object from an existing rasqal_expression object. * * Return value: a new #rasqal_expression object or NULL on failure **/ rasqal_expression* rasqal_new_expression_from_expression(rasqal_expression* e) { e->usage++; return e; } /** * rasqal_free_expression: * @e: #rasqal_expression object * * Destructor - destroy a #rasqal_expression object. * **/ void rasqal_free_expression(rasqal_expression* e) { if(--e->usage) return; rasqal_expression_clear(e); RASQAL_FREE(rasqal_expression, e); } /** * rasqal_expression_foreach: * @e: #rasqal_expression to visit * @fn: visit function * @user_data: user data to pass to visit function * * Visit a user function over a #rasqal_expression * * @deprecated: Use rasqal_expression_visit() instead. * * Return value: 0 if the visit was truncated. **/ int rasqal_expression_foreach(rasqal_expression* e, rasqal_expression_foreach_fn fn, void *user_data) { RASQAL_DEPRECATED_MESSAGE("use rasqal_expression_visit"); return rasqal_expression_visit(e, (rasqal_expression_visit_fn)fn, user_data); } /** * rasqal_expression_visit: * @e: #rasqal_expression to visit * @fn: visit function * @user_data: user data to pass to visit function * * Visit a user function over a #rasqal_expression * * If the user function @fn returns 0, the visit is truncated. * * Return value: 0 if the visit was truncated. **/ int rasqal_expression_visit(rasqal_expression* e, rasqal_expression_visit_fn fn, void *user_data) { int i; int result=0; /* This ordering allows fn to potentially edit 'e' in-place */ result=fn(user_data, e); if(result) return result; switch(e->op) { case RASQAL_EXPR_AND: case RASQAL_EXPR_OR: case RASQAL_EXPR_EQ: case RASQAL_EXPR_NEQ: case RASQAL_EXPR_LT: case RASQAL_EXPR_GT: case RASQAL_EXPR_LE: case RASQAL_EXPR_GE: case RASQAL_EXPR_PLUS: case RASQAL_EXPR_MINUS: case RASQAL_EXPR_STAR: case RASQAL_EXPR_SLASH: case RASQAL_EXPR_REM: case RASQAL_EXPR_STR_EQ: case RASQAL_EXPR_STR_NEQ: case RASQAL_EXPR_LANGMATCHES: return rasqal_expression_visit(e->arg1, fn, user_data) || rasqal_expression_visit(e->arg2, fn, user_data); break; case RASQAL_EXPR_REGEX: return rasqal_expression_visit(e->arg1, fn, user_data) || rasqal_expression_visit(e->arg2, fn, user_data) || (e->arg3 && rasqal_expression_visit(e->arg3, fn, user_data)); break; case RASQAL_EXPR_TILDE: case RASQAL_EXPR_BANG: case RASQAL_EXPR_UMINUS: case RASQAL_EXPR_BOUND: case RASQAL_EXPR_STR: case RASQAL_EXPR_LANG: case RASQAL_EXPR_DATATYPE: case RASQAL_EXPR_ISURI: case RASQAL_EXPR_ISBLANK: case RASQAL_EXPR_ISLITERAL: case RASQAL_EXPR_CAST: case RASQAL_EXPR_ORDER_COND_ASC: case RASQAL_EXPR_ORDER_COND_DESC: return rasqal_expression_visit(e->arg1, fn, user_data); break; case RASQAL_EXPR_STR_MATCH: case RASQAL_EXPR_STR_NMATCH: return fn(user_data, e->arg1); break; case RASQAL_EXPR_LITERAL: return 0; case RASQAL_EXPR_FUNCTION: for(i=0; iargs); i++) { rasqal_expression* e2=(rasqal_expression*)raptor_sequence_get_at(e->args, i); if(!rasqal_expression_visit(e2, fn, user_data)) { result=0; break; } } return result; break; case RASQAL_EXPR_UNKNOWN: default: RASQAL_FATAL2("Unknown operation %d", e->op); } } static RASQAL_INLINE int rasqal_expression_as_boolean(rasqal_expression* e, int *error) { if(e->op == RASQAL_EXPR_LITERAL) return rasqal_literal_as_boolean(e->literal, error); abort(); } static RASQAL_INLINE int rasqal_expression_as_integer(rasqal_expression* e, int *error) { if(e->op == RASQAL_EXPR_LITERAL) return rasqal_literal_as_integer(e->literal, error); abort(); } static RASQAL_INLINE int rasqal_expression_compare(rasqal_expression* e1, rasqal_expression* e2, int flags, int *error) { *error=0; if(e1->op == RASQAL_EXPR_LITERAL && e1->op == e2->op) return rasqal_literal_compare(e1->literal, e2->literal, flags, error); if(e1->op !=RASQAL_EXPR_LITERAL) RASQAL_FATAL2("Unexpected e1 op %d\n", e1->op); else RASQAL_FATAL2("Unexpected e2 op %d\n", e2->op); } /** * rasqal_expression_evaluate: * @query: #rasqal_query this expression belongs to * @e: The expression to evaluate. * @flags: Flags for rasqal_literal_compare() and RASQAL_COMPARE_NOCASE for string matches. * * Evaluate a #rasqal_expression tree to give a #rasqal_literal result * or error. * * Return value: a #rasqal_literal value or NULL on failure. **/ rasqal_literal* rasqal_expression_evaluate(rasqal_query *query, rasqal_expression* e, int flags) { int error=0; rasqal_literal* result=NULL; #ifdef RASQAL_DEBUG RASQAL_DEBUG2("evaluating expression %p: ", e); rasqal_expression_print(e, stderr); fprintf(stderr, "\n"); #endif switch(e->op) { case RASQAL_EXPR_AND: { rasqal_literal *l; int b1=0; int b2=0; int error1=0; int error2=0; l=rasqal_expression_evaluate(query, e->arg1, flags); if(!l) error1=1; else { b1=rasqal_literal_as_boolean(l, &error1); rasqal_free_literal(l); } l=rasqal_expression_evaluate(query, e->arg2, flags); if(!l) error2=1; else { b2=rasqal_literal_as_boolean(l, &error2); rasqal_free_literal(l); } /* See http://www.w3.org/TR/2005/WD-rdf-sparql-query-20051123/#truthTable */ if(!error1 && !error2) { /* No type error, answer is A && B */ b1 = b1 && b2; } else { if((!b1 && error2) || (error1 && !b2)) /* F && E => F. E && F => F. */ b1=0; else /* Otherwise E */ error=1; } if(error) goto failed; else result=rasqal_new_boolean_literal(b1); break; } case RASQAL_EXPR_OR: { rasqal_literal *l; int b1=0; int b2=0; int error1=0; int error2=0; l=rasqal_expression_evaluate(query, e->arg1, flags); if(!l) error1=1; else { b1=rasqal_literal_as_boolean(l, &error1); rasqal_free_literal(l); } l=rasqal_expression_evaluate(query, e->arg2, flags); if(!l) error2=1; else { b2=rasqal_literal_as_boolean(l, &error2); rasqal_free_literal(l); } /* See http://www.w3.org/TR/2005/WD-rdf-sparql-query-20051123/#truthTable */ if(!error1 && !error2) { /* No type error, answer is A || B */ b1= b1 || b2; } else { if((b1 && error2) || (error1 && b2)) /* T || E => T. E || T => T */ b1=1; else /* Otherwise E */ error=1; } if(error) goto failed; else result=rasqal_new_boolean_literal(b1); break; } case RASQAL_EXPR_EQ: { rasqal_literal *l1, *l2; int b; l1=rasqal_expression_evaluate(query, e->arg1, flags); if(!l1) goto failed; l2=rasqal_expression_evaluate(query, e->arg2, flags); if(!l2) { rasqal_free_literal(l1); goto failed; } b=(rasqal_literal_compare(l1, l2, flags, &error) == 0); rasqal_free_literal(l1); rasqal_free_literal(l2); if(error) goto failed; result=rasqal_new_boolean_literal(b); break; } case RASQAL_EXPR_NEQ: { rasqal_literal *l1, *l2; int b; l1=rasqal_expression_evaluate(query, e->arg1, flags); if(!l1) goto failed; l2=rasqal_expression_evaluate(query, e->arg2, flags); if(!l2) { rasqal_free_literal(l1); goto failed; } b=(rasqal_literal_compare(l1, l2, flags, &error) != 0); rasqal_free_literal(l1); rasqal_free_literal(l2); if(error) goto failed; result=rasqal_new_boolean_literal(b); break; } case RASQAL_EXPR_LT: { rasqal_literal *l1, *l2; int b; l1=rasqal_expression_evaluate(query, e->arg1, flags); if(!l1) goto failed; l2=rasqal_expression_evaluate(query, e->arg2, flags); if(!l2) { rasqal_free_literal(l1); goto failed; } b=(rasqal_literal_compare(l1, l2, flags, &error) < 0); rasqal_free_literal(l1); rasqal_free_literal(l2); if(error) goto failed; result=rasqal_new_boolean_literal(b); break; } case RASQAL_EXPR_GT: { rasqal_literal *l1, *l2; int b; l1=rasqal_expression_evaluate(query, e->arg1, flags); if(!l1) goto failed; l2=rasqal_expression_evaluate(query, e->arg2, flags); if(!l2) { rasqal_free_literal(l1); goto failed; } b=(rasqal_literal_compare(l1, l2, flags, &error) > 0); rasqal_free_literal(l1); rasqal_free_literal(l2); if(error) goto failed; result=rasqal_new_boolean_literal(b); break; } case RASQAL_EXPR_LE: { rasqal_literal *l1, *l2; int b; l1=rasqal_expression_evaluate(query, e->arg1, flags); if(!l1) goto failed; l2=rasqal_expression_evaluate(query, e->arg2, flags); if(!l2) { rasqal_free_literal(l1); goto failed; } b=(rasqal_literal_compare(l1, l2, flags, &error) <= 0); rasqal_free_literal(l1); rasqal_free_literal(l2); if(error) goto failed; result=rasqal_new_boolean_literal(b); break; } case RASQAL_EXPR_GE: { rasqal_literal *l1, *l2; int b; l1=rasqal_expression_evaluate(query, e->arg1, flags); if(!l1) goto failed; l2=rasqal_expression_evaluate(query, e->arg2, flags); if(!l2) { rasqal_free_literal(l1); goto failed; } b=(rasqal_literal_compare(l1, l2, flags, &error) >= 0); rasqal_free_literal(l1); rasqal_free_literal(l2); if(error) goto failed; result=rasqal_new_boolean_literal(b); break; } case RASQAL_EXPR_UMINUS: { rasqal_literal *l; double d; l=rasqal_expression_evaluate(query, e->arg1, flags); if(!l) goto failed; d= -rasqal_literal_as_floating(l, &error); rasqal_free_literal(l); if(error) goto failed; result=rasqal_new_double_literal(d); break; } case RASQAL_EXPR_BOUND: { rasqal_literal *l; rasqal_variable* v; int b; l=rasqal_expression_evaluate(query, e->arg1, flags); if(!l) goto failed; v=rasqal_literal_as_variable(l); if(!v) { rasqal_free_literal(l); goto failed; } b=(v->value != NULL); rasqal_free_literal(l); result=rasqal_new_boolean_literal(b); break; } case RASQAL_EXPR_STR: { rasqal_literal* l; const unsigned char* s; unsigned char* new_s; size_t len; l=rasqal_expression_evaluate(query, e->arg1, flags); if(!l) goto failed; /* Note: flags removes RASQAL_COMPARE_XQUERY as this is the * explicit stringify operation */ s=rasqal_literal_as_string_flags(l, (flags & ~RASQAL_COMPARE_XQUERY), &error); if(!s || error) { rasqal_free_literal(l); goto failed; } len=strlen((const char*)s); new_s=(unsigned char *)RASQAL_MALLOC(cstring, len+1); strncpy((char*)new_s, (const char*)s, len+1); result=rasqal_new_string_literal(new_s, NULL, NULL, NULL); rasqal_free_literal(l); break; } case RASQAL_EXPR_LANG: { rasqal_literal* l; rasqal_variable* v; unsigned char* new_language; int free_literal=1; l=rasqal_expression_evaluate(query, e->arg1, flags); if(!l) goto failed; v=rasqal_literal_as_variable(l); if(v) { rasqal_free_literal(l); l=v->value; free_literal=0; if(!l) goto failed; } if(l->type == RASQAL_LITERAL_STRING && l->language) { new_language=(unsigned char*)RASQAL_MALLOC(cstring, strlen(l->language)+1); strcpy((char*)new_language, l->language); } else { new_language=(unsigned char*)RASQAL_MALLOC(cstring, 1); *new_language='\0'; } result=rasqal_new_string_literal(new_language, NULL, NULL, NULL); if(free_literal) rasqal_free_literal(l); break; } case RASQAL_EXPR_LANGMATCHES: { rasqal_literal *l1, *l2; const unsigned char* s1; const unsigned char* s2; int b; l1=rasqal_expression_evaluate(query, e->arg1, flags); if(!l1) goto failed; l2=rasqal_expression_evaluate(query, e->arg2, flags); if(!l2) { rasqal_free_literal(l1); goto failed; } /* Returns true if language-range (first argument) matches * language-tag (second argument) per Tags for the * Identification of Languages [RFC3066] section 2.5. RFC3066 * ( http://www.ietf.org/rfc/rfc3066.txt ) * defines a case-insensitive, hierarchical matching * algorithm which operates on ISO-defined subtags for * language and country codes, and user defined subtags. In * SPARQL, a language-range of "*" matches any non-empty * language-tag string. * -- http://www.w3.org/TR/rdf-sparql-query/#func-langMatches */ /* FIXME - seems to me it got the description of '*' in the * wrong argument position */ s1=rasqal_literal_as_string_flags(l1, flags, &error); s2=rasqal_literal_as_string_flags(l2, flags, &error); if(error) { b=0; } else if(s1 && s2 && *s1 && *s2) { /* Two non-empty arguments */ if(s2[0] == '*' && !s2[1]) b= 1; else /* FIXME - this is not a language compare */ b= (rasqal_strcasecmp((const char*)s1, (const char*)s2) == 0); } else /* FIXME - false may not be the right answer for all strings */ b=0; rasqal_free_literal(l1); rasqal_free_literal(l2); result=rasqal_new_boolean_literal(b); break; } case RASQAL_EXPR_DATATYPE: { rasqal_literal* l; rasqal_variable* v; int free_literal=1; l=rasqal_expression_evaluate(query, e->arg1, flags); if(!l) goto failed; v=rasqal_literal_as_variable(l); if(v) { rasqal_free_literal(l); l=v->value; free_literal=0; if(!l) goto failed; } if(!l->datatype) { if(free_literal) rasqal_free_literal(l); goto failed; } result=rasqal_new_uri_literal(raptor_uri_copy(l->datatype)); if(free_literal) rasqal_free_literal(l); break; } case RASQAL_EXPR_ISURI: { rasqal_literal *l; rasqal_variable* v; int free_literal=1; int b; l=rasqal_expression_evaluate(query, e->arg1, flags); if(!l) goto failed; v=rasqal_literal_as_variable(l); if(v) { rasqal_free_literal(l); l=v->value; free_literal=0; if(!l) goto failed; } b=(l->type == RASQAL_LITERAL_URI); if(free_literal) rasqal_free_literal(l); result=rasqal_new_boolean_literal(b); break; } case RASQAL_EXPR_ISBLANK: { rasqal_literal *l; rasqal_variable* v; int free_literal=1; int b; l=rasqal_expression_evaluate(query, e->arg1, flags); if(!l) goto failed; v=rasqal_literal_as_variable(l); if(v) { rasqal_free_literal(l); l=v->value; free_literal=0; if(!l) goto failed; } b=(l->type == RASQAL_LITERAL_BLANK); if(free_literal) rasqal_free_literal(l); result=rasqal_new_boolean_literal(b); break; } case RASQAL_EXPR_ISLITERAL: { rasqal_literal *l; rasqal_variable* v; int free_literal=1; int b; l=rasqal_expression_evaluate(query, e->arg1, flags); if(!l) goto failed; v=rasqal_literal_as_variable(l); if(v) { rasqal_free_literal(l); l=v->value; free_literal=0; if(!l) goto failed; } b=(l->type == RASQAL_LITERAL_STRING); if(free_literal) rasqal_free_literal(l); result=rasqal_new_boolean_literal(b); break; } case RASQAL_EXPR_PLUS: { rasqal_literal *l1, *l2; double d; int error1=0; int error2=0; l1=rasqal_expression_evaluate(query, e->arg1, flags); if(!l1) goto failed; l2=rasqal_expression_evaluate(query, e->arg2, flags); if(!l2) { rasqal_free_literal(l1); goto failed; } d=rasqal_literal_as_floating(l1, &error1) + rasqal_literal_as_floating(l2, &error2); rasqal_free_literal(l1); rasqal_free_literal(l2); if(error || error1 || error2) goto failed; result=rasqal_new_double_literal(d); break; } case RASQAL_EXPR_MINUS: { rasqal_literal *l1, *l2; double d; int error1=0; int error2=0; l1=rasqal_expression_evaluate(query, e->arg1, flags); if(!l1) goto failed; l2=rasqal_expression_evaluate(query, e->arg2, flags); if(!l2) { rasqal_free_literal(l1); goto failed; } d=rasqal_literal_as_floating(l1, &error1) - rasqal_literal_as_floating(l2, &error2); rasqal_free_literal(l1); rasqal_free_literal(l2); if(error || error1 || error2) goto failed; result=rasqal_new_double_literal(d); break; } case RASQAL_EXPR_STAR: { rasqal_literal *l1, *l2; double d; int error1=0; int error2=0; l1=rasqal_expression_evaluate(query, e->arg1, flags); if(!l1) goto failed; l2=rasqal_expression_evaluate(query, e->arg2, flags); if(!l2) { rasqal_free_literal(l1); goto failed; } d=rasqal_literal_as_floating(l1, &error1) * rasqal_literal_as_floating(l2, &error2); rasqal_free_literal(l1); rasqal_free_literal(l2); if(error || error1 || error2) goto failed; result=rasqal_new_double_literal(d); break; } case RASQAL_EXPR_SLASH: { rasqal_literal *l1, *l2; double d; int error1=0; int error2=0; l1=rasqal_expression_evaluate(query, e->arg1, flags); if(!l1) goto failed; l2=rasqal_expression_evaluate(query, e->arg2, flags); if(!l2) { rasqal_free_literal(l1); goto failed; } d=rasqal_literal_as_floating(l1, &error1) / rasqal_literal_as_floating(l2, &error2); rasqal_free_literal(l1); rasqal_free_literal(l2); if(error || error1 || error2) goto failed; result=rasqal_new_double_literal(d); break; } case RASQAL_EXPR_REM: { rasqal_literal *l1, *l2; int i; int error1=0; int error2=0; l1=rasqal_expression_evaluate(query, e->arg1, flags); if(!l1) goto failed; l2=rasqal_expression_evaluate(query, e->arg2, flags); if(!l2) { rasqal_free_literal(l1); goto failed; } i=rasqal_literal_as_integer(l1, &error1) % rasqal_literal_as_integer(l2, &error2); rasqal_free_literal(l1); rasqal_free_literal(l2); if(error || error1 || error2) goto failed; result=rasqal_new_integer_literal(RASQAL_LITERAL_INTEGER, i); break; } case RASQAL_EXPR_STR_EQ: { rasqal_literal *l1, *l2; int b; l1=rasqal_expression_evaluate(query, e->arg1, flags); if(!l1) goto failed; l2=rasqal_expression_evaluate(query, e->arg2, flags); if(!l2) { rasqal_free_literal(l1); goto failed; } b=(rasqal_literal_compare(l1, l2, flags | RASQAL_COMPARE_NOCASE, &error) == 0); rasqal_free_literal(l1); rasqal_free_literal(l2); if(error) goto failed; result=rasqal_new_boolean_literal(b); break; } case RASQAL_EXPR_STR_NEQ: { rasqal_literal *l1, *l2; int b; l1=rasqal_expression_evaluate(query, e->arg1, flags); if(!l1) goto failed; l2=rasqal_expression_evaluate(query, e->arg2, flags); if(!l2) { rasqal_free_literal(l1); goto failed; } b=(rasqal_literal_compare(l1, l2, flags | RASQAL_COMPARE_NOCASE, &error) != 0); rasqal_free_literal(l1); rasqal_free_literal(l2); if(error) goto failed; result=rasqal_new_boolean_literal(b); break; } case RASQAL_EXPR_TILDE: { rasqal_literal *l; int i; l=rasqal_expression_evaluate(query, e->arg1, flags); if(!l) goto failed; i= ~ rasqal_literal_as_integer(l, &error); rasqal_free_literal(l); if(error) goto failed; result=rasqal_new_integer_literal(RASQAL_LITERAL_INTEGER, i); break; } case RASQAL_EXPR_BANG: { rasqal_literal *l; int b; l=rasqal_expression_evaluate(query, e->arg1, flags); if(!l) goto failed; b= ! rasqal_literal_as_boolean(l, &error); rasqal_free_literal(l); if(error) goto failed; result=rasqal_new_boolean_literal(b); break; } case RASQAL_EXPR_STR_MATCH: case RASQAL_EXPR_STR_NMATCH: case RASQAL_EXPR_REGEX: { int b=0; int flag_i=0; /* flags contains i */ const unsigned char *p; const unsigned char *match_string; const unsigned char *pattern; const unsigned char *regex_flags; rasqal_literal *l1, *l2, *l3; int rc=0; #ifdef RASQAL_REGEX_PCRE pcre* re; int options=0; const char *re_error=NULL; int erroffset=0; #endif #ifdef RASQAL_REGEX_POSIX regex_t reg; int options=REG_EXTENDED | REG_NOSUB; #endif l1=rasqal_expression_evaluate(query, e->arg1, flags); if(!l1) goto failed; match_string=rasqal_literal_as_string_flags(l1, flags, &error); if(error || !match_string) { rasqal_free_literal(l1); goto failed; } l3=NULL; regex_flags=NULL; if(e->op == RASQAL_EXPR_REGEX) { l2=rasqal_expression_evaluate(query, e->arg2, flags); if(!l2) { rasqal_free_literal(l1); goto failed; } if(e->arg3) { l3=rasqal_expression_evaluate(query, e->arg3, flags); if(!l3) { rasqal_free_literal(l1); rasqal_free_literal(l2); goto failed; } regex_flags=l3->string; } } else { l2=e->literal; regex_flags=l2->flags; } pattern=l2->string; for(p=regex_flags; p && *p; p++) if(*p == 'i') flag_i++; #ifdef RASQAL_REGEX_PCRE if(flag_i) options |= PCRE_CASELESS; re=pcre_compile((const char*)pattern, options, &re_error, &erroffset, NULL); if(!re) rasqal_query_error(query, "Regex compile of '%s' failed - %s", pattern, re_error); else { rc=pcre_exec(re, NULL, /* no study */ (const char*)match_string, strlen((const char*)match_string), 0 /* startoffset */, 0 /* options */, NULL, 0 /* ovector, ovecsize - no matches wanted */ ); if(rc >= 0) b=1; else if(rc != PCRE_ERROR_NOMATCH) { rasqal_query_error(query, "Regex match failed - returned code %d", rc); rc= -1; } else rc=0; } #endif #ifdef RASQAL_REGEX_POSIX if(flag_i) options |=REG_ICASE; rc=regcomp(®, (const char*)pattern, options); if(rc) { rasqal_query_error(query, "Regex compile of '%s' failed", pattern); rc= -1; } else { rc=regexec(®, (const char*)match_string, 0, NULL, /* nmatch, regmatch_t pmatch[] - no matches wanted */ 0 /* eflags */ ); if(!rc) b=1; else if (rc != REG_NOMATCH) { rasqal_query_error(query, "Regex match failed - returned code %d", rc); rc= -1; } else rc= 0; } regfree(®); #endif #ifdef RASQAL_REGEX_NONE rasqal_query_warning(query, "Regex support missing, cannot compare '%s' to '%s'", match_string, pattern); b=1; rc= -1; #endif RASQAL_DEBUG5("regex match returned %s for '%s' against '%s' (flags=%s)\n", b ? "true" : "false", match_string, pattern, l2->flags ? (char*)l2->flags : ""); if(e->op == RASQAL_EXPR_STR_NMATCH) b=1-b; rasqal_free_literal(l1); if(e->op == RASQAL_EXPR_REGEX) { rasqal_free_literal(l2); if(l3) rasqal_free_literal(l3); } if(rc<0) goto failed; result=rasqal_new_boolean_literal(b); break; } case RASQAL_EXPR_LITERAL: result=rasqal_new_literal_from_literal(e->literal); break; case RASQAL_EXPR_FUNCTION: rasqal_query_warning(query, "No function expressions support at present. Returning false."); result=rasqal_new_boolean_literal(0); break; case RASQAL_EXPR_CAST: { rasqal_literal *l1; const unsigned char *string; unsigned char *new_string; raptor_uri *uri; l1=rasqal_expression_evaluate(query, e->arg1, flags); if(!l1) goto failed; string=rasqal_literal_as_string_flags(l1, flags, &error); if(error) { rasqal_free_literal(l1); goto failed; } new_string=(unsigned char*)RASQAL_MALLOC(string, strlen((const char*)string)+1); strcpy((char*)new_string, (const char*)string); uri=raptor_uri_copy(e->name); rasqal_free_literal(l1); result=rasqal_new_string_literal(new_string, NULL, uri, NULL); break; } case RASQAL_EXPR_ORDER_COND_ASC: case RASQAL_EXPR_ORDER_COND_DESC: result=rasqal_expression_evaluate(query, e->arg1, flags); break; case RASQAL_EXPR_UNKNOWN: default: RASQAL_FATAL2("Unknown operation %d", e->op); } failed: #ifdef RASQAL_DEBUG RASQAL_DEBUG2("result of %p: ", e); if(result) rasqal_literal_print(result, stderr); else fputs("(NULL)",stderr); fputc('\n', stderr); #endif return result; } static const char* rasqal_op_labels[RASQAL_EXPR_LAST+1]={ "UNKNOWN", "and", "or", "eq", "neq", "lt", "gt", "le", "ge", "uminus", "plus", "minus", "star", "slash", "rem", "str_eq", "str_ne", "str_match", "str_nmatch", "tilde", "bang", "literal", "function", "bound", "str", "lang", "datatype", "isUri", "isBlank", "isLiteral", "cast", "order asc", "order desc", "langMatches", "regex" }; /** * rasqal_expression_print_op: * @e: the #rasqal_expression object * @fh: the #FILE* handle to print to * * Print a rasqal expression operator in a debug format. * * The print debug format may change in any release. **/ void rasqal_expression_print_op(rasqal_expression* e, FILE* fh) { rasqal_op op=e->op; if(op > RASQAL_EXPR_LAST) op=RASQAL_EXPR_UNKNOWN; fputs(rasqal_op_labels[(int)op], fh); } /** * rasqal_expression_print: * @e: #rasqal_expression object. * @fh: The #FILE* handle to print to. * * Print a Rasqal expression in a debug format. * * The print debug format may change in any release. **/ void rasqal_expression_print(rasqal_expression* e, FILE* fh) { fputs("expr(", fh); switch(e->op) { case RASQAL_EXPR_AND: case RASQAL_EXPR_OR: case RASQAL_EXPR_EQ: case RASQAL_EXPR_NEQ: case RASQAL_EXPR_LT: case RASQAL_EXPR_GT: case RASQAL_EXPR_LE: case RASQAL_EXPR_GE: case RASQAL_EXPR_PLUS: case RASQAL_EXPR_MINUS: case RASQAL_EXPR_STAR: case RASQAL_EXPR_SLASH: case RASQAL_EXPR_REM: case RASQAL_EXPR_STR_EQ: case RASQAL_EXPR_STR_NEQ: case RASQAL_EXPR_LANGMATCHES: case RASQAL_EXPR_REGEX: fputs("op ", fh); rasqal_expression_print_op(e, fh); fputc('(', fh); rasqal_expression_print(e->arg1, fh); fputs(", ", fh); rasqal_expression_print(e->arg2, fh); /* There is only one 3-op expression and it's handled here */ if(e->op == RASQAL_EXPR_REGEX && e->arg3) { fputs(", ", fh); rasqal_expression_print(e->arg3, fh); } fputc(')', fh); break; case RASQAL_EXPR_STR_MATCH: case RASQAL_EXPR_STR_NMATCH: fputs("op ", fh); rasqal_expression_print_op(e, fh); fputc('(', fh); rasqal_expression_print(e->arg1, fh); fputs(", ", fh); rasqal_literal_print(e->literal, fh); fputc(')', fh); break; case RASQAL_EXPR_TILDE: case RASQAL_EXPR_BANG: case RASQAL_EXPR_UMINUS: case RASQAL_EXPR_BOUND: case RASQAL_EXPR_STR: case RASQAL_EXPR_LANG: case RASQAL_EXPR_DATATYPE: case RASQAL_EXPR_ISURI: case RASQAL_EXPR_ISBLANK: case RASQAL_EXPR_ISLITERAL: case RASQAL_EXPR_ORDER_COND_ASC: case RASQAL_EXPR_ORDER_COND_DESC: fputs("op ", fh); rasqal_expression_print_op(e, fh); fputc('(', fh); rasqal_expression_print(e->arg1, fh); fputc(')', fh); break; case RASQAL_EXPR_LITERAL: rasqal_literal_print(e->literal, fh); break; case RASQAL_EXPR_FUNCTION: fputs("function(uri=", fh); raptor_uri_print(e->name, fh); fputs(", args=", fh); raptor_sequence_print(e->args, fh); fputc(')', fh); break; case RASQAL_EXPR_CAST: fputs("cast(type=", fh); raptor_uri_print(e->name, fh); fputs(", value=", fh); rasqal_expression_print(e->arg1, fh); fputc(')', fh); break; case RASQAL_EXPR_UNKNOWN: default: RASQAL_FATAL2("Unknown operation %d", e->op); } fputc(')', fh); } /* for use with rasqal_expression_visit and user_data=rasqal_query */ int rasqal_expression_has_qname(void *user_data, rasqal_expression *e) { if(e->op == RASQAL_EXPR_LITERAL) return rasqal_literal_has_qname(e->literal); return 0; } /* for use with rasqal_expression_visit and user_data=rasqal_query */ int rasqal_expression_expand_qname(void *user_data, rasqal_expression *e) { if(e->op == RASQAL_EXPR_LITERAL) return rasqal_literal_expand_qname(user_data, e->literal); return 0; } int rasqal_expression_is_constant(rasqal_expression* e) { int i; int result=0; switch(e->op) { case RASQAL_EXPR_AND: case RASQAL_EXPR_OR: case RASQAL_EXPR_EQ: case RASQAL_EXPR_NEQ: case RASQAL_EXPR_LT: case RASQAL_EXPR_GT: case RASQAL_EXPR_LE: case RASQAL_EXPR_GE: case RASQAL_EXPR_PLUS: case RASQAL_EXPR_MINUS: case RASQAL_EXPR_STAR: case RASQAL_EXPR_SLASH: case RASQAL_EXPR_REM: case RASQAL_EXPR_STR_EQ: case RASQAL_EXPR_STR_NEQ: case RASQAL_EXPR_LANGMATCHES: result=rasqal_expression_is_constant(e->arg1) && rasqal_expression_is_constant(e->arg2); break; case RASQAL_EXPR_REGEX: result=rasqal_expression_is_constant(e->arg1) && rasqal_expression_is_constant(e->arg2) && (e->arg3 && rasqal_expression_is_constant(e->arg3)); break; case RASQAL_EXPR_STR_MATCH: case RASQAL_EXPR_STR_NMATCH: result=rasqal_expression_is_constant(e->arg1) && rasqal_literal_is_constant(e->literal); break; case RASQAL_EXPR_TILDE: case RASQAL_EXPR_BANG: case RASQAL_EXPR_UMINUS: case RASQAL_EXPR_BOUND: case RASQAL_EXPR_STR: case RASQAL_EXPR_LANG: case RASQAL_EXPR_DATATYPE: case RASQAL_EXPR_ISURI: case RASQAL_EXPR_ISBLANK: case RASQAL_EXPR_ISLITERAL: case RASQAL_EXPR_ORDER_COND_ASC: case RASQAL_EXPR_ORDER_COND_DESC: result=rasqal_expression_is_constant(e->arg1); break; case RASQAL_EXPR_LITERAL: result=rasqal_literal_is_constant(e->literal); break; case RASQAL_EXPR_FUNCTION: result=1; for(i=0; iargs); i++) { rasqal_expression* e2=(rasqal_expression*)raptor_sequence_get_at(e->args, i); if(!rasqal_expression_is_constant(e2)) { result=0; break; } } break; case RASQAL_EXPR_CAST: result=rasqal_expression_is_constant(e->arg1); break; case RASQAL_EXPR_UNKNOWN: default: RASQAL_FATAL2("Unknown operation %d", e->op); } return result; } void rasqal_expression_convert_to_literal(rasqal_expression* e, rasqal_literal* l) { int usage=e->usage; /* update expression 'e' in place */ rasqal_expression_clear(e); memset(e, 0, sizeof(rasqal_expression)); e->usage=usage; e->op=RASQAL_EXPR_LITERAL; e->literal=l; } #endif /* not STANDALONE */ #ifdef STANDALONE #include int main(int argc, char *argv[]); #define assert_match(function, result, string) do { if(strcmp(result, string)) { fprintf(stderr, #function " failed - returned %s, expected %s\n", result, string); exit(1); } } while(0) int main(int argc, char *argv[]) { const char *program=rasqal_basename(argv[0]); rasqal_literal *lit1, *lit2; rasqal_expression *expr1, *expr2; rasqal_expression* expr; rasqal_literal* result; int error=0; raptor_init(); rasqal_uri_init(); lit1=rasqal_new_integer_literal(RASQAL_LITERAL_INTEGER, 1); expr1=rasqal_new_literal_expression(lit1); lit2=rasqal_new_integer_literal(RASQAL_LITERAL_INTEGER, 1); expr2=rasqal_new_literal_expression(lit2); expr=rasqal_new_2op_expression(RASQAL_EXPR_PLUS, expr1, expr2); fprintf(stderr, "%s: expression: ", program); rasqal_expression_print(expr, stderr); fputc('\n', stderr); result=rasqal_expression_evaluate(NULL, expr, 0); if(result) { int bresult; fprintf(stderr, "%s: expression result: \n", program); rasqal_literal_print(result, stderr); fputc('\n', stderr); bresult=rasqal_literal_as_boolean(result, &error); if(error) { fprintf(stderr, "%s: boolean expression FAILED\n", program); } else fprintf(stderr, "%s: boolean expression result: %d\n", program, bresult); } else fprintf(stderr, "%s: expression evaluation FAILED with error\n", program); rasqal_free_expression(expr); if(result) rasqal_free_literal(result); rasqal_uri_finish(); raptor_finish(); return error; } #endif