/*
* Copyright(C) 1999-2020 National Technology & Engineering Solutions
* of Sandia, LLC (NTESS).  Under the terms of Contract DE-NA0003525 with
* NTESS, the U.S. Government retains certain rights in this software.
*
* See packages/seacas/LICENSE for details
*/

%{
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>

#include <sstream>
#include <vector>
#include <cmath>
#include "PhactoriParserInterface.h"
#include "vtk_jsoncpp.h"

namespace {

  class ParseData
  {
  public:
    ParseData()
      {
      this->root = Json::Value(Json::objectValue);
      this->ims_shortcut = Json::Value(Json::objectValue);
      this->separator = "_";
      this->checkNodeIDRange = false;
      this->checkElementIDRange = false;
      }
    
     ~ParseData()
       {
       this->root.clear();
       this->ims_shortcut.clear();
       this->separator.clear();
       this->cbn.clear();
       this->d.clear();
       }

    Json::Value root;
    Json::Value ims_shortcut;
    int cb;
    std::string cbn;
    std::vector<double> d;
    std::vector<int> intValues;
    std::vector<std::string> s;
    int count;
    Iovs::PhactoriParserInterface::varMap nodeVars;
    Iovs::PhactoriParserInterface::varMap elementVars;
    Iovs::PhactoriParserInterface::varMap globalVars;
    bool checkNodeIDRange;
    Iovs::PhactoriParserInterface::idRange nodeIDRange;
    bool checkElementIDRange;
    Iovs::PhactoriParserInterface::idRange elementIDRange;
    std::string separator;
  };

  ParseData& c(void* pd) {
    return *(static_cast<ParseData*>(pd));
  }
 
  std::vector<std::string> getTokensBeforeEqualsAsLowerCase(
      const std::string & line);
  std::string getStringAfterEquals(const std::string & line);
  std::string getStringBeforeEquals(const std::string & line);
  void toLowerCase(std::string & s);
  bool stringStartsAndEndsWithQuotes(const std::string & s);
  bool containsPhactoriCommandThatNeedsQuotes(std::vector<std::string>& tl);
  void addQuotesToString(std::string & s);
  std::string trim(const std::string& s);

  std::string preProcessPhactoriInput(const std::string & input) {
      std::istringstream iss(input);

      std::string result;
      for (std::string line; std::getline(iss, line); ) {
          std::vector<std::string> tl = getTokensBeforeEqualsAsLowerCase(line);
          std::string ae = getStringAfterEquals(line);
          if(containsPhactoriCommandThatNeedsQuotes(tl) &&
              !stringStartsAndEndsWithQuotes(ae)) {
              addQuotesToString(ae);
              result += (getStringBeforeEquals(line) + "=" + ae + "\n");
          }
          else {
              result += line + "\n";
          }
      }
      return result;
  }

  std::vector<std::string> getTokensBeforeEqualsAsLowerCase(
      const std::string & line) {

      std::vector<std::string> tl;
      std::istringstream iss(line);
      std::string token;
      while(iss >> token) {
         if (token == "=") {
             break;
         }
         else {
             toLowerCase(token);
             tl.push_back(token);
         }
      }
      return tl;
  }

  std::string getStringAfterEquals(const std::string & line) {
      return line.substr(line.find("=") + 1);
  }

  std::string getStringBeforeEquals(const std::string & line) {
      return line.substr(0, line.find("="));
  }

  void toLowerCase(std::string & s) {
      std::transform(s.begin(), s.end(), s.begin(), ::tolower);
  }

  bool stringStartsAndEndsWithQuotes(const std::string & s) {
      size_t first = s.find_first_not_of(' ');
      bool startWithQuote = s.rfind("\"", first) == first;
      size_t last = s.find_last_not_of(' ');
      bool endsWithQuote = s.rfind("\"", last) == last;
      return startWithQuote && endsWithQuote;
  }

  void addQuotesToString(std::string & s) {
      s = "\"" + trim(s) + "\"";
  }

  std::string trim(const std::string& s) {
    size_t first = s.find_first_not_of(' ');
    if (std::string::npos == first) {
        return s;
    }
    size_t last = s.find_last_not_of(' ');
    return s.substr(first, (last - first + 1));
  }

  bool containsPhactoriCommandThatNeedsQuotes(std::vector<std::string>& tl) {
      if (tl.size() == 1 && tl[0] == "function") {
          return true;
      }
      else if (tl.size() == 2 && (tl[0] == "plot" || tl[0] == "image") &&
          tl[1] == "basename") {
          return true;
      }
      else if (tl.size() == 2 && (tl[0] == "plot" || tl[0] == "image") &&
          tl[1] == "basedirectory") {
          return true;
      }
      else if (tl.size() == 3 && tl[0] == "image" && tl[1] == "name" &&
          tl[2] == "addon") {
          return true;
      }
      else if (tl.size() == 4 && (tl[0] == "x" || tl[0] == "y" ||
          tl[0] == "z") && tl[1] == "axis" && tl[2] == "label" &&
          tl[3] == "name") {
          return true;
      }
      return false;
  }

} // End anonymous namespace

typedef struct yy_buffer_state * YY_BUFFER_STATE;
typedef void*                 yyscan_t;
int yyparse(yyscan_t scanner, void* pd);

#include "phactoriParser.tab.hpp"

extern "C"
{
  YY_BUFFER_STATE catalyst_scan_string(const char * str, yyscan_t yyscanner);
  void catalyst_delete_buffer(YY_BUFFER_STATE buffer, yyscan_t yyscanner);
  int yylex(YYSTYPE *yylval_param, YYLTYPE * llocp, yyscan_t yyscanner);
  int catalystlex_init ( yyscan_t * ptr_yy_globals ) ;
  int catalystlex_destroy ( yyscan_t yyscanner ) ;
  void catalystset_in( FILE * in_str , yyscan_t scanner );

  int catalystwrap()
    {
    return 1;
    }
}

namespace {

void yyerror (YYLTYPE * llocp, yyscan_t yyscanner, void* pd, char const *msg);
bool check_variable_name(YYLTYPE * llocp,
                         yyscan_t yyscanner,
                         ParseData& pd,
                         const std::string& name,
                         Iovs::PhactoriParserInterface::VarType vt);


bool follow_chain(ParseData& pd,
                  std::string& n,
                  Json::Value& nl)
{
  nl[n] = true;
  while(1)
    {
    if(pd.root["operation blocks"][n].isMember("input"))
      {
      n = pd.root["operation blocks"][n]["input"].asString();
      if(nl.isMember(n))
        return true;
      else
        nl[n] = true;
      }
    else if(pd.root["operation blocks"][n].isMember("operation group list"))
      {
      Json::Value op_names = pd.root["operation blocks"][n]["operation group list"];
      for( Json::ValueIterator nitr = op_names.begin() ;
                               nitr != op_names.end() ; nitr++)
        {
        n = (*nitr).asString();
        if(nl.isMember(n))
          return true;
        else
          nl[n] = true;
        }
      }
    else
      break;
    }
  return false;
}

bool has_cycle(ParseData& pd,
               Json::ValueIterator itr,
               const std::string& key)
{
  if((*itr).isMember(key))
    {
    if((*itr).isMember("operation group list"))
      {
      Json::Value op_names = (*itr)["operation group list"];
      for( Json::ValueIterator nitr = op_names.begin() ;
                               nitr != op_names.end() ; nitr++)
        {
        Json::Value nl(Json::objectValue);
        std::string n = (*nitr).asString();
        if(follow_chain(pd, n, nl))
          return true;
        }
      }
    else
      {
      Json::Value nl(Json::objectValue);
      std::string n = (*itr)[key].asString();
      if(follow_chain(pd, n, nl))
        return true;
      }
    }
  return false;
}

bool check_cycles(YYLTYPE * llocp, yyscan_t yyscanner, ParseData& pd)
{
  for(Json::ValueIterator itr = pd.root["operation blocks"].begin() ;
                          itr != pd.root["operation blocks"].end() ; itr++)
    {
    if(has_cycle(pd, itr, "input") ||
       has_cycle(pd, itr, "operation group list"))
      {
      std::string s = "Error: operation (" + itr.key().asString() + ") contains an operation cycle";
      yyerror(llocp, yyscanner, &pd, s.c_str());
      return false;
      }
    }

  for(Json::ValueIterator itr = pd.root["imageset blocks"].begin() ;
                          itr != pd.root["imageset blocks"].end() ; itr++)
    {
    if(has_cycle(pd, itr, "operation"))
      {
      std::string s = "Error: imageset (" + itr.key().asString() + ") contains an operation cycle";
      yyerror(llocp, yyscanner, &pd, s.c_str());
      return false;
      }
    }

  for(Json::ValueIterator itr = pd.root["scatter plot blocks"].begin() ;
                          itr != pd.root["scatter plot blocks"].end() ; itr++)
    {
    if(has_cycle(pd, itr, "operation"))
      {
      std::string s = "Error: scatter plot (" + itr.key().asString() + ") contains an operation cycle";
      yyerror(llocp, yyscanner, &pd, s.c_str());
      return false;
      }
    }

  for(Json::ValueIterator itr = pd.root["plot over time blocks"].begin() ;
                          itr != pd.root["plot over time blocks"].end() ; itr++)
    {
    if(has_cycle(pd, itr, "operation"))
      {
      std::string s = "Error: plot over time (" + itr.key().asString() + ") contains an operation cycle";
      yyerror(llocp, yyscanner, &pd, s.c_str());
      return false;
      }
    }

  return true;
}

bool check_names(YYLTYPE * llocp, yyscan_t yyscanner, ParseData& pd)
{
  if(pd.ims_shortcut.size())
    return true;


  /* CHECK IMAGESETS */

  for(Json::ValueIterator itr = pd.root["imageset blocks"].begin() ;
                          itr != pd.root["imageset blocks"].end() ; itr++)
    {
    if((*itr).isMember("camera"))
      {
      std::string n = (*itr)["camera"].asString();
      if(!pd.root["camera blocks"].isMember(n))
        {
        std::string s = "Error: imageset (" + itr.key().asString() + ") references camera ("
                                            + n + ") which does not exist";
        yyerror(llocp, yyscanner, &pd, s.c_str());
        return false;
        }
      }

    if((*itr).isMember("representation"))
      {
      std::string n = (*itr)["representation"].asString();
      if(!pd.root["representation blocks"].isMember(n))
        {
        std::string s = "Error: imageset (" + itr.key().asString() + ") references representation ("
                                            + n + ") which does not exist";
        yyerror(llocp, yyscanner, &pd, s.c_str());
        return false;
        }
      }

    if((*itr).isMember("operation"))
      {
      std::string n = (*itr)["operation"].asString();
      if(!pd.root["operation blocks"].isMember(n))
        {
        std::string s = "Error: imageset (" + itr.key().asString() + ") references operation ("
                                            + n + ") which does not exist";
        yyerror(llocp, yyscanner, &pd, s.c_str());
        return false;
        }
      }
    }

  /* CHECK OPERATIONS */

  for(Json::ValueIterator itr = pd.root["operation blocks"].begin() ;
                          itr != pd.root["operation blocks"].end() ; itr++)
    {
    std::string n;
    bool error = false;
    if((*itr).isMember("input"))
      {
      n = (*itr)["input"].asString();
      if(!pd.root["operation blocks"].isMember(n))
        error = true;
      }
    else if((*itr).isMember("operation group list"))
      {
      Json::Value op_names = (*itr)["operation group list"];
      for( Json::ValueIterator nitr = op_names.begin() ;
                               nitr != op_names.end() ; nitr++ )
        {
        n = (*nitr).asString();
        if(!pd.root["operation blocks"].isMember(n))
          {
          error = true;
          break;
          }
        }
      }
    if(error)
      {
      std::string s = "Error: operation (" + itr.key().asString() + ") references operation ("
                                           + n + ") which does not exist";
      yyerror(llocp, yyscanner, &pd, s.c_str());
      return false;
      }
    }

  /* CHECK SCATTER PLOTS */

  for(Json::ValueIterator itr = pd.root["scatter plot blocks"].begin() ;
                          itr != pd.root["scatter plot blocks"].end() ; itr++)
    {
    if((*itr).isMember("operation"))
      {
      std::string n = (*itr)["operation"].asString();
      if(!pd.root["operation blocks"].isMember(n))
        {
        std::string s = "Error: scatter plot (" + itr.key().asString() + ") references operation ("
                                                + n + ") which does not exist";
        yyerror(llocp, yyscanner, &pd, s.c_str());
        return false;
        }
      }
    }

  /* CHECK PLOT OVER TIME */

  for(Json::ValueIterator itr = pd.root["plot over time blocks"].begin() ;
                          itr != pd.root["plot over time blocks"].end() ; itr++)
    {
    if((*itr).isMember("operation"))
      {
      std::string n = (*itr)["operation"].asString();
      if(!pd.root["operation blocks"].isMember(n))
        {
        std::string s = "Error: plot over time (" + itr.key().asString() + ") references operation ("
                                                  + n + ") which does not exist";
        yyerror(llocp, yyscanner, &pd, s.c_str());
        return false;
        }
      }
    }

  return true;
}

bool check_color_name(YYLTYPE * llocp, yyscan_t yyscanner, ParseData& pd, const std::string& cn)
{
  std::string bn = cn;
  std::transform(bn.begin(), bn.end(), bn.begin(), ::tolower);
  if(bn == "default" ||
     bn == "cool_to_warm" ||
     bn == "blue_to_red_rainbow" ||
     bn == "blue_to_blue_rainbow" ||
     bn == "grayscale" ||
     bn == "x_ray" ||
     bn == "blue_to_yellow" ||
     bn == "black_body_radiation" ||
     bn == "cielab_blue_to_red_black" ||
     bn == "blue_and_white" ||
     bn == "black" ||
     bn == "orange_and_white" ||
     bn == "cold_and_hot" ||
     bn == "rainbow_desaturated" ||
     bn == "rainbow_blended_white" ||
     bn == "rainbow_blended_grey")
    {
    return true;
    }
  yyerror(llocp, yyscanner, &pd, ("Error: invalid preset color name " + cn).c_str());
  return false;
}

bool check_plot_axes(YYLTYPE * llocp, 
                     yyscan_t yyscanner, 
                     ParseData& pd, 
                     const Json::Value& c, 
                     const std::string& axis)
{
  if(c.isMember(axis + " axis range") && (c.isMember(axis + " axis use current data range") ||
                                    c.isMember(axis + " axis use cumulative data range") ||
                                    c.isMember(axis + " axis minimum range") ||
                                    c.isMember(axis + " axis maximum range")) )
    {
      std::string s = "Plot block contains '" + axis + " axis range' and additional commands\n"
                      "to specify the axis. '" + axis + " axis range' must be used\n"
                      "without other axis commands\n";
      yyerror(llocp, yyscanner, &pd, s.c_str());
      return false;
    }

  if(c[axis + " axis use current data range"].asBool() &&
     c[axis + " axis use cumulative data range"].asBool() )
    {
    std::string s = "Plot block contains '" + axis + " axis use current data range' and\n"
                    "'" + axis + " axis use cumulative data range'.  Can only specify one\n"
                    "of these commands.\n";
    yyerror(llocp, yyscanner, &pd, s.c_str());
    return false;
    }

  if(c.isMember(axis + " axis minimum range") &&
     c.isMember(axis + " axis maximum range") )
    {
    if(c[axis + " axis minimum range"][1] >
       c[axis + " axis maximum range"][0] )
      {
      yyerror(llocp, yyscanner, &pd, ("Error: plot block contains invalid minimum and maximum " + axis + " axis ranges").c_str());
      return false;
      }
    }

  return true;
}

bool check_calculator_function_variable_names(YYLTYPE * llocp,
                                              yyscan_t yyscanner,
                                              ParseData& pd,
                                              const Json::Value& c,
                                              Iovs::PhactoriParserInterface::varMap& vm)
{

  for(Iovs::PhactoriParserInterface::varMap::iterator it=vm.begin();
      it != vm.end();
      ++it)
    {
    std::size_t found = c["function"][0].asString().find(it->first);
    if(found != std::string::npos)
      if(!check_variable_name(llocp, yyscanner, pd, it->first, it->second))
        return false;
    }

  return true;
}

bool check_calculator_block(YYLTYPE * llocp,
                            yyscan_t yyscanner,
                            ParseData& pd,
                            const Json::Value& c)
{
  if(!c.isMember("function"))
    {
    yyerror(llocp, yyscanner, &pd, "Error: calculator block does not contain a 'function' statement");
    return false;
    }

  if(!c.isMember("output variable name"))
    {
    yyerror(llocp, yyscanner, &pd, "Error: calculator block does not contain a 'output variable name' statement");
    return false;
    }

  if(!c.isMember("element or node data"))
    {
    yyerror(llocp, yyscanner, &pd, "Error: calculator block does not contain a 'element or node data' statement");
    return false;
    }

  if(c["element or node data"].asString() == "node" && !pd.nodeVars.empty())
    {
    pd.nodeVars[c["output variable name"].asString()] = Iovs::PhactoriParserInterface::ALLTYPE;
    if(!check_calculator_function_variable_names(llocp, yyscanner, pd, c, pd.nodeVars))
      {
      yyerror(llocp, yyscanner, &pd, "Error: calculator function contains unknown variable name.");
      return false;
      }
    }
  else if(c["element or node data"].asString() == "element" && !pd.elementVars.empty())
    {
    pd.elementVars[c["output variable name"].asString()] =  Iovs::PhactoriParserInterface::ALLTYPE;
    if(!check_calculator_function_variable_names(llocp, yyscanner, pd, c, pd.elementVars))
      {
      yyerror(llocp, yyscanner, &pd, "Error: calculator function contains unknown variable name.");
      return false;
      }
    }

  return true;
}

bool check_plot_over_time_block(YYLTYPE * llocp,
                                yyscan_t yyscanner,
                                ParseData& pd,
                                const Json::Value& c)
{
  int count = 0;
  if(c.isMember("all variables")) count++;
  if(c.isMember("variable scalar")) count++;
  if(c.isMember("variable vector magnitude")) count++;
  if(c.isMember("variable vector component")) count++;
  if(c.isMember("variable tensor component")) count++;

  if(count > 1)
    {
    yyerror(llocp, yyscanner, &pd, "Error: plot over time block contains multiple 'variable ...' commands, only one allowed");
    return false;
    }

  if(!count)
    {
    yyerror(llocp, yyscanner, &pd, "Error: plot over time block contains variable to plot, at least one variable required");
    return false;
    }

  bool ret = true;
  ret = ret && check_plot_axes(llocp, yyscanner, pd, c, "y");

  return ret;
}

bool check_scatter_plot_block(YYLTYPE * llocp,
                              yyscan_t yyscanner,
                              ParseData& pd,
                              const Json::Value& c)
{
  int all_variables_count = 0;
  int x_axis_variable_count = 0;
  int y_axis_variable_count = 0;
  if(c.isMember("all variables")) all_variables_count++;
  if(c.isMember("x axis variable scalar")) x_axis_variable_count++;
  if(c.isMember("y axis variable scalar")) y_axis_variable_count++;
  if(c.isMember("x axis variable vector magnitude")) x_axis_variable_count++;
  if(c.isMember("y axis variable vector magnitude")) y_axis_variable_count++;
  if(c.isMember("x axis variable vector component")) x_axis_variable_count++;
  if(c.isMember("y axis variable vector component")) y_axis_variable_count++;
  if(c.isMember("x axis variable tensor component")) x_axis_variable_count++;
  if(c.isMember("y axis variable tensor component")) y_axis_variable_count++;

  if( all_variables_count &&
     (x_axis_variable_count || y_axis_variable_count) )
    {
    std::string s =  "Scatter Plot block contains the 'all variables' command with \n"
                      "other '<x|y> axis variable ...' commands. Only one allowed.";
    yyerror(llocp, yyscanner, &pd, s.c_str());
    return false;
    }
  else if(x_axis_variable_count > 1)
    {
    std::string s = "Scatter Plot block contains more than one 'x axis variable ...' \n"
                    "command. Only one allowed.";
    yyerror(llocp, yyscanner, &pd, s.c_str());
    return false;
    }
  else if(y_axis_variable_count > 1)
    {
    std::string s = "Scatter Plot block contains more than one 'y axis variable ...' \n"
                    "command. Only one allowed.";
    yyerror(llocp, yyscanner, &pd, s.c_str());
    return false;
    }

  if(all_variables_count == 0 &&
     y_axis_variable_count == 0)
    {
    yyerror(llocp, yyscanner, &pd, "Scatter Plot block contains no y data to plot.");
    return false;
    }

  bool ret = true;
  ret = ret && check_plot_axes(llocp, yyscanner, pd, c, "x");
  ret = ret && check_plot_axes(llocp, yyscanner, pd, c, "y");

  return ret;
}

bool check_contour(YYLTYPE * llocp,
                   yyscan_t yyscanner,
                   ParseData& pd,
                   const Json::Value& c)
{
  int count = 0;
  if(c.isMember("contour value")) count++;
  if(c.isMember("contour value sequence")) count++;

  if(!count)
    {
    yyerror(llocp, yyscanner, &pd, "Error: contour operation contains no 'contour value ...' commands, one required");
    return false;
    }

  count = 0;
  if(c.isMember("variable scalar")) count++;
  if(c.isMember("variable vector magnitude")) count++;
  if(c.isMember("variable vector component")) count++;
  if(c.isMember("variable tensor component")) count++;

  if(count > 1)
    {
    yyerror(llocp, yyscanner, &pd, "Error: contour operation contains multiple 'variable ...' commands, only one allowed");
    return false;
    }

  if(!count)
    {
    yyerror(llocp, yyscanner, &pd, "Error: contour operation contains no 'variable ...' commands, one is required");
    return false;
    }

  return true;
}

bool check_threshold(YYLTYPE * llocp,
                     yyscan_t yyscanner,
                     ParseData& pd,
                     const Json::Value& c)
{
  int count = 0;
  if(c.isMember("keep between")) count++;
  if(c.isMember("keep above")) count++;
  if(c.isMember("keep below")) count++;

  if(count > 1)
    {
    yyerror(llocp, yyscanner, &pd, "Error: threshold operation contains multiple 'keep ...' commands, only one allowed");
    return false;
    }

  if(!count)
    {
    yyerror(llocp, yyscanner, &pd, "Error: threshold operation contains no 'keep ...' commands, one is required");
    return false;
    }

  count = 0;
  if(c.isMember("variable scalar")) count++;
  if(c.isMember("variable vector magnitude")) count++;
  if(c.isMember("variable vector component")) count++;
  if(c.isMember("variable tensor component")) count++;

  if(count > 1)
    {
    yyerror(llocp, yyscanner, &pd, "Error: threshold operation contains multiple 'variable ...' commands, only one allowed");
    return false;
    }

  if(!count)
    {
    yyerror(llocp, yyscanner, &pd, "Error: threshold operation contains no 'variable ...' commands, one is required");
    return false;
    }

  return true;
}

bool check_box_clip(YYLTYPE * llocp,
                    yyscan_t yyscanner,
                    ParseData& pd,
                    const Json::Value& c)
{
  int count = 0;
  if(c.isMember("center at absolute point")) count++;
  if(c.isMember("center at relative point")) count++;
  if(c.isMember("center at node")) count++;
  if(c.isMember("center at element")) count++;

  if(count > 1)
    {
    yyerror(llocp, yyscanner, &pd, "Error: box clip operation contains multiple 'center at ...' commands, only one allowed");
    return false;
    }

  count = 0;
  if(c.isMember("absolute extents")) count++;
  if(c.isMember("relative extents")) count++;

  if(count > 1)
    {
    yyerror(llocp, yyscanner, &pd, "Error: box clip operation contains both 'absolute extents' and 'relative extents' commands, only one allowed");
    return false;
    }

  return true;
}

bool check_clip_or_slice(YYLTYPE * llocp,
                         yyscan_t yyscanner,
                         ParseData& pd,
                         const Json::Value& c)
{
  int count = 0;
  if(c.isMember("absolute point on plane")) count++;
  if(c.isMember("relative point on plane")) count++;
  if(c.isMember("node on plane")) count++;
  if(c.isMember("element on plane")) count++;

  if(count > 1)
    {
    if(pd.cb == TOKCLIP)
      yyerror(llocp, yyscanner, &pd, "Error: clip operation contains multiple '... point on plane' commands, only one allowed");
    else if(pd.cb == TOKSLICE)
      yyerror(llocp, yyscanner, &pd, "Error: slice operation contains multiple '... point on plane' commands, only one allowed");
    return false;
    }
  return true;
}

bool check_camera(YYLTYPE * llocp,
                  yyscan_t yyscanner,
                  ParseData& pd,
                  const Json::Value& c)
{
  if(c.isMember("look at relative distance") &&
     c.isMember("look at absolute distance"))
    {
    yyerror(llocp, yyscanner, &pd, "Error: camera block contains both 'look at relative distance' and 'look at absolute distance'");
    return false;
    }

  int look_at_count = 0;
  if(c.isMember("look at absolute point")) look_at_count++;
  if(c.isMember("look at relative point")) look_at_count++;
  if(c.isMember("look at element")) look_at_count++;
  if(c.isMember("look at node")) look_at_count++;

  if(look_at_count > 1)
    {
    yyerror(llocp, yyscanner, &pd, "Error: camera block contains multiple 'look at .. ' operations, only one allowed");
    return false;
    }

  if(!c.isMember("camera type") ||
      c["camera type"].asString() == "multicamera8")
    return true;

  int camera_at_count = 0;
  if(c.isMember("camera at relative point")) camera_at_count++;
  if(c.isMember("camera at absolute point")) camera_at_count++;
  if(c.isMember("camera at element")) camera_at_count++;
  if(c.isMember("camera at node")) camera_at_count++;
  if(c.isMember("camera at element displaced")) camera_at_count++;
  if(c.isMember("camera at node displaced")) camera_at_count++;

  if(camera_at_count > 1)
    {
    yyerror(llocp, yyscanner, &pd, "Error: camera block contains multiple 'camera at ... ' operations, only one allowed");
    return false;
    }

  bool has_look_direction = c.isMember("look direction");
  bool has_camera_position = camera_at_count >= 1;
  bool has_look_at_point = look_at_count >= 1;
  bool has_camera_distance = c.isMember("look at relative distance") ||
                             c.isMember("look at absolute distance");

  if(has_camera_position &&
     has_look_at_point &&
     has_look_direction)
    {
    std::string s = "Block contains an invalid camera specification. \n"
                    "Cameras may not have a 'camera at ...' command and \n"
                    "a 'look at ...' command and a 'look direction' command";
    yyerror(llocp, yyscanner, &pd, s.c_str());
    return false;
    }

  if(has_camera_position &&
     has_look_at_point &&
     has_camera_distance)
    {
    std::string s = "Block contains an invalid camera specification. \n"
                    "Cameras may not have a 'camera at ...' command and \n"
                    "a 'look at ...' command and a 'look at relative|absolute distance' command";
    yyerror(llocp, yyscanner, &pd, s.c_str());
    return false;
    }

  if(has_camera_position &&
     has_look_direction &&
     has_camera_distance)
    {
    std::string s = "Block contains an invalid camera specification. \n"
                    "Cameras may not have a 'camera at ...' command and \n"
                    "a 'look direction' command and a 'look at relative|absolute distance' command";
    yyerror(llocp, yyscanner, &pd, s.c_str());
    return false;
    }

  return true;
}

bool check_imageset(YYLTYPE * llocp,
                    yyscan_t yyscanner,
                    ParseData& pd,
                    const Json::Value& r)
{
  if(r.isMember("representation") &&
    (r.isMember("show surfaces") ||
     r.isMember("show edges") ||
     r.isMember("show bounding box") ||
     r.isMember("color by scalar") ||
     r.isMember("color by vector magnitude") ||
     r.isMember("color by vector component") ||
     r.isMember("color by tensor component") ||
     r.isMember("color by solid color") ||
     r.isMember("color by blockid") ||
     r.isMember("show color legend") ||
     r.isMember("color legend range") ||
     r.isMember("color legend use current data range") ||
     r.isMember("color legend use cumulative data range") ||
     r.isMember("color legend minimum range") ||
     r.isMember("color legend maximum range") ||
     r.isMember("color legend position") ||
     r.isMember("invert color scale") ||
     r.isMember("show time annotation") ||
     r.isMember("time annotation position") ||
     r.isMember("show axes") ||
     r.isMember("show x axis label") ||
     r.isMember("show y axis label") ||
     r.isMember("show z axis label") ||
     r.isMember("x axis label name") ||
     r.isMember("y axis label name") ||
     r.isMember("z axis label name") ||
     r.isMember("show x axis tic marks") ||
     r.isMember("show y axis tic marks") ||
     r.isMember("show z axis tic marks") ||
     r.isMember("show x axis minor tic marks") ||
     r.isMember("show y axis minor tic marks") ||
     r.isMember("show z axis minor tic marks") ||
     r.isMember("show x axis tic mark labels") ||
     r.isMember("show y axis tic mark labels") ||
     r.isMember("show z axis tic mark labels") ||
     r.isMember("show orientation axes") ))
    {
    yyerror(llocp, yyscanner, &pd, "Error: imageset block contains both representation shortcuts and a representation block reference");
    return false;
    }

  if(r.isMember("camera") &&
    (r.isMember("look at absolute point") ||
     r.isMember("look at relative point") ||
     r.isMember("look at element") ||
     r.isMember("look at node") ||
     r.isMember("look at absolute distance") ||
     r.isMember("look at relative distance") ||
     r.isMember("up vector") ||
     r.isMember("camera fov") ))
    {
    yyerror(llocp, yyscanner, &pd, "Error: imageset block contains both camera shortcuts and a camera block reference");
    return false;
    }

  return true;
}

bool check_representation(YYLTYPE * llocp,
                          yyscan_t yyscanner,
                          ParseData& pd,
                          const Json::Value& r)
{
  int count = 0;
  if(r.isMember("color by scalar")) count++;
  if(r.isMember("color by vector magnitude")) count++;
  if(r.isMember("color by vector component")) count++;
  if(r.isMember("color by tensor component")) count++;
  if(r.isMember("color by solid color")) count++;
  if(r.isMember("color by blockid")) count++;

  if(count > 1)
    {
    yyerror(llocp, yyscanner, &pd, "Error: representation block contains multiple color by operations");
    return false;
    }

  if(r["show bounding box"].asBool() &&
     r["show surfaces"].asBool())
    {
    yyerror(llocp, yyscanner, &pd, "Error: representation block contains both 'show bounding box = true' and 'show surfaces = true'");
    return false;
    }

  if(r["show bounding box"].asBool() &&
     r["show edges"].asBool())
    {
    yyerror(llocp, yyscanner, &pd, "Error: representation block contains both 'show bounding box = true' and 'show edges = true'");
    return false;
    }

  if(r.isMember("color legend range") &&
    (r.isMember("color legend use cumulative data range") ||
     r.isMember("color legend use current data range") ||
     r.isMember("color legend minimum range") ||
     r.isMember("color legend maximum range") ))
    {
    yyerror(llocp, yyscanner, &pd, "Error: representation block contains color legend range and additional color legend commands");
    return false;
    }

  if(r.isMember("color legend use cumulative data range") &&
     r.isMember("color legend use current data range") )
    {
    yyerror(llocp, yyscanner, &pd, "Error: representation block contains both color legend cumulative and current data range commands");
    return false;
    }

  if(r.isMember("color legend minimum range") &&
     r.isMember("color legend maximum range") )
    {
    if(r["color legend minimum range"][1] >
       r["color legend maximum range"][0] )
      {
      yyerror(llocp, yyscanner, &pd, "Error: representation block contains invalid minimum and maximum color legend ranges");
      return false;
      }
    }

  return true;
}

bool check_nodeID(YYLTYPE * llocp,
                  yyscan_t yyscanner,
                  ParseData& pd,
                  int id)
{
  if(pd.checkNodeIDRange)
    {
    if(id < pd.nodeIDRange.first || id > pd.nodeIDRange.second)
      {
      std::stringstream ss;
      ss << id;
      yyerror(llocp, yyscanner, &pd, ("Error: node id out of range " + ss.str()).c_str());
      return false;
      }
    }
  return true;
}

bool check_elementID(YYLTYPE * llocp,
                     yyscan_t yyscanner,
                     ParseData& pd,
                     int id)
{
  if(pd.checkElementIDRange)
    {
    if(id < pd.elementIDRange.first || id > pd.elementIDRange.second)
      {
      std::stringstream ss;
      ss << id;
      yyerror(llocp, yyscanner, &pd, ("Error: element id out of range " + ss.str()).c_str());
      return false;
      }
    }
  return true;
}

void switch_variable_to_used(const std::string& name,
                             Iovs::PhactoriParserInterface::varMap& v)
{
  if(v.find(name) != v.end())
   {
   if( v[name] == Iovs::PhactoriParserInterface::ALLTYPE )
     v[name] = Iovs::PhactoriParserInterface::ALLTYPE_USED;
   else if( v[name] == Iovs::PhactoriParserInterface::TENSOR )
     v[name] = Iovs::PhactoriParserInterface::TENSOR_USED;
   else if( v[name] == Iovs::PhactoriParserInterface::SCALAR )
     v[name] = Iovs::PhactoriParserInterface::SCALAR_USED;
   else if( v[name] == Iovs::PhactoriParserInterface::VECTOR )
     v[name] = Iovs::PhactoriParserInterface::VECTOR_USED;
   }
}

bool compare_variable_type(const std::string& name, 
                           Iovs::PhactoriParserInterface::varMap& v,
                           Iovs::PhactoriParserInterface::VarType vt)
{
  if(v.find(name) != v.end())
   {
   if( v[name] == vt || 
       v[name] == Iovs::PhactoriParserInterface::ALLTYPE || 
       v[name] == Iovs::PhactoriParserInterface::ALLTYPE_USED )
     return true;
   else if( v[name] == Iovs::PhactoriParserInterface::SCALAR_USED &&
             vt == Iovs::PhactoriParserInterface::SCALAR )
     return true;
   else if( v[name] == Iovs::PhactoriParserInterface::VECTOR_USED &&
             vt == Iovs::PhactoriParserInterface::VECTOR )
     return true;
   else if( v[name] == Iovs::PhactoriParserInterface::TENSOR_USED && 
             vt == Iovs::PhactoriParserInterface::TENSOR )
     return true;
   }
  return false;
}

bool check_variable_name(YYLTYPE * llocp,
                         yyscan_t yyscanner,
                         ParseData& pd,
                         const std::string& name, 
                         Iovs::PhactoriParserInterface::VarType vt)
{
  if (!pd.nodeVars.empty() && !pd.elementVars.empty() && !pd.globalVars.empty())
    {
    bool is_node_var = (pd.nodeVars.find(name) != pd.nodeVars.end());
    bool is_element_var = (pd.elementVars.find(name) != pd.elementVars.end());
    bool is_global_var = (pd.globalVars.find(name) != pd.globalVars.end());

    if( !is_node_var &&
        !is_global_var &&
        !is_element_var )
      {
      yyerror(llocp, yyscanner, &pd, ("Error: variable name not found: " + name).c_str());
      return false;
      }

    bool found_match = false;

    if(is_node_var)
      {
      if( compare_variable_type(name, pd.nodeVars, vt) )
        {
        switch_variable_to_used(name, pd.nodeVars);
        found_match = true;
        }
      }

    if(is_global_var)
      {
      if( compare_variable_type(name, pd.globalVars, vt) )
        {
        switch_variable_to_used(name, pd.globalVars);
        found_match = true;
        }
      }

    if(is_element_var)
      {
      if( compare_variable_type(name, pd.elementVars, vt) )
        {
        switch_variable_to_used(name, pd.elementVars);
        found_match = true;
        }
      }

    if(!found_match)
      {
      std::string t = "scalar";
      if(vt == Iovs::PhactoriParserInterface::VECTOR)
        t = "vector";
      else if(vt == Iovs::PhactoriParserInterface::TENSOR)
        t = "tensor";
      yyerror(llocp, yyscanner, &pd, ("Error: variable is not " + t + " type: " + name).c_str());
      return false;
      }

    }

  return true;
}

bool check_vector_component(YYLTYPE * llocp,
                            yyscan_t yyscanner,
                            ParseData& pd,
                            const std::string& name)
{
  std::string sep = pd.separator;
  unsigned int offset = sep.size() + 1;
  if(name.size() > offset)
    {
    std::string term  = name.substr(name.size() - offset);
    if(term == sep + "X" || term == sep + "Y" || term == sep + "Z" ||
       term == sep + "x" || term == sep + "y" || term == sep + "z")
      return check_variable_name(llocp, yyscanner, pd, name.substr(0,name.size() - offset),
          Iovs::PhactoriParserInterface::VECTOR);
    }

  yyerror(llocp, yyscanner, &pd, ("Error: invalid vector name component (must be x, y, or z) " + name).c_str());
  return false;
}

bool check_tensor_component(YYLTYPE * llocp,
                            yyscan_t yyscanner,
                            ParseData& pd,
                            const std::string& name)
{
  std::string sep = pd.separator;
  unsigned int offset = sep.size() + 2;
  if(name.size() > offset)
    {
    std::string term  = name.substr(name.size() - offset);
    if(term == sep + "XX" || term == sep + "XY" || term == sep + "XZ" ||
       term == sep + "YX" || term == sep + "YY" || term == sep + "YZ" ||
       term == sep + "ZX" || term == sep + "ZY" || term == sep + "ZZ" ||
       term == sep + "xx" || term == sep + "xy" || term == sep + "xz" ||
       term == sep + "yx" || term == sep + "yy" || term == sep + "yz" ||
       term == sep + "zx" || term == sep + "zy" || term == sep + "zz")
      return check_variable_name(llocp, yyscanner, pd, name.substr(0,name.size() - offset),
          Iovs::PhactoriParserInterface::TENSOR);
    }

  yyerror(llocp, yyscanner, &pd, ("Error: invalid tensor name component (must be xx, yy, zz, zx, zy, xy, or yz) " + name).c_str());
  return false;
}

bool check_catalyst_shortcuts(YYLTYPE * llocp,
                              yyscan_t yyscanner,
                              ParseData& pd)
{
  if( pd.ims_shortcut.size() &&
     (pd.root["representation blocks"].size() ||
      pd.root["imageset blocks"].size() ||
      pd.root["camera blocks"].size() ||
      pd.root["operation blocks"].size() ||
      pd.root["scatter plot blocks"].size() ||
      pd.root["plot over time blocks"].size()) )
    {
    yyerror(llocp, yyscanner, &pd, "Error: catalyst block shortcuts used with declared blocks.");
    return false;
    }

  bool ret = true;
  if(pd.ims_shortcut.size())
    {
    pd.root["imageset blocks"]["\\#ImageSet\\$ Shortcut"] = pd.ims_shortcut;
    ret = ret && check_representation(llocp, yyscanner, pd, pd.ims_shortcut);
    ret = ret && check_camera(llocp, yyscanner, pd, pd.ims_shortcut);
    }
  return ret;
}

bool initialize_block(YYLTYPE * llocp,
                      yyscan_t yyscanner,
                      ParseData& pd)
{
  if(pd.ims_shortcut.size())
    {
    yyerror(llocp, yyscanner, &pd, "Error: catalyst block shortcuts used with declared blocks.");
    return false;
    }

  if(pd.cb == TOKREPRESENTATION &&
     !pd.root["representation blocks"][pd.cbn])
    {
    pd.root["representation blocks"][pd.cbn] = Json::Value(Json::objectValue);
    }
  else if(pd.cb == TOKMULTICAMERA8 &&
          !pd.root["camera blocks"][pd.cbn])
    {
    pd.root["camera blocks"][pd.cbn] = Json::Value(Json::objectValue);
    pd.root["camera blocks"][pd.cbn]["camera type"] = "multicamera8";
    }
  else if(pd.cb == TOKCAMERA &&
          !pd.root["camera blocks"][pd.cbn])
    {
    pd.root["camera blocks"][pd.cbn] = Json::Value(Json::objectValue);
    pd.root["camera blocks"][pd.cbn]["camera type"] = "camera";
    }
  else if(pd.cb == TOKIMAGESET &&
          !pd.root["imageset blocks"][pd.cbn])
    {
    pd.root["imageset blocks"][pd.cbn] = Json::Value(Json::objectValue);
    }
  else if(pd.cb == TOKCLIP &&
          !pd.root["operation blocks"][pd.cbn])
    {
    pd.root["operation blocks"][pd.cbn] = Json::Value(Json::objectValue);
    pd.root["operation blocks"][pd.cbn]["type"] = "clip";
    }
  else if(pd.cb == TOKSLICE &&
          !pd.root["operation blocks"][pd.cbn])
    {
    pd.root["operation blocks"][pd.cbn] = Json::Value(Json::objectValue);
    pd.root["operation blocks"][pd.cbn]["type"] = "slice";
    }
  else if(pd.cb == TOKBOXCLIP &&
          !pd.root["operation blocks"][pd.cbn])
    {
    pd.root["operation blocks"][pd.cbn] = Json::Value(Json::objectValue);
    pd.root["operation blocks"][pd.cbn]["type"] = "boxclip";
    }
  else if(pd.cb == TOKTHRESHOLD &&
          !pd.root["operation blocks"][pd.cbn])
    {
    pd.root["operation blocks"][pd.cbn] = Json::Value(Json::objectValue);
    pd.root["operation blocks"][pd.cbn]["type"] = "threshold";
    }
  else if(pd.cb == TOKCONTOUR &&
          !pd.root["operation blocks"][pd.cbn])
    {
    pd.root["operation blocks"][pd.cbn] = Json::Value(Json::objectValue);
    pd.root["operation blocks"][pd.cbn]["type"] = "contour";
    }
  else if(pd.cb == TOKSCATTERPLOT &&
          !pd.root["scatter plot blocks"][pd.cbn])
    {
    pd.root["scatter plot blocks"][pd.cbn] = Json::Value(Json::objectValue);
    }
  else if(pd.cb == TOKPLOTOVERTIME &&
          !pd.root["plot over time blocks"][pd.cbn])
    {
    pd.root["plot over time blocks"][pd.cbn] = Json::Value(Json::objectValue);
    }
  else if(pd.cb == TOKGROUP &&
          !pd.root["operation blocks"][pd.cbn])
    {
    pd.root["operation blocks"][pd.cbn] = Json::Value(Json::objectValue);
    pd.root["operation blocks"][pd.cbn]["type"] = "group";
    }
  else if(pd.cb == TOKEXTRACTBLOCK &&
          !pd.root["operation blocks"][pd.cbn])
    {
    pd.root["operation blocks"][pd.cbn] = Json::Value(Json::objectValue);
    pd.root["operation blocks"][pd.cbn]["type"] = "extractblock";
    }
  else if(pd.cb == TOKTRANSFORM &&
          !pd.root["operation blocks"][pd.cbn])
    {
    pd.root["operation blocks"][pd.cbn] = Json::Value(Json::objectValue);
    pd.root["operation blocks"][pd.cbn]["type"] = "transform";
    }
  else if(pd.cb == TOKWARPBYVECTOR &&
          !pd.root["operation blocks"][pd.cbn])
    {
    pd.root["operation blocks"][pd.cbn] = Json::Value(Json::objectValue);
    pd.root["operation blocks"][pd.cbn]["type"] = "warpbyvector";
    }
  else if(pd.cb == TOKCALCULATOR &&
          !pd.root["operation blocks"][pd.cbn])
    {
    pd.root["operation blocks"][pd.cbn] = Json::Value(Json::objectValue);
    pd.root["operation blocks"][pd.cbn]["type"] = "calculator";
    }
  else if(pd.cb == TOKEXPERIMENTALOPERATION &&
          !pd.root["operation blocks"][pd.cbn])
    {
    pd.root["operation blocks"][pd.cbn] = Json::Value(Json::objectValue);
    pd.root["operation blocks"][pd.cbn]["type"] = "experimentaloperation";
    }
  else if(pd.cb == TOKVISUALMARKER &&
          !pd.root["visual marker blocks"][pd.cbn])
    {
    pd.root["visual marker blocks"][pd.cbn] = Json::Value(Json::objectValue);
    pd.root["visual marker blocks"][pd.cbn]["type"] = "visualmarker";
    }
  else if(pd.cb == TOKEXPERIMENTALBLOCK &&
          !pd.root["experimental blocks"][pd.cbn])
    {
    pd.root["experimental blocks"][pd.cbn] = Json::Value(Json::objectValue);
    pd.root["experimental blocks"][pd.cbn]["type"] = "experimentalblock";
    }
  else if(pd.cb == TOKONOFFCRITERIABLOCK &&
          !pd.root["onoff criteria blocks"][pd.cbn])
    {
    pd.root["onoff criteria blocks"][pd.cbn] = Json::Value(Json::objectValue);
    pd.root["onoff criteria blocks"][pd.cbn]["type"] = "onoffcriteriablock";
    }
  else
    {
    yyerror(llocp, yyscanner, &pd, ("Error: duplicate block name " + pd.cbn).c_str());
    return false;
    }

  return true;
}

void clip_or_boxclip_or_slice_shortcut_command(ParseData& pd,
                                               const std::string& command,
                                               const std::string& boxclip_type,
                                               std::vector<double>& values)
{
  Json::Value a(Json::arrayValue);
  Json::Value p(Json::arrayValue);
  Json::Value n(Json::arrayValue);
  p[0] = values[0];
  p[1] = values[1];
  p[2] = values[2];
  n[0] = values[3];
  n[1] = values[4];
  n[2] = values[5];

  a[0] = p;
  a[1] = n;

  if(command == "boxclip")
    {
    std::string r = boxclip_type;
    std::transform(r.begin(), r.end(), r.begin(), ::tolower);
    a[2] = r;
    }

  if (pd.cb == TOKCATALYST)
    pd.ims_shortcut[command] = a;
  else if(pd.cb == TOKIMAGESET)
    pd.root["imageset blocks"][pd.cbn][command] = a;
}

void data_point_command(ParseData& pd,
                        const std::string& command,
                        const std::string& min_max_center,
                        const std::string& variable_type,
                        const std::string& variable,
                        const std::string& operation)
{
  std::string mmc = min_max_center;
  std::string vt = variable_type;
  Json::Value a(Json::arrayValue);
  a[0] = mmc;
  a[1] = vt;
  a[2] = variable;
  a[3] = operation;
  if(pd.cb == TOKVISUALMARKER)
    pd.root["visual marker blocks"][pd.cbn][command] = a;
  else if(pd.cb == TOKCLIP ||
          pd.cb == TOKBOXCLIP ||
          pd.cb == TOKSLICE ||
          pd.cb == TOKCAMERA)
    pd.root["operation blocks"][pd.cbn][command] = a;
}

void data_point_command(ParseData& pd,
                        const std::string& command,
                        const std::string& min_max_center,
                        const std::string& variable_type,
                        const std::string& variable,
                        const std::string& operation,
                        double x,
                        double y,
                        double z)
{
  std::string mmc = min_max_center;
  std::string vt = variable_type;
  Json::Value a(Json::arrayValue);
  a[0] = mmc;
  a[1] = vt;
  a[2] = variable;
  a[3] = operation;
  a[4] = x;
  a[5] = y;
  a[6] = z;
  if(pd.cb == TOKVISUALMARKER)
    pd.root["visual marker blocks"][pd.cbn][command] = a;
  else if(pd.cb == TOKCLIP ||
          pd.cb == TOKBOXCLIP ||
          pd.cb == TOKSLICE ||
          pd.cb == TOKCAMERA)
    pd.root["operation blocks"][pd.cbn][command] = a;
}

void threshold_or_contour_shortcut_command(ParseData& pd,
                                           const std::string& command,
                                           const std::string& data_type,
                                           const std::string& variable_name,
                                           const std::string& range,
                                           std::vector<double>& values)
{
  std::string dt = data_type;
  std::string r = range;
  std::transform(dt.begin(), dt.end(), dt.begin(), ::tolower);
  std::transform(r.begin(), r.end(), r.begin(), ::tolower);
  Json::Value a(Json::arrayValue);

  a[0] = dt;
  a[1] = variable_name;
  a[2] = r;
  Json::Value d(Json::arrayValue);
  for(int i=0;i<values.size();i++)
    d[i] = values[i];
  a[3] = d;

  if (pd.cb == TOKCATALYST)
    pd.ims_shortcut[command] = a;
  else if(pd.cb == TOKIMAGESET)
    pd.root["imageset blocks"][pd.cbn][command] = a;
}

void four_argument_command(ParseData& pd,
                           const std::string& command,
                           int id,
                           double x,
                           double y,
                           double z)
{
  Json::Value a(Json::arrayValue);
  a[0] = id;
  a[1] = x;
  a[2] = y;
  a[3] = z;
  pd.root["camera blocks"][pd.cbn][command] = a;
}

bool check_two_argument_command(YYLTYPE * llocp,
                                yyscan_t yyscanner,
                                ParseData& pd,
                                const std::string& command, 
                                const std::string& position, 
                                double value)
{
  if(value < 0 || value > 1)
    {
    yyerror(llocp, yyscanner, &pd, ("Error: " + command + " <size> must be between 0 and 1, inclusive").c_str());
    return false;
    }

  std::string r = position;
  std::transform(r.begin(), r.end(), r.begin(), ::tolower);

  Json::Value a(Json::arrayValue);
  a[0] = r;
  a[1] = value;
  if (pd.cb == TOKCATALYST)
    pd.ims_shortcut[command] = a;
  else if(pd.cb == TOKREPRESENTATION)
    pd.root["representation blocks"][pd.cbn][command] = a;
  else if(pd.cb == TOKIMAGESET)
    pd.root["imageset blocks"][pd.cbn][command] = a;
  return true;
}

void remove_quotes(std::string& s)
{
  if ( s.size() > 1 && s[0] == '"' )
    {
    s.erase( 0, 1 );
    s.erase( s.size() - 1 );
    }
}

void no_argument_command(ParseData& pd,
                         const std::string& command)
{
  if (pd.cb == TOKCATALYST)
    pd.ims_shortcut[command] = true;
  else if(pd.cb == TOKREPRESENTATION)
    pd.root["representation blocks"][pd.cbn][command] = true;
  else if(pd.cb == TOKMULTICAMERA8 || pd.cb == TOKCAMERA)
    pd.root["camera blocks"][pd.cbn][command] = true;
  else if(pd.cb == TOKIMAGESET)
    pd.root["imageset blocks"][pd.cbn][command] = true;
  else if(pd.cb == TOKCLIP ||
          pd.cb == TOKBOXCLIP ||
          pd.cb == TOKTHRESHOLD ||
          pd.cb == TOKCONTOUR ||
          pd.cb == TOKSLICE)
    pd.root["operation blocks"][pd.cbn][command] = true;
  else if(pd.cb == TOKSCATTERPLOT)
    pd.root["scatter plot blocks"][pd.cbn][command] = true;
  else if(pd.cb == TOKPLOTOVERTIME)
    pd.root["plot over time blocks"][pd.cbn][command] = true;
}

template<class T>
void vector_command(ParseData& pd,
                    const std::string& command, 
                    std::vector<T>& values)
{
  Json::Value a(Json::arrayValue);
  for(int i=0;i<values.size();i++)
    a[i] = values[i];

  if (pd.cb == TOKCATALYST)
    pd.ims_shortcut[command] = a;
  else if(pd.cb == TOKREPRESENTATION)
    pd.root["representation blocks"][pd.cbn][command] = a;
  else if(pd.cb == TOKMULTICAMERA8 || pd.cb == TOKCAMERA)
    pd.root["camera blocks"][pd.cbn][command] = a;
  else if(pd.cb == TOKIMAGESET)
    pd.root["imageset blocks"][pd.cbn][command] = a;
  else if(pd.cb == TOKCLIP ||
          pd.cb == TOKBOXCLIP ||
          pd.cb == TOKTHRESHOLD ||
          pd.cb == TOKCONTOUR ||
          pd.cb == TOKSLICE ||
          pd.cb == TOKTRANSFORM ||
          pd.cb == TOKEXPERIMENTALOPERATION)
    pd.root["operation blocks"][pd.cbn][command] = a;
  else if(pd.cb == TOKSCATTERPLOT)
    pd.root["scatter plot blocks"][pd.cbn][command] = a;
  else if(pd.cb == TOKPLOTOVERTIME)
    pd.root["plot over time blocks"][pd.cbn][command] = a;
  else if(pd.cb == TOKVISUALMARKER)
    pd.root["visual marker blocks"][pd.cbn][command] = a;
  else if(pd.cb == TOKEXPERIMENTALBLOCK)
    pd.root["experimental blocks"][pd.cbn][command] = a;
  else if(pd.cb == TOKONOFFCRITERIABLOCK)
    pd.root["onoff criteria blocks"][pd.cbn][command] = a;
}

void string_vector_command(ParseData& pd,
                           const std::string& command, 
                           std::vector<std::string>& values)
{
  Json::Value a(Json::arrayValue);
  for(int i=0;i<values.size();i++)
    a[i] = values[i];

  if (pd.cb == TOKGROUP ||
      pd.cb == TOKEXTRACTBLOCK ||
      pd.cb == TOKCALCULATOR)
    pd.root["operation blocks"][pd.cbn][command] = a;
  else if(pd.cb == TOKIMAGESET)
    pd.root["imageset blocks"][pd.cbn][command] = a;
  else if(pd.cb == TOKONOFFCRITERIABLOCK)
    pd.root["onoff criteria blocks"][pd.cbn][command] = a;
}

void identifier_command(ParseData& pd,
                        const std::string& command, 
                        const std::string& identifier)
{
  if (pd.cb == TOKCATALYST)
    pd.ims_shortcut[command] = identifier;
  else if(pd.cb == TOKREPRESENTATION)
    pd.root["representation blocks"][pd.cbn][command] = identifier;
  else if(pd.cb == TOKMULTICAMERA8 || pd.cb == TOKCAMERA)
    pd.root["camera blocks"][pd.cbn][command] = identifier;
  else if(pd.cb == TOKIMAGESET)
    pd.root["imageset blocks"][pd.cbn][command] = identifier;
  else if(pd.cb == TOKCLIP ||
          pd.cb == TOKBOXCLIP ||
          pd.cb == TOKTHRESHOLD ||
          pd.cb == TOKCONTOUR ||
          pd.cb == TOKSLICE ||
          pd.cb == TOKEXTRACTBLOCK ||
          pd.cb == TOKTRANSFORM ||
          pd.cb == TOKWARPBYVECTOR ||
          pd.cb == TOKCALCULATOR ||
          pd.cb == TOKEXPERIMENTALOPERATION)
    pd.root["operation blocks"][pd.cbn][command] = identifier;
  else if(pd.cb == TOKSCATTERPLOT)
    pd.root["scatter plot blocks"][pd.cbn][command] = identifier;
  else if(pd.cb == TOKPLOTOVERTIME)
    pd.root["plot over time blocks"][pd.cbn][command] = identifier;
  else if(pd.cb == TOKVISUALMARKER)
    pd.root["visual marker blocks"][pd.cbn][command] = identifier;
  else if(pd.cb == TOKEXPERIMENTALBLOCK)
    pd.root["experimental blocks"][pd.cbn][command] = identifier;
  else if(pd.cb == TOKONOFFCRITERIABLOCK)
    pd.root["onoff criteria blocks"][pd.cbn][command] = identifier;
}

void boolean_command(ParseData& pd,
                     const std::string& command,
                     bool value)
{
  if (pd.cb == TOKCATALYST)
    pd.ims_shortcut[command] = value;
  else if(pd.cb == TOKREPRESENTATION)
    pd.root["representation blocks"][pd.cbn][command] = value;
  else if(pd.cb == TOKMULTICAMERA8 || pd.cb == TOKCAMERA)
    pd.root["camera blocks"][pd.cbn][command] = value;
  else if(pd.cb == TOKIMAGESET)
    pd.root["imageset blocks"][pd.cbn][command] = value;
  else if(pd.cb == TOKCLIP ||
          pd.cb == TOKBOXCLIP ||
          pd.cb == TOKTHRESHOLD ||
          pd.cb == TOKCONTOUR ||
          pd.cb == TOKSLICE)
    pd.root["operation blocks"][pd.cbn][command] = value;
  else if(pd.cb == TOKSCATTERPLOT)
    pd.root["scatter plot blocks"][pd.cbn][command] = value;
  else if(pd.cb == TOKPLOTOVERTIME)
    pd.root["plot over time blocks"][pd.cbn][command] = value;
}

void integer_command(ParseData& pd,
                     const std::string& command, 
                     int value)
{
  if (pd.cb == TOKCATALYST)
    pd.ims_shortcut[command] = value;
  else if(pd.cb == TOKREPRESENTATION)
    pd.root["representation blocks"][pd.cbn][command] = value;
  else if(pd.cb == TOKMULTICAMERA8 || pd.cb == TOKCAMERA)
    pd.root["camera blocks"][pd.cbn][command] = value;
  else if(pd.cb == TOKIMAGESET)
    pd.root["imageset blocks"][pd.cbn][command] = value;
  else if(pd.cb == TOKCLIP ||
          pd.cb == TOKBOXCLIP ||
          pd.cb == TOKTHRESHOLD ||
          pd.cb == TOKCONTOUR ||
          pd.cb == TOKSLICE ||
          pd.cb == TOKEXPERIMENTALOPERATION)
    pd.root["operation blocks"][pd.cbn][command] = value;
  else if(pd.cb == TOKSCATTERPLOT)
    pd.root["scatter plot blocks"][pd.cbn][command] = value;
  else if(pd.cb == TOKPLOTOVERTIME)
    pd.root["plot over time blocks"][pd.cbn][command] = value;
  else if(pd.cb == TOKVISUALMARKER)
    pd.root["visual marker blocks"][pd.cbn][command] = value;
  else if(pd.cb == TOKEXPERIMENTALBLOCK)
    pd.root["experimental blocks"][pd.cbn][command] = value;
  else if(pd.cb == TOKONOFFCRITERIABLOCK)
    pd.root["onoff criteria blocks"][pd.cbn][command] = value;
}

void double_command(ParseData& pd,
                    const std::string& command,
                    double value)
{
  if (pd.cb == TOKCATALYST)
    pd.ims_shortcut[command] = value;
  else if(pd.cb == TOKREPRESENTATION)
    pd.root["representation blocks"][pd.cbn][command] = value;
  else if(pd.cb == TOKMULTICAMERA8 || pd.cb == TOKCAMERA)
    pd.root["camera blocks"][pd.cbn][command] = value;
  else if(pd.cb == TOKIMAGESET)
    pd.root["imageset blocks"][pd.cbn][command] = value;
  else if(pd.cb == TOKCLIP ||
          pd.cb == TOKBOXCLIP ||
          pd.cb == TOKTHRESHOLD ||
          pd.cb == TOKCONTOUR ||
          pd.cb == TOKSLICE ||
          pd.cb == TOKWARPBYVECTOR ||
          pd.cb == TOKEXPERIMENTALOPERATION)
    pd.root["operation blocks"][pd.cbn][command] = value;
  else if(pd.cb == TOKSCATTERPLOT)
    pd.root["scatter plot blocks"][pd.cbn][command] = value;
  else if(pd.cb == TOKPLOTOVERTIME)
    pd.root["plot over time blocks"][pd.cbn][command] = value;
  else if(pd.cb == TOKVISUALMARKER)
    pd.root["visual marker blocks"][pd.cbn][command] = value;
  else if(pd.cb == TOKEXPERIMENTALBLOCK)
    pd.root["experimental blocks"][pd.cbn][command] = value;
}


void initialize_json(ParseData& pd)
{
  pd.root.clear();
  pd.ims_shortcut.clear();
  pd.root["camera blocks"] = Json::Value(Json::objectValue);
  pd.root["representation blocks"] = Json::Value(Json::objectValue);
  pd.root["imageset blocks"] = Json::Value(Json::objectValue);
  pd.root["operation blocks"] = Json::Value(Json::objectValue);
  pd.root["scatter plot blocks"] = Json::Value(Json::objectValue);
  pd.root["plot over time blocks"] = Json::Value(Json::objectValue);
  pd.root["visual marker blocks"] = Json::Value(Json::objectValue);
  pd.root["experimental blocks"] = Json::Value(Json::objectValue);
  pd.root["onoff criteria blocks"] = Json::Value(Json::objectValue);
}

void yyerror (YYLTYPE * llocp, yyscan_t yyscanner, void* pd, char const *msg)
{
  fflush(stdout);
  printf("%s, line = %d, column = %d\n", msg, llocp->first_line, llocp->first_column);
}

} // End anonymous namespace

namespace Iovs {

void parsePhactoriString(const std::string & s,
                         Iovs::PhactoriParserInterface::ParseInfo & pinfo)
{
  ParseData pd;
  pd.nodeVars = pinfo.nodeVars;
  pd.elementVars = pinfo.elementVars;
  pd.globalVars = pinfo.globalVars;
  pd.checkNodeIDRange = pinfo.checkNodeIDRange;
  pd.nodeIDRange = pinfo.nodeIDRange;
  pd.checkElementIDRange = pinfo.checkElementIDRange;
  pd.elementIDRange = pinfo.elementIDRange;
  pd.separator = pinfo.separator;

  std::string pps = preProcessPhactoriInput(s);

  yyscan_t myscanner;
  catalystlex_init(&myscanner);
  YY_BUFFER_STATE buffer = catalyst_scan_string(pps.c_str(), myscanner);
  int ret = yyparse(myscanner, &pd);
  catalyst_delete_buffer(buffer, myscanner);
  catalystlex_destroy(myscanner);
  Json::StreamWriterBuilder builder;
  builder["indentation"] = "";
  pinfo.jsonParseResult = Json::writeString(builder, pd.root);
  pinfo.parseFailed = ret;
}

} // end namespace Iovs

%}

