A library for working with phylogenetic and population genetic data.
v0.27.0
parser.hpp
Go to the documentation of this file.
1 #ifndef GENESIS_UTILS_IO_PARSER_H_
2 #define GENESIS_UTILS_IO_PARSER_H_
3 
4 /*
5  Genesis - A toolkit for working with phylogenetic data.
6  Copyright (C) 2014-2022 Lucas Czech
7 
8  This program is free software: you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation, either version 3 of the License, or
11  (at your option) any later version.
12 
13  This program is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with this program. If not, see <http://www.gnu.org/licenses/>.
20 
21  Contact:
22  Lucas Czech <lczech@carnegiescience.edu>
23  Department of Plant Biology, Carnegie Institution For Science
24  260 Panama Street, Stanford, CA 94305, USA
25 */
26 
36 
37 #include <cassert>
38 #include <cctype>
39 #include <limits>
40 #include <stdexcept>
41 
42 namespace genesis {
43 namespace utils {
44 
45 // =================================================================================================
46 // Integer
47 // =================================================================================================
48 
56 template<class T>
58 {
59  T x = 0;
60 
61  if( ! source || ! utils::is_digit( *source ) ) {
62  throw std::runtime_error(
63  "Expecting digit in " + source.source_name() + " at " + source.at() + "."
64  );
65  }
66 
67  while( source && utils::is_digit( *source )) {
68  T y = *source - '0';
69 
70  if( x > ( std::numeric_limits<T>::max() - y ) / 10 ) {
71  throw std::overflow_error(
72  "Numerical overflow in " + source.source_name() + " at " + source.at() + "."
73  );
74  }
75 
76  x = 10 * x + y;
77  ++source;
78  }
79  return x;
80 }
81 
91 template<class T>
93 {
94  if( !source ) {
95  throw std::runtime_error(
96  "Expecting number in " + source.source_name() + " at " + source.at() + "."
97  );
98  }
99 
100  if( *source == '-' ) {
101  ++source;
102 
103  if( ! source || ! utils::is_digit( *source ) ) {
104  throw std::runtime_error(
105  "Expecting digit in " + source.source_name() + " at " + source.at() + "."
106  );
107  }
108 
109  T x = 0;
110  while( source && utils::is_digit( *source )) {
111  T y = *source - '0';
112 
113  if( x < ( std::numeric_limits<T>::min() + y ) / 10 ) {
114  throw std::underflow_error(
115  "Numerical underflow in " + source.source_name() + " at " + source.at() + "."
116  );
117  }
118 
119  x = 10 * x - y;
120  ++source;
121  }
122  return x;
123  }
124 
125  if( *source == '+' ) {
126  ++source;
127  }
128  return parse_unsigned_integer<T>(source);
129 }
130 
134 template<class T>
136 {
137  return parse_signed_integer<T>(source);
138 }
139 
140 // =================================================================================================
141 // Float
142 // =================================================================================================
143 
155 template<class T>
157 {
158  T x = 0.0;
159 
160  if( !source ) {
161  throw std::runtime_error(
162  "Expecting float number in " + source.source_name() + " at " + source.at() + "."
163  );
164  }
165 
166  // Sign
167  bool is_neg = false;
168  if( *source == '-' ){
169  is_neg = true;
170  ++source;
171  } else if( *source == '+' ) {
172  ++source;
173  }
174 
175  // Integer Part
176  bool found_mantisse = false;
177  while( source && utils::is_digit( *source )) {
178  int y = *source - '0';
179  x *= 10;
180  x += y;
181  ++source;
182  found_mantisse = true;
183  }
184 
185  // Decimal part
186  if( source && *source == '.' ) {
187  ++source;
188 
189  if( ! source || ! utils::is_digit( *source ) ) {
190  throw std::runtime_error(
191  "Invalid float number in " + source.source_name() + " at " + source.at() + "."
192  );
193  }
194 
195  T pos = 1.0;
196  while( source && utils::is_digit( *source )) {
197  pos /= 10.0;
198  int y = *source - '0';
199  x += y * pos;
200  ++source;
201  found_mantisse = true;
202  }
203  }
204 
205  // We need to have some digits before the exponential part.
206  if( ! found_mantisse ) {
207  throw std::runtime_error(
208  "Invalid float number in " + source.source_name() + " at " + source.at() + "."
209  );
210  }
211 
212  // Exponential part
213  if( source && utils::to_lower(*source) == 'e' ) {
214  ++source;
215 
216  // Read the exp. If there are no digits, this throws.
217  int e = parse_signed_integer<int>( source );
218 
219  if( e != 0 ) {
220  T base;
221  if( e < 0 ) {
222  base = 0.1;
223  e = -e;
224  } else {
225  base = 10;
226  }
227 
228  while( e != 1 ) {
229  if( ( e & 1 ) == 0 ) {
230  base = base * base;
231  e >>= 1;
232  } else {
233  x *= base;
234  --e;
235  }
236  }
237  x *= base;
238  }
239  }
240 
241  // Sign
242  if (is_neg) {
243  x = -x;
244  }
245 
246  return x;
247 }
248 
249 // =================================================================================================
250 // General Number String
251 // =================================================================================================
252 
263 std::string parse_number_string(
264  utils::InputStream& source
265 );
266 
267 // =================================================================================================
268 // String
269 // =================================================================================================
270 
296 std::string parse_quoted_string(
297  utils::InputStream& source,
298  bool use_escapes = true,
299  bool use_twin_quotes = false,
300  bool include_qmarks = false
301 );
302 
303 } // namespace utils
304 } // namespace genesis
305 
306 #endif // include guard
genesis::utils::InputStream::at
std::string at() const
Return a textual representation of the current input position in the form "line:column".
Definition: input_stream.hpp:481
genesis::utils::InputStream
Stream interface for reading data from an InputSource, that keeps track of line and column counters.
Definition: input_stream.hpp:81
genesis::utils::InputStream::source_name
std::string source_name() const
Get the input source name where this stream reads from.
Definition: input_stream.hpp:522
genesis::utils::parse_integer
T parse_integer(utils::InputStream &source)
Alias for parse_signed_integer().
Definition: parser.hpp:135
genesis::utils::parse_float
T parse_float(utils::InputStream &source)
Read a floating point number from a stream and return it.
Definition: parser.hpp:156
input_stream.hpp
genesis
Container namespace for all symbols of genesis in order to keep them separate when used as a library.
Definition: placement/formats/edge_color.cpp:42
genesis::utils::parse_unsigned_integer
T parse_unsigned_integer(utils::InputStream &source)
Read an unsigned integer from a stream and return it.
Definition: parser.hpp:57
genesis::utils::is_digit
constexpr bool is_digit(char c) noexcept
Return whether a char is a digit (0-9), ASCII-only.
Definition: char.hpp:95
char.hpp
genesis::utils::parse_signed_integer
T parse_signed_integer(utils::InputStream &source)
Read a signed integer from a stream and return it.
Definition: parser.hpp:92
genesis::utils::to_lower
constexpr char to_lower(char c) noexcept
Return the lower case version of a letter, ASCII-only.
Definition: char.hpp:221
genesis::utils::parse_quoted_string
std::string parse_quoted_string(utils::InputStream &source, bool use_escapes, bool use_twin_quotes, bool include_qmarks)
Read a string in quotation marks from a stream and return it.
Definition: parser.cpp:116
genesis::utils::parse_number_string
std::string parse_number_string(utils::InputStream &source)
Read a general number string from an input stream.
Definition: parser.cpp:48