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 9482 : skipToEnd(Client cntxt)
67 : {
68 9482 : char c;
69 9852 : while ((c = *CURRENT(cntxt)) != ';' && c && c != '\n')
70 370 : nextChar(cntxt);
71 9482 : if (c && c != '\n')
72 9396 : nextChar(cntxt);
73 9482 : }
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 24182 : echoInput(Client cntxt)
141 : {
142 24182 : char *c = CURRENT(cntxt);
143 24182 : 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 24182 : }
151 :
152 : static inline void
153 215708 : skipSpace(Client cntxt)
154 : {
155 215708 : char *s = &currChar(cntxt);
156 253540 : for (;;) {
157 234624 : switch (*s++) {
158 18916 : case ' ':
159 : case '\t':
160 : case '\n':
161 : case '\r':
162 18916 : nextChar(cntxt);
163 18916 : break;
164 : default:
165 215708 : return;
166 : }
167 : }
168 : }
169 :
170 : static inline void
171 86975 : advance(Client cntxt, size_t length)
172 : {
173 86975 : cntxt->yycur += length;
174 86975 : skipSpace(cntxt);
175 27683 : }
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 53052 : idLength(Client cntxt)
331 : {
332 53052 : str s, t;
333 53052 : int len = 0;
334 :
335 53052 : skipSpace(cntxt);
336 53054 : s = CURRENT(cntxt);
337 53054 : t = s;
338 :
339 53054 : if (!idCharacter[(unsigned char) (*s)])
340 : return 0;
341 : /* avoid a clash with old temporaries */
342 48223 : if (s[0] == TMPMARKER)
343 65 : s[0] = REFMARKER;
344 : /* prepare escape of temporary names */
345 48223 : s++;
346 317714 : while (len < IDLENGTH && idCharacter2[(unsigned char) (*s)]) {
347 269491 : s++;
348 269491 : len++;
349 : }
350 48223 : if (len == IDLENGTH)
351 : // skip remainder
352 0 : while (idCharacter2[(unsigned char) (*s)])
353 0 : s++;
354 48223 : return (int) (s - t);
355 : }
356 :
357 : /* Simple type identifiers can not be marked with a type variable. */
358 : static size_t
359 5088 : typeidLength(Client cntxt)
360 : {
361 5088 : size_t l;
362 5088 : char id[IDLENGTH], *t = id;
363 5088 : str s;
364 5088 : skipSpace(cntxt);
365 5092 : s = CURRENT(cntxt);
366 :
367 5092 : if (!idCharacter[(unsigned char) (*s)])
368 : return 0;
369 5092 : l = 1;
370 5092 : *t++ = *s++;
371 5092 : while (l < IDLENGTH
372 15435 : && (idCharacter[(unsigned char) (*s)]
373 5126 : || isdigit((unsigned char) *s))) {
374 10343 : *t++ = *s++;
375 10343 : l++;
376 : }
377 : /* recognize the special type variables {any, any_<nr>} */
378 5092 : if (strncmp(id, "any", 3) == 0)
379 : return 3;
380 5052 : 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 24784 : MALlookahead(Client cntxt, str kw, int length)
400 : {
401 24784 : int i;
402 :
403 : /* avoid double test or use lowercase only. */
404 24784 : if (currChar(cntxt) == *kw &&
405 24058 : 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 captialized versions */
411 50737 : for (i = 0; i < length; i++)
412 50737 : 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 24784 : MALkeyword(Client cntxt, str kw, int length)
423 : {
424 24784 : skipSpace(cntxt);
425 24784 : 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 1531 : keyphrase1(Client cntxt, str kw)
439 : {
440 1531 : skipSpace(cntxt);
441 1531 : if (currChar(cntxt) == *kw) {
442 1415 : advance(cntxt, 1);
443 1415 : return 1;
444 : }
445 : return 0;
446 : }
447 :
448 : static inline int
449 7315 : keyphrase2(Client cntxt, str kw)
450 : {
451 7315 : skipSpace(cntxt);
452 7315 : if (CURRENT(cntxt)[0] == kw[0] && CURRENT(cntxt)[1] == kw[1]) {
453 6989 : advance(cntxt, 2);
454 6989 : 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 2756 : stringLength(Client cntxt)
468 : {
469 2756 : int l = 0;
470 2756 : int quote = 0;
471 2756 : str s;
472 2756 : skipSpace(cntxt);
473 2756 : s = CURRENT(cntxt);
474 :
475 2756 : if (*s != '"')
476 : return 0;
477 164920 : for (s++; *s; l++, s++) {
478 164919 : if (quote) {
479 : quote = 0;
480 : } else {
481 142832 : if (*s == '"')
482 : break;
483 140076 : quote = *s == '\\';
484 : }
485 : }
486 2757 : 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 2756 : strCopy(Client cntxt, int length)
495 : {
496 2756 : str s;
497 2756 : int i;
498 :
499 2756 : i = length < 4 ? 4 : length;
500 2756 : s = GDKmalloc(i);
501 2758 : if (s == 0)
502 : return NULL;
503 2758 : memcpy(s, CURRENT(cntxt) + 1, (size_t) (length - 2));
504 2758 : s[length - 2] = 0;
505 2758 : mal_unquote(s);
506 2758 : 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 2746 : operatorLength(Client cntxt)
516 : {
517 2746 : int l = 0;
518 2746 : str s;
519 :
520 2746 : skipSpace(cntxt);
521 2976 : for (s = CURRENT(cntxt); *s; s++) {
522 2957 : if (opCharacter[(unsigned char) (*s)])
523 227 : l++;
524 : else
525 2730 : 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 31142 : cstToken(Client cntxt, ValPtr cst)
537 : {
538 31142 : int i = 0;
539 31142 : str s = CURRENT(cntxt);
540 :
541 31142 : *cst = (ValRecord) {
542 : .vtype = TYPE_int,
543 : .val.lval = 0,
544 : .bat = false,
545 : };
546 31142 : switch (*s) {
547 : case '{':
548 : case '[':
549 : /* JSON Literal */
550 : break;
551 2754 : case '"':
552 2754 : i = stringLength(cntxt);
553 2754 : VALset(cst, TYPE_str, strCopy(cntxt, i));
554 2754 : return i;
555 21 : case '-':
556 21 : i++;
557 21 : s++;
558 : /* fall through */
559 1584 : case '0':
560 1584 : 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 9901 : while (isdigit((unsigned char) *s)) {
581 6051 : i++;
582 6051 : s++;
583 : }
584 :
585 : /* fall through */
586 : case '.':
587 3850 : 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 3850 : 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 3850 : 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 3850 : 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 3850 : 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 3773 : 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 3769 : 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 3767 : handleInts:
696 3767 : assert(cst->vtype != TYPE_lng);
697 : #ifdef HAVE_HGE
698 3767 : assert(cst->vtype != TYPE_hge);
699 : #endif
700 3767 : if (cst->vtype == TYPE_int) {
701 : #ifdef HAVE_HGE
702 3684 : size_t len = sizeof(hge);
703 3684 : hge l, *pval = &l;
704 3684 : if (hgeFromStr(CURRENT(cntxt), &len, &pval, false) < 0)
705 0 : l = hge_nil;
706 :
707 3684 : if ((hge) GDK_int_min <= l && l <= (hge) GDK_int_max) {
708 3677 : cst->vtype = TYPE_int;
709 3677 : 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 2156 : case 'n':
753 2156 : if (strncmp(s, "nil", 3) == 0 && !isalnum((unsigned char) *(s + 3)) &&
754 : *(s + 3) != '_') {
755 2089 : cst->vtype = TYPE_void;
756 2089 : cst->len = 0;
757 2089 : cst->val.oval = oid_nil;
758 2089 : 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 5088 : typeAlias(Client cntxt, int tpe)
780 : {
781 5088 : int t;
782 :
783 5088 : 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 5088 : simpleTypeId(Client cntxt)
804 : {
805 5088 : int tpe;
806 5088 : size_t l;
807 :
808 5088 : nextChar(cntxt);
809 5088 : l = typeidLength(cntxt);
810 5093 : if (l == 0) {
811 0 : parseError(cntxt, "Type identifier expected\n");
812 0 : cntxt->yycur--; /* keep it */
813 0 : return -1;
814 : }
815 5093 : if (l == 3 && CURRENT(cntxt)[0] == 'b' && CURRENT(cntxt)[1] == 'a' && CURRENT(cntxt)[2] == 't')
816 : tpe = newBatType(TYPE_any);
817 : else
818 5093 : tpe = getAtomIndex(CURRENT(cntxt), l, -1);
819 5086 : 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 5086 : advance(cntxt, l);
825 5086 : return tpe;
826 : }
827 :
828 : static int
829 5234 : parseTypeId(Client cntxt)
830 : {
831 5234 : int i = TYPE_any, kt = 0;
832 5234 : char *s = CURRENT(cntxt);
833 5234 : int tt;
834 :
835 5234 : 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 4667 : if (currChar(cntxt) == ':') {
880 4667 : tt = simpleTypeId(cntxt);
881 4668 : kt = typeAlias(cntxt, tt);
882 4667 : if (kt < 0)
883 : return kt;
884 4667 : if (kt > 0)
885 15 : setTypeIndex(tt, kt);
886 4667 : return tt;
887 : }
888 0 : parseError(cntxt, "<type identifier> expected\n");
889 0 : return -1;
890 : }
891 :
892 : static inline int
893 9569 : typeElm(Client cntxt, int def)
894 : {
895 9569 : if (currChar(cntxt) != ':')
896 : return def; /* no type qualifier */
897 5231 : 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 critial 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 temporarilly 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 18056 : term(Client cntxt, MalBlkPtr curBlk, InstrPtr *curInstr, int ret)
1025 : {
1026 18056 : int i, idx, free = 1;
1027 18056 : ValRecord cst;
1028 18056 : int cstidx = -1;
1029 18056 : malType tpe = TYPE_any;
1030 :
1031 18056 : if ((i = cstToken(cntxt, &cst))) {
1032 10391 : advance(cntxt, i);
1033 10391 : 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 10391 : cstidx = fndConstant(curBlk, &cst, MAL_VAR_WINDOW);
1040 10390 : if (cstidx >= 0) {
1041 :
1042 1724 : if (currChar(cntxt) == ':') {
1043 164 : tpe = typeElm(cntxt, getVarType(curBlk, cstidx));
1044 164 : if (tpe < 0)
1045 : return 3;
1046 164 : if (tpe != getVarType(curBlk, cstidx)) {
1047 3 : cstidx = defConstant(curBlk, tpe, &cst);
1048 3 : if (cstidx < 0)
1049 : return 3;
1050 3 : setPolymorphic(*curInstr, tpe, FALSE);
1051 3 : free = 0;
1052 : }
1053 1560 : } else if (cst.vtype != getVarType(curBlk, cstidx)) {
1054 0 : cstidx = defConstant(curBlk, cst.vtype, &cst);
1055 0 : if (cstidx < 0)
1056 : return 3;
1057 0 : setPolymorphic(*curInstr, cst.vtype, FALSE);
1058 0 : free = 0;
1059 : }
1060 : /* protect against leaks coming from constant reuse */
1061 1724 : if (free && ATOMextern(cst.vtype) && cst.val.pval)
1062 45 : VALclear(&cst);
1063 1724 : *curInstr = pushArgument(curBlk, *curInstr, cstidx);
1064 1724 : return ret;
1065 : } else {
1066 : /* add a new constant literal, the :type could be erroneously be a coltype */
1067 8666 : tpe = typeElm(cntxt, cst.vtype);
1068 8665 : if (tpe < 0)
1069 : return 3;
1070 8665 : cst.bat = isaBatType(tpe);
1071 8665 : cstidx = defConstant(curBlk, tpe, &cst);
1072 8664 : if (cstidx < 0)
1073 : return 3;
1074 8660 : setPolymorphic(*curInstr, tpe, FALSE);
1075 8659 : *curInstr = pushArgument(curBlk, *curInstr, cstidx);
1076 8660 : return ret;
1077 : }
1078 7666 : } else if ((i = idLength(cntxt))) {
1079 7390 : if ((idx = findVariableLength(curBlk, CURRENT(cntxt), i)) == -1) {
1080 5 : idx = newVariable(curBlk, CURRENT(cntxt), i, TYPE_any);
1081 5 : advance(cntxt, i);
1082 5 : if (idx < 0)
1083 : return 0;
1084 : } else {
1085 7385 : advance(cntxt, i);
1086 : }
1087 7390 : if (currChar(cntxt) == ':') {
1088 : /* skip the type description */
1089 2 : tpe = typeElm(cntxt, TYPE_any);
1090 2 : if (getVarType(curBlk, idx) == TYPE_any)
1091 1 : setVarType(curBlk, idx, tpe);
1092 1 : else if (getVarType(curBlk, idx) != tpe) {
1093 : /* non-matching types */
1094 : return 4;
1095 : }
1096 : }
1097 7390 : *curInstr = pushArgument(curBlk, *curInstr, idx);
1098 276 : } else if (currChar(cntxt) == ':') {
1099 274 : tpe = typeElm(cntxt, TYPE_any);
1100 274 : if (tpe < 0)
1101 : return 3;
1102 274 : setPolymorphic(*curInstr, tpe, FALSE);
1103 274 : idx = newTypeVariable(curBlk, tpe);
1104 274 : *curInstr = pushArgument(curBlk, *curInstr, idx);
1105 274 : return ret;
1106 : }
1107 : return 0;
1108 : }
1109 :
1110 : static int
1111 5 : parseAtom(Client cntxt)
1112 : {
1113 5 : const char *modnme = 0;
1114 5 : int l, tpe;
1115 5 : char *nxt = CURRENT(cntxt);
1116 :
1117 5 : if ((l = idLength(cntxt)) <= 0) {
1118 0 : parseError(cntxt, "atom name expected\n");
1119 0 : return -1;
1120 : }
1121 :
1122 : /* parse: ATOM id:type */
1123 5 : modnme = putNameLen(nxt, l);
1124 5 : if (modnme == NULL) {
1125 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1126 0 : return -1;
1127 : }
1128 5 : advance(cntxt, l);
1129 5 : if (currChar(cntxt) != ':')
1130 : tpe = TYPE_void; /* no type qualifier */
1131 : else
1132 5 : tpe = parseTypeId(cntxt);
1133 5 : if (ATOMindex(modnme) < 0) {
1134 4 : if (cntxt->curprg->def->errors)
1135 0 : freeException(cntxt->curprg->def->errors);
1136 4 : cntxt->curprg->def->errors = malAtomDefinition(modnme, tpe);
1137 : }
1138 5 : if (strcmp(modnme, "user"))
1139 5 : cntxt->curmodule = fixModule(modnme);
1140 : else
1141 0 : cntxt->curmodule = cntxt->usermodule;
1142 5 : cntxt->usermodule->isAtomModule = TRUE;
1143 5 : skipSpace(cntxt);
1144 5 : helpInfo(cntxt, &cntxt->usermodule->help);
1145 5 : return 0;
1146 : }
1147 :
1148 : /*
1149 : * All modules, except 'user', should be global
1150 : */
1151 : static int
1152 3 : parseModule(Client cntxt)
1153 : {
1154 3 : const char *modnme = 0;
1155 3 : int l;
1156 3 : char *nxt;
1157 :
1158 3 : nxt = CURRENT(cntxt);
1159 3 : if ((l = idLength(cntxt)) <= 0) {
1160 0 : parseError(cntxt, "<module path> expected\n");
1161 0 : return -1;
1162 : }
1163 3 : modnme = putNameLen(nxt, l);
1164 3 : if (modnme == NULL) {
1165 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1166 0 : return -1;
1167 : }
1168 3 : advance(cntxt, l);
1169 3 : if (strcmp(modnme, cntxt->usermodule->name) == 0) {
1170 : // ignore this module definition
1171 3 : } else if (getModule(modnme) == NULL) {
1172 3 : if (globalModule(modnme) == NULL)
1173 0 : parseError(cntxt, "<module> could not be created");
1174 : }
1175 3 : if (strcmp(modnme, "user"))
1176 3 : cntxt->curmodule = fixModule(modnme);
1177 : else
1178 0 : cntxt->curmodule = cntxt->usermodule;
1179 3 : skipSpace(cntxt);
1180 3 : helpInfo(cntxt, &cntxt->usermodule->help);
1181 3 : return 0;
1182 : }
1183 :
1184 : /*
1185 : * Include files should be handled in line with parsing. This way we
1186 : * are ensured that any possible signature definition will be known
1187 : * afterwards. The effect is that errors in the include sequence are
1188 : * marked as warnings.
1189 : */
1190 : static int
1191 2 : parseInclude(Client cntxt)
1192 : {
1193 2 : const char *modnme = 0;
1194 2 : char *s;
1195 2 : int x;
1196 2 : char *nxt;
1197 :
1198 2 : nxt = CURRENT(cntxt);
1199 :
1200 2 : if ((x = idLength(cntxt)) > 0) {
1201 2 : modnme = putNameLen(nxt, x);
1202 2 : advance(cntxt, x);
1203 0 : } else if ((x = stringLength(cntxt)) > 0) {
1204 0 : modnme = putNameLen(nxt + 1, x - 1);
1205 0 : advance(cntxt, x);
1206 : } else {
1207 0 : parseError(cntxt, "<module name> expected\n");
1208 0 : return -1;
1209 : }
1210 2 : if (modnme == NULL) {
1211 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1212 0 : return -1;
1213 : }
1214 :
1215 2 : if (currChar(cntxt) != ';') {
1216 0 : parseError(cntxt, "';' expected\n");
1217 0 : return 0;
1218 : }
1219 2 : skipToEnd(cntxt);
1220 :
1221 2 : if (!malLibraryEnabled(modnme)) {
1222 : return 0;
1223 : }
1224 :
1225 2 : if (getModule(modnme) == NULL) {
1226 1 : s = loadLibrary(modnme, FALSE);
1227 1 : if (s) {
1228 1 : parseError(cntxt, s);
1229 1 : freeException(s);
1230 1 : return 0;
1231 : }
1232 : }
1233 1 : if ((s = malInclude(cntxt, modnme, 0))) {
1234 0 : parseError(cntxt, s);
1235 0 : freeException(s);
1236 0 : return 0;
1237 : }
1238 : return 0;
1239 : }
1240 :
1241 : /* return the combined count of the number of arguments and the number
1242 : * of return values so that we can allocate enough space in the
1243 : * instruction; returns -1 on error (missing closing parenthesis) */
1244 : static int
1245 212 : cntArgsReturns(Client cntxt, int *retc)
1246 : {
1247 212 : size_t yycur = cntxt->yycur;
1248 212 : int cnt = 0;
1249 212 : char ch;
1250 :
1251 212 : ch = currChar(cntxt);
1252 212 : if (ch != ')') {
1253 : cnt++;
1254 1129 : while (ch != ')' && ch && !NL(ch)) {
1255 1038 : if (ch == ',')
1256 43 : cnt++;
1257 1038 : nextChar(cntxt);
1258 1038 : ch = currChar(cntxt);
1259 : }
1260 : }
1261 91 : if (ch != ')') {
1262 0 : parseError(cntxt, "')' expected\n");
1263 0 : cntxt->yycur = yycur;
1264 0 : return -1;
1265 : }
1266 212 : advance(cntxt, 1);
1267 212 : ch = currChar(cntxt);
1268 212 : if (ch == '(') {
1269 13 : advance(cntxt, 1);
1270 13 : ch = currChar(cntxt);
1271 13 : cnt++;
1272 13 : (*retc)++;
1273 304 : while (ch != ')' && ch && !NL(ch)) {
1274 291 : if (ch == ',') {
1275 39 : cnt++;
1276 39 : (*retc)++;
1277 : }
1278 291 : nextChar(cntxt);
1279 291 : ch = currChar(cntxt);
1280 : }
1281 13 : if (ch != ')') {
1282 0 : parseError(cntxt, "')' expected\n");
1283 0 : cntxt->yycur = yycur;
1284 0 : return -1;
1285 : }
1286 : } else {
1287 199 : cnt++;
1288 199 : (*retc)++;
1289 : }
1290 212 : cntxt->yycur = yycur;
1291 212 : return cnt;
1292 : }
1293 :
1294 : static void
1295 0 : mf_destroy(mel_func *f)
1296 : {
1297 0 : if (f) {
1298 0 : if (f->args)
1299 0 : GDKfree(f->args);
1300 0 : GDKfree(f);
1301 : }
1302 0 : }
1303 :
1304 : static int
1305 22 : argument(Client cntxt, mel_func *curFunc, mel_arg *curArg)
1306 : {
1307 22 : malType type;
1308 :
1309 22 : int l = idLength(cntxt);
1310 22 : *curArg = (mel_arg){ .isbat = 0 };
1311 22 : if (l > 0) {
1312 15 : char *varname = CURRENT(cntxt);
1313 15 : (void)varname; /* not used */
1314 :
1315 15 : advance(cntxt, l);
1316 15 : type = typeElm(cntxt, TYPE_any);
1317 15 : if (type < 0)
1318 : return -1;
1319 15 : int tt = getBatType(type);
1320 15 : if (tt != TYPE_any)
1321 13 : strcpy(curArg->type, BATatoms[tt].name);
1322 15 : if (isaBatType(type))
1323 0 : curArg->isbat = true;
1324 15 : if (isPolymorphic(type)) {
1325 2 : curArg->nr = getTypeIndex(type);
1326 2 : setPoly(curFunc, type);
1327 2 : tt = TYPE_any;
1328 : }
1329 15 : curArg->typeid = tt;
1330 7 : } else if (currChar(cntxt) == ':') {
1331 7 : type = typeElm(cntxt, TYPE_any);
1332 7 : int tt = getBatType(type);
1333 7 : if (tt != TYPE_any)
1334 5 : strcpy(curArg->type, BATatoms[tt].name);
1335 7 : if (isaBatType(type))
1336 3 : curArg->isbat = true;
1337 7 : if (isPolymorphic(type)) {
1338 1 : curArg->nr = getTypeIndex(type);
1339 1 : setPoly(curFunc, type);
1340 1 : tt = TYPE_any;
1341 : }
1342 7 : curArg->typeid = tt;
1343 : } else {
1344 0 : parseError(cntxt, "argument expected\n");
1345 0 : return -1;
1346 : }
1347 : return 0;
1348 : }
1349 :
1350 : static mel_func *
1351 13 : fcnCommandPatternHeader(Client cntxt, int kind)
1352 : {
1353 13 : int l;
1354 13 : malType tpe;
1355 13 : const char *fnme;
1356 13 : const char *modnme = NULL;
1357 13 : char ch;
1358 :
1359 13 : l = operatorLength(cntxt);
1360 13 : if (l == 0)
1361 12 : l = idLength(cntxt);
1362 12 : if (l == 0) {
1363 0 : parseError(cntxt, "<identifier> | <operator> expected\n");
1364 0 : return NULL;
1365 : }
1366 :
1367 13 : fnme = putNameLen(((char *) CURRENT(cntxt)), l);
1368 13 : if (fnme == NULL) {
1369 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1370 0 : return NULL;
1371 : }
1372 13 : advance(cntxt, l);
1373 :
1374 13 : if (currChar(cntxt) == '.') {
1375 0 : nextChar(cntxt); /* skip '.' */
1376 0 : modnme = fnme;
1377 0 : if (strcmp(modnme, "user") && getModule(modnme) == NULL) {
1378 0 : if (globalModule(modnme) == NULL) {
1379 0 : parseError(cntxt, "<module> name not defined\n");
1380 0 : return NULL;
1381 : }
1382 : }
1383 0 : l = operatorLength(cntxt);
1384 0 : if (l == 0)
1385 0 : l = idLength(cntxt);
1386 0 : if (l == 0) {
1387 0 : parseError(cntxt, "<identifier> | <operator> expected\n");
1388 0 : return NULL;
1389 : }
1390 0 : fnme = putNameLen(((char *) CURRENT(cntxt)), l);
1391 0 : if (fnme == NULL) {
1392 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1393 0 : return NULL;
1394 : }
1395 0 : advance(cntxt, l);
1396 : } else
1397 13 : modnme = cntxt->curmodule->name;
1398 :
1399 13 : if (currChar(cntxt) != '(') {
1400 0 : parseError(cntxt, "function header '(' expected\n");
1401 0 : return NULL;
1402 : }
1403 13 : advance(cntxt, 1);
1404 :
1405 : /* keep current prg also active ! */
1406 13 : int retc = 0, nargs = cntArgsReturns(cntxt, &retc);
1407 13 : if (nargs < 0)
1408 : return 0;
1409 :
1410 : /* one extra for argument/return manipulation */
1411 13 : assert(kind == COMMANDsymbol || kind == PATTERNsymbol);
1412 :
1413 13 : mel_func *curFunc = (mel_func*)GDKmalloc(sizeof(mel_func));
1414 13 : if (curFunc)
1415 13 : curFunc->args = NULL;
1416 13 : if (curFunc && nargs)
1417 13 : curFunc->args = (mel_arg*)GDKmalloc(sizeof(mel_arg)*nargs);
1418 :
1419 13 : if (cntxt->curprg == NULL || cntxt->curprg->def->errors || curFunc == NULL || (nargs && curFunc->args == NULL)) {
1420 0 : mf_destroy(curFunc);
1421 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1422 0 : return NULL;
1423 : }
1424 :
1425 13 : curFunc->fcn = fnme;
1426 13 : curFunc->mod = modnme;
1427 13 : curFunc->cname = NULL;
1428 13 : curFunc->command = false;
1429 13 : if (kind == COMMANDsymbol)
1430 5 : curFunc->command = true;
1431 13 : curFunc->unsafe = 0;
1432 13 : curFunc->vargs = 0;
1433 13 : curFunc->vrets = 0;
1434 13 : curFunc->poly = 0;
1435 13 : curFunc->retc = retc;
1436 13 : curFunc->argc = nargs;
1437 13 : curFunc->comment = NULL;
1438 :
1439 : /* get calling parameters */
1440 13 : ch = currChar(cntxt);
1441 13 : int i = retc;
1442 19 : while (ch != ')' && ch && !NL(ch)) {
1443 18 : if (argument(cntxt, curFunc, curFunc->args+i) < 0) {
1444 0 : mf_destroy(curFunc);
1445 0 : return NULL;
1446 : }
1447 : /* the last argument may be variable length */
1448 18 : if (MALkeyword(cntxt, "...", 3)) {
1449 6 : curFunc->vargs = true;
1450 6 : setPoly(curFunc, TYPE_any);
1451 6 : break;
1452 : }
1453 12 : if ((ch = currChar(cntxt)) != ',') {
1454 6 : if (ch == ')')
1455 : break;
1456 0 : mf_destroy(curFunc);
1457 0 : parseError(cntxt, "',' expected\n");
1458 0 : return NULL;
1459 : } else {
1460 6 : nextChar(cntxt); /* skip ',' */
1461 6 : i++;
1462 : }
1463 6 : skipSpace(cntxt);
1464 6 : ch = currChar(cntxt);
1465 : }
1466 13 : if (currChar(cntxt) != ')') {
1467 0 : mf_destroy(curFunc);
1468 0 : parseError(cntxt, "')' expected\n");
1469 0 : return NULL;
1470 : }
1471 13 : advance(cntxt, 1); /* skip ')' */
1472 : /*
1473 : The return type is either a single type or multiple return type structure.
1474 : We simply keep track of the number of arguments added and
1475 : during the final phase reshuffle the return values to the beginning (?)
1476 : */
1477 13 : if (currChar(cntxt) == ':') {
1478 7 : tpe = typeElm(cntxt, TYPE_void);
1479 7 : curFunc->args[0].vargs = 0;
1480 7 : curFunc->args[0].nr = 0;
1481 7 : if (isPolymorphic(tpe)) {
1482 1 : curFunc->args[0].nr = getTypeIndex(tpe);
1483 1 : setPoly(curFunc, tpe);
1484 : }
1485 7 : if (isaBatType(tpe))
1486 3 : curFunc->args[0].isbat = true;
1487 : else
1488 4 : curFunc->args[0].isbat = false;
1489 7 : int tt = getBatType(tpe);
1490 7 : curFunc->args[0].typeid = tt;
1491 7 : curFunc->args[0].opt = 0;
1492 : /* we may be confronted by a variable target type list */
1493 7 : if (MALkeyword(cntxt, "...", 3)) {
1494 3 : curFunc->args[0].vargs = true;
1495 3 : curFunc->vrets = true;
1496 3 : setPoly(curFunc, TYPE_any);
1497 : }
1498 6 : } else if (keyphrase1(cntxt, "(")) { /* deal with compound return */
1499 3 : int i = 0;
1500 : /* parse multi-target result */
1501 : /* skipSpace(cntxt); */
1502 3 : ch = currChar(cntxt);
1503 4 : while (ch != ')' && ch && !NL(ch)) {
1504 4 : if (argument(cntxt, curFunc, curFunc->args+i) < 0) {
1505 0 : mf_destroy(curFunc);
1506 0 : return NULL;
1507 : }
1508 : /* we may be confronted by a variable target type list */
1509 4 : if (MALkeyword(cntxt, "...", 3)) {
1510 3 : curFunc->args[i].vargs = true;
1511 3 : curFunc->vrets = true;
1512 3 : setPoly(curFunc, TYPE_any);
1513 : }
1514 4 : if ((ch = currChar(cntxt)) != ',') {
1515 3 : if (ch == ')')
1516 : break;
1517 0 : parseError(cntxt, "',' expected\n");
1518 0 : return curFunc;
1519 : } else {
1520 1 : nextChar(cntxt); /* skip ',' */
1521 1 : i++;
1522 : }
1523 1 : skipSpace(cntxt);
1524 1 : ch = currChar(cntxt);
1525 : }
1526 3 : if (currChar(cntxt) != ')') {
1527 0 : mf_destroy(curFunc);
1528 0 : parseError(cntxt, "')' expected\n");
1529 0 : return NULL;
1530 : }
1531 3 : nextChar(cntxt); /* skip ')' */
1532 : }
1533 : return curFunc;
1534 : }
1535 :
1536 : static Symbol
1537 13 : parseCommandPattern(Client cntxt, int kind, MALfcn address)
1538 : {
1539 13 : mel_func *curFunc = fcnCommandPatternHeader(cntxt, kind);
1540 13 : if (curFunc == NULL) {
1541 0 : cntxt->blkmode = 0;
1542 0 : return NULL;
1543 : }
1544 13 : const char *modnme = curFunc->mod;
1545 13 : if (modnme && (getModule(modnme) == FALSE && strcmp(modnme, "user"))) {
1546 : // introduce the module
1547 0 : if (globalModule(modnme) == NULL) {
1548 0 : mf_destroy(curFunc);
1549 0 : parseError(cntxt, "<module> could not be defined\n");
1550 0 : return NULL;
1551 : }
1552 : }
1553 0 : modnme = modnme ? modnme : cntxt->usermodule->name;
1554 :
1555 13 : size_t l = strlen(modnme);
1556 13 : modnme = putNameLen(modnme, l);
1557 13 : if (modnme == NULL) {
1558 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1559 0 : return NULL;
1560 : }
1561 :
1562 13 : Symbol curPrg = newFunctionArgs(modnme, curFunc->fcn, kind, -1);
1563 13 : if (!curPrg) {
1564 0 : mf_destroy(curFunc);
1565 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1566 0 : return NULL;
1567 : }
1568 13 : curPrg->func = curFunc;
1569 13 : curPrg->def = NULL;
1570 13 : curPrg->allocated = true;
1571 :
1572 13 : skipSpace(cntxt);
1573 13 : if (MALkeyword(cntxt, "address", 7)) {
1574 13 : int i;
1575 13 : i = idLength(cntxt);
1576 13 : if (i == 0) {
1577 0 : parseError(cntxt, "address <identifier> expected\n");
1578 0 : return NULL;
1579 : }
1580 13 : cntxt->blkmode = 0;
1581 :
1582 13 : size_t sz = (size_t) (i < IDLENGTH ? i : IDLENGTH - 1);
1583 13 : curFunc->cname = GDKmalloc(sz+1);
1584 13 : if (!curFunc->cname) {
1585 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1586 0 : freeSymbol(curPrg);
1587 0 : return NULL;
1588 : }
1589 13 : memcpy((char*)curFunc->cname, CURRENT(cntxt), sz);
1590 13 : ((char*)curFunc->cname)[sz] = 0;
1591 : /* avoid a clash with old temporaries */
1592 13 : advance(cntxt, i);
1593 13 : curFunc->imp = getAddress(curFunc->mod, curFunc->cname);
1594 :
1595 13 : if (cntxt->usermodule->isAtomModule) {
1596 3 : if (curFunc->imp == NULL) {
1597 0 : parseError(cntxt, "<address> not found\n");
1598 0 : freeSymbol(curPrg);
1599 0 : return NULL;
1600 : }
1601 3 : malAtomProperty(curFunc);
1602 : }
1603 13 : skipSpace(cntxt);
1604 0 : } else if (address) {
1605 0 : curFunc->mod = modnme;
1606 0 : curFunc->imp = address;
1607 : }
1608 13 : if (strcmp(modnme, "user") == 0 || getModule(modnme)) {
1609 13 : if (strcmp(modnme, "user") == 0)
1610 10 : insertSymbol(cntxt->usermodule, curPrg);
1611 : else
1612 3 : insertSymbol(getModule(modnme), curPrg);
1613 : } else {
1614 0 : freeSymbol(curPrg);
1615 0 : parseError(cntxt, "<module> not found\n");
1616 0 : return NULL;
1617 : }
1618 :
1619 13 : helpInfo(cntxt, &curFunc->comment);
1620 13 : return curPrg;
1621 : }
1622 :
1623 : static MalBlkPtr
1624 199 : fcnHeader(Client cntxt, int kind)
1625 : {
1626 199 : int l;
1627 199 : malType tpe;
1628 199 : const char *fnme;
1629 199 : const char *modnme = NULL;
1630 199 : char ch;
1631 199 : Symbol curPrg;
1632 199 : MalBlkPtr curBlk = 0;
1633 199 : InstrPtr curInstr;
1634 :
1635 199 : l = operatorLength(cntxt);
1636 199 : if (l == 0)
1637 199 : l = idLength(cntxt);
1638 199 : if (l == 0) {
1639 0 : parseError(cntxt, "<identifier> | <operator> expected\n");
1640 0 : return 0;
1641 : }
1642 :
1643 199 : fnme = putNameLen(((char *) CURRENT(cntxt)), l);
1644 199 : if (fnme == NULL) {
1645 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1646 0 : return NULL;
1647 : }
1648 199 : advance(cntxt, l);
1649 :
1650 199 : if (currChar(cntxt) == '.') {
1651 8 : nextChar(cntxt); /* skip '.' */
1652 8 : modnme = fnme;
1653 8 : if (strcmp(modnme, "user") && getModule(modnme) == NULL) {
1654 1 : if (globalModule(modnme) == NULL) {
1655 0 : parseError(cntxt, "<module> name not defined\n");
1656 0 : return 0;
1657 : }
1658 : }
1659 8 : l = operatorLength(cntxt);
1660 8 : if (l == 0)
1661 8 : l = idLength(cntxt);
1662 8 : if (l == 0) {
1663 0 : parseError(cntxt, "<identifier> | <operator> expected\n");
1664 0 : return 0;
1665 : }
1666 8 : fnme = putNameLen(((char *) CURRENT(cntxt)), l);
1667 8 : if (fnme == NULL) {
1668 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1669 0 : return NULL;
1670 : }
1671 8 : advance(cntxt, l);
1672 : } else
1673 191 : modnme = cntxt->curmodule->name;
1674 :
1675 : /* temporary suspend capturing statements in main block */
1676 199 : if (cntxt->backup) {
1677 0 : parseError(cntxt, "mal_parser: unexpected recursion\n");
1678 0 : return 0;
1679 : }
1680 199 : if (currChar(cntxt) != '(') {
1681 0 : parseError(cntxt, "function header '(' expected\n");
1682 0 : return curBlk;
1683 : }
1684 199 : advance(cntxt, 1);
1685 :
1686 199 : assert(!cntxt->backup);
1687 199 : cntxt->backup = cntxt->curprg;
1688 199 : int retc = 0, nargs = cntArgsReturns(cntxt, &retc);
1689 199 : (void)retc;
1690 199 : if (nargs < 0)
1691 : return 0;
1692 : /* one extra for argument/return manipulation */
1693 199 : cntxt->curprg = newFunctionArgs(modnme, fnme, kind, nargs + 1);
1694 199 : if (cntxt->curprg == NULL) {
1695 : /* reinstate curprg to have a place for the error */
1696 0 : cntxt->curprg = cntxt->backup;
1697 0 : cntxt->backup = NULL;
1698 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1699 0 : return 0;
1700 : }
1701 199 : cntxt->curprg->def->errors = cntxt->backup->def->errors;
1702 199 : cntxt->backup->def->errors = 0;
1703 199 : curPrg = cntxt->curprg;
1704 199 : curBlk = curPrg->def;
1705 199 : curInstr = getInstrPtr(curBlk, 0);
1706 :
1707 : /* get calling parameters */
1708 199 : ch = currChar(cntxt);
1709 236 : while (ch != ')' && ch && !NL(ch)) {
1710 116 : curInstr = binding(cntxt, curBlk, curInstr, 1);
1711 : /* the last argument may be variable length */
1712 116 : if (MALkeyword(cntxt, "...", 3)) {
1713 1 : curInstr->varargs |= VARARGS;
1714 1 : setPolymorphic(curInstr, TYPE_any, TRUE);
1715 1 : break;
1716 : }
1717 115 : if ((ch = currChar(cntxt)) != ',') {
1718 78 : if (ch == ')')
1719 : break;
1720 0 : if (cntxt->backup)
1721 0 : curBlk = NULL;
1722 0 : parseError(cntxt, "',' expected\n");
1723 0 : return curBlk;
1724 : } else
1725 37 : nextChar(cntxt); /* skip ',' */
1726 37 : skipSpace(cntxt);
1727 37 : ch = currChar(cntxt);
1728 : }
1729 199 : if (currChar(cntxt) != ')') {
1730 0 : freeInstruction(curInstr);
1731 0 : if (cntxt->backup)
1732 0 : curBlk = NULL;
1733 0 : parseError(cntxt, "')' expected\n");
1734 0 : return curBlk;
1735 : }
1736 199 : advance(cntxt, 1); /* skip ')' */
1737 : /*
1738 : The return type is either a single type or multiple return type structure.
1739 : We simply keep track of the number of arguments added and
1740 : during the final phase reshuffle the return values to the beginning (?)
1741 : */
1742 199 : if (currChar(cntxt) == ':') {
1743 76 : tpe = typeElm(cntxt, TYPE_void);
1744 76 : setPolymorphic(curInstr, tpe, TRUE);
1745 76 : setVarType(curBlk, curInstr->argv[0], tpe);
1746 : /* we may be confronted by a variable target type list */
1747 76 : if (MALkeyword(cntxt, "...", 3)) {
1748 0 : curInstr->varargs |= VARRETS;
1749 0 : setPolymorphic(curInstr, TYPE_any, TRUE);
1750 : }
1751 :
1752 123 : } else if (keyphrase1(cntxt, "(")) { /* deal with compound return */
1753 10 : int retc = curInstr->argc, i1, i2 = 0;
1754 10 : int max;
1755 10 : short *newarg;
1756 : /* parse multi-target result */
1757 : /* skipSpace(cntxt); */
1758 10 : ch = currChar(cntxt);
1759 48 : while (ch != ')' && ch && !NL(ch)) {
1760 48 : curInstr = binding(cntxt, curBlk, curInstr, 0);
1761 : /* we may be confronted by a variable target type list */
1762 48 : if (MALkeyword(cntxt, "...", 3)) {
1763 0 : curInstr->varargs |= VARRETS;
1764 0 : setPolymorphic(curInstr, TYPE_any, TRUE);
1765 : }
1766 48 : if ((ch = currChar(cntxt)) != ',') {
1767 10 : if (ch == ')')
1768 : break;
1769 0 : if (cntxt->backup)
1770 0 : curBlk = NULL;
1771 0 : parseError(cntxt, "',' expected\n");
1772 0 : return curBlk;
1773 : } else {
1774 38 : nextChar(cntxt); /* skip ',' */
1775 : }
1776 38 : skipSpace(cntxt);
1777 38 : ch = currChar(cntxt);
1778 : }
1779 : /* re-arrange the parameters, results first */
1780 10 : max = curInstr->maxarg;
1781 10 : newarg = (short *) GDKmalloc(max * sizeof(curInstr->argv[0]));
1782 10 : if (newarg == NULL) {
1783 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1784 0 : if (cntxt->backup)
1785 0 : curBlk = NULL;
1786 0 : return curBlk;
1787 : }
1788 58 : for (i1 = retc; i1 < curInstr->argc; i1++)
1789 48 : newarg[i2++] = curInstr->argv[i1];
1790 10 : curInstr->retc = curInstr->argc - retc;
1791 22 : for (i1 = 1; i1 < retc; i1++)
1792 12 : newarg[i2++] = curInstr->argv[i1];
1793 10 : curInstr->argc = i2;
1794 20 : for (; i2 < max; i2++)
1795 10 : newarg[i2] = 0;
1796 80 : for (i1 = 0; i1 < max; i1++)
1797 70 : curInstr->argv[i1] = newarg[i1];
1798 10 : GDKfree(newarg);
1799 10 : if (currChar(cntxt) != ')') {
1800 0 : freeInstruction(curInstr);
1801 0 : if (cntxt->backup)
1802 0 : curBlk = NULL;
1803 0 : parseError(cntxt, "')' expected\n");
1804 0 : return curBlk;
1805 : }
1806 10 : nextChar(cntxt); /* skip ')' */
1807 : } else { /* default */
1808 113 : setVarType(curBlk, 0, TYPE_void);
1809 : }
1810 199 : if (curInstr != getInstrPtr(curBlk, 0)) {
1811 0 : freeInstruction(getInstrPtr(curBlk, 0));
1812 0 : putInstrPtr(curBlk, 0, curInstr);
1813 : }
1814 : return curBlk;
1815 : }
1816 :
1817 : static MalBlkPtr
1818 199 : parseFunction(Client cntxt, int kind)
1819 : {
1820 199 : MalBlkPtr curBlk = 0;
1821 :
1822 199 : curBlk = fcnHeader(cntxt, kind);
1823 199 : if (curBlk == NULL)
1824 : return curBlk;
1825 199 : if (MALkeyword(cntxt, "address", 7)) {
1826 : /* TO BE DEPRECATED */
1827 1 : str nme;
1828 1 : int i;
1829 1 : InstrPtr curInstr = getInstrPtr(curBlk, 0);
1830 1 : i = idLength(cntxt);
1831 1 : if (i == 0) {
1832 0 : parseError(cntxt, "<identifier> expected\n");
1833 0 : return 0;
1834 : }
1835 1 : nme = idCopy(cntxt, i);
1836 1 : if (nme == NULL) {
1837 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1838 0 : return 0;
1839 : }
1840 1 : curInstr->fcn = getAddress(getModuleId(curInstr), nme);
1841 1 : GDKfree(nme);
1842 1 : if (curInstr->fcn == NULL) {
1843 1 : parseError(cntxt, "<address> not found\n");
1844 1 : return 0;
1845 : }
1846 0 : skipSpace(cntxt);
1847 : }
1848 : /* block is terminated at the END statement */
1849 198 : helpInfo(cntxt, &curBlk->help);
1850 198 : return curBlk;
1851 : }
1852 :
1853 : /*
1854 : * Functions and factories end with a labeled end-statement.
1855 : * The routine below checks for misalignment of the closing statements.
1856 : * Any instruction parsed after the function block is considered an error.
1857 : */
1858 : static int
1859 292 : parseEnd(Client cntxt)
1860 : {
1861 292 : Symbol curPrg = 0;
1862 292 : size_t l;
1863 292 : InstrPtr sig;
1864 292 : str errors = MAL_SUCCEED, msg = MAL_SUCCEED;
1865 :
1866 292 : if (MALkeyword(cntxt, "end", 3)) {
1867 204 : curPrg = cntxt->curprg;
1868 204 : l = idLength(cntxt);
1869 204 : if (l == 0)
1870 37 : l = operatorLength(cntxt);
1871 204 : sig = getInstrPtr(cntxt->curprg->def, 0);
1872 204 : if (strncmp(CURRENT(cntxt), getModuleId(sig), l) == 0) {
1873 37 : advance(cntxt, l);
1874 37 : skipSpace(cntxt);
1875 37 : if (currChar(cntxt) == '.')
1876 0 : nextChar(cntxt);
1877 37 : skipSpace(cntxt);
1878 37 : l = idLength(cntxt);
1879 37 : if (l == 0)
1880 37 : l = operatorLength(cntxt);
1881 : }
1882 : /* parse fcn */
1883 204 : if ((l == strlen(curPrg->name) &&
1884 204 : strncmp(CURRENT(cntxt), curPrg->name, l) == 0) || l == 0)
1885 195 : advance(cntxt, l);
1886 : else
1887 9 : parseError(cntxt, "non matching end label\n");
1888 204 : pushEndInstruction(cntxt->curprg->def);
1889 204 : cntxt->blkmode = 0;
1890 204 : if (strcmp(getModuleId(sig), "user") == 0)
1891 199 : insertSymbol(cntxt->usermodule, cntxt->curprg);
1892 : else
1893 5 : insertSymbol(getModule(getModuleId(sig)), cntxt->curprg);
1894 :
1895 204 : if (cntxt->curprg->def->errors) {
1896 11 : errors = cntxt->curprg->def->errors;
1897 11 : cntxt->curprg->def->errors = 0;
1898 : }
1899 : // check for newly identified errors
1900 204 : msg = chkProgram(cntxt->usermodule, cntxt->curprg->def);
1901 204 : if (errors == NULL)
1902 : errors = msg;
1903 : else
1904 11 : freeException(msg);
1905 204 : if (errors == NULL) {
1906 168 : errors = cntxt->curprg->def->errors;
1907 168 : cntxt->curprg->def->errors = 0;
1908 36 : } else if (cntxt->curprg->def->errors) {
1909 : //collect all errors for reporting
1910 0 : str new = GDKmalloc(strlen(errors) +
1911 : strlen(cntxt->curprg->def->errors) + 16);
1912 0 : if (new) {
1913 0 : strcpy(new, errors);
1914 0 : if (new[strlen(new) - 1] != '\n')
1915 0 : strcat(new, "\n");
1916 0 : strcat(new, "!");
1917 0 : strcat(new, cntxt->curprg->def->errors);
1918 :
1919 0 : freeException(errors);
1920 0 : freeException(cntxt->curprg->def->errors);
1921 :
1922 0 : cntxt->curprg->def->errors = 0;
1923 0 : errors = new;
1924 : }
1925 : }
1926 :
1927 204 : if (cntxt->backup) {
1928 194 : cntxt->curprg = cntxt->backup;
1929 194 : cntxt->backup = 0;
1930 : } else {
1931 10 : str msg;
1932 10 : if ((msg = MSinitClientPrg(cntxt, cntxt->curmodule->name,
1933 : "main")) != MAL_SUCCEED) {
1934 0 : if (errors) {
1935 0 : str new = GDKmalloc(strlen(errors) + strlen(msg) + 3);
1936 0 : if (new) {
1937 0 : strcpy(new, msg);
1938 0 : if (new[strlen(new) - 1] != '\n')
1939 0 : strcat(new, "\n");
1940 0 : strcat(new, errors);
1941 0 : freeException(errors);
1942 0 : cntxt->curprg->def->errors = new;
1943 : } else {
1944 0 : cntxt->curprg->def->errors = errors;
1945 : }
1946 0 : freeException(msg);
1947 : } else {
1948 0 : cntxt->curprg->def->errors = msg;
1949 : }
1950 0 : return 1;
1951 : }
1952 : }
1953 : // pass collected errors to context
1954 204 : assert(cntxt->curprg->def->errors == NULL);
1955 204 : cntxt->curprg->def->errors = errors;
1956 204 : return 1;
1957 : }
1958 : return 0;
1959 : }
1960 :
1961 : /*
1962 : * Most instructions are simple assignments, possibly
1963 : * modified with a barrier/catch tag.
1964 : *
1965 : * The basic types are also predefined as a variable.
1966 : * This makes it easier to communicate types to MAL patterns.
1967 : */
1968 :
1969 : #define GETvariable(FREE) \
1970 : if ((varid = findVariableLength(curBlk, CURRENT(cntxt), l)) == -1) { \
1971 : varid = newVariable(curBlk, CURRENT(cntxt), l, TYPE_any); \
1972 : advance(cntxt, l); \
1973 : if (varid < 0) { FREE; return; } \
1974 : } else \
1975 : advance(cntxt, l);
1976 :
1977 : /* The parameter of parseArguments is the return value of the enclosing function. */
1978 : static int
1979 8958 : parseArguments(Client cntxt, MalBlkPtr curBlk, InstrPtr *curInstr)
1980 : {
1981 24554 : while (currChar(cntxt) != ')') {
1982 15598 : switch (term(cntxt, curBlk, curInstr, 0)) {
1983 : case 0:
1984 15596 : break;
1985 : case 2:
1986 : return 2;
1987 : case 3:
1988 : return 3;
1989 0 : case 4:
1990 0 : parseError(cntxt, "Argument type overwrites previous definition\n");
1991 0 : return 0;
1992 0 : default:
1993 0 : parseError(cntxt, "<factor> expected\n");
1994 0 : return 1;
1995 : }
1996 15596 : if (currChar(cntxt) == ',')
1997 7293 : advance(cntxt, 1);
1998 8303 : else if (currChar(cntxt) != ')') {
1999 0 : parseError(cntxt, "',' expected\n");
2000 0 : cntxt->yycur--; /* keep it */
2001 0 : break;
2002 : }
2003 : }
2004 8956 : if (currChar(cntxt) == ')')
2005 8956 : advance(cntxt, 1);
2006 : return 0;
2007 : }
2008 :
2009 : static void
2010 11698 : parseAssign(Client cntxt, int cntrl)
2011 : {
2012 11698 : InstrPtr curInstr;
2013 11698 : MalBlkPtr curBlk;
2014 11698 : Symbol curPrg;
2015 11698 : int i = 0, l, type = TYPE_any, varid = -1;
2016 11698 : const char *arg = 0;
2017 11698 : ValRecord cst;
2018 :
2019 11698 : curPrg = cntxt->curprg;
2020 11698 : curBlk = curPrg->def;
2021 11698 : if ((curInstr = newInstruction(curBlk, NULL, NULL)) == NULL) {
2022 13 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
2023 2345 : return;
2024 : }
2025 :
2026 11686 : if (cntrl) {
2027 592 : curInstr->token = ASSIGNsymbol;
2028 592 : curInstr->barrier = cntrl;
2029 : }
2030 :
2031 : /* start the parsing by recognition of the lhs of an assignment */
2032 11686 : if (currChar(cntxt) == '(') {
2033 : /* parsing multi-assignment */
2034 218 : advance(cntxt, 1);
2035 218 : curInstr->argc = 0; /*reset to handle pushArg correctly !! */
2036 218 : curInstr->retc = 0;
2037 1838 : while (currChar(cntxt) != ')' && currChar(cntxt)) {
2038 1620 : l = idLength(cntxt);
2039 1620 : i = cstToken(cntxt, &cst);
2040 1620 : if (l == 0 || i) {
2041 1 : parseError(cntxt, "<identifier> or <literal> expected\n");
2042 1 : freeInstruction(curInstr);
2043 1 : return;
2044 : }
2045 1619 : GETvariable(freeInstruction(curInstr));
2046 1619 : if (currChar(cntxt) == ':') {
2047 31 : type = typeElm(cntxt, getVarType(curBlk, varid));
2048 31 : if (type < 0)
2049 0 : goto part3;
2050 31 : setPolymorphic(curInstr, type, FALSE);
2051 31 : setVarType(curBlk, varid, type);
2052 : }
2053 1619 : curInstr = pushArgument(curBlk, curInstr, varid);
2054 1619 : curInstr->retc++;
2055 1619 : if (currChar(cntxt) == ')')
2056 : break;
2057 1402 : if (currChar(cntxt) == ',')
2058 1402 : keyphrase1(cntxt, ",");
2059 : }
2060 217 : advance(cntxt, 1); /* skip ')' */
2061 217 : if (curInstr->retc == 0) {
2062 : /* add dummy variable */
2063 0 : curInstr = pushArgument(curBlk, curInstr,
2064 : newTmpVariable(curBlk, TYPE_any));
2065 0 : curInstr->retc++;
2066 : }
2067 : } else {
2068 : /* are we dealing with a simple assignment? */
2069 11468 : l = idLength(cntxt);
2070 11469 : i = cstToken(cntxt, &cst);
2071 11466 : if (l == 0 || i) {
2072 : /* we haven't seen a target variable */
2073 : /* flow of control statements may end here. */
2074 : /* shouldn't allow for nameless controls todo */
2075 6 : if (i && cst.vtype == TYPE_str)
2076 0 : GDKfree(cst.val.sval);
2077 6 : if (cntrl == LEAVEsymbol || cntrl == REDOsymbol ||
2078 6 : cntrl == RETURNsymbol || cntrl == EXITsymbol) {
2079 4 : curInstr->argv[0] = getBarrierEnvelop(curBlk);
2080 4 : if (currChar(cntxt) != ';') {
2081 0 : freeInstruction(curInstr);
2082 0 : parseError(cntxt,
2083 : "<identifier> or <literal> expected in control statement\n");
2084 0 : return;
2085 : }
2086 4 : pushInstruction(curBlk, curInstr);
2087 4 : return;
2088 : }
2089 2 : getArg(curInstr, 0) = newTmpVariable(curBlk, TYPE_any);
2090 2 : freeInstruction(curInstr);
2091 2 : parseError(cntxt, "<identifier> or <literal> expected\n");
2092 2 : return;
2093 : }
2094 : /* Check if we are dealing with module.fcn call */
2095 11460 : if (CURRENT(cntxt)[l] == '.' || CURRENT(cntxt)[l] == '(') {
2096 4363 : curInstr->argv[0] = newTmpVariable(curBlk, TYPE_any);
2097 4363 : goto FCNcallparse;
2098 : }
2099 :
2100 : /* Get target variable details */
2101 7097 : GETvariable(freeInstruction(curInstr));
2102 7096 : if (!(currChar(cntxt) == ':' && CURRENT(cntxt)[1] == '=')) {
2103 446 : curInstr->argv[0] = varid;
2104 446 : if (currChar(cntxt) == ':') {
2105 164 : type = typeElm(cntxt, getVarType(curBlk, varid));
2106 164 : if (type < 0)
2107 0 : goto part3;
2108 164 : setPolymorphic(curInstr, type, FALSE);
2109 164 : setVarType(curBlk, varid, type);
2110 : }
2111 : }
2112 7096 : curInstr->argv[0] = varid;
2113 : }
2114 : /* look for assignment operator */
2115 7313 : if (!keyphrase2(cntxt, ":=")) {
2116 : /* no assignment !! a control variable is allowed */
2117 : /* for the case RETURN X, we normalize it to include the function arguments */
2118 326 : if (cntrl == RETURNsymbol) {
2119 31 : int e;
2120 31 : InstrPtr sig = getInstrPtr(curBlk, 0);
2121 31 : curInstr->retc = 0;
2122 64 : for (e = 0; e < sig->retc; e++)
2123 33 : curInstr = pushReturn(curBlk, curInstr, getArg(sig, e));
2124 : }
2125 :
2126 326 : goto part3;
2127 : }
2128 6989 : if (currChar(cntxt) == '(') {
2129 : /* parse multi assignment */
2130 9 : advance(cntxt, 1);
2131 9 : switch (parseArguments(cntxt, curBlk, &curInstr)) {
2132 0 : case 2:
2133 0 : goto part2;
2134 9 : default:
2135 : case 3:
2136 9 : goto part3;
2137 : }
2138 : /* unreachable */
2139 : }
2140 : /*
2141 : * We have so far the LHS part of an assignment. The remainder is
2142 : * either a simple term expression, a multi assignent, or the start
2143 : * of a function call.
2144 : */
2145 6980 : FCNcallparse:
2146 11343 : if ((l = idLength(cntxt)) && CURRENT(cntxt)[l] == '(') {
2147 : /* parseError(cntxt,"<module> expected\n"); */
2148 62 : setModuleId(curInstr, cntxt->curmodule->name);
2149 62 : i = l;
2150 62 : goto FCNcallparse2;
2151 11282 : } else if ((l = idLength(cntxt)) && CURRENT(cntxt)[l] == '.') {
2152 : /* continue with parseing a function/operator call */
2153 8888 : arg = putNameLen(CURRENT(cntxt), l);
2154 8889 : if (arg == NULL) {
2155 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
2156 0 : freeInstruction(curInstr);
2157 0 : return;
2158 : }
2159 8889 : advance(cntxt, l + 1); /* skip '.' too */
2160 8889 : setModuleId(curInstr, arg);
2161 8889 : i = idLength(cntxt);
2162 8889 : if (i == 0)
2163 68 : i = operatorLength(cntxt);
2164 8821 : FCNcallparse2:
2165 130 : if (i) {
2166 8951 : setFunctionId(curInstr, putNameLen(((char *) CURRENT(cntxt)), i));
2167 8951 : if (getFunctionId(curInstr) == NULL) {
2168 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
2169 0 : freeInstruction(curInstr);
2170 0 : return;
2171 : }
2172 8951 : advance(cntxt, i);
2173 : } else {
2174 0 : parseError(cntxt, "<functionname> expected\n");
2175 0 : freeInstruction(curInstr);
2176 0 : return;
2177 : }
2178 8951 : skipSpace(cntxt);
2179 8951 : if (currChar(cntxt) != '(') {
2180 2 : parseError(cntxt, "'(' expected\n");
2181 2 : freeInstruction(curInstr);
2182 2 : return;
2183 : }
2184 8949 : advance(cntxt, 1);
2185 8949 : switch (parseArguments(cntxt, curBlk, &curInstr)) {
2186 0 : case 2:
2187 0 : goto part2;
2188 8949 : default:
2189 : case 3:
2190 8949 : goto part3;
2191 : }
2192 : /* unreachable */
2193 : }
2194 : /* Handle the ordinary assignments and expressions */
2195 2392 : switch (term(cntxt, curBlk, &curInstr, 2)) {
2196 2214 : case 2:
2197 2214 : goto part2;
2198 3 : case 3:
2199 3 : goto part3;
2200 : }
2201 : part2: /* consume <operator><term> part of expression */
2202 2383 : if ((i = operatorLength(cntxt))) {
2203 : /* simple arithmetic operator expression */
2204 122 : setFunctionId(curInstr, putNameLen(((char *) CURRENT(cntxt)), i));
2205 122 : if (getFunctionId(curInstr) == NULL) {
2206 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
2207 0 : freeInstruction(curInstr);
2208 0 : return;
2209 : }
2210 122 : advance(cntxt, i);
2211 122 : curInstr->modname = putName("calc");
2212 122 : if (curInstr->modname == NULL) {
2213 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
2214 0 : freeInstruction(curInstr);
2215 0 : return;
2216 : }
2217 122 : if ((l = idLength(cntxt))
2218 54 : && !(l == 3 && strncmp(CURRENT(cntxt), "nil", 3) == 0)) {
2219 54 : GETvariable(freeInstruction(curInstr));
2220 54 : curInstr = pushArgument(curBlk, curInstr, varid);
2221 54 : goto part3;
2222 : }
2223 68 : switch (term(cntxt, curBlk, &curInstr, 3)) {
2224 0 : case 2:
2225 0 : goto part2;
2226 67 : case 3:
2227 67 : goto part3;
2228 : }
2229 1 : parseError(cntxt, "<term> expected\n");
2230 1 : freeInstruction(curInstr);
2231 1 : return;
2232 : } else {
2233 2266 : skipSpace(cntxt);
2234 2260 : if (currChar(cntxt) == '(') {
2235 0 : parseError(cntxt, "module name missing\n");
2236 0 : freeInstruction(curInstr);
2237 0 : return;
2238 2260 : } else if (currChar(cntxt) != ';' && currChar(cntxt) != '#') {
2239 1 : parseError(cntxt, "operator expected\n");
2240 1 : freeInstruction(curInstr);
2241 1 : return;
2242 : }
2243 2259 : pushInstruction(curBlk, curInstr);
2244 2259 : return;
2245 : }
2246 9408 : part3:
2247 9408 : skipSpace(cntxt);
2248 9407 : if (currChar(cntxt) != ';') {
2249 39 : parseError(cntxt, "';' expected\n");
2250 39 : skipToEnd(cntxt);
2251 39 : freeInstruction(curInstr);
2252 39 : return;
2253 : }
2254 9368 : skipToEnd(cntxt);
2255 9368 : if (cntrl == RETURNsymbol
2256 36 : && !(curInstr->token == ASSIGNsymbol || getModuleId(curInstr) != 0)) {
2257 0 : parseError(cntxt, "return assignment expected\n");
2258 0 : freeInstruction(curInstr);
2259 0 : return;
2260 : }
2261 9368 : pushInstruction(curBlk, curInstr);
2262 : }
2263 :
2264 : void
2265 10411 : parseMAL(Client cntxt, Symbol curPrg, int skipcomments, int lines,
2266 : MALfcn address)
2267 : {
2268 10411 : int cntrl = 0;
2269 : /*Symbol curPrg= cntxt->curprg; */
2270 10411 : char c;
2271 10411 : int inlineProp = 0, unsafeProp = 0;
2272 :
2273 10411 : (void) curPrg;
2274 10411 : echoInput(cntxt);
2275 : /* here the work takes place */
2276 41635 : while ((c = currChar(cntxt)) && lines > 0) {
2277 31224 : switch (c) {
2278 13762 : case '\n':
2279 : case '\r':
2280 : case '\f':
2281 13762 : lines -= c == '\n';
2282 13762 : nextChar(cntxt);
2283 13762 : echoInput(cntxt);
2284 13765 : continue;
2285 5303 : case ';':
2286 : case '\t':
2287 : case ' ':
2288 5303 : nextChar(cntxt);
2289 5303 : continue;
2290 13 : case '#':
2291 : { /* keep the full line comments */
2292 13 : char start[256], *e = start, c;
2293 13 : MalBlkPtr curBlk = cntxt->curprg->def;
2294 13 : InstrPtr curInstr;
2295 :
2296 13 : *e = 0;
2297 13 : nextChar(cntxt);
2298 228 : while ((c = currChar(cntxt))) {
2299 228 : if (e < start + 256 - 1)
2300 228 : *e++ = c;
2301 228 : nextChar(cntxt);
2302 228 : if (c == '\n' || c == '\r') {
2303 13 : *e = 0;
2304 13 : if (e > start)
2305 13 : e--;
2306 : /* prevChar(cntxt); */
2307 : break;
2308 : }
2309 : }
2310 13 : if (e > start)
2311 13 : *e = 0;
2312 13 : if (!skipcomments && e > start && curBlk->stop > 0) {
2313 13 : ValRecord cst;
2314 13 : if ((curInstr = newInstruction(curBlk, NULL, NULL)) == NULL) {
2315 1 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
2316 1 : continue;
2317 : }
2318 12 : curInstr->token = REMsymbol;
2319 12 : curInstr->barrier = 0;
2320 12 : if (VALinit(&cst, TYPE_str, start) == NULL) {
2321 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
2322 0 : freeInstruction(curInstr);
2323 0 : continue;
2324 : }
2325 12 : int cstidx = defConstant(curBlk, TYPE_str, &cst);
2326 12 : if (cstidx < 0) {
2327 0 : freeInstruction(curInstr);
2328 0 : continue;
2329 : }
2330 12 : getArg(curInstr, 0) = cstidx;
2331 12 : setVarDisabled(curBlk, getArg(curInstr, 0));
2332 12 : pushInstruction(curBlk, curInstr);
2333 : }
2334 12 : echoInput(cntxt);
2335 : }
2336 12 : continue;
2337 97 : case 'A':
2338 : case 'a':
2339 97 : if (MALkeyword(cntxt, "atom", 4) && parseAtom(cntxt) == 0)
2340 : break;
2341 92 : goto allLeft;
2342 1213 : case 'b':
2343 : case 'B':
2344 1213 : if (MALkeyword(cntxt, "barrier", 7)) {
2345 165 : cntxt->blkmode++;
2346 165 : cntrl = BARRIERsymbol;
2347 : }
2348 1213 : goto allLeft;
2349 469 : case 'C':
2350 : case 'c':
2351 469 : if (MALkeyword(cntxt, "command", 7)) {
2352 5 : Symbol p = parseCommandPattern(cntxt, COMMANDsymbol, address);
2353 5 : if (p) {
2354 5 : p->func->unsafe = unsafeProp;
2355 : }
2356 5 : if (inlineProp)
2357 0 : parseError(cntxt, "<identifier> expected\n");
2358 5 : inlineProp = 0;
2359 5 : unsafeProp = 0;
2360 5 : continue;
2361 : }
2362 464 : if (MALkeyword(cntxt, "catch", 5)) {
2363 24 : cntxt->blkmode++;
2364 24 : cntrl = CATCHsymbol;
2365 24 : goto allLeft;
2366 : }
2367 440 : goto allLeft;
2368 487 : case 'E':
2369 : case 'e':
2370 487 : if (MALkeyword(cntxt, "exit", 4)) {
2371 195 : if (cntxt->blkmode > 0)
2372 189 : cntxt->blkmode--;
2373 : cntrl = EXITsymbol;
2374 292 : } else if (parseEnd(cntxt)) {
2375 : break;
2376 : }
2377 283 : goto allLeft;
2378 333 : case 'F':
2379 : case 'f':
2380 333 : if (MALkeyword(cntxt, "function", 8)) {
2381 199 : MalBlkPtr p;
2382 199 : cntxt->blkmode++;
2383 199 : if ((p = parseFunction(cntxt, FUNCTIONsymbol))) {
2384 198 : p->unsafeProp = unsafeProp;
2385 198 : cntxt->curprg->def->inlineProp = inlineProp;
2386 198 : cntxt->curprg->def->unsafeProp = unsafeProp;
2387 198 : inlineProp = 0;
2388 198 : unsafeProp = 0;
2389 198 : break;
2390 : }
2391 : }
2392 135 : goto allLeft;
2393 1796 : case 'I':
2394 : case 'i':
2395 1796 : if (MALkeyword(cntxt, "inline", 6)) {
2396 23 : inlineProp = 1;
2397 23 : skipSpace(cntxt);
2398 23 : continue;
2399 1773 : } else if (MALkeyword(cntxt, "include", 7)) {
2400 2 : parseInclude(cntxt);
2401 2 : break;
2402 : }
2403 1771 : goto allLeft;
2404 81 : case 'L':
2405 : case 'l':
2406 81 : if (MALkeyword(cntxt, "leave", 5))
2407 40 : cntrl = LEAVEsymbol;
2408 81 : goto allLeft;
2409 79 : case 'M':
2410 : case 'm':
2411 79 : if (MALkeyword(cntxt, "module", 6) && parseModule(cntxt) == 0)
2412 : break;
2413 76 : goto allLeft;
2414 63 : case 'P':
2415 : case 'p':
2416 63 : if (MALkeyword(cntxt, "pattern", 7)) {
2417 8 : if (inlineProp)
2418 0 : parseError(cntxt, "parseError:INLINE ignored\n");
2419 8 : Symbol p = parseCommandPattern(cntxt, PATTERNsymbol, address);
2420 8 : if (p) {
2421 8 : p->func->unsafe = unsafeProp;
2422 : }
2423 8 : inlineProp = 0;
2424 8 : unsafeProp = 0;
2425 8 : continue;
2426 : }
2427 55 : goto allLeft;
2428 5686 : case 'R':
2429 : case 'r':
2430 5686 : if (MALkeyword(cntxt, "redo", 4)) {
2431 85 : cntrl = REDOsymbol;
2432 85 : goto allLeft;
2433 : }
2434 5604 : if (MALkeyword(cntxt, "raise", 5)) {
2435 7 : cntrl = RAISEsymbol;
2436 7 : goto allLeft;
2437 : }
2438 5597 : if (MALkeyword(cntxt, "return", 6)) {
2439 76 : cntrl = RETURNsymbol;
2440 : }
2441 5597 : goto allLeft;
2442 51 : case 'U':
2443 : case 'u':
2444 51 : if (MALkeyword(cntxt, "unsafe", 6)) {
2445 0 : unsafeProp = 1;
2446 0 : skipSpace(cntxt);
2447 0 : continue;
2448 : }
2449 : /* fall through */
2450 : default:
2451 51 : allLeft:
2452 11701 : parseAssign(cntxt, cntrl);
2453 11701 : cntrl = 0;
2454 : }
2455 : }
2456 10411 : skipSpace(cntxt);
2457 10411 : }
|