--- /dev/null
+#include <stdexcept>
+#include "booleanevaluator.h"
+
+using namespace std;
+
+/* I'd rather have overloads with different slots, but that creates an
+ambiguity because slots have template constructors. */
+BooleanEvaluator::BooleanEvaluator(const Slot &s, bool allow_compare):
+ slot(s),
+ ops(allow_compare ? "&|!=^" : "&|!")
+{ }
+
+bool BooleanEvaluator::evaluate(const string &str)
+{
+ string buf;
+ last_was_op = true;
+ for(string::const_iterator i=str.begin();; ++i)
+ {
+ if(i!=str.end() && (isalnum(*i) || (!buf.empty() && (*i=='_' || *i=='-'))))
+ buf += *i;
+ else
+ {
+ if(!buf.empty())
+ {
+ if(!last_was_op)
+ throw runtime_error("syntax error at "+buf);
+
+ char op = (op_stack.empty() ? 0 : op_stack.back());
+ if(op=='=' || op=='^')
+ {
+ op_stack.pop_back();
+ string var = var_stack.back();
+ var_stack.pop_back();
+ bool value = (slot(var, &buf) == (op=='='));
+ value_stack.push_back(value);
+ }
+ else
+ var_stack.push_back(buf);
+
+ buf.clear();
+ last_was_op = false;
+ }
+
+ if(i==str.end())
+ break;
+ else if(isspace(*i))
+ ;
+ else if(ops.find(*i)!=string::npos)
+ push_op(*i);
+ else
+ throw runtime_error("syntax error at "+string(1, *i));
+ }
+ }
+
+ collapse(0);
+
+ bool value = pop_value();
+ if(!value_stack.empty())
+ throw runtime_error("too many values");
+
+ return value;
+}
+
+void BooleanEvaluator::push_op(char op)
+{
+ if(last_was_op!=is_unary(op))
+ throw runtime_error("syntax error at "+string(1, op));
+ // TODO Disallow mixing of ! and =/^
+ if(is_logic(op) && !var_stack.empty())
+ value_stack.push_back(pop_value());
+
+ if(!is_unary(op))
+ collapse(precedence(op));
+ op_stack.push_back(op);
+ last_was_op = true;
+}
+
+bool BooleanEvaluator::pop_value()
+{
+ if(!var_stack.empty())
+ {
+ string var = var_stack.back();
+ var_stack.pop_back();
+ return slot(var, 0);
+ }
+ else if(!value_stack.empty())
+ {
+ bool value = value_stack.back();
+ value_stack.pop_back();
+ return value;
+ }
+
+ throw runtime_error("value stack underflow");
+}
+
+void BooleanEvaluator::collapse(unsigned until)
+{
+ while(!op_stack.empty())
+ {
+ char op = op_stack.back();
+ if(precedence(op)<until)
+ return;
+
+ op_stack.pop_back();
+ bool value1 = pop_value();
+ if(is_unary(op))
+ {
+ if(op=='!')
+ value1 = !value1;
+ }
+ else
+ {
+ bool value2 = pop_value();
+ if(op=='&')
+ value1 = (value1 && value2);
+ else if(op=='|')
+ value1 = (value1 || value2);
+ }
+ value_stack.push_back(value1);
+ }
+}
+
+unsigned BooleanEvaluator::precedence(char op)
+{
+ if(op=='&')
+ return 1;
+ else if(op=='=' || op=='^')
+ return 2;
+ else if(op=='!')
+ return 3;
+ else
+ return 0;
+}
+
+bool BooleanEvaluator::is_unary(char op)
+{
+ return op=='!';
+}
+
+bool BooleanEvaluator::is_logic(char op)
+{
+ return (op=='&' || op=='|' || op=='!');
+}
--- /dev/null
+#ifndef BOOLEANEVALUATOR_H_
+#define BOOLEANEVALUATOR_H_
+
+#include <string>
+#include <vector>
+#include <sigc++/slot.h>
+
+class BooleanEvaluator
+{
+public:
+ typedef sigc::slot<bool, const std::string &, const std::string *> Slot;
+
+private:
+ Slot slot;
+ std::string ops;
+ std::vector<std::string> var_stack;
+ std::vector<unsigned char> value_stack;
+ std::vector<char> op_stack;
+ bool last_was_op;
+
+public:
+ BooleanEvaluator(const Slot &, bool = true);
+
+ bool evaluate(const std::string &);
+private:
+ void push_op(char);
+ bool pop_value();
+ void collapse(unsigned);
+ unsigned precedence(char);
+ bool is_unary(char);
+ bool is_logic(char);
+};
+
+#endif
#include <msp/fs/utils.h>
#include <msp/io/print.h>
#include <msp/strings/lexicalcast.h>
+#include "booleanevaluator.h"
#include "builder.h"
#include "component.h"
#include "csourcefile.h"
void Component::Loader::if_feature(const string &cond)
{
- bool match = obj.package.match_feature(cond);
+ BooleanEvaluator eval(sigc::mem_fun(&obj.package, &SourcePackage::match_feature));
+ bool match = eval.evaluate(cond);
obj.package.get_builder().get_logger().log("configure",
format("%s/%s: feature %s %smatched", obj.package.get_name(), obj.name, cond, (match ? "" : "not ")));
if(match)
#include <msp/io/print.h>
#include <msp/strings/lexicalcast.h>
#include <msp/strings/utils.h>
+#include "booleanevaluator.h"
#include "builder.h"
#include "package.h"
void Package::Loader::if_arch(const string &cond)
{
- bool match = obj.builder.get_current_arch().match_name(cond);
+ BooleanEvaluator eval(sigc::hide<1>(sigc::mem_fun(&obj.builder.get_current_arch(), &Architecture::match_name)), false);
+ bool match = eval.evaluate(cond);
obj.builder.get_logger().log("configure", format("%s: arch %s %smatched", obj.name, cond, (match ? "" : "not ")));
if(match)
load_sub_with(*this);
#include <msp/strings/lexicalcast.h>
#include <msp/strings/utils.h>
#include "binarypackage.h"
+#include "booleanevaluator.h"
#include "builder.h"
#include "file.h"
#include "pkgconfigfile.h"
return source_dir/arch.get_name();
}
-bool SourcePackage::match_feature(const string &cond) const
+bool SourcePackage::match_feature(const string &feat, const string *comp) const
{
- string::size_type equals = cond.find('=');
- if(equals!=string::npos)
- {
- if(equals==0)
- throw invalid_argument("SourcePackage::match_feature");
- bool negate = cond[equals-1]=='!';
- string feat = cond.substr(0, equals-negate);
- string value = config.get_option("with_"+feat).value;
- return (value==cond.substr(equals+1))!=negate;
- }
+ string value = config.get_option("with_"+feat).value;
+ if(comp)
+ return value==*comp;
else
- {
- bool negate = (cond[0]=='!');
- string feat = cond.substr(negate);
- string value = config.get_option("with_"+feat).value;
- return lexical_cast<bool>(value)!=negate;
- }
+ return lexical_cast<bool>(value);
}
void SourcePackage::set_build_type(const BuildType &t)
void SourcePackage::Loader::if_feature(const string &cond)
{
- bool match = obj.match_feature(cond);
+ BooleanEvaluator eval(sigc::mem_fun(&obj, &SourcePackage::match_feature));
+ bool match = eval.evaluate(cond);
obj.builder.get_logger().log("configure", format("%s: feature %s %smatched", obj.name, cond, (match ? "" : "not ")));
if(match)
load_sub_with(*this);
const Toolchain &get_toolchain() const { return local_tools; }
const ComponentList &get_components() const { return components; }
const Config &get_config() const { return config; }
- bool match_feature(const std::string &) const;
+ bool match_feature(const std::string &, const std::string *) const;
void set_build_type(const BuildType &);
const BuildInfo &get_build_info() const { return build_info; }
private: