
// -*- mode: c++; c-basic-offset:4 -*-

// This file is part of libdap, A C++ implementation of the OPeNDAP Data
// Access Protocol.

// Copyright (c) 2002,2003 OPeNDAP, Inc.
// Author: James Gallagher <jgallagher@opendap.org>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
// 
// This library 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
// Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
 
// (c) COPYRIGHT URI/MIT 1995-1999
// Please read the full copyright statement in the file COPYRIGHT_URI.
//
// Authors:
//      jhrg,jimg       James Gallagher <jgallagher@gso.uri.edu>

/*
  This is the parser for the DODS constraint expression grammar. The parser
  calls various `helper' functions defined by the DAP classes which either
  implement the operations (in the case of relational ops) or store
  information (in the case of selection operations). 

  jhrg 9/5/95
*/

%code requires {

#include "config.h"

#include <cassert>
#include <cstdlib>
#include <cstring>

#include <iostream>
#include <sstream>
#include <iterator>
#include <string>
#include <stack>

//#define DODS_DEBUG

#include "debug.h"
#include "escaping.h"

#include "DDS.h"
#include "ConstraintEvaluator.h"

#include "BaseType.h"

#include "Byte.h"
#include "Int16.h"
#include "UInt16.h"
#include "Int32.h"
#include "UInt32.h"
#include "Float32.h"
#include "Float64.h"
#include "Str.h"
#include "Url.h"
#include "Array.h"
#include "Structure.h"
#include "Sequence.h"
#include "Grid.h"

#include "Error.h"

#include "util.h"
#include "parser.h"
#include "ce_parser.h"
#include "expr.h"
#include "RValue.h"

using std::cerr;
using std::endl;
using namespace libdap ;

#define EVALUATOR(arg) (static_cast<ce_parser_arg*>(arg)->get_eval())
#define DDS(arg) (static_cast<ce_parser_arg*>(arg)->get_dds())

// #define YYPARSE_PARAM arg

int ce_exprlex(void);		/* the scanner; see expr.lex */

void ce_exprerror(ce_parser_arg *arg, const string &s); 
void ce_exprerror(ce_parser_arg *arg, const string &s, const string &s2);
void no_such_func(ce_parser_arg *arg, const string &name);
void no_such_ident(ce_parser_arg *arg, const string &name, const string &word);

int_list *make_array_index(value &i1, value &i2, value &i3);
int_list *make_array_index(value &i1, value &i2);
int_list *make_array_index(value &i1);
int_list_list *make_array_indices(int_list *index);
int_list_list *append_array_index(int_list_list *indices, int_list *index);

void delete_array_indices(int_list_list *indices);
bool bracket_projection(DDS &table, const char *name, int_list_list *indices);

void process_array_indices(BaseType *variable, int_list_list *indices); 
void process_grid_indices(BaseType *variable, int_list_list *indices); 
void process_sequence_indices(BaseType *variable, int_list_list *indices);

/* Replace these with method calls. jhrg 8/31/06 */
bool is_array_t(BaseType *variable);
bool is_grid_t(BaseType *variable);
bool is_sequence_t(BaseType *variable);

BaseType *make_variable(ConstraintEvaluator &eval, const value &val);
bool_func get_function(const ConstraintEvaluator &eval, const char *name);
btp_func get_btp_function(const ConstraintEvaluator &eval, const char *name);
proj_func get_proj_function(const ConstraintEvaluator &eval, const char *name);

template<class arg_list, class arg_type>
arg_list make_fast_arg_list(unsigned long vector_size_hint, arg_type arg_value);

template<class arg_list, class arg_type>
arg_list make_fast_arg_list(arg_list int_values, arg_type arg_value);

template<class t, class T>
rvalue *build_constant_array(vector<t> *values, DDS *dds);

}

%require "2.4"

%parse-param {ce_parser_arg *arg}
%name-prefix "ce_expr"
%defines
%debug
%verbose

%union {
    bool boolean;
    int op;
    char id[ID_MAX];
    
    libdap::dods_byte byte_value;
    libdap::dods_int16 int16_value;
    libdap::dods_uint16 uint16_value;
    libdap::dods_int32 int32_value;
    libdap::dods_uint32 uint32_value;
    libdap::dods_float32 float32_value;
    libdap::dods_float64 float64_value;
    
    libdap::byte_arg_list byte_values;
    libdap::int16_arg_list int16_values;
    libdap::uint16_arg_list uint16_values;
    libdap::int32_arg_list int32_values;
    libdap::uint32_arg_list uint32_values;
    libdap::float32_arg_list float32_values;
    libdap::float64_arg_list float64_values;
    
    libdap::value val;               // value is defined in expr.h

    libdap::bool_func b_func;
    libdap::btp_func bt_func;

    libdap::int_list *int_l_ptr;
    libdap::int_list_list *int_ll_ptr;
    
    libdap::rvalue *rval_ptr;
    libdap::rvalue_list *r_val_l_ptr;
}

%token <val> SCAN_STR

%token <id> SCAN_WORD

