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

Generated by: LCOV version 1.14