LCOV - code coverage report
Current view: top level - monetdb5/mal - mal_parser.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1104 1424 77.5 %
Date: 2024-10-04 20:04:04 Functions: 37 38 97.4 %

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

Generated by: LCOV version 1.14