]> gerrit.simantics Code Review - simantics/fmil.git/blob - org.simantics.fmil.core/native/FMILibrary/src/XML/src/FMI2/fmi2_xml_query.c
Add FMILibrary-2.0.3 to org.simantics.fmil.core\native.
[simantics/fmil.git] / org.simantics.fmil.core / native / FMILibrary / src / XML / src / FMI2 / fmi2_xml_query.c
1 /*
2     Copyright (C) 2012 Modelon AB
3
4     This program is free software: you can redistribute it and/or modify
5     it under the terms of the BSD style license.
6
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.
11
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>.
14 */
15 #include <stdio.h>
16 #include <ctype.h>\r
17
18 #include <jm_vector.h>
19 #include <jm_stack.h>
20 #include <jm_wc_match.h>
21 #include "fmi2_xml_query.h"
22
23
24 jm_name_ID_map_t fmi2_xml_q_elementary_map[fmi2_xml_elementary_enu_num+1] =
25 {
26         #define FMI2_XML_Q_ELEMENTARY_MAP(name) {#name , fmi2_xml_q_elmentary_enu_##name},
27     FMI2_XML_Q_ELEMENTARY(FMI2_XML_Q_ELEMENTARY_MAP)
28         {0,0}
29 };
30
31 fmi2_xml_q_scan_elementary_ft fmi2_xml_q_scan_elementary_handles[fmi2_xml_elementary_enu_num + 1] = {
32         #define FMI2_XML_Q_ELEMENTARY_SCAN(name) fmi2_xml_q_scan_elementary_##name,
33     FMI2_XML_Q_ELEMENTARY(FMI2_XML_Q_ELEMENTARY_SCAN)
34         0
35 };
36
37 fmi2_xml_q_eval_elementary_ft fmi2_xml_q_eval_elementary_handles[fmi2_xml_elementary_enu_num + 1] = {
38         #define FMI2_XML_Q_ELEMENTARY_EVAL(name) fmi2_xml_q_eval_elementary_##name,
39     FMI2_XML_Q_ELEMENTARY(FMI2_XML_Q_ELEMENTARY_EVAL)
40         0
41 };
42
43 jm_name_ID_map_t fmi2_xml_q_op_map[] = {
44         {"or", fmi2_xml_q_term_enu_OR},
45         {"and", fmi2_xml_q_term_enu_AND},
46         {"not", fmi2_xml_q_term_enu_NOT},
47         {"&&", fmi2_xml_q_term_enu_AND},
48         {"||", fmi2_xml_q_term_enu_OR},
49         {"!", fmi2_xml_q_term_enu_NOT},
50         {0,-1}
51 };
52
53 static void fmi2_xml_q_skip_space(jm_string* cur) {
54     jm_string curChP = *cur;
55     char curCh;
56     if(!curChP) return;
57     curCh = *curChP;
58     while(curCh || (curCh == ' ') || (curCh == '\t')) {
59         curChP++; curCh = *curChP;
60     }
61     *cur = curChP;
62 }
63
64 int fmi2_xml_q_scan_string(fmi2_xml_q_context_t* context, char** param_str) {
65         fmi2_xml_q_expression_t* expr = &context->expr;
66         char* dest; 
67         jm_string cur = context->query + context->curCh;
68     char ch = *cur;
69     char strterm ;
70     size_t strlen = 0; 
71
72         if((ch == '\'') || (ch == '"')) /* either ' or " can be used as string terminator */
73                 strterm = ch;
74     else 
75                 return -1;
76     do {
77         ch = cur[strlen+1];
78         dest = jm_vector_push_back(char)(&expr->strbuf, ch);
79                 assert(dest);
80         strlen++;
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;
86     return strlen;
87 }
88
89
90 int fmi2_xml_q_scan_elementary_name(fmi2_xml_q_context_t* context, fmi2_xml_q_terminal_t* term) {
91         jm_string startCh = &context->query[context->curCh];
92         jm_string curCh = startCh;
93         size_t len;
94
95         /* expecting: [<spaces>]'='[<spaces>]<string> */
96
97         fmi2_xml_q_skip_space(&curCh);
98         if(*curCh != '=') 
99                 return (int)(startCh - curCh);
100         curCh++;
101         fmi2_xml_q_skip_space(&curCh);
102
103         len = (int)(curCh - startCh);
104
105         context->curCh += len;
106         startCh += len;
107
108         if( fmi2_xml_q_scan_string(context, &term->param_str)  < 0) 
109                 return (int)(startCh - curCh);
110         
111         /*      treat as regexp - > skip for now
112         if(term->param_str[0] == '^') {         
113
114         }
115         else  */
116         {               
117                 term->param_i = strlen(term->param_str);
118                 if( (strchr ( term->param_str, '*') != 0) || (strchr ( term->param_str, '?') != 0) ){
119                         /* treat as wildcard */
120                         term->param_i *= -1;
121                 }
122         }
123         return (int)(curCh - startCh);
124 }
125
126 int fmi2_xml_q_eval_elementary_name(fmi2_xml_variable_t* var, fmi2_xml_q_terminal_t* term) {
127         assert(term->specific == fmi2_xml_q_elmentary_enu_name);
128
129         if(term->param_i < 0) {
130                 return jm_wc_match(term->param_str, fmi2_xml_get_variable_name(var));
131         }
132         else
133                 return (strncmp(term->param_str, fmi2_xml_get_variable_name(var), term->param_i) == 0);
134         return 0;
135 }
136
137 int fmi2_xml_q_scan_elementary_unit(fmi2_xml_q_context_t* context, fmi2_xml_q_terminal_t* term) {
138         jm_string startCh = &context->query[context->curCh];
139         jm_string curCh = startCh;
140         size_t len;
141
142         /* expecting: [<spaces>]'='[<spaces>]<string> */
143
144         fmi2_xml_q_skip_space(&curCh);
145         if(*curCh != '=') 
146                 return (int)(startCh - curCh);
147         curCh++;
148         fmi2_xml_q_skip_space(&curCh);
149
150         len = (int)(curCh - startCh);
151
152         context->curCh += len;
153         startCh += len;
154
155         if( fmi2_xml_q_scan_string(context, &term->param_str)  < 0) 
156                 return (int)(startCh - curCh);
157         return (int)(curCh - startCh);
158 }
159
160 int fmi2_xml_q_eval_elementary_unit(fmi2_xml_variable_t* var, fmi2_xml_q_terminal_t* term) {
161         return 0;
162 }
163
164 int fmi2_xml_q_get_number(fmi2_xml_q_context_t* context, char* cur, double* val, char* buf) {
165         int len;
166         if(sscanf(cur, "%lg%s", val, buf) != 2) return 0;
167         len = strlen(cur) - strlen(buf);
168         return len;
169 }
170
171 int fmi2_xml_q_get_keyword(fmi2_xml_q_context_t* context, char* cur, size_t* len, char* buf) {
172     char ch = *cur;
173     size_t i = 0, id;
174     *len = 0;
175         if( (ch == '|') || (ch == '&') ) {              
176                 if(ch ==  cur[i+1]) {
177                         buf[i++] = ch;
178                         buf[i++] = ch;
179                 }
180         }
181         else {
182                 while(isalpha(ch)) {
183                         buf[i++] = tolower(ch);
184                         ch = cur[i];
185                 }
186         }
187         
188     if(!i) return -1;
189
190         {
191                 jm_name_ID_map_t key;
192                 jm_name_ID_map_t* map;
193                 key.name = buf;
194                 map = jm_vector_bsearch(jm_name_ID_map_t)(&context->elementary_map, &key,jm_compare_name);
195                 if(!map) return -1;
196                 id = map->ID;
197         }
198     *len = i;
199     return id;
200 }
201
202
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];
211         switch(cur) {
212         case '*':
213             jm_vector_push_back_char(re, '.');
214             jm_vector_push_back_char(re,'*');
215             break;
216         case '?':
217             jm_vector_push_back_char(re,'.');
218             break;
219         default:
220             jm_vector_push_back_char(re,'\\');
221             jm_vector_push_back_char(re,cur);
222         }
223     }
224     jm_vector_push_back_char(re, '$');
225     jm_vector_push_back_char(re, 0);
226     return 0;
227 }
228
229 int fmi2_xml_evaluate_terminal(fmi2_xml_variable_t* var, fmi2_xml_q_terminal_t* term) {
230         return fmi2_xml_q_eval_elementary_handles[term->specific](var, term);
231 }
232
233 int fmi2_xml_q_filter_variable(fmi2_xml_variable_t* var, fmi2_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         fmi2_xml_q_terminal_t * term;
237     for(cur = 0; cur < len; cur++) {
238         fmi2_xml_q_terminal_t *argL, *argR;
239         size_t curlen = jm_vector_get_size(jm_voidp)(stack);
240
241                 term = (fmi2_xml_q_terminal_t *)jm_vector_get_item(jm_voidp)(&expr->expression, cur);
242         argL = (curlen > 0) ? (fmi2_xml_q_terminal_t *)jm_vector_get_item(jm_voidp)(stack,curlen -1):0;
243         argR = (curlen > 1) ? (fmi2_xml_q_terminal_t *)jm_vector_get_item(jm_voidp)(stack,curlen -2):0;
244
245         switch(term -> kind) {
246         case fmi2_xml_q_term_enu_AND:
247             assert(argL && argR);
248             jm_vector_resize(jm_voidp)(stack, curlen -2);
249             if((argL->kind == fmi2_xml_q_term_enu_FALSE) || (argR->kind == fmi2_xml_q_term_enu_FALSE))
250                 jm_vector_push_back(jm_voidp)(stack, &expr->termFalse);
251             else {
252                 jm_vector_push_back(jm_voidp)(stack, (fmi2_xml_evaluate_terminal(var, argL) && fmi2_xml_evaluate_terminal(var, argR))?   &expr->termTrue: &expr->termFalse );
253             }
254             break;
255         case fmi2_xml_q_term_enu_OR:
256             assert(argL && argR);
257             jm_vector_resize(jm_voidp)(stack, curlen -2);
258             if((argL->kind == fmi2_xml_q_term_enu_TRUE) || (argR->kind == fmi2_xml_q_term_enu_TRUE))
259                 jm_vector_push_back(jm_voidp)(stack, &expr->termTrue);
260             else {
261                 jm_vector_push_back(jm_voidp)(stack, (fmi2_xml_evaluate_terminal(var, argL) || fmi2_xml_evaluate_terminal(var, argR))?   &expr->termTrue: &expr->termFalse);
262             }
263             break;
264         case fmi2_xml_q_term_enu_NOT:
265                         assert(argL);
266             jm_vector_resize(jm_voidp)(stack, curlen -1);
267             if(argL->kind == fmi2_xml_q_term_enu_TRUE)
268                 jm_vector_push_back(jm_voidp)(stack, &expr->termFalse);
269                         else if(argL->kind == fmi2_xml_q_term_enu_TRUE)
270                 jm_vector_push_back(jm_voidp)(stack, &expr->termTrue);
271             else {
272                 jm_vector_push_back(jm_voidp)(stack, (fmi2_xml_evaluate_terminal(var, argL)?   &expr->termFalse: &expr->termTrue));
273             }
274                         break;
275                 case fmi2_xml_q_term_enu_LP:
276                 case fmi2_xml_q_term_enu_RP:
277                         assert(0);
278                         break;
279         default:
280             jm_vector_push_back(jm_voidp)(stack, term); /* only evaluate when needed. push as is at first */
281         }
282     }
283     assert(jm_vector_get_size(jm_voidp)(stack) == 1);
284         
285     term = (fmi2_xml_q_terminal_t *)jm_vector_get_item(jm_voidp)(stack,0);
286     if(term->kind == fmi2_xml_q_term_enu_FALSE) return 0;
287         assert(term->kind == fmi2_xml_q_term_enu_TRUE);
288     return 1;
289 }
290
291 fmi2_xml_q_expression_t* fmi2_xml_alloc_expression(jm_string query) {
292         return 0;
293 }
294
295 int fmi2_xml_q_parse_elementary(fmi2_xml_q_context_t* context, fmi2_xml_q_terminal_t* term) {
296         return 0;
297 }
298
299 void fmi2_xml_q_init_context(fmi2_xml_q_context_t* c, jm_callbacks* cb) {
300         size_t l = jm_vector_init(jm_name_ID_map_t)(&c->elementary_map, fmi2_xml_elementary_enu_num, cb);
301         assert(l);
302         for(l = 0; l < fmi2_xml_elementary_enu_num; l++) {
303                 jm_vector_set_item(jm_name_ID_map_t)(&c->elementary_map, l,fmi2_xml_q_elementary_map[l]);
304         }
305         jm_vector_qsort(jm_name_ID_map_t)(&c->elementary_map,jm_compare_name);
306         c->query = 0;
307         c->qlen = 0;
308         c->curCh = 0;
309         jm_vector_init(char)(&c->buf,0,cb);
310
311         {
312                 fmi2_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 =  fmi2_xml_q_term_enu_FALSE;
316                 expr->termTrue.kind =   fmi2_xml_q_term_enu_TRUE;
317                 jm_vector_init(fmi2_xml_q_terminal_t)(&expr->terms,0,cb);
318                 jm_vector_init(char)(&expr->strbuf, 0, cb);
319         }
320 }
321
322 void fmi2_xml_q_free_context_data(fmi2_xml_q_context_t* c){
323         fmi2_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(fmi2_xml_q_terminal_t)(&expr->terms);
329         jm_vector_free_data(char)(&expr->strbuf);
330 }
331
332 int fmi2_xml_q_parse_terminal(fmi2_xml_q_context_t* context, fmi2_xml_q_terminal_t** ppterm) {
333         int offset = 0;
334     fmi2_xml_q_terminal_t* pterm;
335         jm_string startCh = context->query + context->curCh;
336         jm_string cur = startCh;
337         pterm = jm_vector_resize1(fmi2_xml_q_terminal_t)(&context->expr.terms);
338     if(!pterm) return -1;
339         *ppterm = pterm;
340     fmi2_xml_q_skip_space(&cur);
341     switch(*cur) {
342     case '(':
343                 pterm->kind = fmi2_xml_q_term_enu_LP;
344                 break;
345     case ')':
346                 pterm->kind = fmi2_xml_q_term_enu_RP;
347                 break;
348     case '&':
349                 pterm->kind = fmi2_xml_q_term_enu_AND;
350                 break;
351     case '|':
352                 pterm->kind = fmi2_xml_q_term_enu_OR;
353                 break;
354     case '!':
355                 pterm->kind = fmi2_xml_q_term_enu_NOT;
356                 break;
357     case 0:
358                 pterm->kind = fmi2_xml_q_term_enu_END;
359                 break;
360     default:
361         fmi2_xml_q_parse_elementary(context, pterm);
362     }
363         fmi2_xml_q_skip_space(&cur);
364         return (int)(cur - startCh);
365 }
366
367 int fmi2_xml_q_parse_query(fmi2_xml_q_context_t* context, jm_string query) {
368         fmi2_xml_q_expression_t* expr = &context->expr;
369     int qlen = strlen(query);
370         int offset = 0, curCh = 0;
371         int expectOperand = 1;
372         size_t stacklen = 0;
373     fmi2_xml_q_terminal_t* stackTop = 0;
374
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;
379
380     while(curCh < qlen) {
381         fmi2_xml_q_terminal_t* term;
382         size_t explen = jm_vector_get_size(jm_voidp)(&expr->expression);
383         fmi2_xml_q_terminal_t* expTop =  explen? (fmi2_xml_q_terminal_t*)jm_vector_get_item(jm_voidp)(&expr->expression,explen -1):0;
384
385                 offset = fmi2_xml_q_parse_terminal(context,  &term);
386
387         if(offset < 0) return -curCh;           
388
389                 stacklen = jm_vector_get_size(jm_voidp)(&expr->stack);
390         stackTop =  stacklen ? (fmi2_xml_q_terminal_t*)jm_vector_get_item(jm_voidp)(&expr->stack,stacklen -1):(fmi2_xml_q_terminal_t*)0;
391
392         switch(term -> kind) {
393         case fmi2_xml_q_term_enu_LP:
394                         if(!expectOperand) return -curCh;
395             jm_vector_push_back(jm_voidp)(&expr->stack, term);
396             break;
397         case fmi2_xml_q_term_enu_RP:
398                         if(expectOperand) return -curCh;
399             while(stackTop && (stackTop->kind != fmi2_xml_q_term_enu_LP)) {
400                 jm_vector_push_back(jm_voidp)(&expr->expression, stackTop);
401                 stacklen--;
402                 jm_vector_resize(jm_voidp)(&expr->stack, stacklen);
403                 stackTop =  stacklen? (fmi2_xml_q_terminal_t*)jm_vector_get_item(jm_voidp)(&expr->stack,stacklen -1):0;
404             }
405             if(!stackTop) return -curCh;
406             jm_vector_resize(jm_voidp)(&expr->stack, stacklen -1);
407             break;
408
409         case fmi2_xml_q_term_enu_AND:
410                         if(expectOperand) return -curCh;
411                         expectOperand = 1;
412             if(!expTop) return -curCh;
413             if(stackTop && (stackTop->kind == fmi2_xml_q_term_enu_AND))
414                     jm_vector_push_back(jm_voidp)(&expr->expression, term);
415                 else
416                     jm_vector_push_back(jm_voidp)(&expr->stack, term);
417             break;
418         case fmi2_xml_q_term_enu_OR:
419                         if(expectOperand) return -curCh;
420                         expectOperand = 1;
421             if(!expTop) return -curCh;
422             while(stackTop && ((stackTop->kind == fmi2_xml_q_term_enu_AND)||(stackTop->kind == fmi2_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);
425                 stacklen--;
426                 stackTop =  stacklen? jm_vector_get_item(jm_voidp)(&expr->stack,stacklen -1):0;
427             }
428             jm_vector_push_back(jm_voidp)(&expr->stack, term);
429             break;
430         default:
431                         if(!expectOperand) return -curCh;
432                         expectOperand = 0;
433             jm_vector_push_back(jm_voidp)(&expr->expression, term);
434
435         }
436                 curCh += offset;
437                 context->curCh = curCh;
438     }
439         if(expectOperand) return -curCh;
440     while(stackTop && (stackTop->kind != fmi2_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);
443         stacklen--;
444         stackTop =  stacklen? (fmi2_xml_q_terminal_t*)jm_vector_get_item(jm_voidp)(&expr->stack,stacklen -1):0;
445     }
446     if(!stackTop) return -curCh;
447     jm_vector_resize(jm_voidp)(&expr->stack, stacklen -1);
448
449     return 0;
450 }
451
452 #define JM_TEMPLATE_INSTANCE_TYPE fmi2_xml_q_terminal_t
453 #include "jm_vector_template.h"