%union
{
        int integer;
        double floating;
        char *string;
}

/* On Off Criteria Block */

%token TOKONOFFCRITERIABLOCK
%token <string> TOKTRIGGERTHRESHOLDS
%token <string> TOKEXPERIMENTAL
%token TOKEXPERIMENTALTYPE
%token TOKTHRESHOLDVARIABLEFUNCTION
%token <string> TOKMINIMUM
%token <string> TOKMAXIMUM
%token <string> TOKMEAN
%token <string> TOKSUM
%token <string> TOKCOUNT
%token TOKTHRESHOLDVALUES
%token TOKFRAMESAFTERTRIGGER
%token TOKMAXIMUMTRIGGERSPERTHRESHOLD

/* On Off Criteria Block */

/* Experimental Block */

%token TOKEXPERIMENTALBLOCK

/* Experimental Block */

/* Visual Marker */

%token TOKVISUALMARKER
%token TOKSHAPE
%token TOKCOLOR
%token TOKABSOLUTESIZE
%token TOKRELATIVESIZE
%token TOKORIENTATION
%token TOKRESOLUTION
%token TOKMARKERATRELATIVEPOINT
%token TOKMARKERATABSOLUTEPOINT
%token TOKMARKERATNODE
%token TOKMARKERATELEMENT
%token TOKMARKERATDATAPOINT
%token TOKVECTORPARAMETER
%token TOKSCALARPARAMETER
%token TOKINTEGERPARAMETER
%token TOKSTRINGPARAMETER
%token <string> TOKMIN
%token <string> TOKMAX
%token <string> TOKNONE

