mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-07-07 20:06:36 +02:00
Expressions
- Implemented Nibbler::getWord. - Re-implemented Nibbler::getDOM. - Modified DOM addressed for context-based attributes from "due" to ".due", the help disambiguate DOM references in expressions. There is now a consistency: <id>.due Task-specific .due Contextual <uuid>.due General - Implemented associated unit tests.
This commit is contained in:
parent
dd75c1af1e
commit
ab6e230f10
6 changed files with 184 additions and 71 deletions
53
src/DOM.cpp
53
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 <uuid>.recur
|
||||
// TODO <uuid>.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;
|
||||
|
||||
// <attr>
|
||||
else if (task.has (name))
|
||||
return task.get (name);
|
||||
|
||||
// <id>.<name>
|
||||
else if (n.getInt (id))
|
||||
|
@ -248,15 +255,17 @@ const std::string DOM::get (const std::string& name, const Task& task)
|
|||
}
|
||||
}
|
||||
|
||||
// [<task>.] <name>
|
||||
if (name == "id") return format (task.id);
|
||||
else if (name == "urgency") return format (task.urgency_c (), 4, 3);
|
||||
else return task.get (name);
|
||||
// [<task>] .<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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 <std::string, std::string> _cache;
|
||||
|
|
|
@ -112,11 +112,11 @@ std::string Expression::evalExpression (const Task& task)
|
|||
std::vector <Variant> 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 <Variant>& 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,14 +1023,10 @@ 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))
|
||||
{
|
||||
char type2;
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include <inttypes.h>
|
||||
#include <Nibbler.h>
|
||||
#include <Date.h>
|
||||
#include <text.h>
|
||||
#include <RX.h>
|
||||
|
||||
const char* c_digits = "0123456789";
|
||||
|
@ -920,38 +921,94 @@ bool Nibbler::getOneOf (
|
|||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
bool Nibbler::getDOM (std::string& found)
|
||||
// [<number>|<uuid>|<word>|].<word>[.<word> ...]
|
||||
bool Nibbler::getDOM (std::string& result)
|
||||
{
|
||||
std::string::size_type i = mCursor;
|
||||
std::string::size_type start = mCursor;
|
||||
if (i < mLength)
|
||||
{
|
||||
save ();
|
||||
|
||||
while ( isdigit (mInput[i]) ||
|
||||
mInput[i] == '.' ||
|
||||
mInput[i] == '-' ||
|
||||
mInput[i] == '_' ||
|
||||
(! ispunct (mInput[i]) &&
|
||||
! isspace (mInput[i])))
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
while (!isdigit (mInput[i]) &&
|
||||
!ispunct (mInput[i]) &&
|
||||
!isspace (mInput[i]))
|
||||
{
|
||||
++i;
|
||||
}
|
||||
|
||||
if (i > mCursor)
|
||||
{
|
||||
found = mInput.substr (start, i - start);
|
||||
|
||||
// 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;
|
||||
|
||||
result = mInput.substr (mCursor, i - mCursor);
|
||||
mCursor = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -66,6 +66,7 @@ public:
|
|||
bool getDate (const std::string&, time_t&);
|
||||
bool getOneOf (const std::vector <std::string>&, std::string&);
|
||||
bool getDOM (std::string&);
|
||||
bool getWord (std::string&);
|
||||
|
||||
bool skipN (const int quantity = 1);
|
||||
bool skip (char);
|
||||
|
|
|
@ -34,7 +34,7 @@ Context context;
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
int main (int argc, char** argv)
|
||||
{
|
||||
UnitTest t (257);
|
||||
UnitTest t (277);
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -412,23 +412,68 @@ 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");
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue