2 Copyright (C) 2012 Modelon AB
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the BSD style license.
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 FMILIB_License.txt file for more details.
12 You should have received a copy of the FMILIB_License.txt file
13 along with this program. If not, contact Modelon AB <http://www.modelon.com>.
18 #include <jm_vector.h>
20 #include <jm_wc_match.h>
21 #include "fmi1_xml_query.h"
24 jm_name_ID_map_t fmi1_xml_q_elementary_map[fmi1_xml_elementary_enu_num+1] =
26 #define FMI1_XML_Q_ELEMENTARY_MAP(name) {#name , fmi1_xml_q_elmentary_enu_##name},
27 FMI1_XML_Q_ELEMENTARY(FMI1_XML_Q_ELEMENTARY_MAP)
31 fmi1_xml_q_scan_elementary_ft fmi1_xml_q_scan_elementary_handles[fmi1_xml_elementary_enu_num + 1] = {
32 #define FMI1_XML_Q_ELEMENTARY_SCAN(name) fmi1_xml_q_scan_elementary_##name,
33 FMI1_XML_Q_ELEMENTARY(FMI1_XML_Q_ELEMENTARY_SCAN)
37 fmi1_xml_q_eval_elementary_ft fmi1_xml_q_eval_elementary_handles[fmi1_xml_elementary_enu_num + 1] = {
38 #define FMI1_XML_Q_ELEMENTARY_EVAL(name) fmi1_xml_q_eval_elementary_##name,
39 FMI1_XML_Q_ELEMENTARY(FMI1_XML_Q_ELEMENTARY_EVAL)
43 jm_name_ID_map_t fmi1_xml_q_op_map[] = {
44 {"or", fmi1_xml_q_term_enu_OR},
45 {"and", fmi1_xml_q_term_enu_AND},
46 {"not", fmi1_xml_q_term_enu_NOT},
47 {"&&", fmi1_xml_q_term_enu_AND},
48 {"||", fmi1_xml_q_term_enu_OR},
49 {"!", fmi1_xml_q_term_enu_NOT},
53 static void fmi1_xml_q_skip_space(jm_string* cur) {
54 jm_string curChP = *cur;
58 while(curCh || (curCh == ' ') || (curCh == '\t')) {
59 curChP++; curCh = *curChP;
64 int fmi1_xml_q_scan_string(fmi1_xml_q_context_t* context, char** param_str) {
65 fmi1_xml_q_expression_t* expr = &context->expr;
67 jm_string cur = context->query + context->curCh;
72 if((ch == '\'') || (ch == '"')) /* either ' or " can be used as string terminator */
78 dest = jm_vector_push_back(char)(&expr->strbuf, ch);
81 } while((ch != strterm) && ch);
82 if(!ch) return -1; /* string is not terminated */
83 *dest = 0; /* put terminating 0*/
84 strlen--; /* last zero is not a part of the string */
85 *param_str = dest - strlen;
90 int fmi1_xml_q_scan_elementary_name(fmi1_xml_q_context_t* context, fmi1_xml_q_terminal_t* term) {
91 jm_string startCh = &context->query[context->curCh];
92 jm_string curCh = startCh;
95 /* expecting: [<spaces>]'='[<spaces>]<string> */
97 fmi1_xml_q_skip_space(&curCh);
99 return (int)(startCh - curCh);
101 fmi1_xml_q_skip_space(&curCh);
103 len = (int)(curCh - startCh);
105 context->curCh += len;
108 if( fmi1_xml_q_scan_string(context, &term->param_str) < 0)
109 return (int)(startCh - curCh);
111 /* treat as regexp - > skip for now
112 if(term->param_str[0] == '^') {
117 term->param_i = strlen(term->param_str);
118 if( (strchr ( term->param_str, '*') != 0) || (strchr ( term->param_str, '?') != 0) ){
119 /* treat as wildcard */
123 return (int)(curCh - startCh);
126 int fmi1_xml_q_eval_elementary_name(fmi1_xml_variable_t* var, fmi1_xml_q_terminal_t* term) {
127 assert(term->specific == fmi1_xml_q_elmentary_enu_name);
129 if(term->param_i < 0) {
130 return jm_wc_match(term->param_str, fmi1_xml_get_variable_name(var));
133 return (strncmp(term->param_str, fmi1_xml_get_variable_name(var), term->param_i) == 0);
137 int fmi1_xml_q_scan_elementary_unit(fmi1_xml_q_context_t* context, fmi1_xml_q_terminal_t* term) {
138 jm_string startCh = &context->query[context->curCh];
139 jm_string curCh = startCh;
142 /* expecting: [<spaces>]'='[<spaces>]<string> */
144 fmi1_xml_q_skip_space(&curCh);
146 return (int)(startCh - curCh);
148 fmi1_xml_q_skip_space(&curCh);
150 len = (int)(curCh - startCh);
152 context->curCh += len;
155 if( fmi1_xml_q_scan_string(context, &term->param_str) < 0)
156 return (int)(startCh - curCh);
157 return (int)(curCh - startCh);
160 int fmi1_xml_q_eval_elementary_unit(fmi1_xml_variable_t* var, fmi1_xml_q_terminal_t* term) {
164 int fmi1_xml_q_get_number(fmi1_xml_q_context_t* context, char* cur, double* val, char* buf) {
166 if(sscanf(cur, "%lg%s", val, buf) != 2) return 0;
167 len = strlen(cur) - strlen(buf);
171 int fmi1_xml_q_get_keyword(fmi1_xml_q_context_t* context, char* cur, size_t* len, char* buf) {
175 if( (ch == '|') || (ch == '&') ) {
183 buf[i++] = tolower(ch);
191 jm_name_ID_map_t key;
192 jm_name_ID_map_t* map;
194 map = jm_vector_bsearch(jm_name_ID_map_t)(&context->elementary_map, &key,jm_compare_name);
203 /* if we ever get to regex then this might be a good function */
204 int pattern2regexp(const char* pattern, jm_vector(char)* re) {
205 size_t plen = strlen(pattern), i;
206 if(jm_vector_reserve_char(re, plen * 2 + 3) < plen) return -1;
207 jm_vector_resize_char(re, 0);
208 jm_vector_push_back_char(re, '^');
209 for(i=0; i < plen; i++) {
210 char cur = pattern[i];
213 jm_vector_push_back_char(re, '.');
214 jm_vector_push_back_char(re,'*');
217 jm_vector_push_back_char(re,'.');
220 jm_vector_push_back_char(re,'\\');
221 jm_vector_push_back_char(re,cur);
224 jm_vector_push_back_char(re, '$');
225 jm_vector_push_back_char(re, 0);
229 int fmi1_xml_evaluate_terminal(fmi1_xml_variable_t* var, fmi1_xml_q_terminal_t* term) {
230 return fmi1_xml_q_eval_elementary_handles[term->specific](var, term);
233 int fmi1_xml_q_filter_variable(fmi1_xml_variable_t* var, fmi1_xml_q_expression_t* expr) {
234 size_t cur, len = jm_vector_get_size(jm_voidp)(&expr->expression);
235 jm_vector(jm_voidp)* stack = &expr->stack;
236 fmi1_xml_q_terminal_t * term;
237 for(cur = 0; cur < len; cur++) {
238 fmi1_xml_q_terminal_t *argL, *argR;
239 size_t curlen = jm_vector_get_size(jm_voidp)(stack);
241 term = (fmi1_xml_q_terminal_t *)jm_vector_get_item(jm_voidp)(&expr->expression, cur);
242 argL = (curlen > 0) ? (fmi1_xml_q_terminal_t *)jm_vector_get_item(jm_voidp)(stack,curlen -1):0;
243 argR = (curlen > 1) ? (fmi1_xml_q_terminal_t *)jm_vector_get_item(jm_voidp)(stack,curlen -2):0;
245 switch(term -> kind) {
246 case fmi1_xml_q_term_enu_AND:
247 assert(argL && argR);
248 jm_vector_resize(jm_voidp)(stack, curlen -2);
249 if((argL->kind == fmi1_xml_q_term_enu_FALSE) || (argR->kind == fmi1_xml_q_term_enu_FALSE))
250 jm_vector_push_back(jm_voidp)(stack, &expr->termFalse);
252 jm_vector_push_back(jm_voidp)(stack, (fmi1_xml_evaluate_terminal(var, argL) && fmi1_xml_evaluate_terminal(var, argR))? &expr->termTrue: &expr->termFalse );
255 case fmi1_xml_q_term_enu_OR:
256 assert(argL && argR);
257 jm_vector_resize(jm_voidp)(stack, curlen -2);
258 if((argL->kind == fmi1_xml_q_term_enu_TRUE) || (argR->kind == fmi1_xml_q_term_enu_TRUE))
259 jm_vector_push_back(jm_voidp)(stack, &expr->termTrue);
261 jm_vector_push_back(jm_voidp)(stack, (fmi1_xml_evaluate_terminal(var, argL) || fmi1_xml_evaluate_terminal(var, argR))? &expr->termTrue: &expr->termFalse);
264 case fmi1_xml_q_term_enu_NOT:
266 jm_vector_resize(jm_voidp)(stack, curlen -1);
267 if(argL->kind == fmi1_xml_q_term_enu_TRUE)
268 jm_vector_push_back(jm_voidp)(stack, &expr->termFalse);
269 else if(argL->kind == fmi1_xml_q_term_enu_TRUE)
270 jm_vector_push_back(jm_voidp)(stack, &expr->termTrue);
272 jm_vector_push_back(jm_voidp)(stack, (fmi1_xml_evaluate_terminal(var, argL)? &expr->termFalse: &expr->termTrue));
275 case fmi1_xml_q_term_enu_LP:
276 case fmi1_xml_q_term_enu_RP:
280 jm_vector_push_back(jm_voidp)(stack, term); /* only evaluate when needed. push as is at first */
283 assert(jm_vector_get_size(jm_voidp)(stack) == 1);
285 term = (fmi1_xml_q_terminal_t *)jm_vector_get_item(jm_voidp)(stack,0);
286 if(term->kind == fmi1_xml_q_term_enu_FALSE) return 0;
287 assert(term->kind == fmi1_xml_q_term_enu_TRUE);
291 fmi1_xml_q_expression_t* fmi1_xml_alloc_expression(jm_string query) {
295 int fmi1_xml_q_parse_elementary(fmi1_xml_q_context_t* context, fmi1_xml_q_terminal_t* term) {
299 void fmi1_xml_q_init_context(fmi1_xml_q_context_t* c, jm_callbacks* cb) {
300 size_t l = jm_vector_init(jm_name_ID_map_t)(&c->elementary_map, fmi1_xml_elementary_enu_num, cb);
302 for(l = 0; l < fmi1_xml_elementary_enu_num; l++) {
303 jm_vector_set_item(jm_name_ID_map_t)(&c->elementary_map, l,fmi1_xml_q_elementary_map[l]);
305 jm_vector_qsort(jm_name_ID_map_t)(&c->elementary_map,jm_compare_name);
309 jm_vector_init(char)(&c->buf,0,cb);
312 fmi1_xml_q_expression_t* expr = &c->expr;
313 jm_vector_init(jm_voidp)(&expr->expression,0,cb);
314 jm_vector_init(jm_voidp)(&expr->stack,0,cb);
315 expr->termFalse.kind = fmi1_xml_q_term_enu_FALSE;
316 expr->termTrue.kind = fmi1_xml_q_term_enu_TRUE;
317 jm_vector_init(fmi1_xml_q_terminal_t)(&expr->terms,0,cb);
318 jm_vector_init(char)(&expr->strbuf, 0, cb);
322 void fmi1_xml_q_free_context_data(fmi1_xml_q_context_t* c){
323 fmi1_xml_q_expression_t* expr = &c->expr;
324 jm_vector_free_data(jm_name_ID_map_t)(&c->elementary_map);
325 jm_vector_free_data(char)(&c->buf);
326 jm_vector_free_data(jm_voidp)(&expr->expression);
327 jm_vector_free_data(jm_voidp)(&expr->stack);
328 jm_vector_free_data(fmi1_xml_q_terminal_t)(&expr->terms);
329 jm_vector_free_data(char)(&expr->strbuf);
332 int fmi1_xml_q_parse_terminal(fmi1_xml_q_context_t* context, fmi1_xml_q_terminal_t** ppterm) {
334 fmi1_xml_q_terminal_t* pterm;
335 jm_string startCh = context->query + context->curCh;
336 jm_string cur = startCh;
337 pterm = jm_vector_resize1(fmi1_xml_q_terminal_t)(&context->expr.terms);
338 if(!pterm) return -1;
340 fmi1_xml_q_skip_space(&cur);
343 pterm->kind = fmi1_xml_q_term_enu_LP;
346 pterm->kind = fmi1_xml_q_term_enu_RP;
349 pterm->kind = fmi1_xml_q_term_enu_AND;
352 pterm->kind = fmi1_xml_q_term_enu_OR;
355 pterm->kind = fmi1_xml_q_term_enu_NOT;
358 pterm->kind = fmi1_xml_q_term_enu_END;
361 fmi1_xml_q_parse_elementary(context, pterm);
363 fmi1_xml_q_skip_space(&cur);
364 return (int)(cur - startCh);
367 int fmi1_xml_q_parse_query(fmi1_xml_q_context_t* context, jm_string query) {
368 fmi1_xml_q_expression_t* expr = &context->expr;
369 int qlen = strlen(query);
370 int offset = 0, curCh = 0;
371 int expectOperand = 1;
373 fmi1_xml_q_terminal_t* stackTop = 0;
375 context->query = query;
376 context->qlen = qlen;
377 if(jm_vector_reserve(char)(&context->buf, qlen) < (size_t)qlen) return -1;
378 if(jm_vector_reserve(char)(&context->expr.strbuf, qlen) < (size_t)qlen) return -1;
380 while(curCh < qlen) {
381 fmi1_xml_q_terminal_t* term;
382 size_t explen = jm_vector_get_size(jm_voidp)(&expr->expression);
383 fmi1_xml_q_terminal_t* expTop = explen? (fmi1_xml_q_terminal_t*)jm_vector_get_item(jm_voidp)(&expr->expression,explen -1):0;
385 offset = fmi1_xml_q_parse_terminal(context, &term);
387 if(offset < 0) return -curCh;
389 stacklen = jm_vector_get_size(jm_voidp)(&expr->stack);
390 stackTop = stacklen ? (fmi1_xml_q_terminal_t*)jm_vector_get_item(jm_voidp)(&expr->stack,stacklen -1):(fmi1_xml_q_terminal_t*)0;
392 switch(term -> kind) {
393 case fmi1_xml_q_term_enu_LP:
394 if(!expectOperand) return -curCh;
395 jm_vector_push_back(jm_voidp)(&expr->stack, term);
397 case fmi1_xml_q_term_enu_RP:
398 if(expectOperand) return -curCh;
399 while(stackTop && (stackTop->kind != fmi1_xml_q_term_enu_LP)) {
400 jm_vector_push_back(jm_voidp)(&expr->expression, stackTop);
402 jm_vector_resize(jm_voidp)(&expr->stack, stacklen);
403 stackTop = stacklen? (fmi1_xml_q_terminal_t*)jm_vector_get_item(jm_voidp)(&expr->stack,stacklen -1):0;
405 if(!stackTop) return -curCh;
406 jm_vector_resize(jm_voidp)(&expr->stack, stacklen -1);
409 case fmi1_xml_q_term_enu_AND:
410 if(expectOperand) return -curCh;
412 if(!expTop) return -curCh;
413 if(stackTop && (stackTop->kind == fmi1_xml_q_term_enu_AND))
414 jm_vector_push_back(jm_voidp)(&expr->expression, term);
416 jm_vector_push_back(jm_voidp)(&expr->stack, term);
418 case fmi1_xml_q_term_enu_OR:
419 if(expectOperand) return -curCh;
421 if(!expTop) return -curCh;
422 while(stackTop && ((stackTop->kind == fmi1_xml_q_term_enu_AND)||(stackTop->kind == fmi1_xml_q_term_enu_OR))) {
423 jm_vector_push_back(jm_voidp)(&expr->expression, stackTop);
424 jm_vector_resize(jm_voidp)(&expr->stack, stacklen -1);
426 stackTop = stacklen? jm_vector_get_item(jm_voidp)(&expr->stack,stacklen -1):0;
428 jm_vector_push_back(jm_voidp)(&expr->stack, term);
431 if(!expectOperand) return -curCh;
433 jm_vector_push_back(jm_voidp)(&expr->expression, term);
437 context->curCh = curCh;
439 if(expectOperand) return -curCh;
440 while(stackTop && (stackTop->kind != fmi1_xml_q_term_enu_LP)) {
441 jm_vector_push_back(jm_voidp)(&expr->expression, stackTop);
442 jm_vector_resize(jm_voidp)(&expr->stack, stacklen -1);
444 stackTop = stacklen? (fmi1_xml_q_terminal_t*)jm_vector_get_item(jm_voidp)(&expr->stack,stacklen -1):0;
446 if(!stackTop) return -curCh;
447 jm_vector_resize(jm_voidp)(&expr->stack, stacklen -1);
452 #define JM_TEMPLATE_INSTANCE_TYPE fmi1_xml_q_terminal_t
453 #include "jm_vector_template.h"