______________________

						  RPN CALCULATOR AGAIN

								  lro
						 ______________________


							<2019-01-04 Fri>


Table of Contents
_________________

New data structure for functions
alist
.. insertion
.. update
calling a function in the loop
Initial Dictionary
Main loop
Conslusion


I'm back with another Installment of the RPN calculator. Today I thought
we could clean up that awful main loop, by have one expression for
symbols, and keep a list of all the functions that we accept, and search
throught it. I might also add a FORTH style syntax for defining your own
functions.


New data structure for functions
================================

  I was thinking that we we need a new data structre for our functions,
  an alist where the car is the symbol we want to use as an identifier
  and the cdr being the function itself, taking one argument, the stack,
  and returning the stack. This will be our "dictionary". But as an
  exercise we will also include a very simple alist implementation.


alist
=====

  We need some fundamental operations for an alist, retrieve value,
  insertion, deletion, update. Luckily R7RS provides some functions for
  us, like /assq/ which retrieve a value from an alist, and if it
  doesn't exist, return `false'.


insertion
~~~~~~~~~

  To insert into an alist we must search the alist and make sure that
  that key doesn't alredy exist, and if it doesn't then append it to our
  list with our chosen values.

  ,----
  | (define (insert-into-alist key val alist)
  |   (let ((mem? (assq key alist)))
  | 	(if mem?
  | 	  (update-alist key val alist)
  | 	  (append alist (list (cons key val))))))
  `----


update
~~~~~~

  To update an alist, we have to find the key, and change its associated
  value, and return the modified dictionary. We will also assume that
  /key/ exists in the alist.

  We need to find the index into the list whee our key is, so we
  traverse our alist until we find it, then we can use that index in
  /list-set!/ which isn't functional but the easiest way of doing it.

  ,----
  | (define (index-in-alist key alist)
  |   (let loop ((list (list-copy alist))
  | 			 (index 0))
  | 	(if (= (length list) 0)
  | 	  #f
  | 	  (let ((list-head-key (car (car list))))
  | 		(if (eq? list-head-key key)
  | 		  index
  | 		  (loop (cdr list) (+ index 1)))))))
  | 
  | (define (update-alist key new-val alist)
  |   (let ((index (index-in-alist key alist)))
  | 	(list-set! alist index (list (cons key new-val)))
  | 	alist))
  `----


calling a function in the loop
==============================

  Now how is calling a function from our main loop going to work? Well
  first we need too check if the symbol is in the dictionary, if it
  isn't then we need to return an error. If it is then we need to use
  /assq/ o get the function and run it pasing it the stack. We will need
  a helper function, /run-func/ that takes the symbol, dictionary and
  the stack.

  ,----
  | (define (run-func sym dict stack)
  |   (let ((func (assq sym dict)))
  | 	(if func
  | 	  ((cdr func) stack)
  | 	  (begin
  | 		(display "ERROR: symbol not in dictionary: ")
  | 		(display sym)
  | 		(newline)
  | 		stack))))
  `----


Initial Dictionary
==================

  This will be the initial dictionary that is passed to the main loop,
  it wil hold all of our "primitives" that we can add to later.

  ,----
  | (define init-dict 
  |   (list (cons '$ (lambda (stack)
  | 				   (let-values (((var stack) (pop stack)))
  | 					 (display var)
  | 					 (newline)
  | 					 stack)))
  | 		(cons '+ (lambda (stack) (rpn-func + stack)))
  | 		(cons '- (lambda (stack) (rpn-func - stack)))
  | 		(cons '* (lambda (stack) (rpn-func * stack)))
  | 		(cons '/ (lambda (stack) (rpn-func / stack)))
  | 		(cons '% (lambda (stack) (rpn-func modulo stack)))
  | 		(cons '! (lambda (stack)
  | 				   (let-values (((var stack) (pop stack))) 
  | 					 (push (fact var) stack))))
  | 		(cons '/ (lambda (stack) (rpn-func / stack)))
  | 		(cons 'dup (lambda (stack) (dup stack)))))
  `----


Main loop
=========

  The main loop now only checks for symbols and passes any to our
  /run-func/ function.  And we have added our dictionary to the loop, so
  that we can modify it in our REPL when we add that functionality.

  ,----
  | (let loop ((input (read)) 
  | 		   (stack '())
  | 		   (dict init-dict))
  |   (cond
  |    ((number? input) (loop (read) (push input stack) dict))
  |    ((symbol? input) (loop (read) (run-func input dict stack) dict))
  |    (else (begin
  | 		   (display "ERROR not valid input: ")
  | 		   (display input)
  | 		   (newline)
  | 		   (loop (read) stack dict)))))
  `----


Conslusion
==========

  So next time we will add the ability to add our own user defined
  functions. In the mean time feel free to email me some feedback at
  lro@sdf.org, or even try adding some functions to the initial
  dictionary yourself, like some trigonometry functions or vector math
  like dot or cross product.