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 9499 : skipToEnd(Client cntxt)
67 : {
68 9499 : char c;
69 9869 : while ((c = *CURRENT(cntxt)) != ';' && c && c != '\n')
70 370 : nextChar(cntxt);
71 9499 : if (c && c != '\n')
72 9413 : nextChar(cntxt);
73 9499 : }
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 24322 : echoInput(Client cntxt)
141 : {
142 24322 : char *c = CURRENT(cntxt);
143 24322 : 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 24322 : }
151 :
152 : static inline void
153 216463 : skipSpace(Client cntxt)
154 : {
155 216463 : char *s = &currChar(cntxt);
156 254523 : for (;;) {
157 235493 : switch (*s++) {
158 19030 : case ' ':
159 : case '\t':
160 : case '\n':
161 : case '\r':
162 19030 : nextChar(cntxt);
163 19030 : break;
164 : default:
165 216463 : return;
166 : }
167 : }
168 : }
169 :
170 : static inline void
171 86828 : advance(Client cntxt, size_t length)
172 : {
173 86828 : cntxt->yycur += length;
174 86828 : skipSpace(cntxt);
175 27754 : }
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 53248 : idLength(Client cntxt)
331 : {
332 53248 : str s, t;
333 53248 : int len = 0;
334 :
335 53248 : skipSpace(cntxt);
336 53248 : s = CURRENT(cntxt);
337 53248 : t = s;
338 :
339 53248 : if (!idCharacter[(unsigned char) (*s)])
340 : return 0;
341 : /* avoid a clash with old temporaries */
342 48350 : if (s[0] == TMPMARKER)
343 65 : s[0] = REFMARKER;
344 : /* prepare escape of temporary names */
345 48350 : s++;
346 322665 : while (len < IDLENGTH && idCharacter2[(unsigned char) (*s)]) {
347 274315 : s++;
348 274315 : len++;
349 : }
350 48350 : if (len == IDLENGTH)
351 : // skip remainder
352 0 : while (idCharacter2[(unsigned char) (*s)])
353 0 : s++;
354 48350 : return (int) (s - t);
355 : }
356 :
357 : /* Simple type identifiers can not be marked with a type variable. */
358 : static size_t
359 5271 : typeidLength(Client cntxt)
360 : {
361 5271 : size_t l;
362 5271 : char id[IDLENGTH], *t = id;
363 5271 : str s;
364 5271 : skipSpace(cntxt);
365 5271 : s = CURRENT(cntxt);
366 :
367 5271 : if (!idCharacter[(unsigned char) (*s)])
368 : return 0;
369 5271 : l = 1;
370 5271 : *t++ = *s++;
371 5271 : while (l < IDLENGTH
372 15982 : && (idCharacter[(unsigned char) (*s)]
373 5310 : || isdigit((unsigned char) *s))) {
374 10711 : *t++ = *s++;
375 10711 : l++;
376 : }
377 : /* recognize the special type variables {any, any_<nr>} */
378 5271 : if (strncmp(id, "any", 3) == 0)
379 : return 3;
380 5231 : 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 24927 : MALlookahead(Client cntxt, str kw, int length)
400 : {
401 24927 : int i;
402 :
403 : /* avoid double test or use lowercase only. */
404 24927 : if (currChar(cntxt) == *kw &&
405 24204 : 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 51036 : for (i = 0; i < length; i++)
412 51036 : 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 24928 : MALkeyword(Client cntxt, str kw, int length)
423 : {
424 24928 : skipSpace(cntxt);
425 24928 : 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 7359 : keyphrase2(Client cntxt, str kw)
450 : {
451 7359 : skipSpace(cntxt);
452 7359 : if (CURRENT(cntxt)[0] == kw[0] && CURRENT(cntxt)[1] == kw[1]) {
453 7033 : advance(cntxt, 2);
454 7033 : return 1;
455 : }
456 : return 0;
457 : }
458 :
459 : /*
460 : * A similar approach is used for string literals.
461 : * Beware, string lengths returned include the
462 : * brackets and escapes. They are eaten away in strCopy.
463 : * We should provide the C-method to split strings and
464 : * concatenate them upon retrieval[todo]
465 : */
466 : static int
467 2782 : stringLength(Client cntxt)
468 : {
469 2782 : int l = 0;
470 2782 : int quote = 0;
471 2782 : str s;
472 2782 : skipSpace(cntxt);
473 2782 : s = CURRENT(cntxt);
474 :
475 2782 : if (*s != '"')
476 : return 0;
477 167146 : for (s++; *s; l++, s++) {
478 167146 : if (quote) {
479 : quote = 0;
480 : } else {
481 144783 : if (*s == '"')
482 : break;
483 142001 : 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 2782 : strCopy(Client cntxt, int length)
495 : {
496 2782 : str s;
497 2782 : int i;
498 :
499 2782 : i = length < 4 ? 4 : length;
500 2782 : s = GDKmalloc(i);
501 2782 : if (s == 0)
502 : return NULL;
503 2782 : memcpy(s, CURRENT(cntxt) + 1, (size_t) (length - 2));
504 2782 : s[length - 2] = 0;
505 2782 : mal_unquote(s);
506 2782 : 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 2785 : operatorLength(Client cntxt)
516 : {
517 2785 : int l = 0;
518 2785 : str s;
519 :
520 2785 : skipSpace(cntxt);
521 3012 : for (s = CURRENT(cntxt); *s; s++) {
522 2992 : if (opCharacter[(unsigned char) (*s)])
523 227 : l++;
524 : else
525 2765 : 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 31255 : cstToken(Client cntxt, ValPtr cst)
537 : {
538 31255 : int i = 0;
539 31255 : str s = CURRENT(cntxt);
540 :
541 31255 : cst->vtype = TYPE_int;
542 31255 : cst->val.lval = 0;
543 31255 : switch (*s) {
544 : case '{':
545 : case '[':
546 : /* JSON Literal */
547 : break;
548 2780 : case '"':
549 2780 : i = stringLength(cntxt);
550 2780 : VALset(cst, TYPE_str, strCopy(cntxt, i));
551 2780 : return i;
552 21 : case '-':
553 21 : i++;
554 21 : s++;
555 : /* fall through */
556 1586 : case '0':
557 1586 : if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
558 : /* deal with hex */
559 0 : i += 2;
560 0 : s += 2;
561 0 : while (isxdigit((unsigned char) *s)) {
562 0 : i++;
563 0 : s++;
564 : }
565 0 : goto handleInts;
566 : }
567 : /* fall through */
568 : case '1':
569 : case '2':
570 : case '3':
571 : case '4':
572 : case '5':
573 : case '6':
574 : case '7':
575 : case '8':
576 : case '9':
577 9927 : while (isdigit((unsigned char) *s)) {
578 6066 : i++;
579 6066 : s++;
580 : }
581 :
582 : /* fall through */
583 : case '.':
584 3861 : if (*s == '.' && isdigit((unsigned char) *(s + 1))) {
585 83 : i++;
586 83 : s++;
587 227 : while (isdigit((unsigned char) *s)) {
588 144 : i++;
589 144 : s++;
590 : }
591 83 : cst->vtype = TYPE_dbl;
592 : }
593 3861 : if (*s == 'e' || *s == 'E') {
594 4 : i++;
595 4 : s++;
596 4 : if (*s == '-' || *s == '+') {
597 2 : i++;
598 2 : s++;
599 : }
600 4 : cst->vtype = TYPE_dbl;
601 8 : while (isdigit((unsigned char) *s)) {
602 4 : i++;
603 4 : s++;
604 : }
605 : }
606 3861 : if (cst->vtype == TYPE_flt) {
607 0 : size_t len = sizeof(flt);
608 0 : float *pval = &cst->val.fval;
609 0 : if (fltFromStr(CURRENT(cntxt), &len, &pval, false) < 0) {
610 0 : parseError(cntxt, GDKerrbuf);
611 0 : return i;
612 : }
613 : }
614 3861 : if (cst->vtype == TYPE_dbl) {
615 83 : size_t len = sizeof(dbl);
616 83 : double *pval = &cst->val.dval;
617 83 : if (dblFromStr(CURRENT(cntxt), &len, &pval, false) < 0) {
618 0 : parseError(cntxt, GDKerrbuf);
619 0 : return i;
620 : }
621 : }
622 3861 : if (*s == '@') {
623 77 : size_t len = sizeof(lng);
624 77 : lng l, *pval = &l;
625 77 : if (lngFromStr(CURRENT(cntxt), &len, &pval, false) < 0) {
626 0 : parseError(cntxt, GDKerrbuf);
627 0 : return i;
628 : }
629 77 : if (is_lng_nil(l) || l < 0
630 : #if SIZEOF_OID < SIZEOF_LNG
631 : || l > GDK_oid_max
632 : #endif
633 : )
634 0 : cst->val.oval = oid_nil;
635 : else
636 77 : cst->val.oval = (oid) l;
637 77 : cst->vtype = TYPE_oid;
638 77 : i++;
639 77 : s++;
640 154 : while (isdigit((unsigned char) *s)) {
641 77 : i++;
642 77 : s++;
643 : }
644 77 : return i;
645 : }
646 3784 : if (*s == 'L') {
647 4 : if (cst->vtype == TYPE_int)
648 4 : cst->vtype = TYPE_lng;
649 4 : if (cst->vtype == TYPE_flt)
650 0 : cst->vtype = TYPE_dbl;
651 4 : i++;
652 4 : s++;
653 4 : if (*s == 'L') {
654 0 : i++;
655 0 : s++;
656 : }
657 4 : if (cst->vtype == TYPE_dbl) {
658 0 : size_t len = sizeof(dbl);
659 0 : dbl *pval = &cst->val.dval;
660 0 : if (dblFromStr(CURRENT(cntxt), &len, &pval, false) < 0) {
661 0 : parseError(cntxt, GDKerrbuf);
662 0 : return i;
663 : }
664 : } else {
665 4 : size_t len = sizeof(lng);
666 4 : lng *pval = &cst->val.lval;
667 4 : if (lngFromStr(CURRENT(cntxt), &len, &pval, false) < 0) {
668 0 : parseError(cntxt, GDKerrbuf);
669 0 : return i;
670 : }
671 : }
672 4 : return i;
673 : }
674 : #ifdef HAVE_HGE
675 3780 : if (*s == 'H' && cst->vtype == TYPE_int) {
676 2 : size_t len = sizeof(hge);
677 2 : hge *pval = &cst->val.hval;
678 2 : cst->vtype = TYPE_hge;
679 2 : i++;
680 2 : s++;
681 2 : if (*s == 'H') {
682 0 : i++;
683 0 : s++;
684 : }
685 2 : if (hgeFromStr(CURRENT(cntxt), &len, &pval, false) < 0) {
686 0 : parseError(cntxt, GDKerrbuf);
687 0 : return i;
688 : }
689 : return i;
690 : }
691 : #endif
692 3778 : handleInts:
693 3778 : assert(cst->vtype != TYPE_lng);
694 : #ifdef HAVE_HGE
695 3778 : assert(cst->vtype != TYPE_hge);
696 : #endif
697 3778 : if (cst->vtype == TYPE_int) {
698 : #ifdef HAVE_HGE
699 3695 : size_t len = sizeof(hge);
700 3695 : hge l, *pval = &l;
701 3695 : if (hgeFromStr(CURRENT(cntxt), &len, &pval, false) < 0)
702 0 : l = hge_nil;
703 :
704 3695 : if ((hge) GDK_int_min <= l && l <= (hge) GDK_int_max) {
705 3688 : cst->vtype = TYPE_int;
706 3688 : cst->val.ival = (int) l;
707 7 : } else if ((hge) GDK_lng_min <= l && l <= (hge) GDK_lng_max) {
708 3 : cst->vtype = TYPE_lng;
709 3 : cst->val.lval = (lng) l;
710 : } else {
711 4 : cst->vtype = TYPE_hge;
712 4 : cst->val.hval = l;
713 : }
714 : #else
715 : size_t len = sizeof(lng);
716 : lng l, *pval = &l;
717 : if (lngFromStr(CURRENT(cntxt), &len, &pval, false) < 0)
718 : l = lng_nil;
719 :
720 : if ((lng) GDK_int_min <= l && l <= (lng) GDK_int_max) {
721 : cst->vtype = TYPE_int;
722 : cst->val.ival = (int) l;
723 : } else {
724 : cst->vtype = TYPE_lng;
725 : cst->val.lval = l;
726 : }
727 : #endif
728 : }
729 : return i;
730 :
731 1260 : case 'f':
732 1260 : if (strncmp(s, "false", 5) == 0 && !isalnum((unsigned char) *(s + 5)) &&
733 : *(s + 5) != '_') {
734 984 : cst->vtype = TYPE_bit;
735 984 : cst->val.btval = 0;
736 984 : cst->len = 1;
737 984 : return 5;
738 : }
739 : return 0;
740 983 : case 't':
741 983 : if (strncmp(s, "true", 4) == 0 && !isalnum((unsigned char) *(s + 4)) &&
742 : *(s + 4) != '_') {
743 716 : cst->vtype = TYPE_bit;
744 716 : cst->val.btval = 1;
745 716 : cst->len = 1;
746 716 : return 4;
747 : }
748 : return 0;
749 2157 : case 'n':
750 2157 : if (strncmp(s, "nil", 3) == 0 && !isalnum((unsigned char) *(s + 3)) &&
751 : *(s + 3) != '_') {
752 2090 : cst->vtype = TYPE_void;
753 2090 : cst->len = 0;
754 2090 : cst->val.oval = oid_nil;
755 2090 : return 3;
756 : }
757 : }
758 : return 0;
759 : }
760 :
761 : #define cstCopy(C,I) idCopy(C,I)
762 :
763 : /* Type qualifier
764 : * Types are recognized as identifiers preceded by a colon.
765 : *
766 : * The type ANY matches any type specifier.
767 : * Appending it with an alias turns it into a type variable.
768 : * The type alias is \$DIGIT (1-9) and can be used to relate types
769 : * by type equality.
770 : * The type variable are defined within the context of a function
771 : * scope.
772 : * Additional information, such as a repetition factor,
773 : * encoding tables, or type dependency should be modeled as properties.
774 : *
775 : * It would make more sense for tpe parameter to be an int, but simpleTypeId returns a size_t
776 : */
777 : static int
778 5271 : typeAlias(Client cntxt, int tpe)
779 : {
780 5271 : int t;
781 :
782 5271 : if (tpe != TYPE_any)
783 : return -1;
784 40 : if (currChar(cntxt) == TMPMARKER) {
785 37 : nextChar(cntxt);
786 37 : t = currChar(cntxt) - '0';
787 37 : if (t <= 0 || t > 9)
788 0 : parseError(cntxt, "[1-9] expected\n");
789 : else
790 37 : nextChar(cntxt);
791 37 : return t;
792 : }
793 : return -1;
794 : }
795 :
796 : /*
797 : * The simple type analysis currently assumes a proper type identifier.
798 : * We should change getMALtype to return a failure instead.
799 : */
800 : static int
801 5271 : simpleTypeId(Client cntxt)
802 : {
803 5271 : int tpe;
804 5271 : size_t l;
805 :
806 5271 : nextChar(cntxt);
807 5271 : l = typeidLength(cntxt);
808 5271 : if (l == 0) {
809 0 : parseError(cntxt, "Type identifier expected\n");
810 0 : cntxt->yycur--; /* keep it */
811 0 : return -1;
812 : }
813 5271 : tpe = getAtomIndex(CURRENT(cntxt), l, -1);
814 5271 : if (tpe < 0) {
815 0 : parseError(cntxt, "Type identifier expected\n");
816 0 : cntxt->yycur -= l; /* keep it */
817 0 : return TYPE_void;
818 : }
819 5271 : advance(cntxt, l);
820 5271 : return tpe;
821 : }
822 :
823 : static int
824 5271 : parseTypeId(Client cntxt)
825 : {
826 5271 : int i = TYPE_any, kt = 0;
827 5271 : char *s = CURRENT(cntxt);
828 5271 : int tt;
829 :
830 5271 : if (strncmp(s, ":bat[", 5) == 0 || strncmp(s, ":BAT[", 5) == 0) {
831 : /* parse :bat[:type] */
832 421 : advance(cntxt, 5);
833 421 : if (currChar(cntxt) == ':') {
834 421 : tt = simpleTypeId(cntxt);
835 421 : kt = typeAlias(cntxt, tt);
836 : } else {
837 0 : parseError(cntxt, "':bat[:any]' expected\n");
838 0 : return -1;
839 : }
840 :
841 421 : i = newBatType(tt);
842 421 : if (kt > 0)
843 22 : setTypeIndex(i, kt);
844 :
845 421 : if (currChar(cntxt) != ']')
846 0 : parseError(cntxt, "']' expected\n");
847 421 : nextChar(cntxt); // skip ']'
848 421 : skipSpace(cntxt);
849 421 : return i;
850 : }
851 4850 : if (currChar(cntxt) == ':') {
852 4850 : tt = simpleTypeId(cntxt);
853 4850 : kt = typeAlias(cntxt, tt);
854 4850 : if (kt > 0)
855 15 : setTypeIndex(tt, kt);
856 4850 : return tt;
857 : }
858 0 : parseError(cntxt, "<type identifier> expected\n");
859 0 : return -1;
860 : }
861 :
862 : static inline int
863 9645 : typeElm(Client cntxt, int def)
864 : {
865 9645 : if (currChar(cntxt) != ':')
866 : return def; /* no type qualifier */
867 5266 : return parseTypeId(cntxt);
868 : }
869 :
870 : /*
871 : * The Parser
872 : * The client is responsible to collect the
873 : * input for parsing in a single string before calling the parser.
874 : * Once the input is available parsing runs in a critial section for
875 : * a single client thread.
876 : *
877 : * The parser uses the rigid structure of the language to speedup
878 : * analysis. In particular, each input line is translated into
879 : * a MAL instruction record as quickly as possible. Its context is
880 : * manipulated during the parsing process, by keeping the curPrg,
881 : * curBlk, and curInstr variables.
882 : *
883 : * The language statements of the parser are gradually introduced, with
884 : * the overall integration framework last.
885 : * The convention is to return a zero when an error has been
886 : * reported or when the structure can not be recognized.
887 : * Furthermore, we assume that blancs have been skipped before entering
888 : * recognition of a new token.
889 : *
890 : * Module statement.
891 : * The module and import commands have immediate effect.
892 : * The module statement switches the location for symbol table update
893 : * to a specific named area. The effect is that all definitions may become
894 : * globally known (?) and symbol table should be temporarilly locked
895 : * for updates by concurrent users.
896 : *
897 : * @multitable @columnfractions 0.15 0.8
898 : * @item moduleStmt
899 : * @tab : @sc{atom} ident [':'ident]
900 : * @item
901 : * @tab | @sc{module} ident
902 : * @end multitable
903 : *
904 : * An atom statement does not introduce a new module.
905 : */
906 : static void
907 219 : helpInfo(Client cntxt, str *help)
908 : {
909 219 : int l = 0;
910 219 : char c, *e, *s;
911 :
912 219 : if (MALkeyword(cntxt, "comment", 7)) {
913 2 : skipSpace(cntxt);
914 : // The comment is either a quoted string or all characters up to the next semicolon
915 2 : c = currChar(cntxt);
916 2 : if (c != '"') {
917 : e = s = CURRENT(cntxt);
918 0 : for (; *e; l++, e++)
919 0 : if (*e == ';')
920 : break;
921 0 : *help = strCopy(cntxt, l);
922 0 : skipToEnd(cntxt);
923 : } else {
924 2 : if ((l = stringLength(cntxt))) {
925 2 : GDKfree(*help);
926 2 : *help = strCopy(cntxt, l);
927 2 : if (*help)
928 2 : advance(cntxt, l - 1);
929 2 : skipToEnd(cntxt);
930 : } else {
931 0 : parseError(cntxt, "<string> expected\n");
932 : }
933 : }
934 217 : } else if (currChar(cntxt) != ';')
935 0 : parseError(cntxt, "';' expected\n");
936 219 : }
937 :
938 : static InstrPtr
939 186 : binding(Client cntxt, MalBlkPtr curBlk, InstrPtr curInstr, int flag)
940 : {
941 186 : int l, varid = -1;
942 186 : malType type;
943 :
944 186 : l = idLength(cntxt);
945 186 : if (l > 0) {
946 145 : varid = findVariableLength(curBlk, CURRENT(cntxt), l);
947 145 : if (varid < 0) {
948 145 : varid = newVariable(curBlk, CURRENT(cntxt), l, TYPE_any);
949 145 : advance(cntxt, l);
950 145 : if (varid < 0)
951 : return curInstr;
952 145 : type = typeElm(cntxt, TYPE_any);
953 145 : if (isPolymorphic(type))
954 30 : setPolymorphic(curInstr, type, TRUE);
955 145 : setVarType(curBlk, varid, type);
956 0 : } else if (flag) {
957 0 : parseError(cntxt, "Argument defined twice\n");
958 0 : typeElm(cntxt, getVarType(curBlk, varid));
959 : } else {
960 0 : advance(cntxt, l);
961 0 : type = typeElm(cntxt, getVarType(curBlk, varid));
962 0 : if (type != getVarType(curBlk, varid))
963 0 : parseError(cntxt, "Incompatible argument type\n");
964 0 : if (isPolymorphic(type))
965 0 : setPolymorphic(curInstr, type, TRUE);
966 0 : setVarType(curBlk, varid, type);
967 : }
968 41 : } else if (currChar(cntxt) == ':') {
969 41 : type = typeElm(cntxt, TYPE_any);
970 41 : varid = newTmpVariable(curBlk, type);
971 41 : if (varid < 0)
972 : return curInstr;
973 41 : if (isPolymorphic(type))
974 2 : setPolymorphic(curInstr, type, TRUE);
975 41 : setVarType(curBlk, varid, type);
976 : } else {
977 0 : parseError(cntxt, "argument expected\n");
978 0 : return curInstr;
979 : }
980 186 : if (varid >= 0)
981 186 : curInstr = pushArgument(curBlk, curInstr, varid);
982 186 : return curInstr;
983 : }
984 :
985 : /*
986 : * At this stage the LHS part has been parsed and the destination
987 : * variables have been set. Next step is to parse the expression,
988 : * which starts with an operand.
989 : * This code is used in both positions of the expression
990 : */
991 : static int
992 18118 : term(Client cntxt, MalBlkPtr curBlk, InstrPtr *curInstr, int ret)
993 : {
994 18118 : int i, idx, free = 1;
995 18118 : ValRecord cst;
996 18118 : int cstidx = -1;
997 18118 : malType tpe = TYPE_any;
998 :
999 18118 : if ((i = cstToken(cntxt, &cst))) {
1000 10428 : advance(cntxt, i);
1001 10428 : if (currChar(cntxt) != ':' && cst.vtype == TYPE_dbl
1002 47 : && cst.val.dval > FLT_MIN && cst.val.dval <= FLT_MAX) {
1003 46 : float dummy = (flt) cst.val.dval;
1004 46 : cst.vtype = TYPE_flt;
1005 46 : cst.val.fval = dummy;
1006 : }
1007 10428 : cstidx = fndConstant(curBlk, &cst, MAL_VAR_WINDOW);
1008 10428 : if (cstidx >= 0) {
1009 :
1010 1529 : if (currChar(cntxt) == ':') {
1011 6 : tpe = typeElm(cntxt, getVarType(curBlk, cstidx));
1012 6 : if (tpe < 0)
1013 : return 3;
1014 6 : if (tpe != getVarType(curBlk, cstidx)) {
1015 1 : cstidx = defConstant(curBlk, tpe, &cst);
1016 1 : if (cstidx < 0)
1017 : return 3;
1018 1 : setPolymorphic(*curInstr, tpe, FALSE);
1019 1 : free = 0;
1020 : }
1021 1523 : } else if (cst.vtype != getVarType(curBlk, cstidx)) {
1022 0 : cstidx = defConstant(curBlk, cst.vtype, &cst);
1023 0 : if (cstidx < 0)
1024 : return 3;
1025 0 : setPolymorphic(*curInstr, cst.vtype, FALSE);
1026 0 : free = 0;
1027 : }
1028 : /* protect against leaks coming from constant reuse */
1029 1529 : if (free && ATOMextern(cst.vtype) && cst.val.pval)
1030 45 : VALclear(&cst);
1031 1529 : *curInstr = pushArgument(curBlk, *curInstr, cstidx);
1032 1529 : return ret;
1033 : } else {
1034 : /* add a new constant literal, the :type could be erroneously be a coltype */
1035 8899 : tpe = typeElm(cntxt, cst.vtype);
1036 8899 : if (tpe < 0)
1037 : return 3;
1038 8899 : cstidx = defConstant(curBlk, tpe, &cst);
1039 8899 : if (cstidx < 0)
1040 : return 3;
1041 8895 : setPolymorphic(*curInstr, tpe, FALSE);
1042 8895 : *curInstr = pushArgument(curBlk, *curInstr, cstidx);
1043 8895 : return ret;
1044 : }
1045 7690 : } else if ((i = idLength(cntxt))) {
1046 7414 : if ((idx = findVariableLength(curBlk, CURRENT(cntxt), i)) == -1) {
1047 5 : idx = newVariable(curBlk, CURRENT(cntxt), i, TYPE_any);
1048 5 : advance(cntxt, i);
1049 5 : if (idx < 0)
1050 : return 0;
1051 : } else {
1052 7409 : advance(cntxt, i);
1053 : }
1054 7414 : if (currChar(cntxt) == ':') {
1055 : /* skip the type description */
1056 2 : tpe = typeElm(cntxt, TYPE_any);
1057 2 : if (getVarType(curBlk, idx) == TYPE_any)
1058 1 : setVarType(curBlk, idx, tpe);
1059 1 : else if (getVarType(curBlk, idx) != tpe) {
1060 : /* non-matching types */
1061 : return 4;
1062 : }
1063 : }
1064 7414 : *curInstr = pushArgument(curBlk, *curInstr, idx);
1065 276 : } else if (currChar(cntxt) == ':') {
1066 274 : tpe = typeElm(cntxt, TYPE_any);
1067 274 : if (tpe < 0)
1068 : return 3;
1069 274 : setPolymorphic(*curInstr, tpe, FALSE);
1070 274 : idx = newTypeVariable(curBlk, tpe);
1071 274 : *curInstr = pushArgument(curBlk, *curInstr, idx);
1072 274 : return ret;
1073 : }
1074 : return 0;
1075 : }
1076 :
1077 : static int
1078 5 : parseAtom(Client cntxt)
1079 : {
1080 5 : const char *modnme = 0;
1081 5 : int l, tpe;
1082 5 : char *nxt = CURRENT(cntxt);
1083 :
1084 5 : if ((l = idLength(cntxt)) <= 0) {
1085 0 : parseError(cntxt, "atom name expected\n");
1086 0 : return -1;
1087 : }
1088 :
1089 : /* parse: ATOM id:type */
1090 5 : modnme = putNameLen(nxt, l);
1091 5 : if (modnme == NULL) {
1092 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1093 0 : return -1;
1094 : }
1095 5 : advance(cntxt, l);
1096 5 : if (currChar(cntxt) != ':')
1097 : tpe = TYPE_void; /* no type qualifier */
1098 : else
1099 5 : tpe = parseTypeId(cntxt);
1100 5 : if (ATOMindex(modnme) < 0) {
1101 4 : if (cntxt->curprg->def->errors)
1102 0 : freeException(cntxt->curprg->def->errors);
1103 4 : cntxt->curprg->def->errors = malAtomDefinition(modnme, tpe);
1104 : }
1105 5 : if (strcmp(modnme, "user"))
1106 5 : cntxt->curmodule = fixModule(modnme);
1107 : else
1108 0 : cntxt->curmodule = cntxt->usermodule;
1109 5 : cntxt->usermodule->isAtomModule = TRUE;
1110 5 : skipSpace(cntxt);
1111 5 : helpInfo(cntxt, &cntxt->usermodule->help);
1112 5 : return 0;
1113 : }
1114 :
1115 : /*
1116 : * All modules, except 'user', should be global
1117 : */
1118 : static int
1119 3 : parseModule(Client cntxt)
1120 : {
1121 3 : const char *modnme = 0;
1122 3 : int l;
1123 3 : char *nxt;
1124 :
1125 3 : nxt = CURRENT(cntxt);
1126 3 : if ((l = idLength(cntxt)) <= 0) {
1127 0 : parseError(cntxt, "<module path> expected\n");
1128 0 : return -1;
1129 : }
1130 3 : modnme = putNameLen(nxt, l);
1131 3 : if (modnme == NULL) {
1132 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1133 0 : return -1;
1134 : }
1135 3 : advance(cntxt, l);
1136 3 : if (strcmp(modnme, cntxt->usermodule->name) == 0) {
1137 : // ignore this module definition
1138 3 : } else if (getModule(modnme) == NULL) {
1139 3 : if (globalModule(modnme) == NULL)
1140 0 : parseError(cntxt, "<module> could not be created");
1141 : }
1142 3 : if (strcmp(modnme, "user"))
1143 3 : cntxt->curmodule = fixModule(modnme);
1144 : else
1145 0 : cntxt->curmodule = cntxt->usermodule;
1146 3 : skipSpace(cntxt);
1147 3 : helpInfo(cntxt, &cntxt->usermodule->help);
1148 3 : return 0;
1149 : }
1150 :
1151 : /*
1152 : * Include files should be handled in line with parsing. This way we
1153 : * are ensured that any possible signature definition will be known
1154 : * afterwards. The effect is that errors in the include sequence are
1155 : * marked as warnings.
1156 : */
1157 : static int
1158 2 : parseInclude(Client cntxt)
1159 : {
1160 2 : const char *modnme = 0;
1161 2 : char *s;
1162 2 : int x;
1163 2 : char *nxt;
1164 :
1165 2 : nxt = CURRENT(cntxt);
1166 :
1167 2 : if ((x = idLength(cntxt)) > 0) {
1168 2 : modnme = putNameLen(nxt, x);
1169 2 : advance(cntxt, x);
1170 0 : } else if ((x = stringLength(cntxt)) > 0) {
1171 0 : modnme = putNameLen(nxt + 1, x - 1);
1172 0 : advance(cntxt, x);
1173 : } else {
1174 0 : parseError(cntxt, "<module name> expected\n");
1175 0 : return -1;
1176 : }
1177 2 : if (modnme == NULL) {
1178 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1179 0 : return -1;
1180 : }
1181 :
1182 2 : if (currChar(cntxt) != ';') {
1183 0 : parseError(cntxt, "';' expected\n");
1184 0 : return 0;
1185 : }
1186 2 : skipToEnd(cntxt);
1187 :
1188 2 : if (!malLibraryEnabled(modnme)) {
1189 : return 0;
1190 : }
1191 :
1192 2 : if (getModule(modnme) == NULL) {
1193 1 : s = loadLibrary(modnme, FALSE);
1194 1 : if (s) {
1195 1 : parseError(cntxt, s);
1196 1 : freeException(s);
1197 1 : return 0;
1198 : }
1199 : }
1200 1 : if ((s = malInclude(cntxt, modnme, 0))) {
1201 0 : parseError(cntxt, s);
1202 0 : freeException(s);
1203 0 : return 0;
1204 : }
1205 : return 0;
1206 : }
1207 :
1208 : /* return the combined count of the number of arguments and the number
1209 : * of return values so that we can allocate enough space in the
1210 : * instruction; returns -1 on error (missing closing parenthesis) */
1211 : static int
1212 212 : cntArgsReturns(Client cntxt)
1213 : {
1214 212 : size_t yycur = cntxt->yycur;
1215 212 : int cnt = 0;
1216 212 : char ch;
1217 :
1218 212 : ch = currChar(cntxt);
1219 212 : if (ch != ')') {
1220 : cnt++;
1221 1129 : while (ch != ')' && ch && !NL(ch)) {
1222 1038 : if (ch == ',')
1223 43 : cnt++;
1224 1038 : nextChar(cntxt);
1225 1038 : ch = currChar(cntxt);
1226 : }
1227 : }
1228 91 : if (ch != ')') {
1229 0 : parseError(cntxt, "')' expected\n");
1230 0 : cntxt->yycur = yycur;
1231 0 : return -1;
1232 : }
1233 212 : advance(cntxt, 1);
1234 212 : ch = currChar(cntxt);
1235 212 : if (ch == '(') {
1236 13 : advance(cntxt, 1);
1237 13 : ch = currChar(cntxt);
1238 13 : cnt++;
1239 304 : while (ch != ')' && ch && !NL(ch)) {
1240 291 : if (ch == ',')
1241 39 : cnt++;
1242 291 : nextChar(cntxt);
1243 291 : ch = currChar(cntxt);
1244 : }
1245 13 : if (ch != ')') {
1246 0 : parseError(cntxt, "')' expected\n");
1247 0 : cntxt->yycur = yycur;
1248 0 : return -1;
1249 : }
1250 : } else {
1251 199 : cnt++;
1252 : }
1253 212 : cntxt->yycur = yycur;
1254 212 : return cnt;
1255 : }
1256 :
1257 : /*
1258 : * Definition
1259 : * The definition statements share a lot in common, which calls for factoring
1260 : * out the code in a few text macros. Upon encountering a definition, we
1261 : * initialize a MAL instruction container. We should also check for
1262 : * non-terminated definitions.
1263 : *
1264 : * Beware, a function signature f(a1..an):(b1..bn) is parsed in such a way that
1265 : * the symbol table and stackframe contains the sequence
1266 : * f,a1..an,b1..bn. This slightly complicates the implementation
1267 : * of the return statement.
1268 : *
1269 : * Note, the function name could be mod.fcn, which calls for storing
1270 : * the function definition in a particular module instead of the current one.
1271 : */
1272 : static MalBlkPtr
1273 212 : fcnHeader(Client cntxt, int kind)
1274 : {
1275 212 : int l;
1276 212 : malType tpe;
1277 212 : const char *fnme;
1278 212 : const char *modnme = NULL;
1279 212 : char ch;
1280 212 : Symbol curPrg;
1281 212 : MalBlkPtr curBlk = 0;
1282 212 : InstrPtr curInstr;
1283 :
1284 212 : l = operatorLength(cntxt);
1285 212 : if (l == 0)
1286 211 : l = idLength(cntxt);
1287 211 : if (l == 0) {
1288 0 : parseError(cntxt, "<identifier> | <operator> expected\n");
1289 0 : return 0;
1290 : }
1291 :
1292 212 : fnme = putNameLen(((char *) CURRENT(cntxt)), l);
1293 212 : if (fnme == NULL) {
1294 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1295 0 : return NULL;
1296 : }
1297 212 : advance(cntxt, l);
1298 :
1299 212 : if (currChar(cntxt) == '.') {
1300 8 : nextChar(cntxt); /* skip '.' */
1301 8 : modnme = fnme;
1302 8 : if (strcmp(modnme, "user") && getModule(modnme) == NULL) {
1303 1 : if (globalModule(modnme) == NULL) {
1304 0 : parseError(cntxt, "<module> name not defined\n");
1305 0 : return 0;
1306 : }
1307 : }
1308 8 : l = operatorLength(cntxt);
1309 8 : if (l == 0)
1310 8 : l = idLength(cntxt);
1311 8 : if (l == 0) {
1312 0 : parseError(cntxt, "<identifier> | <operator> expected\n");
1313 0 : return 0;
1314 : }
1315 8 : fnme = putNameLen(((char *) CURRENT(cntxt)), l);
1316 8 : if (fnme == NULL) {
1317 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1318 0 : return NULL;
1319 : }
1320 8 : advance(cntxt, l);
1321 : } else
1322 204 : modnme = cntxt->curmodule->name;
1323 :
1324 : /* temporary suspend capturing statements in main block */
1325 212 : if (cntxt->backup) {
1326 0 : parseError(cntxt, "mal_parser: unexpected recursion\n");
1327 0 : return 0;
1328 : }
1329 212 : if (currChar(cntxt) != '(') {
1330 0 : parseError(cntxt, "function header '(' expected\n");
1331 0 : return curBlk;
1332 : }
1333 212 : advance(cntxt, 1);
1334 :
1335 212 : assert(!cntxt->backup);
1336 212 : cntxt->backup = cntxt->curprg;
1337 212 : int nargs = cntArgsReturns(cntxt);
1338 212 : if (nargs < 0)
1339 : return 0;
1340 : /* one extra for argument/return manipulation */
1341 212 : cntxt->curprg = newFunctionArgs(modnme, fnme, kind, nargs + 1);
1342 212 : if (cntxt->curprg == NULL) {
1343 : /* reinstate curprg to have a place for the error */
1344 0 : cntxt->curprg = cntxt->backup;
1345 0 : cntxt->backup = NULL;
1346 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1347 0 : return 0;
1348 : }
1349 212 : cntxt->curprg->def->errors = cntxt->backup->def->errors;
1350 212 : cntxt->backup->def->errors = 0;
1351 212 : curPrg = cntxt->curprg;
1352 212 : curBlk = curPrg->def;
1353 212 : curInstr = getInstrPtr(curBlk, 0);
1354 :
1355 : /* get calling parameters */
1356 212 : ch = currChar(cntxt);
1357 255 : while (ch != ')' && ch && !NL(ch)) {
1358 134 : curInstr = binding(cntxt, curBlk, curInstr, 1);
1359 : /* the last argument may be variable length */
1360 134 : if (MALkeyword(cntxt, "...", 3)) {
1361 7 : curInstr->varargs |= VARARGS;
1362 7 : setPolymorphic(curInstr, TYPE_any, TRUE);
1363 7 : break;
1364 : }
1365 127 : if ((ch = currChar(cntxt)) != ',') {
1366 84 : if (ch == ')')
1367 : break;
1368 0 : if (cntxt->backup)
1369 0 : curBlk = NULL;
1370 0 : parseError(cntxt, "',' expected\n");
1371 0 : return curBlk;
1372 : } else
1373 43 : nextChar(cntxt); /* skip ',' */
1374 43 : skipSpace(cntxt);
1375 43 : ch = currChar(cntxt);
1376 : }
1377 212 : if (currChar(cntxt) != ')') {
1378 0 : freeInstruction(curInstr);
1379 0 : if (cntxt->backup)
1380 0 : curBlk = NULL;
1381 0 : parseError(cntxt, "')' expected\n");
1382 0 : return curBlk;
1383 : }
1384 212 : advance(cntxt, 1); /* skip ')' */
1385 : /*
1386 : The return type is either a single type or multiple return type structure.
1387 : We simply keep track of the number of arguments added and
1388 : during the final phase reshuffle the return values to the beginning (?)
1389 : */
1390 212 : if (currChar(cntxt) == ':') {
1391 83 : tpe = typeElm(cntxt, TYPE_void);
1392 83 : setPolymorphic(curInstr, tpe, TRUE);
1393 83 : setVarType(curBlk, curInstr->argv[0], tpe);
1394 : /* we may be confronted by a variable target type list */
1395 83 : if (MALkeyword(cntxt, "...", 3)) {
1396 3 : curInstr->varargs |= VARRETS;
1397 3 : setPolymorphic(curInstr, TYPE_any, TRUE);
1398 : }
1399 :
1400 129 : } else if (keyphrase1(cntxt, "(")) { /* deal with compound return */
1401 13 : int retc = curInstr->argc, i1, i2 = 0;
1402 13 : int max;
1403 13 : short *newarg;
1404 : /* parse multi-target result */
1405 : /* skipSpace(cntxt); */
1406 13 : ch = currChar(cntxt);
1407 52 : while (ch != ')' && ch && !NL(ch)) {
1408 52 : curInstr = binding(cntxt, curBlk, curInstr, 0);
1409 : /* we may be confronted by a variable target type list */
1410 52 : if (MALkeyword(cntxt, "...", 3)) {
1411 3 : curInstr->varargs |= VARRETS;
1412 3 : setPolymorphic(curInstr, TYPE_any, TRUE);
1413 : }
1414 52 : if ((ch = currChar(cntxt)) != ',') {
1415 13 : if (ch == ')')
1416 : break;
1417 0 : if (cntxt->backup)
1418 0 : curBlk = NULL;
1419 0 : parseError(cntxt, "',' expected\n");
1420 0 : return curBlk;
1421 : } else {
1422 39 : nextChar(cntxt); /* skip ',' */
1423 : }
1424 39 : skipSpace(cntxt);
1425 39 : ch = currChar(cntxt);
1426 : }
1427 : /* re-arrange the parameters, results first */
1428 13 : max = curInstr->maxarg;
1429 13 : newarg = (short *) GDKmalloc(max * sizeof(curInstr->argv[0]));
1430 13 : if (newarg == NULL) {
1431 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1432 0 : if (cntxt->backup)
1433 0 : curBlk = NULL;
1434 0 : return curBlk;
1435 : }
1436 65 : for (i1 = retc; i1 < curInstr->argc; i1++)
1437 52 : newarg[i2++] = curInstr->argv[i1];
1438 13 : curInstr->retc = curInstr->argc - retc;
1439 31 : for (i1 = 1; i1 < retc; i1++)
1440 18 : newarg[i2++] = curInstr->argv[i1];
1441 13 : curInstr->argc = i2;
1442 26 : for (; i2 < max; i2++)
1443 13 : newarg[i2] = 0;
1444 96 : for (i1 = 0; i1 < max; i1++)
1445 83 : curInstr->argv[i1] = newarg[i1];
1446 13 : GDKfree(newarg);
1447 13 : if (currChar(cntxt) != ')') {
1448 0 : freeInstruction(curInstr);
1449 0 : if (cntxt->backup)
1450 0 : curBlk = NULL;
1451 0 : parseError(cntxt, "')' expected\n");
1452 0 : return curBlk;
1453 : }
1454 13 : nextChar(cntxt); /* skip ')' */
1455 : } else { /* default */
1456 116 : setVarType(curBlk, 0, TYPE_void);
1457 : }
1458 212 : if (curInstr != getInstrPtr(curBlk, 0)) {
1459 0 : freeInstruction(getInstrPtr(curBlk, 0));
1460 0 : putInstrPtr(curBlk, 0, curInstr);
1461 : }
1462 : return curBlk;
1463 : }
1464 :
1465 : static MalBlkPtr
1466 13 : parseCommandPattern(Client cntxt, int kind, MALfcn address)
1467 : {
1468 13 : MalBlkPtr curBlk = 0;
1469 13 : Symbol curPrg = 0;
1470 13 : InstrPtr curInstr = 0;
1471 13 : const char *modnme = NULL;
1472 13 : size_t l = 0;
1473 13 : str msg = MAL_SUCCEED;
1474 :
1475 13 : curBlk = fcnHeader(cntxt, kind);
1476 13 : if (curBlk == NULL) {
1477 0 : cntxt->blkmode = 0;
1478 0 : return curBlk;
1479 : }
1480 13 : getInstrPtr(curBlk, 0)->token = kind;
1481 13 : curPrg = cntxt->curprg;
1482 13 : curPrg->kind = kind;
1483 13 : curInstr = getInstrPtr(curBlk, 0);
1484 :
1485 13 : modnme = getModuleId(getInstrPtr(curBlk, 0));
1486 13 : if (modnme && (getModule(modnme) == FALSE && strcmp(modnme, "user"))) {
1487 : // introduce the module
1488 0 : if (globalModule(modnme) == NULL) {
1489 0 : parseError(cntxt, "<module> could not be defined\n");
1490 0 : return 0;
1491 : }
1492 : }
1493 0 : modnme = modnme ? modnme : cntxt->usermodule->name;
1494 :
1495 13 : l = strlen(modnme);
1496 13 : modnme = putNameLen(modnme, l);
1497 13 : if (modnme == NULL) {
1498 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1499 0 : return NULL;
1500 : }
1501 13 : if (strcmp(modnme, "user") == 0 || getModule(modnme)) {
1502 13 : if (strcmp(modnme, "user") == 0)
1503 10 : insertSymbol(cntxt->usermodule, curPrg);
1504 : else
1505 3 : insertSymbol(getModule(modnme), curPrg);
1506 13 : if (!cntxt->curprg->def->errors)
1507 13 : msg = chkProgram(cntxt->usermodule, curBlk);
1508 13 : if (msg && !cntxt->curprg->def->errors)
1509 0 : cntxt->curprg->def->errors = msg;
1510 13 : if (cntxt->curprg->def->errors)
1511 0 : freeException(cntxt->curprg->def->errors);
1512 13 : cntxt->curprg->def->errors = cntxt->backup->def->errors;
1513 13 : cntxt->backup->def->errors = 0;
1514 13 : cntxt->curprg = cntxt->backup;
1515 13 : cntxt->backup = 0;
1516 : } else {
1517 0 : parseError(cntxt, "<module> not found\n");
1518 0 : return 0;
1519 : }
1520 : /*
1521 : * Short-cut function calls
1522 : * Most functions are (dynamically) linked with the kernel as
1523 : * commands or pattern definitions. This enables for fast execution.
1524 : *
1525 : * In addition we allow functions to be bound to both
1526 : * a linked C-function and a MAL specification block.
1527 : * It the function address is not available, the interpreter
1528 : * will use the MAL block instead.
1529 : * This scheme is intended for just-in-time compilation.
1530 : *
1531 : * [note, command and patterns do not have a MAL block]
1532 : */
1533 13 : if (MALkeyword(cntxt, "address", 7)) {
1534 : /* TO BE DEPRECATED */
1535 13 : int i;
1536 13 : i = idLength(cntxt);
1537 13 : if (i == 0) {
1538 0 : parseError(cntxt, "address <identifier> expected\n");
1539 0 : return 0;
1540 : }
1541 13 : cntxt->blkmode = 0;
1542 13 : if (getModuleId(curInstr))
1543 13 : setModuleId(curInstr, NULL);
1544 13 : setModuleScope(curInstr, findModule(cntxt->usermodule, modnme));
1545 :
1546 13 : memcpy(curBlk->binding, CURRENT(cntxt),
1547 13 : (size_t) (i < IDLENGTH ? i : IDLENGTH - 1));
1548 13 : curBlk->binding[(i < IDLENGTH ? i : IDLENGTH - 1)] = 0;
1549 : /* avoid a clash with old temporaries */
1550 13 : advance(cntxt, i);
1551 13 : curInstr->fcn = getAddress(getModuleId(curInstr), curBlk->binding);
1552 :
1553 13 : if (cntxt->usermodule->isAtomModule) {
1554 3 : if (curInstr->fcn == NULL) {
1555 0 : parseError(cntxt, "<address> not found\n");
1556 0 : return 0;
1557 : }
1558 3 : malAtomProperty(curBlk, curInstr);
1559 : }
1560 13 : skipSpace(cntxt);
1561 0 : } else if (address) {
1562 0 : setModuleScope(curInstr, findModule(cntxt->usermodule, modnme));
1563 0 : setModuleId(curInstr, modnme);
1564 0 : curInstr->fcn = address;
1565 : }
1566 13 : helpInfo(cntxt, &curBlk->help);
1567 13 : return curBlk;
1568 : }
1569 :
1570 : static MalBlkPtr
1571 199 : parseFunction(Client cntxt, int kind)
1572 : {
1573 199 : MalBlkPtr curBlk = 0;
1574 :
1575 199 : curBlk = fcnHeader(cntxt, kind);
1576 199 : if (curBlk == NULL)
1577 : return curBlk;
1578 199 : if (MALkeyword(cntxt, "address", 7)) {
1579 : /* TO BE DEPRECATED */
1580 1 : str nme;
1581 1 : int i;
1582 1 : InstrPtr curInstr = getInstrPtr(curBlk, 0);
1583 1 : i = idLength(cntxt);
1584 1 : if (i == 0) {
1585 0 : parseError(cntxt, "<identifier> expected\n");
1586 0 : return 0;
1587 : }
1588 1 : nme = idCopy(cntxt, i);
1589 1 : if (nme == NULL) {
1590 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1591 0 : return 0;
1592 : }
1593 1 : curInstr->fcn = getAddress(getModuleId(curInstr), nme);
1594 1 : GDKfree(nme);
1595 1 : if (curInstr->fcn == NULL) {
1596 1 : parseError(cntxt, "<address> not found\n");
1597 1 : return 0;
1598 : }
1599 0 : skipSpace(cntxt);
1600 : }
1601 : /* block is terminated at the END statement */
1602 198 : helpInfo(cntxt, &curBlk->help);
1603 198 : return curBlk;
1604 : }
1605 :
1606 : /*
1607 : * Functions and factories end with a labeled end-statement.
1608 : * The routine below checks for misalignment of the closing statements.
1609 : * Any instruction parsed after the function block is considered an error.
1610 : */
1611 : static int
1612 292 : parseEnd(Client cntxt)
1613 : {
1614 292 : Symbol curPrg = 0;
1615 292 : size_t l;
1616 292 : InstrPtr sig;
1617 292 : str errors = MAL_SUCCEED, msg = MAL_SUCCEED;
1618 :
1619 292 : if (MALkeyword(cntxt, "end", 3)) {
1620 204 : curPrg = cntxt->curprg;
1621 204 : l = idLength(cntxt);
1622 204 : if (l == 0)
1623 37 : l = operatorLength(cntxt);
1624 204 : sig = getInstrPtr(cntxt->curprg->def, 0);
1625 204 : if (strncmp(CURRENT(cntxt), getModuleId(sig), l) == 0) {
1626 37 : advance(cntxt, l);
1627 37 : skipSpace(cntxt);
1628 37 : if (currChar(cntxt) == '.')
1629 0 : nextChar(cntxt);
1630 37 : skipSpace(cntxt);
1631 37 : l = idLength(cntxt);
1632 37 : if (l == 0)
1633 37 : l = operatorLength(cntxt);
1634 : }
1635 : /* parse fcn */
1636 204 : if ((l == strlen(curPrg->name) &&
1637 204 : strncmp(CURRENT(cntxt), curPrg->name, l) == 0) || l == 0)
1638 195 : advance(cntxt, l);
1639 : else
1640 9 : parseError(cntxt, "non matching end label\n");
1641 204 : pushEndInstruction(cntxt->curprg->def);
1642 204 : cntxt->blkmode = 0;
1643 204 : if (strcmp(getModuleId(sig), "user") == 0)
1644 199 : insertSymbol(cntxt->usermodule, cntxt->curprg);
1645 : else
1646 5 : insertSymbol(getModule(getModuleId(sig)), cntxt->curprg);
1647 :
1648 204 : if (cntxt->curprg->def->errors) {
1649 11 : errors = cntxt->curprg->def->errors;
1650 11 : cntxt->curprg->def->errors = 0;
1651 : }
1652 : // check for newly identified errors
1653 204 : msg = chkProgram(cntxt->usermodule, cntxt->curprg->def);
1654 204 : if (errors == NULL)
1655 : errors = msg;
1656 : else
1657 11 : freeException(msg);
1658 204 : if (errors == NULL) {
1659 169 : errors = cntxt->curprg->def->errors;
1660 169 : cntxt->curprg->def->errors = 0;
1661 35 : } else if (cntxt->curprg->def->errors) {
1662 : //collect all errors for reporting
1663 0 : str new = GDKmalloc(strlen(errors) +
1664 : strlen(cntxt->curprg->def->errors) + 16);
1665 0 : if (new) {
1666 0 : strcpy(new, errors);
1667 0 : if (new[strlen(new) - 1] != '\n')
1668 0 : strcat(new, "\n");
1669 0 : strcat(new, "!");
1670 0 : strcat(new, cntxt->curprg->def->errors);
1671 :
1672 0 : freeException(errors);
1673 0 : freeException(cntxt->curprg->def->errors);
1674 :
1675 0 : cntxt->curprg->def->errors = 0;
1676 0 : errors = new;
1677 : }
1678 : }
1679 :
1680 204 : if (cntxt->backup) {
1681 194 : cntxt->curprg = cntxt->backup;
1682 194 : cntxt->backup = 0;
1683 : } else {
1684 10 : str msg;
1685 10 : if ((msg = MSinitClientPrg(cntxt, cntxt->curmodule->name,
1686 : "main")) != MAL_SUCCEED) {
1687 0 : if (errors) {
1688 0 : str new = GDKmalloc(strlen(errors) + strlen(msg) + 3);
1689 0 : if (new) {
1690 0 : strcpy(new, msg);
1691 0 : if (new[strlen(new) - 1] != '\n')
1692 0 : strcat(new, "\n");
1693 0 : strcat(new, errors);
1694 0 : freeException(errors);
1695 0 : cntxt->curprg->def->errors = new;
1696 : } else {
1697 0 : cntxt->curprg->def->errors = errors;
1698 : }
1699 0 : freeException(msg);
1700 : } else {
1701 0 : cntxt->curprg->def->errors = msg;
1702 : }
1703 0 : return 1;
1704 : }
1705 : }
1706 : // pass collected errors to context
1707 204 : assert(cntxt->curprg->def->errors == NULL);
1708 204 : cntxt->curprg->def->errors = errors;
1709 204 : return 1;
1710 : }
1711 : return 0;
1712 : }
1713 :
1714 : /*
1715 : * Most instructions are simple assignments, possibly
1716 : * modified with a barrier/catch tag.
1717 : *
1718 : * The basic types are also predefined as a variable.
1719 : * This makes it easier to communicate types to MAL patterns.
1720 : */
1721 :
1722 : #define GETvariable(FREE) \
1723 : if ((varid = findVariableLength(curBlk, CURRENT(cntxt), l)) == -1) { \
1724 : varid = newVariable(curBlk, CURRENT(cntxt),l, TYPE_any); \
1725 : advance(cntxt, l); \
1726 : if(varid < 0) { FREE; return; } \
1727 : } else \
1728 : advance(cntxt, l);
1729 :
1730 : /* The parameter of parseArguments is the return value of the enclosing function. */
1731 : static int
1732 8974 : parseArguments(Client cntxt, MalBlkPtr curBlk, InstrPtr *curInstr)
1733 : {
1734 24597 : while (currChar(cntxt) != ')') {
1735 15624 : switch (term(cntxt, curBlk, curInstr, 0)) {
1736 : case 0:
1737 15623 : break;
1738 : case 2:
1739 : return 2;
1740 : case 3:
1741 : return 3;
1742 0 : case 4:
1743 0 : parseError(cntxt, "Argument type overwrites previous definition\n");
1744 0 : return 0;
1745 0 : default:
1746 0 : parseError(cntxt, "<factor> expected\n");
1747 0 : return 1;
1748 : }
1749 15623 : if (currChar(cntxt) == ',')
1750 7312 : advance(cntxt, 1);
1751 8311 : else if (currChar(cntxt) != ')') {
1752 0 : parseError(cntxt, "',' expected\n");
1753 0 : cntxt->yycur--; /* keep it */
1754 0 : break;
1755 : }
1756 : }
1757 8973 : if (currChar(cntxt) == ')')
1758 8973 : advance(cntxt, 1);
1759 : return 0;
1760 : }
1761 :
1762 : static void
1763 11748 : parseAssign(Client cntxt, int cntrl)
1764 : {
1765 11748 : InstrPtr curInstr;
1766 11748 : MalBlkPtr curBlk;
1767 11748 : Symbol curPrg;
1768 11748 : int i = 0, l, type = TYPE_any, varid = -1;
1769 11748 : const char *arg = 0;
1770 11748 : ValRecord cst;
1771 :
1772 11748 : curPrg = cntxt->curprg;
1773 11748 : curBlk = curPrg->def;
1774 11748 : if ((curInstr = newInstruction(curBlk, NULL, NULL)) == NULL) {
1775 13 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1776 2376 : return;
1777 : }
1778 :
1779 11735 : if (cntrl) {
1780 592 : curInstr->token = ASSIGNsymbol;
1781 592 : curInstr->barrier = cntrl;
1782 : }
1783 :
1784 : /* start the parsing by recognition of the lhs of an assignment */
1785 11735 : if (currChar(cntxt) == '(') {
1786 : /* parsing multi-assignment */
1787 218 : advance(cntxt, 1);
1788 218 : curInstr->argc = 0; /*reset to handle pushArg correctly !! */
1789 218 : curInstr->retc = 0;
1790 1838 : while (currChar(cntxt) != ')' && currChar(cntxt)) {
1791 1620 : l = idLength(cntxt);
1792 1620 : i = cstToken(cntxt, &cst);
1793 1620 : if (l == 0 || i) {
1794 1 : parseError(cntxt, "<identifier> or <literal> expected\n");
1795 1 : freeInstruction(curInstr);
1796 1 : return;
1797 : }
1798 1619 : GETvariable(freeInstruction(curInstr));
1799 1619 : if (currChar(cntxt) == ':') {
1800 31 : type = typeElm(cntxt, getVarType(curBlk, varid));
1801 31 : if (type < 0)
1802 0 : goto part3;
1803 31 : setPolymorphic(curInstr, type, FALSE);
1804 31 : setVarType(curBlk, varid, type);
1805 : }
1806 1619 : curInstr = pushArgument(curBlk, curInstr, varid);
1807 1619 : curInstr->retc++;
1808 1619 : if (currChar(cntxt) == ')')
1809 : break;
1810 1402 : if (currChar(cntxt) == ',')
1811 1402 : keyphrase1(cntxt, ",");
1812 : }
1813 217 : advance(cntxt, 1); /* skip ')' */
1814 217 : if (curInstr->retc == 0) {
1815 : /* add dummy variable */
1816 0 : curInstr = pushArgument(curBlk, curInstr,
1817 : newTmpVariable(curBlk, TYPE_any));
1818 0 : curInstr->retc++;
1819 : }
1820 : } else {
1821 : /* are we dealing with a simple assignment? */
1822 11517 : l = idLength(cntxt);
1823 11517 : i = cstToken(cntxt, &cst);
1824 11517 : if (l == 0 || i) {
1825 : /* we haven't seen a target variable */
1826 : /* flow of control statements may end here. */
1827 : /* shouldn't allow for nameless controls todo */
1828 6 : if (i && cst.vtype == TYPE_str)
1829 0 : GDKfree(cst.val.sval);
1830 6 : if (cntrl == LEAVEsymbol || cntrl == REDOsymbol ||
1831 6 : cntrl == RETURNsymbol || cntrl == EXITsymbol) {
1832 4 : curInstr->argv[0] = getBarrierEnvelop(curBlk);
1833 4 : if (currChar(cntxt) != ';') {
1834 0 : freeInstruction(curInstr);
1835 0 : parseError(cntxt,
1836 : "<identifier> or <literal> expected in control statement\n");
1837 0 : return;
1838 : }
1839 4 : pushInstruction(curBlk, curInstr);
1840 4 : return;
1841 : }
1842 2 : getArg(curInstr, 0) = newTmpVariable(curBlk, TYPE_any);
1843 2 : freeInstruction(curInstr);
1844 2 : parseError(cntxt, "<identifier> or <literal> expected\n");
1845 2 : return;
1846 : }
1847 : /* Check if we are dealing with module.fcn call */
1848 11511 : if (CURRENT(cntxt)[l] == '.' || CURRENT(cntxt)[l] == '(') {
1849 4368 : curInstr->argv[0] = newTmpVariable(curBlk, TYPE_any);
1850 4368 : goto FCNcallparse;
1851 : }
1852 :
1853 : /* Get target variable details */
1854 7143 : GETvariable(freeInstruction(curInstr));
1855 7143 : if (!(currChar(cntxt) == ':' && CURRENT(cntxt)[1] == '=')) {
1856 449 : curInstr->argv[0] = varid;
1857 449 : if (currChar(cntxt) == ':') {
1858 164 : type = typeElm(cntxt, getVarType(curBlk, varid));
1859 164 : if (type < 0)
1860 0 : goto part3;
1861 164 : setPolymorphic(curInstr, type, FALSE);
1862 164 : setVarType(curBlk, varid, type);
1863 : }
1864 : }
1865 7143 : curInstr->argv[0] = varid;
1866 : }
1867 : /* look for assignment operator */
1868 7360 : if (!keyphrase2(cntxt, ":=")) {
1869 : /* no assignment !! a control variable is allowed */
1870 : /* for the case RETURN X, we normalize it to include the function arguments */
1871 326 : if (cntrl == RETURNsymbol) {
1872 31 : int e;
1873 31 : InstrPtr sig = getInstrPtr(curBlk, 0);
1874 31 : curInstr->retc = 0;
1875 64 : for (e = 0; e < sig->retc; e++)
1876 33 : curInstr = pushReturn(curBlk, curInstr, getArg(sig, e));
1877 : }
1878 :
1879 326 : goto part3;
1880 : }
1881 7034 : if (currChar(cntxt) == '(') {
1882 : /* parse multi assignment */
1883 9 : advance(cntxt, 1);
1884 9 : switch (parseArguments(cntxt, curBlk, &curInstr)) {
1885 0 : case 2:
1886 0 : goto part2;
1887 9 : default:
1888 : case 3:
1889 9 : goto part3;
1890 : }
1891 : /* unreachable */
1892 : }
1893 : /*
1894 : * We have so far the LHS part of an assignment. The remainder is
1895 : * either a simple term expression, a multi assignent, or the start
1896 : * of a function call.
1897 : */
1898 7025 : FCNcallparse:
1899 11393 : if ((l = idLength(cntxt)) && CURRENT(cntxt)[l] == '(') {
1900 : /* parseError(cntxt,"<module> expected\n"); */
1901 62 : setModuleId(curInstr, cntxt->curmodule->name);
1902 62 : i = l;
1903 62 : goto FCNcallparse2;
1904 11331 : } else if ((l = idLength(cntxt)) && CURRENT(cntxt)[l] == '.') {
1905 : /* continue with parseing a function/operator call */
1906 8905 : arg = putNameLen(CURRENT(cntxt), l);
1907 8905 : if (arg == NULL) {
1908 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1909 0 : freeInstruction(curInstr);
1910 0 : return;
1911 : }
1912 8905 : advance(cntxt, l + 1); /* skip '.' too */
1913 8905 : setModuleId(curInstr, arg);
1914 8905 : i = idLength(cntxt);
1915 8905 : if (i == 0)
1916 68 : i = operatorLength(cntxt);
1917 8837 : FCNcallparse2:
1918 130 : if (i) {
1919 8967 : setFunctionId(curInstr, putNameLen(((char *) CURRENT(cntxt)), i));
1920 8967 : if (getFunctionId(curInstr) == NULL) {
1921 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1922 0 : freeInstruction(curInstr);
1923 0 : return;
1924 : }
1925 8967 : advance(cntxt, i);
1926 : } else {
1927 0 : parseError(cntxt, "<functionname> expected\n");
1928 0 : freeInstruction(curInstr);
1929 0 : return;
1930 : }
1931 8967 : skipSpace(cntxt);
1932 8967 : if (currChar(cntxt) != '(') {
1933 2 : parseError(cntxt, "'(' expected\n");
1934 2 : freeInstruction(curInstr);
1935 2 : return;
1936 : }
1937 8965 : advance(cntxt, 1);
1938 8965 : switch (parseArguments(cntxt, curBlk, &curInstr)) {
1939 0 : case 2:
1940 0 : goto part2;
1941 8965 : default:
1942 : case 3:
1943 8965 : goto part3;
1944 : }
1945 : /* unreachable */
1946 : }
1947 : /* Handle the ordinary assignments and expressions */
1948 2426 : switch (term(cntxt, curBlk, &curInstr, 2)) {
1949 2254 : case 2:
1950 2254 : goto part2;
1951 3 : case 3:
1952 3 : goto part3;
1953 : }
1954 : part2: /* consume <operator><term> part of expression */
1955 2423 : if ((i = operatorLength(cntxt))) {
1956 : /* simple arithmetic operator expression */
1957 122 : setFunctionId(curInstr, putNameLen(((char *) CURRENT(cntxt)), i));
1958 122 : if (getFunctionId(curInstr) == NULL) {
1959 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1960 0 : freeInstruction(curInstr);
1961 0 : return;
1962 : }
1963 122 : advance(cntxt, i);
1964 122 : curInstr->modname = putName("calc");
1965 122 : if (curInstr->modname == NULL) {
1966 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1967 0 : freeInstruction(curInstr);
1968 0 : return;
1969 : }
1970 122 : if ((l = idLength(cntxt))
1971 54 : && !(l == 3 && strncmp(CURRENT(cntxt), "nil", 3) == 0)) {
1972 54 : GETvariable(freeInstruction(curInstr));
1973 54 : curInstr = pushArgument(curBlk, curInstr, varid);
1974 54 : goto part3;
1975 : }
1976 68 : switch (term(cntxt, curBlk, &curInstr, 3)) {
1977 0 : case 2:
1978 0 : goto part2;
1979 67 : case 3:
1980 67 : goto part3;
1981 : }
1982 1 : parseError(cntxt, "<term> expected\n");
1983 1 : freeInstruction(curInstr);
1984 1 : return;
1985 : } else {
1986 2301 : skipSpace(cntxt);
1987 2301 : if (currChar(cntxt) == '(') {
1988 0 : parseError(cntxt, "module name missing\n");
1989 0 : freeInstruction(curInstr);
1990 0 : return;
1991 2301 : } else if (currChar(cntxt) != ';' && currChar(cntxt) != '#') {
1992 1 : parseError(cntxt, "operator expected\n");
1993 1 : freeInstruction(curInstr);
1994 1 : return;
1995 : }
1996 2300 : pushInstruction(curBlk, curInstr);
1997 2300 : return;
1998 : }
1999 9424 : part3:
2000 9424 : skipSpace(cntxt);
2001 9424 : if (currChar(cntxt) != ';') {
2002 39 : parseError(cntxt, "';' expected\n");
2003 39 : skipToEnd(cntxt);
2004 39 : freeInstruction(curInstr);
2005 39 : return;
2006 : }
2007 9385 : skipToEnd(cntxt);
2008 9385 : if (cntrl == RETURNsymbol
2009 36 : && !(curInstr->token == ASSIGNsymbol || getModuleId(curInstr) != 0)) {
2010 0 : parseError(cntxt, "return assignment expected\n");
2011 0 : freeInstruction(curInstr);
2012 0 : return;
2013 : }
2014 9385 : pushInstruction(curBlk, curInstr);
2015 : }
2016 :
2017 : void
2018 10461 : parseMAL(Client cntxt, Symbol curPrg, int skipcomments, int lines,
2019 : MALfcn address)
2020 : {
2021 10461 : int cntrl = 0;
2022 : /*Symbol curPrg= cntxt->curprg; */
2023 10461 : char c;
2024 10461 : int inlineProp = 0, unsafeProp = 0;
2025 :
2026 10461 : (void) curPrg;
2027 10461 : echoInput(cntxt);
2028 : /* here the work takes place */
2029 41858 : while ((c = currChar(cntxt)) && lines > 0) {
2030 31397 : switch (c) {
2031 13851 : case '\n':
2032 : case '\r':
2033 : case '\f':
2034 13851 : lines -= c == '\n';
2035 13851 : nextChar(cntxt);
2036 13851 : echoInput(cntxt);
2037 13851 : continue;
2038 5337 : case ';':
2039 : case '\t':
2040 : case ' ':
2041 5337 : nextChar(cntxt);
2042 5337 : continue;
2043 13 : case '#':
2044 : { /* keep the full line comments */
2045 13 : char start[256], *e = start, c;
2046 13 : MalBlkPtr curBlk = cntxt->curprg->def;
2047 13 : InstrPtr curInstr;
2048 :
2049 13 : *e = 0;
2050 13 : nextChar(cntxt);
2051 228 : while ((c = currChar(cntxt))) {
2052 228 : if (e < start + 256 - 1)
2053 228 : *e++ = c;
2054 228 : nextChar(cntxt);
2055 228 : if (c == '\n' || c == '\r') {
2056 13 : *e = 0;
2057 13 : if (e > start)
2058 13 : e--;
2059 : /* prevChar(cntxt); */
2060 : break;
2061 : }
2062 : }
2063 13 : if (e > start)
2064 13 : *e = 0;
2065 13 : if (!skipcomments && e > start && curBlk->stop > 0) {
2066 13 : ValRecord cst;
2067 13 : if ((curInstr = newInstruction(curBlk, NULL, NULL)) == NULL) {
2068 1 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
2069 1 : continue;
2070 : }
2071 12 : curInstr->token = REMsymbol;
2072 12 : curInstr->barrier = 0;
2073 12 : if (VALinit(&cst, TYPE_str, start) == NULL) {
2074 0 : parseError(cntxt, SQLSTATE(HY013) MAL_MALLOC_FAIL);
2075 0 : freeInstruction(curInstr);
2076 0 : continue;
2077 : }
2078 12 : int cstidx = defConstant(curBlk, TYPE_str, &cst);
2079 12 : if (cstidx < 0) {
2080 0 : freeInstruction(curInstr);
2081 0 : continue;
2082 : }
2083 12 : getArg(curInstr, 0) = cstidx;
2084 12 : setVarDisabled(curBlk, getArg(curInstr, 0));
2085 12 : pushInstruction(curBlk, curInstr);
2086 : }
2087 12 : echoInput(cntxt);
2088 : }
2089 12 : continue;
2090 97 : case 'A':
2091 : case 'a':
2092 97 : if (MALkeyword(cntxt, "atom", 4) && parseAtom(cntxt) == 0)
2093 : break;
2094 92 : goto allLeft;
2095 1213 : case 'b':
2096 : case 'B':
2097 1213 : if (MALkeyword(cntxt, "barrier", 7)) {
2098 165 : cntxt->blkmode++;
2099 165 : cntrl = BARRIERsymbol;
2100 : }
2101 1213 : goto allLeft;
2102 469 : case 'C':
2103 : case 'c':
2104 469 : if (MALkeyword(cntxt, "command", 7)) {
2105 5 : MalBlkPtr p = parseCommandPattern(cntxt, COMMANDsymbol, address);
2106 5 : if (p) {
2107 5 : p->unsafeProp = unsafeProp;
2108 : }
2109 5 : cntxt->curprg->def->unsafeProp = unsafeProp;
2110 5 : if (inlineProp)
2111 0 : parseError(cntxt, "<identifier> expected\n");
2112 5 : inlineProp = 0;
2113 5 : unsafeProp = 0;
2114 5 : continue;
2115 : }
2116 464 : if (MALkeyword(cntxt, "catch", 5)) {
2117 24 : cntxt->blkmode++;
2118 24 : cntrl = CATCHsymbol;
2119 24 : goto allLeft;
2120 : }
2121 440 : goto allLeft;
2122 487 : case 'E':
2123 : case 'e':
2124 487 : if (MALkeyword(cntxt, "exit", 4)) {
2125 195 : if (cntxt->blkmode > 0)
2126 189 : cntxt->blkmode--;
2127 : cntrl = EXITsymbol;
2128 292 : } else if (parseEnd(cntxt)) {
2129 : break;
2130 : }
2131 283 : goto allLeft;
2132 333 : case 'F':
2133 : case 'f':
2134 333 : if (MALkeyword(cntxt, "function", 8)) {
2135 199 : MalBlkPtr p;
2136 199 : cntxt->blkmode++;
2137 199 : if ((p = parseFunction(cntxt, FUNCTIONsymbol))) {
2138 198 : p->unsafeProp = unsafeProp;
2139 198 : cntxt->curprg->def->inlineProp = inlineProp;
2140 198 : cntxt->curprg->def->unsafeProp = unsafeProp;
2141 198 : inlineProp = 0;
2142 198 : unsafeProp = 0;
2143 198 : break;
2144 : }
2145 : }
2146 135 : goto allLeft;
2147 1796 : case 'I':
2148 : case 'i':
2149 1796 : if (MALkeyword(cntxt, "inline", 6)) {
2150 23 : inlineProp = 1;
2151 23 : skipSpace(cntxt);
2152 23 : continue;
2153 1773 : } else if (MALkeyword(cntxt, "include", 7)) {
2154 2 : parseInclude(cntxt);
2155 2 : break;
2156 : }
2157 1771 : goto allLeft;
2158 81 : case 'L':
2159 : case 'l':
2160 81 : if (MALkeyword(cntxt, "leave", 5))
2161 40 : cntrl = LEAVEsymbol;
2162 81 : goto allLeft;
2163 79 : case 'M':
2164 : case 'm':
2165 79 : if (MALkeyword(cntxt, "module", 6) && parseModule(cntxt) == 0)
2166 : break;
2167 76 : goto allLeft;
2168 63 : case 'P':
2169 : case 'p':
2170 63 : if (MALkeyword(cntxt, "pattern", 7)) {
2171 8 : MalBlkPtr p;
2172 8 : if (inlineProp)
2173 0 : parseError(cntxt, "parseError:INLINE ignored\n");
2174 8 : p = parseCommandPattern(cntxt, PATTERNsymbol, address);
2175 8 : if (p) {
2176 8 : p->unsafeProp = unsafeProp;
2177 : }
2178 8 : cntxt->curprg->def->unsafeProp = unsafeProp;
2179 8 : inlineProp = 0;
2180 8 : unsafeProp = 0;
2181 8 : continue;
2182 : }
2183 55 : goto allLeft;
2184 5736 : case 'R':
2185 : case 'r':
2186 5736 : if (MALkeyword(cntxt, "redo", 4)) {
2187 85 : cntrl = REDOsymbol;
2188 85 : goto allLeft;
2189 : }
2190 5651 : if (MALkeyword(cntxt, "raise", 5)) {
2191 7 : cntrl = RAISEsymbol;
2192 7 : goto allLeft;
2193 : }
2194 5644 : if (MALkeyword(cntxt, "return", 6)) {
2195 76 : cntrl = RETURNsymbol;
2196 : }
2197 5644 : goto allLeft;
2198 51 : case 'U':
2199 : case 'u':
2200 51 : if (MALkeyword(cntxt, "unsafe", 6)) {
2201 0 : unsafeProp = 1;
2202 0 : skipSpace(cntxt);
2203 0 : continue;
2204 : }
2205 : /* fall through */
2206 : default:
2207 51 : allLeft:
2208 11748 : parseAssign(cntxt, cntrl);
2209 11748 : cntrl = 0;
2210 : }
2211 : }
2212 10461 : skipSpace(cntxt);
2213 10461 : }
|