From c77c6f172f9d4f5ec249b69192579ad24cc3196c Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Fri, 17 Jun 2011 01:00:03 -0400 Subject: [PATCH] Expressions - Many operators implemented - DOM::get partially implemented --- src/Arguments.cpp | 46 ++++++--- src/DOM.cpp | 169 ++++++++++++++++++++++++--------- src/DOM.h | 2 + src/Expression.cpp | 188 ++++++++++++++++++++++++++++++------- src/Expression.h | 3 + src/commands/CmdCustom.cpp | 8 +- 6 files changed, 315 insertions(+), 101 deletions(-) diff --git a/src/Arguments.cpp b/src/Arguments.cpp index f765eacd7..5ff14619c 100644 --- a/src/Arguments.cpp +++ b/src/Arguments.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include extern Context context; @@ -111,11 +112,11 @@ static struct { "!=", 9, 'b', 1, 'l' }, // Inequal { "=", 9, 'b', 1, 'l' }, // Equal - { "^", 16, 'b', 1, 'r' }, // Exponent +// { "^", 16, 'b', 1, 'r' }, // Exponent { ">", 10, 'b', 1, 'l' }, // Greater than { "~", 9, 'b', 1, 'l' }, // Regex match { "!", 15, 'u', 1, 'r' }, // Not - { "-", 15, 'u', 1, 'r' }, // Unary minus +// { "-", 15, 'u', 1, 'r' }, // Unary minus { "*", 13, 'b', 1, 'l' }, // Multiplication { "/", 13, 'b', 1, 'l' }, // Division { "%", 13, 'b', 1, 'l' }, // Modulus @@ -149,8 +150,11 @@ void Arguments::capture (int argc, const char** argv) { for (int i = 0; i < argc; ++i) { + // The "i != 0" guarantees that argv[0] does not get split, because it may + // be an absolute path, and Expression::expand_tokens would make a dog's + // dinner out of it. std::vector parts; - if (is_multipart (argv[i], parts)) + if (is_multipart (argv[i], parts) && i != 0) { std::vector ::iterator part; for (part = parts.begin (); part != parts.end (); ++part) @@ -270,22 +274,16 @@ void Arguments::categorize () } // rc: + // Note: This doesn't break a sequence chain. else if (arg->first.substr (0, 3) == "rc:") { - found_non_sequence = true; - if (found_sequence) - found_something_after_sequence = true; - arg->second = "rc"; } // rc.: + // Note: This doesn't break a sequence chain. else if (arg->first.substr (0, 3) == "rc.") { - found_non_sequence = true; - if (found_sequence) - found_something_after_sequence = true; - arg->second = "override"; } @@ -447,7 +445,7 @@ void Arguments::categorize () found_sequence) { // TODO Invoke the info command. -// std::cout << STRING_ASSUME_INFO << "\n"; + std::cout << STRING_ASSUME_INFO << "\n"; // parseCmd.command = "info"; } } @@ -925,10 +923,28 @@ bool Arguments::is_attribute (const std::string& input, std::string& canonical) // Guess at the full attribute name. std::vector candidates; for (unsigned i = 0; i < NUM_ATT_NAMES; ++i) + { + // Short-circuit: exact matches cause immediate return. + if (attributeNames[i] == input) + { + canonical = input; + return true; + } + candidates.push_back (attributeNames[i]); + } for (unsigned i = 0; i < NUM_MODIFIABLE_ATT_NAMES; ++i) + { + // Short-circuit: exact matches cause immediate return. + if (modifiableAttributeNames[i] == input) + { + canonical = input; + return true; + } + candidates.push_back (modifiableAttributeNames[i]); + } std::vector matches; autoComplete (input, candidates, matches); @@ -948,7 +964,13 @@ bool Arguments::is_modifier (const std::string& input) // Guess at the full attribute name. std::vector candidates; for (unsigned i = 0; i < NUM_MODIFIER_NAMES; ++i) + { + // Short-circuit: exact matches cause immediate return. + if (modifierNames[i] == input) + return true; + candidates.push_back (modifierNames[i]); + } std::vector matches; autoComplete (input, candidates, matches); diff --git a/src/DOM.cpp b/src/DOM.cpp index 916ea83d4..bfdde971c 100644 --- a/src/DOM.cpp +++ b/src/DOM.cpp @@ -63,26 +63,6 @@ DOM::~DOM () // context.width // context.height // -// . -// .{entry,start,end,due,until,wait} -// .{entry,start,end,due,until,wait}.year -// .{entry,start,end,due,until,wait}.month -// .{entry,start,end,due,until,wait}.day -// .{entry,start,end,due,until,wait}.hour -// .{entry,start,end,due,until,wait}.minute -// .{entry,start,end,due,until,wait}.second -// .description -// .project -// .priority -// .parent -// .status -// .tags -// .urgency -// .recur -// .depends -// -// . -// // TODO report.. <-- context.reports // TODO stats. <-- context.stats // @@ -93,8 +73,6 @@ const std::string DOM::get (const std::string& name) { int len = name.length (); Nibbler n (name); - int id; - std::string uuid; // Primitives if (is_primitive (name)) @@ -137,30 +115,6 @@ const std::string DOM::get (const std::string& name) throw format (STRING_DOM_UNREC, name); } - // . - else if (n.getInt (id)) - { - if (n.skip ('.')) - { - std::string ref; - n.getUntilEOS (ref); - - if (ref == "description") - ; - // TODO return task.get ("description"); - } - } - - // TODO . - else if (n.getUUID (uuid)) - { - std::string attr; - if (n.skip ('.') && - n.getUntilEOS (attr)) - { - } - } - // TODO report. // TODO stats. @@ -205,6 +159,129 @@ const std::string DOM::get (const std::string& name) return ""; } +//////////////////////////////////////////////////////////////////////////////// +// DOM Supported References: +// +// TODO .{entry,start,end,due,until,wait} +// TODO .description +// TODO .project +// TODO .priority +// TODO .parent +// TODO .status +// TODO .tags +// TODO .urgency +// TODO .recur +// TODO .depends +// +// TODO .{entry,start,end,due,until,wait} +// TODO .description +// TODO .project +// TODO .priority +// TODO .parent +// TODO .status +// TODO .tags +// TODO .urgency +// TODO .recur +// TODO .depends +// +// {entry,start,end,due,until,wait} +// description +// project +// priority +// parent +// status +// tags +// urgency +// recur +// depends +// +const std::string DOM::get (const std::string& name, Task& task) +{ + Nibbler n (name); + int id; + std::string uuid; + + // Primitives + if (is_primitive (name)) + return name; + + // . + else if (n.getInt (id)) + { + if (n.skip ('.')) + { + // TODO Obtain task 'id' from TDB2. + + std::string attr; + n.getUntilEOS (attr); + + if (attr == "description") return task.get ("description"); + else if (attr == "status") return task.get ("status"); + else if (attr == "project") return task.get ("project"); + else if (attr == "priority") return task.get ("priority"); + else if (attr == "parent") return task.get ("parent"); + else if (attr == "tags") return task.get ("tags"); + else if (attr == "urgency") return format (task.urgency (), 4, 3); + else if (attr == "recur") return task.get ("recur"); + else if (attr == "depends") return task.get ("depends"); + else if (attr == "entry") return task.get ("entry"); + else if (attr == "start") return task.get ("start"); + else if (attr == "end") return task.get ("end"); + else if (attr == "due") return task.get ("due"); + else if (attr == "until") return task.get ("until"); + else if (attr == "wait") return task.get ("wait"); + } + } + + // . + else if (n.getUUID (uuid)) + { + if (n.skip ('.')) + { + // TODO Obtain task 'uuid' from TDB2. + + std::string attr; + n.getUntilEOS (attr); + + if (attr == "description") return task.get ("description"); + else if (attr == "status") return task.get ("status"); + else if (attr == "project") return task.get ("project"); + else if (attr == "priority") return task.get ("priority"); + else if (attr == "parent") return task.get ("parent"); + else if (attr == "tags") return task.get ("tags"); + else if (attr == "urgency") return format (task.urgency (), 4, 3); + else if (attr == "recur") return task.get ("recur"); + else if (attr == "depends") return task.get ("depends"); + else if (attr == "entry") return task.get ("entry"); + else if (attr == "start") return task.get ("start"); + else if (attr == "end") return task.get ("end"); + else if (attr == "due") return task.get ("due"); + else if (attr == "until") return task.get ("until"); + else if (attr == "wait") return task.get ("wait"); + } + } + + // [.] + if (name == "description") return task.get ("description"); + else if (name == "status") return task.get ("status"); + else if (name == "project") return task.get ("project"); + else if (name == "priority") return task.get ("priority"); + else if (name == "parent") return task.get ("parent"); + else if (name == "tags") return task.get ("tags"); + else if (name == "urgency") return format (task.urgency (), 4, 3); + else if (name == "recur") return task.get ("recur"); + else if (name == "depends") return task.get ("depends"); + else if (name == "entry") return task.get ("entry"); + else if (name == "start") return task.get ("start"); + else if (name == "end") return task.get ("end"); + else if (name == "due") return task.get ("due"); + else if (name == "until") return task.get ("until"); + else if (name == "wait") return task.get ("wait"); + + // Delegate to the context-free version of DOM::get. + return this->get (name); +} + //////////////////////////////////////////////////////////////////////////////// void DOM::set (const std::string& name, const std::string& value) { diff --git a/src/DOM.h b/src/DOM.h index d5b553902..790441d3a 100644 --- a/src/DOM.h +++ b/src/DOM.h @@ -29,6 +29,7 @@ #define L10N // Localization complete. #include +#include #include class DOM @@ -38,6 +39,7 @@ public: ~DOM (); const std::string get (const std::string&); + const std::string get (const std::string&, Task&); void set (const std::string&, const std::string&); private: diff --git a/src/Expression.cpp b/src/Expression.cpp index b81aa90d8..2455d1832 100644 --- a/src/Expression.cpp +++ b/src/Expression.cpp @@ -46,12 +46,6 @@ Expression::Expression (Arguments& arguments) { _args.dump ("Expression::Expression"); - bool new_style = is_new_style () && context.config.getBoolean ("expressions"); - if (new_style) - context.debug ("Filter --> new"); - else - context.debug ("Filter --> old"); - expand_sequence (); implicit_and (); expand_tag (); @@ -84,108 +78,231 @@ bool Expression::eval (Task& task) { if (arg->second == "op") { - // We already know it's an operator, but we need to know more. Or do we? -/* - char type; - int precedence; - char associativity; - Arguments::is_operator (arg->first, type, precedence, associativity); -*/ - // TODO Need helpers that pop, and error out if necessary. - if (arg->first == "+") + if (arg->first == "and") { + if (value_stack.size () < 2) + throw std::string ("Error: Insufficient operands."); + Variant right (value_stack.back ()); value_stack.pop_back (); Variant left (value_stack.back ()); value_stack.pop_back (); - context.debug ("eval left=" + left.format ()); - context.debug ("eval right=" + right.format ()); - left = left + right; - context.debug ("eval + --> " + left.format ()); - + left = left && right; value_stack.push_back (left); } - else if (arg->first == "and") - { - } - else if (arg->first == "xor") { + if (value_stack.size () < 2) + throw std::string ("Error: Insufficient operands."); + + Variant right (value_stack.back ()); + value_stack.pop_back (); + Variant left (value_stack.back ()); + value_stack.pop_back (); + + left = (left && !right) || (!left && right); + value_stack.push_back (left); } else if (arg->first == "or") { + if (value_stack.size () < 2) + throw std::string ("Error: Insufficient operands."); + + Variant right (value_stack.back ()); + value_stack.pop_back (); + Variant left (value_stack.back ()); + value_stack.pop_back (); + + left = left || right; + value_stack.push_back (left); } else if (arg->first == "<=") { + if (value_stack.size () < 2) + throw std::string ("Error: Insufficient operands."); + + Variant right (value_stack.back ()); + value_stack.pop_back (); + Variant left (value_stack.back ()); + value_stack.pop_back (); + + left = left <= right; + value_stack.push_back (left); } else if (arg->first == ">=") { + if (value_stack.size () < 2) + throw std::string ("Error: Insufficient operands."); + + Variant right (value_stack.back ()); + value_stack.pop_back (); + Variant left (value_stack.back ()); + value_stack.pop_back (); + + left = left >= right; + value_stack.push_back (left); } else if (arg->first == "!~") { + if (value_stack.size () < 2) + throw std::string ("Error: Insufficient operands."); + + // TODO +/* + if left == "description" then it really means description or annotations or project. + if left == "tags" then it just means tags. +*/ } else if (arg->first == "!=") { + if (value_stack.size () < 2) + throw std::string ("Error: Insufficient operands."); + + Variant right (value_stack.back ()); + value_stack.pop_back (); + Variant left (value_stack.back ()); + value_stack.pop_back (); + + left = left != right; + value_stack.push_back (left); } else if (arg->first == "=") { - } + if (value_stack.size () < 2) + throw std::string ("Error: Insufficient operands."); - else if (arg->first == "^") - { + Variant right (value_stack.back ()); + value_stack.pop_back (); + Variant left (value_stack.back ()); + value_stack.pop_back (); + + left = left == right; + value_stack.push_back (left); } else if (arg->first == ">") { + if (value_stack.size () < 2) + throw std::string ("Error: Insufficient operands."); + + Variant right (value_stack.back ()); + value_stack.pop_back (); + Variant left (value_stack.back ()); + value_stack.pop_back (); + + left = left > right; + value_stack.push_back (left); } else if (arg->first == "~") { + if (value_stack.size () < 2) + throw std::string ("Error: Insufficient operands."); + + // TODO +/* + if left == "description" then it really means description or annotations or project. + if left == "tags" then it just means tags. +*/ } else if (arg->first == "!") { + if (value_stack.size () < 1) + throw std::string ("Error: Insufficient operands."); + + Variant left (value_stack.back ()); + value_stack.pop_back (); + + left = ! left; + value_stack.push_back (left); } else if (arg->first == "*") { + if (value_stack.size () < 2) + throw std::string ("Error: Insufficient operands."); + + Variant right (value_stack.back ()); + value_stack.pop_back (); + Variant left (value_stack.back ()); + value_stack.pop_back (); + + left = left * right; + value_stack.push_back (left); } else if (arg->first == "/") { + if (value_stack.size () < 2) + throw std::string ("Error: Insufficient operands."); + + Variant right (value_stack.back ()); + value_stack.pop_back (); + Variant left (value_stack.back ()); + value_stack.pop_back (); + + left = left / right; + value_stack.push_back (left); } else if (arg->first == "%") { + if (value_stack.size () < 2) + throw std::string ("Error: Insufficient operands."); + + // TODO Implement modulus. } else if (arg->first == "+") { + if (value_stack.size () < 2) + throw std::string ("Error: Insufficient operands."); + + Variant right (value_stack.back ()); + value_stack.pop_back (); + Variant left (value_stack.back ()); + value_stack.pop_back (); + + left = left + right; + value_stack.push_back (left); } else if (arg->first == "-") { + if (value_stack.size () < 2) + throw std::string ("Error: Insufficient operands."); + + Variant right (value_stack.back ()); + value_stack.pop_back (); + Variant left (value_stack.back ()); + value_stack.pop_back (); + + left = left - right; + value_stack.push_back (left); } else if (arg->first == "<") { - } + if (value_stack.size () < 2) + throw std::string ("Error: Insufficient operands."); - else if (arg->first == "(") - { - } + Variant right (value_stack.back ()); + value_stack.pop_back (); + Variant left (value_stack.back ()); + value_stack.pop_back (); - else if (arg->first == ")") - { + left = left < right; + value_stack.push_back (left); } else @@ -196,7 +313,7 @@ bool Expression::eval (Task& task) else { if (arg->second == "lvalue") - value_stack.push_back (Variant (context.dom.get (arg->first))); + value_stack.push_back (Variant (context.dom.get (arg->first, task))); else if (arg->second == "int") value_stack.push_back (Variant ((int) strtol (arg->first.c_str (), NULL, 10))); @@ -212,12 +329,11 @@ bool Expression::eval (Task& task) else throw std::string ("Error: Expression::eval unrecognized operand '") + + "'."; } - } // Coerce stack element to boolean. Variant result (value_stack.back ()); - context.debug ("eval result=" + result.format ()); + //context.debug ("eval result=" + result.format ()); value_stack.pop_back (); bool pass_fail = result.boolean (); diff --git a/src/Expression.h b/src/Expression.h index 39bb5b40a..4f0f0131c 100644 --- a/src/Expression.h +++ b/src/Expression.h @@ -53,6 +53,9 @@ private: bool is_new_style (); +private: + + private: Arguments _args; }; diff --git a/src/commands/CmdCustom.cpp b/src/commands/CmdCustom.cpp index e7dba60a7..37150e72f 100644 --- a/src/commands/CmdCustom.cpp +++ b/src/commands/CmdCustom.cpp @@ -92,7 +92,7 @@ int CmdCustom::execute (std::string& output) context.tdb.commit (); context.tdb.unlock (); -//////////////////////////////////// + // Filter. Arguments f = context.args.extract_read_only_filter (); Expression e (f); @@ -102,12 +102,6 @@ int CmdCustom::execute (std::string& output) if (e.eval (*task)) filtered.push_back (*task); - std::cout << "# tasks=" << tasks.size () << "\n" - << "# filtered=" << filtered.size () << "\n"; -//return 0; - -//////////////////////////////////// - // Sort the tasks. std::vector sequence; for (unsigned int i = 0; i < filtered.size (); ++i)