%token <op> SCAN_EQUAL
%token <op> SCAN_NOT_EQUAL
%token <op> SCAN_GREATER
%token <op> SCAN_GREATER_EQL
%token <op> SCAN_LESS
%token <op> SCAN_LESS_EQL
%token <op> SCAN_REGEXP

%token <op> SCAN_STAR

%token <op> SCAN_HASH_BYTE
%token <op> SCAN_HASH_INT16
%token <op> SCAN_HASH_UINT16
%token <op> SCAN_HASH_INT32
%token <op> SCAN_HASH_UINT32
%token <op> SCAN_HASH_FLOAT32
%token <op> SCAN_HASH_FLOAT64

%type <boolean> constraint_expr projection proj_clause proj_function
%type <boolean> array_projection selection clause bool_function arg_length_hint
%type <id> array_proj_clause name
%type <op> rel_op

%type <int_l_ptr> array_index
%type <int_ll_ptr> array_indices

%type <rval_ptr> r_value id_or_const array_const_special_form array_projection_rvalue
%type <r_val_l_ptr> r_value_list arg_list

%type <byte_value> fast_byte_arg
%type <byte_values> fast_byte_arg_list

%type <int16_value> fast_int16_arg 
%type <int16_values> fast_int16_arg_list

%type <uint16_value> fast_uint16_arg 
%type <uint16_values> fast_uint16_arg_list

%type <int32_value> fast_int32_arg 
%type <int32_values> fast_int32_arg_list

%type <uint32_value> fast_uint32_arg 
%type <uint32_values> fast_uint32_arg_list

%type <float32_value> fast_float32_arg 
%type <float32_values> fast_float32_arg_list

%type <float64_value> fast_float64_arg 
%type <float64_values> fast_float64_arg_list

%code {
/* This global is used by the rule 'arg_length_hint' so that the hint can 
   be used during the paraent rule's parse. See fast_int32_arg_list. */
unsigned long arg_length_hint_value = 0;
}

%%

constraint_expr: /* empty constraint --> send all */
         {
             DBG(cerr << "Mark all variables" << endl);
		     DDS(arg)->mark_all(true);
		     $$ = true;
		 }
         /* projection only */
         | projection
		 /* selection only --> project everything */
         | '&' { DDS(arg)->mark_all(true); } selection
         { 
		     $$ = $3;
		 }
         | projection '&' selection
         {
		     $$ = $1 && $3;
		 }
;

projection: proj_clause
         | proj_clause ',' projection
         {
		     $$ = $1 && $3;
		 }
;

proj_clause: name 
         {
		     BaseType *var = DDS(arg)->var($1);
		     if (var) {
			     DBG(cerr << "Marking " << $1 << endl);
			     $$ = DDS(arg)->mark($1, true);
			     DBG(cerr << "result: " << $$ << endl);
		     }
		     else {
			     no_such_ident(arg, $1, "identifier");
		     }
		}
        | proj_function
        {
		    $$ = $1;
		}
		| array_projection
        {
		    $$ = $1;
		}
        | array_const_special_form
        {
            Array *array = dynamic_cast<Array*>($1->bvalue(*DDS(arg)));
            if (array) { 
                /* When the special form appears here (not as a function argument)
                set send_p so the data will be sent and add it to the DDS. This 
                streamlines testing (and is likely what is intended). */
                
                array->set_send_p(true);
                DDS(arg)->add_var_nocopy(array);
                
                return true;
            }
            else {
                ce_exprerror(arg, "Could not create the anonymous vector using the # special form");
                return false;
            }
        }
;

/* The value parsed by arg_length_hint is stored in a global variable by that rule
   so that it can be used during the parse of fast_byte_arg_list. */

/* return a rvalue */
array_const_special_form: SCAN_HASH_BYTE '(' arg_length_hint ':' fast_byte_arg_list ')'
        {
            $$ = build_constant_array<dods_byte, Byte>($5, DDS(arg));
        }
;

array_const_special_form: SCAN_HASH_INT16 '(' arg_length_hint ':' fast_int16_arg_list ')'
        {
            $$ = build_constant_array<dods_int16, Int16>($5, DDS(arg));
        }
;

array_const_special_form: SCAN_HASH_UINT16 '(' arg_length_hint ':' fast_uint16_arg_list ')'
        {
            $$ = build_constant_array<dods_uint16, UInt16>($5, DDS(arg));
        }
;

array_const_special_form: SCAN_HASH_INT32 '(' arg_length_hint ':' fast_int32_arg_list ')'
        {
            $$ = build_constant_array<dods_int32, Int32>($5, DDS(arg));
        }
;

array_const_special_form: SCAN_HASH_UINT32 '(' arg_length_hint ':' fast_uint32_arg_list ')'
        {
            $$ = build_constant_array<dods_uint32, UInt32>($5, DDS(arg));
        }
;

array_const_special_form: SCAN_HASH_FLOAT32 '(' arg_length_hint ':' fast_float32_arg_list ')'
        {
            $$ = build_constant_array<dods_float32, Float32>($5, DDS(arg));
        }
;

