From 70ba19fd5eb7d24d6704ad50033742ea3431581a Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Sun, 6 Apr 2014 00:29:14 -0400 Subject: [PATCH] Bug TW-1254 - TW-1254 Calc command can segfault on negative numbers (thanks to Renato Alves). - Implemented a recursive descent parser to syntax check the expression, and disambiguate unary minus. The syntax checking is not generating any diagnostics yet. --- ChangeLog | 2 + src/Eval.cpp | 426 +++++++++++++++++++++++++++++++++++++++++++++--- src/Eval.h | 11 ++ src/ISO8601.cpp | 5 +- src/ISO8601.h | 1 + 5 files changed, 424 insertions(+), 21 deletions(-) diff --git a/ChangeLog b/ChangeLog index 40406dbed..303874c67 100644 --- a/ChangeLog +++ b/ChangeLog @@ -34,6 +34,8 @@ Bugs - TD-42 Cannot compile taskd - GNUTLS_VERSION undefined in diag.cpp (thanks to Michele Vetturi). - TD-45 Fix preprocessor define (thanks to Jochen Sprickerhof). +- TW-1254 Calc command can segfault on negative numbers (thanks to Renato + Alves). - TW-1282 incorrect URLs in man task-sync (thanks to Jeremiah Marks). - TW-1288 Added missing locking for task modifications (thanks to Kosta H, Ralph Bean, Adam Coddington). diff --git a/src/Eval.cpp b/src/Eval.cpp index 0963eb9ef..69ffdfef8 100644 --- a/src/Eval.cpp +++ b/src/Eval.cpp @@ -40,32 +40,38 @@ static struct } operators[] = { // Operator Precedence Type Associativity - { "and", 5, 'b', 'l' }, // Conjunction - { "xor", 4, 'b', 'l' }, // Disjunction + { "^", 16, 'b', 'r' }, // Exponent - { "or", 3, 'b', 'l' }, // Disjunction - { "<=", 10, 'b', 'l' }, // Less than or equal - { ">=", 10, 'b', 'l' }, // Greater than or equal - { "!~", 9, 'b', 'l' }, // Regex non-match - { "!=", 9, 'b', 'l' }, // Inequal + { "!", 15, 'u', 'r' }, // Unary not + { "_neg_", 15, 'u', 'r' }, // Unary minus + { "_pos_", 15, 'u', 'r' }, // Unary plus - { "==", 9, 'b', 'l' }, // Equal - { "=", 9, 'b', 'l' }, // Equal - { "^", 16, 'b', 'r' }, // Exponent - { ">", 10, 'b', 'l' }, // Greater than - { "~", 9, 'b', 'l' }, // Regex match - { "!", 15, 'u', 'r' }, // Not + { "_hastag_", 14, 'b', 'l'}, // +tag [Pseudo-op] + { "_notag_", 14, 'b', 'l'}, // -tag [Pseudo-op] - { "_hastag_", 9, 'b', 'l'}, // +tag [Pseudo-op] - { "_notag_", 9, 'b', 'l'}, // -tag [Pseudo-op] - - { "-", 15, 'u', 'r' }, // Unary minus { "*", 13, 'b', 'l' }, // Multiplication { "/", 13, 'b', 'l' }, // Division { "%", 13, 'b', 'l' }, // Modulus + { "+", 12, 'b', 'l' }, // Addition { "-", 12, 'b', 'l' }, // Subtraction + + { "<=", 10, 'b', 'l' }, // Less than or equal + { ">=", 10, 'b', 'l' }, // Greater than or equal + { ">", 10, 'b', 'l' }, // Greater than { "<", 10, 'b', 'l' }, // Less than + + { "=", 9, 'b', 'l' }, // Equal + { "==", 9, 'b', 'l' }, // Equal + { "!=", 9, 'b', 'l' }, // Inequal + + { "~", 8, 'b', 'l' }, // Regex match + { "!~", 8, 'b', 'l' }, // Regex non-match + + { "and", 5, 'b', 'l' }, // Conjunction + { "or", 4, 'b', 'l' }, // Disjunction + { "xor", 3, 'b', 'l' }, // Disjunction + { "(", 0, 'b', 'l' }, // Precedence start { ")", 0, 'b', 'l' }, // Precedence end }; @@ -106,6 +112,9 @@ void Eval::evaluateInfixExpression (const std::string& e, Variant& v) const std::cout << "# token infix '" << token << "' " << Lexer::type_name (type) << "\n"; } + // Parse for syntax checking and operator replacement. + infixParse (tokens); + // Convert infix --> postfix. infixToPostfix (tokens); @@ -163,7 +172,7 @@ void Eval::evaluatePostfixStack ( std::vector >::const_iterator token; for (token = tokens.begin (); token != tokens.end (); ++token) { - // Unary operator. + // Unary operators. if (token->second == Lexer::typeOperator && token->first == "!") { @@ -171,6 +180,18 @@ void Eval::evaluatePostfixStack ( values.pop_back (); values.push_back (! right); } + else if (token->second == Lexer::typeOperator && + token->first == "_neg_") + { + Variant right = values.back (); + values.pop_back (); + values.push_back (Variant (0) - right); + } + else if (token->second == Lexer::typeOperator && + token->first == "_pos_") + { + // NOP? + } // Binary operators. else if (token->second == Lexer::typeOperator) @@ -279,6 +300,373 @@ void Eval::evaluatePostfixStack ( result = values[0]; } +//////////////////////////////////////////////////////////////////////////////// +// +// Grammar: +// Logical --> Regex {( "and" | "or" | "xor" ) Regex} +// Regex --> Equality {( "~" | "!~" ) Equality} +// Equality --> Comparative {( "==" | "=" | "!=" ) Comparative} +// Comparative --> Arithmetic {( "<=" | "<" | ">=" | ">" ) Arithmetic} +// Arithmetic --> Geometric {( "+" | "-" ) Geometric} +// Geometric --> Tag {( "*" | "/" | "%" ) Tag} +// Tag --> Unary {( "_hastag_" | "_notag_" ) Unary} +// Unary --> [( "-" | "+" | "!" )] Exponent +// Exponent --> Primitive ["^" Primitive] +// Primitive --> "(" Logical ")" | Variant +// +void Eval::infixParse ( + std::vector >& infix) const +{ + if (_debug) + std::cout << "# infixParse\n"; + + try + { + int i = 0; + if (parseLogical (infix, i)) + if (_debug) + std::cout << "# no errors.\n"; + } + + catch (const std::string& error) + { + std::cerr << error << "\n"; + } + + catch (...) + { + std::cerr << "Unknown error.\n"; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Logical --> Regex {( "and" | "or" | "xor" ) Regex} +bool Eval::parseLogical ( + std::vector >& infix, + int &i) const +{ + if (_debug) + std::cout << "# parseLogical\n"; + + if (i < infix.size () && + parseRegex (infix, i)) + { + while ((infix[i].first == "and" || + infix[i].first == "or" || + infix[i].first == "xor") && + infix[i].second == Lexer::typeOperator) + { + if (_debug) + std::cout << "# " << infix[i].first << "\n"; + + ++i; + if (! parseRegex (infix, i)) + return false; + } + + return true; + } + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// Regex --> Equality {( "~" | "!~" ) Equality} +bool Eval::parseRegex ( + std::vector >& infix, + int &i) const +{ + if (_debug) + std::cout << "# parseRegex\n"; + + if (i < infix.size () && + parseEquality (infix, i)) + { + while ((infix[i].first == "~" || + infix[i].first == "!~") && + infix[i].second == Lexer::typeOperator) + { + if (_debug) + std::cout << "# " << infix[i].first << "\n"; + + ++i; + if (! parseEquality (infix, i)) + return false; + } + + return true; + } + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// Equality --> Comparative {( "==" | "=" | "!=" ) Comparative} +bool Eval::parseEquality ( + std::vector >& infix, + int &i) const +{ + if (_debug) + std::cout << "# parseEquality\n"; + + if (i < infix.size () && + parseComparative (infix, i)) + { + while ((infix[i].first == "==" || + infix[i].first == "=" || + infix[i].first == "!=") && + infix[i].second == Lexer::typeOperator) + { + if (_debug) + std::cout << "# " << infix[i].first << "\n"; + + ++i; + if (! parseComparative (infix, i)) + return false; + } + + return true; + } + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// Comparative --> Arithmetic {( "<=" | "<" | ">=" | ">" ) Arithmetic} +bool Eval::parseComparative ( + std::vector >& infix, + int &i) const +{ + if (_debug) + std::cout << "# parseComparative\n"; + + if (i < infix.size () && + parseArithmetic (infix, i)) + { + while ((infix[i].first == "<=" || + infix[i].first == "<" || + infix[i].first == ">=" || + infix[i].first == ">") && + infix[i].second == Lexer::typeOperator) + { + if (_debug) + std::cout << "# " << infix[i].first << "\n"; + + ++i; + if (! parseArithmetic (infix, i)) + return false; + } + + return true; + } + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// Arithmetic --> Geometric {( "+" | "-" ) Geometric} +bool Eval::parseArithmetic ( + std::vector >& infix, + int &i) const +{ + if (_debug) + std::cout << "# parseArithmetic\n"; + + if (i < infix.size () && + parseGeometric (infix, i)) + { + while ((infix[i].first == "+" || + infix[i].first == "-") && + infix[i].second == Lexer::typeOperator) + { + if (_debug) + std::cout << "# " << infix[i].first << "\n"; + + ++i; + if (! parseGeometric (infix, i)) + return false; + } + + return true; + } + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// Geometric --> Tag {( "*" | "/" | "%" ) Tag} +bool Eval::parseGeometric ( + std::vector >& infix, + int &i) const +{ + if (_debug) + std::cout << "# parseGeometric\n"; + + if (i < infix.size () && + parseTag (infix, i)) + { + while ((infix[i].first == "*" || + infix[i].first == "/" || + infix[i].first == "%") && + infix[i].second == Lexer::typeOperator) + { + if (_debug) + std::cout << "# " << infix[i].first << "\n"; + + ++i; + if (! parseTag (infix, i)) + return false; + } + + return true; + } + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// Tag --> Unary {( "_hastag_" | "_notag_" ) Unary} +bool Eval::parseTag ( + std::vector >& infix, + int &i) const +{ + if (_debug) + std::cout << "# parseTag\n"; + + if (i < infix.size () && + parseUnary (infix, i)) + { + while ((infix[i].first == "_hastag_" || + infix[i].first == "_notag_") && + infix[i].second == Lexer::typeOperator) + { + if (_debug) + std::cout << "# " << infix[i].first << "\n"; + + ++i; + if (! parseUnary (infix, i)) + return false; + } + + return true; + } + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// Unary --> [( "-" | "+" | "!" )] Exponent +bool Eval::parseUnary ( + std::vector >& infix, + int &i) const +{ + if (_debug) + std::cout << "# parseUnary\n"; + + if (i < infix.size ()) + { + if (infix[i].first == "-") + { + if (_debug) + std::cout << "# '-' --> '_neg_'\n"; + + infix[i].first = "_neg_"; + ++i; + } + else if (infix[i].first == "+") + { + if (_debug) + std::cout << "# '+' --> '_pos_'\n"; + + infix[i].first = "_pos_"; + ++i; + } + else if (infix[i].first == "!") + { + if (_debug) + std::cout << "# " << infix[i].first << "\n"; + ++i; + } + } + + return parseExponent (infix, i); +} + +//////////////////////////////////////////////////////////////////////////////// +// Exponent --> Primitive ["^" Primitive] +bool Eval::parseExponent ( + std::vector >& infix, + int &i) const +{ + if (_debug) + std::cout << "# parseExponent\n"; + + if (i < infix.size () && + parsePrimitive (infix, i)) + { + while (infix[i].first == "^" && + infix[i].second == Lexer::typeOperator) + { + if (_debug) + std::cout << "# " << infix[i].first << "\n"; + + ++i; + if (! parsePrimitive (infix, i)) + return false; + } + + return true; + } + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// Primitive --> "(" Logical ")" | Variant +bool Eval::parsePrimitive ( + std::vector >& infix, + int &i) const +{ + if (_debug) + std::cout << "# parseVariant\n"; + + if (i < infix.size ()) + { + if (infix[i].first == "(") + { + if (_debug) + std::cout << "# " << infix[i].first << "\n"; + + ++i; + if (parseLogical (infix, i)) + { + if (i < infix.size () && + infix[i].first == ")") + { + if (_debug) + std::cout << "# " << infix[i].first << "\n"; + + ++i; + return true; + } + } + } + else + { + if (infix[i].second != Lexer::typeOperator) + { + if (_debug) + std::cout << "# " << infix[i].first << "\n"; + + ++i; + return true; + } + } + } + + return false; +} + //////////////////////////////////////////////////////////////////////////////// // Dijkstra Shunting Algorithm. // http://en.wikipedia.org/wiki/Shunting-yard_algorithm @@ -311,7 +699,7 @@ void Eval::evaluatePostfixStack ( // Pop the operator onto the output queue. // Exit. // -void Eval:: infixToPostfix ( +void Eval::infixToPostfix ( std::vector >& infix) const { // Short circuit. diff --git a/src/Eval.h b/src/Eval.h index 3094c303a..258673a0c 100644 --- a/src/Eval.h +++ b/src/Eval.h @@ -50,6 +50,17 @@ public: private: void evaluatePostfixStack (const std::vector >&, Variant&) const; void infixToPostfix (std::vector >&) const; + void infixParse (std::vector >&) const; + bool parseLogical (std::vector >&, int &) const; + bool parseRegex (std::vector >&, int &) const; + bool parseEquality (std::vector >&, int &) const; + bool parseComparative (std::vector >&, int &) const; + bool parseArithmetic (std::vector >&, int &) const; + bool parseGeometric (std::vector >&, int &) const; + bool parseTag (std::vector >&, int &) const; + bool parseUnary (std::vector >&, int &) const; + bool parseExponent (std::vector >&, int &) const; + bool parsePrimitive (std::vector >&, int &) const; bool identifyOperator (const std::string&, char&, int&, char&) const; private: diff --git a/src/ISO8601.cpp b/src/ISO8601.cpp index aba9bb9d6..ef6397205 100644 --- a/src/ISO8601.cpp +++ b/src/ISO8601.cpp @@ -25,6 +25,7 @@ //////////////////////////////////////////////////////////////////////////////// #include +//#include // TODO Remove #include //////////////////////////////////////////////////////////////////////////////// @@ -755,9 +756,9 @@ void ISO8601d::resolve () } //////////////////////////////////////////////////////////////////////////////// -/* void ISO8601d::dump () { +/* std::cout << "# Y=" << _year << " M=" << _month << " W=" << _week @@ -770,8 +771,8 @@ void ISO8601d::dump () << " ambi=" << _ambiguity << " --> " << _value << "\n"; -} */ +} //////////////////////////////////////////////////////////////////////////////// ISO8601p::ISO8601p () diff --git a/src/ISO8601.h b/src/ISO8601.h index 85a5779de..c892a31dd 100644 --- a/src/ISO8601.h +++ b/src/ISO8601.h @@ -59,6 +59,7 @@ private: int dayOfWeek (int, int, int); bool validate (); void resolve (); + void dump (); public: bool _ambiguity;