/* Visual Marker */


/* Experimental Operation */

%token TOKEXPERIMENTALOPERATION
%token TOKTYPE
%token TOKVECTOROPPARAM
%token TOKSCALAROPPARAM
%token TOKINTEGEROPPARAM
%token TOKSTRINGOPPARAM

/* Experimental Operation */

/* Calculator */

%token TOKCALCULATOR
%token TOKOUTPUTVARIABLENAME
%token TOKFUNCTION
%token TOKELEMENTORNODEDATA
%token TOKCOORDINATERESULTFLAG
%token TOKRESULTNORMALSFLAG
%token TOKPYTHONCALCULATORFLAG
%token <string> TOKNODE
%token <string> TOKELEMENT

/* Calculator */

/* Warp By Vector */

%token TOKWARPBYVECTOR
%token TOKVARIABLEVECTOR

/* Warp By Vector */

/* Transform */

%token TOKTRANSFORM
%token TOKSCALE
%token TOKROTATE
%token TOKTRANSLATE

/* Transform */

/* Extract Block */

%token TOKEXTRACTBLOCK
%token TOKINCLUDEBLOCKS
%token TOKEXCLUDEBLOCKS

/* Extract Block */

/* Group */

%token TOKOPERATIONGROUPLIST
%token TOKGROUP

