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 9449 : skipToEnd(Client cntxt)
67 : {
68 9449 : char c;
69 9819 : while ((c = *CURRENT(cntxt)) != ';' && c && c != '\n')
70 370 : nextChar(cntxt);
71 9449 : if (c && c != '\n')
72 9364 : nextChar(cntxt);
73 9449 : }
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 = GDKmalloc((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 : mb->errors = new;
123 71 : if (old) {
124 24 : new = stpcpy(new, old);
125 24 : GDKfree(old);
126 : }
127 71 : new = stpcpy(new, line);
128 71 : new = stpcpy(new, marker);
129 :
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 24273 : echoInput(Client cntxt)
141 : {
142 24273 : char *c = CURRENT(cntxt);
143 24273 : 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 24273 : }
151 :
152 : static inline void
153 215872 : skipSpace(Client cntxt)
154 : {
155 215872 : char *s = &currChar(cntxt);
156 253864 : for (;;) {
157 234868 : switch (*s++) {
158 18996 : case ' ':
159 : case '\t':
160 : case '\n':
161 : case '\r':
162 18996 : nextChar(cntxt);
163 18996 : break;
164 : default:
165 215872 : return;
166 : }
167 : }
168 : }
169 :
170 : static inline void
171 86968 : advance(Client cntxt, size_t length)
172 : {
173 86968 : cntxt->yycur += length;
174 86968 : skipSpace(cntxt);
175 27664 : }
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 53022 : idLength(Client cntxt)
331 : {
332 53022 : str s, t;
333 53022 : int len = 0;
334 :
335 53022 : skipSpace(cntxt);
336 53020 : s = CURRENT(cntxt);
337 53020 : t = s;
338 :
339 53020 : if (!idCharacter[(unsigned char) (*s)])
340 : return 0;
341 : /* avoid a clash with old temporaries */
342 48113 : if (s[0] == TMPMARKER)
343 65 : s[0] = REFMARKER;
344 : /* prepare escape of temporary names */
345 48113 : s++;
346 318781 : while (len < IDLENGTH && idCharacter2[(unsigned char) (*s)]) {
347 270668 : s++;
348 270668 : len++;
349 : }
350 48113 : if (len == IDLENGTH)
351 : // skip remainder
352 0 : while (idCharacter2[(unsigned char) (*s)])
353 0 : s++;
354 48113 : return (int) (s - t);
355 : }
356 :
357 : /* Simple type identifiers can not be marked with a type variable. */
358 : static size_t
359 5124 : typeidLength(Client cntxt)
360 : {
361 5124 : size_t l;
362 5124 : char id[IDLENGTH], *t = id;
363 5124 : str s;
364 5124 : skipSpace(cntxt);
365 5132 : s = CURRENT(cntxt);
366 :
367 5132 : if (!idCharacter[(unsigned char) (*s)])
368 : return 0;
369 5132 : l = 1;
370 5132 : *t++ = *s++;
371 5132 : while (l < IDLENGTH
372 15535 : && (idCharacter[(unsigned char) (*s)]
373 5161 : || isdigit((unsigned char) *s))) {
374 10403 : *t++ = *s++;
375 10403 : l++;
376 : }
377 : /* recognize the special type variables {any, any_<nr>} */
378 5132 : if (strncmp(id, "any", 3) == 0)
379 : return 3;
380 5092 : 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 24932 : MALlookahead(Client cntxt, str kw, int length)
400 : {
401 24932 : int i;
402 :
403 : /* avoid double test or use lowercase only. */
404 24932 : if (currChar(cntxt) == *kw &&
405 24201 : strncmp(CURRENT(cntxt), kw, length) == 0 &&
406 1065 : !idCharacter[(unsigned char) (CURRENT(cntxt)[length])] &&
407 1065 : !isdigit((unsigned char) (CURRENT(cntxt)[length]))) {
408 : return 1;
409 : }
410 : /* check for capitalized versions */
411 51048 : for (i = 0; i < length; i++)
412 51048 : 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 24909 : MALkeyword(Client cntxt, str kw, int length)
423 : {
424 24909 : skipSpace(cntxt);
425 24923 : if (MALlookahead(cntxt, kw, length)) {
426 1065 : advance(cntxt, length);
427 1065 : 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 1532 : keyphrase1(Client cntxt, str kw)
439 : {
440 1532 : skipSpace(cntxt);
441 1532 : if (currChar(cntxt) == *kw) {
442 1417 : advance(cntxt, 1);
443 1417 : return 1;
444 : }
445 : return 0;
446 : }
447 :
448 : static inline int
449 7337 : keyphrase2(Client cntxt, str kw)
450 : {
451 7337 : skipSpace(cntxt);
452 7337 : if (CURRENT(cntxt)[0] == kw[0] && CURRENT(cntxt)[1] == kw[1]) {
453 7015 : advance(cntxt, 2);
454 7015 : 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 2778 : stringLength(Client cntxt)
468 : {
469 2778 : int l = 0;
470 2778 : int quote = 0;
471 2778 : str s;
472 2778 : skipSpace(cntxt);
473 2781 : s = CURRENT(cntxt);
474 :
475 2781 : if (*s != '"')
476 : return 0;
477 166013 : for (s++; *s; l++, s++) {
478 166016 : if (quote) {
479 : quote = 0;
480 : } else {
481 143792 : if (*s == '"')
482 : break;
483 141007 : quote = *s == '\\';
484 : }
485 : }
486 2782 : 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 2780 : strCopy(Client cntxt, int length)
495 : {
496 2780 : str s;
497 2780 : int i;
498 :
499 2780 : i = length < 4 ? 4 : length;
500 2780 : s = GDKmalloc(i);
501 2786 : if (s == 0)
502 : return NULL;
503 2786 : memcpy(s, CURRENT(cntxt) + 1, (size_t) (length - 2));
504 2786 : s[length - 2] = 0;
505 2786 : mal_unquote(s);
506 2786 : 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 2781 : operatorLength(Client cntxt)
516 : {
517 2781 : int l = 0;
518 2781 : str s;
519 :
520 2781 : skipSpace(cntxt);
521 3006 : for (s = CURRENT(cntxt); *s; s++) {
522 2987 : if (opCharacter[(unsigned char) (*s)])
523 224 : l++;
524 : else
525 2763 : 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 31150 : cstToken(Client cntxt, ValPtr cst)
537 : {
538 31150 : int i = 0;
539 31150 : str s = CURRENT(cntxt);
540 :
541 31150 : *cst = (ValRecord) {
542 : .vtype = TYPE_int,
543 : .val.lval = 0,
544 : .bat = false,
545 : };
546 31150 : switch (*s) {
547 : case '{':
548 : case '[':
549 : /* JSON Literal */
550 : break;
551 2777 : case '"':
552 2777 : i = stringLength(cntxt);
553 2779 : VALset(cst, TYPE_str, strCopy(cntxt, i));
554 2778 : return i;
555 21 : case '-':
556 21 : i++;
557 21 : s++;
558 : /* fall through */
559 1586 : case '0':
560 1586 : 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 9813 : while (isdigit((unsigned char) *s)) {
581 5967 : i++;
582 5967 : s++;
583 : }
584 :
585 : /* fall through */
586 : case '.':
587 3846 : if (*s == '.' && isdigit((unsigned char) *(s + 1))) {
588 79 : i++;
589 79 : s++;
590 219 : while (isdigit((unsigned char) *s)) {
591 140 : i++;
592 140 : s++;
593 : }
594 79 : cst->vtype = TYPE_dbl;
595 : }
596 3846 : 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 3846 : 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 3846 : if (cst->vtype == TYPE_dbl) {
618 79 : size_t len = sizeof(dbl);
619 79 : double *pval = &cst->val.dval;
620 79 : if (dblFromStr(CURRENT(cntxt), &len, &pval, false) < 0) {
621 0 : parseError(cntxt, GDKerrbuf);
622 0 : return i;
623 : }
624 : }
625 3846 : 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 3769 : 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 3765 : 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 3763 : handleInts:
696 3763 : assert(cst->vtype != TYPE_lng);
697 : #ifdef HAVE_HGE
698 3763 : assert(cst->vtype != TYPE_hge);
699 : #endif
700 3763 : 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 3685 : if ((hge) GDK_int_min <= l && l <= (hge) GDK_int_max) {
708 3678 : cst->vtype = TYPE_int;
709 3678 : 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 979 : case 't':
744 979 : 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 5127 : typeAlias(Client cntxt, int tpe)
780 : {
781 5127 : int t;
782 :
783 5127 : 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 5124 : simpleTypeId(Client cntxt)
804 : {
805 5124 : int tpe;
806 5124 : size_t l;
807 :
808 5124 : nextChar(cntxt);
809 5124 : l = typeidLength(cntxt);
810 5131 : if (l == 0) {
811 0 : parseError(cntxt, "Type identifier expected\n");
812 0 : cntxt->yycur--; /* keep it */
813 0 : return -1;
814 : }
815 5131 : if (l == 3 && CURRENT(cntxt)[0] == 'b' && CURRENT(cntxt)[1] == 'a' && CURRENT(cntxt)[2] == 't')
816 : tpe = newBatType(TYPE_any);
817 : else
818 5131 : tpe = getAtomIndex(CURRENT(cntxt), l, -1);
819 5125 : 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 5125 : advance(cntxt, l);
825 5125 : return tpe;
826 : }
827 :
828 : static int
829 5271 : parseTypeId(Client cntxt)
830 : {
831 5271 : int i = TYPE_any, kt = 0;
832 5271 : char *s = CURRENT(cntxt);
833 5271 : int tt;
834 :
835 5271 : 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 4704 : if (currChar(cntxt) == ':') {
880 4704 : tt = simpleTypeId(cntxt);
881 4710 : kt = typeAlias(cntxt, tt);
882 4710 : if (kt < 0)
883 : return kt;
884 4710 : if (kt > 0)
885 15 : setTypeIndex(tt, kt);
886 4710 : return tt;
887 : }
888 0 : parseError(cntxt, "<type identifier> expected\n");
889 0 : return -1;
890 : }
891 :
892 : static inline int
893 9589 : typeElm(Client cntxt, int def)
894 : {
895 9589 : if (currChar(cntxt) != ':')
896 : return def; /* no type qualifier */
897 5273 : 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 218 : helpInfo(Client cntxt, str *help)
938 : {
939 218 : int l = 0;
940 218 : char c, *e, *s;
941 :
942 218 : 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 216 : } else if (currChar(cntxt) != ';')
965 0 : parseError(cntxt, "';' expected\n");
966 218 : }
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 18063 : term(Client cntxt, MalBlkPtr curBlk, InstrPtr *curInstr, int ret)
1025 : {
1026 18063 : int i, idx, free = 1;
1027 18063 : ValRecord cst;
1028 18063 : int cstidx = -1;
1029 18063 : malType tpe = TYPE_any;
1030 :
1031 18063 : if ((i = cstToken(cntxt, &cst))) {
1032 10424 : advance(cntxt, i);
1033 10416 : if (currChar(cntxt) != ':' && cst.vtype == TYPE_dbl
1034 43 : && cst.val.dval > FLT_MIN && cst.val.dval <= FLT_MAX) {
1035 42 : float dummy = (flt) cst.val.dval;
1036 42 : cst.vtype = TYPE_flt;
1037 42 : cst.val.fval = dummy;
1038 : }
1039 10416 : cstidx = fndConstant(curBlk, &cst, MAL_VAR_WINDOW);
1040 10417 : if (cstidx >= 0) {
1041 :
1042 1731 : 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 1563 : } 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 1731 : if (free && ATOMextern(cst.vtype) && cst.val.pval)
1063 45 : VALclear(&cst);
1064 1731 : *curInstr = pushArgument(curBlk, *curInstr, cstidx);
1065 1731 : return ret;
1066 : } else {
1067 : /* add a new constant literal, the :type could be erroneously be a coltype */
1068 8686 : tpe = typeElm(cntxt, cst.vtype);
1069 8689 : if (tpe < 0)
1070 : return 3;
1071 8689 : cst.bat = isaBatType(tpe);
1072 8689 : cstidx = defConstant(curBlk, tpe, &cst);
1073 8688 : if (cstidx < 0)
1074 : return 3;
1075 8684 : setPolymorphic(*curInstr, tpe, FALSE);
1076 8683 : *curInstr = pushArgument(curBlk, *curInstr, cstidx);
1077 8679 : return ret;
1078 : }
1079 7649 : } else if ((i = idLength(cntxt))) {
1080 7375 : 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 7370 : advance(cntxt, i);
1087 : }
1088 7375 : 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 7375 : *curInstr = pushArgument(curBlk, *curInstr, idx);
1099 274 : } else if (currChar(cntxt) == ':') {
1100 272 : tpe = typeElm(cntxt, TYPE_any);
1101 272 : if (tpe < 0)
1102 : return 3;
1103 272 : setPolymorphic(*curInstr, tpe, FALSE);
1104 272 : idx = newTypeVariable(curBlk, tpe);
1105 272 : *curInstr = pushArgument(curBlk, *curInstr, idx);
1106 272 : 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 (modnme != userRef)
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 (modnme != userRef)
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 211 : cntArgsReturns(Client cntxt, int *retc)
1247 : {
1248 211 : size_t yycur = cntxt->yycur;
1249 211 : int cnt = 0;
1250 211 : char ch;
1251 :
1252 211 : ch = currChar(cntxt);
1253 211 : 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 211 : advance(cntxt, 1);
1268 211 : ch = currChar(cntxt);
1269 211 : 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 198 : cnt++;
1289 198 : (*retc)++;
1290 : }
1291 211 : cntxt->yycur = yycur;
1292 211 : 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 (modnme != userRef && 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 && modnme != userRef)) {
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 (modnme == userRef) {
1610 10 : insertSymbol(cntxt->usermodule, curPrg);
1611 3 : } else if (getModule(modnme)) {
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 : char *comment = NULL;
1620 13 : helpInfo(cntxt, &comment);
1621 13 : curFunc->comment = comment;
1622 13 : return curPrg;
1623 : }
1624 :
1625 : static MalBlkPtr
1626 198 : fcnHeader(Client cntxt, int kind)
1627 : {
1628 198 : int l;
1629 198 : malType tpe;
1630 198 : const char *fnme;
1631 198 : const char *modnme = NULL;
1632 198 : char ch;
1633 198 : Symbol curPrg;
1634 198 : MalBlkPtr curBlk = 0;
1635 198 : InstrPtr curInstr;
1636 :
1637 198 : l = operatorLength(cntxt);
1638 198 : if (l == 0)
1639 198 : l = idLength(cntxt);
1640 198 : if (l == 0) {
1641 0 : parseError(cntxt, "<identifier> | <operator> expected\n");
1642 0 : return 0;
1643 : }
1644 :
1645 198 : fnme = putNameLen(((char *) CURRENT(cntxt)), l);
1646 198 : if (fnme == NULL) {
1647 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1648 0 : return NULL;
1649 : }
1650 198 : advance(cntxt, l);
1651 :
1652 198 : if (currChar(cntxt) == '.') {
1653 8 : nextChar(cntxt); /* skip '.' */
1654 8 : modnme = fnme;
1655 8 : if (modnme != userRef && getModule(modnme) == NULL) {
1656 1 : if (globalModule(modnme) == NULL) {
1657 0 : parseError(cntxt, "<module> name not defined\n");
1658 0 : return 0;
1659 : }
1660 : }
1661 8 : l = operatorLength(cntxt);
1662 8 : if (l == 0)
1663 8 : l = idLength(cntxt);
1664 8 : if (l == 0) {
1665 0 : parseError(cntxt, "<identifier> | <operator> expected\n");
1666 0 : return 0;
1667 : }
1668 8 : fnme = putNameLen(((char *) CURRENT(cntxt)), l);
1669 8 : if (fnme == NULL) {
1670 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1671 0 : return NULL;
1672 : }
1673 8 : advance(cntxt, l);
1674 : } else
1675 190 : modnme = cntxt->curmodule->name;
1676 :
1677 : /* temporary suspend capturing statements in main block */
1678 198 : if (cntxt->backup) {
1679 0 : parseError(cntxt, "mal_parser: unexpected recursion\n");
1680 0 : return 0;
1681 : }
1682 198 : if (currChar(cntxt) != '(') {
1683 0 : parseError(cntxt, "function header '(' expected\n");
1684 0 : return curBlk;
1685 : }
1686 198 : advance(cntxt, 1);
1687 :
1688 198 : assert(!cntxt->backup);
1689 198 : cntxt->backup = cntxt->curprg;
1690 198 : int retc = 0, nargs = cntArgsReturns(cntxt, &retc);
1691 198 : (void)retc;
1692 198 : if (nargs < 0)
1693 : return 0;
1694 : /* one extra for argument/return manipulation */
1695 198 : cntxt->curprg = newFunctionArgs(modnme, fnme, kind, nargs + 1);
1696 198 : if (cntxt->curprg == NULL) {
1697 : /* reinstate curprg to have a place for the error */
1698 0 : cntxt->curprg = cntxt->backup;
1699 0 : cntxt->backup = NULL;
1700 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1701 0 : return 0;
1702 : }
1703 198 : cntxt->curprg->def->errors = cntxt->backup->def->errors;
1704 198 : cntxt->backup->def->errors = 0;
1705 198 : curPrg = cntxt->curprg;
1706 198 : curBlk = curPrg->def;
1707 198 : curInstr = getInstrPtr(curBlk, 0);
1708 :
1709 : /* get calling parameters */
1710 198 : ch = currChar(cntxt);
1711 235 : while (ch != ')' && ch && !NL(ch)) {
1712 116 : curInstr = binding(cntxt, curBlk, curInstr, 1);
1713 : /* the last argument may be variable length */
1714 116 : if (MALkeyword(cntxt, "...", 3)) {
1715 1 : curInstr->varargs |= VARARGS;
1716 1 : setPolymorphic(curInstr, TYPE_any, TRUE);
1717 1 : break;
1718 : }
1719 115 : if ((ch = currChar(cntxt)) != ',') {
1720 78 : if (ch == ')')
1721 : break;
1722 0 : if (cntxt->backup)
1723 0 : curBlk = NULL;
1724 0 : parseError(cntxt, "',' expected\n");
1725 0 : return curBlk;
1726 : } else
1727 37 : nextChar(cntxt); /* skip ',' */
1728 37 : skipSpace(cntxt);
1729 37 : ch = currChar(cntxt);
1730 : }
1731 198 : if (currChar(cntxt) != ')') {
1732 0 : freeInstruction(curInstr);
1733 0 : if (cntxt->backup)
1734 0 : curBlk = NULL;
1735 0 : parseError(cntxt, "')' expected\n");
1736 0 : return curBlk;
1737 : }
1738 198 : advance(cntxt, 1); /* skip ')' */
1739 : /*
1740 : The return type is either a single type or multiple return type structure.
1741 : We simply keep track of the number of arguments added and
1742 : during the final phase reshuffle the return values to the beginning (?)
1743 : */
1744 198 : if (currChar(cntxt) == ':') {
1745 76 : tpe = typeElm(cntxt, TYPE_void);
1746 76 : setPolymorphic(curInstr, tpe, TRUE);
1747 76 : setVarType(curBlk, curInstr->argv[0], tpe);
1748 : /* we may be confronted by a variable target type list */
1749 76 : if (MALkeyword(cntxt, "...", 3)) {
1750 0 : curInstr->varargs |= VARRETS;
1751 0 : setPolymorphic(curInstr, TYPE_any, TRUE);
1752 : }
1753 :
1754 122 : } else if (keyphrase1(cntxt, "(")) { /* deal with compound return */
1755 10 : int retc = curInstr->argc, i1, i2 = 0;
1756 10 : int max;
1757 10 : short *newarg;
1758 : /* parse multi-target result */
1759 : /* skipSpace(cntxt); */
1760 10 : ch = currChar(cntxt);
1761 48 : while (ch != ')' && ch && !NL(ch)) {
1762 48 : curInstr = binding(cntxt, curBlk, curInstr, 0);
1763 : /* we may be confronted by a variable target type list */
1764 48 : if (MALkeyword(cntxt, "...", 3)) {
1765 0 : curInstr->varargs |= VARRETS;
1766 0 : setPolymorphic(curInstr, TYPE_any, TRUE);
1767 : }
1768 48 : if ((ch = currChar(cntxt)) != ',') {
1769 10 : if (ch == ')')
1770 : break;
1771 0 : if (cntxt->backup)
1772 0 : curBlk = NULL;
1773 0 : parseError(cntxt, "',' expected\n");
1774 0 : return curBlk;
1775 : } else {
1776 38 : nextChar(cntxt); /* skip ',' */
1777 : }
1778 38 : skipSpace(cntxt);
1779 38 : ch = currChar(cntxt);
1780 : }
1781 : /* re-arrange the parameters, results first */
1782 10 : max = curInstr->maxarg;
1783 10 : newarg = (short *) GDKmalloc(max * sizeof(curInstr->argv[0]));
1784 10 : if (newarg == NULL) {
1785 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1786 0 : if (cntxt->backup)
1787 0 : curBlk = NULL;
1788 0 : return curBlk;
1789 : }
1790 58 : for (i1 = retc; i1 < curInstr->argc; i1++)
1791 48 : newarg[i2++] = curInstr->argv[i1];
1792 10 : curInstr->retc = curInstr->argc - retc;
1793 22 : for (i1 = 1; i1 < retc; i1++)
1794 12 : newarg[i2++] = curInstr->argv[i1];
1795 10 : curInstr->argc = i2;
1796 20 : for (; i2 < max; i2++)
1797 10 : newarg[i2] = 0;
1798 80 : for (i1 = 0; i1 < max; i1++)
1799 70 : curInstr->argv[i1] = newarg[i1];
1800 10 : GDKfree(newarg);
1801 10 : if (currChar(cntxt) != ')') {
1802 0 : freeInstruction(curInstr);
1803 0 : if (cntxt->backup)
1804 0 : curBlk = NULL;
1805 0 : parseError(cntxt, "')' expected\n");
1806 0 : return curBlk;
1807 : }
1808 10 : nextChar(cntxt); /* skip ')' */
1809 : } else { /* default */
1810 112 : setVarType(curBlk, 0, TYPE_void);
1811 : }
1812 198 : if (curInstr != getInstrPtr(curBlk, 0)) {
1813 0 : freeInstruction(getInstrPtr(curBlk, 0));
1814 0 : putInstrPtr(curBlk, 0, curInstr);
1815 : }
1816 : return curBlk;
1817 : }
1818 :
1819 : static MalBlkPtr
1820 198 : parseFunction(Client cntxt, int kind)
1821 : {
1822 198 : MalBlkPtr curBlk = 0;
1823 :
1824 198 : curBlk = fcnHeader(cntxt, kind);
1825 198 : if (curBlk == NULL)
1826 : return curBlk;
1827 198 : if (MALkeyword(cntxt, "address", 7)) {
1828 : /* TO BE DEPRECATED */
1829 1 : str nme;
1830 1 : int i;
1831 1 : InstrPtr curInstr = getInstrPtr(curBlk, 0);
1832 1 : i = idLength(cntxt);
1833 1 : if (i == 0) {
1834 0 : parseError(cntxt, "<identifier> expected\n");
1835 0 : return 0;
1836 : }
1837 1 : nme = idCopy(cntxt, i);
1838 1 : if (nme == NULL) {
1839 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1840 0 : return 0;
1841 : }
1842 1 : curInstr->fcn = getAddress(getModuleId(curInstr), nme);
1843 1 : GDKfree(nme);
1844 1 : if (curInstr->fcn == NULL) {
1845 1 : parseError(cntxt, "<address> not found\n");
1846 1 : return 0;
1847 : }
1848 0 : skipSpace(cntxt);
1849 : }
1850 : /* block is terminated at the END statement */
1851 197 : helpInfo(cntxt, &curBlk->help);
1852 197 : return curBlk;
1853 : }
1854 :
1855 : /*
1856 : * Functions and factories end with a labeled end-statement.
1857 : * The routine below checks for misalignment of the closing statements.
1858 : * Any instruction parsed after the function block is considered an error.
1859 : */
1860 : static int
1861 291 : parseEnd(Client cntxt)
1862 : {
1863 291 : Symbol curPrg = 0;
1864 291 : size_t l;
1865 291 : InstrPtr sig;
1866 291 : str errors = MAL_SUCCEED, msg = MAL_SUCCEED;
1867 :
1868 291 : if (MALkeyword(cntxt, "end", 3)) {
1869 203 : curPrg = cntxt->curprg;
1870 203 : l = idLength(cntxt);
1871 203 : if (l == 0)
1872 37 : l = operatorLength(cntxt);
1873 203 : sig = getInstrPtr(cntxt->curprg->def, 0);
1874 203 : if (strncmp(CURRENT(cntxt), getModuleId(sig), l) == 0) {
1875 37 : advance(cntxt, l);
1876 37 : skipSpace(cntxt);
1877 37 : if (currChar(cntxt) == '.')
1878 0 : nextChar(cntxt);
1879 37 : skipSpace(cntxt);
1880 37 : l = idLength(cntxt);
1881 37 : if (l == 0)
1882 37 : l = operatorLength(cntxt);
1883 : }
1884 : /* parse fcn */
1885 203 : if ((l == strlen(curPrg->name) &&
1886 203 : strncmp(CURRENT(cntxt), curPrg->name, l) == 0) || l == 0)
1887 194 : advance(cntxt, l);
1888 : else
1889 9 : parseError(cntxt, "non matching end label\n");
1890 203 : pushEndInstruction(cntxt->curprg->def);
1891 203 : cntxt->blkmode = 0;
1892 203 : if (getModuleId(sig) == userRef)
1893 198 : insertSymbol(cntxt->usermodule, cntxt->curprg);
1894 : else
1895 5 : insertSymbol(getModule(getModuleId(sig)), cntxt->curprg);
1896 :
1897 203 : if (cntxt->curprg->def->errors) {
1898 11 : errors = cntxt->curprg->def->errors;
1899 11 : cntxt->curprg->def->errors = 0;
1900 : }
1901 : // check for newly identified errors
1902 203 : msg = chkProgram(cntxt->usermodule, cntxt->curprg->def);
1903 203 : if (errors == NULL)
1904 : errors = msg;
1905 : else
1906 11 : freeException(msg);
1907 203 : if (errors == NULL) {
1908 167 : errors = cntxt->curprg->def->errors;
1909 167 : cntxt->curprg->def->errors = 0;
1910 36 : } else if (cntxt->curprg->def->errors) {
1911 : //collect all errors for reporting
1912 0 : str new = GDKmalloc(strlen(errors) +
1913 : strlen(cntxt->curprg->def->errors) + 16);
1914 0 : if (new) {
1915 0 : char *p = stpcpy(new, errors);
1916 0 : if (p[-1] != '\n')
1917 0 : *p++ = '\n';
1918 0 : *p++ = '!';
1919 0 : strcpy(p, cntxt->curprg->def->errors);
1920 :
1921 0 : freeException(errors);
1922 0 : freeException(cntxt->curprg->def->errors);
1923 :
1924 0 : cntxt->curprg->def->errors = 0;
1925 0 : errors = new;
1926 : }
1927 : }
1928 :
1929 203 : if (cntxt->backup) {
1930 193 : cntxt->curprg = cntxt->backup;
1931 193 : cntxt->backup = 0;
1932 : } else {
1933 10 : str msg;
1934 10 : if ((msg = MSinitClientPrg(cntxt, cntxt->curmodule->name,
1935 : mainRef)) != MAL_SUCCEED) {
1936 0 : if (errors) {
1937 0 : str new = GDKmalloc(strlen(errors) + strlen(msg) + 3);
1938 0 : if (new) {
1939 0 : char *p = stpcpy(new, msg);
1940 0 : if (p[-1] != '\n')
1941 0 : *p++ = '\n';
1942 0 : strcpy(p, errors);
1943 0 : freeException(errors);
1944 0 : cntxt->curprg->def->errors = new;
1945 : } else {
1946 0 : cntxt->curprg->def->errors = errors;
1947 : }
1948 0 : freeException(msg);
1949 : } else {
1950 0 : cntxt->curprg->def->errors = msg;
1951 : }
1952 0 : return 1;
1953 : }
1954 : }
1955 : // pass collected errors to context
1956 203 : assert(cntxt->curprg->def->errors == NULL);
1957 203 : cntxt->curprg->def->errors = errors;
1958 203 : return 1;
1959 : }
1960 : return 0;
1961 : }
1962 :
1963 : /*
1964 : * Most instructions are simple assignments, possibly
1965 : * modified with a barrier/catch tag.
1966 : *
1967 : * The basic types are also predefined as a variable.
1968 : * This makes it easier to communicate types to MAL patterns.
1969 : */
1970 :
1971 : #define GETvariable(FREE) \
1972 : if ((varid = findVariableLength(curBlk, CURRENT(cntxt), l)) == -1) { \
1973 : varid = newVariable(curBlk, CURRENT(cntxt), l, TYPE_any); \
1974 : advance(cntxt, l); \
1975 : if (varid < 0) { FREE; return; } \
1976 : } else \
1977 : advance(cntxt, l);
1978 :
1979 : /* The parameter of parseArguments is the return value of the enclosing function. */
1980 : static int
1981 8929 : parseArguments(Client cntxt, MalBlkPtr curBlk, InstrPtr *curInstr)
1982 : {
1983 24503 : while (currChar(cntxt) != ')') {
1984 15575 : switch (term(cntxt, curBlk, curInstr, 0)) {
1985 : case 0:
1986 15574 : break;
1987 : case 2:
1988 : return 2;
1989 : case 3:
1990 : return 3;
1991 0 : case 4:
1992 0 : parseError(cntxt, "Argument type overwrites previous definition\n");
1993 0 : return 0;
1994 0 : default:
1995 0 : parseError(cntxt, "<factor> expected\n");
1996 0 : return 1;
1997 : }
1998 15574 : if (currChar(cntxt) == ',')
1999 7307 : advance(cntxt, 1);
2000 8267 : else if (currChar(cntxt) != ')') {
2001 0 : parseError(cntxt, "',' expected\n");
2002 0 : cntxt->yycur--; /* keep it */
2003 0 : break;
2004 : }
2005 : }
2006 8928 : if (currChar(cntxt) == ')')
2007 8928 : advance(cntxt, 1);
2008 : return 0;
2009 : }
2010 :
2011 : static void
2012 11706 : parseAssign(Client cntxt, int cntrl)
2013 : {
2014 11706 : InstrPtr curInstr;
2015 11706 : MalBlkPtr curBlk;
2016 11706 : Symbol curPrg;
2017 11706 : int i = 0, l, type = TYPE_any, varid = -1;
2018 11706 : const char *arg = 0;
2019 11706 : ValRecord cst;
2020 :
2021 11706 : curPrg = cntxt->curprg;
2022 11706 : curBlk = curPrg->def;
2023 11706 : if ((curInstr = newInstruction(curBlk, NULL, NULL)) == NULL) {
2024 13 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
2025 2384 : return;
2026 : }
2027 :
2028 11691 : if (cntrl) {
2029 589 : curInstr->token = ASSIGNsymbol;
2030 589 : curInstr->barrier = cntrl;
2031 : }
2032 :
2033 : /* start the parsing by recognition of the lhs of an assignment */
2034 11691 : if (currChar(cntxt) == '(') {
2035 : /* parsing multi-assignment */
2036 219 : advance(cntxt, 1);
2037 219 : curInstr->argc = 0; /*reset to handle pushArg correctly !! */
2038 219 : curInstr->retc = 0;
2039 1842 : while (currChar(cntxt) != ')' && currChar(cntxt)) {
2040 1623 : l = idLength(cntxt);
2041 1623 : i = cstToken(cntxt, &cst);
2042 1623 : if (l == 0 || i) {
2043 1 : parseError(cntxt, "<identifier> or <literal> expected\n");
2044 1 : freeInstruction(curInstr);
2045 1 : return;
2046 : }
2047 1622 : GETvariable(freeInstruction(curInstr));
2048 1622 : if (currChar(cntxt) == ':') {
2049 31 : type = typeElm(cntxt, getVarType(curBlk, varid));
2050 31 : if (type < 0)
2051 0 : goto part3;
2052 31 : setPolymorphic(curInstr, type, FALSE);
2053 31 : setVarType(curBlk, varid, type);
2054 : }
2055 1622 : curInstr = pushArgument(curBlk, curInstr, varid);
2056 1622 : curInstr->retc++;
2057 1622 : if (currChar(cntxt) == ')')
2058 : break;
2059 1404 : if (currChar(cntxt) == ',')
2060 1404 : keyphrase1(cntxt, ",");
2061 : }
2062 218 : advance(cntxt, 1); /* skip ')' */
2063 218 : if (curInstr->retc == 0) {
2064 : /* add dummy variable */
2065 0 : curInstr = pushArgument(curBlk, curInstr,
2066 : newTmpVariable(curBlk, TYPE_any));
2067 0 : curInstr->retc++;
2068 : }
2069 : } else {
2070 : /* are we dealing with a simple assignment? */
2071 11472 : l = idLength(cntxt);
2072 11465 : i = cstToken(cntxt, &cst);
2073 11461 : if (l == 0 || i) {
2074 : /* we haven't seen a target variable */
2075 : /* flow of control statements may end here. */
2076 : /* shouldn't allow for nameless controls todo */
2077 6 : if (i && cst.vtype == TYPE_str)
2078 0 : GDKfree(cst.val.sval);
2079 6 : if (cntrl == LEAVEsymbol || cntrl == REDOsymbol ||
2080 6 : cntrl == RETURNsymbol || cntrl == EXITsymbol) {
2081 4 : curInstr->argv[0] = getBarrierEnvelop(curBlk);
2082 4 : if (currChar(cntxt) != ';') {
2083 0 : freeInstruction(curInstr);
2084 0 : parseError(cntxt,
2085 : "<identifier> or <literal> expected in control statement\n");
2086 0 : return;
2087 : }
2088 4 : pushInstruction(curBlk, curInstr);
2089 4 : return;
2090 : }
2091 2 : getArg(curInstr, 0) = newTmpVariable(curBlk, TYPE_any);
2092 2 : freeInstruction(curInstr);
2093 2 : parseError(cntxt, "<identifier> or <literal> expected\n");
2094 2 : return;
2095 : }
2096 : /* Check if we are dealing with module.fcn call */
2097 11455 : if (CURRENT(cntxt)[l] == '.' || CURRENT(cntxt)[l] == '(') {
2098 4346 : curInstr->argv[0] = newTmpVariable(curBlk, TYPE_any);
2099 4346 : goto FCNcallparse;
2100 : }
2101 :
2102 : /* Get target variable details */
2103 7109 : GETvariable(freeInstruction(curInstr));
2104 7116 : if (!(currChar(cntxt) == ':' && CURRENT(cntxt)[1] == '=')) {
2105 445 : curInstr->argv[0] = varid;
2106 445 : if (currChar(cntxt) == ':') {
2107 164 : type = typeElm(cntxt, getVarType(curBlk, varid));
2108 164 : if (type < 0)
2109 0 : goto part3;
2110 164 : setPolymorphic(curInstr, type, FALSE);
2111 164 : setVarType(curBlk, varid, type);
2112 : }
2113 : }
2114 7116 : curInstr->argv[0] = varid;
2115 : }
2116 : /* look for assignment operator */
2117 7334 : if (!keyphrase2(cntxt, ":=")) {
2118 : /* no assignment !! a control variable is allowed */
2119 : /* for the case RETURN X, we normalize it to include the function arguments */
2120 325 : if (cntrl == RETURNsymbol) {
2121 31 : int e;
2122 31 : InstrPtr sig = getInstrPtr(curBlk, 0);
2123 31 : curInstr->retc = 0;
2124 64 : for (e = 0; e < sig->retc; e++)
2125 33 : curInstr = pushReturn(curBlk, curInstr, getArg(sig, e));
2126 : }
2127 :
2128 325 : goto part3;
2129 : }
2130 7016 : if (currChar(cntxt) == '(') {
2131 : /* parse multi assignment */
2132 9 : advance(cntxt, 1);
2133 9 : switch (parseArguments(cntxt, curBlk, &curInstr)) {
2134 0 : case 2:
2135 0 : goto part2;
2136 9 : default:
2137 : case 3:
2138 9 : goto part3;
2139 : }
2140 : /* unreachable */
2141 : }
2142 : /*
2143 : * We have so far the LHS part of an assignment. The remainder is
2144 : * either a simple term expression, a multi assignent, or the start
2145 : * of a function call.
2146 : */
2147 7007 : FCNcallparse:
2148 11353 : if ((l = idLength(cntxt)) && CURRENT(cntxt)[l] == '(') {
2149 : /* parseError(cntxt,"<module> expected\n"); */
2150 62 : setModuleId(curInstr, cntxt->curmodule->name);
2151 62 : i = l;
2152 62 : goto FCNcallparse2;
2153 11287 : } else if ((l = idLength(cntxt)) && CURRENT(cntxt)[l] == '.') {
2154 : /* continue with parsing a function/operator call */
2155 8858 : arg = putNameLen(CURRENT(cntxt), l);
2156 8860 : if (arg == NULL) {
2157 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
2158 0 : freeInstruction(curInstr);
2159 0 : return;
2160 : }
2161 8860 : advance(cntxt, l + 1); /* skip '.' too */
2162 8860 : setModuleId(curInstr, arg);
2163 8860 : i = idLength(cntxt);
2164 8860 : if (i == 0)
2165 68 : i = operatorLength(cntxt);
2166 8792 : FCNcallparse2:
2167 130 : if (i) {
2168 8922 : setFunctionId(curInstr, putNameLen(((char *) CURRENT(cntxt)), i));
2169 8922 : if (getFunctionId(curInstr) == NULL) {
2170 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
2171 0 : freeInstruction(curInstr);
2172 0 : return;
2173 : }
2174 8922 : advance(cntxt, i);
2175 : } else {
2176 0 : parseError(cntxt, "<functionname> expected\n");
2177 0 : freeInstruction(curInstr);
2178 0 : return;
2179 : }
2180 8922 : skipSpace(cntxt);
2181 8922 : if (currChar(cntxt) != '(') {
2182 2 : parseError(cntxt, "'(' expected\n");
2183 2 : freeInstruction(curInstr);
2184 2 : return;
2185 : }
2186 8920 : advance(cntxt, 1);
2187 8920 : switch (parseArguments(cntxt, curBlk, &curInstr)) {
2188 0 : case 2:
2189 0 : goto part2;
2190 8920 : default:
2191 : case 3:
2192 8920 : goto part3;
2193 : }
2194 : /* unreachable */
2195 : }
2196 : /* Handle the ordinary assignments and expressions */
2197 2432 : switch (term(cntxt, curBlk, &curInstr, 2)) {
2198 2253 : case 2:
2199 2253 : goto part2;
2200 3 : case 3:
2201 3 : goto part3;
2202 : }
2203 : part2: /* consume <operator><term> part of expression */
2204 2418 : if ((i = operatorLength(cntxt))) {
2205 : /* simple arithmetic operator expression */
2206 119 : setFunctionId(curInstr, putNameLen(((char *) CURRENT(cntxt)), i));
2207 119 : if (getFunctionId(curInstr) == NULL) {
2208 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
2209 0 : freeInstruction(curInstr);
2210 0 : return;
2211 : }
2212 119 : advance(cntxt, i);
2213 119 : curInstr->modname = calcRef;
2214 119 : if ((l = idLength(cntxt))
2215 53 : && !(l == 3 && strncmp(CURRENT(cntxt), "nil", 3) == 0)) {
2216 53 : GETvariable(freeInstruction(curInstr));
2217 53 : curInstr = pushArgument(curBlk, curInstr, varid);
2218 53 : goto part3;
2219 : }
2220 66 : switch (term(cntxt, curBlk, &curInstr, 3)) {
2221 0 : case 2:
2222 0 : goto part2;
2223 65 : case 3:
2224 65 : goto part3;
2225 : }
2226 1 : parseError(cntxt, "<term> expected\n");
2227 1 : freeInstruction(curInstr);
2228 1 : return;
2229 : } else {
2230 2303 : skipSpace(cntxt);
2231 2301 : if (currChar(cntxt) == '(') {
2232 0 : parseError(cntxt, "module name missing\n");
2233 0 : freeInstruction(curInstr);
2234 0 : return;
2235 2301 : } else if (currChar(cntxt) != ';' && currChar(cntxt) != '#') {
2236 1 : parseError(cntxt, "operator expected\n");
2237 1 : freeInstruction(curInstr);
2238 1 : return;
2239 : }
2240 2300 : pushInstruction(curBlk, curInstr);
2241 2300 : return;
2242 : }
2243 9375 : part3:
2244 9375 : skipSpace(cntxt);
2245 9374 : if (currChar(cntxt) != ';') {
2246 39 : parseError(cntxt, "';' expected\n");
2247 39 : skipToEnd(cntxt);
2248 39 : freeInstruction(curInstr);
2249 39 : return;
2250 : }
2251 9335 : skipToEnd(cntxt);
2252 9336 : if (cntrl == RETURNsymbol
2253 36 : && !(curInstr->token == ASSIGNsymbol || getModuleId(curInstr) != 0)) {
2254 0 : parseError(cntxt, "return assignment expected\n");
2255 0 : freeInstruction(curInstr);
2256 0 : return;
2257 : }
2258 9336 : pushInstruction(curBlk, curInstr);
2259 : }
2260 :
2261 : void
2262 10445 : parseMAL(Client cntxt, Symbol curPrg, int skipcomments, int lines,
2263 : MALfcn address)
2264 : {
2265 10445 : int cntrl = 0;
2266 : /*Symbol curPrg= cntxt->curprg; */
2267 10445 : char c;
2268 10445 : int inlineProp = 0, unsafeProp = 0;
2269 :
2270 10445 : (void) curPrg;
2271 10445 : echoInput(cntxt);
2272 : /* here the work takes place */
2273 41597 : while ((c = currChar(cntxt)) && lines > 0) {
2274 31140 : switch (c) {
2275 13815 : case '\n':
2276 : case '\r':
2277 : case '\f':
2278 13815 : lines -= c == '\n';
2279 13815 : nextChar(cntxt);
2280 13815 : echoInput(cntxt);
2281 13814 : continue;
2282 5175 : case ';':
2283 : case '\t':
2284 : case ' ':
2285 5175 : nextChar(cntxt);
2286 5175 : continue;
2287 13 : case '#':
2288 : { /* keep the full line comments */
2289 13 : char start[256], *e = start, c;
2290 13 : MalBlkPtr curBlk = cntxt->curprg->def;
2291 13 : InstrPtr curInstr;
2292 :
2293 13 : *e = 0;
2294 13 : nextChar(cntxt);
2295 228 : while ((c = currChar(cntxt))) {
2296 228 : if (e < start + 256 - 1)
2297 228 : *e++ = c;
2298 228 : nextChar(cntxt);
2299 228 : if (c == '\n' || c == '\r') {
2300 13 : *e = 0;
2301 13 : if (e > start)
2302 13 : e--;
2303 : /* prevChar(cntxt); */
2304 : break;
2305 : }
2306 : }
2307 13 : if (e > start)
2308 13 : *e = 0;
2309 13 : if (!skipcomments && e > start && curBlk->stop > 0) {
2310 13 : ValRecord cst;
2311 13 : if ((curInstr = newInstruction(curBlk, NULL, NULL)) == NULL) {
2312 1 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
2313 1 : continue;
2314 : }
2315 12 : curInstr->token = REMsymbol;
2316 12 : curInstr->barrier = 0;
2317 12 : if (VALinit(&cst, TYPE_str, start) == NULL) {
2318 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
2319 0 : freeInstruction(curInstr);
2320 0 : continue;
2321 : }
2322 12 : int cstidx = defConstant(curBlk, TYPE_str, &cst);
2323 12 : if (cstidx < 0) {
2324 0 : freeInstruction(curInstr);
2325 0 : continue;
2326 : }
2327 12 : getArg(curInstr, 0) = cstidx;
2328 12 : setVarDisabled(curBlk, getArg(curInstr, 0));
2329 12 : pushInstruction(curBlk, curInstr);
2330 : }
2331 12 : echoInput(cntxt);
2332 : }
2333 12 : continue;
2334 97 : case 'A':
2335 : case 'a':
2336 97 : if (MALkeyword(cntxt, "atom", 4) && parseAtom(cntxt) == 0)
2337 : break;
2338 92 : goto allLeft;
2339 1205 : case 'b':
2340 : case 'B':
2341 1205 : if (MALkeyword(cntxt, "barrier", 7)) {
2342 164 : cntxt->blkmode++;
2343 164 : cntrl = BARRIERsymbol;
2344 : }
2345 1205 : goto allLeft;
2346 459 : case 'C':
2347 : case 'c':
2348 459 : if (MALkeyword(cntxt, "command", 7)) {
2349 5 : Symbol p = parseCommandPattern(cntxt, COMMANDsymbol, address);
2350 5 : if (p) {
2351 5 : p->func->unsafe = unsafeProp;
2352 : }
2353 5 : if (inlineProp)
2354 0 : parseError(cntxt, "<identifier> expected\n");
2355 5 : inlineProp = 0;
2356 5 : unsafeProp = 0;
2357 5 : continue;
2358 : }
2359 454 : if (MALkeyword(cntxt, "catch", 5)) {
2360 24 : cntxt->blkmode++;
2361 24 : cntrl = CATCHsymbol;
2362 24 : goto allLeft;
2363 : }
2364 430 : goto allLeft;
2365 485 : case 'E':
2366 : case 'e':
2367 485 : if (MALkeyword(cntxt, "exit", 4)) {
2368 194 : if (cntxt->blkmode > 0)
2369 188 : cntxt->blkmode--;
2370 : cntrl = EXITsymbol;
2371 291 : } else if (parseEnd(cntxt)) {
2372 : break;
2373 : }
2374 282 : goto allLeft;
2375 332 : case 'F':
2376 : case 'f':
2377 332 : if (MALkeyword(cntxt, "function", 8)) {
2378 198 : MalBlkPtr p;
2379 198 : cntxt->blkmode++;
2380 198 : if ((p = parseFunction(cntxt, FUNCTIONsymbol))) {
2381 197 : p->unsafeProp = unsafeProp;
2382 197 : cntxt->curprg->def->inlineProp = inlineProp;
2383 197 : cntxt->curprg->def->unsafeProp = unsafeProp;
2384 197 : inlineProp = 0;
2385 197 : unsafeProp = 0;
2386 197 : break;
2387 : }
2388 : }
2389 135 : goto allLeft;
2390 1777 : case 'I':
2391 : case 'i':
2392 1777 : if (MALkeyword(cntxt, "inline", 6)) {
2393 23 : inlineProp = 1;
2394 23 : skipSpace(cntxt);
2395 23 : continue;
2396 1754 : } else if (MALkeyword(cntxt, "include", 7)) {
2397 2 : parseInclude(cntxt);
2398 2 : break;
2399 : }
2400 1752 : goto allLeft;
2401 80 : case 'L':
2402 : case 'l':
2403 80 : if (MALkeyword(cntxt, "leave", 5))
2404 40 : cntrl = LEAVEsymbol;
2405 80 : goto allLeft;
2406 77 : case 'M':
2407 : case 'm':
2408 77 : if (MALkeyword(cntxt, "module", 6) && parseModule(cntxt) == 0)
2409 : break;
2410 74 : goto allLeft;
2411 63 : case 'P':
2412 : case 'p':
2413 63 : if (MALkeyword(cntxt, "pattern", 7)) {
2414 8 : if (inlineProp)
2415 0 : parseError(cntxt, "parseError:INLINE ignored\n");
2416 8 : Symbol p = parseCommandPattern(cntxt, PATTERNsymbol, address);
2417 8 : if (p) {
2418 8 : p->func->unsafe = unsafeProp;
2419 : }
2420 8 : inlineProp = 0;
2421 8 : unsafeProp = 0;
2422 8 : continue;
2423 : }
2424 55 : goto allLeft;
2425 5749 : case 'R':
2426 : case 'r':
2427 5749 : if (MALkeyword(cntxt, "redo", 4)) {
2428 84 : cntrl = REDOsymbol;
2429 84 : goto allLeft;
2430 : }
2431 5675 : if (MALkeyword(cntxt, "raise", 5)) {
2432 7 : cntrl = RAISEsymbol;
2433 7 : goto allLeft;
2434 : }
2435 5671 : if (MALkeyword(cntxt, "return", 6)) {
2436 76 : cntrl = RETURNsymbol;
2437 : }
2438 5676 : goto allLeft;
2439 50 : case 'U':
2440 : case 'u':
2441 50 : if (MALkeyword(cntxt, "unsafe", 6)) {
2442 0 : unsafeProp = 1;
2443 0 : skipSpace(cntxt);
2444 0 : continue;
2445 : }
2446 : /* fall through */
2447 : default:
2448 50 : allLeft:
2449 11709 : parseAssign(cntxt, cntrl);
2450 11709 : cntrl = 0;
2451 : }
2452 : }
2453 10457 : skipSpace(cntxt);
2454 10460 : }
|