summaryrefslogblamecommitdiffstatshomepage
path: root/src/lib/util/opresolv.cpp
blob: 05b8a5e8898fd8fb5e3a27c9c67ff0f676c52adb (plain) (tree)
1
2
3
4
5
6
7
8
9
10

                                 

                                                                             
                




                                                                             


                   
                   

                 
                    
                     
 
 
                
 
                                                                            
                     


                                                                            
        

                                                    
                                                               
 
                                    
                                                  
 

                                                                                        
                                                                                        
         


                                               
 

                                                                                 
                 


                                                                 
 
                                                                      
                                                                                                                                                 
                         

                                                 
                         
 

                                                                                     

                 
 
 
 
                                                    
        



                                                    


 
                                                    
                           
                                                    
 
                                                                                                                    

                      
                                                      
                                   


 
                                                    

                                                     
                                                    
 
                                                                           
 



                                                                                                               
 

                                                                                       
 

                                                                                       
                 
                                                               
                 
         
 
 

                                                    
        

                                                    
                                                                
 


                                  
                                                                                    



                                      


 
                                                    
        
                                                    
 
                                                                                
 








                                                                                             
 
 
                                                    
              
                                                    
 





                                                
 
 
                                                    
                 






                                                                  


 
                                                    
                
                                                    
 
                                                                                                             
 

                               


 
                                                    
                
                                                    
 
                                                                         
 
                    
         








                                                                                          
         
                       


 
                                                    
               
                                                    
 

                                                                       
 


 
                                                    
                               
                                                    
 
                                                                                                                     
 

                                   


 
                                                    
                              
                                                    
 
                                                                             
 


























































































                                                                                                             

                                                                                                             








                                                                                          


 
                                                    
                        
                                                    
 
                                                              
 
                                          


 
                                                    
                
                                                    
 
                                                          
 

                                                       


 
                                                    
                    
                                                    
 
                                               
 

                                                           
 
 
                                                    
                    










                                                                                       
 


                                        


 
                                                    
                         
                                                    
 
                                                     
 


                                                      
 
 
                                                    
                          
                                                    
 



                                                      


 
                                                    
                     
                                                    
 
                                           
 


                                                       
 











                                                      
         

                                             
         


                                      

 

                                                    
                      

                                                    
                                            
 






















                                                       

 

                                                    
                         

                                                    
                                                                                                             
 



                                                                                 
 
 
 
                   
// license:BSD-3-Clause
// copyright-holders:Nathan Woods
/****************************************************************************

    opresolv.cpp

    Extensible ranged option resolution handling

****************************************************************************/

#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include "pool.h"
#include "corestr.h"
#include "opresolv.h"


