cloudy trunk
Loading...
Searching...
No Matches
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>
13namespace
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
26typedef std::map<string,double> symtab;
27STATIC bool ParseExpr(deque<Token> &chTokens, vector<double> &valstack,
28 const symtab &tab);
29
30const 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 */
37const 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
83bool 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
93bool Parser::isComment(void) const
94{
95 return lgInputComment(m_card);
96}
98{
99 return ( m_card[0]=='C' && (m_card[1]==' ' || m_card[1]== '\0')) ||
100 isComment();
101}
102bool Parser::isVar(void) const
103{
104 return ( *m_ptr=='$' );
105}
106std::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
147void 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");
161 fprintf( ioQQQ, " Sorry.\n" );
163}
165{
166 input.readarray(m_card_raw,&m_lgEOF);
168 if (m_lgEOF)
169 return false;
170 else
171 return true;
172}
173
174const char *Parser::StandardEnergyUnit(void) const
175{
176 return ::StandardEnergyUnit(m_card);
177}
178string Parser::StandardFluxUnit(void) const
179{
180 return ::StandardFluxUnit(m_card);
181}
182void 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 */
209long 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 {
222 if( nMatch( elementnames.chElementNameShort[i] ) )
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 */
233NORETURN 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}
269double Parser::getNumberPlain( const char * )
270{
271 return FFmtRead();
272}
273double Parser::getNumberCheck( const char *chDesc )
274{
275 double val = FFmtRead();
276 if( lgEOL() )
277 {
278 NoNumb(chDesc);
279 }
280 return val;
281}
282double Parser::getNumberDefault( const char *, double fdef )
283{
284 double val = FFmtRead();
285 if( lgEOL() )
286 {
287 val = fdef;
288 }
289 return val;
290}
291double 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}
308double Parser::getNumberCheckAlwaysLog( const char *chDesc )
309{
310 double val = getNumberCheck(chDesc);
311 val = pow(10., val);
312 return val;
313}
314double 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}
327double 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}
336double 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
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
446void 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
466STATIC 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
496STATIC 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
524STATIC 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
559STATIC 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
582STATIC 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}
t_called called
Definition called.cpp:5
FILE * ioQQQ
Definition cddefines.cpp:7
#define ASSERT(exp)
Definition cddefines.h:578
#define STATIC
Definition cddefines.h:97
const int LIMELM
Definition cddefines.h:258
#define EXIT_SUCCESS
Definition cddefines.h:138
#define EXIT_FAILURE
Definition cddefines.h:140
#define cdEXIT(FAIL)
Definition cddefines.h:434
float realnum
Definition cddefines.h:103
#define DEBUG_ENTRY(funcname)
Definition cddefines.h:684
bool getline(void)
Definition parser.cpp:164
long int GetElem(void) const
Definition parser.cpp:209
bool m_lgEOL
Definition parser.h:37
double FFmtRead(void)
Definition parser.cpp:353
bool nMatch(const char *chKey) const
Definition parser.h:135
const char * nWord(const char *chKey) const
Definition parser.cpp:30
const char * m_ptr
Definition parser.h:36
double getWaveOpt()
Definition parser.cpp:244
const char * StandardEnergyUnit(void) const
Definition parser.cpp:174
double getNumberCheckAlwaysLog(const char *chDesc)
Definition parser.cpp:308
bool isCommandComment(void) const
Definition parser.cpp:97
long int m_len
Definition parser.h:35
char m_card[INPUT_LINE_LENGTH]
Definition parser.h:33
void getLineID(char *LabelBuf, realnum *wave)
Definition parser.cpp:446
bool isComment(void) const
Definition parser.cpp:93
bool lgEOL(void) const
Definition parser.h:98
string StandardFluxUnit(void) const
Definition parser.cpp:178
double getNumberPlain(const char *chDesc)
Definition parser.cpp:269
double getNumberCheckLogLinNegImplLog(const char *chDesc)
Definition parser.cpp:291
std::map< string, double > m_symtab
Definition parser.h:39
double getNumberCheck(const char *chDesc)
Definition parser.cpp:273
NORETURN void NoNumb(const char *chDesc) const
Definition parser.cpp:233
double getNumberCheckAlwaysLogLim(const char *chDesc, double flim)
Definition parser.cpp:314
std::string getVarName(void)
Definition parser.cpp:106
void echo(void) const
Definition parser.cpp:147
double getWave()
Definition parser.cpp:260
char m_card_raw[INPUT_LINE_LENGTH]
Definition parser.h:34
double getNumberDefault(const char *chDesc, double fdef)
Definition parser.cpp:282
bool m_lgEOF
Definition parser.h:42
string getCommand(long i)
Definition parser.h:215
double getNumberDefaultAlwaysLog(const char *chDesc, double fdef)
Definition parser.cpp:327
const CloudyCommand *const m_Commands
Definition parser.h:38
void doSetVar(void)
Definition parser.cpp:119
void newlineProcess(void)
Definition parser.h:59
NORETURN void CommandError(void) const
Definition parser.cpp:154
bool isVar(void) const
Definition parser.cpp:102
int PrintLine(FILE *fp) const
Definition parser.h:204
void help(FILE *fp) const
Definition parser.cpp:182
char chPoint(void) const
Definition parser.h:83
double getNumberDefaultNegImplLog(const char *chDesc, double fdef)
Definition parser.cpp:336
#define NORETURN
Definition cpu.h:383
t_elementnames elementnames
t_input input
Definition input.cpp:12
bool lgInputComment(const char *chLine)
Definition input.cpp:18
bool isBoundaryChar(char c)
Definition parser.cpp:83
STATIC bool ParseExpr(deque< Token > &chTokens, vector< double > &valstack, const symtab &tab)
Definition parser.cpp:582
std::map< string, double > symtab
Definition parser.cpp:26
const char * nWord(const char *chKey, const char *chCard)
Definition parser.cpp:37
STATIC bool ParseExp(deque< Token > &chTokens, vector< double > &valstack, const symtab &tab)
Definition parser.cpp:524
STATIC bool ParseProduct(deque< Token > &chTokens, vector< double > &valstack, const symtab &tab)
Definition parser.cpp:559
STATIC bool doop(vector< double > &valstack, const string &op)
Definition parser.cpp:496
STATIC bool ParseNumber(deque< Token > &chTokens, vector< double > &valstack, const symtab &tab)
Definition parser.cpp:466