/* Group */

/* Plot Over Time */

%token TOKPLOTID
%token TOKPLOTMEAN
%token TOKPLOTMAXIMUM
%token TOKPLOTMINIMUM
%token TOKPLOTOVERTIME

/* Plot Over Time */

/* Scatter Plot */

%token TOKPLOTBASEDIRECTORY
%token TOKPLOTBASENAME
%token TOKYAXISMAXIMUMRANGE
%token TOKXAXISMAXIMUMRANGE
%token TOKYAXISMINIMUMRANGE
%token TOKXAXISMINIMUMRANGE
%token TOKYAXISUSECUMULATIVEDATARANGE
%token TOKXAXISUSECUMULATIVEDATARANGE
%token TOKYAXISUSECURRENTDATARANGE
%token TOKXAXISUSECURRENTDATARANGE
%token TOKYAXISRANGE
%token TOKXAXISRANGE
%token TOKYAXISVARIABLETENSORCOMPONENT
%token TOKXAXISVARIABLETENSORCOMPONENT
%token TOKYAXISVARIABLEVECTORCOMPONENT
%token TOKXAXISVARIABLEVECTORCOMPONENT
%token TOKYAXISVARIABLEVECTORMAGNITUDE
%token TOKXAXISVARIABLEVECTORMAGNITUDE
%token TOKYAXISVARIABLESCALAR
%token TOKXAXISVARIABLESCALAR
%token TOKALLVARIABLES
%token TOKSCATTERPLOT

/* Scatter Plot */

/* Contour */

%token TOKCONTOURVALUESEQUENCE
%token TOKCONTOURVALUE
%token TOKENDCONTOUR

/* Contour */

/* Threshold */

%token TOKVARIABLETENSORCOMPONENT
%token TOKVARIABLEVECTORCOMPONENT
%token TOKVARIABLEVECTORMAGNITUDE
%token TOKVARIABLESCALAR
%token TOKENDTHRESHOLD

/* Threshold */

/* BoxClip */

%token TOKKEEPINSIDEBOX
%token TOKROTATIONS
%token TOKRELATIVEEXTENTS
%token TOKABSOLUTEEXTENTS
%token TOKCENTERATELEMENT
%token TOKCENTERATNODE
%token TOKCENTERATRELATIVEPOINT
%token TOKCENTERATABSOLUTEPOINT
%token TOKENDBOXCLIP
%token TOKCENTERATDATAPOINT

/* BoxClip */

/* Slice */

%token TOKENDSLICE

/* Slice */

/* Clip */

%token <string> TOKNEGATIVE
%token <string> TOKPOSITIVE
%token <string> TOKSMOOTH
%token <string> TOKCRINKLE
%token TOKSIDETOKEEP
%token TOKCUTTYPE
%token TOKPLANENORMAL
%token TOKELEMENTONPLANE
%token TOKNODEONPLANE
%token TOKABSOLUTEPOINTONPLANE
%token TOKRELATIVEPOINTONPLANE
%token TOKINPUT
%token TOKENDCLIP
%token TOKDATAPOINTONPLANE
%token TOKPLANESPECIFICATION
%token TOKTHREEPOINTS
%token TOKPOINTANDNORMAL
%token <string> TOKA
%token <string> TOKB
%token <string> TOKC

/* Clip */

/* ImageSet */

%token <string> TOKCENTER
%token TOKPOINT
%token <string> TOKKEEPINSIDE
%token <string> TOKKEEPOUTSIDE
%token TOKSLICE
%token TOKBOXCLIP
%token TOKEXTENTS
%token TOKNORMAL
%token TOKCLIP
%token <string> TOKVALUESEQUENCE
%token <string> TOKVALUELIST
%token TOKCONTOUR
%token <string> TOKKEEPBELOW
%token <string> TOKKEEPABOVE
%token <string> TOKKEEPBETWEEN
%token <string> TOKTENSORCOMPONENT
%token <string> TOKVECTORCOMPONENT
%token <string> TOKVECTORMAGNITUDE
%token <string> TOKSCALAR
%token TOKTHRESHOLD
%token TOKAXESCOLOR
%token TOKTEXTCOLOR
%token TOKEDGECOLOR
%token TOKSURFACECOLOR
%token TOKBACKGROUNDCOLOR
%token TOKIMAGESIZE
%token TOKIMAGEDIGITCOUNT
%token TOKJPG
%token TOKPNG
%token TOKIMAGEFORMAT
%token TOKIMAGEBASEDIRECTORY
%token TOKIMAGEBASENAME
%token TOKOPERATION
%token TOKOPERATION2
%token TOKOPERATION3
%token TOKIMAGESET
%token TOKONOFFCRITERIA
%token TOKMARKERS

/* ImageSet */

/* Camera */

%token TOKCAMERAATDATAPOINTDISPLACED
%token TOKCAMERAATDATAPOINT
%token TOKCAMERAATNODEDISPLACED
%token TOKCAMERAATELEMENTDISPLACED
%token TOKCAMERAATNODE
%token TOKCAMERAATELEMENT
%token TOKCAMERAATRELATIVEPOINT
%token TOKCAMERAATABSOLUTEPOINT
%token TOKLOOKDIRECTION
%token TOKCAMERA

/* Camera */

/* MultiCamera8 */

%token TOKCAMERAFOV
%token TOKUPVECTOR
%token TOKLOOKATABSOLUTEDISTANCE
%token TOKLOOKATRELATIVEDISTANCE
%token TOKLOOKATNODE
%token TOKLOOKATELEMENT
%token TOKLOOKATRELATIVEPOINT
%token TOKLOOKATABSOLUTEPOINT
%token TOKMULTICAMERA8

/* MultiCamera8 */

/* Representation */

%token TOKHIGHLIGHTSUBRANGE
%token TOKCOLORLEGENDSUBRANGE
%token TOKIMAGENAMEADDON
%token TOKSHOWORIENTATIONAXES
%token TOKSHOWZAXISTICMARKLABELS
%token TOKSHOWYAXISTICMARKLABELS
%token TOKSHOWXAXISTICMARKLABELS
%token TOKSHOWZAXISMINORTICMARKS
%token TOKSHOWYAXISMINORTICMARKS
%token TOKSHOWXAXISMINORTICMARKS
%token TOKSHOWZAXISTICMARKS
%token TOKSHOWYAXISTICMARKS
%token TOKSHOWXAXISTICMARKS
%token TOKZAXISLABELNAME
%token TOKYAXISLABELNAME
%token TOKXAXISLABELNAME
%token TOKSHOWZAXISLABEL
%token TOKSHOWYAXISLABEL
%token TOKSHOWXAXISLABEL
%token TOKSHOWAXES
%token TOKTIMEANNOTATIONPOSITION
%token TOKSHOWTIMEANNOTATION
%token TOKINVERTCOLORSCALE
%token TOKPRESETCOLORSCALE
%token TOKCOLORLEGENDPOSITION
%token TOKCOLORLEGENDMAXIMUMRANGE
%token TOKCOLORLEGENDMINIMUMRANGE
%token TOKCOLORLEGENDUSECUMULATIVEDATARANGE
%token TOKCOLORLEGENDUSECURRENTDATARANGE
%token TOKCOLORLEGENDRANGE
%token TOKSHOWCOLORLEGEND
%token TOKCOLORBYBLOCKID
%token TOKCOLORBYSOLIDCOLOR
%token TOKSHOWEDGES
%token TOKCOLORBYSCALAR
%token TOKSHOWBOUNDINGBOX
%token TOKCOLORBYVECTORMAGNITUDE
%token TOKCOLORBYVECTORCOMPONENT
%token TOKCOLORBYTENSORCOMPONENT
%token TOKREPRESENTATION
%token TOKREPRESENTATION2
%token TOKREPRESENTATION3
%token TOKEQUAL
%token TOKSHOWSURFACES
%token <string> TOKTOP
%token <string> TOKLEFT
%token <string> TOKBOTTOM
%token <string> TOKRIGHT
%token <string> TOKTOPLEFT
%token <string> TOKTOPRIGHT
%token <string> TOKBOTTOMLEFT
%token <string> TOKBOTTOMRIGHT

/* Representation */

/* Catalyst */

%token TOKBEGIN
%token TOKCATALYST
%token TOKEND

/* Catalyst */

%token <integer> TOKFALSE
%token <integer> TOKTRUE
%token <floating> TOKVALUE
%token <string> TOKIDENTIFIER
%token <string> TOKSTRING

%error-verbose
%define api.pure
%locations

%lex-param {yyscan_t scanner}

%parse-param {yyscan_t scanner}
%parse-param {void* pd}

%type <string> element_or_node_data_flag
%type <string> operation_data_type
%type <string> threshold_value
%type <integer> boolean
%type <string> position
%type <string> box_clip_type
%type <string> cut_type_string
%type <string> side_type
%type <string> min_max_center
%type <string> variable_type
%type <string> on_off_type
%type <string> threshold_variable
%type <string> point_location

%%

/* START CATALYST BLOCK */

commands
      :
      | start_catalyst_block statements catalyst_end
      {
      if(!check_catalyst_shortcuts(&yylloc, scanner, c(pd)))
        YYABORT;
      if(!check_names(&yylloc, scanner, c(pd)))
        YYABORT;
      if(!check_cycles(&yylloc, scanner, c(pd)))
        YYABORT;
      }
      ;

catalyst_end
      : TOKEND
      | TOKEND TOKCATALYST
      ;

start_catalyst_block
      : TOKBEGIN TOKCATALYST { initialize_json(c(pd)); c(pd).cb = TOKCATALYST; }
      ;

statements
      :
      | statements statement
      ;

statement
      : show_surfaces
      | show_edges
      | show_bouding_box
      | color_by_scalar
      | color_by_vector_magnitude
      | color_by_vector_component
      | color_by_tensor_component
      | color_by_solid_color
      | color_by_blockid
      | show_color_legend
      | color_legend_range
      | color_legend_use_current_data_range
      | color_legend_use_cumulative_data_range
      | color_legend_minimum_range
      | color_legend_maximum_range
      | color_legend_position
      | preset_color_scale
      | invert_color_scale
      | show_time_annotation
      | time_annotation_position
      | show_axes
      | show_x_axis_label
      | show_y_axis_label
      | show_z_axis_label
      | x_axis_label_name
      | y_axis_label_name
      | z_axis_label_name
      | show_x_axis_tic_marks
      | show_y_axis_tic_marks
      | show_z_axis_tic_marks
      | show_x_axis_minor_tic_marks
      | show_y_axis_minor_tic_marks
      | show_z_axis_minor_tic_marks
      | show_x_axis_tic_mark_labels
      | show_y_axis_tic_mark_labels
      | show_z_axis_tic_mark_labels
      | show_orientation_axes
      | image_name_addon
      | look_at_absolute_point
      | look_at_relative_point
      | look_at_element
      | look_at_node
      | look_at_absolute_distance
      | look_at_relative_distance
      | up_vector
      | camera_fov
      | image_basename
      | image_basedirectory
      | image_format
      | image_digit_count
      | image_size
      | background_color
      | surface_color
      | edge_color
      | text_color
      | axes_color
      | threshold_shortcut
      | contour_shortcut
      | clip_shortcut
      | box_clip_shortcut
      | slice_shortcut
      | representation_block
      | multicamera8_block
      | camera_block
      | imageset_block
      | clip_block
      | slice_block
      | box_clip_block
      | threshold_block
      | contour_block
      | scatter_plot_block
      | plot_over_time_block
      | group_block
      | extract_block
      | transform_block
      | warp_by_vector
      | calculator
      | experimental_operation
      | visual_marker_block
      | experimental_block
      | on_off_criteria_block
      ;

/* END CATALYST BLOCK */

/* START REPRESENTATION BLOCK */

representation_block
      : start_representation_block representation_statements representation_end
      {
      if(!check_representation(&yylloc, scanner, c(pd), c(pd).root["representation blocks"][c(pd).cbn]))
        YYABORT;
      c(pd).cb = TOKCATALYST;
      }
      ;

representation_end
      : TOKEND
      | TOKEND TOKREPRESENTATION
      ;

start_representation_block
      : TOKBEGIN TOKREPRESENTATION TOKIDENTIFIER
      {
      c(pd).cb = TOKREPRESENTATION;
      c(pd).cbn = $3;
      c(pd).count = 1;
      if(!initialize_block(&yylloc, scanner, c(pd)))
        YYABORT;
      }
      ;

representation_statements
      :
      | representation_statements representation_statement
      ;

representation_statement
      : show_surfaces
      | show_edges
      | show_bouding_box
      | color_by_vector_magnitude
      | color_by_scalar
      | color_by_vector_component
      | color_by_tensor_component
      | color_by_solid_color
      | color_by_blockid
      | color_legend_range
      | show_color_legend
      | color_legend_use_current_data_range
      | color_legend_use_cumulative_data_range
      | color_legend_minimum_range
      | color_legend_maximum_range
      | color_legend_position
      | preset_color_scale
      | invert_color_scale
      | show_time_annotation
      | time_annotation_position
      | show_axes
      | show_x_axis_label
      | show_y_axis_label
      | show_z_axis_label
      | x_axis_label_name
      | y_axis_label_name
      | z_axis_label_name
      | show_x_axis_tic_marks
      | show_y_axis_tic_marks
      | show_z_axis_tic_marks
      | show_x_axis_minor_tic_marks
      | show_y_axis_minor_tic_marks
      | show_z_axis_minor_tic_marks
      | show_x_axis_tic_mark_labels
      | show_y_axis_tic_mark_labels
      | show_z_axis_tic_mark_labels
      | show_orientation_axes
      | image_name_addon
      | color_legend_subrange
      | highlight_subrange
      ;

boolean
      : TOKFALSE
      | TOKTRUE
      ;

