File: acl2s-interface-utils.lsp

package info (click to toggle)
acl2 8.6%2Bdfsg-3
  • links: PTS
  • area: main
  • in suites: forky, sid
  • size: 1,138,276 kB
  • sloc: lisp: 17,818,294; java: 125,359; python: 28,122; javascript: 23,458; cpp: 18,851; ansic: 11,569; perl: 7,678; xml: 5,591; sh: 3,978; makefile: 3,840; ruby: 2,633; yacc: 1,126; ml: 763; awk: 295; csh: 233; lex: 197; php: 178; tcl: 49; asm: 23; haskell: 17
file content (222 lines) | stat: -rw-r--r-- 9,353 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
#|
  Some utility functions built on top of the ACL2s interface.
|#
(in-package "ACL2S-INTERFACE-EXTRAS")

;;;; Error conditions
(define-condition acl2s-interface-error (error)
  ((desc :initarg :desc :reader acl2s-interface-error/desc)
   (query :initarg :query :reader acl2s-interface-error/query)
   (err :initarg :err :reader acl2s-interface-error/err))
  (:report
   (lambda (condition stream)
     (format
      stream
      "Error occurred when running acl2s interface call:~% error ~a ~% for query: ~a ~% ~S"
      (acl2s-interface-error/err condition)
      (acl2s-interface-error/query condition)
      (acl2s-interface-error/desc condition)))))

(define-condition unexpected-response-error (error)
  ((desc :initarg :desc :reader unexpected-response-error/desc)
   (query :initarg :query :reader unexpected-response-error/query)
   (res :initarg :res :reader unexpected-response-error/res))
  (:report
   (lambda (condition stream)
     (format
      stream
      "Unexpected response from acl2s interface call:~% error ~a ~% for query: ~a ~% ~S"
      (unexpected-response-error/res condition)
      (unexpected-response-error/query condition)
      (unexpected-response-error/desc condition)))))

;;;; Utilities for dealing with propositional expressions
(defun replace-in (x to-replace replace-with)
  "Replace to-replace with replace-with inside of x, recursively if x is a cons.
Uses 'equal to compare elements with to-replace."
  (cond ((equal x to-replace) replace-with)
        ((consp x) (cons
                    (replace-in (car x) to-replace replace-with)
                    (replace-in (cdr x) to-replace replace-with)))
        (t x)))

(defun flatten-and-step (fs)
  "Given a list of statements, expand any conjunctions."
  (cond ((endp fs) nil)
        ((or (equal 'and (caar fs)) (equal '^ (caar fs))) ;; support ACL2s ^ in addition to and
         (append (cdar fs) (flatten-and-step (cdr fs))))
        (t (cons (car fs) (flatten-and-step (cdr fs))))))

