| /* |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| */ |
| |
| #include "qpid/broker/SelectorExpression.h" |
| |
| #include "qpid/broker/Selector.h" |
| #include "qpid/broker/SelectorToken.h" |
| #include "qpid/broker/SelectorValue.h" |
| #include "qpid/sys/IntegerTypes.h" |
| #include "qpid/sys/regex.h" |
| |
| #include <cstdlib> |
| #include <cerrno> |
| #include <string> |
| #include <memory> |
| #include <ostream> |
| |
| #include <boost/lexical_cast.hpp> |
| #include <boost/scoped_ptr.hpp> |
| #include <boost/ptr_container/ptr_vector.hpp> |
| |
| /* |
| * Syntax for JMS style selector expressions (informal): |
| * This is a mixture of regular expression and EBNF formalism |
| * |
| * The top level term is SelectExpression |
| * |
| * // Lexical elements |
| * |
| * Alpha ::= [a-zA-Z] |
| * Digit ::= [0-9] |
| * HexDigit ::= [0-9a-fA-F] |
| * OctDigit ::= [0-7] |
| * BinDigit ::= [0-1] |
| * |
| * IdentifierInitial ::= Alpha | "_" | "$" |
| * IdentifierPart ::= IdentifierInitial | Digit | "." |
| * Identifier ::= IdentifierInitial IdentifierPart* |
| * Constraint : Identifier NOT IN ("NULL", "TRUE", "FALSE", "NOT", "AND", "OR", "BETWEEN", "LIKE", "IN", "IS") // Case insensitive |
| * |
| * LiteralString ::= ("'" [^']* "'")+ // Repeats to cope with embedded single quote |
| * |
| * // LiteralExactNumeric is a little simplified as it also allows underscores ("_") as internal seperators and suffix "l" or "L" |
| * LiteralExactNumeric ::= "0x" HexDigit+ | "0X" HexDigit+ | "0b" BinDigit+ | "0B" BinDigit+ | "0" OctDigit* | Digit+ |
| * |
| * // LiteralApproxNumeric is a little simplified as it also allows suffix "d", "D", "f", "F" |
| * Exponent ::= ('+'|'-')? LiteralExactNumeric |
| * LiteralApproxNumeric ::= ( Digit "." Digit* ( "E" Exponent )? ) | |
| * ( "." Digit+ ( "E" Exponent )? ) | |
| * ( Digit+ "E" Exponent ) |
| * LiteralBool ::= "TRUE" | "FALSE" |
| * |
| * Literal ::= LiteralBool | LiteralString | LiteralApproxNumeric | LiteralExactNumeric |
| * |
| * EqOps ::= "=" | "<>" |
| * ComparisonOps ::= EqOps | ">" | ">=" | "<" | "<=" |
| * AddOps ::= "+" | "-" |
| * MultiplyOps ::= "*" | "/" |
| * |
| * // Expression Syntax |
| * |
| * SelectExpression ::= OrExpression? // An empty expression is equivalent to "true" |
| * |
| * OrExpression ::= AndExpression ( "OR" AndExpression )* |
| * |
| * AndExpression :: = ComparisonExpression ( "AND" ComparisonExpression )* |
| * |
| * ComparisonExpression ::= AddExpression "IS" "NOT"? "NULL" | |
| * AddExpression "NOT"? "LIKE" LiteralString [ "ESCAPE" LiteralString ] | |
| * AddExpression "NOT"? "BETWEEN" AddExpression "AND" AddExpression | |
| * AddExpression "NOT"? "IN" "(" PrimaryExpression ("," PrimaryExpression)* ")" | |
| * AddExpression ComparisonOps AddExpression | |
| * "NOT" ComparisonExpression | |
| * AddExpression |
| * |
| * AddExpression :: = MultiplyExpression ( AddOps MultiplyExpression )* |
| * |
| * MultiplyExpression :: = UnaryArithExpression ( MultiplyOps UnaryArithExpression )* |
| * |
| * UnaryArithExpression ::= "-" LiteralExactNumeric | // This is a special case to simplify negative ints |
| * AddOps AddExpression | |
| * "(" OrExpression ")" | |
| * PrimaryExpression |
| * |
| * PrimaryExpression :: = Identifier | |
| * Literal |
| */ |
| |
| |
| using std::string; |
| using std::ostream; |
| |
| namespace qpid { |
| namespace broker { |
| |
| class Expression { |
| public: |
| virtual ~Expression() {} |
| virtual void repr(std::ostream&) const = 0; |
| virtual Value eval(const SelectorEnv&) const = 0; |
| |
| virtual BoolOrNone eval_bool(const SelectorEnv& env) const { |
| Value v = eval(env); |
| if (v.type==Value::T_BOOL) return BoolOrNone(v.b); |
| else return BN_UNKNOWN; |
| } |
| }; |
| |
| class BoolExpression : public Expression { |
| public: |
| virtual ~BoolExpression() {} |
| virtual void repr(std::ostream&) const = 0; |
| virtual BoolOrNone eval_bool(const SelectorEnv&) const = 0; |
| |
| Value eval(const SelectorEnv& env) const { |
| return eval_bool(env); |
| } |
| }; |
| |
| // Operators |
| |
| class ComparisonOperator { |
| public: |
| virtual ~ComparisonOperator() {} |
| virtual void repr(ostream&) const = 0; |
| virtual BoolOrNone eval(Expression&, Expression&, const SelectorEnv&) const = 0; |
| }; |
| |
| class UnaryBooleanOperator { |
| public: |
| virtual ~UnaryBooleanOperator() {} |
| virtual void repr(ostream&) const = 0; |
| virtual BoolOrNone eval(Expression&, const SelectorEnv&) const = 0; |
| }; |
| |
| class ArithmeticOperator { |
| public: |
| virtual ~ArithmeticOperator() {} |
| virtual void repr(ostream&) const = 0; |
| virtual Value eval(Expression&, Expression&, const SelectorEnv&) const = 0; |
| }; |
| |
| class UnaryArithmeticOperator { |
| public: |
| virtual ~UnaryArithmeticOperator() {} |
| virtual void repr(ostream&) const = 0; |
| virtual Value eval(Expression&, const SelectorEnv&) const = 0; |
| }; |
| |
| //////////////////////////////////////////////////// |
| |
| // Convenience outputters |
| |
| ostream& operator<<(ostream& os, const Expression& e) |
| { |
| e.repr(os); |
| return os; |
| } |
| |
| ostream& operator<<(ostream& os, const ComparisonOperator& e) |
| { |
| e.repr(os); |
| return os; |
| } |
| |
| ostream& operator<<(ostream& os, const UnaryBooleanOperator& e) |
| { |
| e.repr(os); |
| return os; |
| } |
| |
| ostream& operator<<(ostream& os, const ArithmeticOperator& e) |
| { |
| e.repr(os); |
| return os; |
| } |
| |
| ostream& operator<<(ostream& os, const UnaryArithmeticOperator& e) |
| { |
| e.repr(os); |
| return os; |
| } |
| |
| // Boolean Expression types... |
| |
| class ComparisonExpression : public BoolExpression { |
| ComparisonOperator* op; |
| boost::scoped_ptr<Expression> e1; |
| boost::scoped_ptr<Expression> e2; |
| |
| public: |
| ComparisonExpression(ComparisonOperator* o, Expression* e, Expression* e_): |
| op(o), |
| e1(e), |
| e2(e_) |
| {} |
| |
| void repr(ostream& os) const { |
| os << "(" << *e1 << *op << *e2 << ")"; |
| } |
| |
| BoolOrNone eval_bool(const SelectorEnv& env) const { |
| return op->eval(*e1, *e2, env); |
| } |
| }; |
| |
| class OrExpression : public BoolExpression { |
| boost::scoped_ptr<Expression> e1; |
| boost::scoped_ptr<Expression> e2; |
| |
| public: |
| OrExpression(Expression* e, Expression* e_): |
| e1(e), |
| e2(e_) |
| {} |
| |
| void repr(ostream& os) const { |
| os << "(" << *e1 << " OR " << *e2 << ")"; |
| } |
| |
| BoolOrNone eval_bool(const SelectorEnv& env) const { |
| BoolOrNone bn1(e1->eval_bool(env)); |
| if (bn1==BN_TRUE) return BN_TRUE; |
| BoolOrNone bn2(e2->eval_bool(env)); |
| if (bn2==BN_TRUE) return BN_TRUE; |
| if (bn1==BN_FALSE && bn2==BN_FALSE) return BN_FALSE; |
| else return BN_UNKNOWN; |
| } |
| }; |
| |
| class AndExpression : public BoolExpression { |
| boost::scoped_ptr<Expression> e1; |
| boost::scoped_ptr<Expression> e2; |
| |
| public: |
| AndExpression(Expression* e, Expression* e_): |
| e1(e), |
| e2(e_) |
| {} |
| |
| void repr(ostream& os) const { |
| os << "(" << *e1 << " AND " << *e2 << ")"; |
| } |
| |
| BoolOrNone eval_bool(const SelectorEnv& env) const { |
| BoolOrNone bn1(e1->eval_bool(env)); |
| if (bn1==BN_FALSE) return BN_FALSE; |
| BoolOrNone bn2(e2->eval_bool(env)); |
| if (bn2==BN_FALSE) return BN_FALSE; |
| if (bn1==BN_TRUE && bn2==BN_TRUE) return BN_TRUE; |
| else return BN_UNKNOWN; |
| } |
| }; |
| |
| class UnaryBooleanExpression : public BoolExpression { |
| UnaryBooleanOperator* op; |
| boost::scoped_ptr<Expression> e1; |
| |
| public: |
| UnaryBooleanExpression(UnaryBooleanOperator* o, Expression* e) : |
| op(o), |
| e1(e) |
| {} |
| |
| void repr(ostream& os) const { |
| os << *op << "(" << *e1 << ")"; |
| } |
| |
| BoolOrNone eval_bool(const SelectorEnv& env) const { |
| return op->eval(*e1, env); |
| } |
| }; |
| |
| class LikeExpression : public BoolExpression { |
| boost::scoped_ptr<Expression> e; |
| string reString; |
| qpid::sys::regex regexBuffer; |
| |
| static string toRegex(const string& s, const string& escape) { |
| string regex("^"); |
| if (escape.size()>1) throw std::logic_error("Internal error"); |
| char e = 0; |
| if (escape.size()==1) { |
| e = escape[0]; |
| } |
| // Translate % -> .*, _ -> ., . ->\. *->\* |
| bool doEscape = false; |
| for (string::const_iterator i = s.begin(); i!=s.end(); ++i) { |
| if ( e!=0 && *i==e ) { |
| doEscape = true; |
| continue; |
| } |
| switch(*i) { |
| case '%': |
| if (doEscape) regex += *i; |
| else regex += ".*"; |
| break; |
| case '_': |
| if (doEscape) regex += *i; |
| else regex += "."; |
| break; |
| case ']': |
| regex += "[]]"; |
| break; |
| case '-': |
| regex += "[-]"; |
| break; |
| // Don't add any more cases here: these are sufficient, |
| // adding more might turn on inadvertent matching |
| case '\\': |
| case '^': |
| case '$': |
| case '.': |
| case '*': |
| case '[': |
| regex += "\\"; |
| // Fallthrough |
| default: |
| regex += *i; |
| break; |
| } |
| doEscape = false; |
| } |
| regex += "$"; |
| return regex; |
| } |
| |
| public: |
| LikeExpression(Expression* e_, const string& like, const string& escape="") : |
| e(e_), |
| reString(toRegex(like, escape)), |
| regexBuffer(reString) |
| {} |
| |
| void repr(ostream& os) const { |
| os << *e << " REGEX_MATCH '" << reString << "'"; |
| } |
| |
| BoolOrNone eval_bool(const SelectorEnv& env) const { |
| Value v(e->eval(env)); |
| if ( v.type!=Value::T_STRING ) return BN_UNKNOWN; |
| return BoolOrNone(qpid::sys::regex_match(*v.s, regexBuffer)); |
| } |
| }; |
| |
| class BetweenExpression : public BoolExpression { |
| boost::scoped_ptr<Expression> e; |
| boost::scoped_ptr<Expression> l; |
| boost::scoped_ptr<Expression> u; |
| |
| public: |
| BetweenExpression(Expression* e_, Expression* l_, Expression* u_) : |
| e(e_), |
| l(l_), |
| u(u_) |
| {} |
| |
| void repr(ostream& os) const { |
| os << *e << " BETWEEN " << *l << " AND " << *u; |
| } |
| |
| BoolOrNone eval_bool(const SelectorEnv& env) const { |
| Value ve(e->eval(env)); |
| Value vl(l->eval(env)); |
| Value vu(u->eval(env)); |
| if (unknown(ve) || unknown(vl) || unknown(vu)) return BN_UNKNOWN; |
| return BoolOrNone(ve>=vl && ve<=vu); |
| } |
| }; |
| |
| class InExpression : public BoolExpression { |
| boost::scoped_ptr<Expression> e; |
| boost::ptr_vector<Expression> l; |
| |
| public: |
| InExpression(Expression* e_, boost::ptr_vector<Expression>& l_) : |
| e(e_) |
| { |
| l.swap(l_); |
| } |
| |
| void repr(ostream& os) const { |
| os << *e << " IN ("; |
| for (std::size_t i = 0; i<l.size(); ++i){ |
| os << l[i] << (i<l.size()-1 ? ", " : ")"); |
| } |
| } |
| |
| BoolOrNone eval_bool(const SelectorEnv& env) const { |
| Value ve(e->eval(env)); |
| if (unknown(ve)) return BN_UNKNOWN; |
| BoolOrNone r = BN_FALSE; |
| for (std::size_t i = 0; i<l.size(); ++i){ |
| Value li(l[i].eval(env)); |
| if (unknown(li)) { |
| r = BN_UNKNOWN; |
| continue; |
| } |
| if (ve==li) return BN_TRUE; |
| } |
| return r; |
| } |
| }; |
| |
| class NotInExpression : public BoolExpression { |
| boost::scoped_ptr<Expression> e; |
| boost::ptr_vector<Expression> l; |
| |
| public: |
| NotInExpression(Expression* e_, boost::ptr_vector<Expression>& l_) : |
| e(e_) |
| { |
| l.swap(l_); |
| } |
| |
| void repr(ostream& os) const { |
| os << *e << " NOT IN ("; |
| for (std::size_t i = 0; i<l.size(); ++i){ |
| os << l[i] << (i<l.size()-1 ? ", " : ")"); |
| } |
| } |
| |
| BoolOrNone eval_bool(const SelectorEnv& env) const { |
| Value ve(e->eval(env)); |
| if (unknown(ve)) return BN_UNKNOWN; |
| BoolOrNone r = BN_TRUE; |
| for (std::size_t i = 0; i<l.size(); ++i){ |
| Value li(l[i].eval(env)); |
| if (unknown(li)) { |
| r = BN_UNKNOWN; |
| continue; |
| } |
| // Check if types are incompatible. If nothing further in the list |
| // matches or is unknown and we had a type incompatibility then |
| // result still false. |
| if (r!=BN_UNKNOWN && |
| !sameType(ve,li) && !(numeric(ve) && numeric(li))) { |
| r = BN_FALSE; |
| continue; |
| } |
| |
| if (ve==li) return BN_FALSE; |
| } |
| return r; |
| } |
| }; |
| |
| // Arithmetic Expression types |
| |
| class ArithmeticExpression : public Expression { |
| ArithmeticOperator* op; |
| boost::scoped_ptr<Expression> e1; |
| boost::scoped_ptr<Expression> e2; |
| |
| public: |
| ArithmeticExpression(ArithmeticOperator* o, Expression* e, Expression* e_): |
| op(o), |
| e1(e), |
| e2(e_) |
| {} |
| |
| void repr(ostream& os) const { |
| os << "(" << *e1 << *op << *e2 << ")"; |
| } |
| |
| Value eval(const SelectorEnv& env) const { |
| return op->eval(*e1, *e2, env); |
| } |
| }; |
| |
| class UnaryArithExpression : public Expression { |
| UnaryArithmeticOperator* op; |
| boost::scoped_ptr<Expression> e1; |
| |
| public: |
| UnaryArithExpression(UnaryArithmeticOperator* o, Expression* e) : |
| op(o), |
| e1(e) |
| {} |
| |
| void repr(ostream& os) const { |
| os << *op << "(" << *e1 << ")"; |
| } |
| |
| Value eval(const SelectorEnv& env) const { |
| return op->eval(*e1, env); |
| } |
| }; |
| |
| // Expression types... |
| |
| class Literal : public Expression { |
| const Value value; |
| |
| public: |
| template <typename T> |
| Literal(const T& v) : |
| value(v) |
| {} |
| |
| void repr(ostream& os) const { |
| os << value; |
| } |
| |
| Value eval(const SelectorEnv&) const { |
| return value; |
| } |
| }; |
| |
| class StringLiteral : public Expression { |
| const string value; |
| |
| public: |
| StringLiteral(const string& v) : |
| value(v) |
| {} |
| |
| void repr(ostream& os) const { |
| os << "'" << value << "'"; |
| } |
| |
| Value eval(const SelectorEnv&) const { |
| return value; |
| } |
| }; |
| |
| class Identifier : public Expression { |
| string identifier; |
| |
| public: |
| Identifier(const string& i) : |
| identifier(i) |
| {} |
| |
| void repr(ostream& os) const { |
| os << "I:" << identifier; |
| } |
| |
| Value eval(const SelectorEnv& env) const { |
| return env.value(identifier); |
| } |
| }; |
| |
| //////////////////////////////////////////////////// |
| |
| // Some operators... |
| |
| typedef bool BoolOp(const Value&, const Value&); |
| |
| BoolOrNone booleval(BoolOp* op, Expression& e1, Expression& e2, const SelectorEnv& env) { |
| const Value v1(e1.eval(env)); |
| if (!unknown(v1)) { |
| const Value v2(e2.eval(env)); |
| if (!unknown(v2)) { |
| return BoolOrNone(op(v1, v2)); |
| } |
| } |
| return BN_UNKNOWN; |
| } |
| |
| // "=" |
| class Eq : public ComparisonOperator { |
| void repr(ostream& os) const { |
| os << "="; |
| } |
| |
| BoolOrNone eval(Expression& e1, Expression& e2, const SelectorEnv& env) const { |
| return booleval(&operator==, e1, e2, env); |
| } |
| }; |
| |
| // "<>" |
| class Neq : public ComparisonOperator { |
| void repr(ostream& os) const { |
| os << "<>"; |
| } |
| |
| BoolOrNone eval(Expression& e1, Expression& e2, const SelectorEnv& env) const { |
| return booleval(&operator!=, e1, e2, env); |
| } |
| }; |
| |
| // "<" |
| class Ls : public ComparisonOperator { |
| void repr(ostream& os) const { |
| os << "<"; |
| } |
| |
| BoolOrNone eval(Expression& e1, Expression& e2, const SelectorEnv& env) const { |
| return booleval(&operator<, e1, e2, env); |
| } |
| }; |
| |
| // ">" |
| class Gr : public ComparisonOperator { |
| void repr(ostream& os) const { |
| os << ">"; |
| } |
| |
| BoolOrNone eval(Expression& e1, Expression& e2, const SelectorEnv& env) const { |
| return booleval(&operator>, e1, e2, env); |
| } |
| }; |
| |
| // "<=" |
| class Lseq : public ComparisonOperator { |
| void repr(ostream& os) const { |
| os << "<="; |
| } |
| |
| BoolOrNone eval(Expression& e1, Expression& e2, const SelectorEnv& env) const { |
| return booleval(&operator<=, e1, e2, env); |
| } |
| }; |
| |
| // ">=" |
| class Greq : public ComparisonOperator { |
| void repr(ostream& os) const { |
| os << ">="; |
| } |
| |
| BoolOrNone eval(Expression& e1, Expression& e2, const SelectorEnv& env) const { |
| return booleval(&operator>=, e1, e2, env); |
| } |
| }; |
| |
| // "IS NULL" |
| class IsNull : public UnaryBooleanOperator { |
| void repr(ostream& os) const { |
| os << "IsNull"; |
| } |
| |
| BoolOrNone eval(Expression& e, const SelectorEnv& env) const { |
| return BoolOrNone(unknown(e.eval(env))); |
| } |
| }; |
| |
| // "IS NOT NULL" |
| class IsNonNull : public UnaryBooleanOperator { |
| void repr(ostream& os) const { |
| os << "IsNonNull"; |
| } |
| |
| BoolOrNone eval(Expression& e, const SelectorEnv& env) const { |
| return BoolOrNone(!unknown(e.eval(env))); |
| } |
| }; |
| |
| // "NOT" |
| class Not : public UnaryBooleanOperator { |
| void repr(ostream& os) const { |
| os << "NOT"; |
| } |
| |
| BoolOrNone eval(Expression& e, const SelectorEnv& env) const { |
| BoolOrNone bn = e.eval_bool(env); |
| if (bn==BN_UNKNOWN) return bn; |
| else return BoolOrNone(!bn); |
| } |
| }; |
| |
| class Negate : public UnaryArithmeticOperator { |
| void repr(ostream& os) const { |
| os << "-"; |
| } |
| |
| Value eval(Expression& e, const SelectorEnv& env) const { |
| return -e.eval(env); |
| } |
| }; |
| |
| class Add : public ArithmeticOperator { |
| void repr(ostream& os) const { |
| os << "+"; |
| } |
| |
| Value eval(Expression& e1, Expression& e2, const SelectorEnv& env) const { |
| return e1.eval(env)+e2.eval(env); |
| } |
| }; |
| |
| class Sub : public ArithmeticOperator { |
| void repr(ostream& os) const { |
| os << "-"; |
| } |
| |
| Value eval(Expression& e1, Expression& e2, const SelectorEnv& env) const { |
| return e1.eval(env)-e2.eval(env); |
| } |
| }; |
| |
| class Mult : public ArithmeticOperator { |
| void repr(ostream& os) const { |
| os << "*"; |
| } |
| |
| Value eval(Expression& e1, Expression& e2, const SelectorEnv& env) const { |
| return e1.eval(env)*e2.eval(env); |
| } |
| }; |
| |
| class Div : public ArithmeticOperator { |
| void repr(ostream& os) const { |
| os << "/"; |
| } |
| |
| Value eval(Expression& e1, Expression& e2, const SelectorEnv& env) const { |
| return e1.eval(env)/e2.eval(env); |
| } |
| }; |
| |
| Eq eqOp; |
| Neq neqOp; |
| Ls lsOp; |
| Gr grOp; |
| Lseq lseqOp; |
| Greq greqOp; |
| IsNull isNullOp; |
| IsNonNull isNonNullOp; |
| Not notOp; |
| |
| Negate negate; |
| Add add; |
| Sub sub; |
| Mult mult; |
| Div div; |
| |
| //////////////////////////////////////////////////// |
| |
| // Top level parser |
| class TopBoolExpression : public TopExpression { |
| boost::scoped_ptr<Expression> expression; |
| |
| void repr(ostream& os) const { |
| expression->repr(os); |
| } |
| |
| bool eval(const SelectorEnv& env) const { |
| BoolOrNone bn = expression->eval_bool(env); |
| if (bn==BN_TRUE) return true; |
| else return false; |
| } |
| |
| public: |
| TopBoolExpression(Expression* be) : |
| expression(be) |
| {} |
| }; |
| |
| void throwParseError(Tokeniser& tokeniser, const string& msg) { |
| tokeniser.returnTokens(); |
| string error("Illegal selector: '"); |
| error += tokeniser.nextToken().val; |
| error += "': "; |
| error += msg; |
| throw std::range_error(error); |
| } |
| |
| class Parse { |
| |
| friend TopExpression* TopExpression::parse(const string&); |
| |
| string error; |
| |
| Expression* selectorExpression(Tokeniser& tokeniser) |
| { |
| if ( tokeniser.nextToken().type==T_EOS ) { |
| return (new Literal(true)); |
| } |
| tokeniser.returnTokens(); |
| std::auto_ptr<Expression> e(orExpression(tokeniser)); |
| return e.release(); |
| } |
| |
| Expression* orExpression(Tokeniser& tokeniser) |
| { |
| std::auto_ptr<Expression> e(andExpression(tokeniser)); |
| if (!e.get()) return 0; |
| while ( tokeniser.nextToken().type==T_OR ) { |
| std::auto_ptr<Expression> e1(e); |
| std::auto_ptr<Expression> e2(andExpression(tokeniser)); |
| if (!e2.get()) return 0; |
| e.reset(new OrExpression(e1.release(), e2.release())); |
| } |
| tokeniser.returnTokens(); |
| return e.release(); |
| } |
| |
| Expression* andExpression(Tokeniser& tokeniser) |
| { |
| std::auto_ptr<Expression> e(comparisonExpression(tokeniser)); |
| if (!e.get()) return 0; |
| while ( tokeniser.nextToken().type==T_AND ) { |
| std::auto_ptr<Expression> e1(e); |
| std::auto_ptr<Expression> e2(comparisonExpression(tokeniser)); |
| if (!e2.get()) return 0; |
| e.reset(new AndExpression(e1.release(), e2.release())); |
| } |
| tokeniser.returnTokens(); |
| return e.release(); |
| } |
| |
| BoolExpression* specialComparisons(Tokeniser& tokeniser, std::auto_ptr<Expression> e1, bool negated = false) { |
| switch (tokeniser.nextToken().type) { |
| case T_LIKE: { |
| const Token t = tokeniser.nextToken(); |
| if ( t.type!=T_STRING ) { |
| error = "expected string after LIKE"; |
| return 0; |
| } |
| // Check for "ESCAPE" |
| std::auto_ptr<BoolExpression> l; |
| if ( tokeniser.nextToken().type==T_ESCAPE ) { |
| const Token e = tokeniser.nextToken(); |
| if ( e.type!=T_STRING ) { |
| error = "expected string after ESCAPE"; |
| return 0; |
| } |
| if (e.val.size()>1) { |
| throwParseError(tokeniser, "single character string required after ESCAPE"); |
| } |
| if (e.val=="%" || e.val=="_") { |
| throwParseError(tokeniser, "'%' and '_' are not allowed as ESCAPE characters"); |
| } |
| l.reset(new LikeExpression(e1.release(), t.val, e.val)); |
| } else { |
| tokeniser.returnTokens(); |
| l.reset(new LikeExpression(e1.release(), t.val)); |
| } |
| return negated ? new UnaryBooleanExpression(¬Op, l.release()) : l.release(); |
| } |
| case T_BETWEEN: { |
| std::auto_ptr<Expression> lower(addExpression(tokeniser)); |
| if ( !lower.get() ) return 0; |
| if ( tokeniser.nextToken().type!=T_AND ) { |
| error = "expected AND after BETWEEN"; |
| return 0; |
| } |
| std::auto_ptr<Expression> upper(addExpression(tokeniser)); |
| if ( !upper.get() ) return 0; |
| std::auto_ptr<BoolExpression> b(new BetweenExpression(e1.release(), lower.release(), upper.release())); |
| return negated ? new UnaryBooleanExpression(¬Op, b.release()) : b.release(); |
| } |
| case T_IN: { |
| if ( tokeniser.nextToken().type!=T_LPAREN ) { |
| error = "missing '(' after IN"; |
| return 0; |
| } |
| boost::ptr_vector<Expression> list; |
| do { |
| std::auto_ptr<Expression> e(addExpression(tokeniser)); |
| if (!e.get()) return 0; |
| list.push_back(e.release()); |
| } while (tokeniser.nextToken().type==T_COMMA); |
| tokeniser.returnTokens(); |
| if ( tokeniser.nextToken().type!=T_RPAREN ) { |
| error = "missing ',' or ')' after IN"; |
| return 0; |
| } |
| if (negated) return new NotInExpression(e1.release(), list); |
| else return new InExpression(e1.release(), list); |
| } |
| default: |
| error = "expected LIKE, IN or BETWEEN"; |
| return 0; |
| } |
| } |
| |
| Expression* comparisonExpression(Tokeniser& tokeniser) |
| { |
| const Token t = tokeniser.nextToken(); |
| if ( t.type==T_NOT ) { |
| std::auto_ptr<Expression> e(comparisonExpression(tokeniser)); |
| if (!e.get()) return 0; |
| return new UnaryBooleanExpression(¬Op, e.release()); |
| } |
| |
| tokeniser.returnTokens(); |
| std::auto_ptr<Expression> e1(addExpression(tokeniser)); |
| if (!e1.get()) return 0; |
| |
| switch (tokeniser.nextToken().type) { |
| // Check for "IS NULL" and "IS NOT NULL" |
| case T_IS: |
| // The rest must be T_NULL or T_NOT, T_NULL |
| switch (tokeniser.nextToken().type) { |
| case T_NULL: |
| return new UnaryBooleanExpression(&isNullOp, e1.release()); |
| case T_NOT: |
| if ( tokeniser.nextToken().type == T_NULL) |
| return new UnaryBooleanExpression(&isNonNullOp, e1.release()); |
| default: |
| error = "expected NULL or NOT NULL after IS"; |
| return 0; |
| } |
| case T_NOT: { |
| return specialComparisons(tokeniser, e1, true); |
| } |
| case T_BETWEEN: |
| case T_LIKE: |
| case T_IN: { |
| tokeniser.returnTokens(); |
| return specialComparisons(tokeniser, e1); |
| } |
| default: |
| break; |
| } |
| tokeniser.returnTokens(); |
| |
| ComparisonOperator* op; |
| switch (tokeniser.nextToken().type) { |
| case T_EQUAL: op = &eqOp; break; |
| case T_NEQ: op = &neqOp; break; |
| case T_LESS: op = &lsOp; break; |
| case T_GRT: op = &grOp; break; |
| case T_LSEQ: op = &lseqOp; break; |
| case T_GREQ: op = &greqOp; break; |
| default: |
| tokeniser.returnTokens(); |
| return e1.release(); |
| } |
| |
| std::auto_ptr<Expression> e2(addExpression(tokeniser)); |
| if (!e2.get()) return 0; |
| |
| return new ComparisonExpression(op, e1.release(), e2.release()); |
| } |
| |
| Expression* addExpression(Tokeniser& tokeniser) |
| { |
| std::auto_ptr<Expression> e(multiplyExpression(tokeniser)); |
| if (!e.get()) return 0; |
| |
| Token t = tokeniser.nextToken(); |
| while (t.type==T_PLUS || t.type==T_MINUS ) { |
| ArithmeticOperator* op; |
| switch (t.type) { |
| case T_PLUS: op = &add; break; |
| case T_MINUS: op = ⊂ break; |
| default: |
| error = "internal error processing binary + or -"; |
| return 0; |
| } |
| std::auto_ptr<Expression> e1(e); |
| std::auto_ptr<Expression> e2(multiplyExpression(tokeniser)); |
| if (!e2.get()) return 0; |
| e.reset(new ArithmeticExpression(op, e1.release(), e2.release())); |
| t = tokeniser.nextToken(); |
| } |
| |
| tokeniser.returnTokens(); |
| return e.release(); |
| } |
| |
| Expression* multiplyExpression(Tokeniser& tokeniser) |
| { |
| std::auto_ptr<Expression> e(unaryArithExpression(tokeniser)); |
| if (!e.get()) return 0; |
| |
| Token t = tokeniser.nextToken(); |
| while (t.type==T_MULT || t.type==T_DIV ) { |
| ArithmeticOperator* op; |
| switch (t.type) { |
| case T_MULT: op = &mult; break; |
| case T_DIV: op = ÷ break; |
| default: |
| error = "internal error processing * or /"; |
| return 0; |
| } |
| std::auto_ptr<Expression> e1(e); |
| std::auto_ptr<Expression> e2(unaryArithExpression(tokeniser)); |
| if (!e2.get()) return 0; |
| e.reset(new ArithmeticExpression(op, e1.release(), e2.release())); |
| t = tokeniser.nextToken(); |
| } |
| |
| tokeniser.returnTokens(); |
| return e.release(); |
| } |
| |
| Expression* unaryArithExpression(Tokeniser& tokeniser) |
| { |
| const Token t = tokeniser.nextToken(); |
| switch (t.type) { |
| case T_LPAREN: { |
| std::auto_ptr<Expression> e(orExpression(tokeniser)); |
| if (!e.get()) return 0; |
| if ( tokeniser.nextToken().type!=T_RPAREN ) { |
| error = "missing ')' after '('"; |
| return 0; |
| } |
| return e.release(); |
| } |
| case T_PLUS: |
| break; // Unary + is no op |
| case T_MINUS: { |
| const Token t = tokeniser.nextToken(); |
| // Special case for negative numerics |
| if (t.type==T_NUMERIC_EXACT) { |
| std::auto_ptr<Expression> e(parseExactNumeric(t, true)); |
| return e.release(); |
| } else { |
| tokeniser.returnTokens(); |
| std::auto_ptr<Expression> e(unaryArithExpression(tokeniser)); |
| if (!e.get()) return 0; |
| return new UnaryArithExpression(&negate, e.release()); |
| } |
| } |
| default: |
| tokeniser.returnTokens(); |
| break; |
| } |
| |
| std::auto_ptr<Expression> e(primaryExpression(tokeniser)); |
| return e.release(); |
| } |
| |
| Expression* parseExactNumeric(const Token& token, bool negate) |
| { |
| int base = 0; |
| string s; |
| std::remove_copy(token.val.begin(), token.val.end(), std::back_inserter(s), '_'); |
| if (s[1]=='b' || s[1]=='B') { |
| base = 2; |
| s = s.substr(2); |
| } else if (s[1]=='x' || s[1]=='X') { |
| base = 16; |
| s = s.substr(2); |
| } if (s[0]=='0') { |
| base = 8; |
| } |
| errno = 0; |
| uint64_t value = strtoull(s.c_str(), 0, base); |
| if (!errno && (base || value<=INT64_MAX)) { |
| int64_t r = value; |
| return new Literal((negate ? -r : r)); |
| } |
| if (negate && value==INT64_MAX+1ull) return new Literal(INT64_MIN); |
| error = "integer literal too big"; |
| return 0; |
| } |
| |
| Expression* parseApproxNumeric(const Token& token) |
| { |
| errno = 0; |
| string s; |
| std::remove_copy(token.val.begin(), token.val.end(), std::back_inserter(s), '_'); |
| double value = std::strtod(s.c_str(), 0); |
| if (!errno) return new Literal(value); |
| error = "floating literal overflow/underflow"; |
| return 0; |
| } |
| |
| Expression* primaryExpression(Tokeniser& tokeniser |
| |
| ) |
| { |
| const Token& t = tokeniser.nextToken(); |
| switch (t.type) { |
| case T_IDENTIFIER: |
| return new Identifier(t.val); |
| case T_STRING: |
| return new StringLiteral(t.val); |
| case T_FALSE: |
| return new Literal(false); |
| case T_TRUE: |
| return new Literal(true); |
| case T_NUMERIC_EXACT: |
| return parseExactNumeric(t, false); |
| case T_NUMERIC_APPROX: |
| return parseApproxNumeric(t); |
| default: |
| error = "expected literal or identifier"; |
| return 0; |
| } |
| } |
| |
| }; |
| |
| TopExpression* TopExpression::parse(const string& exp) |
| { |
| string::const_iterator s = exp.begin(); |
| string::const_iterator e = exp.end(); |
| Tokeniser tokeniser(s,e); |
| Parse parse; |
| std::auto_ptr<Expression> b(parse.selectorExpression(tokeniser)); |
| if (!b.get()) { |
| throwParseError(tokeniser, parse.error); |
| } |
| if (tokeniser.nextToken().type != T_EOS) { |
| throwParseError(tokeniser, "extra input"); |
| } |
| return new TopBoolExpression(b.release()); |
| } |
| |
| }} |