dc.py
download raw
import sys
import inspect
import re
import getopt
from fractions import Fraction as F
parser = re.compile(r"(?P<number>_?[1-9A-F][0-9A-F]*([.][0-9A-F]*)?|_?0?[.][0-9A-F]*|0?)"
r"|(?:\s*)"
r"|(?P<op>.)")
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()