Hazelnupp.cpp
Go to the documentation of this file.
#include "Hazelnupp.h"
#include "VoidValue.h"
#include "IntValue.h"
#include "FloatValue.h"
#include "StringValue.h"
#include "ListValue.h"
#include "HazelnuppException.h"
#include "StringTools.h"
#include <iostream>
#include <cstdlib>
using namespace Hazelnp;
Hazelnupp::Hazelnupp()
{
return;
}
Hazelnupp::Hazelnupp(const int argc, const char* const* argv)
{
Parse(argc, argv);
return;
}
Hazelnupp::~Hazelnupp()
{
for (auto& it : parameters)
delete it.second;
parameters.clear();
return;
}
void Hazelnupp::Parse(const int argc, const char* const* argv)
{
try
{
// Populate raw arguments
PopulateRawArgs(argc, argv);
// Expand abbreviations
ExpandAbbreviations();
executableName = std::string(rawArgs[0]);
std::size_t i = 1;
while (i < rawArgs.size())
{
if ((rawArgs[i].length() > 2) && (rawArgs[i].substr(0, 2) == "--"))
{
Parameter* param = nullptr;
i = ParseNextParameter(i, param);
parameters.insert(std::pair<std::string, Parameter*>(param->Key(), param));
}
else
i++;
}
// Apply constraints such as default values, and required parameters.
// Types have already been enforced.
ApplyConstraints();
}
catch (const HazelnuppConstraintTypeMissmatch& hctm)
{
if (crashOnFail)
{
std::cerr << "Fatal error: Command-line parameter value-type mismatch at \"" << hctm.What() << "\"!";
quick_exit(-1009);
}
else
throw hctm; // yeet
}
catch (const HazelnuppConstraintMissingValue& hctm)
{
if (crashOnFail)
{
std::cerr << "Fatal error: Missing required command-line parameter \"" << hctm.What() << "\"!";
quick_exit(-1010);
}
else
throw hctm; // yeet
}
return;
}
std::size_t Hazelnupp::ParseNextParameter(const std::size_t parIndex, Parameter*& out_Par)
{
std::size_t i = parIndex;
const std::string key = rawArgs[parIndex];
std::vector<std::string> values;
// Get values
for (i++; i < rawArgs.size(); i++)
// If not another parameter
if ((rawArgs[i].length() < 2) || (rawArgs[i].substr(0, 2) != "--"))
values.emplace_back(rawArgs[i]);
else
{
break;
}
// Fetch constraint info
const ParamConstraint* pcn = GetConstraintForKey(key);
Value* parsedVal = ParseValue(values, pcn);
if (parsedVal != nullptr)
{
out_Par = new Parameter(key, parsedVal);
delete parsedVal;
parsedVal = nullptr;
}
else
throw std::runtime_error("Unable to parse parameter!");
return i;
}
void Hazelnupp::PopulateRawArgs(const int argc, const char* const* argv)
{
rawArgs.clear();
rawArgs.reserve(argc);
for (int i = 0; i < argc; i++)
rawArgs.emplace_back(std::string(argv[i]));
return;
}
void Hazelnupp::ExpandAbbreviations()
{
// Abort if no abbreviations
if (abbreviations.size() == 0)
return;
for (std::string& arg : rawArgs)
{
// Is arg registered as an abbreviation?
auto abbr = abbreviations.find(arg);
if (abbr != abbreviations.end())
{
// Yes: replace arg with the long form
arg = abbr->second;
}
}
return;
}
bool Hazelnupp::HasParam(const std::string& key) const
{
return parameters.find(key) != parameters.end();
}
Value* Hazelnupp::ParseValue(const std::vector<std::string>& values, const ParamConstraint* constraint)
{
// Constraint values
const bool constrainType = (constraint != nullptr) && (constraint->constrainType);
// Void-type
if (values.size() == 0)
{
// Is a list forced via a constraint? If yes, return an empty list
if ((constrainType) &&
(constraint->wantedType == DATA_TYPE::LIST))
return new ListValue();
return new VoidValue;
}
// Force void type by constraint
if ((constrainType) &&
(constraint->wantedType == DATA_TYPE::VOID))
{
return new VoidValue;
}
// List-type
else if (values.size() > 1)
{
// Should the type be something other than list?
if ((constrainType) &&
(constraint->wantedType != DATA_TYPE::LIST))
{
throw HazelnuppConstraintTypeMissmatch(values[0] + " " + values[1]);
}
ListValue* newList = new ListValue();
for (const std::string& val : values)
{
Value* tmp = ParseValue({ val });
newList->AddValue(tmp);
delete tmp;
}
return newList;
}
// Now we're only dealing with a single value
const std::string& val = values[0];
// String
if (!StringTools::IsNumeric(val, true))
{
// Is the type not supposed to be a string?
// void and list are already sorted out
if ((constrainType) &&
(constraint->wantedType != DATA_TYPE::STRING))
{
// We can only force a list-value from here
if (constraint->wantedType == DATA_TYPE::LIST)
{
ListValue* list = new ListValue();
Value* tmp = ParseValue({ val });
list->AddValue(tmp);
delete tmp;
tmp = nullptr;
return list;
}
// Else it not possible to convert to a numeric
else
throw HazelnuppConstraintTypeMissmatch(val);
}
return new StringValue(val);
}
// In this case we have a numeric value.
// We should still produce a string if requested
if ((constrainType) &&
(constraint->wantedType == DATA_TYPE::STRING))
return new StringValue(val);
// Numeric
bool isInt;
long double num;
if (StringTools::ParseNumber(val, isInt, num))
{
// Is the type constrained?
// (only int and float left)
if (constrainType)
{
// Must it be an integer?
if (constraint->wantedType == DATA_TYPE::INT)
return new IntValue((long long int)num);
// Must it be a floating point?
else if (constraint->wantedType == DATA_TYPE::FLOAT)
return new FloatValue(num);
// Else it must be a List
else
{
ListValue* list = new ListValue();
Value* tmp = ParseValue({ val });
list->AddValue(tmp);
delete tmp;
tmp = nullptr;
return list;
}
}
// Type is not constrained
else
{
// Integer
if (isInt)
return new IntValue((long long int)num);
// Double
return new FloatValue(num);
}
}
// Failed
return nullptr;
}
bool Hazelnupp::GetCrashOnFail() const
{
return crashOnFail;
}
void Hazelnupp::ApplyConstraints()
{
// Enforce required parameters / default values
for (const auto& pc : constraints)
// Parameter in question is not supplied
if (!HasParam(pc.second.key))
{
// Do we have a default value?
if (pc.second.defaultValue.size() > 0)
{
// Then create it now, by its default value
Value* tmp = ParseValue(pc.second.defaultValue, &pc.second);
parameters.insert(std::pair<std::string, Parameter*>(
pc.second.key,
new Parameter(pc.second.key, tmp)
));
delete tmp;
tmp = nullptr;
}
// So we do not have a default value...
else
{
// Is it important to have the missing parameter?
if (pc.second.required)
// Throw an error message then
throw HazelnuppConstraintMissingValue(pc.second.key);
}
}
return;
}
const std::string& Hazelnupp::GetExecutableName() const
{
return executableName;
}
const Value& Hazelnupp::operator[](const std::string& key) const
{
// Throw exception if param is unknown
if (!HasParam(key))
throw HazelnuppInvalidKeyException();
return *parameters.find(key)->second->GetValue();
}
void Hazelnupp::RegisterAbbreviation(const std::string& abbrev, const std::string& target)
{
abbreviations.insert(std::pair<std::string, std::string>(abbrev, target));
return;
}
const std::string& Hazelnupp::GetAbbreviation(const std::string& abbrev) const
{
return abbreviations.find(abbrev)->second;
}
bool Hazelnupp::HasAbbreviation(const std::string& abbrev) const
{
return abbreviations.find(abbrev) != abbreviations.end();
}
void Hazelnupp::ClearAbbreviations()
{
abbreviations.clear();
return;
}
void Hazelnupp::RegisterConstraints(const std::vector<ParamConstraint>& constraints)
{
for (const ParamConstraint& pc : constraints)
{
// Does this constraint already exist?
const auto constraint = this->constraints.find(pc.key);
// If yes, replace it.
if (constraint != this->constraints.end())
constraint->second = pc;
// Else, create a new pair
else
this->constraints.insert(std::pair<std::string, ParamConstraint>(
pc.key,
pc
));
}
return;
}
void Hazelnupp::ClearConstraints()
{
constraints.clear();
return;
}
void Hazelnupp::SetCrashOnFail(bool crashOnFail)
{
this->crashOnFail = crashOnFail;
return;
}
const ParamConstraint* Hazelnupp::GetConstraintForKey(const std::string& key) const
{
const auto constraint = constraints.find(key);
if (constraint == constraints.end())
return nullptr;
return &constraint->second;
}
HazelnuppException.h
Hazelnp
Definition: DataType.h:3
Hazelnp::IntValue
Specializations for integer values (uses long long int)
Definition: IntValue.h:8
Hazelnp::StringTools::IsNumeric
static bool IsNumeric(const std::string &str, const bool allowDecimalPoint=false)
Will return true if the given string consists only of digits (including signage)
Definition: StringTools.cpp:56
Hazelnp::StringTools::ParseNumber
static bool ParseNumber(const std::string &str, bool &out_isInt, long double &out_number)
Will convert the number in str to a number.
Definition: StringTools.cpp:82
Hazelnp::Hazelnupp::ClearConstraints
void ClearConstraints()
Will delete all constraints.
Definition: Hazelnupp.cpp:375
Hazelnp::HazelnuppInvalidKeyException
Gets thrown when an non-existent key gets dereferenced.
Definition: HazelnuppException.h:26
Hazelnp::Hazelnupp::GetExecutableName
const std::string & GetExecutableName() const
Will return argv[0], the name of the executable.
Definition: Hazelnupp.cpp:318
Hazelnp::Hazelnupp::~Hazelnupp
~Hazelnupp()
Definition: Hazelnupp.cpp:25
Hazelnp::Hazelnupp::operator[]
const Value & operator[](const std::string &key) const
Will return the value given a key.
Definition: Hazelnupp.cpp:323
StringValue.h
Hazelnp::HazelnuppException::What
const std::string & What() const
Will return an error message.
Definition: HazelnuppException.h:15
IntValue.h
Hazelnp::Hazelnupp::Hazelnupp
Hazelnupp()
Definition: Hazelnupp.cpp:14
Hazelnp::Hazelnupp::ClearAbbreviations
void ClearAbbreviations()
Will delete all abbreviations.
Definition: Hazelnupp.cpp:348
Hazelnp::DATA_TYPE::VOID
@ VOID
Hazelnp::FloatValue
Specializations for floating point values (uses long double)
Definition: FloatValue.h:9
Hazelnp::Value
Abstract class for values.
Definition: Value.h:10
Hazelnp::Hazelnupp::GetCrashOnFail
bool GetCrashOnFail() const
Gets whether the application crashes on an exception whilst parsing, and prints to stderr.
Definition: Hazelnupp.cpp:279
Hazelnp::DATA_TYPE::LIST
@ LIST
Hazelnp::Parameter
Definition: Parameter.h:8
Hazelnp::ParamConstraint::wantedType
DATA_TYPE wantedType
Constrain the parameter to this value. Requires constrainType to be set to true.
Definition: ParamConstraint.h:57
Hazelnp::ParamConstraint
Definition: ParamConstraint.h:8
Hazelnp::Hazelnupp::HasParam
bool HasParam(const std::string &key) const
Will check wether a parameter exists given a key, or not.
Definition: Hazelnupp.cpp:153
Hazelnp::Hazelnupp::Parse
void Parse(const int argc, const char *const *argv)
Will parse command line arguments.
Definition: Hazelnupp.cpp:35
ListValue.h
Hazelnp::ParamConstraint::constrainType
bool constrainType
Should this parameter be forced to be of a certain type? Remember to set constrainTo to the wanted ...
Definition: ParamConstraint.h:54
Hazelnp::Parameter::Key
const std::string & Key() const
Will return the key of this parameter.
Definition: Parameter.cpp:21
Hazelnp::HazelnuppConstraintMissingValue
Gets thrown when a parameter constrained to be required is not provided, and has no default value set...
Definition: HazelnuppException.h:62
Hazelnp::Hazelnupp::RegisterAbbreviation
void RegisterAbbreviation(const std::string &abbrev, const std::string &target)
Will register an abbreviation (like -f for –force)
Definition: Hazelnupp.cpp:332
Hazelnp::HazelnuppConstraintTypeMissmatch
Gets thrown when a parameter is of a type that does not match the required type, and is not convertib...
Definition: HazelnuppException.h:53
VoidValue.h
Hazelnp::Hazelnupp::RegisterConstraints
void RegisterConstraints(const std::vector< ParamConstraint > &constraints)
Will register parameter constraints.
Definition: Hazelnupp.cpp:354
Hazelnp::Hazelnupp::GetAbbreviation
const std::string & GetAbbreviation(const std::string &abbrev) const
Will return the long form of an abbreviation (like –force for -f)
Definition: Hazelnupp.cpp:338
Hazelnupp.h
Hazelnp::ListValue::AddValue
void AddValue(const Value *value)
Will add this value to the list.
Definition: ListValue.cpp:33
Hazelnp::VoidValue
Specializations for void values.
Definition: VoidValue.h:8
Hazelnp::DATA_TYPE::FLOAT
@ FLOAT
Hazelnp::DATA_TYPE::INT
@ INT
FloatValue.h
Hazelnp::Hazelnupp::HasAbbreviation
bool HasAbbreviation(const std::string &abbrev) const
Will check wether or not an abbreviation is registered.
Definition: Hazelnupp.cpp:343
Hazelnp::DATA_TYPE::STRING
@ STRING
Hazelnp::Hazelnupp::SetCrashOnFail
void SetCrashOnFail(bool crashOnFail)
Sets whether to crash the application, and print to stderr, when an exception is raised whilst parsin...
Definition: Hazelnupp.cpp:381
Hazelnp::ListValue
Specializations for list values (uses std::vector<Value*>)
Definition: ListValue.h:9
Hazelnp::StringValue
Specializations for string values (uses std::string)
Definition: StringValue.h:9
StringTools.h