src/paip-compiled.lisp @ 3a8ee0586fdf
Add destructive unification
Will be used in the PAIP compiler.
author |
Steve Losh <steve@stevelosh.com> |
date |
Tue, 22 Mar 2016 14:31:35 +0000 |
parents |
(none) |
children |
3729fdede843 |
(in-package #:bones.paip)
;;;; Variables
(define-constant unbound "Unbound"
:test #'equal
:documentation "A magic constant representing an unbound variable.")
(defvar *var-counter* 0
"The number of variables created so far.")
(defstruct (var (:constructor ? ())
(:print-function print-var))
(name (incf *var-counter*)) ; The variable's name (defaults to a new number)
(binding unbound)) ; The variable's binding (defaults to unbound)
(defun* print-var ((var var) stream depth)
(if (or (and (numberp *print-level*)
(>= depth *print-level*))
(var-p (deref var)))
(format stream "?~A" (var-name var))
(write var :stream stream)))
(defun* bound-p ((var var))
(:returns boolean)
"Return whether the given variable has been bound."
(not (eq (var-binding var) unbound)))
(defmacro deref (expr)
"Chase all the bindings for the given expression in place."
`(progn
(loop :while (and (var-p ,expr) (bound-p ,expr))
:do (setf ,expr (var-binding ,expr)))
,expr))
;;;; Bindings
(defvar *trail* (make-array 200 :fill-pointer 0 :adjustable t)
"The trail of variable bindings performed so far.")
(defun* set-binding! ((var var) value)
(:returns (eql t))
"Set `var`'s binding to `value` after saving it in the trail.
Always returns `t` (success).
"
(when (not (eq var value))
(vector-push-extend var *trail*)
(setf (var-binding var) value))
t)
(defun* undo-bindings! ((old-trail integer))
(:returns :void)
"Undo all bindings back to a given point in the trail.
The point is specified by giving the desired fill pointer.
"
(loop :until (= (fill-pointer *trail*) old-trail)
:do (setf (var-binding (vector-pop *trail*)) unbound))
(values))
;;;; Unification
(defun* unify! (x y)
(:returns boolean)
"Destructively unify two expressions, returning whether it was successful.
Any variables in `x` and `y` may have their bindings set.
"
(cond
;; If they're identical objects (taking bindings into account), they unify.
((eql (deref x) (deref y)) t)
;; If they're not identical, but one is a variable, bind it to the other.
((var-p x) (set-binding! x y))
((var-p y) (set-binding! y x))
;; If they're both non-empty lists, unify the cars and cdrs.
((and (consp x) (consp y))
(and (unify! (first x) (first y))
(unify! (rest x) (rest y))))
;; Otherwise they don't unify.
(t nil)))