/* glplan5.c (l_parse) */

/*----------------------------------------------------------------------
-- Copyright (C) 2000, 2001, 2002 Andrew Makhorin <mao@mai2.rcnet.ru>,
--               Department for Applied Informatics, Moscow Aviation
--               Institute, Moscow, Russia. All rights reserved.
--
-- This file is a part of GLPK (GNU Linear Programming Kit).
--
-- GLPK is free software; you can redistribute it and/or modify it
-- under the terms of the GNU General Public License as published by
-- the Free Software Foundation; either version 2, or (at your option)
-- any later version.
--
-- GLPK is distributed in the hope that it will be useful, but WITHOUT
-- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
-- License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with GLPK; see the file COPYING. If not, write to the Free
-- Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-- 02111-1307, USA.
----------------------------------------------------------------------*/

#include <ctype.h>
#include <string.h>
#include "glplang.h"

/*----------------------------------------------------------------------
-- constant - parse constant literal.
--
-- This routine parses constant literal using the syntax:
--
-- <constant> ::= false
-- <constant> ::= true
-- <constant> ::= nil
-- <constant> ::= <integer number>
-- <constant> ::= <real number>
--
-- and returns a pointer to the resultant array (which is scalar in the
-- given case). */

SPAR *constant(void)
{     SPAR *spar;
      MEMB *memb;
      spar = get_atom(pdb->spar_pool);
      if (t_name("false"))
      {  /* scalar predicate (assertion) *false* */
         strcpy(spar->name, "<false>");
         spar->type = 'P';
         spar->dim = 0;
         spar->first = spar->last = NULL;
      }
      else if (t_name("true"))
      {  /* scalar predicate (assertion) *true* */
         strcpy(spar->name, "<true>");
         spar->type = 'P';
         spar->dim = 0;
         spar->first = spar->last = memb = get_atom(pdb->memb_pool);
         memb->link = NULL;
         memb->next = NULL;
      }
      else if (t_name("nil"))
      {  /* numerical empty scalar */
         strcpy(spar->name, "<nil>");
         spar->type = 'X';
         spar->dim = 0;
         spar->first = spar->last = NULL;
      }
      else if (token == T_INT || token == T_REAL)
      {  /* scalar constant expression */
         strcpy(spar->name, "<const>");
         spar->type = 'X';
         spar->dim = 0;
         spar->first = spar->last = memb = get_atom(pdb->memb_pool);
         memb->link = make_const(pdb->text->t_real);
         memb->next = NULL;
      }
      else
         insist(token != token);
      get_token(/* false | true | number */);
      return spar;
}

/*----------------------------------------------------------------------
-- designator - parse array designator.
--
-- This routine parses array designator (which is similar to single or
-- subscripted variable in programming languages) using the syntax:
--
-- <array> ::= <name>
-- <item> ::= <name>
-- <designator> ::= <array>
-- <designator> ::= <array> [ <selector list> ]
-- <selector list> ::= <selector>
-- <selector list> ::= <selector list> , <selector>
-- <selector> ::= <letter>
-- <selector> ::= <letter> + <integer>
-- <selector> ::= <letter> - <integer>
-- <selector> ::= <letter> ++ <integer>
-- <selector> ::= <letter> -- <integer>
-- <selector> ::= # <item>
--
-- and returns a pointer to the resultant array. */

