__________________________

						CLEANING UP THE RPN CALC

								  lro
					   __________________________


							<2019-01-07 Mon>


Table of Contents
_________________

Current architecture
Saving user functions


So Last post I alluded that I was going to clean things things up a bit
and add a way to save any user define functions.

Lets have a general look at how things work at the moment, then go from
there.


Current architecture
====================

  At the moment, when the main loop encounters a function, it calls
  /run-func/ which checks if the `func' is the dictionary, and if it is
  run it otherwise print an error. And when the function is called, if
  its a primitive, the function is /rpn-func/, which takes a scheme
  function and how many arguments it needs, then /pop/s the appropriate
  number of arguments and calls the function on tose arguments and
  /push/es the result to the stack that is then returned. If a user
  defined function is called, then the list of functions is executed
  using /run-func/, and the altered stack passed along to the next
  function in the list.

  ,----
  |       +----------+ 	  number   +------------+  list   +----------+
  |   +---|   push   |<------------| Main loop  |-------->| new-func |----|
  |   |   +----------+			   +------------+         +----------+    |
  |   |                             ^    |   ^ ^                          |
  |   +-----------------------------+    |   | |--------------------------|
  |                                      |   |
  |                                      |   |
  |                               symbol |   ------|
  |                                      |         |
  |                                 +----------+   |
  |                                 | run-func |   |
  |                                 +----------+   |
  |                                      |         |
  |                                      |         |
  |                                  +------+      |
  |                                  | func |      |
  |                                  +------+      |
  |                                      |         |
  |                                      |         |
  |                                 +----------+   |
  |                                 | rpn-func |----
  |                                 +----------+
  `----

  <file:rpn-calc-func-call-diagram.png>

  So its not terribly complex, but I would prefer if calling a function
  didn't jump thorugh so many hoops. But it isn't really that much
  abstraction. If I wanted to remove, I could write /rpn-func/ as a
  macro that expanded to a lambda that was juct the appropiate
  /let-values/ for many arguments the function took.

  For example:

  ,----
  | (rpn-func + 2)
  `----

  Would expand too:

  ,----
  | (lambda (stack dict)
  |   (let*-values (((var1 stack) (pop stack))
  | 				  ((var2 stack) (pop stack)))
  | 	  (push (+ var1 var2) stack)))
  `----

  Which would be awesome, but I don't know if it's worth it. For now I
  think i'll just keep things as they are with regards to function
  calls. I don't want to use macros to much, I think it would be best to
  try and keep them to a minimum, so I'm not going to build `init-dict'
  at compile time either, maybe in my next phlog post as a little detour
  I can go ham on the macros, both as practice and something interesting
  to phlog about.


Saving user functions
=====================

  Looking at how I add user functions to the dictionary, it would be
  pretty easy to add some code to /new-func/ that writes the user
  functions to a file like /user-funcs/ and just write the lists to
  it. Then when we start the main loop, we can call a function that
  finds this file, and loads all the functions into the dictionary at
  start up. We can abuse/use read again by using /parameterize/ to make
  the standard input the /user-funcs/ file and add all the functions at
  startup using /new-func/.

  Unfortunately, there is no standard way in R7RS to append to a file,
  what bullshit.

  So we have some options:
  1. read through the whole /user-funcs/ to get to the end. (slow)
  2. just overwrite the file everytime we save. (stupid)
  3. read /user-funcs/ in at startup, save to a string, and append new
     functions to that string, and saves that string to the file.

  We will go with number 3.

  So firstly we need a function to add a new user func to the
  /user-funcs/ string. I have used /parameterize/ so that I can use
  /display/ which will print our newlines correctly so that "your-funcs"
  has each function on a line. It then _overwrites_ the file with the
  old funcs and the new func, and returns that string as well.

  ,----
  | (define (add-user-func list user-funcs file)
  |   (let ((func-to-add (list-as-string list)))
  | 	(parameterize ((current-output-port (open-output-file file)))
  | 	  (let ((new-user-funcs (string-append user-funcs func-to-add "\n")))
  | 		(display new-user-funcs)
  | 		(close-output-port (current-output-port))
  | 		new-user-funcs))))
  `----

  I needed a function that converted lists to a string, because schemes
  /list->string/ just converts a list of chars to one string.

  ,----
  | (define (list-as-string list)
  |   (parameterize ((current-output-port (open-output-string)))
  | 	(write list)
  | 	(get-output-string (current-output-port))))
  `----

  Now we can enter new functions to our save file, which is held in a
  variable.

  ,----
  | (define funcs-file "your-funcs")
  `----

  And now we need functions to retrieve the functions from the save file
  at the start of the main loop.

  ,----
  | (define (load-funcs-from-file-dict file dict)
  |   (with-input-from-file file
  | 	(lambda ()
  | 	  (let loop ((input (read))
  | 				 (dict dict))
  | 		(if (eof-object? input)
  | 		  dict
  | 		  (loop (read) (new-func input dict)))))))
  | 
  | (define (load-funcs-from-file-str file)
  |   (with-input-from-file file
  | 	(lambda ()
  | 	  (let loop ((next-str (read-string 10))
  | 				 (str ""))
  | 		(if (eof-object? next-str)
  | 		  str
  | 		  (loop (read-string 10) (string-append str next-str)))))))
  `----

  And our new main loop

  ,----
  | (let loop ((input (read)) 
  | 		   (stack '())
  | 		   (dict (load-funcs-from-file-dict funcs-file init-dict))
  | 		   (user-funcs (load-funcs-from-file-str funcs-file)))
  |   (cond
  |    ((number? input) (loop (read) (push input stack) dict user-funcs))
  |    ((list? input) (let ((user-funcs (add-user-func input user-funcs funcs-file)))
  | 					(loop (read) stack (new-func input dict) user-funcs)))
  |    ((symbol? input) (loop (read) (run-func input dict stack) dict user-funcs))
  |    (else (begin
  | 		   (display "ERROR not valid input: ")
  | 		   (display input)
  | 		   (newline)
  | 		   (loop (read) stack dict)))))
  `----

  I also found a bud in my /update-alist/ function, it added an extra
  layer of parenthesis when updating functions.

  ,----
  | (define (update-alist key new-val alist)
  |   (let ((index (index-in-alist key alist)))
  | 	(list-set! alist index (cons key new-val))
  | 	alist))
  `----

  So thats it for now, if anyone has any feature suggestions or patches
  or bug reports, feel free to email me, lro@sdf.org.

  Next time we might do some macro programming, or just actually go
  through and use this thing and do some cool RPN maths programming,
  we'll see.