Files

93 lines
2.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Mathematical expression evaluator with precedence (Punkt vor Strich).
Supports +, -, *, /, (, ), and decimal numbers. Safe no eval()."""
import re
from decimal import Decimal, InvalidOperation
TOKEN_NUM = 'NUM'
TOKEN_PLUS = '+'
TOKEN_MINUS = '-'
TOKEN_MUL = '*'
TOKEN_DIV = '/'
TOKEN_LPAR = '('
TOKEN_RPAR = ')'
def tokenize(expr):
tokens = []
i = 0
while i < len(expr):
c = expr[i]
if c in ' \t':
i += 1
continue
if c in '+-*/()':
tokens.append(c)
i += 1
elif c.isdigit() or c == '.':
start = i
had_dot = False
while i < len(expr) and (expr[i].isdigit() or (expr[i] == '.' and not had_dot)):
if expr[i] == '.':
had_dot = True
i += 1
tokens.append(('NUM', expr[start:i]))
else:
raise ValueError(f"Ungültiges Zeichen: '{c}'")
return tokens
def parse_primary(tokens, pos):
if pos >= len(tokens):
raise ValueError("Erwarte Zahl oder (")
tok = tokens[pos]
if isinstance(tok, tuple) and tok[0] == 'NUM':
return Decimal(tok[1]), pos + 1
if tok == '(':
val, pos = parse_expr(tokens, pos + 1)
if pos >= len(tokens) or tokens[pos] != ')':
raise ValueError("Fehlende schließende Klammer")
return val, pos + 1
raise ValueError(f"Unerwartetes Token: {tok}")
def parse_mul(tokens, pos):
val, pos = parse_primary(tokens, pos)
while pos < len(tokens):
tok = tokens[pos]
if tok in ('*', '/'):
right, pos = parse_primary(tokens, pos + 1)
if tok == '*':
val *= right
else:
if right == 0:
raise ValueError("Division durch Null")
val /= right
else:
break
return val, pos
def parse_expr(tokens, pos):
val, pos = parse_mul(tokens, pos)
while pos < len(tokens):
tok = tokens[pos]
if tok in ('+', '-'):
right, pos = parse_mul(tokens, pos + 1)
if tok == '+':
val += right
else:
val -= right
else:
break
return val, pos
def berechne_formel(expr_str):
if not expr_str or not expr_str.strip():
return 0
# Deutsche Dezimaltrenner ersetzen
expr_str = expr_str.replace(',', '.')
tokens = tokenize(expr_str.strip())
if not tokens:
return 0
val, pos = parse_expr(tokens, 0)
if pos != len(tokens):
raise ValueError("Überflüssige Zeichen am Ende")
return float(val)