text: /* Copyright 2015-2019.  Triad National Security, LLC. This material was produced
   * under U.S. Government contract 89233218CNA000001 for Los Alamos National 
   * Laboratory (LANL), which is operated by Triad National Security, LLC
   * for the U.S. Department of Energy. The U.S. Government has rights to use,
   * reproduce, and distribute this software.  NEITHER THE GOVERNMENT NOR
   * TRIAD NATIONAL SECURITY, LLC MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR
   * ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE.  If software is modified
   * to produce derivative works, such modified software should be clearly marked,
   * so as not to confuse it with the version available from LANL.
   *
   * Licensed under the Apache License, Version 2.0 (the "License"); you may not
   * use this file except in compliance with the License. You may obtain a copy
   * of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software distributed
   * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   * CONDITIONS OF ANY KIND, either express or implied. See the License for the
   * specific language governing permissions and limitations under the License.
   *
   * Under this license, it is required to include a reference to this work. We
   * request that each derivative work contain a reference to LANL Copyright
   * Disclosure C15076/LA-CC-15-054 so that this work's impact can be roughly
   * measured.
   *
   * This is LANL Copyright Disclosure C15076/LA-CC-15-054
   */
  
  /*
   *  PowerParser is a general purpose input file parser for software applications.
   *
   *  Authors: Chuck Wingate   XCP-2   caw@lanl.gov
   *           Robert Robey    XCP-2   brobey@lanl.gov
   */
  
  // ***************************************************************************
  // ***************************************************************************
  // This class holds each word from the line.
  // ***************************************************************************
  // ***************************************************************************
  
  #include <iostream>
  #include <iomanip>
  #include <string>
  #include <sstream>
  #include <vector>
  #include <map>
  #include <deque>
  
  #include <stdio.h>
  #include <stdlib.h>
  #include <assert.h>
  
  #ifdef SGI
  #else
  #include <cctype>
  #endif
  
  #include "Word.hh"
  
  namespace PP
  {
  using std::string;
  using std::cout;
  using std::endl;
  //using std::isdigit;
  using std::stringstream;
  using std::setprecision;
  using std::vector;
  using std::map;
  using std::deque;
  using std::pair;
  
  
  // ===========================================================================
  // Default constructor.
  // ===========================================================================
  Word::Word() 
  {
      wstr = "";
      init();
  }
  
  
  // ===========================================================================
  // Construct given a string.
  // ===========================================================================
  Word::Word(string s)
  {
      wstr = s;
      init();
      set_type();
  }
  
  
  // ===========================================================================
  // Construct given a string. Also set the map of variables.
  // ===========================================================================
  Word::Word(string s, int lnum, int file_lnum, string fname,
             deque<string> *lstr)
  {
      wstr = s;
      init();
      line_number = lnum;
      file_line_number = file_lnum;
      filename = fname;
      lines = lstr;
      set_type();
  }
  
  
  // ===========================================================================
  // Construct given a double
  // ===========================================================================
  Word::Word(double d, int lnum, int file_lnum, string fname,
             deque<string> *lstr)
  {
      stringstream ss;
      ss << setprecision(15) << d;
      wstr = ss.str();
      init();
      type = DOUBLE;
      line_number = lnum;
      file_line_number = file_lnum;
      filename = fname;
      lines = lstr;
  }
  
  
  // ===========================================================================
  // Construct given an integer.
  // ===========================================================================
  Word::Word(int ia, int lnum, int file_lnum, string fname,
             deque<string> *lstr)
  {
      stringstream ss;
      ss << ia;
      wstr = ss.str();
      init();
      type = DOUBLE;
      line_number = lnum;
      file_line_number = file_lnum;
      filename = fname;
      lines = lstr;
  }
  
  
  // ===========================================================================
  /*! = operator. */
  // ===========================================================================
  Word Word::operator=(const Word &ws) 
  {
     if (&ws == this) return *this;
     wstr = ws.wstr;
     processed = ws.processed;
     type = ws.type;
     negate = ws.negate;
     line_number = ws.line_number;
     file_line_number = ws.file_line_number;
     filename = ws.filename;
     lines = ws.lines;
     multiplicity = ws.multiplicity;
     op_level = ws.op_level;
     op_type = ws.op_type;
     return *this;
  }
  
  
  // ===========================================================================
  /*! Copy constructor. */
  // ===========================================================================
  Word::Word(const Word &ws) 
  {
     wstr = ws.wstr;
     processed= ws.processed;
     type = ws.type;
     negate = ws.negate;
     line_number = ws.line_number;
     file_line_number = ws.file_line_number;
     filename = ws.filename;
     lines = ws.lines;
     multiplicity = ws.multiplicity;
     op_level = ws.op_level;
     op_type = ws.op_type;
  }
  
  
  // ===========================================================================
  // Common initialization routine called from constructors.
  // ===========================================================================
  void Word::init()
  {
      processed = false;
      type = WUNKNOWN;
      negate = false;
      lines = NULL;
      line_number = 0;
      file_line_number = 0;
      filename = "";
      multiplicity = 1;
      op_level = -1;
      op_type = "";
  }
  
  
  
  // ===========================================================================
  /*! Destructor */
  // ===========================================================================
  Word::~Word()
  {
  }
  
  
  // ***************************************************************************
  // ***************************************************************************
  // ***************************************************************************
  // Change the value of a word.
  // ***************************************************************************
  // ***************************************************************************
  // ***************************************************************************
  
  // ===========================================================================
  // Set the word to a double.
  // ===========================================================================
  void Word::set_value(double d)
  {
      stringstream ss;
      ss << setprecision(15) << d;
      wstr = ss.str();
      set_type();
  }
  
  
  // ===========================================================================
  // Set the word to a string.
  // ===========================================================================
  void Word::set_value(string s)
  {
      wstr = s;
      set_type();
  }
  
  
  // ===========================================================================
  // Set the word to a string.
  // Use this when you want to do set_value("lasjdf"), otherwise c++ cannot
  // get which set_type to use (and it does not tell you it is having trouble).
  // ===========================================================================
  void Word::set_value(const char *s)
  {
      wstr = s;
      set_type();
  }
  
  
  // ===========================================================================
  // Set the word to a boolean.
  // ===========================================================================
  void Word::set_value(bool b)
  {
      if (!b) wstr = "false";
      if (b)  wstr = "true";
      set_type();
  }
  
  
  
  
  // ***************************************************************************
  // ***************************************************************************
  // ***************************************************************************
  // This section handles the type of the word, whether it is an operator,
  // a function, a string, etc.
  // ***************************************************************************
  // ***************************************************************************
  // ***************************************************************************
  
  
  // ===========================================================================
  /*! Set the type of word. */
  // ===========================================================================
  void Word::set_type()
  {
     // Make sure the type is initialized. If the word is not anything else,
     // then it is a string.
     type = WSTRING;
  
     // Just for convenience.
     int len = (int)wstr.size();
  
     // First determine if the word starts and ends with quotes. If it does,
     // then it is a string. We do not strip off the quote symbols at this
     // point because we might be in a comment region where the quotes donot
     // matter. Later after the comments are stripped out, we check for
     // matching quotes and remove them.
     if ((wstr[0] == '\"') || (wstr[0] == '\'') ||
         (wstr[len-1] == '\"') || (wstr[len-1] == '\'')) {
        type = WSTRING;
        //wstr.erase(wstr.end() - 1);
        //wstr.erase(wstr.begin());
        return;
     }
  
     // Check for a delimiter.
     if (wstr == "(") { type = OPEN_PARENS; return; }
     if (wstr == ")") { type = CLOSED_PARENS; return; }
     if (wstr == "[") { type = OPEN_SQUARE_BRACKET; return; }
     if (wstr == "]") { type = CLOSED_SQUARE_BRACKET; return; }
     if (wstr == "{") { type = OPEN_BRACE; return; }
     if (wstr == "}") { type = CLOSED_BRACE; return; }
  
     // Comma is used for a couple of things.
     if (wstr == ",") { type = COMMA; return; }
  
     // Variables always begin with $. Of course, if the word is in quotes it is
     // not a variable even if it does begin with $.
     if (wstr[0] == '$') { type = VARIABLE; return; }
  
     // Check for an operator.
     if (wstr == "++")    { type=OPERATOR; op_level=7; op_type="arithmetic"; return; }
     if (wstr == "--")    { type=OPERATOR; op_level=7; op_type="arithmetic"; return; }
  
     if (wstr == "**")    { type=OPERATOR; op_level=6; op_type="arithmetic"; return; }
  
     // Do not implement the % operator, it is too much like the fortran %
     // operator which is for referencing components of a fortran structure.
     //if (wstr == "%")     { type=OPERATOR; op_level=5; op_type="arithmetic"; return; }
     if (wstr == "*")     { type=OPERATOR; op_level=5; op_type="arithmetic"; return; }
     if (wstr == "/")     { type=OPERATOR; op_level=5; op_type="arithmetic"; return; }
  
     if (wstr == "+")     { type=OPERATOR; op_level=4; op_type="arithmetic"; return; }
     if (wstr == "-")     { type=OPERATOR; op_level=4; op_type="arithmetic"; return; }
  
     if (wstr == ".gt.")  { type=OPERATOR; op_level=3; op_type="relational"; return; }
     if (wstr == ".ge.")  { type=OPERATOR; op_level=3; op_type="relational"; return; }
     if (wstr == ".lt.")  { type=OPERATOR; op_level=3; op_type="relational"; return; }
     if (wstr == ".le.")  { type=OPERATOR; op_level=3; op_type="relational"; return; }
     if (wstr == ".eq.")  { type=OPERATOR; op_level=3; op_type="relational"; return; }
     if (wstr == ".ne.")  { type=OPERATOR; op_level=3; op_type="relational"; return; }
  
     if (wstr == ".hggt.")  { type=OPERATOR; op_level=3; op_type="relational"; return; }
     if (wstr == ".hgge.")  { type=OPERATOR; op_level=3; op_type="relational"; return; }
     if (wstr == ".hglt.")  { type=OPERATOR; op_level=3; op_type="relational"; return; }
     if (wstr == ".hgle.")  { type=OPERATOR; op_level=3; op_type="relational"; return; }
     if (wstr == ".hgeq.")  { type=OPERATOR; op_level=3; op_type="relational"; return; }
     if (wstr == ".hgne.")  { type=OPERATOR; op_level=3; op_type="relational"; return; }
  
     if (wstr == ".not.") { type=OPERATOR; op_level=2; op_type="logical";    return; }
  
     if (wstr == ".and.") { type=OPERATOR; op_level=1; op_type="logical";    return; }
  
     if (wstr == ".or.")  { type=OPERATOR; op_level=0; op_type="logical";    return; }
  
     // Equals sign.
     if (wstr == "=")  { type = EQUALS; return; }
  
     // At this point the word is either a string or a number.
  
     // If the word begins with a + or - sign, then it is numeric.
     bool start_with_pm = false;
     if (wstr[0] == '+') start_with_pm = true;
     if (wstr[0] == '-') start_with_pm = true;
  
     // If the word does not begin with a + or - sign or a digit, or a
     // ., or an e or a d then it is a string.
     if (!start_with_pm) {
         if(!isdigit(wstr[0])) {
             if (wstr[0] != '.') {
                 if (wstr[0] != 'e') {
                     if (wstr[0] != 'E') {
                         if (wstr[0] != 'd') {
                             if (wstr[0] != 'D') {
                                 type = WSTRING; return;
                             }
                         }
                     }
                 }
             }
         }
     }
  
     // Check for all digits, i.e. an integer.
     bool is_number = true;
     int istart = 0;
     if (start_with_pm) istart = 1;
     for (int i=istart; i<(int)wstr.size(); i++) {
        if (!isdigit(wstr[i])) {
           is_number = false;
           break;
        }
     }
     if (is_number) { type = INTEGER; return; }
  
     // Check for floating point.
     // If there is anything in the string that is not a component of a
     // floating point number, then that makes it a string.
     is_number = true;
     for (int i=0; i<(int)wstr.size(); i++) {
        if (!isdigit(wstr[i]) && wstr[i]!='.' && wstr[i]!='e' && wstr[i]!='E' &&
            wstr[i]!='d' && wstr[i]!='D' && wstr[i]!='+' && wstr[i]!='-') {
           is_number = false;
           break;
        }
     }
     //if (is_number) { type = DOUBLE; return; }
     if (!is_number) { type = WSTRING; return; }
  
     // We suspect a floating point number.
     // At this point, everything in the string is a component of a floating
     // point number, i.e.  "+ - digit e E d D ."
     type = DOUBLE;
  
     // Check that strings that start with "e E d D" really are numbers.
     // And for numbers like e+01, e-05, etc, the atof or strtod functions do not
     // interpret those as numbers, therefore we insert a 1 in front of the e
     // so that atof and strtod will call it a number.
     if (wstr[0] == 'e' || wstr[0] == 'E' || wstr[0] == 'd' || wstr[0] == 'D') {
  
         // Check for proper syntax after the "e E d D".
         if (!check_after_e(wstr, 1, (int)wstr.size()-1)) {
             type = WSTRING;
             return;
         }
  
         // This appears to be a number, insert the digit.
         wstr.insert(0, "1");
         return;
     }
  
     // The string appears to be a floating point number (fpn), check syntax.
     // First, find the location of the "e E d D". Check that there can be
     // only one "e E d D" in the string.
     int ie = -1;
     for (int i=0; i<(int)wstr.size(); i++) {
         if (wstr[i] == 'e' || wstr[i] == 'E' || wstr[i] == 'd' ||
             wstr[i] == 'D') {
             ie = i;
             break;
         }
     }
     if (ie > -1) {
         for (int i=ie+1; i<(int)wstr.size(); i++) {
             if (wstr[i] == 'e' || wstr[i] == 'E' || wstr[i] == 'd' ||
                 wstr[i] == 'D') {
                 type = WSTRING;
                 return;
             }
         }
     }
  
     // Check that the characters before the "e E d D" are valid. If no
     // "e E d D" was found then check the entire string as if it preceeded
     // an "e E d D".
     int ic1 = 0;
     int ic2 = (int)wstr.size() - 1;
     if (ie > -1) {
         ic2 = ie - 1;
     }
     if (!check_before_e(wstr, ic1, ic2)) {
         type = WSTRING;
         return;
     }
  
     // All other cases handled, this must be a fpn (type DOUBLE).
     return;
  }
  
  
  // ===========================================================================
  // The input string, s, could be a floating point number (fpn). It has been
  // determined that s contains an "e E d D" located at position i2+1. Check
  // everything before the "e E d D", postions i1 through i2 inclusive to
  // verify that this is a fpn.
  //
  // It is also possible that an "e E d D" was not found in which case the
  // entire string is checked as if it preceeded an "e E d D".
  //
  // Return false if this is a string
  //        true  if this could be a fpn
  // ===========================================================================
  bool Word::check_before_e(string s, int i1, int i2)
  {
      // If there is nothing before the "e E d D" then this still could be a fpn.
      int size = i2 - i1 + 1;
      if (size < 1) return true;
             
      // The first character could be "+ -", but the remaining characters
      // cannot be "+ -".
      int ie1 = i1;
      if (s[i1] == '+' || s[i1] == '-') ie1 = i1+1;
      for (int i=ie1; i<=i2; i++) {
          if (s[i] == '+' || s[i] == '-') return false;
      }
  
      // Locate the optional "." character. There can only be one ".".
      int pointdex = -1;
      for (int i=ie1; i<=i2; i++) {
          if (s[i] == '.') {
              pointdex = i;
              break;
          }
      }
      if (pointdex > -1) {
          for (int i=pointdex+1; i<=i2; i++) {
              if (s[i] == '.') return false;
          }
      }
  
  
      // Everything before and after the point must be a digit (except
      // that the very first character could be "+ -").
      if (pointdex > -1) {
          for (int i=ie1; i<pointdex; i++) {
              if (!isdigit(s[i])) return false;
          }
          for (int i=pointdex+1; i<=i2; i++) {
              if (!isdigit(s[i])) return false;
          }
      }
  
      // If there is no point then everything must be digits (except
      // that the very first character could be "+ -").
      if (pointdex == -1) {
          for (int i=ie1; i<=i2; i++) {
              if (!isdigit(s[i])) return false;
          }
      }
  
      // All other cases handled, this could be a fpn.
      return true;
  }
      
  
  // ===========================================================================
  // The input string, s, could be a floating point number (fpn). It has been
  // determined that s contains an "e E d D" located at position i1-1. Check
  // everything after the "e E d D", postions i1 through i2 inclusive to
  // verify that this is a fpn.
  //
  // Return false if this is a string
  //        true  if this could be a fpn
  // ===========================================================================
  bool Word::check_after_e(string s, int i1, int i2)
  {
      // If there is nothing after the "e E d D" then that is not a number.
      int size = i2 - i1 + 1;
      if (size < 1) return false;
             
      // The character following the "e E d D" must be "+ - digit"
      if (s[i1] != '+' && s[i1] != '-' && (!isdigit(s[i1])))
          return false;
  
      // Everything after the e or e+ or e- must be a digit.
      int ie1 = i1;
      if (s[i1] == '+' || s[i1] == '-') ie1 = i1+1;
      for (int i=ie1; i<=i2; i++) {
          if (!isdigit(s[i])) return false;
      }
  
      // All other cases handled, this could be a fpn.
      return true;
  }
      
  
  
  // ===========================================================================
  /*! Print the type of word. */
  // ===========================================================================
  void Word::print_type(stringstream &ss)
  {
     if (type == WUNKNOWN) ss << "unknown";
     if (type == WSTRING) ss << "string";
     if (type == INTEGER) ss << "integer";
     if (type == DOUBLE) ss << "double";
     if (type == EQUALS) ss << "=";
     if (type == OPERATOR) ss << "operator";
     if (type == OPEN_PARENS) ss << "(";
     if (type == CLOSED_PARENS) ss << ")";
     if (type == OPEN_SQUARE_BRACKET) ss << "[";
     if (type == CLOSED_SQUARE_BRACKET) ss << "]";
     if (type == OPEN_BRACE) ss << "{";
     if (type == CLOSED_BRACE) ss << "}";
     if (type == COMMA) ss << ",";
     if (type == VARIABLE) ss << "variable";
  }
  
  
  // ===========================================================================
  // Check to see if the word is boolean (true or false)
  // ===========================================================================
  bool Word::is_bool()
  {
      bool retValue = false;
  
      // We don't want to test all possible combinations of spellings for
      // "True" vs "true" vs "tRue" etc.  So we create a temporary string that
      // is all lower case and compare against the temporary.
  
      // Create a copy of the string that can be modified locally.
      string str( wstr );
  
      // Force the string to all lower case.
      string_to_lower( str );
      
      // The comparison is a character-by-character over the length of the
      // first string.  To avoid uninitialized memory reads we convert char
      // array literals to strings.
      if( str == string("true") || str == string(".true.") ) 
          retValue = true;
      if ( str == string("false") || str == string(".false.") )
          retValue = true;
  
     return retValue;
  }
  
  
  // ***************************************************************************
  // ***************************************************************************
  // ***************************************************************************
  // This section provides the methods for returning the word as double,
  // int, bool, etc.
  // ***************************************************************************
  // ***************************************************************************
  // ***************************************************************************
  
  // ===========================================================================
  // Get the word as a boolean
  // ===========================================================================
  bool Word::get_bool(stringstream &serr, int &ierr)
  {
      // Mark this word as having been processed.
      processed = true;
  
      bool retValue = false;
  
      // We don't want to test all possible combinations of spellings for
      // "True" vs "true" vs "tRue" etc.  So we create a temporary string that
      // is all lower case and compare against the temporary.
      string str( wstr );
  
      // Force the string to all lower case.
      string_to_lower( str );
      
      // The comparison is a character-by-character over the length of the
      // first string.  To avoid uninitialized memory reads we convert char
      // array literals to strings.
      if( str == string("true") || str == string(".true.") ) 
          retValue = true;
      else if ( str == string("false") || str == string(".false.") )
          retValue = false;
      else { // We default any other bool value to false but also warn the user. 
          if (lines != NULL) {
              serr << endl;
              serr << "*** FATAL ERROR in line " << file_line_number << ":" << endl;
              serr << "    " << (*lines)[line_number-1] << endl;
              serr << "in file: " << filename << endl;
          }
          serr << "Values on this line should be true or false "
              "(or .true. or .false.)" << endl;
          serr << "    (any case is fine, for example true, True, TrUe "
              "are all ok)" << endl;
          serr << "Instead found value: " << wstr << endl << endl;
          ierr = 2;
          retValue = false;
      }
  
      // Apply the negate flag if it is turned on.
      if (negate) {
          bool b = false;
          if (retValue == false) b = true;
          retValue = b;
      }
  
      return retValue;
  }        
  
  
  // ===========================================================================
  // Get the word as as int, error processing version.
  // ===========================================================================
  int Word::get_int(stringstream &serr, int &ierr)
  {
      // Mark this word as having been processed.
      processed = true;
  
      // The word must at least be a number.
      if ((type != DOUBLE) && (type != INTEGER)) {
          if (lines != NULL) {
              serr << endl;
              serr << "*** FATAL ERROR in line " << file_line_number << ":" << endl;
              serr << "    " << (*lines)[line_number-1] << endl;
              serr << "in file: " << filename << endl;
          }
          serr << "Expected a numerical, integer value." << endl;
          serr << "Instead got: " << wstr << endl << endl;
          ierr = 2;
          return 0;
      }
  
      // The word might begin with a + or - sign.
      bool start_with_pm = false;
      if (wstr[0] == '+') start_with_pm = true;
      if (wstr[0] == '-') start_with_pm = true;
  
      // We allow 2. or 2.0 for example as an integer, but not 2.3.
      bool dot_found = false;
      bool is_int = true;
      int istart = 0;
      if (start_with_pm) istart = 1;
      for (int i=istart; i<(int)wstr.size(); i++) {
          if (!isdigit(wstr[i]) && wstr[i]!='.' ) {
              is_int = false;
              break;
          }
          if (wstr[i]=='.' ) {
              dot_found = true;
              continue;
          }
          if (dot_found && wstr[i]!='0' ) {
              is_int = false;
              break;
          }
      }
      if (!is_int) {
          if (lines != NULL) {
              serr << endl;
              serr << "*** FATAL ERROR in line " << file_line_number << ":" << endl;
              serr << "    " << (*lines)[line_number-1] << endl;
              serr << "in file: " << filename << endl;
          }
          serr << "Expected an integer." << endl;
          serr << "For example, 2 or 3, even 2. or 2.0 is ok." << endl;
          serr << "Instead got: " << wstr << endl << endl;
          ierr = 2;
          return 0;
      }
  
      
      // Apply the negate flag if it is turned on.
      int iret = atoi(wstr.c_str() ); 
      if (negate) iret *= -1;
      return iret;
  }
  
  int64_t Word::get_int64_t(stringstream &serr, int &ierr)
  {
      // Mark this word as having been processed.
      processed = true;
  
      // The word must at least be a number.
      if ((type != DOUBLE) && (type != INTEGER)) {
          if (lines != NULL) {
              serr << endl;
              serr << "*** FATAL ERROR in line " << file_line_number << ":" << endl;
              serr << "    " << (*lines)[line_number-1] << endl;
              serr << "in file: " << filename << endl;
          }
          serr << "Expected a numerical, integer value." << endl;
          serr << "Instead got: " << wstr << endl << endl;
          ierr = 2;
          return 0;
      }
  
      // The word might begin with a + or - sign.
      bool start_with_pm = false;
      if (wstr[0] == '+') start_with_pm = true;
      if (wstr[0] == '-') start_with_pm = true;
  
      // We allow 2. or 2.0 for example as an integer, but not 2.3.
      bool dot_found = false;
      bool is_int = true;
      int istart = 0;
      if (start_with_pm) istart = 1;
      for (int i=istart; i<(int)wstr.size(); i++) {
          if (!isdigit(wstr[i]) && wstr[i]!='.' ) {
              is_int = false;
              break;
          }
          if (wstr[i]=='.' ) {
              dot_found = true;
              continue;
          }
          if (dot_found && wstr[i]!='0' ) {
              is_int = false;
              break;
          }
      }
      if (!is_int) {
          if (lines != NULL) {
              serr << endl;
              serr << "*** FATAL ERROR in line " << file_line_number << ":" << endl;
              serr << "    " << (*lines)[line_number-1] << endl;
              serr << "in file: " << filename << endl;
          }
          serr << "Expected an integer." << endl;
          serr << "For example, 2 or 3, even 2. or 2.0 is ok." << endl;
          serr << "Instead got: " << wstr << endl << endl;
          ierr = 2;
          return 0;
      }
  
      std::stringstream sstr(wstr);
      int64_t iret;
      sstr >> iret;
      // Apply the negate flag if it is turned on.
      if (negate) iret *= -1;
      return iret;
  }        
  
  // ===========================================================================
  // Get the word as a double, error processing version.
  // ===========================================================================
  double Word::get_double(stringstream &serr, int &ierr)
  {
      // Mark this word as having been processed.
      processed = true;
  
      // The word must at least be a number.
      if ((type != DOUBLE) && (type != INTEGER)) {
          if (lines != NULL) {
              serr << endl;
              serr << "*** FATAL ERROR in line " << file_line_number << ":" << endl;
              serr << "    " << (*lines)[line_number-1] << endl;
              serr << "in file: " << filename << endl;
          }
          serr << "Expected a numerical value." << endl;
          serr << "Instead got: " << wstr << endl << endl;
          ierr = 2;
          return 0;
      }
  
      // Use a temporary string that might be modified.
      string s = wstr;
  
      // We allow exponents using d and D in addition to e and E, for example
      // 1.d14 or -1.38D-18. The problem with this is that atof and strtod
      // do not allow d or D, therefore we have to replace d or D with e
      // before sending it to atof or strtod.
      if (type == DOUBLE) {
          for (int i=0; i<(int)s.size(); i++) {
              if (s[i] == 'd') s[i] = 'e';
              if (s[i] == 'D') s[i] = 'e';
          }
      }
  
      // Convert the string to a double.
      double d = atof(s.c_str()); 
  
      // Apply the negate flag if it is turned on.
      if (negate) d *= -1.0;
      return d;
  }        
  
  
  // ===========================================================================
  // Get the word as a single character, error processing version.
  // ===========================================================================
  char Word::get_single_char(stringstream &serr, int &ierr)
  {
      // To suppress compiler warnings of unused parameters
      //assert(serr == serr);
      assert(ierr == ierr);
  
      // Mark this word as having been processed.
      processed = true;
  
      return wstr[0];
  }
  
  
  // ===========================================================================
  // Get the word as an int, no error processing version.
  // ===========================================================================
  int Word::get_int()
  {
     int dummy;
     return get_val( dummy );
  }
  
  // support uint64_t
  int64_t Word::get_int64_t()
  {
     int64_t dummy;
     return get_val( dummy );
  }
  
  // ===========================================================================
  // Get the word as a float
  // ===========================================================================
  float Word::get_float()
  {
     float dummy;
     return get_val( dummy );
  }        
  
  // ===========================================================================
  // Get the word as a double
  // ===========================================================================
  double Word::get_double()
  {
     double dummy;
     return get_val( dummy );
  }        
  
  
  // ===========================================================================
  // Get the word as a Type T. 
  // ===========================================================================
  template< class T >
  T Word::get_val( T &dummyValue )
  {
     // To suppress compiler warnings of unused parameters
     assert(dummyValue == dummyValue);
  
     T retValue;
  
     // Mark this word as having been processed.
     processed = true;
  
     // Convert the word to the requested data type.
     retValue = convertFromString( retValue, wstr );
  
     return retValue;
  }        
  
  //! Explicit instantiation of supported template types.  If more types are
  //! needed those explicit versions must be listed here.  We are not using
  //! automatic inclusion (we would need to move the function definition into
  //! the header file for that).  The listed versions below are the only ones
  //! that will be included in the library.
  template int     Word::get_val( int&     );
  template int64_t Word::get_val( int64_t& );
  template float   Word::get_val( float&   );
  template double  Word::get_val( double&  );
  //template bool  Word::get_val( bool&    );
  template string  Word::get_val( string&  );
  
  // ===========================================================================
  // Convert from string to return type explicitly.
  // ===========================================================================
  
  //! rtti is only used for type identification (each overloaded function must
  //! have a unique signature.  The return value is not part of the signature).
  
  // Convert string to integer.
  int    Word::convertFromString( const int &rtti, const string &s ) const 
  { 
     // To suppress compiler warnings of unused parameters
     assert(rtti == rtti);
  
     int iret = atoi( s.c_str() ); 
     if (negate) iret *= -1;
     return iret;
  }
  
  // Convert string to int64_t.
  int64_t Word::convertFromString( const int64_t &rtti, const string &s ) const 
  {
     // To suppress compiler warnings of unused parameters
     assert(rtti == rtti);
  
     int64_t iret;
     std::stringstream( s ) >> iret;
     if (negate) iret *= -1;
     return iret;
  }
  
  // Convert string to string (do nothing).
  string Word::convertFromString( const string &rtti, const string &s ) const 
  { 
     // To suppress compiler warnings of unused parameters
     assert(rtti == rtti);
  
      return s; 
  }
  
  // Convert string to float.
  float Word::convertFromString( const float &rtti, const string &s ) const 
  { 
     // To suppress compiler warnings of unused parameters
     assert(rtti == rtti);
  
      // Use a temporary string that might be modified.
      string sm = s;
  
      // We allow exponents using d and D in addition to e and E, for example
      // 1.d14 or -1.38D-18. The problem with this is that atof and strtod
      // do not allow d or D, therefore we have to replace d or D with e
      // before sending it to atof or strtod.
      for (int i=0; i<(int)sm.size(); i++) {
          if (sm[i] == 'd') sm[i] = 'e';
          if (sm[i] == 'D') sm[i] = 'e';
      }
  
      float f = (float)atof( sm.c_str() );  
      if (negate) f *= -1.;
      return f;
  }
  
  // Convert string to double.
  double Word::convertFromString( const double &rtti, const string &s ) const 
  { 
      // To suppress compiler warnings of unused parameters
      assert(rtti == rtti);
  
      // Use a temporary string that might be modified.
      string sm = s;
  
      // We allow exponents using d and D in addition to e and E, for example
      // 1.d14 or -1.38D-18. The problem with this is that atof and strtod
      // do not allow d or D, therefore we have to replace d or D with e
      // before sending it to atof or strtod.
      for (int i=0; i<(int)sm.size(); i++) {
          if (sm[i] == 'd') sm[i] = 'e';
          if (sm[i] == 'D') sm[i] = 'e';
      }
  
      double d = atof( sm.c_str() );  
      if (negate) d *= -1.;
      return d;
  }
  
  
  // ***************************************************************************
  // ***************************************************************************
  // ***************************************************************************
  // Utility functions.
  // ***************************************************************************
  // ***************************************************************************
  // ***************************************************************************
  
  
  // ===========================================================================
  // Return the word as a string for printing. This is usually just the word
  // but if it has multiplicity, then include that in the return string.
  // ===========================================================================
  string Word::get_print_string(bool enc_quotes)
  {
      bool equotes = true;
      if (!enc_quotes) equotes = false;
      if (wstr == "true") equotes = false;
      if (wstr == "false") equotes = false;
  
  
      string sq = "";
  
      if (equotes) {
          if (type == WSTRING) sq = "\"";
          sq += wstr;
          if (type == WSTRING) sq += "\"";
      }
      else {
          sq = wstr;
      }
  
      if (multiplicity <= 1) return sq;
  
      stringstream ss;
      ss << multiplicity;
      string s = "";
      s = ss.str() + "*" + sq;
      return s;
  }
  
  
  // ===========================================================================
  // If a word starts or ends with quotes, make sure the quotes match, if not
  // generate a fatal error, and then strip off the quotes.
  // ===========================================================================
  void Word::handle_quotes(stringstream &serr, int &ierr)
  {
      // Just for convenience.
      int len = (int)wstr.size();
  
      // Check for matching quotes, generate a fatal error if they do not match.
      bool ferr = false;
      if ((wstr[0] == '\"') && (wstr[len-1] != '\"')) ferr = true;
      if ((wstr[0] == '\'') && (wstr[len-1] != '\'')) ferr = true;
      if ((wstr[len-1] == '\"') && (wstr[0] != '\"')) ferr = true;
      if ((wstr[len-1] == '\'') && (wstr[0] != '\'')) ferr = true;
  
      if (ferr) {
          fatal_error(serr, ierr);
          serr << "Quotes mismatch found." << endl;
          serr << "A starting quotes must have a closing quotes." << endl;
          serr << "Double quotes, \", must be matched with double quotes."
               << endl;
          serr << "Single quotes, \', must be matched with single quotes."
               << endl;
          ierr = 2;
      }
  
      // Determine if the word starts and ends with quotes. If it does,
      // then we strip off the quote symbols.
      if (((wstr[0] == '\"') || (wstr[0] == '\'')) &&
          ((wstr[len-1] == '\"') || (wstr[len-1] == '\''))) {
          wstr.erase(wstr.end() - 1);
          wstr.erase(wstr.begin());
          return;
      }
  }
  
  
  // ===========================================================================
  // Erase a single character from the word, ic is the index of the character
  // to be erased (starting from 0).
  // ===========================================================================
  void Word::erase_char(int ic)
  {
      if (ic >= (int)wstr.size()) return;
      wstr.erase(wstr.begin() + ic);
  }
  
  
  
  // ===========================================================================
  // Fatal error
  // ===========================================================================
  void Word::fatal_error(stringstream &serr, int &ierr)
  {
      // To suppress compiler warnings of unused parameters
      assert(ierr == ierr);
  
      serr << endl;
      serr << "*** FATAL ERROR in line " << file_line_number << ":" << endl;
      serr << "    " << (*lines)[line_number-1] << endl;
      serr << "in file: " << filename << endl;
  }
  
  void Word::warning(stringstream &serr, int &ierr)
  {
      // To suppress compiler warnings of unused parameters
      assert(ierr == ierr);
  
      serr << endl;
      serr << "*** WARNING in line " << file_line_number << ":" << endl;
      serr << "    " << (*lines)[line_number-1] << endl;
      serr << "in file: " << filename << endl;
  }
  
  
  // ===========================================================================
  // Negate a word.
  // ===========================================================================
  void Word::negate_value()
  {
     int len = (int)wstr.size();
  
     if (type == INTEGER || type == DOUBLE) {
        negate = false;
  
        // If the string starts with a - sign, then delete it.
        for (int i=0; i<len; i++) {
           if (wstr[i] == ' ' || wstr[i] == '\t') continue;
           if (wstr[i] == '-') {
              wstr[i] = ' ';
              return;
           }
           break;
        }
  
        // The string did not start with a minus sign so insert one.
        // Using insert causes link problems with SGI CC
        string s = "-" + wstr;
        wstr = s;
        //wstr.insert(0, s);
        return;
     }
  }
  
  
  
  // ===========================================================================
  // Convert the input string to lower case.
  // ===========================================================================
  void Word::string_to_lower( string &s ) const 
  {
     int i, c, d;
     int len = (int)s.size();
     for (i=0; i<len; i++) {
        c = (int)s[i];
        d = c;
        if (isalpha(c)) d = tolower(c);
        s[i] = (char)d;
     }
  }
  
  
  
  } // End of the PP namespace
  
  