Repository: pyccel/pyccel (python)

Issue:
Floor Division Assignment Operator Not Supported
### Relevant Discussion
Floor division assignment operator is not supported unlike all other assignment operators. Surprisingly this fails in the syntactic stage.

### Describe the feature
Floor division assignment should be supported with all appropriate types

### Test Code

```python
if __name__ == '__main__':
    a = -3.0
    a //= 2
    b = -3
    b //= 2
```

### Proposed Solution
It should be added here:
```Python

    def _visit_AugAssign(self, stmt):

        lhs = self._visit(stmt.target)
        rhs = self._visit(stmt.value)
        if isinstance(stmt.op, ast.Add):
            return AugAssign(lhs, '+', rhs)
        elif isinstance(stmt.op, ast.Sub):
            return AugAssign(lhs, '-', rhs)
        elif isinstance(stmt.op, ast.Mult):
            return AugAssign(lhs, '*', rhs)
        elif isinstance(stmt.op, ast.Div):
            return AugAssign(lhs, '/', rhs)
        elif isinstance(stmt.op, ast.Mod):
            return AugAssign(lhs, '%', rhs)
        elif isinstance(stmt.op, ast.BitOr):
            return AugAssign(lhs, '|', rhs)
        elif isinstance(stmt.op, ast.BitAnd):
            return AugAssign(lhs, '&', rhs)
        elif isinstance(stmt.op, ast.LShift):
            return AugAssign(lhs, '<<', rhs)
        elif isinstance(stmt.op, ast.RShift):
            return AugAssign(lhs, '>>', rhs)
        else:
            return errors.report(PYCCEL_RESTRICTION_TODO, symbol = stmt,
                    severity='error')
```

Pull request description:
Add support for the floor division assignment operator
Add support for the floor division assignment operator. Fixes #2192

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b3ac1629..ab445e68 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -78,6 +78,7 @@ All notable changes to this project will be documented in this file.
 -   #2139 : Add support for `__getitem__`
 -   #337 : Add support for returning tuples from functions.
 -   #2194 : Add support for strings as arguments.
+-   #2192 : Add support for the floor division assignment operator.
 -   \[INTERNALS\] Add abstract class `SetMethod` to handle calls to various set methods.
 -   \[INTERNALS\] Added `container_rank` property to `ast.datatypes.PyccelType` objects.
 -   \[INTERNALS\] Add a `__call__` method to `FunctionDef` to create `FunctionCall` instances.
diff --git a/pyccel/ast/core.py b/pyccel/ast/core.py
index 6f1a3d07..7a08b795 100644
--- a/pyccel/ast/core.py
+++ b/pyccel/ast/core.py
@@ -27,6 +27,7 @@ from .literals  import NilArgument, LiteralTrue
 
 from .operators import PyccelAdd, PyccelMinus, PyccelMul, PyccelDiv, PyccelMod
 from .operators import PyccelOperator, PyccelAssociativeParenthesis, PyccelIs
+from .operators import PyccelFloorDiv
 
 from .variable import DottedName, IndexedElement
 from .variable import Variable, AnnotatedPyccelSymbol
@@ -721,6 +722,7 @@ class AugAssign(Assign):
             '-' : PyccelMinus,
             '*' : PyccelMul,
             '/' : PyccelDiv,
+            '//': PyccelFloorDiv,
             '%' : PyccelMod,
             '|' : PyccelBitOr,
             '&' : PyccelBitAnd,
diff --git a/pyccel/codegen/printing/ccode.py b/pyccel/codegen/printing/ccode.py
index cc42796d..bad66ea2 100644
--- a/pyccel/codegen/printing/ccode.py
+++ b/pyccel/codegen/printing/ccode.py
@@ -2453,7 +2453,7 @@ class CCodePrinter(CodePrinter):
         lhs = expr.lhs
         rhs = expr.rhs
 
-        if op == '%' and isinstance(lhs.dtype.primitive_type, PrimitiveFloatingPointType):
+        if op == '//' or (op == '%' and isinstance(lhs.dtype.primitive_type, PrimitiveFloatingPointType)):
             _expr = expr.to_basic_assign()
             expr.invalidate_node()
             return self._print(_expr)
diff --git a/pyccel/parser/syntactic.py b/pyccel/parser/syntactic.py
index 32f8dfff..d88a3971 100644
--- a/pyccel/parser/syntactic.py
+++ b/pyccel/parser/syntactic.py
@@ -502,6 +502,8 @@ class SyntaxParser(BasicParser):
             return AugAssign(lhs, '<<', rhs)
         elif isinstance(stmt.op, ast.RShift):
             return AugAssign(lhs, '>>', rhs)
+        elif isinstance(stmt.op, ast.FloorDiv):
+            return AugAssign(lhs, '//', rhs)
         else:
             return errors.report(PYCCEL_RESTRICTION_TODO, symbol = stmt,
                     severity='error')


Test patch:
diff --git a/tests/epyccel/modules/augassign.py b/tests/epyccel/modules/augassign.py
index 48f26811..7f8e327f 100644
--- a/tests/epyccel/modules/augassign.py
+++ b/tests/epyccel/modules/augassign.py
@@ -139,3 +139,6 @@ def augassign_array_func(x : 'float[:]', y : 'float[:]'):
     def fun1(x: 'float[:]') -> 'float[:]':
         return x + 1
     x %= fun1(y)
+
+def augassign_floor_div(a : 'float[:]'):
+    a //= 3
diff --git a/tests/epyccel/test_epyccel_augassign.py b/tests/epyccel/test_epyccel_augassign.py
index 66b5fd47..78b7fcb2 100644
--- a/tests/epyccel/test_epyccel_augassign.py
+++ b/tests/epyccel/test_epyccel_augassign.py
@@ -256,3 +256,15 @@ def test_augassign_array_func(language):
     func_epyc(x_epyc,y)
 
     assert np.allclose(x, x_epyc, rtol=RTOL, atol=ATOL)
+
+def test_augassign_floor_div(language):
+    func = mod.augassign_floor_div
+    func_epyc = epyccel(func, language = language)
+
+    x1_float = random((5,))*10
+    x2_float = x1_float.copy()
+
+    func(x1_float)
+    func_epyc(x2_float)
+
+    assert np.allclose(x1_float, x2_float, rtol=RTOL, atol=ATOL)