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 fundamental and VectorSpace types
54 // (caller excludes none/invalid)
55 template<class Type>
56 static void addTokens(tokenList& toks, const Type& val)
57 {
58  if constexpr (std::is_same_v<bool, Type>)
59  {
60  // For bool
61  toks.emplace_back() = token::boolean(val);
62  }
63  else if constexpr (std::is_arithmetic_v<Type>)
64  {
65  // For label, float, double, ...
66  toks.emplace_back() = val;
67  }
68  else
69  {
71 
72  constexpr direction nParen =
73  2*(is_vectorspace_v<Type> || (nCmpt > 1) ? 1 : 0);
74 
75  const label nOld = toks.size();
76  toks.resize(nOld + label(nCmpt + nParen));
77 
78  auto iter = toks.begin(nOld);
79 
80  if (nParen)
81  {
82  *iter = token::BEGIN_LIST;
83  ++iter;
84  }
85 
86  for (direction cmpt = 0; cmpt < nCmpt; ++cmpt)
87  {
88  *iter = component(val, cmpt);
89  ++iter;
90  }
91 
92  if (nParen)
93  {
94  *iter = token::END_LIST;
95  ++iter;
96  }
97  }
98 }
99 
100 #endif
101 
102 
103 // General output of type (caller excludes none/invalid)
104 template<class Type>
105 static void putType(Ostream& os, const Type& val)
106 {
107  os << val;
108 }
110 
111 //- Specialized for bool.
112 //- Write as (true/false) via Switch to avoid bool/label ambiguity
113 template<>
114 void putType<bool>(Ostream& os, const bool& val)
115 {
116  // Note: prefer Switch() vs (std::ios::boolalpha) to avoid any
117  // potential locale issues.
118  os << Switch(val);
119 }
120 
121 
122 //- Specialized for scalar.
123 //- Write with '.' to avoid scalar/label ambiguity
124 template<>
125 void putType<scalar>(Ostream& os, const scalar& val)
126 {
127  const auto oldflags = os.setf(std::ios::showpoint);
128  os << val;
129  os.flags(oldflags); // Restore
130 }
131 
132 } // End namespace Foam
133 
134 
135 // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
136 
139 {
141 
142  const token& tok0 = is.peek();
143 
145  {
146  // Expecting "( content )" - eg, (x y z), (xx xy ...)
147 
148  // First component starts after the opening '('.
149  // Can use the current index if the '(' actually came from
150  // the putBack.
151 
152  const label firstCmpti = (is.tokenIndex() + (is.hasPutback() ? 0 : 1));
153 
154  // Search for closing ')', require all components to be numbers
155  for (label endCmpti = firstCmpti; endCmpti < is.size(); ++endCmpti)
156  {
157  const token& tok = is[endCmpti];
158 
162 
164  {
165  // Select based on the number of components
166  // cf. pTraits<Type>::nComponents
167 
168  switch (endCmpti - firstCmpti)
169  {
170  case 0: // Explicitly provided '()' - ie, none
172  break;
173 
174  case 1: // pTraits<sphericalTensor>::nComponents
176  break;
177 
178  // FUTURE?
179  // case 2: // pTraits<complex>::nComponents
180  // whichCode = exprTypeTraits<complex>::value;
181  // break;
182 
183  case 3: // pTraits<vector>::nComponents
184  whichCode = exprTypeTraits<vector>::value;
185  break;
186 
187  case 6: // pTraits<symmTensor>::nComponents
189  break;
190 
191  case 9: // pTraits<tensor>::nComponents
192  whichCode = exprTypeTraits<tensor>::value;
193  break;
194  }
195 
196  // Closing ')' terminates peeking
197  break;
198  }
199  else if (!tok.isNumber())
200  {
201  // All components should be numeric
202  break;
203  }
204  }
205  }
206  else if (tok0.good())
207  {
209 
210  if (tok0.isScalar())
211  {
212  whichCode = exprTypeTraits<scalar>::value;
213  }
214  else if (tok0.isLabel())
215  {
216  whichCode = exprTypeTraits<label>::value;
217  }
218  else if (Switch(tok0).good())
219  {
220  whichCode = exprTypeTraits<bool>::value;
221  }
222 
223  // Treat anything else as 'invalid', which also implicitly
224  // includes the token "bad"
225  // else if (tok0.isWord("bad"))
226  // {
227  // whichCode = expressions::valueTypeCode::INVALID;
228  // }
229  }
230 
231  return whichCode;
232 }
234 
236 (
237  const std::string& str,
238  exprValue& val
239 )
240 {
241  ITstream is(str);
242 
243  // No trailing non-whitespace!
244  return (val.readTokens(is) && !is.nRemainingTokens());
245 }
246 
248 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
249 
251 {
252  std::memset(static_cast<void*>(this), '\0', sizeof(*this));
253  // Redundant: typeCode_ = expressions::valueTypeCode::NONE;
254 }
255 
256 
257 void Foam::expressions::exprValue::deepCopy(const exprValue& rhs)
258 {
259  if (this != &rhs)
260  {
261  // Self-assignment is a no-op
262  std::memcpy(static_cast<void*>(this), &rhs, sizeof(*this));
263  }
264 }
265 
266 
267 #if 0
268 Foam::tokenList Foam::expressions::exprValue::tokens(bool prune) const
269 {
270  // Handling for NONE, INVALID:
271  // - NONE => pair of ( ) brackets
272  // - INVALID => "bad" as a word
273  //
274  // With prune:
275  // - no output for either
276 
277  tokenList toks;
278 
279  switch (typeCode_)
280  {
282  {
283  if (!prune)
284  {
285  toks.resize(2);
286  toks.front() = token::BEGIN_LIST;
287  toks.back() = token::END_LIST;
288  }
289  break;
290  }
291 
293  {
294  if (!prune)
295  {
296  toks.emplace_back(word("bad"));
297  }
298  break;
299  }
300 
301  #undef doLocalCode
302  #define doLocalCode(Type, UnusedParam) \
303  \
304  case expressions::valueTypeCode::type_##Type : \
305  { \
306  const Type* dataPtr = data_.get<Type>(); \
307  if (dataPtr) \
308  { \
309  addTokens<Type>(toks, *dataPtr); \
310  } \
311  break; \
312  }
313 
315  #undef doLocalCode
316 
317  // exprValue may only be a subset of valueTypeCode types
318  default: break;
319  }
320 
321  return toks;
322 }
323 #endif
324 
325 
326 void Foam::expressions::exprValue::write(Ostream& os, bool prune) const
327 {
328  // Handling for NONE, INVALID:
329  // - NONE => pair of ( ) brackets
330  // - INVALID => "bad" as a word
331  //
332  // With prune:
333  // - no output for either
334 
335  switch (typeCode_)
336  {
338  {
339  if (!prune)
340  {
342  }
343  break;
344  }
345 
347  {
348  if (!prune)
349  {
350  os << word("bad");
351  }
352  break;
353  }
354 
355  #undef doLocalCode
356  #define doLocalCode(Type, UnusedParam) \
357  \
358  case expressions::valueTypeCode::type_##Type : \
359  { \
360  const Type* dataPtr = data_.get<Type>(); \
361  if (dataPtr) \
362  { \
363  putType(os, *dataPtr); \
364  } \
365  break; \
366  }
367 
369  #undef doLocalCode
370 
371  // exprValue may only be a subset of valueTypeCode types
372  default: break;
373  }
374 }
375 
376 
378 {
379  ITstream* stream = dynamic_cast<ITstream*>(&is);
380 
381  // Reading via tokens - simple for now
382  // Expect either a single token (scalar, label, word etc)
383  // or ( ... ) content
384 
385  ITstream toks;
386 
387  if (!stream)
388  {
389  token tok(is);
390 
392 
394  {
395  // Expecting "( content )" - eg, (x y z), (xx xy ...)
396  do
397  {
398  toks.add_tokens(tok);
399 
400  is >> tok;
402  }
403  while (!tok.isPunctuation(token::END_LIST));
404 
406  {
407  toks.add_tokens(tok);
408  }
409  }
410  else if (tok.good())
411  {
412  toks.add_tokens(tok);
413  }
414 
415  // Truncate to number tokens read
416  toks.resize(toks.tokenIndex());
417  toks.seek(0);
418 
419  stream = &toks;
420  }
421 
422  return readTokens(*stream);
423 }
424 
425 
427 {
428  clear(); // type: none, value: zero
429 
430  const valueTypeCode whichCode(exprValue::peekType(is));
431 
432  switch (whichCode)
433  {
435  {
436  typeCode_ = whichCode;
437  is.skip(2); // Skip tokens: '( )'
438  return true;
439  }
440 
441  // This one should be rare or even impossible
443  {
444  typeCode_ = whichCode;
445  if (!is.bad() && is.peek().isWord("bad"))
446  {
447  is.skip(1); // Skip token: "bad"
448  return true;
449  }
450  return false; // Some type of failure..
451  }
452 
453  #undef doLocalCode
454  #define doLocalCode(Type, UnusedParam) \
455  \
456  case expressions::valueTypeCode::type_##Type : \
457  { \
458  data_.set<Type>(pTraits<Type>(is)); \
459  typeCode_ = whichCode; \
460  return true; \
461  }
462 
464  #undef doLocalCode
465 
466  // exprValue may only be a subset of valueTypeCode types
467  default: break;
468  }
469 
470  return false;
471 }
473 
475 (
476  const exprValue& rhs
477 ) const
478 {
479  if (typeCode_ != rhs.typeCode_)
480  {
481  // First compare by type
482  return (int(typeCode_) - int(rhs.typeCode_));
483  }
484  else if ((this == &rhs) || !good())
485  {
486  // Identical: same object or not good
487  // (ie, no further comparison possible)
488  return 0;
489  }
490 
491  // Types are identical (and good)
492  // - compare by value.
493  // This is even messier than usual, since can only rely on
494  // operator< being defined
495 
496  switch (typeCode_)
497  {
498  #undef doLocalCode
499  #define doLocalCode(Type, UnusedParam) \
500  \
501  case expressions::valueTypeCode::type_##Type : \
502  { \
503  const Type* a = data_.get<Type>(); \
504  const Type* b = rhs.data_.get<Type>(); \
505  return \
506  ( \
507  (a && b) \
508  ? ((*a < *b) ? -1 : (*b < *a) ? 1 : 0) \
509  : 0 \
510  ); \
511  break; \
512  }
513 
515  #undef doLocalCode
516 
517  // exprValue may only be a subset of valueTypeCode types
518  default: break;
519  }
520 
521  // Should not happen
522  return 0;
523 }
524 
526 // * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * //
527 
529 {
530  if (typeCode_ != rhs.typeCode_)
531  {
532  // Types must match
533  return false;
534  }
535  else if (this == &rhs)
536  {
537  return true;
538  }
539 
540  switch (typeCode_)
541  {
542  #undef doLocalCode
543  #define doLocalCode(Type, UnusedParam) \
544  \
545  case expressions::valueTypeCode::type_##Type : \
546  { \
547  const Type* a = data_.get<Type>(); \
548  const Type* b = rhs.data_.get<Type>(); \
549  return (a && b && (*a == *b)); \
550  break; \
551  }
552 
554  #undef doLocalCode
555 
556  // exprValue may only be a subset of valueTypeCode types
557  default: break;
558  }
559 
560  return false;
561 }
562 
563 
565 {
566  return (this->compare(rhs) < 0);
567 }
568 
569 
570 // * * * * * * * * * * * * * * * IOstream Operators * * * * * * * * * * * * //
571 
572 Foam::Istream& Foam::operator>>
573 (
574  Istream& is,
576 )
577 {
578  val.read(is);
579  return is;
580 }
582 
583 Foam::Ostream& Foam::operator<<
584 (
585  Ostream& os,
586  const expressions::exprValue& val
587 )
588 {
589  val.write(os, false); // no pruning
590  return os;
591 }
592 
593 
594 template<>
595 Foam::Ostream& Foam::operator<<
596 (
597  Ostream& os,
598  const InfoProxy<expressions::exprValue>& iproxy
599 )
600 {
601  const auto& val = *iproxy;
602 
603  if (val.typeCode() == expressions::valueTypeCode::NONE)
604  {
605  os << "none";
606  }
607  else if (val.typeCode() == expressions::valueTypeCode::INVALID)
608  {
609  os << "bad";
610  }
611  else
612  {
613  os << val.valueTypeName() << ": ";
614  val.write(os); // pruning is immaterial - !good() already handled
615  }
616 
617  return os;
618 }
619 
620 
621 // ************************************************************************* //
void add_tokens(const token &tok)
Copy append a token at the current tokenIndex, incrementing the index.
Definition: ITstream.C:674
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:114
bool good() const noexcept
True if the Switch represents a valid enumeration.
Definition: Switch.C:307
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:150
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:600
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
The vector-space number of components: default is 1.
Definition: pTraits.H:140
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:529
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:472
label nRemainingTokens() const noexcept
Number of tokens remaining.
Definition: ITstream.H:492
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:135
bool hasPutback() const noexcept
True if putback token is in use.
Definition: ITstream.H:417
static bool read(const std::string &str, exprValue &val)
Read entire string as a exprValue, skipping leading/trailing whitespace.
Definition: exprValue.C:233
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:472
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:385
bool operator<(const exprValue &rhs) const
Compare (type,value)
Definition: exprValue.C:561
void write(Ostream &os, bool prune=false) const
Write the (type-specific) content.
Definition: exprValue.C:323
An Ostream is an abstract base class for all output systems (streams, files, token lists...
Definition: Ostream.H:56
const direction noexcept
Definition: scalarImpl.H:255
bool readTokens(ITstream &is)
Guess type and read tokens (if possible)
Definition: exprValue.C:423
bool operator==(const exprValue &rhs) const
Compare (type,value) for equality.
Definition: exprValue.C:525
OBJstream os(runTime.globalPath()/outputName)
#define FUNCTION_NAME
T & emplace_back(Args &&... args)
Construct an element at the end of the list, return reference to the new list element.
Definition: ListI.H:205
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:122
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
const token & peek() const noexcept
Failsafe peek at what the next read would return, including handling of any putback.
Definition: ITstream.C:313
surface1 clear()
bool isLabel() const noexcept
Token is LABEL.
Definition: tokenI.H:599
void clear()
Reset to &#39;none&#39;.
Definition: exprValue.C:247
bool skip(label n=1) noexcept
Move tokenIndex relative to the current position.
Definition: ITstream.C:371
static void putType(Ostream &os, const Type &val)
Definition: exprValue.C:98
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:109
void seek(label pos) noexcept
Move tokenIndex to the specified position and adjust the stream status (open/good/eof ...
Definition: ITstream.C:339
An input stream of tokens.
Definition: ITstream.H:52
Namespace for OpenFOAM.