Istream.cpp
Go to the documentation of this file.
1 /*------------------------------- phasicFlow ---------------------------------
2  O C enter of
3  O O E ngineering and
4  O O M ultiscale modeling of
5  OOOOOOO F luid flow
6 ------------------------------------------------------------------------------
7  Copyright (C): www.cemf.ir
8  email: hamid.r.norouzi AT gmail.com
9 ------------------------------------------------------------------------------
10 Licence:
11  This file is part of phasicFlow code. It is a free software for simulating
12  granular and multiphase flows. You can redistribute it and/or modify it under
13  the terms of GNU General Public License v3 or any other later versions.
14 
15  phasicFlow is distributed to help others in their research in the field of
16  granular and multiphase flows, but WITHOUT ANY WARRANTY; without even the
17  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 
19 -----------------------------------------------------------------------------*/
20 // based on OpenFOAM stream, with some modifications/simplifications
21 // to be tailored to our needs
22 
23 
24 #include "Istream.hpp"
25 #include "token.hpp"
26 #include "error.hpp"
27 
28 
29 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
30 
31 // Truncate error message for readability
32 static constexpr const unsigned errLen = 80;
33 
34 
35 namespace
36 {
37 
38 // Convert a single character to a word with length 1
39 inline static pFlow::word charToWord(char c)
40 {
41  return pFlow::word(std::string(1, c), false);
42 }
43 
44 // Permit slash-scoping of entries
45 static inline bool validVariableChar(char c)
46 {
47  return (pFlow::validWord(c) || c == '/');
48 }
49 
50 } // End anonymous namespace
51 
52 
54 {
55  char c = 0;
56 
57  while (true)
58  {
59  // Get next non-whitespace character
60  while (get(c) && isspace(c))
61  {}
62 
63  // Return if stream is bad - ie, previous get() failed
64  if (bad() || isspace(c))
65  {
66  return 0;
67  }
68 
69  // Is this the start of a C/C++ comment?
70  if (c == '/')
71  {
72  if (!get(c))
73  {
74  // Cannot get another character - return this one
75  return '/';
76  }
77 
78  if (c == '/')
79  {
80  // C++ style single-line comment - skip through past end-of-line
81  while (get(c) && c != '\n')
82  {}
83  }
84  else if (c == '*')
85  {
86  // Within a C-style comment
87  while (true)
88  {
89  // Search for end of C-style comment - '*/'
90  if (get(c) && c == '*')
91  {
92  if (get(c))
93  {
94  if (c == '/')
95  {
96  // matched '*/'
97  break;
98  }
99  else if (c == '*')
100  {
101  // check again
102  putback(c);
103  }
104  }
105  }
106 
107  if (!good())
108  {
109  return 0;
110  }
111  }
112  }
113  else
114  {
115  // The '/' did not start a C/C++ comment - return it
116  putback(c);
117  return '/';
118  }
119  }
120  else
121  {
122  // A valid character - return it
123  return c;
124  }
125  }
126 
127  return 0;
128 }
129 
130 
132 {
133  word val;
134  if (read(val).bad())
135  {
136  t.setBad();
137  }
138  else
139  {
140  t = std::move(val); // Move contents to token
141  }
142 }
143 
144 
146 {
147  constexpr const unsigned maxLen = 1024;
148  static char buf[maxLen];
149 
150  unsigned nChar = 0;
151  unsigned depth = 0; // Track depth of (..) or {..} nesting
152  char c;
153 
154  // First character must be '$'
155  if (!get(c) || c != token::DOLLAR)
156  {
157  ioErrorInFile( name(), lineNumber())
158  << "Invalid first character found : " << c << nl;
159  fatalExit;
160  }
161  buf[nChar++] = c;
162 
163  // Next character should also exist.
164  // This should never fail, since it was checked before calling.
165  if (!get(c))
166  {
167  str.assign(buf, nChar);
169  << "Truncated variable name : " << str << nl;
170 
171  return *this;
172  }
173  buf[nChar++] = c;
174 
175  str.clear();
176  if (c == token::BEGIN_BLOCK)
177  {
178  // Processing ${...} style.
179  ++depth;
180 
181  // Could check that the next char is good and not one of '{}'
182  // since this would indicate "${}", "${{..." or truncated "${"
183 
184  while (get(c))
185  {
186  buf[nChar++] = c;
187  if (nChar == maxLen)
188  {
189  str.append(buf, nChar);
190  nChar = 0;
191  }
192  if (c == token::BEGIN_BLOCK)
193  {
194  ++depth;
195  }
196  else if (c == token::END_BLOCK)
197  {
198  --depth;
199  if (!depth)
200  {
201  // Found closing '}' character
202  str.append(buf, nChar);
203  return *this;
204  }
205  }
206  }
207 
208  // Should never reach here on normal input
209 
210  str.append(buf, nChar); // Finalize pending buffer input
211 
212  nChar = str.length();
213  if (str.length() > errLen)
214  {
215  str.erase(errLen);
216  }
217 
218  ioErrorInFile(name(), lineNumber())
219  << "stream terminated while reading variable '"
220  << str.c_str() << "...' [" << static_cast<int32>(nChar) << "]\n";
221  fatalExit;
222 
223  return *this;
224  }
225  else if (validVariableChar(c))
226  {
227  // Processing $var style
228 
229  while
230  (
231  (nChar < maxLen) && get(c)
232  && (validVariableChar(c))
233  )
234  {
235  if (c == token::BEGIN_LIST)
236  {
237  ++depth;
238  }
239  else if (c == token::END_LIST)
240  {
241  if (!depth)
242  {
243  break; // Closed ')' without an opening '(' ? ... stop
244  }
245  --depth;
246  }
247 
248  buf[nChar++] = c;
249  }
250  }
251  else
252  {
253  // Invalid character. Terminate string (for message) without
254  // including the invalid character in the count.
255 
256  buf[nChar--] = '\0';
257 
259  << "Bad variable name: " << buf << nl << endl;
260  }
261 
262  if (nChar >= maxLen)
263  {
264  buf[errLen] = '\0';
265 
266  ioErrorInFile(name(), lineNumber())
267  << "variable '" << buf << "...'\n"
268  << " is too long (max. " << static_cast<int32>(maxLen) << " characters)";
269  fatalExit;
270 
271  return *this;
272  }
273 
274  buf[nChar] = '\0'; // Terminate string
275 
276  if (bad())
277  {
278  // Could probably skip this check
279  buf[errLen] = '\0';
280 
281  ioErrorInFile(name(), lineNumber())
282  << "Problem while reading variable '" << buf << "...' after "
283  << static_cast<int32>(nChar) << " characters\n";
284  fatalExit;
285 
286  ioErrorInFile(name(), lineNumber());
287 
288  return *this;
289  }
290 
291  if (depth)
292  {
294  << "Missing " << static_cast<int32>(depth)
295  << " closing ')' while parsing" << nl << nl
296  << buf << nl << endl;
297  }
298 
299  // Finalize
300  str.assign(buf, nChar);
301  putback(c);
302 
303  return *this;
304 }
305 
306 
308 (
309  std::istream& is,
310  const word& streamName
311 )
312 :
313  iIstream(),
314  name_(streamName),
315  is_(is)
316 {
317  if (is_.good())
318  {
319  setOpened();
320  setGood();
321  }
322  else
323  {
324  setState(is_.rdstate());
325  }
326 }
327 
329 {
330  is_.get(c);
331  setState(is_.rdstate());
332 
333  if (good() && c == '\n')
334  {
335  ++lineNumber_;
336  }
337 
338  return *this;
339 }
340 
341 
343 {
344  return is_.peek();
345 }
346 
347 
348 pFlow::Istream& pFlow::Istream::getLine(std::string& str, char delim)
349 {
350  std::getline(is_, str, delim);
351  setState(is_.rdstate());
352 
353  if (delim == '\n')
354  {
355  ++lineNumber_;
356  }
357 
358  return *this;
359 }
360 
361 
362 std::streamsize pFlow::Istream::getLine(std::nullptr_t, char delim)
363 {
364  is_.ignore(std::numeric_limits<std::streamsize>::max(), delim);
365  setState(is_.rdstate());
366 
367  std::streamsize count = is_.gcount();
368 
369  if (count && delim == '\n')
370  {
371  ++lineNumber_;
372  }
373 
374  return count;
375 }
376 
377 
379 {
380  if (c == '\n')
381  {
382  --lineNumber_;
383  }
384 
385  if (!is_.putback(c))
386  {
387  setBad();
388  }
389 
390  setState(is_.rdstate());
391 
392  return *this;
393 }
394 
396 {
397  constexpr const unsigned maxLen = 128; // Max length for units/scalars
398  static char buf[maxLen];
399 
400  // Return the put back token if it exists
401  if (Istream::getBack(t))
402  {
403  return *this;
404  }
405 
406  // Assume that the streams supplied are in working order.
407  // Lines are counted by '\n'
408 
409  // Get next 'valid character': i.e. proceed through any whitespace
410  // and/or comments until a semantically valid character is found
411 
412  char c = nextValid();
413 
414  // Set the line number of this token to the current stream line number
415  t.lineNumber() = lineNumber();
416 
417  // Return on error
418  if (!c)
419  {
420  t.setBad();
421  return *this;
422  }
423 
424  // Analyse input starting with this character.
425  switch (c)
426  {
427  // Check for punctuation first - same as token::isseparator()
428 
429  case token::END_STATEMENT :
430  case token::BEGIN_LIST :
431  case token::END_LIST :
432  case token::BEGIN_SQR :
433  case token::END_SQR :
434  case token::BEGIN_BLOCK :
435  case token::END_BLOCK :
436  case token::COLON :
437  case token::COMMA :
438  case token::DIVIDE :
439  {
441  return *this;
442  }
443 
444  // String: enclosed by double quotes.
445  case token::BEGIN_STRING :
446  {
447  putback(c);
448 
449  word val;
450  if (readString(val).bad())
451  {
452  t.setBad();
453  }
454  else
455  {
456  t = std::move(val); // Move contents to token
457  }
458 
459  return *this;
460  }
461  // Dictionary variable (as rvalue)
462  case token::DOLLAR :
463  {
464  char nextC;
465  if (read(nextC).bad())
466  {
467  // Return lone '$' as word
468  t = charToWord(c);
469  }
470  else
471  {
472  // Put back both so that '$...' is included in the variable
473  putback(nextC);
474  putback(c);
475 
476  word val;
477  if (readVariable(val).bad())
478  {
479  t.setBad();
480  }
481  else
482  {
483  t = std::move(val); // Move contents to token
484  t.setType(token::tokenType::VARIABLE);
485  }
486  }
487 
488  return *this;
489  }
490 
491  // Number: integer or floating point
492  //
493  // ideally match the equivalent of this regular expression
494  //
495  // /[-+]?([0-9]+\.?[0-9]*|\.[0-9]+)([Ee][-+]?[0-9]+)?/
496  //
497  case '-' :
498  case '.' :
499  case '0' : case '1' : case '2' : case '3' : case '4' :
500  case '5' : case '6' : case '7' : case '8' : case '9' :
501  {
502  int64 int64Val = (c != '.'); // used as bool here
503 
504  unsigned nChar = 0;
505  buf[nChar++] = c;
506 
507  // get everything that could resemble a number and let
508  // readScalar determine the validity
509  while
510  (
511  is_.get(c)
512  && (
513  isdigit(c)
514  || c == '+'
515  || c == '-'
516  || c == '.'
517  || c == 'E'
518  || c == 'e'
519  )
520  )
521  {
522  if (int64Val)
523  {
524  int64Val = isdigit(c);
525  }
526 
527  buf[nChar++] = c;
528  if (nChar == maxLen)
529  {
530  // Runaway argument - avoid buffer overflow
531  buf[maxLen-1] = '\0';
532 
533  ioErrorInFile( name(), lineNumber())
534  << "number '" << buf << "...'\n"
535  << " is too long (max. " <<
536  static_cast<int32>(maxLen) << " characters)";
537  fatalExit;
538 
539  t.setBad();
540  return *this;
541  }
542  }
543  buf[nChar] = '\0'; // Terminate string
544 
545  setState(is_.rdstate());
546  if (is_.bad())
547  {
548  t.setBad();
549  }
550  else
551  {
552  is_.putback(c);
553 
554  if (nChar == 1 && buf[0] == '-')
555  {
556  // A single '-' is punctuation
558  }
559  else if (int64Val && readInt64(buf, int64Val))
560  {
561  t = int64Val;
562  }
563  else
564  {
565  real realVal;
566 
567  if (readReal(buf, realVal))
568  {
569  // A scalar or too big to fit as a unit
570  t = realVal;
571  }
572  else
573  {
574  t.setBad();
575  }
576  }
577  }
578 
579  return *this;
580  }
581 
582  // Should be a word (which can also be a single character)
583  default:
584  {
585  putback(c);
586  readWordToken(t);
587 
588  return *this;
589  }
590  }
591 }
592 
593 
595 {
596  c = nextValid();
597  return *this;
598 }
599 
600 
602 {
603 
604  constexpr const unsigned maxLen = 1024;
605  static char buf[maxLen];
606 
607  unsigned nChar = 0;
608  unsigned depth = 0; // Track depth of (..) nesting
609  char c;
610 
611  while
612  (
613  (nChar < maxLen)
614  && get(c)
615  && validWord(c)
616  )
617  {
618  if (c == token::BEGIN_LIST)
619  {
620  ++depth;
621  }
622  else if (c == token::END_LIST)
623  {
624  if (!depth)
625  {
626  break; // Closed ')' without an opening '(' ? ... stop
627  }
628  --depth;
629  }
630 
631  buf[nChar++] = c;
632  }
633 
634  if (nChar >= maxLen)
635  {
636  buf[errLen] = '\0';
637  ioErrorInFile(name(), lineNumber())
638  << "word '" << buf << "...'\n"
639  << " is too long (max. " <<
640  static_cast<int32>(maxLen) << " characters)";
641  fatalExit;
642 
643  return *this;
644  }
645 
646  buf[nChar] = '\0'; // Terminate string
647 
648  if (bad())
649  {
650  // Could probably skip this check
651  buf[errLen] = '\0';
652 
653  ioErrorInFile(name(), lineNumber())
654  << "Problem while reading word '" << buf << "...' after "
655  << static_cast<int32>(nChar) << " characters\n";
656  fatalExit;
657 
658  return *this;
659  }
660 
661  if (nChar == 0)
662  {
663  ioErrorInFile(name(), lineNumber())
664  << "Invalid first character found : " << c;
665  fatalExit;
666  }
667  else if (depth)
668  {
670  << "Missing " << static_cast<int32>(depth)
671  << " closing ')' while parsing" << nl << nl
672  << buf << nl << endl;
673  }
674 
675  // Finalize: content already validated, assign without additional checks.
676  str.assign(buf, nChar);
677  putback(c);
678 
679  return *this;
680 }
681 
682 
684 {
685  constexpr const unsigned maxLen = 1024;
686  static char buf[maxLen];
687 
688  char c;
689 
690  if (!get(c))
691  {
692  ioErrorInFile(name(), lineNumber())
693  << "cannot read start of string";
694  fatalExit;
695 
696  return *this;
697  }
698 
699  // Note, we could also handle single-quoted strings here (if desired)
700  if (c != token::BEGIN_STRING)
701  {
702  ioErrorInFile(name(), lineNumber())
703  << "Incorrect start of string character found : " << c;
704  fatalExit;
705 
706  return *this;
707  }
708 
709  unsigned nChar = 0;
710  bool escaped = false;
711 
712  while
713  (
714  (nChar < maxLen)
715  && get(c)
716  )
717  {
718  if (c == token::END_STRING)
719  {
720  if (escaped)
721  {
722  escaped = false;
723  --nChar; // Overwrite backslash
724  }
725  else
726  {
727  // Done reading
728  str.assign(buf, nChar);
729  return *this;
730  }
731  }
732  else if (c == token::NL)
733  {
734  if (escaped)
735  {
736  escaped = false;
737  --nChar; // Overwrite backslash
738  }
739  else
740  {
741  buf[errLen] = buf[nChar] = '\0';
742 
743  ioErrorInFile(name(), lineNumber())
744  << "found '\\n' while reading string \""
745  << buf << "...\"";
746  fatalExit;
747 
748  return *this;
749  }
750  }
751  else if (c == '\\')
752  {
753  escaped = !escaped; // toggle state (retains backslashes)
754  }
755  else
756  {
757  escaped = false;
758  }
759 
760  buf[nChar++] = c;
761  }
762 
763  if (nChar >= maxLen)
764  {
765  buf[errLen] = '\0';
766 
767  ioErrorInFile(name(), lineNumber())
768  << "string \"" << buf << "...\"\n"
769  << " is too long (max. " << static_cast<int32>(maxLen) << " characters)";
770  fatalExit;
771 
772  return *this;
773  }
774 
775  // Don't worry about a dangling backslash if string terminated prematurely
776  buf[errLen] = buf[nChar] = '\0';
777 
778  ioErrorInFile(name(), lineNumber())
779  << "Problem while reading string \"" << buf << "...\"";
780  fatalExit;
781 
782  return *this;
783 }
784 
786 {
787  is_ >> val;
788  setState(is_.rdstate());
789  return *this;
790 }
791 
793 {
794  is_ >> val;
795  setState(is_.rdstate());
796  return *this;
797 }
798 
800 {
801  is_ >> val;
802  setState(is_.rdstate());
803  return *this;
804 }
805 
807 {
808  is_ >> val;
809  setState(is_.rdstate());
810  return *this;
811 }
812 
814 {
815  is_ >> val;
816  setState(is_.rdstate());
817  return *this;
818 }
819 
821 {
822  is_ >> val;
823  setState(is_.rdstate());
824  return *this;
825 }
826 
828 {
829  is_ >> val;
830  setState(is_.rdstate());
831  return *this;
832 }
833 
835 {
836  is_ >> val;
837  setState(is_.rdstate());
838  return *this;
839 }
840 
841 
843 {
844  is_ >> val;
845  setState(is_.rdstate());
846  return *this;
847 }
848 
849 
851 {
852  lineNumber_ = 1; // Reset line number
853 
854  stdStream().clear(); // Clear the iostate error state flags
855  setGood(); // Sync local copy of iostate
856 
857  // pubseekpos() rather than seekg() so that it works with gzstream
858  stdStream().rdbuf()->pubseekpos(0, std::ios_base::in);
859 }
860 
861 
862 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
863 
864 std::ios_base::fmtflags pFlow::Istream::flags() const
865 {
866  return is_.flags();
867 }
868 
869 
870 std::ios_base::fmtflags pFlow::Istream::flags(const ios_base::fmtflags f)
871 {
872  return is_.flags(f);
873 }
874 
875 
876 // ************************************************************************* //
pFlow::token::setType
bool setType(const tokenType tokType)
Definition: tokenI.hpp:290
pFlow::real
float real
Definition: builtinTypes.hpp:46
fatalExit
#define fatalExit
Definition: error.hpp:57
pFlow::token
Definition: token.hpp:42
pFlow::Istream::nextValid
char nextValid()
Definition: Istream.cpp:53
pFlow::Istream::readWordToken
void readWordToken(token &t)
Definition: Istream.cpp:131
errLen
static constexpr const unsigned errLen
Definition: Istream.cpp:32
warningInFunction
#define warningInFunction
Definition: error.hpp:55
pFlow::token::punctuationToken
punctuationToken
Definition: token.hpp:81
pFlow::token::END_BLOCK
@ END_BLOCK
End block [isseparator].
Definition: token.hpp:94
pFlow::uint32
unsigned int uint32
Definition: builtinTypes.hpp:59
pFlow::readReal
bool readReal(const word &w, real &val)
Definition: bTypesFunctions.cpp:335
token.hpp
pFlow::word
std::string word
Definition: builtinTypes.hpp:63
pFlow::token::NL
@ NL
Newline [isspace].
Definition: token.hpp:86
pFlow::int64
long long int int64
Definition: builtinTypes.hpp:55
pFlow::validWord
bool validWord(char c)
Definition: bTypesFunctions.cpp:180
Istream.hpp
pFlow::endl
iOstream & endl(iOstream &os)
Definition: iOstream.hpp:312
pFlow::token::BEGIN_BLOCK
@ BEGIN_BLOCK
Begin block [isseparator].
Definition: token.hpp:93
pFlow::readInt64
bool readInt64(const word &w, int64 &val)
Definition: bTypesFunctions.cpp:262
pFlow::Istream::rewind
virtual void rewind()
Definition: Istream.cpp:850
pFlow::Istream::readString
virtual iIstream & readString(word &str) override
Definition: Istream.cpp:683
pFlow::Istream
Definition: Istream.hpp:38
pFlow::int16
short int int16
Definition: builtinTypes.hpp:51
pFlow::uint16
unsigned short int uint16
Definition: builtinTypes.hpp:57
pFlow::iIstream
Definition: iIstream.hpp:33
pFlow::IOstream::bad
bool bad() const
Definition: IOstream.hpp:168
pFlow::iIstream::getBack
bool getBack(token &tok)
Definition: iIstream.cpp:27
pFlow::Istream::readVariable
Istream & readVariable(word &str)
Definition: Istream.cpp:145
pFlow::int32
int int32
Definition: builtinTypes.hpp:53
pFlow::algorithms::KOKKOS::max
INLINE_FUNCTION_H Type max(const Type *first, int32 numElems)
Definition: kokkosAlgorithms.hpp:104
pFlow::Istream::read
virtual iIstream & read(token &t) override
Definition: Istream.cpp:395
pFlow::token::BEGIN_STRING
@ BEGIN_STRING
Begin string with double quote.
Definition: token.hpp:104
pFlow::token::END_LIST
@ END_LIST
End list [isseparator].
Definition: token.hpp:90
pFlow::token::DOLLAR
@ DOLLAR
Dollar - start variable.
Definition: token.hpp:97
pFlow::count
auto count(const Vector< T, Allocator > &vec, const T &val)
Definition: VectorAlgorithm.hpp:26
pFlow::Istream::get
Istream & get(char &c)
Definition: Istream.cpp:328
pFlow::token::COLON
@ COLON
Colon [isseparator].
Definition: token.hpp:95
pFlow::token::END_STATEMENT
@ END_STATEMENT
End entry [isseparator].
Definition: token.hpp:88
pFlow::IOstream::good
bool good() const
Definition: IOstream.hpp:150
pFlow::token::SUBTRACT
@ SUBTRACT
Subtract or start of negative number.
Definition: token.hpp:101
pFlow::token::BEGIN_LIST
@ BEGIN_LIST
Begin list [isseparator].
Definition: token.hpp:89
pFlow::Istream::Istream
Istream(std::istream &is, const word &streamName)
Definition: Istream.cpp:308
pFlow::token::BEGIN_SQR
@ BEGIN_SQR
Begin dimensions [isseparator].
Definition: token.hpp:91
pFlow::token::DIVIDE
@ DIVIDE
Divide [isseparator].
Definition: token.hpp:102
ioErrorInFile
#define ioErrorInFile(fileName, lineNumber)
Definition: error.hpp:49
pFlow::label
std::size_t label
Definition: builtinTypes.hpp:61
pFlow::token::COMMA
@ COMMA
Comma [isseparator].
Definition: token.hpp:96
pFlow::int8
signed char int8
Definition: builtinTypes.hpp:49
pFlow::Istream::peek
int peek()
Definition: Istream.cpp:342
pFlow::Istream::flags
virtual ios_base::fmtflags flags() const
Definition: Istream.cpp:864
pFlow::token::END_STRING
@ END_STRING
End string with double quote.
Definition: token.hpp:105
pFlow::Istream::putback
Istream & putback(const char c)
Definition: Istream.cpp:378
pFlow::Istream::getLine
Istream & getLine(word &str, char delim='\n')
Definition: Istream.cpp:348
pFlow::nl
constexpr char nl
Definition: iOstream.hpp:409
pFlow::token::END_SQR
@ END_SQR
End dimensions [isseparator].
Definition: token.hpp:92
pFlow::token::setBad
void setBad()
Definition: tokenI.hpp:658
pFlow::token::lineNumber
int32 lineNumber() const
Definition: tokenI.hpp:360
error.hpp