cloudy  trunk
parser.cpp
Go to the documentation of this file.
1 /* This file is part of Cloudy and is copyright (C)1978-2013 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 "energy.h"
8 #include "flux.h"
9 #include "input.h"
10 #include "elementnames.h"
11 
12 #include <deque>
13 namespace
14 {
15  class Token
16  {
17  public:
18  enum symType { symNull, symNumber, symOp, symVar };
19  string s;
20  symType t;
21  explicit Token(enum symType type) : s(""), t(type) {}
22  explicit Token() : s(""), t(symNull) {}
23  };
24 }
25 
26 typedef std::map<string,double> symtab;
27 STATIC bool ParseExpr(deque<Token> &chTokens, vector<double> &valstack,
28  const symtab &tab);
29 
30 const char *Parser::nWord(const char *chKey) const
31 {
32  return ::nWord(chKey, m_card);
33 }
34 
35 /*nWord determine whether match to a keyword occurs on command line,
36  * return value is 0 if no match, and position of match within string if hit */
37 const char *nWord(const char *chKey,
38  const char *chCard)
39 {
40  DEBUG_ENTRY( "nWord()" );
41 
42  // Ignore leading space in chKey -- logic below is designed
43  // to avoid the need to include this in the first place
44  while (isspace(*chKey))
45  {
46  ++chKey;
47  }
48 
49  const long lenkey = strlen(chKey);
50  ASSERT( lenkey > 0 );
51 
52  bool atBoundary = true, inQuote=false;
53  for (const char *ptr = chCard; *ptr; ++ptr)
54  {
55  if (!inQuote)
56  {
57  if (*ptr == '\"')
58  {
59  inQuote = true;
60  }
61  else
62  {
63  if ( atBoundary && strncmp( ptr, chKey, lenkey) == 0 )
64  {
65  return ptr;
66  }
67 
68  atBoundary = isBoundaryChar(*ptr);
69  }
70  }
71  else
72  {
73  if (*ptr == '\"')
74  {
75  inQuote = false;
76  }
77  }
78  }
79 
80  return NULL;
81 }
82 
83 bool isBoundaryChar(char c)
84 {
85  const bool lgAnyWhitespacePrecedesWord = false;
86 
87  if (lgAnyWhitespacePrecedesWord)
88  return isspace(c) ? true : false ;
89  else // Words are strings starting with A-Z, a-z or _
90  return (! isalpha(c) ) && c != '_';
91 }
92 
93 bool Parser::isComment(void) const
94 {
95  return lgInputComment(m_card);
96 }
97 bool Parser::isCommandComment(void) const
98 {
99  return ( m_card[0]=='C' && (m_card[1]==' ' || m_card[1]== '\0')) ||
100  isComment();
101 }
102 bool Parser::isVar(void) const
103 {
104  return ( *m_ptr=='$' );
105 }
106 std::string Parser::getVarName(void)
107 {
108  std::string name("");
109  while (*m_ptr)
110  {
111  char c = *m_ptr;
112  if (!(isalnum(c) || c == '_'))
113  break;
114  name += c;
115  ++m_ptr;
116  }
117  return name;
118 }
120 {
121  DEBUG_ENTRY(" Parser::doSetVar()");
122  char c='\0';
123  ++m_ptr;
124  std::string name = getVarName();
125  while (*m_ptr)
126  {
127  c = *m_ptr;
128  ++m_ptr;
129  if (c == '=')
130  break;
131  }
132  if (! *m_ptr)
133  {
134  fprintf(ioQQQ,"Expected '=' in variable definition\n");
136  }
137  while (*m_ptr)
138  {
139  c = *m_ptr;
140  if (c != ' ')
141  break;
142  ++m_ptr;
143  }
144  m_symtab[name] = FFmtRead();
145 }
146 
147 void Parser::echo(void) const
148 {
149  /* >>chng 04 jan 21, add HIDE option, mostly for print quiet command */
150  if( called.lgTalk && !::nMatch("HIDE",m_card) )
151  fprintf( ioQQQ, "%23c* %-80s*\n", ' ', m_card_raw );
152 }
153 
155 {
156  DEBUG_ENTRY(" Parser::CommandError()");
157  fprintf( ioQQQ, " Unrecognized command. Key=\"%4.4s\". This is routine ParseCommands.\n",
158  m_card );
159  fprintf( ioQQQ, " The line image was\n");
160  PrintLine(ioQQQ);
161  fprintf( ioQQQ, " Sorry.\n" );
163 }
164 bool Parser::getline(void)
165 {
167  newlineProcess();
168  if (m_lgEOF)
169  return false;
170  else
171  return true;
172 }
173 
174 const char *Parser::StandardEnergyUnit(void) const
175 {
177 }
178 string Parser::StandardFluxUnit(void) const
179 {
181 }
182 void Parser::help(FILE *fp) const
183 {
184  DEBUG_ENTRY("Parser::help()");
185  fprintf(fp,"Available commands are:\n\n");
186  long int i=0, l=0, len;
187  while (1)
188  {
189  len = strlen(m_Commands[i].name);
190  if (l+len+2 > 80)
191  {
192  fprintf(fp,"\n");
193  l = 0;
194  }
195  l += len+2;
196  fprintf(fp,"%s",m_Commands[i].name);
197  ++i;
198  if (m_Commands[i].name == NULL)
199  break;
200  fprintf(fp,", ");
201  }
202 
203  fprintf(fp,"\n\nSorry, no further help available yet -- try Hazy.\n\n");
205 }
206 
207 /*GetElem scans line image, finds element. returns atomic number j,
208  * on C scale, -1 if no hit. chCARD_CAPS must be in CAPS to hit element */
209 long int Parser::GetElem(void ) const
210 {
211  int i;
212 
213  DEBUG_ENTRY( "GetElem()" );
214 
215  /* find which element */
216 
217  /* >>>chng 99 apr 17, lower limit to loop had been 1, so search started with helium,
218  * change to 0 so we can pick up hydrogen. needed for parseasserts command */
219  /* find match with element name, start with helium */
220  for( i=0; i<(int)LIMELM; ++i )
221  {
223  {
224  /* return value is in C counting, hydrogen would be 0*/
225  return i;
226  }
227  }
228  /* fall through, did not hit, return -1 as error condition */
229  return (-1 );
230 }
231 
232 /*NoNumb general error handler for no numbers on input line */
233 NORETURN void Parser::NoNumb(const char * chDesc) const
234 {
235  DEBUG_ENTRY( "NoNumb()" );
236 
237  /* general catch-all for no number when there should have been */
238  fprintf( ioQQQ, " There is a problem on the following command line:\n" );
239  fprintf( ioQQQ, " %s\n", m_card_raw );
240  fprintf( ioQQQ, " A value for %s should have been on this line.\n Sorry.\n",chDesc );
242  }
243 
245 {
246  double val = FFmtRead();
247  /* check for optional micron or cm units, else interpret as Angstroms */
248  if( chPoint() == 'M' )
249  {
250  /* microns */
251  val *= 1e4;
252  }
253  else if( chPoint() == 'C' )
254  {
255  /* centimeters */
256  val *= 1e8;
257  }
258  return val;
259 }
261 {
262  double val = getWaveOpt();
263  if( lgEOL() )
264  {
265  NoNumb("wavelength");
266  }
267  return val;
268 }
269 double Parser::getNumberPlain( const char * )
270 {
271  return FFmtRead();
272 }
273 double Parser::getNumberCheck( const char *chDesc )
274 {
275  double val = FFmtRead();
276  if( lgEOL() )
277  {
278  NoNumb(chDesc);
279  }
280  return val;
281 }
282 double Parser::getNumberDefault( const char *, double fdef )
283 {
284  double val = FFmtRead();
285  if( lgEOL() )
286  {
287  val = fdef;
288  }
289  return val;
290 }
291 double Parser::getNumberCheckLogLinNegImplLog( const char *chDesc )
292 {
293  double val = getNumberCheck(chDesc);
294  if( nMatch(" LOG") )
295  {
296  val = pow(10.,val);
297  }
298  else if(! nMatch("LINE") )
299  {
300  /* log, linear not specified, neg so log */
301  if( val <= 0. )
302  {
303  val = pow(10.,val);
304  }
305  }
306  return val;
307 }
308 double Parser::getNumberCheckAlwaysLog( const char *chDesc )
309 {
310  double val = getNumberCheck(chDesc);
311  val = pow(10., val);
312  return val;
313 }
314 double Parser::getNumberCheckAlwaysLogLim( const char *chDesc, double flim )
315 {
316  double val = getNumberCheck(chDesc);
317  if ( val > flim )
318  {
319  fprintf(ioQQQ,"WARNING - the log of %s is too "
320  "large, I shall probably crash. The value was %.2e\n",
321  chDesc, val );
322  fflush(ioQQQ);
323  }
324  val = pow(10., val);
325  return val;
326 }
327 double Parser::getNumberDefaultAlwaysLog( const char *, double fdef )
328 {
329  double val = pow(10.,FFmtRead());
330  if ( lgEOL() )
331  {
332  val = fdef;
333  }
334  return val;
335 }
336 double Parser::getNumberDefaultNegImplLog( const char *, double fdef )
337 {
338  double val = FFmtRead();
339  if ( lgEOL() )
340  {
341  val = fdef;
342  }
343  if (val < 0.0)
344  {
345  val = pow(10.,val);
346  }
347  return val;
348 }
349 
350 /*FFmtRead scan input line for free format number */
351 
352 
353 double Parser::FFmtRead(void)
354 {
355 
356  DEBUG_ENTRY( "Parser::FFmtRead()" );
357 
358  char chr = '\0';
359  // eol_ptr points one beyond last valid char
360  const char * const eol_ptr = m_card+m_len;
361 
362  // Look for start of next expression
363  while( m_ptr < eol_ptr && ( chr = *m_ptr++ ) != '\0' )
364  {
365  if ( chr == '$')
366  break;
367  const char *lptr = m_ptr;
368  char lchr = chr;
369  if( lchr == '-' || lchr == '+' )
370  lchr = *lptr++;
371  if( lchr == '.' )
372  lchr = *lptr;
373  if( isdigit(lchr) )
374  break;
375  }
376 
377  if( m_ptr == eol_ptr || chr == '\0' )
378  {
379  m_lgEOL = true;
380  return 0.;
381  }
382 
383  // Lexer for expression
384  deque<Token> chTokens(0);
385  bool lgCommaFound = false, lgLastComma = false;
386  do
387  {
388  lgCommaFound = lgLastComma;
389  if( chr != ',' )
390  {
391  if (chr == '^' || chr == '*' || chr == '/' )
392  {
393  chTokens.push_back(Token(Token::symOp));
394  chTokens.back().s += chr;
395  }
396  else if (chr == '$')
397  {
398  chTokens.push_back(Token(Token::symVar));
399  chTokens.back().s += getVarName();
400  }
401  else
402  {
403  if (chTokens.size() == 0 || chTokens.back().t != Token::symNumber)
404  chTokens.push_back(Token(Token::symNumber));
405  chTokens.back().s += chr;
406  }
407  }
408  else
409  {
410  /* don't complain about comma if it appears after number,
411  as determined by exiting loop before this sets lgCommaFound */
412  lgLastComma = true;
413 
414  }
415  if( m_ptr == eol_ptr )
416  break;
417  chr = *m_ptr++;
418  }
419  while( isdigit(chr) || chr == '.' || chr == '-' || chr == '+' || chr == ','
420  || chr == 'e' || chr == 'E' || chr == '^' || chr == '*' || chr == '/'
421  || chr == '$' );
422 
423  if( lgCommaFound )
424  {
425  fprintf( ioQQQ, " PROBLEM - a comma was found embedded in a number, this is deprecated.\n" );
426  fprintf(ioQQQ, "== %-80s ==\n",m_card);
427  }
428 
429  // Parse tokens
430  vector<double> valstack;
431  const bool lgParseOK = ParseExpr(chTokens, valstack, m_symtab);
432  if (!lgParseOK || 1 != valstack.size())
433  {
434  fprintf(ioQQQ," PROBLEM - syntax error in number\n");
435  fprintf(ioQQQ, "== %-80s ==\n",m_card);
436  }
437 
438  double value = valstack[0];
439 
440  m_lgEOL = false;
441  m_ptr--; // m_ptr already points 1 beyond where next read should start
442 
443  return value;
444 }
445 
446 void Parser::getLineID(char *LabelBuf, realnum *wave)
447 {
448  /* order on line is label (col 1-4), wavelength */
449  strncpy( LabelBuf, getCommand(4).c_str() , 4 );
450 
451  /* null terminate the string*/
452  LabelBuf[4] = 0;
453 
454  /* now get wavelength */
455  *wave = (realnum)getWaveOpt();
456 
457 }
458 
459 // Simple recursive descent parser for expressions
460 //
461 // for discussion, see e.g. http://www.ddj.com/architect/184406384
462 //
463 // for a possibly more efficient alternative, see
464 // http://eli.thegreenplace.net/2010/01/02/top-down-operator-precedence-parsing/
465 
466 STATIC bool ParseNumber(deque<Token> &chTokens, vector<double> &valstack,
467  const symtab &tab)
468 {
469  DEBUG_ENTRY(" ParseNumber()");
470  if ( chTokens.size() < 1)
471  return false;
472 
473  if (Token::symNumber == chTokens[0].t)
474  {
475  valstack.push_back(atof(chTokens[0].s.c_str()));
476  chTokens.pop_front();
477  return true;
478  }
479  if (Token::symVar == chTokens[0].t)
480  {
481  symtab::const_iterator var = tab.find(chTokens[0].s);
482  if (var == tab.end())
483  {
484  fprintf(ioQQQ,"ERROR: No value found for variable $%s\n",
485  chTokens[0].s.c_str());
487  }
488  valstack.push_back(var->second);
489  chTokens.pop_front();
490  return true;
491  }
492 
493  return false;
494 }
495 
496 STATIC bool doop(vector<double> &valstack, const string &op)
497 {
498  const double v2 = valstack.back();
499  valstack.pop_back();
500  const double v1 = valstack.back();
501  valstack.pop_back();
502  double result;
503  if (op == "^")
504  {
505  result = pow(v1,v2);
506  }
507  else if (op == "*")
508  {
509  result = v1*v2;
510  }
511  else if (op == "/")
512  {
513  result = v1/v2;
514  }
515  else
516  {
517  fprintf(ioQQQ,"Unknown operator '%s'\n",op.c_str());
518  return false;
519  }
520  valstack.push_back(result);
521  return true;
522 }
523 
524 STATIC bool ParseExp(deque<Token> &chTokens, vector<double> &valstack,
525  const symtab& tab)
526 {
527  // Right-associative -- need to buffer into stack
528  vector<string> opstack;
529  if (!ParseNumber(chTokens, valstack, tab))
530  return false;
531 
532  while (1)
533  {
534  if ( chTokens.size() == 0 )
535  break;
536 
537  if ( chTokens.size() < 2 )
538  return false;
539 
540  if ( Token::symOp != chTokens[0].t || "^" != chTokens[0].s )
541  break;
542 
543  opstack.push_back(chTokens[0].s);
544  chTokens.pop_front();
545 
546  if (!ParseNumber(chTokens, valstack, tab))
547  return false;
548  }
549 
550  while (!opstack.empty())
551  {
552  if (!doop(valstack, opstack.back()))
553  return false;
554  opstack.pop_back();
555  }
556  return true;
557 }
558 
559 STATIC bool ParseProduct(deque<Token> &chTokens, vector<double> &valstack,
560  const symtab& tab)
561 {
562  // Left-associative
563  if (!ParseExp(chTokens, valstack, tab))
564  return false;
565 
566  while ( chTokens.size() > 0 &&
567  Token::symOp == chTokens[0].t &&
568  ( "*" == chTokens[0].s || "/" == chTokens[0].s ) )
569  {
570  string op = chTokens[0].s;
571  chTokens.pop_front();
572 
573  if (!ParseExp(chTokens, valstack, tab))
574  return false;
575 
576  if (!doop(valstack, op))
577  return false;
578  }
579  return true;
580 }
581 
582 STATIC bool ParseExpr(deque<Token> &chTokens, vector<double> &valstack,
583  const symtab& tab)
584 {
585  if (ParseProduct(chTokens, valstack,tab))
586  return true;
587  return false;
588 }
Parser::nMatch
bool nMatch(const char *chKey) const
Definition: parser.h:135
Parser::getVarName
std::string getVarName(void)
Definition: parser.cpp:106
Parser::m_len
long int m_len
Definition: parser.h:35
Parser::chPoint
char chPoint(void) const
Definition: parser.h:83
Parser::FFmtRead
double FFmtRead(void)
Definition: parser.cpp:353
Parser::echo
void echo(void) const
Definition: parser.cpp:147
ParseProduct
STATIC bool ParseProduct(deque< Token > &chTokens, vector< double > &valstack, const symtab &tab)
Definition: parser.cpp:559
Parser::m_symtab
std::map< string, double > m_symtab
Definition: parser.h:39
Parser::StandardFluxUnit
string StandardFluxUnit(void) const
Definition: parser.cpp:178
Parser::getNumberDefaultAlwaysLog
double getNumberDefaultAlwaysLog(const char *chDesc, double fdef)
Definition: parser.cpp:327
elementnames.h
Parser::isComment
bool isComment(void) const
Definition: parser.cpp:93
energy.h
ioQQQ
FILE * ioQQQ
Definition: cddefines.cpp:7
elementnames
t_elementnames elementnames
Definition: elementnames.cpp:5
Parser::m_card
char m_card[INPUT_LINE_LENGTH]
Definition: parser.h:33
Parser::CommandError
NORETURN void CommandError(void) const
Definition: parser.cpp:154
Parser::nWord
const char * nWord(const char *chKey) const
Definition: parser.cpp:30
realnum
float realnum
Definition: cddefines.h:103
STATIC
#define STATIC
Definition: cddefines.h:97
Parser::StandardEnergyUnit
const char * StandardEnergyUnit(void) const
Definition: parser.cpp:174
Parser::m_lgEOF
bool m_lgEOF
Definition: parser.h:42
Parser::m_ptr
const char * m_ptr
Definition: parser.h:36
StandardFluxUnit
string StandardFluxUnit(const char *chCard)
Definition: flux.cpp:207
lgInputComment
bool lgInputComment(const char *chLine)
Definition: input.cpp:18
Parser::getNumberCheckLogLinNegImplLog
double getNumberCheckLogLinNegImplLog(const char *chDesc)
Definition: parser.cpp:291
input
t_input input
Definition: input.cpp:12
ASSERT
#define ASSERT(exp)
Definition: cddefines.h:578
EXIT_SUCCESS
#define EXIT_SUCCESS
Definition: cddefines.h:138
Parser::getWave
double getWave()
Definition: parser.cpp:260
ParseExp
STATIC bool ParseExp(deque< Token > &chTokens, vector< double > &valstack, const symtab &tab)
Definition: parser.cpp:524
Parser::doSetVar
void doSetVar(void)
Definition: parser.cpp:119
nWord
const char * nWord(const char *chKey, const char *chCard)
Definition: parser.cpp:37
flux.h
Parser::help
void help(FILE *fp) const
Definition: parser.cpp:182
Parser::m_card_raw
char m_card_raw[INPUT_LINE_LENGTH]
Definition: parser.h:34
EXIT_FAILURE
#define EXIT_FAILURE
Definition: cddefines.h:140
Parser::NoNumb
NORETURN void NoNumb(const char *chDesc) const
Definition: parser.cpp:233
Parser::getline
bool getline(void)
Definition: parser.cpp:164
Parser::m_lgEOL
bool m_lgEOL
Definition: parser.h:37
cddefines.h
NORETURN
#define NORETURN
Definition: cpu.h:383
isBoundaryChar
bool isBoundaryChar(char c)
Definition: parser.cpp:83
t_called::lgTalk
bool lgTalk
Definition: called.h:12
Parser::isCommandComment
bool isCommandComment(void) const
Definition: parser.cpp:97
Parser::getNumberDefaultNegImplLog
double getNumberDefaultNegImplLog(const char *chDesc, double fdef)
Definition: parser.cpp:336
Parser::newlineProcess
void newlineProcess(void)
Definition: parser.h:59
Parser::getNumberPlain
double getNumberPlain(const char *chDesc)
Definition: parser.cpp:269
doop
STATIC bool doop(vector< double > &valstack, const string &op)
Definition: parser.cpp:496
LIMELM
const int LIMELM
Definition: cddefines.h:258
StandardEnergyUnit
const char * StandardEnergyUnit(const char *chCard)
Definition: energy.cpp:47
cdEXIT
#define cdEXIT(FAIL)
Definition: cddefines.h:434
Parser::lgEOL
bool lgEOL(void) const
Definition: parser.h:98
t_elementnames::chElementNameShort
char chElementNameShort[LIMELM][CHARS_ELEMENT_NAME_SHORT]
Definition: elementnames.h:21
Parser::getNumberCheckAlwaysLogLim
double getNumberCheckAlwaysLogLim(const char *chDesc, double flim)
Definition: parser.cpp:314
Parser::getNumberCheck
double getNumberCheck(const char *chDesc)
Definition: parser.cpp:273
Parser::GetElem
long int GetElem(void) const
Definition: parser.cpp:209
parser.h
Parser::getNumberDefault
double getNumberDefault(const char *chDesc, double fdef)
Definition: parser.cpp:282
Parser::getLineID
void getLineID(char *LabelBuf, realnum *wave)
Definition: parser.cpp:446
called
t_called called
Definition: called.cpp:5
ParseNumber
STATIC bool ParseNumber(deque< Token > &chTokens, vector< double > &valstack, const symtab &tab)
Definition: parser.cpp:466
Parser::getNumberCheckAlwaysLog
double getNumberCheckAlwaysLog(const char *chDesc)
Definition: parser.cpp:308
symtab
std::map< string, double > symtab
Definition: parser.cpp:26
called.h
Parser::PrintLine
int PrintLine(FILE *fp) const
Definition: parser.h:204
Parser::isVar
bool isVar(void) const
Definition: parser.cpp:102
input.h
Parser::getCommand
string getCommand(long i)
Definition: parser.h:215
DEBUG_ENTRY
#define DEBUG_ENTRY(funcname)
Definition: cddefines.h:684
Parser::getWaveOpt
double getWaveOpt()
Definition: parser.cpp:244
t_input::readarray
void readarray(char *chCard, bool *lgEOF)
Definition: input.cpp:114
Parser::m_Commands
const CloudyCommand *const m_Commands
Definition: parser.h:38
ParseExpr
STATIC bool ParseExpr(deque< Token > &chTokens, vector< double > &valstack, const symtab &tab)
Definition: parser.cpp:582