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

Generated by: LCOV version 1.14