xref: /reactos/dll/win32/wbemprox/wql.y (revision 1734f297)
1 %{
2 
3 /*
4  * Copyright 2012 Hans Leidekker for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #include <stdarg.h>
22 
23 #include "windef.h"
24 #include "winbase.h"
25 #include "wbemcli.h"
26 #include "wbemprox_private.h"
27 
28 #include "wine/list.h"
29 #include "wine/debug.h"
30 
31 WINE_DEFAULT_DEBUG_CHANNEL(wbemprox);
32 
33 struct parser
34 {
35     const WCHAR *cmd;
36     UINT idx;
37     UINT len;
38     HRESULT error;
39     struct view **view;
40     struct list *mem;
41 };
42 
43 struct string
44 {
45     const WCHAR *data;
46     int len;
47 };
48 
49 static void *alloc_mem( struct parser *parser, UINT size )
50 {
51     struct list *mem = heap_alloc( sizeof(struct list) + size );
52     list_add_tail( parser->mem, mem );
53     return &mem[1];
54 }
55 
56 static struct property *alloc_property( struct parser *parser, const WCHAR *class, const WCHAR *name )
57 {
58     struct property *prop = alloc_mem( parser, sizeof(*prop) );
59     if (prop)
60     {
61         prop->name  = name;
62         prop->class = class;
63         prop->next  = NULL;
64     }
65     return prop;
66 }
67 
68 static struct keyword *alloc_keyword( struct parser *parser, const WCHAR *name, const WCHAR *value )
69 {
70     struct keyword *keyword = alloc_mem( parser, sizeof(*keyword) );
71     if (keyword)
72     {
73         keyword->name  = name;
74         keyword->value = value;
75         keyword->next  = NULL;
76     }
77     return keyword;
78 }
79 
80 static WCHAR *get_string( struct parser *parser, const struct string *str )
81 {
82     const WCHAR *p = str->data;
83     int len = str->len;
84     WCHAR *ret;
85 
86     if ((p[0] == '\"' && p[len - 1] != '\"') ||
87         (p[0] == '\'' && p[len - 1] != '\'')) return NULL;
88     if ((p[0] == '\"' && p[len - 1] == '\"') ||
89         (p[0] == '\'' && p[len - 1] == '\''))
90     {
91         p++;
92         len -= 2;
93     }
94     if (!(ret = alloc_mem( parser, (len + 1) * sizeof(WCHAR) ))) return NULL;
95     memcpy( ret, p, len * sizeof(WCHAR) );
96     ret[len] = 0;
97     return ret;
98 }
99 
100 static WCHAR *get_path( struct parser *parser, const struct string *str )
101 {
102     const WCHAR *p = str->data;
103     int len = str->len;
104     WCHAR *ret;
105 
106     if (p[0] == '{' && p[len - 1] == '}')
107     {
108         p++;
109         len -= 2;
110     }
111 
112     if (!(ret = alloc_mem( parser, (len + 1) * sizeof(WCHAR) ))) return NULL;
113     memcpy( ret, p, len * sizeof(WCHAR) );
114     ret[len] = 0;
115     return ret;
116 }
117 
118 static int get_int( struct parser *parser )
119 {
120     const WCHAR *p = &parser->cmd[parser->idx];
121     int i, ret = 0;
122 
123     for (i = 0; i < parser->len; i++)
124     {
125         if (p[i] < '0' || p[i] > '9')
126         {
127             ERR("should only be numbers here!\n");
128             break;
129         }
130         ret = (p[i] - '0') + ret * 10;
131     }
132     return ret;
133 }
134 
135 static struct expr *expr_complex( struct parser *parser, struct expr *l, UINT op, struct expr *r )
136 {
137     struct expr *e = alloc_mem( parser, sizeof(*e) );
138     if (e)
139     {
140         e->type = EXPR_COMPLEX;
141         e->u.expr.left = l;
142         e->u.expr.op = op;
143         e->u.expr.right = r;
144     }
145     return e;
146 }
147 
148 static struct expr *expr_unary( struct parser *parser, struct expr *l, UINT op )
149 {
150     struct expr *e = alloc_mem( parser, sizeof(*e) );
151     if (e)
152     {
153         e->type = EXPR_UNARY;
154         e->u.expr.left = l;
155         e->u.expr.op = op;
156         e->u.expr.right = NULL;
157     }
158     return e;
159 }
160 
161 static struct expr *expr_ival( struct parser *parser, int val )
162 {
163     struct expr *e = alloc_mem( parser, sizeof *e );
164     if (e)
165     {
166         e->type = EXPR_IVAL;
167         e->u.ival = val;
168     }
169     return e;
170 }
171 
172 static struct expr *expr_sval( struct parser *parser, const struct string *str )
173 {
174     struct expr *e = alloc_mem( parser, sizeof *e );
175     if (e)
176     {
177         e->type = EXPR_SVAL;
178         e->u.sval = get_string( parser, str );
179         if (!e->u.sval)
180             return NULL; /* e will be freed by query destructor */
181     }
182     return e;
183 }
184 
185 static struct expr *expr_bval( struct parser *parser, int val )
186 {
187     struct expr *e = alloc_mem( parser, sizeof *e );
188     if (e)
189     {
190         e->type = EXPR_BVAL;
191         e->u.ival = val;
192     }
193     return e;
194 }
195 
196 static struct expr *expr_propval( struct parser *parser, const struct property *prop )
197 {
198     struct expr *e = alloc_mem( parser, sizeof *e );
199     if (e)
200     {
201         e->type = EXPR_PROPVAL;
202         e->u.propval = prop;
203     }
204     return e;
205 }
206 
207 static int wql_error( struct parser *parser, const char *str );
208 static int wql_lex( void *val, struct parser *parser );
209 
210 #define PARSER_BUBBLE_UP_VIEW( parser, result, current_view ) \
211     *parser->view = current_view; \
212     result = current_view
213 
214 %}
215 
216 %lex-param { struct parser *ctx }
217 %parse-param { struct parser *ctx }
218 %define parse.error verbose
219 %pure-parser
220 
221 %union
222 {
223     struct string str;
224     WCHAR *string;
225     struct property *proplist;
226     struct keyword *keywordlist;
227     struct view *view;
228     struct expr *expr;
229     int integer;
230 }
231 
232 %token TK_SELECT TK_FROM TK_STAR TK_COMMA TK_DOT TK_IS TK_LP TK_RP TK_NULL TK_FALSE TK_TRUE
233 %token TK_INTEGER TK_WHERE TK_SPACE TK_MINUS TK_ILLEGAL TK_BY TK_ASSOCIATORS TK_OF
234 %token <str> TK_STRING TK_ID TK_PATH
235 
236 %type <string> id path
237 %type <proplist> prop proplist
238 %type <keywordlist> keyword keywordlist
239 %type <view> query select associatorsof
240 %type <expr> expr prop_val const_val string_val
241 %type <integer> number
242 
243 %left TK_OR TK_AND TK_NOT TK_EQ TK_NE TK_LT TK_GT TK_LE TK_GE TK_LIKE
244 
245 %%
246 
247 query:
248     select
249   |
250     associatorsof
251     ;
252 
253 path:
254     TK_PATH
255         {
256             $$ = get_path( ctx, &$1 );
257             if (!$$)
258                 YYABORT;
259         }
260     ;
261 
262 keyword:
263     id
264         {
265             $$ = alloc_keyword( ctx, $1, NULL );
266             if (!$$)
267                 YYABORT;
268         }
269  | id TK_EQ id
270         {
271             $$ = alloc_keyword( ctx, $1, $3 );
272             if (!$$)
273                 YYABORT;
274         }
275     ;
276 
277 keywordlist:
278     keyword
279   | keyword keywordlist
280         {
281             $1->next = $2;
282         }
283     ;
284 
285 associatorsof:
286     TK_ASSOCIATORS TK_OF path
287         {
288             HRESULT hr;
289             struct parser *parser = ctx;
290             struct view *view;
291 
292             hr = create_view( VIEW_TYPE_ASSOCIATORS, $3, NULL, NULL, NULL, NULL, &view );
293             if (hr != S_OK)
294                 YYABORT;
295 
296             PARSER_BUBBLE_UP_VIEW( parser, $$, view );
297         }
298   | TK_ASSOCIATORS TK_OF path TK_WHERE keywordlist
299         {
300             HRESULT hr;
301             struct parser *parser = ctx;
302             struct view *view;
303 
304             hr = create_view( VIEW_TYPE_ASSOCIATORS, $3, $5, NULL, NULL, NULL, &view );
305             if (hr != S_OK)
306                 YYABORT;
307 
308             PARSER_BUBBLE_UP_VIEW( parser, $$, view );
309         }
310     ;
311 
312 select:
313     TK_SELECT TK_FROM id
314         {
315             HRESULT hr;
316             struct parser *parser = ctx;
317             struct view *view;
318 
319             hr = create_view( VIEW_TYPE_SELECT, NULL, NULL, $3, NULL, NULL, &view );
320             if (hr != S_OK)
321                 YYABORT;
322 
323             PARSER_BUBBLE_UP_VIEW( parser, $$, view );
324         }
325   | TK_SELECT proplist TK_FROM id
326         {
327             HRESULT hr;
328             struct parser *parser = ctx;
329             struct view *view;
330 
331             hr = create_view( VIEW_TYPE_SELECT, NULL, NULL, $4, $2, NULL, &view );
332             if (hr != S_OK)
333                 YYABORT;
334 
335             PARSER_BUBBLE_UP_VIEW( parser, $$, view );
336         }
337   | TK_SELECT proplist TK_FROM id TK_WHERE expr
338         {
339             HRESULT hr;
340             struct parser *parser = ctx;
341             struct view *view;
342 
343             hr = create_view( VIEW_TYPE_SELECT, NULL, NULL, $4, $2, $6, &view );
344             if (hr != S_OK)
345                 YYABORT;
346 
347             PARSER_BUBBLE_UP_VIEW( parser, $$, view );
348         }
349     ;
350 
351 proplist:
352     prop
353   | prop TK_COMMA proplist
354         {
355             $1->next = $3;
356         }
357   | TK_STAR
358         {
359             $$ = NULL;
360         }
361     ;
362 
363 prop:
364     id TK_DOT id
365         {
366             $$ = alloc_property( ctx, $1, $3 );
367             if (!$$)
368                 YYABORT;
369         }
370   | id
371         {
372             $$ = alloc_property( ctx, NULL, $1 );
373             if (!$$)
374                 YYABORT;
375         }
376     ;
377 
378 id:
379     TK_ID
380         {
381             $$ = get_string( ctx, &$1 );
382             if (!$$)
383                 YYABORT;
384         }
385     ;
386 
387 number:
388     TK_INTEGER
389         {
390             $$ = get_int( ctx );
391         }
392     ;
393 
394 expr:
395     TK_LP expr TK_RP
396         {
397             $$ = $2;
398             if (!$$)
399                 YYABORT;
400         }
401   | expr TK_AND expr
402         {
403             $$ = expr_complex( ctx, $1, OP_AND, $3 );
404             if (!$$)
405                 YYABORT;
406         }
407   | expr TK_OR expr
408         {
409             $$ = expr_complex( ctx, $1, OP_OR, $3 );
410             if (!$$)
411                 YYABORT;
412         }
413   | TK_NOT expr
414         {
415             $$ = expr_unary( ctx, $2, OP_NOT );
416             if (!$$)
417                 YYABORT;
418         }
419   | prop_val TK_EQ const_val
420         {
421             $$ = expr_complex( ctx, $1, OP_EQ, $3 );
422             if (!$$)
423                 YYABORT;
424         }
425   | prop_val TK_GT const_val
426         {
427             $$ = expr_complex( ctx, $1, OP_GT, $3 );
428             if (!$$)
429                 YYABORT;
430         }
431   | prop_val TK_LT const_val
432         {
433             $$ = expr_complex( ctx, $1, OP_LT, $3 );
434             if (!$$)
435                 YYABORT;
436         }
437   | prop_val TK_LE const_val
438         {
439             $$ = expr_complex( ctx, $1, OP_LE, $3 );
440             if (!$$)
441                 YYABORT;
442         }
443   | prop_val TK_GE const_val
444         {
445             $$ = expr_complex( ctx, $1, OP_GE, $3 );
446             if (!$$)
447                 YYABORT;
448         }
449   | prop_val TK_NE const_val
450         {
451             $$ = expr_complex( ctx, $1, OP_NE, $3 );
452             if (!$$)
453                 YYABORT;
454         }
455   | const_val TK_EQ prop_val
456         {
457             $$ = expr_complex( ctx, $1, OP_EQ, $3 );
458             if (!$$)
459                 YYABORT;
460         }
461   | const_val TK_GT prop_val
462         {
463             $$ = expr_complex( ctx, $1, OP_GT, $3 );
464             if (!$$)
465                 YYABORT;
466         }
467   | const_val TK_LT prop_val
468         {
469             $$ = expr_complex( ctx, $1, OP_LT, $3 );
470             if (!$$)
471                 YYABORT;
472         }
473   | const_val TK_LE prop_val
474         {
475             $$ = expr_complex( ctx, $1, OP_LE, $3 );
476             if (!$$)
477                 YYABORT;
478         }
479   | const_val TK_GE prop_val
480         {
481             $$ = expr_complex( ctx, $1, OP_GE, $3 );
482             if (!$$)
483                 YYABORT;
484         }
485   | const_val TK_NE prop_val
486         {
487             $$ = expr_complex( ctx, $1, OP_NE, $3 );
488             if (!$$)
489                 YYABORT;
490         }
491   | prop_val TK_LIKE string_val
492         {
493             $$ = expr_complex( ctx, $1, OP_LIKE, $3 );
494             if (!$$)
495                 YYABORT;
496         }
497   | prop_val TK_IS TK_NULL
498         {
499             $$ = expr_unary( ctx, $1, OP_ISNULL );
500             if (!$$)
501                 YYABORT;
502         }
503   | prop_val TK_IS TK_NOT TK_NULL
504         {
505             $$ = expr_unary( ctx, $1, OP_NOTNULL );
506             if (!$$)
507                 YYABORT;
508         }
509   | prop_val TK_EQ TK_NULL
510         {
511             $$ = expr_unary( ctx, $1, OP_ISNULL );
512             if (!$$)
513                 YYABORT;
514         }
515   | TK_NULL TK_EQ prop_val
516         {
517             $$ = expr_unary( ctx, $3, OP_ISNULL );
518             if (!$$)
519                 YYABORT;
520         }
521   | prop_val TK_NE TK_NULL
522         {
523             $$ = expr_unary( ctx, $1, OP_NOTNULL );
524             if (!$$)
525                 YYABORT;
526         }
527   | TK_NULL TK_NE prop_val
528         {
529             $$ = expr_unary( ctx, $3, OP_NOTNULL );
530             if (!$$)
531                 YYABORT;
532         }
533     ;
534 
535 string_val:
536     TK_STRING
537         {
538             $$ = expr_sval( ctx, &$1 );
539             if (!$$)
540                 YYABORT;
541         }
542     ;
543 
544 prop_val:
545     prop
546         {
547             $$ = expr_propval( ctx, $1 );
548             if (!$$)
549                 YYABORT;
550         }
551     ;
552 
553 const_val:
554     number
555         {
556             $$ = expr_ival( ctx, $1 );
557             if (!$$)
558                 YYABORT;
559         }
560   | TK_STRING
561         {
562             $$ = expr_sval( ctx, &$1 );
563             if (!$$)
564                 YYABORT;
565         }
566   | TK_TRUE
567         {
568             $$ = expr_bval( ctx, -1 );
569             if (!$$)
570                 YYABORT;
571         }
572   | TK_FALSE
573         {
574             $$ = expr_bval( ctx, 0 );
575             if (!$$)
576                 YYABORT;
577         }
578     ;
579 
580 %%
581 
582 HRESULT parse_query( const WCHAR *str, struct view **view, struct list *mem )
583 {
584     struct parser parser;
585     int ret;
586 
587     *view = NULL;
588 
589     parser.cmd   = str;
590     parser.idx   = 0;
591     parser.len   = 0;
592     parser.error = WBEM_E_INVALID_QUERY;
593     parser.view  = view;
594     parser.mem   = mem;
595 
596     ret = wql_parse( &parser );
597     TRACE("wql_parse returned %d\n", ret);
598     if (ret)
599     {
600         if (*parser.view)
601         {
602             destroy_view( *parser.view );
603             *parser.view = NULL;
604         }
605         return parser.error;
606     }
607     return S_OK;
608 }
609 
610 static const char id_char[] =
611 {
612     0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
613     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
614     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
615     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
616     0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
617     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
618     0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
619     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
620     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
621     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
622     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
623     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
624     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
625     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
626     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
627     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
628 };
629 
630 struct wql_keyword
631 {
632     const WCHAR *name;
633     unsigned int len;
634     int type;
635 };
636 
637 #define MIN_TOKEN_LEN 2
638 #define MAX_TOKEN_LEN 11
639 
640 static const WCHAR andW[] = {'A','N','D'};
641 static const WCHAR associatorsW[] = {'A','S','S','O','C','I','A','T','O','R','S'};
642 static const WCHAR byW[] = {'B','Y'};
643 static const WCHAR falseW[] = {'F','A','L','S','E'};
644 static const WCHAR fromW[] = {'F','R','O','M'};
645 static const WCHAR isW[] = {'I','S'};
646 static const WCHAR likeW[] = {'L','I','K','E'};
647 static const WCHAR notW[] = {'N','O','T'};
648 static const WCHAR nullW[] = {'N','U','L','L'};
649 static const WCHAR ofW[] = {'O','F'};
650 static const WCHAR orW[] = {'O','R'};
651 static const WCHAR selectW[] = {'S','E','L','E','C','T'};
652 static const WCHAR trueW[] = {'T','R','U','E'};
653 static const WCHAR whereW[] = {'W','H','E','R','E'};
654 
655 static const struct wql_keyword keyword_table[] =
656 {
657     { andW,         ARRAY_SIZE(andW),         TK_AND },
658     { associatorsW, ARRAY_SIZE(associatorsW), TK_ASSOCIATORS },
659     { byW,          ARRAY_SIZE(byW),          TK_BY },
660     { falseW,       ARRAY_SIZE(falseW),       TK_FALSE },
661     { fromW,        ARRAY_SIZE(fromW),        TK_FROM },
662     { isW,          ARRAY_SIZE(isW),          TK_IS },
663     { likeW,        ARRAY_SIZE(likeW),        TK_LIKE },
664     { notW,         ARRAY_SIZE(notW),         TK_NOT },
665     { nullW,        ARRAY_SIZE(nullW),        TK_NULL },
666     { ofW,          ARRAY_SIZE(ofW),          TK_OF },
667     { orW,          ARRAY_SIZE(orW),          TK_OR },
668     { selectW,      ARRAY_SIZE(selectW),      TK_SELECT },
669     { trueW,        ARRAY_SIZE(trueW),        TK_TRUE },
670     { whereW,       ARRAY_SIZE(whereW),       TK_WHERE }
671 };
672 
673 static int __cdecl cmp_keyword( const void *arg1, const void *arg2 )
674 {
675     const struct wql_keyword *key1 = arg1, *key2 = arg2;
676     int len = min( key1->len, key2->len );
677     int ret;
678 
679     if ((ret = _wcsnicmp( key1->name, key2->name, len ))) return ret;
680     if (key1->len < key2->len) return -1;
681     else if (key1->len > key2->len) return 1;
682     return 0;
683 }
684 
685 static int keyword_type( const WCHAR *str, unsigned int len )
686 {
687     struct wql_keyword key, *ret;
688 
689     if (len < MIN_TOKEN_LEN || len > MAX_TOKEN_LEN) return TK_ID;
690 
691     key.name = str;
692     key.len  = len;
693     key.type = 0;
694     ret = bsearch( &key, keyword_table, ARRAY_SIZE(keyword_table), sizeof(struct wql_keyword), cmp_keyword );
695     if (ret) return ret->type;
696     return TK_ID;
697 }
698 
699 static int get_token( const WCHAR *s, int *token )
700 {
701     int i;
702 
703     switch (*s)
704     {
705     case ' ':
706     case '\t':
707     case '\r':
708     case '\n':
709         for (i = 1; iswspace( s[i] ); i++) {}
710         *token = TK_SPACE;
711         return i;
712     case '-':
713         if (!s[1]) return -1;
714         *token = TK_MINUS;
715         return 1;
716     case '(':
717         *token = TK_LP;
718         return 1;
719     case ')':
720         *token = TK_RP;
721         return 1;
722     case '{':
723         for (i = 1; s[i] && s[i] != '}'; i++) {}
724         if (s[i] != '}')
725         {
726             *token = TK_ILLEGAL;
727             return i;
728         }
729         *token = TK_PATH;
730         return i + 1;
731     case '*':
732         *token = TK_STAR;
733         return 1;
734     case '=':
735         *token = TK_EQ;
736         return 1;
737     case '<':
738         if (s[1] == '=' )
739         {
740             *token = TK_LE;
741             return 2;
742         }
743         else if (s[1] == '>')
744         {
745             *token = TK_NE;
746             return 2;
747         }
748         else
749         {
750             *token = TK_LT;
751             return 1;
752         }
753     case '>':
754         if (s[1] == '=')
755         {
756             *token = TK_GE;
757             return 2;
758         }
759         else
760         {
761             *token = TK_GT;
762             return 1;
763         }
764     case '!':
765         if (s[1] != '=')
766         {
767             *token = TK_ILLEGAL;
768             return 2;
769         }
770         else
771         {
772             *token = TK_NE;
773             return 2;
774         }
775     case ',':
776         *token = TK_COMMA;
777         return 1;
778     case '\"':
779     case '\'':
780         for (i = 1; s[i]; i++)
781         {
782             if (s[i] == s[0]) break;
783         }
784         if (s[i]) i++;
785         *token = TK_STRING;
786         return i;
787     case '.':
788         if (!iswdigit( s[1] ))
789         {
790             *token = TK_DOT;
791             return 1;
792         }
793         /* fall through */
794     case '0': case '1': case '2': case '3': case '4':
795     case '5': case '6': case '7': case '8': case '9':
796         *token = TK_INTEGER;
797         for (i = 1; iswdigit( s[i] ); i++) {}
798         return i;
799     default:
800         if (!id_char[*s]) break;
801 
802         for (i = 1; id_char[s[i]]; i++) {}
803         *token = keyword_type( s, i );
804         return i;
805     }
806     *token = TK_ILLEGAL;
807     return 1;
808 }
809 
810 static int wql_lex( void *p, struct parser *parser )
811 {
812     struct string *str = p;
813     int token = -1;
814     do
815     {
816         parser->idx += parser->len;
817         if (!parser->cmd[parser->idx]) return 0;
818         parser->len = get_token( &parser->cmd[parser->idx], &token );
819         if (!parser->len) break;
820 
821         str->data = &parser->cmd[parser->idx];
822         str->len = parser->len;
823     } while (token == TK_SPACE);
824     return token;
825 }
826 
827 static int wql_error( struct parser *parser, const char *str )
828 {
829     ERR("%s\n", str);
830     return 0;
831 }
832