Scarab  v2.4.2
Project 8 C++ Utility Library
param_helpers.cc
Go to the documentation of this file.
1 /*
2  * param_helpers.cc
3  *
4  * Created on: Jan 10, 2019
5  * Author: N.S. Oblath
6  */
7 
8 #define SCARAB_API_EXPORTS
9 
10 #include "param_helpers.hh"
11 
12 #include "logger.hh"
13 
14 LOGGER( plog, "param_helpers" );
15 
16 namespace scarab
17 {
18  param_ptr_t simple_parser::parse_address( const std::string& an_addr, param_ptr_t a_value )
19  {
20  // this method can throw scarab::error
21 
22  if( an_addr.empty() )
23  {
24  throw error() << "No address for keyword argument with value <" << *a_value << ">";
25  }
26 
27  std::string::size_type t_div_pos = an_addr.find( f_node_separator );
28  std::string t_top_addr = an_addr.substr( 0, t_div_pos );
29 
30  param_ptr_t t_new_param = new_param_from_addr( t_top_addr );
31 
32  if( t_new_param->is_node() )
33  {
34  // only nodes are allowed at the top level
35  std::string t_next_addr = t_div_pos == an_addr.npos ? "" : an_addr.substr( t_div_pos+1 );
36  add_next( *t_new_param, t_top_addr, t_next_addr, std::move(a_value) ); // this can throw scarab::error
37  return t_new_param;
38  }
39 
40  throw error() << "Top param type must be a node; this address is invalid: <" << an_addr << ">";
41  }
42 
43  void simple_parser::add_next( param& a_parent, const std::string& an_addr_in_parent, const std::string& a_next_addr, param_ptr_t a_value )
44  {
45  // this method can throw scarab::error
46 
47  param_ptr_t t_child;
48 
49  // check if the next address is empty;
50  // if so, parse the value
51  // if not, parse the child
52  if( a_next_addr.empty() )
53  {
54  // the child is a param_value or param
55  t_child = std::move(a_value);
56  }
57  else
58  {
59  // the child is a param_array or param_node
60  std::string::size_type t_div_pos = a_next_addr.find( f_node_separator );
61  std::string t_top_addr = a_next_addr.substr( 0, t_div_pos );
62  t_child = new_param_from_addr( t_top_addr );
63  std::string t_next_addr = t_div_pos == a_next_addr.npos ? "" : a_next_addr.substr( t_div_pos+1 );
64  add_next( *t_child, t_top_addr, t_next_addr, std::move(a_value) ); // this can throw scarab::error
65  }
66 
67  // add the child as appropriate for the parent type
68  if( a_parent.is_array() )
69  {
70  unsigned index = std::stoul( an_addr_in_parent );
71  param_array& t_parent_array = a_parent.as_array();
72  if( index > t_parent_array.size() ) t_parent_array.resize( index+1 );
73  if( t_child )
74  {
75  a_parent.as_array().assign( std::stoul( an_addr_in_parent ), std::move(t_child) );
76  }
77  }
78  else if( a_parent.is_node() )
79  {
80  if( t_child )
81  {
82  a_parent.as_node().replace( an_addr_in_parent, std::move(t_child) );
83  }
84  }
85  else
86  {
87  throw error() << "Invalid param type: only nodes and arrays accepted";
88  }
89 
90  return;
91  }
92 
93  param_ptr_t simple_parser::new_param_from_addr( const std::string& an_addr )
94  {
95  if( an_addr.empty() ) return std::unique_ptr< param_value >( ( new param_value() ) );
96 
97  // check whether all of the characters in the address are digits
98  // if any are not, then the address cannot be an integer array index, so return a param_node
99  for( const char& ch : an_addr )
100  {
101  if( ! std::isdigit( ch ) ) return std::unique_ptr< param_node >( new param_node() );
102  }
103  // if we're here, then the address is an integer, so return a param_array
104  return std::unique_ptr< param_array >( new param_array() );
105  }
106 
107  param_ptr_t simple_parser::parse_value( const std::string& a_value )
108  {
109  // we've found the value; now check if it's a number or a string
110  if( a_value.empty() )
111  {
112  LTRACE( plog, "Parsed value as NULL" );
113  return std::unique_ptr< param >( new param() );
114  }
115  // if "true" or "false", then bool
116  else if( a_value == "true" )
117  {
118  LTRACE( plog, "Parsing value (" << a_value << ") as bool(true)" );
119  return std::unique_ptr< param_value >( new param_value( true ) );
120  }
121  else if( a_value == "false" )
122  {
123  LTRACE( plog, "Parsing value (" << a_value << ") as bool(false)" );
124  return std::unique_ptr< param_value >( new param_value( false ) );
125  }
126  else
127  {
128  // To test if the string is numeric:
129  // 1. if it has 2 decimal points, it's not numeric (IP addresses, for example, would pass the second test)
130  // 2. double is the most general form of number, so if it fails that conversion, it's not numeric.
131  double t_double;
132  std::stringstream t_conv_double( a_value );
133  if( a_value.find( '.' ) == a_value.rfind( '.' ) &&
134  a_value.find( '-' ) == a_value.rfind( '-' ) &&
135  ! (t_conv_double >> t_double).fail() )
136  {
137  // now we know the value is numeric
138  if( a_value.find( '.' ) != std::string::npos ||
139  a_value.find( 'e' ) != std::string::npos ||
140  a_value.find( 'E' ) != std::string::npos )
141  {
142  // value is a floating-point number, since it has a decimal point
143  LTRACE( plog, "Parsing value (" << a_value << ") as double(" << t_double << ")" );
144  return std::unique_ptr< param_value >( new param_value( t_double ) );
145  }
146  else if( a_value[ 0 ] == '-' )
147  {
148  // value is a signed integer if it's negative
149  LTRACE( plog, "Parsing value (" << a_value << ") as int(" << (int64_t)t_double << ")" );
150  return std::unique_ptr< param_value >( new param_value( (int64_t)t_double ) );
151  }
152  else
153  {
154  // value is assumed to be unsigned if it's positive
155  LTRACE( plog, "Parsing value (" << a_value << ") as uint(" << (uint64_t)t_double << ")" );
156  return std::unique_ptr< param_value >( new param_value( (uint64_t)t_double ) );
157  }
158  }
159  else
160  {
161  // value is not numeric; treat as a string
162  LTRACE( plog, "Parsing value (" << a_value << ") as a string" );
163  return std::unique_ptr< param_value >( new param_value( a_value ) );
164  }
165  }
166  }
167 
168 }
169 
170 
171 
void replace(const std::string &a_name, const param &a_value)
Creates a copy of a_value; overwrites if the key exits.
Definition: param_node.hh:274
LOGGER(plog, "param_helpers")
static void add_next(param &a_parent, const std::string &an_addr_in_parent, const std::string &a_next_addr, param_ptr_t a_value=param_ptr_t())
static param_ptr_t parse_address(const std::string &an_addr, param_ptr_t a_value=param_ptr_t())
Converts an address into a nested param structure, and optionally attaches a value.
static const char f_node_separator
#define LTRACE(...)
Definition: logger.hh:359
Contains the logger class and macros, based on Kasper&#39;s KLogger class.
virtual bool is_array() const
virtual bool is_node() const
std::unique_ptr< param > param_ptr_t
Definition: param_base.hh:23
param_node & as_node()
static param_ptr_t parse_value(const std::string &a_value)
Attempts to determine whether a value encoded as a string is an int, and unsigned int...
void assign(unsigned a_index, const param &a_value)
Definition: param_array.hh:209
param_array & as_array()
unsigned size() const
Definition: param_array.hh:162
static param_ptr_t new_param_from_addr(const std::string &an_addr)
void resize(unsigned a_size)
Definition: param_array.cc:79