(in-package "FRAMES") ;;------------------------------------------------------------------------------ ;; ;; File: MICRO-FRAME.LISP ;; Created: 10/17/94 ;; Author: Will Fitzgerald ;; ;; Description: based on various versions of frame code developed by Chris ;; Riesbeck ;; ;;------------------------------------------------------------------------------ ;;------------------------------------------------------------------------------ ;; Packages ;;------------------------------------------------------------------------------ (eval-when (load eval compile) (unless (find-package :frames) (make-package :frames))) (in-package :frames) (use-package :tables) (export '(define-frame def-frame frame-of ->frame ->name frame-p abstractions specializations all-abstractions features attribute-value inherited-attribute-value abstp specp part-of whole-of abst-or-whole-of spec-or-part-of find-frame clear-frame-memory print-frames)) ;;------------------------------------------------------------------------------ ;; data structures for frames: Frames form a class, whose instances are stored ;; in a table keyed by their symbolic names. ;;------------------------------------------------------------------------------ (deftable frame-of) (defclass frame () ((name :initarg :name :accessor name) (abstractions :initarg :abstractions :initform nil :accessor abstractions) (specializations :initarg :specializations :initform nil :accessor specializations) (all-abstractions :initarg :all-abstractions :accessor all-abstractions) (features :initarg :features :initform nil :accessor features))) (defmethod print-object ((frame frame) stream) (format stream "[~S]" (name frame))) (defmethod name ((frame t)) frame) (defun frame-p (object) (typep object (find-class 'frame))) (defun ->frame (object) (if (frame-p object) object (frame-of object))) (defun ->name (object) (if (frame-p object) (name object) object)) (defun force-frame (name) (or (frame-of name) (setf (frame-of name) (make-instance 'frame :name name :all-abstractions (list name))))) ;;------------------------------------------------------------------------------ ;; Data structure for features (slots). A attribute/value pair. ;;------------------------------------------------------------------------------ (defclass feature () ((attribute :initarg :attribute :accessor attribute) (value :initarg :value :initform nil :accessor value))) (defun feature-p (object) (typep object (find-class 'feature))) (defmethod make-feature (attribute value) (make-instance 'feature :attribute attribute :value value)) (defun make-features (attribute-value-list) (loop for (attribute value) in attribute-value-list collect (make-feature attribute value))) (defmethod print-object ((feature feature) stream) (with-slots (attribute value) feature (print-unreadable-object (feature stream :type t :identity t) (format stream "~S ~S" attribute value)))) (defmethod feature-named ((frame frame) attribute) (loop for feature in (features frame) when (eq (attribute feature) attribute) return feature)) (defmethod attribute-value ((frame frame) attribute) (let ((feature (feature-named frame attribute))) (if feature (value feature) nil))) (defmethod (setf attribute-value) (value (frame frame) attribute) (let ((feature (feature-named frame attribute))) (if feature (setf (value feature) value) (let ((new-feature (make-feature :attribute attribute :value value))) (push new-feature (features frame)) value)))) ;; inherited attribute values (defun inherited-attribute-value (frame attribute) (or (attribute-value (->frame frame) attribute) (loop for abstraction in (abstractions frame) thereis (inherited-attribute-value (->frame abstraction) attribute)))) (defmethod part-of ((part frame) (whole frame)) (member (name part) (all-features whole) :key 'value)) (defmethod part-of ((part t) (whole t)) (let ((whole (frame-of whole)) (part (frame-of part))) (if (and whole part) (part-of part whole) nil))) (defmethod whole-of ((whole t) (part t)) (part-of whole part)) ;;------------------------------------------------------------------------------ ;; Abstractions and specializations ;;------------------------------------------------------------------------------ (defmethod all-abstractions ((frame t)) (let ((frame-maybe (frame-of frame))) (if frame-maybe (all-abstractions frame-maybe) (list frame)))) (defmethod update-specializations ((frame frame)) (loop for abstraction in (abstractions frame) doing (setf (specializations (force-frame abstraction)) (pushnew (name frame) (specializations (frame-of abstraction)))))) (defmethod update-abstractions ((frame frame)) (setf (all-abstractions frame) (calculate-all-abstractions frame)) (loop for specialization in (specializations frame) doing (update-abstractions (frame-of specialization)))) (defmethod calculate-all-abstractions* ((frame frame)) (cond ((null (abstractions frame)) nil) (t (append (abstractions frame) (loop for abstraction in (abstractions frame) appending (calculate-all-abstractions* (force-frame abstraction))))))) (defmethod calculate-all-abstractions ((frame frame)) (cons (name frame) (remove-duplicates (calculate-all-abstractions* frame)))) (defmethod abstp ((abst frame) (spec frame)) (member (name abst) (all-abstractions spec) :test 'eq)) (defmethod abstp ((abst t) (spec t)) (let ((af (frame-of abst)) (sf (frame-of spec))) (if (and af sf) (abstp af sf) (eql abst spec)))) (defmethod specp ((spec t) (abst t)) (abstp abst spec)) (defun abst-or-whole-of (big small) (or (abstp big small) (whole-of big small))) (defun spec-or-part-of (small big) (or (specp small big) (part-of small big))) ;;------------------------------------------------------------------------------ ;; Interface to clear memory and define frames ;;------------------------------------------------------------------------------ (defun clear-frame-memory () (clear-table (frame-of))) (set-macro-character #\[ #'(lambda(stream char) (declare (ignore char)) `(frames:frame-of ',@(read-delimited-list #\] stream t))) nil ; not non-terminating. Cannot be embedded w/in symbols ) ;;; causes a right-bracket w/o a left to signal an error (set-macro-character #\] (get-macro-character #\) ) nil) (defun define-frame (name abstractions attribute-value-list) (let ((frame (force-frame name))) (setf (abstractions frame) (mapcar 'name abstractions)) (setf (features frame) (make-features attribute-value-list)) (update-specializations frame) (update-abstractions frame) frame)) (defmacro def-frame (name &optional abstractions &rest attribute-value-list) `(define-frame ',name ',abstractions ',attribute-value-list)) ;;------------------------------------------------------------------------------ ;; Frame finding ;;------------------------------------------------------------------------------ (defmethod all-features ((frame frame)) (remove-duplicates (append (loop for abstraction in (abstractions frame) appending (all-features (frame-of abstraction))) (features frame)) :key 'attribute)) (defun find-frame (abst features) "Find a frame starting at abst, with the features listed." (if (null features) (->frame abst) (let ((specs (find-specs abst features))) (if (and (null (rest specs)) (features-subsetp features (first specs))) (->frame (first specs)) (define-frame (gen-frame-name (first specs)) specs features))))) (defun find-specs (abst features) "Find the most specific specialization of abst." (or (remove-duplicates (loop for spec in (specializations (->frame abst)) when (features-abstp spec features) nconc (find-specs spec features))) (list abst))) (defun features-abstp (abst features) (loop for (attribute value) in features always (abstp (inherited-attribute-value (->frame abst) attribute) value))) (defun features-subsetp (features abst) (subsetp features (all-features (->frame abst)) :test #'(lambda (feature-list feature) (and (eql (first feature-list) (attribute feature)) (eql (second feature-list) (value feature)))))) (defun gen-frame-name (name) (gentemp (format nil "~A-" (symbol-name name)))) (defun features->feature-specs (features) (loop for feature in features collecting (list (attribute feature) (value feature)))) ;;; Printing utilities ;;; ---------------------------------------------------------------------- ;;; (DISPLAY-FRAME frame [stream]) => no values ;;; DISPLAY-FRAME prints the frame in a readable fashion on the stream ;;; (which defaults to the standard output). The frame argument ;;; can be either the name of a frame or an internal frame structure. ;;; Nested frames are printed in full form the first time they are ;;; seen. (defun display-frame (frame &optional (stream *standard-output*)) (cond ((null frame) nil) ((not (frame-p frame)) (display-frame (frame-of frame))) (t (let ((*frames-shown* '())) (declare (special *frames-shown*)) (format stream "~%~S~%" (name frame)) (pprint-frame-info frame stream 4) (format stream "~%") (values))))) ;;; (PPRINT-frame-INFO frame stream left-margin) => undefined ;;; PPRINT-frame-INFO prints internal frame structures in a readable ;;; fashion on stream, indented left-margin number of spaces. (defun pprint-frame-info (frame stream left-margin) (declare (special *frames-shown*)) (unless (or (null frame) (member frame *frames-shown*)) (push frame *frames-shown*) (loop for abst in (abstractions frame) do (format stream "~VT:ISA ~S~%" left-margin abst)) (loop for feature in (features frame) do (format stream "~VT~S ~S~%" left-margin (attribute feature) (value feature)) (pprint-frame-info (frame-of (value feature)) stream (+ left-margin 4))))) (defun display-frames-with-roles (name roles &optional (stream *standard-output*) &aux shown) (labels ((show (name prefix) (let* ((frame (->frame name)) (specs (specializations frame)) (features (and roles (features frame)))) (cond ((member name shown) (format stream (if (or specs features) "~S...~%" "~S~%") name)) (t (format stream "~S~%" name) (push name shown) (when features (let ((bar (if specs "|" " "))) (dolist (feature features) (when (and (feature-p feature) (member (attribute feature) roles)) (format stream "~A ~A ~S ~S~%" prefix bar (attribute feature) (value feature)))))) (when specs (do ((next-prefix (format nil "~A | " prefix)) (last-prefix (format nil "~A " prefix)) (l specs (cdr l))) ((null (cdr l)) (format stream "~A +-- " prefix) (show (car l) last-prefix)) (format stream "~A |-- " prefix) (show (car l) next-prefix)))))))) (show name "") name))