diff --git a/MutualRecursion.py b/MutualRecursion.py index 016a1cca5e18a4bbcc564303576818338bf6b732..4e1d2c5215e18312a54c45731b34bee455351253 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