You could approach it as "splitting" the string with operators being your separators. This would save you from trying to represent numbers in your regular expression.
So you only need an expression that will pick up the 5 operators and parentheses. This can be expressed using a pipe between operators with the longest operator (**) being first.
import re
symbols = ["**","+","-","*","/","(",")"] # longest first
tokens = re.compile("("+"|".join(map(re.escape,symbols))+")")
# placing symbols in a group makes re.split keep the separators
def tokenize(exp):
parts = map(str.strip,tokens.split(exp)) # split and strip spaces
return list(filter(None,parts)) # remove empty parts
exp = '(( 200 + (4 * 3.14)) / ( 2 ** 3 ))'
print(tokenize(exp))
['(', '(', '200', '+', '(', '4', '*', '3.14', ')', ')', '/', '(', '2', '**', '3', ')', ')']
exp = '(23.23+23)'
print(tokenize(exp))
['(', '23.23', '+', '23', ')']
exp = '((23**2)/23)'
print(tokenize(exp))
['(', '(', '23', '**', '2', ')', '/', '23', ')']
exp = '(23.34-(3*2))'
print(tokenize(exp))
['(', '23.34', '-', '(', '3', '*', '2', ')', ')']
Then you can perform a second pass and validate that the components are either an operator or a valid number as well as check that the expression is well formed with matching parentheses and alternating operator/operand. At that point you will know exactly what part of the expression is incorrect.
for example:
def validate(exp):
parts = tokenize(exp)
error = ""
pLevel = 0
previous = "$"
for errorPos,part in enumerate(parts):
pLevel += (part=="(")-(part==")")
if pLevel<0: error="too many closing parentheses";break
if part in "**+/)" and previous in "$**+-/(" :
error = "missing operand";break
if part not in "**+-/)" and previous not in "$**+-/(":
error = "missing operator";break
previous = part
if part in ["**","*","+","-","/","(",")"]: continue
if all(p.isdigit() for p in part.split(".",1)): continue
error = "invalid operand: " + part
break
if not error and pLevel!=0:
errorPos,error = len(parts),"unbalanced parentheses"
if not error and previous in "**+-/":
errorPos,error = len(parts),"missing operand"
if error:
print("".join(parts))
indent = " " * sum(map(len,parts[:errorPos]))
print(indent+"^")
print(indent+"|__ Error!",error)
...
validate('(( 200 + (4 * 3,14)) / ( 2 ** 3 ))')
((200+(4*3,14))/(2**3))
^
|__ Error! invalid operand: 3,14
validate('(( 200 + (4 * 3.14)) / ( 2 ** 3 )')
((200+(4*3.14))/(2**3)
^
|__ Error! unbalanced parentheses
validate('(( 200 + (4 * 3.14)))) / ( 2 ** 3 )')
((200+(4*3.14))))/(2**3)
^
|__ Error! too many closing parentheses
validate('(( 200 + *(4 * 3,14)) / ( 2 ** 3 ))')
((200+*(4*3,14))/(2**3))
^
|__ Error! missing operand
validate('(( 200 + ()(4 * 3,14)) / ( 2 ** 3 ))')
((200+()(4*3,14))/(2**3))
^
|__ Error! missing operand
validate('(( (200 + )(4 * 3,14)) / ( 2 ** 3 ))')
(((200+)(4*3,14))/(2**3))
^
|__ Error! missing operand
validate('(( (200 + 2)(4 * 3,14)) / ( 2 ** 3 ))')
(((200+2)(4*3,14))/(2**3))
^
|__ Error! missing operator