SPAR *designator(char *name)
{     SPAR *orig;          /* original array */
      int suff;            /* name suffix:
                              0   - no suffix
                              'L' - .lo
                              'U' - .up
                              'S' - .fx */
      /* the following four objects represent the selector list related
         to the original array: */
      int form[MAX_DIM];   /* subscript form:
                              0 - linear lag/lead (i + n, i - n)
                              1 - circular lag/lead (i ++ n, i -- n)
                              2 - item reference (# item) */
      int mute[MAX_DIM];   /* mute letter (form = 0 or 1) */
      int lval[MAX_DIM];   /* lag/lead value (form = 0 or 1) */
      ITEM *iref[MAX_DIM]; /* item reference (form = 2) */
      SPAR *spar;          /* resultant array */
      AVLNODE *node;
      MEMB *memb;
      int k, t;
      /* determine original array */
      node = find_by_key(pdb->tree, name);
      if (node == NULL)
         fatal("`%s' not declared", name);
      if (node->type == 'I')
         fatal("invalid use of item `%s'", name);
      orig = node->link;
      /* parse optional suffix */
      suff = 0;
      if (t_spec("."))
      {  if (!(orig->type == 'V' || orig->type == 'C'))
            fatal("suffix not allowed");
         get_token(/* . */);
         if (t_name("lo"))
            suff = 'L';
         else if (t_name("up"))
            suff = 'U';
         else if (t_name("fx"))
            suff = 'S';
         else
            fatal("invalid suffix `.%s'", image);
         get_token(/* lo | up | fx */);
      }
      /* parse optional subscript list */
      if (!t_spec("["))
      {  if (orig->dim > 0)
            fatal("subscript list required for `%s'", orig->name);
         goto skip;
      }
      if (orig->dim == 0)
         fatal("invalid use of subscript list for `%s'", orig->name);
      get_token(/* [ */);
      k = 0;
      for (;;)
      {  /* parse the next subscript */
         if (k == orig->dim)
            fatal("too many subscripts for `%s'", orig->name);
         if (token == T_NAME)
         {  /* parse mute letter */
            if (!(strlen(image) == 1 && islower(image[0])))
               fatal("invalid mute letter `%s'", image);
            /* identical mute letters should refer to identical sets */
            t = find_mute(k, mute, image[0]);
            if (t >= 0 && orig->set[t] != orig->set[k])
               fatal("mute letter `%c' refers to different sets",
                  image[0]);
            form[k] = 0;
            mute[k] = image[0];
            lval[k] = 0;
            iref[k] = NULL;
            get_token(/* mute letter */);
            /* parse optional lead/lag operator */
            if (t_spec("+"))
               form[k] = 0, lval[k] = +1;
            else if (t_spec("-"))
               form[k] = 0, lval[k] = -1;
            else if (t_spec("++"))
               form[k] = 1, lval[k] = +1;
            else if (t_spec("--"))
               form[k] = 1, lval[k] = -1;
            if (lval[k] != 0)
            {  get_token(/* + | - | ++ | -- */);
               if (token != T_INT)
                  fatal("invalid lead/lag value");
               lval[k] *= pdb->text->t_int;
               get_token(/* integer */);
            }
         }
         else if (t_spec("#"))
         {  /* parse item reference */
            get_token(/* # */);
            if (token != T_NAME)
               fatal("item name missing or invalid");
            node = find_by_key(pdb->tree, image);
            if (node == NULL)
               fatal("item `%s' not declared", image);
            if (node->type != 'I')
               fatal("invalid use of `%s'", image);
            if (orig->set[k] != ((ITEM *)node->link)->set)
               fatal("`%s' belongs to incompatible set", image);
            form[k] = 2;
            mute[k] = '?';
            lval[k] = 0;
            iref[k] = node->link;
            get_token(/* item */);
         }
         else
            fatal("invalid subscript");
         k++;
         if (t_spec("]")) break;
         if (!t_spec(",")) fatal("missing right bracket");
         get_token(/* , */);
      }
      if (k != orig->dim)
         fatal("too few subscripts for `%s'", orig->name);
      get_token(/* ] */);
skip: /* create resultant array */
      spar = get_atom(pdb->spar_pool);
      strcpy(spar->name, orig->name);
      switch (orig->type)
      {  case 'S': spar->type = 'P'; break;
         case 'P': spar->type = 'P'; break;
         case 'X': spar->type = 'X'; break;
         case 'V': spar->type = 'X'; break;
         case 'C': spar->type = 'X'; break;
         default: insist(orig->type != orig->type);
      }
      spar->dim = 0; /* unknown yet */
      spar->first = spar->last = NULL;
      /* determine domain of the resultant array */
      for (k = 0; k < orig->dim; k++)
      {  if (form[k] == 0 || form[k] == 1)
         {  if (find_mute(spar->dim, spar->mute, mute[k]) < 0)
            {  spar->set[spar->dim] = orig->set[k];
               spar->mute[spar->dim] = mute[k];
               spar->dim++;
            }
         }
      }
      /* look through the list of the original array elements */
      for (memb = orig->first; memb != NULL; memb = memb->next)
      {  ITEM *item[MAX_DIM];
         MEMB *temp;
         /* check variable/constraint type to imitate the corresponding
            pseudo arrays .lo, .up, and .fx */
         if (suff != 0)
         {  int type;
            switch (orig->type)
            {  case 'V':
                  type = ((VAR *)memb->link)->type;
                  break;
               case 'C':
                  type = ((CONS *)memb->link)->type;
                  break;
               default:
                  insist(orig->type != orig->type);
            }
            switch (suff)
            {  case 'L':
                  if (!(type == 'L' || type == 'D')) goto miss;
                  break;
               case 'U':
                  if (!(type == 'U' || type == 'D')) goto miss;
                  break;
               case 'S':
                  if (type != 'S') goto miss;
                  break;
               default:
                  insist(suff != suff);
            }
         }
         /* look through the tuple of the original element */
         for (k = 0; k < orig->dim; k++)
         {  /* copy the next subscript */
            item[k] = memb->item[k];
            /* perform specified operation on the subscript */
            switch (form[k])
            {  case 0:
                  /* linear lag/lead */
                  if (lval[k] < 0)
                  {  for (t = 1; t <= -lval[k]; t++)
                     {  item[k] = item[k]->next;
                        if (item[k] == NULL) goto miss;
                     }
                  }
                  else if (lval[k] > 0)
                  {  for (t = 1; t <= +lval[k]; t++)
                     {  item[k] = item[k]->prev;
                        if (item[k] == NULL) goto miss;
                     }
                  }
                  break;
               case 1:
                  /* circular lag/lead */
                  if (lval[k] < 0)
                  {  for (t = 1; t <= -lval[k]; t++)
                     {  item[k] = item[k]->next;
                        if (item[k] == NULL)
                           item[k] = orig->set[k]->first->item[0];
                     }
                  }
                  else if (lval[k] > 0)
                  {  for (t = 1; t <= +lval[k]; t++)
                     {  item[k] = item[k]->prev;
                        if (item[k] == NULL)
                           item[k] = orig->set[k]->last->item[0];
                     }
                  }
                  break;
               case 2:
                  /* selection */
                  if (item[k] != iref[k]) goto miss;
                  break;
               default:
                  insist(form[k] != form[k]);
            }
            /* identical mute letters should correspond to identical
               index set elements */
            t = find_mute(k, mute, mute[k]);
            if (t >= 0 && item[t] != item[k]) goto miss;
         }
         /* create resultant element and build its tuple */
         temp = get_atom(pdb->memb_pool);
         for (k = 0; k < orig->dim; k++)
         {  if (form[k] == 0 || form[k] == 1)
            {  t = find_mute(spar->dim, spar->mute, mute[k]);
               insist(t >= 0);
               temp->item[t] = item[k];
            }
         }
         /* assign a value to the resultant element */
         switch (orig->type)
         {  case 'S':
               temp->link = NULL;
               break;
            case 'P':
               temp->link = NULL;
               break;
            case 'X':
               temp->link = copy_expr(memb->link);
               break;
            case 'V':
               switch (suff)
               {  case 0:
                     temp->link = make_refer(orig, memb);
                     break;
                  case 'L':
                     temp->link = make_const(((VAR *)memb->link)->lb);
                     break;
                  case 'U':
                     temp->link = make_const(((VAR *)memb->link)->ub);
                     break;
                  case 'S':
                     temp->link = make_const(((VAR *)memb->link)->lb);
                     break;
                  default:
                     insist(suff != suff);
               }
               break;
            case 'C':
               switch (suff)
               {  case 0:
                     temp->link = copy_expr(((CONS *)memb->link)->expr);
                     break;
                  case 'L':
                     temp->link = make_const(((CONS *)memb->link)->lb);
                     break;
                  case 'U':
                     temp->link = make_const(((CONS *)memb->link)->ub);
                     break;
                  case 'S':
                     temp->link = make_const(((CONS *)memb->link)->lb);
                     break;
                  default:
                     insist(suff != suff);
               }
               break;
            default:
               insist(spar->type != spar->type);
         }
         temp->next = NULL;
         /* add new element to the resultant array */
         if (spar->first == NULL)
            spar->first = temp;
         else
            spar->last->next = temp;
         spar->last = temp;
miss:    ;
      }
      /* check delimiter */
      if (t_spec("]")) get_token(/* ] */);
      /* bring result to the calling routine */
      return spar;
}

