exprValue.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) 2021-2024 OpenCFD Ltd.
9 -------------------------------------------------------------------------------
10 License
11  This file is part of OpenFOAM.
12 
13  OpenFOAM is free software: you can redistribute it and/or modify it
14  under the terms of the GNU General Public License as published by
15  the Free Software Foundation, either version 3 of the License, or
16  (at your option) any later version.
17 
18  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
19  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
20  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21  for more details.
22 
23  You should have received a copy of the GNU General Public License
24  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
25 
26 \*---------------------------------------------------------------------------*/
27 
28 #include "exprValue.H"
29 #include "error.H"
30 #include "ITstream.H"
31 #include "Switch.H"
32 #include <cstring> // For memcpy, memset
33 
34 // * * * * * * * * * * * * * * * * Details * * * * * * * * * * * * * * * * //
35 
37 (
38  const std::string& msg
39 ) noexcept
40 {
42  << "non-specialized: " << msg.c_str() << endl
43  << abort(FatalError);
44 }
45 
46 
47 // * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
48 
49 namespace Foam
50 {
51 
52 #if 0
53 // General way to add tokens for VectorSpace types
54 // (caller excludes none/invalid)
55 template<class Type>
56 static void addTokens(tokenList& toks, const Type& val)
57 {
59  const direction nParen = 2*(pTraits<Type>::rank || (nCmpt > 1) ? 1 : 0);
60 
61  const label nOld = toks.size();
62  toks.resize(nOld + label(nCmpt + nParen));
63 
64  auto iter = toks.begin(nOld);
65 
66  if (nParen)
67  {
68  *iter = token::BEGIN_LIST;
69  ++iter;
70  }
71 
72  for (direction cmpt = 0; cmpt < nCmpt; ++cmpt)
73  {
74  *iter = component(val, cmpt);
75  ++iter;
76  }
77 
78  if (nParen)
79  {
80  *iter = token::END_LIST;
81  ++iter;
82  }
83 }
84 
85 
86 //- Specialized for bool
87 template<>
88 void addTokens<bool>(tokenList& toks, const bool& val)
89 {
90  toks.emplace_back() = token::boolean(val);
91 }
92 
93 
94 //- Specialized for label
95 template<>
96 void addTokens<label>(tokenList& toks, const label& val)
97 {
98  toks.emplace_back() = val;
99 }
100 
101 
102 //- Specialized for scalar
103 template<>
104 void addTokens<scalar>(tokenList& toks, const scalar& val)
105 {
106  toks.emplace_back() = val;
107 }
108 
109 #endif
110 
111 
112 // General output of type (caller excludes none/invalid)
113 template<class Type>
114 static void putType(Ostream& os, const Type& val)
115 {
116  os << val;
117 }
118 
119 
120 //- Specialized for bool.
121 //- Write as (true/false) via Switch to avoid bool/label ambiguity
122 template<>
123 void putType<bool>(Ostream& os, const bool& val)
124 {
125  // Note: prefer Switch() vs (std::ios::boolalpha) to avoid any
126  // potential locale issues.
127  os << Switch(val);
128 }
129 
130 
131 //- Specialized for scalar.
132 //- Write with '.' to avoid scalar/label ambiguity
133 template<>
134 void putType<scalar>(Ostream& os, const scalar& val)
135 {
136  const auto oldflags = os.setf(std::ios::showpoint);
137  os << val;
138  os.flags(oldflags); // Restore
139 }
140 
141 } // End namespace Foam
142 
143 
144 // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
145 
147 Foam::expressions::exprValue::peekType(const ITstream& is)
148 {
151  const token& tok0 = is.peek();
152 
154  {
155  // Expecting "( content )" - eg, (x y z), (xx xy ...)
156 
157  // First component starts after the opening '('.
158  // Can use the current index if the '(' actually came from
159  // the putBack.
160 
161  const label firstCmpti = (is.tokenIndex() + (is.hasPutback() ? 0 : 1));
162 
163  // Search for closing ')', require all components to be numbers
164  for (label endCmpti = firstCmpti; endCmpti < is.size(); ++endCmpti)
165  {
166  const token& tok = is[endCmpti];
167 
171 
173  {
174  // Select based on the number of components
175  // cf. pTraits<Type>::nComponents
176 
177  switch (endCmpti - firstCmpti)
178  {
179  case 0: // Explicitly provided '()' - ie, none
181  break;
182 
183  case 1: // pTraits<sphericalTensor>::nComponents
185  break;
186 
187  // FUTURE?
188  // case 2: // pTraits<complex>::nComponents
189  // whichCode = exprTypeTraits<complex>::value;
190  // break;
191 
192  case 3: // pTraits<vector>::nComponents
193  whichCode = exprTypeTraits<vector>::value;
194  break;
195 
196  case 6: // pTraits<symmTensor>::nComponents
198  break;
199 
200  case 9: // pTraits<tensor>::nComponents
201  whichCode = exprTypeTraits<tensor>::value;
202  break;
203  }
204 
205  // Closing ')' terminates peeking
206  break;
207  }
208  else if (!tok.isNumber())
209  {
210  // All components should be numeric
211  break;
212  }
213  }
214  }
215  else if (tok0.good())
216  {
218 
219  if (tok0.isScalar())
220  {
221  whichCode = exprTypeTraits<scalar>::value;
222  }
223  else if (tok0.isLabel())
224  {
225  whichCode = exprTypeTraits<label>::value;
226  }
227  else if (Switch(tok0).good())
228  {
229  whichCode = exprTypeTraits<bool>::value;
230  }
231 
232  // Treat anything else as 'invalid', which also implicitly
233  // includes the token "bad"
234  // else if (tok0.isWord("bad"))
235  // {
236  // whichCode = expressions::valueTypeCode::INVALID;
237  // }
238  }
239 
240  return whichCode;
241 }
242 
243 
245 (
246  const std::string& str,
247  exprValue& val
248 )
249 {
250  ITstream is(str);
251 
252  // No trailing non-whitespace!
253  return (val.readTokens(is) && !is.nRemainingTokens());
254 }
255 
256 
257 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
258 
260 {
261  std::memset(static_cast<void*>(this), '\0', sizeof(*this));
262  // Redundant: typeCode_ = expressions::valueTypeCode::NONE;
263 }
264 
265 
266 void Foam::expressions::exprValue::deepCopy(const exprValue& rhs)
267 {
268  if (this != &rhs)
269  {
270  // Self-assignment is a no-op
271  std::memcpy(static_cast<void*>(this), &rhs, sizeof(*this));
272  }
273 }
274 
275 
276 #if 0
277 Foam::tokenList Foam::expressions::exprValue::tokens(bool prune) const
278 {
279  // Handling for NONE, INVALID:
280  // - NONE => pair of ( ) brackets
281  // - INVALID => "bad" as a word
282  //
283  // With prune:
284  // - no output for either
285 
286  tokenList toks;
287 
288  switch (typeCode_)
289  {
291  {
292  if (!prune)
293  {
294  toks.resize(2);
295  toks.front() = token::BEGIN_LIST;
296  toks.back() = token::END_LIST;
297  }
298  break;
299  }
300 
302  {
303  if (!prune)
304  {
305  toks.emplace_back(word("bad"));
306  }
307  break;
308  }
309 
310  #undef doLocalCode
311  #define doLocalCode(Type, UnusedParam) \
312  \
313  case expressions::valueTypeCode::type_##Type : \
314  { \
315  const Type* dataPtr = data_.get<Type>(); \
316  if (dataPtr) \
317  { \
318  addTokens<Type>(toks, *dataPtr); \
319  } \
320  break; \
321  }
322 
324  #undef doLocalCode
325 
326  // exprValue may only be a subset of valueTypeCode types
327  default: break;
328  }
329 
330  return toks;
331 }
332 #endif
333 
334 
335 void Foam::expressions::exprValue::write(Ostream& os, bool prune) const
336 {
337  // Handling for NONE, INVALID:
338  // - NONE => pair of ( ) brackets
339  // - INVALID => "bad" as a word
340  //
341  // With prune:
342  // - no output for either
343 
344  switch (typeCode_)
345  {
347  {
348  if (!prune)
349  {
351  }
352  break;
353  }
354 
356  {
357  if (!prune)
358  {
359  os << word("bad");
360  }
361  break;
362  }
363 
364  #undef doLocalCode
365  #define doLocalCode(Type, UnusedParam) \
366  \
367  case expressions::valueTypeCode::type_##Type : \
368  { \
369  const Type* dataPtr = data_.get<Type>(); \
370  if (dataPtr) \
371  { \
372  putType(os, *dataPtr); \
373  } \
374  break; \
375  }
376 
378  #undef doLocalCode
379 
380  // exprValue may only be a subset of valueTypeCode types
381  default: break;
382  }
383 }
384 
385 
387 {
388  ITstream* stream = dynamic_cast<ITstream*>(&is);
390  // Reading via tokens - simple for now
391  // Expect either a single token (scalar, label, word etc)
392  // or ( ... ) content
393 
394  ITstream toks;
395 
396  if (!stream)
397  {
398  token tok(is);
399 
401 
403  {
404  // Expecting "( content )" - eg, (x y z), (xx xy ...)
405  do
406  {
407  toks.add_tokens(tok);
408 
409  is >> tok;
411  }
412  while (!tok.isPunctuation(token::END_LIST));
413 
415  {
416  toks.add_tokens(tok);
417  }
418  }
419  else if (tok.good())
420  {
421  toks.add_tokens(tok);
422  }
423 
424  // Truncate to number tokens read
425  toks.resize(toks.tokenIndex());
426  toks.seek(0);
427 
428  stream = &toks;
429  }
430 
431  return readTokens(*stream);
432 }
433 
434 
436 {
437  clear(); // type: none, value: zero
439  const valueTypeCode whichCode(exprValue::peekType(is));
440 
441  switch (whichCode)
442  {
444  {
445  typeCode_ = whichCode;
446  is.skip(2); // Skip tokens: '( )'
447  return true;
448  }
449 
450  // This one should be rare or even impossible
452  {
453  typeCode_ = whichCode;
454  if (!is.bad() && is.peek().isWord("bad"))
455  {
456  is.skip(1); // Skip token: "bad"
457  return true;
458  }
459  return false; // Some type of failure..
460  }
461 
462  #undef doLocalCode
463  #define doLocalCode(Type, UnusedParam) \
464  \
465  case expressions::valueTypeCode::type_##Type : \
466  { \
467  data_.set<Type>(pTraits<Type>(is)); \
468  typeCode_ = whichCode; \
469  return true; \
470  }
471 
473  #undef doLocalCode
474 
475  // exprValue may only be a subset of valueTypeCode types
476  default: break;
477  }
478 
479  return false;
480 }
481 
482 
484 (
485  const exprValue& rhs
486 ) const
487 {
488  if (typeCode_ != rhs.typeCode_)
489  {
490  // First compare by type
491  return (int(typeCode_) - int(rhs.typeCode_));
492  }
493  else if ((this == &rhs) || !good())
494  {
495  // Identical: same object or not good
496  // (ie, no further comparison possible)
497  return 0;
498  }
499 
500  // Types are identical (and good)
501  // - compare by value.
502  // This is even messier than usual, since can only rely on
503  // operator< being defined
504 
505  switch (typeCode_)
506  {
507  #undef doLocalCode
508  #define doLocalCode(Type, UnusedParam) \
509  \
510  case expressions::valueTypeCode::type_##Type : \
511  { \
512  const Type* a = data_.get<Type>(); \
513  const Type* b = rhs.data_.get<Type>(); \
514  return \
515  ( \
516  (a && b) \
517  ? ((*a < *b) ? -1 : (*b < *a) ? 1 : 0) \
518  : 0 \
519  ); \
520  break; \
521  }
522 
524  #undef doLocalCode
525 
526  // exprValue may only be a subset of valueTypeCode types
527  default: break;
528  }
529 
530  // Should not happen
531  return 0;
532 }
533 
534 
535 // * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * //
536 
538 {
539  if (typeCode_ != rhs.typeCode_)
540  {
541  // Types must match
542  return false;
543  }
544  else if (this == &rhs)
545  {
546  return true;
547  }
548 
549  switch (typeCode_)
550  {
551  #undef doLocalCode
552  #define doLocalCode(Type, UnusedParam) \
553  \
554  case expressions::valueTypeCode::type_##Type : \
555  { \
556  const Type* a = data_.get<Type>(); \
557  const Type* b = rhs.data_.get<Type>(); \
558  return (a && b && (*a == *b)); \
559  break; \
560  }
561 
563  #undef doLocalCode
564 
565  // exprValue may only be a subset of valueTypeCode types
566  default: break;
567  }
568 
569  return false;
570 }
571 
572 
573 bool Foam::expressions::exprValue::operator<(const exprValue& rhs) const
574 {
575  return (this->compare(rhs) < 0);
576 }
577 
578 
579 // * * * * * * * * * * * * * * * IOstream Operators * * * * * * * * * * * * //
580 
581 Foam::Istream& Foam::operator>>
582 (
583  Istream& is,
585 )
586 {
587  val.read(is);
588  return is;
589 }
590 
591 
592 Foam::Ostream& Foam::operator<<
593 (
594  Ostream& os,
595  const expressions::exprValue& val
596 )
597 {
598  val.write(os, false); // no pruning
599  return os;
600 }
601 
602 
603 template<>
604 Foam::Ostream& Foam::operator<<
605 (
606  Ostream& os,
607  const InfoProxy<expressions::exprValue>& iproxy
608 )
609 {
610  const auto& val = *iproxy;
611 
612  if (val.typeCode() == expressions::valueTypeCode::NONE)
613  {
614  os << "none";
615  }
616  else if (val.typeCode() == expressions::valueTypeCode::INVALID)
617  {
618  os << "bad";
619  }
620  else
621  {
622  os << val.valueTypeName() << ": ";
623  val.write(os); // pruning is immaterial - !good() already handled
624  }
625 
626  return os;
627 }
628 
629 
630 // ************************************************************************* //
void add_tokens(const token &tok)
Copy append a token at the current tokenIndex, incrementing the index.
Definition: ITstream.C:690
bool good() const noexcept
True if token is not UNDEFINED or ERROR.
Definition: tokenI.H:507
bool isPunctuation() const noexcept
Token is PUNCTUATION.
Definition: tokenI.H:561
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:116
bool isWord() const noexcept
Token is word-variant (WORD, DIRECTIVE)
Definition: tokenI.H:729
uint8_t direction
Definition: direction.H:46
bool bad() const noexcept
True if stream is corrupted.
Definition: IOstream.H:305
static void notSpecialized(const std::string &msg) noexcept
Runtime &#39;assert&#39; for unimplemented generic methods.
Definition: exprValue.C:30
void resize(const label len)
Adjust allocated size of list.
Definition: ListI.H:153
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:608
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition: BitOps.H:56
A polymorphic typed union of simple primitive and VectorSpace types. It uses a &#39;fatter&#39; representatio...
Definition: exprValue.H:157
An Istream is an abstract base class for all input systems (streams, files, token lists etc)...
Definition: Istream.H:57
A token holds an item read from Istream.
Definition: token.H:65
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
A traits class, which is primarily used for primitives and vector-space.
Definition: pTraits.H:75
No type, or default initialized type.
A simple wrapper around bool so that it can be read as a word: true/false, on/off, yes/no, any/none. Also accepts 0/1 as a string and shortcuts t/f, y/n.
Definition: Switch.H:77
Begin list [isseparator].
Definition: token.H:161
virtual std::ios_base::fmtflags flags() const override
Get current stream flags.
Definition: OSstream.H:163
std::ios_base::fmtflags setf(std::ios_base::fmtflags f)
Set stream flag(s), return old stream flags.
Definition: IOstream.H:487
label tokenIndex() const noexcept
The current token index when reading, or the insertion point.
Definition: ITstream.H:487
label nRemainingTokens() const noexcept
Number of tokens remaining.
Definition: ITstream.H:507
List< token > tokenList
List of token, used for dictionary primitive entry (for example)
Definition: tokenList.H:32
bool isScalar() const noexcept
Token is FLOAT or DOUBLE.
Definition: tokenI.H:681
Invalid/unknown/error type.
virtual Istream & read(token &)=0
Return next token from stream.
A class for handling words, derived from Foam::string.
Definition: word.H:63
static expressions::valueTypeCode peekType(const ITstream &is)
Detect the type from the available tokens.
Definition: exprValue.C:150
static bool read(const std::string &str, exprValue &val)
Read entire string as a exprValue, skipping leading/trailing whitespace.
Definition: exprValue.C:248
valueTypeCode
An enumeration of known and expected expression value types.
Definition: exprTraits.H:81
int compare(const exprValue &rhs) const
Compare (type,value)
Definition: exprValue.C:487
virtual bool write(const token &tok)=0
Write token to stream or otherwise handle it.
End list [isseparator].
Definition: token.H:162
errorManip< error > abort(error &err)
Definition: errorManip.H:139
iterator begin() noexcept
Return an iterator to begin traversing the UList.
Definition: UListI.H:392
bool operator<(const exprValue &rhs) const
Compare (type,value)
Definition: exprValue.C:576
void write(Ostream &os, bool prune=false) const
Write the (type-specific) content.
Definition: exprValue.C:338
An Ostream is an abstract base class for all output systems (streams, files, token lists...
Definition: Ostream.H:56
const direction noexcept
Definition: Scalar.H:258
bool readTokens(ITstream &is)
Guess type and read tokens (if possible)
Definition: exprValue.C:438
bool operator==(const exprValue &rhs) const
Compare (type,value) for equality.
Definition: exprValue.C:540
OBJstream os(runTime.globalPath()/outputName)
#define FUNCTION_NAME
Simple type identifiers for polymorphic expression values. The definitions are similar to std::integr...
Definition: exprTraits.H:143
#define doLocalCode(Type, UnusedParam)
void putType< scalar >(Ostream &os, const scalar &val)
Specialized for scalar. Write with &#39;.&#39; to avoid scalar/label ambiguity.
Definition: exprValue.C:137
bool fatalCheck(const char *operation) const
Check IOstream status for given operation.
Definition: IOstream.C:51
#define FOR_ALL_EXPR_VALUE_TYPES(Macro,...)
Definition: exprValue.H:49
static token boolean(bool on) noexcept
Create a bool token.
Definition: tokenI.H:26
static constexpr ::Foam::expressions::valueTypeCode value
Definition: exprTraits.H:177
const token & peek() const noexcept
Failsafe peek at what the next read would return, including handling of any putback.
Definition: ITstream.C:329
surface1 clear()
bool isLabel() const noexcept
Token is LABEL.
Definition: tokenI.H:599
void clear()
Reset to &#39;none&#39;.
Definition: exprValue.C:262
bool skip(label n=1) noexcept
Move tokenIndex relative to the current position.
Definition: ITstream.C:387
static void putType(Ostream &os, const Type &val)
Definition: exprValue.C:113
bool isNumber() const noexcept
Token is LABEL, FLOAT or DOUBLE.
Definition: tokenI.H:707
void component(FieldField< Field, typename FieldField< Field, Type >::cmptType > &sf, const FieldField< Field, Type > &f, const direction d)
void putType< bool >(Ostream &os, const bool &val)
Specialized for bool. Write as (true/false) via Switch to avoid bool/label ambiguity.
Definition: exprValue.C:124
void seek(label pos) noexcept
Move tokenIndex to the specified position and adjust the stream status (open/good/eof ...
Definition: ITstream.C:355
An input stream of tokens.
Definition: ITstream.H:52
Namespace for OpenFOAM.