AAL sources (5 of 8)

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


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

(provide 'parser)
(require 'initial "initial.lisp")

;;; 'Parsing' rules and patterns.  This is not the natural-language parser for
;;; AAL; rather, it contains functions that translate from lists to internal
;;; forms of patterns, actions and rules.


;;; A pattern is a list.  The following cars are special:

;;; (not <pattern>)		succeeds only if pattern fails
;;; (or <pattern>*)		succeeds if any of the patterns succeeds
;;; (and <pattern>*)		succeeds if all of the patterns succeed
;;; (do <action>*)	        the actions are executed; always succeeds
;;; any action			The action is executed; succeeds if its
;;; 				 result is non-NIL.  All free variables
;;; 				 in the action must be instantiated.

;;; Syntactic sugar for patterns: 
;;; <lisp expression> => (lisp <lisp expression>) if the car of the expression
;;; is in *lisp-names*.

(defun list->pattern (list)
  (cond
    ((stringp list)
     (list->action list))
    ((not (listp list))
     (error "Illegal pattern: ~a" list))
    ((eq (car list) 'not)
     (if (not (singleton? (cdr list)))
	 (error "Too many patterns in a not: ~a" list)
	 `(not ,(list->pattern (second list)))))
    ((member (car list) '(and or))
     (cons (car list) (mapcar #'list->pattern (cdr list))))
    ((eq (car list) 'do)
     `(do ,(list->actions (cdr list))))
    ((aal-action? (car list))
     (list->action list))
    ((member (car list) *lisp-names*)
     `(lisp ,list))
    (t list)))

(defun simple-pattern? (pat)
  (not (or (member (car pat) '(and or not do))
	   (aal-action? (car pat)))))

;;; An AAL action is one of the following:

;;; (rule-list <rule>*)			like a cond
;;; (block <action>*)			like a progn
;;; (rule <pattern> <action>)		does action if pattern is satisfied;
;;;					  returns NIL if it isn't
;;; (lisp <lisp expression>)		evaluates lisp expression
;;; (every <var> <pattern> <action>)    does action for every binding of var
;;;					  satisfying pattern; returns last
;;; (choose <var> <pattern>)		chooses at random a binding of var
;;;					  satisfying pattern; returns binding
;;; (let <var> <action>)		binds var to result of action; returns
;;; 					  result of action
;;; (assert <pattern>)			add to the database; always succeeds
;;; (retract <pattern>)			remove from database; always succeeds
;;; (query <pattern>)			invoke the deducer with the pattern

;;; Other actions are defined in interp.lisp.  They are not "parsed".

(defun list->actions (list)
  (singleton-optimize (mapcar #'list->action list) 'block))

(defun list->action (list)
  (list->action-desugared (desugar-action list)))

(defun var-of (action)
  (if (member (car action) '(every choose let))
      (second action)
      (error "action ~a does not have a var" action)))

(defun pattern-of (action)
  (case (car action)
    ((rule assert retract query)
     (second action))
    ((every choose)
     (third action))
    (otherwise
      (error "action ~a does not have a pattern" action))))

(defun action-of (action)
  (case (car action)
    ((rule let)
     (third action))
    (every
      (fourth action))
    (otherwise
      (error "action ~a does not have an action" action))))

(defun expression-of (action)
  (if (eq (car action) 'lisp)
      (second action)
      (error "action ~a does not have an expression" action)))

(defun list->action-desugared (list)
  ;; Handles lists whose car is already known to be an action word
  (case (car list)
    (rule-list `(rule-list ,@(mapcar #'list->rule (cdr list))))
    (block `(block ,@(mapcar #'list->action (cdr list))))
    (rule  `(rule ,(list->pattern (pattern-of list)) ,(list->action (action-of list))))
    (lisp  list)
    (every `(every ,(check-var list) ,(list->pattern (pattern-of list)) 
		   ,(list->action (action-of list))))
    (choose `(choose ,(check-var list) ,(list->pattern (pattern-of list))))
    (let  `(let ,(check-var list) ,(list->action (action-of list))))
    (assert `(assert ,(list->pattern (pattern-of list))))
    (retract `(retract ,(list->pattern (pattern-of list))))
    (query `(query ,(list->pattern (pattern-of list))))
    (otherwise list)))

(defun check-var (list)
  (let ((var (var-of list)))
    (if (not (var? var))
      (error "variable expected instead of ~a in ~a" var list)
      var)))

;;; Syntactic sugar: 
;;; blocks are sometimes implicit; also:

;;; (<rule>*) => (rule-list <rule>*)
;;; (<pattern>* -> <action>*) => (rule (and <pattern>*) (block <action>*))
;;; <lisp expression> => (lisp <lisp expression>) if the car of the expression
;;; 	is in the list *lisp-names*
;;; (<- <pattern>) => (query <pattern>)
;;; <string> => (lisp (format t <string>))
;;; (<string> ...) => (lisp (format t <string> ...))
;;; <pattern> => (query <pattern>) if its car is the same as the consequent of a
;;; 	previously defined b-rule
;;; <pattern> => (assert <pattern>) if its car doesn't fit anything else
;;; (not <pattern>) => (retract <pattern>)
;;; (choose <var> <pattern>*) => (choose <var> (and <pattern>*))
;;; (let <var> <action>*) => (let <var> (block <action>*))
;;; (every <var> <pattern> <action>*) => (every <var> <pattern> (block <action>*))
;;; <lisp atom> (other than string) => (lisp <lisp atom>)

(defun desugar-action (list)
  (if (stringp list)
      (setq list (list list)))
  (if (atom list)
      `(lisp ,list)
      (let ((car (car list)))
	(cond
	  ((stringp car)
	   `(lisp (eval (format t ,(string-append "~&" car "~%") 
				,@(mapcar #'var->sd (cdr list))))))
	  ((eq car 'every)
	   `(every ,(var-of list) ,(pattern-of list)
		   ,(singleton-optimize (cdddr list) 'block)))
	  ((eq car 'let)
	   `(let ,(var-of list) ,(singleton-optimize (cddr list) 'block)))
	  ((eq car 'choose)
	   `(choose ,(var-of list) ,(singleton-optimize (cddr list) 'and)))
	  ((aal-action? car)
	   list)
	  ((eq car '<-)
	   `(query ,(second list)))
	  ((eq car 'not)
	   `(retract ,(second list)))
	  ((member '-> list)
	   (desugar-rule list))
	  ((member car *lisp-names*)
	   `(lisp ,list))
	  ((member car *backward-predicates*)
	   `(query ,list))
	  ((listp (car list))
	   (cons 'rule-list list))
	  (t
	   `(assert ,list))))))


(defun var->sd (thing)
  ;; If thing is a var, translate it to (printed-rep var).
  (if (var? thing)
      `(printed-rep ',thing)
      thing))



(defun aal-action? (thing)
  ;; We can tell a symbol is the name of an action by seeing if its ACTION
  ;; property is non-NIL.
  (and (symbolp thing)
       (get thing 'action)))

(defun list->rule (list)
  (list->action-desugared (desugar-rule list)))

(defun desugar-rule (list)
  (let ((->pos (member '-> list)))
    (if (not ->pos)
	(error "illegal rule: ~a" list)
	(let* ((ant-lists (ldiff list ->pos))
	       (conseq-lists (cdr ->pos))
	       (pattern (singleton-optimize ant-lists 'and))
	       (action (singleton-optimize conseq-lists 'block)))
	  `(rule ,pattern ,action)))))



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Backward rules.
;;; (b-rule <consequent> <antecedent>)

(defun consequent-of (b-rule)
  (second b-rule))

(defun antecedent-of (b-rule)
  (third b-rule))

(defun list->b-rule (list)
  (list->b-rule-desugared (desugar-b-rule list)))

(defun list->b-rule-desugared (list)
  (let ((conseq (list->pattern (consequent-of list))))
    (if (not (simple-pattern? conseq))
	(error "The consequent of a backwards rule must be simple: ~a" list)
	`(b-rule ,conseq
		 ,(list->pattern (antecedent-of list))))))

;;; Syntactic sugar: 
;;; (<consequent> <- <antecedent>*) => (b-rule <consequent> (and <antecedent>*))

(defun desugar-b-rule (list)
  (let ((<-pos (member '<- list)))
    (if (not <-pos)
	(error "illegal backward rule: ~a" list)
	(let* ((conseq-list (ldiff list <-pos))
	       (ant-lists (cdr <-pos)))
	  (if (not (singleton? conseq-list))
	      (error "backward rules have exactly one consequent: ~a" list)
	      `(b-rule ,(car conseq-list) 
		       ,(singleton-optimize ant-lists 'and)))))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Forward rules.

(defun list->f-rule (list)
  ;; The only thing we do here is error-checking.
  (let ((rule (list->rule list)))
    (if (not (simple-pattern? (pattern-of rule)))
	(error "Forward rules must have only a single, simple pattern: ~a" list)
	rule)))

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

;;; Syntax of a requirement:
;;; <pattern> or (<pattern> . <action>)
;;; This allows ((on a b) "foo") or ((on a b) "foo ~a" *obj), which are the
;;; usual cases.

(defun list->requirements (list)
  (cons 'list (mapcar #'list->requirement list)))

(defun list->requirement (list)
  (if (listp (car list))
      (let ((pattern (car list))
	    (action (cdr list)))
	`(make-requirement :pattern ',(list->pattern pattern)
			   :failure-action ',(list->action action)))
      `(make-requirement :pattern ',(list->pattern list))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defun singleton-optimize (list first-el)
  ;; If list has one element, return it; else return a list of the elements
  ;; with first-el as its first element.
  (if (singleton? list)
      (car list)
      (cons first-el list)))

(defun singleton? (list)
  ;; Returns T if list has only one element
  (null (cdr list)))

;;; End parser.lisp.



More information about the Alt.sources mailing list