import sys import inspect import re import getopt from fractions import Fraction as F parser = re.compile(r"(?P_?[1-9A-F][0-9A-F]*([.][0-9A-F]*)?|_?0?[.][0-9A-F]*|0?)" r"|(?:\s*)" r"|(?P.)") ibase = obase = 10 scale = 0 stack = [] ops = {} def make_op(name): def inner(f): in_arity = len(inspect.getfullargspec(f).args) def wrapper(): args = reversed([stack.pop() for _ in range(in_arity)]) result = f(*args) if result is None: return if isinstance(result, tuple): stack.extend(result) else: stack.append(result) ops[name] = wrapper return f return inner def push(x): stack.append(x) def pop(): return stack.pop() def powbgex(b, x): n = 1 bn = b while (bn2 := bn*bn) < x: n *= 2 bn = bn2 while bn < x: bn *= b n += 1 return n, bn basechars = "0123456789ABCDEF" def as_base(n: int, b: int, m: int = 0): if n == 0: return "0" assert n > 0 assert 2 <= b <= 16 result = [] while n or m: if m: m -= 1 n, d = divmod(n, b) result.append(basechars[d]) return "".join(reversed(result)) def format_num_in_base(x, b, sc): if x < 0: s = "-" x = -x else: s = "" whole, frac = divmod(x, 1) w = as_base(whole, b) if not frac: return s + w nplaces, bn = powbgex(b, frac.denominator) #print(f"{nplaces} {bn} {frac.denominator} {frac.denominator % bn}") if bn % frac.denominator: nplaces = max(nplaces, sc) f = as_base(int(frac * b ** nplaces), b, nplaces) return s + w + "." + f def print_num_in_base(x, b, sc): print(format_num_in_base(x, b, sc)) @make_op("c") def clear_stack(): del stack[:] @make_op("f") def print_stack_nondestructive(): for a in reversed(stack): print_num(a) @make_op("n") def print_num_nonl(x): print(format_num_in_base(x, obase, scale), end="") @make_op("P") def print_num(x): print_num_in_base(x, obase, scale) return x @make_op("p") def ppop(x): print_num(x) @make_op("+") def add(x, y): return x + y @make_op("-") def sub(x, y): return x - y @make_op("*") def mul(x, y): return x * y @make_op("/") def div(x, y): return x / y @make_op("d") def dup(x): return (x, x) @make_op("i") def set_ibase(x): global ibase ibase = int(x) @make_op("I") def get_ibase(): return ibase @make_op("o") def set_obase(x): global obase obase = int(x) @make_op("O") def get_obase(): return obase @make_op("k") def set_scale(x): global scale scale = int(x) @make_op("K") def get_scale(x): return scale def parse_number(x): if x.startswith("_"): result = -parse_number(x[1:]) return result whole, dot, frac = x.partition('.') result = 0 if whole: result = F(int(whole, ibase)) if frac: result += F(int(frac, ibase), ibase ** len(frac)) return result def invalid_fun(op): print(f"{op!r} ({ord(op)}) unimplemented") def parse_execute(line: str): for m in parser.finditer(line): if number := m.group("number"): push(parse_number(number)) elif op := m.group("op"): fun = ops.get(op, None) if fun is None: invalid_fun(op) else: fun() def main(): opts, args = getopt.getopt(sys.argv[1:], "e:i") interact = True if opts: interact = False for k, v in opts: if k == '-e': try: parse_execute(v) except Exception as e: print(e) if k == '-i': interact = True while interact: try: line = input("pydc> ") except EOFError: break try: parse_execute(line) except Exception as e: print(e) if stack: print("On stack at exit:") print_stack_nondestructive() if __name__ == '__main__': main()