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