array_const_special_form: SCAN_HASH_FLOAT64 '(' arg_length_hint ':' fast_float64_arg_list ')'
        {
            $$ = build_constant_array<dods_float64, Float64>($5, DDS(arg));
        }
;

/* Here the arg length hint is stored in a global so it can be used by the 
   function that allocates the vector. The value is passed to vector::reserve(). */
   
arg_length_hint: SCAN_WORD
          {
              if (!check_int32($1))
                  throw Error(malformed_expr, "$<type>(hint, value, ...) special form expected hint to be an integer");
                   
              arg_length_hint_value = atoi($1);
              $$ = true;
          }
;

/* return an int_arg_list (a std::vector<int>*) */
fast_byte_arg_list: fast_byte_arg
          {
              $$ = make_fast_arg_list<byte_arg_list, dods_byte>(arg_length_hint_value, $1);
          }
          | fast_byte_arg_list ',' fast_byte_arg
          {
              $$ = make_fast_arg_list<byte_arg_list, dods_byte>($1, $3);
          }
;

/* This rule does not check SCAN_WORD nor does it perform www escaping */
fast_byte_arg: SCAN_WORD
          {
              $$ = strtol($1, 0, 0);
          }
;

/* return an int_arg_list (a std::vector<int>*) */
fast_int16_arg_list: fast_int16_arg
          {
              $$ = make_fast_arg_list<int16_arg_list, dods_int16>(arg_length_hint_value, $1);
          }
          | fast_int16_arg_list ',' fast_int16_arg
          {
              $$ = make_fast_arg_list<int16_arg_list, dods_int16>($1, $3);
          }
;

/* This rule does not check SCAN_WORD nor does it perform www escaping */
fast_int16_arg: SCAN_WORD
          {
              $$ = strtol($1, 0, 0);
          }
;

/* return an int_arg_list (a std::vector<int>*) */
fast_uint16_arg_list: fast_uint16_arg
          {
              $$ = make_fast_arg_list<uint16_arg_list, dods_uint16>(arg_length_hint_value, $1);
          }
          | fast_uint16_arg_list ',' fast_uint16_arg
          {
              $$ = make_fast_arg_list<uint16_arg_list, dods_uint16>($1, $3);
          }
;

/* This rule does not check SCAN_WORD nor does it perform www escaping */
fast_uint16_arg: SCAN_WORD
          {
              $$ = strtoul($1, 0, 0);
          }
;

/* return an int_arg_list (a std::vector<int>*) */
fast_int32_arg_list: fast_int32_arg
          {
              $$ = make_fast_arg_list<int32_arg_list, dods_int32>(arg_length_hint_value, $1);
          }
          | fast_int32_arg_list ',' fast_int32_arg
          {
              $$ = make_fast_arg_list<int32_arg_list, dods_int32>($1, $3);
          }
;

/* This rule does not check SCAN_WORD nor does it perform www escaping */
fast_int32_arg: SCAN_WORD
          {
              $$ = strtol($1, 0, 0);
          }
;

/* return an int_arg_list (a std::vector<int>*) */
fast_uint32_arg_list: fast_uint32_arg
          {
              $$ = make_fast_arg_list<uint32_arg_list, dods_uint32>(arg_length_hint_value, $1);
          }
          | fast_uint32_arg_list ',' fast_uint32_arg
          {
              $$ = make_fast_arg_list<uint32_arg_list, dods_uint32>($1, $3);
          }
;

/* This rule does not check SCAN_WORD nor does it perform www escaping */
fast_uint32_arg: SCAN_WORD
          {
              $$ = strtoul($1, 0, 0);
          }
;

/* return an int_arg_list (a std::vector<int>*) */
fast_float32_arg_list: fast_float32_arg
          {
              $$ = make_fast_arg_list<float32_arg_list, dods_float32>(arg_length_hint_value, $1);
          }
          | fast_float32_arg_list ',' fast_float32_arg
          {
              $$ = make_fast_arg_list<float32_arg_list, dods_float32>($1, $3);
          }
;

/* This rule does not check SCAN_WORD nor does it perform www escaping */
fast_float32_arg: SCAN_WORD
          {
              $$ = strtof($1, 0);
          }
;

/* return an int_arg_list (a std::vector<int>*) */
fast_float64_arg_list: fast_float64_arg
          {
              $$ = make_fast_arg_list<float64_arg_list, dods_float64>(arg_length_hint_value, $1);
          }
          | fast_float64_arg_list ',' fast_float64_arg
          {
              $$ = make_fast_arg_list<float64_arg_list, dods_float64>($1, $3);
          }
;

/* This rule does not check SCAN_WORD nor does it perform www escaping */
fast_float64_arg: SCAN_WORD
          {
              $$ = strtod($1, 0);
          }
;