namespace util {

/***************************************************************************
    option_resolution
***************************************************************************/

// -------------------------------------------------
//  ctor
// -------------------------------------------------

option_resolution::option_resolution(const option_guide &guide)
{
	// reserve space for entries
	m_entries.reserve(guide.entries().size());

	// initialize each of the entries; can't use foreach because we need to scan the
	// ENUM_VALUE entries
	for (auto iter = guide.entries().begin(); iter != guide.entries().end(); iter++)
	{
		// create the entry
		m_entries.emplace_back(*iter);
		entry &entry(m_entries.back());

		// if this is an enumeration, identify the values
		if (iter->type() == option_guide::entry::option_type::ENUM_BEGIN)
		{
			// enum values begin after the ENUM_BEGIN
			auto enum_value_begin = iter + 1;
			auto enum_value_end = enum_value_begin;

			// and identify all entries of type ENUM_VALUE
			while (enum_value_end != guide.entries().end() && enum_value_end->type() == option_guide::entry::option_type::ENUM_VALUE)
			{
				iter++;
				enum_value_end++;
			}

			// set the range
			entry.set_enum_value_range(enum_value_begin, enum_value_end);
		}
	}
}


// -------------------------------------------------
//  dtor
// -------------------------------------------------

option_resolution::~option_resolution()
{
}


// -------------------------------------------------
//  lookup_in_specification
// -------------------------------------------------

const char *option_resolution::lookup_in_specification(const char *specification, const option_guide::entry &option)
{
	const char *s;
	s = strchr(specification, option.parameter());
	return s ? s + 1 : nullptr;
}


// -------------------------------------------------
//  set_specification - sets the option specification
//  and mutates values accordingly
// -------------------------------------------------

void option_resolution::set_specification(const std::string &specification)
{
	for (auto &entry : m_entries)
	{
		// find this entry's info in the specification
		auto entry_specification = lookup_in_specification(specification.c_str(), entry.m_guide_entry);

		// parse this entry's specification (e.g. - set up ranges and defaults)
		entry.parse_specification(entry_specification);

		// is this a range typed that needs to be defaulted for the first time?
		if (entry.is_pertinent() && entry.is_ranged() && entry.value().empty())
		{
			entry.set_value(entry.default_value());
		}
	}
}


// -------------------------------------------------
//  find
// -------------------------------------------------

option_resolution::entry *option_resolution::find(int parameter)
{
	auto iter = std::find_if(
		m_entries.begin(),
		m_entries.end(),
		[parameter](const entry &e) { return e.parameter() == parameter; });

	return iter != m_entries.end()
		? &*iter
		: nullptr;
}


// -------------------------------------------------
//  find
// -------------------------------------------------

option_resolution::entry *option_resolution::find(const std::string &identifier)
{
	auto iter = std::find_if(
		m_entries.begin(),
		m_entries.end(),
		[&](const entry &e) { return !strcmp(e.identifier(), identifier.c_str()); });

	return iter != m_entries.end()
		? &*iter
		: nullptr;
}


// -------------------------------------------------
//  lookup_int
// -------------------------------------------------

int option_resolution::lookup_int(int parameter)
{
	auto entry = find(parameter);
	assert(entry != nullptr);
	return entry->value_int();
}


// -------------------------------------------------
//  lookup_string
// -------------------------------------------------

const std::string &option_resolution::lookup_string(int parameter)
{
	auto entry = find(parameter);
	assert(entry != nullptr);
	return entry->value();
}


// -------------------------------------------------
//  error_string
// -------------------------------------------------

option_resolution::error option_resolution::get_default(const char *specification, int option_char, int *val)
{
	// NYI
	return error::INTERNAL;
}


// -------------------------------------------------
//  error_string
// -------------------------------------------------

const char *option_resolution::error_string(option_resolution::error err)
{
	switch (err)
	{
	case error::SUCCESS:                return "The operation completed successfully";
	case error::OUTOFMEMORY:            return "Out of memory";
	case error::PARAMOUTOFRANGE:        return "Parameter out of range";
	case error::PARAMNOTSPECIFIED:      return "Parameter not specified";
	case error::PARAMNOTFOUND:          return "Unknown parameter";
	case error::PARAMALREADYSPECIFIED:  return "Parameter specified multiple times";
	case error::BADPARAM:               return "Invalid parameter";
	case error::SYNTAX:                 return "Syntax error";
	case error::INTERNAL:               return "Internal error";
	}
	return nullptr;
}


// -------------------------------------------------
//  entry::ctor
// -------------------------------------------------

option_resolution::entry::entry(const option_guide::entry &guide_entry)
	: m_guide_entry(guide_entry), m_is_pertinent(false)
{
}


// -------------------------------------------------
//  entry::set_enum_value_range
// -------------------------------------------------

void option_resolution::entry::set_enum_value_range(const option_guide::entry *begin, const option_guide::entry *end)
{
	m_enum_value_begin = begin;
	m_enum_value_end = end;
}


// -------------------------------------------------
//  entry::parse_specification
// -------------------------------------------------

void option_resolution::entry::parse_specification(const char *specification)
{
	// clear some items out
	m_ranges.clear();
	m_default_value.clear();

	// is this even pertinent?
	m_is_pertinent = (specification != nullptr) && (specification[0] != '\0');
	if (m_is_pertinent)
	{
		int value = 0;
		bool in_range = false;
		bool in_default = false;
		bool default_specified = false;
		bool half_range = false;
		size_t pos = 0;

		m_ranges.emplace_back();

		while (specification[pos] && !isalpha(specification[pos]))
		{
			if (specification[pos] == '-')
			{
				// range specifier
				assert(!in_range && !in_default && "Syntax error in specification");

				in_range = true;
				pos++;

				m_ranges.back().max = -1;
				if (!half_range)
				{
					m_ranges.back().min = -1;
					half_range = true;
				}
			}
			else if (specification[pos] == '[')
			{
				// begin default value
				assert(!in_default && !default_specified && "Syntax error in specification");

				in_default = true;
				pos++;
			}
			else if (specification[pos] == ']')
			{
				// end default value
				assert(in_default && "Syntax error in specification");

				in_default = false;
				default_specified = true;
				pos++;

				m_default_value = numeric_value(value);
			}
			else if (specification[pos] == '/')
			{
				// value separator
				assert(!in_default && !in_range && "Syntax error in specification");
				pos++;

				// if we are spitting out ranges, complete the range
				if (half_range)
				{
					m_ranges.emplace_back();
					half_range = false;
				}
			}
			else if (specification[pos] == ';')
			{
				// basic separator
				pos++;
			}
			else if (isdigit(specification[pos]))
			{
				// numeric value */
				value = 0;
				do
				{
					value *= 10;
					value += specification[pos++] - '0';
				} while (isdigit(specification[pos]));

				if (!half_range)
				{
					m_ranges.back().min = value;
					half_range = true;
				}
				m_ranges.back().max = value;
				in_range = false;
			}
			else
			{
				// invalid character - abort because we cannot recover from this syntax error
				throw false;
			}
		}

		// we can't have zero length guidelines strings
		assert(pos > 0);

		// appease compiler for scenarios where assert() has been preprocessed out
		(void)(in_range && in_default && default_specified);
	}
}


// -------------------------------------------------
//  entry::numeric_value
// -------------------------------------------------

std::string option_resolution::entry::numeric_value(int value)
{
	return string_format("%d", value);
}


// -------------------------------------------------
//  entry::value
// -------------------------------------------------

const std::string &option_resolution::entry::value() const
{
	static std::string empty_string;
	return is_pertinent() ? m_value : empty_string;
}


// -------------------------------------------------
//  entry::value_int
// -------------------------------------------------

int option_resolution::entry::value_int() const
{
	return is_pertinent() ? atoi(m_value.c_str()) : -1;
}


// -------------------------------------------------
//  entry::set_value
// -------------------------------------------------

bool option_resolution::entry::set_value(const std::string &value)
{
	// reject the value if this isn't pertinent
	if (!is_pertinent())
		return false;

	// if this is ranged, check against the ranges
	if (is_ranged() && find_in_ranges(std::atoi(value.c_str())) == m_ranges.cend())
		return false;

	// looks good!  change the value
	m_value = value;
	return true;
}


// -------------------------------------------------
//  entry::can_bump_lower
// -------------------------------------------------

bool option_resolution::entry::can_bump_lower() const
{
	return !m_ranges.empty()
		&& value_int() > m_ranges.front().min;
}


// -------------------------------------------------
//  entry::can_bump_higher
// -------------------------------------------------

bool option_resolution::entry::can_bump_higher() const
{
	return !m_ranges.empty()
		&& value_int() < m_ranges.back().max;
}


// -------------------------------------------------
//  entry::bump_lower
// -------------------------------------------------

bool option_resolution::entry::bump_lower()
{
	auto old_value = value_int();
	auto current_range = find_in_ranges(old_value);
	assert(current_range != m_ranges.end());

	int new_value;
	if (old_value > current_range->min)
	{
		// decrement within current range
		new_value = old_value - 1;
	}
	else if (current_range != m_ranges.begin())
	{
		// go to the top of the previous range
		new_value = (current_range - 1)->max;
	}
	else
	{
		// at the minimum; don't bump
		new_value = old_value;
	}

	set_value(new_value);
	return new_value != old_value;
}


// -------------------------------------------------
//  entry::bump_higher
// -------------------------------------------------

bool option_resolution::entry::bump_higher()
{
	auto old_value = value_int();
	auto current_range = find_in_ranges(old_value);
	assert(current_range != m_ranges.end());

	int new_value;
	if (old_value < current_range->max)
	{
		// increment within current range
		new_value = old_value + 1;
	}
	else if (current_range != (m_ranges.end() - 1))
	{
		// go to the bottom of the next range
		new_value = (current_range + 1)->min;
	}
	else
	{
		// at the minimum; don't bump
		new_value = old_value;
	}

	set_value(new_value);
	return new_value != old_value;
}


// -------------------------------------------------
//  entry::find_in_ranges
// -------------------------------------------------

option_resolution::entry::rangelist::const_iterator option_resolution::entry::find_in_ranges(int value) const
{
	return std::find_if(
		m_ranges.begin(),
		m_ranges.end(),
		[&](const auto &r) { return r.min <= value && value <= r.max; });
}


} // namespace util