ifeqEntry.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | www.openfoam.com
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8  Copyright (C) 2018 OpenFOAM Foundation
9  Copyright (C) 2019-2023 OpenCFD Ltd.
10 -------------------------------------------------------------------------------
11 License
12  This file is part of OpenFOAM.
13 
14  OpenFOAM is free software: you can redistribute it and/or modify it
15  under the terms of the GNU General Public License as published by
16  the Free Software Foundation, either version 3 of the License, or
17  (at your option) any later version.
18 
19  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22  for more details.
23 
24  You should have received a copy of the GNU General Public License
25  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26 
27 \*---------------------------------------------------------------------------*/
28 
29 #include "ifeqEntry.H"
30 #include "ifEntry.H"
31 #include "stringOps.H"
33 
34 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
35 
36 namespace Foam
37 {
38 namespace functionEntries
39 {
40  defineTypeNameAndDebug(ifeqEntry, 0);
41 
43  (
44  functionEntry,
45  ifeqEntry,
46  execute,
47  dictionaryIstream,
48  ifeq
49  );
50 } // End namespace functionEntries
51 } // End namespace Foam
52 
53 
54 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
55 
56 void Foam::functionEntries::ifeqEntry::readToken(token& t, Istream& is)
57 {
58  // Skip dummy tokens - avoids entry::getKeyword consuming #else, #endif
59  do
60  {
61  if (is.read(t).bad() || is.eof() || !t.good())
62  {
63  return;
64  }
65  }
66  while (t == token::END_STATEMENT);
67 }
68 
69 
70 Foam::token Foam::functionEntries::ifeqEntry::expandToken
71 (
72  const dictionary& dict,
73  const string& keyword,
74  const token& t
75 )
76 {
77  if (keyword[0] == '$')
78  {
79  const word varName(keyword.substr(1));
80 
81  // Lookup the variable name in the given dictionary
82  const entry* ePtr = dict.findScoped(varName, keyType::REGEX_RECURSIVE);
83  if (ePtr)
84  {
85  return token(ePtr->stream());
86  }
87  else
88  {
89  // String expansion. Allow unset variables
90  string expanded(keyword);
91  stringOps::inplaceExpand(expanded, dict, true, true);
92 
93  // Re-form as a string token so we can compare to string
94  return token(expanded, t.lineNumber());
95  }
96  }
97  else if (!t.isString())
98  {
99  // Re-form as a string token so we can compare to string
100  return token(keyword, t.lineNumber());
101  }
102 
103  return t;
104 }
105 
106 
107 Foam::token Foam::functionEntries::ifeqEntry::expandToken
108 (
109  const dictionary& dict,
110  const token& t
111 )
112 {
113  if (t.isWord())
114  {
115  return expandToken(dict, t.wordToken(), t);
116  }
117  else if (t.isVariable())
118  {
119  return expandToken(dict, t.stringToken(), t);
120  }
121  else if (t.isString())
122  {
123  return expandToken(dict, t.stringToken(), t);
124  }
125 
126  return t;
127 }
128 
129 
130 bool Foam::functionEntries::ifeqEntry::equalToken
131 (
132  const token& t1,
133  const token& t2
134 )
135 {
136  const bool eqType = (t1.type() == t2.type());
137 
138  switch (t1.type())
139  {
140  case token::UNDEFINED:
141  return eqType;
142 
143  case token::BOOL:
144  return (eqType && t1.boolToken() == t2.boolToken());
145 
146  case token::FLAG:
147  return (eqType && t1.flagToken() == t2.flagToken());
148 
149  case token::PUNCTUATION:
150  return (eqType && t1.pToken() == t2.pToken());
151 
152  case token::WORD:
153  case token::DIRECTIVE:
154  if (t2.isWord())
155  {
156  return t1.wordToken() == t2.wordToken();
157  }
158  else if (t2.isString())
159  {
160  const wordRe w2(t2.stringToken(), wordRe::DETECT);
161  return w2.match(t1.wordToken());
162  }
163  return false;
164 
165  case token::STRING:
166  if (eqType)
167  {
168  const wordRe w1(t1.stringToken(), wordRe::DETECT);
169  const wordRe w2(t2.stringToken(), wordRe::DETECT);
170  return w1.match(w2) || w2.match(w1);
171  }
172  else if (t2.isWord())
173  {
174  const wordRe w1(t1.stringToken(), wordRe::DETECT);
175  return w1.match(t2.wordToken());
176  }
177  return false;
178 
179  case token::VARIABLE:
180  case token::VERBATIM:
181  case token::CHAR_DATA:
182  if (t2.isStringType())
183  {
184  return t1.stringToken() == t2.stringToken();
185  }
186  return false;
187 
188  case token::LABEL:
189  if (eqType)
190  {
191  return t1.labelToken() == t2.labelToken();
192  }
193  else if (t2.isScalar())
194  {
195  return t1.labelToken() == t2.scalarToken();
196  }
197  return false;
198 
199  case token::FLOAT:
200  if (eqType)
201  {
202  return equal(t1.floatToken(), t2.floatToken());
203  }
204  else if (t2.isLabel())
205  {
206  return t1.floatToken() == t2.labelToken();
207  }
208  else if (t2.isScalar())
209  {
210  return t1.scalarToken() == t2.scalarToken();
211  }
212  return false;
213 
214  case token::DOUBLE:
215  if (eqType)
216  {
217  return equal(t1.doubleToken(), t2.doubleToken());
218  }
219  else if (t2.isLabel())
220  {
221  return t1.doubleToken() == t2.labelToken();
222  }
223  else if (t2.isScalar())
224  {
225  return t1.scalarToken() == t2.scalarToken();
226  }
227  return false;
228 
229  case token::EXPRESSION:
230  return false;
231 
232  case token::COMPOUND:
233  return false;
234 
235  case token::ERROR:
236  return eqType;
237  }
238 
239  return false;
240 }
241 
242 
243 void Foam::functionEntries::ifeqEntry::skipUntil
244 (
245  DynamicList<filePos>& stack,
246  const dictionary& parentDict,
247  const word& endDirective,
248  Istream& is
249 )
250 {
251  while (!is.eof())
252  {
253  token t;
254  readToken(t, is);
255 
256  if (!t.isDirective())
257  {
258  continue;
259  }
260  else if (t.wordToken() == "#if" || t.wordToken() == "#ifeq")
261  {
262  stack.push_back(filePos(is.name(), is.lineNumber()));
263  skipUntil(stack, parentDict, "#endif", is);
264  stack.pop_back();
265  }
266  else if (t.wordToken() == endDirective)
267  {
268  return;
269  }
270  }
271 
272  FatalIOErrorInFunction(parentDict)
273  << "Did not find matching " << endDirective << nl
275 }
276 
277 
278 // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
279 
281 (
282  const bool doIf,
283  DynamicList<filePos>& stack,
284  dictionary& parentDict,
285  Istream& is
286 )
287 {
288  while (!is.eof())
289  {
290  token t;
291  readToken(t, is);
292  bool pending = false;
293 
294  if (t.isDirective())
295  {
296  if (t.wordToken() == "#ifeq")
297  {
298  // Recurse to evaluate
299  execute(stack, parentDict, is);
300  }
301  else if (t.wordToken() == "#if")
302  {
303  // Recurse to evaluate
304  ifEntry::execute(stack, parentDict, is);
305  }
306  else if
307  (
308  doIf
309  && (t.wordToken() == "#else" || t.wordToken() == "#elif")
310  )
311  {
312  // Now skip until #endif
313  skipUntil(stack, parentDict, "#endif", is);
314  stack.pop_back();
315  break;
316  }
317  else if (t.wordToken() == "#endif")
318  {
319  stack.pop_back();
320  break;
321  }
322  else
323  {
324  pending = true;
325  }
326  }
327  else
328  {
329  pending = true;
330  }
331 
332  if (pending)
333  {
334  is.putBack(t);
335  bool ok = entry::New(parentDict, is);
336  if (!ok)
337  {
338  return false;
339  }
340  }
341  }
342  return true;
343 }
344 
345 
347 (
348  const bool doIf,
349  DynamicList<filePos>& stack,
350  dictionary& parentDict,
351  Istream& is
352 )
353 {
354  if (doIf)
355  {
356  evaluate(true, stack, parentDict, is);
357  }
358  else
359  {
360  // Fast-forward to #else
361  token t;
362  while (!is.eof())
363  {
364  readToken(t, is);
365 
366  // Only consider directives
367  if (!t.isDirective())
368  {
369  continue;
370  }
371 
372  if (t.wordToken() == "#if" || t.wordToken() == "#ifeq")
373  {
374  stack.push_back(filePos(is.name(), is.lineNumber()));
375  skipUntil(stack, parentDict, "#endif", is);
376  stack.pop_back();
377  }
378  else if (t.wordToken() == "#else")
379  {
380  break;
381  }
382  else if (t.wordToken() == "#elif")
383  {
384  // const label lineNo = is.lineNumber();
385 
386  // Read line
387  string line;
388  dynamic_cast<ISstream&>(is).getLine(line);
389  line += ';';
390  IStringStream lineStream(line);
391  const primitiveEntry e("ifEntry", parentDict, lineStream);
392 
393  if (ifEntry::isTrue(e.stream()))
394  {
395  // Info<< "Using #elif " << doIf << " - line " << lineNo
396  // << " in file " << is.relativeName() << endl;
397  break;
398  }
399  }
400  else if (t.wordToken() == "#endif")
401  {
402  stack.pop_back();
403  break;
404  }
405  }
406 
407  if (t.wordToken() == "#else")
408  {
409  // Evaluate until we hit #endif
410  evaluate(false, stack, parentDict, is);
411  }
412  else if (t.wordToken() == "#elif")
413  {
414  // Evaluate until we hit #else or #endif
415  evaluate(true, stack, parentDict, is);
416  }
417  }
418  return true;
419 }
420 
421 
423 (
424  DynamicList<filePos>& stack,
425  dictionary& parentDict,
426  Istream& is
427 )
428 {
429  const label nNested = stack.size();
430 
431  stack.push_back(filePos(is.name(), is.lineNumber()));
432 
433  // Read first token and expand any string
434  token cond1(is);
435  cond1 = expandToken(parentDict, cond1);
436 
437  // Read second token and expand any string
438  token cond2(is);
439  cond2 = expandToken(parentDict, cond2);
440 
441  const bool equal = equalToken(cond1, cond2);
442 
443  // Info<< "Using #" << typeName << " " << cond1
444  // << " == " << cond2
445  // << " at line " << stack.back().second()
446  // << " in file " << stack.back().first() << endl;
447 
448  bool ok = ifeqEntry::execute(equal, stack, parentDict, is);
449 
450  if (stack.size() != nNested)
451  {
452  FatalIOErrorInFunction(parentDict)
453  << "Did not find matching #endif for condition starting"
454  << " at line " << stack.back().second()
455  << " in file " << stack.back().first() << exit(FatalIOError);
456  }
457 
458  return ok;
459 }
460 
461 
462 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
463 
465 (
466  dictionary& parentDict,
467  Istream& is
468 )
469 {
470  DynamicList<filePos> stack(10);
471  return execute(stack, parentDict, is);
472 }
473 
474 
475 // ************************************************************************* //
boolean type
Definition: token.H:84
dictionary dict
single character punctuation
Definition: token.H:83
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
defineTypeNameAndDebug(codeStream, 0)
A list of keyword definitions, which are a keyword followed by a number of values (eg...
Definition: dictionary.H:129
bool equal(const T &a, const T &b)
Compare two values for equality.
Definition: label.H:164
stream flag (1-byte bitmask)
Definition: token.H:82
const word & wordToken() const
Return const reference to the word contents.
Definition: tokenI.H:729
An Istream is an abstract base class for all input systems (streams, files, token lists etc)...
Definition: Istream.H:57
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
A token holds an item read from Istream.
Definition: token.H:65
#define w2
Definition: blockCreate.C:28
End entry [isseparator].
Definition: token.H:160
Foam::word.
Definition: token.H:90
void pop_back(label n=1)
Reduce size by 1 or more elements. Can be called on an empty list.
Definition: DynamicListI.H:701
label (integer) type
Definition: token.H:85
void putBack(const token &tok)
Put back a token (copy). Only a single put back is permitted.
Definition: Istream.C:71
Compound type such as List<label> etc.
Definition: token.H:92
const dimensionedScalar e
Elementary charge.
Definition: createFields.H:11
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects...
Definition: DynamicList.H:51
string evaluate(label fieldWidth, const std::string &s, size_t pos=0, size_t len=std::string::npos)
String evaluation with specified (positive, non-zero) field width.
float (single-precision) type
Definition: token.H:86
void inplaceExpand(std::string &s, const HashTable< string > &mapping, const char sigil='$')
Inplace expand occurrences of variables according to the mapping. Does not use environment values...
Definition: stringOps.C:718
Token error encountered.
Definition: token.H:79
Foam::string (usually double-quoted)
Definition: token.H:91
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:627
static bool evaluate(const bool doIf, DynamicList< filePos > &stack, dictionary &parentDict, Istream &is)
Definition: ifeqEntry.C:274
double (double-precision) type
Definition: token.H:87
Macros for easy insertion into member function selection tables.
String-variant: plain character content.
Definition: token.H:103
addNamedToMemberFunctionSelectionTable(functionEntry, calcEntry, execute, dictionaryIstream, calc)
static bool New(dictionary &parentDict, Istream &is, const inputMode inpMode=inputMode::GLOBAL, const int endChar=0)
Construct from an Istream and insert into the dictionary.
Definition: entryIO.C:98
#define w1
Definition: blockCreate.C:27
bool isDirective() const noexcept
Token is DIRECTIVE (word variant)
Definition: tokenI.H:723
An undefined token-type.
Definition: token.H:78
static bool execute(const bool equal, DynamicList< filePos > &stack, dictionary &parentDict, Istream &is)
Main driver: depending on &#39;equal&#39; starts evaluating or skips forward to else.
Definition: ifeqEntry.C:340
bool eof() const noexcept
True if end of input seen.
Definition: IOstream.H:289
Namespace for OpenFOAM.
Detect if the string contains meta-characters.
Definition: wordRe.H:113
IOerror FatalIOError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL IO ERROR&#39; header text and ...