/* This rule does not check SCAN_WORD nor does it perform www escaping */
proj_function:  SCAN_WORD '(' arg_list ')'
	    {
		    proj_func p_f = 0;
		    btp_func f = 0;

		    if ((f = get_btp_function(*(EVALUATOR(arg)), $1))) {
			    EVALUATOR(arg)->append_clause(f, $3);
			    $$ = true;
		    }
		    else if ((p_f = get_proj_function(*(EVALUATOR(arg)), $1))) { 
		        DDS &dds = dynamic_cast<DDS&>(*(DDS(arg)));
			    BaseType **args = build_btp_args( $3, dds );
			    (*p_f)(($3) ? $3->size():0, args, dds, *(EVALUATOR(arg)));
			    delete[] args;
			    $$ = true;
		    }
		    else {
			    no_such_func(arg, $1);
		    }
		}
;

selection:	clause
		| selection '&' clause
                {
		    $$ = $1 && $3;
		}
;

clause:		r_value rel_op '{' r_value_list '}'
                {
		    if ($1) {
			EVALUATOR(arg)->append_clause($2, $1, $4);
			$$ = true;
		    }
		    else
			$$ = false;
		}
		| r_value rel_op r_value
                {
		    if ($1) {
			rvalue_list *rv = new rvalue_list;
			rv->push_back($3);
			EVALUATOR(arg)->append_clause($2, $1, rv);
			$$ = true;
		    }
		    else
			$$ = false;
		}
		| bool_function
                {
		    $$ = $1;
		}
;

/* This rule does not check SCAN_WORD nor does it perform www escaping */
bool_function: SCAN_WORD '(' arg_list ')'
	       {
		   bool_func b_func = get_function((*EVALUATOR(arg)), $1);
		   if (!b_func) {
		       no_such_func(arg, $1);
		   }
		   else {
		       EVALUATOR(arg)->append_clause(b_func, $3);
		       $$ = true;
		   }
	       }
;

/* This rule does not check SCAN_WORD nor does it perform www escaping */
r_value: id_or_const
		| SCAN_WORD '(' arg_list ')'
		{
		    btp_func func = get_btp_function((*EVALUATOR(arg)), $1);
		    if (func) {
			    $$ = new rvalue(func, $3);
		    } 
		    else { 
			    no_such_func(arg, $1);
		    }
		}
        | array_const_special_form
        {
            $$ = $1;
        }
        | array_projection_rvalue
        {
            $$ = $1;
        }
;

r_value_list:	r_value
		{
		    if ($1)
			    $$ = make_rvalue_list($1);
		    else
			    $$ = 0;
		}
		| r_value_list ',' r_value
        {
		    if ($1 && $3)
			    $$ = append_rvalue_list($1, $3);
		    else
			    $$ = 0;
		}
;

arg_list: r_value_list
          {  
		      $$ = $1;
	      }
          | /* Null, argument lists may be empty */
          { 
		      $$ = 0; 
	      }
;

id_or_const: SCAN_WORD
		{ 
		    BaseType *btp = DDS(arg)->var(www2id($1));
		    if (btp) {
                btp->set_in_selection(true);
                $$ = new rvalue(btp);
		    }
		    else {
			    value new_val;
			    string name_tmp;
			    if (check_int32($1)) {
			        new_val.type = dods_int32_c;
			        new_val.v.i = atoi($1);
			    }
			    else if (check_uint32($1)) {
			        new_val.type = dods_uint32_c;
			        new_val.v.ui = atoi($1);
			    }
			    else if (check_float64($1)) {
			        new_val.type = dods_float64_c;
			        new_val.v.f = atof($1);
			    }
			    else {
			        new_val.type = dods_str_c;
				// The 'new' here was used because www2id() modifies the
				// std::string arg in place but 'value' holds a string*.
			        // new_val.v.s = new string(www2id($1));
				// I replcaed this with a local tmp to avoid the dynamic
				// allocation and the need to call delete. This was part of
				// the fix for ticket 2240. jhrg 7/30/14
				name_tmp = $1;
				name_tmp = www2id(name_tmp);
				new_val.v.s = &name_tmp;
			    }
			    BaseType *btp = make_variable((*EVALUATOR(arg)), new_val);
			    $$ = new rvalue(btp);
		    }
		}
        | SCAN_STR
                {
                    if ($1.type != dods_str_c || $1.v.s == 0 || $1.v.s->empty())
                        ce_exprerror(arg, "Malformed string", "");
                        
                    BaseType *var = DDS(arg)->var(www2id(*($1.v.s)));
                    if (var) {
                        $$ = new rvalue(var);
                    }
                    else {
                        var = make_variable((*EVALUATOR(arg)), $1); 
                        $$ = new rvalue(var);
                    }
		    // When the scanner (ce_expr.lex) returns the SCAN_STR token type
		    // it makes a local copy of the string in a new std::string object
		    // that we must delete. Fix for a bug report by Aron.Bartle@mechdyne.com
		    // See ticket 2240. jhrg 7/30/14
		    delete $1.v.s;
                }
;

/* this must return an rvalue. It should run bracket_projection() 
   and then return the BaseType of the Array wrapped in a RValue
   object. */
array_projection_rvalue : name array_indices
                {
                    if (!bracket_projection((*DDS(arg)), $1, $2))
                      no_such_ident(arg, $1, "array, grid or sequence");
                    
                    // strncpy($$, $1, ID_MAX-1);
                    // $$[ID_MAX-1] = '\0';

                    DDS(arg)->mark($1, true);
                    $$ = new rvalue(DDS(arg)->var($1));
                }
