diff --git a/src/DOM.cpp b/src/DOM.cpp index 43b219aef..2b7145597 100644 --- a/src/DOM.cpp +++ b/src/DOM.cpp @@ -82,10 +82,11 @@ const std::string DOM::get (const std::string& name) int len = name.length (); Nibbler n (name); + std::string copy_name (name); // Primitives - if (is_primitive (name)) - return /*_cache[name] =*/ name; + if (is_literal (copy_name)) + return /*_cache[name] =*/ copy_name; // rc. --> context.config else if (len > 3 && @@ -162,6 +163,7 @@ const std::string DOM::get (const std::string& name) throw format (STRING_DOM_UNREC, name); } + // Pass-through. return /*_cache[name] =*/ name; } @@ -190,16 +192,16 @@ const std::string DOM::get (const std::string& name) // TODO .recur // TODO .depends // -// {entry,start,end,due,until,wait} -// description -// project -// priority -// parent -// status -// tags -// urgency -// recur -// 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, const Task& task) { @@ -215,8 +217,13 @@ const std::string DOM::get (const std::string& name, const Task& task) std::string uuid; // Primitives - if (is_primitive (name)) - return /*_cache[name] =*/ name; + std::string copy_name (name); + if (is_literal (copy_name)) + return /*_cache[name] =*/ copy_name; + + // + else if (task.has (name)) + return task.get (name); // . else if (n.getInt (id)) @@ -248,15 +255,17 @@ const std::string DOM::get (const std::string& name, const Task& task) } } - // [.] - if (name == "id") return format (task.id); - else if (name == "urgency") return format (task.urgency_c (), 4, 3); - else return task.get (name); + // [] . + if (name == ".id") return format (task.id); + else if (name == ".urgency") return format (task.urgency_c (), 4, 3); + else if (task.has (name.substr (1))) return task.get (name.substr (1)); // Delegate to the context-free version of DOM::get. return this->get (name); } +// TODO Need a context-specific DOM::set. + //////////////////////////////////////////////////////////////////////////////// void DOM::set (const std::string& name, const std::string& value) { @@ -275,7 +284,7 @@ void DOM::set (const std::string& name, const std::string& value) } //////////////////////////////////////////////////////////////////////////////// -bool DOM::is_primitive (const std::string& input) +bool DOM::is_literal (std::string& input) { std::string s; double d; @@ -283,7 +292,11 @@ bool DOM::is_primitive (const std::string& input) // Date? if (Date::valid (input, context.config.get ("dateformat"))) + { + input = Date (input).toEpochString (); + std::cout << "# DOM::is_literal '" << input << "' --> date\n"; return true; + } // Duration? if (Duration::valid (input)) @@ -309,7 +322,7 @@ bool DOM::is_primitive (const std::string& input) if (n.getInt (i) && n.depleted ()) return true; -// std::cout << "# DOM::is_primitive '" << input << "' --> unknown\n"; +// std::cout << "# DOM::is_literal '" << input << "' --> unknown\n"; return false; } diff --git a/src/DOM.h b/src/DOM.h index fa9fae4a1..821af1caf 100644 --- a/src/DOM.h +++ b/src/DOM.h @@ -43,7 +43,7 @@ public: void set (const std::string&, const std::string&); private: - bool is_primitive (const std::string&); + bool is_literal (std::string&); private: std::map _cache; diff --git a/src/Expression.cpp b/src/Expression.cpp index b4b214a9e..7bf5cb4f7 100644 --- a/src/Expression.cpp +++ b/src/Expression.cpp @@ -112,11 +112,11 @@ std::string Expression::evalExpression (const Task& task) std::vector value_stack; eval (task, value_stack); - // Coerce stack element to boolean. + // Coerce stack element to string. Variant result (value_stack.back ()); value_stack.pop_back (); result.cast (Variant::v_string); - return result._string; + return context.dom.get (result._string, task); } //////////////////////////////////////////////////////////////////////////////// @@ -417,7 +417,7 @@ void Expression::eval (const Task& task, std::vector & value_stack) throw std::string ("Unsupported operator '") + arg->_first + "'."; } - // It's not an operator, it's either and lvalue or some form of rvalue. + // It's not an operator, it's an lvalue or some form of rvalue. else { Variant operand; @@ -480,6 +480,7 @@ void Expression::create_variant ( else if (type == "rvalue" || type == "string" || type == "rx") + // TODO Is unquoteText necessary? variant = Variant (unquoteText (value)); else @@ -651,7 +652,7 @@ void Expression::tokenize ( if (! n.getUntilWS (s)) n.getUntilEOS (s); - tokens.push_back (Triple (s, "?", category)); + tokens.push_back (Triple (s, "string", category)); } n.skipWS (); @@ -1022,13 +1023,9 @@ void Expression::postfix () } 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)) { diff --git a/src/Nibbler.cpp b/src/Nibbler.cpp index 5143c8c38..7e799c674 100644 --- a/src/Nibbler.cpp +++ b/src/Nibbler.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include const char* c_digits = "0123456789"; @@ -920,37 +921,93 @@ bool Nibbler::getOneOf ( } //////////////////////////////////////////////////////////////////////////////// -bool Nibbler::getDOM (std::string& found) +// [|||].[. ...] +bool Nibbler::getDOM (std::string& result) { - 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]))) + std::string::size_type i = mCursor; + if (i < mLength) { - ++i; + save (); + + std::string left; + std::string right; + int number; + + if (skip ('.') && + getWord (right)) + { + while (skip ('.') && + getWord (right)) + ; + + result = mInput.substr (i, mCursor - i); + return true; + } + + restore (); + if (getWord (left) && + skip ('.') && + getWord (right)) + { + while (skip ('.') && + getWord (right)) + ; + + result = mInput.substr (i, mCursor - i); + return true; + } + + restore (); + if (getInt (number) && + skip ('.') && + getWord (right)) + { + while (skip ('.') && + getWord (right)) + ; + + result = mInput.substr (i, mCursor - i); + return true; + } + + restore (); + if (getUUID (left) && + skip ('.') && + getWord (right)) + { + while (skip ('.') && + getWord (right)) + ; + + result = mInput.substr (i, mCursor - i); + return true; + } } - if (i > mCursor) + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// A word is a contiguous string of non-space, non-digit, non-punct characters. +bool Nibbler::getWord (std::string& result) +{ + std::string::size_type i = mCursor; + + if (i < mLength) { - found = mInput.substr (start, i - start); + while (!isdigit (mInput[i]) && + !ispunct (mInput[i]) && + !isspace (mInput[i])) + { + ++i; + } - // If found is simple a number, then it is not a DOM reference. - double d; - Nibbler exclusion (found); - if (exclusion.getNumber (d) && exclusion.depleted ()) - return false; - - int in; - if (exclusion.getInt (in) && exclusion.depleted ()) - return false; - - mCursor = i; - return true; + if (i > mCursor) + { + result = mInput.substr (mCursor, i - mCursor); + mCursor = i; + return true; + } } return false; diff --git a/src/Nibbler.h b/src/Nibbler.h index d2453a10a..08bf014c8 100644 --- a/src/Nibbler.h +++ b/src/Nibbler.h @@ -66,6 +66,7 @@ public: bool getDate (const std::string&, time_t&); bool getOneOf (const std::vector &, std::string&); bool getDOM (std::string&); + bool getWord (std::string&); bool skipN (const int quantity = 1); bool skip (char); diff --git a/test/nibbler.t.cpp b/test/nibbler.t.cpp index 080571d11..be02bf90d 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 (257); + UnitTest t (277); try { @@ -412,29 +412,74 @@ int main (int argc, char** argv) // 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'"); + + // positive. + n = Nibbler (".due "); + t.ok (n.getDOM (s), "'.due' getDOM -> ok"); + t.is (s, ".due", "'.due' getDOM -> '.due'"); + + n = Nibbler ("123.due "); + t.ok (n.getDOM (s), "'123.due' getDOM -> ok"); + t.is (s, "123.due", "'123.due' getDOM -> '123.due'"); + + n = Nibbler ("ebeeab00-ccf8-464b-8b58-f7f2d606edfb.due "); + t.ok (n.getDOM (s), "'ebeeab00-ccf8-464b-8b58-f7f2d606edfb.due' getDOM -> ok"); + t.is (s, "ebeeab00-ccf8-464b-8b58-f7f2d606edfb.due", + "'ebeeab00-ccf8-464b-8b58-f7f2d606edfb.due' getDOM -> 'ebeeab00-ccf8-464b-8b58-f7f2d606edfb.due"); + + n = Nibbler ("rc.one.two.three.four.five.six.seven "); + t.ok (n.getDOM (s), "'rc.one.two.three.four.five.six.seven' getDOM -> 'rc.one.two.three.four.five.six.seven'"); + t.is (s, "rc.one.two.three.four.five.six.seven", + "'rc.one.two.three.four.five.six.seven' getDOM -> 'rc.one.two.three.four.five.six.seven'"); + + // negative. + n = Nibbler ("1+2 "); + t.notok (n.getDOM (s), "'1+2' getDOM -> notok"); + + n = Nibbler ("..foo "); + t.notok (n.getDOM (s), "'..foo' getDOM -> notok"); + + // bool getWord (std::string&); + t.diag ("Nibbler::getWord"); + n = Nibbler ("one two th3ee"); + t.ok (n.getWord (s), "'one' getWord -> ok"); + t.is (s, "one", "'one' getWord -> '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.getWord (s), "'two' getWord -> ok"); + t.is (s, "two", "'two' getWord -> 'two'"); 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.getWord (s), "'th' getWord -> ok"); + t.is (s, "th", "'th' getWord -> 'th'"); + t.ok (n.skip ('3'), "skip(3)"); + t.ok (n.getWord (s), "'ee' getWord -> ok"); + t.is (s, "ee", "'ee' getWord -> 'ee'"); t.ok (n.depleted (), "depleted"); + t.diag ("Nibbler::getWord"); + n = Nibbler ("one TWO,three,f "); + t.ok (n.getWord (s), "'one TWO,three,f ' getWord -> ok"); + t.is (s, "one", " ' TWO,three,f ' getWord -> one"); + t.ok (n.skipWS (), " 'TWO,three,f ' skipWS -> ok"); + + t.ok (n.getWord (s), " 'TWO,three,f ' getWord -> ok"); + t.is (s, "TWO", " ',three,f ' getWord -> TWO"); + t.ok (n.skip (','), " 'three,f ' skip , -> ok"); + + t.ok (n.getWord (s), " 'three,f ' getWord -> ok"); + t.is (s, "three", " ',f ' getWord -> three"); + t.ok (n.skip (','), " 'f ' skip , -> ok"); + + t.ok (n.getWord (s), " 'f ' getWord -> ok"); + t.is (s, "f", " ' ' getWord -> f"); + t.ok (n.skipWS (), " '' skip , -> ok"); + t.ok (n.depleted (), " '' depleted -> true"); + // bool getUntilEOL (std::string&); t.diag ("Nibbler::getUntilEOL"); n = Nibbler ("one\ntwo"); - t.ok (n.getUntilEOL (s), "'one\\ntwo' : getUntilEOL () -> true"); - t.is (s, "one", "'one\\ntwo' : getUntilEOL () -> 'one'"); - t.ok (n.skip ('\n'), " '\\ntwo' : skip ('\\n') -> true"); + t.ok (n.getUntilEOL (s), "'one\\ntwo' : getUntilEOL () -> true"); + t.is (s, "one", "'one\\ntwo' : getUntilEOL () -> 'one'"); + t.ok (n.skip ('\n'), " '\\ntwo' : skip ('\\n') -> true"); t.ok (n.getUntilEOL (s), " 'two' : getUntilEOL () -> true"); t.is (s, "two", " 'two' : getUntilEOL () -> 'two'"); t.ok (n.depleted (), " '' : depleted () -> true");