src/wam/ui.lisp @ 72bbdd515725

Rewrite the compiler

A few days ago I found a bug in the compiler that I decided merited basically an
entire rewrite of it.

This was long overdue.  The compiler kind of grew organically and unhealthily
over time as I wrapped my head around how the whole WAM is structured, and now
that I understand a lot more I can do things right.

This new implementation is a lot "flatter" than the old one.  It makes use of
CLOS classes and generic methods to un-nest a lot of the crap that was
previously happening in bigass `labels` blocks.  This is a lot easier to read
and understand because you can take things a piece at a time.

Unfortunately, it's currently a lot slower than the old one.  But at least it's
*correct*, and now I can start taking a look at optimizing the performance with
a cleaner base to start from.

Notes/ideas for the near future:

* Switch to structs instead of CLOS classes for all the bits and bobs in the
  compilation process.
* Inline hot functions in the compilation process.
* Type hint the fucking compiler already.  I've put this off for far too long.
* Move the compiler to its own package for easier profiling and to maintain my
  shreds of sanity.
* Look into that generic-function-inlining library thing I saw on Reddit...
* Remove the last vestiges of `match` and kill the dependency on optima.
author Steve Losh <steve@stevelosh.com>
date Tue, 07 Jun 2016 14:49:20 +0000
parents 4d33f11b074f
children 410acaae0c14
(in-package #:bones.wam)


(defparameter *database* nil)
(defvar *results* nil)


(defun make-database ()
  (make-wam))

(defmacro with-database (database &body body)
  `(let ((*database* ,database))
     ,@body))

(defmacro with-fresh-database (&body body)
  `(with-database (make-database) ,@body))


(defun add-rules (rules)
  (compile-rules *database* rules))


(defmacro rule (&body body)
  `(add-rules '(,body)))

(defmacro fact (&body body)
  `(add-rules '(,body)))

(defmacro rules (&body rules)
  `(add-rules ',rules))

(defmacro facts (&body rules)
  `(add-rules ',(mapcar #'list rules)))


(defun display-results (results)
  (format t "~%")
  (loop :for (var result . more) :on results :by #'cddr :do
        (format t "~S = ~S~%" var result)))

(defun display-results-one (results)
  (display-results results)
  t)

(defun display-results-all (results)
  (display-results results)
  nil)

(defun display-results-interactive (results)
  (display-results results)
  (format t "~%More? [Yn] ")
  (force-output)
  (switch ((read-line) :test #'string=)
    ("y" nil)
    ("" nil)
    ("n" t)
    (t t)))


(defun perform-query (query mode)
  (run-query *database* query
             :result-function
             (ecase mode
               (:interactive #'display-results-interactive)
               (:all #'display-results-all)
               (:one #'display-results-one))
             :status-function
             (lambda (failp)
               (if failp
                 (princ "No.")
                 (princ "Yes."))))
  (values))


(defun return-results-one (results)
  (setf *results* results)
  t)

(defun return-results-all (results)
  (push results *results*)
  nil)


(defun perform-return (query mode)
  (ecase mode
    (:all (let ((*results* nil))
            (run-query *database* query
                       :result-function
                       #'return-results-all)
            (values *results* (ensure-boolean *results*))))
    (:one (let* ((no-results (gensym))
                 (*results* no-results))
            (run-query *database* query
                       :result-function
                       #'return-results-one)
            (if (eql *results* no-results)
              (values nil nil)
              (values *results* t))))))


(defun perform-prove (query)
  (nth-value 1 (perform-return query :one)))


(defmacro query (&body body)
  `(perform-query ',body :interactive))

(defmacro query-all (&body body)
  `(perform-query ',body :all))

(defmacro query-one (&body body)
  `(perform-query ',body :one))


(defmacro return-all (&body body)
  `(perform-return ',body :all))

(defmacro return-one (&body body)
  `(perform-return ',body :one))

(defmacro prove (&body body)
  `(perform-prove ',body))


(defun dump (&optional full-code)
  (dump-wam-full *database*)
  (when full-code
    (dump-wam-code *database*)))