;

array_projection : array_proj_clause
                {
                    $$ = (*DDS(arg)).mark($1, true);
                }
;
                
array_proj_clause: name array_indices
                {
                    //string name = www2id($1);
                    if (!bracket_projection((*DDS(arg)), $1, $2))
                      no_such_ident(arg, $1, "array, grid or sequence");
                    
                    strncpy($$, $1, ID_MAX-1);
                    $$[ID_MAX-1] = '\0';
                }
                | array_proj_clause name
                {
                    string name = string($1) + string($2);
                    strncpy($$, name.c_str(), ID_MAX-1);
                    $$[ID_MAX-1] = '\0';
                }
                | array_proj_clause name array_indices
                {
                    string name = string($1) + string($2);
                    if (!bracket_projection((*DDS(arg)), name.c_str(), $3))
                      no_such_ident(arg, name.c_str(), "array, grid or sequence");

                    strncpy($$, name.c_str(), ID_MAX-1);
                    $$[ID_MAX-1] = '\0';
                }
;

name:           SCAN_WORD
                { 
                    strncpy($$, www2id($1).c_str(), ID_MAX-1);
                    $$[ID_MAX-1] = '\0';
                }
                | SCAN_STR
                {
                    if ($1.type != dods_str_c || $1.v.s == 0 || $1.v.s->empty())
                        ce_exprerror(arg, "Malformed string", "");
                        
                    strncpy($$, www2id(*($1.v.s)).c_str(), ID_MAX-1);
		    // See comment about regarding the scanner's behavior WRT SCAN_STR.
		    // jhrg 7/30/14
                    delete $1.v.s;

                    $$[ID_MAX-1] = '\0';
                }
;

array_indices:  array_index
                {
		    $$ = make_array_indices($1);
		}
                | array_indices array_index
                {
		    $$ = append_array_index($1, $2);
		}
;

/*
 * We added [*], [n:*] and [n:m:*] to the syntax for array projections.
 * These mean, resp., all the elements, elements from n to the end, and
 * from n to the end with a stride of m. To encode this with as little 
 * disruption as possible, we represent the star with -1. jhrg 12/20/12
 */ 
array_index:

'[' SCAN_WORD ']'
{
    if (!check_uint32($2))
        throw Error(malformed_expr, "The word `" + string($2) + "' is not a valid array index.");
    value i;
    i.type = dods_uint32_c;
    i.v.i = atoi($2);
    $$ = make_array_index(i);
}
|   '[' SCAN_STAR ']'
{
    value i;
    i.type = dods_int32_c;
    i.v.i =-1;
    $$ = make_array_index(i);
}
|'[' SCAN_WORD ':' SCAN_WORD ']'
{
    if (!check_uint32($2))
        throw Error(malformed_expr, "The word `" + string($2) + "' is not a valid array index.");
    if (!check_uint32($4))
        throw Error(malformed_expr, "The word `" + string($4) + "' is not a valid array index.");
    value i,j;
    i.type = j.type = dods_uint32_c;
    i.v.i = atoi($2);
    j.v.i = atoi($4);
    $$ = make_array_index(i, j);
}
|'[' SCAN_WORD ':' SCAN_STAR ']'
{
    if (!check_uint32($2))
        throw Error(malformed_expr, "The word `" + string($2) + "' is not a valid array index.");
    value i,j;
    i.type = dods_uint32_c;
    j.type = dods_int32_c;  /* signed */
    i.v.i = atoi($2);
    j.v.i = -1;
    $$ = make_array_index(i, j);
}
| '[' SCAN_WORD ':' SCAN_WORD ':' SCAN_WORD ']'
{
    if (!check_uint32($2))
        throw Error(malformed_expr, "The word `" + string($2) + "' is not a valid array index.");
    if (!check_uint32($4))
        throw Error(malformed_expr, "The word `" + string($4) + "' is not a valid array index.");
    if (!check_uint32($6))
        throw Error(malformed_expr, "The word `" + string($6) + "' is not a valid array index.");
    value i, j, k;
    i.type = j.type = k.type = dods_uint32_c;
    i.v.i = atoi($2);
    j.v.i = atoi($4);
    k.v.i = atoi($6);
    $$ = make_array_index(i, j, k);
}
| '[' SCAN_WORD ':' SCAN_WORD ':' SCAN_STAR ']'
{
    if (!check_uint32($2))
        throw Error(malformed_expr, "The word `" + string($2) + "' is not a valid array index.");
    if (!check_uint32($4))
        throw Error(malformed_expr, "The word `" + string($4) + "' is not a valid array index.");
    value i, j, k;
    i.type = j.type = dods_uint32_c;
    k.type = dods_int32_c;
    i.v.i = atoi($2);
    j.v.i = atoi($4);
    k.v.i = -1;
    $$ = make_array_index(i, j, k);
}
;

