rpn_dev.py

Created by garycmartin

Created on August 23, 2018

2.7 KB

More powerful version of the RPN code in development, treat as unstable expect to use most of your available program memory. Additions over the simple RPN version are; str and rcl for storing and recalling a memory value; auto for standard format numbers; fix for setting fixed decimal notation (push a number to the stack first for the number of places, i.e. 2 fix for rounding to two decimal places); eng for engineering notation (where the exponent is in powers of a thousand); si for displaying International System units (G, M, k, m, u, n, etc).


from math import *
from random import random

ops = {'+': [2,lambda x,y: x+y],
 '-': [2,lambda x,y: x-y],
 '*': [2,lambda x,y: x*y],
 '/': [2,lambda x,y: x/y],
 'pow': [2,lambda x,y: pow(x,y)],
 'sqr': [1,lambda x: x*x],
 'recip': [1,lambda x : 1/x],
 '!': [1,lambda x: fact(int(round(x)))],
 'deg': [1,lambda x: degrees(x)],
 'rad': [1,lambda x: radians(x)],
 'log': [1,lambda x: log(x)],
 'log10': [1,lambda x: log10(x)],
 'sqrt': [1,lambda x: sqrt(x)],
 'sin': [1,lambda x: sin(x)],
 'cos': [1,lambda x: cos(x)],
 'tan': [1,lambda x: tan(x)],
 'asin': [1,lambda x: asin(x)],
 'acos': [1,lambda x: acos(x)],
 'atan': [1,lambda x: atan(x)],
 'pi': [0,pi],
 'e': [0,e],
 'rnd': [0,random()]}

def evaluate(tokens, s, mem):
 for t in tokens:
  if t == 'swap':
   if len(s) >= 2:
    a = s.pop(); b = s.pop()
    s.append(a); s.append(b)
  elif t == 'roll':
   if len(s) >= 2: s.insert(0, s.pop())
  elif t == 'drop':
   if len(s) >= 1: s.pop()
  elif t == 'clr':
   s = []
  elif t == 'sto':
   if len(s) >= 1: mem = s[-1]
  elif t == 'rcl':
   s.append(mem)
  elif t in ops:
   op = ops[t][1]
   q = ops[t][0]
   if q == 0:
    s.append(op)
   elif q == 1 and len(s) >= 1:
    a = s.pop()
    try: s.append(op(a))
    except: print("Err"); s.append(a)
   elif q == 2 and len(s) >= 2:
    a = s.pop(); b = s.pop()
    try: s.append(op(b,a))
    except: print("Err"); s.append(b); s.append(a)
  elif set(t).issubset(set("0123456789.-")):
   s.append(float(t))
  else:
   print("Err %s" % t)
 return s, mem

def p(s):
 print(s, end='')

def fact(n):
 f = 1
 while n > 0:
  f *= n; n -= 1
 return f

def eng_not(n, fix):
 for exp in range(-24, 25, 3):
  if abs(n) < pow(10, exp): break
 return '{}E{}'.format(n/float(pow(10, exp-3)), exp-3)

def si_not(n, fix):
 i = 0
 for exp in range(-24, 25, 3):
  if abs(n) < pow(10, exp): break
  i += 1
 u = 'yzafpnum kMGTPEZY'[i-1]
 if u == ' ': u = ''
 return '{}{}'.format(n/float(pow(10, exp-3)), u)

s = []
mem = 0.0
fix = 3
mode = 0
while True:
 expr = input('> ')
 if expr == 'q':
  break
 elif expr == 'fix':
  if len(s) >= 1: fix = abs(int(round(s.pop()))); auto = False
 elif expr == 'auto':
  mode=0
 elif expr == 'eng':
  mode=1
 elif expr == 'si':
  mode=2
 elif expr == '?':
  print('Help:', end=' ')
  w = 0
  for o in sorted(list(ops.keys()) + ['fix','eng','si','auto','sto','rcl','?','q','clr','swap','roll','drop']):
   print(o, end=' '); w += 1
   if w > 5: print(); w = 0
  if w != 0: print()
  continue
 elif len(expr) == 0:
   continue
 else:
   s, mem = evaluate(expr.split(), s, mem)
 print('[', end='')
 for i in s:
  if mode==0: p(i)
  elif mode==1: p(eng_not(i, fix))
  elif mode==2: p(si_not(i, fix))
  else: p('{:.{p}f}'.format(i, p=fix))
  if i is not s[-1]: p(', ')
 else:
  print(']')