Repository: sympy/sympy

Issue:
`_print_Add` is fundamentally wrong
The `print_Add` in `printing/str.py` is making very strong and incorrect assumptions leading to incorrectly printed string (and c/latex/....) in a lot of cases (and this breaks our code generation in [Devito](https://github.com/devitocodes/devito) since incorrect sign leads to unstable kernels producing NaNs)

```
In [15]: from sympy import *

In [16]: a,b,c,d = symbols('a b c d')

In [17]: expr = Add(Add(-a, b, evaluate=False), Add(-c, d,  evaluate=False),  evaluate=False)

In [18]: expr
Out[18]: -(a + b) - (c + d) # this should be (-a + b) + (-c + d)
```

Basically, `_print_Add` always assume that any term `t` that is an Add that starts with a `-` is automatically a `-(t)` which is wrong since it would be a `Mul` .  The lines leading to the issues are lines 55-65  [Here](https://github.com/sympy/sympy/blob/45dcb6cae616eca10ca6df1a874d6aed25bc1fb4/sympy/printing/str.py#L55)

```            
            t = self._print(term)
            if t.startswith('-'):
                sign = "-"
                t = t[1:]
            else:
                sign = "+"
            if precedence(term) < PREC or isinstance(term, Add):
                l.extend([sign, "(%s)" % t])
            else:
                l.extend([sign, t])
```

That simply check if the string starts with `-` without any regards for the expression. so `-a +b` is printed as `- (a+b)` since it's an Add and starts with `-`.



Discussion:

Patch:
diff --git a/sympy/printing/str.py b/sympy/printing/str.py
index 89d25d02fb..b5339a96f9 100644
--- a/sympy/printing/str.py
+++ b/sympy/printing/str.py
@@ -4,7 +4,7 @@
 
 from typing import Any, Dict as tDict
 
-from sympy.core import S, Rational, Pow, Basic, Mul, Number, Add
+from sympy.core import S, Rational, Pow, Basic, Mul, Number
 from sympy.core.mul import _keep_coeff
 from sympy.core.relational import Relational
 from sympy.core.sorting import default_sort_key
@@ -50,16 +50,16 @@ def emptyPrinter(self, expr):
     def _print_Add(self, expr, order=None):
         terms = self._as_ordered_terms(expr, order=order)
 
-        PREC = precedence(expr)
+        prec = precedence(expr)
         l = []
         for term in terms:
             t = self._print(term)
-            if t.startswith('-'):
+            if t.startswith('-') and not term.is_Add:
                 sign = "-"
                 t = t[1:]
             else:
                 sign = "+"
-            if precedence(term) < PREC or isinstance(term, Add):
+            if precedence(term) < prec or term.is_Add:
                 l.extend([sign, "(%s)" % t])
             else:
                 l.extend([sign, t])


Test patch:
diff --git a/sympy/printing/tests/test_str.py b/sympy/printing/tests/test_str.py
index 475d19d27a..145015cd0e 100644
--- a/sympy/printing/tests/test_str.py
+++ b/sympy/printing/tests/test_str.py
@@ -87,6 +87,9 @@ def test_Add():
     assert str(x - z*y**2*z*w) == "-w*y**2*z**2 + x"
     assert str(x - 1*y*x*y) == "-x*y**2 + x"
     assert str(sin(x).series(x, 0, 15)) == "x - x**3/6 + x**5/120 - x**7/5040 + x**9/362880 - x**11/39916800 + x**13/6227020800 + O(x**15)"
+    assert str(Add(Add(-w, x, evaluate=False), Add(-y, z,  evaluate=False),  evaluate=False)) == "(-w + x) + (-y + z)"
+    assert str(Add(Add(-x, -y, evaluate=False), -z, evaluate=False)) == "-z + (-x - y)"
+    assert str(Add(Add(Add(-x, -y, evaluate=False), -z, evaluate=False), -t, evaluate=False)) == "-t + (-z + (-x - y))"
 
 
 def test_Catalan():