show_surfaces
      : TOKSHOWSURFACES TOKEQUAL boolean { boolean_command(c(pd), "show surfaces", $3 == 1); }
      ;

show_edges
      : TOKSHOWEDGES TOKEQUAL boolean { boolean_command(c(pd), "show edges", $3 == 1); }
      ;

show_bouding_box
      : TOKSHOWBOUNDINGBOX TOKEQUAL boolean { boolean_command(c(pd), "show bounding box", $3 == 1); }
      ;

color_by_scalar
      : TOKCOLORBYSCALAR TOKEQUAL TOKIDENTIFIER 
      { 
      if(!check_variable_name(&yylloc, scanner, c(pd), $3, Iovs::PhactoriParserInterface::SCALAR)) 
        YYABORT;
      identifier_command(c(pd), "color by scalar", $3);
      }
      ;

color_by_vector_magnitude
      : TOKCOLORBYVECTORMAGNITUDE TOKEQUAL TOKIDENTIFIER
      {
      if(!check_variable_name(&yylloc, scanner, c(pd), $3, Iovs::PhactoriParserInterface::VECTOR)) 
        YYABORT;
      identifier_command(c(pd), "color by vector magnitude", $3);
      }
      ;

color_by_vector_component
      : TOKCOLORBYVECTORCOMPONENT TOKEQUAL TOKIDENTIFIER
      {
      if(!check_vector_component(&yylloc, scanner, c(pd), $3))
        YYABORT;
      identifier_command(c(pd), "color by vector component", $3);
      }
      ;

color_by_tensor_component
      : TOKCOLORBYTENSORCOMPONENT TOKEQUAL TOKIDENTIFIER
      {
      if(!check_tensor_component(&yylloc, scanner, c(pd), $3))
        YYABORT;
      identifier_command(c(pd), "color by tensor component", $3);
      }
      ;

color_by_solid_color
      : TOKCOLORBYSOLIDCOLOR TOKEQUAL TOKVALUE TOKVALUE TOKVALUE
      {
      if($3 < 0 || $3 > 1 ||
         $4 < 0 || $4 > 1 ||
         $5 < 0 || $5 > 1)
        {
        yyerror(&yylloc, scanner, pd, "Error: color by solid color arguments must be between 0 and 1, inclusive.");
        YYABORT;
        }
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      vector_command(c(pd), "color by solid color", c(pd).d); 
      } 
      ;

color_by_blockid
      : TOKCOLORBYBLOCKID { no_argument_command(c(pd), "color by blockid"); }
      ;

show_color_legend
      : TOKSHOWCOLORLEGEND TOKEQUAL boolean { boolean_command(c(pd), "show color legend", $3 == 1); }
      ;

color_legend_range
      : TOKCOLORLEGENDRANGE TOKEQUAL TOKVALUE TOKVALUE
      {
      if($3 > $4)
        {
        yyerror(&yylloc, scanner, pd, "Error: color legend range argument <min> must be less than argument <max>.");
        YYABORT;
        }
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      vector_command(c(pd), "color legend range", c(pd).d);
      }
      ;

color_legend_use_current_data_range
      : TOKCOLORLEGENDUSECURRENTDATARANGE { no_argument_command(c(pd), "color legend use current data range"); }
      ;

color_legend_use_cumulative_data_range
      : TOKCOLORLEGENDUSECUMULATIVEDATARANGE { no_argument_command(c(pd), "color legend use cumulative data range"); }
      ;

color_legend_minimum_range
      : TOKCOLORLEGENDMINIMUMRANGE TOKEQUAL TOKVALUE TOKVALUE
      {
      if($3 > $4)
        {
        yyerror(&yylloc, scanner, pd, "Error: color legend minimum range argument <min> must be less than argument <max>.");
        YYABORT;
        }
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      vector_command(c(pd), "color legend minimum range", c(pd).d);
      }
      ;

color_legend_maximum_range
      : TOKCOLORLEGENDMAXIMUMRANGE TOKEQUAL TOKVALUE TOKVALUE
      {
      if($3 > $4)
        {
        yyerror(&yylloc, scanner, pd, "Error: color legend maximum range argument <min> must be less than argument <max>.");
        YYABORT;
        }
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      vector_command(c(pd), "color legend maximum range", c(pd).d);
      }
      ;

position
      : TOKTOP
      | TOKLEFT
      | TOKBOTTOM
      | TOKRIGHT
      | TOKTOPLEFT
      | TOKTOPRIGHT
      | TOKBOTTOMRIGHT
      | TOKBOTTOMLEFT
      ;

color_legend_position
      : TOKCOLORLEGENDPOSITION TOKEQUAL position TOKVALUE
        { if(!check_two_argument_command(&yylloc, scanner, c(pd), "color legend position", $3, $4)) YYABORT; }
      ;

preset_color_scale
      : TOKPRESETCOLORSCALE TOKEQUAL TOKIDENTIFIER
      {
      std::string cn($3);
      if(!check_color_name(&yylloc, scanner, c(pd), cn))
        YYABORT;
      identifier_command(c(pd), "preset color scale", $3);
      }
      ;

invert_color_scale
      : TOKINVERTCOLORSCALE TOKEQUAL boolean { boolean_command(c(pd), "invert color scale", $3 == 1); }
      ;

show_time_annotation
      : TOKSHOWTIMEANNOTATION TOKEQUAL boolean { boolean_command(c(pd), "show time annotation", $3 == 1); }
      ;

time_annotation_position
      : TOKTIMEANNOTATIONPOSITION TOKEQUAL position TOKVALUE
        { if(!check_two_argument_command(&yylloc, scanner, c(pd), "time annotation position", $3, $4)) YYABORT; }
      ;

show_axes
      : TOKSHOWAXES TOKEQUAL boolean { boolean_command(c(pd), "show axes", $3 == 1); }
      ;

show_x_axis_label
      : TOKSHOWXAXISLABEL TOKEQUAL boolean { boolean_command(c(pd), "show x axis label", $3 == 1); }
      ;

show_y_axis_label
      : TOKSHOWYAXISLABEL TOKEQUAL boolean { boolean_command(c(pd), "show y axis label", $3 == 1); }
      ;

show_z_axis_label
      : TOKSHOWZAXISLABEL TOKEQUAL boolean { boolean_command(c(pd), "show z axis label", $3 == 1); }
      ;

x_axis_label_name
      : TOKXAXISLABELNAME TOKEQUAL TOKSTRING
      {
      std::string s($3);
      remove_quotes(s);
      identifier_command(c(pd), "x axis label name", s);
      }
      ;

y_axis_label_name
      : TOKYAXISLABELNAME TOKEQUAL TOKSTRING
      {
      std::string s($3);
      remove_quotes(s);
      identifier_command(c(pd), "y axis label name", s);
      }
      ;

z_axis_label_name
      : TOKZAXISLABELNAME TOKEQUAL TOKSTRING
      {
      std::string s($3);
      remove_quotes(s);
      identifier_command(c(pd), "z axis label name", s);
      }
      ;

show_x_axis_tic_marks
      : TOKSHOWXAXISTICMARKS TOKEQUAL boolean { boolean_command(c(pd), "show x axis tic marks", $3 == 1); }
      ;

show_y_axis_tic_marks
      : TOKSHOWYAXISTICMARKS TOKEQUAL boolean { boolean_command(c(pd), "show y axis tic marks", $3 == 1); }
      ;

show_z_axis_tic_marks
      : TOKSHOWZAXISTICMARKS TOKEQUAL boolean { boolean_command(c(pd), "show z axis tic marks", $3 == 1); }
      ;

show_x_axis_minor_tic_marks
      : TOKSHOWXAXISMINORTICMARKS TOKEQUAL boolean { boolean_command(c(pd), "show x axis minor tic marks", $3 == 1); }
      ;

show_y_axis_minor_tic_marks
      : TOKSHOWYAXISMINORTICMARKS TOKEQUAL boolean { boolean_command(c(pd), "show y axis minor tic marks", $3 == 1); }
      ;

show_z_axis_minor_tic_marks
      : TOKSHOWZAXISMINORTICMARKS TOKEQUAL boolean { boolean_command(c(pd), "show z axis minor tic marks", $3 == 1); }
      ;

show_x_axis_tic_mark_labels
      : TOKSHOWXAXISTICMARKLABELS TOKEQUAL boolean { boolean_command(c(pd), "show x axis tic mark labels", $3 == 1); }
      ;

show_y_axis_tic_mark_labels
      : TOKSHOWYAXISTICMARKLABELS TOKEQUAL boolean { boolean_command(c(pd), "show y axis tic mark labels", $3 == 1); }
      ;

show_z_axis_tic_mark_labels
      : TOKSHOWZAXISTICMARKLABELS TOKEQUAL boolean { boolean_command(c(pd), "show z axis tic mark labels", $3 == 1); }
      ;

show_orientation_axes
      : TOKSHOWORIENTATIONAXES TOKEQUAL boolean { boolean_command(c(pd), "show orientation axes", $3 == 1); }
      ;

image_name_addon
      : TOKIMAGENAMEADDON TOKEQUAL TOKSTRING
      {
      std::string s($3);
      remove_quotes(s);
      identifier_command(c(pd), "image name addon", s);
      }
      ;

color_legend_subrange
      : TOKCOLORLEGENDSUBRANGE TOKEQUAL TOKVALUE TOKVALUE
      {
      if($3 > $4)
        {
        yyerror(&yylloc, scanner, pd, "Error: color legend subrange argument <min> must be less than argument <max>.");
        YYABORT;
        }
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      vector_command(c(pd), "color legend subrange", c(pd).d);
      }

highlight_subrange
      : TOKHIGHLIGHTSUBRANGE TOKEQUAL TOKVALUE TOKVALUE TOKVALUE TOKVALUE TOKVALUE
      {
      if($3 > $4)
        {
        yyerror(&yylloc, scanner, pd, "Error: highlight subrange argument <min> must be less than argument <max>.");
        YYABORT;
        }
      if($5 < 0 || $5 > 1 ||
         $6 < 0 || $6 > 1 ||
         $7 < 0 || $7 > 1)
        {
        yyerror(&yylloc, scanner, pd, "Error: highlight subrange <color> arguments must be between 0 and 1, inclusive.");
        YYABORT;
        }
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      c(pd).d.push_back($6);
      c(pd).d.push_back($7);
      std::ostringstream convert;
      convert << c(pd).count;
      vector_command(c(pd), "highlight subrange " + convert.str(), c(pd).d);
      c(pd).count++;
      }
      ;

/* END REPRESENTATION BLOCK */

/* START MULTICAMERA8 BLOCK */

multicamera8_block
      : start_multicamera8_block multicamera8_statements multicamera8_end
      {
      if(!check_camera(&yylloc, scanner, c(pd), c(pd).root["camera blocks"][c(pd).cbn]))
        YYABORT;
      c(pd).cb = TOKCATALYST;
      }
      ;

multicamera8_end
      : TOKEND
      | TOKEND TOKMULTICAMERA8
      ;

start_multicamera8_block
      : TOKBEGIN TOKMULTICAMERA8 TOKIDENTIFIER
      {
      c(pd).cb = TOKMULTICAMERA8;
      c(pd).cbn = $3;
      if(!initialize_block(&yylloc, scanner, c(pd)))
        YYABORT;
      }
      ;

multicamera8_statements
      :
      | multicamera8_statements multicamera8_statement
      ;

multicamera8_statement
      : look_at_absolute_point
      | look_at_relative_point
      | look_at_element
      | look_at_node
      | look_at_absolute_distance
      | look_at_relative_distance
      | up_vector
      | camera_fov
      | image_name_addon
      ;

look_at_absolute_point
      : TOKLOOKATABSOLUTEPOINT TOKEQUAL TOKVALUE TOKVALUE TOKVALUE
      {
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      vector_command(c(pd), "look at absolute point", c(pd).d);
      }
      ;

look_at_relative_point
      : TOKLOOKATRELATIVEPOINT TOKEQUAL TOKVALUE TOKVALUE TOKVALUE
      {
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      vector_command(c(pd), "look at relative point", c(pd).d);
      }
      ;

look_at_element
      : TOKLOOKATELEMENT TOKEQUAL TOKVALUE
      {
      if( (std::floor($3) != $3) ||
          ($3 < 1) )
        {
        yyerror(&yylloc, scanner, pd, "Error: look at element <id> must be an integer greater than 0");
        YYABORT;
        }
      check_elementID(&yylloc, scanner, c(pd), (int) $3);
      integer_command(c(pd), "look at element", (int) $3);
      }
      ;

look_at_node
      : TOKLOOKATNODE TOKEQUAL TOKVALUE
      {
      if( (std::floor($3) != $3) ||
          ($3 < 1) )
        {
        yyerror(&yylloc, scanner, pd, "Error: look at node <id> must be an integer greater than 0");
        YYABORT;
        }
      check_nodeID(&yylloc, scanner, c(pd), (int) $3);
      integer_command(c(pd), "look at node", (int) $3);
      }
      ;

look_at_absolute_distance
      : TOKLOOKATABSOLUTEDISTANCE TOKEQUAL TOKVALUE
      {
      if( $3 <= 0.0 )
        {
        yyerror(&yylloc, scanner, pd, "Error: look at absolute distance <value> must be greater than 0");
        YYABORT;
        }
      double_command(c(pd), "look at absolute distance", $3);
      }
      ;

look_at_relative_distance
      : TOKLOOKATRELATIVEDISTANCE TOKEQUAL TOKVALUE
      {
      if( $3 <= 0.0 )
        {
        yyerror(&yylloc, scanner, pd, "Error: look at relative distance <value> must be greater than 0");
        YYABORT;
        }
      double_command(c(pd), "look at relative distance", $3);
      }
      ;

up_vector
      : TOKUPVECTOR TOKEQUAL TOKVALUE TOKVALUE TOKVALUE
      {
      if( $3 == 0.0 && $4 == 0.0 && $5 == 0.0 )
        {
        yyerror(&yylloc, scanner, pd, "Error: up vector cannot be all zeros");
        YYABORT;
        }
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      vector_command(c(pd), "up vector", c(pd).d);
      }
      ;

camera_fov
      : TOKCAMERAFOV TOKEQUAL TOKVALUE
      {
      if( $3 <= 0.0 || $3 > 180.0 )
        {
        yyerror(&yylloc, scanner, pd, "Error: camera fov <value> must be greater than 0 and less than or equal 180");
        YYABORT;
        }
      double_command(c(pd), "camera fov", $3);
      }
      ;

/* END MULTICAMERA8 BLOCK */

/* START CAMERA BLOCK */

camera_block
      : start_camera_block camera_statements camera_end
      {
      if(!check_camera(&yylloc, scanner, c(pd), c(pd).root["camera blocks"][c(pd).cbn]))
        YYABORT;
      c(pd).cb = TOKCATALYST;
      }
      ;

camera_end
      : TOKEND
      | TOKEND TOKCAMERA
      ;

start_camera_block
      : TOKBEGIN TOKCAMERA TOKIDENTIFIER
      {
      c(pd).cb = TOKCAMERA;
      c(pd).cbn = $3;
      if(!initialize_block(&yylloc, scanner, c(pd)))
        YYABORT;
      }
      ;

camera_statements
      :
      | camera_statements camera_statement
      ;

camera_statement
      : look_at_absolute_point
      | look_at_relative_point
      | look_at_element
      | look_at_node
      | look_at_absolute_distance
      | look_at_relative_distance
      | up_vector
      | camera_fov
      | image_name_addon
      | look_direction
      | camera_at_absolute_point
      | camera_at_relative_point
      | camera_at_element
      | camera_at_node
      | camera_at_element_displaced
      | camera_at_node_displaced
      | camera_at_data_point
      | camera_at_data_point_displaced
      ;

look_direction
      : TOKLOOKDIRECTION TOKEQUAL TOKVALUE TOKVALUE TOKVALUE
      {
      if( $3 == 0.0 && $4 == 0.0 && $5 == 0.0 )
        {
        yyerror(&yylloc, scanner, pd, "Error: look direction vector cannot be all zeros");
        YYABORT;
        }
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      vector_command(c(pd), "look direction", c(pd).d);
      }
      ;

camera_at_absolute_point
      : TOKCAMERAATABSOLUTEPOINT TOKEQUAL TOKVALUE TOKVALUE TOKVALUE
      {
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      vector_command(c(pd), "camera at absolute point", c(pd).d);
      }
      ;

camera_at_relative_point
      : TOKCAMERAATRELATIVEPOINT TOKEQUAL TOKVALUE TOKVALUE TOKVALUE
      {
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      vector_command(c(pd), "camera at relative point", c(pd).d);
      }
      ;

camera_at_element
      : TOKCAMERAATELEMENT TOKEQUAL TOKVALUE
      {
      if( (std::floor($3) != $3) ||
          ($3 < 1) )
        {
        yyerror(&yylloc, scanner, pd, "Error: camera at element <id> must be an integer greater than 0");
        YYABORT;
        }
      check_elementID(&yylloc, scanner, c(pd), (int) $3);
      integer_command(c(pd), "camera at element", (int) $3);
      }
      ;

camera_at_node
      : TOKCAMERAATNODE TOKEQUAL TOKVALUE
      {
      if( (std::floor($3) != $3) ||
          ($3 < 1) )
        {
        yyerror(&yylloc, scanner, pd, "Error: camera at node <id> must be an integer greater than 0");
        YYABORT;
        }
      check_nodeID(&yylloc, scanner, c(pd), (int) $3);
      integer_command(c(pd), "camera at node", (int) $3);
      }
      ;

camera_at_element_displaced
      : TOKCAMERAATELEMENTDISPLACED TOKEQUAL TOKVALUE TOKVALUE TOKVALUE TOKVALUE
      {
      if( (std::floor($3) != $3) ||
          ($3 < 1) )
        {
        yyerror(&yylloc, scanner, pd, "Error: camera at element displaced <id> must be an integer greater than 0");
        YYABORT;
        }
      check_elementID(&yylloc, scanner, c(pd), (int) $3);
      four_argument_command(c(pd), "camera at element displaced", (int) $3, $4, $5, $6);
      }
      ;

camera_at_node_displaced
      : TOKCAMERAATNODEDISPLACED TOKEQUAL TOKVALUE TOKVALUE TOKVALUE TOKVALUE
      {
      if( (std::floor($3) != $3) ||
          ($3 < 1) )
        {
        yyerror(&yylloc, scanner, pd, "Error: camera at node displaced <id> must be an integer greater than 0");
        YYABORT;
        }
      check_nodeID(&yylloc, scanner, c(pd), (int) $3);
      four_argument_command(c(pd), "camera at node displaced", (int) $3, $4, $5, $6);
      }
      ;

camera_at_data_point
      : TOKCAMERAATDATAPOINT TOKEQUAL min_max_center variable_type TOKIDENTIFIER TOKIDENTIFIER
      {
      data_point_command(c(pd), "camera at data point", $3, $4, $5, $6);
      }
      ;

camera_at_data_point_displaced
      : TOKCAMERAATDATAPOINTDISPLACED TOKEQUAL min_max_center variable_type TOKIDENTIFIER TOKIDENTIFIER TOKVALUE TOKVALUE TOKVALUE
      {
      data_point_command(c(pd), "camera at data point displaced", $3, $4, $5, $6, $7, $8, $9);
      }
      ;

/* END CAMERA BLOCK */

/* START IMAGESET BLOCK */

imageset_block
      : start_imageset_block imageset_statements imageset_end
      {
      bool ret = true;
      ret = ret && check_imageset(&yylloc, scanner, c(pd), c(pd).root["imageset blocks"][c(pd).cbn]);
      ret = ret && check_representation(&yylloc, scanner, c(pd), c(pd).root["imageset blocks"][c(pd).cbn]);
      ret = ret && check_camera(&yylloc, scanner, c(pd), c(pd).root["imageset blocks"][c(pd).cbn]);
      if(!ret)
         YYABORT;
      c(pd).cb = TOKCATALYST;
      }
      ;

