cloudy  trunk
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
parser.cpp
Go to the documentation of this file.
1 /* This file is part of Cloudy and is copyright (C)1978-2022 by Gary J. Ferland and
2  * others. For conditions of distribution and use see copyright notice in license.txt */
3 
4 #include "cddefines.h"
5 #include "parser.h"
6 #include "called.h"
7 #include "flux.h"
8 #include "input.h"
9 #include "elementnames.h"
10 #include "service.h"
11 #include "depth_table.h"
12 #include "lines.h"
13 
14 #include <deque>
15 namespace
16 {
17  class Token
18  {
19  public:
20  enum symType { symNull, symNumber, symOp, symVar };
21  string s;
22  symType t;
23  explicit Token(enum symType type) : s(""), t(type) {}
24  explicit Token() : s(""), t(symNull) {}
25  };
26 }
27 
28 typedef std::map<string,double> symtab;
29 STATIC bool ParseExpr(deque<Token> &chTokens, vector<double> &valstack,
30  const symtab &tab);
31 
32 const char *Parser::nWord(const char *chKey) const
33 {
34  return ::nWord(chKey, m_card);
35 }
36 
37 bool Parser::at_end() const
38 {
39  return current() == '\0';
40 }
41 char Parser::current_raw() const
42 {
43  return m_card_raw[m_off];
44 }
46 {
47  while (!at_end() && isspace(current()))
48  ++m_off;
49 }
51 {
53  // transform(m_card.begin(),m_card.end(),m_card.begin(),toupper);
54  ::caps(m_card);
55  m_len = strlen(m_card);
56  m_off = 0;
57  m_lgEOL = false;
58 }
59 
60 /*nWord determine whether match to a keyword occurs on command line,
61  * return value is 0 if no match, and position of match within string if hit */
62 const char *nWord(const char *chKey,
63  const char *chCard)
64 {
65  DEBUG_ENTRY( "nWord()" );
66 
67  // Ignore leading space in chKey -- logic below is designed
68  // to avoid the need to include this in the first place
69  while (isspace(*chKey))
70  {
71  ++chKey;
72  }
73 
74  const long lenkey = strlen(chKey);
75  ASSERT( lenkey > 0 );
76 
77  bool atBoundary = true, inQuote=false;
78  for (const char *ptr = chCard; *ptr; ++ptr)
79  {
80  if (!inQuote)
81  {
82  if (*ptr == '\"')
83  {
84  inQuote = true;
85  }
86  else
87  {
88  if ( atBoundary && strncmp( ptr, chKey, lenkey) == 0 )
89  {
90  return ptr;
91  }
92 
93  atBoundary = isBoundaryChar(*ptr);
94  }
95  }
96  else
97  {
98  if (*ptr == '\"')
99  {
100  inQuote = false;
101  }
102  }
103  }
104 
105  return NULL;
106 }
107 
108 /* check if there is something left at the end of the line after skipping whitespace */
110 {
111  skip_whitespace();
112  return at_end();
113 }
114 
115 void Parser::showLocation(FILE *io) const
116 {
117  fprintf( io, " %s\n", m_card_comment );
118  fprintf( io, " " );
119  // make sure tabs are treated correctly....
120  for( long i=0; i < m_off; ++i )
121  {
122  if( m_card_comment[i] == '\t' )
123  fputc( '\t', io );
124  else
125  fputc( ' ', io );
126  }
127  fprintf( io, "^\n" );
128 }
129 
130 bool isBoundaryChar(char c)
131 {
132  const bool lgAnyWhitespacePrecedesWord = false;
133 
134  if (lgAnyWhitespacePrecedesWord)
135  return isspace(c) ? true : false ;
136  else // Words are strings starting with A-Z, a-z or _
137  return (! isalpha(c) ) && c != '_';
138 }
139 
140 bool Parser::isComment(void) const
141 {
143 }
144 bool Parser::isVar(void) const
145 {
146  return ( current()=='$' );
147 }
148 std::string Parser::getVarName(void)
149 {
150  std::string name("");
151  while (!at_end())
152  {
153  char c = current();
154  if (!(isalnum(c) || c == '_'))
155  break;
156  name += c;
157  ++m_off;
158  }
159  return name;
160 }
162 {
163  DEBUG_ENTRY( "Parser::doSetVar()" );
164  char c='\0';
165  ++m_off;
166  std::string name = getVarName();
167  while (!at_end())
168  {
169  c = current();
170  ++m_off;
171  if (c == '=')
172  break;
173  }
174  if (at_end())
175  {
176  fprintf(ioQQQ,"Expected '=' in variable definition\n");
178  }
179  while (!at_end())
180  {
181  c = current();
182  if (c != ' ')
183  break;
184  ++m_off;
185  }
186  m_symtab[name] = FFmtRead();
187 }
188 
189 void Parser::echo(void) const
190 {
191  /* >>chng 04 jan 21, add HIDE option, mostly for print quiet command */
192  if( called.lgTalk && !::nMatch("HIDE",m_card) )
193  fprintf( ioQQQ, "%23c* %-80s*\n", ' ', m_card_comment );
194 }
195 
196 bool Parser::last(void) const
197 {
198  // using m_card_raw here could lead to spurious EOF detection if the line
199  // is a full-line comment, in which case m_card_raw would be an empty line
200  return m_lgEOF || lgInputEOF(m_card_comment);
201 }
202 
204 {
205  Error(
206  " A filename or label must be specified within double quotes, but no quotes were encountered on this command.\n"
207  " Name must be surrounded by exactly two double quotes, like \"name.txt\". \n"
208  );
209 }
210 
213 int Parser::GetQuote( string& chLabel )
214 {
215  DEBUG_ENTRY( "Parser::GetQuote()" );
216 
217  string str_raw( m_card_raw );
218 
219  /* find first quote start of string, string begins and ends with quotes */
220  size_t p0 = str_raw.find( '\"' );
221  /* get pointer to next quote and read label */
222  size_t p1 = ( p0 != string::npos ) ? GetString( str_raw, p0, chLabel ) : string::npos;
223 
224  /* check that pointers are valid */
225  if( p0 == string::npos || p1 == string::npos )
226  {
227  /* this branch, ok if not present, return null string in that case */
228  chLabel = "";
229  /* return value of 1 indicates did not find double quotes */
230  return 1;
231  }
232 
233  /* get pointer to first quote in local copy of line image in calling routine */
234  char *i0 = m_card_raw+p0;
235  char *i1 = m_card_raw+p1;
236  char *iLoc = m_card+p0;
237 
238  // blank out label once finished, to not be picked up later
239  // erase quotes as well, so that we can find second label, by PvH
240  while( i0 < i1 )
241  {
242  *i0++ = ' ';
243  *iLoc++ = ' ';
244  }
245  /* return condition of 0 indicates success */
246  return 0;
247 }
248 
249 NORETURN void Parser::Error(const char* msg) const
250 {
251  DEBUG_ENTRY( "Parser::Error()" );
252  fprintf( ioQQQ, " Parser failure\n");
253  if (msg)
254  fprintf(ioQQQ,"%s",msg);
255  fprintf( ioQQQ, " The line image was\n");
256  PrintLine(ioQQQ);
257  fprintf( ioQQQ, " Sorry.\n" );
259 }
261 {
262  DEBUG_ENTRY( "Parser::CommandError()" );
263  fprintf( ioQQQ, " Unrecognized command. Key=\"%4.4s\". This is routine ParseCommands.\n",
264  m_card );
265  fprintf( ioQQQ, " The line image was\n");
266  PrintLine(ioQQQ);
267  fprintf( ioQQQ, " Sorry.\n" );
269  fprintf( ioQQQ, " This looks like an invalid old-style comment."
270  " Please use scripts/ccc.pl to convert this input script to convert to #.\n" );
272 }
273 bool Parser::getline(void)
274 {
276  newlineProcess();
277  if (m_lgEOF)
278  return false;
279  else
280  {
281  input.lgVisible[input.nRead] = this->nMatch("HIDE") ? false : input.lgVisibilityStatus;
282  return true;
283  }
284 }
285 
286 const char *Parser::StandardEnergyUnit(void) const
287 {
289 }
290 string Parser::StandardFluxUnit(void) const
291 {
293 }
294 void Parser::help(FILE *fp) const
295 {
296  DEBUG_ENTRY( "Parser::help()" );
297  fprintf(fp,"Available commands are:\n\n");
298  long int i=0, l=0, len;
299  while (1)
300  {
301  len = strlen(m_Commands[i].name);
302  if (l+len+2 > 80)
303  {
304  fprintf(fp,"\n");
305  l = 0;
306  }
307  l += len+2;
308  fprintf(fp,"%s",m_Commands[i].name);
309  ++i;
310  if (m_Commands[i].name == NULL)
311  break;
312  fprintf(fp,", ");
313  }
314 
315  fprintf(fp,"\n\nSorry, no further help available yet -- try Hazy.\n\n");
317 }
318 
319 /*GetElem scans line image, finds element. returns atomic number j,
320  * on C scale, -1 if no hit. chCARD_CAPS must be in CAPS to hit element */
321 long int Parser::GetElem(void ) const
322 {
323  int i;
324 
325  DEBUG_ENTRY( "Parser::GetElem()" );
326 
327  /* find which element */
328 
329  /* >>>chng 99 apr 17, lower limit to loop had been 1, so search started with helium,
330  * change to 0 so we can pick up hydrogen. needed for parseasserts command */
331  /* find match with element name, start with helium */
332  for( i=0; i<(int)LIMELM; ++i )
333  {
335  {
336  /* return value is in C counting, hydrogen would be 0*/
337  return i;
338  }
339  }
340  /* fall through, did not hit, return -1 as error condition */
341  return (-1 );
342 }
343 
344 /*NoNumb general error handler for no numbers on input line */
345 NORETURN void Parser::NoNumb(const char * chDesc) const
346 {
347  DEBUG_ENTRY( "Parser::NoNumb()" );
348 
349  /* general catch-all for no number when there should have been */
350  fprintf( ioQQQ, " There is a problem on the following command line:\n" );
351  fprintf( ioQQQ, " %s\n", m_card_raw );
352  fprintf( ioQQQ, " A value for %s should have been on this line.\n Sorry.\n",chDesc );
354  }
355 
357 {
358  double val = FFmtRead();
359  /* check for optional micron or cm units, else interpret as Angstroms */
360  if( current() == 'A' )
361  {
362  /* Angstrom */
363  ++m_off;
364  }
365  else if( current() == 'M' )
366  {
367  /* microns */
368  val *= 1e4;
369  ++m_off;
370  }
371  else if( current() == 'C' )
372  {
373  /* centimeters */
374  val *= 1e8;
375  ++m_off;
376  }
377  return val;
378 }
380 {
381  double val = getWaveOpt();
382  if( lgEOL() )
383  {
384  NoNumb("wavelength");
385  }
386  return val;
387 }
388 double Parser::getNumberPlain( const char * )
389 {
390  return FFmtRead();
391 }
392 double Parser::getNumberCheck( const char *chDesc )
393 {
394  double val = FFmtRead();
395  if( lgEOL() )
396  {
397  NoNumb(chDesc);
398  }
399  return val;
400 }
401 double Parser::getNumberDefault( const char *, double fdef )
402 {
403  double val = FFmtRead();
404  if( lgEOL() )
405  {
406  val = fdef;
407  }
408  return val;
409 }
410 double Parser::getNumberCheckLogLinNegImplLog( const char *chDesc )
411 {
412  double val = getNumberCheck(chDesc);
413  if( nMatch(" LOG") )
414  {
415  val = exp10(val);
416  }
417  else if(! nMatch("LINE") )
418  {
419  /* log, linear not specified, neg so log */
420  if( val <= 0. )
421  {
422  val = exp10(val);
423  }
424  }
425  return val;
426 }
427 double Parser::getNumberCheckAlwaysLog( const char *chDesc )
428 {
429  double val = getNumberCheck(chDesc);
430  val = exp10( val);
431  return val;
432 }
433 double Parser::getNumberCheckAlwaysLogLim( const char *chDesc, double flim )
434 {
435  double val = getNumberCheck(chDesc);
436  if ( val > flim )
437  {
438  fprintf(ioQQQ,"WARNING - the log of %s is too "
439  "large, I shall probably crash. The value was %.2e\n",
440  chDesc, val );
441  fflush(ioQQQ);
442  }
443  val = exp10( val);
444  return val;
445 }
446 double Parser::getNumberDefaultAlwaysLog( const char *, double fdef )
447 {
448  double val = exp10(FFmtRead());
449  if ( lgEOL() )
450  {
451  val = fdef;
452  }
453  return val;
454 }
455 double Parser::getNumberDefaultNegImplLog( const char *, double fdef )
456 {
457  double val = FFmtRead();
458  if ( lgEOL() )
459  {
460  val = fdef;
461  }
462  if (val < 0.0)
463  {
464  val = exp10(val);
465  }
466  return val;
467 }
468 
469 /*FFmtRead scan input line for free format number */
470 
471 
472 double Parser::FFmtRead(void)
473 {
474 
475  DEBUG_ENTRY( "Parser::FFmtRead()" );
476 
477  // Look for start of next expression
478  while( m_off < m_len)
479  {
480  if ( current() == '$' )
481  break;
482  int loff = m_off;
483  char lchr = current();
484  if( lchr == '-' || lchr == '+' )
485  {
486  ++loff;
487  lchr = m_card[loff];
488  }
489  if( lchr == '.' )
490  {
491  ++loff;
492  lchr = m_card[loff];
493  }
494  if( isdigit(lchr) )
495  break;
496  ++m_off;
497  }
498 
499  if( m_off == m_len )
500  {
501  m_lgEOL = true;
502  return 0.;
503  }
504 
505  // Lexer for expression
506  deque<Token> chTokens(0);
507  for(char chr = current();
508  m_off < m_len &&
509  (isdigit(chr) || chr == '.' || chr == '-' || chr == '+'
510  || chr == 'e' || chr == 'E' || chr == '^' || chr == '*' || chr == '/'
511  || chr == '$')
512  ; chr = current())
513  {
514  if (chr == '^' || chr == '*' || chr == '/' )
515  {
516  chTokens.push_back(Token(Token::symOp));
517  chTokens.back().s += chr;
518  ++m_off;
519  }
520  else if (chr == '$')
521  {
522  chTokens.push_back(Token(Token::symVar));
523  ++m_off;
524  chTokens.back().s += getVarName();
525  }
526  else
527  {
528  if (chTokens.size() == 0 || chTokens.back().t != Token::symNumber)
529  chTokens.push_back(Token(Token::symNumber));
530  chTokens.back().s += chr;
531  ++m_off;
532  }
533  }
534 
535  ASSERT (chTokens.size() != 0);
536 
537  // Parse tokens
538  vector<double> valstack;
539  const bool lgParseOK = ParseExpr(chTokens, valstack, m_symtab);
540  if (!lgParseOK || 1 != valstack.size())
541  {
542  fprintf(ioQQQ," PROBLEM - syntax error in number or expression on line\n");
543  fprintf(ioQQQ, "== %-80s ==\n",m_card);
544  m_lgEOL = true;
545  return 0.;
546  }
547 
548  double value = valstack[0];
549 
550  m_lgEOL = false;
551  return value;
552 }
553 
554 string Parser::getFirstChunk(long nchar)
555 {
556  DEBUG_ENTRY( "Parser::getFirstChunk()" );
557  if (m_len < nchar)
558  {
559  fprintf(ioQQQ,
560  "PROBLEM --"
561  " input line too short to provide %ld character label\n"
562  "== %-80s ==\n", nchar,m_card);
564  }
565  m_off = nchar;
566  return string(m_card).substr(0,nchar);
567 }
568 
570 {
571  DEBUG_ENTRY( "Parser::getLineID()" );
572  LineID line;
573  if( m_card_raw[0] == '\"' )
574  {
575  if( GetQuote( line.chLabel ) == 0 )
577  else
578  {
579  fprintf( ioQQQ, "getLineID found invalid quoted string:\n" );
580  showLocation();
582  }
583  }
584  else
585  {
586  /* order on line is label (col 1-4), wavelength */
587  line.chLabel = getFirstChunk(4);
588  }
589 
590  // Normalize common error "H 1 " or "H 1" for "H 1"
591  if ( line.chLabel.size() == 3 || line.chLabel.size() == 4 )
592  {
593  if ( line.chLabel[1] == ' ' &&
594  ( line.chLabel.size() == 3 || line.chLabel[3] == ' ') )
595  {
596  fprintf(ioQQQ,"WARNING: read \"%s\" as spectrum\n",line.chLabel.c_str());
597  if (line.chLabel.size() == 3)
598  line.chLabel += line.chLabel[2];
599  else
600  line.chLabel[3] = line.chLabel[2];
601  line.chLabel[2] = ' ';
602  fprintf(ioQQQ,"Assuming required spectrum is \"%s\"\n",line.chLabel.c_str());
603  }
604  }
605 
606  /* now get wavelength */
607  line.wave = (realnum)getWave();
608  return line;
609 }
610 
611 // Simple recursive descent parser for expressions
612 //
613 // for discussion, see e.g. http://www.ddj.com/architect/184406384
614 //
615 // for a possibly more efficient alternative, see
616 // http://eli.thegreenplace.net/2010/01/02/top-down-operator-precedence-parsing/
617 
618 STATIC bool ParseNumber(deque<Token> &chTokens, vector<double> &valstack,
619  const symtab &tab)
620 {
621  DEBUG_ENTRY( "ParseNumber()" );
622  if ( chTokens.size() < 1)
623  return false;
624 
625  if (Token::symNumber == chTokens[0].t)
626  {
627  valstack.push_back(atof(chTokens[0].s.c_str()));
628  chTokens.pop_front();
629  return true;
630  }
631  if (Token::symVar == chTokens[0].t)
632  {
633  symtab::const_iterator var = tab.find(chTokens[0].s);
634  if (var == tab.end())
635  {
636  fprintf(ioQQQ,"ERROR: No value found for variable $%s\n",
637  chTokens[0].s.c_str());
639  }
640  valstack.push_back(var->second);
641  chTokens.pop_front();
642  return true;
643  }
644 
645  return false;
646 }
647 
648 STATIC bool doop(vector<double> &valstack, const string &op)
649 {
650  DEBUG_ENTRY( "doop()" );
651  const double v2 = valstack.back();
652  valstack.pop_back();
653  const double v1 = valstack.back();
654  valstack.pop_back();
655  double result;
656  if (op == "^")
657  {
658  result = pow(v1,v2);
659  }
660  else if (op == "*")
661  {
662  result = v1*v2;
663  }
664  else if (op == "/")
665  {
666  result = v1/v2;
667  }
668  else
669  {
670  fprintf(ioQQQ,"Unknown operator '%s'\n",op.c_str());
671  return false;
672  }
673  valstack.push_back(result);
674  return true;
675 }
676 
677 STATIC bool ParseExp(deque<Token> &chTokens, vector<double> &valstack,
678  const symtab& tab)
679 {
680  DEBUG_ENTRY( "ParseExp()" );
681  // Right-associative -- need to buffer into stack
682  vector<string> opstack;
683  if (!ParseNumber(chTokens, valstack, tab))
684  return false;
685 
686  while (1)
687  {
688  if ( chTokens.size() == 0 )
689  break;
690 
691  if ( chTokens.size() < 2 )
692  return false;
693 
694  if ( Token::symOp != chTokens[0].t || "^" != chTokens[0].s )
695  break;
696 
697  opstack.push_back(chTokens[0].s);
698  chTokens.pop_front();
699 
700  if (!ParseNumber(chTokens, valstack, tab))
701  return false;
702  }
703 
704  while (!opstack.empty())
705  {
706  if (!doop(valstack, opstack.back()))
707  return false;
708  opstack.pop_back();
709  }
710  return true;
711 }
712 
713 STATIC bool ParseProduct(deque<Token> &chTokens, vector<double> &valstack,
714  const symtab& tab)
715 {
716  DEBUG_ENTRY( "ParseProduct()" );
717  // Left-associative
718  if (!ParseExp(chTokens, valstack, tab))
719  return false;
720 
721  while ( chTokens.size() > 0 &&
722  Token::symOp == chTokens[0].t &&
723  ( "*" == chTokens[0].s || "/" == chTokens[0].s ) )
724  {
725  string op = chTokens[0].s;
726  chTokens.pop_front();
727 
728  if (!ParseExp(chTokens, valstack, tab))
729  return false;
730 
731  if (!doop(valstack, op))
732  return false;
733  }
734  return true;
735 }
736 
737 STATIC bool ParseExpr(deque<Token> &chTokens, vector<double> &valstack,
738  const symtab& tab)
739 {
740  DEBUG_ENTRY( "ParseExpr()" );
741  if (ParseProduct(chTokens, valstack,tab))
742  return true;
743  return false;
744 }
745 
746 bool Parser::hasCommand(const char *s2)
747 {
748  DEBUG_ENTRY( "Parser::hasCommand()" );
749  size_t len = strlen(s2);
750  if (::strncmp(m_card, s2, len) != 0)
751  return false;
752 
753  while (isspace(m_card[len-1]))
754  {
755  --len;
756  }
757 
758  // Fast forward over any tail to command, i.e. ignore non-space
759  // characters after len but before next space
760  m_off = len;
761  while (!at_end() && (isalnum(current()) || current() == '_'))
762  ++m_off;
763  skip_whitespace();
764 
765  return true;
766 }
767 
768 void Parser::getPairs(vector<double>& a, vector<double> & b)
769 {
770  DEBUG_ENTRY( "Parser::getPairs()" );
771  a.resize(0);
772  b.resize(0);
773  for(;;)
774  {
775  getline();
776 
777  if( m_lgEOF )
778  {
779  fprintf( ioQQQ, " Hit EOF while reading element list; use END to end list.\n" );
781  }
782 
783  if( isComment())
784  continue;
785 
786  /* this would be a line starting with END to say end on list */
787  if( hasCommand("END") )
788  {
789  return;
790  }
791  a.push_back(FFmtRead());
792  if( lgEOL() )
793  {
794  fprintf( ioQQQ, " There must be a two numbers on this line, or END.\n" );
795  fprintf( ioQQQ, " Sorry.\n" );
797  }
798  b.push_back(FFmtRead());
799  if( lgEOL() )
800  {
801  fprintf( ioQQQ, " There must be a two numbers on this line, or END.\n" );
802  fprintf( ioQQQ, " Sorry.\n" );
804  }
805  }
806 }
807 
808 
809 inline Symbol maybeNumber(bool numOK, const Symbol &s)
810 {
811  DEBUG_ENTRY( "maybeNumber()" );
812  if (numOK)
813  {
814  return s;
815  }
816  else
817  {
818  fprintf(ioQQQ,"Parser error, incomplete number"
819  " at end of input term, error after '%s'\n",s.value.c_str());
820  return Symbol(Symbol::ERROR,s.value);
821  }
822 }
823 
825 {
826  DEBUG_ENTRY( "Parser::getSymbol()" );
827  // Eat leading space
828  while (!at_end())
829  {
830  char c = current();
831  if (c != ' ' && c != '\t')
832  break;
833  ++m_off;
834  }
835 
836  if ( at_end() || current() == '\n')
837  return Symbol(Symbol::EOSTAT,"");
838 
839  if (isdigit(current()) || current() == '-' || current() == '.')
840  {
841  Symbol s(Symbol::NUMBER,"");
842  bool numOK=false;
843  if (current() == '-')
844  {
845  s.value += current();
846  ++m_off;
847  if (at_end())
848  return maybeNumber(numOK,s);
849  }
850  if (isdigit(current()))
851  {
852  numOK = true;
853  do
854  {
855  s.value += current();
856  ++m_off;
857  }
858  while (!at_end() && isdigit(current()));
859  if (at_end() )
860  return maybeNumber(numOK,s);
861  }
862  if (current() == '.')
863  {
864  s.value += current();
865  ++m_off;
866  }
867  while (!at_end() && isdigit(current()))
868  {
869  numOK = true;
870  s.value += current();
871  ++m_off;
872  }
873  if ( at_end() || current() != 'E' || !numOK)
874  {
875  return maybeNumber(numOK,s);
876  }
877  s.value += current();
878  ++m_off;
879  numOK = false;
880  if (current() == '-' || current() == '+')
881  {
882  s.value += current();
883  ++m_off;
884  if (at_end())
885  return maybeNumber(numOK,s);
886  }
887  while (!at_end() && isdigit(current()))
888  {
889  numOK = true;
890  s.value += current();
891  ++m_off;
892  }
893  return maybeNumber(numOK,s);
894  }
895 
896  if (isalpha(current()))
897  {
898  Symbol s(Symbol::NAME,"");
899  do
900  {
901  s.value += current();
902  ++m_off;
903  }
904  while (!at_end() && (isalnum(current()) || current() == '_'));
905  return s;
906  }
907 
908  if ( current() == '"' )
909  {
910  Symbol s(Symbol::STRING,"");
911  ++m_off;
912  while (!at_end() && current() != '\"')
913  {
914  if (current() == '\\')
915  {
916  ++m_off;
917  if (at_end())
918  {
919  fprintf(ioQQQ,"Parser error, escape character '\\'"
920  " at end of input term\n");
921  return Symbol(Symbol::ERROR,s.value);
922  }
923  }
924  s.value += current_raw();
925  ++m_off;
926  }
927 
928  if (at_end())
929  {
930  fprintf(ioQQQ,"Parser error, unterminated string\n");
931  return Symbol(Symbol::ERROR,s.value);
932  }
933  ++m_off;
934  return s;
935  }
936 
937  if ( current() == ',' || current() == '(' || current() == ')' || current() == '=' )
938  {
939  Symbol s(Symbol::OPERATOR,"");
940  s.value += current();
941  ++m_off;
942  return s;
943  }
944 
945  fprintf(ioQQQ,"Parser error, character not recognized '%c'\n",
946  current());
947  return Symbol(Symbol::ERROR,"");
948 }
949 void Parser::readList(vector<string>& list, const char* chName)
950 {
951  DEBUG_ENTRY( "Parser::readList()" );
952  list.clear();
953  while( 1 )
954  {
955  getline();
956  if( m_lgEOF )
957  {
958  fprintf( ioQQQ,
959  " Save %s hit EOF while reading list; use END to end list.\n" ,chName);
960  fprintf( ioQQQ,
961  " This command requires either a species within quotes or the keyword ALL.\n" );
963  }
964  if( isComment())
965  continue;
966  if (hasCommand("END" ))
967  break;
968  string chTerm;
969  if (!GetQuote(chTerm))
970  list.push_back(chTerm);
971  else
972  list.push_back(m_card_raw);
973  }
974 }
975 
977 {
978  DEBUG_ENTRY( "Parser::readLaw()" );
979  if( nMatch("DEPT") )
980  {
981  table.lgDepth = true;
982  }
983  else
984  {
985  table.lgDepth = false;
986  }
987  if (table.nvals != 0)
988  {
989  fprintf( ioQQQ, " Warning: over-writing existing table\n" );
990  table.clear();
991  }
992 
993  getline();
994  table.dist.push_back(FFmtRead());
995  table.val.push_back(FFmtRead());
996  if( lgEOL() )
997  {
998  fprintf( ioQQQ, " No pairs entered - can\'t interpolate.\n Sorry.\n" );
1000  }
1001 
1002  table.nvals = 2;
1003  bool lgEnd = false;
1004 
1005  /* read pairs of numbers until we find line starting with END */
1006  /* >>chng 04 jan 27, loop to LIMTABDLAW from LIMTABD, as per
1007  * var definitions, caught by Will Henney */
1008  while( !lgEnd )
1009  {
1010  getline();
1011  lgEnd = m_lgEOF;
1012  if( !lgEnd )
1013  {
1014  lgEnd = hasCommand("END");
1015  }
1016 
1017  if( !lgEnd )
1018  {
1019  double dist = FFmtRead();
1020  double val = FFmtRead();
1021  if (lgEOL())
1022  NoNumb("radius, value pair on each line");
1023  table.dist.push_back( dist );
1024  table.val.push_back( val );
1025  table.nvals += 1;
1026  }
1027  }
1028  --table.nvals;
1029 
1030  for( long i=1; i < table.nvals; i++ )
1031  {
1032  /* the radius values are assumed to be strictly increasing */
1033  if( table.dist[i] <= table.dist[i-1] )
1034  {
1035  fprintf( ioQQQ, " Radii must be in increasing order. Sorry.\n" );
1037  }
1038  }
1039 }
bool nMatch(const char *chKey) const
Definition: parser.h:150
long int nvals
Definition: depth_table.h:17
bool hasCommand(const char *s2)
Definition: parser.cpp:746
Symbol maybeNumber(bool numOK, const Symbol &s)
Definition: parser.cpp:809
void echo(void) const
Definition: parser.cpp:189
realnum wave
Definition: lines.h:18
double FFmtRead(void)
Definition: parser.cpp:472
double exp10(double x)
Definition: cddefines.h:1368
#define NORETURN
Definition: cpu.h:455
t_input input
Definition: input.cpp:12
double getNumberCheckLogLinNegImplLog(const char *chDesc)
Definition: parser.cpp:410
bool m_lgEOL
Definition: parser.h:50
STATIC bool ParseProduct(deque< Token > &chTokens, vector< double > &valstack, const symtab &tab)
Definition: parser.cpp:713
void readList(vector< string > &list, const char *chName)
Definition: parser.cpp:949
bool lgReachedEnd()
Definition: parser.cpp:109
void skip_whitespace()
Definition: parser.cpp:45
int GetQuote(string &chLabel)
Definition: parser.cpp:213
long int nRead
Definition: input.h:105
char m_card[INPUT_LINE_LENGTH]
Definition: parser.h:45
bool at_end() const
Definition: parser.cpp:37
void newlineProcess()
Definition: parser.cpp:50
double getNumberDefaultAlwaysLog(const char *chDesc, double fdef)
Definition: parser.cpp:446
STATIC bool ParseExpr(deque< Token > &chTokens, vector< double > &valstack, const symtab &tab)
Definition: parser.cpp:737
bool lgInputEOF(const char *chLine)
Definition: input.cpp:84
long int m_off
Definition: parser.h:49
Definition: lines.h:14
size_t GetString(const string &s, size_t p, string &buf)
Definition: input.cpp:152
FILE * ioQQQ
Definition: cddefines.cpp:7
string chLabel
Definition: lines.h:17
double getWave()
Definition: parser.cpp:379
bool lgTalk
Definition: called.h:12
bool isBoundaryChar(char c)
Definition: parser.cpp:130
Symbol getSymbol()
Definition: parser.cpp:824
STATIC bool doop(vector< double > &valstack, const string &op)
Definition: parser.cpp:648
void trimTrailingWhiteSpace(string &str)
Definition: service.cpp:153
NORETURN void StringError() const
Definition: parser.cpp:203
double getNumberPlain(const char *chDesc)
Definition: parser.cpp:388
char m_card_raw[INPUT_LINE_LENGTH]
Definition: parser.h:46
t_elementnames elementnames
Definition: elementnames.cpp:5
LineID getLineID()
Definition: parser.cpp:569
bool lgDepth
Definition: depth_table.h:11
string StandardFluxUnit(void) const
Definition: parser.cpp:290
#define STATIC
Definition: cddefines.h:118
void showLocation(FILE *io=ioQQQ) const
Definition: parser.cpp:115
bool lgVisible[NKRD]
Definition: input.h:96
float realnum
Definition: cddefines.h:124
const char * StandardEnergyUnit(void) const
Definition: parser.cpp:286
#define EXIT_FAILURE
Definition: cddefines.h:168
char m_card_comment[INPUT_LINE_LENGTH]
Definition: parser.h:47
bool last(void) const
Definition: parser.cpp:196
const int INPUT_LINE_LENGTH
Definition: cddefines.h:301
void clear()
Definition: depth_table.h:24
#define cdEXIT(FAIL)
Definition: cddefines.h:482
NORETURN void NoNumb(const char *chDesc) const
Definition: parser.cpp:345
STATIC bool ParseExp(deque< Token > &chTokens, vector< double > &valstack, const symtab &tab)
Definition: parser.cpp:677
long int GetElem(void) const
Definition: parser.cpp:321
const char * nWord(const char *chKey) const
Definition: parser.cpp:32
string getFirstChunk(long i)
Definition: parser.cpp:554
string StandardFluxUnit(const char *chCard)
Definition: flux.cpp:205
char chElementNameShort[LIMELM][CHARS_ELEMENT_NAME_SHORT]
Definition: elementnames.h:21
bool isVar(void) const
Definition: parser.cpp:144
const char * nWord(const char *chKey, const char *chCard)
Definition: parser.cpp:62
STATIC double dist(long, realnum[], realnum[])
long int m_len
Definition: parser.h:48
vector< double > dist
Definition: depth_table.h:13
double getNumberCheckAlwaysLogLim(const char *chDesc, double flim)
Definition: parser.cpp:433
void help(FILE *fp) const
Definition: parser.cpp:294
bool lgIsExpungedCommentSeq(const char *s)
Definition: input.cpp:59
#define ASSERT(exp)
Definition: cddefines.h:613
bool lgVisibilityStatus
Definition: input.h:99
const char * StandardEnergyUnit(const char *chCard)
Definition: energy.cpp:44
bool getline()
Definition: parser.cpp:273
const int LIMELM
Definition: cddefines.h:308
NORETURN void Error(const char *msg) const
Definition: parser.cpp:249
#define DEBUG_ENTRY(funcname)
Definition: cddefines.h:723
vector< double > val
Definition: depth_table.h:14
STATIC bool ParseNumber(deque< Token > &chTokens, vector< double > &valstack, const symtab &tab)
Definition: parser.cpp:618
double getNumberCheckAlwaysLog(const char *chDesc)
Definition: parser.cpp:427
const CloudyCommand *const m_Commands
Definition: parser.h:51
double getNumberDefault(const char *chDesc, double fdef)
Definition: parser.cpp:401
char current(void) const
Definition: parser.h:74
std::map< string, double > symtab
Definition: parser.cpp:28
bool lgEOL(void) const
Definition: parser.h:113
void readarray(char *chCardStripped, char *chCardComment, bool *lgEOF)
Definition: input.cpp:208
string value
Definition: parser.h:38
int fprintf(const Output &stream, const char *format,...)
Definition: service.cpp:1121
NORETURN void CommandError(void) const
Definition: parser.cpp:260
void getPairs(vector< double > &a, vector< double > &b)
Definition: parser.cpp:768
bool lgInputComment(const char *chLine)
Definition: input.cpp:72
char current_raw() const
Definition: parser.cpp:41
int PrintLine(FILE *fp) const
Definition: parser.h:206
std::string getVarName(void)
Definition: parser.cpp:148
void caps(char *chCard)
Definition: service.cpp:295
bool m_lgEOF
Definition: parser.h:55
Definition: parser.h:34
void readLaw(DepthTable &table)
Definition: parser.cpp:976
void doSetVar(void)
Definition: parser.cpp:161
double getNumberDefaultNegImplLog(const char *chDesc, double fdef)
Definition: parser.cpp:455
double getWaveOpt()
Definition: parser.cpp:356
std::map< string, double > m_symtab
Definition: parser.h:52
t_called called
Definition: called.cpp:4
bool isComment(void) const
Definition: parser.cpp:140
#define EXIT_SUCCESS
Definition: cddefines.h:166
double getNumberCheck(const char *chDesc)
Definition: parser.cpp:392