/*----------------------------------------------------------------------
-- data_func - parse data() built-in function call.
--
-- This routine parses a call to the data() built-in function using the
-- syntax:
--
-- <data func> ::= data ( <domain list> : <data list> )
-- <domain list> ::= <domain> | <domain list> , <domain>
-- <domain> ::= <mute letter> in <set>
-- <data list> ::= <datum> | <data list> , <datum>
-- <datum> ::= <tuple> , <expression>
-- <tuple> ::= <set item> | <tuple> , <set item>
--
-- and returns a pointer to the resultant array. */

SPAR *data_func(void)
{     SPAR *spar, *temp;
      MEMB *memb;
      ITEM *item[MAX_DIM];
      AVLNODE *node;
      int k;
      insist(t_spec("("));
      get_token(/* ( */);
      /* create array */
      spar = get_atom(pdb->spar_pool);
      strcpy(spar->name, "<data>");
      spar->type = 'X';
      spar->dim = 0;
      spar->first = spar->last = NULL;
      create_index(spar);
      /* parse domain list */
      for (;;)
      {  if (spar->dim == MAX_DIM)
            fatal("array dimension too high");
         if (token != T_NAME)
            fatal("mute letter missing or invalid");
         if (!(strlen(image) == 1 && islower(image[0])))
            fatal("invalid mute letter `%s'", image);
         if (find_mute(spar->dim, spar->mute, image[0]) >= 0)
            fatal("duplicate mute letter `%s'", image);
         spar->mute[spar->dim] = image[0];
         get_token(/* letter */);
         if (!t_name("in"))
            fatal("missing keyword `in'");
         get_token(/* in */);
         if (token != T_NAME)
            fatal("set name missing or invalid");
         node = find_by_key(pdb->tree, image);
         if (node == NULL)
            fatal("set `%s' not declared", image);
         if (node->type != 'S')
            fatal("invalid use of `%s'", image);
         spar->set[spar->dim] = node->link;
         get_token(/* set name */);
         spar->dim++;
         if (t_spec(":")) break;
         if (!t_spec(",")) fatal("missing colon");
         get_token(/* , */);
      }
      get_token(/* : */);
      /* parse data list */
      if (t_spec(")")) goto skip;
      for (;;)
      {  /* parse subscript list (tuple) */
         for (k = 0; k < spar->dim; k++)
         {  if (t_spec("."))
            {  if (spar->first == NULL)
                  fatal("invalid use of period");
               item[k] = spar->last->item[k];
               get_token(/* . */);
            }
            else
            {  if (token != T_NAME)
                  fatal("item name missing or invalid");
               node = find_by_key(pdb->tree, image);
               if (node == NULL)
                  fatal("item `%s' not declared", image);
               if (node->type != 'I')
                  fatal("invalid use of `%s'", image);
               item[k] = node->link;
               if (item[k]->set != spar->set[k])
                  fatal("`%s' not belong to `%s'", image,
                     spar->set[k]->name);
               get_token(/* item name */);
            }
            if (t_spec(",")) get_token(/* , */);
         }
         /* check for duplicate tuple */
         if (find_memb(item) != NULL)
            fatal("duplicate tuple detected");
         /* parse expression */
         temp = expression();
         if (!(temp->type == 'X' && temp->dim == 0))
            fatal("scalar arithmetic value required");
         memb = temp->first;
         if (memb != NULL)
         {  /* add member to the resultant array */
            insist(memb->link != NULL);
            insist(memb->next == NULL);
            for (k = 0; k < spar->dim; k++) memb->item[k] = item[k];
            memb->next = NULL;
            if (spar->first == NULL)
               spar->first = memb;
            else
               spar->last->next = memb;
            spar->last = memb;
            index_memb(memb);
         }
         free_atom(pdb->spar_pool, temp);
         /* check delimiter */
         if (t_spec(")")) break;
         if (t_spec(",")) get_token(/* , */);
      }
skip: delete_index();
      get_token(/* ) */);
      return spar;
}

