131 lines
4.9 KiB
Diff
131 lines
4.9 KiB
Diff
From: Tomas Babej <tomas@tbabej.com>
|
|
Date: Sat, 30 Jan 2021 17:50:58 -0500
|
|
Subject: Backport parser issues fixes
|
|
|
|
This backports upstream PR #2405 and commit a5eee5f, which contain extra
|
|
fixes and tests for issues with numeric project names (amongst others)
|
|
which were being wrongly interpreted as expressions.
|
|
|
|
Addresses upstream issues TW-{2388,2386,2257,1908,1896}
|
|
---
|
|
|
|
diff --git a/src/CLI2.cpp b/src/CLI2.cpp
|
|
index ec2bd6c7..c49eb4d7 100644
|
|
--- a/src/CLI2.cpp
|
|
+++ b/src/CLI2.cpp
|
|
@@ -1277,7 +1277,9 @@ void CLI2::desugarFilterAttributes ()
|
|
A2 op ("", Lexer::Type::op);
|
|
op.tag ("FILTER");
|
|
|
|
- A2 rhs ("", values[0]._lextype);
|
|
+ // Attribute types that do not support evaluation should be interpreted
|
|
+ // as strings (currently this means that string attributes are not evaluated)
|
|
+ A2 rhs ("", evalSupported ? values[0]._lextype: Lexer::Type::string);
|
|
rhs.tag ("FILTER");
|
|
|
|
// Special case for '<name>:<value>'.
|
|
@@ -1371,7 +1373,7 @@ void CLI2::desugarFilterAttributes ()
|
|
|
|
// Do not modify this construct without full understanding.
|
|
// Getting this wrong breaks a whole lot of filtering tests.
|
|
- if (values.size () > 1 || evalSupported)
|
|
+ if (evalSupported)
|
|
{
|
|
for (auto& v : values)
|
|
reconstructed.push_back (v);
|
|
@@ -1946,6 +1948,41 @@ void CLI2::desugarFilterPlainArgs ()
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
+// Detects if the bracket at iterator it is a start or end of an empty paren expression
|
|
+// Examples:
|
|
+// ( status = pending ) ( )
|
|
+// ^
|
|
+// it -----| => true
|
|
+//
|
|
+// ( status = pending ) ( project = Home )
|
|
+// ^
|
|
+// it -----| => false
|
|
+bool CLI2::isEmptyParenExpression (std::vector<A2>::iterator it, bool forward /* = true */) const
|
|
+{
|
|
+ int open = 0;
|
|
+ int closed = 0;
|
|
+
|
|
+ for (auto a = it; a != (forward ? _args.end (): _args.begin()); (forward ? ++a: --a))
|
|
+ {
|
|
+ if (a->attribute("raw") == "(")
|
|
+ open++;
|
|
+ else if (a->attribute("raw") == ")")
|
|
+ closed++;
|
|
+ else
|
|
+ // Encountering a non-paren token means there is something between parenthees
|
|
+ return false;
|
|
+
|
|
+ // Getting balanced parentheses means we have an empty paren expression
|
|
+ if (open == closed && open != 0)
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ // Should not end here.
|
|
+ return false;
|
|
+}
|
|
+
|
|
+
|
|
+////////////////////////////////////////////////////////////////////////////////
|
|
// Two consecutive FILTER, non-OP arguments that are not "(" or ")" need an
|
|
// "and" operator inserted between them.
|
|
//
|
|
@@ -1971,10 +2008,18 @@ void CLI2::insertJunctions ()
|
|
// Insert AND between terms.
|
|
else if (a != prev)
|
|
{
|
|
- if ((prev->_lextype != Lexer::Type::op && a->attribute ("raw") == "(") ||
|
|
- (prev->_lextype != Lexer::Type::op && a->_lextype != Lexer::Type::op) ||
|
|
- (prev->attribute ("raw") == ")" && a->_lextype != Lexer::Type::op) ||
|
|
- (prev->attribute ("raw") == ")" && a->attribute ("raw") == "("))
|
|
+ if ((prev->_lextype != Lexer::Type::op &&
|
|
+ a->attribute ("raw") == "(" &&
|
|
+ ! isEmptyParenExpression(a, true) ) ||
|
|
+ (prev->attribute ("raw") == ")" &&
|
|
+ a->_lextype != Lexer::Type::op &&
|
|
+ ! isEmptyParenExpression(prev, false)) ||
|
|
+ (prev->attribute ("raw") == ")" &&
|
|
+ a->attribute ("raw") == "(" &&
|
|
+ ! isEmptyParenExpression(a, true) &&
|
|
+ ! isEmptyParenExpression(prev, false)) ||
|
|
+ (prev->_lextype != Lexer::Type::op &&
|
|
+ a->_lextype != Lexer::Type::op))
|
|
{
|
|
A2 opOr ("and", Lexer::Type::op);
|
|
opOr.tag ("FILTER");
|
|
diff --git a/src/CLI2.h b/src/CLI2.h
|
|
index f6b0ce94..025be4c8 100644
|
|
--- a/src/CLI2.h
|
|
+++ b/src/CLI2.h
|
|
@@ -99,6 +99,7 @@ private:
|
|
void findUUIDs ();
|
|
void insertIDExpr ();
|
|
void lexFilterArgs ();
|
|
+ bool isEmptyParenExpression (std::vector<A2>::iterator it, bool forward = true) const;
|
|
void desugarFilterPlainArgs ();
|
|
void insertJunctions ();
|
|
void defaultCommand ();
|
|
diff --git a/src/columns/ColTypeString.cpp b/src/columns/ColTypeString.cpp
|
|
index 37c1433d..4ef97578 100644
|
|
--- a/src/columns/ColTypeString.cpp
|
|
+++ b/src/columns/ColTypeString.cpp
|
|
@@ -58,7 +58,12 @@ void ColumnTypeString::modify (Task& task, const std::string& value)
|
|
std::string domRef;
|
|
Lexer::Type type;
|
|
if (lexer.token (domRef, type) &&
|
|
- type == Lexer::Type::dom)
|
|
+ type == Lexer::Type::dom &&
|
|
+ // Ensure 'value' contains only the DOM reference and no other tokens
|
|
+ // The lexer.token returns false for end-of-string.
|
|
+ // This works as long as all the DOM references we should support consist
|
|
+ // only of a single token.
|
|
+ lexer.token (domRef, type) == false)
|
|
{
|
|
Eval e;
|
|
e.addSource (domSource);
|