From ee26c4658cb10ef4f917080cc84bef1caf0a8d14 Mon Sep 17 00:00:00 2001
From: Christopher Bohn <bohn@unl.edu>
Date: Wed, 17 Mar 2021 13:42:38 -0500
Subject: [PATCH] Introduced additional error handling and doctests to parse
 function

---
 MutualRecursion.py | 51 ++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 47 insertions(+), 4 deletions(-)

diff --git a/MutualRecursion.py b/MutualRecursion.py
index 016a1cc..4e1d2c5 100755
--- a/MutualRecursion.py
+++ b/MutualRecursion.py
@@ -7,10 +7,45 @@ def parse(expression):
     """
     Expects an expression in the form of left-paren left-expression operator right-expression right-paren,
     where each left/right expression is either an integer or another expression.
-    For example, '(1+2)' or '((1+2)-(3*4))'
+
+    Expressions may have literal sub-expressions
+    >>> parse('(1 + 2)')
+    ('1', '+', '2')
+
+    And an expression may have non-literal sub-expressions
+    >>> parse('((1+2) - (3*4))')
+    ('(1+2)', '-', '(3*4)')
+
+    Spaces may be included in the expression
+    >>> parse('(1 - (3 * 4))')
+    ('1', '-', '(3*4)')
+
+    But spaces are not required
+    >>> parse('((1+2)-3)')
+    ('(1+2)', '-', '3')
+
+    The expression must be enclosed within parentheses
+    >>> parse('1+2')
+    Traceback (most recent call last):
+      ...
+    SyntaxError: Expression must be bounded by parenthesis. 1+2 does not satisfy.
+
+    And the parentheses must be balanced
+    >>> parse('((1+2)')
+    Traceback (most recent call last):
+      ...
+    SyntaxError: Expression must have balanced parentheses. ((1+2) does not satisfy.
+
+    Finally, the expression must have two sub-expressions separated by one an arithmetic operator from {+ - * /}
+    >>> parse('(3 3)')
+    Traceback (most recent call last):
+      ...
+    SyntaxError: Expression must have an operator (3 3) does not satisfy.
     """
+    if expression == '':
+        raise SyntaxError('Vacuous expressions are disallowed.')
     if expression[0] != '(' or expression[-1] != ')':
-        raise SyntaxError(f'Expression must be bounded by parenthesis. ${expression} does not satisfy.')
+        raise SyntaxError(f'Expression must be bounded by parenthesis. {expression} does not satisfy.')
     full_expression = expression.replace(' ', '')[1:-1]
     if full_expression[0] != '(':  # the left expression is an integer
         index = math.inf
@@ -18,6 +53,8 @@ def parse(expression):
             this_operator_index = full_expression.find(operator)
             if -1 < this_operator_index < index:
                 index = this_operator_index
+        if index == math.inf:
+            raise SyntaxError(f'Expression must have an operator {expression} does not satisfy.')
     else:
         parentheses_count = 1
         index = 1
@@ -27,9 +64,15 @@ def parse(expression):
             if full_expression[index] == ')':
                 parentheses_count -= 1
             index += 1
+            if index == len(full_expression) and parentheses_count > 0:
+                raise SyntaxError(f'Expression must have balanced parentheses. {expression} does not satisfy.')
     left_expression = full_expression[:index]
-    operator = full_expression[index]
-    right_expression = full_expression[index + 1:]
+    try:
+        operator = full_expression[index]
+        right_expression = full_expression[index + 1:]
+    except IndexError:
+        raise SyntaxError(f'Non-literal expression must have two subexpressions and an operator. '
+                          f'{expression} does not satisfy.')
     return left_expression, operator, right_expression
 
 
-- 
GitLab