elmacyc proto 1

Run Settings
LanguagePython
Language Version
Run Command
from fractions import Fraction from functools import reduce from math import sin, pi def todo(): raise InterruptedError("Not implemented") # AFAIK ` is unused in TeX, so it's used to emulate Compose key # https://habr.com/ru/articles/80091/ def pseudoCompose(s: str) -> str: # TODO don't iterate dict items _dict = { "oo": "°", "xx": "×", # Greek "GW": "Ω", "gm": "μ", "gp": "π", } for k, v in _dict.items(): s = s.replace(f"`{k}", v) return s class Unit: _units = { "g", # Gram "m", # Meter "A", # Ampere "K", # Kelvin "mol", # Mole "cd", # Candela "s", # Second } _prefixes = list(zip(( "y", # yocto "z", # zepto "a", # atto "f", # femto "p", # pico "n", # nano "μ", # micro "m", # milli "k", # kilo "M", # mega "G", # giga "T", # tera "P", # peta "E", # exa "Z", # zetta "Y", # yotta ), range(-24, 25, 3))) + [ ("c", -2), # centi ("d", -1), # deci ("da", 1), # deca ("h", 2), # hecto ] _bin_prefixes = list(zip(( "Ki", # kibi "Mi", # mebi "Gi", # gibi "Ti", # tebi "Pi", # pebi "Ei", # exbi "Zi", # zebi "Yi", # yobi ), range(10, 81, 10))) _conversions = { "N": (3, [("g", 1), ("m", 1)], [("s", 2)]), # Newton "Pa": (3, [("g", 1)], [("m", 1), ("s", 2)]), # Pascal "J": (3, [("g", 1), ("m", 2)], [("s", 2)]), # Joule "W": (3, [("g", 1), ("m", 2)], [("s", 3)]), # Watt "C": (1, [("A", 1), ("s", 1)], []), # Coulomb "V": (3, [("g", 1), ("m", 2)], [("A", 1), ("s", 3)]), # Volt "Ω": (3, [("g", 1), ("m", 2)], [("A", 2), ("s", 3)]), # Ohm "Hz": (1, [], [("s", 1)]), # Hertz "L": (-3, [("m", 3)], []), # Liter } def __init__(self): print(self._prefixes) class Integer: def __init__(self, value: Fraction, unit: Unit): if value.denominator != 1: raise ValueError("Non-integer value") self.value = value self.unit = unit def __repr__(self) -> str: return f"{self.value!r} {self.unit!r})" class Natural(Integer): def __init__(self, value: Fraction, unit: Unit): if not (value > 0): raise ValueError('Non-natural value') super().__init__() def __repr__(self) -> str: return f"{self.value!r} {self.unit!r})" class Statement: _ops = { # Operations: # * Priority (0 = function) # * Number of args (None = inf) # * Calc function '+': (1, None, lambda x: sum(x)), '-': (1, 2, lambda x: x[0] - x[1]), '*': (2, None, lambda x: reduce(lambda a, b: a * b, x)), '/': (2, 2, lambda x: x[0] / x[1]), '**': (3, 2, lambda x: x[0] ** x[1]), 'sin': (0, 1, lambda x: sin(x[0])), # Add your own functions 'inc': (0, 1, lambda x: x[0] + 1), } def __init__(self, elm): self.elm = elm def __repr__(self, e = None, prev = (0, True)) -> str: if e is None: e = self.elm if type(e) not in (list, tuple): return str(e) match e[0]: case '=': return " = ".join(self.__repr__(i) for i in e[1:]) case 'var': return e[1] case _ if e[0] in self._ops.keys(): if (self._ops[e[0]][1] is not None) and (len(e) - 1 != self._ops[e[0]][1]): raise ValueError('Invalid statement') cur = self._ops[e[0]] if cur[0] != 0: br = (cur[0] < prev[0]) or \ ((cur[0] == prev[0]) and prev[1] is not None) return ('(' if br else '') + (" " + e[0] + " ").join(self.__repr__(i, cur) for i in e[1:]) + (')' if br else '') return e[0] + "(" + ", ".join(self.__repr__(i, cur) for i in e[1:]) + ")" def reduce(self, e = None): if e is None: e = self.elm if e[0] == 'var': return e # Convert '-' → '+'; '/' → '*' r = None if e[0] == '-': e[0] = '+' r = [ '*', e[2], -1, ] if e[0] == '/': e[0] = '*' r = [ '**', e[2], -1, ] if r is not None: e.pop(2) e.insert(2, r) # Reduce each element (+ previous conversion) to_remove = dict() for i in range(1, len(e)): if type(e[i]) is list: r = [self.reduce(e[i])] to_remove[i] = r[::-1] for i in tuple(to_remove.keys())[::-1]: e.pop(i) for ri in to_remove[i]: e.insert(i, ri) # Unwrap nested operations with infinite args to_remove = dict() for i in range(1, len(e)): if type(e[i]) is list: if (e[i][0] == e[0]) and (e[0] in filter(lambda x: self._ops[x][1] == None, self._ops.keys())): r = list(map(lambda x: self.reduce(x) if type(x) is list else x, e[i][1:])) to_remove[i] = r[::-1] for i in tuple(to_remove.keys())[::-1]: e.pop(i) for ri in to_remove[i]: e.insert(i, ri) # Find calculated numbers nums = tuple(filter(lambda x: type(x) is not list and type(x) is not str, e[1:])) # Reduce numbers if every argument is a number or there are some numbers in infinite-arg operation red = None if (e[0] in self._ops) and (((self._ops[e[0]][1] is None) and (len(nums) > 1)) or (self._ops[e[0]][1] == len(nums))): red = self._ops[e[0]][2](nums) # Substitute calculated numbers with reduced value if red is not None: e = [e[0]] + list(filter(lambda x: x not in nums, e[1:])) + [red] # Return elm or it's only argument if len(e) == 2: return e[1] return e class Store: _store = dict() def __init__(self): pass def __repr__(self) -> str: return f"{self._store!r}" def var(self, v: str) -> str: if v not in self._store: self._store[v] = None if self._store[v] is None: return ['var', v] return self._store[v] def set(self, v: str, n): self._store[v] = n store = Store() s = Statement([ '=', [ '/', [ '*', store.var('x'), 2, [ '-', 2, [ '+', 2, [ '-', 2, 1, ], ], ], [ '**', 2, 2, ], ], 8, ], [ '+', [ '+', [ '-', [ '*', 2, store.var('x'), ], 1, ], [ '*', 4, 1, ], ], 2, [ 'sin', [ '/', pi, 2, ], ], ], ]) print(s.elm) print(s) s.reduce() print(s.elm) print(s)
Editor Settings
Theme
Key bindings
Full width
Lines