(defvar *forth-operations* (make-hash-table))
(defmacro defforth (name min &body body)
"Define a Forth command and store it in *FORTH-OPERATIONS*.
A Forth command requires one argument, called LST and returns two values,
the first is a new list and the second is whether the command succeeded."
(let* ((fn-name (concatenate 'string "forth-" (symbol-name name)))
(fn-symbol (intern (string-upcase fn-name))))
`(progn
(defun ,fn-symbol (lst)
(if (>= (length lst) ,min)
(values (progn ,@body) t)
(values nil nil)))
(setf (gethash ',name *forth-operations*) #',fn-symbol))))
(defun call-forth (fn lst)
"Calls a Forth function of name FN with LST."
(funcall (gethash fn *forth-operations*) lst))
(defun forth-commands (lst command-list)
"Execute a series of Forth commands on a list.
E.g. (FORTH-COMMANDS '(A B C) '(OVER SWAP))."
(if (null command-list)
lst
(forth-commands (call-forth (car command-list) lst) (cdr command-list))))
(defforth swap 2
(nconc (list (second lst) (first lst))
(nthcdr 2 lst)))
(defforth dup 1
(cons (first lst) lst))
(defforth over 2
(cons (second lst) lst))
(defforth rot 3
(nconc (list (third lst) (first lst) (second lst))
(nthcdr 3 lst)))
(defforth drop 1
(rest lst))
(defforth 2swap 4
(nconc (list (third lst) (fourth lst) (first lst) (second lst))
(nthcdr 4 lst)))
(defforth 2dup 2
(nconc (list (first lst) (second lst))
lst))
(defforth 2over 4
(cons (caddr lst) (cons (cadddr lst) lst)))
(defforth 2rot 6
(nconc (list (fifth lst) (sixth lst)
(first lst) (second lst)
(third lst) (fourth lst))
(nthcdr 6 lst)))
(defforth 2drop 2
(nthcdr 2 lst))
(defforth nip 2
(forth-commands lst '(swap drop)))
(defforth tuck 2
(forth-commands lst '(swap over)))
(defun find-stack-operations (from-list to-list &optional (max 5))
"Finds the shortest list of Forth operations to convert a list to the
given form. MAX gives an upper limit to the search length.
E.g. (FIND-STACK-OPERATIONS '(A B C) '(A B C A B C))."
(let ((from (reverse from-list))
(to (reverse to-list)))
(labels ((rec (lst n &optional acc)
(cond ((equal lst to)
(format t "~{~a~^ ~}~%" (nreverse acc))
(return-from find-stack-operations t))
((zerop n) nil)
(t (maphash (lambda (name fn)
(multiple-value-bind (new-lst okayp)
(funcall fn lst)
(when okayp
(rec new-lst (1- n) (cons name acc)))))
*forth-operations*)))))
(do ((n 0 (1+ n)))
((> n max) (format t "This would be too complicated.~%"))
(rec from n)))))