rel_op:		SCAN_EQUAL
		| SCAN_NOT_EQUAL
		| SCAN_GREATER
		| SCAN_GREATER_EQL
		| SCAN_LESS
		| SCAN_LESS_EQL
		| SCAN_REGEXP
;

%%

// All these error reporting function now throw instances of Error. The expr
// parser no longer returns an error code to indicate and error. 2/16/2000
// jhrg.

void
ce_exprerror(ce_parser_arg *, const string &s)
{
    string msg = "Constraint expression parse error: " +s;
    throw Error(malformed_expr, msg);
}

void ce_exprerror(ce_parser_arg *, const string &s, const string &s2)
{
    string msg = "Constraint expression parse error: " + s + ": " + s2;
    throw Error(malformed_expr, msg);    
}

void no_such_ident(ce_parser_arg *arg, const string &name, const string &word)
{
#if 0
    string msg = "No such " + word + " in dataset";
    ce_exprerror(arg, msg , name);
#endif
    string msg = "Constraint expression parse error: No such " + word + " in dataset: " + name;
    throw Error(no_such_variable, msg);    
    
}

void no_such_func(ce_parser_arg *arg, const string &name)
{
    ce_exprerror(arg, "Not a registered function", name);
}

/* If we're calling this, assume var is not a Sequence. But assume that the
 name contains a dot and it's a separator. Look for the rightmost dot and
 then look to see if the name to the left is a sequence. Return a pointer
 to the sequence if it is otherwise return null. Uses tail-recursion to
 'walk' back from right to left looking at each dot. This way the sequence
 will be found even if there are structures between the field and the
 Sequence. */
static Sequence *
parent_is_sequence(DDS &table, const string &n)
{
    string::size_type dotpos = n.find_last_of('.');
    if (dotpos == string::npos)
        return 0;

    string s = n.substr(0, dotpos);

    // If the thing returned by table.var is not a Sequence, this cast
    // will yield null.
    Sequence *seq = dynamic_cast<Sequence*> (table.var(s));
    if (seq)
        return seq;
    else
        return parent_is_sequence(table, s);
}

bool bracket_projection(DDS &table, const char *name, int_list_list *indices)
{
    BaseType *var = table.var(name);
    Sequence *seq; // used in last else-if clause

    if (!var)
        return false;

    if (is_array_t(var)) {
        /* calls to set_send_p should be replaced with
         calls to DDS::mark so that arrays of Structures,
         etc. will be processed correctly when individual
         elements are projected using short names.
         9/1/98 jhrg */
        /* var->set_send_p(true); */
        //table.mark(name, true);
        // We don't call mark() here for an array. Instead it is called from
        // within the parser. jhrg 10/10/08
        process_array_indices(var, indices); // throws on error
        delete_array_indices(indices);
    }
    else if (is_grid_t(var)) {
        process_grid_indices(var, indices);
        table.mark(name, true);
        delete_array_indices(indices);
    }
    else if (is_sequence_t(var)) {
        table.mark(name, true);
        process_sequence_indices(var, indices);
        delete_array_indices(indices);
    }
    else if ((seq = parent_is_sequence(table, name))) {
        process_sequence_indices(seq, indices);
        table.mark(name, true);
        delete_array_indices(indices);
    }
    else {
        return false;
    }

    return true;
}

// Given three values (I1, I2, I3), all of which must be integers, build an
// int_list which contains those values.
// 
// Note that we added support for * in the rightmost position of an index
// (i.e., [*], [n:*], [n:m:*]) and indicate that star using -1 as an index value.
// Bescause of this change, the test for the type of the rightmost value in
// the index subexpr was changed to include signed int.
// jhrg 12/20/12
//
// Returns: A pointer to an int_list of three integers or NULL if any of the
// values are not integers.

int_list *
make_array_index(value &i1, value &i2, value &i3)
{
    if (i1.type != dods_uint32_c || i2.type != dods_uint32_c || (i3.type != dods_uint32_c && i3.type != dods_int32_c))
        return (int_list *) 0;

    int_list *index = new int_list;

    index->push_back((int) i1.v.i);
    index->push_back((int) i2.v.i);
    index->push_back((int) i3.v.i);

    DBG(cout << "index: ");
    DBG(copy(index->begin(), index->end(), ostream_iterator<int>(cerr, " ")));
    DBG(cerr << endl);

    return index;
}

int_list *
make_array_index(value &i1, value &i2)
{
    if (i1.type != dods_uint32_c || (i2.type != dods_uint32_c && i2.type != dods_int32_c))
	return (int_list *) 0;

    int_list *index = new int_list;

    index->push_back((int) i1.v.i);
    index->push_back(1);
    index->push_back((int) i2.v.i);

    DBG(cout << "index: ");
    DBG(copy(index->begin(), index->end(), ostream_iterator<int>(cerr, " ")));
    DBG(cerr << endl);

    return index;
}

