src/glpmpl01.c
changeset 1 c445c931472f
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/glpmpl01.c	Mon Dec 06 13:09:21 2010 +0100
     1.3 @@ -0,0 +1,4715 @@
     1.4 +/* glpmpl01.c */
     1.5 +
     1.6 +/***********************************************************************
     1.7 +*  This code is part of GLPK (GNU Linear Programming Kit).
     1.8 +*
     1.9 +*  Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
    1.10 +*  2009, 2010 Andrew Makhorin, Department for Applied Informatics,
    1.11 +*  Moscow Aviation Institute, Moscow, Russia. All rights reserved.
    1.12 +*  E-mail: <mao@gnu.org>.
    1.13 +*
    1.14 +*  GLPK is free software: you can redistribute it and/or modify it
    1.15 +*  under the terms of the GNU General Public License as published by
    1.16 +*  the Free Software Foundation, either version 3 of the License, or
    1.17 +*  (at your option) any later version.
    1.18 +*
    1.19 +*  GLPK is distributed in the hope that it will be useful, but WITHOUT
    1.20 +*  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
    1.21 +*  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
    1.22 +*  License for more details.
    1.23 +*
    1.24 +*  You should have received a copy of the GNU General Public License
    1.25 +*  along with GLPK. If not, see <http://www.gnu.org/licenses/>.
    1.26 +***********************************************************************/
    1.27 +
    1.28 +#define _GLPSTD_STDIO
    1.29 +#include "glpmpl.h"
    1.30 +#define dmp_get_atomv dmp_get_atom
    1.31 +
    1.32 +/**********************************************************************/
    1.33 +/* * *                  PROCESSING MODEL SECTION                  * * */
    1.34 +/**********************************************************************/
    1.35 +
    1.36 +/*----------------------------------------------------------------------
    1.37 +-- enter_context - enter current token into context queue.
    1.38 +--
    1.39 +-- This routine enters the current token into the context queue. */
    1.40 +
    1.41 +void enter_context(MPL *mpl)
    1.42 +{     char *image, *s;
    1.43 +      if (mpl->token == T_EOF)
    1.44 +         image = "_|_";
    1.45 +      else if (mpl->token == T_STRING)
    1.46 +         image = "'...'";
    1.47 +      else
    1.48 +         image = mpl->image;
    1.49 +      xassert(0 <= mpl->c_ptr && mpl->c_ptr < CONTEXT_SIZE);
    1.50 +      mpl->context[mpl->c_ptr++] = ' ';
    1.51 +      if (mpl->c_ptr == CONTEXT_SIZE) mpl->c_ptr = 0;
    1.52 +      for (s = image; *s != '\0'; s++)
    1.53 +      {  mpl->context[mpl->c_ptr++] = *s;
    1.54 +         if (mpl->c_ptr == CONTEXT_SIZE) mpl->c_ptr = 0;
    1.55 +      }
    1.56 +      return;
    1.57 +}
    1.58 +
    1.59 +/*----------------------------------------------------------------------
    1.60 +-- print_context - print current content of context queue.
    1.61 +--
    1.62 +-- This routine prints current content of the context queue. */
    1.63 +
    1.64 +void print_context(MPL *mpl)
    1.65 +{     int c;
    1.66 +      while (mpl->c_ptr > 0)
    1.67 +      {  mpl->c_ptr--;
    1.68 +         c = mpl->context[0];
    1.69 +         memmove(mpl->context, mpl->context+1, CONTEXT_SIZE-1);
    1.70 +         mpl->context[CONTEXT_SIZE-1] = (char)c;
    1.71 +      }
    1.72 +      xprintf("Context: %s%.*s\n", mpl->context[0] == ' ' ? "" : "...",
    1.73 +         CONTEXT_SIZE, mpl->context);
    1.74 +      return;
    1.75 +}
    1.76 +
    1.77 +/*----------------------------------------------------------------------
    1.78 +-- get_char - scan next character from input text file.
    1.79 +--
    1.80 +-- This routine scans a next ASCII character from the input text file.
    1.81 +-- In case of end-of-file, the character is assigned EOF. */
    1.82 +
    1.83 +void get_char(MPL *mpl)
    1.84 +{     int c;
    1.85 +      if (mpl->c == EOF) goto done;
    1.86 +      if (mpl->c == '\n') mpl->line++;
    1.87 +      c = read_char(mpl);
    1.88 +      if (c == EOF)
    1.89 +      {  if (mpl->c == '\n')
    1.90 +            mpl->line--;
    1.91 +         else
    1.92 +            warning(mpl, "final NL missing before end of file");
    1.93 +      }
    1.94 +      else if (c == '\n')
    1.95 +         ;
    1.96 +      else if (isspace(c))
    1.97 +         c = ' ';
    1.98 +      else if (iscntrl(c))
    1.99 +      {  enter_context(mpl);
   1.100 +         error(mpl, "control character 0x%02X not allowed", c);
   1.101 +      }
   1.102 +      mpl->c = c;
   1.103 +done: return;
   1.104 +}
   1.105 +
   1.106 +/*----------------------------------------------------------------------
   1.107 +-- append_char - append character to current token.
   1.108 +--
   1.109 +-- This routine appends the current character to the current token and
   1.110 +-- then scans a next character. */
   1.111 +
   1.112 +void append_char(MPL *mpl)
   1.113 +{     xassert(0 <= mpl->imlen && mpl->imlen <= MAX_LENGTH);
   1.114 +      if (mpl->imlen == MAX_LENGTH)
   1.115 +      {  switch (mpl->token)
   1.116 +         {  case T_NAME:
   1.117 +               enter_context(mpl);
   1.118 +               error(mpl, "symbolic name %s... too long", mpl->image);
   1.119 +            case T_SYMBOL:
   1.120 +               enter_context(mpl);
   1.121 +               error(mpl, "symbol %s... too long", mpl->image);
   1.122 +            case T_NUMBER:
   1.123 +               enter_context(mpl);
   1.124 +               error(mpl, "numeric literal %s... too long", mpl->image);
   1.125 +            case T_STRING:
   1.126 +               enter_context(mpl);
   1.127 +               error(mpl, "string literal too long");
   1.128 +            default:
   1.129 +               xassert(mpl != mpl);
   1.130 +         }
   1.131 +      }
   1.132 +      mpl->image[mpl->imlen++] = (char)mpl->c;
   1.133 +      mpl->image[mpl->imlen] = '\0';
   1.134 +      get_char(mpl);
   1.135 +      return;
   1.136 +}
   1.137 +
   1.138 +/*----------------------------------------------------------------------
   1.139 +-- get_token - scan next token from input text file.
   1.140 +--
   1.141 +-- This routine scans a next token from the input text file using the
   1.142 +-- standard finite automation technique. */
   1.143 +
   1.144 +void get_token(MPL *mpl)
   1.145 +{     /* save the current token */
   1.146 +      mpl->b_token = mpl->token;
   1.147 +      mpl->b_imlen = mpl->imlen;
   1.148 +      strcpy(mpl->b_image, mpl->image);
   1.149 +      mpl->b_value = mpl->value;
   1.150 +      /* if the next token is already scanned, make it current */
   1.151 +      if (mpl->f_scan)
   1.152 +      {  mpl->f_scan = 0;
   1.153 +         mpl->token = mpl->f_token;
   1.154 +         mpl->imlen = mpl->f_imlen;
   1.155 +         strcpy(mpl->image, mpl->f_image);
   1.156 +         mpl->value = mpl->f_value;
   1.157 +         goto done;
   1.158 +      }
   1.159 +loop: /* nothing has been scanned so far */
   1.160 +      mpl->token = 0;
   1.161 +      mpl->imlen = 0;
   1.162 +      mpl->image[0] = '\0';
   1.163 +      mpl->value = 0.0;
   1.164 +      /* skip any uninteresting characters */
   1.165 +      while (mpl->c == ' ' || mpl->c == '\n') get_char(mpl);
   1.166 +      /* recognize and construct the token */
   1.167 +      if (mpl->c == EOF)
   1.168 +      {  /* end-of-file reached */
   1.169 +         mpl->token = T_EOF;
   1.170 +      }
   1.171 +      else if (mpl->c == '#')
   1.172 +      {  /* comment; skip anything until end-of-line */
   1.173 +         while (mpl->c != '\n' && mpl->c != EOF) get_char(mpl);
   1.174 +         goto loop;
   1.175 +      }
   1.176 +      else if (!mpl->flag_d && (isalpha(mpl->c) || mpl->c == '_'))
   1.177 +      {  /* symbolic name or reserved keyword */
   1.178 +         mpl->token = T_NAME;
   1.179 +         while (isalnum(mpl->c) || mpl->c == '_') append_char(mpl);
   1.180 +         if (strcmp(mpl->image, "and") == 0)
   1.181 +            mpl->token = T_AND;
   1.182 +         else if (strcmp(mpl->image, "by") == 0)
   1.183 +            mpl->token = T_BY;
   1.184 +         else if (strcmp(mpl->image, "cross") == 0)
   1.185 +            mpl->token = T_CROSS;
   1.186 +         else if (strcmp(mpl->image, "diff") == 0)
   1.187 +            mpl->token = T_DIFF;
   1.188 +         else if (strcmp(mpl->image, "div") == 0)
   1.189 +            mpl->token = T_DIV;
   1.190 +         else if (strcmp(mpl->image, "else") == 0)
   1.191 +            mpl->token = T_ELSE;
   1.192 +         else if (strcmp(mpl->image, "if") == 0)
   1.193 +            mpl->token = T_IF;
   1.194 +         else if (strcmp(mpl->image, "in") == 0)
   1.195 +            mpl->token = T_IN;
   1.196 +#if 1 /* 21/VII-2006 */
   1.197 +         else if (strcmp(mpl->image, "Infinity") == 0)
   1.198 +            mpl->token = T_INFINITY;
   1.199 +#endif
   1.200 +         else if (strcmp(mpl->image, "inter") == 0)
   1.201 +            mpl->token = T_INTER;
   1.202 +         else if (strcmp(mpl->image, "less") == 0)
   1.203 +            mpl->token = T_LESS;
   1.204 +         else if (strcmp(mpl->image, "mod") == 0)
   1.205 +            mpl->token = T_MOD;
   1.206 +         else if (strcmp(mpl->image, "not") == 0)
   1.207 +            mpl->token = T_NOT;
   1.208 +         else if (strcmp(mpl->image, "or") == 0)
   1.209 +            mpl->token = T_OR;
   1.210 +         else if (strcmp(mpl->image, "s") == 0 && mpl->c == '.')
   1.211 +         {  mpl->token = T_SPTP;
   1.212 +            append_char(mpl);
   1.213 +            if (mpl->c != 't')
   1.214 +sptp:       {  enter_context(mpl);
   1.215 +               error(mpl, "keyword s.t. incomplete");
   1.216 +            }
   1.217 +            append_char(mpl);
   1.218 +            if (mpl->c != '.') goto sptp;
   1.219 +            append_char(mpl);
   1.220 +         }
   1.221 +         else if (strcmp(mpl->image, "symdiff") == 0)
   1.222 +            mpl->token = T_SYMDIFF;
   1.223 +         else if (strcmp(mpl->image, "then") == 0)
   1.224 +            mpl->token = T_THEN;
   1.225 +         else if (strcmp(mpl->image, "union") == 0)
   1.226 +            mpl->token = T_UNION;
   1.227 +         else if (strcmp(mpl->image, "within") == 0)
   1.228 +            mpl->token = T_WITHIN;
   1.229 +      }
   1.230 +      else if (!mpl->flag_d && isdigit(mpl->c))
   1.231 +      {  /* numeric literal */
   1.232 +         mpl->token = T_NUMBER;
   1.233 +         /* scan integer part */
   1.234 +         while (isdigit(mpl->c)) append_char(mpl);
   1.235 +         /* scan optional fractional part */
   1.236 +         if (mpl->c == '.')
   1.237 +         {  append_char(mpl);
   1.238 +            if (mpl->c == '.')
   1.239 +            {  /* hmm, it is not the fractional part, it is dots that
   1.240 +                  follow the integer part */
   1.241 +               mpl->imlen--;
   1.242 +               mpl->image[mpl->imlen] = '\0';
   1.243 +               mpl->f_dots = 1;
   1.244 +               goto conv;
   1.245 +            }
   1.246 +frac:       while (isdigit(mpl->c)) append_char(mpl);
   1.247 +         }
   1.248 +         /* scan optional decimal exponent */
   1.249 +         if (mpl->c == 'e' || mpl->c == 'E')
   1.250 +         {  append_char(mpl);
   1.251 +            if (mpl->c == '+' || mpl->c == '-') append_char(mpl);
   1.252 +            if (!isdigit(mpl->c))
   1.253 +            {  enter_context(mpl);
   1.254 +               error(mpl, "numeric literal %s incomplete", mpl->image);
   1.255 +            }
   1.256 +            while (isdigit(mpl->c)) append_char(mpl);
   1.257 +         }
   1.258 +         /* there must be no letter following the numeric literal */
   1.259 +         if (isalpha(mpl->c) || mpl->c == '_')
   1.260 +         {  enter_context(mpl);
   1.261 +            error(mpl, "symbol %s%c... should be enclosed in quotes",
   1.262 +               mpl->image, mpl->c);
   1.263 +         }
   1.264 +conv:    /* convert numeric literal to floating-point */
   1.265 +         if (str2num(mpl->image, &mpl->value))
   1.266 +err:     {  enter_context(mpl);
   1.267 +            error(mpl, "cannot convert numeric literal %s to floating-p"
   1.268 +               "oint number", mpl->image);
   1.269 +         }
   1.270 +      }
   1.271 +      else if (mpl->c == '\'' || mpl->c == '"')
   1.272 +      {  /* character string */
   1.273 +         int quote = mpl->c;
   1.274 +         mpl->token = T_STRING;
   1.275 +         get_char(mpl);
   1.276 +         for (;;)
   1.277 +         {  if (mpl->c == '\n' || mpl->c == EOF)
   1.278 +            {  enter_context(mpl);
   1.279 +               error(mpl, "unexpected end of line; string literal incom"
   1.280 +                  "plete");
   1.281 +            }
   1.282 +            if (mpl->c == quote)
   1.283 +            {  get_char(mpl);
   1.284 +               if (mpl->c != quote) break;
   1.285 +            }
   1.286 +            append_char(mpl);
   1.287 +         }
   1.288 +      }
   1.289 +      else if (!mpl->flag_d && mpl->c == '+')
   1.290 +         mpl->token = T_PLUS, append_char(mpl);
   1.291 +      else if (!mpl->flag_d && mpl->c == '-')
   1.292 +         mpl->token = T_MINUS, append_char(mpl);
   1.293 +      else if (mpl->c == '*')
   1.294 +      {  mpl->token = T_ASTERISK, append_char(mpl);
   1.295 +         if (mpl->c == '*')
   1.296 +            mpl->token = T_POWER, append_char(mpl);
   1.297 +      }
   1.298 +      else if (mpl->c == '/')
   1.299 +      {  mpl->token = T_SLASH, append_char(mpl);
   1.300 +         if (mpl->c == '*')
   1.301 +         {  /* comment sequence */
   1.302 +            get_char(mpl);
   1.303 +            for (;;)
   1.304 +            {  if (mpl->c == EOF)
   1.305 +               {  /* do not call enter_context at this point */
   1.306 +                  error(mpl, "unexpected end of file; comment sequence "
   1.307 +                     "incomplete");
   1.308 +               }
   1.309 +               else if (mpl->c == '*')
   1.310 +               {  get_char(mpl);
   1.311 +                  if (mpl->c == '/') break;
   1.312 +               }
   1.313 +               else
   1.314 +                  get_char(mpl);
   1.315 +            }
   1.316 +            get_char(mpl);
   1.317 +            goto loop;
   1.318 +         }
   1.319 +      }
   1.320 +      else if (mpl->c == '^')
   1.321 +         mpl->token = T_POWER, append_char(mpl);
   1.322 +      else if (mpl->c == '<')
   1.323 +      {  mpl->token = T_LT, append_char(mpl);
   1.324 +         if (mpl->c == '=')
   1.325 +            mpl->token = T_LE, append_char(mpl);
   1.326 +         else if (mpl->c == '>')
   1.327 +            mpl->token = T_NE, append_char(mpl);
   1.328 +#if 1 /* 11/II-2008 */
   1.329 +         else if (mpl->c == '-')
   1.330 +            mpl->token = T_INPUT, append_char(mpl);
   1.331 +#endif
   1.332 +      }
   1.333 +      else if (mpl->c == '=')
   1.334 +      {  mpl->token = T_EQ, append_char(mpl);
   1.335 +         if (mpl->c == '=') append_char(mpl);
   1.336 +      }
   1.337 +      else if (mpl->c == '>')
   1.338 +      {  mpl->token = T_GT, append_char(mpl);
   1.339 +         if (mpl->c == '=')
   1.340 +            mpl->token = T_GE, append_char(mpl);
   1.341 +#if 1 /* 14/VII-2006 */
   1.342 +         else if (mpl->c == '>')
   1.343 +            mpl->token = T_APPEND, append_char(mpl);
   1.344 +#endif
   1.345 +      }
   1.346 +      else if (mpl->c == '!')
   1.347 +      {  mpl->token = T_NOT, append_char(mpl);
   1.348 +         if (mpl->c == '=')
   1.349 +            mpl->token = T_NE, append_char(mpl);
   1.350 +      }
   1.351 +      else if (mpl->c == '&')
   1.352 +      {  mpl->token = T_CONCAT, append_char(mpl);
   1.353 +         if (mpl->c == '&')
   1.354 +            mpl->token = T_AND, append_char(mpl);
   1.355 +      }
   1.356 +      else if (mpl->c == '|')
   1.357 +      {  mpl->token = T_BAR, append_char(mpl);
   1.358 +         if (mpl->c == '|')
   1.359 +            mpl->token = T_OR, append_char(mpl);
   1.360 +      }
   1.361 +      else if (!mpl->flag_d && mpl->c == '.')
   1.362 +      {  mpl->token = T_POINT, append_char(mpl);
   1.363 +         if (mpl->f_dots)
   1.364 +         {  /* dots; the first dot was read on the previous call to the
   1.365 +               scanner, so the current character is the second dot */
   1.366 +            mpl->token = T_DOTS;
   1.367 +            mpl->imlen = 2;
   1.368 +            strcpy(mpl->image, "..");
   1.369 +            mpl->f_dots = 0;
   1.370 +         }
   1.371 +         else if (mpl->c == '.')
   1.372 +            mpl->token = T_DOTS, append_char(mpl);
   1.373 +         else if (isdigit(mpl->c))
   1.374 +         {  /* numeric literal that begins with the decimal point */
   1.375 +            mpl->token = T_NUMBER, append_char(mpl);
   1.376 +            goto frac;
   1.377 +         }
   1.378 +      }
   1.379 +      else if (mpl->c == ',')
   1.380 +         mpl->token = T_COMMA, append_char(mpl);
   1.381 +      else if (mpl->c == ':')
   1.382 +      {  mpl->token = T_COLON, append_char(mpl);
   1.383 +         if (mpl->c == '=')
   1.384 +            mpl->token = T_ASSIGN, append_char(mpl);
   1.385 +      }
   1.386 +      else if (mpl->c == ';')
   1.387 +         mpl->token = T_SEMICOLON, append_char(mpl);
   1.388 +      else if (mpl->c == '(')
   1.389 +         mpl->token = T_LEFT, append_char(mpl);
   1.390 +      else if (mpl->c == ')')
   1.391 +         mpl->token = T_RIGHT, append_char(mpl);
   1.392 +      else if (mpl->c == '[')
   1.393 +         mpl->token = T_LBRACKET, append_char(mpl);
   1.394 +      else if (mpl->c == ']')
   1.395 +         mpl->token = T_RBRACKET, append_char(mpl);
   1.396 +      else if (mpl->c == '{')
   1.397 +         mpl->token = T_LBRACE, append_char(mpl);
   1.398 +      else if (mpl->c == '}')
   1.399 +         mpl->token = T_RBRACE, append_char(mpl);
   1.400 +#if 1 /* 11/II-2008 */
   1.401 +      else if (mpl->c == '~')
   1.402 +         mpl->token = T_TILDE, append_char(mpl);
   1.403 +#endif
   1.404 +      else if (isalnum(mpl->c) || strchr("+-._", mpl->c) != NULL)
   1.405 +      {  /* symbol */
   1.406 +         xassert(mpl->flag_d);
   1.407 +         mpl->token = T_SYMBOL;
   1.408 +         while (isalnum(mpl->c) || strchr("+-._", mpl->c) != NULL)
   1.409 +            append_char(mpl);
   1.410 +         switch (str2num(mpl->image, &mpl->value))
   1.411 +         {  case 0:
   1.412 +               mpl->token = T_NUMBER;
   1.413 +               break;
   1.414 +            case 1:
   1.415 +               goto err;
   1.416 +            case 2:
   1.417 +               break;
   1.418 +            default:
   1.419 +               xassert(mpl != mpl);
   1.420 +         }
   1.421 +      }
   1.422 +      else
   1.423 +      {  enter_context(mpl);
   1.424 +         error(mpl, "character %c not allowed", mpl->c);
   1.425 +      }
   1.426 +      /* enter the current token into the context queue */
   1.427 +      enter_context(mpl);
   1.428 +      /* reset the flag, which may be set by indexing_expression() and
   1.429 +         is used by expression_list() */
   1.430 +      mpl->flag_x = 0;
   1.431 +done: return;
   1.432 +}
   1.433 +
   1.434 +/*----------------------------------------------------------------------
   1.435 +-- unget_token - return current token back to input stream.
   1.436 +--
   1.437 +-- This routine returns the current token back to the input stream, so
   1.438 +-- the previously scanned token becomes the current one. */
   1.439 +
   1.440 +void unget_token(MPL *mpl)
   1.441 +{     /* save the current token, which becomes the next one */
   1.442 +      xassert(!mpl->f_scan);
   1.443 +      mpl->f_scan = 1;
   1.444 +      mpl->f_token = mpl->token;
   1.445 +      mpl->f_imlen = mpl->imlen;
   1.446 +      strcpy(mpl->f_image, mpl->image);
   1.447 +      mpl->f_value = mpl->value;
   1.448 +      /* restore the previous token, which becomes the current one */
   1.449 +      mpl->token = mpl->b_token;
   1.450 +      mpl->imlen = mpl->b_imlen;
   1.451 +      strcpy(mpl->image, mpl->b_image);
   1.452 +      mpl->value = mpl->b_value;
   1.453 +      return;
   1.454 +}
   1.455 +
   1.456 +/*----------------------------------------------------------------------
   1.457 +-- is_keyword - check if current token is given non-reserved keyword.
   1.458 +--
   1.459 +-- If the current token is given (non-reserved) keyword, this routine
   1.460 +-- returns non-zero. Otherwise zero is returned. */
   1.461 +
   1.462 +int is_keyword(MPL *mpl, char *keyword)
   1.463 +{     return
   1.464 +         mpl->token == T_NAME && strcmp(mpl->image, keyword) == 0;
   1.465 +}
   1.466 +
   1.467 +/*----------------------------------------------------------------------
   1.468 +-- is_reserved - check if current token is reserved keyword.
   1.469 +--
   1.470 +-- If the current token is a reserved keyword, this routine returns
   1.471 +-- non-zero. Otherwise zero is returned. */
   1.472 +
   1.473 +int is_reserved(MPL *mpl)
   1.474 +{     return
   1.475 +         mpl->token == T_AND && mpl->image[0] == 'a' ||
   1.476 +         mpl->token == T_BY ||
   1.477 +         mpl->token == T_CROSS ||
   1.478 +         mpl->token == T_DIFF ||
   1.479 +         mpl->token == T_DIV ||
   1.480 +         mpl->token == T_ELSE ||
   1.481 +         mpl->token == T_IF ||
   1.482 +         mpl->token == T_IN ||
   1.483 +         mpl->token == T_INTER ||
   1.484 +         mpl->token == T_LESS ||
   1.485 +         mpl->token == T_MOD ||
   1.486 +         mpl->token == T_NOT && mpl->image[0] == 'n' ||
   1.487 +         mpl->token == T_OR && mpl->image[0] == 'o' ||
   1.488 +         mpl->token == T_SYMDIFF ||
   1.489 +         mpl->token == T_THEN ||
   1.490 +         mpl->token == T_UNION ||
   1.491 +         mpl->token == T_WITHIN;
   1.492 +}
   1.493 +
   1.494 +/*----------------------------------------------------------------------
   1.495 +-- make_code - generate pseudo-code (basic routine).
   1.496 +--
   1.497 +-- This routine generates specified pseudo-code. It is assumed that all
   1.498 +-- other translator routines use this basic routine. */
   1.499 +
   1.500 +CODE *make_code(MPL *mpl, int op, OPERANDS *arg, int type, int dim)
   1.501 +{     CODE *code;
   1.502 +      DOMAIN *domain;
   1.503 +      DOMAIN_BLOCK *block;
   1.504 +      ARG_LIST *e;
   1.505 +      /* generate pseudo-code */
   1.506 +      code = alloc(CODE);
   1.507 +      code->op = op;
   1.508 +      code->vflag = 0; /* is inherited from operand(s) */
   1.509 +      /* copy operands and also make them referring to the pseudo-code
   1.510 +         being generated, because the latter becomes the parent for all
   1.511 +         its operands */
   1.512 +      memset(&code->arg, '?', sizeof(OPERANDS));
   1.513 +      switch (op)
   1.514 +      {  case O_NUMBER:
   1.515 +            code->arg.num = arg->num;
   1.516 +            break;
   1.517 +         case O_STRING:
   1.518 +            code->arg.str = arg->str;
   1.519 +            break;
   1.520 +         case O_INDEX:
   1.521 +            code->arg.index.slot = arg->index.slot;
   1.522 +            code->arg.index.next = arg->index.next;
   1.523 +            break;
   1.524 +         case O_MEMNUM:
   1.525 +         case O_MEMSYM:
   1.526 +            for (e = arg->par.list; e != NULL; e = e->next)
   1.527 +            {  xassert(e->x != NULL);
   1.528 +               xassert(e->x->up == NULL);
   1.529 +               e->x->up = code;
   1.530 +               code->vflag |= e->x->vflag;
   1.531 +            }
   1.532 +            code->arg.par.par = arg->par.par;
   1.533 +            code->arg.par.list = arg->par.list;
   1.534 +            break;
   1.535 +         case O_MEMSET:
   1.536 +            for (e = arg->set.list; e != NULL; e = e->next)
   1.537 +            {  xassert(e->x != NULL);
   1.538 +               xassert(e->x->up == NULL);
   1.539 +               e->x->up = code;
   1.540 +               code->vflag |= e->x->vflag;
   1.541 +            }
   1.542 +            code->arg.set.set = arg->set.set;
   1.543 +            code->arg.set.list = arg->set.list;
   1.544 +            break;
   1.545 +         case O_MEMVAR:
   1.546 +            for (e = arg->var.list; e != NULL; e = e->next)
   1.547 +            {  xassert(e->x != NULL);
   1.548 +               xassert(e->x->up == NULL);
   1.549 +               e->x->up = code;
   1.550 +               code->vflag |= e->x->vflag;
   1.551 +            }
   1.552 +            code->arg.var.var = arg->var.var;
   1.553 +            code->arg.var.list = arg->var.list;
   1.554 +#if 1 /* 15/V-2010 */
   1.555 +            code->arg.var.suff = arg->var.suff;
   1.556 +#endif
   1.557 +            break;
   1.558 +#if 1 /* 15/V-2010 */
   1.559 +         case O_MEMCON:
   1.560 +            for (e = arg->con.list; e != NULL; e = e->next)
   1.561 +            {  xassert(e->x != NULL);
   1.562 +               xassert(e->x->up == NULL);
   1.563 +               e->x->up = code;
   1.564 +               code->vflag |= e->x->vflag;
   1.565 +            }
   1.566 +            code->arg.con.con = arg->con.con;
   1.567 +            code->arg.con.list = arg->con.list;
   1.568 +            code->arg.con.suff = arg->con.suff;
   1.569 +            break;
   1.570 +#endif
   1.571 +         case O_TUPLE:
   1.572 +         case O_MAKE:
   1.573 +            for (e = arg->list; e != NULL; e = e->next)
   1.574 +            {  xassert(e->x != NULL);
   1.575 +               xassert(e->x->up == NULL);
   1.576 +               e->x->up = code;
   1.577 +               code->vflag |= e->x->vflag;
   1.578 +            }
   1.579 +            code->arg.list = arg->list;
   1.580 +            break;
   1.581 +         case O_SLICE:
   1.582 +            xassert(arg->slice != NULL);
   1.583 +            code->arg.slice = arg->slice;
   1.584 +            break;
   1.585 +         case O_IRAND224:
   1.586 +         case O_UNIFORM01:
   1.587 +         case O_NORMAL01:
   1.588 +         case O_GMTIME:
   1.589 +            code->vflag = 1;
   1.590 +            break;
   1.591 +         case O_CVTNUM:
   1.592 +         case O_CVTSYM:
   1.593 +         case O_CVTLOG:
   1.594 +         case O_CVTTUP:
   1.595 +         case O_CVTLFM:
   1.596 +         case O_PLUS:
   1.597 +         case O_MINUS:
   1.598 +         case O_NOT:
   1.599 +         case O_ABS:
   1.600 +         case O_CEIL:
   1.601 +         case O_FLOOR:
   1.602 +         case O_EXP:
   1.603 +         case O_LOG:
   1.604 +         case O_LOG10:
   1.605 +         case O_SQRT:
   1.606 +         case O_SIN:
   1.607 +         case O_COS:
   1.608 +         case O_ATAN:
   1.609 +         case O_ROUND:
   1.610 +         case O_TRUNC:
   1.611 +         case O_CARD:
   1.612 +         case O_LENGTH:
   1.613 +            /* unary operation */
   1.614 +            xassert(arg->arg.x != NULL);
   1.615 +            xassert(arg->arg.x->up == NULL);
   1.616 +            arg->arg.x->up = code;
   1.617 +            code->vflag |= arg->arg.x->vflag;
   1.618 +            code->arg.arg.x = arg->arg.x;
   1.619 +            break;
   1.620 +         case O_ADD:
   1.621 +         case O_SUB:
   1.622 +         case O_LESS:
   1.623 +         case O_MUL:
   1.624 +         case O_DIV:
   1.625 +         case O_IDIV:
   1.626 +         case O_MOD:
   1.627 +         case O_POWER:
   1.628 +         case O_ATAN2:
   1.629 +         case O_ROUND2:
   1.630 +         case O_TRUNC2:
   1.631 +         case O_UNIFORM:
   1.632 +            if (op == O_UNIFORM) code->vflag = 1;
   1.633 +         case O_NORMAL:
   1.634 +            if (op == O_NORMAL) code->vflag = 1;
   1.635 +         case O_CONCAT:
   1.636 +         case O_LT:
   1.637 +         case O_LE:
   1.638 +         case O_EQ:
   1.639 +         case O_GE:
   1.640 +         case O_GT:
   1.641 +         case O_NE:
   1.642 +         case O_AND:
   1.643 +         case O_OR:
   1.644 +         case O_UNION:
   1.645 +         case O_DIFF:
   1.646 +         case O_SYMDIFF:
   1.647 +         case O_INTER:
   1.648 +         case O_CROSS:
   1.649 +         case O_IN:
   1.650 +         case O_NOTIN:
   1.651 +         case O_WITHIN:
   1.652 +         case O_NOTWITHIN:
   1.653 +         case O_SUBSTR:
   1.654 +         case O_STR2TIME:
   1.655 +         case O_TIME2STR:
   1.656 +            /* binary operation */
   1.657 +            xassert(arg->arg.x != NULL);
   1.658 +            xassert(arg->arg.x->up == NULL);
   1.659 +            arg->arg.x->up = code;
   1.660 +            code->vflag |= arg->arg.x->vflag;
   1.661 +            xassert(arg->arg.y != NULL);
   1.662 +            xassert(arg->arg.y->up == NULL);
   1.663 +            arg->arg.y->up = code;
   1.664 +            code->vflag |= arg->arg.y->vflag;
   1.665 +            code->arg.arg.x = arg->arg.x;
   1.666 +            code->arg.arg.y = arg->arg.y;
   1.667 +            break;
   1.668 +         case O_DOTS:
   1.669 +         case O_FORK:
   1.670 +         case O_SUBSTR3:
   1.671 +            /* ternary operation */
   1.672 +            xassert(arg->arg.x != NULL);
   1.673 +            xassert(arg->arg.x->up == NULL);
   1.674 +            arg->arg.x->up = code;
   1.675 +            code->vflag |= arg->arg.x->vflag;
   1.676 +            xassert(arg->arg.y != NULL);
   1.677 +            xassert(arg->arg.y->up == NULL);
   1.678 +            arg->arg.y->up = code;
   1.679 +            code->vflag |= arg->arg.y->vflag;
   1.680 +            if (arg->arg.z != NULL)
   1.681 +            {  xassert(arg->arg.z->up == NULL);
   1.682 +               arg->arg.z->up = code;
   1.683 +               code->vflag |= arg->arg.z->vflag;
   1.684 +            }
   1.685 +            code->arg.arg.x = arg->arg.x;
   1.686 +            code->arg.arg.y = arg->arg.y;
   1.687 +            code->arg.arg.z = arg->arg.z;
   1.688 +            break;
   1.689 +         case O_MIN:
   1.690 +         case O_MAX:
   1.691 +            /* n-ary operation */
   1.692 +            for (e = arg->list; e != NULL; e = e->next)
   1.693 +            {  xassert(e->x != NULL);
   1.694 +               xassert(e->x->up == NULL);
   1.695 +               e->x->up = code;
   1.696 +               code->vflag |= e->x->vflag;
   1.697 +            }
   1.698 +            code->arg.list = arg->list;
   1.699 +            break;
   1.700 +         case O_SUM:
   1.701 +         case O_PROD:
   1.702 +         case O_MINIMUM:
   1.703 +         case O_MAXIMUM:
   1.704 +         case O_FORALL:
   1.705 +         case O_EXISTS:
   1.706 +         case O_SETOF:
   1.707 +         case O_BUILD:
   1.708 +            /* iterated operation */
   1.709 +            domain = arg->loop.domain;
   1.710 +            xassert(domain != NULL);
   1.711 +            if (domain->code != NULL)
   1.712 +            {  xassert(domain->code->up == NULL);
   1.713 +               domain->code->up = code;
   1.714 +               code->vflag |= domain->code->vflag;
   1.715 +            }
   1.716 +            for (block = domain->list; block != NULL; block =
   1.717 +               block->next)
   1.718 +            {  xassert(block->code != NULL);
   1.719 +               xassert(block->code->up == NULL);
   1.720 +               block->code->up = code;
   1.721 +               code->vflag |= block->code->vflag;
   1.722 +            }
   1.723 +            if (arg->loop.x != NULL)
   1.724 +            {  xassert(arg->loop.x->up == NULL);
   1.725 +               arg->loop.x->up = code;
   1.726 +               code->vflag |= arg->loop.x->vflag;
   1.727 +            }
   1.728 +            code->arg.loop.domain = arg->loop.domain;
   1.729 +            code->arg.loop.x = arg->loop.x;
   1.730 +            break;
   1.731 +         default:
   1.732 +            xassert(op != op);
   1.733 +      }
   1.734 +      /* set other attributes of the pseudo-code */
   1.735 +      code->type = type;
   1.736 +      code->dim = dim;
   1.737 +      code->up = NULL;
   1.738 +      code->valid = 0;
   1.739 +      memset(&code->value, '?', sizeof(VALUE));
   1.740 +      return code;
   1.741 +}
   1.742 +
   1.743 +/*----------------------------------------------------------------------
   1.744 +-- make_unary - generate pseudo-code for unary operation.
   1.745 +--
   1.746 +-- This routine generates pseudo-code for unary operation. */
   1.747 +
   1.748 +CODE *make_unary(MPL *mpl, int op, CODE *x, int type, int dim)
   1.749 +{     CODE *code;
   1.750 +      OPERANDS arg;
   1.751 +      xassert(x != NULL);
   1.752 +      arg.arg.x = x;
   1.753 +      code = make_code(mpl, op, &arg, type, dim);
   1.754 +      return code;
   1.755 +}
   1.756 +
   1.757 +/*----------------------------------------------------------------------
   1.758 +-- make_binary - generate pseudo-code for binary operation.
   1.759 +--
   1.760 +-- This routine generates pseudo-code for binary operation. */
   1.761 +
   1.762 +CODE *make_binary(MPL *mpl, int op, CODE *x, CODE *y, int type,
   1.763 +      int dim)
   1.764 +{     CODE *code;
   1.765 +      OPERANDS arg;
   1.766 +      xassert(x != NULL);
   1.767 +      xassert(y != NULL);
   1.768 +      arg.arg.x = x;
   1.769 +      arg.arg.y = y;
   1.770 +      code = make_code(mpl, op, &arg, type, dim);
   1.771 +      return code;
   1.772 +}
   1.773 +
   1.774 +/*----------------------------------------------------------------------
   1.775 +-- make_ternary - generate pseudo-code for ternary operation.
   1.776 +--
   1.777 +-- This routine generates pseudo-code for ternary operation. */
   1.778 +
   1.779 +CODE *make_ternary(MPL *mpl, int op, CODE *x, CODE *y, CODE *z,
   1.780 +      int type, int dim)
   1.781 +{     CODE *code;
   1.782 +      OPERANDS arg;
   1.783 +      xassert(x != NULL);
   1.784 +      xassert(y != NULL);
   1.785 +      /* third operand can be NULL */
   1.786 +      arg.arg.x = x;
   1.787 +      arg.arg.y = y;
   1.788 +      arg.arg.z = z;
   1.789 +      code = make_code(mpl, op, &arg, type, dim);
   1.790 +      return code;
   1.791 +}
   1.792 +
   1.793 +/*----------------------------------------------------------------------
   1.794 +-- numeric_literal - parse reference to numeric literal.
   1.795 +--
   1.796 +-- This routine parses primary expression using the syntax:
   1.797 +--
   1.798 +-- <primary expression> ::= <numeric literal> */
   1.799 +
   1.800 +CODE *numeric_literal(MPL *mpl)
   1.801 +{     CODE *code;
   1.802 +      OPERANDS arg;
   1.803 +      xassert(mpl->token == T_NUMBER);
   1.804 +      arg.num = mpl->value;
   1.805 +      code = make_code(mpl, O_NUMBER, &arg, A_NUMERIC, 0);
   1.806 +      get_token(mpl /* <numeric literal> */);
   1.807 +      return code;
   1.808 +}
   1.809 +
   1.810 +/*----------------------------------------------------------------------
   1.811 +-- string_literal - parse reference to string literal.
   1.812 +--
   1.813 +-- This routine parses primary expression using the syntax:
   1.814 +--
   1.815 +-- <primary expression> ::= <string literal> */
   1.816 +
   1.817 +CODE *string_literal(MPL *mpl)
   1.818 +{     CODE *code;
   1.819 +      OPERANDS arg;
   1.820 +      xassert(mpl->token == T_STRING);
   1.821 +      arg.str = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1);
   1.822 +      strcpy(arg.str, mpl->image);
   1.823 +      code = make_code(mpl, O_STRING, &arg, A_SYMBOLIC, 0);
   1.824 +      get_token(mpl /* <string literal> */);
   1.825 +      return code;
   1.826 +}
   1.827 +
   1.828 +/*----------------------------------------------------------------------
   1.829 +-- create_arg_list - create empty operands list.
   1.830 +--
   1.831 +-- This routine creates operands list, which is initially empty. */
   1.832 +
   1.833 +ARG_LIST *create_arg_list(MPL *mpl)
   1.834 +{     ARG_LIST *list;
   1.835 +      xassert(mpl == mpl);
   1.836 +      list = NULL;
   1.837 +      return list;
   1.838 +}
   1.839 +
   1.840 +/*----------------------------------------------------------------------
   1.841 +-- expand_arg_list - append operand to operands list.
   1.842 +--
   1.843 +-- This routine appends new operand to specified operands list. */
   1.844 +
   1.845 +ARG_LIST *expand_arg_list(MPL *mpl, ARG_LIST *list, CODE *x)
   1.846 +{     ARG_LIST *tail, *temp;
   1.847 +      xassert(x != NULL);
   1.848 +      /* create new operands list entry */
   1.849 +      tail = alloc(ARG_LIST);
   1.850 +      tail->x = x;
   1.851 +      tail->next = NULL;
   1.852 +      /* and append it to the operands list */
   1.853 +      if (list == NULL)
   1.854 +         list = tail;
   1.855 +      else
   1.856 +      {  for (temp = list; temp->next != NULL; temp = temp->next);
   1.857 +         temp->next = tail;
   1.858 +      }
   1.859 +      return list;
   1.860 +}
   1.861 +
   1.862 +/*----------------------------------------------------------------------
   1.863 +-- arg_list_len - determine length of operands list.
   1.864 +--
   1.865 +-- This routine returns the number of operands in operands list. */
   1.866 +
   1.867 +int arg_list_len(MPL *mpl, ARG_LIST *list)
   1.868 +{     ARG_LIST *temp;
   1.869 +      int len;
   1.870 +      xassert(mpl == mpl);
   1.871 +      len = 0;
   1.872 +      for (temp = list; temp != NULL; temp = temp->next) len++;
   1.873 +      return len;
   1.874 +}
   1.875 +
   1.876 +/*----------------------------------------------------------------------
   1.877 +-- subscript_list - parse subscript list.
   1.878 +--
   1.879 +-- This routine parses subscript list using the syntax:
   1.880 +--
   1.881 +-- <subscript list> ::= <subscript>
   1.882 +-- <subscript list> ::= <subscript list> , <subscript>
   1.883 +-- <subscript> ::= <expression 5> */
   1.884 +
   1.885 +ARG_LIST *subscript_list(MPL *mpl)
   1.886 +{     ARG_LIST *list;
   1.887 +      CODE *x;
   1.888 +      list = create_arg_list(mpl);
   1.889 +      for (;;)
   1.890 +      {  /* parse subscript expression */
   1.891 +         x = expression_5(mpl);
   1.892 +         /* convert it to symbolic type, if necessary */
   1.893 +         if (x->type == A_NUMERIC)
   1.894 +            x = make_unary(mpl, O_CVTSYM, x, A_SYMBOLIC, 0);
   1.895 +         /* check that now the expression is of symbolic type */
   1.896 +         if (x->type != A_SYMBOLIC)
   1.897 +            error(mpl, "subscript expression has invalid type");
   1.898 +         xassert(x->dim == 0);
   1.899 +         /* and append it to the subscript list */
   1.900 +         list = expand_arg_list(mpl, list, x);
   1.901 +         /* check a token that follows the subscript expression */
   1.902 +         if (mpl->token == T_COMMA)
   1.903 +            get_token(mpl /* , */);
   1.904 +         else if (mpl->token == T_RBRACKET)
   1.905 +            break;
   1.906 +         else
   1.907 +            error(mpl, "syntax error in subscript list");
   1.908 +      }
   1.909 +      return list;
   1.910 +}
   1.911 +
   1.912 +#if 1 /* 15/V-2010 */
   1.913 +/*----------------------------------------------------------------------
   1.914 +-- object_reference - parse reference to named object.
   1.915 +--
   1.916 +-- This routine parses primary expression using the syntax:
   1.917 +--
   1.918 +-- <primary expression> ::= <dummy index>
   1.919 +-- <primary expression> ::= <set name>
   1.920 +-- <primary expression> ::= <set name> [ <subscript list> ]
   1.921 +-- <primary expression> ::= <parameter name>
   1.922 +-- <primary expression> ::= <parameter name> [ <subscript list> ]
   1.923 +-- <primary expression> ::= <variable name> <suffix>
   1.924 +-- <primary expression> ::= <variable name> [ <subscript list> ]
   1.925 +--                          <suffix>
   1.926 +-- <primary expression> ::= <constraint name> <suffix>
   1.927 +-- <primary expression> ::= <constraint name> [ <subscript list> ]
   1.928 +--                          <suffix>
   1.929 +-- <dummy index> ::= <symbolic name>
   1.930 +-- <set name> ::= <symbolic name>
   1.931 +-- <parameter name> ::= <symbolic name>
   1.932 +-- <variable name> ::= <symbolic name>
   1.933 +-- <constraint name> ::= <symbolic name>
   1.934 +-- <suffix> ::= <empty> | .lb | .ub | .status | .val | .dual */
   1.935 +
   1.936 +CODE *object_reference(MPL *mpl)
   1.937 +{     AVLNODE *node;
   1.938 +      DOMAIN_SLOT *slot;
   1.939 +      SET *set;
   1.940 +      PARAMETER *par;
   1.941 +      VARIABLE *var;
   1.942 +      CONSTRAINT *con;
   1.943 +      ARG_LIST *list;
   1.944 +      OPERANDS arg;
   1.945 +      CODE *code;
   1.946 +      char *name;
   1.947 +      int dim, suff;
   1.948 +      /* find the object in the symbolic name table */
   1.949 +      xassert(mpl->token == T_NAME);
   1.950 +      node = avl_find_node(mpl->tree, mpl->image);
   1.951 +      if (node == NULL)
   1.952 +         error(mpl, "%s not defined", mpl->image);
   1.953 +      /* check the object type and obtain its dimension */
   1.954 +      switch (avl_get_node_type(node))
   1.955 +      {  case A_INDEX:
   1.956 +            /* dummy index */
   1.957 +            slot = (DOMAIN_SLOT *)avl_get_node_link(node);
   1.958 +            name = slot->name;
   1.959 +            dim = 0;
   1.960 +            break;
   1.961 +         case A_SET:
   1.962 +            /* model set */
   1.963 +            set = (SET *)avl_get_node_link(node);
   1.964 +            name = set->name;
   1.965 +            dim = set->dim;
   1.966 +            /* if a set object is referenced in its own declaration and
   1.967 +               the dimen attribute is not specified yet, use dimen 1 by
   1.968 +               default */
   1.969 +            if (set->dimen == 0) set->dimen = 1;
   1.970 +            break;
   1.971 +         case A_PARAMETER:
   1.972 +            /* model parameter */
   1.973 +            par = (PARAMETER *)avl_get_node_link(node);
   1.974 +            name = par->name;
   1.975 +            dim = par->dim;
   1.976 +            break;
   1.977 +         case A_VARIABLE:
   1.978 +            /* model variable */
   1.979 +            var = (VARIABLE *)avl_get_node_link(node);
   1.980 +            name = var->name;
   1.981 +            dim = var->dim;
   1.982 +            break;
   1.983 +         case A_CONSTRAINT:
   1.984 +            /* model constraint or objective */
   1.985 +            con = (CONSTRAINT *)avl_get_node_link(node);
   1.986 +            name = con->name;
   1.987 +            dim = con->dim;
   1.988 +            break;
   1.989 +         default:
   1.990 +            xassert(node != node);
   1.991 +      }
   1.992 +      get_token(mpl /* <symbolic name> */);
   1.993 +      /* parse optional subscript list */
   1.994 +      if (mpl->token == T_LBRACKET)
   1.995 +      {  /* subscript list is specified */
   1.996 +         if (dim == 0)
   1.997 +            error(mpl, "%s cannot be subscripted", name);
   1.998 +         get_token(mpl /* [ */);
   1.999 +         list = subscript_list(mpl);
  1.1000 +         if (dim != arg_list_len(mpl, list))
  1.1001 +            error(mpl, "%s must have %d subscript%s rather than %d",
  1.1002 +               name, dim, dim == 1 ? "" : "s", arg_list_len(mpl, list));
  1.1003 +         xassert(mpl->token == T_RBRACKET);
  1.1004 +         get_token(mpl /* ] */);
  1.1005 +      }
  1.1006 +      else
  1.1007 +      {  /* subscript list is not specified */
  1.1008 +         if (dim != 0)
  1.1009 +            error(mpl, "%s must be subscripted", name);
  1.1010 +         list = create_arg_list(mpl);
  1.1011 +      }
  1.1012 +      /* parse optional suffix */
  1.1013 +      if (!mpl->flag_s && avl_get_node_type(node) == A_VARIABLE)
  1.1014 +         suff = DOT_NONE;
  1.1015 +      else
  1.1016 +         suff = DOT_VAL;
  1.1017 +      if (mpl->token == T_POINT)
  1.1018 +      {  get_token(mpl /* . */);
  1.1019 +         if (mpl->token != T_NAME)
  1.1020 +            error(mpl, "invalid use of period");
  1.1021 +         if (!(avl_get_node_type(node) == A_VARIABLE ||
  1.1022 +               avl_get_node_type(node) == A_CONSTRAINT))
  1.1023 +            error(mpl, "%s cannot have a suffix", name);
  1.1024 +         if (strcmp(mpl->image, "lb") == 0)
  1.1025 +            suff = DOT_LB;
  1.1026 +         else if (strcmp(mpl->image, "ub") == 0)
  1.1027 +            suff = DOT_UB;
  1.1028 +         else if (strcmp(mpl->image, "status") == 0)
  1.1029 +            suff = DOT_STATUS;
  1.1030 +         else if (strcmp(mpl->image, "val") == 0)
  1.1031 +            suff = DOT_VAL;
  1.1032 +         else if (strcmp(mpl->image, "dual") == 0)
  1.1033 +            suff = DOT_DUAL;
  1.1034 +         else
  1.1035 +            error(mpl, "suffix .%s invalid", mpl->image);
  1.1036 +         get_token(mpl /* suffix */);
  1.1037 +      }
  1.1038 +      /* generate pseudo-code to take value of the object */
  1.1039 +      switch (avl_get_node_type(node))
  1.1040 +      {  case A_INDEX:
  1.1041 +            arg.index.slot = slot;
  1.1042 +            arg.index.next = slot->list;
  1.1043 +            code = make_code(mpl, O_INDEX, &arg, A_SYMBOLIC, 0);
  1.1044 +            slot->list = code;
  1.1045 +            break;
  1.1046 +         case A_SET:
  1.1047 +            arg.set.set = set;
  1.1048 +            arg.set.list = list;
  1.1049 +            code = make_code(mpl, O_MEMSET, &arg, A_ELEMSET,
  1.1050 +               set->dimen);
  1.1051 +            break;
  1.1052 +         case A_PARAMETER:
  1.1053 +            arg.par.par = par;
  1.1054 +            arg.par.list = list;
  1.1055 +            if (par->type == A_SYMBOLIC)
  1.1056 +               code = make_code(mpl, O_MEMSYM, &arg, A_SYMBOLIC, 0);
  1.1057 +            else
  1.1058 +               code = make_code(mpl, O_MEMNUM, &arg, A_NUMERIC, 0);
  1.1059 +            break;
  1.1060 +         case A_VARIABLE:
  1.1061 +            if (!mpl->flag_s && (suff == DOT_STATUS || suff == DOT_VAL
  1.1062 +               || suff == DOT_DUAL))
  1.1063 +               error(mpl, "invalid reference to status, primal value, o"
  1.1064 +                  "r dual value of variable %s above solve statement",
  1.1065 +                  var->name);
  1.1066 +            arg.var.var = var;
  1.1067 +            arg.var.list = list;
  1.1068 +            arg.var.suff = suff;
  1.1069 +            code = make_code(mpl, O_MEMVAR, &arg, suff == DOT_NONE ?
  1.1070 +               A_FORMULA : A_NUMERIC, 0);
  1.1071 +            break;
  1.1072 +         case A_CONSTRAINT:
  1.1073 +            if (!mpl->flag_s && (suff == DOT_STATUS || suff == DOT_VAL
  1.1074 +               || suff == DOT_DUAL))
  1.1075 +               error(mpl, "invalid reference to status, primal value, o"
  1.1076 +                  "r dual value of %s %s above solve statement",
  1.1077 +                  con->type == A_CONSTRAINT ? "constraint" : "objective"
  1.1078 +                  , con->name);
  1.1079 +            arg.con.con = con;
  1.1080 +            arg.con.list = list;
  1.1081 +            arg.con.suff = suff;
  1.1082 +            code = make_code(mpl, O_MEMCON, &arg, A_NUMERIC, 0);
  1.1083 +            break;
  1.1084 +         default:
  1.1085 +            xassert(node != node);
  1.1086 +      }
  1.1087 +      return code;
  1.1088 +}
  1.1089 +#endif
  1.1090 +
  1.1091 +/*----------------------------------------------------------------------
  1.1092 +-- numeric_argument - parse argument passed to built-in function.
  1.1093 +--
  1.1094 +-- This routine parses an argument passed to numeric built-in function
  1.1095 +-- using the syntax:
  1.1096 +--
  1.1097 +-- <arg> ::= <expression 5> */
  1.1098 +
  1.1099 +CODE *numeric_argument(MPL *mpl, char *func)
  1.1100 +{     CODE *x;
  1.1101 +      x = expression_5(mpl);
  1.1102 +      /* convert the argument to numeric type, if necessary */
  1.1103 +      if (x->type == A_SYMBOLIC)
  1.1104 +         x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0);
  1.1105 +      /* check that now the argument is of numeric type */
  1.1106 +      if (x->type != A_NUMERIC)
  1.1107 +         error(mpl, "argument for %s has invalid type", func);
  1.1108 +      xassert(x->dim == 0);
  1.1109 +      return x;
  1.1110 +}
  1.1111 +
  1.1112 +#if 1 /* 15/VII-2006 */
  1.1113 +CODE *symbolic_argument(MPL *mpl, char *func)
  1.1114 +{     CODE *x;
  1.1115 +      x = expression_5(mpl);
  1.1116 +      /* convert the argument to symbolic type, if necessary */
  1.1117 +      if (x->type == A_NUMERIC)
  1.1118 +         x = make_unary(mpl, O_CVTSYM, x, A_SYMBOLIC, 0);
  1.1119 +      /* check that now the argument is of symbolic type */
  1.1120 +      if (x->type != A_SYMBOLIC)
  1.1121 +         error(mpl, "argument for %s has invalid type", func);
  1.1122 +      xassert(x->dim == 0);
  1.1123 +      return x;
  1.1124 +}
  1.1125 +#endif
  1.1126 +
  1.1127 +#if 1 /* 15/VII-2006 */
  1.1128 +CODE *elemset_argument(MPL *mpl, char *func)
  1.1129 +{     CODE *x;
  1.1130 +      x = expression_9(mpl);
  1.1131 +      if (x->type != A_ELEMSET)
  1.1132 +         error(mpl, "argument for %s has invalid type", func);
  1.1133 +      xassert(x->dim > 0);
  1.1134 +      return x;
  1.1135 +}
  1.1136 +#endif
  1.1137 +
  1.1138 +/*----------------------------------------------------------------------
  1.1139 +-- function_reference - parse reference to built-in function.
  1.1140 +--
  1.1141 +-- This routine parses primary expression using the syntax:
  1.1142 +--
  1.1143 +-- <primary expression> ::= abs ( <arg> )
  1.1144 +-- <primary expression> ::= ceil ( <arg> )
  1.1145 +-- <primary expression> ::= floor ( <arg> )
  1.1146 +-- <primary expression> ::= exp ( <arg> )
  1.1147 +-- <primary expression> ::= log ( <arg> )
  1.1148 +-- <primary expression> ::= log10 ( <arg> )
  1.1149 +-- <primary expression> ::= max ( <arg list> )
  1.1150 +-- <primary expression> ::= min ( <arg list> )
  1.1151 +-- <primary expression> ::= sqrt ( <arg> )
  1.1152 +-- <primary expression> ::= sin ( <arg> )
  1.1153 +-- <primary expression> ::= cos ( <arg> )
  1.1154 +-- <primary expression> ::= atan ( <arg> )
  1.1155 +-- <primary expression> ::= atan2 ( <arg> , <arg> )
  1.1156 +-- <primary expression> ::= round ( <arg> )
  1.1157 +-- <primary expression> ::= round ( <arg> , <arg> )
  1.1158 +-- <primary expression> ::= trunc ( <arg> )
  1.1159 +-- <primary expression> ::= trunc ( <arg> , <arg> )
  1.1160 +-- <primary expression> ::= Irand224 ( )
  1.1161 +-- <primary expression> ::= Uniform01 ( )
  1.1162 +-- <primary expression> ::= Uniform ( <arg> , <arg> )
  1.1163 +-- <primary expression> ::= Normal01 ( )
  1.1164 +-- <primary expression> ::= Normal ( <arg> , <arg> )
  1.1165 +-- <primary expression> ::= card ( <arg> )
  1.1166 +-- <primary expression> ::= length ( <arg> )
  1.1167 +-- <primary expression> ::= substr ( <arg> , <arg> )
  1.1168 +-- <primary expression> ::= substr ( <arg> , <arg> , <arg> )
  1.1169 +-- <primary expression> ::= str2time ( <arg> , <arg> )
  1.1170 +-- <primary expression> ::= time2str ( <arg> , <arg> )
  1.1171 +-- <primary expression> ::= gmtime ( )
  1.1172 +-- <arg list> ::= <arg>
  1.1173 +-- <arg list> ::= <arg list> , <arg> */
  1.1174 +
  1.1175 +CODE *function_reference(MPL *mpl)
  1.1176 +{     CODE *code;
  1.1177 +      OPERANDS arg;
  1.1178 +      int op;
  1.1179 +      char func[15+1];
  1.1180 +      /* determine operation code */
  1.1181 +      xassert(mpl->token == T_NAME);
  1.1182 +      if (strcmp(mpl->image, "abs") == 0)
  1.1183 +         op = O_ABS;
  1.1184 +      else if (strcmp(mpl->image, "ceil") == 0)
  1.1185 +         op = O_CEIL;
  1.1186 +      else if (strcmp(mpl->image, "floor") == 0)
  1.1187 +         op = O_FLOOR;
  1.1188 +      else if (strcmp(mpl->image, "exp") == 0)
  1.1189 +         op = O_EXP;
  1.1190 +      else if (strcmp(mpl->image, "log") == 0)
  1.1191 +         op = O_LOG;
  1.1192 +      else if (strcmp(mpl->image, "log10") == 0)
  1.1193 +         op = O_LOG10;
  1.1194 +      else if (strcmp(mpl->image, "sqrt") == 0)
  1.1195 +         op = O_SQRT;
  1.1196 +      else if (strcmp(mpl->image, "sin") == 0)
  1.1197 +         op = O_SIN;
  1.1198 +      else if (strcmp(mpl->image, "cos") == 0)
  1.1199 +         op = O_COS;
  1.1200 +      else if (strcmp(mpl->image, "atan") == 0)
  1.1201 +         op = O_ATAN;
  1.1202 +      else if (strcmp(mpl->image, "min") == 0)
  1.1203 +         op = O_MIN;
  1.1204 +      else if (strcmp(mpl->image, "max") == 0)
  1.1205 +         op = O_MAX;
  1.1206 +      else if (strcmp(mpl->image, "round") == 0)
  1.1207 +         op = O_ROUND;
  1.1208 +      else if (strcmp(mpl->image, "trunc") == 0)
  1.1209 +         op = O_TRUNC;
  1.1210 +      else if (strcmp(mpl->image, "Irand224") == 0)
  1.1211 +         op = O_IRAND224;
  1.1212 +      else if (strcmp(mpl->image, "Uniform01") == 0)
  1.1213 +         op = O_UNIFORM01;
  1.1214 +      else if (strcmp(mpl->image, "Uniform") == 0)
  1.1215 +         op = O_UNIFORM;
  1.1216 +      else if (strcmp(mpl->image, "Normal01") == 0)
  1.1217 +         op = O_NORMAL01;
  1.1218 +      else if (strcmp(mpl->image, "Normal") == 0)
  1.1219 +         op = O_NORMAL;
  1.1220 +      else if (strcmp(mpl->image, "card") == 0)
  1.1221 +         op = O_CARD;
  1.1222 +      else if (strcmp(mpl->image, "length") == 0)
  1.1223 +         op = O_LENGTH;
  1.1224 +      else if (strcmp(mpl->image, "substr") == 0)
  1.1225 +         op = O_SUBSTR;
  1.1226 +      else if (strcmp(mpl->image, "str2time") == 0)
  1.1227 +         op = O_STR2TIME;
  1.1228 +      else if (strcmp(mpl->image, "time2str") == 0)
  1.1229 +         op = O_TIME2STR;
  1.1230 +      else if (strcmp(mpl->image, "gmtime") == 0)
  1.1231 +         op = O_GMTIME;
  1.1232 +      else
  1.1233 +         error(mpl, "function %s unknown", mpl->image);
  1.1234 +      /* save symbolic name of the function */
  1.1235 +      strcpy(func, mpl->image);
  1.1236 +      xassert(strlen(func) < sizeof(func));
  1.1237 +      get_token(mpl /* <symbolic name> */);
  1.1238 +      /* check the left parenthesis that follows the function name */
  1.1239 +      xassert(mpl->token == T_LEFT);
  1.1240 +      get_token(mpl /* ( */);
  1.1241 +      /* parse argument list */
  1.1242 +      if (op == O_MIN || op == O_MAX)
  1.1243 +      {  /* min and max allow arbitrary number of arguments */
  1.1244 +         arg.list = create_arg_list(mpl);
  1.1245 +         /* parse argument list */
  1.1246 +         for (;;)
  1.1247 +         {  /* parse argument and append it to the operands list */
  1.1248 +            arg.list = expand_arg_list(mpl, arg.list,
  1.1249 +               numeric_argument(mpl, func));
  1.1250 +            /* check a token that follows the argument */
  1.1251 +            if (mpl->token == T_COMMA)
  1.1252 +               get_token(mpl /* , */);
  1.1253 +            else if (mpl->token == T_RIGHT)
  1.1254 +               break;
  1.1255 +            else
  1.1256 +               error(mpl, "syntax error in argument list for %s", func);
  1.1257 +         }
  1.1258 +      }
  1.1259 +      else if (op == O_IRAND224 || op == O_UNIFORM01 || op ==
  1.1260 +         O_NORMAL01 || op == O_GMTIME)
  1.1261 +      {  /* Irand224, Uniform01, Normal01, gmtime need no arguments */
  1.1262 +         if (mpl->token != T_RIGHT)
  1.1263 +            error(mpl, "%s needs no arguments", func);
  1.1264 +      }
  1.1265 +      else if (op == O_UNIFORM || op == O_NORMAL)
  1.1266 +      {  /* Uniform and Normal need two arguments */
  1.1267 +         /* parse the first argument */
  1.1268 +         arg.arg.x = numeric_argument(mpl, func);
  1.1269 +         /* check a token that follows the first argument */
  1.1270 +         if (mpl->token == T_COMMA)
  1.1271 +            ;
  1.1272 +         else if (mpl->token == T_RIGHT)
  1.1273 +            error(mpl, "%s needs two arguments", func);
  1.1274 +         else
  1.1275 +            error(mpl, "syntax error in argument for %s", func);
  1.1276 +         get_token(mpl /* , */);
  1.1277 +         /* parse the second argument */
  1.1278 +         arg.arg.y = numeric_argument(mpl, func);
  1.1279 +         /* check a token that follows the second argument */
  1.1280 +         if (mpl->token == T_COMMA)
  1.1281 +            error(mpl, "%s needs two argument", func);
  1.1282 +         else if (mpl->token == T_RIGHT)
  1.1283 +            ;
  1.1284 +         else
  1.1285 +            error(mpl, "syntax error in argument for %s", func);
  1.1286 +      }
  1.1287 +      else if (op == O_ATAN || op == O_ROUND || op == O_TRUNC)
  1.1288 +      {  /* atan, round, and trunc need one or two arguments */
  1.1289 +         /* parse the first argument */
  1.1290 +         arg.arg.x = numeric_argument(mpl, func);
  1.1291 +         /* parse the second argument, if specified */
  1.1292 +         if (mpl->token == T_COMMA)
  1.1293 +         {  switch (op)
  1.1294 +            {  case O_ATAN:  op = O_ATAN2;  break;
  1.1295 +               case O_ROUND: op = O_ROUND2; break;
  1.1296 +               case O_TRUNC: op = O_TRUNC2; break;
  1.1297 +               default: xassert(op != op);
  1.1298 +            }
  1.1299 +            get_token(mpl /* , */);
  1.1300 +            arg.arg.y = numeric_argument(mpl, func);
  1.1301 +         }
  1.1302 +         /* check a token that follows the last argument */
  1.1303 +         if (mpl->token == T_COMMA)
  1.1304 +            error(mpl, "%s needs one or two arguments", func);
  1.1305 +         else if (mpl->token == T_RIGHT)
  1.1306 +            ;
  1.1307 +         else
  1.1308 +            error(mpl, "syntax error in argument for %s", func);
  1.1309 +      }
  1.1310 +      else if (op == O_SUBSTR)
  1.1311 +      {  /* substr needs two or three arguments */
  1.1312 +         /* parse the first argument */
  1.1313 +         arg.arg.x = symbolic_argument(mpl, func);
  1.1314 +         /* check a token that follows the first argument */
  1.1315 +         if (mpl->token == T_COMMA)
  1.1316 +            ;
  1.1317 +         else if (mpl->token == T_RIGHT)
  1.1318 +            error(mpl, "%s needs two or three arguments", func);
  1.1319 +         else
  1.1320 +            error(mpl, "syntax error in argument for %s", func);
  1.1321 +         get_token(mpl /* , */);
  1.1322 +         /* parse the second argument */
  1.1323 +         arg.arg.y = numeric_argument(mpl, func);
  1.1324 +         /* parse the third argument, if specified */
  1.1325 +         if (mpl->token == T_COMMA)
  1.1326 +         {  op = O_SUBSTR3;
  1.1327 +            get_token(mpl /* , */);
  1.1328 +            arg.arg.z = numeric_argument(mpl, func);
  1.1329 +         }
  1.1330 +         /* check a token that follows the last argument */
  1.1331 +         if (mpl->token == T_COMMA)
  1.1332 +            error(mpl, "%s needs two or three arguments", func);
  1.1333 +         else if (mpl->token == T_RIGHT)
  1.1334 +            ;
  1.1335 +         else
  1.1336 +            error(mpl, "syntax error in argument for %s", func);
  1.1337 +      }
  1.1338 +      else if (op == O_STR2TIME)
  1.1339 +      {  /* str2time needs two arguments, both symbolic */
  1.1340 +         /* parse the first argument */
  1.1341 +         arg.arg.x = symbolic_argument(mpl, func);
  1.1342 +         /* check a token that follows the first argument */
  1.1343 +         if (mpl->token == T_COMMA)
  1.1344 +            ;
  1.1345 +         else if (mpl->token == T_RIGHT)
  1.1346 +            error(mpl, "%s needs two arguments", func);
  1.1347 +         else
  1.1348 +            error(mpl, "syntax error in argument for %s", func);
  1.1349 +         get_token(mpl /* , */);
  1.1350 +         /* parse the second argument */
  1.1351 +         arg.arg.y = symbolic_argument(mpl, func);
  1.1352 +         /* check a token that follows the second argument */
  1.1353 +         if (mpl->token == T_COMMA)
  1.1354 +            error(mpl, "%s needs two argument", func);
  1.1355 +         else if (mpl->token == T_RIGHT)
  1.1356 +            ;
  1.1357 +         else
  1.1358 +            error(mpl, "syntax error in argument for %s", func);
  1.1359 +      }
  1.1360 +      else if (op == O_TIME2STR)
  1.1361 +      {  /* time2str needs two arguments, numeric and symbolic */
  1.1362 +         /* parse the first argument */
  1.1363 +         arg.arg.x = numeric_argument(mpl, func);
  1.1364 +         /* check a token that follows the first argument */
  1.1365 +         if (mpl->token == T_COMMA)
  1.1366 +            ;
  1.1367 +         else if (mpl->token == T_RIGHT)
  1.1368 +            error(mpl, "%s needs two arguments", func);
  1.1369 +         else
  1.1370 +            error(mpl, "syntax error in argument for %s", func);
  1.1371 +         get_token(mpl /* , */);
  1.1372 +         /* parse the second argument */
  1.1373 +         arg.arg.y = symbolic_argument(mpl, func);
  1.1374 +         /* check a token that follows the second argument */
  1.1375 +         if (mpl->token == T_COMMA)
  1.1376 +            error(mpl, "%s needs two argument", func);
  1.1377 +         else if (mpl->token == T_RIGHT)
  1.1378 +            ;
  1.1379 +         else
  1.1380 +            error(mpl, "syntax error in argument for %s", func);
  1.1381 +      }
  1.1382 +      else
  1.1383 +      {  /* other functions need one argument */
  1.1384 +         if (op == O_CARD)
  1.1385 +            arg.arg.x = elemset_argument(mpl, func);
  1.1386 +         else if (op == O_LENGTH)
  1.1387 +            arg.arg.x = symbolic_argument(mpl, func);
  1.1388 +         else
  1.1389 +            arg.arg.x = numeric_argument(mpl, func);
  1.1390 +         /* check a token that follows the argument */
  1.1391 +         if (mpl->token == T_COMMA)
  1.1392 +            error(mpl, "%s needs one argument", func);
  1.1393 +         else if (mpl->token == T_RIGHT)
  1.1394 +            ;
  1.1395 +         else
  1.1396 +            error(mpl, "syntax error in argument for %s", func);
  1.1397 +      }
  1.1398 +      /* make pseudo-code to call the built-in function */
  1.1399 +      if (op == O_SUBSTR || op == O_SUBSTR3 || op == O_TIME2STR)
  1.1400 +         code = make_code(mpl, op, &arg, A_SYMBOLIC, 0);
  1.1401 +      else
  1.1402 +         code = make_code(mpl, op, &arg, A_NUMERIC, 0);
  1.1403 +      /* the reference ends with the right parenthesis */
  1.1404 +      xassert(mpl->token == T_RIGHT);
  1.1405 +      get_token(mpl /* ) */);
  1.1406 +      return code;
  1.1407 +}
  1.1408 +
  1.1409 +/*----------------------------------------------------------------------
  1.1410 +-- create_domain - create empty domain.
  1.1411 +--
  1.1412 +-- This routine creates empty domain, which is initially empty, i.e.
  1.1413 +-- has no domain blocks. */
  1.1414 +
  1.1415 +DOMAIN *create_domain(MPL *mpl)
  1.1416 +{     DOMAIN *domain;
  1.1417 +      domain = alloc(DOMAIN);
  1.1418 +      domain->list = NULL;
  1.1419 +      domain->code = NULL;
  1.1420 +      return domain;
  1.1421 +}
  1.1422 +
  1.1423 +/*----------------------------------------------------------------------
  1.1424 +-- create_block - create empty domain block.
  1.1425 +--
  1.1426 +-- This routine creates empty domain block, which is initially empty,
  1.1427 +-- i.e. has no domain slots. */
  1.1428 +
  1.1429 +DOMAIN_BLOCK *create_block(MPL *mpl)
  1.1430 +{     DOMAIN_BLOCK *block;
  1.1431 +      block = alloc(DOMAIN_BLOCK);
  1.1432 +      block->list = NULL;
  1.1433 +      block->code = NULL;
  1.1434 +      block->backup = NULL;
  1.1435 +      block->next = NULL;
  1.1436 +      return block;
  1.1437 +}
  1.1438 +
  1.1439 +/*----------------------------------------------------------------------
  1.1440 +-- append_block - append domain block to specified domain.
  1.1441 +--
  1.1442 +-- This routine adds given domain block to the end of the block list of
  1.1443 +-- specified domain. */
  1.1444 +
  1.1445 +void append_block(MPL *mpl, DOMAIN *domain, DOMAIN_BLOCK *block)
  1.1446 +{     DOMAIN_BLOCK *temp;
  1.1447 +      xassert(mpl == mpl);
  1.1448 +      xassert(domain != NULL);
  1.1449 +      xassert(block != NULL);
  1.1450 +      xassert(block->next == NULL);
  1.1451 +      if (domain->list == NULL)
  1.1452 +         domain->list = block;
  1.1453 +      else
  1.1454 +      {  for (temp = domain->list; temp->next != NULL; temp =
  1.1455 +            temp->next);
  1.1456 +         temp->next = block;
  1.1457 +      }
  1.1458 +      return;
  1.1459 +}
  1.1460 +
  1.1461 +/*----------------------------------------------------------------------
  1.1462 +-- append_slot - create and append new slot to domain block.
  1.1463 +--
  1.1464 +-- This routine creates new domain slot and adds it to the end of slot
  1.1465 +-- list of specified domain block.
  1.1466 +--
  1.1467 +-- The parameter name is symbolic name of the dummy index associated
  1.1468 +-- with the slot (the character string must be allocated). NULL means
  1.1469 +-- the dummy index is not explicitly specified.
  1.1470 +--
  1.1471 +-- The parameter code is pseudo-code for computing symbolic value, at
  1.1472 +-- which the dummy index is bounded. NULL means the dummy index is free
  1.1473 +-- in the domain scope. */
  1.1474 +
  1.1475 +DOMAIN_SLOT *append_slot(MPL *mpl, DOMAIN_BLOCK *block, char *name,
  1.1476 +      CODE *code)
  1.1477 +{     DOMAIN_SLOT *slot, *temp;
  1.1478 +      xassert(block != NULL);
  1.1479 +      slot = alloc(DOMAIN_SLOT);
  1.1480 +      slot->name = name;
  1.1481 +      slot->code = code;
  1.1482 +      slot->value = NULL;
  1.1483 +      slot->list = NULL;
  1.1484 +      slot->next = NULL;
  1.1485 +      if (block->list == NULL)
  1.1486 +         block->list = slot;
  1.1487 +      else
  1.1488 +      {  for (temp = block->list; temp->next != NULL; temp =
  1.1489 +            temp->next);
  1.1490 +         temp->next = slot;
  1.1491 +      }
  1.1492 +      return slot;
  1.1493 +}
  1.1494 +
  1.1495 +/*----------------------------------------------------------------------
  1.1496 +-- expression_list - parse expression list.
  1.1497 +--
  1.1498 +-- This routine parses a list of one or more expressions enclosed into
  1.1499 +-- the parentheses using the syntax:
  1.1500 +--
  1.1501 +-- <primary expression> ::= ( <expression list> )
  1.1502 +-- <expression list> ::= <expression 13>
  1.1503 +-- <expression list> ::= <expression 13> , <expression list>
  1.1504 +--
  1.1505 +-- Note that this construction may have three different meanings:
  1.1506 +--
  1.1507 +-- 1. If <expression list> consists of only one expression, <primary
  1.1508 +--    expression> is a parenthesized expression, which may be of any
  1.1509 +--    valid type (not necessarily 1-tuple).
  1.1510 +--
  1.1511 +-- 2. If <expression list> consists of several expressions separated by
  1.1512 +--    commae, where no expression is undeclared symbolic name, <primary
  1.1513 +--    expression> is a n-tuple.
  1.1514 +--
  1.1515 +-- 3. If <expression list> consists of several expressions separated by
  1.1516 +--    commae, where at least one expression is undeclared symbolic name
  1.1517 +--    (that denotes a dummy index), <primary expression> is a slice and
  1.1518 +--    can be only used as constituent of indexing expression. */
  1.1519 +
  1.1520 +#define max_dim 20
  1.1521 +/* maximal number of components allowed within parentheses */
  1.1522 +
  1.1523 +CODE *expression_list(MPL *mpl)
  1.1524 +{     CODE *code;
  1.1525 +      OPERANDS arg;
  1.1526 +      struct { char *name; CODE *code; } list[1+max_dim];
  1.1527 +      int flag_x, next_token, dim, j, slice = 0;
  1.1528 +      xassert(mpl->token == T_LEFT);
  1.1529 +      /* the flag, which allows recognizing undeclared symbolic names
  1.1530 +         as dummy indices, will be automatically reset by get_token(),
  1.1531 +         so save it before scanning the next token */
  1.1532 +      flag_x = mpl->flag_x;
  1.1533 +      get_token(mpl /* ( */);
  1.1534 +      /* parse <expression list> */
  1.1535 +      for (dim = 1; ; dim++)
  1.1536 +      {  if (dim > max_dim)
  1.1537 +            error(mpl, "too many components within parentheses");
  1.1538 +         /* current component of <expression list> can be either dummy
  1.1539 +            index or expression */
  1.1540 +         if (mpl->token == T_NAME)
  1.1541 +         {  /* symbolic name is recognized as dummy index only if:
  1.1542 +               the flag, which allows that, is set, and
  1.1543 +               the name is followed by comma or right parenthesis, and
  1.1544 +               the name is undeclared */
  1.1545 +            get_token(mpl /* <symbolic name> */);
  1.1546 +            next_token = mpl->token;
  1.1547 +            unget_token(mpl);
  1.1548 +            if (!(flag_x &&
  1.1549 +                  (next_token == T_COMMA || next_token == T_RIGHT) &&
  1.1550 +                  avl_find_node(mpl->tree, mpl->image) == NULL))
  1.1551 +            {  /* this is not dummy index */
  1.1552 +               goto expr;
  1.1553 +            }
  1.1554 +            /* all dummy indices within the same slice must have unique
  1.1555 +               symbolic names */
  1.1556 +            for (j = 1; j < dim; j++)
  1.1557 +            {  if (list[j].name != NULL && strcmp(list[j].name,
  1.1558 +                  mpl->image) == 0)
  1.1559 +                  error(mpl, "duplicate dummy index %s not allowed",
  1.1560 +                     mpl->image);
  1.1561 +            }
  1.1562 +            /* current component of <expression list> is dummy index */
  1.1563 +            list[dim].name
  1.1564 +               = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1);
  1.1565 +            strcpy(list[dim].name, mpl->image);
  1.1566 +            list[dim].code = NULL;
  1.1567 +            get_token(mpl /* <symbolic name> */);
  1.1568 +            /* <expression list> is a slice, because at least one dummy
  1.1569 +               index has appeared */
  1.1570 +            slice = 1;
  1.1571 +            /* note that the context ( <dummy index> ) is not allowed,
  1.1572 +               i.e. in this case <primary expression> is considered as
  1.1573 +               a parenthesized expression */
  1.1574 +            if (dim == 1 && mpl->token == T_RIGHT)
  1.1575 +               error(mpl, "%s not defined", list[dim].name);
  1.1576 +         }
  1.1577 +         else
  1.1578 +expr:    {  /* current component of <expression list> is expression */
  1.1579 +            code = expression_13(mpl);
  1.1580 +            /* if the current expression is followed by comma or it is
  1.1581 +               not the very first expression, entire <expression list>
  1.1582 +               is n-tuple or slice, in which case the current expression
  1.1583 +               should be converted to symbolic type, if necessary */
  1.1584 +            if (mpl->token == T_COMMA || dim > 1)
  1.1585 +            {  if (code->type == A_NUMERIC)
  1.1586 +                  code = make_unary(mpl, O_CVTSYM, code, A_SYMBOLIC, 0);
  1.1587 +               /* now the expression must be of symbolic type */
  1.1588 +               if (code->type != A_SYMBOLIC)
  1.1589 +                  error(mpl, "component expression has invalid type");
  1.1590 +               xassert(code->dim == 0);
  1.1591 +            }
  1.1592 +            list[dim].name = NULL;
  1.1593 +            list[dim].code = code;
  1.1594 +         }
  1.1595 +         /* check a token that follows the current component */
  1.1596 +         if (mpl->token == T_COMMA)
  1.1597 +            get_token(mpl /* , */);
  1.1598 +         else if (mpl->token == T_RIGHT)
  1.1599 +            break;
  1.1600 +         else
  1.1601 +            error(mpl, "right parenthesis missing where expected");
  1.1602 +      }
  1.1603 +      /* generate pseudo-code for <primary expression> */
  1.1604 +      if (dim == 1 && !slice)
  1.1605 +      {  /* <primary expression> is a parenthesized expression */
  1.1606 +         code = list[1].code;
  1.1607 +      }
  1.1608 +      else if (!slice)
  1.1609 +      {  /* <primary expression> is a n-tuple */
  1.1610 +         arg.list = create_arg_list(mpl);
  1.1611 +         for (j = 1; j <= dim; j++)
  1.1612 +            arg.list = expand_arg_list(mpl, arg.list, list[j].code);
  1.1613 +         code = make_code(mpl, O_TUPLE, &arg, A_TUPLE, dim);
  1.1614 +      }
  1.1615 +      else
  1.1616 +      {  /* <primary expression> is a slice */
  1.1617 +         arg.slice = create_block(mpl);
  1.1618 +         for (j = 1; j <= dim; j++)
  1.1619 +            append_slot(mpl, arg.slice, list[j].name, list[j].code);
  1.1620 +         /* note that actually pseudo-codes with op = O_SLICE are never
  1.1621 +            evaluated */
  1.1622 +         code = make_code(mpl, O_SLICE, &arg, A_TUPLE, dim);
  1.1623 +      }
  1.1624 +      get_token(mpl /* ) */);
  1.1625 +      /* if <primary expression> is a slice, there must be the keyword
  1.1626 +         'in', which follows the right parenthesis */
  1.1627 +      if (slice && mpl->token != T_IN)
  1.1628 +         error(mpl, "keyword in missing where expected");
  1.1629 +      /* if the slice flag is set and there is the keyword 'in', which
  1.1630 +         follows <primary expression>, the latter must be a slice */
  1.1631 +      if (flag_x && mpl->token == T_IN && !slice)
  1.1632 +      {  if (dim == 1)
  1.1633 +            error(mpl, "syntax error in indexing expression");
  1.1634 +         else
  1.1635 +            error(mpl, "0-ary slice not allowed");
  1.1636 +      }
  1.1637 +      return code;
  1.1638 +}
  1.1639 +
  1.1640 +/*----------------------------------------------------------------------
  1.1641 +-- literal set - parse literal set.
  1.1642 +--
  1.1643 +-- This routine parses literal set using the syntax:
  1.1644 +--
  1.1645 +-- <literal set> ::= { <member list> }
  1.1646 +-- <member list> ::= <member expression>
  1.1647 +-- <member list> ::= <member list> , <member expression>
  1.1648 +-- <member expression> ::= <expression 5>
  1.1649 +--
  1.1650 +-- It is assumed that the left curly brace and the very first member
  1.1651 +-- expression that follows it are already parsed. The right curly brace
  1.1652 +-- remains unscanned on exit. */
  1.1653 +
  1.1654 +CODE *literal_set(MPL *mpl, CODE *code)
  1.1655 +{     OPERANDS arg;
  1.1656 +      int j;
  1.1657 +      xassert(code != NULL);
  1.1658 +      arg.list = create_arg_list(mpl);
  1.1659 +      /* parse <member list> */
  1.1660 +      for (j = 1; ; j++)
  1.1661 +      {  /* all member expressions must be n-tuples; so, if the current
  1.1662 +            expression is not n-tuple, convert it to 1-tuple */
  1.1663 +         if (code->type == A_NUMERIC)
  1.1664 +            code = make_unary(mpl, O_CVTSYM, code, A_SYMBOLIC, 0);
  1.1665 +         if (code->type == A_SYMBOLIC)
  1.1666 +            code = make_unary(mpl, O_CVTTUP, code, A_TUPLE, 1);
  1.1667 +         /* now the expression must be n-tuple */
  1.1668 +         if (code->type != A_TUPLE)
  1.1669 +            error(mpl, "member expression has invalid type");
  1.1670 +         /* all member expressions must have identical dimension */
  1.1671 +         if (arg.list != NULL && arg.list->x->dim != code->dim)
  1.1672 +            error(mpl, "member %d has %d component%s while member %d ha"
  1.1673 +               "s %d component%s",
  1.1674 +               j-1, arg.list->x->dim, arg.list->x->dim == 1 ? "" : "s",
  1.1675 +               j, code->dim, code->dim == 1 ? "" : "s");
  1.1676 +         /* append the current expression to the member list */
  1.1677 +         arg.list = expand_arg_list(mpl, arg.list, code);
  1.1678 +         /* check a token that follows the current expression */
  1.1679 +         if (mpl->token == T_COMMA)
  1.1680 +            get_token(mpl /* , */);
  1.1681 +         else if (mpl->token == T_RBRACE)
  1.1682 +            break;
  1.1683 +         else
  1.1684 +            error(mpl, "syntax error in literal set");
  1.1685 +         /* parse the next expression that follows the comma */
  1.1686 +         code = expression_5(mpl);
  1.1687 +      }
  1.1688 +      /* generate pseudo-code for <literal set> */
  1.1689 +      code = make_code(mpl, O_MAKE, &arg, A_ELEMSET, arg.list->x->dim);
  1.1690 +      return code;
  1.1691 +}
  1.1692 +
  1.1693 +/*----------------------------------------------------------------------
  1.1694 +-- indexing_expression - parse indexing expression.
  1.1695 +--
  1.1696 +-- This routine parses indexing expression using the syntax:
  1.1697 +--
  1.1698 +-- <indexing expression> ::= <literal set>
  1.1699 +-- <indexing expression> ::= { <indexing list> }
  1.1700 +-- <indexing expression> ::= { <indexing list> : <logical expression> }
  1.1701 +-- <indexing list> ::= <indexing element>
  1.1702 +-- <indexing list> ::= <indexing list> , <indexing element>
  1.1703 +-- <indexing element> ::= <basic expression>
  1.1704 +-- <indexing element> ::= <dummy index> in <basic expression>
  1.1705 +-- <indexing element> ::= <slice> in <basic expression>
  1.1706 +-- <dummy index> ::= <symbolic name>
  1.1707 +-- <slice> ::= ( <expression list> )
  1.1708 +-- <basic expression> ::= <expression 9>
  1.1709 +-- <logical expression> ::= <expression 13>
  1.1710 +--
  1.1711 +-- This routine creates domain for <indexing expression>, where each
  1.1712 +-- domain block corresponds to <indexing element>, and each domain slot
  1.1713 +-- corresponds to individual indexing position. */
  1.1714 +
  1.1715 +DOMAIN *indexing_expression(MPL *mpl)
  1.1716 +{     DOMAIN *domain;
  1.1717 +      DOMAIN_BLOCK *block;
  1.1718 +      DOMAIN_SLOT *slot;
  1.1719 +      CODE *code;
  1.1720 +      xassert(mpl->token == T_LBRACE);
  1.1721 +      get_token(mpl /* { */);
  1.1722 +      if (mpl->token == T_RBRACE)
  1.1723 +         error(mpl, "empty indexing expression not allowed");
  1.1724 +      /* create domain to be constructed */
  1.1725 +      domain = create_domain(mpl);
  1.1726 +      /* parse either <member list> or <indexing list> that follows the
  1.1727 +         left brace */
  1.1728 +      for (;;)
  1.1729 +      {  /* domain block for <indexing element> is not created yet */
  1.1730 +         block = NULL;
  1.1731 +         /* pseudo-code for <basic expression> is not generated yet */
  1.1732 +         code = NULL;
  1.1733 +         /* check a token, which <indexing element> begins with */
  1.1734 +         if (mpl->token == T_NAME)
  1.1735 +         {  /* it is a symbolic name */
  1.1736 +            int next_token;
  1.1737 +            char *name;
  1.1738 +            /* symbolic name is recognized as dummy index only if it is
  1.1739 +               followed by the keyword 'in' and not declared */
  1.1740 +            get_token(mpl /* <symbolic name> */);
  1.1741 +            next_token = mpl->token;
  1.1742 +            unget_token(mpl);
  1.1743 +            if (!(next_token == T_IN &&
  1.1744 +                  avl_find_node(mpl->tree, mpl->image) == NULL))
  1.1745 +            {  /* this is not dummy index; the symbolic name begins an
  1.1746 +                  expression, which is either <basic expression> or the
  1.1747 +                  very first <member expression> in <literal set> */
  1.1748 +               goto expr;
  1.1749 +            }
  1.1750 +            /* create domain block with one slot, which is assigned the
  1.1751 +               dummy index */
  1.1752 +            block = create_block(mpl);
  1.1753 +            name = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1);
  1.1754 +            strcpy(name, mpl->image);
  1.1755 +            append_slot(mpl, block, name, NULL);
  1.1756 +            get_token(mpl /* <symbolic name> */);
  1.1757 +            /* the keyword 'in' is already checked above */
  1.1758 +            xassert(mpl->token == T_IN);
  1.1759 +            get_token(mpl /* in */);
  1.1760 +            /* <basic expression> that follows the keyword 'in' will be
  1.1761 +               parsed below */
  1.1762 +         }
  1.1763 +         else if (mpl->token == T_LEFT)
  1.1764 +         {  /* it is the left parenthesis; parse expression that begins
  1.1765 +               with this parenthesis (the flag is set in order to allow
  1.1766 +               recognizing slices; see the routine expression_list) */
  1.1767 +            mpl->flag_x = 1;
  1.1768 +            code = expression_9(mpl);
  1.1769 +            if (code->op != O_SLICE)
  1.1770 +            {  /* this is either <basic expression> or the very first
  1.1771 +                  <member expression> in <literal set> */
  1.1772 +               goto expr;
  1.1773 +            }
  1.1774 +            /* this is a slice; besides the corresponding domain block
  1.1775 +               is already created by expression_list() */
  1.1776 +            block = code->arg.slice;
  1.1777 +            code = NULL; /* <basic expression> is not parsed yet */
  1.1778 +            /* the keyword 'in' following the slice is already checked
  1.1779 +               by expression_list() */
  1.1780 +            xassert(mpl->token == T_IN);
  1.1781 +            get_token(mpl /* in */);
  1.1782 +            /* <basic expression> that follows the keyword 'in' will be
  1.1783 +               parsed below */
  1.1784 +         }
  1.1785 +expr:    /* parse expression that follows either the keyword 'in' (in
  1.1786 +            which case it can be <basic expression) or the left brace
  1.1787 +            (in which case it can be <basic expression> as well as the
  1.1788 +            very first <member expression> in <literal set>); note that
  1.1789 +            this expression can be already parsed above */
  1.1790 +         if (code == NULL) code = expression_9(mpl);
  1.1791 +         /* check the type of the expression just parsed */
  1.1792 +         if (code->type != A_ELEMSET)
  1.1793 +         {  /* it is not <basic expression> and therefore it can only
  1.1794 +               be the very first <member expression> in <literal set>;
  1.1795 +               however, then there must be no dummy index neither slice
  1.1796 +               between the left brace and this expression */
  1.1797 +            if (block != NULL)
  1.1798 +               error(mpl, "domain expression has invalid type");
  1.1799 +            /* parse the rest part of <literal set> and make this set
  1.1800 +               be <basic expression>, i.e. the construction {a, b, c}
  1.1801 +               is parsed as it were written as {A}, where A = {a, b, c}
  1.1802 +               is a temporary elemental set */
  1.1803 +            code = literal_set(mpl, code);
  1.1804 +         }
  1.1805 +         /* now pseudo-code for <basic set> has been built */
  1.1806 +         xassert(code != NULL);
  1.1807 +         xassert(code->type == A_ELEMSET);
  1.1808 +         xassert(code->dim > 0);
  1.1809 +         /* if domain block for the current <indexing element> is still
  1.1810 +            not created, create it for fake slice of the same dimension
  1.1811 +            as <basic set> */
  1.1812 +         if (block == NULL)
  1.1813 +         {  int j;
  1.1814 +            block = create_block(mpl);
  1.1815 +            for (j = 1; j <= code->dim; j++)
  1.1816 +               append_slot(mpl, block, NULL, NULL);
  1.1817 +         }
  1.1818 +         /* number of indexing positions in <indexing element> must be
  1.1819 +            the same as dimension of n-tuples in basic set */
  1.1820 +         {  int dim = 0;
  1.1821 +            for (slot = block->list; slot != NULL; slot = slot->next)
  1.1822 +               dim++;
  1.1823 +            if (dim != code->dim)
  1.1824 +               error(mpl,"%d %s specified for set of dimension %d",
  1.1825 +                  dim, dim == 1 ? "index" : "indices", code->dim);
  1.1826 +         }
  1.1827 +         /* store pseudo-code for <basic set> in the domain block */
  1.1828 +         xassert(block->code == NULL);
  1.1829 +         block->code = code;
  1.1830 +         /* and append the domain block to the domain */
  1.1831 +         append_block(mpl, domain, block);
  1.1832 +         /* the current <indexing element> has been completely parsed;
  1.1833 +            include all its dummy indices into the symbolic name table
  1.1834 +            to make them available for referencing from expressions;
  1.1835 +            implicit declarations of dummy indices remain valid while
  1.1836 +            the corresponding domain scope is valid */
  1.1837 +         for (slot = block->list; slot != NULL; slot = slot->next)
  1.1838 +         if (slot->name != NULL)
  1.1839 +         {  AVLNODE *node;
  1.1840 +            xassert(avl_find_node(mpl->tree, slot->name) == NULL);
  1.1841 +            node = avl_insert_node(mpl->tree, slot->name);
  1.1842 +            avl_set_node_type(node, A_INDEX);
  1.1843 +            avl_set_node_link(node, (void *)slot);
  1.1844 +         }
  1.1845 +         /* check a token that follows <indexing element> */
  1.1846 +         if (mpl->token == T_COMMA)
  1.1847 +            get_token(mpl /* , */);
  1.1848 +         else if (mpl->token == T_COLON || mpl->token == T_RBRACE)
  1.1849 +            break;
  1.1850 +         else
  1.1851 +            error(mpl, "syntax error in indexing expression");
  1.1852 +      }
  1.1853 +      /* parse <logical expression> that follows the colon */
  1.1854 +      if (mpl->token == T_COLON)
  1.1855 +      {  get_token(mpl /* : */);
  1.1856 +         code = expression_13(mpl);
  1.1857 +         /* convert the expression to logical type, if necessary */
  1.1858 +         if (code->type == A_SYMBOLIC)
  1.1859 +            code = make_unary(mpl, O_CVTNUM, code, A_NUMERIC, 0);
  1.1860 +         if (code->type == A_NUMERIC)
  1.1861 +            code = make_unary(mpl, O_CVTLOG, code, A_LOGICAL, 0);
  1.1862 +         /* now the expression must be of logical type */
  1.1863 +         if (code->type != A_LOGICAL)
  1.1864 +            error(mpl, "expression following colon has invalid type");
  1.1865 +         xassert(code->dim == 0);
  1.1866 +         domain->code = code;
  1.1867 +         /* the right brace must follow the logical expression */
  1.1868 +         if (mpl->token != T_RBRACE)
  1.1869 +            error(mpl, "syntax error in indexing expression");
  1.1870 +      }
  1.1871 +      get_token(mpl /* } */);
  1.1872 +      return domain;
  1.1873 +}
  1.1874 +
  1.1875 +/*----------------------------------------------------------------------
  1.1876 +-- close_scope - close scope of indexing expression.
  1.1877 +--
  1.1878 +-- The routine closes the scope of indexing expression specified by its
  1.1879 +-- domain and thereby makes all dummy indices introduced in the indexing
  1.1880 +-- expression no longer available for referencing. */
  1.1881 +
  1.1882 +void close_scope(MPL *mpl, DOMAIN *domain)
  1.1883 +{     DOMAIN_BLOCK *block;
  1.1884 +      DOMAIN_SLOT *slot;
  1.1885 +      AVLNODE *node;
  1.1886 +      xassert(domain != NULL);
  1.1887 +      /* remove all dummy indices from the symbolic names table */
  1.1888 +      for (block = domain->list; block != NULL; block = block->next)
  1.1889 +      {  for (slot = block->list; slot != NULL; slot = slot->next)
  1.1890 +         {  if (slot->name != NULL)
  1.1891 +            {  node = avl_find_node(mpl->tree, slot->name);
  1.1892 +               xassert(node != NULL);
  1.1893 +               xassert(avl_get_node_type(node) == A_INDEX);
  1.1894 +               avl_delete_node(mpl->tree, node);
  1.1895 +            }
  1.1896 +         }
  1.1897 +      }
  1.1898 +      return;
  1.1899 +}
  1.1900 +
  1.1901 +/*----------------------------------------------------------------------
  1.1902 +-- iterated_expression - parse iterated expression.
  1.1903 +--
  1.1904 +-- This routine parses primary expression using the syntax:
  1.1905 +--
  1.1906 +-- <primary expression> ::= <iterated expression>
  1.1907 +-- <iterated expression> ::= sum <indexing expression> <expression 3>
  1.1908 +-- <iterated expression> ::= prod <indexing expression> <expression 3>
  1.1909 +-- <iterated expression> ::= min <indexing expression> <expression 3>
  1.1910 +-- <iterated expression> ::= max <indexing expression> <expression 3>
  1.1911 +-- <iterated expression> ::= exists <indexing expression>
  1.1912 +--                           <expression 12>
  1.1913 +-- <iterated expression> ::= forall <indexing expression>
  1.1914 +--                           <expression 12>
  1.1915 +-- <iterated expression> ::= setof <indexing expression> <expression 5>
  1.1916 +--
  1.1917 +-- Note that parsing "integrand" depends on the iterated operator. */
  1.1918 +
  1.1919 +#if 1 /* 07/IX-2008 */
  1.1920 +static void link_up(CODE *code)
  1.1921 +{     /* if we have something like sum{(i+1,j,k-1) in E} x[i,j,k],
  1.1922 +         where i and k are dummy indices defined out of the iterated
  1.1923 +         expression, we should link up pseudo-code for computing i+1
  1.1924 +         and k-1 to pseudo-code for computing the iterated expression;
  1.1925 +         this is needed to invalidate current value of the iterated
  1.1926 +         expression once i or k have been changed */
  1.1927 +      DOMAIN_BLOCK *block;
  1.1928 +      DOMAIN_SLOT *slot;
  1.1929 +      for (block = code->arg.loop.domain->list; block != NULL;
  1.1930 +         block = block->next)
  1.1931 +      {  for (slot = block->list; slot != NULL; slot = slot->next)
  1.1932 +         {  if (slot->code != NULL)
  1.1933 +            {  xassert(slot->code->up == NULL);
  1.1934 +               slot->code->up = code;
  1.1935 +            }
  1.1936 +         }
  1.1937 +      }
  1.1938 +      return;
  1.1939 +}
  1.1940 +#endif
  1.1941 +
  1.1942 +CODE *iterated_expression(MPL *mpl)
  1.1943 +{     CODE *code;
  1.1944 +      OPERANDS arg;
  1.1945 +      int op;
  1.1946 +      char opstr[8];
  1.1947 +      /* determine operation code */
  1.1948 +      xassert(mpl->token == T_NAME);
  1.1949 +      if (strcmp(mpl->image, "sum") == 0)
  1.1950 +         op = O_SUM;
  1.1951 +      else if (strcmp(mpl->image, "prod") == 0)
  1.1952 +         op = O_PROD;
  1.1953 +      else if (strcmp(mpl->image, "min") == 0)
  1.1954 +         op = O_MINIMUM;
  1.1955 +      else if (strcmp(mpl->image, "max") == 0)
  1.1956 +         op = O_MAXIMUM;
  1.1957 +      else if (strcmp(mpl->image, "forall") == 0)
  1.1958 +         op = O_FORALL;
  1.1959 +      else if (strcmp(mpl->image, "exists") == 0)
  1.1960 +         op = O_EXISTS;
  1.1961 +      else if (strcmp(mpl->image, "setof") == 0)
  1.1962 +         op = O_SETOF;
  1.1963 +      else
  1.1964 +         error(mpl, "operator %s unknown", mpl->image);
  1.1965 +      strcpy(opstr, mpl->image);
  1.1966 +      xassert(strlen(opstr) < sizeof(opstr));
  1.1967 +      get_token(mpl /* <symbolic name> */);
  1.1968 +      /* check the left brace that follows the operator name */
  1.1969 +      xassert(mpl->token == T_LBRACE);
  1.1970 +      /* parse indexing expression that controls iterating */
  1.1971 +      arg.loop.domain = indexing_expression(mpl);
  1.1972 +      /* parse "integrand" expression and generate pseudo-code */
  1.1973 +      switch (op)
  1.1974 +      {  case O_SUM:
  1.1975 +         case O_PROD:
  1.1976 +         case O_MINIMUM:
  1.1977 +         case O_MAXIMUM:
  1.1978 +            arg.loop.x = expression_3(mpl);
  1.1979 +            /* convert the integrand to numeric type, if necessary */
  1.1980 +            if (arg.loop.x->type == A_SYMBOLIC)
  1.1981 +               arg.loop.x = make_unary(mpl, O_CVTNUM, arg.loop.x,
  1.1982 +                  A_NUMERIC, 0);
  1.1983 +            /* now the integrand must be of numeric type or linear form
  1.1984 +               (the latter is only allowed for the sum operator) */
  1.1985 +            if (!(arg.loop.x->type == A_NUMERIC ||
  1.1986 +                  op == O_SUM && arg.loop.x->type == A_FORMULA))
  1.1987 +err:           error(mpl, "integrand following %s{...} has invalid type"
  1.1988 +                  , opstr);
  1.1989 +            xassert(arg.loop.x->dim == 0);
  1.1990 +            /* generate pseudo-code */
  1.1991 +            code = make_code(mpl, op, &arg, arg.loop.x->type, 0);
  1.1992 +            break;
  1.1993 +         case O_FORALL:
  1.1994 +         case O_EXISTS:
  1.1995 +            arg.loop.x = expression_12(mpl);
  1.1996 +            /* convert the integrand to logical type, if necessary */
  1.1997 +            if (arg.loop.x->type == A_SYMBOLIC)
  1.1998 +               arg.loop.x = make_unary(mpl, O_CVTNUM, arg.loop.x,
  1.1999 +                  A_NUMERIC, 0);
  1.2000 +            if (arg.loop.x->type == A_NUMERIC)
  1.2001 +               arg.loop.x = make_unary(mpl, O_CVTLOG, arg.loop.x,
  1.2002 +                  A_LOGICAL, 0);
  1.2003 +            /* now the integrand must be of logical type */
  1.2004 +            if (arg.loop.x->type != A_LOGICAL) goto err;
  1.2005 +            xassert(arg.loop.x->dim == 0);
  1.2006 +            /* generate pseudo-code */
  1.2007 +            code = make_code(mpl, op, &arg, A_LOGICAL, 0);
  1.2008 +            break;
  1.2009 +         case O_SETOF:
  1.2010 +            arg.loop.x = expression_5(mpl);
  1.2011 +            /* convert the integrand to 1-tuple, if necessary */
  1.2012 +            if (arg.loop.x->type == A_NUMERIC)
  1.2013 +               arg.loop.x = make_unary(mpl, O_CVTSYM, arg.loop.x,
  1.2014 +                  A_SYMBOLIC, 0);
  1.2015 +            if (arg.loop.x->type == A_SYMBOLIC)
  1.2016 +               arg.loop.x = make_unary(mpl, O_CVTTUP, arg.loop.x,
  1.2017 +                  A_TUPLE, 1);
  1.2018 +            /* now the integrand must be n-tuple */
  1.2019 +            if (arg.loop.x->type != A_TUPLE) goto err;
  1.2020 +            xassert(arg.loop.x->dim > 0);
  1.2021 +            /* generate pseudo-code */
  1.2022 +            code = make_code(mpl, op, &arg, A_ELEMSET, arg.loop.x->dim);
  1.2023 +            break;
  1.2024 +         default:
  1.2025 +            xassert(op != op);
  1.2026 +      }
  1.2027 +      /* close the scope of the indexing expression */
  1.2028 +      close_scope(mpl, arg.loop.domain);
  1.2029 +#if 1 /* 07/IX-2008 */
  1.2030 +      link_up(code);
  1.2031 +#endif
  1.2032 +      return code;
  1.2033 +}
  1.2034 +
  1.2035 +/*----------------------------------------------------------------------
  1.2036 +-- domain_arity - determine arity of domain.
  1.2037 +--
  1.2038 +-- This routine returns arity of specified domain, which is number of
  1.2039 +-- its free dummy indices. */
  1.2040 +
  1.2041 +int domain_arity(MPL *mpl, DOMAIN *domain)
  1.2042 +{     DOMAIN_BLOCK *block;
  1.2043 +      DOMAIN_SLOT *slot;
  1.2044 +      int arity;
  1.2045 +      xassert(mpl == mpl);
  1.2046 +      arity = 0;
  1.2047 +      for (block = domain->list; block != NULL; block = block->next)
  1.2048 +         for (slot = block->list; slot != NULL; slot = slot->next)
  1.2049 +            if (slot->code == NULL) arity++;
  1.2050 +      return arity;
  1.2051 +}
  1.2052 +
  1.2053 +/*----------------------------------------------------------------------
  1.2054 +-- set_expression - parse set expression.
  1.2055 +--
  1.2056 +-- This routine parses primary expression using the syntax:
  1.2057 +--
  1.2058 +-- <primary expression> ::= { }
  1.2059 +-- <primary expression> ::= <indexing expression> */
  1.2060 +
  1.2061 +CODE *set_expression(MPL *mpl)
  1.2062 +{     CODE *code;
  1.2063 +      OPERANDS arg;
  1.2064 +      xassert(mpl->token == T_LBRACE);
  1.2065 +      get_token(mpl /* { */);
  1.2066 +      /* check a token that follows the left brace */
  1.2067 +      if (mpl->token == T_RBRACE)
  1.2068 +      {  /* it is the right brace, so the resultant is an empty set of
  1.2069 +            dimension 1 */
  1.2070 +         arg.list = NULL;
  1.2071 +         /* generate pseudo-code to build the resultant set */
  1.2072 +         code = make_code(mpl, O_MAKE, &arg, A_ELEMSET, 1);
  1.2073 +         get_token(mpl /* } */);
  1.2074 +      }
  1.2075 +      else
  1.2076 +      {  /* the next token begins an indexing expression */
  1.2077 +         unget_token(mpl);
  1.2078 +         arg.loop.domain = indexing_expression(mpl);
  1.2079 +         arg.loop.x = NULL; /* integrand is not used */
  1.2080 +         /* close the scope of the indexing expression */
  1.2081 +         close_scope(mpl, arg.loop.domain);
  1.2082 +         /* generate pseudo-code to build the resultant set */
  1.2083 +         code = make_code(mpl, O_BUILD, &arg, A_ELEMSET,
  1.2084 +            domain_arity(mpl, arg.loop.domain));
  1.2085 +#if 1 /* 07/IX-2008 */
  1.2086 +         link_up(code);
  1.2087 +#endif
  1.2088 +      }
  1.2089 +      return code;
  1.2090 +}
  1.2091 +
  1.2092 +/*----------------------------------------------------------------------
  1.2093 +-- branched_expression - parse conditional expression.
  1.2094 +--
  1.2095 +-- This routine parses primary expression using the syntax:
  1.2096 +--
  1.2097 +-- <primary expression> ::= <branched expression>
  1.2098 +-- <branched expression> ::= if <logical expression> then <expression 9>
  1.2099 +-- <branched expression> ::= if <logical expression> then <expression 9>
  1.2100 +--                           else <expression 9>
  1.2101 +-- <logical expression> ::= <expression 13> */
  1.2102 +
  1.2103 +CODE *branched_expression(MPL *mpl)
  1.2104 +{     CODE *code, *x, *y, *z;
  1.2105 +      xassert(mpl->token == T_IF);
  1.2106 +      get_token(mpl /* if */);
  1.2107 +      /* parse <logical expression> that follows 'if' */
  1.2108 +      x = expression_13(mpl);
  1.2109 +      /* convert the expression to logical type, if necessary */
  1.2110 +      if (x->type == A_SYMBOLIC)
  1.2111 +         x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0);
  1.2112 +      if (x->type == A_NUMERIC)
  1.2113 +         x = make_unary(mpl, O_CVTLOG, x, A_LOGICAL, 0);
  1.2114 +      /* now the expression must be of logical type */
  1.2115 +      if (x->type != A_LOGICAL)
  1.2116 +         error(mpl, "expression following if has invalid type");
  1.2117 +      xassert(x->dim == 0);
  1.2118 +      /* the keyword 'then' must follow the logical expression */
  1.2119 +      if (mpl->token != T_THEN)
  1.2120 +         error(mpl, "keyword then missing where expected");
  1.2121 +      get_token(mpl /* then */);
  1.2122 +      /* parse <expression> that follows 'then' and check its type */
  1.2123 +      y = expression_9(mpl);
  1.2124 +      if (!(y->type == A_NUMERIC || y->type == A_SYMBOLIC ||
  1.2125 +            y->type == A_ELEMSET || y->type == A_FORMULA))
  1.2126 +         error(mpl, "expression following then has invalid type");
  1.2127 +      /* if the expression that follows the keyword 'then' is elemental
  1.2128 +         set, the keyword 'else' cannot be omitted; otherwise else-part
  1.2129 +         is optional */
  1.2130 +      if (mpl->token != T_ELSE)
  1.2131 +      {  if (y->type == A_ELEMSET)
  1.2132 +            error(mpl, "keyword else missing where expected");
  1.2133 +         z = NULL;
  1.2134 +         goto skip;
  1.2135 +      }
  1.2136 +      get_token(mpl /* else */);
  1.2137 +      /* parse <expression> that follow 'else' and check its type */
  1.2138 +      z = expression_9(mpl);
  1.2139 +      if (!(z->type == A_NUMERIC || z->type == A_SYMBOLIC ||
  1.2140 +            z->type == A_ELEMSET || z->type == A_FORMULA))
  1.2141 +         error(mpl, "expression following else has invalid type");
  1.2142 +      /* convert to identical types, if necessary */
  1.2143 +      if (y->type == A_FORMULA || z->type == A_FORMULA)
  1.2144 +      {  if (y->type == A_SYMBOLIC)
  1.2145 +            y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0);
  1.2146 +         if (y->type == A_NUMERIC)
  1.2147 +            y = make_unary(mpl, O_CVTLFM, y, A_FORMULA, 0);
  1.2148 +         if (z->type == A_SYMBOLIC)
  1.2149 +            z = make_unary(mpl, O_CVTNUM, z, A_NUMERIC, 0);
  1.2150 +         if (z->type == A_NUMERIC)
  1.2151 +            z = make_unary(mpl, O_CVTLFM, z, A_FORMULA, 0);
  1.2152 +      }
  1.2153 +      if (y->type == A_SYMBOLIC || z->type == A_SYMBOLIC)
  1.2154 +      {  if (y->type == A_NUMERIC)
  1.2155 +            y = make_unary(mpl, O_CVTSYM, y, A_SYMBOLIC, 0);
  1.2156 +         if (z->type == A_NUMERIC)
  1.2157 +            z = make_unary(mpl, O_CVTSYM, z, A_SYMBOLIC, 0);
  1.2158 +      }
  1.2159 +      /* now both expressions must have identical types */
  1.2160 +      if (y->type != z->type)
  1.2161 +         error(mpl, "expressions following then and else have incompati"
  1.2162 +            "ble types");
  1.2163 +      /* and identical dimensions */
  1.2164 +      if (y->dim != z->dim)
  1.2165 +         error(mpl, "expressions following then and else have different"
  1.2166 +            " dimensions %d and %d, respectively", y->dim, z->dim);
  1.2167 +skip: /* generate pseudo-code to perform branching */
  1.2168 +      code = make_ternary(mpl, O_FORK, x, y, z, y->type, y->dim);
  1.2169 +      return code;
  1.2170 +}
  1.2171 +
  1.2172 +/*----------------------------------------------------------------------
  1.2173 +-- primary_expression - parse primary expression.
  1.2174 +--
  1.2175 +-- This routine parses primary expression using the syntax:
  1.2176 +--
  1.2177 +-- <primary expression> ::= <numeric literal>
  1.2178 +-- <primary expression> ::= Infinity
  1.2179 +-- <primary expression> ::= <string literal>
  1.2180 +-- <primary expression> ::= <dummy index>
  1.2181 +-- <primary expression> ::= <set name>
  1.2182 +-- <primary expression> ::= <set name> [ <subscript list> ]
  1.2183 +-- <primary expression> ::= <parameter name>
  1.2184 +-- <primary expression> ::= <parameter name> [ <subscript list> ]
  1.2185 +-- <primary expression> ::= <variable name>
  1.2186 +-- <primary expression> ::= <variable name> [ <subscript list> ]
  1.2187 +-- <primary expression> ::= <built-in function> ( <argument list> )
  1.2188 +-- <primary expression> ::= ( <expression list> )
  1.2189 +-- <primary expression> ::= <iterated expression>
  1.2190 +-- <primary expression> ::= { }
  1.2191 +-- <primary expression> ::= <indexing expression>
  1.2192 +-- <primary expression> ::= <branched expression>
  1.2193 +--
  1.2194 +-- For complete list of syntactic rules for <primary expression> see
  1.2195 +-- comments to the corresponding parsing routines. */
  1.2196 +
  1.2197 +CODE *primary_expression(MPL *mpl)
  1.2198 +{     CODE *code;
  1.2199 +      if (mpl->token == T_NUMBER)
  1.2200 +      {  /* parse numeric literal */
  1.2201 +         code = numeric_literal(mpl);
  1.2202 +      }
  1.2203 +#if 1 /* 21/VII-2006 */
  1.2204 +      else if (mpl->token == T_INFINITY)
  1.2205 +      {  /* parse "infinity" */
  1.2206 +         OPERANDS arg;
  1.2207 +         arg.num = DBL_MAX;
  1.2208 +         code = make_code(mpl, O_NUMBER, &arg, A_NUMERIC, 0);
  1.2209 +         get_token(mpl /* Infinity */);
  1.2210 +      }
  1.2211 +#endif
  1.2212 +      else if (mpl->token == T_STRING)
  1.2213 +      {  /* parse string literal */
  1.2214 +         code = string_literal(mpl);
  1.2215 +      }
  1.2216 +      else if (mpl->token == T_NAME)
  1.2217 +      {  int next_token;
  1.2218 +         get_token(mpl /* <symbolic name> */);
  1.2219 +         next_token = mpl->token;
  1.2220 +         unget_token(mpl);
  1.2221 +         /* check a token that follows <symbolic name> */
  1.2222 +         switch (next_token)
  1.2223 +         {  case T_LBRACKET:
  1.2224 +               /* parse reference to subscripted object */
  1.2225 +               code = object_reference(mpl);
  1.2226 +               break;
  1.2227 +            case T_LEFT:
  1.2228 +               /* parse reference to built-in function */
  1.2229 +               code = function_reference(mpl);
  1.2230 +               break;
  1.2231 +            case T_LBRACE:
  1.2232 +               /* parse iterated expression */
  1.2233 +               code = iterated_expression(mpl);
  1.2234 +               break;
  1.2235 +            default:
  1.2236 +               /* parse reference to unsubscripted object */
  1.2237 +               code = object_reference(mpl);
  1.2238 +               break;
  1.2239 +         }
  1.2240 +      }
  1.2241 +      else if (mpl->token == T_LEFT)
  1.2242 +      {  /* parse parenthesized expression */
  1.2243 +         code = expression_list(mpl);
  1.2244 +      }
  1.2245 +      else if (mpl->token == T_LBRACE)
  1.2246 +      {  /* parse set expression */
  1.2247 +         code = set_expression(mpl);
  1.2248 +      }
  1.2249 +      else if (mpl->token == T_IF)
  1.2250 +      {  /* parse conditional expression */
  1.2251 +         code = branched_expression(mpl);
  1.2252 +      }
  1.2253 +      else if (is_reserved(mpl))
  1.2254 +      {  /* other reserved keywords cannot be used here */
  1.2255 +         error(mpl, "invalid use of reserved keyword %s", mpl->image);
  1.2256 +      }
  1.2257 +      else
  1.2258 +         error(mpl, "syntax error in expression");
  1.2259 +      return code;
  1.2260 +}
  1.2261 +
  1.2262 +/*----------------------------------------------------------------------
  1.2263 +-- error_preceding - raise error if preceding operand has wrong type.
  1.2264 +--
  1.2265 +-- This routine is called to raise error if operand that precedes some
  1.2266 +-- infix operator has invalid type. */
  1.2267 +
  1.2268 +void error_preceding(MPL *mpl, char *opstr)
  1.2269 +{     error(mpl, "operand preceding %s has invalid type", opstr);
  1.2270 +      /* no return */
  1.2271 +}
  1.2272 +
  1.2273 +/*----------------------------------------------------------------------
  1.2274 +-- error_following - raise error if following operand has wrong type.
  1.2275 +--
  1.2276 +-- This routine is called to raise error if operand that follows some
  1.2277 +-- infix operator has invalid type. */
  1.2278 +
  1.2279 +void error_following(MPL *mpl, char *opstr)
  1.2280 +{     error(mpl, "operand following %s has invalid type", opstr);
  1.2281 +      /* no return */
  1.2282 +}
  1.2283 +
  1.2284 +/*----------------------------------------------------------------------
  1.2285 +-- error_dimension - raise error if operands have different dimension.
  1.2286 +--
  1.2287 +-- This routine is called to raise error if two operands of some infix
  1.2288 +-- operator have different dimension. */
  1.2289 +
  1.2290 +void error_dimension(MPL *mpl, char *opstr, int dim1, int dim2)
  1.2291 +{     error(mpl, "operands preceding and following %s have different di"
  1.2292 +         "mensions %d and %d, respectively", opstr, dim1, dim2);
  1.2293 +      /* no return */
  1.2294 +}
  1.2295 +
  1.2296 +/*----------------------------------------------------------------------
  1.2297 +-- expression_0 - parse expression of level 0.
  1.2298 +--
  1.2299 +-- This routine parses expression of level 0 using the syntax:
  1.2300 +--
  1.2301 +-- <expression 0> ::= <primary expression> */
  1.2302 +
  1.2303 +CODE *expression_0(MPL *mpl)
  1.2304 +{     CODE *code;
  1.2305 +      code = primary_expression(mpl);
  1.2306 +      return code;
  1.2307 +}
  1.2308 +
  1.2309 +/*----------------------------------------------------------------------
  1.2310 +-- expression_1 - parse expression of level 1.
  1.2311 +--
  1.2312 +-- This routine parses expression of level 1 using the syntax:
  1.2313 +--
  1.2314 +-- <expression 1> ::= <expression 0>
  1.2315 +-- <expression 1> ::= <expression 0> <power> <expression 1>
  1.2316 +-- <expression 1> ::= <expression 0> <power> <expression 2>
  1.2317 +-- <power> ::= ^ | ** */
  1.2318 +
  1.2319 +CODE *expression_1(MPL *mpl)
  1.2320 +{     CODE *x, *y;
  1.2321 +      char opstr[8];
  1.2322 +      x = expression_0(mpl);
  1.2323 +      if (mpl->token == T_POWER)
  1.2324 +      {  strcpy(opstr, mpl->image);
  1.2325 +         xassert(strlen(opstr) < sizeof(opstr));
  1.2326 +         if (x->type == A_SYMBOLIC)
  1.2327 +            x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0);
  1.2328 +         if (x->type != A_NUMERIC)
  1.2329 +            error_preceding(mpl, opstr);
  1.2330 +         get_token(mpl /* ^ | ** */);
  1.2331 +         if (mpl->token == T_PLUS || mpl->token == T_MINUS)
  1.2332 +            y = expression_2(mpl);
  1.2333 +         else
  1.2334 +            y = expression_1(mpl);
  1.2335 +         if (y->type == A_SYMBOLIC)
  1.2336 +            y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0);
  1.2337 +         if (y->type != A_NUMERIC)
  1.2338 +            error_following(mpl, opstr);
  1.2339 +         x = make_binary(mpl, O_POWER, x, y, A_NUMERIC, 0);
  1.2340 +      }
  1.2341 +      return x;
  1.2342 +}
  1.2343 +
  1.2344 +/*----------------------------------------------------------------------
  1.2345 +-- expression_2 - parse expression of level 2.
  1.2346 +--
  1.2347 +-- This routine parses expression of level 2 using the syntax:
  1.2348 +--
  1.2349 +-- <expression 2> ::= <expression 1>
  1.2350 +-- <expression 2> ::= + <expression 1>
  1.2351 +-- <expression 2> ::= - <expression 1> */
  1.2352 +
  1.2353 +CODE *expression_2(MPL *mpl)
  1.2354 +{     CODE *x;
  1.2355 +      if (mpl->token == T_PLUS)
  1.2356 +      {  get_token(mpl /* + */);
  1.2357 +         x = expression_1(mpl);
  1.2358 +         if (x->type == A_SYMBOLIC)
  1.2359 +            x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0);
  1.2360 +         if (!(x->type == A_NUMERIC || x->type == A_FORMULA))
  1.2361 +            error_following(mpl, "+");
  1.2362 +         x = make_unary(mpl, O_PLUS, x, x->type, 0);
  1.2363 +      }
  1.2364 +      else if (mpl->token == T_MINUS)
  1.2365 +      {  get_token(mpl /* - */);
  1.2366 +         x = expression_1(mpl);
  1.2367 +         if (x->type == A_SYMBOLIC)
  1.2368 +            x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0);
  1.2369 +         if (!(x->type == A_NUMERIC || x->type == A_FORMULA))
  1.2370 +            error_following(mpl, "-");
  1.2371 +         x = make_unary(mpl, O_MINUS, x, x->type, 0);
  1.2372 +      }
  1.2373 +      else
  1.2374 +         x = expression_1(mpl);
  1.2375 +      return x;
  1.2376 +}
  1.2377 +
  1.2378 +/*----------------------------------------------------------------------
  1.2379 +-- expression_3 - parse expression of level 3.
  1.2380 +--
  1.2381 +-- This routine parses expression of level 3 using the syntax:
  1.2382 +--
  1.2383 +-- <expression 3> ::= <expression 2>
  1.2384 +-- <expression 3> ::= <expression 3> * <expression 2>
  1.2385 +-- <expression 3> ::= <expression 3> / <expression 2>
  1.2386 +-- <expression 3> ::= <expression 3> div <expression 2>
  1.2387 +-- <expression 3> ::= <expression 3> mod <expression 2> */
  1.2388 +
  1.2389 +CODE *expression_3(MPL *mpl)
  1.2390 +{     CODE *x, *y;
  1.2391 +      x = expression_2(mpl);
  1.2392 +      for (;;)
  1.2393 +      {  if (mpl->token == T_ASTERISK)
  1.2394 +         {  if (x->type == A_SYMBOLIC)
  1.2395 +               x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0);
  1.2396 +            if (!(x->type == A_NUMERIC || x->type == A_FORMULA))
  1.2397 +               error_preceding(mpl, "*");
  1.2398 +            get_token(mpl /* * */);
  1.2399 +            y = expression_2(mpl);
  1.2400 +            if (y->type == A_SYMBOLIC)
  1.2401 +               y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0);
  1.2402 +            if (!(y->type == A_NUMERIC || y->type == A_FORMULA))
  1.2403 +               error_following(mpl, "*");
  1.2404 +            if (x->type == A_FORMULA && y->type == A_FORMULA)
  1.2405 +               error(mpl, "multiplication of linear forms not allowed");
  1.2406 +            if (x->type == A_NUMERIC && y->type == A_NUMERIC)
  1.2407 +               x = make_binary(mpl, O_MUL, x, y, A_NUMERIC, 0);
  1.2408 +            else
  1.2409 +               x = make_binary(mpl, O_MUL, x, y, A_FORMULA, 0);
  1.2410 +         }
  1.2411 +         else if (mpl->token == T_SLASH)
  1.2412 +         {  if (x->type == A_SYMBOLIC)
  1.2413 +               x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0);
  1.2414 +            if (!(x->type == A_NUMERIC || x->type == A_FORMULA))
  1.2415 +               error_preceding(mpl, "/");
  1.2416 +            get_token(mpl /* / */);
  1.2417 +            y = expression_2(mpl);
  1.2418 +            if (y->type == A_SYMBOLIC)
  1.2419 +               y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0);
  1.2420 +            if (y->type != A_NUMERIC)
  1.2421 +               error_following(mpl, "/");
  1.2422 +            if (x->type == A_NUMERIC)
  1.2423 +               x = make_binary(mpl, O_DIV, x, y, A_NUMERIC, 0);
  1.2424 +            else
  1.2425 +               x = make_binary(mpl, O_DIV, x, y, A_FORMULA, 0);
  1.2426 +         }
  1.2427 +         else if (mpl->token == T_DIV)
  1.2428 +         {  if (x->type == A_SYMBOLIC)
  1.2429 +               x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0);
  1.2430 +            if (x->type != A_NUMERIC)
  1.2431 +               error_preceding(mpl, "div");
  1.2432 +            get_token(mpl /* div */);
  1.2433 +            y = expression_2(mpl);
  1.2434 +            if (y->type == A_SYMBOLIC)
  1.2435 +               y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0);
  1.2436 +            if (y->type != A_NUMERIC)
  1.2437 +               error_following(mpl, "div");
  1.2438 +            x = make_binary(mpl, O_IDIV, x, y, A_NUMERIC, 0);
  1.2439 +         }
  1.2440 +         else if (mpl->token == T_MOD)
  1.2441 +         {  if (x->type == A_SYMBOLIC)
  1.2442 +               x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0);
  1.2443 +            if (x->type != A_NUMERIC)
  1.2444 +               error_preceding(mpl, "mod");
  1.2445 +            get_token(mpl /* mod */);
  1.2446 +            y = expression_2(mpl);
  1.2447 +            if (y->type == A_SYMBOLIC)
  1.2448 +               y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0);
  1.2449 +            if (y->type != A_NUMERIC)
  1.2450 +               error_following(mpl, "mod");
  1.2451 +            x = make_binary(mpl, O_MOD, x, y, A_NUMERIC, 0);
  1.2452 +         }
  1.2453 +         else
  1.2454 +            break;
  1.2455 +      }
  1.2456 +      return x;
  1.2457 +}
  1.2458 +
  1.2459 +/*----------------------------------------------------------------------
  1.2460 +-- expression_4 - parse expression of level 4.
  1.2461 +--
  1.2462 +-- This routine parses expression of level 4 using the syntax:
  1.2463 +--
  1.2464 +-- <expression 4> ::= <expression 3>
  1.2465 +-- <expression 4> ::= <expression 4> + <expression 3>
  1.2466 +-- <expression 4> ::= <expression 4> - <expression 3>
  1.2467 +-- <expression 4> ::= <expression 4> less <expression 3> */
  1.2468 +
  1.2469 +CODE *expression_4(MPL *mpl)
  1.2470 +{     CODE *x, *y;
  1.2471 +      x = expression_3(mpl);
  1.2472 +      for (;;)
  1.2473 +      {  if (mpl->token == T_PLUS)
  1.2474 +         {  if (x->type == A_SYMBOLIC)
  1.2475 +               x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0);
  1.2476 +            if (!(x->type == A_NUMERIC || x->type == A_FORMULA))
  1.2477 +               error_preceding(mpl, "+");
  1.2478 +            get_token(mpl /* + */);
  1.2479 +            y = expression_3(mpl);
  1.2480 +            if (y->type == A_SYMBOLIC)
  1.2481 +               y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0);
  1.2482 +            if (!(y->type == A_NUMERIC || y->type == A_FORMULA))
  1.2483 +               error_following(mpl, "+");
  1.2484 +            if (x->type == A_NUMERIC && y->type == A_FORMULA)
  1.2485 +               x = make_unary(mpl, O_CVTLFM, x, A_FORMULA, 0);
  1.2486 +            if (x->type == A_FORMULA && y->type == A_NUMERIC)
  1.2487 +               y = make_unary(mpl, O_CVTLFM, y, A_FORMULA, 0);
  1.2488 +            x = make_binary(mpl, O_ADD, x, y, x->type, 0);
  1.2489 +         }
  1.2490 +         else if (mpl->token == T_MINUS)
  1.2491 +         {  if (x->type == A_SYMBOLIC)
  1.2492 +               x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0);
  1.2493 +            if (!(x->type == A_NUMERIC || x->type == A_FORMULA))
  1.2494 +               error_preceding(mpl, "-");
  1.2495 +            get_token(mpl /* - */);
  1.2496 +            y = expression_3(mpl);
  1.2497 +            if (y->type == A_SYMBOLIC)
  1.2498 +               y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0);
  1.2499 +            if (!(y->type == A_NUMERIC || y->type == A_FORMULA))
  1.2500 +               error_following(mpl, "-");
  1.2501 +            if (x->type == A_NUMERIC && y->type == A_FORMULA)
  1.2502 +               x = make_unary(mpl, O_CVTLFM, x, A_FORMULA, 0);
  1.2503 +            if (x->type == A_FORMULA && y->type == A_NUMERIC)
  1.2504 +               y = make_unary(mpl, O_CVTLFM, y, A_FORMULA, 0);
  1.2505 +            x = make_binary(mpl, O_SUB, x, y, x->type, 0);
  1.2506 +         }
  1.2507 +         else if (mpl->token == T_LESS)
  1.2508 +         {  if (x->type == A_SYMBOLIC)
  1.2509 +               x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0);
  1.2510 +            if (x->type != A_NUMERIC)
  1.2511 +               error_preceding(mpl, "less");
  1.2512 +            get_token(mpl /* less */);
  1.2513 +            y = expression_3(mpl);
  1.2514 +            if (y->type == A_SYMBOLIC)
  1.2515 +               y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0);
  1.2516 +            if (y->type != A_NUMERIC)
  1.2517 +               error_following(mpl, "less");
  1.2518 +            x = make_binary(mpl, O_LESS, x, y, A_NUMERIC, 0);
  1.2519 +         }
  1.2520 +         else
  1.2521 +            break;
  1.2522 +      }
  1.2523 +      return x;
  1.2524 +}
  1.2525 +
  1.2526 +/*----------------------------------------------------------------------
  1.2527 +-- expression_5 - parse expression of level 5.
  1.2528 +--
  1.2529 +-- This routine parses expression of level 5 using the syntax:
  1.2530 +--
  1.2531 +-- <expression 5> ::= <expression 4>
  1.2532 +-- <expression 5> ::= <expression 5> & <expression 4> */
  1.2533 +
  1.2534 +CODE *expression_5(MPL *mpl)
  1.2535 +{     CODE *x, *y;
  1.2536 +      x = expression_4(mpl);
  1.2537 +      for (;;)
  1.2538 +      {  if (mpl->token == T_CONCAT)
  1.2539 +         {  if (x->type == A_NUMERIC)
  1.2540 +               x = make_unary(mpl, O_CVTSYM, x, A_SYMBOLIC, 0);
  1.2541 +            if (x->type != A_SYMBOLIC)
  1.2542 +               error_preceding(mpl, "&");
  1.2543 +            get_token(mpl /* & */);
  1.2544 +            y = expression_4(mpl);
  1.2545 +            if (y->type == A_NUMERIC)
  1.2546 +               y = make_unary(mpl, O_CVTSYM, y, A_SYMBOLIC, 0);
  1.2547 +            if (y->type != A_SYMBOLIC)
  1.2548 +               error_following(mpl, "&");
  1.2549 +            x = make_binary(mpl, O_CONCAT, x, y, A_SYMBOLIC, 0);
  1.2550 +         }
  1.2551 +         else
  1.2552 +            break;
  1.2553 +      }
  1.2554 +      return x;
  1.2555 +}
  1.2556 +
  1.2557 +/*----------------------------------------------------------------------
  1.2558 +-- expression_6 - parse expression of level 6.
  1.2559 +--
  1.2560 +-- This routine parses expression of level 6 using the syntax:
  1.2561 +--
  1.2562 +-- <expression 6> ::= <expression 5>
  1.2563 +-- <expression 6> ::= <expression 5> .. <expression 5>
  1.2564 +-- <expression 6> ::= <expression 5> .. <expression 5> by
  1.2565 +--                    <expression 5> */
  1.2566 +
  1.2567 +CODE *expression_6(MPL *mpl)
  1.2568 +{     CODE *x, *y, *z;
  1.2569 +      x = expression_5(mpl);
  1.2570 +      if (mpl->token == T_DOTS)
  1.2571 +      {  if (x->type == A_SYMBOLIC)
  1.2572 +            x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0);
  1.2573 +         if (x->type != A_NUMERIC)
  1.2574 +            error_preceding(mpl, "..");
  1.2575 +         get_token(mpl /* .. */);
  1.2576 +         y = expression_5(mpl);
  1.2577 +         if (y->type == A_SYMBOLIC)
  1.2578 +            y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0);
  1.2579 +         if (y->type != A_NUMERIC)
  1.2580 +            error_following(mpl, "..");
  1.2581 +         if (mpl->token == T_BY)
  1.2582 +         {  get_token(mpl /* by */);
  1.2583 +            z = expression_5(mpl);
  1.2584 +            if (z->type == A_SYMBOLIC)
  1.2585 +               z = make_unary(mpl, O_CVTNUM, z, A_NUMERIC, 0);
  1.2586 +            if (z->type != A_NUMERIC)
  1.2587 +               error_following(mpl, "by");
  1.2588 +         }
  1.2589 +         else
  1.2590 +            z = NULL;
  1.2591 +         x = make_ternary(mpl, O_DOTS, x, y, z, A_ELEMSET, 1);
  1.2592 +      }
  1.2593 +      return x;
  1.2594 +}
  1.2595 +
  1.2596 +/*----------------------------------------------------------------------
  1.2597 +-- expression_7 - parse expression of level 7.
  1.2598 +--
  1.2599 +-- This routine parses expression of level 7 using the syntax:
  1.2600 +--
  1.2601 +-- <expression 7> ::= <expression 6>
  1.2602 +-- <expression 7> ::= <expression 7> cross <expression 6> */
  1.2603 +
  1.2604 +CODE *expression_7(MPL *mpl)
  1.2605 +{     CODE *x, *y;
  1.2606 +      x = expression_6(mpl);
  1.2607 +      for (;;)
  1.2608 +      {  if (mpl->token == T_CROSS)
  1.2609 +         {  if (x->type != A_ELEMSET)
  1.2610 +               error_preceding(mpl, "cross");
  1.2611 +            get_token(mpl /* cross */);
  1.2612 +            y = expression_6(mpl);
  1.2613 +            if (y->type != A_ELEMSET)
  1.2614 +               error_following(mpl, "cross");
  1.2615 +            x = make_binary(mpl, O_CROSS, x, y, A_ELEMSET,
  1.2616 +               x->dim + y->dim);
  1.2617 +         }
  1.2618 +         else
  1.2619 +            break;
  1.2620 +      }
  1.2621 +      return x;
  1.2622 +}
  1.2623 +
  1.2624 +/*----------------------------------------------------------------------
  1.2625 +-- expression_8 - parse expression of level 8.
  1.2626 +--
  1.2627 +-- This routine parses expression of level 8 using the syntax:
  1.2628 +--
  1.2629 +-- <expression 8> ::= <expression 7>
  1.2630 +-- <expression 8> ::= <expression 8> inter <expression 7> */
  1.2631 +
  1.2632 +CODE *expression_8(MPL *mpl)
  1.2633 +{     CODE *x, *y;
  1.2634 +      x = expression_7(mpl);
  1.2635 +      for (;;)
  1.2636 +      {  if (mpl->token == T_INTER)
  1.2637 +         {  if (x->type != A_ELEMSET)
  1.2638 +               error_preceding(mpl, "inter");
  1.2639 +            get_token(mpl /* inter */);
  1.2640 +            y = expression_7(mpl);
  1.2641 +            if (y->type != A_ELEMSET)
  1.2642 +               error_following(mpl, "inter");
  1.2643 +            if (x->dim != y->dim)
  1.2644 +               error_dimension(mpl, "inter", x->dim, y->dim);
  1.2645 +            x = make_binary(mpl, O_INTER, x, y, A_ELEMSET, x->dim);
  1.2646 +         }
  1.2647 +         else
  1.2648 +            break;
  1.2649 +      }
  1.2650 +      return x;
  1.2651 +}
  1.2652 +
  1.2653 +/*----------------------------------------------------------------------
  1.2654 +-- expression_9 - parse expression of level 9.
  1.2655 +--
  1.2656 +-- This routine parses expression of level 9 using the syntax:
  1.2657 +--
  1.2658 +-- <expression 9> ::= <expression 8>
  1.2659 +-- <expression 9> ::= <expression 9> union <expression 8>
  1.2660 +-- <expression 9> ::= <expression 9> diff <expression 8>
  1.2661 +-- <expression 9> ::= <expression 9> symdiff <expression 8> */
  1.2662 +
  1.2663 +CODE *expression_9(MPL *mpl)
  1.2664 +{     CODE *x, *y;
  1.2665 +      x = expression_8(mpl);
  1.2666 +      for (;;)
  1.2667 +      {  if (mpl->token == T_UNION)
  1.2668 +         {  if (x->type != A_ELEMSET)
  1.2669 +               error_preceding(mpl, "union");
  1.2670 +            get_token(mpl /* union */);
  1.2671 +            y = expression_8(mpl);
  1.2672 +            if (y->type != A_ELEMSET)
  1.2673 +               error_following(mpl, "union");
  1.2674 +            if (x->dim != y->dim)
  1.2675 +               error_dimension(mpl, "union", x->dim, y->dim);
  1.2676 +            x = make_binary(mpl, O_UNION, x, y, A_ELEMSET, x->dim);
  1.2677 +         }
  1.2678 +         else if (mpl->token == T_DIFF)
  1.2679 +         {  if (x->type != A_ELEMSET)
  1.2680 +               error_preceding(mpl, "diff");
  1.2681 +            get_token(mpl /* diff */);
  1.2682 +            y = expression_8(mpl);
  1.2683 +            if (y->type != A_ELEMSET)
  1.2684 +               error_following(mpl, "diff");
  1.2685 +            if (x->dim != y->dim)
  1.2686 +               error_dimension(mpl, "diff", x->dim, y->dim);
  1.2687 +            x = make_binary(mpl, O_DIFF, x, y, A_ELEMSET, x->dim);
  1.2688 +         }
  1.2689 +         else if (mpl->token == T_SYMDIFF)
  1.2690 +         {  if (x->type != A_ELEMSET)
  1.2691 +               error_preceding(mpl, "symdiff");
  1.2692 +            get_token(mpl /* symdiff */);
  1.2693 +            y = expression_8(mpl);
  1.2694 +            if (y->type != A_ELEMSET)
  1.2695 +               error_following(mpl, "symdiff");
  1.2696 +            if (x->dim != y->dim)
  1.2697 +               error_dimension(mpl, "symdiff", x->dim, y->dim);
  1.2698 +            x = make_binary(mpl, O_SYMDIFF, x, y, A_ELEMSET, x->dim);
  1.2699 +         }
  1.2700 +         else
  1.2701 +            break;
  1.2702 +      }
  1.2703 +      return x;
  1.2704 +}
  1.2705 +
  1.2706 +/*----------------------------------------------------------------------
  1.2707 +-- expression_10 - parse expression of level 10.
  1.2708 +--
  1.2709 +-- This routine parses expression of level 10 using the syntax:
  1.2710 +--
  1.2711 +-- <expression 10> ::= <expression 9>
  1.2712 +-- <expression 10> ::= <expression 9> <rho> <expression 9>
  1.2713 +-- <rho> ::= < | <= | = | == | >= | > | <> | != | in | not in | ! in |
  1.2714 +--           within | not within | ! within */
  1.2715 +
  1.2716 +CODE *expression_10(MPL *mpl)
  1.2717 +{     CODE *x, *y;
  1.2718 +      int op = -1;
  1.2719 +      char opstr[16];
  1.2720 +      x = expression_9(mpl);
  1.2721 +      strcpy(opstr, "");
  1.2722 +      switch (mpl->token)
  1.2723 +      {  case T_LT:
  1.2724 +            op = O_LT; break;
  1.2725 +         case T_LE:
  1.2726 +            op = O_LE; break;
  1.2727 +         case T_EQ:
  1.2728 +            op = O_EQ; break;
  1.2729 +         case T_GE:
  1.2730 +            op = O_GE; break;
  1.2731 +         case T_GT:
  1.2732 +            op = O_GT; break;
  1.2733 +         case T_NE:
  1.2734 +            op = O_NE; break;
  1.2735 +         case T_IN:
  1.2736 +            op = O_IN; break;
  1.2737 +         case T_WITHIN:
  1.2738 +            op = O_WITHIN; break;
  1.2739 +         case T_NOT:
  1.2740 +            strcpy(opstr, mpl->image);
  1.2741 +            get_token(mpl /* not | ! */);
  1.2742 +            if (mpl->token == T_IN)
  1.2743 +               op = O_NOTIN;
  1.2744 +            else if (mpl->token == T_WITHIN)
  1.2745 +               op = O_NOTWITHIN;
  1.2746 +            else
  1.2747 +               error(mpl, "invalid use of %s", opstr);
  1.2748 +            strcat(opstr, " ");
  1.2749 +            break;
  1.2750 +         default:
  1.2751 +            goto done;
  1.2752 +      }
  1.2753 +      strcat(opstr, mpl->image);
  1.2754 +      xassert(strlen(opstr) < sizeof(opstr));
  1.2755 +      switch (op)
  1.2756 +      {  case O_EQ:
  1.2757 +         case O_NE:
  1.2758 +#if 1 /* 02/VIII-2008 */
  1.2759 +         case O_LT:
  1.2760 +         case O_LE:
  1.2761 +         case O_GT:
  1.2762 +         case O_GE:
  1.2763 +#endif
  1.2764 +            if (!(x->type == A_NUMERIC || x->type == A_SYMBOLIC))
  1.2765 +               error_preceding(mpl, opstr);
  1.2766 +            get_token(mpl /* <rho> */);
  1.2767 +            y = expression_9(mpl);
  1.2768 +            if (!(y->type == A_NUMERIC || y->type == A_SYMBOLIC))
  1.2769 +               error_following(mpl, opstr);
  1.2770 +            if (x->type == A_NUMERIC && y->type == A_SYMBOLIC)
  1.2771 +               x = make_unary(mpl, O_CVTSYM, x, A_SYMBOLIC, 0);
  1.2772 +            if (x->type == A_SYMBOLIC && y->type == A_NUMERIC)
  1.2773 +               y = make_unary(mpl, O_CVTSYM, y, A_SYMBOLIC, 0);
  1.2774 +            x = make_binary(mpl, op, x, y, A_LOGICAL, 0);
  1.2775 +            break;
  1.2776 +#if 0 /* 02/VIII-2008 */
  1.2777 +         case O_LT:
  1.2778 +         case O_LE:
  1.2779 +         case O_GT:
  1.2780 +         case O_GE:
  1.2781 +            if (x->type == A_SYMBOLIC)
  1.2782 +               x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0);
  1.2783 +            if (x->type != A_NUMERIC)
  1.2784 +               error_preceding(mpl, opstr);
  1.2785 +            get_token(mpl /* <rho> */);
  1.2786 +            y = expression_9(mpl);
  1.2787 +            if (y->type == A_SYMBOLIC)
  1.2788 +               y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0);
  1.2789 +            if (y->type != A_NUMERIC)
  1.2790 +               error_following(mpl, opstr);
  1.2791 +            x = make_binary(mpl, op, x, y, A_LOGICAL, 0);
  1.2792 +            break;
  1.2793 +#endif
  1.2794 +         case O_IN:
  1.2795 +         case O_NOTIN:
  1.2796 +            if (x->type == A_NUMERIC)
  1.2797 +               x = make_unary(mpl, O_CVTSYM, x, A_SYMBOLIC, 0);
  1.2798 +            if (x->type == A_SYMBOLIC)
  1.2799 +               x = make_unary(mpl, O_CVTTUP, x, A_TUPLE, 1);
  1.2800 +            if (x->type != A_TUPLE)
  1.2801 +               error_preceding(mpl, opstr);
  1.2802 +            get_token(mpl /* <rho> */);
  1.2803 +            y = expression_9(mpl);
  1.2804 +            if (y->type != A_ELEMSET)
  1.2805 +               error_following(mpl, opstr);
  1.2806 +            if (x->dim != y->dim)
  1.2807 +               error_dimension(mpl, opstr, x->dim, y->dim);
  1.2808 +            x = make_binary(mpl, op, x, y, A_LOGICAL, 0);
  1.2809 +            break;
  1.2810 +         case O_WITHIN:
  1.2811 +         case O_NOTWITHIN:
  1.2812 +            if (x->type != A_ELEMSET)
  1.2813 +               error_preceding(mpl, opstr);
  1.2814 +            get_token(mpl /* <rho> */);
  1.2815 +            y = expression_9(mpl);
  1.2816 +            if (y->type != A_ELEMSET)
  1.2817 +               error_following(mpl, opstr);
  1.2818 +            if (x->dim != y->dim)
  1.2819 +               error_dimension(mpl, opstr, x->dim, y->dim);
  1.2820 +            x = make_binary(mpl, op, x, y, A_LOGICAL, 0);
  1.2821 +            break;
  1.2822 +         default:
  1.2823 +            xassert(op != op);
  1.2824 +      }
  1.2825 +done: return x;
  1.2826 +}
  1.2827 +
  1.2828 +/*----------------------------------------------------------------------
  1.2829 +-- expression_11 - parse expression of level 11.
  1.2830 +--
  1.2831 +-- This routine parses expression of level 11 using the syntax:
  1.2832 +--
  1.2833 +-- <expression 11> ::= <expression 10>
  1.2834 +-- <expression 11> ::= not <expression 10>
  1.2835 +-- <expression 11> ::= ! <expression 10> */
  1.2836 +
  1.2837 +CODE *expression_11(MPL *mpl)
  1.2838 +{     CODE *x;
  1.2839 +      char opstr[8];
  1.2840 +      if (mpl->token == T_NOT)
  1.2841 +      {  strcpy(opstr, mpl->image);
  1.2842 +         xassert(strlen(opstr) < sizeof(opstr));
  1.2843 +         get_token(mpl /* not | ! */);
  1.2844 +         x = expression_10(mpl);
  1.2845 +         if (x->type == A_SYMBOLIC)
  1.2846 +            x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0);
  1.2847 +         if (x->type == A_NUMERIC)
  1.2848 +            x = make_unary(mpl, O_CVTLOG, x, A_LOGICAL, 0);
  1.2849 +         if (x->type != A_LOGICAL)
  1.2850 +            error_following(mpl, opstr);
  1.2851 +         x = make_unary(mpl, O_NOT, x, A_LOGICAL, 0);
  1.2852 +      }
  1.2853 +      else
  1.2854 +         x = expression_10(mpl);
  1.2855 +      return x;
  1.2856 +}
  1.2857 +
  1.2858 +/*----------------------------------------------------------------------
  1.2859 +-- expression_12 - parse expression of level 12.
  1.2860 +--
  1.2861 +-- This routine parses expression of level 12 using the syntax:
  1.2862 +--
  1.2863 +-- <expression 12> ::= <expression 11>
  1.2864 +-- <expression 12> ::= <expression 12> and <expression 11>
  1.2865 +-- <expression 12> ::= <expression 12> && <expression 11> */
  1.2866 +
  1.2867 +CODE *expression_12(MPL *mpl)
  1.2868 +{     CODE *x, *y;
  1.2869 +      char opstr[8];
  1.2870 +      x = expression_11(mpl);
  1.2871 +      for (;;)
  1.2872 +      {  if (mpl->token == T_AND)
  1.2873 +         {  strcpy(opstr, mpl->image);
  1.2874 +            xassert(strlen(opstr) < sizeof(opstr));
  1.2875 +            if (x->type == A_SYMBOLIC)
  1.2876 +               x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0);
  1.2877 +            if (x->type == A_NUMERIC)
  1.2878 +               x = make_unary(mpl, O_CVTLOG, x, A_LOGICAL, 0);
  1.2879 +            if (x->type != A_LOGICAL)
  1.2880 +               error_preceding(mpl, opstr);
  1.2881 +            get_token(mpl /* and | && */);
  1.2882 +            y = expression_11(mpl);
  1.2883 +            if (y->type == A_SYMBOLIC)
  1.2884 +               y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0);
  1.2885 +            if (y->type == A_NUMERIC)
  1.2886 +               y = make_unary(mpl, O_CVTLOG, y, A_LOGICAL, 0);
  1.2887 +            if (y->type != A_LOGICAL)
  1.2888 +               error_following(mpl, opstr);
  1.2889 +            x = make_binary(mpl, O_AND, x, y, A_LOGICAL, 0);
  1.2890 +         }
  1.2891 +         else
  1.2892 +            break;
  1.2893 +      }
  1.2894 +      return x;
  1.2895 +}
  1.2896 +
  1.2897 +/*----------------------------------------------------------------------
  1.2898 +-- expression_13 - parse expression of level 13.
  1.2899 +--
  1.2900 +-- This routine parses expression of level 13 using the syntax:
  1.2901 +--
  1.2902 +-- <expression 13> ::= <expression 12>
  1.2903 +-- <expression 13> ::= <expression 13> or <expression 12>
  1.2904 +-- <expression 13> ::= <expression 13> || <expression 12> */
  1.2905 +
  1.2906 +CODE *expression_13(MPL *mpl)
  1.2907 +{     CODE *x, *y;
  1.2908 +      char opstr[8];
  1.2909 +      x = expression_12(mpl);
  1.2910 +      for (;;)
  1.2911 +      {  if (mpl->token == T_OR)
  1.2912 +         {  strcpy(opstr, mpl->image);
  1.2913 +            xassert(strlen(opstr) < sizeof(opstr));
  1.2914 +            if (x->type == A_SYMBOLIC)
  1.2915 +               x = make_unary(mpl, O_CVTNUM, x, A_NUMERIC, 0);
  1.2916 +            if (x->type == A_NUMERIC)
  1.2917 +               x = make_unary(mpl, O_CVTLOG, x, A_LOGICAL, 0);
  1.2918 +            if (x->type != A_LOGICAL)
  1.2919 +               error_preceding(mpl, opstr);
  1.2920 +            get_token(mpl /* or | || */);
  1.2921 +            y = expression_12(mpl);
  1.2922 +            if (y->type == A_SYMBOLIC)
  1.2923 +               y = make_unary(mpl, O_CVTNUM, y, A_NUMERIC, 0);
  1.2924 +            if (y->type == A_NUMERIC)
  1.2925 +               y = make_unary(mpl, O_CVTLOG, y, A_LOGICAL, 0);
  1.2926 +            if (y->type != A_LOGICAL)
  1.2927 +               error_following(mpl, opstr);
  1.2928 +            x = make_binary(mpl, O_OR, x, y, A_LOGICAL, 0);
  1.2929 +         }
  1.2930 +         else
  1.2931 +            break;
  1.2932 +      }
  1.2933 +      return x;
  1.2934 +}
  1.2935 +
  1.2936 +/*----------------------------------------------------------------------
  1.2937 +-- set_statement - parse set statement.
  1.2938 +--
  1.2939 +-- This routine parses set statement using the syntax:
  1.2940 +--
  1.2941 +-- <set statement> ::= set <symbolic name> <alias> <domain>
  1.2942 +--                     <attributes> ;
  1.2943 +-- <alias> ::= <empty>
  1.2944 +-- <alias> ::= <string literal>
  1.2945 +-- <domain> ::= <empty>
  1.2946 +-- <domain> ::= <indexing expression>
  1.2947 +-- <attributes> ::= <empty>
  1.2948 +-- <attributes> ::= <attributes> , dimen <numeric literal>
  1.2949 +-- <attributes> ::= <attributes> , within <expression 9>
  1.2950 +-- <attributes> ::= <attributes> , := <expression 9>
  1.2951 +-- <attributes> ::= <attributes> , default <expression 9>
  1.2952 +--
  1.2953 +-- Commae in <attributes> are optional and may be omitted anywhere. */
  1.2954 +
  1.2955 +SET *set_statement(MPL *mpl)
  1.2956 +{     SET *set;
  1.2957 +      int dimen_used = 0;
  1.2958 +      xassert(is_keyword(mpl, "set"));
  1.2959 +      get_token(mpl /* set */);
  1.2960 +      /* symbolic name must follow the keyword 'set' */
  1.2961 +      if (mpl->token == T_NAME)
  1.2962 +         ;
  1.2963 +      else if (is_reserved(mpl))
  1.2964 +         error(mpl, "invalid use of reserved keyword %s", mpl->image);
  1.2965 +      else
  1.2966 +         error(mpl, "symbolic name missing where expected");
  1.2967 +      /* there must be no other object with the same name */
  1.2968 +      if (avl_find_node(mpl->tree, mpl->image) != NULL)
  1.2969 +         error(mpl, "%s multiply declared", mpl->image);
  1.2970 +      /* create model set */
  1.2971 +      set = alloc(SET);
  1.2972 +      set->name = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1);
  1.2973 +      strcpy(set->name, mpl->image);
  1.2974 +      set->alias = NULL;
  1.2975 +      set->dim = 0;
  1.2976 +      set->domain = NULL;
  1.2977 +      set->dimen = 0;
  1.2978 +      set->within = NULL;
  1.2979 +      set->assign = NULL;
  1.2980 +      set->option = NULL;
  1.2981 +      set->gadget = NULL;
  1.2982 +      set->data = 0;
  1.2983 +      set->array = NULL;
  1.2984 +      get_token(mpl /* <symbolic name> */);
  1.2985 +      /* parse optional alias */
  1.2986 +      if (mpl->token == T_STRING)
  1.2987 +      {  set->alias = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1);
  1.2988 +         strcpy(set->alias, mpl->image);
  1.2989 +         get_token(mpl /* <string literal> */);
  1.2990 +      }
  1.2991 +      /* parse optional indexing expression */
  1.2992 +      if (mpl->token == T_LBRACE)
  1.2993 +      {  set->domain = indexing_expression(mpl);
  1.2994 +         set->dim = domain_arity(mpl, set->domain);
  1.2995 +      }
  1.2996 +      /* include the set name in the symbolic names table */
  1.2997 +      {  AVLNODE *node;
  1.2998 +         node = avl_insert_node(mpl->tree, set->name);
  1.2999 +         avl_set_node_type(node, A_SET);
  1.3000 +         avl_set_node_link(node, (void *)set);
  1.3001 +      }
  1.3002 +      /* parse the list of optional attributes */
  1.3003 +      for (;;)
  1.3004 +      {  if (mpl->token == T_COMMA)
  1.3005 +            get_token(mpl /* , */);
  1.3006 +         else if (mpl->token == T_SEMICOLON)
  1.3007 +            break;
  1.3008 +         if (is_keyword(mpl, "dimen"))
  1.3009 +         {  /* dimension of set members */
  1.3010 +            int dimen;
  1.3011 +            get_token(mpl /* dimen */);
  1.3012 +            if (!(mpl->token == T_NUMBER &&
  1.3013 +                  1.0 <= mpl->value && mpl->value <= 20.0 &&
  1.3014 +                  floor(mpl->value) == mpl->value))
  1.3015 +               error(mpl, "dimension must be integer between 1 and 20");
  1.3016 +            dimen = (int)(mpl->value + 0.5);
  1.3017 +            if (dimen_used)
  1.3018 +               error(mpl, "at most one dimension attribute allowed");
  1.3019 +            if (set->dimen > 0)
  1.3020 +               error(mpl, "dimension %d conflicts with dimension %d alr"
  1.3021 +                  "eady determined", dimen, set->dimen);
  1.3022 +            set->dimen = dimen;
  1.3023 +            dimen_used = 1;
  1.3024 +            get_token(mpl /* <numeric literal> */);
  1.3025 +         }
  1.3026 +         else if (mpl->token == T_WITHIN || mpl->token == T_IN)
  1.3027 +         {  /* restricting superset */
  1.3028 +            WITHIN *within, *temp;
  1.3029 +            if (mpl->token == T_IN && !mpl->as_within)
  1.3030 +            {  warning(mpl, "keyword in understood as within");
  1.3031 +               mpl->as_within = 1;
  1.3032 +            }
  1.3033 +            get_token(mpl /* within */);
  1.3034 +            /* create new restricting superset list entry and append it
  1.3035 +               to the within-list */
  1.3036 +            within = alloc(WITHIN);
  1.3037 +            within->code = NULL;
  1.3038 +            within->next = NULL;
  1.3039 +            if (set->within == NULL)
  1.3040 +               set->within = within;
  1.3041 +            else
  1.3042 +            {  for (temp = set->within; temp->next != NULL; temp =
  1.3043 +                  temp->next);
  1.3044 +               temp->next = within;
  1.3045 +            }
  1.3046 +            /* parse an expression that follows 'within' */
  1.3047 +            within->code = expression_9(mpl);
  1.3048 +            if (within->code->type != A_ELEMSET)
  1.3049 +               error(mpl, "expression following within has invalid type"
  1.3050 +                  );
  1.3051 +            xassert(within->code->dim > 0);
  1.3052 +            /* check/set dimension of set members */
  1.3053 +            if (set->dimen == 0) set->dimen = within->code->dim;
  1.3054 +            if (set->dimen != within->code->dim)
  1.3055 +               error(mpl, "set expression following within must have di"
  1.3056 +                  "mension %d rather than %d",
  1.3057 +                  set->dimen, within->code->dim);
  1.3058 +         }
  1.3059 +         else if (mpl->token == T_ASSIGN)
  1.3060 +         {  /* assignment expression */
  1.3061 +            if (!(set->assign == NULL && set->option == NULL &&
  1.3062 +                  set->gadget == NULL))
  1.3063 +err:           error(mpl, "at most one := or default/data allowed");
  1.3064 +            get_token(mpl /* := */);
  1.3065 +            /* parse an expression that follows ':=' */
  1.3066 +            set->assign = expression_9(mpl);
  1.3067 +            if (set->assign->type != A_ELEMSET)
  1.3068 +               error(mpl, "expression following := has invalid type");
  1.3069 +            xassert(set->assign->dim > 0);
  1.3070 +            /* check/set dimension of set members */
  1.3071 +            if (set->dimen == 0) set->dimen = set->assign->dim;
  1.3072 +            if (set->dimen != set->assign->dim)
  1.3073 +               error(mpl, "set expression following := must have dimens"
  1.3074 +                  "ion %d rather than %d",
  1.3075 +                  set->dimen, set->assign->dim);
  1.3076 +         }
  1.3077 +         else if (is_keyword(mpl, "default"))
  1.3078 +         {  /* expression for default value */
  1.3079 +            if (!(set->assign == NULL && set->option == NULL)) goto err;
  1.3080 +            get_token(mpl /* := */);
  1.3081 +            /* parse an expression that follows 'default' */
  1.3082 +            set->option = expression_9(mpl);
  1.3083 +            if (set->option->type != A_ELEMSET)
  1.3084 +               error(mpl, "expression following default has invalid typ"
  1.3085 +                  "e");
  1.3086 +            xassert(set->option->dim > 0);
  1.3087 +            /* check/set dimension of set members */
  1.3088 +            if (set->dimen == 0) set->dimen = set->option->dim;
  1.3089 +            if (set->dimen != set->option->dim)
  1.3090 +               error(mpl, "set expression following default must have d"
  1.3091 +                  "imension %d rather than %d",
  1.3092 +                  set->dimen, set->option->dim);
  1.3093 +         }
  1.3094 +#if 1 /* 12/XII-2008 */
  1.3095 +         else if (is_keyword(mpl, "data"))
  1.3096 +         {  /* gadget to initialize the set by data from plain set */
  1.3097 +            GADGET *gadget;
  1.3098 +            AVLNODE *node;
  1.3099 +            int i, k, fff[20];
  1.3100 +            if (!(set->assign == NULL && set->gadget == NULL)) goto err;
  1.3101 +            get_token(mpl /* data */);
  1.3102 +            set->gadget = gadget = alloc(GADGET);
  1.3103 +            /* set name must follow the keyword 'data' */
  1.3104 +            if (mpl->token == T_NAME)
  1.3105 +               ;
  1.3106 +            else if (is_reserved(mpl))
  1.3107 +               error(mpl, "invalid use of reserved keyword %s",
  1.3108 +                  mpl->image);
  1.3109 +            else
  1.3110 +               error(mpl, "set name missing where expected");
  1.3111 +            /* find the set in the symbolic name table */
  1.3112 +            node = avl_find_node(mpl->tree, mpl->image);
  1.3113 +            if (node == NULL)
  1.3114 +               error(mpl, "%s not defined", mpl->image);
  1.3115 +            if (avl_get_node_type(node) != A_SET)
  1.3116 +err1:          error(mpl, "%s not a plain set", mpl->image);
  1.3117 +            gadget->set = avl_get_node_link(node);
  1.3118 +            if (gadget->set->dim != 0) goto err1;
  1.3119 +            if (gadget->set == set)
  1.3120 +               error(mpl, "set cannot be initialized by itself");
  1.3121 +            /* check and set dimensions */
  1.3122 +            if (set->dim >= gadget->set->dimen)
  1.3123 +err2:          error(mpl, "dimension of %s too small", mpl->image);
  1.3124 +            if (set->dimen == 0)
  1.3125 +               set->dimen = gadget->set->dimen - set->dim;
  1.3126 +            if (set->dim + set->dimen > gadget->set->dimen)
  1.3127 +               goto err2;
  1.3128 +            else if (set->dim + set->dimen < gadget->set->dimen)
  1.3129 +               error(mpl, "dimension of %s too big", mpl->image);
  1.3130 +            get_token(mpl /* set name */);
  1.3131 +            /* left parenthesis must follow the set name */
  1.3132 +            if (mpl->token == T_LEFT)
  1.3133 +               get_token(mpl /* ( */);
  1.3134 +            else
  1.3135 +               error(mpl, "left parenthesis missing where expected");
  1.3136 +            /* parse permutation of component numbers */
  1.3137 +            for (k = 0; k < gadget->set->dimen; k++) fff[k] = 0;
  1.3138 +            k = 0;
  1.3139 +            for (;;)
  1.3140 +            {  if (mpl->token != T_NUMBER)
  1.3141 +                  error(mpl, "component number missing where expected");
  1.3142 +               if (str2int(mpl->image, &i) != 0)
  1.3143 +err3:             error(mpl, "component number must be integer between "
  1.3144 +                     "1 and %d", gadget->set->dimen);
  1.3145 +               if (!(1 <= i && i <= gadget->set->dimen)) goto err3;
  1.3146 +               if (fff[i-1] != 0)
  1.3147 +                  error(mpl, "component %d multiply specified", i);
  1.3148 +               gadget->ind[k++] = i, fff[i-1] = 1;
  1.3149 +               xassert(k <= gadget->set->dimen);
  1.3150 +               get_token(mpl /* number */);
  1.3151 +               if (mpl->token == T_COMMA)
  1.3152 +                  get_token(mpl /* , */);
  1.3153 +               else if (mpl->token == T_RIGHT)
  1.3154 +                  break;
  1.3155 +               else
  1.3156 +                  error(mpl, "syntax error in data attribute");
  1.3157 +            }
  1.3158 +            if (k < gadget->set->dimen)
  1.3159 +               error(mpl, "there are must be %d components rather than "
  1.3160 +                  "%d", gadget->set->dimen, k);
  1.3161 +            get_token(mpl /* ) */);
  1.3162 +         }
  1.3163 +#endif
  1.3164 +         else
  1.3165 +            error(mpl, "syntax error in set statement");
  1.3166 +      }
  1.3167 +      /* close the domain scope */
  1.3168 +      if (set->domain != NULL) close_scope(mpl, set->domain);
  1.3169 +      /* if dimension of set members is still unknown, set it to 1 */
  1.3170 +      if (set->dimen == 0) set->dimen = 1;
  1.3171 +      /* the set statement has been completely parsed */
  1.3172 +      xassert(mpl->token == T_SEMICOLON);
  1.3173 +      get_token(mpl /* ; */);
  1.3174 +      return set;
  1.3175 +}
  1.3176 +
  1.3177 +/*----------------------------------------------------------------------
  1.3178 +-- parameter_statement - parse parameter statement.
  1.3179 +--
  1.3180 +-- This routine parses parameter statement using the syntax:
  1.3181 +--
  1.3182 +-- <parameter statement> ::= param <symbolic name> <alias> <domain>
  1.3183 +--                           <attributes> ;
  1.3184 +-- <alias> ::= <empty>
  1.3185 +-- <alias> ::= <string literal>
  1.3186 +-- <domain> ::= <empty>
  1.3187 +-- <domain> ::= <indexing expression>
  1.3188 +-- <attributes> ::= <empty>
  1.3189 +-- <attributes> ::= <attributes> , integer
  1.3190 +-- <attributes> ::= <attributes> , binary
  1.3191 +-- <attributes> ::= <attributes> , symbolic
  1.3192 +-- <attributes> ::= <attributes> , <rho> <expression 5>
  1.3193 +-- <attributes> ::= <attributes> , in <expression 9>
  1.3194 +-- <attributes> ::= <attributes> , := <expression 5>
  1.3195 +-- <attributes> ::= <attributes> , default <expression 5>
  1.3196 +-- <rho> ::= < | <= | = | == | >= | > | <> | !=
  1.3197 +--
  1.3198 +-- Commae in <attributes> are optional and may be omitted anywhere. */
  1.3199 +
  1.3200 +PARAMETER *parameter_statement(MPL *mpl)
  1.3201 +{     PARAMETER *par;
  1.3202 +      int integer_used = 0, binary_used = 0, symbolic_used = 0;
  1.3203 +      xassert(is_keyword(mpl, "param"));
  1.3204 +      get_token(mpl /* param */);
  1.3205 +      /* symbolic name must follow the keyword 'param' */
  1.3206 +      if (mpl->token == T_NAME)
  1.3207 +         ;
  1.3208 +      else if (is_reserved(mpl))
  1.3209 +         error(mpl, "invalid use of reserved keyword %s", mpl->image);
  1.3210 +      else
  1.3211 +         error(mpl, "symbolic name missing where expected");
  1.3212 +      /* there must be no other object with the same name */
  1.3213 +      if (avl_find_node(mpl->tree, mpl->image) != NULL)
  1.3214 +         error(mpl, "%s multiply declared", mpl->image);
  1.3215 +      /* create model parameter */
  1.3216 +      par = alloc(PARAMETER);
  1.3217 +      par->name = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1);
  1.3218 +      strcpy(par->name, mpl->image);
  1.3219 +      par->alias = NULL;
  1.3220 +      par->dim = 0;
  1.3221 +      par->domain = NULL;
  1.3222 +      par->type = A_NUMERIC;
  1.3223 +      par->cond = NULL;
  1.3224 +      par->in = NULL;
  1.3225 +      par->assign = NULL;
  1.3226 +      par->option = NULL;
  1.3227 +      par->data = 0;
  1.3228 +      par->defval = NULL;
  1.3229 +      par->array = NULL;
  1.3230 +      get_token(mpl /* <symbolic name> */);
  1.3231 +      /* parse optional alias */
  1.3232 +      if (mpl->token == T_STRING)
  1.3233 +      {  par->alias = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1);
  1.3234 +         strcpy(par->alias, mpl->image);
  1.3235 +         get_token(mpl /* <string literal> */);
  1.3236 +      }
  1.3237 +      /* parse optional indexing expression */
  1.3238 +      if (mpl->token == T_LBRACE)
  1.3239 +      {  par->domain = indexing_expression(mpl);
  1.3240 +         par->dim = domain_arity(mpl, par->domain);
  1.3241 +      }
  1.3242 +      /* include the parameter name in the symbolic names table */
  1.3243 +      {  AVLNODE *node;
  1.3244 +         node = avl_insert_node(mpl->tree, par->name);
  1.3245 +         avl_set_node_type(node, A_PARAMETER);
  1.3246 +         avl_set_node_link(node, (void *)par);
  1.3247 +      }
  1.3248 +      /* parse the list of optional attributes */
  1.3249 +      for (;;)
  1.3250 +      {  if (mpl->token == T_COMMA)
  1.3251 +            get_token(mpl /* , */);
  1.3252 +         else if (mpl->token == T_SEMICOLON)
  1.3253 +            break;
  1.3254 +         if (is_keyword(mpl, "integer"))
  1.3255 +         {  if (integer_used)
  1.3256 +               error(mpl, "at most one integer allowed");
  1.3257 +            if (par->type == A_SYMBOLIC)
  1.3258 +               error(mpl, "symbolic parameter cannot be integer");
  1.3259 +            if (par->type != A_BINARY) par->type = A_INTEGER;
  1.3260 +            integer_used = 1;
  1.3261 +            get_token(mpl /* integer */);
  1.3262 +         }
  1.3263 +         else if (is_keyword(mpl, "binary"))
  1.3264 +bin:     {  if (binary_used)
  1.3265 +               error(mpl, "at most one binary allowed");
  1.3266 +            if (par->type == A_SYMBOLIC)
  1.3267 +               error(mpl, "symbolic parameter cannot be binary");
  1.3268 +            par->type = A_BINARY;
  1.3269 +            binary_used = 1;
  1.3270 +            get_token(mpl /* binary */);
  1.3271 +         }
  1.3272 +         else if (is_keyword(mpl, "logical"))
  1.3273 +         {  if (!mpl->as_binary)
  1.3274 +            {  warning(mpl, "keyword logical understood as binary");
  1.3275 +               mpl->as_binary = 1;
  1.3276 +            }
  1.3277 +            goto bin;
  1.3278 +         }
  1.3279 +         else if (is_keyword(mpl, "symbolic"))
  1.3280 +         {  if (symbolic_used)
  1.3281 +               error(mpl, "at most one symbolic allowed");
  1.3282 +            if (par->type != A_NUMERIC)
  1.3283 +               error(mpl, "integer or binary parameter cannot be symbol"
  1.3284 +                  "ic");
  1.3285 +            /* the parameter may be referenced from expressions given
  1.3286 +               in the same parameter declaration, so its type must be
  1.3287 +               completed before parsing that expressions */
  1.3288 +            if (!(par->cond == NULL && par->in == NULL &&
  1.3289 +                  par->assign == NULL && par->option == NULL))
  1.3290 +               error(mpl, "keyword symbolic must precede any other para"
  1.3291 +                  "meter attributes");
  1.3292 +            par->type = A_SYMBOLIC;
  1.3293 +            symbolic_used = 1;
  1.3294 +            get_token(mpl /* symbolic */);
  1.3295 +         }
  1.3296 +         else if (mpl->token == T_LT || mpl->token == T_LE ||
  1.3297 +                  mpl->token == T_EQ || mpl->token == T_GE ||
  1.3298 +                  mpl->token == T_GT || mpl->token == T_NE)
  1.3299 +         {  /* restricting condition */
  1.3300 +            CONDITION *cond, *temp;
  1.3301 +            char opstr[8];
  1.3302 +            /* create new restricting condition list entry and append
  1.3303 +               it to the conditions list */
  1.3304 +            cond = alloc(CONDITION);
  1.3305 +            switch (mpl->token)
  1.3306 +            {  case T_LT:
  1.3307 +                  cond->rho = O_LT, strcpy(opstr, mpl->image); break;
  1.3308 +               case T_LE:
  1.3309 +                  cond->rho = O_LE, strcpy(opstr, mpl->image); break;
  1.3310 +               case T_EQ:
  1.3311 +                  cond->rho = O_EQ, strcpy(opstr, mpl->image); break;
  1.3312 +               case T_GE:
  1.3313 +                  cond->rho = O_GE, strcpy(opstr, mpl->image); break;
  1.3314 +               case T_GT:
  1.3315 +                  cond->rho = O_GT, strcpy(opstr, mpl->image); break;
  1.3316 +               case T_NE:
  1.3317 +                  cond->rho = O_NE, strcpy(opstr, mpl->image); break;
  1.3318 +               default:
  1.3319 +                  xassert(mpl->token != mpl->token);
  1.3320 +            }
  1.3321 +            xassert(strlen(opstr) < sizeof(opstr));
  1.3322 +            cond->code = NULL;
  1.3323 +            cond->next = NULL;
  1.3324 +            if (par->cond == NULL)
  1.3325 +               par->cond = cond;
  1.3326 +            else
  1.3327 +            {  for (temp = par->cond; temp->next != NULL; temp =
  1.3328 +                  temp->next);
  1.3329 +               temp->next = cond;
  1.3330 +            }
  1.3331 +#if 0 /* 13/VIII-2008 */
  1.3332 +            if (par->type == A_SYMBOLIC &&
  1.3333 +               !(cond->rho == O_EQ || cond->rho == O_NE))
  1.3334 +               error(mpl, "inequality restriction not allowed");
  1.3335 +#endif
  1.3336 +            get_token(mpl /* rho */);
  1.3337 +            /* parse an expression that follows relational operator */
  1.3338 +            cond->code = expression_5(mpl);
  1.3339 +            if (!(cond->code->type == A_NUMERIC ||
  1.3340 +                  cond->code->type == A_SYMBOLIC))
  1.3341 +               error(mpl, "expression following %s has invalid type",
  1.3342 +                  opstr);
  1.3343 +            xassert(cond->code->dim == 0);
  1.3344 +            /* convert to the parameter type, if necessary */
  1.3345 +            if (par->type != A_SYMBOLIC && cond->code->type ==
  1.3346 +               A_SYMBOLIC)
  1.3347 +               cond->code = make_unary(mpl, O_CVTNUM, cond->code,
  1.3348 +                  A_NUMERIC, 0);
  1.3349 +            if (par->type == A_SYMBOLIC && cond->code->type !=
  1.3350 +               A_SYMBOLIC)
  1.3351 +               cond->code = make_unary(mpl, O_CVTSYM, cond->code,
  1.3352 +                  A_SYMBOLIC, 0);
  1.3353 +         }
  1.3354 +         else if (mpl->token == T_IN || mpl->token == T_WITHIN)
  1.3355 +         {  /* restricting superset */
  1.3356 +            WITHIN *in, *temp;
  1.3357 +            if (mpl->token == T_WITHIN && !mpl->as_in)
  1.3358 +            {  warning(mpl, "keyword within understood as in");
  1.3359 +               mpl->as_in = 1;
  1.3360 +            }
  1.3361 +            get_token(mpl /* in */);
  1.3362 +            /* create new restricting superset list entry and append it
  1.3363 +               to the in-list */
  1.3364 +            in = alloc(WITHIN);
  1.3365 +            in->code = NULL;
  1.3366 +            in->next = NULL;
  1.3367 +            if (par->in == NULL)
  1.3368 +               par->in = in;
  1.3369 +            else
  1.3370 +            {  for (temp = par->in; temp->next != NULL; temp =
  1.3371 +                  temp->next);
  1.3372 +               temp->next = in;
  1.3373 +            }
  1.3374 +            /* parse an expression that follows 'in' */
  1.3375 +            in->code = expression_9(mpl);
  1.3376 +            if (in->code->type != A_ELEMSET)
  1.3377 +               error(mpl, "expression following in has invalid type");
  1.3378 +            xassert(in->code->dim > 0);
  1.3379 +            if (in->code->dim != 1)
  1.3380 +               error(mpl, "set expression following in must have dimens"
  1.3381 +                  "ion 1 rather than %d", in->code->dim);
  1.3382 +         }
  1.3383 +         else if (mpl->token == T_ASSIGN)
  1.3384 +         {  /* assignment expression */
  1.3385 +            if (!(par->assign == NULL && par->option == NULL))
  1.3386 +err:           error(mpl, "at most one := or default allowed");
  1.3387 +            get_token(mpl /* := */);
  1.3388 +            /* parse an expression that follows ':=' */
  1.3389 +            par->assign = expression_5(mpl);
  1.3390 +            /* the expression must be of numeric/symbolic type */
  1.3391 +            if (!(par->assign->type == A_NUMERIC ||
  1.3392 +                  par->assign->type == A_SYMBOLIC))
  1.3393 +               error(mpl, "expression following := has invalid type");
  1.3394 +            xassert(par->assign->dim == 0);
  1.3395 +            /* convert to the parameter type, if necessary */
  1.3396 +            if (par->type != A_SYMBOLIC && par->assign->type ==
  1.3397 +               A_SYMBOLIC)
  1.3398 +               par->assign = make_unary(mpl, O_CVTNUM, par->assign,
  1.3399 +                  A_NUMERIC, 0);
  1.3400 +            if (par->type == A_SYMBOLIC && par->assign->type !=
  1.3401 +               A_SYMBOLIC)
  1.3402 +               par->assign = make_unary(mpl, O_CVTSYM, par->assign,
  1.3403 +                  A_SYMBOLIC, 0);
  1.3404 +         }
  1.3405 +         else if (is_keyword(mpl, "default"))
  1.3406 +         {  /* expression for default value */
  1.3407 +            if (!(par->assign == NULL && par->option == NULL)) goto err;
  1.3408 +            get_token(mpl /* default */);
  1.3409 +            /* parse an expression that follows 'default' */
  1.3410 +            par->option = expression_5(mpl);
  1.3411 +            if (!(par->option->type == A_NUMERIC ||
  1.3412 +                  par->option->type == A_SYMBOLIC))
  1.3413 +               error(mpl, "expression following default has invalid typ"
  1.3414 +                  "e");
  1.3415 +            xassert(par->option->dim == 0);
  1.3416 +            /* convert to the parameter type, if necessary */
  1.3417 +            if (par->type != A_SYMBOLIC && par->option->type ==
  1.3418 +               A_SYMBOLIC)
  1.3419 +               par->option = make_unary(mpl, O_CVTNUM, par->option,
  1.3420 +                  A_NUMERIC, 0);
  1.3421 +            if (par->type == A_SYMBOLIC && par->option->type !=
  1.3422 +               A_SYMBOLIC)
  1.3423 +               par->option = make_unary(mpl, O_CVTSYM, par->option,
  1.3424 +                  A_SYMBOLIC, 0);
  1.3425 +         }
  1.3426 +         else
  1.3427 +            error(mpl, "syntax error in parameter statement");
  1.3428 +      }
  1.3429 +      /* close the domain scope */
  1.3430 +      if (par->domain != NULL) close_scope(mpl, par->domain);
  1.3431 +      /* the parameter statement has been completely parsed */
  1.3432 +      xassert(mpl->token == T_SEMICOLON);
  1.3433 +      get_token(mpl /* ; */);
  1.3434 +      return par;
  1.3435 +}
  1.3436 +
  1.3437 +/*----------------------------------------------------------------------
  1.3438 +-- variable_statement - parse variable statement.
  1.3439 +--
  1.3440 +-- This routine parses variable statement using the syntax:
  1.3441 +--
  1.3442 +-- <variable statement> ::= var <symbolic name> <alias> <domain>
  1.3443 +--                          <attributes> ;
  1.3444 +-- <alias> ::= <empty>
  1.3445 +-- <alias> ::= <string literal>
  1.3446 +-- <domain> ::= <empty>
  1.3447 +-- <domain> ::= <indexing expression>
  1.3448 +-- <attributes> ::= <empty>
  1.3449 +-- <attributes> ::= <attributes> , integer
  1.3450 +-- <attributes> ::= <attributes> , binary
  1.3451 +-- <attributes> ::= <attributes> , <rho> <expression 5>
  1.3452 +-- <rho> ::= >= | <= | = | ==
  1.3453 +--
  1.3454 +-- Commae in <attributes> are optional and may be omitted anywhere. */
  1.3455 +
  1.3456 +VARIABLE *variable_statement(MPL *mpl)
  1.3457 +{     VARIABLE *var;
  1.3458 +      int integer_used = 0, binary_used = 0;
  1.3459 +      xassert(is_keyword(mpl, "var"));
  1.3460 +      if (mpl->flag_s)
  1.3461 +         error(mpl, "variable statement must precede solve statement");
  1.3462 +      get_token(mpl /* var */);
  1.3463 +      /* symbolic name must follow the keyword 'var' */
  1.3464 +      if (mpl->token == T_NAME)
  1.3465 +         ;
  1.3466 +      else if (is_reserved(mpl))
  1.3467 +         error(mpl, "invalid use of reserved keyword %s", mpl->image);
  1.3468 +      else
  1.3469 +         error(mpl, "symbolic name missing where expected");
  1.3470 +      /* there must be no other object with the same name */
  1.3471 +      if (avl_find_node(mpl->tree, mpl->image) != NULL)
  1.3472 +         error(mpl, "%s multiply declared", mpl->image);
  1.3473 +      /* create model variable */
  1.3474 +      var = alloc(VARIABLE);
  1.3475 +      var->name = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1);
  1.3476 +      strcpy(var->name, mpl->image);
  1.3477 +      var->alias = NULL;
  1.3478 +      var->dim = 0;
  1.3479 +      var->domain = NULL;
  1.3480 +      var->type = A_NUMERIC;
  1.3481 +      var->lbnd = NULL;
  1.3482 +      var->ubnd = NULL;
  1.3483 +      var->array = NULL;
  1.3484 +      get_token(mpl /* <symbolic name> */);
  1.3485 +      /* parse optional alias */
  1.3486 +      if (mpl->token == T_STRING)
  1.3487 +      {  var->alias = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1);
  1.3488 +         strcpy(var->alias, mpl->image);
  1.3489 +         get_token(mpl /* <string literal> */);
  1.3490 +      }
  1.3491 +      /* parse optional indexing expression */
  1.3492 +      if (mpl->token == T_LBRACE)
  1.3493 +      {  var->domain = indexing_expression(mpl);
  1.3494 +         var->dim = domain_arity(mpl, var->domain);
  1.3495 +      }
  1.3496 +      /* include the variable name in the symbolic names table */
  1.3497 +      {  AVLNODE *node;
  1.3498 +         node = avl_insert_node(mpl->tree, var->name);
  1.3499 +         avl_set_node_type(node, A_VARIABLE);
  1.3500 +         avl_set_node_link(node, (void *)var);
  1.3501 +      }
  1.3502 +      /* parse the list of optional attributes */
  1.3503 +      for (;;)
  1.3504 +      {  if (mpl->token == T_COMMA)
  1.3505 +            get_token(mpl /* , */);
  1.3506 +         else if (mpl->token == T_SEMICOLON)
  1.3507 +            break;
  1.3508 +         if (is_keyword(mpl, "integer"))
  1.3509 +         {  if (integer_used)
  1.3510 +               error(mpl, "at most one integer allowed");
  1.3511 +            if (var->type != A_BINARY) var->type = A_INTEGER;
  1.3512 +            integer_used = 1;
  1.3513 +            get_token(mpl /* integer */);
  1.3514 +         }
  1.3515 +         else if (is_keyword(mpl, "binary"))
  1.3516 +bin:     {  if (binary_used)
  1.3517 +               error(mpl, "at most one binary allowed");
  1.3518 +            var->type = A_BINARY;
  1.3519 +            binary_used = 1;
  1.3520 +            get_token(mpl /* binary */);
  1.3521 +         }
  1.3522 +         else if (is_keyword(mpl, "logical"))
  1.3523 +         {  if (!mpl->as_binary)
  1.3524 +            {  warning(mpl, "keyword logical understood as binary");
  1.3525 +               mpl->as_binary = 1;
  1.3526 +            }
  1.3527 +            goto bin;
  1.3528 +         }
  1.3529 +         else if (is_keyword(mpl, "symbolic"))
  1.3530 +            error(mpl, "variable cannot be symbolic");
  1.3531 +         else if (mpl->token == T_GE)
  1.3532 +         {  /* lower bound */
  1.3533 +            if (var->lbnd != NULL)
  1.3534 +            {  if (var->lbnd == var->ubnd)
  1.3535 +                  error(mpl, "both fixed value and lower bound not allo"
  1.3536 +                     "wed");
  1.3537 +               else
  1.3538 +                  error(mpl, "at most one lower bound allowed");
  1.3539 +            }
  1.3540 +            get_token(mpl /* >= */);
  1.3541 +            /* parse an expression that specifies the lower bound */
  1.3542 +            var->lbnd = expression_5(mpl);
  1.3543 +            if (var->lbnd->type == A_SYMBOLIC)
  1.3544 +               var->lbnd = make_unary(mpl, O_CVTNUM, var->lbnd,
  1.3545 +                  A_NUMERIC, 0);
  1.3546 +            if (var->lbnd->type != A_NUMERIC)
  1.3547 +               error(mpl, "expression following >= has invalid type");
  1.3548 +            xassert(var->lbnd->dim == 0);
  1.3549 +         }
  1.3550 +         else if (mpl->token == T_LE)
  1.3551 +         {  /* upper bound */
  1.3552 +            if (var->ubnd != NULL)
  1.3553 +            {  if (var->ubnd == var->lbnd)
  1.3554 +                  error(mpl, "both fixed value and upper bound not allo"
  1.3555 +                     "wed");
  1.3556 +               else
  1.3557 +                  error(mpl, "at most one upper bound allowed");
  1.3558 +            }
  1.3559 +            get_token(mpl /* <= */);
  1.3560 +            /* parse an expression that specifies the upper bound */
  1.3561 +            var->ubnd = expression_5(mpl);
  1.3562 +            if (var->ubnd->type == A_SYMBOLIC)
  1.3563 +               var->ubnd = make_unary(mpl, O_CVTNUM, var->ubnd,
  1.3564 +                  A_NUMERIC, 0);
  1.3565 +            if (var->ubnd->type != A_NUMERIC)
  1.3566 +               error(mpl, "expression following <= has invalid type");
  1.3567 +            xassert(var->ubnd->dim == 0);
  1.3568 +         }
  1.3569 +         else if (mpl->token == T_EQ)
  1.3570 +         {  /* fixed value */
  1.3571 +            char opstr[8];
  1.3572 +            if (!(var->lbnd == NULL && var->ubnd == NULL))
  1.3573 +            {  if (var->lbnd == var->ubnd)
  1.3574 +                  error(mpl, "at most one fixed value allowed");
  1.3575 +               else if (var->lbnd != NULL)
  1.3576 +                  error(mpl, "both lower bound and fixed value not allo"
  1.3577 +                     "wed");
  1.3578 +               else
  1.3579 +                  error(mpl, "both upper bound and fixed value not allo"
  1.3580 +                     "wed");
  1.3581 +            }
  1.3582 +            strcpy(opstr, mpl->image);
  1.3583 +            xassert(strlen(opstr) < sizeof(opstr));
  1.3584 +            get_token(mpl /* = | == */);
  1.3585 +            /* parse an expression that specifies the fixed value */
  1.3586 +            var->lbnd = expression_5(mpl);
  1.3587 +            if (var->lbnd->type == A_SYMBOLIC)
  1.3588 +               var->lbnd = make_unary(mpl, O_CVTNUM, var->lbnd,
  1.3589 +                  A_NUMERIC, 0);
  1.3590 +            if (var->lbnd->type != A_NUMERIC)
  1.3591 +               error(mpl, "expression following %s has invalid type",
  1.3592 +                  opstr);
  1.3593 +            xassert(var->lbnd->dim == 0);
  1.3594 +            /* indicate that the variable is fixed, not bounded */
  1.3595 +            var->ubnd = var->lbnd;
  1.3596 +         }
  1.3597 +         else if (mpl->token == T_LT || mpl->token == T_GT ||
  1.3598 +                  mpl->token == T_NE)
  1.3599 +            error(mpl, "strict bound not allowed");
  1.3600 +         else
  1.3601 +            error(mpl, "syntax error in variable statement");
  1.3602 +      }
  1.3603 +      /* close the domain scope */
  1.3604 +      if (var->domain != NULL) close_scope(mpl, var->domain);
  1.3605 +      /* the variable statement has been completely parsed */
  1.3606 +      xassert(mpl->token == T_SEMICOLON);
  1.3607 +      get_token(mpl /* ; */);
  1.3608 +      return var;
  1.3609 +}
  1.3610 +
  1.3611 +/*----------------------------------------------------------------------
  1.3612 +-- constraint_statement - parse constraint statement.
  1.3613 +--
  1.3614 +-- This routine parses constraint statement using the syntax:
  1.3615 +--
  1.3616 +-- <constraint statement> ::= <subject to> <symbolic name> <alias>
  1.3617 +--                            <domain> : <constraint> ;
  1.3618 +-- <subject to> ::= <empty>
  1.3619 +-- <subject to> ::= subject to
  1.3620 +-- <subject to> ::= subj to
  1.3621 +-- <subject to> ::= s.t.
  1.3622 +-- <alias> ::= <empty>
  1.3623 +-- <alias> ::= <string literal>
  1.3624 +-- <domain> ::= <empty>
  1.3625 +-- <domain> ::= <indexing expression>
  1.3626 +-- <constraint> ::= <formula> , >= <formula>
  1.3627 +-- <constraint> ::= <formula> , <= <formula>
  1.3628 +-- <constraint> ::= <formula> , = <formula>
  1.3629 +-- <constraint> ::= <formula> , <= <formula> , <= <formula>
  1.3630 +-- <constraint> ::= <formula> , >= <formula> , >= <formula>
  1.3631 +-- <formula> ::= <expression 5>
  1.3632 +--
  1.3633 +-- Commae in <constraint> are optional and may be omitted anywhere. */
  1.3634 +
  1.3635 +CONSTRAINT *constraint_statement(MPL *mpl)
  1.3636 +{     CONSTRAINT *con;
  1.3637 +      CODE *first, *second, *third;
  1.3638 +      int rho;
  1.3639 +      char opstr[8];
  1.3640 +      if (mpl->flag_s)
  1.3641 +         error(mpl, "constraint statement must precede solve statement")
  1.3642 +            ;
  1.3643 +      if (is_keyword(mpl, "subject"))
  1.3644 +      {  get_token(mpl /* subject */);
  1.3645 +         if (!is_keyword(mpl, "to"))
  1.3646 +            error(mpl, "keyword subject to incomplete");
  1.3647 +         get_token(mpl /* to */);
  1.3648 +      }
  1.3649 +      else if (is_keyword(mpl, "subj"))
  1.3650 +      {  get_token(mpl /* subj */);
  1.3651 +         if (!is_keyword(mpl, "to"))
  1.3652 +            error(mpl, "keyword subj to incomplete");
  1.3653 +         get_token(mpl /* to */);
  1.3654 +      }
  1.3655 +      else if (mpl->token == T_SPTP)
  1.3656 +         get_token(mpl /* s.t. */);
  1.3657 +      /* the current token must be symbolic name of constraint */
  1.3658 +      if (mpl->token == T_NAME)
  1.3659 +         ;
  1.3660 +      else if (is_reserved(mpl))
  1.3661 +         error(mpl, "invalid use of reserved keyword %s", mpl->image);
  1.3662 +      else
  1.3663 +         error(mpl, "symbolic name missing where expected");
  1.3664 +      /* there must be no other object with the same name */
  1.3665 +      if (avl_find_node(mpl->tree, mpl->image) != NULL)
  1.3666 +         error(mpl, "%s multiply declared", mpl->image);
  1.3667 +      /* create model constraint */
  1.3668 +      con = alloc(CONSTRAINT);
  1.3669 +      con->name = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1);
  1.3670 +      strcpy(con->name, mpl->image);
  1.3671 +      con->alias = NULL;
  1.3672 +      con->dim = 0;
  1.3673 +      con->domain = NULL;
  1.3674 +      con->type = A_CONSTRAINT;
  1.3675 +      con->code = NULL;
  1.3676 +      con->lbnd = NULL;
  1.3677 +      con->ubnd = NULL;
  1.3678 +      con->array = NULL;
  1.3679 +      get_token(mpl /* <symbolic name> */);
  1.3680 +      /* parse optional alias */
  1.3681 +      if (mpl->token == T_STRING)
  1.3682 +      {  con->alias = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1);
  1.3683 +         strcpy(con->alias, mpl->image);
  1.3684 +         get_token(mpl /* <string literal> */);
  1.3685 +      }
  1.3686 +      /* parse optional indexing expression */
  1.3687 +      if (mpl->token == T_LBRACE)
  1.3688 +      {  con->domain = indexing_expression(mpl);
  1.3689 +         con->dim = domain_arity(mpl, con->domain);
  1.3690 +      }
  1.3691 +      /* include the constraint name in the symbolic names table */
  1.3692 +      {  AVLNODE *node;
  1.3693 +         node = avl_insert_node(mpl->tree, con->name);
  1.3694 +         avl_set_node_type(node, A_CONSTRAINT);
  1.3695 +         avl_set_node_link(node, (void *)con);
  1.3696 +      }
  1.3697 +      /* the colon must precede the first expression */
  1.3698 +      if (mpl->token != T_COLON)
  1.3699 +         error(mpl, "colon missing where expected");
  1.3700 +      get_token(mpl /* : */);
  1.3701 +      /* parse the first expression */
  1.3702 +      first = expression_5(mpl);
  1.3703 +      if (first->type == A_SYMBOLIC)
  1.3704 +         first = make_unary(mpl, O_CVTNUM, first, A_NUMERIC, 0);
  1.3705 +      if (!(first->type == A_NUMERIC || first->type == A_FORMULA))
  1.3706 +         error(mpl, "expression following colon has invalid type");
  1.3707 +      xassert(first->dim == 0);
  1.3708 +      /* relational operator must follow the first expression */
  1.3709 +      if (mpl->token == T_COMMA) get_token(mpl /* , */);
  1.3710 +      switch (mpl->token)
  1.3711 +      {  case T_LE:
  1.3712 +         case T_GE:
  1.3713 +         case T_EQ:
  1.3714 +            break;
  1.3715 +         case T_LT:
  1.3716 +         case T_GT:
  1.3717 +         case T_NE:
  1.3718 +            error(mpl, "strict inequality not allowed");
  1.3719 +         case T_SEMICOLON:
  1.3720 +            error(mpl, "constraint must be equality or inequality");
  1.3721 +         default:
  1.3722 +            goto err;
  1.3723 +      }
  1.3724 +      rho = mpl->token;
  1.3725 +      strcpy(opstr, mpl->image);
  1.3726 +      xassert(strlen(opstr) < sizeof(opstr));
  1.3727 +      get_token(mpl /* rho */);
  1.3728 +      /* parse the second expression */
  1.3729 +      second = expression_5(mpl);
  1.3730 +      if (second->type == A_SYMBOLIC)
  1.3731 +         second = make_unary(mpl, O_CVTNUM, second, A_NUMERIC, 0);
  1.3732 +      if (!(second->type == A_NUMERIC || second->type == A_FORMULA))
  1.3733 +         error(mpl, "expression following %s has invalid type", opstr);
  1.3734 +      xassert(second->dim == 0);
  1.3735 +      /* check a token that follow the second expression */
  1.3736 +      if (mpl->token == T_COMMA)
  1.3737 +      {  get_token(mpl /* , */);
  1.3738 +         if (mpl->token == T_SEMICOLON) goto err;
  1.3739 +      }
  1.3740 +      if (mpl->token == T_LT || mpl->token == T_LE ||
  1.3741 +          mpl->token == T_EQ || mpl->token == T_GE ||
  1.3742 +          mpl->token == T_GT || mpl->token == T_NE)
  1.3743 +      {  /* it is another relational operator, therefore the constraint
  1.3744 +            is double inequality */
  1.3745 +         if (rho == T_EQ || mpl->token != rho)
  1.3746 +            error(mpl, "double inequality must be ... <= ... <= ... or "
  1.3747 +               "... >= ... >= ...");
  1.3748 +         /* the first expression cannot be linear form */
  1.3749 +         if (first->type == A_FORMULA)
  1.3750 +            error(mpl, "leftmost expression in double inequality cannot"
  1.3751 +               " be linear form");
  1.3752 +         get_token(mpl /* rho */);
  1.3753 +         /* parse the third expression */
  1.3754 +         third = expression_5(mpl);
  1.3755 +         if (third->type == A_SYMBOLIC)
  1.3756 +            third = make_unary(mpl, O_CVTNUM, second, A_NUMERIC, 0);
  1.3757 +         if (!(third->type == A_NUMERIC || third->type == A_FORMULA))
  1.3758 +            error(mpl, "rightmost expression in double inequality const"
  1.3759 +               "raint has invalid type");
  1.3760 +         xassert(third->dim == 0);
  1.3761 +         /* the third expression also cannot be linear form */
  1.3762 +         if (third->type == A_FORMULA)
  1.3763 +            error(mpl, "rightmost expression in double inequality canno"
  1.3764 +               "t be linear form");
  1.3765 +      }
  1.3766 +      else
  1.3767 +      {  /* the constraint is equality or single inequality */
  1.3768 +         third = NULL;
  1.3769 +      }
  1.3770 +      /* close the domain scope */
  1.3771 +      if (con->domain != NULL) close_scope(mpl, con->domain);
  1.3772 +      /* convert all expressions to linear form, if necessary */
  1.3773 +      if (first->type != A_FORMULA)
  1.3774 +         first = make_unary(mpl, O_CVTLFM, first, A_FORMULA, 0);
  1.3775 +      if (second->type != A_FORMULA)
  1.3776 +         second = make_unary(mpl, O_CVTLFM, second, A_FORMULA, 0);
  1.3777 +      if (third != NULL)
  1.3778 +         third = make_unary(mpl, O_CVTLFM, third, A_FORMULA, 0);
  1.3779 +      /* arrange expressions in the constraint */
  1.3780 +      if (third == NULL)
  1.3781 +      {  /* the constraint is equality or single inequality */
  1.3782 +         switch (rho)
  1.3783 +         {  case T_LE:
  1.3784 +               /* first <= second */
  1.3785 +               con->code = first;
  1.3786 +               con->lbnd = NULL;
  1.3787 +               con->ubnd = second;
  1.3788 +               break;
  1.3789 +            case T_GE:
  1.3790 +               /* first >= second */
  1.3791 +               con->code = first;
  1.3792 +               con->lbnd = second;
  1.3793 +               con->ubnd = NULL;
  1.3794 +               break;
  1.3795 +            case T_EQ:
  1.3796 +               /* first = second */
  1.3797 +               con->code = first;
  1.3798 +               con->lbnd = second;
  1.3799 +               con->ubnd = second;
  1.3800 +               break;
  1.3801 +            default:
  1.3802 +               xassert(rho != rho);
  1.3803 +         }
  1.3804 +      }
  1.3805 +      else
  1.3806 +      {  /* the constraint is double inequality */
  1.3807 +         switch (rho)
  1.3808 +         {  case T_LE:
  1.3809 +               /* first <= second <= third */
  1.3810 +               con->code = second;
  1.3811 +               con->lbnd = first;
  1.3812 +               con->ubnd = third;
  1.3813 +               break;
  1.3814 +            case T_GE:
  1.3815 +               /* first >= second >= third */
  1.3816 +               con->code = second;
  1.3817 +               con->lbnd = third;
  1.3818 +               con->ubnd = first;
  1.3819 +               break;
  1.3820 +            default:
  1.3821 +               xassert(rho != rho);
  1.3822 +         }
  1.3823 +      }
  1.3824 +      /* the constraint statement has been completely parsed */
  1.3825 +      if (mpl->token != T_SEMICOLON)
  1.3826 +err:     error(mpl, "syntax error in constraint statement");
  1.3827 +      get_token(mpl /* ; */);
  1.3828 +      return con;
  1.3829 +}
  1.3830 +
  1.3831 +/*----------------------------------------------------------------------
  1.3832 +-- objective_statement - parse objective statement.
  1.3833 +--
  1.3834 +-- This routine parses objective statement using the syntax:
  1.3835 +--
  1.3836 +-- <objective statement> ::= <verb> <symbolic name> <alias> <domain> :
  1.3837 +--                           <formula> ;
  1.3838 +-- <verb> ::= minimize
  1.3839 +-- <verb> ::= maximize
  1.3840 +-- <alias> ::= <empty>
  1.3841 +-- <alias> ::= <string literal>
  1.3842 +-- <domain> ::= <empty>
  1.3843 +-- <domain> ::= <indexing expression>
  1.3844 +-- <formula> ::= <expression 5> */
  1.3845 +
  1.3846 +CONSTRAINT *objective_statement(MPL *mpl)
  1.3847 +{     CONSTRAINT *obj;
  1.3848 +      int type;
  1.3849 +      if (is_keyword(mpl, "minimize"))
  1.3850 +         type = A_MINIMIZE;
  1.3851 +      else if (is_keyword(mpl, "maximize"))
  1.3852 +         type = A_MAXIMIZE;
  1.3853 +      else
  1.3854 +         xassert(mpl != mpl);
  1.3855 +      if (mpl->flag_s)
  1.3856 +         error(mpl, "objective statement must precede solve statement");
  1.3857 +      get_token(mpl /* minimize | maximize */);
  1.3858 +      /* symbolic name must follow the verb 'minimize' or 'maximize' */
  1.3859 +      if (mpl->token == T_NAME)
  1.3860 +         ;
  1.3861 +      else if (is_reserved(mpl))
  1.3862 +         error(mpl, "invalid use of reserved keyword %s", mpl->image);
  1.3863 +      else
  1.3864 +         error(mpl, "symbolic name missing where expected");
  1.3865 +      /* there must be no other object with the same name */
  1.3866 +      if (avl_find_node(mpl->tree, mpl->image) != NULL)
  1.3867 +         error(mpl, "%s multiply declared", mpl->image);
  1.3868 +      /* create model objective */
  1.3869 +      obj = alloc(CONSTRAINT);
  1.3870 +      obj->name = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1);
  1.3871 +      strcpy(obj->name, mpl->image);
  1.3872 +      obj->alias = NULL;
  1.3873 +      obj->dim = 0;
  1.3874 +      obj->domain = NULL;
  1.3875 +      obj->type = type;
  1.3876 +      obj->code = NULL;
  1.3877 +      obj->lbnd = NULL;
  1.3878 +      obj->ubnd = NULL;
  1.3879 +      obj->array = NULL;
  1.3880 +      get_token(mpl /* <symbolic name> */);
  1.3881 +      /* parse optional alias */
  1.3882 +      if (mpl->token == T_STRING)
  1.3883 +      {  obj->alias = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1);
  1.3884 +         strcpy(obj->alias, mpl->image);
  1.3885 +         get_token(mpl /* <string literal> */);
  1.3886 +      }
  1.3887 +      /* parse optional indexing expression */
  1.3888 +      if (mpl->token == T_LBRACE)
  1.3889 +      {  obj->domain = indexing_expression(mpl);
  1.3890 +         obj->dim = domain_arity(mpl, obj->domain);
  1.3891 +      }
  1.3892 +      /* include the constraint name in the symbolic names table */
  1.3893 +      {  AVLNODE *node;
  1.3894 +         node = avl_insert_node(mpl->tree, obj->name);
  1.3895 +         avl_set_node_type(node, A_CONSTRAINT);
  1.3896 +         avl_set_node_link(node, (void *)obj);
  1.3897 +      }
  1.3898 +      /* the colon must precede the objective expression */
  1.3899 +      if (mpl->token != T_COLON)
  1.3900 +         error(mpl, "colon missing where expected");
  1.3901 +      get_token(mpl /* : */);
  1.3902 +      /* parse the objective expression */
  1.3903 +      obj->code = expression_5(mpl);
  1.3904 +      if (obj->code->type == A_SYMBOLIC)
  1.3905 +         obj->code = make_unary(mpl, O_CVTNUM, obj->code, A_NUMERIC, 0);
  1.3906 +      if (obj->code->type == A_NUMERIC)
  1.3907 +         obj->code = make_unary(mpl, O_CVTLFM, obj->code, A_FORMULA, 0);
  1.3908 +      if (obj->code->type != A_FORMULA)
  1.3909 +         error(mpl, "expression following colon has invalid type");
  1.3910 +      xassert(obj->code->dim == 0);
  1.3911 +      /* close the domain scope */
  1.3912 +      if (obj->domain != NULL) close_scope(mpl, obj->domain);
  1.3913 +      /* the objective statement has been completely parsed */
  1.3914 +      if (mpl->token != T_SEMICOLON)
  1.3915 +         error(mpl, "syntax error in objective statement");
  1.3916 +      get_token(mpl /* ; */);
  1.3917 +      return obj;
  1.3918 +}
  1.3919 +
  1.3920 +#if 1 /* 11/II-2008 */
  1.3921 +/***********************************************************************
  1.3922 +*  table_statement - parse table statement
  1.3923 +*
  1.3924 +*  This routine parses table statement using the syntax:
  1.3925 +*
  1.3926 +*  <table statement> ::= <input table statement>
  1.3927 +*  <table statement> ::= <output table statement>
  1.3928 +*
  1.3929 +*  <input table statement> ::=
  1.3930 +*        table <table name> <alias> IN <argument list> :
  1.3931 +*        <input set> [ <field list> ] , <input list> ;
  1.3932 +*  <alias> ::= <empty>
  1.3933 +*  <alias> ::= <string literal>
  1.3934 +*  <argument list> ::= <expression 5>
  1.3935 +*  <argument list> ::= <argument list> <expression 5>
  1.3936 +*  <argument list> ::= <argument list> , <expression 5>
  1.3937 +*  <input set> ::= <empty>
  1.3938 +*  <input set> ::= <set name> <-
  1.3939 +*  <field list> ::= <field name>
  1.3940 +*  <field list> ::= <field list> , <field name>
  1.3941 +*  <input list> ::= <input item>
  1.3942 +*  <input list> ::= <input list> , <input item>
  1.3943 +*  <input item> ::= <parameter name>
  1.3944 +*  <input item> ::= <parameter name> ~ <field name>
  1.3945 +*
  1.3946 +*  <output table statement> ::=
  1.3947 +*        table <table name> <alias> <domain> OUT <argument list> :
  1.3948 +*        <output list> ;
  1.3949 +*  <domain> ::= <indexing expression>
  1.3950 +*  <output list> ::= <output item>
  1.3951 +*  <output list> ::= <output list> , <output item>
  1.3952 +*  <output item> ::= <expression 5>
  1.3953 +*  <output item> ::= <expression 5> ~ <field name> */
  1.3954 +
  1.3955 +TABLE *table_statement(MPL *mpl)
  1.3956 +{     TABLE *tab;
  1.3957 +      TABARG *last_arg, *arg;
  1.3958 +      TABFLD *last_fld, *fld;
  1.3959 +      TABIN *last_in, *in;
  1.3960 +      TABOUT *last_out, *out;
  1.3961 +      AVLNODE *node;
  1.3962 +      int nflds;
  1.3963 +      char name[MAX_LENGTH+1];
  1.3964 +      xassert(is_keyword(mpl, "table"));
  1.3965 +      get_token(mpl /* solve */);
  1.3966 +      /* symbolic name must follow the keyword table */
  1.3967 +      if (mpl->token == T_NAME)
  1.3968 +         ;
  1.3969 +      else if (is_reserved(mpl))
  1.3970 +         error(mpl, "invalid use of reserved keyword %s", mpl->image);
  1.3971 +      else
  1.3972 +         error(mpl, "symbolic name missing where expected");
  1.3973 +      /* there must be no other object with the same name */
  1.3974 +      if (avl_find_node(mpl->tree, mpl->image) != NULL)
  1.3975 +         error(mpl, "%s multiply declared", mpl->image);
  1.3976 +      /* create data table */
  1.3977 +      tab = alloc(TABLE);
  1.3978 +      tab->name = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1);
  1.3979 +      strcpy(tab->name, mpl->image);
  1.3980 +      get_token(mpl /* <symbolic name> */);
  1.3981 +      /* parse optional alias */
  1.3982 +      if (mpl->token == T_STRING)
  1.3983 +      {  tab->alias = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1);
  1.3984 +         strcpy(tab->alias, mpl->image);
  1.3985 +         get_token(mpl /* <string literal> */);
  1.3986 +      }
  1.3987 +      else
  1.3988 +         tab->alias = NULL;
  1.3989 +      /* parse optional indexing expression */
  1.3990 +      if (mpl->token == T_LBRACE)
  1.3991 +      {  /* this is output table */
  1.3992 +         tab->type = A_OUTPUT;
  1.3993 +         tab->u.out.domain = indexing_expression(mpl);
  1.3994 +         if (!is_keyword(mpl, "OUT"))
  1.3995 +            error(mpl, "keyword OUT missing where expected");
  1.3996 +         get_token(mpl /* OUT */);
  1.3997 +      }
  1.3998 +      else
  1.3999 +      {  /* this is input table */
  1.4000 +         tab->type = A_INPUT;
  1.4001 +         if (!is_keyword(mpl, "IN"))
  1.4002 +            error(mpl, "keyword IN missing where expected");
  1.4003 +         get_token(mpl /* IN */);
  1.4004 +      }
  1.4005 +      /* parse argument list */
  1.4006 +      tab->arg = last_arg = NULL;
  1.4007 +      for (;;)
  1.4008 +      {  /* create argument list entry */
  1.4009 +         arg = alloc(TABARG);
  1.4010 +         /* parse argument expression */
  1.4011 +         if (mpl->token == T_COMMA || mpl->token == T_COLON ||
  1.4012 +             mpl->token == T_SEMICOLON)
  1.4013 +            error(mpl, "argument expression missing where expected");
  1.4014 +         arg->code = expression_5(mpl);
  1.4015 +         /* convert the result to symbolic type, if necessary */
  1.4016 +         if (arg->code->type == A_NUMERIC)
  1.4017 +            arg->code =
  1.4018 +               make_unary(mpl, O_CVTSYM, arg->code, A_SYMBOLIC, 0);
  1.4019 +         /* check that now the result is of symbolic type */
  1.4020 +         if (arg->code->type != A_SYMBOLIC)
  1.4021 +            error(mpl, "argument expression has invalid type");
  1.4022 +         /* add the entry to the end of the list */
  1.4023 +         arg->next = NULL;
  1.4024 +         if (last_arg == NULL)
  1.4025 +            tab->arg = arg;
  1.4026 +         else
  1.4027 +            last_arg->next = arg;
  1.4028 +         last_arg = arg;
  1.4029 +         /* argument expression has been parsed */
  1.4030 +         if (mpl->token == T_COMMA)
  1.4031 +            get_token(mpl /* , */);
  1.4032 +         else if (mpl->token == T_COLON || mpl->token == T_SEMICOLON)
  1.4033 +            break;
  1.4034 +      }
  1.4035 +      xassert(tab->arg != NULL);
  1.4036 +      /* argument list must end with colon */
  1.4037 +      if (mpl->token == T_COLON)
  1.4038 +         get_token(mpl /* : */);
  1.4039 +      else
  1.4040 +         error(mpl, "colon missing where expected");
  1.4041 +      /* parse specific part of the table statement */
  1.4042 +      switch (tab->type)
  1.4043 +      {  case A_INPUT:  goto input_table;
  1.4044 +         case A_OUTPUT: goto output_table;
  1.4045 +         default:       xassert(tab != tab);
  1.4046 +      }
  1.4047 +input_table:
  1.4048 +      /* parse optional set name */
  1.4049 +      if (mpl->token == T_NAME)
  1.4050 +      {  node = avl_find_node(mpl->tree, mpl->image);
  1.4051 +         if (node == NULL)
  1.4052 +            error(mpl, "%s not defined", mpl->image);
  1.4053 +         if (avl_get_node_type(node) != A_SET)
  1.4054 +            error(mpl, "%s not a set", mpl->image);
  1.4055 +         tab->u.in.set = (SET *)avl_get_node_link(node);
  1.4056 +         if (tab->u.in.set->assign != NULL)
  1.4057 +            error(mpl, "%s needs no data", mpl->image);
  1.4058 +         if (tab->u.in.set->dim != 0)
  1.4059 +            error(mpl, "%s must be a simple set", mpl->image);
  1.4060 +         get_token(mpl /* <symbolic name> */);
  1.4061 +         if (mpl->token == T_INPUT)
  1.4062 +            get_token(mpl /* <- */);
  1.4063 +         else
  1.4064 +            error(mpl, "delimiter <- missing where expected");
  1.4065 +      }
  1.4066 +      else if (is_reserved(mpl))
  1.4067 +         error(mpl, "invalid use of reserved keyword %s", mpl->image);
  1.4068 +      else
  1.4069 +         tab->u.in.set = NULL;
  1.4070 +      /* parse field list */
  1.4071 +      tab->u.in.fld = last_fld = NULL;
  1.4072 +      nflds = 0;
  1.4073 +      if (mpl->token == T_LBRACKET)
  1.4074 +         get_token(mpl /* [ */);
  1.4075 +      else
  1.4076 +         error(mpl, "field list missing where expected");
  1.4077 +      for (;;)
  1.4078 +      {  /* create field list entry */
  1.4079 +         fld = alloc(TABFLD);
  1.4080 +         /* parse field name */
  1.4081 +         if (mpl->token == T_NAME)
  1.4082 +            ;
  1.4083 +         else if (is_reserved(mpl))
  1.4084 +            error(mpl,
  1.4085 +               "invalid use of reserved keyword %s", mpl->image);
  1.4086 +         else
  1.4087 +            error(mpl, "field name missing where expected");
  1.4088 +         fld->name = dmp_get_atomv(mpl->pool, strlen(mpl->image)+1);
  1.4089 +         strcpy(fld->name, mpl->image);
  1.4090 +         get_token(mpl /* <symbolic name> */);
  1.4091 +         /* add the entry to the end of the list */
  1.4092 +         fld->next = NULL;
  1.4093 +         if (last_fld == NULL)
  1.4094 +            tab->u.in.fld = fld;
  1.4095 +         else
  1.4096 +            last_fld->next = fld;
  1.4097 +         last_fld = fld;
  1.4098 +         nflds++;
  1.4099 +         /* field name has been parsed */
  1.4100 +         if (mpl->token == T_COMMA)
  1.4101 +            get_token(mpl /* , */);
  1.4102 +         else if (mpl->token == T_RBRACKET)
  1.4103 +            break;
  1.4104 +         else
  1.4105 +            error(mpl, "syntax error in field list");
  1.4106 +      }
  1.4107 +      /* check that the set dimen is equal to the number of fields */
  1.4108 +      if (tab->u.in.set != NULL && tab->u.in.set->dimen != nflds)
  1.4109 +         error(mpl, "there must be %d field%s rather than %d",
  1.4110 +            tab->u.in.set->dimen, tab->u.in.set->dimen == 1 ? "" : "s",
  1.4111 +            nflds);
  1.4112 +      get_token(mpl /* ] */);
  1.4113 +      /* parse optional input list */
  1.4114 +      tab->u.in.list = last_in = NULL;
  1.4115 +      while (mpl->token == T_COMMA)
  1.4116 +      {  get_token(mpl /* , */);
  1.4117 +         /* create input list entry */
  1.4118 +         in = alloc(TABIN);
  1.4119 +         /* parse parameter name */
  1.4120 +         if (mpl->token == T_NAME)
  1.4121 +            ;
  1.4122 +         else if (is_reserved(mpl))
  1.4123 +            error(mpl,
  1.4124 +               "invalid use of reserved keyword %s", mpl->image);
  1.4125 +         else
  1.4126 +            error(mpl, "parameter name missing where expected");
  1.4127 +         node = avl_find_node(mpl->tree, mpl->image);
  1.4128 +         if (node == NULL)
  1.4129 +            error(mpl, "%s not defined", mpl->image);
  1.4130 +         if (avl_get_node_type(node) != A_PARAMETER)
  1.4131 +            error(mpl, "%s not a parameter", mpl->image);
  1.4132 +         in->par = (PARAMETER *)avl_get_node_link(node);
  1.4133 +         if (in->par->dim != nflds)
  1.4134 +            error(mpl, "%s must have %d subscript%s rather than %d",
  1.4135 +               mpl->image, nflds, nflds == 1 ? "" : "s", in->par->dim);
  1.4136 +         if (in->par->assign != NULL)
  1.4137 +            error(mpl, "%s needs no data", mpl->image);
  1.4138 +         get_token(mpl /* <symbolic name> */);
  1.4139 +         /* parse optional field name */
  1.4140 +         if (mpl->token == T_TILDE)
  1.4141 +         {  get_token(mpl /* ~ */);
  1.4142 +            /* parse field name */
  1.4143 +            if (mpl->token == T_NAME)
  1.4144 +               ;
  1.4145 +            else if (is_reserved(mpl))
  1.4146 +               error(mpl,
  1.4147 +                  "invalid use of reserved keyword %s", mpl->image);
  1.4148 +            else
  1.4149 +               error(mpl, "field name missing where expected");
  1.4150 +            xassert(strlen(mpl->image) < sizeof(name));
  1.4151 +            strcpy(name, mpl->image);
  1.4152 +            get_token(mpl /* <symbolic name> */);
  1.4153 +         }
  1.4154 +         else
  1.4155 +         {  /* field name is the same as the parameter name */
  1.4156 +            xassert(strlen(in->par->name) < sizeof(name));
  1.4157 +            strcpy(name, in->par->name);
  1.4158 +         }
  1.4159 +         /* assign field name */
  1.4160 +         in->name = dmp_get_atomv(mpl->pool, strlen(name)+1);
  1.4161 +         strcpy(in->name, name);
  1.4162 +         /* add the entry to the end of the list */
  1.4163 +         in->next = NULL;
  1.4164 +         if (last_in == NULL)
  1.4165 +            tab->u.in.list = in;
  1.4166 +         else
  1.4167 +            last_in->next = in;
  1.4168 +         last_in = in;
  1.4169 +      }
  1.4170 +      goto end_of_table;
  1.4171 +output_table:
  1.4172 +      /* parse output list */
  1.4173 +      tab->u.out.list = last_out = NULL;
  1.4174 +      for (;;)
  1.4175 +      {  /* create output list entry */
  1.4176 +         out = alloc(TABOUT);
  1.4177 +         /* parse expression */
  1.4178 +         if (mpl->token == T_COMMA || mpl->token == T_SEMICOLON)
  1.4179 +            error(mpl, "expression missing where expected");
  1.4180 +         if (mpl->token == T_NAME)
  1.4181 +         {  xassert(strlen(mpl->image) < sizeof(name));
  1.4182 +            strcpy(name, mpl->image);
  1.4183 +         }
  1.4184 +         else
  1.4185 +            name[0] = '\0';
  1.4186 +         out->code = expression_5(mpl);
  1.4187 +         /* parse optional field name */
  1.4188 +         if (mpl->token == T_TILDE)
  1.4189 +         {  get_token(mpl /* ~ */);
  1.4190 +            /* parse field name */
  1.4191 +            if (mpl->token == T_NAME)
  1.4192 +               ;
  1.4193 +            else if (is_reserved(mpl))
  1.4194 +               error(mpl,
  1.4195 +                  "invalid use of reserved keyword %s", mpl->image);
  1.4196 +            else
  1.4197 +               error(mpl, "field name missing where expected");
  1.4198 +            xassert(strlen(mpl->image) < sizeof(name));
  1.4199 +            strcpy(name, mpl->image);
  1.4200 +            get_token(mpl /* <symbolic name> */);
  1.4201 +         }
  1.4202 +         /* assign field name */
  1.4203 +         if (name[0] == '\0')
  1.4204 +            error(mpl, "field name required");
  1.4205 +         out->name = dmp_get_atomv(mpl->pool, strlen(name)+1);
  1.4206 +         strcpy(out->name, name);
  1.4207 +         /* add the entry to the end of the list */
  1.4208 +         out->next = NULL;
  1.4209 +         if (last_out == NULL)
  1.4210 +            tab->u.out.list = out;
  1.4211 +         else
  1.4212 +            last_out->next = out;
  1.4213 +         last_out = out;
  1.4214 +         /* output item has been parsed */
  1.4215 +         if (mpl->token == T_COMMA)
  1.4216 +            get_token(mpl /* , */);
  1.4217 +         else if (mpl->token == T_SEMICOLON)
  1.4218 +            break;
  1.4219 +         else
  1.4220 +            error(mpl, "syntax error in output list");
  1.4221 +      }
  1.4222 +      /* close the domain scope */
  1.4223 +      close_scope(mpl,tab->u.out.domain);
  1.4224 +end_of_table:
  1.4225 +      /* the table statement must end with semicolon */
  1.4226 +      if (mpl->token != T_SEMICOLON)
  1.4227 +         error(mpl, "syntax error in table statement");
  1.4228 +      get_token(mpl /* ; */);
  1.4229 +      return tab;
  1.4230 +}
  1.4231 +#endif
  1.4232 +
  1.4233 +/*----------------------------------------------------------------------
  1.4234 +-- solve_statement - parse solve statement.
  1.4235 +--
  1.4236 +-- This routine parses solve statement using the syntax:
  1.4237 +--
  1.4238 +-- <solve statement> ::= solve ;
  1.4239 +--
  1.4240 +-- The solve statement can be used at most once. */
  1.4241 +
  1.4242 +void *solve_statement(MPL *mpl)
  1.4243 +{     xassert(is_keyword(mpl, "solve"));
  1.4244 +      if (mpl->flag_s)
  1.4245 +         error(mpl, "at most one solve statement allowed");
  1.4246 +      mpl->flag_s = 1;
  1.4247 +      get_token(mpl /* solve */);
  1.4248 +      /* semicolon must follow solve statement */
  1.4249 +      if (mpl->token != T_SEMICOLON)
  1.4250 +         error(mpl, "syntax error in solve statement");
  1.4251 +      get_token(mpl /* ; */);
  1.4252 +      return NULL;
  1.4253 +}
  1.4254 +
  1.4255 +/*----------------------------------------------------------------------
  1.4256 +-- check_statement - parse check statement.
  1.4257 +--
  1.4258 +-- This routine parses check statement using the syntax:
  1.4259 +--
  1.4260 +-- <check statement> ::= check <domain> : <expression 13> ;
  1.4261 +-- <domain> ::= <empty>
  1.4262 +-- <domain> ::= <indexing expression>
  1.4263 +--
  1.4264 +-- If <domain> is omitted, colon following it may also be omitted. */
  1.4265 +
  1.4266 +CHECK *check_statement(MPL *mpl)
  1.4267 +{     CHECK *chk;
  1.4268 +      xassert(is_keyword(mpl, "check"));
  1.4269 +      /* create check descriptor */
  1.4270 +      chk = alloc(CHECK);
  1.4271 +      chk->domain = NULL;
  1.4272 +      chk->code = NULL;
  1.4273 +      get_token(mpl /* check */);
  1.4274 +      /* parse optional indexing expression */
  1.4275 +      if (mpl->token == T_LBRACE)
  1.4276 +      {  chk->domain = indexing_expression(mpl);
  1.4277 +#if 0
  1.4278 +         if (mpl->token != T_COLON)
  1.4279 +            error(mpl, "colon missing where expected");
  1.4280 +#endif
  1.4281 +      }
  1.4282 +      /* skip optional colon */
  1.4283 +      if (mpl->token == T_COLON) get_token(mpl /* : */);
  1.4284 +      /* parse logical expression */
  1.4285 +      chk->code = expression_13(mpl);
  1.4286 +      if (chk->code->type != A_LOGICAL)
  1.4287 +         error(mpl, "expression has invalid type");
  1.4288 +      xassert(chk->code->dim == 0);
  1.4289 +      /* close the domain scope */
  1.4290 +      if (chk->domain != NULL) close_scope(mpl, chk->domain);
  1.4291 +      /* the check statement has been completely parsed */
  1.4292 +      if (mpl->token != T_SEMICOLON)
  1.4293 +         error(mpl, "syntax error in check statement");
  1.4294 +      get_token(mpl /* ; */);
  1.4295 +      return chk;
  1.4296 +}
  1.4297 +
  1.4298 +#if 1 /* 15/V-2010 */
  1.4299 +/*----------------------------------------------------------------------
  1.4300 +-- display_statement - parse display statement.
  1.4301 +--
  1.4302 +-- This routine parses display statement using the syntax:
  1.4303 +--
  1.4304 +-- <display statement> ::= display <domain> : <display list> ;
  1.4305 +-- <display statement> ::= display <domain> <display list> ;
  1.4306 +-- <domain> ::= <empty>
  1.4307 +-- <domain> ::= <indexing expression>
  1.4308 +-- <display list> ::= <display entry>
  1.4309 +-- <display list> ::= <display list> , <display entry>
  1.4310 +-- <display entry> ::= <dummy index>
  1.4311 +-- <display entry> ::= <set name>
  1.4312 +-- <display entry> ::= <set name> [ <subscript list> ]
  1.4313 +-- <display entry> ::= <parameter name>
  1.4314 +-- <display entry> ::= <parameter name> [ <subscript list> ]
  1.4315 +-- <display entry> ::= <variable name>
  1.4316 +-- <display entry> ::= <variable name> [ <subscript list> ]
  1.4317 +-- <display entry> ::= <constraint name>
  1.4318 +-- <display entry> ::= <constraint name> [ <subscript list> ]
  1.4319 +-- <display entry> ::= <expression 13> */
  1.4320 +
  1.4321 +DISPLAY *display_statement(MPL *mpl)
  1.4322 +{     DISPLAY *dpy;
  1.4323 +      DISPLAY1 *entry, *last_entry;
  1.4324 +      xassert(is_keyword(mpl, "display"));
  1.4325 +      /* create display descriptor */
  1.4326 +      dpy = alloc(DISPLAY);
  1.4327 +      dpy->domain = NULL;
  1.4328 +      dpy->list = last_entry = NULL;
  1.4329 +      get_token(mpl /* display */);
  1.4330 +      /* parse optional indexing expression */
  1.4331 +      if (mpl->token == T_LBRACE)
  1.4332 +         dpy->domain = indexing_expression(mpl);
  1.4333 +      /* skip optional colon */
  1.4334 +      if (mpl->token == T_COLON) get_token(mpl /* : */);
  1.4335 +      /* parse display list */
  1.4336 +      for (;;)
  1.4337 +      {  /* create new display entry */
  1.4338 +         entry = alloc(DISPLAY1);
  1.4339 +         entry->type = 0;
  1.4340 +         entry->next = NULL;
  1.4341 +         /* and append it to the display list */
  1.4342 +         if (dpy->list == NULL)
  1.4343 +            dpy->list = entry;
  1.4344 +         else
  1.4345 +            last_entry->next = entry;
  1.4346 +         last_entry = entry;
  1.4347 +         /* parse display entry */
  1.4348 +         if (mpl->token == T_NAME)
  1.4349 +         {  AVLNODE *node;
  1.4350 +            int next_token;
  1.4351 +            get_token(mpl /* <symbolic name> */);
  1.4352 +            next_token = mpl->token;
  1.4353 +            unget_token(mpl);
  1.4354 +            if (!(next_token == T_COMMA || next_token == T_SEMICOLON))
  1.4355 +            {  /* symbolic name begins expression */
  1.4356 +               goto expr;
  1.4357 +            }
  1.4358 +            /* display entry is dummy index or model object */
  1.4359 +            node = avl_find_node(mpl->tree, mpl->image);
  1.4360 +            if (node == NULL)
  1.4361 +               error(mpl, "%s not defined", mpl->image);
  1.4362 +            entry->type = avl_get_node_type(node);
  1.4363 +            switch (avl_get_node_type(node))
  1.4364 +            {  case A_INDEX:
  1.4365 +                  entry->u.slot =
  1.4366 +                     (DOMAIN_SLOT *)avl_get_node_link(node);
  1.4367 +                  break;
  1.4368 +               case A_SET:
  1.4369 +                  entry->u.set = (SET *)avl_get_node_link(node);
  1.4370 +                  break;
  1.4371 +               case A_PARAMETER:
  1.4372 +                  entry->u.par = (PARAMETER *)avl_get_node_link(node);
  1.4373 +                  break;
  1.4374 +               case A_VARIABLE:
  1.4375 +                  entry->u.var = (VARIABLE *)avl_get_node_link(node);
  1.4376 +                  if (!mpl->flag_s)
  1.4377 +                     error(mpl, "invalid reference to variable %s above"
  1.4378 +                        " solve statement", entry->u.var->name);
  1.4379 +                  break;
  1.4380 +               case A_CONSTRAINT:
  1.4381 +                  entry->u.con = (CONSTRAINT *)avl_get_node_link(node);
  1.4382 +                  if (!mpl->flag_s)
  1.4383 +                     error(mpl, "invalid reference to %s %s above solve"
  1.4384 +                        " statement",
  1.4385 +                        entry->u.con->type == A_CONSTRAINT ?
  1.4386 +                        "constraint" : "objective", entry->u.con->name);
  1.4387 +                  break;
  1.4388 +               default:
  1.4389 +                  xassert(node != node);
  1.4390 +            }
  1.4391 +            get_token(mpl /* <symbolic name> */);
  1.4392 +         }
  1.4393 +         else
  1.4394 +expr:    {  /* display entry is expression */
  1.4395 +            entry->type = A_EXPRESSION;
  1.4396 +            entry->u.code = expression_13(mpl);
  1.4397 +         }
  1.4398 +         /* check a token that follows the entry parsed */
  1.4399 +         if (mpl->token == T_COMMA)
  1.4400 +            get_token(mpl /* , */);
  1.4401 +         else
  1.4402 +            break;
  1.4403 +      }
  1.4404 +      /* close the domain scope */
  1.4405 +      if (dpy->domain != NULL) close_scope(mpl, dpy->domain);
  1.4406 +      /* the display statement has been completely parsed */
  1.4407 +      if (mpl->token != T_SEMICOLON)
  1.4408 +         error(mpl, "syntax error in display statement");
  1.4409 +      get_token(mpl /* ; */);
  1.4410 +      return dpy;
  1.4411 +}
  1.4412 +#endif
  1.4413 +
  1.4414 +/*----------------------------------------------------------------------
  1.4415 +-- printf_statement - parse printf statement.
  1.4416 +--
  1.4417 +-- This routine parses print statement using the syntax:
  1.4418 +--
  1.4419 +-- <printf statement> ::= <printf clause> ;
  1.4420 +-- <printf statement> ::= <printf clause> > <file name> ;
  1.4421 +-- <printf statement> ::= <printf clause> >> <file name> ;
  1.4422 +-- <printf clause> ::= printf <domain> : <format> <printf list>
  1.4423 +-- <printf clause> ::= printf <domain> <format> <printf list>
  1.4424 +-- <domain> ::= <empty>
  1.4425 +-- <domain> ::= <indexing expression>
  1.4426 +-- <format> ::= <expression 5>
  1.4427 +-- <printf list> ::= <empty>
  1.4428 +-- <printf list> ::= <printf list> , <printf entry>
  1.4429 +-- <printf entry> ::= <expression 9>
  1.4430 +-- <file name> ::= <expression 5> */
  1.4431 +
  1.4432 +PRINTF *printf_statement(MPL *mpl)
  1.4433 +{     PRINTF *prt;
  1.4434 +      PRINTF1 *entry, *last_entry;
  1.4435 +      xassert(is_keyword(mpl, "printf"));
  1.4436 +      /* create printf descriptor */
  1.4437 +      prt = alloc(PRINTF);
  1.4438 +      prt->domain = NULL;
  1.4439 +      prt->fmt = NULL;
  1.4440 +      prt->list = last_entry = NULL;
  1.4441 +      get_token(mpl /* printf */);
  1.4442 +      /* parse optional indexing expression */
  1.4443 +      if (mpl->token == T_LBRACE)
  1.4444 +      {  prt->domain = indexing_expression(mpl);
  1.4445 +#if 0
  1.4446 +         if (mpl->token != T_COLON)
  1.4447 +            error(mpl, "colon missing where expected");
  1.4448 +#endif
  1.4449 +      }
  1.4450 +      /* skip optional colon */
  1.4451 +      if (mpl->token == T_COLON) get_token(mpl /* : */);
  1.4452 +      /* parse expression for format string */
  1.4453 +      prt->fmt = expression_5(mpl);
  1.4454 +      /* convert it to symbolic type, if necessary */
  1.4455 +      if (prt->fmt->type == A_NUMERIC)
  1.4456 +         prt->fmt = make_unary(mpl, O_CVTSYM, prt->fmt, A_SYMBOLIC, 0);
  1.4457 +      /* check that now the expression is of symbolic type */
  1.4458 +      if (prt->fmt->type != A_SYMBOLIC)
  1.4459 +         error(mpl, "format expression has invalid type");
  1.4460 +      /* parse printf list */
  1.4461 +      while (mpl->token == T_COMMA)
  1.4462 +      {  get_token(mpl /* , */);
  1.4463 +         /* create new printf entry */
  1.4464 +         entry = alloc(PRINTF1);
  1.4465 +         entry->code = NULL;
  1.4466 +         entry->next = NULL;
  1.4467 +         /* and append it to the printf list */
  1.4468 +         if (prt->list == NULL)
  1.4469 +            prt->list = entry;
  1.4470 +         else
  1.4471 +            last_entry->next = entry;
  1.4472 +         last_entry = entry;
  1.4473 +         /* parse printf entry */
  1.4474 +         entry->code = expression_9(mpl);
  1.4475 +         if (!(entry->code->type == A_NUMERIC ||
  1.4476 +               entry->code->type == A_SYMBOLIC ||
  1.4477 +               entry->code->type == A_LOGICAL))
  1.4478 +            error(mpl, "only numeric, symbolic, or logical expression a"
  1.4479 +               "llowed");
  1.4480 +      }
  1.4481 +      /* close the domain scope */
  1.4482 +      if (prt->domain != NULL) close_scope(mpl, prt->domain);
  1.4483 +#if 1 /* 14/VII-2006 */
  1.4484 +      /* parse optional redirection */
  1.4485 +      prt->fname = NULL, prt->app = 0;
  1.4486 +      if (mpl->token == T_GT || mpl->token == T_APPEND)
  1.4487 +      {  prt->app = (mpl->token == T_APPEND);
  1.4488 +         get_token(mpl /* > or >> */);
  1.4489 +         /* parse expression for file name string */
  1.4490 +         prt->fname = expression_5(mpl);
  1.4491 +         /* convert it to symbolic type, if necessary */
  1.4492 +         if (prt->fname->type == A_NUMERIC)
  1.4493 +            prt->fname = make_unary(mpl, O_CVTSYM, prt->fname,
  1.4494 +               A_SYMBOLIC, 0);
  1.4495 +         /* check that now the expression is of symbolic type */
  1.4496 +         if (prt->fname->type != A_SYMBOLIC)
  1.4497 +            error(mpl, "file name expression has invalid type");
  1.4498 +      }
  1.4499 +#endif
  1.4500 +      /* the printf statement has been completely parsed */
  1.4501 +      if (mpl->token != T_SEMICOLON)
  1.4502 +         error(mpl, "syntax error in printf statement");
  1.4503 +      get_token(mpl /* ; */);
  1.4504 +      return prt;
  1.4505 +}
  1.4506 +
  1.4507 +/*----------------------------------------------------------------------
  1.4508 +-- for_statement - parse for statement.
  1.4509 +--
  1.4510 +-- This routine parses for statement using the syntax:
  1.4511 +--
  1.4512 +-- <for statement> ::= for <domain> <statement>
  1.4513 +-- <for statement> ::= for <domain> { <statement list> }
  1.4514 +-- <domain> ::= <indexing expression>
  1.4515 +-- <statement list> ::= <empty>
  1.4516 +-- <statement list> ::= <statement list> <statement>
  1.4517 +-- <statement> ::= <check statement>
  1.4518 +-- <statement> ::= <display statement>
  1.4519 +-- <statement> ::= <printf statement>
  1.4520 +-- <statement> ::= <for statement> */
  1.4521 +
  1.4522 +FOR *for_statement(MPL *mpl)
  1.4523 +{     FOR *fur;
  1.4524 +      STATEMENT *stmt, *last_stmt;
  1.4525 +      xassert(is_keyword(mpl, "for"));
  1.4526 +      /* create for descriptor */
  1.4527 +      fur = alloc(FOR);
  1.4528 +      fur->domain = NULL;
  1.4529 +      fur->list = last_stmt = NULL;
  1.4530 +      get_token(mpl /* for */);
  1.4531 +      /* parse indexing expression */
  1.4532 +      if (mpl->token != T_LBRACE)
  1.4533 +         error(mpl, "indexing expression missing where expected");
  1.4534 +      fur->domain = indexing_expression(mpl);
  1.4535 +      /* skip optional colon */
  1.4536 +      if (mpl->token == T_COLON) get_token(mpl /* : */);
  1.4537 +      /* parse for statement body */
  1.4538 +      if (mpl->token != T_LBRACE)
  1.4539 +      {  /* parse simple statement */
  1.4540 +         fur->list = simple_statement(mpl, 1);
  1.4541 +      }
  1.4542 +      else
  1.4543 +      {  /* parse compound statement */
  1.4544 +         get_token(mpl /* { */);
  1.4545 +         while (mpl->token != T_RBRACE)
  1.4546 +         {  /* parse statement */
  1.4547 +            stmt = simple_statement(mpl, 1);
  1.4548 +            /* and append it to the end of the statement list */
  1.4549 +            if (last_stmt == NULL)
  1.4550 +               fur->list = stmt;
  1.4551 +            else
  1.4552 +               last_stmt->next = stmt;
  1.4553 +            last_stmt = stmt;
  1.4554 +         }
  1.4555 +         get_token(mpl /* } */);
  1.4556 +      }
  1.4557 +      /* close the domain scope */
  1.4558 +      xassert(fur->domain != NULL);
  1.4559 +      close_scope(mpl, fur->domain);
  1.4560 +      /* the for statement has been completely parsed */
  1.4561 +      return fur;
  1.4562 +}
  1.4563 +
  1.4564 +/*----------------------------------------------------------------------
  1.4565 +-- end_statement - parse end statement.
  1.4566 +--
  1.4567 +-- This routine parses end statement using the syntax:
  1.4568 +--
  1.4569 +-- <end statement> ::= end ; <eof> */
  1.4570 +
  1.4571 +void end_statement(MPL *mpl)
  1.4572 +{     if (!mpl->flag_d && is_keyword(mpl, "end") ||
  1.4573 +           mpl->flag_d && is_literal(mpl, "end"))
  1.4574 +      {  get_token(mpl /* end */);
  1.4575 +         if (mpl->token == T_SEMICOLON)
  1.4576 +            get_token(mpl /* ; */);
  1.4577 +         else
  1.4578 +            warning(mpl, "no semicolon following end statement; missing"
  1.4579 +               " semicolon inserted");
  1.4580 +      }
  1.4581 +      else
  1.4582 +         warning(mpl, "unexpected end of file; missing end statement in"
  1.4583 +            "serted");
  1.4584 +      if (mpl->token != T_EOF)
  1.4585 +         warning(mpl, "some text detected beyond end statement; text ig"
  1.4586 +            "nored");
  1.4587 +      return;
  1.4588 +}
  1.4589 +
  1.4590 +/*----------------------------------------------------------------------
  1.4591 +-- simple_statement - parse simple statement.
  1.4592 +--
  1.4593 +-- This routine parses simple statement using the syntax:
  1.4594 +--
  1.4595 +-- <statement> ::= <set statement>
  1.4596 +-- <statement> ::= <parameter statement>
  1.4597 +-- <statement> ::= <variable statement>
  1.4598 +-- <statement> ::= <constraint statement>
  1.4599 +-- <statement> ::= <objective statement>
  1.4600 +-- <statement> ::= <solve statement>
  1.4601 +-- <statement> ::= <check statement>
  1.4602 +-- <statement> ::= <display statement>
  1.4603 +-- <statement> ::= <printf statement>
  1.4604 +-- <statement> ::= <for statement>
  1.4605 +--
  1.4606 +-- If the flag spec is set, some statements cannot be used. */
  1.4607 +
  1.4608 +STATEMENT *simple_statement(MPL *mpl, int spec)
  1.4609 +{     STATEMENT *stmt;
  1.4610 +      stmt = alloc(STATEMENT);
  1.4611 +      stmt->line = mpl->line;
  1.4612 +      stmt->next = NULL;
  1.4613 +      if (is_keyword(mpl, "set"))
  1.4614 +      {  if (spec)
  1.4615 +            error(mpl, "set statement not allowed here");
  1.4616 +         stmt->type = A_SET;
  1.4617 +         stmt->u.set = set_statement(mpl);
  1.4618 +      }
  1.4619 +      else if (is_keyword(mpl, "param"))
  1.4620 +      {  if (spec)
  1.4621 +            error(mpl, "parameter statement not allowed here");
  1.4622 +         stmt->type = A_PARAMETER;
  1.4623 +         stmt->u.par = parameter_statement(mpl);
  1.4624 +      }
  1.4625 +      else if (is_keyword(mpl, "var"))
  1.4626 +      {  if (spec)
  1.4627 +            error(mpl, "variable statement not allowed here");
  1.4628 +         stmt->type = A_VARIABLE;
  1.4629 +         stmt->u.var = variable_statement(mpl);
  1.4630 +      }
  1.4631 +      else if (is_keyword(mpl, "subject") ||
  1.4632 +               is_keyword(mpl, "subj") ||
  1.4633 +               mpl->token == T_SPTP)
  1.4634 +      {  if (spec)
  1.4635 +            error(mpl, "constraint statement not allowed here");
  1.4636 +         stmt->type = A_CONSTRAINT;
  1.4637 +         stmt->u.con = constraint_statement(mpl);
  1.4638 +      }
  1.4639 +      else if (is_keyword(mpl, "minimize") ||
  1.4640 +               is_keyword(mpl, "maximize"))
  1.4641 +      {  if (spec)
  1.4642 +            error(mpl, "objective statement not allowed here");
  1.4643 +         stmt->type = A_CONSTRAINT;
  1.4644 +         stmt->u.con = objective_statement(mpl);
  1.4645 +      }
  1.4646 +#if 1 /* 11/II-2008 */
  1.4647 +      else if (is_keyword(mpl, "table"))
  1.4648 +      {  if (spec)
  1.4649 +            error(mpl, "table statement not allowed here");
  1.4650 +         stmt->type = A_TABLE;
  1.4651 +         stmt->u.tab = table_statement(mpl);
  1.4652 +      }
  1.4653 +#endif
  1.4654 +      else if (is_keyword(mpl, "solve"))
  1.4655 +      {  if (spec)
  1.4656 +            error(mpl, "solve statement not allowed here");
  1.4657 +         stmt->type = A_SOLVE;
  1.4658 +         stmt->u.slv = solve_statement(mpl);
  1.4659 +      }
  1.4660 +      else if (is_keyword(mpl, "check"))
  1.4661 +      {  stmt->type = A_CHECK;
  1.4662 +         stmt->u.chk = check_statement(mpl);
  1.4663 +      }
  1.4664 +      else if (is_keyword(mpl, "display"))
  1.4665 +      {  stmt->type = A_DISPLAY;
  1.4666 +         stmt->u.dpy = display_statement(mpl);
  1.4667 +      }
  1.4668 +      else if (is_keyword(mpl, "printf"))
  1.4669 +      {  stmt->type = A_PRINTF;
  1.4670 +         stmt->u.prt = printf_statement(mpl);
  1.4671 +      }
  1.4672 +      else if (is_keyword(mpl, "for"))
  1.4673 +      {  stmt->type = A_FOR;
  1.4674 +         stmt->u.fur = for_statement(mpl);
  1.4675 +      }
  1.4676 +      else if (mpl->token == T_NAME)
  1.4677 +      {  if (spec)
  1.4678 +            error(mpl, "constraint statement not allowed here");
  1.4679 +         stmt->type = A_CONSTRAINT;
  1.4680 +         stmt->u.con = constraint_statement(mpl);
  1.4681 +      }
  1.4682 +      else if (is_reserved(mpl))
  1.4683 +         error(mpl, "invalid use of reserved keyword %s", mpl->image);
  1.4684 +      else
  1.4685 +         error(mpl, "syntax error in model section");
  1.4686 +      return stmt;
  1.4687 +}
  1.4688 +
  1.4689 +/*----------------------------------------------------------------------
  1.4690 +-- model_section - parse model section.
  1.4691 +--
  1.4692 +-- This routine parses model section using the syntax:
  1.4693 +--
  1.4694 +-- <model section> ::= <empty>
  1.4695 +-- <model section> ::= <model section> <statement>
  1.4696 +--
  1.4697 +-- Parsing model section is terminated by either the keyword 'data', or
  1.4698 +-- the keyword 'end', or the end of file. */
  1.4699 +
  1.4700 +void model_section(MPL *mpl)
  1.4701 +{     STATEMENT *stmt, *last_stmt;
  1.4702 +      xassert(mpl->model == NULL);
  1.4703 +      last_stmt = NULL;
  1.4704 +      while (!(mpl->token == T_EOF || is_keyword(mpl, "data") ||
  1.4705 +               is_keyword(mpl, "end")))
  1.4706 +      {  /* parse statement */
  1.4707 +         stmt = simple_statement(mpl, 0);
  1.4708 +         /* and append it to the end of the statement list */
  1.4709 +         if (last_stmt == NULL)
  1.4710 +            mpl->model = stmt;
  1.4711 +         else
  1.4712 +            last_stmt->next = stmt;
  1.4713 +         last_stmt = stmt;
  1.4714 +      }
  1.4715 +      return;
  1.4716 +}
  1.4717 +
  1.4718 +/* eof */