--- /dev/null
+/*
+ Copyright (C) 2012 Modelon AB
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the BSD style license.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ FMILIB_License.txt file for more details.
+
+ You should have received a copy of the FMILIB_License.txt file
+ along with this program. If not, contact Modelon AB <http://www.modelon.com>.
+*/
+#include <stdio.h>
+#include <ctype.h>\r
+
+#include <jm_vector.h>
+#include <jm_stack.h>
+#include <jm_wc_match.h>
+#include "fmi2_xml_query.h"
+
+
+jm_name_ID_map_t fmi2_xml_q_elementary_map[fmi2_xml_elementary_enu_num+1] =
+{
+ #define FMI2_XML_Q_ELEMENTARY_MAP(name) {#name , fmi2_xml_q_elmentary_enu_##name},
+ FMI2_XML_Q_ELEMENTARY(FMI2_XML_Q_ELEMENTARY_MAP)
+ {0,0}
+};
+
+fmi2_xml_q_scan_elementary_ft fmi2_xml_q_scan_elementary_handles[fmi2_xml_elementary_enu_num + 1] = {
+ #define FMI2_XML_Q_ELEMENTARY_SCAN(name) fmi2_xml_q_scan_elementary_##name,
+ FMI2_XML_Q_ELEMENTARY(FMI2_XML_Q_ELEMENTARY_SCAN)
+ 0
+};
+
+fmi2_xml_q_eval_elementary_ft fmi2_xml_q_eval_elementary_handles[fmi2_xml_elementary_enu_num + 1] = {
+ #define FMI2_XML_Q_ELEMENTARY_EVAL(name) fmi2_xml_q_eval_elementary_##name,
+ FMI2_XML_Q_ELEMENTARY(FMI2_XML_Q_ELEMENTARY_EVAL)
+ 0
+};
+
+jm_name_ID_map_t fmi2_xml_q_op_map[] = {
+ {"or", fmi2_xml_q_term_enu_OR},
+ {"and", fmi2_xml_q_term_enu_AND},
+ {"not", fmi2_xml_q_term_enu_NOT},
+ {"&&", fmi2_xml_q_term_enu_AND},
+ {"||", fmi2_xml_q_term_enu_OR},
+ {"!", fmi2_xml_q_term_enu_NOT},
+ {0,-1}
+};
+
+static void fmi2_xml_q_skip_space(jm_string* cur) {
+ jm_string curChP = *cur;
+ char curCh;
+ if(!curChP) return;
+ curCh = *curChP;
+ while(curCh || (curCh == ' ') || (curCh == '\t')) {
+ curChP++; curCh = *curChP;
+ }
+ *cur = curChP;
+}
+
+int fmi2_xml_q_scan_string(fmi2_xml_q_context_t* context, char** param_str) {
+ fmi2_xml_q_expression_t* expr = &context->expr;
+ char* dest;
+ jm_string cur = context->query + context->curCh;
+ char ch = *cur;
+ char strterm ;
+ size_t strlen = 0;
+
+ if((ch == '\'') || (ch == '"')) /* either ' or " can be used as string terminator */
+ strterm = ch;
+ else
+ return -1;
+ do {
+ ch = cur[strlen+1];
+ dest = jm_vector_push_back(char)(&expr->strbuf, ch);
+ assert(dest);
+ strlen++;
+ } while((ch != strterm) && ch);
+ if(!ch) return -1; /* string is not terminated */
+ *dest = 0; /* put terminating 0*/
+ strlen--; /* last zero is not a part of the string */
+ *param_str = dest - strlen;
+ return strlen;
+}
+
+
+int fmi2_xml_q_scan_elementary_name(fmi2_xml_q_context_t* context, fmi2_xml_q_terminal_t* term) {
+ jm_string startCh = &context->query[context->curCh];
+ jm_string curCh = startCh;
+ size_t len;
+
+ /* expecting: [<spaces>]'='[<spaces>]<string> */
+
+ fmi2_xml_q_skip_space(&curCh);
+ if(*curCh != '=')
+ return (int)(startCh - curCh);
+ curCh++;
+ fmi2_xml_q_skip_space(&curCh);
+
+ len = (int)(curCh - startCh);
+
+ context->curCh += len;
+ startCh += len;
+
+ if( fmi2_xml_q_scan_string(context, &term->param_str) < 0)
+ return (int)(startCh - curCh);
+
+ /* treat as regexp - > skip for now
+ if(term->param_str[0] == '^') {
+
+ }
+ else */
+ {
+ term->param_i = strlen(term->param_str);
+ if( (strchr ( term->param_str, '*') != 0) || (strchr ( term->param_str, '?') != 0) ){
+ /* treat as wildcard */
+ term->param_i *= -1;
+ }
+ }
+ return (int)(curCh - startCh);
+}
+
+int fmi2_xml_q_eval_elementary_name(fmi2_xml_variable_t* var, fmi2_xml_q_terminal_t* term) {
+ assert(term->specific == fmi2_xml_q_elmentary_enu_name);
+
+ if(term->param_i < 0) {
+ return jm_wc_match(term->param_str, fmi2_xml_get_variable_name(var));
+ }
+ else
+ return (strncmp(term->param_str, fmi2_xml_get_variable_name(var), term->param_i) == 0);
+ return 0;
+}
+
+int fmi2_xml_q_scan_elementary_unit(fmi2_xml_q_context_t* context, fmi2_xml_q_terminal_t* term) {
+ jm_string startCh = &context->query[context->curCh];
+ jm_string curCh = startCh;
+ size_t len;
+
+ /* expecting: [<spaces>]'='[<spaces>]<string> */
+
+ fmi2_xml_q_skip_space(&curCh);
+ if(*curCh != '=')
+ return (int)(startCh - curCh);
+ curCh++;
+ fmi2_xml_q_skip_space(&curCh);
+
+ len = (int)(curCh - startCh);
+
+ context->curCh += len;
+ startCh += len;
+
+ if( fmi2_xml_q_scan_string(context, &term->param_str) < 0)
+ return (int)(startCh - curCh);
+ return (int)(curCh - startCh);
+}
+
+int fmi2_xml_q_eval_elementary_unit(fmi2_xml_variable_t* var, fmi2_xml_q_terminal_t* term) {
+ return 0;
+}
+
+int fmi2_xml_q_get_number(fmi2_xml_q_context_t* context, char* cur, double* val, char* buf) {
+ int len;
+ if(sscanf(cur, "%lg%s", val, buf) != 2) return 0;
+ len = strlen(cur) - strlen(buf);
+ return len;
+}
+
+int fmi2_xml_q_get_keyword(fmi2_xml_q_context_t* context, char* cur, size_t* len, char* buf) {
+ char ch = *cur;
+ size_t i = 0, id;
+ *len = 0;
+ if( (ch == '|') || (ch == '&') ) {
+ if(ch == cur[i+1]) {
+ buf[i++] = ch;
+ buf[i++] = ch;
+ }
+ }
+ else {
+ while(isalpha(ch)) {
+ buf[i++] = tolower(ch);
+ ch = cur[i];
+ }
+ }
+
+ if(!i) return -1;
+
+ {
+ jm_name_ID_map_t key;
+ jm_name_ID_map_t* map;
+ key.name = buf;
+ map = jm_vector_bsearch(jm_name_ID_map_t)(&context->elementary_map, &key,jm_compare_name);
+ if(!map) return -1;
+ id = map->ID;
+ }
+ *len = i;
+ return id;
+}
+
+
+/* if we ever get to regex then this might be a good function */
+int pattern2regexp(const char* pattern, jm_vector(char)* re) {
+ size_t plen = strlen(pattern), i;
+ if(jm_vector_reserve_char(re, plen * 2 + 3) < plen) return -1;
+ jm_vector_resize_char(re, 0);
+ jm_vector_push_back_char(re, '^');
+ for(i=0; i < plen; i++) {
+ char cur = pattern[i];
+ switch(cur) {
+ case '*':
+ jm_vector_push_back_char(re, '.');
+ jm_vector_push_back_char(re,'*');
+ break;
+ case '?':
+ jm_vector_push_back_char(re,'.');
+ break;
+ default:
+ jm_vector_push_back_char(re,'\\');
+ jm_vector_push_back_char(re,cur);
+ }
+ }
+ jm_vector_push_back_char(re, '$');
+ jm_vector_push_back_char(re, 0);
+ return 0;
+}
+
+int fmi2_xml_evaluate_terminal(fmi2_xml_variable_t* var, fmi2_xml_q_terminal_t* term) {
+ return fmi2_xml_q_eval_elementary_handles[term->specific](var, term);
+}
+
+int fmi2_xml_q_filter_variable(fmi2_xml_variable_t* var, fmi2_xml_q_expression_t* expr) {
+ size_t cur, len = jm_vector_get_size(jm_voidp)(&expr->expression);
+ jm_vector(jm_voidp)* stack = &expr->stack;
+ fmi2_xml_q_terminal_t * term;
+ for(cur = 0; cur < len; cur++) {
+ fmi2_xml_q_terminal_t *argL, *argR;
+ size_t curlen = jm_vector_get_size(jm_voidp)(stack);
+
+ term = (fmi2_xml_q_terminal_t *)jm_vector_get_item(jm_voidp)(&expr->expression, cur);
+ argL = (curlen > 0) ? (fmi2_xml_q_terminal_t *)jm_vector_get_item(jm_voidp)(stack,curlen -1):0;
+ argR = (curlen > 1) ? (fmi2_xml_q_terminal_t *)jm_vector_get_item(jm_voidp)(stack,curlen -2):0;
+
+ switch(term -> kind) {
+ case fmi2_xml_q_term_enu_AND:
+ assert(argL && argR);
+ jm_vector_resize(jm_voidp)(stack, curlen -2);
+ if((argL->kind == fmi2_xml_q_term_enu_FALSE) || (argR->kind == fmi2_xml_q_term_enu_FALSE))
+ jm_vector_push_back(jm_voidp)(stack, &expr->termFalse);
+ else {
+ jm_vector_push_back(jm_voidp)(stack, (fmi2_xml_evaluate_terminal(var, argL) && fmi2_xml_evaluate_terminal(var, argR))? &expr->termTrue: &expr->termFalse );
+ }
+ break;
+ case fmi2_xml_q_term_enu_OR:
+ assert(argL && argR);
+ jm_vector_resize(jm_voidp)(stack, curlen -2);
+ if((argL->kind == fmi2_xml_q_term_enu_TRUE) || (argR->kind == fmi2_xml_q_term_enu_TRUE))
+ jm_vector_push_back(jm_voidp)(stack, &expr->termTrue);
+ else {
+ jm_vector_push_back(jm_voidp)(stack, (fmi2_xml_evaluate_terminal(var, argL) || fmi2_xml_evaluate_terminal(var, argR))? &expr->termTrue: &expr->termFalse);
+ }
+ break;
+ case fmi2_xml_q_term_enu_NOT:
+ assert(argL);
+ jm_vector_resize(jm_voidp)(stack, curlen -1);
+ if(argL->kind == fmi2_xml_q_term_enu_TRUE)
+ jm_vector_push_back(jm_voidp)(stack, &expr->termFalse);
+ else if(argL->kind == fmi2_xml_q_term_enu_TRUE)
+ jm_vector_push_back(jm_voidp)(stack, &expr->termTrue);
+ else {
+ jm_vector_push_back(jm_voidp)(stack, (fmi2_xml_evaluate_terminal(var, argL)? &expr->termFalse: &expr->termTrue));
+ }
+ break;
+ case fmi2_xml_q_term_enu_LP:
+ case fmi2_xml_q_term_enu_RP:
+ assert(0);
+ break;
+ default:
+ jm_vector_push_back(jm_voidp)(stack, term); /* only evaluate when needed. push as is at first */
+ }
+ }
+ assert(jm_vector_get_size(jm_voidp)(stack) == 1);
+
+ term = (fmi2_xml_q_terminal_t *)jm_vector_get_item(jm_voidp)(stack,0);
+ if(term->kind == fmi2_xml_q_term_enu_FALSE) return 0;
+ assert(term->kind == fmi2_xml_q_term_enu_TRUE);
+ return 1;
+}
+
+fmi2_xml_q_expression_t* fmi2_xml_alloc_expression(jm_string query) {
+ return 0;
+}
+
+int fmi2_xml_q_parse_elementary(fmi2_xml_q_context_t* context, fmi2_xml_q_terminal_t* term) {
+ return 0;
+}
+
+void fmi2_xml_q_init_context(fmi2_xml_q_context_t* c, jm_callbacks* cb) {
+ size_t l = jm_vector_init(jm_name_ID_map_t)(&c->elementary_map, fmi2_xml_elementary_enu_num, cb);
+ assert(l);
+ for(l = 0; l < fmi2_xml_elementary_enu_num; l++) {
+ jm_vector_set_item(jm_name_ID_map_t)(&c->elementary_map, l,fmi2_xml_q_elementary_map[l]);
+ }
+ jm_vector_qsort(jm_name_ID_map_t)(&c->elementary_map,jm_compare_name);
+ c->query = 0;
+ c->qlen = 0;
+ c->curCh = 0;
+ jm_vector_init(char)(&c->buf,0,cb);
+
+ {
+ fmi2_xml_q_expression_t* expr = &c->expr;
+ jm_vector_init(jm_voidp)(&expr->expression,0,cb);
+ jm_vector_init(jm_voidp)(&expr->stack,0,cb);
+ expr->termFalse.kind = fmi2_xml_q_term_enu_FALSE;
+ expr->termTrue.kind = fmi2_xml_q_term_enu_TRUE;
+ jm_vector_init(fmi2_xml_q_terminal_t)(&expr->terms,0,cb);
+ jm_vector_init(char)(&expr->strbuf, 0, cb);
+ }
+}
+
+void fmi2_xml_q_free_context_data(fmi2_xml_q_context_t* c){
+ fmi2_xml_q_expression_t* expr = &c->expr;
+ jm_vector_free_data(jm_name_ID_map_t)(&c->elementary_map);
+ jm_vector_free_data(char)(&c->buf);
+ jm_vector_free_data(jm_voidp)(&expr->expression);
+ jm_vector_free_data(jm_voidp)(&expr->stack);
+ jm_vector_free_data(fmi2_xml_q_terminal_t)(&expr->terms);
+ jm_vector_free_data(char)(&expr->strbuf);
+}
+
+int fmi2_xml_q_parse_terminal(fmi2_xml_q_context_t* context, fmi2_xml_q_terminal_t** ppterm) {
+ int offset = 0;
+ fmi2_xml_q_terminal_t* pterm;
+ jm_string startCh = context->query + context->curCh;
+ jm_string cur = startCh;
+ pterm = jm_vector_resize1(fmi2_xml_q_terminal_t)(&context->expr.terms);
+ if(!pterm) return -1;
+ *ppterm = pterm;
+ fmi2_xml_q_skip_space(&cur);
+ switch(*cur) {
+ case '(':
+ pterm->kind = fmi2_xml_q_term_enu_LP;
+ break;
+ case ')':
+ pterm->kind = fmi2_xml_q_term_enu_RP;
+ break;
+ case '&':
+ pterm->kind = fmi2_xml_q_term_enu_AND;
+ break;
+ case '|':
+ pterm->kind = fmi2_xml_q_term_enu_OR;
+ break;
+ case '!':
+ pterm->kind = fmi2_xml_q_term_enu_NOT;
+ break;
+ case 0:
+ pterm->kind = fmi2_xml_q_term_enu_END;
+ break;
+ default:
+ fmi2_xml_q_parse_elementary(context, pterm);
+ }
+ fmi2_xml_q_skip_space(&cur);
+ return (int)(cur - startCh);
+}
+
+int fmi2_xml_q_parse_query(fmi2_xml_q_context_t* context, jm_string query) {
+ fmi2_xml_q_expression_t* expr = &context->expr;
+ int qlen = strlen(query);
+ int offset = 0, curCh = 0;
+ int expectOperand = 1;
+ size_t stacklen = 0;
+ fmi2_xml_q_terminal_t* stackTop = 0;
+
+ context->query = query;
+ context->qlen = qlen;
+ if(jm_vector_reserve(char)(&context->buf, qlen) < (size_t)qlen) return -1;
+ if(jm_vector_reserve(char)(&context->expr.strbuf, qlen) < (size_t)qlen) return -1;
+
+ while(curCh < qlen) {
+ fmi2_xml_q_terminal_t* term;
+ size_t explen = jm_vector_get_size(jm_voidp)(&expr->expression);
+ fmi2_xml_q_terminal_t* expTop = explen? (fmi2_xml_q_terminal_t*)jm_vector_get_item(jm_voidp)(&expr->expression,explen -1):0;
+
+ offset = fmi2_xml_q_parse_terminal(context, &term);
+
+ if(offset < 0) return -curCh;
+
+ stacklen = jm_vector_get_size(jm_voidp)(&expr->stack);
+ stackTop = stacklen ? (fmi2_xml_q_terminal_t*)jm_vector_get_item(jm_voidp)(&expr->stack,stacklen -1):(fmi2_xml_q_terminal_t*)0;
+
+ switch(term -> kind) {
+ case fmi2_xml_q_term_enu_LP:
+ if(!expectOperand) return -curCh;
+ jm_vector_push_back(jm_voidp)(&expr->stack, term);
+ break;
+ case fmi2_xml_q_term_enu_RP:
+ if(expectOperand) return -curCh;
+ while(stackTop && (stackTop->kind != fmi2_xml_q_term_enu_LP)) {
+ jm_vector_push_back(jm_voidp)(&expr->expression, stackTop);
+ stacklen--;
+ jm_vector_resize(jm_voidp)(&expr->stack, stacklen);
+ stackTop = stacklen? (fmi2_xml_q_terminal_t*)jm_vector_get_item(jm_voidp)(&expr->stack,stacklen -1):0;
+ }
+ if(!stackTop) return -curCh;
+ jm_vector_resize(jm_voidp)(&expr->stack, stacklen -1);
+ break;
+
+ case fmi2_xml_q_term_enu_AND:
+ if(expectOperand) return -curCh;
+ expectOperand = 1;
+ if(!expTop) return -curCh;
+ if(stackTop && (stackTop->kind == fmi2_xml_q_term_enu_AND))
+ jm_vector_push_back(jm_voidp)(&expr->expression, term);
+ else
+ jm_vector_push_back(jm_voidp)(&expr->stack, term);
+ break;
+ case fmi2_xml_q_term_enu_OR:
+ if(expectOperand) return -curCh;
+ expectOperand = 1;
+ if(!expTop) return -curCh;
+ while(stackTop && ((stackTop->kind == fmi2_xml_q_term_enu_AND)||(stackTop->kind == fmi2_xml_q_term_enu_OR))) {
+ jm_vector_push_back(jm_voidp)(&expr->expression, stackTop);
+ jm_vector_resize(jm_voidp)(&expr->stack, stacklen -1);
+ stacklen--;
+ stackTop = stacklen? jm_vector_get_item(jm_voidp)(&expr->stack,stacklen -1):0;
+ }
+ jm_vector_push_back(jm_voidp)(&expr->stack, term);
+ break;
+ default:
+ if(!expectOperand) return -curCh;
+ expectOperand = 0;
+ jm_vector_push_back(jm_voidp)(&expr->expression, term);
+
+ }
+ curCh += offset;
+ context->curCh = curCh;
+ }
+ if(expectOperand) return -curCh;
+ while(stackTop && (stackTop->kind != fmi2_xml_q_term_enu_LP)) {
+ jm_vector_push_back(jm_voidp)(&expr->expression, stackTop);
+ jm_vector_resize(jm_voidp)(&expr->stack, stacklen -1);
+ stacklen--;
+ stackTop = stacklen? (fmi2_xml_q_terminal_t*)jm_vector_get_item(jm_voidp)(&expr->stack,stacklen -1):0;
+ }
+ if(!stackTop) return -curCh;
+ jm_vector_resize(jm_voidp)(&expr->stack, stacklen -1);
+
+ return 0;
+}
+
+#define JM_TEMPLATE_INSTANCE_TYPE fmi2_xml_q_terminal_t
+#include "jm_vector_template.h"