int_list *
make_array_index(value &i1)
{
    if (i1.type != dods_uint32_c && i1.type != dods_int32_c)
	return (int_list *) 0;

    int_list *index = new int_list;

    // When the CE is Array[*] that means all of the elements, but the value
    // of i1 will be -1. Make the projection triple be 0:1:-1 which is a 
    // pattern that libdap::Array will recognize.
    if (i1.v.i == -1)
        index->push_back(0);
    else
        index->push_back((int) i1.v.i);
    index->push_back(1);
    index->push_back((int) i1.v.i);

    DBG(cout << "index: ");
    DBG(copy(index->begin(), index->end(), ostream_iterator<int>(cerr, " ")));
    DBG(cerr << endl);

    return index;
}

int_list_list *
make_array_indices(int_list *index)
{
    int_list_list *indices = new int_list_list;

    DBG(cout << "index: ");
    DBG(copy(index->begin(), index->end(), ostream_iterator<int>(cerr, " ")));
    DBG(cerr << endl);

    assert(index);
    indices->push_back(index);

    return indices;
}

int_list_list *
append_array_index(int_list_list *indices, int_list *index)
{
    assert(indices);
    assert(index);

    indices->push_back(index);

    return indices;
}

// Delete an array indices list. 

void delete_array_indices(int_list_list *indices)
{
    assert(indices);

    for (int_list_citer i = indices->begin(); i != indices->end(); i++) {
	int_list *il = *i;
	assert(il);
	delete il;
    }

    delete indices;
}

bool is_array_t(BaseType *variable)
{
    assert(variable);

    if (variable->type() != dods_array_c)
	return false;
    else
	return true;
}

bool is_grid_t(BaseType *variable)
{
    assert(variable);

    if (variable->type() != dods_grid_c)
	return false;
    else
	return true;
}

bool is_sequence_t(BaseType *variable)
{
    assert(variable);

    if (variable->type() != dods_sequence_c)
	return false;
    else
	return true;
}

void process_array_indices(BaseType *variable, int_list_list *indices)
{
    assert(variable);

    Array *a = dynamic_cast<Array *>(variable); // replace with dynamic cast
    if (!a)
        throw Error(malformed_expr,
		    string("The constraint expression evaluator expected an array; ") + variable->name() + " is not an array.");

    if (a->dimensions(true) != (unsigned) indices->size())
        throw Error(malformed_expr,
		    string("Error: The number of dimensions in the constraint for ") + variable->name()
                    + " must match the number in the array.");

    DBG(cerr << "Before applying projection to array:" << endl);
    DBG(a->print_decl(cerr, "", true, false, true));

    assert(indices);
    
    int_list_citer p = indices->begin();
    Array::Dim_iter r = a->dim_begin();
    for (; p != indices->end() && r != a->dim_end(); p++, r++) {
        int_list *index = *p;
        assert(index);

        int_citer q = index->begin();
        assert(q != index->end());
        int start = *q;

        q++;
        int stride = *q;

        q++;
        int stop = *q;

        q++;
        if (q != index->end())
            throw Error(malformed_expr, string("Too many values in index list for ") + a->name() + ".");

        DBG(cerr << "process_array_indices: Setting constraint on " 
            << a->name() << "[" << start << ":" << stop << "]"
            << endl);

        // It's possible that an array will appear more than once in a CE
        // (for example, if an array of structures is constrained so that
        // only two fields are projected and there's an associated hyperslab).
        // However, in this case the two hyperslabs must be equal; test for
        // that here.
        //
        // We added '*' to mean 'the last element' in the array and use an index of -1
        // to indicate that. If 'stop' is -1, don't test it here because dimension_stop()
        // won't be -1 but the actual ending index of the array. jhrg 12/20/12

        if (a->send_p() && (a->dimension_start(r, true) != start || (a->dimension_stop(r, true) != stop && stop != -1)
                            || a->dimension_stride(r, true) != stride))
            throw Error(malformed_expr,
                    string("The Array was already projected differently in the constraint expression: ") + a->name() + ".");

        a->add_constraint(r, start, stride, stop);

        DBG(cerr << "Set Constraint: " << a->dimension_size(r, true) << endl);
    }

    DBG(cerr << "After applying projection to array:" << endl);
    DBG(a->print_decl(cerr, "", true, false, true));


    if (p != indices->end() && r == a->dim_end())
        throw Error(malformed_expr, string("Too many indices in constraint for ") + a->name() + ".");
}