imageset_end
      : TOKEND
      | TOKEND TOKIMAGESET
      ;

start_imageset_block
      : TOKBEGIN TOKIMAGESET TOKIDENTIFIER
      {
      c(pd).cb = TOKIMAGESET;
      c(pd).cbn = $3;
      if(!initialize_block(&yylloc, scanner, c(pd)))
        YYABORT;
      }
      ;

imageset_statements
      :
      | imageset_statements imageset_statement
      ;

imageset_statement
      : camera
      | representation
      | representation2
      | representation3
      | operation
      | operation2
      | operation3
      | image_basename
      | image_basedirectory
      | image_format
      | image_digit_count
      | image_size
      | background_color
      | surface_color
      | edge_color
      | text_color
      | axes_color
      | threshold_shortcut
      | contour_shortcut
      | clip_shortcut
      | box_clip_shortcut
      | slice_shortcut
      | on_off_criteria
      | markers

      /* Representation and MultiCamera8 Shortcuts */

      | show_surfaces
      | show_edges
      | show_bouding_box
      | color_by_scalar
      | color_by_vector_magnitude
      | color_by_vector_component
      | color_by_tensor_component
      | color_by_solid_color
      | color_by_blockid
      | show_color_legend
      | color_legend_range
      | color_legend_use_current_data_range
      | color_legend_use_cumulative_data_range
      | color_legend_minimum_range
      | color_legend_maximum_range
      | color_legend_position
      | preset_color_scale
      | invert_color_scale
      | show_time_annotation
      | time_annotation_position
      | show_axes
      | show_x_axis_label
      | show_y_axis_label
      | show_z_axis_label
      | x_axis_label_name
      | y_axis_label_name
      | z_axis_label_name
      | show_x_axis_tic_marks
      | show_y_axis_tic_marks
      | show_z_axis_tic_marks
      | show_x_axis_minor_tic_marks
      | show_y_axis_minor_tic_marks
      | show_z_axis_minor_tic_marks
      | show_x_axis_tic_mark_labels
      | show_y_axis_tic_mark_labels
      | show_z_axis_tic_mark_labels
      | show_orientation_axes
      | look_at_absolute_point
      | look_at_relative_point
      | look_at_element
      | look_at_node
      | look_at_absolute_distance
      | look_at_relative_distance
      | up_vector
      | camera_fov

      /* Representation and MultiCamera8 Shortcuts */

      ;

camera
      : TOKCAMERA TOKEQUAL TOKIDENTIFIER
      {
      identifier_command(c(pd), "camera", $3);
      }
      ;

representation
      : TOKREPRESENTATION TOKEQUAL TOKIDENTIFIER
      {
      identifier_command(c(pd), "representation", $3);
      }
      ;

representation2
      : TOKREPRESENTATION2 TOKEQUAL TOKIDENTIFIER
      {
      identifier_command(c(pd), "representation2", $3);
      }
      ;

representation3
      : TOKREPRESENTATION3 TOKEQUAL TOKIDENTIFIER
      {
      identifier_command(c(pd), "representation3", $3);
      }
      ;

operation
      : TOKOPERATION TOKEQUAL TOKIDENTIFIER
      {
      identifier_command(c(pd), "operation", $3);
      }
      ;

operation2
      : TOKOPERATION2 TOKEQUAL TOKIDENTIFIER
      {
      identifier_command(c(pd), "operation2", $3);
      }
      ;

operation3
      : TOKOPERATION3 TOKEQUAL TOKIDENTIFIER
      {
      identifier_command(c(pd), "operation3", $3);
      }
      ;

image_basename
      : TOKIMAGEBASENAME TOKEQUAL TOKSTRING
      {
      std::string s($3);
      remove_quotes(s);
      identifier_command(c(pd), "image basename", s);
      }
      ;

image_basedirectory
      : TOKIMAGEBASEDIRECTORY TOKEQUAL TOKSTRING
      {
      std::string s($3);
      remove_quotes(s);
      identifier_command(c(pd), "image basedirectory", s);
      }
      ;

image_format
      : TOKIMAGEFORMAT TOKEQUAL TOKJPG { identifier_command(c(pd), "image format", "jpg"); }
      | TOKIMAGEFORMAT TOKEQUAL TOKPNG { identifier_command(c(pd), "image format", "png"); }
      ;

image_digit_count
      : TOKIMAGEDIGITCOUNT TOKEQUAL TOKVALUE
      {
      if( (std::floor($3) != $3) ||
          ($3 < 0) )
        {
        yyerror(&yylloc, scanner, pd, "Error: image digit count <count> must be an integer greater than or equal to 0");
        YYABORT;
        }
      integer_command(c(pd), "image digit count", (int) $3);
      }
      ;

image_size
      : TOKIMAGESIZE TOKEQUAL TOKVALUE TOKVALUE
      {
      if( (std::floor($3) != $3) ||
          ($3 < 1) ||
          ($3 > 4096) ||
          (std::floor($4) != $4) ||
          ($4 < 1) ||
          ($4 > 4096) )
        {
        yyerror(&yylloc, scanner, pd, "Error: image size <width> <height> must be integers greater than or equal to 1, and less than or equal to 4096");
        YYABORT;
        }
      c(pd).intValues.clear();
      c(pd).intValues.push_back($3);
      c(pd).intValues.push_back($4);
      vector_command(c(pd), "image size", c(pd).intValues);
      }
      ;

background_color
      : TOKBACKGROUNDCOLOR TOKEQUAL TOKVALUE TOKVALUE TOKVALUE
      {
      if($3 < 0 || $3 > 1 ||
         $4 < 0 || $4 > 1 ||
         $5 < 0 || $5 > 1)
        {
        yyerror(&yylloc, scanner, pd, "Error: background color <color> arguments must be between 0 and 1, inclusive.");
        YYABORT;
        }
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      vector_command(c(pd), "background color", c(pd).d); 
      } 
      ;

surface_color
      : TOKSURFACECOLOR TOKEQUAL TOKVALUE TOKVALUE TOKVALUE
      {
      if($3 < 0 || $3 > 1 ||
         $4 < 0 || $4 > 1 ||
         $5 < 0 || $5 > 1)
        {
        yyerror(&yylloc, scanner, pd, "Error: surface color <color> arguments must be between 0 and 1, inclusive.");
        YYABORT;
        }
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      vector_command(c(pd), "surface color", c(pd).d); 
      } 
      ;

edge_color
      : TOKEDGECOLOR TOKEQUAL TOKVALUE TOKVALUE TOKVALUE
      {
      if($3 < 0 || $3 > 1 ||
         $4 < 0 || $4 > 1 ||
         $5 < 0 || $5 > 1)
        {
        yyerror(&yylloc, scanner, pd, "Error: edge color <color> arguments must be between 0 and 1, inclusive.");
        YYABORT;
        }
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      vector_command(c(pd), "edge color", c(pd).d); 
      } 
      ;

text_color
      : TOKTEXTCOLOR TOKEQUAL TOKVALUE TOKVALUE TOKVALUE
      {
      if($3 < 0 || $3 > 1 ||
         $4 < 0 || $4 > 1 ||
         $5 < 0 || $5 > 1)
        {
        yyerror(&yylloc, scanner, pd, "Error: text color <color> arguments must be between 0 and 1, inclusive.");
        YYABORT;
        }
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      vector_command(c(pd), "text color", c(pd).d); 
      } 
      ;

axes_color
      : TOKAXESCOLOR TOKEQUAL TOKVALUE TOKVALUE TOKVALUE
      {
      if($3 < 0 || $3 > 1 ||
         $4 < 0 || $4 > 1 ||
         $5 < 0 || $5 > 1)
        {
        yyerror(&yylloc, scanner, pd, "Error: axes color <color> arguments must be between 0 and 1, inclusive.");
        YYABORT;
        }
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      vector_command(c(pd), "axes color", c(pd).d); 
      } 
      ;

threshold_shortcut
      : TOKTHRESHOLD TOKEQUAL operation_data_type TOKIDENTIFIER TOKKEEPBETWEEN TOKVALUE TOKVALUE
      {
      c(pd).d.clear();
      c(pd).d.push_back($6);
      c(pd).d.push_back($7);
      threshold_or_contour_shortcut_command(c(pd), "threshold", $3, $4, $5, c(pd).d);
      }
      | TOKTHRESHOLD TOKEQUAL operation_data_type TOKIDENTIFIER threshold_value TOKVALUE
      {
      c(pd).d.clear();
      c(pd).d.push_back($6);
      threshold_or_contour_shortcut_command(c(pd), "threshold", $3, $4, $5, c(pd).d);
      }
      ;

operation_data_type
      : TOKSCALAR
      | TOKVECTORMAGNITUDE
      | TOKVECTORCOMPONENT
      | TOKTENSORCOMPONENT
      ;

threshold_value
      : TOKKEEPABOVE
      | TOKKEEPBELOW
      ;

contour_shortcut
      : TOKCONTOUR TOKEQUAL operation_data_type TOKIDENTIFIER TOKVALUESEQUENCE TOKVALUE TOKVALUE TOKVALUE
      {
      if($6 >= $8)
        {
        yyerror(&yylloc, scanner, pd, "Error: contour shortcut contains 'value sequence' with start greater than or equal to stop");
        YYABORT;
        }
      if($7 <= 0)
        {
        yyerror(&yylloc, scanner, pd, "Error: contour shortcut contains 'value sequence' with a negative or zero step");
        YYABORT;
        }
      c(pd).d.clear();
      c(pd).d.push_back($6);
      c(pd).d.push_back($7);
      c(pd).d.push_back($8);
      threshold_or_contour_shortcut_command(c(pd), "contour", $3, $4, $5, c(pd).d);
      }
      | TOKCONTOUR TOKEQUAL operation_data_type TOKIDENTIFIER TOKVALUELIST { c(pd).d.clear(); } contour_value_list
      {
      threshold_or_contour_shortcut_command(c(pd), "contour", $3, $4, $5, c(pd).d);
      }
      ;

contour_value_list
      : contour_value_member
      | contour_value_list contour_value_member
      ;

contour_value_member
      : TOKVALUE { c(pd).d.push_back($1); }
      ;

clip_shortcut
      : TOKCLIP TOKEQUAL TOKPOINT TOKVALUE TOKVALUE TOKVALUE TOKNORMAL TOKVALUE TOKVALUE TOKVALUE
      {
      if( $8 == 0.0 && $9 == 0.0 && $10 == 0.0 )
        {
        yyerror(&yylloc, scanner, pd, "Error: clip shortcut vector cannot be all zeros");
        YYABORT;
        }
      c(pd).d.clear();
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      c(pd).d.push_back($6);
      c(pd).d.push_back($8);
      c(pd).d.push_back($9);
      c(pd).d.push_back($10);
      clip_or_boxclip_or_slice_shortcut_command(c(pd), "clip", "", c(pd).d);
      }
      ;

box_clip_shortcut
      : TOKBOXCLIP TOKEQUAL TOKCENTER TOKVALUE TOKVALUE TOKVALUE TOKEXTENTS TOKVALUE TOKVALUE TOKVALUE box_clip_type
      {
      if( $8 == 0.0 && $9 == 0.0 && $10 == 0.0 )
        {
        yyerror(&yylloc, scanner, pd, "Error: box clip shortcut extents cannot be all zeros");
        YYABORT;
        }
      c(pd).d.clear();
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      c(pd).d.push_back($6);
      c(pd).d.push_back($8);
      c(pd).d.push_back($9);
      c(pd).d.push_back($10);
      clip_or_boxclip_or_slice_shortcut_command(c(pd), "boxclip", $11, c(pd).d);
      }
      ;

box_clip_type
      : TOKKEEPINSIDE
      | TOKKEEPOUTSIDE
      ;

slice_shortcut
      : TOKSLICE TOKEQUAL TOKPOINT TOKVALUE TOKVALUE TOKVALUE TOKNORMAL TOKVALUE TOKVALUE TOKVALUE
      {
      if( $8 == 0.0 && $9 == 0.0 && $10 == 0.0 )
        {
        yyerror(&yylloc, scanner, pd, "Error: slice shortcut vector cannot be all zeros");
        YYABORT;
        }
      c(pd).d.clear();
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      c(pd).d.push_back($6);
      c(pd).d.push_back($8);
      c(pd).d.push_back($9);
      c(pd).d.push_back($10);
      clip_or_boxclip_or_slice_shortcut_command(c(pd), "slice", "", c(pd).d);
      }
      ;

on_off_criteria
      : TOKONOFFCRITERIA TOKEQUAL { c(pd).s.clear(); } identifier_list
      {
      string_vector_command(c(pd), "onoff criteria", c(pd).s);
      }
      ;

identifier_list
      : identifier_list_member
      | identifier_list identifier_list_member
      ;

identifier_list_member
      : TOKIDENTIFIER { c(pd).s.push_back($1); }
      ;

markers
      : TOKMARKERS TOKEQUAL { c(pd).s.clear(); } identifier_list
      {
      string_vector_command(c(pd), "markers", c(pd).s);
      }
      ;

/* END IMAGESET BLOCK */

/* START CLIP BLOCK */

clip_block
      : start_clip_block clip_statements clip_end
      {
      if(!check_clip_or_slice(&yylloc, scanner, c(pd), c(pd).root["operation blocks"][c(pd).cbn]))
        YYABORT;
      c(pd).cb = TOKCATALYST;
      }
      ;

clip_end
      : TOKEND
      | TOKENDCLIP
      ;

start_clip_block
      : TOKBEGIN TOKCLIP TOKIDENTIFIER
      {
      c(pd).cb = TOKCLIP;
      c(pd).cbn = $3;
      if(!initialize_block(&yylloc, scanner, c(pd)))
        YYABORT;
      }
      ;

clip_statements
      :
      | clip_statements clip_statement
      ;

clip_statement
      : input
      | absolute_point_on_plane
      | relative_point_on_plane
      | node_on_plane
      | element_on_plane
      | plane_normal
      | cut_type
      | side_to_keep
      | data_point_on_plane
      | plane_specification
      ;

input
      : TOKINPUT TOKEQUAL TOKIDENTIFIER
      {
      std::string s($3);
      if(s == c(pd).cbn)
        {
        yyerror(&yylloc, scanner, pd, "Error: operation <input> cannot match operation name");
        YYABORT;
        }

      identifier_command(c(pd), "input", $3);
      }
      ;

point_location
      : TOKA
      | TOKB
      | TOKC
      ;

absolute_point_on_plane
      : TOKABSOLUTEPOINTONPLANE TOKEQUAL TOKVALUE TOKVALUE TOKVALUE
      {
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      vector_command(c(pd), "absolute point on plane", c(pd).d); 
      }
      | TOKABSOLUTEPOINTONPLANE point_location TOKEQUAL TOKVALUE TOKVALUE TOKVALUE
      {
      std::string ct($2);
      std::transform(ct.begin(), ct.end(), ct.begin(), ::toupper);
      c(pd).d.clear();
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      c(pd).d.push_back($6);
      vector_command(c(pd), "absolute point on plane " + ct, c(pd).d); 
      }
      ;

relative_point_on_plane
      : TOKRELATIVEPOINTONPLANE TOKEQUAL TOKVALUE TOKVALUE TOKVALUE
      {
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      vector_command(c(pd), "relative point on plane", c(pd).d);
      }
      | TOKRELATIVEPOINTONPLANE point_location TOKEQUAL TOKVALUE TOKVALUE TOKVALUE
      {
      std::string ct($2);
      std::transform(ct.begin(), ct.end(), ct.begin(), ::toupper);
      c(pd).d.clear();
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      c(pd).d.push_back($6);
      vector_command(c(pd), "relative point on plane " + ct, c(pd).d); 
      }
      ;

node_on_plane
      : TOKNODEONPLANE TOKEQUAL TOKVALUE
      {
      if( (std::floor($3) != $3) ||
          ($3 < 1) )
        {
        yyerror(&yylloc, scanner, pd, "Error: node on plane <id> must be an integer greater than 0");
        YYABORT;
        }
      check_nodeID(&yylloc, scanner, c(pd), (int) $3);
      integer_command(c(pd), "node on plane", (int) $3);
      }
      | TOKNODEONPLANE point_location TOKEQUAL TOKVALUE
      {
      if( (std::floor($4) != $4) ||
          ($4 < 1) )
        {
        yyerror(&yylloc, scanner, pd, "Error: node on plane <id> must be an integer greater than 0");
        YYABORT;
        }
      check_nodeID(&yylloc, scanner, c(pd), (int) $4);
      std::string ct($2);
      std::transform(ct.begin(), ct.end(), ct.begin(), ::toupper);
      integer_command(c(pd), "node on plane  " + ct, (int) $4);
      }
      ;

element_on_plane
      : TOKELEMENTONPLANE TOKEQUAL TOKVALUE
      {
      if( (std::floor($3) != $3) ||
          ($3 < 1) )
        {
        yyerror(&yylloc, scanner, pd, "Error: element on plane <id> must be an integer greater than 0");
        YYABORT;
        }
      check_elementID(&yylloc, scanner, c(pd), (int) $3);
      integer_command(c(pd), "element on plane", (int) $3);
      }
      | TOKELEMENTONPLANE point_location TOKEQUAL TOKVALUE
      {
      if( (std::floor($4) != $4) ||
          ($4 < 1) )
        {
        yyerror(&yylloc, scanner, pd, "Error: element on plane <id> must be an integer greater than 0");
        YYABORT;
        }
      check_elementID(&yylloc, scanner, c(pd), (int) $4);
      std::string ct($2);
      std::transform(ct.begin(), ct.end(), ct.begin(), ::toupper);
      integer_command(c(pd), "element on plane " + ct, (int) $4);
      }
      ;

plane_normal
      : TOKPLANENORMAL TOKEQUAL TOKVALUE TOKVALUE TOKVALUE
      {
      if( $3 == 0.0 && $4 == 0.0 && $5 == 0.0 )
        {
        yyerror(&yylloc, scanner, pd, "Error: plane normal cannot be all zeros");
        YYABORT;
        }
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      vector_command(c(pd), "plane normal", c(pd).d);
      }

cut_type
      : TOKCUTTYPE TOKEQUAL cut_type_string
      {
      std::string ct($3);
      std::transform(ct.begin(), ct.end(), ct.begin(), ::tolower);
      identifier_command(c(pd), "cut type", ct);
      }
      ;

cut_type_string
      : TOKCRINKLE
      | TOKSMOOTH
      ;

side_to_keep
      : TOKSIDETOKEEP TOKEQUAL side_type
      {
      std::string ct($3);
      std::transform(ct.begin(), ct.end(), ct.begin(), ::tolower);
      identifier_command(c(pd), "side to keep", ct);
      }
      ;

side_type
      : TOKPOSITIVE
      | TOKNEGATIVE
      ;

data_point_on_plane
      : TOKDATAPOINTONPLANE TOKEQUAL min_max_center variable_type TOKIDENTIFIER TOKIDENTIFIER
      {
      data_point_command(c(pd), "data point on plane", $3, $4, $5, $6);
      }
      | TOKDATAPOINTONPLANE point_location TOKEQUAL min_max_center variable_type TOKIDENTIFIER TOKIDENTIFIER
      {
      std::string ct($2);
      std::transform(ct.begin(), ct.end(), ct.begin(), ::toupper);
      data_point_command(c(pd), "data point on plane " + ct, $4, $5, $6, $7);
      }
      ;

