;; -*- Mode:LISP; Package:(BOXER GLOBAL 1000); Base:10.; Fonts:CPTFONT -*- ;; ;; (C) Copyright 1983 MIT ;; ;; Permission to use, copy, modify, distribute, and sell this software ;; and its documentation for any purpose is hereby granted without fee, ;; provided that the above copyright notice appear in all copies and that ;; both that copyright notice and this permission notice appear in ;; supporting documentation, and that the name of M.I.T. not be used in ;; advertising or publicity pertaining to distribution of the software ;; without specific, written prior permission. M.I.T. makes no ;; representations about the suitability of this software for any ;; purpose. It is provided "as is" without express or implied warranty. ;; ;; ;; Deep Binding in Boxer. ;;Dynamic Boxer variables exist in an alist. You get the value of a ;;variable by calling the lookup function on it. ;; ;;If the variable is not found in the alist, then the static variables of the boxes in ;;the lexical scope of the outermost box being executed are searched. This searching ;;happens by asking the DOIT'ed box to look up the variable in its static ;;alist, and failing finding it there to ask the box it is inside of to do the same, ;;all the way to the toplevel box. ;;If this search fails, then the lookup function checks the global lispm value cell ;;of the symbol. This keeps it from having to search a long ``tail'' of primitive ;;values. ;;FUNCTION CALLING. ;;When a function is called, the funcalling mechanism boxer-binds the input variables of the ;;box being called to be the argument values. It does this by lisp-binding the big alist ;;to be a cons of those variable names and values on the front of ;;the big alist. This lisp binding goes away when the funcall primitive returns. ;; ;;In addition to the input variables, then alist of static variables for the current box ;;is copied and added to the big alist temporary binding. It is copied since in our ;;copy-and-execute model, modifications to the static bindings of a box made while the ;;box is being are not retained when the box returns. ;;***this is not yet implemented*** ;; ;; TELL ;;TELL binds *BOXER-BINDING-ALIST-ROOT* to NIL (to hide any dynamic bindings) ;;and binds *BOXER-BINDING-ALIST-ROOT* to box being told. (deff boxer-error 'ferror) (defvar *currently-executing-box* nil "BOXER-FUNCALL binds this to the box it is funcalling.") (DEFVAR *BOXER-STATIC-VARIABLES-ROOT* NIL "The DOIT key binds the box whose region is being run to be this box.") (DEFMACRO WITH-STATIC-ROOT-BOUND (NEW-ROOT &BODY BODY) `(LET ((*BOXER-STATIC-VARIABLES-ROOT* ,NEW-ROOT)) . ,BODY)) (DEFVAR *BOXER-DYNAMIC-VARIABLES-ALIST* NIL) (DEFMACRO WITH-DYNAMIC-VALUES-BOUND (NEW-FRAME &BODY BODY) `(LET ((*BOXER-DYNAMIC-VARIABLES-ALIST* (ADJOIN-FRAME ,NEW-FRAME *BOXER-DYNAMIC-VARIABLES-ALIST*))) . ,BODY)) (DEFMACRO WITH-NEW-DYNAMIC-VALUES (NEW-FRAME &BODY BODY) `(LET ((*BOXER-DYNAMIC-VARIABLES-ALIST* (ADJOIN-FRAME ,NEW-FRAME NIL))) . ,BODY)) (defmacro boxer-let* (bindings &body body) `(let ((*boxer-binding-alist-root* (nconc (mapcar #'(lambda (pair) (cons (car pair) (eval (cadr pair)))) ',bindings) *boxer-binding-alist-root*))) .,body)) ;;Handling the dynamic environment ;;; this need to flatten out any exporting boxes (SLOW !!!) ;;; The whole exporting scheme needs to be re-implemented for speed ;;; and here's an example why.... (DEFUN GET-LOCAL-ENV (BOX) (COND ((BOX? BOX) (LET* ((BINDINGS (TELL BOX :GET-STATIC-VARIABLES-ALIST)) (EXPORTS (MAPCAR #'CDR (SUBSET #'(LAMBDA (X) (EQ (CAR X) *EXPORTING-BOX-MARKER*)) BINDINGS))) (parsed-bindings (with-collection (dolist (b bindings) (unless (eq (car b) *exporting-box-marker*) (collect b)))))) (LEXPR-FUNCALL #'APPEND parsed-bindings (MAP-TELL EXPORTS :GET-STATIC-VARIABLES-ALIST)))) ((NUMBERP BOX) NIL) (T (EVBOX-BINDINGS BOX)))) ;;; This is doing EXPLICIT copying of local variables because we are only copying the args and ;;; NOT the function itself whenever we funcall (DEFSUBST MAKE-FRAME (BOX &OPTIONAL ARGS) (NCONC (NCONS (CONS :FRAME-HEADER BOX)) (PAIRLIS ;side effects are safe because of (GET-ARG-NAMES BOX) ;PAIRLIS ARGS) (LET ((*EVALUATOR-COPYING-FUNCTION* #'SHALLOW-COPY-FOR-ARGLIST)) (MAPCAR #'(LAMBDA (X) (CONS (CAR X) (COPY-FOR-EVAL (CDR X)))) (GET-LOCAL-ENV BOX))))) (DEFSUBST ADJOIN-FRAME (FRAME ENV) (APPEND FRAME ENV)) ;;Variable lookup function ;; note that box can be an EVbox (defun lookup-static-variable (variable box) (cond ((box? box) (tell box :lookup-static-variable-check-superiors variable)) ((evbox? box) (assq variable (evbox-bindings box))) (t (ferror "Don't know how to look up the variable, ~S, in ~S" variable box)))) (DEFUN BOXER-SYMEVAL (VARIABLE) (LET ((ENTRY (ASSQ VARIABLE *BOXER-DYNAMIC-VARIABLES-ALIST*))) (COND ((NOT (NULL ENTRY)) (CDR ENTRY)) ((SETQ ENTRY (lookup-static-variable VARIABLE *BOXER-STATIC-VARIABLES-ROOT*)) (CDR ENTRY)) ((BOUNDP VARIABLE) ;global primitive? (SYMEVAL VARIABLE)) ;we cache them to avoid a long tail in the alist. (T (BOXER-ERROR "The variable ~A is not bound." VARIABLE))))) (DEFUN BOXER-BOUNDP (VARIABLE) (or (assq variable *BOXER-DYNAMIC-VARIABLES-ALIST*) (LOOKUP-STATIC-VARIABLE variable *BOXER-STATIC-VARIABLES-ROOT*) (boundp variable))) ;global primitive? ;; local lookup function ;; This takes an alist and looks up the variable. If there are EXPORTS into the alist, then ;; we recurse through the alists of the exports as well ;; GET-NAMED uses this ;; Note that this is doing a depth first search of the exports (where we might actually want ;; a breadth first search (DEFUN LOOKUP-LOCAL-VARIABLE (VAR ALIST) (LET ((EXPORTS (SUBSET #'(LAMBDA (X) (EQ (CAR X) *EXPORTING-BOX-MARKER*)) ALIST)) (THING (CDR (ASSQ VAR ALIST)))) (IF (NOT (NULL THING)) THING (DOLIST (EXPORT EXPORTS) (LET ((VALUE (LOOKUP-LOCAL-VARIABLE VAR (GET-LOCAL-ENV (CDR EXPORT))))) (WHEN (NOT (NULL VALUE)) (RETURN VALUE))))))) ;;; KEEP this around for the parser ;Variable setting function with searching. Errors if there is no such variable. ;Copied from lookup function. ;This is a low-level function. Note that sometimes variable "setting" ;is implemented as box-alteration. ;(defun boxer-set (variable value) ; (let ((entry (assq variable *BOXER-DYNAMIC-VARIABLES-ALIST*))) ; (cond ((access-pair? variable) ; (let ((*BOXER-STATIC-VARIABLES-ROOT* (boxer-eval (access-pair-superbox variable))) ; (*BOXER-DYNAMIC-VARIABLES-ALIST* NIL)) ; (boxer-set (caar (get-pre-box-rows (access-pair-subbox variable))) value))) ; ((not (null entry)) (setf (cdr entry) value)) ; (t (setq entry (tell *BOXER-STATIC-VARIABLES-ROOT* ; :LOOKUP-STATIC-VARIABLE-CHECK-SUPERIORS ; variable)) ; (if (not (null entry)) ; (setf (cdr entry) value) ; (boxer-error "The variable ~S is not bound." variable)))))) ;;; Weird stuff. ;;; Since there's no consistency about EVBOX objects we'll just add this here. (defun add-static-variable-to-evbox (evbox variable value) (if (eq variable *exporting-box-marker*) (add-static-variable-to-evbox-internal evbox variable value) (let ((entry (assq variable (evbox-bindings evbox)))) (cond ((null entry) (add-static-variable-to-evbox-internal evbox variable value)) (t (format t "Warning, replacing the old value of ~A" variable) (setf (cdr entry) value)))))) (defun add-static-variable-to-evbox-internal (evbox variable value) (set-evbox-bindings evbox (cons (cons variable value) (evbox-bindings evbox)))) ;;;Lower level methods. ;;;Adds the variable/value pair to the current box's static variable alist. ;;;Needs to be smart about altering the alist -- or maybe re-calculating it or something? ;;;This implementation is broken since you won't be able to access the variable after ;;;you use it. (DEFMETHOD (BOX :SET-STATIC-VARIABLES-ALIST) (NEW-ALIST) ;; the file system uses this one. (SETQ STATIC-VARIABLES-ALIST NEW-ALIST)) (DEFMETHOD (BOX :GET-STATIC-VARIABLES-ALIST) () ;; the file system uses this one too. STATIC-VARIABLES-ALIST) (defun boxer-add-static-variable (variable value) (tell (or *CURRENTLY-EXECUTING-BOX* *BOXER-STATIC-VARIABLES-ROOT*) :ADD-STATIC-VARIABLE-PAIR variable value)) (defmethod (box :add-static-variable-pair) (variable value) (let ((entry (assq variable static-variables-alist))) (WHEN (AND (NOT-NULL (TELL SELF :LOOKUP-STATIC-VARIABLE-IN-BOX-ONLY VARIABLE)) (NEQ (CDR (TELL SELF :LOOKUP-STATIC-VARIABLE-IN-BOX-ONLY VARIABLE)) VALUE) (NEQ VARIABLE *EXPORTING-BOX-MARKER*)) ;; The name is already defined in the current box to be something else (FORMAT T "Warning, replacing the old value of ~A "VARIABLE)) (WHEN (SPRITE-BOX? VALUE) ;; This is not the correct solution since you might want to keep ;; some named sprites private to the graphics box. This should ;; cause the average user to win most of the time though (TELL SELF :EXPORT-VARIABLE VARIABLE)) (COND ((AND (NEQ VARIABLE *EXPORTING-BOX-MARKER*) (not (null entry))) (setf (cdr entry) value)) ((AND (EQ VARIABLE *EXPORTING-BOX-MARKER*) (EQ VALUE (CDR ENTRY)))) ;;try and cut down on multiple copies of the same box being exported (T (push (cons variable value) static-variables-alist))))) (DEFMETHOD (BOX :REMOVE-ALL-STATIC-BINDINGS) (VALUE) "Removes all the variables which may be bound to VALUE. " (LOOP WITH NEW-EXPORTS = NIL FOR PAIR IN STATIC-VARIABLES-ALIST UNLESS (EQ (CDR PAIR) VALUE) COLLECT PAIR INTO NEW-ALIST WHEN (AND (LISTP EXPORTS) (EQ (CDR PAIR) VALUE)) DO (SETQ NEW-EXPORTS (DELQ (CAR PAIR) EXPORTS)) FINALLY (SETQ STATIC-VARIABLES-ALIST NEW-ALIST) (unless (eq exports *EXPORT-ALL-VARIABLES-MARKER*) (setq EXPORTS NEW-EXPORTS)))) (DEFMETHOD (BOX :REMOVE-STATIC-VARIABLE) (VARIABLE) "Removes only the single variable binding from the Box's environment. " (SETQ STATIC-VARIABLES-ALIST (DELQ (ASSQ VARIABLE STATIC-VARIABLES-ALIST) STATIC-VARIABLES-ALIST)) (WHEN (AND (NOT-NULL EXPORTS) (NEQ EXPORTS *EXPORT-ALL-VARIABLES-MARKER*)) (SETQ EXPORTS (DELQ VARIABLE EXPORTS)))) (DEFMETHOD (BOX :SET-EXPORTS) (NEW-EXPORTS) (SETQ EXPORTS NEW-EXPORTS)) (DEFMETHOD (BOX :GET-EXPORTS) () (IF (EQ EXPORTS *EXPORT-ALL-VARIABLES-MARKER*) (MAPCAR #'CAR STATIC-VARIABLES-ALIST) EXPORTS)) (DEFMETHOD (BOX :EXPORT-ALL-VARIABLES) () (WHEN (NULL EXPORTS) (TELL (TELL SELF :SUPERIOR-BOX) :ADD-STATIC-VARIABLE-PAIR *EXPORTING-BOX-MARKER* SELF)) (SETQ EXPORTS *EXPORT-ALL-VARIABLES-MARKER*)) (DEFMETHOD (BOX :EXPORT-VARIABLE) (VARIABLE) (LET ((VALUE (TELL SELF :LOOKUP-STATIC-VARIABLE-IN-BOX-ONLY VARIABLE))) (UNLESS (NULL VALUE) (WHEN (NULL EXPORTS) (TELL (TELL SELF :SUPERIOR-BOX) :ADD-STATIC-VARIABLE-PAIR *EXPORTING-BOX-MARKER* SELF)) (UNLESS (EQ EXPORTS *EXPORT-ALL-VARIABLES-MARKER*) (PUSH VARIABLE EXPORTS))))) (DEFMETHOD (BOX :GET-EXPORTING-BOXES) () "Get a list of all the other boxes which export their variable bindings to this one. " (MAPCAR #'CDR (SUBSET #'(LAMBDA (X) (EQ (CAR X) *EXPORTING-BOX-MARKER*)) STATIC-VARIABLES-ALIST))) (DEFMETHOD (BOX :LOOKUP-STATIC-VARIABLE-IN-EXPORTS) (VARIABLE) (LET ((EXPORTING-BOXES (TELL SELF :GET-EXPORTING-BOXES)) (EXPORTING-P (OR (EQ EXPORTS *EXPORT-ALL-VARIABLES-MARKER* ) (MEMQ VARIABLE EXPORTS))) (VALUE (ASSQ VARIABLE STATIC-VARIABLES-ALIST))) (COND ((AND VALUE EXPORTING-P) VALUE) ((AND ;(OR (EQ EXPORTS *EXPORT-ALL-VARIABLES-MARKER* ) (MEMQ VARIABLE EXPORTS)) ;allow exported variables to automatically be visible anywhere up the chain ;of exporting boxes. (NOT-NULL EXPORTING-BOXES)) (DOLIST (BOX EXPORTING-BOXES) (LET ((BINDING-PAIR (TELL BOX :LOOKUP-STATIC-VARIABLE-IN-EXPORTS VARIABLE))) (WHEN (NOT-NULL BINDING-PAIR) (RETURN BINDING-PAIR)))))))) (DEFMETHOD (BOX :LOOKUP-STATIC-VARIABLE-IN-BOX-ONLY) (VARIABLE) (LET ((VALUE (ASSQ VARIABLE STATIC-VARIABLES-ALIST)) (EXPORTING-BOXES (TELL SELF :GET-EXPORTING-BOXES))) (COND (VALUE VALUE) ((NOT-NULL EXPORTING-BOXES) (DOLIST (BOX EXPORTING-BOXES) (LET ((BINDING-PAIR (TELL BOX :LOOKUP-STATIC-VARIABLE-IN-EXPORTS VARIABLE))) (WHEN (NOT-NULL BINDING-PAIR) (RETURN BINDING-PAIR)))))))) (DEFMETHOD (BOX :SUPERIOR-BOX-FOR-BINDINGS) () (TELL SELF :SUPERIOR-BOX)) (DEFMETHOD (PORT-BOX :SUPERIOR-BOX-FOR-BINDINGS) () (TELL-CHECK-NIL PORTS :SUPERIOR-BOX)) (defmethod (box :lookup-static-variable-check-superiors) (variable) (let ((value (assq variable static-variables-alist)) (EXPORTING-BOXES (TELL SELF :GET-EXPORTING-BOXES)) (superior)) (cond (value value) ;; if we found it, return it ((NOT-NULL EXPORTING-BOXES) ;; first, look in the boxes which export their variables to this box (let ((result (DOLIST (BOX EXPORTING-BOXES) (LET ((BINDING-PAIR (TELL BOX :LOOKUP-STATIC-VARIABLE-IN-EXPORTS VARIABLE))) (WHEN (NOT-NULL BINDING-PAIR) (RETURN BINDING-PAIR)))))) (if result result (tell (tell self :superior-box-FOR-BINDINGS) :lookup-static-variable-check-superiors variable)))) ((setq superior (tell self :superior-box-FOR-BINDINGS)) (tell superior :lookup-static-variable-check-superiors variable)) (t nil)))) (DEFMETHOD (BOX :LOCAL-LIBRARY) () (OR LOCAL-LIBRARY (SETQ LOCAL-LIBRARY (MAKE-INITIALIZED-BOX ':TYPE ':LL-BOX ':EXPORTS *EXPORT-ALL-VARIABLES-MARKER*)))) ;; the file system uses this one (DEFMETHOD (BOX :SET-LOCAL-LIBRARY) (NEW-LL) (SETQ LOCAL-LIBRARY NEW-LL)) (DEFMETHOD (BOX :REMOVE-LOCAL-LIBRARY) () (WHEN (NOT-NULL LOCAL-LIBRARY) (TELL SELF :REMOVE-ALL-STATIC-BINDINGS LOCAL-LIBRARY) (SETQ LOCAL-LIBRARY NIL)))