AAL sources (7 of 8)

Jonathan Amsterdam jba at wheaties.ai.mit.edu
Sun Jun 11 07:33:16 AEST 1989


;;; -*- Mode: LISP; Syntax: Common-lisp; Package: USER; Base: 10 -*-

;;; Interpreter for AAL.
;;; Copyright 1988 by Jonathan Amsterdam.  All Rights Reserved.

(provide 'interp)
(require 'initial "initial.lisp")
(require 'streams "streams.lisp")

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; The main loop of the adventure system:
;;; 1. Run all expired BEFORE-TICK timers.
;;; 2. If between turns, run all expired BEFORE-TURN timers.
;;; 3. If between turns, prompt for command and parse input.
;;; 4. Do the requested command.
;;; 5. Increment TICK.
;;; 6. Run all expired AFTER-TICK timers.
;;; 7. If the command's duration has not expired, goto step 1.
;;; 8. Increment TURN.
;;; 9. Run all expired AFTER-TURN timers.

;;; Ticks measure time passage in the game.  Turns just measure the player's
;;; inputs; a turn may take 0, 1 or several ticks.  For zero-tick turns, the
;;; tick timers are not run, but the turn timers are. 

;;; NOTE: This whole turn/tick distinction and its implementation needs to be
;;; rethought.  It theory it's a nice idea to be able to have turns take longer
;;; than one time unit.  You might want to have a walk down a long hall to take
;;; longer than just going through a doorway, or have filling a gallon jug from
;;; a spigot take longer than filling a cup.  The problem is that it's hard to
;;; actually spread out the execution of a command over an extended amount of
;;; time.  Instead, it's modelled here by doing the action all at once, then
;;; counting off the time.  This can differ from the "right" way when timers go
;;; off during the time of the action.  Take the spigot case: say a timer goes
;;; off at some point, shutting off the supply to the spigot.  If the shutoff
;;; occurs in the middle of an action, the player should get an amount of
;;; liquid proportional to the portion of the action completed; but in this
;;; implementation, the player would get the full amount of liquid.
;;;    Another problem, easier to solve, is that in this implementation
;;; durations are numbers associated with commands; so the "n" command can only
;;; have one duration.  As the above examples make clear, the duration should
;;; be a function of all the things involved in the command.

(defun run ()
  (let ((action-duration 1)
	(tick-to-resume 0))
    (catch 'end-game
      (loop
        (unless (= action-duration 0)
	  (run-expired-timers :before :tick))
	(when (or *abort-action* (>= *tick* tick-to-resume))
	  (setq *abort-action* nil)
	  (run-expired-timers :before :turn)
	  (setq action-duration (input-and-act))
	  (setq tick-to-resume (+ action-duration *tick*)))
	(unless (= action-duration 0)
	  (inc-tick)
	  (run-expired-timers :after :tick)
	  (when (>= *tick* tick-to-resume)
	    (inc-turn)
	    (run-expired-timers :after :turn)))))))

(defun inc-tick ()
  ;; We keep the actual tick count (in lisp variable *tick*) separate from the
  ;; AAL global *tick so that the AAL program can't alter the real value.  (And
  ;; similarly for turn.)
  (incf *tick*)
  (set-global '*tick *tick*))

(defun inc-turn ()
  (incf *turn*)
  (set-global '*turn *turn*))

(defun input-and-act ()
  (if (not (prompt-and-parse))
      0
      (initiate-command (global-value '*command))))

(defun end-game ()
  (format t "~2%The game is over.~%")
  (display-score)
  (throw 'end-game nil))

;;; Scoring is simple: just ask every object for its maximum score and the
;;; current score.  The problem is that scores must be associated with objects
;;; (including locations); you can't easily arrange to get points for, say,
;;; surviving past the 30th turn.

(defun display-score ()
  (let ((score (compute-score))
	(max-score (compute-max-score)))
    (format t "Your score is ~d out of a possible ~d " score max-score)
    (format t "(that's ~d%).~%" 
	    (round (* (/ score (if (zerop max-score) 1 (float max-score)))
		      100)))
    (format t "You've taken ~d turns in ~d ticks.~%" *turn* *tick*)
    score))

(defun compute-score ()
  (sum-action-results 'score))

(defun compute-max-score ()
  (sum-action-results 'max-score))

(defun sum-action-results (prop)
  ;; For every object that has property prop, run the action, and accumulate
  ;; the results.
  (let ((sum 0))
    (dolist (obj *objects*)
      (let ((action (get obj prop)))
	(if action
	    (incf sum (or (execute-action-in-object obj action) 0)))))
    sum))

  
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Timers.

(defun run-expired-timers (before-after turn-tick)
  (let ((timer-list (get-headed-timer-list before-after turn-tick))
	(time (if (eq turn-tick :turn) *turn* *tick*)))
    (dolist (ti (cdr timer-list))
      (if (> time (timer-time-to-run ti))
	  (error "time ~a > timer ~a time" time ti)
	  (when (= time (timer-time-to-run ti))
	    (report "~&Running timer ~a~%" ti)
	    (execute-action (timer-action ti) *globals*)
	    (if (> (timer-renew-time ti) 0)
		(setf (timer-time-to-run ti) (+ time (timer-renew-time ti)))))))
    ;; purge expired timers
    (setf (cdr timer-list)
	  (delete-if #'(lambda (ti) (= time (timer-time-to-run ti))) (cdr timer-list)))))

(defun get-headed-timer-list (before-after turn-tick)
  (assoc turn-tick (cdr (assoc before-after *timers*))))

(defun add-timer (timer)
  (push timer (cdr (get-headed-timer-list (timer-before-after timer)
					  (timer-turn-tick timer)))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Global and local variables.

(defun global-value (var)
  (alist-var-value var *globals*))

(defun alist-var-value (var alist)
  ;; This is simpler than var-value: it doesn't have to deal with variables
  ;; bound to other variables.
  (let ((pair (assoc var alist)))
    (if pair
	(cdr pair)
	(error "The variable ~a is unbound" var))))

(defun set-global (var value) 
  (set-var var value *globals*))

(defun set-var (var value alist)
  ;; You can't set variables that don't exist.  That's why globals have to be
  ;; declared.
  (let ((pair (assoc var alist)))
    (if pair
	(setf (cdr pair) value)
	(error "Attempt to set unbound AAL variable ~a" var))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Parsing the player's input.

;;; The syntax of a command is: [agent,] verb ...  where "..." is specified by
;;; the SYNTAX property of the verb.  There are several weaknesses in the
;;; parsing method: commands are tied too closely to their syntax (the first
;;; word of the command line must be the name of the command); commands can
;;; have only one syntax (so you can't have both "give the bone to the dog" and
;;; "give the dog the bone") and each thing (command or object) mentioned must
;;; be one word long.

(defun prompt-and-parse ()
  ;; Returns T if successful.
  (fresh-line)
  (format t "~d> " (1+ *turn*))
  (let* ((string-input (read-line))
	 (input (string->list string-input))
	 (comma-list (member :comma input))
	 (agent-list (ldiff input comma-list))
	 (verb-list (or (cdr comma-list) input)))
    (cond
      ((null comma-list)
       (set-global '*agent 'player))
      ((not (singleton? agent-list))
       (format t "~&Syntax is: <agent>, ...~%")
       (return-from prompt-and-parse nil))
      (t
       (set-global '*agent (car agent-list))))
    (let* ((verb (car verb-list))
	   (command (get-command-name verb))
	   (syntax  (get command 'syntax)))
      (when (null command)
	(format t "~&I don't know the word ~a.~%" verb)
	(return-from prompt-and-parse nil))
      (set-global '*verb verb)
      (set-global '*command command)
      (parse-by-syntax (cdr verb-list) syntax))))

(defun parse-by-syntax (input-list syntax-list)
  ;; Returns T iff successful.
  ;; This is basically like unification: the input list contains symbols, and
  ;; the syntax list contains symbols and possibly variables.  We set the
  ;; global values of the variables to what they match, and confirm that the
  ;; symbols match.
  ;;   If the input list is shorter, error.  Ideally, the program would figure
  ;; out reasonable values for the missing variables.  But not now.
  ;;   If the syntax list is shorter, that's OK.
  (cond
    ((null input-list)
     (cond
       ((null syntax-list)
	t)
       (t
	(format t  "~&I need more info~%")
	nil)))
    ((null syntax-list)
     t)
    ((var? (car syntax-list))
     (set-global (car syntax-list) (car input-list))
     (parse-by-syntax (cdr input-list) (cdr syntax-list)))
    ((eql (car input-list) (car syntax-list))
     (parse-by-syntax (cdr input-list) (cdr syntax-list)))
    (t
     (format t  "~&The word ~a should be ~a~%" (car input-list)
	     (car syntax-list))
     nil)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Reading a string into a list without character-by-character parsing: it's
;;; easy using read-from-string, except that we have to watch out for commas,
;;; which are the only legal punctuation.  Other punctuation might cause
;;; problems too, but this implementation doesn't worry about that.  (We have
;;; to watch out for commas because in Common Lisp, they're illegal outside a
;;; backquote.)


(defvar *hacked-readtable* (copy-readtable))

(defun comma-reader-func (stream char)
  (declare (ignore stream char))
  :comma)

(set-macro-character #\, #'comma-reader-func nil *hacked-readtable*)

(defun string->list (string)
  ;; Temporarily rebind the readtable to my own version, put parens around the
  ;; string, and read it.
  (let ((*readtable* *hacked-readtable*))
    (read-from-string (string-append "(" string ")"))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Doing a command:

;;; 1. Check the REQUIRES conditions in the order specified by the
;;;    REQUIREMENTS-ORDER property of the command.  If one is not satisfied,
;;;    print a message and return 0 duration.
;;; 2. Begin executing the first of the actions found in the order specified by
;;;    the ACTIONS-ORDER property of the command.  If that action returns
;;;    :CONTINUE (by the use of the (continue) action) then keep going.

;;; This is a generalization of what was presented in the article; there, the
;;; requirements order was fixed as (*command *agent *obj *instr) and the
;;; actions order as (*agent *obj *instr *command).  Those are still basically
;;; the default, except that the location (*loc) has been added to allow rooms
;;; to have a say in what goes on.  It is also possible for a command to
;;; override the default order.  See the "command" macro in comp.lisp.

(defun initiate-command (command)
  ;;Returns the duration of the action in ticks.
  (cond
    ((not (satisfies-requirements command (get command 'requirements-order)))
       0)
    (t
     (execute-command command (get command 'actions-order))
     (or (get command 'duration) 1))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Requirements.

(defun satisfies-requirements (command req-order)
  (every #'(lambda (var) (check-requirements command var))
	 req-order))

(defun check-requirements (command case)
  (let* ((obj (global-value case))
	 (reqs (get-requirements obj command case)))
    (dolist (req reqs)
      (setf (requirement-succeeded? req) nil))
    (let ((result (call-function-in-object #'(lambda (bindings)
						(check-reqs reqs bindings))
					   obj)))
      (if (not (eq result t))
	  (execute-action-in-object obj result))
      (eq result t))))

;;; Checking requirements: the failure message is printed only if the pattern
;;; never succeeds.  Once a pattern succeeds, its message will not be printed.

(defun check-reqs (reqs bindings)
  ;; Returns either T if all requirements can be satisfied, or the action to
  ;; be done if they can't.
  (if (null reqs)
      t
      (let* ((req (car reqs))
	     (binding-stream (deduce (requirement-pattern req) bindings))
	     (f-action nil))
	(cond
	  ((stream-empty? binding-stream)
	   (return-from check-reqs (if (requirement-succeeded? req)
				       nil
				       (requirement-failure-action req))))
	  (t
	   (setf (requirement-succeeded? req) t)
	   (dostream (binds binding-stream)
	     (let ((result (check-reqs (cdr reqs) binds)))
	       (if (eq result t)
		   (return-from check-reqs t)
		   (if result
		       (setq f-action result)))))
	   f-action)))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Executing user commands.

(defun execute-command (command actions-order)
  ;; The variables in actions-order are checked in order for actions pertaining
  ;; to this command.  When an action is found, it is executed.  If the rules
  ;; of the action actually fired, then execute-command returns, unless the
  ;; result of the rules was :continue.  If no rules fired, execute-command
  ;; continues looking.  The result of the action is returned, or NIL if no
  ;; action fired.
  (dolist (case actions-order)
    (let* ((obj (global-value case))
	    (action (get-action obj command case)))
      (if action
	  (let ((result (execute-action*-in-object obj action)))
	    (if (not (member result '(:did-not-fire :continue)))
		(return result)))))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Executing actions.

(defun execute-action (action bindings)
  ;; Returns two values.  The first is the result of the action, or NIL if no
  ;; rule in the action fired.  The second is the new bindings (this would only
  ;; be used internally.)  You can't distinguish between an action consisting
  ;; of rules returning NIL as a result of one of its rules firing, vs. having
  ;; none of its rules fire.
  (multiple-value-bind (result new-bindings)
      (execute-action* action bindings)
    (values (if (eq result :did-not-fire) nil result) new-bindings)))

(defun execute-action* (action bindings)
  ;; Differs from execute-action only in that it returns :DID-NOT-FIRE instead
  ;; of NIL when appropriate.
  (funcall (get (car action) 'action) action bindings))
  
(defun execute-action-in-object (obj action)
  ;; This makes sure that the object's local variables are accessible.
  (call-function-in-object #'(lambda (bindings) 
				(execute-action action bindings))
			   obj))

(defun execute-action*-in-object (obj action)
  (call-function-in-object #'(lambda (bindings) 
				(execute-action* action bindings))
			   obj))


(defunp (action block) (block bindings)
  ;; (block <action>*).  Does all the actions one after the other.
  ;; Returns the value of the last action, like PROGN.  Accumulates bindings.
  (let (result)
    (dolist (action (cdr block))
      (multiple-value-setq (result bindings) (execute-action action bindings)))
    (values result bindings)))

;;; A rule-list is a list of forward rules.  The first rule whose pattern is
;;; satisfied is executed, and the value of the action of that rule is
;;; returned.  :DID-NOT-FIRE is returned if no rules in the list match.  The
;;; bindings are consulted to obtain values for free variables in the rules.
;;; Bindings are not accumulated from rule to rule; the top-level bindings are
;;; used throughout.

(defunp (action rule-list) (rule-list bindings)
  (dolist (rule (cdr rule-list))
    (let ((result (action-rule-func rule bindings)))
      (if (not (eq result :did-not-fire))
	  (return-from action-rule-list-func (values result bindings)))))
  (values :did-not-fire bindings))

(defunp (action rule) (rule bindings)
  ;; (rule <pattern> <action>)
  (let ((bindings-stream (deduce (pattern-of rule) bindings)))
    (if (stream-empty? bindings-stream)
	(values :did-not-fire bindings)
	;; It's crucial here that execute-action does *not* return
	;; :did-not-fire; if it did, then the rule-list function might think
	;; this rule didn't fire, when at this point we know it did.
	(execute-action (action-of rule) (stream-car bindings-stream)))))

(defunp (action every) (every bindings)
  ;; (every <var> <pattern> <action>)
  ;; Get a list of bindings for the variable, using the pattern; then execute
  ;; the action for each binding.  Return the value of the last action; but do
  ;; not alter the bindings.  NOTE: we should really add the bindings of all
  ;; the variables in the pattern.
  (let* ((var (var-of every))
	 (action (action-of every))
	 (var-values (unique-values var (pattern-of every) bindings))
	 (new-bindings-list (mapcar #'(lambda (val) (add-binding var val bindings))
				    var-values))
	 (result))
    (dolist (new-bindings new-bindings-list)
      (setq result (execute-action action new-bindings)))
    (values result bindings)))

(defun unique-values (var pattern bindings)
  ;; Returns a list of values of var satisfying pattern, with no duplicate
  ;; values.
  (let* (;;get the stream of bindings satisfying pattern...
	 (bindings-stream (deduce pattern bindings))
	 ;;turn it into a list...
	 (bindings-list (stream->list bindings-stream))
	 ;;remove the values for var... 
	 (values-list (mapcar #'(lambda (b) (var-value var b))
				bindings-list)))
    ;; return the values with duplicates deleted.
    (delete-duplicates values-list)))
  
(defunp (action let) (let bindings)
  ;; (let <var> <action>)
  ;; Execute the action and bind the result to the variable; return the result
  ;; of the action, and the new bindings.
  (let ((result (execute-action (action-of let) bindings)))
    (values result (add-binding (var-of let) result bindings))))

(defunp (action choose) (choose bindings)
  ;; (choose <var> <pattern>)
  ;; This is like a let, except the value for the variable is chosen randomly
  ;; from those that match the pattern.  The result of choose is the value, and
  ;; it also augments the bindings.
  (let ((result (randomly-choose-from-list 
		  (unique-values (var-of choose)
				 (pattern-of choose) bindings))))
    (values result (add-binding (var-of choose) result bindings))))

(defun randomly-choose-from-list (list)
  (let ((n (random (length list))))
    (nth n list)))

(defunp (action lisp) (lisp-action bindings)
  ;; Returns the result of applying the car of lisp expression to its cdr, and
  ;; the same bindings.  (If the expression is an atom, it's just returned.) We
  ;; have to go through the expression replacing AAL variables with their
  ;; values.  Note that we are not evaluating the expression; the difference is
  ;; that our way, the arguments are not evaluated.
  (let ((expr (instantiate (expression-of lisp-action) bindings)))
    (if (atom (expression-of lisp-action))
	(values expr bindings)
	(values (apply (car expr) (cdr expr)) bindings))))

(defunp (action assert) (assert bindings)
  ;; Get the pattern and instantiate it.  It must be simple and contain no
  ;; unbound variables.
  (let ((pattern (second assert)))
    (if (not (simple-pattern? pattern))
	(error "Cannot assert the pattern ~a because it is not simple" 
	       pattern)
	(values (assert (instantiate pattern bindings)) 
		bindings))))

(defunp (action retract) (retract bindings)
  ;; This is similar to assert
  (let ((pattern (pattern-of retract)))
    (if (not (simple-pattern? pattern))
	(error "Cannot retract the pattern ~a because it is not simple" 
	       pattern)
	(values (retract (instantiate pattern bindings)) 
		bindings))))


;;; (query <pattern>)
(defunp (action query) (query bindings)
  ;; Calls the deducer on the pattern.  Returns what the deducer returns, and
  ;; augments the bindings by returning the first binding-list in the stream
  ;; returned by the deducer, if any.
  (let ((result (deduce (pattern-of query) bindings)))
    (values result (if (stream-empty? result)
		       bindings
		       (stream-car result)))))

;;; (continue)
(defunp (action continue) (continue bindings)
  (declare (ignore continue))
  (values :continue bindings))

;;; (end-game)
(defunp (action end-game) (form bindings)
  (declare (ignore form bindings))
  (end-game))

;;; (display-score)
(defunp (action display-score) (form bindings)
  (declare (ignore form))
  (values (display-score) bindings))

(defmacro with-instantiated-arg (&body body)
  ;; This simplifies the expression of simple actions that take only one
  ;; argument and instantiate it.
  `(let ((arg (instantiate (second form) bindings)))
     (values (progn , at body) bindings)))

;;; (destroy <obj>)
(defunp (action destroy) (form bindings)
  ;; (destroy obj) removes all facts in the database that mention obj.
  ;; We can't use the deducer directly to do this because we have to handle
  ;; assertions of all arities.
  (with-instantiated-arg (destroy arg)))

(defun destroy (obj)
  (dolist (stmt *db*)
    (when (and (null (antecedent-of stmt))		;it's a fact
	       (member obj (consequent-of stmt)))
      (retract (consequent-of stmt))))
  t)

;;; (value <obj> <var>)  For local variables of an object that the code is not
;;; executing within.
(defunp (action value) (form bindings)
  (with-instantiated-arg
    (alist-var-value (third form) (get arg 'vars))))

;;; (set <var> <value>) sets globals.
;;; (set (<obj> <var>) <value>) sets locals.
(defunp (action set) (form bindings)
  (if (not (= (length form) 3))
      (error "In ~a: wrong number of args" form)
      (multiple-value-bind (obj var value)
	  (parse-modify-form form)
	(let ((alist (if obj (get (instantiate obj bindings) 'vars) bindings)))
	  (if (not (var? var))
	      (error "In ~a: ~a is not a variable" form var)
	      (values (set-var var (instantiate value bindings) alist)
		      bindings))))))

(defun parse-modify-form (form)
  ;; form is either (<name> (<obj> <var>) [<value>]) or (<name> <var>
  ;; [<value>]).  Return three values: obj, var, value.
  (let ((varspec (second form)))
    (if (listp varspec)
	(if (not (= (length varspec) 2))
	    (error "In ~a: illegal var: ~a" form (second form))
	    (values (first varspec) (second varspec) (third form)))
	(values nil varspec (third form)))))

;;; (inc <var> [<amount>]) for globals
;;; (inc (<obj> <var>) [<amount>]) for locals
(defunp (action inc) (form bindings)
  (values (modify-var form bindings #'+) bindings))

;;; Same as inc
(defunp (action dec) (form bindings)
  (values (modify-var form bindings #'-) bindings))

(defun modify-var (form bindings func)
  ;; Form should be (name (obj var) [amount]) or (name var [amount])
    (multiple-value-bind (obj var amount-form)
	(parse-modify-form form)
      (if (not (var? var))
	  (error "In ~a: ~a is not a variable" form var)
	  (let* ((alist (if obj (get (instantiate obj bindings) 'vars) bindings))
		 (amount (if amount-form (instantiate amount-form bindings) 1))
		 (value (var-value var alist)))
	    (cond
	      ((eq value :unbound)
	       (error "In ~a: variable ~a is unbound" form var))
	      ((not (numberp value))
	       (error "In ~a: variable ~a's value, ~a, is not a number" 
		      form var value))
	      ((not (numberp amount))
	       (error "In ~a: the argument, ~a, is not a number" 
		      form amount-form))
	      (t
	       (set-var var (funcall func value amount) alist)))))))


;;; (display <action>)
(defunp (action display) (form bindings)
  (with-instantiated-arg 
    (let ((pr (printed-rep arg)))
      (format t "~&~a~%" pr)
      pr)))

(defun printed-rep (thing)
  (if (symbolp thing)
      (or (get thing 'desc) (string-downcase thing))
      thing))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Miscellaneous.

(defun call-function-in-object (func obj)
  ;; The main thing here is handling the object's local variables correctly.
  ;; They must be added to the bindings so that their values will be found, and
  ;; we also have to handle setting them.  This is what we do: we nconc the
  ;; list of locals onto the bindings, first keeping the last cons of the
  ;; locals.  Accessing will work as usual.  Set will destructively modify the
  ;; binding--new ones can't be created.  When execution is done, we restore
  ;; the locals to their former state.
  ;;   This can only be called at top-level.  It doesn't return bindings,
  ;; just a result.  The function to be called must take one argument, the
  ;; bindings. 
  (let ((locals (get obj 'vars)))
    (if (null locals)
	;; This is the easy case.
	;; We use values to assure that we're only returning one value.
	(values (funcall func *globals*))
	(let* ((last-cons (last locals))
	       (*protected-vars* (nconc locals *globals*))
	       (result (funcall func *protected-vars*)))
	    (setf (cdr last-cons) nil)
	    result))))

(defun instantiate (pattern bindings)
  ;; Create a copy of the pattern with variables replaced by their values.  It
  ;; is an error if there is an unbound variable in the pattern.
  (labels ((instantiate-1 (pat bindings)
	     (cond
	       ((null pat)
		nil)
	       ((atom pat)
		(if (not (var? pat))
		    pat
		    (let ((value (var-value pat bindings)))
		      (if (eq value :unbound)
			  (error "Pattern ~a contains unbound variable ~a" 
				 pattern pat)
			  value))))
	       (t
		(cons (instantiate-1 (car pat) bindings)
		      (instantiate-1 (cdr pat) bindings))))))
    (instantiate-1 pattern bindings)))

;;; End interp.lisp.
      



More information about the Alt.sources mailing list