Commit 33c377b3 authored by hbristow's avatar hbristow

complete rewrite of ArgumentParser

parent d5abe79f
...@@ -165,7 +165,9 @@ public: ...@@ -165,7 +165,9 @@ public:
// MATLAB TYPES // MATLAB TYPES
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
Bridge& operator=(const mxArray* obj) { ptr_ = obj; return *this; } Bridge& operator=(const mxArray* obj) { ptr_ = obj; return *this; }
Bridge(const mxArray* obj) : ptr_(obj) {} Bridge& operator=(const MxArray& obj) { ptr_ = obj; return *this; }
Bridge(const MxArray& obj) : ptr_(obj) {}
Bridge(const mxArray* obj) : ptr_(obj) {}
MxArray toMxArray() { return ptr_; } MxArray toMxArray() { return ptr_; }
......
...@@ -49,6 +49,8 @@ ...@@ -49,6 +49,8 @@
#include <vector> #include <vector>
#include <algorithm> #include <algorithm>
#include <sstream> #include <sstream>
#include <iostream>
#include <iterator>
#include <opencv2/core.hpp> #include <opencv2/core.hpp>
#if __cplusplus > 201103 #if __cplusplus > 201103
#include <unordered_set> #include <unordered_set>
...@@ -533,6 +535,7 @@ private: ...@@ -533,6 +535,7 @@ private:
typedef std::vector<size_t> IndexVector; typedef std::vector<size_t> IndexVector;
typedef std::vector<MxArray> MxArrayVector; typedef std::vector<MxArray> MxArrayVector;
typedef std::vector<Variant> VariantVector; typedef std::vector<Variant> VariantVector;
/* @class Variant /* @class Variant
* @brief Describes a variant of arguments to a method * @brief Describes a variant of arguments to a method
* *
...@@ -542,43 +545,79 @@ private: ...@@ -542,43 +545,79 @@ private:
* inputs for a method invocation. * inputs for a method invocation.
*/ */
class Variant { class Variant {
private:
String name_;
size_t Nreq_;
size_t Nopt_;
StringVector keys_;
IndexVector order_;
bool valid_;
size_t nparsed_;
size_t nkeys_;
size_t working_opt_;
bool expecting_val_;
bool using_named_;
size_t find(const String& key) const {
return std::find(keys_.begin(), keys_.end(), key) - keys_.begin();
}
public: public:
Variant(const String& _name, size_t _nreq, size_t _nopt, const StringVector& _keys) /*! @brief default constructor */
: name(_name), nreq(_nreq), nopt(_nopt), keys(_keys), using_named(false) {} Variant() : Nreq_(0), Nopt_(0), valid_(false) {}
String name; /*! @brief construct a new variant spec */
size_t nreq; Variant(const String& name, size_t Nreq, size_t Nopt, const StringVector& keys)
size_t nopt; : name_(name), Nreq_(Nreq), Nopt_(Nopt), keys_(keys),
StringVector keys; order_(Nreq+Nopt, Nreq+2*Nopt), valid_(true), nparsed_(0), nkeys_(0),
IndexVector order; working_opt_(0), expecting_val_(false), using_named_(false) {}
bool using_named; /*! @brief return the total number of arguments the variant can take */
/*! @brief return true if the named-argument is in the Variant */ size_t size() const { return Nreq_ + Nopt_; }
bool count(const String& key) { return std::find(keys.begin(), keys.end(), key) != keys.end(); } /*! @brief has the variant been fulfilled? */
/*! @brief remove a key by index from the Variant */ bool fulfilled() const { return (valid_ && nparsed_ >= Nreq_ && !expecting_val_); }
void erase(const size_t idx) { keys.erase(keys.begin()+idx); } /*! @brief is the variant in a valid state (though not necessarily fulfilled) */
/*! @brief remove a key by name from the Variant */ bool valid() const { return valid_; }
void erase(const String& key) { keys.erase(std::find(keys.begin(), keys.end(), key)); } /*! @brief check if the named argument exists in the variant */
/*! @brief convert a Variant to a string representation */ bool exist(const String& key) const { return find(key) != keys_.size(); }
String toString(const String& method_name=String("f")) const { /*! @brief retrieve the order mapping raw inputs to their position in the variant */
const IndexVector& order() const { return order_; }
size_t order(size_t n) const { return order_[n]; }
/*! @brief attempt to parse the next argument as a value */
bool parseNextAsValue() {
if (!valid_) {}
else if ((using_named_ && !expecting_val_) || (nparsed_-nkeys_ == Nreq_+Nopt_)) { valid_ = false; }
else if (nparsed_ < Nreq_) { order_[nparsed_] = nparsed_; }
else if (!using_named_) { order_[nparsed_] = nparsed_; }
else if (using_named_ && expecting_val_) { order_[Nreq_ + working_opt_] = nparsed_; }
nparsed_++;
expecting_val_ = false;
return valid_;
}
/*! @biref attempt to parse the next argument as a name (key) */
bool parseNextAsKey(const String& key) {
if (!valid_) {}
else if ((nparsed_ < Nreq_) || (nparsed_-nkeys_ == Nreq_+Nopt_)) { valid_ = false; }
else if (using_named_ && expecting_val_) { valid_ = false; }
else if ((working_opt_ = find(key)) == keys_.size()) { valid_ = false; }
else { using_named_ = true; expecting_val_ = true; nkeys_++; nparsed_++; }
return valid_;
}
String toString(const String& method_name="f") const {
std::ostringstream s; std::ostringstream s;
s << method_name << "("; s << method_name << "(";
for (size_t n = 0; n < nreq; ++n) { for (size_t n = 0; n < Nreq_; ++n) { s << "src" << n+1 << (n != Nreq_-1 ? ", " : ""); }
s << "src" << n+1; if (n != nreq-1) s << ", "; if (Nreq_ && Nopt_) s << ", ";
} for (size_t n = 0; n < keys_.size(); ++n) { s << "'" << keys_[n] << "', " << keys_[n] << (n != Nopt_-1 ? ", " : ""); }
if (nreq && nopt) s << ", ";
for (size_t n = 0; n < keys.size(); ++n) {
s << "'" << keys[n] << "', " << keys[n];
if (n != keys.size()-1) s << ", ";
}
s << ");"; s << ");";
return s.str(); return s.str();
} }
}; };
/*! @brief given an input and output vector of arguments, and a variant spec, sort */
void sortArguments(Variant& v, MxArrayVector& in, MxArrayVector& out) { void sortArguments(Variant& v, MxArrayVector& in, MxArrayVector& out) {
// allocate the output array with ALL arguments // allocate the output array with ALL arguments
out.resize(v.nreq + v.nopt); out.resize(v.size());
std::copy(v.order().begin(), v.order().end(), std::ostream_iterator<size_t>(std::cout, ", "));
// reorder the inputs based on the variant ordering // reorder the inputs based on the variant ordering
for (size_t n = 0; n < v.order.size(); ++n) { for (size_t n = 0; n < v.size(); ++n) {
swap(in[n], out[v.order[n]]); if (v.order(n) >= in.size()) continue;
swap(in[v.order(n)], out[n]);
} }
} }
MxArrayVector filled_; MxArrayVector filled_;
...@@ -587,6 +626,7 @@ private: ...@@ -587,6 +626,7 @@ private:
String method_name_; String method_name_;
public: public:
ArgumentParser(const String& method_name) : method_name_(method_name) {} ArgumentParser(const String& method_name) : method_name_(method_name) {}
/*! @brief add a function call variant to the parser /*! @brief add a function call variant to the parser
* *
* Adds a function-call signature to the parser. The function call *must* be * Adds a function-call signature to the parser. The function call *must* be
...@@ -606,10 +646,12 @@ public: ...@@ -606,10 +646,12 @@ public:
void addVariant(const String& name, size_t nreq, size_t nopt, StringVector keys) { void addVariant(const String& name, size_t nreq, size_t nopt, StringVector keys) {
variants_.push_back(Variant(name, nreq, nopt, keys)); variants_.push_back(Variant(name, nreq, nopt, keys));
} }
/*! @brief check if the valid variant is the key name */ /*! @brief check if the valid variant is the key name */
bool variantIs(const String& name) { bool variantIs(const String& name) {
return name.compare(valid_) == 0; return name.compare(valid_) == 0;
} }
/*! @brief parse a vector of input arguments /*! @brief parse a vector of input arguments
* *
* This method parses a vector of input arguments, attempting to match them * This method parses a vector of input arguments, attempting to match them
...@@ -633,51 +675,19 @@ public: ...@@ -633,51 +675,19 @@ public:
for (MxArrayVector::const_iterator input = inputs.begin(); input != inputs.end(); ++input) { for (MxArrayVector::const_iterator input = inputs.begin(); input != inputs.end(); ++input) {
String name = input->isString() ? input->toString() : String(); String name = input->isString() ? input->toString() : String();
for (VariantVector::iterator candidate = candidates.begin(); candidate < candidates.end(); ++candidate) { for (VariantVector::iterator candidate = candidates.begin(); candidate < candidates.end(); ++candidate) {
// check if the input is a key if (candidate->exist(name)) {
bool key = candidate->count(name); mexPrintf("%d\n", candidate->parseNextAsKey(name));
} else {
/* mexPrintf("%d\n", candidate->parseNextAsValue());
* FAILURE CASES
* 1. too many inputs, or
* 2. name is not a key and we're expecting a key
* 3. name is a key, and
* we're still expecting required arguments, or
* we're expecting an argument for a previous key
*/
if ((!candidate->nreq && !candidate->nopt) ||
(!key && !candidate->nreq && candidate->keys.size() == candidate->nopt && candidate->using_named) ||
(key && (candidate->nreq || candidate->keys.size() < candidate->nopt))) {
candidate = candidates.erase(candidate)--;
}
// VALID CASES
// Still parsing required argments (input is not a key)
else if (!key && candidate->nreq) {
candidate->nreq--;
}
// Parsing optional arguments and a named argument is encountered
else if (key && !candidate->nreq && candidate->nopt > 0 && candidate->keys.size() == candidate->nopt) {
candidate->erase(name);
candidate->using_named = true;
}
// Parsing input for a named argument
else if (!key && candidate->keys.size() < candidate->nopt) {
candidate->nopt--;
}
// Parsing un-named optional arguments
else if (!key && !candidate->nreq && candidate->nopt && candidate->keys.size() && !candidate->using_named) {
candidate->erase(0);
candidate->nopt--;
} }
} }
} }
// if any candidates remain, check that they have been fully parsed mexPrintf("We get here!\n");
// make sure the candidates have been fulfilled
for (VariantVector::iterator candidate = candidates.begin(); candidate < candidates.end(); ++candidate) { for (VariantVector::iterator candidate = candidates.begin(); candidate < candidates.end(); ++candidate) {
if (candidate->nreq || candidate->keys.size() < candidate->nopt) { if (!candidate->fulfilled()) {
candidate = candidates.erase(candidate)--; candidate = candidates.erase(candidate)--;
} }
} }
...@@ -697,6 +707,8 @@ public: ...@@ -697,6 +707,8 @@ public:
error(String("No matching method signatures for given arguments. Valid variants are:").append(variant_string)); error(String("No matching method signatures for given arguments. Valid variants are:").append(variant_string));
} }
// Unique candidate!
sortArguments(candidates[0], const_cast<MxArrayVector&>(inputs), outputs);
return outputs; return outputs;
} }
}; };
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment