Warning: this is an htmlized version!
The original is here, and
the conversion rules are here.
#!/usr/bin/env rexx

------------------------------------------------------------------------------
--        sc: a simple rpn calculator leveraging (oo)rexx's algorism        --
------------------------------------------------------------------------------

-- entry ---------------------------------------------------------------------

calculator=.calculator~new

file=.stream~new('~/.scrc')
loop while file~lines<>0
  if \calculator~evaluate(file~linein) then exit 1
end

signal on notready
signal on user bye
loop forever
  calculator~evaluate(linein())
end

bye:
notready:
exit

-- evaluation ----------------------------------------------------------------

::options digits 12

::class Calculator
  ::method init
    expose stack macro register binops digits fuzz comparator

    stack=.stack~new        -- data stack
    macro=.table~new        -- bundled and user defined macros
    register=.table~new     -- user registers (variables)

    -- binary operators (these pass through to rexx)
    binopWords='<= ** <> % & | * // + < = - > >= /'
    binops=.set~new~~putAll(binopWords~makeArray(' '))

    digits=digits()         -- used for sharing digits across methods
    fuzz=fuzz()             --    "   "       " fuzz   across methods

    comparator=.alnumcomparator~new

  ::method evaluate
    expose stack macro
    signal on user bye
    signal on user hiccup
    signal on user underflow
    -- remove comments, trim
    parse arg line '#' .
    stripped=strip(line)
    -- interpret line
    select
      -- macro definitions
      when pos(':', stripped)=1 then do
        parse lower value substr(stripped, 2) with name body
        select
          when body=''               then macro~remove(name)
          when wordpos(name, body)>0 then say '[macro recursion disallowed]'
        otherwise
          macro[name]=body
        end
        return 1
      end
      -- normal evaluation
      otherwise
        loop while line<>''
          parse lower var line word line
          if \self~evalWord(word) then return 0
        end
    end
    return 1
    bye:
      raise propagate
    underflow:
      say '[stack underflow]'
    hiccup:
      if \stack~isEmpty then say 'stack was:' stack~image
      stack~empty
      return 0

  ::method evalWord
    expose stack macro register digits fuzz
    use arg word
    signal on syntax
    signal on user bye
    signal on user hiccup
    signal on user underflow
    numeric digits digits  -- we might want to factor out consumers
    select
      -- numbers always self evaluate
      when isNumber?(word)      then stack~push(asDecimal(word))
      -- special purpose register syntax (word prefixes)
      when pos('->', word)=1    then register[substr(word, 3)]=stack~pop
      when pos('<-', word)=1    then self~fetchRegister(substr(word, 3))
      -- macros expand before primitives, after registers
      when macro~hasIndex(word) then return self~evaluate(macro[word])
      -- primitives
      when word='!'             then stack~push(factorial(stack~pop, digits))
      when word='.'             then say stack~pop
      when word='.bin'          then say '0b'd2b(stack~pop)
      when word='.hex'          then say '0x'd2x(stack~pop)
      when word='.oct'          then say '0o'd2o(stack~pop)
      when word='.p'            then say self~withPlaces
      when word='.s'            then say stack~image
      when word='2drop'         then stack~~pop~pop
      when word='2dup'          then stack~~over~over
      when word='bye'           then raise user bye
      when word='ceil'          then stack~push(stack~pop~ceiling)
      when word='choose'        then stack~push(choose(stack~pop, stack~pop, digits))
      when word='clear'         then register~empty
      when word='digits'        then stack~push(digits)
      when word='drop'          then stack~pop
      when word='dup'           then stack~push(stack~peek)
      when word='floor'         then stack~push(stack~pop~floor)
      when word='fuzz'          then stack~push(fuzz)
      when word='max'           then stack~push(max(stack~pop, stack~pop))
      when word='min'           then stack~push(min(stack~pop, stack~pop))
      when word='negate'        then stack~push(-stack~pop)
      when word='nip'           then stack~~swap~pop
      when word='not'           then stack~push(\stack~pop)
      when word='over'          then stack~over
      when word='rand'          then stack~push(random(stack~pop-1))
      when word='round'         then stack~push(stack~pop~round)
      when word='set-digits'    then digits=stack~pop
      when word='set-fuzz'      then fuzz=stack~pop
      when word='swap'          then stack~swap
      when word='tuck'          then stack~~swap~over
      when self~isBinop?(word)  then self~binop(word)
      -- constants
      when word='e'             then stack~push(.constants~e+0)
      when word='pi'            then stack~push(.constants~pi+0)
      -- non-decimal primitives
      when word='acos'          then self~rxMathUnary('RxCalcArcCos')
      when word='asin'          then self~rxMathUnary('RxCalcArcSin')
      when word='atan'          then self~rxMathUnary('RxCalcArcTan')
      when word='cos'           then self~rxMathUnary('RxCalcCos')
      when word='log'           then self~rxMathUnary('RxCalcLog')
      when word='log10'         then self~rxMathUnary('RxCalcLog10')
      when word='sin'           then self~rxMathUnary('RxCalcSin')
      when word='sqrt'          then self~rxMathUnary('RxCalcSqrt')
      when word='tan'           then self~rxMathUnary('RxCalcTan')
      -- usage
      when word='.macros'       then self~showMacros
      when word='.registers'    then self~showRegisters
      when word='.version'      then self~showRexxVersion
      when word='help'          then self~showHelp
      when word='words'         then self~showHelp
    otherwise
      say '["'word'" is not defined]'
      raise user hiccup
    end
    return 1
    syntax:
      say '[(syntax)' errortext(rc)']'
      raise user hiccup array ('syntax', errortext(rc), rc)
    bye: hiccup: underflow: raise propagate

  ::method fetchRegister private
    expose stack register
    parse lower arg name
    select
      when register~hasIndex(name) then stack~push(register[name])
    otherwise
      say '[unknown register "'name'"]'
      raise user hiccup
    end

  ::method withPlaces private
    expose stack
    signal on user underflow
    stack~swap
    return format(stack~pop,, stack~pop)
    underflow: raise propagate

  ::method isBinop? private
    expose binops
    use arg op
    return binops~hasItem(op)

  ::method binop private
    expose stack digits fuzz
    signal on user underflow
    use arg op
    b=stack~pop
    a=stack~pop
    numeric digits digits
    numeric fuzz fuzz
    stack~push(a~send(op, b))
    return
    underflow: raise propagate

  ::method rxMathUnary private
    expose stack digits
    parse arg fn
    signal on user underflow
    precision=digits
    if precision>16 then precision=16
    call (fn) stack~pop, precision
    stack~push(result)
    return
    underflow: raise propagate

  ::method showMacros
    expose macro comparator
    names=macro~allIndexes~sortWith(comparator)
    width=maxOver(names, 'length')+1
    loop name over names
      say ':'left(name, width) strip(macro[name])
    end
    return

  ::method showRegisters
    expose register comparator
    names=register~allIndexes~sortWith(comparator)
    width=maxOver(names, 'length')+1
    loop name over names
      say left(name, width) register[name]
    end
    return

  ::method showHelp
    expose binops macro register comparator
    ms=macro~items; rs=register~items
    -- primitives are manually populated for now, since they aren't indexes
    prims=('! . .bin .hex .macros .oct .p .registers .s .version 2drop 2dup',
           'acos asin atan ceil choose clear cos digits drop dup e floor',
           'fuzz help log log10 max min negate nip not over pi rand',
           'round set-digits set-fuzz sin sqrt swap tan tuck words'),
           ~makeArray(' ')
    -- combine primitives, binary operators and currently defined macros
    words=.set~new~~putAll(prims)~~putAll(binops)~~putAll(macro~allIndexes)
    say words~allItems~sortWith(comparator)~makeString(, ' ')
    say '(see also .macros ['ms 'defined] and .registers ['rs 'defined])'

  ::method showRexxVersion
    parse version v
    say v

-- numbers -------------------------------------------------------------------

::routine isNumber?
  use arg word
  return datatype(word, 'n')=1 | isOctal?(word) | isHex?(word) | isBin?(word)

::routine asDecimal
  use arg word
  select
    when isBin?(word)          then return x2d(b2x(substr(word, 3)))
    when isHex?(word)          then return x2d(substr(word, 3))
    when isOctal?(word)        then return o2d(substr(word, 3))
    when datatype(word, 'n')=1 then return word+0
  otherwise
    raise user hiccup
  end

::routine isBin?
  parse lower arg word
  return pos('0b', word)=1 & verify(substr(word, 3), '01')=0

::routine isOctal?
  parse lower arg word
  return pos('0o', word)=1 & verify(substr(word, 3), '01234567')=0

::routine isHex?
  parse lower arg word
  return pos('0x', word)=1 & verify(substr(word, 3), '0123456789abcdef')=0

::routine o2d
  parse arg n
  total=0
  r=n~reverse
  loop i=1 to n~length
    d=r[i]
    total+=d*(8**(i-1))
  end
  return total

::routine d2o
  parse arg n
  digits=''
  loop while n>0
    digits=(n//8)digits
    n%=8
  end
  return digits

::routine d2b
  parse arg n
  return strip(x2b(d2x(n)), 'l', 0)

-- calculations --------------------------------------------------------------

::routine factorial
  parse arg n, digits
  numeric digits digits
  f=1
  loop i=2 to n
    f*=i
  end
  return f

::routine fallingFactorial
  parse arg x, n, digits
  numeric digits digits
  ff=x
  loop k=1 to n-1
    ff*=x-k
  end
  return ff

::routine choose
  parse arg n, k, digits
  return fallingFactorial(n, k, digits)/factorial(k, digits)

-- utils ---------------------------------------------------------------------

::routine maxOver -- generalise if necessary
  use arg xs, msg
  m=0
  loop x over xs
    m=max(m, x~send(msg))
  end
  return m

-- stack ---------------------------------------------------------------------

::class Stack
  ::method init
    expose queue
    queue=.queue~new

  ::method isEmpty
    expose queue
    return queue~isEmpty

  ::method push
    expose queue
    use arg v
    queue~push(v)

  ::method pop
    expose queue
    if queue~isEmpty then raise user underflow
    else return queue~pull

  ::method peek
    expose queue
    if queue~isEmpty then raise user underflow
    else return queue~peek

  ::method swap
    expose queue
    if queue~items<2 then raise user underflow
    a=queue[1]; b=queue[2]
    queue[1]=b; queue[2]=a

  ::method over
    expose queue
    if queue~items<2 then raise user underflow
    queue~push(queue[2])

  ::method empty
    expose queue
    queue~empty

  ::method image
    expose queue
    img='<'queue~items'>'
    i=queue~last
    loop while i<>.nil
      img=img queue~at(i)
      i=queue~previous(i)
    end
    return img

-- sorting -------------------------------------------------------------------

::class AlnumComparator mixinclass Comparator
  ::method alnum?
    return arg(1)~verify(,
      '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz')=0

  ::method symbol?
    return \self~alnum?(arg(1))

  ::method compare
    use strict arg left, right
    ll=left~length; rl=right~length
    -- compare min length characters (or fewer, if possible)
    loop i=1 to min(ll, rl)
      l=left~substr(i, 1); r=right~substr(i, 1)
      select
        when l=r then iterate
        when self~symbol?(l) & self~alnum?(r) then return -1
        when self~symbol?(r) & self~alnum?(l) then return  1
        otherwise return l~compareTo(r)
      end
    end
    -- one string is a prefix of the other, so use length to order them
    return ll~compareTo(rl)

------------------------------------------------------------------------------

::class constants
-- https://oeis.org/A001113
::constant e  2.71828182845904523536028747135266249775724709369995957496696762772407663035354759457138217852516642742746
-- https://oeis.org/A000796
::constant pi 3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214

::requires 'rxmath' LIBRARY


/*
* (eepitch-shell)
* (eepitch-kill)
* (eepitch-shell)
./repl.rexx
.routine~newFile('sc.rexx')~call
1 2 + .
bye

-- foo
-- (find-oorexxrefpage)
-- (find-oorexxreftext)

-- (find-oorexxrefpage (+ 22 91) "3.2. ::CLASS")
-- (find-oorexxreftext (+ 22 91) "3.2. ::CLASS")



*/