diff --git a/src/A3.h b/src/A3.h index 36224cd30..ceb8972eb 100644 --- a/src/A3.h +++ b/src/A3.h @@ -30,6 +30,7 @@ #include #include +#include #include #define ARGUMENTS_SEQUENCE_MAX_RANGE 1000 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 99e90b82c..47b368513 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -17,7 +17,6 @@ set (task_SRCS A3.cpp A3.h Directory.cpp Directory.h Duration.cpp Duration.h E9.cpp E9.h - Expression.cpp Expression.h File.cpp File.h Hooks.cpp Hooks.h JSON.cpp JSON.h diff --git a/src/E9.cpp b/src/E9.cpp index 1d813bd63..e9e995fb5 100644 --- a/src/E9.cpp +++ b/src/E9.cpp @@ -85,6 +85,7 @@ void E9::eval (const Task& task, std::vector & value_stack) // Unary operators. if (arg->_raw == "!") { + // TODO Are there sufficient arguments? Arg right = value_stack.back (); value_stack.pop_back (); operator_not (result, right); @@ -93,6 +94,7 @@ void E9::eval (const Task& task, std::vector & value_stack) // Binary operators. else { + // TODO Are there sufficient arguments? Arg right = value_stack.back (); value_stack.pop_back (); Arg left = value_stack.back (); @@ -180,6 +182,10 @@ void E9::operator_xor (Arg& result, Arg& left, Arg& right) } //////////////////////////////////////////////////////////////////////////////// +// TODO Special handling for priority. +// if (left._string != "H" && right._string == "H") result = true; +// else if (left._string == "L" && right._string == "M") result = true; +// else if (left._string == "" && right._string != "") result = true; void E9::operator_lt (Arg& result, Arg& left, Arg& right) { @@ -192,6 +198,11 @@ void E9::operator_lt (Arg& result, Arg& left, Arg& right) } //////////////////////////////////////////////////////////////////////////////// +// TODO Special handling for priority. +// if (left._string == right._string ) result = true; +// else if ( right._string == "H") result = true; +// else if (left._string == "L" && right._string == "M") result = true; +// else if (left._string == "" ) result = true; void E9::operator_lte (Arg& result, Arg& left, Arg& right) { @@ -204,6 +215,11 @@ void E9::operator_lte (Arg& result, Arg& left, Arg& right) } //////////////////////////////////////////////////////////////////////////////// +// TODO Special handling for priority. +// if (left._string == right._string ) result = true; +// else if (left._string == "H" ) result = true; +// else if (left._string == "M" && right._string == "L") result = true; +// else if ( right._string == "" ) result = true; void E9::operator_gte (Arg& result, Arg& left, Arg& right) { @@ -216,6 +232,10 @@ void E9::operator_gte (Arg& result, Arg& left, Arg& right) } //////////////////////////////////////////////////////////////////////////////// +// TODO Special handling for priority. +// if (left._string == "H" && right._string != "H") result = true; +// else if (left._string == "M" && right._string == "L") result = true; +// else if (left._string != "" && right._string == "") result = true; void E9::operator_gt (Arg& result, Arg& left, Arg& right) { @@ -240,6 +260,18 @@ void E9::operator_inequal (Arg& result, Arg& left, Arg& right) } //////////////////////////////////////////////////////////////////////////////// +// bool result = false; +// if (left._raw == "project" || left._raw == "recur") +// { +// left.cast (Variant::v_string); +// right.cast (Variant::v_string); +// if (right._string.length () <= left._string.length ()) +// result = compare (right._string, +// left._string.substr (0, right._string.length ()), +// (bool) case_sensitive); +// } +// else +// result = (left == right); void E9::operator_equal (Arg& result, Arg& left, Arg& right) { @@ -264,6 +296,16 @@ void E9::operator_match (Arg& result, Arg& left, Arg& right) } //////////////////////////////////////////////////////////////////////////////// +// bool case_sensitive = context.config.getBoolean ("search.case.sensitive"); +// bool result = !eval_match (left, right, case_sensitive); +// +// // Matches against description are really against either description, +// // annotations or project. +// // Short-circuit if match already failed. +// if (result && left._raw == "description") +// { +// // TODO check further. +// } void E9::operator_nomatch (Arg& result, Arg& left, Arg& right) { @@ -324,3 +366,30 @@ void E9::operator_subtract (Arg& result, Arg& left, Arg& right) } //////////////////////////////////////////////////////////////////////////////// +/* +bool Expression::eval_match (Variant& left, Variant& right, bool case_sensitive) +{ + if (right._raw_type == "rx") + { + left.cast (Variant::v_string); + right.cast (Variant::v_string); + + // Create a cached entry, if it does not already exist. + if (_regexes.find (right._string) == _regexes.end ()) + _regexes[right._string] = RX (right._string, case_sensitive); + + if (_regexes[right._string].match (left._string)) + return true; + } + else + { + left.cast (Variant::v_string); + right.cast (Variant::v_string); + if (find (left._string, right._string, (bool) case_sensitive) != std::string::npos) + return true; + } + + return false; +} +*/ +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/Expression.cpp b/src/Expression.cpp deleted file mode 100644 index 8f2ff15c5..000000000 --- a/src/Expression.cpp +++ /dev/null @@ -1,1087 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// taskwarrior - a command line task list manager. -// -// Copyright 2006 - 2011, Paul Beckingham, Federico Hernandez. -// All rights reserved. -// -// This program is free software; you can redistribute it and/or modify it under -// the terms of the GNU General Public License as published by the Free Software -// Foundation; either version 2 of the License, or (at your option) any later -// version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -// details. -// -// You should have received a copy of the GNU General Public License along with -// this program; if not, write to the -// -// Free Software Foundation, Inc., -// 51 Franklin Street, Fifth Floor, -// Boston, MA -// 02110-1301 -// USA -// -//////////////////////////////////////////////////////////////////////////////// - -#include // TODO Remove. -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -extern Context context; - -//////////////////////////////////////////////////////////////////////////////// -// Perform all the necessary steps prior to an eval call. -Expression::Expression (Arguments& arguments) -: _args (arguments) -, _prepared (false) -{ -} - -//////////////////////////////////////////////////////////////////////////////// -Expression::~Expression () -{ -} - -//////////////////////////////////////////////////////////////////////////////// -bool Expression::evalFilter (const Task& task) -{ - if (_args.size () == 0) - return true; - - if (!_prepared) - { -// _args.dump ("Expression::evalFilter"); - - expand_sequence (); - implicit_and (); - expand_tag (); - expand_pattern (); - expand_attr (); - expand_attmod (); - expand_word (); - expand_tokens (); - postfix (); - - _prepared = true; - } - - // Evaluate the expression. - std::vector value_stack; - eval (task, value_stack); - - // Coerce stack element to boolean. - Variant result (value_stack.back ()); - value_stack.pop_back (); - return result.boolean (); -} - -//////////////////////////////////////////////////////////////////////////////// -std::string Expression::evalExpression (const Task& task) -{ - if (_args.size () == 0) - return ""; - - if (!_prepared) - { -// _args.dump ("Expression::evalExpression"); - -// expand_sequence (); -// implicit_and (); -// expand_tag (); -// expand_pattern (); -// expand_attr (); -// expand_attmod (); -// expand_word (); - expand_tokens (); - postfix (); - - _prepared = true; - } - - // Evaluate the expression. - std::vector value_stack; - eval (task, value_stack); - - // Coerce stack element to string. - Variant result (value_stack.back ()); - value_stack.pop_back (); - result.cast (Variant::v_string); - return context.dom.get (result._string, task); -} - -//////////////////////////////////////////////////////////////////////////////// -void Expression::eval (const Task& task, std::vector & value_stack) -{ - // Case sensitivity is configurable. - bool case_sensitive = context.config.getBoolean ("search.case.sensitive"); - - std::vector ::const_iterator arg; - for (arg = _args.begin (); arg != _args.end (); ++arg) - { - if (arg->_second == "op") - { -// std::cout << "# operator " << arg->_first << "\n"; - - // Handle the unary operator first. - if (arg->_first == "!") - { - // Are there sufficient arguments? - if (value_stack.size () < 1) - throw std::string ("Error: Insufficient operands for '!' operator."); - - Variant right (value_stack.back ()); - if (right._raw_type == "lvalue") - { - right = Variant (context.dom.get (right._raw, task)); - right._raw = value_stack.back ()._raw; - right._raw_type = value_stack.back ()._raw_type; - } - value_stack.pop_back (); - -// std::cout << "# " << " ! " << right.dump () << "\n"; - bool result = !right; - right = Variant (result); - right._raw_type = "bool"; - -// std::cout << "# --> " << right.dump () << "\n"; - value_stack.push_back (right); - - // This only occurs here, because the unary operators are handled, and - // now the binary operators will be processed. - continue; - } - - // Are there sufficient arguments? - if (value_stack.size () < 2) - throw std::string ("Error: Insufficient operands for '") + arg->_first + "' operator."; - - // rvalue (string, rx, int, number, dom ...). - Variant right (value_stack.back ()); - if (right._raw_type == "lvalue") - { - right = Variant (context.dom.get (right._raw, task)); - right._raw = value_stack.back ()._raw; - right._raw_type = value_stack.back ()._raw_type; - } - value_stack.pop_back (); -// std::cout << "# right variant " << right.dump () << "\n"; - - // lvalue (dom). - Variant left (value_stack.back ()); - if (left._raw_type == "lvalue") - { - left = Variant (context.dom.get (left._raw, task)); - left._raw = value_stack.back ()._raw; - left._raw_type = value_stack.back ()._raw_type; - } - value_stack.pop_back (); -// std::cout << "# left variant " << left.dump () << "\n"; - - // Now the binary operators. - if (arg->_first == "and") - { -// std::cout << "# " << left.dump () << " and " << right.dump () << "\n"; - bool result = (left && right); - left = Variant (result); - left._raw_type = "bool"; - -// std::cout << "# --> " << left.dump () << "\n"; - value_stack.push_back (left); - } - - else if (arg->_first == "xor") - { -// std::cout << "# " << left.dump () << " xor " << right.dump () << "\n"; - bool left_bool = left.boolean (); - bool right_bool = right.boolean (); - bool result = (left_bool && !right_bool) || (!left_bool && right_bool); - left = Variant (result); - left._raw_type = "bool"; - -// std::cout << "# --> " << left.dump () << "\n"; - value_stack.push_back (left); - } - - else if (arg->_first == "or") - { -// std::cout << "# " << left.dump () << " or " << right.dump () << "\n"; - bool result = (left || right); - left = Variant (result); - left._raw_type = "bool"; - -// std::cout << "# --> " << left.dump () << "\n"; - value_stack.push_back (left); - } - - else if (arg->_first == "<=") - { -// std::cout << "# " << left.dump () << " <= " << right.dump () << "\n"; - bool result = false; - if (left._raw == "priority") - { - left.cast (Variant::v_string); - right.cast (Variant::v_string); - - if (left._string == right._string ) result = true; - else if ( right._string == "H") result = true; - else if (left._string == "L" && right._string == "M") result = true; - else if (left._string == "" ) result = true; - } - else - result = (left <= right); - - left = Variant (result); - left._raw_type = "bool"; - - value_stack.push_back (left); - } - - else if (arg->_first == ">=") - { -// std::cout << "# " << left.dump () << " >= " << right.dump () << "\n"; - bool result = false; - if (left._raw == "priority") - { - left.cast (Variant::v_string); - right.cast (Variant::v_string); - - if (left._string == right._string ) result = true; - else if (left._string == "H" ) result = true; - else if (left._string == "M" && right._string == "L") result = true; - else if ( right._string == "" ) result = true; - } - else - result = (left >= right); - - left = Variant (result); - left._raw_type = "bool"; - - value_stack.push_back (left); - } - - else if (arg->_first == "!~") - { -// std::cout << "# " << left.dump () << " !~ " << right.dump () << "\n"; - bool case_sensitive = context.config.getBoolean ("search.case.sensitive"); - bool result = !eval_match (left, right, case_sensitive); - - // Matches against description are really against either description, - // annotations or project. - // Short-circuit if match already failed. - if (result && left._raw == "description") - { - // TODO check further. - } - - left = Variant (result); - left._raw_type = "bool"; - -// std::cout << "# --> " << left.dump () << "\n"; - value_stack.push_back (left); - } - - else if (arg->_first == "!=") - { -// std::cout << "# " << left.dump () << " != " << right.dump () << "\n"; - bool result = (left != right); - left = Variant (result); - left._raw_type = "bool"; - -// std::cout << "# --> " << left.dump () << "\n"; - value_stack.push_back (left); - } - - else if (arg->_first == "=") - { -// std::cout << "# " << left.dump () << " = " << right.dump () << "\n"; - bool result = false; - if (left._raw == "project" || left._raw == "recur") - { - left.cast (Variant::v_string); - right.cast (Variant::v_string); - if (right._string.length () <= left._string.length ()) - result = compare (right._string, - left._string.substr (0, right._string.length ()), - (bool) case_sensitive); - } - else - result = (left == right); - - left = Variant (result); - left._raw_type = "bool"; - -// std::cout << "# --> " << left.dump () << "\n"; - value_stack.push_back (left); - } - - else if (arg->_first == ">") - { -// std::cout << "# " << left.dump () << " > " << right.dump () << "\n"; - bool result = false; - if (left._raw == "priority") - { - left.cast (Variant::v_string); - right.cast (Variant::v_string); - - if (left._string == "H" && right._string != "H") result = true; - else if (left._string == "M" && right._string == "L") result = true; - else if (left._string != "" && right._string == "") result = true; - } - else - result = (left > right); - - left = Variant (result); - left._raw_type = "bool"; - - value_stack.push_back (left); - } - - else if (arg->_first == "~") - { -// std::cout << "# " << left.dump () << " ~ " << right.dump () << "\n"; - bool case_sensitive = context.config.getBoolean ("search.case.sensitive"); - bool result = eval_match (left, right, case_sensitive); - - // Matches against description are really against either description, - // annotations or project. - // Short-circuit if match is already found. - if (!result && left._raw == "description") - { - // TODO check further. - } - - left = Variant (result); - left._raw_type = "bool"; - -// std::cout << "# --> " << left.dump () << "\n"; - value_stack.push_back (left); - } - - else if (arg->_first == "*") - { - left = left * right; - value_stack.push_back (left); - } - - else if (arg->_first == "/") - { - left = left / right; - value_stack.push_back (left); - } - - else if (arg->_first == "+") - { - left = left + right; - value_stack.push_back (left); - } - - else if (arg->_first == "-") - { - left = left - right; - value_stack.push_back (left); - } - - else if (arg->_first == "<") - { -// std::cout << "# " << left.dump () << " < " << right.dump () << "\n"; - bool result = false; - if (left._raw == "priority") - { - left.cast (Variant::v_string); - right.cast (Variant::v_string); - - if (left._string != "H" && right._string == "H") result = true; - else if (left._string == "L" && right._string == "M") result = true; - else if (left._string == "" && right._string != "") result = true; - } - else - result = (left < right); - - left = Variant (result); - left._raw_type = "bool"; - - value_stack.push_back (left); - } - - else - throw std::string ("Unsupported operator '") + arg->_first + "'."; - } - - // It's not an operator, it's an lvalue or some form of rvalue. - else - { - Variant operand; - create_variant (operand, arg->_first, arg->_second); - value_stack.push_back (operand); - } - } - - // Check for stack remnants. - if (value_stack.size () != 1) - throw std::string ("Error: Expression::eval found extra items on the stack."); -} - -//////////////////////////////////////////////////////////////////////////////// -bool Expression::eval_match (Variant& left, Variant& right, bool case_sensitive) -{ - if (right._raw_type == "rx") - { - left.cast (Variant::v_string); - right.cast (Variant::v_string); - - // Create a cached entry, if it does not already exist. - if (_regexes.find (right._string) == _regexes.end ()) - _regexes[right._string] = RX (right._string, case_sensitive); - - if (_regexes[right._string].match (left._string)) - return true; - } - else - { - left.cast (Variant::v_string); - right.cast (Variant::v_string); - if (find (left._string, right._string, (bool) case_sensitive) != std::string::npos) - return true; - } - - return false; -} - -//////////////////////////////////////////////////////////////////////////////// -void Expression::create_variant ( - Variant& variant, - const std::string& value, - const std::string& type) -{ -// std::cout << "# create_variant " << value << "/" << type << "\n"; - - // DOM references are not resolved until the operator is processed. This - // preserves the original name, which helps determine how to apply the - // operator. - if (type == "lvalue") - variant = Variant (value); - - else if (type == "int") - variant = Variant ((int) strtol (value.c_str (), NULL, 10)); - - else if (type == "number") - variant = Variant (strtod (value.c_str (), NULL)); - - else if (type == "rvalue" || - type == "string" || - type == "rx") - // TODO Is unquoteText necessary? - variant = Variant (unquoteText (value)); - - else - throw std::string ("Unrecognized operand '") + type + "'."; - - variant._raw = value; - variant._raw_type = type; -} - -//////////////////////////////////////////////////////////////////////////////// -// Convert: 1,3-5,00000000-0000-0000-0000-000000000000 -// -// To: (id=1 or (id>=3 and id<=5) or -// uuid="00000000-0000-0000-0000-000000000000") -void Expression::expand_sequence () -{ - Arguments temp; - - // Extract all the components of a sequence. - std::vector ids; - std::vector uuids; - std::vector ::iterator arg; - for (arg = _args.begin (); arg != _args.end (); ++arg) - { - if (arg->_third == "id") - Arguments::extract_id (arg->_first, ids); - - else if (arg->_third == "uuid") - Arguments::extract_uuid (arg->_first, uuids); - } - - // If there is no sequence, we're done. - if (ids.size () == 0 && uuids.size () == 0) - return; - - // Construct the algebraic form. - std::stringstream sequence; - sequence << "("; - for (unsigned int i = 0; i < ids.size (); ++i) - { - if (i) - sequence << " or "; - - sequence << "id=" << ids[i]; - } - - if (uuids.size ()) - { - if (sequence.str ().length () > 1) - sequence << " or "; - - for (unsigned int i = 0; i < uuids.size (); ++i) - { - if (i) - sequence << " or "; - - sequence << "uuid=\"" << uuids[i] << "\""; - } - } - - sequence << ")"; - - // Copy everything up to the first id/uuid. - for (arg = _args.begin (); arg != _args.end (); ++arg) - { - if (arg->_third == "id" || arg->_third == "uuid") - break; - - temp.push_back (*arg); - } - - // Now insert the new sequence expression. - temp.push_back (Triple (sequence.str (), "exp", "seq")); - - // Now copy everything after the last id/uuid. - bool found_id = false; - for (arg = _args.begin (); arg != _args.end (); ++arg) - { - if (arg->_third == "id" || arg->_third == "uuid") - found_id = true; - - else if (found_id) - temp.push_back (*arg); - } - - _args.swap (temp); -// _args.dump ("Expression::expand_sequence"); -} - -//////////////////////////////////////////////////////////////////////////////// -void Expression::expand_tokens () -{ - Arguments temp; - bool delta = false; - - // Get a list of all operators. - std::vector operators = Arguments::operator_list (); - - // Look at all args. - std::vector ::iterator arg; - for (arg = _args.begin (); arg != _args.end (); ++arg) - { - if (arg->_second == "exp") - { - tokenize (arg->_first, arg->_third, operators, temp); - delta = true; - } - else - temp.push_back (*arg); - } - - if (delta) - { - _args.swap (temp); -// _args.dump ("Expression::expand_tokens"); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Nibble the whole bloody thing. Nuke it from orbit - it's the only way to be -// sure. -void Expression::tokenize ( - const std::string& input, - const std::string& category, - std::vector & operators, - Arguments& tokens) -{ - // Date format, for both parsing and rendering. - std::string date_format = context.config.get ("dateformat"); - - // Nibble each arg token by token. - Nibbler n (input); - - // Fake polymorphism. - std::string s; - int i; - double d; - time_t t; - - while (! n.depleted ()) - { - if (n.getQuoted ('"', s, true) || - n.getQuoted ('\'', s, true)) - tokens.push_back (Triple (s, "string", category)); - - else if (n.getQuoted ('/', s, true)) - tokens.push_back (Triple (s, "pattern", category)); - - else if (n.getOneOf (operators, s)) - tokens.push_back (Triple (s, "op", category)); - - else if (n.getDOM (s)) - tokens.push_back (Triple (s, "lvalue", category)); - - else if (n.getNumber (d)) - tokens.push_back (Triple (format (d), "number", category)); - - else if (n.getDateISO (t)) - tokens.push_back (Triple (Date (t).toISO (), "date", category)); - - else if (n.getDate (date_format, t)) - tokens.push_back (Triple (Date (t).toString (date_format), "date", category)); - - else if (n.getInt (i)) - tokens.push_back (Triple (format (i), "int", category)); - - else if (n.getWord (s)) - tokens.push_back (Triple (s, "rvalue", category)); - - else - { - if (! n.getUntilWS (s)) - n.getUntilEOS (s); - - tokens.push_back (Triple (s, "string", category)); - } - - n.skipWS (); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Inserts the 'and' operator by default between terms that are not separated by -// at least one operator. -// -// Converts: -// to: and -// -void Expression::implicit_and () -{ - Arguments temp; - bool delta = false; - - std::string previous = "op"; - std::vector ::iterator arg; - for (arg = _args.begin (); arg != _args.end (); ++arg) - { - // Old-style filters need 'and' conjunctions. - if (previous != "op" && - arg->_third != "op") - { - temp.push_back (Triple ("and", "op", "-")); - delta = true; - } - - // Now insert the adjacent non-operator. - temp.push_back (*arg); - previous = arg->_third; - } - - if (delta) - { - _args.swap (temp); -// _args.dump ("Expression::implicit_and"); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Convert: +with -without -// To: tags ~ with -// tags !~ without -void Expression::expand_tag () -{ - Arguments temp; - bool delta = false; - - std::vector ::iterator arg; - for (arg = _args.begin (); arg != _args.end (); ++arg) - { - if (arg->_third == "tag") - { - char type; - std::string value; - Arguments::extract_tag (arg->_first, type, value); - - temp.push_back (Triple ("tags", "lvalue", arg->_third)); - temp.push_back (Triple (type == '+' ? "~" : "!~", "op", arg->_third)); - temp.push_back (Triple (value, "string", arg->_third)); - delta = true; - } - else - temp.push_back (*arg); - } - - if (delta) - { - _args.swap (temp); -// _args.dump ("Expression::expand_tag"); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Convert: /foo/ -// To: description ~ foo -void Expression::expand_pattern () -{ - Arguments temp; - bool delta = false; - - std::vector ::iterator arg; - for (arg = _args.begin (); arg != _args.end (); ++arg) - { - if (arg->_third == "pattern") - { - std::string value; - Arguments::extract_pattern (arg->_first, value); - - temp.push_back (Triple ("description", "lvalue", arg->_third)); - temp.push_back (Triple ("~", "op", arg->_third)); - temp.push_back (Triple (value, "rx", arg->_third)); - delta = true; - } - else - temp.push_back (*arg); - } - - if (delta) - { - _args.swap (temp); -// _args.dump ("Expression::expand_pattern"); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// +----------------+ +----------+ +------+ +---------+ -// | : | | | | = | | | -// +----------------+ +----------+ +------+ +---------+ -// | | --> | | | op | | exp | -// +----------------+ +----------+ +------+ +---------+ -// | attr | | attr | | attr | | attr | -// +----------------+ +----------+ +------+ +---------+ -// -void Expression::expand_attr () -{ - Arguments temp; - bool delta = false; - - std::vector ::iterator arg; - for (arg = _args.begin (); arg != _args.end (); ++arg) - { - if (arg->_third == "attr") - { - std::string name; - std::string value; - Arguments::extract_attr (arg->_first, name, value); - - // Canonicalize 'name'. - Arguments::is_attribute (name, name); - -/* - // Always quote the value, so that empty values, or values containing spaces - // are preserved. - value = "\"" + value + "\""; -*/ - temp.push_back (Triple (name, "lvalue", arg->_third)); - temp.push_back (Triple ("=", "op", arg->_third)); - temp.push_back (Triple (value, "exp", arg->_third)); - delta = true; - } - else - temp.push_back (*arg); - } - - if (delta) - { - _args.swap (temp); -// _args.dump ("Expression::expand_attr"); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// +----------------------+ +----------+ +-------+ +---------------+ -// | .: | | | | | | | -// +----------------------+ +----------+ +-------+ +---------------+ -// | | --> | | | op | | exp/string/rx | -// +----------------------+ +----------+ +-------+ +---------------+ -// | attr | | attr | | attr | | attr | -// +----------------------+ +----------+ +-------+ +---------------+ -// -void Expression::expand_attmod () -{ - Arguments temp; - bool delta = false; - - std::vector ::iterator arg; - for (arg = _args.begin (); arg != _args.end (); ++arg) - { - if (arg->_third == "attmod") - { - std::string name; - std::string mod; - std::string value; - std::string sense; - Arguments::extract_attmod (arg->_first, name, mod, value, sense); - Arguments::is_attribute (name, name); - Arguments::is_modifier (mod); - -/* - // Always quote the value, so that empty values, or values containing spaces - // are preserved. - std::string raw_value = value; - value = "\"" + value + "\""; -*/ - - if (mod == "before" || mod == "under" || mod == "below") - { - temp.push_back (Triple (name, "lvalue", arg->_third)); - temp.push_back (Triple ("<", "op", arg->_third)); - temp.push_back (Triple (value, "exp", arg->_third)); - } - else if (mod == "after" || mod == "over" || mod == "above") - { - temp.push_back (Triple (name, "lvalue", arg->_third)); - temp.push_back (Triple (">", "op", arg->_third)); - temp.push_back (Triple (value, "exp", arg->_third)); - } - else if (mod == "none") - { - temp.push_back (Triple (name, "lvalue", arg->_third)); - temp.push_back (Triple ("==", "op", arg->_third)); - temp.push_back (Triple ("", "string", arg->_third)); - } - else if (mod == "any") - { - temp.push_back (Triple (name, "lvalue", arg->_third)); - temp.push_back (Triple ("!=", "op", arg->_third)); - temp.push_back (Triple ("", "string", arg->_third)); - } - else if (mod == "is" || mod == "equals") - { - temp.push_back (Triple (name, "lvalue", arg->_third)); - temp.push_back (Triple ("=", "op", arg->_third)); - temp.push_back (Triple (value, "exp", arg->_third)); - } - else if (mod == "isnt" || mod == "not") - { - temp.push_back (Triple (name, "lvalue", arg->_third)); - temp.push_back (Triple ("!=", "op", arg->_third)); - temp.push_back (Triple (value, "exp", arg->_third)); - } - else if (mod == "has" || mod == "contains") - { - temp.push_back (Triple (name, "lvalue", arg->_third)); - temp.push_back (Triple ("~", "op", arg->_third)); - temp.push_back (Triple (value, "rx", arg->_third)); - } - else if (mod == "hasnt") - { - temp.push_back (Triple (name, "lvalue", arg->_third)); - temp.push_back (Triple ("!~", "op", arg->_third)); - temp.push_back (Triple (value, "rx", arg->_third)); - } - else if (mod == "startswith" || mod == "left") - { - temp.push_back (Triple (name, "lvalue", arg->_third)); - temp.push_back (Triple ("~", "op", arg->_third)); -// temp.push_back (Triple ("^" + raw_value, "rx", arg->_third)); - temp.push_back (Triple ("^" + value, "rx", arg->_third)); - } - else if (mod == "endswith" || mod == "right") - { - temp.push_back (Triple (name, "lvalue", arg->_third)); - temp.push_back (Triple ("~", "op", arg->_third)); -// temp.push_back (Triple (raw_value + "$", "rx", arg->_third)); - temp.push_back (Triple (value + "$", "rx", arg->_third)); - } - else if (mod == "word") - { - temp.push_back (Triple (name, "lvalue", arg->_third)); - temp.push_back (Triple ("~", "op", arg->_third)); -// temp.push_back (Triple ("\\b" + raw_value + "\\b", "rx", arg->_third)); - temp.push_back (Triple ("\\b" + value + "\\b", "rx", arg->_third)); - } - else if (mod == "noword") - { - temp.push_back (Triple (name, "lvalue", arg->_third)); - temp.push_back (Triple ("!~", "op", arg->_third)); -// temp.push_back (Triple ("\\b" + raw_value + "\\b", "rx", arg->_third)); - temp.push_back (Triple ("\\b" + value + "\\b", "rx", arg->_third)); - } - else - throw std::string ("Error: unrecognized attribute modifier '") + mod + "'."; - - delta = true; - } - else - temp.push_back (*arg); - } - - if (delta) - { - _args.swap (temp); -// _args.dump ("Expression::expand_attmod"); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Convert: -// To: description ~ -void Expression::expand_word () -{ - Arguments temp; - bool delta = false; - - std::vector ::iterator arg; - for (arg = _args.begin (); arg != _args.end (); ++arg) - { - if (arg->_third == "word") - { - temp.push_back (Triple ("description", "lvalue", arg->_third)); - temp.push_back (Triple ("~", "op", arg->_third)); - temp.push_back (Triple ("\"" + arg->_first + "\"", "string", arg->_third)); - - delta = true; - } - else - temp.push_back (*arg); - } - - if (delta) - { - _args.swap (temp); -// _args.dump ("Expression::expand_word"); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Dijkstra Shunting Algorithm. -// http://en.wikipedia.org/wiki/Shunting-yard_algorithm -// -// While there are tokens to be read: -// Read a token. -// 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 -// equal to that of o2, -// or o1 is right-associative and its precedence is less than that -// of o2, -// pop o2 off the stack, onto the output queue; -// push o1 onto the stack. -// If the token is a left parenthesis, then push it onto the stack. -// If the token is a right parenthesis: -// Until the token at the top of the stack is a left parenthesis, pop -// operators off the stack onto the output queue. -// Pop the left parenthesis from the stack, but not onto the output queue. -// If the token at the top of the stack is a function token, pop it onto -// 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 -// there are mismatched parentheses. -// Pop the operator onto the output queue. -// Exit. -// -void Expression::postfix () -{ - Arguments temp; - - std::pair item; - Arguments op_stack; - char type; - int precedence; - char associativity; - - std::vector ::iterator arg; - for (arg = _args.begin (); arg != _args.end (); ++arg) - { - if (arg->_first == "(") - { - op_stack.push_back (*arg); - } - else if (arg->_first == ")") - { - while (op_stack.size () > 0 && - op_stack.back ()._first != "(") - { - temp.push_back (op_stack.back ()); - 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.back ()._first, type2, precedence2, associativity2) && - ((associativity == 'l' && precedence <= precedence2) || - (associativity == 'r' && precedence < precedence2))) - { - temp.push_back (op_stack.back ()); - op_stack.pop_back (); - } - - op_stack.push_back (*arg); - } - else - { - temp.push_back (*arg); - } - } - - while (op_stack.size () != 0) - { - if (op_stack.back ()._first == "(" || - op_stack.back ()._first == ")") - throw std::string ("Mismatched parentheses in expression"); - - temp.push_back (op_stack.back ()); - op_stack.pop_back (); - } - - _args.swap (temp); -// _args.dump ("Expression::toPostfix"); -} - -//////////////////////////////////////////////////////////////////////////////// -// Test whether the _original arguments are old style or new style. -// -// Old style: no single argument corresponds to an operator, ie no 'and', 'or', -// etc. -// -// New style: at least one argument that is an operator. -// -bool Expression::is_new_style () -{ - std::vector ::iterator arg; - for (arg = _args.begin (); arg != _args.end (); ++arg) - if (Arguments::is_symbol_operator (arg->_first)) - return true; - - return false; -} - -//////////////////////////////////////////////////////////////////////////////// diff --git a/src/Expression.h b/src/Expression.h deleted file mode 100644 index 6b4237141..000000000 --- a/src/Expression.h +++ /dev/null @@ -1,73 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// taskwarrior - a command line task list manager. -// -// Copyright 2006 - 2011, Paul Beckingham, Federico Hernandez. -// All rights reserved. -// -// This program is free software; you can redistribute it and/or modify it under -// the terms of the GNU General Public License as published by the Free Software -// Foundation; either version 2 of the License, or (at your option) any later -// version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -// details. -// -// You should have received a copy of the GNU General Public License along with -// this program; if not, write to the -// -// Free Software Foundation, Inc., -// 51 Franklin Street, Fifth Floor, -// Boston, MA -// 02110-1301 -// USA -// -//////////////////////////////////////////////////////////////////////////////// -#ifndef INCLUDED_EXPRESSION -#define INCLUDED_EXPRESSION -#define L10N // Localization complete. - -#include -#include -#include -#include -#include -#include - -class Expression -{ -public: - Expression (Arguments&); - ~Expression (); - bool eval (const Task&); - bool evalFilter (const Task&); - std::string evalExpression (const Task&); - void eval (const Task&, std::vector &); - -private: - void expand_sequence (); - void implicit_and (); - void expand_tag (); - void expand_pattern (); - void expand_attr (); - void expand_attmod (); - void expand_word (); - void expand_tokens (); - void postfix (); - - void tokenize (const std::string&, const std::string&, std::vector &, Arguments&); - void create_variant (Variant&, const std::string&, const std::string&); - bool is_new_style (); - -private: - bool eval_match (Variant&, Variant&, bool); - -private: - Arguments _args; - std::map _regexes; - bool _prepared; -}; - -#endif -//////////////////////////////////////////////////////////////////////////////// diff --git a/src/commands/Command.cpp b/src/commands/Command.cpp index f2d4f4c07..cd8d8eacc 100644 --- a/src/commands/Command.cpp +++ b/src/commands/Command.cpp @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include @@ -280,8 +279,7 @@ void Command::filter (std::vector & input, std::vector & output) if (f.size ()) { - Expression e (f); - E9 expr (filt); + E9 e (filt); std::vector ::iterator task; for (task = input.begin (); task != input.end (); ++task) @@ -310,8 +308,7 @@ void Command::filter (std::vector & output) if (f.size ()) { const std::vector & pending = context.tdb2.pending.get_tasks (); - Expression e (f); - E9 expr (filt); + E9 e (filt); output.clear (); std::vector ::const_iterator task; @@ -429,9 +426,9 @@ void Command::modify_task ( if (Arguments::is_attribute (name, name)) // Canonicalize { // All values must be eval'd first. - Arguments fragment; - fragment.push_back (Triple (value, "exp", "attr")); - Expression e (fragment); + A3 fragment; + fragment.push_back (Arg (value, "attr")); + E9 e (fragment); std::string result = e.evalExpression (task); context.debug (std::string ("Eval '") + value + "' --> '" + result + "'");