diff --git a/src/Arguments.cpp b/src/Arguments.cpp index 1d3b090e9..803d4f257 100644 --- a/src/Arguments.cpp +++ b/src/Arguments.cpp @@ -89,6 +89,8 @@ static const char* modifierNames[] = }; // Supported operators, borrowed from C++, particularly the precedence. +// Note: table is sorted by length of operator string, so searches match +// longest first. static struct { std::string op; @@ -99,39 +101,27 @@ static struct } operators[] = { // Operator Precedence Type Symbol Associativity + { "and", 5, 'b', 0, 'l' }, // Conjunction + { "xor", 4, 'b', 0, 'l' }, // Disjunction + + { "or", 3, 'b', 0, 'l' }, // Disjunction + { "<=", 10, 'b', 1, 'l' }, // Less than or equal + { ">=", 10, 'b', 1, 'l' }, // Greater than or equal + { "!~", 9, 'b', 1, 'l' }, // Regex non-match + { "!=", 9, 'b', 1, 'l' }, // Inequal + + { "=", 9, 'b', 1, 'l' }, // Equal { "^", 16, 'b', 1, 'r' }, // Exponent - + { ">", 10, 'b', 1, 'l' }, // Greater than + { "~", 9, 'b', 1, 'l' }, // Regex match { "!", 15, 'u', 1, 'r' }, // Not - { "not", 15, 'u', 0, 'r' }, // Not { "-", 15, 'u', 1, 'r' }, // Unary minus - { "*", 13, 'b', 1, 'l' }, // Multiplication { "/", 13, 'b', 1, 'l' }, // Division { "%", 13, 'b', 1, 'l' }, // Modulus - { "+", 12, 'b', 1, 'l' }, // Addition { "-", 12, 'b', 1, 'l' }, // Subtraction - { "<", 10, 'b', 1, 'l' }, // Less than - { "lt", 10, 'b', 0, 'l' }, // Less than - { "<=", 10, 'b', 1, 'l' }, // Less than or equal - { "le", 10, 'b', 0, 'l' }, // Less than or equal - { ">=", 10, 'b', 1, 'l' }, // Greater than or equal - { "ge", 10, 'b', 0, 'l' }, // Greater than or equal - { ">", 10, 'b', 1, 'l' }, // Greater than - { "gt", 10, 'b', 0, 'l' }, // Greater than - - { "~", 9, 'b', 1, 'l' }, // Regex match - { "!~", 9, 'b', 1, 'l' }, // Regex non-match - { "=", 9, 'b', 1, 'l' }, // Equal - { "eq", 9, 'b', 0, 'l' }, // Equal - { "!=", 9, 'b', 1, 'l' }, // Inequal - { "ne", 9, 'b', 0, 'l' }, // Inequal - - { "and", 5, 'b', 0, 'l' }, // Conjunction - - { "or", 4, 'b', 0, 'l' }, // Disjunction - { "(", 0, 'b', 1, 'l' }, // Precedence start { ")", 0, 'b', 1, 'l' }, // Precedence end }; @@ -724,8 +714,6 @@ bool Arguments::is_attmod (const std::string& input) n.getUntilEOS (value) || n.depleted ()) { - return ! is_expression (value); - // Validate and canonicalize attribute and modifier names. if (is_attribute (name, name) && is_modifier (modifier)) diff --git a/src/Expression.cpp b/src/Expression.cpp index 785604225..4cadc10a8 100644 --- a/src/Expression.cpp +++ b/src/Expression.cpp @@ -25,6 +25,7 @@ // //////////////////////////////////////////////////////////////////////////////// +#include // TODO Remove. #include #include #include @@ -61,7 +62,7 @@ Expression::Expression (Arguments& arguments) expand_attr (); expand_attmod (); expand_word (); - expand_expression (); + expand_tokens (); postfix (); } } @@ -79,30 +80,35 @@ bool Expression::eval (Task& task) std::vector >::iterator arg; for (arg = _args.begin (); arg != _args.end (); ++arg) { - // if (arg->second != "op") - // value_stack.push_back (Variant (*arg)); + if (arg->second == "op") + { + char type; + int precedence; + char associativity; + Arguments::is_operator (arg->first, type, precedence, associativity); - // else - // { - // if (arg->first == "+") - // { - // pop - // pop - // add the two operands - // push result - // } - // else if (arg->first == "?") - // { - // } - // else if (arg->first == "?") - // { - // } - // else - // throw std::string ("Unsupported operator '") + arg->first + "'."; - // } + if (arg->first == "+") + { + // TODO pop + // TODO pop + // TODO add the operators + // TODO push the result + } + + +/* + else + throw std::string ("Unsupported operator '") + arg->first + "'."; +*/ + } +/* + else + value_stack.push_back (Variant (*arg)); +*/ } - return true; + // TODO Return the value that is on the stack. + return false; } //////////////////////////////////////////////////////////////////////////////// @@ -191,6 +197,7 @@ void Expression::expand_sequence () void Expression::expand_tokens () { Arguments temp; + bool delta = false; // Get a list of all operators. std::vector operators = Arguments::operator_list (); @@ -208,24 +215,48 @@ void Expression::expand_tokens () std::vector >::iterator arg; for (arg = _args.begin (); arg != _args.end (); ++arg) { - Nibbler n (arg->first); + if (arg->second == "exp") + { + // Nibble each arg token by token. + Nibbler n (arg->first); - if (n.getQuoted ('"', s, true) || - n.getQuoted ('\'', s, true)) - temp.push_back (std::make_pair (s, "string")); + while (! n.depleted ()) + { + if (n.getQuoted ('"', s, true) || + n.getQuoted ('\'', s, true)) + temp.push_back (std::make_pair (s, "string")); - else if (n.getNumber (d)) - temp.push_back (std::make_pair (format (d), "number")); + else if (n.getOneOf (operators, s)) + temp.push_back (std::make_pair (s, "op")); - else if (n.getInt (i)) - temp.push_back (std::make_pair (format (i), "int")); + else if (n.getDOM (s)) + temp.push_back (std::make_pair (s, "dom")); - else if (n.getDateISO (t)) - temp.push_back (std::make_pair (Date (t).toISO (), "date")); + else if (n.getNumber (d)) + temp.push_back (std::make_pair (format (d), "number")); - else if (n.getDate (date_format, t)) - temp.push_back (std::make_pair (Date (t).toString (date_format), "date")); + else if (n.getInt (i)) + temp.push_back (std::make_pair (format (i), "int")); + else if (n.getDateISO (t)) + temp.push_back (std::make_pair (Date (t).toISO (), "date")); + + else if (n.getDate (date_format, t)) + temp.push_back (std::make_pair (Date (t).toString (date_format), "date")); + + else + { + if (! n.getUntilWS (s)) + n.getUntilEOS (s); + + temp.push_back (std::make_pair (s, "?")); + } + + n.skipWS (); + } + + delta = true; + } else temp.push_back (*arg); } @@ -519,109 +550,6 @@ void Expression::expand_word () } } -//////////////////////////////////////////////////////////////////////////////// -// Look for "exp" arguments, and convert them to one of: -// "date" -// "duration" -// Lexer::tokenize -void Expression::expand_expression () -{ - Arguments temp; - bool delta = false; - - // Get a list of all operators. - std::vector operators = Arguments::operator_list (); - - // Look for all 'exp' args. - std::vector >::iterator arg; - for (arg = _args.begin (); arg != _args.end (); ++arg) - { - if (arg->second == "exp") - { - // Split expression into space-separated tokens. - std::vector tokens; - split (tokens, unquoteText (arg->first), ' '); - - std::vector ::iterator token; - for (token = tokens.begin (); token != tokens.end (); ++token) - { - if (Date::valid (*token, context.config.get ("dateformat"))) - temp.push_back (std::make_pair (*token, "date")); - - else if (Duration::valid (*token)) - temp.push_back (std::make_pair (*token, "duration")); - - else if (Arguments::is_id (*token)) - temp.push_back (std::make_pair (*token, "int")); - - else if (Arguments::is_uuid (*token)) - temp.push_back (std::make_pair (*token, "string")); - - else if (Arguments::is_operator (*token)) - temp.push_back (std::make_pair (*token, "op")); - - // The expression does not appear to be syntactic sugar, so it should be - // lexed. - else - { - Lexer lexer (*token); - lexer.skipWhitespace (true); - lexer.coalesceAlpha (true); - lexer.coalesceDigits (true); - lexer.coalesceQuoted (true); - - // Each operator of length > 1 is a special token. - std::vector ::iterator op; - for (op = operators.begin (); op != operators.end (); ++op) - if (op->length () > 1) - lexer.specialToken (*op); - - std::vector ltokens; - lexer.tokenize (ltokens); - - std::vector ::iterator ltoken; - for (ltoken = ltokens.begin (); ltoken != ltokens.end (); ++ltoken) - { - if (Date::valid (*ltoken, context.config.get ("dateformat"))) - temp.push_back (std::make_pair (*ltoken, "date")); - - else if (Duration::valid (*ltoken)) - temp.push_back (std::make_pair (*ltoken, "duration")); - - else if (Arguments::is_id (*ltoken)) - temp.push_back (std::make_pair (*ltoken, "int")); - - else if (Arguments::is_uuid (*ltoken)) - temp.push_back (std::make_pair (*ltoken, "string")); - - else if (Arguments::is_operator (*ltoken)) - temp.push_back (std::make_pair (*ltoken, "op")); - - else if (Arguments::is_id (*ltoken)) - temp.push_back (std::make_pair (*ltoken, "int")); - - else if (Arguments::is_uuid (*ltoken)) - temp.push_back (std::make_pair (*ltoken, "string")); - - else - temp.push_back (std::make_pair (*ltoken, "lvalue")); - } - } - } - - delta = true; - } - else - temp.push_back (*arg); - } - - if (delta) - { - _args.swap (temp); - _args.dump ("Expression::expand_expression"); - } -} - //////////////////////////////////////////////////////////////////////////////// // Dijkstra Shunting Algorithm. // http://en.wikipedia.org/wiki/Shunting-yard_algorithm diff --git a/src/Expression.h b/src/Expression.h index d92d333a9..39bb5b40a 100644 --- a/src/Expression.h +++ b/src/Expression.h @@ -48,7 +48,6 @@ private: void expand_attr (); void expand_attmod (); void expand_word (); - void expand_expression (); void expand_tokens (); void postfix (); diff --git a/src/Nibbler.cpp b/src/Nibbler.cpp index 20465d8c3..33454c315 100644 --- a/src/Nibbler.cpp +++ b/src/Nibbler.cpp @@ -908,6 +908,32 @@ bool Nibbler::getOneOf ( return false; } +//////////////////////////////////////////////////////////////////////////////// +bool Nibbler::getDOM (std::string& found) +{ + std::string::size_type i = mCursor; + std::string::size_type start = mCursor; + + while ( isdigit (mInput[i]) || + mInput[i] == '.' || + mInput[i] == '-' || + mInput[i] == '_' || + (! ispunct (mInput[i]) && + ! isspace (mInput[i]))) + { + ++i; + } + + if (i > mCursor) + { + found = mInput.substr (start, i - start); + mCursor = i; + return true; + } + + return false; +} + //////////////////////////////////////////////////////////////////////////////// bool Nibbler::skipN (const int quantity /* = 1 */) { diff --git a/src/Nibbler.h b/src/Nibbler.h index 3e21cbc87..d2453a10a 100644 --- a/src/Nibbler.h +++ b/src/Nibbler.h @@ -65,6 +65,7 @@ public: bool getDateISO (time_t&); bool getDate (const std::string&, time_t&); bool getOneOf (const std::vector &, std::string&); + bool getDOM (std::string&); bool skipN (const int quantity = 1); bool skip (char); diff --git a/test/arguments.t.cpp b/test/arguments.t.cpp index c6e6a2860..875a0ccd9 100644 --- a/test/arguments.t.cpp +++ b/test/arguments.t.cpp @@ -34,7 +34,7 @@ Context context; //////////////////////////////////////////////////////////////////////////////// int main (int argc, char** argv) { - UnitTest t (103); + UnitTest t (97); const char* fake[] = { @@ -66,21 +66,21 @@ int main (int argc, char** argv) "combine good"); // bool is_attr (const std::string&); - t.ok (Arguments::is_attr ("name:"), "name: -> attr"); - t.ok (Arguments::is_attr ("name:\"\""), "name:\"\" -> attr"); - t.ok (Arguments::is_attr ("name:one"), "name:one -> attr"); - t.ok (Arguments::is_attr ("name:\"one\""), "name:\"one\" -> attr"); - t.ok (Arguments::is_attr ("name:\"one two\""), "name:\"one two\" -> attr"); + t.ok (Arguments::is_attr ("project:"), "name: -> attr"); + t.ok (Arguments::is_attr ("project:\"\""), "name:\"\" -> attr"); + t.ok (Arguments::is_attr ("project:one"), "name:one -> attr"); + t.ok (Arguments::is_attr ("project:\"one\""), "name:\"one\" -> attr"); + t.ok (Arguments::is_attr ("project:\"one two\""), "name:\"one two\" -> attr"); t.notok (Arguments::is_attr ("name"), "name -> not attr"); t.notok (Arguments::is_attr ("(name=val and 1<2)"), "(name=val and 1<2) -> not attr"); // bool is_attmod (const std::string&); - t.ok (Arguments::is_attmod ("name.is:"), "name.is: -> attmod"); - t.ok (Arguments::is_attmod ("name.is:\"\""), "name.is:\"\" -> attmod"); - t.ok (Arguments::is_attmod ("name.is:one"), "name.is:one -> attmod"); - t.ok (Arguments::is_attmod ("name.is:\"one\""), "name.is:\"one\" -> attmod"); - t.ok (Arguments::is_attmod ("name.is:\"one two\""), "name.is:\"one two\" -> attmod"); + t.ok (Arguments::is_attmod ("project.is:"), "name.is: -> attmod"); + t.ok (Arguments::is_attmod ("project.is:\"\""), "name.is:\"\" -> attmod"); + t.ok (Arguments::is_attmod ("project.is:one"), "name.is:one -> attmod"); + t.ok (Arguments::is_attmod ("project.is:\"one\""), "name.is:\"one\" -> attmod"); + t.ok (Arguments::is_attmod ("project.is:\"one two\""), "name.is:\"one two\" -> attmod"); t.notok (Arguments::is_attmod ("name"), "name -> not attmod"); t.notok (Arguments::is_attmod ("(name=value and 1<2"), "(name=value and 1<2 -> not attmod"); @@ -133,7 +133,6 @@ int main (int argc, char** argv) // bool is_operator (const std::string&); t.ok (Arguments::is_operator ("^"), "^ -> operator"); t.ok (Arguments::is_operator ("!"), "! -> operator"); - t.ok (Arguments::is_operator ("not"), "not -> operator"); t.ok (Arguments::is_operator ("-"), "- -> operator"); t.ok (Arguments::is_operator ("*"), "* -> operator"); t.ok (Arguments::is_operator ("/"), "/ -> operator"); @@ -141,20 +140,15 @@ int main (int argc, char** argv) t.ok (Arguments::is_operator ("+"), "+ -> operator"); t.ok (Arguments::is_operator ("-"), "- -> operator"); t.ok (Arguments::is_operator ("<"), "< -> operator"); - t.ok (Arguments::is_operator ("lt"), "lt -> operator"); t.ok (Arguments::is_operator ("<="), "<= -> operator"); - t.ok (Arguments::is_operator ("le"), "le -> operator"); t.ok (Arguments::is_operator (">="), ">= -> operator"); - t.ok (Arguments::is_operator ("ge"), "ge -> operator"); t.ok (Arguments::is_operator (">"), "> -> operator"); - t.ok (Arguments::is_operator ("gt"), "gt -> operator"); t.ok (Arguments::is_operator ("~"), "~ -> operator"); t.ok (Arguments::is_operator ("!~"), "!~ -> operator"); t.ok (Arguments::is_operator ("="), "= -> operator"); - t.ok (Arguments::is_operator ("eq"), "eq -> operator"); t.ok (Arguments::is_operator ("!="), "!= -> operator"); - t.ok (Arguments::is_operator ("ne"), "ne -> operator"); t.ok (Arguments::is_operator ("and"), "and -> operator"); + t.ok (Arguments::is_operator ("xor"), "xor -> operator"); t.ok (Arguments::is_operator ("or"), "or -> operator"); t.ok (Arguments::is_operator ("("), "( -> operator"); t.ok (Arguments::is_operator (")"), ") -> operator"); @@ -165,7 +159,7 @@ int main (int argc, char** argv) t.ok (Arguments::is_expression ("1+1"), "1+1 -> expression"); t.ok (Arguments::is_expression ("a~b"), "a~b -> expression"); t.ok (Arguments::is_expression ("(1)"), "(1) -> expression"); - t.ok (Arguments::is_expression ("not a"), "not a -> expression"); + t.ok (Arguments::is_expression ("!a"), "!a -> expression"); // static bool valid_modifier (const std::string&); t.ok (Arguments::valid_modifier ("before"), "before -> modifier"); diff --git a/test/nibbler.t.cpp b/test/nibbler.t.cpp index 80503d41d..080571d11 100644 --- a/test/nibbler.t.cpp +++ b/test/nibbler.t.cpp @@ -34,7 +34,7 @@ Context context; //////////////////////////////////////////////////////////////////////////////// int main (int argc, char** argv) { - UnitTest t (242); + UnitTest t (257); try { @@ -408,7 +408,26 @@ int main (int argc, char** argv) t.is (s, "two", " 'twothreefour': getOneOf () -> two"); t.ok (n.getOneOf (options, s), " 'threefour': getOneOf () -> true"); t.is (s, "three", " 'threefour': getOneOf () -> three"); - t.notok (n.getOneOf (options, s), " 'four': getOneOf () -> fasle"); + t.notok (n.getOneOf (options, s), " 'four': getOneOf () -> false"); + + // bool getDOM (std::string&); + t.diag ("Nibbler::getDOM"); + n = Nibbler ("one one.two one.two.three 1.project a0-a0-a0.due"); + t.ok (n.getDOM (s), "'one' getDOM -> ok"); + t.is (s, "one", "'one' getDOM -> 'one'"); + t.ok (n.skipWS (), "skipWS"); + t.ok (n.getDOM (s), "'one.two' getDOM -> ok"); + t.is (s, "one.two", "'one.two' getDOM -> ok"); + t.ok (n.skipWS (), "skipWS"); + t.ok (n.getDOM (s), "'one.two.three' getDOM -> ok"); + t.is (s, "one.two.three", "'one.two.three' getDOM -> ok"); + t.ok (n.skipWS (), "skipWS"); + t.ok (n.getDOM (s), "'1.project' getDOM -> ok"); + t.is (s, "1.project", "'1.project' getDOM -> ok"); + t.ok (n.skipWS (), "skipWS"); + t.ok (n.getDOM (s), "'a0-a0-a0.due' getDOM -> ok"); + t.is (s, "a0-a0-a0.due", "'a0-a0-a0.due' getDOM -> ok"); + t.ok (n.depleted (), "depleted"); // bool getUntilEOL (std::string&); t.diag ("Nibbler::getUntilEOL");