--- /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=='!');
+}