mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-09-10 22:20:37 +02:00
Expressions
- Implemented an operator lookup table with type, associativity and precedence. - Implemented Expression::to_postfix that generates a postfix expression list using a Dijkstra Shunt.
This commit is contained in:
parent
ed8454c202
commit
d83b2d5e36
5 changed files with 132 additions and 30 deletions
|
@ -57,27 +57,51 @@ static const char* modifierNames[] =
|
||||||
"noword"
|
"noword"
|
||||||
};
|
};
|
||||||
|
|
||||||
// Supported operators, synonyms on same line.
|
// Supported operators, borrowed from C++, particularly the precedence.
|
||||||
static const char* operators[] =
|
static struct
|
||||||
{
|
{
|
||||||
"+", // Addition, unary plus
|
std::string op;
|
||||||
"-", // Subtraction, unary minus
|
int precedence;
|
||||||
"*", // Multiplication
|
char type;
|
||||||
"/", // Division
|
char associativity;
|
||||||
"%", // Modulus
|
} operators[] =
|
||||||
"~", // Substring/regex match
|
{
|
||||||
"!~", // Substring/regex no-match
|
// Operator Precedence Type Associativity
|
||||||
"<", "lt", // Less than
|
{ "^", 16, 'b', 'r' }, // Exponent
|
||||||
"<=", "le", // Less than or equal
|
|
||||||
"=", "eq", // Equal
|
{ "!", 15, 'u', 'r' }, // Not
|
||||||
"!=", "ne", // Not equal
|
{ "not", 15, 'u', 'r' }, // Not
|
||||||
">=", "ge", // Greater than or equal
|
{ "-", 15, 'u', 'r' }, // Unary minus
|
||||||
">", "gt", // Greater than
|
|
||||||
"!", "not", // Not
|
{ "*", 13, 'b', 'l' }, // Multiplication
|
||||||
"and", // Conjunction
|
{ "/", 13, 'b', 'l' }, // Division
|
||||||
"or", // Disjunction
|
{ "%", 13, 'b', 'l' }, // Modulus
|
||||||
"(", // Precedence start
|
|
||||||
")" // Precedence end
|
{ "+", 12, 'b', 'l' }, // Addition
|
||||||
|
{ "-", 12, 'b', 'l' }, // Subtraction
|
||||||
|
|
||||||
|
{ "<", 10, 'b', 'l' }, // Less than
|
||||||
|
{ "lt", 10, 'b', 'l' }, // Less than
|
||||||
|
{ "<=", 10, 'b', 'l' }, // Less than or equal
|
||||||
|
{ "le", 10, 'b', 'l' }, // Less than or equal
|
||||||
|
{ ">=", 10, 'b', 'l' }, // Greater than or equal
|
||||||
|
{ "ge", 10, 'b', 'l' }, // Greater than or equal
|
||||||
|
{ ">", 10, 'b', 'l' }, // Greater than
|
||||||
|
{ "gt", 10, 'b', 'l' }, // Greater than
|
||||||
|
|
||||||
|
{ "~", 9, 'b', 'l' }, // Regex match
|
||||||
|
{ "!~", 9, 'b', 'l' }, // Regex non-match
|
||||||
|
{ "=", 9, 'b', 'l' }, // Equal
|
||||||
|
{ "eq", 9, 'b', 'l' }, // Equal
|
||||||
|
{ "!=", 9, 'b', 'l' }, // Inequal
|
||||||
|
{ "ne", 9, 'b', 'l' }, // Inequal
|
||||||
|
|
||||||
|
{ "and", 5, 'b', 'l' }, // Conjunction
|
||||||
|
|
||||||
|
{ "or", 4, 'b', 'l' }, // Disjunction
|
||||||
|
|
||||||
|
{ "(", 0, 'b', 'l' }, // Precedence start
|
||||||
|
{ ")", 0, 'b', 'l' }, // Precedence end
|
||||||
};
|
};
|
||||||
|
|
||||||
#define NUM_MODIFIER_NAMES (sizeof (modifierNames) / sizeof (modifierNames[0]))
|
#define NUM_MODIFIER_NAMES (sizeof (modifierNames) / sizeof (modifierNames[0]))
|
||||||
|
@ -730,16 +754,34 @@ bool Arguments::is_tag (const std::string& input)
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// "+", "-", "*", "/", "%", "~", "!~", "<" ...
|
|
||||||
bool Arguments::is_operator (const std::string& input)
|
bool Arguments::is_operator (const std::string& input)
|
||||||
{
|
{
|
||||||
for (unsigned int i = 0; i < NUM_OPERATORS; ++i)
|
for (unsigned int i = 0; i < NUM_OPERATORS; ++i)
|
||||||
if (operators[i] == input)
|
if (operators[i].op == input)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
bool Arguments::is_operator (
|
||||||
|
const std::string& input,
|
||||||
|
char& type,
|
||||||
|
int& precedence,
|
||||||
|
char& associativity)
|
||||||
|
{
|
||||||
|
for (unsigned int i = 0; i < NUM_OPERATORS; ++i)
|
||||||
|
if (operators[i].op == input)
|
||||||
|
{
|
||||||
|
type = operators[i].type;
|
||||||
|
precedence = operators[i].precedence;
|
||||||
|
associativity = operators[i].associativity;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
bool Arguments::is_expression (const std::string& input)
|
bool Arguments::is_expression (const std::string& input)
|
||||||
{
|
{
|
||||||
|
|
|
@ -64,6 +64,7 @@ public:
|
||||||
static bool is_uuid (const std::string&);
|
static bool is_uuid (const std::string&);
|
||||||
static bool is_tag (const std::string&);
|
static bool is_tag (const std::string&);
|
||||||
static bool is_operator (const std::string&);
|
static bool is_operator (const std::string&);
|
||||||
|
static bool is_operator (const std::string&, char&, int&, char&);
|
||||||
static bool is_expression (const std::string&);
|
static bool is_expression (const std::string&);
|
||||||
|
|
||||||
// TODO Decide if these are really useful.
|
// TODO Decide if these are really useful.
|
||||||
|
|
|
@ -109,7 +109,6 @@ void Expression::expand_sequence ()
|
||||||
}
|
}
|
||||||
|
|
||||||
sequence << ")";
|
sequence << ")";
|
||||||
std::cout << "# sequence '" << sequence.str () << "'\n";
|
|
||||||
|
|
||||||
// Copy everything up to the first id/uuid.
|
// Copy everything up to the first id/uuid.
|
||||||
for (arg = _original.begin (); arg != _original.end (); ++arg)
|
for (arg = _original.begin (); arg != _original.end (); ++arg)
|
||||||
|
@ -385,13 +384,6 @@ void Expression::to_infix ()
|
||||||
//
|
//
|
||||||
// While there are tokens to be read:
|
// While there are tokens to be read:
|
||||||
// Read a token.
|
// Read a token.
|
||||||
// If the token is a number, then add it to the output queue.
|
|
||||||
// If the token is a function token, then push it onto the stack.
|
|
||||||
// If the token is a function argument separator (e.g., a comma):
|
|
||||||
// Until the token at the top of the stack is a left parenthesis, pop
|
|
||||||
// operators off the stack onto the output queue. If no left parentheses
|
|
||||||
// are encountered, either the separator was misplaced or parentheses were
|
|
||||||
// mismatched.
|
|
||||||
// If the token is an operator, o1, then:
|
// If the token is an operator, o1, then:
|
||||||
// while there is an operator token, o2, at the top of the stack, and
|
// while there is an operator token, o2, at the top of the stack, and
|
||||||
// either o1 is left-associative and its precedence is less than or
|
// either o1 is left-associative and its precedence is less than or
|
||||||
|
@ -409,6 +401,8 @@ void Expression::to_infix ()
|
||||||
// the output queue.
|
// the output queue.
|
||||||
// If the stack runs out without finding a left parenthesis, then there
|
// If the stack runs out without finding a left parenthesis, then there
|
||||||
// are mismatched parentheses.
|
// are mismatched parentheses.
|
||||||
|
// If the token is a number, then add it to the output queue.
|
||||||
|
//
|
||||||
// When there are no more tokens to read:
|
// When there are no more tokens to read:
|
||||||
// While there are still operator tokens in the stack:
|
// While there are still operator tokens in the stack:
|
||||||
// If the operator token on the top of the stack is a parenthesis, then
|
// If the operator token on the top of the stack is a parenthesis, then
|
||||||
|
@ -420,6 +414,69 @@ void Expression::to_postfix ()
|
||||||
{
|
{
|
||||||
_postfix.clear ();
|
_postfix.clear ();
|
||||||
|
|
||||||
|
std::pair <std::string, std::string> item;
|
||||||
|
Arguments op_stack;
|
||||||
|
char type;
|
||||||
|
int precedence;
|
||||||
|
char associativity;
|
||||||
|
|
||||||
|
std::vector <std::pair <std::string, std::string> >::iterator arg;
|
||||||
|
for (arg = _infix.begin (); arg != _infix.end (); ++arg)
|
||||||
|
{
|
||||||
|
if (arg->first == "(")
|
||||||
|
{
|
||||||
|
op_stack.push_back (*arg);
|
||||||
|
}
|
||||||
|
else if (arg->first == ")")
|
||||||
|
{
|
||||||
|
while (op_stack.size () > 0 &&
|
||||||
|
op_stack[op_stack.size () - 1].first != "(")
|
||||||
|
{
|
||||||
|
_postfix.push_back (op_stack[op_stack.size () - 1]);
|
||||||
|
op_stack.pop_back ();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (op_stack.size ())
|
||||||
|
{
|
||||||
|
op_stack.pop_back ();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw std::string ("Mismatched parentheses in expression");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (Arguments::is_operator (arg->first, type, precedence, associativity))
|
||||||
|
{
|
||||||
|
char type2;
|
||||||
|
int precedence2;
|
||||||
|
char associativity2;
|
||||||
|
while (op_stack.size () > 0 &&
|
||||||
|
Arguments::is_operator (op_stack[op_stack.size () - 1].first, type2, precedence2, associativity2) &&
|
||||||
|
((associativity == 'l' && precedence <= precedence2) ||
|
||||||
|
(associativity == 'r' && precedence < precedence2)))
|
||||||
|
{
|
||||||
|
_postfix.push_back (op_stack[op_stack.size () - 1]);
|
||||||
|
op_stack.pop_back ();
|
||||||
|
}
|
||||||
|
|
||||||
|
op_stack.push_back (*arg);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_postfix.push_back (*arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (op_stack.size () != 0)
|
||||||
|
{
|
||||||
|
if (op_stack[op_stack.size () - 1].first == "(" ||
|
||||||
|
op_stack[op_stack.size () - 1].first == ")")
|
||||||
|
throw std::string ("Mismatched parentheses in expression");
|
||||||
|
|
||||||
|
_postfix.push_back (op_stack[op_stack.size () - 1]);
|
||||||
|
op_stack.pop_back ();
|
||||||
|
}
|
||||||
|
|
||||||
_postfix.dump ("Expression::toPostfix");
|
_postfix.dump ("Expression::toPostfix");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#define L10N // Localization complete.
|
#define L10N // Localization complete.
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <stack>
|
||||||
#include <Arguments.h>
|
#include <Arguments.h>
|
||||||
#include <Task.h>
|
#include <Task.h>
|
||||||
|
|
||||||
|
|
1
test/.gitignore
vendored
1
test/.gitignore
vendored
|
@ -15,6 +15,7 @@ file.t
|
||||||
filt.t
|
filt.t
|
||||||
i18n.t
|
i18n.t
|
||||||
json.t
|
json.t
|
||||||
|
lexer.t
|
||||||
lisp.t
|
lisp.t
|
||||||
list.t
|
list.t
|
||||||
nibbler.t
|
nibbler.t
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue