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.
This commit is contained in:
Paul Beckingham 2014-04-06 00:29:14 -04:00
parent 620f9b40b2
commit 70ba19fd5e
5 changed files with 424 additions and 21 deletions

View file

@ -34,6 +34,8 @@ Bugs
- TD-42 Cannot compile taskd - GNUTLS_VERSION undefined in diag.cpp (thanks - TD-42 Cannot compile taskd - GNUTLS_VERSION undefined in diag.cpp (thanks
to Michele Vetturi). to Michele Vetturi).
- TD-45 Fix preprocessor define (thanks to Jochen Sprickerhof). - 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-1282 incorrect URLs in man task-sync (thanks to Jeremiah Marks).
- TW-1288 Added missing locking for task modifications (thanks to Kosta H, - TW-1288 Added missing locking for task modifications (thanks to Kosta H,
Ralph Bean, Adam Coddington). Ralph Bean, Adam Coddington).

View file

@ -40,32 +40,38 @@ static struct
} operators[] = } operators[] =
{ {
// Operator Precedence Type Associativity // Operator Precedence Type Associativity
{ "and", 5, 'b', 'l' }, // Conjunction
{ "xor", 4, 'b', 'l' }, // Disjunction
{ "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
{ "==", 9, 'b', 'l' }, // Equal
{ "=", 9, 'b', 'l' }, // Equal
{ "^", 16, 'b', 'r' }, // Exponent { "^", 16, 'b', 'r' }, // Exponent
{ ">", 10, 'b', 'l' }, // Greater than
{ "~", 9, 'b', 'l' }, // Regex match
{ "!", 15, 'u', 'r' }, // Not
{ "_hastag_", 9, 'b', 'l'}, // +tag [Pseudo-op] { "!", 15, 'u', 'r' }, // Unary not
{ "_notag_", 9, 'b', 'l'}, // -tag [Pseudo-op] { "_neg_", 15, 'u', 'r' }, // Unary minus
{ "_pos_", 15, 'u', 'r' }, // Unary plus
{ "_hastag_", 14, 'b', 'l'}, // +tag [Pseudo-op]
{ "_notag_", 14, 'b', 'l'}, // -tag [Pseudo-op]
{ "-", 15, 'u', 'r' }, // Unary minus
{ "*", 13, 'b', 'l' }, // Multiplication { "*", 13, 'b', 'l' }, // Multiplication
{ "/", 13, 'b', 'l' }, // Division { "/", 13, 'b', 'l' }, // Division
{ "%", 13, 'b', 'l' }, // Modulus { "%", 13, 'b', 'l' }, // Modulus
{ "+", 12, 'b', 'l' }, // Addition { "+", 12, 'b', 'l' }, // Addition
{ "-", 12, 'b', 'l' }, // Subtraction { "-", 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 { "<", 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 start
{ ")", 0, 'b', 'l' }, // Precedence end { ")", 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"; std::cout << "# token infix '" << token << "' " << Lexer::type_name (type) << "\n";
} }
// Parse for syntax checking and operator replacement.
infixParse (tokens);
// Convert infix --> postfix. // Convert infix --> postfix.
infixToPostfix (tokens); infixToPostfix (tokens);
@ -163,7 +172,7 @@ void Eval::evaluatePostfixStack (
std::vector <std::pair <std::string, Lexer::Type> >::const_iterator token; std::vector <std::pair <std::string, Lexer::Type> >::const_iterator token;
for (token = tokens.begin (); token != tokens.end (); ++token) for (token = tokens.begin (); token != tokens.end (); ++token)
{ {
// Unary operator. // Unary operators.
if (token->second == Lexer::typeOperator && if (token->second == Lexer::typeOperator &&
token->first == "!") token->first == "!")
{ {
@ -171,6 +180,18 @@ void Eval::evaluatePostfixStack (
values.pop_back (); values.pop_back ();
values.push_back (! right); 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. // Binary operators.
else if (token->second == Lexer::typeOperator) else if (token->second == Lexer::typeOperator)
@ -279,6 +300,373 @@ void Eval::evaluatePostfixStack (
result = values[0]; 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 <std::pair <std::string, Lexer::Type> >& 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 <std::pair <std::string, Lexer::Type> >& 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 <std::pair <std::string, Lexer::Type> >& 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 <std::pair <std::string, Lexer::Type> >& 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 <std::pair <std::string, Lexer::Type> >& 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 <std::pair <std::string, Lexer::Type> >& 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 <std::pair <std::string, Lexer::Type> >& 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 <std::pair <std::string, Lexer::Type> >& 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 <std::pair <std::string, Lexer::Type> >& 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 <std::pair <std::string, Lexer::Type> >& 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 <std::pair <std::string, Lexer::Type> >& 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. // Dijkstra Shunting Algorithm.
// http://en.wikipedia.org/wiki/Shunting-yard_algorithm // http://en.wikipedia.org/wiki/Shunting-yard_algorithm

View file

@ -50,6 +50,17 @@ public:
private: private:
void evaluatePostfixStack (const std::vector <std::pair <std::string, Lexer::Type> >&, Variant&) const; void evaluatePostfixStack (const std::vector <std::pair <std::string, Lexer::Type> >&, Variant&) const;
void infixToPostfix (std::vector <std::pair <std::string, Lexer::Type> >&) const; void infixToPostfix (std::vector <std::pair <std::string, Lexer::Type> >&) const;
void infixParse (std::vector <std::pair <std::string, Lexer::Type> >&) const;
bool parseLogical (std::vector <std::pair <std::string, Lexer::Type> >&, int &) const;
bool parseRegex (std::vector <std::pair <std::string, Lexer::Type> >&, int &) const;
bool parseEquality (std::vector <std::pair <std::string, Lexer::Type> >&, int &) const;
bool parseComparative (std::vector <std::pair <std::string, Lexer::Type> >&, int &) const;
bool parseArithmetic (std::vector <std::pair <std::string, Lexer::Type> >&, int &) const;
bool parseGeometric (std::vector <std::pair <std::string, Lexer::Type> >&, int &) const;
bool parseTag (std::vector <std::pair <std::string, Lexer::Type> >&, int &) const;
bool parseUnary (std::vector <std::pair <std::string, Lexer::Type> >&, int &) const;
bool parseExponent (std::vector <std::pair <std::string, Lexer::Type> >&, int &) const;
bool parsePrimitive (std::vector <std::pair <std::string, Lexer::Type> >&, int &) const;
bool identifyOperator (const std::string&, char&, int&, char&) const; bool identifyOperator (const std::string&, char&, int&, char&) const;
private: private:

View file

@ -25,6 +25,7 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <cmake.h> #include <cmake.h>
//#include <iostream> // TODO Remove
#include <ISO8601.h> #include <ISO8601.h>
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -755,9 +756,9 @@ void ISO8601d::resolve ()
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/*
void ISO8601d::dump () void ISO8601d::dump ()
{ {
/*
std::cout << "# Y=" << _year std::cout << "# Y=" << _year
<< " M=" << _month << " M=" << _month
<< " W=" << _week << " W=" << _week
@ -770,8 +771,8 @@ void ISO8601d::dump ()
<< " ambi=" << _ambiguity << " ambi=" << _ambiguity
<< " --> " << _value << " --> " << _value
<< "\n"; << "\n";
}
*/ */
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
ISO8601p::ISO8601p () ISO8601p::ISO8601p ()

View file

@ -59,6 +59,7 @@ private:
int dayOfWeek (int, int, int); int dayOfWeek (int, int, int);
bool validate (); bool validate ();
void resolve (); void resolve ();
void dump ();
public: public:
bool _ambiguity; bool _ambiguity;