Line data Source code
1 : /*
2 : * SPDX-License-Identifier: MPL-2.0
3 : *
4 : * This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this
6 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 : *
8 : * Copyright 2024 MonetDB Foundation;
9 : * Copyright August 2008 - 2023 MonetDB B.V.;
10 : * Copyright 1997 - July 2008 CWI.
11 : */
12 :
13 : /* (c): M. L. Kersten
14 : */
15 :
16 : #include "monetdb_config.h"
17 : #include "mal_parser.h"
18 : #include "mal_resolve.h"
19 : #include "mal_linker.h"
20 : #include "mal_atom.h" /* for malAtomDefinition(), malAtomProperty() */
21 : #include "mal_interpreter.h" /* for showErrors() */
22 : #include "mal_instruction.h" /* for pushEndInstruction(), findVariableLength() */
23 : #include "mal_namespace.h"
24 : #include "mal_utils.h"
25 : #include "mal_builder.h"
26 : #include "mal_type.h"
27 : #include "mal_session.h"
28 : #include "mal_private.h"
29 :
30 : #define FATALINPUT (MAXERRORS+1)
31 : #define NL(X) ((X)=='\n' || (X)=='\r')
32 :
33 : static str idCopy(Client cntxt, int len);
34 : static str strCopy(Client cntxt, int len);
35 :
36 : /*
37 : * For error reporting we may have to find the start of the previous line,
38 : * which, ofcourse, is easy given the client buffer.
39 : * The remaining functions are self-explanatory.
40 : */
41 : static str
42 142 : lastline(Client cntxt)
43 : {
44 142 : str s = CURRENT(cntxt);
45 142 : if (NL(*s))
46 4 : s++;
47 592 : while (s > cntxt->fdin->buf && !NL(*s))
48 450 : s--;
49 142 : if (NL(*s))
50 128 : s++;
51 142 : return s;
52 : }
53 :
54 : static ssize_t
55 71 : position(Client cntxt)
56 : {
57 71 : str s = lastline(cntxt);
58 71 : return (ssize_t) (CURRENT(cntxt) - s);
59 : }
60 :
61 : /*
62 : * Upon encountering an error we skip to the nearest semicolon,
63 : * or comment terminated by a new line
64 : */
65 : static inline void
66 9516 : skipToEnd(Client cntxt)
67 : {
68 9516 : char c;
69 9886 : while ((c = *CURRENT(cntxt)) != ';' && c && c != '\n')
70 370 : nextChar(cntxt);
71 9516 : if (c && c != '\n')
72 9430 : nextChar(cntxt);
73 9516 : }
74 :
75 : /*
76 : * Keep on syntax error for reflection and correction.
77 : */
78 : static void
79 71 : parseError(Client cntxt, str msg)
80 : {
81 71 : MalBlkPtr mb;
82 71 : char *old, *new;
83 71 : char buf[1028] = { 0 };
84 71 : char *s = buf, *t, *line = "", *marker = "";
85 71 : char *l = lastline(cntxt);
86 71 : ssize_t i;
87 :
88 71 : if (cntxt->backup) {
89 5 : freeSymbol(cntxt->curprg);
90 5 : cntxt->curprg = cntxt->backup;
91 5 : cntxt->backup = 0;
92 : }
93 :
94 71 : mb = cntxt->curprg->def;
95 71 : s = buf;
96 680 : for (t = l; *t && *t != '\n' && s < buf + sizeof(buf) - 4; t++) {
97 609 : *s++ = *t;
98 : }
99 71 : *s++ = '\n';
100 71 : *s = 0;
101 71 : line = createException(SYNTAX, "parseError", "%s", buf);
102 :
103 : /* produce the position marker */
104 71 : s = buf;
105 71 : i = position(cntxt);
106 232 : for (; i > 0 && s < buf + sizeof(buf) - 4; i--) {
107 165 : *s++ = ((l && *(l + 1) && *l++ != '\t')) ? ' ' : '\t';
108 : }
109 71 : *s++ = '^';
110 71 : *s = 0;
111 71 : marker = createException(SYNTAX, "parseError", "%s%s", buf, msg);
112 :
113 71 : old = mb->errors;
114 71 : new = GDKzalloc((old ? strlen(old) : 0) + strlen(line) + strlen(marker) +
115 : 64);
116 71 : if (new == NULL) {
117 0 : freeException(line);
118 0 : freeException(marker);
119 0 : skipToEnd(cntxt);
120 0 : return; // just stick to old error message
121 : }
122 71 : if (old) {
123 24 : strcpy(new, old);
124 24 : GDKfree(old);
125 : }
126 71 : strcat(new, line);
127 71 : strcat(new, marker);
128 :
129 71 : mb->errors = new;
130 71 : freeException(line);
131 71 : freeException(marker);
132 71 : skipToEnd(cntxt);
133 : }
134 :
135 : /* Before a line is parsed we check for a request to echo it.
136 : * This command should be executed at the beginning of a parse
137 : * request and each time we encounter EOL.
138 : */
139 : static void
140 24379 : echoInput(Client cntxt)
141 : {
142 24379 : char *c = CURRENT(cntxt);
143 24379 : if (cntxt->listing == 1 && *c && !NL(*c)) {
144 0 : mnstr_printf(cntxt->fdout, "#");
145 0 : while (*c && !NL(*c)) {
146 0 : mnstr_printf(cntxt->fdout, "%c", *c++);
147 : }
148 0 : mnstr_printf(cntxt->fdout, "\n");
149 : }
150 24379 : }
151 :
152 : static inline void
153 217042 : skipSpace(Client cntxt)
154 : {
155 217042 : char *s = &currChar(cntxt);
156 255200 : for (;;) {
157 236121 : switch (*s++) {
158 19079 : case ' ':
159 : case '\t':
160 : case '\n':
161 : case '\r':
162 19079 : nextChar(cntxt);
163 19079 : break;
164 : default:
165 217042 : return;
166 : }
167 : }
168 : }
169 :
170 : static inline void
171 87433 : advance(Client cntxt, size_t length)
172 : {
173 87433 : cntxt->yycur += length;
174 87433 : skipSpace(cntxt);
175 27807 : }
176 :
177 : /*
178 : * The most recurring situation is to recognize identifiers.
179 : * This process is split into a few steps to simplify subsequent
180 : * construction and comparison.
181 : * IdLength searches the end of an identifier without changing
182 : * the cursor into the input pool.
183 : * IdCopy subsequently prepares a GDK string for inclusion in the
184 : * instruction datastructures.
185 : */
186 :
187 : static const bool opCharacter[256] = {
188 : ['$'] = true,
189 : ['!'] = true,
190 : ['%'] = true,
191 : ['&'] = true,
192 : ['*'] = true,
193 : ['+'] = true,
194 : ['-'] = true,
195 : ['/'] = true,
196 : [':'] = true,
197 : ['<'] = true,
198 : ['='] = true,
199 : ['>'] = true,
200 : ['\\'] = true,
201 : ['^'] = true,
202 : ['|'] = true,
203 : ['~'] = true,
204 : };
205 :
206 : static const bool idCharacter[256] = {
207 : ['a'] = true,
208 : ['b'] = true,
209 : ['c'] = true,
210 : ['d'] = true,
211 : ['e'] = true,
212 : ['f'] = true,
213 : ['g'] = true,
214 : ['h'] = true,
215 : ['i'] = true,
216 : ['j'] = true,
217 : ['k'] = true,
218 : ['l'] = true,
219 : ['m'] = true,
220 : ['n'] = true,
221 : ['o'] = true,
222 : ['p'] = true,
223 : ['q'] = true,
224 : ['r'] = true,
225 : ['s'] = true,
226 : ['t'] = true,
227 : ['u'] = true,
228 : ['v'] = true,
229 : ['w'] = true,
230 : ['x'] = true,
231 : ['y'] = true,
232 : ['z'] = true,
233 : ['A'] = true,
234 : ['B'] = true,
235 : ['C'] = true,
236 : ['D'] = true,
237 : ['E'] = true,
238 : ['F'] = true,
239 : ['G'] = true,
240 : ['H'] = true,
241 : ['I'] = true,
242 : ['J'] = true,
243 : ['K'] = true,
244 : ['L'] = true,
245 : ['M'] = true,
246 : ['N'] = true,
247 : ['O'] = true,
248 : ['P'] = true,
249 : ['Q'] = true,
250 : ['R'] = true,
251 : ['S'] = true,
252 : ['T'] = true,
253 : ['U'] = true,
254 : ['V'] = true,
255 : ['W'] = true,
256 : ['X'] = true,
257 : ['Y'] = true,
258 : ['Z'] = true,
259 : [TMPMARKER] = true,
260 : };
261 :
262 : static const bool idCharacter2[256] = {
263 : ['a'] = true,
264 : ['b'] = true,
265 : ['c'] = true,
266 : ['d'] = true,
267 : ['e'] = true,
268 : ['f'] = true,
269 : ['g'] = true,
270 : ['h'] = true,
271 : ['i'] = true,
272 : ['j'] = true,
273 : ['k'] = true,
274 : ['l'] = true,
275 : ['m'] = true,
276 : ['n'] = true,
277 : ['o'] = true,
278 : ['p'] = true,
279 : ['q'] = true,
280 : ['r'] = true,
281 : ['s'] = true,
282 : ['t'] = true,
283 : ['u'] = true,
284 : ['v'] = true,
285 : ['w'] = true,
286 : ['x'] = true,
287 : ['y'] = true,
288 : ['z'] = true,
289 : ['A'] = true,
290 : ['B'] = true,
291 : ['C'] = true,
292 : ['D'] = true,
293 : ['E'] = true,
294 : ['F'] = true,
295 : ['G'] = true,
296 : ['H'] = true,
297 : ['I'] = true,
298 : ['J'] = true,
299 : ['K'] = true,
300 : ['L'] = true,
301 : ['M'] = true,
302 : ['N'] = true,
303 : ['O'] = true,
304 : ['P'] = true,
305 : ['Q'] = true,
306 : ['R'] = true,
307 : ['S'] = true,
308 : ['T'] = true,
309 : ['U'] = true,
310 : ['V'] = true,
311 : ['W'] = true,
312 : ['X'] = true,
313 : ['Y'] = true,
314 : ['Z'] = true,
315 : ['0'] = true,
316 : ['1'] = true,
317 : ['2'] = true,
318 : ['3'] = true,
319 : ['4'] = true,
320 : ['5'] = true,
321 : ['6'] = true,
322 : ['7'] = true,
323 : ['8'] = true,
324 : ['9'] = true,
325 : [TMPMARKER] = true,
326 : ['@'] = true,
327 : };
328 :
329 : static int
330 53359 : idLength(Client cntxt)
331 : {
332 53359 : str s, t;
333 53359 : int len = 0;
334 :
335 53359 : skipSpace(cntxt);
336 53367 : s = CURRENT(cntxt);
337 53367 : t = s;
338 :
339 53367 : if (!idCharacter[(unsigned char) (*s)])
340 : return 0;
341 : /* avoid a clash with old temporaries */
342 48446 : if (s[0] == TMPMARKER)
343 65 : s[0] = REFMARKER;
344 : /* prepare escape of temporary names */
345 48446 : s++;
346 320050 : while (len < IDLENGTH && idCharacter2[(unsigned char) (*s)]) {
347 271604 : s++;
348 271604 : len++;
349 : }
350 48446 : if (len == IDLENGTH)
351 : // skip remainder
352 0 : while (idCharacter2[(unsigned char) (*s)])
353 0 : s++;
354 48446 : return (int) (s - t);
355 : }
356 :
357 : /* Simple type identifiers can not be marked with a type variable. */
358 : static size_t
359 5137 : typeidLength(Client cntxt)
360 : {
361 5137 : size_t l;
362 5137 : char id[IDLENGTH], *t = id;
363 5137 : str s;
364 5137 : skipSpace(cntxt);
365 5142 : s = CURRENT(cntxt);
366 :
367 5142 : if (!idCharacter[(unsigned char) (*s)])
368 : return 0;
369 5142 : l = 1;
370 5142 : *t++ = *s++;
371 5142 : while (l < IDLENGTH
372 15579 : && (idCharacter[(unsigned char) (*s)]
373 5173 : || isdigit((unsigned char) *s))) {
374 10437 : *t++ = *s++;
375 10437 : l++;
376 : }
377 : /* recognize the special type variables {any, any_<nr>} */
378 5142 : if (strncmp(id, "any", 3) == 0)
379 : return 3;
380 5102 : if (strncmp(id, "any_", 4) == 0)
381 0 : return 4;
382 : return l;
383 : }
384 :
385 : static str
386 1 : idCopy(Client cntxt, int length)
387 : {
388 1 : str s = GDKmalloc(length + 1);
389 1 : if (s == NULL)
390 : return NULL;
391 1 : memcpy(s, CURRENT(cntxt), (size_t) length);
392 1 : s[length] = 0;
393 : /* avoid a clash with old temporaries */
394 1 : advance(cntxt, length);
395 1 : return s;
396 : }
397 :
398 : static int
399 25015 : MALlookahead(Client cntxt, str kw, int length)
400 : {
401 25015 : int i;
402 :
403 : /* avoid double test or use lowercase only. */
404 25015 : if (currChar(cntxt) == *kw &&
405 24282 : strncmp(CURRENT(cntxt), kw, length) == 0 &&
406 1070 : !idCharacter[(unsigned char) (CURRENT(cntxt)[length])] &&
407 1070 : !isdigit((unsigned char) (CURRENT(cntxt)[length]))) {
408 : return 1;
409 : }
410 : /* check for capitalized versions */
411 51213 : for (i = 0; i < length; i++)
412 51213 : if (tolower(CURRENT(cntxt)[i]) != kw[i])
413 : return 0;
414 0 : if (!idCharacter[(unsigned char) (CURRENT(cntxt)[length])] &&
415 0 : !isdigit((unsigned char) (CURRENT(cntxt)[length]))) {
416 0 : return 1;
417 : }
418 : return 0;
419 : }
420 :
421 : static inline int
422 25000 : MALkeyword(Client cntxt, str kw, int length)
423 : {
424 25000 : skipSpace(cntxt);
425 25005 : if (MALlookahead(cntxt, kw, length)) {
426 1070 : advance(cntxt, length);
427 1070 : return 1;
428 : }
429 : return 0;
430 : }
431 :
432 : /*
433 : * Keyphrase testing is limited to a few characters only
434 : * (check manually). To speed this up we use a pipelined and inline macros.
435 : */
436 :
437 : static inline int
438 1533 : keyphrase1(Client cntxt, str kw)
439 : {
440 1533 : skipSpace(cntxt);
441 1533 : if (currChar(cntxt) == *kw) {
442 1417 : advance(cntxt, 1);
443 1417 : return 1;
444 : }
445 : return 0;
446 : }
447 :
448 : static inline int
449 7382 : keyphrase2(Client cntxt, str kw)
450 : {
451 7382 : skipSpace(cntxt);
452 7382 : if (CURRENT(cntxt)[0] == kw[0] && CURRENT(cntxt)[1] == kw[1]) {
453 7059 : advance(cntxt, 2);
454 7059 : return 1;
455 : }
456 : return 0;
457 : }
458 :
459 : /*
460 : * A similar approach is used for string literals.
461 : * Beware, string lengths returned include the
462 : * brackets and escapes. They are eaten away in strCopy.
463 : * We should provide the C-method to split strings and
464 : * concatenate them upon retrieval[todo]
465 : */
466 : static int
467 2782 : stringLength(Client cntxt)
468 : {
469 2782 : int l = 0;
470 2782 : int quote = 0;
471 2782 : str s;
472 2782 : skipSpace(cntxt);
473 2784 : s = CURRENT(cntxt);
474 :
475 2784 : if (*s != '"')
476 : return 0;
477 166041 : for (s++; *s; l++, s++) {
478 166049 : if (quote) {
479 : quote = 0;
480 : } else {
481 143856 : if (*s == '"')
482 : break;
483 141064 : quote = *s == '\\';
484 : }
485 : }
486 2784 : return l + 2;
487 : }
488 :
489 : /*Beware, the idcmp routine uses a short cast to compare multiple bytes
490 : * at once. This may cause problems when the net string length is zero.
491 : */
492 :
493 : str
494 2787 : strCopy(Client cntxt, int length)
495 : {
496 2787 : str s;
497 2787 : int i;
498 :
499 2787 : i = length < 4 ? 4 : length;
500 2787 : s = GDKmalloc(i);
501 2793 : if (s == 0)
502 : return NULL;
503 2793 : memcpy(s, CURRENT(cntxt) + 1, (size_t) (length - 2));
504 2793 : s[length - 2] = 0;
505 2793 : mal_unquote(s);
506 2793 : return s;
507 : }
508 :
509 : /*
510 : * And a similar approach is used for operator names.
511 : * A lookup table is considered, because it generally is
512 : * faster then a non-dense switch.
513 : */
514 : static int
515 2789 : operatorLength(Client cntxt)
516 : {
517 2789 : int l = 0;
518 2789 : str s;
519 :
520 2789 : skipSpace(cntxt);
521 3019 : for (s = CURRENT(cntxt); *s; s++) {
522 3002 : if (opCharacter[(unsigned char) (*s)])
523 227 : l++;
524 : else
525 2775 : return l;
526 : }
527 : return l;
528 : }
529 :
530 : /*
531 : * The lexical analyser for constants is a little more complex.
532 : * Aside from getting its length, we need an indication of its type.
533 : * The constant structure is initialized for later use.
534 : */
535 : static int
536 31308 : cstToken(Client cntxt, ValPtr cst)
537 : {
538 31308 : int i = 0;
539 31308 : str s = CURRENT(cntxt);
540 :
541 31308 : *cst = (ValRecord) {
542 : .vtype = TYPE_int,
543 : .val.lval = 0,
544 : .bat = false,
545 : };
546 31308 : switch (*s) {
547 : case '{':
548 : case '[':
549 : /* JSON Literal */
550 : break;
551 2782 : case '"':
552 2782 : i = stringLength(cntxt);
553 2785 : VALset(cst, TYPE_str, strCopy(cntxt, i));
554 2790 : return i;
555 21 : case '-':
556 21 : i++;
557 21 : s++;
558 : /* fall through */
559 1591 : case '0':
560 1591 : if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
561 : /* deal with hex */
562 0 : i += 2;
563 0 : s += 2;
564 0 : while (isxdigit((unsigned char) *s)) {
565 0 : i++;
566 0 : s++;
567 : }
568 0 : goto handleInts;
569 : }
570 : /* fall through */
571 : case '1':
572 : case '2':
573 : case '3':
574 : case '4':
575 : case '5':
576 : case '6':
577 : case '7':
578 : case '8':
579 : case '9':
580 9898 : while (isdigit((unsigned char) *s)) {
581 6028 : i++;
582 6028 : s++;
583 : }
584 :
585 : /* fall through */
586 : case '.':
587 3870 : if (*s == '.' && isdigit((unsigned char) *(s + 1))) {
588 83 : i++;
589 83 : s++;
590 227 : while (isdigit((unsigned char) *s)) {
591 144 : i++;
592 144 : s++;
593 : }
594 83 : cst->vtype = TYPE_dbl;
595 : }
596 3870 : if (*s == 'e' || *s == 'E') {
597 4 : i++;
598 4 : s++;
599 4 : if (*s == '-' || *s == '+') {
600 2 : i++;
601 2 : s++;
602 : }
603 4 : cst->vtype = TYPE_dbl;
604 8 : while (isdigit((unsigned char) *s)) {
605 4 : i++;
606 4 : s++;
607 : }
608 : }
609 3870 : if (cst->vtype == TYPE_flt) {
610 0 : size_t len = sizeof(flt);
611 0 : float *pval = &cst->val.fval;
612 0 : if (fltFromStr(CURRENT(cntxt), &len, &pval, false) < 0) {
613 0 : parseError(cntxt, GDKerrbuf);
614 0 : return i;
615 : }
616 : }
617 3870 : if (cst->vtype == TYPE_dbl) {
618 83 : size_t len = sizeof(dbl);
619 83 : double *pval = &cst->val.dval;
620 83 : if (dblFromStr(CURRENT(cntxt), &len, &pval, false) < 0) {
621 0 : parseError(cntxt, GDKerrbuf);
622 0 : return i;
623 : }
624 : }
625 3870 : if (*s == '@') {
626 77 : size_t len = sizeof(lng);
627 77 : lng l, *pval = &l;
628 77 : if (lngFromStr(CURRENT(cntxt), &len, &pval, false) < 0) {
629 0 : parseError(cntxt, GDKerrbuf);
630 0 : return i;
631 : }
632 77 : if (is_lng_nil(l) || l < 0
633 : #if SIZEOF_OID < SIZEOF_LNG
634 : || l > GDK_oid_max
635 : #endif
636 : )
637 0 : cst->val.oval = oid_nil;
638 : else
639 77 : cst->val.oval = (oid) l;
640 77 : cst->vtype = TYPE_oid;
641 77 : i++;
642 77 : s++;
643 154 : while (isdigit((unsigned char) *s)) {
644 77 : i++;
645 77 : s++;
646 : }
647 77 : return i;
648 : }
649 3793 : if (*s == 'L') {
650 4 : if (cst->vtype == TYPE_int)
651 4 : cst->vtype = TYPE_lng;
652 4 : if (cst->vtype == TYPE_flt)
653 0 : cst->vtype = TYPE_dbl;
654 4 : i++;
655 4 : s++;
656 4 : if (*s == 'L') {
657 0 : i++;
658 0 : s++;
659 : }
660 4 : if (cst->vtype == TYPE_dbl) {
661 0 : size_t len = sizeof(dbl);
662 0 : dbl *pval = &cst->val.dval;
663 0 : if (dblFromStr(CURRENT(cntxt), &len, &pval, false) < 0) {
664 0 : parseError(cntxt, GDKerrbuf);
665 0 : return i;
666 : }
667 : } else {
668 4 : size_t len = sizeof(lng);
669 4 : lng *pval = &cst->val.lval;
670 4 : if (lngFromStr(CURRENT(cntxt), &len, &pval, false) < 0) {
671 0 : parseError(cntxt, GDKerrbuf);
672 0 : return i;
673 : }
674 : }
675 4 : return i;
676 : }
677 : #ifdef HAVE_HGE
678 3789 : if (*s == 'H' && cst->vtype == TYPE_int) {
679 2 : size_t len = sizeof(hge);
680 2 : hge *pval = &cst->val.hval;
681 2 : cst->vtype = TYPE_hge;
682 2 : i++;
683 2 : s++;
684 2 : if (*s == 'H') {
685 0 : i++;
686 0 : s++;
687 : }
688 2 : if (hgeFromStr(CURRENT(cntxt), &len, &pval, false) < 0) {
689 0 : parseError(cntxt, GDKerrbuf);
690 0 : return i;
691 : }
692 : return i;
693 : }
694 : #endif
695 3787 : handleInts:
696 3787 : assert(cst->vtype != TYPE_lng);
697 : #ifdef HAVE_HGE
698 3787 : assert(cst->vtype != TYPE_hge);
699 : #endif
700 3787 : if (cst->vtype == TYPE_int) {
701 : #ifdef HAVE_HGE
702 3704 : size_t len = sizeof(hge);
703 3704 : hge l, *pval = &l;
704 3704 : if (hgeFromStr(CURRENT(cntxt), &len, &pval, false) < 0)
705 0 : l = hge_nil;
706 :
707 3704 : if ((hge) GDK_int_min <= l && l <= (hge) GDK_int_max) {
708 3697 : cst->vtype = TYPE_int;
709 3697 : cst->val.ival = (int) l;
710 7 : } else if ((hge) GDK_lng_min <= l && l <= (hge) GDK_lng_max) {
711 3 : cst->vtype = TYPE_lng;
712 3 : cst->val.lval = (lng) l;
713 : } else {
714 4 : cst->vtype = TYPE_hge;
715 4 : cst->val.hval = l;
716 : }
717 : #else
718 : size_t len = sizeof(lng);
719 : lng l, *pval = &l;
720 : if (lngFromStr(CURRENT(cntxt), &len, &pval, false) < 0)
721 : l = lng_nil;
722 :
723 : if ((lng) GDK_int_min <= l && l <= (lng) GDK_int_max) {
724 : cst->vtype = TYPE_int;
725 : cst->val.ival = (int) l;
726 : } else {
727 : cst->vtype = TYPE_lng;
728 : cst->val.lval = l;
729 : }
730 : #endif
731 : }
732 : return i;
733 :
734 1260 : case 'f':
735 1260 : if (strncmp(s, "false", 5) == 0 && !isalnum((unsigned char) *(s + 5)) &&
736 : *(s + 5) != '_') {
737 984 : cst->vtype = TYPE_bit;
738 984 : cst->val.btval = 0;
739 984 : cst->len = 1;
740 984 : return 5;
741 : }
742 : return 0;
743 983 : case 't':
744 983 : if (strncmp(s, "true", 4) == 0 && !isalnum((unsigned char) *(s + 4)) &&
745 : *(s + 4) != '_') {
746 716 : cst->vtype = TYPE_bit;
747 716 : cst->val.btval = 1;
748 716 : cst->len = 1;
749 716 : return 4;
750 : }
751 : return 0;
752 2162 : case 'n':
753 2162 : if (strncmp(s, "nil", 3) == 0 && !isalnum((unsigned char) *(s + 3)) &&
754 : *(s + 3) != '_') {
755 2095 : cst->vtype = TYPE_void;
756 2095 : cst->len = 0;
757 2095 : cst->val.oval = oid_nil;
758 2095 : return 3;
759 : }
760 : }
761 : return 0;
762 : }
763 :
764 : #define cstCopy(C,I) idCopy(C,I)
765 :
766 : /* Type qualifier
767 : * Types are recognized as identifiers preceded by a colon.
768 : *
769 : * The type ANY matches any type specifier.
770 : * Appending it with an alias turns it into a type variable.
771 : * The type alias is \$DIGIT (1-3) and can be used to relate types
772 : * by type equality.
773 : * The type variable are defined within the context of a function
774 : * scope.
775 : * Additional information, such as a repetition factor,
776 : * encoding tables, or type dependency should be modeled as properties.
777 : */
778 : static int
779 5137 : typeAlias(Client cntxt, int tpe)
780 : {
781 5137 : int t;
782 :
783 5137 : if (tpe != TYPE_any)
784 : return 0;
785 40 : if (currChar(cntxt) == TMPMARKER) {
786 37 : nextChar(cntxt);
787 37 : t = currChar(cntxt) - '0';
788 37 : if (t <= 0 || t > 3) {
789 0 : parseError(cntxt, "[1-3] expected\n");
790 0 : return -1;
791 : } else
792 37 : nextChar(cntxt);
793 37 : return t;
794 : }
795 : return 0;
796 : }
797 :
798 : /*
799 : * The simple type analysis currently assumes a proper type identifier.
800 : * We should change getMALtype to return a failure instead.
801 : */
802 : static int
803 5136 : simpleTypeId(Client cntxt)
804 : {
805 5136 : int tpe;
806 5136 : size_t l;
807 :
808 5136 : nextChar(cntxt);
809 5136 : l = typeidLength(cntxt);
810 5145 : if (l == 0) {
811 0 : parseError(cntxt, "Type identifier expected\n");
812 0 : cntxt->yycur--; /* keep it */
813 0 : return -1;
814 : }
815 5145 : if (l == 3 && CURRENT(cntxt)[0] == 'b' && CURRENT(cntxt)[1] == 'a' && CURRENT(cntxt)[2] == 't')
816 : tpe = newBatType(TYPE_any);
817 : else
818 5145 : tpe = getAtomIndex(CURRENT(cntxt), l, -1);
819 5134 : if (tpe < 0) {
820 0 : parseError(cntxt, "Type identifier expected\n");
821 0 : cntxt->yycur -= l; /* keep it */
822 0 : return TYPE_void;
823 : }
824 5134 : advance(cntxt, l);
825 5134 : return tpe;
826 : }
827 :
828 : static int
829 5282 : parseTypeId(Client cntxt)
830 : {
831 5282 : int i = TYPE_any, kt = 0;
832 5282 : char *s = CURRENT(cntxt);
833 5282 : int tt;
834 :
835 5282 : if (strncmp(s, ":bat", 4) == 0 || strncmp(s, ":BAT", 4) == 0) {
836 567 : int opt = 0;
837 : /* parse :bat[:type] */
838 567 : advance(cntxt, 4);
839 567 : if (currChar(cntxt) == '?') {
840 0 : opt = 1;
841 0 : advance(cntxt, 1);
842 : }
843 567 : if (currChar(cntxt) != '[') {
844 146 : if (opt)
845 : setOptBat(i);
846 : else
847 146 : i = newBatType(TYPE_any);
848 146 : return i;
849 : if (!opt)
850 : return newBatType(TYPE_any);
851 :
852 : parseError(cntxt, "':bat[:type]' expected\n");
853 : return -1;
854 : }
855 421 : advance(cntxt, 1);
856 421 : if (currChar(cntxt) == ':') {
857 421 : tt = simpleTypeId(cntxt);
858 421 : kt = typeAlias(cntxt, tt);
859 421 : if (kt < 0)
860 : return kt;
861 : } else {
862 0 : parseError(cntxt, "':bat[:any]' expected\n");
863 0 : return -1;
864 : }
865 :
866 421 : if (!opt)
867 421 : i = newBatType(tt);
868 421 : if (kt > 0)
869 22 : setTypeIndex(i, kt);
870 421 : if (opt)
871 0 : setOptBat(i);
872 :
873 421 : if (currChar(cntxt) != ']')
874 0 : parseError(cntxt, "']' expected\n");
875 421 : nextChar(cntxt); // skip ']'
876 421 : skipSpace(cntxt);
877 421 : return i;
878 : }
879 4715 : if (currChar(cntxt) == ':') {
880 4715 : tt = simpleTypeId(cntxt);
881 4715 : kt = typeAlias(cntxt, tt);
882 4718 : if (kt < 0)
883 : return kt;
884 4718 : if (kt > 0)
885 15 : setTypeIndex(tt, kt);
886 4718 : return tt;
887 : }
888 0 : parseError(cntxt, "<type identifier> expected\n");
889 0 : return -1;
890 : }
891 :
892 : static inline int
893 9613 : typeElm(Client cntxt, int def)
894 : {
895 9613 : if (currChar(cntxt) != ':')
896 : return def; /* no type qualifier */
897 5280 : return parseTypeId(cntxt);
898 : }
899 :
900 : /*
901 : * The Parser
902 : * The client is responsible to collect the
903 : * input for parsing in a single string before calling the parser.
904 : * Once the input is available parsing runs in a critical section for
905 : * a single client thread.
906 : *
907 : * The parser uses the rigid structure of the language to speedup
908 : * analysis. In particular, each input line is translated into
909 : * a MAL instruction record as quickly as possible. Its context is
910 : * manipulated during the parsing process, by keeping the curPrg,
911 : * curBlk, and curInstr variables.
912 : *
913 : * The language statements of the parser are gradually introduced, with
914 : * the overall integration framework last.
915 : * The convention is to return a zero when an error has been
916 : * reported or when the structure can not be recognized.
917 : * Furthermore, we assume that blancs have been skipped before entering
918 : * recognition of a new token.
919 : *
920 : * Module statement.
921 : * The module and import commands have immediate effect.
922 : * The module statement switches the location for symbol table update
923 : * to a specific named area. The effect is that all definitions may become
924 : * globally known (?) and symbol table should be temporarily locked
925 : * for updates by concurrent users.
926 : *
927 : * @multitable @columnfractions 0.15 0.8
928 : * @item moduleStmt
929 : * @tab : @sc{atom} ident [':'ident]
930 : * @item
931 : * @tab | @sc{module} ident
932 : * @end multitable
933 : *
934 : * An atom statement does not introduce a new module.
935 : */
936 : static void
937 219 : helpInfo(Client cntxt, str *help)
938 : {
939 219 : int l = 0;
940 219 : char c, *e, *s;
941 :
942 219 : if (MALkeyword(cntxt, "comment", 7)) {
943 2 : skipSpace(cntxt);
944 : // The comment is either a quoted string or all characters up to the next semicolon
945 2 : c = currChar(cntxt);
946 2 : if (c != '"') {
947 : e = s = CURRENT(cntxt);
948 0 : for (; *e; l++, e++)
949 0 : if (*e == ';')
950 : break;
951 0 : *help = strCopy(cntxt, l);
952 0 : skipToEnd(cntxt);
953 : } else {
954 2 : if ((l = stringLength(cntxt))) {
955 2 : GDKfree(*help);
956 2 : *help = strCopy(cntxt, l);
957 2 : if (*help)
958 2 : advance(cntxt, l - 1);
959 2 : skipToEnd(cntxt);
960 : } else {
961 0 : parseError(cntxt, "<string> expected\n");
962 : }
963 : }
964 217 : } else if (currChar(cntxt) != ';')
965 0 : parseError(cntxt, "';' expected\n");
966 219 : }
967 :
968 : static InstrPtr
969 164 : binding(Client cntxt, MalBlkPtr curBlk, InstrPtr curInstr, int flag)
970 : {
971 164 : int l, varid = -1;
972 164 : malType type;
973 :
974 164 : l = idLength(cntxt);
975 164 : if (l > 0) {
976 130 : varid = findVariableLength(curBlk, CURRENT(cntxt), l);
977 130 : if (varid < 0) {
978 130 : varid = newVariable(curBlk, CURRENT(cntxt), l, TYPE_any);
979 130 : advance(cntxt, l);
980 130 : if (varid < 0)
981 : return curInstr;
982 130 : type = typeElm(cntxt, TYPE_any);
983 130 : if (type < 0)
984 : return curInstr;
985 130 : if (isPolymorphic(type))
986 27 : setPolymorphic(curInstr, type, TRUE);
987 130 : setVarType(curBlk, varid, type);
988 0 : } else if (flag) {
989 0 : parseError(cntxt, "Argument defined twice\n");
990 0 : typeElm(cntxt, getVarType(curBlk, varid));
991 : } else {
992 0 : advance(cntxt, l);
993 0 : type = typeElm(cntxt, getVarType(curBlk, varid));
994 0 : if (type != getVarType(curBlk, varid))
995 0 : parseError(cntxt, "Incompatible argument type\n");
996 0 : if (isPolymorphic(type))
997 0 : setPolymorphic(curInstr, type, TRUE);
998 0 : setVarType(curBlk, varid, type);
999 : }
1000 34 : } else if (currChar(cntxt) == ':') {
1001 34 : type = typeElm(cntxt, TYPE_any);
1002 34 : varid = newTmpVariable(curBlk, type);
1003 34 : if (varid < 0)
1004 : return curInstr;
1005 34 : if (isPolymorphic(type))
1006 0 : setPolymorphic(curInstr, type, TRUE);
1007 34 : setVarType(curBlk, varid, type);
1008 : } else {
1009 0 : parseError(cntxt, "argument expected\n");
1010 0 : return curInstr;
1011 : }
1012 164 : if (varid >= 0)
1013 164 : curInstr = pushArgument(curBlk, curInstr, varid);
1014 164 : return curInstr;
1015 : }
1016 :
1017 : /*
1018 : * At this stage the LHS part has been parsed and the destination
1019 : * variables have been set. Next step is to parse the expression,
1020 : * which starts with an operand.
1021 : * This code is used in both positions of the expression
1022 : */
1023 : static int
1024 18151 : term(Client cntxt, MalBlkPtr curBlk, InstrPtr *curInstr, int ret)
1025 : {
1026 18151 : int i, idx, free = 1;
1027 18151 : ValRecord cst;
1028 18151 : int cstidx = -1;
1029 18151 : malType tpe = TYPE_any;
1030 :
1031 18151 : if ((i = cstToken(cntxt, &cst))) {
1032 10447 : advance(cntxt, i);
1033 10443 : if (currChar(cntxt) != ':' && cst.vtype == TYPE_dbl
1034 47 : && cst.val.dval > FLT_MIN && cst.val.dval <= FLT_MAX) {
1035 46 : float dummy = (flt) cst.val.dval;
1036 46 : cst.vtype = TYPE_flt;
1037 46 : cst.val.fval = dummy;
1038 : }
1039 10443 : cstidx = fndConstant(curBlk, &cst, MAL_VAR_WINDOW);
1040 10449 : if (cstidx >= 0) {
1041 :
1042 1740 : if (currChar(cntxt) == ':') {
1043 168 : tpe = typeElm(cntxt, getVarType(curBlk, cstidx));
1044 168 : if (tpe < 0)
1045 : return 3;
1046 168 : cst.bat = isaBatType(tpe);
1047 168 : if (tpe != getVarType(curBlk, cstidx)) {
1048 3 : cstidx = defConstant(curBlk, tpe, &cst);
1049 3 : if (cstidx < 0)
1050 : return 3;
1051 3 : setPolymorphic(*curInstr, tpe, FALSE);
1052 3 : free = 0;
1053 : }
1054 1572 : } else if (cst.vtype != getVarType(curBlk, cstidx)) {
1055 0 : cstidx = defConstant(curBlk, cst.vtype, &cst);
1056 0 : if (cstidx < 0)
1057 : return 3;
1058 0 : setPolymorphic(*curInstr, cst.vtype, FALSE);
1059 0 : free = 0;
1060 : }
1061 : /* protect against leaks coming from constant reuse */
1062 1740 : if (free && ATOMextern(cst.vtype) && cst.val.pval)
1063 48 : VALclear(&cst);
1064 1740 : *curInstr = pushArgument(curBlk, *curInstr, cstidx);
1065 1740 : return ret;
1066 : } else {
1067 : /* add a new constant literal, the :type could be erroneously be a coltype */
1068 8709 : tpe = typeElm(cntxt, cst.vtype);
1069 8706 : if (tpe < 0)
1070 : return 3;
1071 8706 : cst.bat = isaBatType(tpe);
1072 8706 : cstidx = defConstant(curBlk, tpe, &cst);
1073 8704 : if (cstidx < 0)
1074 : return 3;
1075 8700 : setPolymorphic(*curInstr, tpe, FALSE);
1076 8702 : *curInstr = pushArgument(curBlk, *curInstr, cstidx);
1077 8700 : return ret;
1078 : }
1079 7706 : } else if ((i = idLength(cntxt))) {
1080 7430 : if ((idx = findVariableLength(curBlk, CURRENT(cntxt), i)) == -1) {
1081 5 : idx = newVariable(curBlk, CURRENT(cntxt), i, TYPE_any);
1082 5 : advance(cntxt, i);
1083 5 : if (idx < 0)
1084 : return 0;
1085 : } else {
1086 7425 : advance(cntxt, i);
1087 : }
1088 7430 : if (currChar(cntxt) == ':') {
1089 : /* skip the type description */
1090 2 : tpe = typeElm(cntxt, TYPE_any);
1091 2 : if (getVarType(curBlk, idx) == TYPE_any)
1092 1 : setVarType(curBlk, idx, tpe);
1093 1 : else if (getVarType(curBlk, idx) != tpe) {
1094 : /* non-matching types */
1095 : return 4;
1096 : }
1097 : }
1098 7430 : *curInstr = pushArgument(curBlk, *curInstr, idx);
1099 276 : } else if (currChar(cntxt) == ':') {
1100 274 : tpe = typeElm(cntxt, TYPE_any);
1101 274 : if (tpe < 0)
1102 : return 3;
1103 274 : setPolymorphic(*curInstr, tpe, FALSE);
1104 274 : idx = newTypeVariable(curBlk, tpe);
1105 274 : *curInstr = pushArgument(curBlk, *curInstr, idx);
1106 274 : return ret;
1107 : }
1108 : return 0;
1109 : }
1110 :
1111 : static int
1112 5 : parseAtom(Client cntxt)
1113 : {
1114 5 : const char *modnme = 0;
1115 5 : int l, tpe;
1116 5 : char *nxt = CURRENT(cntxt);
1117 :
1118 5 : if ((l = idLength(cntxt)) <= 0) {
1119 0 : parseError(cntxt, "atom name expected\n");
1120 0 : return -1;
1121 : }
1122 :
1123 : /* parse: ATOM id:type */
1124 5 : modnme = putNameLen(nxt, l);
1125 5 : if (modnme == NULL) {
1126 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1127 0 : return -1;
1128 : }
1129 5 : advance(cntxt, l);
1130 5 : if (currChar(cntxt) != ':')
1131 : tpe = TYPE_void; /* no type qualifier */
1132 : else
1133 5 : tpe = parseTypeId(cntxt);
1134 5 : if (ATOMindex(modnme) < 0) {
1135 4 : if (cntxt->curprg->def->errors)
1136 0 : freeException(cntxt->curprg->def->errors);
1137 4 : cntxt->curprg->def->errors = malAtomDefinition(modnme, tpe);
1138 : }
1139 5 : if (strcmp(modnme, "user"))
1140 5 : cntxt->curmodule = fixModule(modnme);
1141 : else
1142 0 : cntxt->curmodule = cntxt->usermodule;
1143 5 : cntxt->usermodule->isAtomModule = TRUE;
1144 5 : skipSpace(cntxt);
1145 5 : helpInfo(cntxt, &cntxt->usermodule->help);
1146 5 : return 0;
1147 : }
1148 :
1149 : /*
1150 : * All modules, except 'user', should be global
1151 : */
1152 : static int
1153 3 : parseModule(Client cntxt)
1154 : {
1155 3 : const char *modnme = 0;
1156 3 : int l;
1157 3 : char *nxt;
1158 :
1159 3 : nxt = CURRENT(cntxt);
1160 3 : if ((l = idLength(cntxt)) <= 0) {
1161 0 : parseError(cntxt, "<module path> expected\n");
1162 0 : return -1;
1163 : }
1164 3 : modnme = putNameLen(nxt, l);
1165 3 : if (modnme == NULL) {
1166 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1167 0 : return -1;
1168 : }
1169 3 : advance(cntxt, l);
1170 3 : if (strcmp(modnme, cntxt->usermodule->name) == 0) {
1171 : // ignore this module definition
1172 3 : } else if (getModule(modnme) == NULL) {
1173 3 : if (globalModule(modnme) == NULL)
1174 0 : parseError(cntxt, "<module> could not be created");
1175 : }
1176 3 : if (strcmp(modnme, "user"))
1177 3 : cntxt->curmodule = fixModule(modnme);
1178 : else
1179 0 : cntxt->curmodule = cntxt->usermodule;
1180 3 : skipSpace(cntxt);
1181 3 : helpInfo(cntxt, &cntxt->usermodule->help);
1182 3 : return 0;
1183 : }
1184 :
1185 : /*
1186 : * Include files should be handled in line with parsing. This way we
1187 : * are ensured that any possible signature definition will be known
1188 : * afterwards. The effect is that errors in the include sequence are
1189 : * marked as warnings.
1190 : */
1191 : static int
1192 2 : parseInclude(Client cntxt)
1193 : {
1194 2 : const char *modnme = 0;
1195 2 : char *s;
1196 2 : int x;
1197 2 : char *nxt;
1198 :
1199 2 : nxt = CURRENT(cntxt);
1200 :
1201 2 : if ((x = idLength(cntxt)) > 0) {
1202 2 : modnme = putNameLen(nxt, x);
1203 2 : advance(cntxt, x);
1204 0 : } else if ((x = stringLength(cntxt)) > 0) {
1205 0 : modnme = putNameLen(nxt + 1, x - 1);
1206 0 : advance(cntxt, x);
1207 : } else {
1208 0 : parseError(cntxt, "<module name> expected\n");
1209 0 : return -1;
1210 : }
1211 2 : if (modnme == NULL) {
1212 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1213 0 : return -1;
1214 : }
1215 :
1216 2 : if (currChar(cntxt) != ';') {
1217 0 : parseError(cntxt, "';' expected\n");
1218 0 : return 0;
1219 : }
1220 2 : skipToEnd(cntxt);
1221 :
1222 2 : if (!malLibraryEnabled(modnme)) {
1223 : return 0;
1224 : }
1225 :
1226 2 : if (getModule(modnme) == NULL) {
1227 1 : s = loadLibrary(modnme, FALSE);
1228 1 : if (s) {
1229 1 : parseError(cntxt, s);
1230 1 : freeException(s);
1231 1 : return 0;
1232 : }
1233 : }
1234 1 : if ((s = malInclude(cntxt, modnme, 0))) {
1235 0 : parseError(cntxt, s);
1236 0 : freeException(s);
1237 0 : return 0;
1238 : }
1239 : return 0;
1240 : }
1241 :
1242 : /* return the combined count of the number of arguments and the number
1243 : * of return values so that we can allocate enough space in the
1244 : * instruction; returns -1 on error (missing closing parenthesis) */
1245 : static int
1246 212 : cntArgsReturns(Client cntxt, int *retc)
1247 : {
1248 212 : size_t yycur = cntxt->yycur;
1249 212 : int cnt = 0;
1250 212 : char ch;
1251 :
1252 212 : ch = currChar(cntxt);
1253 212 : if (ch != ')') {
1254 : cnt++;
1255 1129 : while (ch != ')' && ch && !NL(ch)) {
1256 1038 : if (ch == ',')
1257 43 : cnt++;
1258 1038 : nextChar(cntxt);
1259 1038 : ch = currChar(cntxt);
1260 : }
1261 : }
1262 91 : if (ch != ')') {
1263 0 : parseError(cntxt, "')' expected\n");
1264 0 : cntxt->yycur = yycur;
1265 0 : return -1;
1266 : }
1267 212 : advance(cntxt, 1);
1268 212 : ch = currChar(cntxt);
1269 212 : if (ch == '(') {
1270 13 : advance(cntxt, 1);
1271 13 : ch = currChar(cntxt);
1272 13 : cnt++;
1273 13 : (*retc)++;
1274 304 : while (ch != ')' && ch && !NL(ch)) {
1275 291 : if (ch == ',') {
1276 39 : cnt++;
1277 39 : (*retc)++;
1278 : }
1279 291 : nextChar(cntxt);
1280 291 : ch = currChar(cntxt);
1281 : }
1282 13 : if (ch != ')') {
1283 0 : parseError(cntxt, "')' expected\n");
1284 0 : cntxt->yycur = yycur;
1285 0 : return -1;
1286 : }
1287 : } else {
1288 199 : cnt++;
1289 199 : (*retc)++;
1290 : }
1291 212 : cntxt->yycur = yycur;
1292 212 : return cnt;
1293 : }
1294 :
1295 : static void
1296 0 : mf_destroy(mel_func *f)
1297 : {
1298 0 : if (f) {
1299 0 : if (f->args)
1300 0 : GDKfree(f->args);
1301 0 : GDKfree(f);
1302 : }
1303 0 : }
1304 :
1305 : static int
1306 22 : argument(Client cntxt, mel_func *curFunc, mel_arg *curArg)
1307 : {
1308 22 : malType type;
1309 :
1310 22 : int l = idLength(cntxt);
1311 22 : *curArg = (mel_arg){ .isbat = 0 };
1312 22 : if (l > 0) {
1313 15 : char *varname = CURRENT(cntxt);
1314 15 : (void)varname; /* not used */
1315 :
1316 15 : advance(cntxt, l);
1317 15 : type = typeElm(cntxt, TYPE_any);
1318 15 : if (type < 0)
1319 : return -1;
1320 15 : int tt = getBatType(type);
1321 15 : if (tt != TYPE_any)
1322 13 : strcpy(curArg->type, BATatoms[tt].name);
1323 15 : if (isaBatType(type))
1324 0 : curArg->isbat = true;
1325 15 : if (isPolymorphic(type)) {
1326 2 : curArg->nr = getTypeIndex(type);
1327 2 : setPoly(curFunc, type);
1328 2 : tt = TYPE_any;
1329 : }
1330 15 : curArg->typeid = tt;
1331 7 : } else if (currChar(cntxt) == ':') {
1332 7 : type = typeElm(cntxt, TYPE_any);
1333 7 : int tt = getBatType(type);
1334 7 : if (tt != TYPE_any)
1335 5 : strcpy(curArg->type, BATatoms[tt].name);
1336 7 : if (isaBatType(type))
1337 3 : curArg->isbat = true;
1338 7 : if (isPolymorphic(type)) {
1339 1 : curArg->nr = getTypeIndex(type);
1340 1 : setPoly(curFunc, type);
1341 1 : tt = TYPE_any;
1342 : }
1343 7 : curArg->typeid = tt;
1344 : } else {
1345 0 : parseError(cntxt, "argument expected\n");
1346 0 : return -1;
1347 : }
1348 : return 0;
1349 : }
1350 :
1351 : static mel_func *
1352 13 : fcnCommandPatternHeader(Client cntxt, int kind)
1353 : {
1354 13 : int l;
1355 13 : malType tpe;
1356 13 : const char *fnme;
1357 13 : const char *modnme = NULL;
1358 13 : char ch;
1359 :
1360 13 : l = operatorLength(cntxt);
1361 13 : if (l == 0)
1362 12 : l = idLength(cntxt);
1363 12 : if (l == 0) {
1364 0 : parseError(cntxt, "<identifier> | <operator> expected\n");
1365 0 : return NULL;
1366 : }
1367 :
1368 13 : fnme = putNameLen(((char *) CURRENT(cntxt)), l);
1369 13 : if (fnme == NULL) {
1370 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1371 0 : return NULL;
1372 : }
1373 13 : advance(cntxt, l);
1374 :
1375 13 : if (currChar(cntxt) == '.') {
1376 0 : nextChar(cntxt); /* skip '.' */
1377 0 : modnme = fnme;
1378 0 : if (strcmp(modnme, "user") && getModule(modnme) == NULL) {
1379 0 : if (globalModule(modnme) == NULL) {
1380 0 : parseError(cntxt, "<module> name not defined\n");
1381 0 : return NULL;
1382 : }
1383 : }
1384 0 : l = operatorLength(cntxt);
1385 0 : if (l == 0)
1386 0 : l = idLength(cntxt);
1387 0 : if (l == 0) {
1388 0 : parseError(cntxt, "<identifier> | <operator> expected\n");
1389 0 : return NULL;
1390 : }
1391 0 : fnme = putNameLen(((char *) CURRENT(cntxt)), l);
1392 0 : if (fnme == NULL) {
1393 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1394 0 : return NULL;
1395 : }
1396 0 : advance(cntxt, l);
1397 : } else
1398 13 : modnme = cntxt->curmodule->name;
1399 :
1400 13 : if (currChar(cntxt) != '(') {
1401 0 : parseError(cntxt, "function header '(' expected\n");
1402 0 : return NULL;
1403 : }
1404 13 : advance(cntxt, 1);
1405 :
1406 : /* keep current prg also active ! */
1407 13 : int retc = 0, nargs = cntArgsReturns(cntxt, &retc);
1408 13 : if (nargs < 0)
1409 : return 0;
1410 :
1411 : /* one extra for argument/return manipulation */
1412 13 : assert(kind == COMMANDsymbol || kind == PATTERNsymbol);
1413 :
1414 13 : mel_func *curFunc = (mel_func*)GDKmalloc(sizeof(mel_func));
1415 13 : if (curFunc)
1416 13 : curFunc->args = NULL;
1417 13 : if (curFunc && nargs)
1418 13 : curFunc->args = (mel_arg*)GDKmalloc(sizeof(mel_arg)*nargs);
1419 :
1420 13 : if (cntxt->curprg == NULL || cntxt->curprg->def->errors || curFunc == NULL || (nargs && curFunc->args == NULL)) {
1421 0 : mf_destroy(curFunc);
1422 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1423 0 : return NULL;
1424 : }
1425 :
1426 13 : curFunc->fcn = fnme;
1427 13 : curFunc->mod = modnme;
1428 13 : curFunc->cname = NULL;
1429 13 : curFunc->command = false;
1430 13 : if (kind == COMMANDsymbol)
1431 5 : curFunc->command = true;
1432 13 : curFunc->unsafe = 0;
1433 13 : curFunc->vargs = 0;
1434 13 : curFunc->vrets = 0;
1435 13 : curFunc->poly = 0;
1436 13 : curFunc->retc = retc;
1437 13 : curFunc->argc = nargs;
1438 13 : curFunc->comment = NULL;
1439 :
1440 : /* get calling parameters */
1441 13 : ch = currChar(cntxt);
1442 13 : int i = retc;
1443 19 : while (ch != ')' && ch && !NL(ch)) {
1444 18 : if (argument(cntxt, curFunc, curFunc->args+i) < 0) {
1445 0 : mf_destroy(curFunc);
1446 0 : return NULL;
1447 : }
1448 : /* the last argument may be variable length */
1449 18 : if (MALkeyword(cntxt, "...", 3)) {
1450 6 : curFunc->vargs = true;
1451 6 : setPoly(curFunc, TYPE_any);
1452 6 : break;
1453 : }
1454 12 : if ((ch = currChar(cntxt)) != ',') {
1455 6 : if (ch == ')')
1456 : break;
1457 0 : mf_destroy(curFunc);
1458 0 : parseError(cntxt, "',' expected\n");
1459 0 : return NULL;
1460 : } else {
1461 6 : nextChar(cntxt); /* skip ',' */
1462 6 : i++;
1463 : }
1464 6 : skipSpace(cntxt);
1465 6 : ch = currChar(cntxt);
1466 : }
1467 13 : if (currChar(cntxt) != ')') {
1468 0 : mf_destroy(curFunc);
1469 0 : parseError(cntxt, "')' expected\n");
1470 0 : return NULL;
1471 : }
1472 13 : advance(cntxt, 1); /* skip ')' */
1473 : /*
1474 : The return type is either a single type or multiple return type structure.
1475 : We simply keep track of the number of arguments added and
1476 : during the final phase reshuffle the return values to the beginning (?)
1477 : */
1478 13 : if (currChar(cntxt) == ':') {
1479 7 : tpe = typeElm(cntxt, TYPE_void);
1480 7 : curFunc->args[0].vargs = 0;
1481 7 : curFunc->args[0].nr = 0;
1482 7 : if (isPolymorphic(tpe)) {
1483 1 : curFunc->args[0].nr = getTypeIndex(tpe);
1484 1 : setPoly(curFunc, tpe);
1485 : }
1486 7 : if (isaBatType(tpe))
1487 3 : curFunc->args[0].isbat = true;
1488 : else
1489 4 : curFunc->args[0].isbat = false;
1490 7 : int tt = getBatType(tpe);
1491 7 : curFunc->args[0].typeid = tt;
1492 7 : curFunc->args[0].opt = 0;
1493 : /* we may be confronted by a variable target type list */
1494 7 : if (MALkeyword(cntxt, "...", 3)) {
1495 3 : curFunc->args[0].vargs = true;
1496 3 : curFunc->vrets = true;
1497 3 : setPoly(curFunc, TYPE_any);
1498 : }
1499 6 : } else if (keyphrase1(cntxt, "(")) { /* deal with compound return */
1500 3 : int i = 0;
1501 : /* parse multi-target result */
1502 : /* skipSpace(cntxt); */
1503 3 : ch = currChar(cntxt);
1504 4 : while (ch != ')' && ch && !NL(ch)) {
1505 4 : if (argument(cntxt, curFunc, curFunc->args+i) < 0) {
1506 0 : mf_destroy(curFunc);
1507 0 : return NULL;
1508 : }
1509 : /* we may be confronted by a variable target type list */
1510 4 : if (MALkeyword(cntxt, "...", 3)) {
1511 3 : curFunc->args[i].vargs = true;
1512 3 : curFunc->vrets = true;
1513 3 : setPoly(curFunc, TYPE_any);
1514 : }
1515 4 : if ((ch = currChar(cntxt)) != ',') {
1516 3 : if (ch == ')')
1517 : break;
1518 0 : parseError(cntxt, "',' expected\n");
1519 0 : return curFunc;
1520 : } else {
1521 1 : nextChar(cntxt); /* skip ',' */
1522 1 : i++;
1523 : }
1524 1 : skipSpace(cntxt);
1525 1 : ch = currChar(cntxt);
1526 : }
1527 3 : if (currChar(cntxt) != ')') {
1528 0 : mf_destroy(curFunc);
1529 0 : parseError(cntxt, "')' expected\n");
1530 0 : return NULL;
1531 : }
1532 3 : nextChar(cntxt); /* skip ')' */
1533 : }
1534 : return curFunc;
1535 : }
1536 :
1537 : static Symbol
1538 13 : parseCommandPattern(Client cntxt, int kind, MALfcn address)
1539 : {
1540 13 : mel_func *curFunc = fcnCommandPatternHeader(cntxt, kind);
1541 13 : if (curFunc == NULL) {
1542 0 : cntxt->blkmode = 0;
1543 0 : return NULL;
1544 : }
1545 13 : const char *modnme = curFunc->mod;
1546 13 : if (modnme && (getModule(modnme) == FALSE && strcmp(modnme, "user"))) {
1547 : // introduce the module
1548 0 : if (globalModule(modnme) == NULL) {
1549 0 : mf_destroy(curFunc);
1550 0 : parseError(cntxt, "<module> could not be defined\n");
1551 0 : return NULL;
1552 : }
1553 : }
1554 0 : modnme = modnme ? modnme : cntxt->usermodule->name;
1555 :
1556 13 : size_t l = strlen(modnme);
1557 13 : modnme = putNameLen(modnme, l);
1558 13 : if (modnme == NULL) {
1559 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1560 0 : return NULL;
1561 : }
1562 :
1563 13 : Symbol curPrg = newFunctionArgs(modnme, curFunc->fcn, kind, -1);
1564 13 : if (!curPrg) {
1565 0 : mf_destroy(curFunc);
1566 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1567 0 : return NULL;
1568 : }
1569 13 : curPrg->func = curFunc;
1570 13 : curPrg->def = NULL;
1571 13 : curPrg->allocated = true;
1572 :
1573 13 : skipSpace(cntxt);
1574 13 : if (MALkeyword(cntxt, "address", 7)) {
1575 13 : int i;
1576 13 : i = idLength(cntxt);
1577 13 : if (i == 0) {
1578 0 : parseError(cntxt, "address <identifier> expected\n");
1579 0 : return NULL;
1580 : }
1581 13 : cntxt->blkmode = 0;
1582 :
1583 13 : size_t sz = (size_t) (i < IDLENGTH ? i : IDLENGTH - 1);
1584 13 : curFunc->cname = GDKmalloc(sz+1);
1585 13 : if (!curFunc->cname) {
1586 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1587 0 : freeSymbol(curPrg);
1588 0 : return NULL;
1589 : }
1590 13 : memcpy((char*)curFunc->cname, CURRENT(cntxt), sz);
1591 13 : ((char*)curFunc->cname)[sz] = 0;
1592 : /* avoid a clash with old temporaries */
1593 13 : advance(cntxt, i);
1594 13 : curFunc->imp = getAddress(curFunc->mod, curFunc->cname);
1595 :
1596 13 : if (cntxt->usermodule->isAtomModule) {
1597 3 : if (curFunc->imp == NULL) {
1598 0 : parseError(cntxt, "<address> not found\n");
1599 0 : freeSymbol(curPrg);
1600 0 : return NULL;
1601 : }
1602 3 : malAtomProperty(curFunc);
1603 : }
1604 13 : skipSpace(cntxt);
1605 0 : } else if (address) {
1606 0 : curFunc->mod = modnme;
1607 0 : curFunc->imp = address;
1608 : }
1609 13 : if (strcmp(modnme, "user") == 0 || getModule(modnme)) {
1610 13 : if (strcmp(modnme, "user") == 0)
1611 10 : insertSymbol(cntxt->usermodule, curPrg);
1612 : else
1613 3 : insertSymbol(getModule(modnme), curPrg);
1614 : } else {
1615 0 : freeSymbol(curPrg);
1616 0 : parseError(cntxt, "<module> not found\n");
1617 0 : return NULL;
1618 : }
1619 :
1620 13 : helpInfo(cntxt, &curFunc->comment);
1621 13 : return curPrg;
1622 : }
1623 :
1624 : static MalBlkPtr
1625 199 : fcnHeader(Client cntxt, int kind)
1626 : {
1627 199 : int l;
1628 199 : malType tpe;
1629 199 : const char *fnme;
1630 199 : const char *modnme = NULL;
1631 199 : char ch;
1632 199 : Symbol curPrg;
1633 199 : MalBlkPtr curBlk = 0;
1634 199 : InstrPtr curInstr;
1635 :
1636 199 : l = operatorLength(cntxt);
1637 199 : if (l == 0)
1638 199 : l = idLength(cntxt);
1639 199 : if (l == 0) {
1640 0 : parseError(cntxt, "<identifier> | <operator> expected\n");
1641 0 : return 0;
1642 : }
1643 :
1644 199 : fnme = putNameLen(((char *) CURRENT(cntxt)), l);
1645 199 : if (fnme == NULL) {
1646 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1647 0 : return NULL;
1648 : }
1649 199 : advance(cntxt, l);
1650 :
1651 199 : if (currChar(cntxt) == '.') {
1652 8 : nextChar(cntxt); /* skip '.' */
1653 8 : modnme = fnme;
1654 8 : if (strcmp(modnme, "user") && getModule(modnme) == NULL) {
1655 1 : if (globalModule(modnme) == NULL) {
1656 0 : parseError(cntxt, "<module> name not defined\n");
1657 0 : return 0;
1658 : }
1659 : }
1660 8 : l = operatorLength(cntxt);
1661 8 : if (l == 0)
1662 8 : l = idLength(cntxt);
1663 8 : if (l == 0) {
1664 0 : parseError(cntxt, "<identifier> | <operator> expected\n");
1665 0 : return 0;
1666 : }
1667 8 : fnme = putNameLen(((char *) CURRENT(cntxt)), l);
1668 8 : if (fnme == NULL) {
1669 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1670 0 : return NULL;
1671 : }
1672 8 : advance(cntxt, l);
1673 : } else
1674 191 : modnme = cntxt->curmodule->name;
1675 :
1676 : /* temporary suspend capturing statements in main block */
1677 199 : if (cntxt->backup) {
1678 0 : parseError(cntxt, "mal_parser: unexpected recursion\n");
1679 0 : return 0;
1680 : }
1681 199 : if (currChar(cntxt) != '(') {
1682 0 : parseError(cntxt, "function header '(' expected\n");
1683 0 : return curBlk;
1684 : }
1685 199 : advance(cntxt, 1);
1686 :
1687 199 : assert(!cntxt->backup);
1688 199 : cntxt->backup = cntxt->curprg;
1689 199 : int retc = 0, nargs = cntArgsReturns(cntxt, &retc);
1690 199 : (void)retc;
1691 199 : if (nargs < 0)
1692 : return 0;
1693 : /* one extra for argument/return manipulation */
1694 199 : cntxt->curprg = newFunctionArgs(modnme, fnme, kind, nargs + 1);
1695 199 : if (cntxt->curprg == NULL) {
1696 : /* reinstate curprg to have a place for the error */
1697 0 : cntxt->curprg = cntxt->backup;
1698 0 : cntxt->backup = NULL;
1699 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1700 0 : return 0;
1701 : }
1702 199 : cntxt->curprg->def->errors = cntxt->backup->def->errors;
1703 199 : cntxt->backup->def->errors = 0;
1704 199 : curPrg = cntxt->curprg;
1705 199 : curBlk = curPrg->def;
1706 199 : curInstr = getInstrPtr(curBlk, 0);
1707 :
1708 : /* get calling parameters */
1709 199 : ch = currChar(cntxt);
1710 236 : while (ch != ')' && ch && !NL(ch)) {
1711 116 : curInstr = binding(cntxt, curBlk, curInstr, 1);
1712 : /* the last argument may be variable length */
1713 116 : if (MALkeyword(cntxt, "...", 3)) {
1714 1 : curInstr->varargs |= VARARGS;
1715 1 : setPolymorphic(curInstr, TYPE_any, TRUE);
1716 1 : break;
1717 : }
1718 115 : if ((ch = currChar(cntxt)) != ',') {
1719 78 : if (ch == ')')
1720 : break;
1721 0 : if (cntxt->backup)
1722 0 : curBlk = NULL;
1723 0 : parseError(cntxt, "',' expected\n");
1724 0 : return curBlk;
1725 : } else
1726 37 : nextChar(cntxt); /* skip ',' */
1727 37 : skipSpace(cntxt);
1728 37 : ch = currChar(cntxt);
1729 : }
1730 199 : if (currChar(cntxt) != ')') {
1731 0 : freeInstruction(curInstr);
1732 0 : if (cntxt->backup)
1733 0 : curBlk = NULL;
1734 0 : parseError(cntxt, "')' expected\n");
1735 0 : return curBlk;
1736 : }
1737 199 : advance(cntxt, 1); /* skip ')' */
1738 : /*
1739 : The return type is either a single type or multiple return type structure.
1740 : We simply keep track of the number of arguments added and
1741 : during the final phase reshuffle the return values to the beginning (?)
1742 : */
1743 199 : if (currChar(cntxt) == ':') {
1744 76 : tpe = typeElm(cntxt, TYPE_void);
1745 76 : setPolymorphic(curInstr, tpe, TRUE);
1746 76 : setVarType(curBlk, curInstr->argv[0], tpe);
1747 : /* we may be confronted by a variable target type list */
1748 76 : if (MALkeyword(cntxt, "...", 3)) {
1749 0 : curInstr->varargs |= VARRETS;
1750 0 : setPolymorphic(curInstr, TYPE_any, TRUE);
1751 : }
1752 :
1753 123 : } else if (keyphrase1(cntxt, "(")) { /* deal with compound return */
1754 10 : int retc = curInstr->argc, i1, i2 = 0;
1755 10 : int max;
1756 10 : short *newarg;
1757 : /* parse multi-target result */
1758 : /* skipSpace(cntxt); */
1759 10 : ch = currChar(cntxt);
1760 48 : while (ch != ')' && ch && !NL(ch)) {
1761 48 : curInstr = binding(cntxt, curBlk, curInstr, 0);
1762 : /* we may be confronted by a variable target type list */
1763 48 : if (MALkeyword(cntxt, "...", 3)) {
1764 0 : curInstr->varargs |= VARRETS;
1765 0 : setPolymorphic(curInstr, TYPE_any, TRUE);
1766 : }
1767 48 : if ((ch = currChar(cntxt)) != ',') {
1768 10 : if (ch == ')')
1769 : break;
1770 0 : if (cntxt->backup)
1771 0 : curBlk = NULL;
1772 0 : parseError(cntxt, "',' expected\n");
1773 0 : return curBlk;
1774 : } else {
1775 38 : nextChar(cntxt); /* skip ',' */
1776 : }
1777 38 : skipSpace(cntxt);
1778 38 : ch = currChar(cntxt);
1779 : }
1780 : /* re-arrange the parameters, results first */
1781 10 : max = curInstr->maxarg;
1782 10 : newarg = (short *) GDKmalloc(max * sizeof(curInstr->argv[0]));
1783 10 : if (newarg == NULL) {
1784 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1785 0 : if (cntxt->backup)
1786 0 : curBlk = NULL;
1787 0 : return curBlk;
1788 : }
1789 58 : for (i1 = retc; i1 < curInstr->argc; i1++)
1790 48 : newarg[i2++] = curInstr->argv[i1];
1791 10 : curInstr->retc = curInstr->argc - retc;
1792 22 : for (i1 = 1; i1 < retc; i1++)
1793 12 : newarg[i2++] = curInstr->argv[i1];
1794 10 : curInstr->argc = i2;
1795 20 : for (; i2 < max; i2++)
1796 10 : newarg[i2] = 0;
1797 80 : for (i1 = 0; i1 < max; i1++)
1798 70 : curInstr->argv[i1] = newarg[i1];
1799 10 : GDKfree(newarg);
1800 10 : if (currChar(cntxt) != ')') {
1801 0 : freeInstruction(curInstr);
1802 0 : if (cntxt->backup)
1803 0 : curBlk = NULL;
1804 0 : parseError(cntxt, "')' expected\n");
1805 0 : return curBlk;
1806 : }
1807 10 : nextChar(cntxt); /* skip ')' */
1808 : } else { /* default */
1809 113 : setVarType(curBlk, 0, TYPE_void);
1810 : }
1811 199 : if (curInstr != getInstrPtr(curBlk, 0)) {
1812 0 : freeInstruction(getInstrPtr(curBlk, 0));
1813 0 : putInstrPtr(curBlk, 0, curInstr);
1814 : }
1815 : return curBlk;
1816 : }
1817 :
1818 : static MalBlkPtr
1819 199 : parseFunction(Client cntxt, int kind)
1820 : {
1821 199 : MalBlkPtr curBlk = 0;
1822 :
1823 199 : curBlk = fcnHeader(cntxt, kind);
1824 199 : if (curBlk == NULL)
1825 : return curBlk;
1826 199 : if (MALkeyword(cntxt, "address", 7)) {
1827 : /* TO BE DEPRECATED */
1828 1 : str nme;
1829 1 : int i;
1830 1 : InstrPtr curInstr = getInstrPtr(curBlk, 0);
1831 1 : i = idLength(cntxt);
1832 1 : if (i == 0) {
1833 0 : parseError(cntxt, "<identifier> expected\n");
1834 0 : return 0;
1835 : }
1836 1 : nme = idCopy(cntxt, i);
1837 1 : if (nme == NULL) {
1838 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1839 0 : return 0;
1840 : }
1841 1 : curInstr->fcn = getAddress(getModuleId(curInstr), nme);
1842 1 : GDKfree(nme);
1843 1 : if (curInstr->fcn == NULL) {
1844 1 : parseError(cntxt, "<address> not found\n");
1845 1 : return 0;
1846 : }
1847 0 : skipSpace(cntxt);
1848 : }
1849 : /* block is terminated at the END statement */
1850 198 : helpInfo(cntxt, &curBlk->help);
1851 198 : return curBlk;
1852 : }
1853 :
1854 : /*
1855 : * Functions and factories end with a labeled end-statement.
1856 : * The routine below checks for misalignment of the closing statements.
1857 : * Any instruction parsed after the function block is considered an error.
1858 : */
1859 : static int
1860 292 : parseEnd(Client cntxt)
1861 : {
1862 292 : Symbol curPrg = 0;
1863 292 : size_t l;
1864 292 : InstrPtr sig;
1865 292 : str errors = MAL_SUCCEED, msg = MAL_SUCCEED;
1866 :
1867 292 : if (MALkeyword(cntxt, "end", 3)) {
1868 204 : curPrg = cntxt->curprg;
1869 204 : l = idLength(cntxt);
1870 204 : if (l == 0)
1871 37 : l = operatorLength(cntxt);
1872 204 : sig = getInstrPtr(cntxt->curprg->def, 0);
1873 204 : if (strncmp(CURRENT(cntxt), getModuleId(sig), l) == 0) {
1874 37 : advance(cntxt, l);
1875 37 : skipSpace(cntxt);
1876 37 : if (currChar(cntxt) == '.')
1877 0 : nextChar(cntxt);
1878 37 : skipSpace(cntxt);
1879 37 : l = idLength(cntxt);
1880 37 : if (l == 0)
1881 37 : l = operatorLength(cntxt);
1882 : }
1883 : /* parse fcn */
1884 204 : if ((l == strlen(curPrg->name) &&
1885 204 : strncmp(CURRENT(cntxt), curPrg->name, l) == 0) || l == 0)
1886 195 : advance(cntxt, l);
1887 : else
1888 9 : parseError(cntxt, "non matching end label\n");
1889 204 : pushEndInstruction(cntxt->curprg->def);
1890 204 : cntxt->blkmode = 0;
1891 204 : if (strcmp(getModuleId(sig), "user") == 0)
1892 199 : insertSymbol(cntxt->usermodule, cntxt->curprg);
1893 : else
1894 5 : insertSymbol(getModule(getModuleId(sig)), cntxt->curprg);
1895 :
1896 204 : if (cntxt->curprg->def->errors) {
1897 11 : errors = cntxt->curprg->def->errors;
1898 11 : cntxt->curprg->def->errors = 0;
1899 : }
1900 : // check for newly identified errors
1901 204 : msg = chkProgram(cntxt->usermodule, cntxt->curprg->def);
1902 204 : if (errors == NULL)
1903 : errors = msg;
1904 : else
1905 11 : freeException(msg);
1906 204 : if (errors == NULL) {
1907 168 : errors = cntxt->curprg->def->errors;
1908 168 : cntxt->curprg->def->errors = 0;
1909 36 : } else if (cntxt->curprg->def->errors) {
1910 : //collect all errors for reporting
1911 0 : str new = GDKmalloc(strlen(errors) +
1912 : strlen(cntxt->curprg->def->errors) + 16);
1913 0 : if (new) {
1914 0 : strcpy(new, errors);
1915 0 : if (new[strlen(new) - 1] != '\n')
1916 0 : strcat(new, "\n");
1917 0 : strcat(new, "!");
1918 0 : strcat(new, cntxt->curprg->def->errors);
1919 :
1920 0 : freeException(errors);
1921 0 : freeException(cntxt->curprg->def->errors);
1922 :
1923 0 : cntxt->curprg->def->errors = 0;
1924 0 : errors = new;
1925 : }
1926 : }
1927 :
1928 204 : if (cntxt->backup) {
1929 194 : cntxt->curprg = cntxt->backup;
1930 194 : cntxt->backup = 0;
1931 : } else {
1932 10 : str msg;
1933 10 : if ((msg = MSinitClientPrg(cntxt, cntxt->curmodule->name,
1934 : "main")) != MAL_SUCCEED) {
1935 0 : if (errors) {
1936 0 : str new = GDKmalloc(strlen(errors) + strlen(msg) + 3);
1937 0 : if (new) {
1938 0 : strcpy(new, msg);
1939 0 : if (new[strlen(new) - 1] != '\n')
1940 0 : strcat(new, "\n");
1941 0 : strcat(new, errors);
1942 0 : freeException(errors);
1943 0 : cntxt->curprg->def->errors = new;
1944 : } else {
1945 0 : cntxt->curprg->def->errors = errors;
1946 : }
1947 0 : freeException(msg);
1948 : } else {
1949 0 : cntxt->curprg->def->errors = msg;
1950 : }
1951 0 : return 1;
1952 : }
1953 : }
1954 : // pass collected errors to context
1955 204 : assert(cntxt->curprg->def->errors == NULL);
1956 204 : cntxt->curprg->def->errors = errors;
1957 204 : return 1;
1958 : }
1959 : return 0;
1960 : }
1961 :
1962 : /*
1963 : * Most instructions are simple assignments, possibly
1964 : * modified with a barrier/catch tag.
1965 : *
1966 : * The basic types are also predefined as a variable.
1967 : * This makes it easier to communicate types to MAL patterns.
1968 : */
1969 :
1970 : #define GETvariable(FREE) \
1971 : if ((varid = findVariableLength(curBlk, CURRENT(cntxt), l)) == -1) { \
1972 : varid = newVariable(curBlk, CURRENT(cntxt), l, TYPE_any); \
1973 : advance(cntxt, l); \
1974 : if (varid < 0) { FREE; return; } \
1975 : } else \
1976 : advance(cntxt, l);
1977 :
1978 : /* The parameter of parseArguments is the return value of the enclosing function. */
1979 : static int
1980 8993 : parseArguments(Client cntxt, MalBlkPtr curBlk, InstrPtr *curInstr)
1981 : {
1982 24641 : while (currChar(cntxt) != ')') {
1983 15650 : switch (term(cntxt, curBlk, curInstr, 0)) {
1984 : case 0:
1985 15648 : break;
1986 : case 2:
1987 : return 2;
1988 : case 3:
1989 : return 3;
1990 0 : case 4:
1991 0 : parseError(cntxt, "Argument type overwrites previous definition\n");
1992 0 : return 0;
1993 0 : default:
1994 0 : parseError(cntxt, "<factor> expected\n");
1995 0 : return 1;
1996 : }
1997 15648 : if (currChar(cntxt) == ',')
1998 7324 : advance(cntxt, 1);
1999 8324 : else if (currChar(cntxt) != ')') {
2000 0 : parseError(cntxt, "',' expected\n");
2001 0 : cntxt->yycur--; /* keep it */
2002 0 : break;
2003 : }
2004 : }
2005 8991 : if (currChar(cntxt) == ')')
2006 8991 : advance(cntxt, 1);
2007 : return 0;
2008 : }
2009 :
2010 : static void
2011 11768 : parseAssign(Client cntxt, int cntrl)
2012 : {
2013 11768 : InstrPtr curInstr;
2014 11768 : MalBlkPtr curBlk;
2015 11768 : Symbol curPrg;
2016 11768 : int i = 0, l, type = TYPE_any, varid = -1;
2017 11768 : const char *arg = 0;
2018 11768 : ValRecord cst;
2019 :
2020 11768 : curPrg = cntxt->curprg;
2021 11768 : curBlk = curPrg->def;
2022 11768 : if ((curInstr = newInstruction(curBlk, NULL, NULL)) == NULL) {
2023 13 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
2024 2383 : return;
2025 : }
2026 :
2027 11769 : if (cntrl) {
2028 592 : curInstr->token = ASSIGNsymbol;
2029 592 : curInstr->barrier = cntrl;
2030 : }
2031 :
2032 : /* start the parsing by recognition of the lhs of an assignment */
2033 11769 : if (currChar(cntxt) == '(') {
2034 : /* parsing multi-assignment */
2035 219 : advance(cntxt, 1);
2036 219 : curInstr->argc = 0; /*reset to handle pushArg correctly !! */
2037 219 : curInstr->retc = 0;
2038 1842 : while (currChar(cntxt) != ')' && currChar(cntxt)) {
2039 1623 : l = idLength(cntxt);
2040 1623 : i = cstToken(cntxt, &cst);
2041 1623 : if (l == 0 || i) {
2042 1 : parseError(cntxt, "<identifier> or <literal> expected\n");
2043 1 : freeInstruction(curInstr);
2044 1 : return;
2045 : }
2046 1622 : GETvariable(freeInstruction(curInstr));
2047 1622 : if (currChar(cntxt) == ':') {
2048 31 : type = typeElm(cntxt, getVarType(curBlk, varid));
2049 31 : if (type < 0)
2050 0 : goto part3;
2051 31 : setPolymorphic(curInstr, type, FALSE);
2052 31 : setVarType(curBlk, varid, type);
2053 : }
2054 1622 : curInstr = pushArgument(curBlk, curInstr, varid);
2055 1622 : curInstr->retc++;
2056 1622 : if (currChar(cntxt) == ')')
2057 : break;
2058 1404 : if (currChar(cntxt) == ',')
2059 1404 : keyphrase1(cntxt, ",");
2060 : }
2061 218 : advance(cntxt, 1); /* skip ')' */
2062 218 : if (curInstr->retc == 0) {
2063 : /* add dummy variable */
2064 0 : curInstr = pushArgument(curBlk, curInstr,
2065 : newTmpVariable(curBlk, TYPE_any));
2066 0 : curInstr->retc++;
2067 : }
2068 : } else {
2069 : /* are we dealing with a simple assignment? */
2070 11550 : l = idLength(cntxt);
2071 11544 : i = cstToken(cntxt, &cst);
2072 11534 : if (l == 0 || i) {
2073 : /* we haven't seen a target variable */
2074 : /* flow of control statements may end here. */
2075 : /* shouldn't allow for nameless controls todo */
2076 6 : if (i && cst.vtype == TYPE_str)
2077 0 : GDKfree(cst.val.sval);
2078 6 : if (cntrl == LEAVEsymbol || cntrl == REDOsymbol ||
2079 6 : cntrl == RETURNsymbol || cntrl == EXITsymbol) {
2080 4 : curInstr->argv[0] = getBarrierEnvelop(curBlk);
2081 4 : if (currChar(cntxt) != ';') {
2082 0 : freeInstruction(curInstr);
2083 0 : parseError(cntxt,
2084 : "<identifier> or <literal> expected in control statement\n");
2085 0 : return;
2086 : }
2087 4 : pushInstruction(curBlk, curInstr);
2088 4 : return;
2089 : }
2090 2 : getArg(curInstr, 0) = newTmpVariable(curBlk, TYPE_any);
2091 2 : freeInstruction(curInstr);
2092 2 : parseError(cntxt, "<identifier> or <literal> expected\n");
2093 2 : return;
2094 : }
2095 : /* Check if we are dealing with module.fcn call */
2096 11528 : if (CURRENT(cntxt)[l] == '.' || CURRENT(cntxt)[l] == '(') {
2097 4374 : curInstr->argv[0] = newTmpVariable(curBlk, TYPE_any);
2098 4374 : goto FCNcallparse;
2099 : }
2100 :
2101 : /* Get target variable details */
2102 7154 : GETvariable(freeInstruction(curInstr));
2103 7161 : if (!(currChar(cntxt) == ':' && CURRENT(cntxt)[1] == '=')) {
2104 445 : curInstr->argv[0] = varid;
2105 445 : if (currChar(cntxt) == ':') {
2106 164 : type = typeElm(cntxt, getVarType(curBlk, varid));
2107 164 : if (type < 0)
2108 0 : goto part3;
2109 164 : setPolymorphic(curInstr, type, FALSE);
2110 164 : setVarType(curBlk, varid, type);
2111 : }
2112 : }
2113 7161 : curInstr->argv[0] = varid;
2114 : }
2115 : /* look for assignment operator */
2116 7379 : if (!keyphrase2(cntxt, ":=")) {
2117 : /* no assignment !! a control variable is allowed */
2118 : /* for the case RETURN X, we normalize it to include the function arguments */
2119 326 : if (cntrl == RETURNsymbol) {
2120 31 : int e;
2121 31 : InstrPtr sig = getInstrPtr(curBlk, 0);
2122 31 : curInstr->retc = 0;
2123 64 : for (e = 0; e < sig->retc; e++)
2124 33 : curInstr = pushReturn(curBlk, curInstr, getArg(sig, e));
2125 : }
2126 :
2127 326 : goto part3;
2128 : }
2129 7061 : if (currChar(cntxt) == '(') {
2130 : /* parse multi assignment */
2131 9 : advance(cntxt, 1);
2132 9 : switch (parseArguments(cntxt, curBlk, &curInstr)) {
2133 0 : case 2:
2134 0 : goto part2;
2135 9 : default:
2136 : case 3:
2137 9 : goto part3;
2138 : }
2139 : /* unreachable */
2140 : }
2141 : /*
2142 : * We have so far the LHS part of an assignment. The remainder is
2143 : * either a simple term expression, a multi assignent, or the start
2144 : * of a function call.
2145 : */
2146 7052 : FCNcallparse:
2147 11426 : if ((l = idLength(cntxt)) && CURRENT(cntxt)[l] == '(') {
2148 : /* parseError(cntxt,"<module> expected\n"); */
2149 62 : setModuleId(curInstr, cntxt->curmodule->name);
2150 62 : i = l;
2151 62 : goto FCNcallparse2;
2152 11358 : } else if ((l = idLength(cntxt)) && CURRENT(cntxt)[l] == '.') {
2153 : /* continue with parsing a function/operator call */
2154 8922 : arg = putNameLen(CURRENT(cntxt), l);
2155 8924 : if (arg == NULL) {
2156 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
2157 0 : freeInstruction(curInstr);
2158 0 : return;
2159 : }
2160 8924 : advance(cntxt, l + 1); /* skip '.' too */
2161 8924 : setModuleId(curInstr, arg);
2162 8924 : i = idLength(cntxt);
2163 8924 : if (i == 0)
2164 68 : i = operatorLength(cntxt);
2165 8856 : FCNcallparse2:
2166 130 : if (i) {
2167 8986 : setFunctionId(curInstr, putNameLen(((char *) CURRENT(cntxt)), i));
2168 8986 : if (getFunctionId(curInstr) == NULL) {
2169 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
2170 0 : freeInstruction(curInstr);
2171 0 : return;
2172 : }
2173 8986 : advance(cntxt, i);
2174 : } else {
2175 0 : parseError(cntxt, "<functionname> expected\n");
2176 0 : freeInstruction(curInstr);
2177 0 : return;
2178 : }
2179 8986 : skipSpace(cntxt);
2180 8986 : if (currChar(cntxt) != '(') {
2181 2 : parseError(cntxt, "'(' expected\n");
2182 2 : freeInstruction(curInstr);
2183 2 : return;
2184 : }
2185 8984 : advance(cntxt, 1);
2186 8984 : switch (parseArguments(cntxt, curBlk, &curInstr)) {
2187 0 : case 2:
2188 0 : goto part2;
2189 8983 : default:
2190 : case 3:
2191 8983 : goto part3;
2192 : }
2193 : /* unreachable */
2194 : }
2195 : /* Handle the ordinary assignments and expressions */
2196 2436 : switch (term(cntxt, curBlk, &curInstr, 2)) {
2197 2257 : case 2:
2198 2257 : goto part2;
2199 3 : case 3:
2200 3 : goto part3;
2201 : }
2202 : part2: /* consume <operator><term> part of expression */
2203 2426 : if ((i = operatorLength(cntxt))) {
2204 : /* simple arithmetic operator expression */
2205 122 : setFunctionId(curInstr, putNameLen(((char *) CURRENT(cntxt)), i));
2206 122 : if (getFunctionId(curInstr) == NULL) {
2207 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
2208 0 : freeInstruction(curInstr);
2209 0 : return;
2210 : }
2211 122 : advance(cntxt, i);
2212 122 : curInstr->modname = putName("calc");
2213 122 : if (curInstr->modname == NULL) {
2214 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
2215 0 : freeInstruction(curInstr);
2216 0 : return;
2217 : }
2218 122 : if ((l = idLength(cntxt))
2219 54 : && !(l == 3 && strncmp(CURRENT(cntxt), "nil", 3) == 0)) {
2220 54 : GETvariable(freeInstruction(curInstr));
2221 54 : curInstr = pushArgument(curBlk, curInstr, varid);
2222 54 : goto part3;
2223 : }
2224 68 : switch (term(cntxt, curBlk, &curInstr, 3)) {
2225 0 : case 2:
2226 0 : goto part2;
2227 67 : case 3:
2228 67 : goto part3;
2229 : }
2230 1 : parseError(cntxt, "<term> expected\n");
2231 1 : freeInstruction(curInstr);
2232 1 : return;
2233 : } else {
2234 2311 : skipSpace(cntxt);
2235 2306 : if (currChar(cntxt) == '(') {
2236 0 : parseError(cntxt, "module name missing\n");
2237 0 : freeInstruction(curInstr);
2238 0 : return;
2239 2306 : } else if (currChar(cntxt) != ';' && currChar(cntxt) != '#') {
2240 1 : parseError(cntxt, "operator expected\n");
2241 1 : freeInstruction(curInstr);
2242 1 : return;
2243 : }
2244 2305 : pushInstruction(curBlk, curInstr);
2245 2305 : return;
2246 : }
2247 9442 : part3:
2248 9442 : skipSpace(cntxt);
2249 9441 : if (currChar(cntxt) != ';') {
2250 39 : parseError(cntxt, "';' expected\n");
2251 39 : skipToEnd(cntxt);
2252 39 : freeInstruction(curInstr);
2253 39 : return;
2254 : }
2255 9402 : skipToEnd(cntxt);
2256 9402 : if (cntrl == RETURNsymbol
2257 36 : && !(curInstr->token == ASSIGNsymbol || getModuleId(curInstr) != 0)) {
2258 0 : parseError(cntxt, "return assignment expected\n");
2259 0 : freeInstruction(curInstr);
2260 0 : return;
2261 : }
2262 9402 : pushInstruction(curBlk, curInstr);
2263 : }
2264 :
2265 : void
2266 10482 : parseMAL(Client cntxt, Symbol curPrg, int skipcomments, int lines,
2267 : MALfcn address)
2268 : {
2269 10482 : int cntrl = 0;
2270 : /*Symbol curPrg= cntxt->curprg; */
2271 10482 : char c;
2272 10482 : int inlineProp = 0, unsafeProp = 0;
2273 :
2274 10482 : (void) curPrg;
2275 10482 : echoInput(cntxt);
2276 : /* here the work takes place */
2277 41955 : while ((c = currChar(cntxt)) && lines > 0) {
2278 31463 : switch (c) {
2279 13888 : case '\n':
2280 : case '\r':
2281 : case '\f':
2282 13888 : lines -= c == '\n';
2283 13888 : nextChar(cntxt);
2284 13888 : echoInput(cntxt);
2285 13888 : continue;
2286 5341 : case ';':
2287 : case '\t':
2288 : case ' ':
2289 5341 : nextChar(cntxt);
2290 5341 : continue;
2291 13 : case '#':
2292 : { /* keep the full line comments */
2293 13 : char start[256], *e = start, c;
2294 13 : MalBlkPtr curBlk = cntxt->curprg->def;
2295 13 : InstrPtr curInstr;
2296 :
2297 13 : *e = 0;
2298 13 : nextChar(cntxt);
2299 228 : while ((c = currChar(cntxt))) {
2300 228 : if (e < start + 256 - 1)
2301 228 : *e++ = c;
2302 228 : nextChar(cntxt);
2303 228 : if (c == '\n' || c == '\r') {
2304 13 : *e = 0;
2305 13 : if (e > start)
2306 13 : e--;
2307 : /* prevChar(cntxt); */
2308 : break;
2309 : }
2310 : }
2311 13 : if (e > start)
2312 13 : *e = 0;
2313 13 : if (!skipcomments && e > start && curBlk->stop > 0) {
2314 13 : ValRecord cst;
2315 13 : if ((curInstr = newInstruction(curBlk, NULL, NULL)) == NULL) {
2316 1 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
2317 1 : continue;
2318 : }
2319 12 : curInstr->token = REMsymbol;
2320 12 : curInstr->barrier = 0;
2321 12 : if (VALinit(&cst, TYPE_str, start) == NULL) {
2322 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
2323 0 : freeInstruction(curInstr);
2324 0 : continue;
2325 : }
2326 12 : int cstidx = defConstant(curBlk, TYPE_str, &cst);
2327 12 : if (cstidx < 0) {
2328 0 : freeInstruction(curInstr);
2329 0 : continue;
2330 : }
2331 12 : getArg(curInstr, 0) = cstidx;
2332 12 : setVarDisabled(curBlk, getArg(curInstr, 0));
2333 12 : pushInstruction(curBlk, curInstr);
2334 : }
2335 12 : echoInput(cntxt);
2336 : }
2337 12 : continue;
2338 97 : case 'A':
2339 : case 'a':
2340 97 : if (MALkeyword(cntxt, "atom", 4) && parseAtom(cntxt) == 0)
2341 : break;
2342 92 : goto allLeft;
2343 1213 : case 'b':
2344 : case 'B':
2345 1213 : if (MALkeyword(cntxt, "barrier", 7)) {
2346 165 : cntxt->blkmode++;
2347 165 : cntrl = BARRIERsymbol;
2348 : }
2349 1213 : goto allLeft;
2350 469 : case 'C':
2351 : case 'c':
2352 469 : if (MALkeyword(cntxt, "command", 7)) {
2353 5 : Symbol p = parseCommandPattern(cntxt, COMMANDsymbol, address);
2354 5 : if (p) {
2355 5 : p->func->unsafe = unsafeProp;
2356 : }
2357 5 : if (inlineProp)
2358 0 : parseError(cntxt, "<identifier> expected\n");
2359 5 : inlineProp = 0;
2360 5 : unsafeProp = 0;
2361 5 : continue;
2362 : }
2363 464 : if (MALkeyword(cntxt, "catch", 5)) {
2364 24 : cntxt->blkmode++;
2365 24 : cntrl = CATCHsymbol;
2366 24 : goto allLeft;
2367 : }
2368 440 : goto allLeft;
2369 487 : case 'E':
2370 : case 'e':
2371 487 : if (MALkeyword(cntxt, "exit", 4)) {
2372 195 : if (cntxt->blkmode > 0)
2373 189 : cntxt->blkmode--;
2374 : cntrl = EXITsymbol;
2375 292 : } else if (parseEnd(cntxt)) {
2376 : break;
2377 : }
2378 283 : goto allLeft;
2379 333 : case 'F':
2380 : case 'f':
2381 333 : if (MALkeyword(cntxt, "function", 8)) {
2382 199 : MalBlkPtr p;
2383 199 : cntxt->blkmode++;
2384 199 : if ((p = parseFunction(cntxt, FUNCTIONsymbol))) {
2385 198 : p->unsafeProp = unsafeProp;
2386 198 : cntxt->curprg->def->inlineProp = inlineProp;
2387 198 : cntxt->curprg->def->unsafeProp = unsafeProp;
2388 198 : inlineProp = 0;
2389 198 : unsafeProp = 0;
2390 198 : break;
2391 : }
2392 : }
2393 135 : goto allLeft;
2394 1796 : case 'I':
2395 : case 'i':
2396 1796 : if (MALkeyword(cntxt, "inline", 6)) {
2397 23 : inlineProp = 1;
2398 23 : skipSpace(cntxt);
2399 23 : continue;
2400 1773 : } else if (MALkeyword(cntxt, "include", 7)) {
2401 2 : parseInclude(cntxt);
2402 2 : break;
2403 : }
2404 1771 : goto allLeft;
2405 81 : case 'L':
2406 : case 'l':
2407 81 : if (MALkeyword(cntxt, "leave", 5))
2408 40 : cntrl = LEAVEsymbol;
2409 81 : goto allLeft;
2410 79 : case 'M':
2411 : case 'm':
2412 79 : if (MALkeyword(cntxt, "module", 6) && parseModule(cntxt) == 0)
2413 : break;
2414 76 : goto allLeft;
2415 63 : case 'P':
2416 : case 'p':
2417 63 : if (MALkeyword(cntxt, "pattern", 7)) {
2418 8 : if (inlineProp)
2419 0 : parseError(cntxt, "parseError:INLINE ignored\n");
2420 8 : Symbol p = parseCommandPattern(cntxt, PATTERNsymbol, address);
2421 8 : if (p) {
2422 8 : p->func->unsafe = unsafeProp;
2423 : }
2424 8 : inlineProp = 0;
2425 8 : unsafeProp = 0;
2426 8 : continue;
2427 : }
2428 55 : goto allLeft;
2429 5760 : case 'R':
2430 : case 'r':
2431 5760 : if (MALkeyword(cntxt, "redo", 4)) {
2432 85 : cntrl = REDOsymbol;
2433 85 : goto allLeft;
2434 : }
2435 5680 : if (MALkeyword(cntxt, "raise", 5)) {
2436 7 : cntrl = RAISEsymbol;
2437 7 : goto allLeft;
2438 : }
2439 5673 : if (MALkeyword(cntxt, "return", 6)) {
2440 76 : cntrl = RETURNsymbol;
2441 : }
2442 5677 : goto allLeft;
2443 51 : case 'U':
2444 : case 'u':
2445 51 : if (MALkeyword(cntxt, "unsafe", 6)) {
2446 0 : unsafeProp = 1;
2447 0 : skipSpace(cntxt);
2448 0 : continue;
2449 : }
2450 : /* fall through */
2451 : default:
2452 51 : allLeft:
2453 11782 : parseAssign(cntxt, cntrl);
2454 11782 : cntrl = 0;
2455 : }
2456 : }
2457 10493 : skipSpace(cntxt);
2458 10494 : }
|