/*----------------------------------------------------------------------
-- sum_func - parse sum() built-in function call.
--
-- This routine parses a call to the sum() built-in function using the
-- syntax:
--
-- <sum func> ::= sum ( <mute part> , <expression> )
-- <mute part> ::= <mute letter> | ( <mute list> )
-- <mute list> ::= <mute letter> | <mute list> , <mute letter>
--
-- and returns a pointer to the resultant array. */

SPAR *sum_func(void)
{     SPAR *x;
      int n, mute[MAX_DIM];
      get_token(/* ( */);
      if (t_spec("("))
      {  /* parenthesized mute letter list */
         get_token(/* ( */);
         n = 0;
         for (;;)
         {  if (n == MAX_DIM)
               fatal("too many mute letters");
            if (token != T_NAME)
               fatal("mute letter missing or invalid");
            if (!(strlen(image) == 1 && islower(image[0])))
               fatal("invalid mute letter `%s'", image);
            if (find_mute(n, mute, image[0]) >= 0)
               fatal("duplicate mute letter `%s'", image);
            mute[n++] = image[0];
            get_token(/* letter */);
            if (t_spec(")")) break;
            if (!t_spec(",")) fatal("missing comma");
            get_token(/* , */);
         }
         get_token(/* ) */);
      }
      else
      {  /* the only mute letter */
         if (token != T_NAME)
            fatal("mute letter missing or invalid");
         if (!(strlen(image) == 1 && islower(image[0])))
            fatal("invalid mute letter `%s'", image);
         n = 1;
         mute[0] = image[0];
         get_token(/* letter */);
      }
      if (!t_spec(",")) fatal("missing comma");
      get_token(/* , */);
      x = expression();
      /* perform summation */
      x = summation(x, n, mute);
      /* check delimiter */
      if (!t_spec(")")) fatal("missing right parenthesis");
      get_token(/* ) */);
      return x;
}

