Archives
Trending
Support
Login
clear text
XML
Django
JavaScript
MATLAB
C
C++
C#
Python
SQL
Shell
Bash
Markdown
YAML
JSON
HTML
CSS
PHP
Java
Ruby
Go
Rust
Swift
Kotlin
Arduino
TypeScript
Perl
Autohotkey
Lua
SQF
R
Scala
Haskell
Groovy
Dart
Clojure
VB.NET
Objective-C
PowerShell
Bash
CoffeeScript
Verilog
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import math import sys import readline # Супер-крутой консольный калькулятор - только стандартная библиотека # Поддерживает: скобки, + - * / // % ^, унарный -, функции math, переменные, история, ans OPS = { '+': (1, 'left'), '-': (1, 'left'), '*': (2, 'left'), '/': (2, 'left'), '//': (2, 'left'), '%': (2, 'left'), '^': (3, 'right'), } UNARY = 'u-' # токен для унарного минуса FUNCTIONS = { 'sin': math.sin, 'cos': math.cos, 'tan': math.tan, 'asin': math.asin, 'acos': math.acos, 'atan': math.atan, 'sqrt': math.sqrt, 'log': lambda x, base=math.e: math.log(x, base) if base != math.e else math.log(x), 'ln': math.log, 'log10': math.log10, 'exp': math.exp, 'pow': math.pow, 'abs': abs, 'floor': math.floor, 'ceil': math.ceil, 'round': round, 'factorial': math.factorial, } CONSTS = { 'pi': math.pi, 'e': math.e, } # токенайзер def tokenize(s): tokens = [] i = 0 L = len(s) while i < L: c = s[i] if c.isspace(): i += 1 continue if c.isdigit() or (c == '.' and i+1 < L and s[i+1].isdigit()): j = i has_dot = False while j < L and (s[j].isdigit() or s[j] == '.'): if s[j] == '.': if has_dot: break has_dot = True j += 1 tokens.append(s[i:j]) i = j continue if c.isalpha() or c == '_': j = i while j < L and (s[j].isalnum() or s[j] == '_'): j += 1 tokens.append(s[i:j]) i = j continue # многосимвольные операторы if s.startswith('//', i): tokens.append('//'); i += 2; continue # одиночные операторы и скобки if c in '+-*/%^(),=%': tokens.append(c) i += 1 continue if c == '=': tokens.append('='); i += 1; continue raise ValueError(f"Неизвестный символ: {c}") return tokens # шантинг-ярд -> ОПЗ (RPN) def to_rpn(tokens): out = [] stack = [] prev = None i = 0 while i < len(tokens): tok = tokens[i] if is_number(tok) or is_variable(tok): out.append(tok) elif tok in FUNCTIONS: stack.append(tok) elif tok == ',': while stack and stack[-1] != '(': out.append(stack.pop()) if not stack: raise ValueError("Неправильно размещённая запятая или несоответствие скобок") elif tok == '(': stack.append(tok) elif tok == ')': while stack and stack[-1] != '(': out.append(stack.pop()) if not stack: raise ValueError("Несоответствие скобок") stack.pop() if stack and stack[-1] in FUNCTIONS: out.append(stack.pop()) elif tok in OPS or tok == '-': # определить унарный минус if tok == '-' and (prev is None or (prev in OPS or prev in ('(', ','))): op = UNARY else: op = tok if op == UNARY: prec = 4 assoc = 'right' else: prec, assoc = OPS.get(op, (None, None)) if prec is None: raise ValueError(f"Неизвестный оператор: {op}") while stack: top = stack[-1] if top == '(': break if top == UNARY: top_prec = 4; top_assoc = 'right' else: if top in OPS: top_prec, top_assoc = OPS[top] else: top_prec = 5; top_assoc = 'left' if (assoc == 'left' and prec <= top_prec) or (assoc == 'right' and prec < top_prec): out.append(stack.pop()) else: break stack.append(op) else: raise ValueError(f"Неизвестный токен в выражении: {tok}") prev = tok i += 1 while stack: t = stack.pop() if t in ('(', ')'): raise ValueError("Несоответствие скобок") out.append(t) return out def is_number(tok): try: float(tok) return True except: return False def is_variable(tok): if tok in FUNCTIONS or tok in ('(', ')', ',',) or tok in OPS or tok == UNARY: return False if tok == 'ans': return True return tok.isidentifier() # вычисление ОПЗ def eval_rpn(rpn, variables): st = [] for tok in rpn: if is_number(tok): st.append(float(tok)) elif tok in variables: st.append(variables[tok]) elif tok in CONSTS: st.append(CONSTS[tok]) elif tok == UNARY: if not st: raise ValueError("Отсутствует операнд для унарного '-'") a = st.pop() st.append(-a) elif tok in OPS: if len(st) < 2: raise ValueError(f"Отсутствует операнд для оператора {tok}") b = st.pop(); a = st.pop() st.append(apply_op(a, b, tok)) elif tok in FUNCTIONS: if tok == 'log' and len(st) >= 2: b = st.pop(); a = st.pop() st.append(FUNCTIONS[tok](a, b)) elif tok == 'pow' and len(st) >= 2: b = st.pop(); a = st.pop() st.append(FUNCTIONS[tok](a, b)) else: if not st: raise ValueError(f"Отсутствует операнд для функции {tok}") a = st.pop() if tok == 'factorial': if not float(a).is_integer() or a < 0: raise ValueError("factorial() определена только для неотрицательных целых") st.append(FUNCTIONS[tok](int(a))) else: st.append(FUNCTIONS[tok](a)) else: raise ValueError(f"Неизвестный токен при вычислении: {tok}") if len(st) != 1: raise ValueError("Неверное выражение") return st[0] def apply_op(a, b, op): if op == '+': return a + b if op == '-': return a - b if op == '*': return a * b if op == '/': if b == 0: raise ZeroDivisionError("деление на ноль") return a / b if op == '//': if b == 0: raise ZeroDivisionError("деление на ноль") return a // b if op == '%': if b == 0: raise ZeroDivisionError("деление по модулю на ноль") return a % b if op == '^': return a ** b raise ValueError(f"Неподдерживаемый оператор: {op}") # верхнеуровневое вычисление, поддерживает присваивание name = expr def evaluate(expr, variables): tokens = tokenize(expr) if not tokens: raise ValueError("Пустое выражение") # присваивание if len(tokens) >= 3 and tokens[1] == '=' and is_variable(tokens[0]): name = tokens[0] rhs = tokens[2:] rpn = to_rpn(rhs) val = eval_rpn(rpn, variables) variables[name] = val variables['ans'] = val return val, f"{name} = {val}" else: rpn = to_rpn(tokens) val = eval_rpn(rpn, variables) variables['ans'] = val return val, str(val) def pretty(x): if isinstance(x, float): if x.is_integer(): return str(int(x)) else: return f"{x:.12g}" return str(x) def banner(): print("SuperKalk v1 - консольный калькулятор") print("Команды: exit, quit, help, vars, history, clear, load <файл>") print("Поддерживаемые функции: " + ", ".join(sorted(FUNCTIONS.keys()))) print("Константы: " + ", ".join(f"{k}={v}" for k, v in CONSTS.items())) print("Пример: x = 2 * (3 + sqrt(4))") print() def repl(): variables = {'ans': 0.0} history = [] banner() while True: try: line = input('>>> ').strip() except (EOFError, KeyboardInterrupt): print() break if not line: continue history.append(line) low = line.lower() if low in ('exit', 'quit'): break if low == 'help': print("Вводите выражения. Примеры:") print(" 2 + 3*4") print(" sin(pi/2) + ln(e)") print(" x = 5") print(" ans * 2") continue if low == 'vars': for k, v in variables.items(): print(f"{k} = {pretty(v)}") continue if low == 'history': for i, h in enumerate(history, 1): print(f"{i}: {h}") continue if low == 'clear': variables = {'ans': 0.0} history = [] print("Переменные и история очищены.") continue if low.startswith('load '): fn = line.split(' ', 1)[1] try: with open(fn, 'r', encoding='utf-8') as f: for l in f: l = l.strip() if not l or l.startswith('#'): continue print('> ' + l) try: val, out = evaluate(l, variables) print(pretty(val)) except Exception as e: print("Ошибка:", e) except Exception as e: print("Не удалось загрузить файл:", e) continue try: val, out = evaluate(line, variables) print(pretty(val)) except Exception as e: print("Ошибка:", e) if __name__ == '__main__': repl()
Mark as private (unlisted)
for 30 minutes
for 6 hours
for 1 day
for 1 week
for 1 month
for 1 year
everlasting (like CCCP)