diff --git a/src/Arguments.cpp b/src/Arguments.cpp index 53997621c..025040359 100644 --- a/src/Arguments.cpp +++ b/src/Arguments.cpp @@ -57,27 +57,51 @@ static const char* modifierNames[] = "noword" }; -// Supported operators, synonyms on same line. -static const char* operators[] = +// Supported operators, borrowed from C++, particularly the precedence. +static struct { - "+", // Addition, unary plus - "-", // Subtraction, unary minus - "*", // Multiplication - "/", // Division - "%", // Modulus - "~", // Substring/regex match - "!~", // Substring/regex no-match - "<", "lt", // Less than - "<=", "le", // Less than or equal - "=", "eq", // Equal - "!=", "ne", // Not equal - ">=", "ge", // Greater than or equal - ">", "gt", // Greater than - "!", "not", // Not - "and", // Conjunction - "or", // Disjunction - "(", // Precedence start - ")" // Precedence end + std::string op; + int precedence; + char type; + char associativity; +} operators[] = +{ + // Operator Precedence Type Associativity + { "^", 16, 'b', 'r' }, // Exponent + + { "!", 15, 'u', 'r' }, // Not + { "not", 15, 'u', 'r' }, // Not + { "-", 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 + { "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])) @@ -730,16 +754,34 @@ bool Arguments::is_tag (const std::string& input) } //////////////////////////////////////////////////////////////////////////////// -// "+", "-", "*", "/", "%", "~", "!~", "<" ... bool Arguments::is_operator (const std::string& input) { for (unsigned int i = 0; i < NUM_OPERATORS; ++i) - if (operators[i] == input) + if (operators[i].op == input) return true; 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) { diff --git a/src/Arguments.h b/src/Arguments.h index 6044e2b89..070c0bdf1 100644 --- a/src/Arguments.h +++ b/src/Arguments.h @@ -64,6 +64,7 @@ public: static bool is_uuid (const std::string&); static bool is_tag (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&); // TODO Decide if these are really useful. diff --git a/src/Expression.cpp b/src/Expression.cpp index 13e72c613..2fc20c48b 100644 --- a/src/Expression.cpp +++ b/src/Expression.cpp @@ -109,7 +109,6 @@ void Expression::expand_sequence () } sequence << ")"; - std::cout << "# sequence '" << sequence.str () << "'\n"; // Copy everything up to the first id/uuid. for (arg = _original.begin (); arg != _original.end (); ++arg) @@ -385,13 +384,6 @@ void Expression::to_infix () // // While there are tokens to be read: // 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: // 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 @@ -409,6 +401,8 @@ void Expression::to_infix () // the output queue. // If the stack runs out without finding a left parenthesis, then there // are mismatched parentheses. +// If the token is a number, then add it to the output queue. +// // When there are no more tokens to read: // While there are still operator tokens in the stack: // If the operator token on the top of the stack is a parenthesis, then @@ -420,6 +414,69 @@ void Expression::to_postfix () { _postfix.clear (); + std::pair item; + Arguments op_stack; + char type; + int precedence; + char associativity; + + std::vector >::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"); } diff --git a/src/Expression.h b/src/Expression.h index 5579e3ffc..8b7dfc359 100644 --- a/src/Expression.h +++ b/src/Expression.h @@ -29,6 +29,7 @@ #define L10N // Localization complete. #include +#include #include #include diff --git a/test/.gitignore b/test/.gitignore index 6b611cc1a..b26578f71 100644 --- a/test/.gitignore +++ b/test/.gitignore @@ -15,6 +15,7 @@ file.t filt.t i18n.t json.t +lexer.t lisp.t list.t nibbler.t