/*----------------------------------------------------------------------
-- table_func - parse table() built-in function call.
--
-- This routine parses a call to the table() built-in function using the
-- syntax:
--
-- <table func> ::= table ( <domain list> : <column list> : <row list> )
-- <domain list> ::= <mute letter> in <set> , <mute letter> in <set>
-- <column list> ::= <set item> | <column list> , <set item>
-- <row list> ::= <row> | <row list> , <row>
-- <row> ::= <set item> <entry list>
-- <entry list> ::= <expression> | <entry list> , <expression>
--
-- and returns a pointer to the resultant array. */

#define MAX_COLS 100

SPAR *table_func(void)
{     SPAR *spar, *temp;
      AVLNODE *node;
      MEMB *memb;
      ITEM *col[1+MAX_COLS], *row, *item[2];
      int n, k;
      insist(t_spec("("));
      get_token(/* ( */);
      /* create resultant 2-d array */
      spar = get_atom(pdb->spar_pool);
      strcpy(spar->name, "<table>");
      spar->type = 'X';
      spar->dim = 2;
      spar->first = spar->last = NULL;
      /* parse domain list */
      for (k = 0; k <= 1; k++)
      {  if (token != T_NAME)
            fatal("mute letter missing or invalid");
         if (!(strlen(image) == 1 && islower(image[0])))
            fatal("invalid mute letter `%s'", image);
         if (find_mute(k, spar->mute, image[0]) >= 0)
            fatal("duplicate mute letter `%s'", image);
         spar->mute[k] = image[0];
         get_token(/* letter */);
         if (!t_name("in"))
            fatal("missing keyword `in'");
         get_token(/* in */);
         if (token != T_NAME)
            fatal("set name missing or invalid");
         node = find_by_key(pdb->tree, image);
         if (node == NULL)
            fatal("set `%s' not declared", image);
         if (node->type != 'S')
            fatal("invalid use of `%s'", image);
         spar->set[k] = node->link;
         get_token(/* set name */);
         if (k == 0)
         {  if (!t_spec(",")) fatal("missing comma");
            get_token(/* , */);
         }
         else
         {  if (!t_spec(":")) fatal("missing colon");
            get_token(/* : */);
         }
      }
      /* parse column list */
      n = 0;
      for (;;)
      {  if (n == MAX_COLS)
            fatal("too many columns");
         if (token != T_NAME)
            fatal("column name missing or invalid");
         node = find_by_key(pdb->tree, image);
         if (node == NULL)
            fatal("item `%s' not declared", image);
         if (node->type != 'I')
            fatal("invalid use of `%s'", image);
         col[++n] = node->link;
         if (col[n]->set != spar->set[1])
            fatal("`%s' not belong to `%s'", image, spar->set[1]->name);
         get_token(/* item name */);
         if (t_spec(":")) break;
         if (t_spec(",")) get_token(/* , */);
      }
      get_token(/* : */);
      /* parse row list */
      create_index(spar);
      for (;;)
      {  if (token != T_NAME)
            fatal("row name missing or invalid");
         node = find_by_key(pdb->tree, image);
         if (node == NULL)
            fatal("item `%s' not declared", image);
         if (node->type != 'I')
            fatal("invalid use of `%s'", image);
         row = node->link;
         if (row->set != spar->set[0])
            fatal("`%s' not belong to `%s'", image, spar->set[0]->name);
         get_token(/* item name */);
         if (t_spec(",")) get_token(/* , */);
         for (k = 1; k <= n; k++)
         {  if (t_spec("."))
            {  /* skip entry */
               get_token(/* . */);
            }
            else
            {  /* check for duplicate tuple */
               item[0] = row, item[1] = col[k];
               if (find_memb(item) != NULL)
                  fatal("duplicate tuple detected");
               /* parse expression */
               temp = expression();
               if (!(temp->type == 'X' && temp->dim == 0))
                  fatal("scalar arithmetic value required");
               memb = temp->first;
               if (memb != NULL)
               {  /* add member to the resultant array */
                  insist(memb->link != NULL);
                  insist(memb->next == NULL);
                  memb->item[0] = item[0];
                  memb->item[1] = item[1];
                  memb->next = NULL;
                  if (spar->first == NULL)
                     spar->first = memb;
                  else
                     spar->last->next = memb;
                  spar->last = memb;
                  index_memb(memb);
               }
               free_atom(pdb->spar_pool, temp);
            }
            if (k < n && t_spec(",")) get_token(/* , */);
         }
         /* check delimiter */
         if (t_spec(")")) break;
         if (t_spec(",")) get_token(/* , */);
      }
      delete_index();
      get_token(/* ) */);
      return spar;
}

#undef MAX_COLS

/*----------------------------------------------------------------------
-- primary - parse primary expression.
--
-- This routine parses primary expression using the syntax:
--
-- <primary> ::= false
-- <primary> ::= true
-- <primary> ::= nil
-- <primary> ::= <numeric constant>
-- <primary> ::= <designator>
-- <primary> ::= <data func>
-- <primary> ::= <sum func>
-- <primary> ::= <table func>
-- <primary> ::= ( <expression> )
-- <designator> ::= <name> | <name> [ ... ]
-- <data func> ::= data ( ... )
-- <sum func> ::= sum ( ... )
-- <table func> ::= table ( ... )
--
-- and returns a pointer to the resultant array (or predicate). */

SPAR *primary(void)
{     SPAR *spar;
      if (t_name("false") || t_name("true") || t_name("nil"))
         spar = constant();
      else if (token == T_INT || token == T_REAL)
         spar = constant();
      else if (token == T_NAME)
      {  char name[MAX_NAME+1];
         strcpy(name, image);
         get_token(/* name */);
         if (t_spec("("))
         {  /* built-in function call */
            if (strcmp(name, "data") == 0)
               spar = data_func();
            else if (strcmp(name, "sum") == 0)
               spar = sum_func();
            else if (strcmp(name, "table") == 0)
               spar = table_func();
            else
               fatal("unknown function `%s'", name);
         }
         else
         {  /* designator */
            spar = designator(name);
         }
      }
      else if (t_spec("("))
      {  get_token(/* ( */);
         spar = expression();
         if (!t_spec(")")) fatal("missing right parenthesis");
         get_token(/* ) */);
      }
      else
         fatal("expression syntax error");
      return spar;
}

/*----------------------------------------------------------------------
-- secondary - parse secondary expression.
--
-- This routine parses secondary expression using the syntax:
--
-- <secondary> ::= <primary>
-- <secondary> ::= <secondary> * <primary>
-- <secondary> ::= <secondary> / <primary>
--
-- and returns a pointer to the resultant array (or predicate). */

SPAR *secondary(void)
{     SPAR *x;
      x = primary();
loop: if (t_spec("*"))
      {  get_token(/* * */);
         x = multiplication(C_MUL, x, primary());
         goto loop;
      }
      else if (t_spec("/"))
      {  get_token(/* / */);
         x = multiplication(C_DIV, x, primary());
         goto loop;
      }
      return x;
}

/*----------------------------------------------------------------------
-- simple_expr - parse simple expression.
--
-- This routine parses simple expression using the syntax:
--
-- <simple expr> ::= <secondary>
-- <simple expr> ::= + <secondary>
-- <simple expr> ::= - <secondary>
-- <simple expr> ::= <simple expr> + <secondary>
-- <simple expr> ::= <simple expr> - <secondary>
--
-- and returns a pointer to the resultant array (or predicate). */

