From 02097e6a1ddbffbc2217005c3c2ebba528f5248f Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Thu, 3 Oct 2013 16:27:12 +0300 Subject: [PATCH] Support boolean logic in conditions Parentheses are missing and a few corner cases trigger obscure errors, but it's good enough for now. --- source/booleanevaluator.cpp | 143 ++++++++++++++++++++++++++++++++++++ source/booleanevaluator.h | 34 +++++++++ source/component.cpp | 4 +- source/package.cpp | 4 +- source/sourcepackage.cpp | 26 ++----- source/sourcepackage.h | 2 +- 6 files changed, 192 insertions(+), 21 deletions(-) create mode 100644 source/booleanevaluator.cpp create mode 100644 source/booleanevaluator.h diff --git a/source/booleanevaluator.cpp b/source/booleanevaluator.cpp new file mode 100644 index 0000000..ecb38fb --- /dev/null +++ b/source/booleanevaluator.cpp @@ -0,0 +1,143 @@ +#include +#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) +#include +#include + +class BooleanEvaluator +{ +public: + typedef sigc::slot Slot; + +private: + Slot slot; + std::string ops; + std::vector var_stack; + std::vector value_stack; + std::vector 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 diff --git a/source/component.cpp b/source/component.cpp index d23e1b3..fa6fd56 100644 --- a/source/component.cpp +++ b/source/component.cpp @@ -4,6 +4,7 @@ #include #include #include +#include "booleanevaluator.h" #include "builder.h" #include "component.h" #include "csourcefile.h" @@ -348,7 +349,8 @@ void Component::Loader::if_arch(const string &cond) 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) diff --git a/source/package.cpp b/source/package.cpp index aa9160e..19ee511 100644 --- a/source/package.cpp +++ b/source/package.cpp @@ -1,6 +1,7 @@ #include #include #include +#include "booleanevaluator.h" #include "builder.h" #include "package.h" @@ -38,7 +39,8 @@ Package::Loader::Loader(Package &p): 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); diff --git a/source/sourcepackage.cpp b/source/sourcepackage.cpp index dbb7592..ddfa00f 100644 --- a/source/sourcepackage.cpp +++ b/source/sourcepackage.cpp @@ -4,6 +4,7 @@ #include #include #include "binarypackage.h" +#include "booleanevaluator.h" #include "builder.h" #include "file.h" #include "pkgconfigfile.h" @@ -63,25 +64,13 @@ FS::Path SourcePackage::get_output_directory() const 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(value)!=negate; - } + return lexical_cast(value); } void SourcePackage::set_build_type(const BuildType &t) @@ -229,7 +218,8 @@ void SourcePackage::Loader::generate(const string &tag) 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); diff --git a/source/sourcepackage.h b/source/sourcepackage.h index 1333bdb..fe79dcc 100644 --- a/source/sourcepackage.h +++ b/source/sourcepackage.h @@ -86,7 +86,7 @@ public: 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: -- 2.45.2