_____________________________

                                         PROGRAMMABLE RPN CALCULATOR

                                                                 lro
                                        _____________________________


                                                       <2019-01-05 Sat>


Table of Contents
_________________

More primitives
User defined functions (attempt 1)
. user-func-from-list
User defined functions (Attempt 2)


Today I am going to add the ability to create user defined functions to
the RPN calculator, and add some more primitives like triginometry
functions and things like truncate, floor etc.


More primitives
===============

 We need some more primitives I think to round out the basic
 functionality of the calculator, so I will add the functions /sin/,
 /cos/, /tan/, /truncate/, /floor/ and /ceiling/.

 But first I want to re-write /rpn-func/, so that it has another
 argument, the number of arguments the function that is passed to it
 takes, so that we can use it for functions like /fact/ that take one
 variable.

 ,----
 | (define (rpn-func func args stack)
 |   (if (= args 1)
 |     (let-values (((var stack) (pop stack)))
 |       (push (func var) stack))
 |     (let*-values (((var1 stack) (pop stack))
 |                               ((var2 stack) (pop stack)))
 |       (push (func var1 var2) stack))))
 `----

 I would love to be able to write a better version of this function, it
 currently only works with functions that take one argument or two, but
 I would lave to write a general version that can run a function of
 arbitrary arguments. It would probably have to be a macro that
 expanded to a function that just had a bunch of nested /let-values/
 and ran the func at the end with required funcs.

 When expanded it would look something like this, if the function took
 two arguments.

 ,----
 | ;; Not working code
 | (lambda (stack)
 |   (let-values (((var1 stack) (pop stack)))
 |     (let-values (((var2 stack) (pop stack)))
 |       (push (func var1 var2) stack)))) ;; func being the function
 |                                                    ;; passed to the macoro
 `----

 But alas, maybe something to work on later. Or if anyone has any ideas
 that would be sick too.

 Any way here is our modified initial dictionary.

 ,----
 | (define (swap stack)
 |   (let ((a (car stack))
 |              (b (cadr stack)))
 |     (append (list b) (list a) (cddr stack))))
 |
 | (define init-dict
 |   (list (cons '$ (lambda (stack)
 |                                (let-values (((var stack) (pop stack)))
 |                                      (display var)
 |                                      (newline)
 |                                      stack)))
 |             (cons '+ (lambda (stack) (rpn-func + 2 stack)))
 |             (cons '- (lambda (stack) (rpn-func - 2 stack)))
 |             (cons '* (lambda (stack) (rpn-func * 2 stack)))
 |             (cons '/ (lambda (stack) (rpn-func / 2 stack)))
 |             (cons '% (lambda (stack) (rpn-func modulo 2 stack)))
 |             (cons '! (lambda (stack) (rpn-func fact 1 stack)))
 |             (cons 'dup (lambda (stack) (dup stack)))
 |             (cons 'swap (lambda (stack) (swap stack)))
 |             (cons 'sin (lambda (stack) (rpn-func sin 1 stack)))
 |             (cons 'cos (lambda (stack) (rpn-func cos 1 stack)))
 |             (cons 'tan (lambda (stack) (rpn-func tan 1 stack)))
 |             (cons 'trunc (lambda (stack) (rpn-func truncate 1 stack)))
 |             (cons 'ceil (lambda (stack) (rpn-func ceiling 1 stack)))
 |             (cons 'floor (lambda (stack) (rpn-func floor 1 stack)))))
 `----


User defined functions (attempt 1)
==================================

 How we are going to approach user defined functions is I think, pretty
 sweet.

 If we enter a list at our command line, the /read/ functions passes
 the whole thing along in one go, so we can have a predicate on our
 main loop that checks if the input is a list, and then pass that list
 to the function that will add it to the dictionary.

 We will use a macro to construct a series of embedded /let/ forms that
 execute each function in turn, or push the number onto the stack, so
 we can have constants in user defined functions.

 Unfortunately we have to change our design sligtly by passing the
 dictionary to every RPN function call, beacause new user functions
 need to refer to the primitives, which can only happen if when
 executing the dictionary is available. So now every function in the
 dictionary takes two arguments, the stack and the dictionary. Which
 isn't great, and I could avoid that by having two dictionaries, the
 primitives and the user. But I would prefer to have only one
 dictionary, so I have to do the ugly thing.

 ,----
 | (define (new-func list dictionary)
 |   (insert-into-alist (car list) (user-func-from-list (cdr list)) dictionary))
 `----


user-func-from-list
~~~~~~~~~~~~~~~~~~~

 Now this macro will take the list, minus the head, because that will
 be the identifier for the function. And return our function. The macro
 will have to to expand from this:

 ,----
 | (dup cos swap sin /)
 `----

 To this:

 ,----
 | (lambda (stack dict)
 |   (let ((stack (run-func '/ dict stack)))
 |     (let ((stack (run-func 'sin dict stack)))
 |       (let ((stack (run-func 'swap dict stack)))
 |             (let ((stack (run-func 'cos dict stack)))
 |               (let ((stack (run-func 'dup dict stack)))
 |                     stack))))))
 `----

 ,----
 | (define-syntax user-func-from-list
 |   (syntax-rules ()
 |     ((user-func-from-list () form . forms)
 |      '(lambda (stack dict) form . forms))
 |
 |     ((user-func-from-list (variable) form . forms)
 |      (user-func-from-list () '(let ((stack (run-func (quote variable) dict stack))) form . forms)))
 |
 |     ((user-func-from-list (variable . variables) form . forms)
 |      (user-func-from-list variables '(let ((stack (run-func (quote variable) dict stack))) form . forms)))
 |
 |     ((user-func-from-list (variable . variables))
 |      (user-func-from-list variables '(let ((stack (run-func (quote variable) dict stack))) stack)))
 |
 |     ((user-func-from-list (quote (variable . variables)))
 |      (user-func-from-list variables '(let ((stack (run-func (quote variable) dict stack))) stack)))))
 `----

 Now the reason that this is attempt is a failure is I was trying to
 use a macro at runtime instead of expansion/compile time. So this
 isn't going to work unfortunately, but a good learning experience I
 think.


User defined functions (Attempt 2)
==================================

 So this time around we are going to keep /new-func/ as it is, but
 /user-func-from-list/ will be a function that returns a function. And
 that function will be the same as the ones in our dictionary, taking
 the stack, but we will have to modify all of our dictionary functions
 to also take the dictionary, so that our user functions can refer to
 all the previously defined functions.

 The function will loop over the list and execute each function in
 turn, and when the list runs out of functions, return the stack.

 ,----
 | (define (user-func-from-list func)
 |   (lambda (stack dict)
 |     (let loop ((func func)
 |                        (stack stack))
 |       (if (= (length func) 1)
 |             (if (number? (car func))
 |               (push (car func) stack)
 |               (run-func (car func) dict stack))
 |             (if (number? (car func))
 |               (loop (cdr func) (push (car func) stack))
 |               (loop (cdr func) (run-func (car func) dict stack)))))))
 `----

 And now we just have to change the main loop to test for lists, and
 pass the list to /new-func/.

 ,----
 | (let loop ((input (read))
 |                (stack '())
 |                (dict init-dict))
 |   (cond
 |    ((number? input) (loop (read) (push input stack) dict))
 |    ((list? input) (loop (read) stack (new-func input 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)))))
 `----

 And thats it! You can define new functions on the command line, but I
 should probably clean up things and maybe make it so you can save
 those user defined functions...