; Authored by Surya Mantha, CS 611, Fall 1989 ; From mantha@cs Sat Dec 9 18:56:23 1989 ; Date: Sat, 9 Dec 89 12:21:52 -0700 ; From: mantha@cs (Surya M Mantha) ; To: ganesh@cs ; ; Common Lisp ; ; Part No. 98678 Rev. 2.01 ; (c) Copyright 1986, Hewlett-Packard Company. All rights reserved. ; ; HP Common Lisp, 19-Oct-89 ; ; (load "/n/cs/u/mantha/thm-prover/p1.l") ; "/n/cs/u/mantha/thm-prover/p1.l" ; (load "/n/cs/u/mantha/thm-prover/v1.l") ; "/n/cs/u/mantha/thm-prover/v1.l" ; ; Proofs for the partial correctness specifications at the end of file ; "v1.l". ; ; Checking syntax of annotated program ....... ; OK. ; Assignment Axiom ; T1: |- {((X = A) AND (Y = B))} (ASSIGN R X) {((Y = B) AND (R = A))} ; ; By Sequencing rule ; T2: T1 |- {((X = A) AND (Y = B))} (SEQ (ASSIGN R X) (ASSIGN X Y) (ASSIGN Y R)) {((X = B) AND (Y = A))} ; ; all proved. ; T ; ; ; ; Checking syntax of annotated program ....... ; OK. ; Assignment Axiom ; T3: |- {T} (ASSIGN R X) {((R = X) AND (0 = 0))} ; ; Assignment Axiom ; T4: |- {((X = (R + (Y * Q))) AND (Y <= R))} (ASSIGN R (R - Y)) {(X = (R + (Y * (Q + 1))))} ; ; By Block rule ; T5: T4 |- {((X = (R + (Y * Q))) AND (Y <= R))} (BLOCK (ASSIGN R (R - Y)) (ASSIGN Q (Q + 1))) {(X = (R + (Y * Q)))} ; ; By While rule ; T6: T5 |- {((R = X) AND (Q = 0))} (WHILE (Y <= R) (ASSERT (X = (R + (Y * Q)))) (BLOCK (ASSIGN R (R - Y)) (ASSIGN Q (Q + 1)))) {((R < Y) AND (X = (R + (Y * Q))))} ; ; By Block rule ; T7: T3 T6 |- {T} (BLOCK (ASSIGN R X) (ASSIGN Q 0) (ASSERT ((R = X) AND (Q = 0))) (WHILE (Y <= R) (ASSERT (X = (R + (Y * Q)))) (BLOCK (ASSIGN R (R - Y)) (ASSIGN Q (Q + 1))))) {((R < Y) AND (X = (R + (Y * Q))))} ; ; all proved. ; T ; ; ; ; Checking syntax of annotated program ....... ; OK. ; Assignment Axiom ; T8: |- {T} (ASSIGN X 1) {(X = 1)} ; ; Assignment Axiom ; T9: |- {((X = 1) AND (X = 1))} (ASSIGN M 0) {(M = 0)} ; ; Assignment Axiom ; T10: |- {((X = 1) AND (NOT (X = 1)))} (ASSIGN M 1) {(M = 0)} ; ; By If-then-else rule ; T11: T9 T10 |- {(X = 1)} (IF2 (X = 1) (ASSIGN M 0) (ASSIGN M 1)) {(M = 0)} ; ; By Block rule ; T12: T8 T11 |- {T} (BLOCK (ASSIGN X 1) (ASSERT (X = 1)) (IF2 (X = 1) (ASSIGN M 0) (ASSIGN M 1))) {(M = 0)} ; ; all proved. ; T ; ; ; "The program containing the "for rule" needs more arithmetical facts ; to be proved correct" ; ; ; Checking syntax of annotated program ....... ; OK. ; Assignment Axiom ; T13: |- {(M >= 1)} (ASSIGN X 0) {(X = 0)} ; ; Assignment Axiom ; T14: |- {((X = (((N - 1) * N) DIV 2)) AND ((1 <= N) AND (N <= M)))} (ASSIGN X (X + N)) {(X = ((((N + 1) - 1) * (N + 1)) DIV 2))} ; ; can't prove: ; ; ERR ; ; (((X = 0) IMPLIES ((M < 1) IMPLIES (0 = (((M * M) + M) DIV 2))))) ; ; NIL ; ; ; From mantha@cs Sat Dec 9 18:57:55 1989 ; Date: Sat, 9 Dec 89 12:19:19 -0700 ; From: mantha@cs (Surya M Mantha) ; To: ganesh@cs ;; This is a Common Lisp implementation of the theorem prover in Gordon's book ;; the following code will generate the pattern matcher (mapcar #'(lambda(x) (setf (get x 'constant ) 't)) '(+ - = < <= >= * T F DIV not and or implies)) ;; testing whether something is a variable (defmacro is-variable (x) `(not (or (null ,x) (numberp ,x) (get ,x 'constant)))) ;;matchfn is a function that matches a pattern against an ;; an expression in the context of a substitution. (defun matchfn (pat exp sub) (if (atom pat) (if (is-variable pat) (if (assoc pat sub) (if (equal (cdr (assoc pat sub)) exp) sub (throw 'fail 'FAIL)) (cons (cons pat exp) sub)) (if (eq pat exp) sub (throw 'fail 'FAIL))) (if (atom exp) (throw 'fail 'FAIL) (matchfn (rest pat) (rest exp) (matchfn (first pat) (first exp) sub))))) ;; match can simply be defined to call matchfn with an ;; empty initial substituition; ;; it must catch any throws from matchfn (defun match (pat exp) (catch 'fail (matchfn pat exp nil))) ;; function rewrite (defvar rewrite-flag) (setq rewrite-flag nil) (defun rewrite1 (eqn exp) (let ((l (first eqn)) (r (third eqn))) (let ((sub (match l exp))) (if (eq sub 'fail) exp (prog2 (cond (rewrite-flag (write-string "the current expression and equation are ") (terpri) (write exp) (terpri) (write (sublis sub eqn)) (terpri)(terpri)) (T nil)) (car (sublis sub (list r)))))))) ;;; Takes a list of equations and tries each equation sequentially (defun rewrite (eqns exp) (if (null eqns) exp (rewrite (rest eqns) (rewrite1 (first eqns) exp)))) ;;; Macro that checks if its argument is an "implication" (defmacro is-imp (x) `(and (listp ,x) (eq (length ,x) 3) (eq (second ,x) 'implies))) (defmacro mk-imp (p q) `(list ,p (quote implies) ,q)) (defmacro antecedent (x) `(first ,x)) (defmacro consequent (x) `(third ,x)) ;; the function imp-subst-simp defined below transforms any S-expression ;; of the form (P implies Q) to one of (P implies Q[T/P]) (defun imp-subst-simp(exp) (if (is-imp exp) (let ((a (antecedent exp)) (c (consequent exp)) ) (mk-imp a (subst 'T a c))) exp)) (defmacro is-eqn (x) `(and (listp ,x) (eq (length ,x) 3) (eq (second ,x) '=))) (defun imp-and-simp(exp) (if (and (is-imp exp) (is-eqn (antecedent exp))) (let ((a (antecedent exp)) (c (consequent exp)) ) (mk-imp a (subst (third a) (first a) c ))) exp)) ;; The function imp-simp first applies imp-subst-simp and then applies ;; imp-and-simp to the result (defun imp-simp (exp) (imp-and-simp (imp-subst-simp exp))) ;; Higher Order Rewriting functions ;; Higher Order functions repeat, depth-conv, top-depth-conv and ;; re-depth-conv are based on Paulson's paper ;; These operators specify the order in which subexpressions are rewritten (defun repeat (f exp) (let ((exp1 (funcall f exp))) (if (equal exp exp1) exp (repeat f exp1)))) ;; function depth-conv repeatedly applies a function f to all ;; subexpressions of an expression exp in a bottomup order. (defun depth-conv (f exp) (if (atom exp) (repeat f exp) (repeat f (cons (depth-conv f (first exp)) (depth-conv f (rest exp)))))) ;;Now use depth-conv to define a fucntion depth-imp-simp that applies ;; imp-simp to all subexpressions of an expression (defun depth-imp-simp (exp) (depth-conv (function (lambda (x) (imp-simp x))) exp)) ;; top-depth-conv that is like depth-conv except it rewrites ;; top down before rewriting bottom up; (defun top-depth-conv (f exp) (let ((exp1 (repeat f exp))) (if (atom exp1) exp1 (repeat f (cons (top-depth-conv f (first exp1)) (top-depth-conv f (rest exp1))))))) ;; Using top-depth-conv define a function for repeatedly rewriting ;; all subexpressions of an expression in top-down order using a ;; supplied list of equations. ;; The topdown order is important (defun top-depth-rewrite (eqns exp) (top-depth-conv (function (lambda (x) (rewrite eqns x))) exp)) ;; depth-rewrite is defined that uses depth-conv instead of top-depth-conv i.e. (defun depth-rewrite (eqns exp) (depth-conv (function (lambda (x) (rewrite eqns x))) exp)) ;; Yet another depth conversion routine is "re-depth-conv" (defun re-depth-conv (f exp) (if (atom exp) (repeat f exp) (let ((exp1 (cons (re-depth-conv f (first exp)) (re-depth-conv f (rest exp))))) (let ((exp2 (funcall f exp1))) (if (equal exp1 exp2) exp1 (re-depth-conv f exp2)))))) ;;The corresponding rewriting function is (defun re-depth-rewrite (eqns exp) (re-depth-conv (function (lambda (x) (rewrite eqns x))) exp)) ;;; The simple theorm prover can now be assembled by defining a function ;;; prove that repeatedly applies depth-imp-simp followed by rewriting ;;; using top-depth-rewrite (defvar *debug* t) (defun prove (eqns exp) (if *debug* (format T "~% Trying to prove ~S ~% .......... ~%" exp)) (repeat (function (lambda (x) (top-depth-rewrite eqns (depth-imp-simp x)))) exp)) ;; The equations used for rewriting will be structured into two list: ;; A list called LOGIC containing various properties of IMPLIES and AND ;; A list called ARITHMETIC containing various arithmetical facts (defvar logic) (setq logic `( ((T implies X) = X) ((F implies X) = T) ((X implies T) = T) ((X implies X) = T) ((T and X) = X) ((X and T) = X) ((F and X) = F) ((X and F) = F) ((X = X) = T) (((X and (not X)) implies Z) = T) (((X and Y) implies Z) = (X implies (Y implies Z))) )) ;; these are the arithmetic facts. The order is important (defvar arithmetic) (setq arithmetic '( ((x >= x) = T) ;;; added to take care of "if" ((x + 0) = x) ((0 + x) = x) ((x * 0) = 0) ((0 * x) = 0) ((x * 1) = x) ((1 * x) = x) ((not(x <= y)) = (y < x)) ((not(x >= y)) = (y > x)) ((not(x < y)) = (x >= y)) (((- x) >= (- y)) = (x <= y)) (((- x) >= y) = (x <= (- y))) ((- 0) = 0) (((x < y) implies (x <= y)) = t) ((x - x) = 0) (((x + y) - z) = (x + (y - z))) (((x - y) * z) = ((x * z) - (y * z))) ((x * (y + z)) = ((x * y) + (x * z))) (((x + y) * z) = ((x * z) + (y * z))) (((x >= y) implies ((x < y) implies z)) = t) (((x <= y) implies ((y < x) implies z)) = t) ((0 div x) = 0) (((x div y) + z) = ((x + (y * z)) div y)) (((x - y) + z) = (x + (z - y))) ((2 * x) = (x + x)) )) ;;The list of facts is the defined by (defvar facts) (setq facts (append logic arithmetic)) ;; an example of somthing that cannot be proved is ;;(prove facts '((( t and (x >= y)) implies ( x = (max x y))) ;; and ;; (( t and (not (x >= y))) implies (y = (max x y))))) ;; it needs axioms about "max" ;;; The following can be proved in the current implementation ;;(time (prove facts '((( x = ((( n - 1) * n) div 2)) and (( 1 <= n) and ( n <= m))) ;; implies ;; (( x + n) = (((( n + 1) - 1) * (n + 1)) div 2 ))))) ;From mantha@cs Sat Dec 9 18:58:14 1989 ;Date: Sat, 9 Dec 89 12:19:23 -0700 ;From: mantha@cs (Surya M Mantha) ;To: ganesh@cs ;; This program does the following ;; It checks the syntactic well formedness of the annotated specs ;; Then it generates the Verification Conditions using the technqiues ;; described in chapter 3 of gordon's book ;; Then it invokes the theorem prover and tries to prove the VCs ;; it either terminates successfully or complains which VC's it could ;; not prove. (defvar *theorem-count* 1) ;;SELECTOR MACROS ;; The following macros extract the componets of a spec (SPEC P C Q) (defmacro precondition (x) `(second ,x)) (defmacro command (x) `(third ,x)) (defmacro postcondition (x) `(fourth ,x)) ;;The macro command-type gets the construtor from a command' ;; used in case-switches in the definition of chk-and-cmd ;; assigned-vars adn vc-gen (defmacro command-type (c) `(first ,c)) ;; Next two macros extract the lhs and rhs of an assignment statement (defmacro lhs (c) `(second ,c)) (defmacro rhs (c) `(third ,c)) ;;The next three macros get the list of commands in a sequence, the ;; components (i.e. local variable declarations and commands) of a block ;; and the name declared in a local variable declaration (defmacro seq-commands (c) `(rest ,c)) (defmacro block-body (c) `(rest ,c)) (defmacro var-name (v) `(second ,v)) ;;The following three macros get the components of conditionals (defmacro if-test (c) `(second ,c)) (defmacro then-part (c) `(third ,c)) (defmacro else-part (c) `(fourth ,c)) ;;the next three macros get the components of an annotated WHILE command (defmacro while-test (c) `(second ,c)) (defmacro while-annotation (c) `(third ,c)) (defmacro while-body (c) `(fourth ,c)) ;;These five macros get the components of an annotated FOR command (defmacro for-var (c) `(second ,c)) (defmacro lower (c) `(third ,c)) (defmacro upper (c) `(fourth ,c)) (defmacro for-annotation (c) `(fifth ,c)) (defmacro for-body (c) `(sixth ,c)) ;;Finally a macro to get the statement contained in an annotation (ASSERT S) (defmacro statement (a) `(second ,a)) ;;CONSTRUCTOR MACROS ;;Now we will define macros for constructing partial correctness specs ;; negated statements,implications and conjunctions (defmacro mk-spec (p c q) `(list (quote SPEC) ,p ,c ,q)) (defmacro mk-not (s) `(list (quote not) ,s)) (defmacro mk-imp (s1 s2) `(list ,s1 (quote implies) ,s2)) (defmacro mk-and (s1 s2) `(list ,s1 (quote and) ,s2)) ;;Three macros are defined to construct arithmetic expressions of the form ;; m+n, m