void process_grid_indices(BaseType *variable, int_list_list *indices)
{
    assert(variable);
    assert(variable->type() == dods_grid_c);
    Grid *g = dynamic_cast<Grid *>(variable);
    if (!g)
        throw Error(unknown_error, "Expected a Grid variable");

    Array *a = g->get_array();

    if (a->dimensions(true) != (unsigned) indices->size())
        throw Error(malformed_expr,
		    string("Error: The number of dimensions in the constraint for ") + variable->name()
		    + " must match the number in the grid.");

    // First do the constraints on the ARRAY in the grid.
    process_array_indices(g->array_var(), indices);

    // Now process the maps.
    Grid::Map_iter r = g->map_begin();

    // Suppress all maps by default.
    for (; r != g->map_end(); r++) {
        (*r)->set_send_p(false);
    }

    // Add specified maps to the current projection.
    assert(indices);
    int_list_citer p = indices->begin();
    r = g->map_begin();
    for (; p != indices->end() && r != g->map_end(); p++, r++) {
        int_list *index = *p;
        assert(index);

        int_citer q = index->begin();
        assert(q != index->end());
        int start = *q;

        q++;
        int stride = *q;

        q++;
        int stop = *q;

        BaseType *btp = *r;
        assert(btp);
        assert(btp->type() == dods_array_c);
        Array *a = (Array *) btp;
        a->set_send_p(true);
        a->reset_constraint();

        q++;
        if (q != index->end()) {
            throw Error(malformed_expr, string("Too many values in index list for ") + a->name() + ".");
        }

        DBG(cerr << "process_grid_indices: Setting constraint on "
            << a->name() << "[" << start << ":"
            << stop << "]" << endl);
    
        Array::Dim_iter si = a->dim_begin();
        a->add_constraint(si, start, stride, stop);

    }

    DBG(cout << "Grid Constraint: ";
	for (Array::Dim_iter dp = ((Array *) g->array_var())->dim_begin();
	     dp != ((Array *) g->array_var())->dim_end(); dp++)
	    cout << ((Array *) g->array_var())->dimension_size(dp, true) << " ";
	    cout << endl
	);

    if (p != indices->end() && r == g->map_end()) {
        throw Error(malformed_expr,
		    string("Too many indices in constraint for ") + (*r)->name() + ".");
    }
}

void process_sequence_indices(BaseType *variable, int_list_list *indices)
{
    assert(variable);
    assert(variable->type() == dods_sequence_c);
    Sequence *s = dynamic_cast<Sequence *> (variable);
    if (!s)
        throw Error(malformed_expr, "Expected a Sequence variable");

    // Add specified maps to the current projection.
    assert(indices);
    for (int_list_citer p = indices->begin(); p != indices->end(); p++) {
        int_list *index = *p;
        assert(index);

        int_citer q = index->begin();
        assert(q != index->end());
        int start = *q;

        q++;
        int stride = *q;

        q++;
        int stop = *q;

        q++;
        if (q != index->end()) {
            throw Error(malformed_expr, string("Too many values in index list for ") + s->name() + ".");
        }

        s->set_row_number_constraint(start, stop, stride);
    }
}

// Given a value, wrap it up in a BaseType and return a pointer to the same.

BaseType *
make_variable(ConstraintEvaluator &eval, const value &val)
{
    BaseType *var;
    switch (val.type) {
    case dods_int32_c: {
        var = new Int32("dummy");
        var->val2buf((void *) &val.v.i);
        break;
    }

    case dods_uint32_c: {
        var = new UInt32("dummy");
        var->val2buf((void *) &val.v.i);
        break;
    }

    case dods_float64_c: {
        var = new Float64("dummy");
        var->val2buf((void *) &val.v.f);
        break;
    }

    case dods_str_c: {
        var = new Str("dummy");
        var->val2buf((void *) val.v.s);
        break;
    }

    default:
        var = (BaseType *) 0;
        return var;
    }

    var->set_read_p(true); // ...so the evaluator will know it has data
    eval.append_constant(var);

    return var;
}

// Given a string (passed in VAL), consult the DDS CE function lookup table
// to see if a function by that name exists. 
// NB: function arguments are type-checked at run-time.
//
// Returns: A pointer to the function or NULL if not such function exists.

bool_func get_function(const ConstraintEvaluator &eval, const char *name)
{
    bool_func f;

    if (eval.find_function(name, &f))
        return f;
    else
        return 0;
}

btp_func get_btp_function(const ConstraintEvaluator &eval, const char *name)
{
    btp_func f;

    if (eval.find_function(name, &f))
        return f;
    else
        return 0;
}

proj_func get_proj_function(const ConstraintEvaluator &eval, const char *name)
{
    proj_func f;

    if (eval.find_function(name, &f))
        return f;
    else
        return 0;
}

template<class arg_type_list, class arg_type>
arg_type_list
make_fast_arg_list(unsigned long vector_size_hint, arg_type value)
{
    arg_type_list args = new std::vector<arg_type>;
    
    if (vector_size_hint > 0) args->reserve(vector_size_hint);
    
    args->push_back(value);
    return args;
}

template<class arg_type_list, class arg_type>
arg_type_list
make_fast_arg_list(arg_type_list values, arg_type value)
{
    values->push_back(value);
    return values;
}

template<class t, class T>
rvalue *build_constant_array(vector<t> *values, DDS *dds)
{
    //vector<t> *values = $5;
            
    T i("");
    Array *array = new Array("", &i);
    array->append_dim(values->size());
    
    // TODO Make set_value_nocopy() methods so that values' pointers can be copied
    // instead of allocating memory twice. jhrg 7/5/13
            
    array->set_value(*values, values->size());
    delete values;
    array->set_read_p(true);
            
    static unsigned long counter = 1;
    string name;
    do {
        name = "g" + long_to_string(counter++);
    } while (dds->var(name));
    array->set_name(name);
            
    return new rvalue(array);
}

