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:
Paul Beckingham 2011-06-07 00:25:21 -04:00
parent ed8454c202
commit d83b2d5e36
5 changed files with 132 additions and 30 deletions

View file

@ -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)
{ {

View file

@ -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.

View file

@ -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");
} }

View file

@ -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
View file

@ -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