plane_specification
      : TOKPLANESPECIFICATION TOKEQUAL TOKTHREEPOINTS { identifier_command(c(pd), "plane specification", "three points"); }
      | TOKPLANESPECIFICATION TOKEQUAL TOKPOINTANDNORMAL { identifier_command(c(pd), "plane specification", "point and normal"); }
      ;


/* END CLIP BLOCK */

/* START SLICE BLOCK */

slice_block
      : start_slice_block slice_statements slice_end
      {
      if(!check_clip_or_slice(&yylloc, scanner, c(pd), c(pd).root["operation blocks"][c(pd).cbn]))
        YYABORT;
      c(pd).cb = TOKCATALYST;
      }
      ;

slice_end
      : TOKEND
      | TOKENDSLICE
      ;

start_slice_block
      : TOKBEGIN TOKSLICE TOKIDENTIFIER
      {
      c(pd).cb = TOKSLICE;
      c(pd).cbn = $3;
      if(!initialize_block(&yylloc, scanner, c(pd)))
        YYABORT;
      }
      ;

slice_statements
      :
      | slice_statements slice_statement
      ;

slice_statement
      : input
      | absolute_point_on_plane
      | relative_point_on_plane
      | node_on_plane
      | element_on_plane
      | plane_normal
      | cut_type
      | data_point_on_plane
      | plane_specification
      ;

/* END SLICE BLOCK */

/* START BOXCLIP BLOCK */

box_clip_block
      : start_box_clip_block box_clip_statements box_clip_end
      {
      if(!check_box_clip(&yylloc, scanner, c(pd), c(pd).root["operation blocks"][c(pd).cbn]))
        YYABORT;
      c(pd).cb = TOKCATALYST;
      }
      ;

box_clip_end
      : TOKEND
      | TOKENDBOXCLIP
      ;

start_box_clip_block
      : TOKBEGIN TOKBOXCLIP TOKIDENTIFIER
      {
      c(pd).cb = TOKBOXCLIP;
      c(pd).cbn = $3;
      if(!initialize_block(&yylloc, scanner, c(pd)))
        YYABORT;
      }
      ;

box_clip_statements
      :
      | box_clip_statements box_clip_statement
      ;

box_clip_statement
      : input
      | center_at_absolute_point
      | center_at_relative_point
      | center_at_node
      | center_at_element
      | absolute_extents
      | relative_extents
      | rotations
      | keep_inside_box
      | cut_type
      | center_at_data_point
      ;

center_at_absolute_point
      : TOKCENTERATABSOLUTEPOINT TOKEQUAL TOKVALUE TOKVALUE TOKVALUE
      {
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      vector_command(c(pd), "center at absolute point", c(pd).d); 
      }
      ;

center_at_relative_point
      : TOKCENTERATRELATIVEPOINT TOKEQUAL TOKVALUE TOKVALUE TOKVALUE
      {
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      vector_command(c(pd), "center at relative point", c(pd).d); 
      }
      ;

center_at_node
      : TOKCENTERATNODE TOKEQUAL TOKVALUE
      {
      if( (std::floor($3) != $3) ||
          ($3 < 1) )
        {
        yyerror(&yylloc, scanner, pd, "Error: center at node <id> must be an integer greater than 0");
        YYABORT;
        }
      check_nodeID(&yylloc, scanner, c(pd), (int) $3);
      integer_command(c(pd), "center at node", (int) $3);
      }
      ;

center_at_element
      : TOKCENTERATELEMENT TOKEQUAL TOKVALUE
      {
      if( (std::floor($3) != $3) ||
          ($3 < 1) )
        {
        yyerror(&yylloc, scanner, pd, "Error: center at element <id> must be an integer greater than 0");
        YYABORT;
        }
      check_elementID(&yylloc, scanner, c(pd), (int) $3);
      integer_command(c(pd), "center at element", (int) $3);
      }
      ;

absolute_extents
      : TOKABSOLUTEEXTENTS TOKEQUAL TOKVALUE TOKVALUE TOKVALUE
      {
      if( $3 < 0.0 || $4 < 0.0 || $5 < 0.0 )
        {
        yyerror(&yylloc, scanner, pd, "Error: absolute extents cannot be less than zero");
        YYABORT;
        }
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      vector_command(c(pd), "absolute extents", c(pd).d);
      }

relative_extents
      : TOKRELATIVEEXTENTS TOKEQUAL TOKVALUE TOKVALUE TOKVALUE
      {
      if( $3 < 0.0 || $4 < 0.0 || $5 < 0.0 )
        {
        yyerror(&yylloc, scanner, pd, "Error: relative extents cannot be less than zero");
        YYABORT;
        }
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      vector_command(c(pd), "relative extents", c(pd).d);
      }

rotations
      : TOKROTATIONS TOKEQUAL TOKVALUE TOKVALUE TOKVALUE
      {
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      vector_command(c(pd), "rotations", c(pd).d);
      }

keep_inside_box
      : TOKKEEPINSIDEBOX TOKEQUAL boolean { boolean_command(c(pd), "keep inside box", $3 == 1); }
      ;

center_at_data_point
      : TOKCENTERATDATAPOINT TOKEQUAL min_max_center variable_type TOKIDENTIFIER TOKIDENTIFIER
      {
      data_point_command(c(pd), "center at data point", $3, $4, $5, $6);
      }
      ;

/* END BOXCLIP BLOCK */

/* START THRESHOLD BLOCK */

threshold_block
      : start_threshold_block threshold_statements threshold_end
      {
      if(!check_threshold(&yylloc, scanner, c(pd), c(pd).root["operation blocks"][c(pd).cbn]))
        YYABORT;
      c(pd).cb = TOKCATALYST;
      }
      ;

threshold_end
      : TOKEND
      | TOKENDTHRESHOLD
      ;

start_threshold_block
      : TOKBEGIN TOKTHRESHOLD TOKIDENTIFIER
      {
      c(pd).cb = TOKTHRESHOLD;
      c(pd).cbn = $3;
      if(!initialize_block(&yylloc, scanner, c(pd)))
        YYABORT;
      }
      ;

threshold_statements
      :
      | threshold_statements threshold_statement
      ;

threshold_statement
      : input
      | variable_scalar
      | variable_vector_magnitude
      | variable_vector_component
      | variable_tensor_component
      | keep_between
      | keep_above
      | keep_below
      ;

variable_scalar
      : TOKVARIABLESCALAR TOKEQUAL TOKIDENTIFIER 
      { 
      if(!check_variable_name(&yylloc, scanner, c(pd), $3, Iovs::PhactoriParserInterface::SCALAR)) 
        YYABORT;
      identifier_command(c(pd), "variable scalar", $3);
      }
      ;

variable_vector_magnitude
      : TOKVARIABLEVECTORMAGNITUDE TOKEQUAL TOKIDENTIFIER 
      { 
      if(!check_variable_name(&yylloc, scanner, c(pd), $3, Iovs::PhactoriParserInterface::VECTOR)) 
        YYABORT;
      identifier_command(c(pd), "variable vector magnitude", $3);
      }
      ;

variable_vector_component
      : TOKVARIABLEVECTORCOMPONENT TOKEQUAL TOKIDENTIFIER
      {
      if(!check_vector_component(&yylloc, scanner, c(pd), $3))
        YYABORT;
      identifier_command(c(pd), "variable vector component", $3);
      }
      ;

variable_tensor_component
      : TOKVARIABLETENSORCOMPONENT TOKEQUAL TOKIDENTIFIER
      {
      if(!check_tensor_component(&yylloc, scanner, c(pd), $3))
        YYABORT;
      identifier_command(c(pd), "variable tensor component", $3);
      }
      ;

keep_between
      : TOKKEEPBETWEEN TOKEQUAL TOKVALUE TOKVALUE
      {
      if($3 > $4)
        {
        yyerror(&yylloc, scanner, pd, "Error: keep between argument <min> must be less than argument <max>.");
        YYABORT;
        }
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      vector_command(c(pd), "keep between", c(pd).d);
      }
      ;

keep_above
      : TOKKEEPABOVE TOKEQUAL TOKVALUE
      {
      double_command(c(pd), "keep above", $3);
      }
      ;

keep_below
      : TOKKEEPBELOW TOKEQUAL TOKVALUE
      {
      double_command(c(pd), "keep below", $3);
      }
      ;


/* END THRESHOLD BLOCK */

/* START CONTOUR BLOCK */

contour_block
      : start_contour_block contour_statements contour_end
      {
      if(!check_contour(&yylloc, scanner, c(pd), c(pd).root["operation blocks"][c(pd).cbn]))
        YYABORT;
      c(pd).cb = TOKCATALYST;
      }
      ;

contour_end
      : TOKEND
      | TOKENDCONTOUR
      ;

start_contour_block
      : TOKBEGIN TOKCONTOUR TOKIDENTIFIER
      {
      c(pd).cb = TOKCONTOUR;
      c(pd).cbn = $3;
      if(!initialize_block(&yylloc, scanner, c(pd)))
        YYABORT;
      }
      ;

contour_statements
      :
      | contour_statements contour_statement
      ;

contour_statement
      : input
      | variable_scalar
      | variable_vector_magnitude
      | variable_vector_component
      | variable_tensor_component
      | contour_value
      | contour_value_sequence
      ;

contour_value
      : TOKCONTOURVALUE TOKEQUAL { c(pd).d.clear(); } contour_value_list
      {
      vector_command(c(pd), "contour value", c(pd).d);
      }
      ;

contour_value_sequence
      : TOKCONTOURVALUESEQUENCE TOKEQUAL TOKVALUE TOKVALUE TOKVALUE
      {
      if($3 >= $5)
        {
        yyerror(&yylloc, scanner, pd, "Error: contour value sequence has start greater than or equal to stop");
        YYABORT;
        }
      if($4 <= 0)
        {
        yyerror(&yylloc, scanner, pd, "Error: contour value sequence has a negative or zero step");
        YYABORT;
        }
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      vector_command(c(pd), "contour value sequence", c(pd).d);
      }
      ;

/* END CONTOUR BLOCK */

/* START SCATTER PLOT BLOCK */

scatter_plot_block
      : start_scatter_plot_block scatter_plot_statements scatter_plot_end
      {
      if(!check_scatter_plot_block(&yylloc, scanner, c(pd), c(pd).root["scatter plot blocks"][c(pd).cbn]))
        YYABORT;
      c(pd).cb = TOKCATALYST;
      }
      ;

scatter_plot_end
      : TOKEND
      | TOKEND TOKSCATTERPLOT
      ;

start_scatter_plot_block
      : TOKBEGIN TOKSCATTERPLOT TOKIDENTIFIER
      {
      c(pd).cb = TOKSCATTERPLOT;
      c(pd).cbn = $3;
      if(!initialize_block(&yylloc, scanner, c(pd)))
        YYABORT;
      }
      ;

scatter_plot_statements
      :
      | scatter_plot_statements scatter_plot_statement
      ;

scatter_plot_statement
      : operation
      | all_variables
      | x_axis_variable_scalar
      | y_axis_variable_scalar
      | x_axis_variable_vector_magnitude
      | y_axis_variable_vector_magnitude
      | x_axis_variable_vector_component
      | y_axis_variable_vector_component
      | x_axis_variable_tensor_component
      | y_axis_variable_tensor_component
      | x_axis_range
      | y_axis_range
      | x_axis_use_current_data_range
      | y_axis_use_current_data_range
      | x_axis_use_cumulative_data_range
      | y_axis_use_cumulative_data_range
      | x_axis_minimum_range
      | y_axis_minimum_range
      | x_axis_maximum_range
      | y_axis_maximum_range
      | plot_base_name
      | plot_base_directory
      | image_format
      | image_digit_count
      | image_size
      ;

all_variables
      : TOKALLVARIABLES { no_argument_command(c(pd), "all variables"); }
      ;

x_axis_variable_scalar
      : TOKXAXISVARIABLESCALAR TOKEQUAL TOKIDENTIFIER 
      { 
      if(!check_variable_name(&yylloc, scanner, c(pd), $3, Iovs::PhactoriParserInterface::SCALAR)) 
        YYABORT;
      identifier_command(c(pd), "x axis variable scalar", $3);
      }
      ;

y_axis_variable_scalar
      : TOKYAXISVARIABLESCALAR TOKEQUAL TOKIDENTIFIER 
      { 
      if(!check_variable_name(&yylloc, scanner, c(pd), $3, Iovs::PhactoriParserInterface::SCALAR))
        YYABORT;
      identifier_command(c(pd), "y axis variable scalar", $3);
      }
      ;

x_axis_variable_vector_magnitude
      : TOKXAXISVARIABLEVECTORMAGNITUDE TOKEQUAL TOKIDENTIFIER 
      { 
      if(!check_variable_name(&yylloc, scanner, c(pd), $3, Iovs::PhactoriParserInterface::VECTOR))
        YYABORT;
      identifier_command(c(pd), "x axis variable vector magnitude", $3);
      }
      ;

y_axis_variable_vector_magnitude
      : TOKYAXISVARIABLEVECTORMAGNITUDE TOKEQUAL TOKIDENTIFIER 
      { 
      if(!check_variable_name(&yylloc, scanner, c(pd), $3, Iovs::PhactoriParserInterface::VECTOR))
        YYABORT;
      identifier_command(c(pd), "y axis variable vector magnitude", $3);
      }
      ;

x_axis_variable_vector_component
      : TOKXAXISVARIABLEVECTORCOMPONENT TOKEQUAL TOKIDENTIFIER
      {
      if(!check_vector_component(&yylloc, scanner, c(pd), $3))
        YYABORT;
      identifier_command(c(pd), "x axis variable vector component", $3);
      }
      ;

y_axis_variable_vector_component
      : TOKYAXISVARIABLEVECTORCOMPONENT TOKEQUAL TOKIDENTIFIER
      {
      if(!check_vector_component(&yylloc, scanner, c(pd), $3))
        YYABORT;
      identifier_command(c(pd), "y axis variable vector component", $3);
      }
      ;

x_axis_variable_tensor_component
      : TOKXAXISVARIABLETENSORCOMPONENT TOKEQUAL TOKIDENTIFIER
      {
      if(!check_tensor_component(&yylloc, scanner, c(pd), $3))
        YYABORT;
      identifier_command(c(pd), "x axis variable tensor component", $3);
      }
      ;

y_axis_variable_tensor_component
      : TOKYAXISVARIABLETENSORCOMPONENT TOKEQUAL TOKIDENTIFIER
      {
      if(!check_tensor_component(&yylloc, scanner, c(pd), $3))
        YYABORT;
      identifier_command(c(pd), "y axis variable tensor component", $3);
      }
      ;

x_axis_range
      : TOKXAXISRANGE TOKEQUAL TOKVALUE TOKVALUE
      {
      if($3 > $4)
        {
        yyerror(&yylloc, scanner, pd, "Error: x axis range argument <min> must be less than argument <max>.");
        YYABORT;
        }
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      vector_command(c(pd), "x axis range", c(pd).d);
      }
      ;

y_axis_range
      : TOKYAXISRANGE TOKEQUAL TOKVALUE TOKVALUE
      {
      if($3 > $4)
        {
        yyerror(&yylloc, scanner, pd, "Error: y axis range argument <min> must be less than argument <max>.");
        YYABORT;
        }
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      vector_command(c(pd), "y axis range", c(pd).d);
      }
      ;

x_axis_use_current_data_range
      : TOKXAXISUSECURRENTDATARANGE { no_argument_command(c(pd), "x axis use current data range"); }
      ;

y_axis_use_current_data_range
      : TOKYAXISUSECURRENTDATARANGE { no_argument_command(c(pd), "y axis use current data range"); }
      ;

x_axis_use_cumulative_data_range
      : TOKXAXISUSECUMULATIVEDATARANGE { no_argument_command(c(pd), "x axis use cumulative data range"); }
      ;

y_axis_use_cumulative_data_range
      : TOKYAXISUSECUMULATIVEDATARANGE { no_argument_command(c(pd), "y axis use cumulative data range"); }
      ;


x_axis_minimum_range
      : TOKXAXISMINIMUMRANGE TOKEQUAL TOKVALUE TOKVALUE
      {
      if($3 > $4)
        {
        yyerror(&yylloc, scanner, pd, "Error: x axis minimum range argument <min> must be less than argument <max>.");
        YYABORT;
        }
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      vector_command(c(pd), "x axis minimum range", c(pd).d);
      }
      ;

y_axis_minimum_range
      : TOKYAXISMINIMUMRANGE TOKEQUAL TOKVALUE TOKVALUE
      {
      if($3 > $4)
        {
        yyerror(&yylloc, scanner, pd, "Error: y axis minimum range argument <min> must be less than argument <max>.");
        YYABORT;
        }
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      vector_command(c(pd), "y axis minimum range", c(pd).d);
      }
      ;

x_axis_maximum_range
      : TOKXAXISMAXIMUMRANGE TOKEQUAL TOKVALUE TOKVALUE
      {
      if($3 > $4)
        {
        yyerror(&yylloc, scanner, pd, "Error: x axis maximum range argument <min> must be less than argument <max>.");
        YYABORT;
        }
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      vector_command(c(pd), "x axis maximum range", c(pd).d);
      }
      ;

y_axis_maximum_range
      : TOKYAXISMAXIMUMRANGE TOKEQUAL TOKVALUE TOKVALUE
      {
      if($3 > $4)
        {
        yyerror(&yylloc, scanner, pd, "Error: y axis maximum range argument <min> must be less than argument <max>.");
        YYABORT;
        }
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      vector_command(c(pd), "y axis maximum range", c(pd).d);
      }
      ;

plot_base_name
      : TOKPLOTBASENAME TOKEQUAL TOKSTRING
      {
      std::string s($3);
      remove_quotes(s);
      identifier_command(c(pd), "plot basename", s);
      }
      ;

plot_base_directory
      : TOKPLOTBASEDIRECTORY TOKEQUAL TOKSTRING
      {
      std::string s($3);
      remove_quotes(s);
      identifier_command(c(pd), "plot basedirectory", s);
      }
      ;


/* END SCATTER PLOT BLOCK */

/* START PLOT OVER TIME BLOCK */

plot_over_time_block
      : start_plot_over_time_block plot_over_time_statements plot_over_time_end
      {
      if(!check_plot_over_time_block(&yylloc, scanner, c(pd), c(pd).root["plot over time blocks"][c(pd).cbn]))
        YYABORT;
      c(pd).cb = TOKCATALYST;
      }
      ;

plot_over_time_end
      : TOKEND
      | TOKEND TOKPLOTOVERTIME
      ;

start_plot_over_time_block
      : TOKBEGIN TOKPLOTOVERTIME TOKIDENTIFIER
      {
      c(pd).cb = TOKPLOTOVERTIME;
      c(pd).cbn = $3;
      if(!initialize_block(&yylloc, scanner, c(pd)))
        YYABORT;
      }
      ;

plot_over_time_statements
      :
      | plot_over_time_statements plot_over_time_statement
      ;

plot_over_time_statement
      : operation
      | all_variables
      | variable_scalar
      | variable_vector_magnitude
      | variable_vector_component
      | variable_tensor_component
      | y_axis_range
      | y_axis_use_current_data_range
      | y_axis_use_cumulative_data_range
      | y_axis_minimum_range
      | y_axis_maximum_range
      | plot_minimum
      | plot_maximum
      | plot_mean
      | plot_id
      | plot_base_name
      | plot_base_directory
      | image_format
      | image_digit_count
      | image_size
      ;

plot_minimum
      : TOKPLOTMINIMUM TOKEQUAL boolean { boolean_command(c(pd), "plot minimum", $3 == 1); }
      ;

plot_maximum
      : TOKPLOTMAXIMUM TOKEQUAL boolean { boolean_command(c(pd), "plot maximum", $3 == 1); }
      ;

plot_mean
      : TOKPLOTMEAN TOKEQUAL boolean { boolean_command(c(pd), "plot mean", $3 == 1); }
      ;

plot_id
      : TOKPLOTID TOKEQUAL { c(pd).d.clear(); } plot_value_list
      {
      vector_command(c(pd), "plot id", c(pd).d);
      }
      ;

plot_value_list
      : plot_value_member
      | plot_value_list plot_value_member
      ;

plot_value_member
      : TOKVALUE
      {
      if( (std::floor($1) != $1) ||
          ($1 < 1) )
        {
        yyerror(&yylloc, scanner, pd, "Error: plot id <id> must be an integer greater than 0");
        YYABORT;
        }
      c(pd).d.push_back($1);
      }
      ;

/* END PLOT OVER TIME BLOCK */

/* START GROUP BLOCK */

group_block
      : start_group_block group_statement group_end
      {
      string_vector_command(c(pd), "operation group list", c(pd).s);
      c(pd).cb = TOKCATALYST;
      }
      ;

group_end
      : TOKEND
      | TOKEND TOKGROUP
      ;

start_group_block
      : TOKBEGIN TOKGROUP TOKIDENTIFIER
      {
      c(pd).cb = TOKGROUP;
      c(pd).cbn = $3;
      if(!initialize_block(&yylloc, scanner, c(pd)))
        YYABORT;
      }
      ;

group_statement
      : TOKOPERATIONGROUPLIST TOKEQUAL { c(pd).s.clear(); } group_id_list
      ;

group_id_list
      : group_id_member
      | group_id_list group_id_member
      ;

group_id_member
      : TOKIDENTIFIER { c(pd).s.push_back($1); }
      ;

/* END GROUP BLOCK */

/* BEGIN EXTRACT BLOCK */

extract_block
      : start_extract_block exclude_block_statement end_extract_block
      | start_extract_block include_block_statement end_extract_block
      {
      c(pd).cb = TOKCATALYST;
      }
      ;

end_extract_block
      : TOKEND
      | TOKEND TOKEXTRACTBLOCK
      ;

start_extract_block
      : TOKBEGIN TOKEXTRACTBLOCK TOKIDENTIFIER
      {
      c(pd).cb = TOKEXTRACTBLOCK;
      c(pd).cbn = $3;
      if(!initialize_block(&yylloc, scanner, c(pd)))
        YYABORT;
      }
      ;

exclude_block_statement
      : input exclude_block
      | exclude_block input
      | exclude_block
      ;

include_block_statement
      : input include_block
      | include_block input
      | include_block
      ;

exclude_block
      : TOKEXCLUDEBLOCKS TOKEQUAL { c(pd).s.clear(); } group_id_list
      {
      string_vector_command(c(pd), "exclude blocks", c(pd).s);
      }
      ;

include_block
      : TOKINCLUDEBLOCKS TOKEQUAL { c(pd).s.clear(); } group_id_list
      {
      string_vector_command(c(pd), "include blocks", c(pd).s);
      }
      ;

/* END EXTRACT BLOCK */

/* BEGIN TRANSFORM BLOCK */

transform_block
      : start_transform_block transform_block_statements end_transform_block
      {
      c(pd).cb = TOKCATALYST;
      }
      ;

end_transform_block
      : TOKEND
      | TOKEND TOKTRANSFORM
      ;

start_transform_block
      : TOKBEGIN TOKTRANSFORM TOKIDENTIFIER
      {
      c(pd).cb = TOKTRANSFORM;
      c(pd).cbn = $3;
      if(!initialize_block(&yylloc, scanner, c(pd)))
        YYABORT;
      }
      ;

transform_block_statements
      :
      | transform_block_statement transform_block_statements
      ;

transform_block_statement
      : input
      | rotate
      | scale
      | translate
      ;

rotate
      : TOKROTATE TOKEQUAL TOKVALUE TOKVALUE TOKVALUE
      {
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      vector_command(c(pd), "rotate", c(pd).d);
      }
      ;

scale
      : TOKSCALE TOKEQUAL TOKVALUE TOKVALUE TOKVALUE
      {
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      vector_command(c(pd), "scale", c(pd).d);
      }
      ;

translate
      : TOKTRANSLATE TOKEQUAL TOKVALUE TOKVALUE TOKVALUE
      {
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      vector_command(c(pd), "translate", c(pd).d);
      }
      ;

/* END TRANSFORM BLOCK */

/* BEGIN WARP BY VECTOR */

warp_by_vector
      : start_warp_by_vector warp_by_vector_statements end_warp_by_vector
      {
      c(pd).cb = TOKCATALYST;
      }
      ;

end_warp_by_vector
      : TOKEND
      | TOKEND TOKWARPBYVECTOR
      ;

start_warp_by_vector
      : TOKBEGIN TOKWARPBYVECTOR TOKIDENTIFIER
      {
      c(pd).cb = TOKWARPBYVECTOR;
      c(pd).cbn = $3;
      if(!initialize_block(&yylloc, scanner, c(pd)))
        YYABORT;
      }
      ;

warp_by_vector_statements
      :
      | warp_by_vector_statement warp_by_vector_statements
      ;

warp_by_vector_statement
      : input
      | warp_scale
      | warp_variable_vector
      ;

warp_scale
      : TOKSCALE TOKEQUAL TOKVALUE
      {
      double_command(c(pd), "scale", $3);
      }
      ;

warp_variable_vector
      : TOKVARIABLEVECTOR TOKEQUAL TOKIDENTIFIER
      {
      if(!check_variable_name(&yylloc, scanner, c(pd), $3, Iovs::PhactoriParserInterface::VECTOR))
        YYABORT;
      identifier_command(c(pd), "variable vector", $3);
      }
      ;

/* END WARP BY VECTOR */

/* START CALCULATOR */

calculator
      : start_calculator calculator_statements end_calculator
      {
      if(!check_calculator_block(&yylloc, scanner, c(pd), c(pd).root["operation blocks"][c(pd).cbn]))
        YYABORT;
      c(pd).cb = TOKCATALYST;
      }
      ;

end_calculator
      : TOKEND
      | TOKEND TOKCALCULATOR
      ;

start_calculator
      : TOKBEGIN TOKCALCULATOR TOKIDENTIFIER
      {
      c(pd).cb = TOKCALCULATOR;
      c(pd).cbn = $3;
      if(!initialize_block(&yylloc, scanner, c(pd)))
        YYABORT;
      }
      ;

calculator_statements
      :
      | calculator_statement calculator_statements
      ;

calculator_statement
      : input
      | output_variable_name
      | function
      | element_or_node_data
      ;

output_variable_name
      : TOKOUTPUTVARIABLENAME TOKEQUAL TOKIDENTIFIER
      {
      std::string s = $3;
/*      if (s.find("sctlyst") != 0)
        {
        yyerror(&yylloc, scanner, pd, "Error: calculator output variable name must begin with sctlyst");
        YYABORT;
        }*/
      identifier_command(c(pd), "output variable name", $3);
      }
      ;

function
      : TOKFUNCTION TOKEQUAL { c(pd).s.clear(); } TOKSTRING
      {
      std::string s($4);
      remove_quotes(s);
      c(pd).s.push_back(s);
      string_vector_command(c(pd), "function", c(pd).s);
      }
      ;

element_or_node_data_flag
      : TOKELEMENT
      | TOKNODE
      ;

element_or_node_data
      : TOKELEMENTORNODEDATA TOKEQUAL element_or_node_data_flag
      {
      std::string s($3);
      remove_quotes(s);
      identifier_command(c(pd), "element or node data", $3);
      }
      ;

/* END CALCULATOR */

/* START EXPERIMENTAL OPERATION */

experimental_operation
      : start_experimental_operation experimental_operation_statements end_experimental_operation
      {
      c(pd).cb = TOKCATALYST;
      }
      ;

end_experimental_operation
      : TOKEND
      | TOKEND TOKEXPERIMENTALOPERATION
      ;

start_experimental_operation
      : TOKBEGIN TOKEXPERIMENTALOPERATION TOKIDENTIFIER
      {
      c(pd).cb = TOKEXPERIMENTALOPERATION;
      c(pd).cbn = $3;
      if(!initialize_block(&yylloc, scanner, c(pd)))
        YYABORT;
      }
      ;

experimental_operation_statements
      :
      | experimental_operation_statement experimental_operation_statements
      ;

experimental_operation_statement
      : input
      | type
      | scalar_opparam
      | vector_opparam
      | integer_opparam
      | string_opparam
      ;

type
      : TOKTYPE TOKEQUAL TOKIDENTIFIER
      {
      identifier_command(c(pd), "type", $3);
      }
      ;

scalar_opparam
      : TOKSCALAROPPARAM TOKEQUAL TOKIDENTIFIER TOKVALUE
      {
      double_command(c(pd), $3, $4);
      }
      ;

vector_opparam
      : TOKVECTOROPPARAM TOKEQUAL TOKIDENTIFIER TOKVALUE TOKVALUE TOKVALUE
      {
      c(pd).d.clear();
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      c(pd).d.push_back($6);
      vector_command(c(pd), $3, c(pd).d);
      }
      ;

integer_opparam
      : TOKINTEGEROPPARAM TOKEQUAL TOKIDENTIFIER TOKVALUE
      {
      if( std::floor($4) != $4 )
        {
        yyerror(&yylloc, scanner, pd, "Error: integer opparam must be an integer");
        YYABORT;
        }
      std::string s($3);
      remove_quotes(s);
      integer_command(c(pd), $3, (int) $4);
      }
      ;

string_opparam
      : TOKSTRINGOPPARAM TOKEQUAL TOKIDENTIFIER TOKSTRING
      {
      std::string s($4);
      remove_quotes(s);
      identifier_command(c(pd), $3, s);
      }
      ;

/* END EXPERIMENTAL OPERATION */

/* START VISUAL MARKER */

visual_marker_block
      : start_visual_marker_block visual_marker_block_statements end_visual_marker_block
      {
      c(pd).cb = TOKCATALYST;
      }
      ;

end_visual_marker_block
      : TOKEND
      | TOKEND TOKVISUALMARKER
      ;

start_visual_marker_block
      : TOKBEGIN TOKVISUALMARKER TOKIDENTIFIER
      {
      c(pd).cb = TOKVISUALMARKER;
      c(pd).cbn = $3;
      if(!initialize_block(&yylloc, scanner, c(pd)))
        YYABORT;
      }
      ;

visual_marker_block_statements
      :
      | visual_marker_block_statement visual_marker_block_statements
      ;

visual_marker_block_statement
      : input
      | type
      | shape
      | color
      | absolute_size
      | relative_size
      | orientation
      | resolution
      | marker_at_relative_point
      | marker_at_absolute_point
      | marker_at_node
      | marker_at_element
      | marker_at_data_point
      | vector_parameter
      | scalar_parameter
      | integer_parameter
      | string_parameter
      ;

shape
      : TOKSHAPE TOKEQUAL TOKIDENTIFIER
      {
      identifier_command(c(pd), "shape", $3);
      }
      ;

color
      : TOKCOLOR TOKEQUAL TOKVALUE TOKVALUE TOKVALUE
      {
      if($3 < 0 || $3 > 1 ||
         $4 < 0 || $4 > 1 ||
         $5 < 0 || $5 > 1)
        {
        yyerror(&yylloc, scanner, pd, "Error: color <color> arguments must be between 0 and 1, inclusive.");
        YYABORT;
        }
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      vector_command(c(pd), "color", c(pd).d);
      }
      ;

absolute_size
      : TOKABSOLUTESIZE TOKEQUAL TOKVALUE TOKVALUE TOKVALUE
      {
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      vector_command(c(pd), "absolute size", c(pd).d);
      }
      ;

relative_size
      : TOKRELATIVESIZE TOKEQUAL TOKVALUE TOKVALUE TOKVALUE
      {
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      vector_command(c(pd), "relative size", c(pd).d);
      }
      ;

orientation
      : TOKORIENTATION TOKEQUAL TOKVALUE TOKVALUE TOKVALUE
      {
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      vector_command(c(pd), "orientation", c(pd).d);
      }
      ;

resolution
      : TOKRESOLUTION TOKEQUAL TOKVALUE
      {
      if( ( std::floor($3) != $3 ) ||
          ( $3 < 4) )
        {
        yyerror(&yylloc, scanner, pd, "Error: resolution must be an integer greater than or equal to 4");
        YYABORT;
        }
      integer_command(c(pd), "resolution", (int) $3);
      }
      ;

marker_at_relative_point
      : TOKMARKERATRELATIVEPOINT TOKEQUAL TOKVALUE TOKVALUE TOKVALUE
      {
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      vector_command(c(pd), "marker at relative point", c(pd).d);
      }
      ;

marker_at_absolute_point
      : TOKMARKERATABSOLUTEPOINT TOKEQUAL TOKVALUE TOKVALUE TOKVALUE
      {
      c(pd).d.clear();
      c(pd).d.push_back($3);
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      vector_command(c(pd), "marker at absolute point", c(pd).d);
      }
      ;

marker_at_element
      : TOKMARKERATELEMENT TOKEQUAL TOKVALUE
      {
      if( (std::floor($3) != $3) ||
          ($3 < 1) )
        {
        yyerror(&yylloc, scanner, pd, "Error: marker at element <id> must be an integer greater than 0");
        YYABORT;
        }
      check_nodeID(&yylloc, scanner, c(pd), (int) $3);
      integer_command(c(pd), "marker at element", (int) $3);
      }
      ;

marker_at_node
      : TOKMARKERATNODE TOKEQUAL TOKVALUE
      {
      if( (std::floor($3) != $3) ||
          ($3 < 1) )
        {
        yyerror(&yylloc, scanner, pd, "Error: marker at node <id> must be an integer greater than 0");
        YYABORT;
        }
      check_nodeID(&yylloc, scanner, c(pd), (int) $3);
      integer_command(c(pd), "marker at node", (int) $3);
      }
      ;

min_max_center
      : TOKMIN
      | TOKMAX
      | TOKCENTER
      ;

variable_type
      : TOKSCALAR
      | TOKVECTORCOMPONENT
      | TOKVECTORMAGNITUDE
      | TOKTENSORCOMPONENT
      | TOKNONE
      ;

marker_at_data_point
      : TOKMARKERATDATAPOINT TOKEQUAL min_max_center variable_type TOKIDENTIFIER TOKIDENTIFIER
      {
      data_point_command(c(pd), "marker at data point", $3, $4, $5, $6);
      }
      ;

vector_parameter
      : TOKVECTORPARAMETER TOKEQUAL TOKIDENTIFIER TOKVALUE TOKVALUE TOKVALUE
      {
      c(pd).d.clear();
      c(pd).d.push_back($4);
      c(pd).d.push_back($5);
      c(pd).d.push_back($6);
      vector_command(c(pd), $3, c(pd).d);
      }
      ;

scalar_parameter
      : TOKSCALARPARAMETER TOKEQUAL TOKIDENTIFIER TOKVALUE
      {
      double_command(c(pd), $3, $4);
      }
      ;

integer_parameter
      : TOKINTEGERPARAMETER TOKEQUAL TOKIDENTIFIER TOKVALUE
      {
      if( std::floor($4) != $4 )
        {
        yyerror(&yylloc, scanner, pd, "Error: integer parameter must be an integer");
        YYABORT;
        }
      integer_command(c(pd), $3, (int) $4);
      }
      ;

string_parameter
      : TOKSTRINGPARAMETER TOKEQUAL TOKIDENTIFIER TOKSTRING
      {
      std::string s($4);
      remove_quotes(s);
      identifier_command(c(pd), $3, s);
      }
      ;

/* END VISUAL MARKER */

/* START EXPERIMENTAL BLOCK */

experimental_block
      : start_experimental_block experimental_block_statements end_experimental_block
      {
      c(pd).cb = TOKCATALYST;
      }
      ;

end_experimental_block
      : TOKEND
      | TOKEND TOKEXPERIMENTALBLOCK
      ;

start_experimental_block
      : TOKBEGIN TOKEXPERIMENTALBLOCK TOKIDENTIFIER
      {
      c(pd).cb = TOKEXPERIMENTALBLOCK;
      c(pd).cbn = $3;
      if(!initialize_block(&yylloc, scanner, c(pd)))
        YYABORT;
      }
      ;

experimental_block_statements
      :
      | experimental_block_statement experimental_block_statements
      ;

experimental_block_statement
      : input
      | type
      | scalar_parameter
      | vector_parameter
      | integer_parameter
      | string_parameter
      ;

/* END EXPERIMENTAL BLOCK */

/* START ON OFF CRITERIA BLOCK */

on_off_criteria_block
      : start_on_off_criteria_block on_off_criteria_block_statements end_on_off_criteria_block
      {
      c(pd).cb = TOKCATALYST;
      }
      ;

end_on_off_criteria_block
      : TOKEND
      | TOKEND TOKONOFFCRITERIABLOCK
      ;

start_on_off_criteria_block
      : TOKBEGIN TOKONOFFCRITERIABLOCK TOKIDENTIFIER
      {
      c(pd).cb = TOKONOFFCRITERIABLOCK;
      c(pd).cbn = $3;
      if(!initialize_block(&yylloc, scanner, c(pd)))
        YYABORT;
      }
      ;

on_off_criteria_block_statements
      :
      | on_off_criteria_block_statement on_off_criteria_block_statements
      ;

on_off_criteria_block_statement
      : input
      | criteria_type
      | experimental_type
      | threshold_variable_function
      | threshold_values
      | variable_scalar
      | variable_vector_magnitude
      | variable_vector_component
      | variable_tensor_component
      | frames_after_trigger
      | maximum_trigger_per_threshold
      | scalar_parameter
      | vector_parameter
      | integer_parameter
      | string_parameter
      ;

on_off_type
      : TOKTRIGGERTHRESHOLDS
      | TOKEXPERIMENTAL
      ;

criteria_type
      : TOKTYPE TOKEQUAL on_off_type
      {
      identifier_command(c(pd), "type", $3);
      }
      ;

experimental_type
      : TOKEXPERIMENTALTYPE TOKEQUAL TOKIDENTIFIER
      {
      identifier_command(c(pd), "experimental type", $3);
      }
      ;

threshold_variable
      : TOKMINIMUM
      | TOKMAXIMUM
      | TOKMEAN
      | TOKSUM
      | TOKCOUNT
      ;

threshold_variable_function
      : TOKTHRESHOLDVARIABLEFUNCTION TOKEQUAL threshold_variable
      {
      identifier_command(c(pd), "threshold variable function", $3);
      }
      ;

threshold_values
      : TOKTHRESHOLDVALUES TOKEQUAL { c(pd).d.clear(); } threshold_value_list
      {
      vector_command(c(pd), "threshold values", c(pd).d);
      }
      ;

threshold_value_list
      : threshold_value_member
      | threshold_value_list threshold_value_member
      ;

threshold_value_member
      : TOKVALUE
      {
      c(pd).d.push_back($1);
      }
      ;

frames_after_trigger
      : TOKFRAMESAFTERTRIGGER TOKEQUAL TOKVALUE
      {
      if( (std::floor($3) != $3) )
        {
        yyerror(&yylloc, scanner, pd, "Error: frames after trigger <frames> must be an integer");
        YYABORT;
        }
      integer_command(c(pd), "frames after trigger", (int) $3);
      }
      ;

maximum_trigger_per_threshold
      : TOKMAXIMUMTRIGGERSPERTHRESHOLD TOKEQUAL TOKVALUE
      {
      if( (std::floor($3) != $3) )
        {
        yyerror(&yylloc, scanner, pd, "Error: maximum triggers per threshold <triggers> must be an integer");
        YYABORT;
        }
      integer_command(c(pd), "maximum triggers per threshold", (int) $3);
      }
      ;

/* END ON OFF CRITERIA BLOCK */

%%