SPAR *simple_expr(void)
{     SPAR *x;
      if (t_spec("+"))
      {  get_token(/* + */);
         x = unary_op(C_POS, secondary());
      }
      else if (t_spec("-"))
      {  get_token(/* - */);
         x = unary_op(C_NEG, secondary());
      }
      else
         x = secondary();
loop: if (t_spec("+"))
      {  get_token(/* + */);
         x = addition(C_ADD, x, secondary());
         goto loop;
      }
      else if (t_spec("-"))
      {  get_token(/* - */);
         x = addition(C_SUB, x, secondary());
         goto loop;
      }
      return x;
}

/*----------------------------------------------------------------------
-- arith_expr - parse arithmetic expression.
--
-- This routine parses arithmetic expression using the syntax:
--
-- <arith expr> ::= <simple expr>
-- <arith expr> ::= <simple expr> where <expression>
--
-- and returns a pointer to the resultant array (or predicate). */

SPAR *arith_expr(void)
{     SPAR *x;
      x = simple_expr();
      if (t_name("where"))
      {  get_token(/* where */);
         x = selection(x, expression());
      }
      return x;
}

/*----------------------------------------------------------------------
-- relation - parse relation expression.
--
-- This routine parses relation expression using the syntax:
--
-- <relation> ::= <arith expr>
-- <relation> ::= <arith expr> <  <arith expr>
-- <relation> ::= <arith expr> <= <arith expr>
-- <relation> ::= <arith expr> =  <arith expr>
-- <relation> ::= <arith expr> >= <arith expr>
-- <relation> ::= <arith expr> >  <arith expr>
-- <relation> ::= <arith expr> != <arith expr>
--
-- and returns a pointer to the resultant predicate (or array). */

SPAR *relation(void)
{     SPAR *x;
      x = arith_expr();
      if (t_spec("<"))
      {  get_token(/* < */);
         x = comparison(C_LT, x, arith_expr());
      }
      else if (t_spec("<="))
      {  get_token(/* <= */);
         x = comparison(C_LE, x, arith_expr());
      }
      else if (t_spec("="))
      {  get_token(/* = */);
         x = comparison(C_EQ, x, arith_expr());
      }
      else if (t_spec(">="))
      {  get_token(/* >= */);
         x = comparison(C_GE, x, arith_expr());
      }
      else if (t_spec(">"))
      {  get_token(/* > */);
         x = comparison(C_GT, x, arith_expr());
      }
      else if (t_spec("!="))
      {  get_token(/* != */);
         x = comparison(C_NE, x, arith_expr());
      }
      return x;
}

/*----------------------------------------------------------------------
-- log_primary - parse logical primary expression.
--
-- This routine parses logical primary expression using the syntax:
--
-- <log primary> ::= <relation>
-- <log primary> ::= not <relation>
--
-- and returns a pointer to the resultant predicate (or array). */

SPAR *log_primary(void)
{     SPAR *x;
      if (t_name("not"))
      {  get_token(/* not */);
         x = logical_not(relation());
      }
      else
         x = relation();
      return x;
}

/*----------------------------------------------------------------------
-- log_secondary - parse logical secondary expression.
--
-- This routine parses logical secondary expression using the syntax:
--
-- <log secondary> ::= <log primary>
-- <log secondary> ::= <log secondary> and <log primary>
--
-- and returns a pointer to the resultant predicate (or array). */

SPAR *log_secondary(void)
{     SPAR *x;
      x = log_primary();
loop: if (t_name("and"))
      {  get_token(/* and */);
         x = logical_and(x, log_primary());
         goto loop;
      }
      return x;
}

/*----------------------------------------------------------------------
-- expression - parse expression of general kind.
--
-- This routine parses expression of general kind using the syntax:
--
-- <expression> ::= <log secondary>
-- <expression> ::= <expression> or <log secondary>
--
-- and returns a pointer to the resultant array or predicate. */

SPAR *expression(void)
{     SPAR *x;
      x = log_secondary();
loop: if (t_name("or"))
      {  get_token(/* or */);
         x = logical_or(x, log_secondary());
         goto loop;
      }
      return x;
}

/* eof */