;; (assert (equal (flatten-and-step '(a b (and c d) (and e (and f g)))) '(a b c d e (and f g))))
;; (assert (equal (flatten-and-step '(a (^ b (and c d)) (and (^ e f) g))) '(a b (and c d) (^ e f) g)))

(defun flatten-and (fs &optional (ls nil))
  "Given a list of terms representing the conjunction of those terms,
flatten any 'ands inside those terms and simplify."
  (if (equal fs ls)
      fs
    (flatten-and (flatten-and-step fs) fs)))

;; (assert (equal (flatten-and '(a b (and c d) (and e (and f g)))) '(a b c d e f g)))
;; (assert (equal (flatten-and '(a (^ b (and c d)) (and (^ e f) g))) '(a b c d e f g)))

(defun conjunction-terms (t1 t2)
  "Given two terms, produce the conjunction of them, simplifying if
either of the terms has a top-level and."
  (let* ((t1-terms (if (or (equal (car t1) 'and) (equal (car t1) '^)) (cdr t1) (list t1)))
         (t2-terms (if (or (equal (car t2) 'and) (equal (car t2) '^)) (cdr t2) (list t2)))
         (terms    (append t1-terms t2-terms))
         (flat-terms (remove-duplicates (flatten-and terms) :test #'equal)))
    `(and ,@flat-terms)))

;; (assert (equal (conjunction-terms 'x 'y) '(and x y)))
;; (assert (equal (conjunction-terms '(and a b) '(^ c d)) '(and a b c d)))

(defun cnf-disjunct-to-or (expr)
  "Given a CNF disjunct, convert to an equivalent ACL2s expression."
  (if (and (consp expr) (endp (cdr expr)))
      (car expr)
    (cons 'or expr)))

;; (assert (equal (cnf-disjunct-to-or '(x)) 'x))
;; (assert (equal (cnf-disjunct-to-or '(x y)) '(or x y)))

(defun conjunction-to-list (term)
  "Convert a term to a list of conjuncts.
If the term is an and, this will return a list containing the arguments to that and.
Otherwise a list containing only the given term is returned."
  (if (and (consp term) (equal 'and (car term)))
      (cdr term)
    (list term)))

(defun get-hyps (expr)
  "Get the hypotheses of an implication, returning nil if the given expression
is not an implication"
  (if (and (consp expr) (equal (car expr) 'implies))
      (conjunction-to-list (second expr))
    nil))

(assert (equal (get-hyps '(implies (and x y) z)) '(x y)))
(assert (equal (get-hyps '(and x y)) nil))
(assert (equal (get-hyps '(implies (and x y (and z w)) q)) '(x y (and z w))))

(defun get-conc (expr)
  "Get the conclusion of an implication, returning nil if the given expression
is not an implication"
  (if (and (consp expr) (equal (car expr) 'implies))
      (third expr)
    nil))

(assert (equal (get-conc '(implies (and x y) z)) 'z))
(assert (equal (get-conc '(and x y)) nil))
(assert (equal (get-conc '(implies (and x y) (implies (and z w) q))) '(implies (and z w) q)))

(defun acl2s-query-error? (res)
  (car res))

(defun thm-query-error? (res)
  (car res))

;; Run a thm on the given statement.
(defun acl2s-thm (q &optional goal-hints)
  (acl2s-query
   `(thm ,q :hints (("Goal" ,@goal-hints)))))

;; A call to guard-obligation inside of an acl2s-query should return something of the form:
;; ('ctx error-message) if guard-obligation had an internal error
;; (i.e. if the expression whose guard obligations are being asked for contains an undefined function)
;; or
;; (nil (term-info obligations)) otherwise
;; where term-info is of the form (:term ...)
;; and obligations is a list of obligations
(defun guard-obligations-query-res-ok? (res)
  "Determine if `res` is of a form that we expect guard-obligation to return."
  (and (consp res)
       (>= (length res) 2)
       (consp (second res))
       (>= (length (second res)) 2)))

(defun guard-obligations-query (expr &key (debug nil) (simplify t))
  "Get the guard obligations for the given expression, possibly with debug info (depending on debug argument)
This will return a CNF of the guard obligations
This will error if the internal acl2s-query returns an unexpected value, or if an error is detected"
  (let ((res (acl2s-query `(mv-let
			    (erp val)
			    (guard-obligation ',expr nil ,debug ,simplify 'ctx state)
			    (mv erp val state)))))
    (cond ((not (guard-obligations-query-res-ok? res))
           (error "acl2s-query returned an unexpected response in guard-obligations-query: ~a" res))
	  ((or (car res) (equal (car res) 'ctx))
	   (error "Error when computing guard obligations for the expression ~S:~%~S" expr res))
	  (t (second (second res))))))

;;(assert (equal (guard-obligations-query '(app x nil)) '(((true-listp x)))))

(defun rguard-obligations-debug (expr &key (simplify t))
  "Get the guard obligations of the given expression.
This returns a list containing lists where:
- the first element is the debug info (describing where the obligation came from).
- the second element is a statement describing the obligation itself.
Obligations are converted to ors here so that they can be directly run in ACL2.
ACL2-numberp is replaced by rationalp inside of obligations."
  (let ((val (guard-obligations-query expr :debug t :simplify simplify)))
    (mapcar (lambda (x)
	      (list (second (car x))
		    (replace-in (cnf-disjunct-to-or (cdr x))
				'acl2-numberp
				'rationalp)))
	    val)))

;;(assert (equal (rguard-obligations-debug '(app x nil))
;;               '(((extra-info ':top-level '(app x nil)) (true-listp x)))))

(defun rguard-obligations (expr &key (simplify t))
  "Get the guard obligations of the given expression.
This returns a list containing statements describing the obligations themselves.
Obligations are converted to ors here so that they can be directly run in ACL2.
ACL2-numberp is replaced by rationalp inside of obligations."
  (let ((val (guard-obligations-query expr :debug nil :simplify simplify)))
    (mapcar (lambda (x) (replace-in (cnf-disjunct-to-or x)
                                    'acl2-numberp
                                    'rationalp))
            val)))

;;(assert (equal (rguard-obligations '(app x nil)) '(true-listp x)))
;;(assert (equal (rguard-obligations '(app x nil) :simplify nil) '((true-listp x) (true-listp 'nil))))

(defun acl2s-untranslate (x)
  "Untranslate an expression."
  (cadr (acl2s-query
	 `(mv nil
	      (untranslate ',x nil (w state))
	      state))))

;;(assert (equal (acl2s-untranslate '(BINARY-+ '1 (BINARY-+ '2 '3))) '(+ 1 2 3)))

(defun reset-file ()
  "Reset the ACL2 state back to before the definition of START-LOAD-FILE"
  (acl2::ld '(:ubt START-LOAD-FILE)))

(defun create-reset-point ()
  "Define a function so that one can undo back to this point in the ACL2 world."
  ;; Create this function so that we can come back to this point in ACL2's history
  (acl2s-event '(defun START-LOAD-FILE () t)))

(defun acl2s-arity (f)
  "A function that determines if f is a function defined in ACL2s and if
so, produces its arity (number of arguments). If f is not a function, then 
the arity is nil. Otherwise, the arity is a natural number. Note that f
can't be a macro."
  (second (acl2s-compute `(acl2::arity ',f (w state)))))

;;(assert (equal (acl2s-arity 'append) nil)) ;; is nil since append is a macro
;;(assert (equal (acl2s-arity 'binary-append) 2))

(declaim (ftype (function (symbol) boolean) is-theorem?))
(defun is-theorem? (sym)
  "Determine if the given symbol symbol names a theorem."
  (let* ((query `(acl2::theorem-namep ',sym (w state)))
         (res (acl2s-compute query)))
    (if (acl2s-query-error? res)
        (error (format nil "Internal acl2s query error for query ~a" query))
      (second res))))