other
(in-package "DEFDATA")
other
(include-book "base")
other
(include-book "sig" :ttags :all)
other
(include-book "xdoc/top" :dir :system)
other
(defxdoc defdata :parents (acl2-sedan macro-libraries cgen) :short "A Data Definition Framework" :long " <h3>Introduction</h3> <p> The defdata framework enables the convenient specification of data types in ACL2s, the ACL2 Sedan. The framework concisely supports common data definition patterns, e.g., list types, map types, enumerated types, record types, and mutually-recursive types. It also provides support for polymorphic functions and tight integration with Tau. </p> <p> The data definition framework is a key component of counterexample generation support in ACL2s, but can be independently used in ACL2, and is available as a community book. </p> <p> The defdata framework maintains both a predicative characterization, via a predicate recognizing elements of the datatype, and an enumerative characterization, via a function that can be used to enumerate all the elements of the datatype. ACL2s picks out the recognizers in a conjecture and can use the datatype enumerators to generate random elements of the datatype. This is a key part of counterexample generation in ACL2s. </p> <p> The defdata framework also allows us to increase the amount of automation ACL2s provides for reasoning about data definitions. For example, our framework generates useful theorems, with appropriate rule-classes, for list types; it generates accessor and constructor functions for records with a suitable theory for reasoning about compositions of these functions; it generates theorems that characterize the type relations such as inclusion and exclusion; and it generates events that support polymorphic type reasoning. </p> <p> The defdata framework is described in our ACL2 2014 paper (a link to the paper appears later). Below, we provide some examples of its use.</p> <p>Note: If you are trying these examples in a package other than ACL2S, then you might have to prefix the basic typenames i.e. acl2s::pos.</p> <h3>Examples</h3> <h4>Union and Product types</h4> @({ (defdata inode nat) (defdata file (cons inode string)) (defdata er-file (oneof file nil)) (defdata UPNest (oneof (oneof (cons (oneof 11 7) pos-list) 'ok symbol-alist) (cons symbol (complex integer -1)) (oneof (oneof 42 (cons pos file) er-file) t 7/6) "nice")) (defdata loi (oneof nil (cons integer loi))) }) <h4>Range types</h4> @({ (defdata cache-miss-ratio (range rational (0 < _ < 1))) (defdata big-unsigned-num (range integer ((expt 2 64) < _))) }) <p> Macros are allowed and the meaning of the defdata body is the given by its macroexpansion. </p> @({ (defdata 3d-point (list rational rational rational)) }) <h4>Enum types</h4> @({ (defdata op-code (enum '(lw sw addi subi beqz jr))) (defun generate-instruction-opcodes (x) (if (eq x 'mips-risc-model) '(lw sw addi subi beqz jr) '())) (defdata op-code1 (enum (generate-instruction-opcodes 'mips-risc-model))) }) <h4>List and Alist types</h4> @({ (defdata files (listof file)) (defdata symb-alist (alistof symbol all)) }) <h4>Mutually-recursive types</h4> @({ (defdata (dir (listof (cons string dir-entry))) (dir-entry (oneof file dir))) (defdata (sexpr (oneof symbol (cons symbol sexpr-list))) (sexpr-list (oneof nil (cons sexpr sexpr-list)))) }) <h4>Record (Struct) types</h4> @({ (defdata reg-num (range integer (0 <= _ < 64))) (defdata immediate-range (range integer (0 <= _ < (expt 2 16)))) (defdata inst (record (op . op-code) (rd . reg-num) (rs1 . reg-num) (imm . immediate-range))) }) <h4>Map (unique and sorted entries) types</h4> @({ (defdata p-addr (range integer (0 <= _ < (expt 2 64)))) (defdata imem (map p-addr inst)) }) <h4>Using record and map types</h4> @({ (b* (;; pick a random imemory (I (nth-imem 4511)) ;; fix a program counter value (pc 1) ;; get the instruction pointed to by pc (instr (mget pc I)) ;; get immediate value field of instr (im (inst-imm instr)) ;; set immediate value field and the pc entry (I1 (mset pc (set-inst-imm (1+ im) instr) I)) ;; alternative way of getting immediate value field (im2 (mget :imm (mget pc I1))) ((inst op rd rs1 ?imm) instr) ) (equal (inst op rd rs1 (1- im2)) instr)) }) <h4>Custom types</h4> @({ (defun make-descending-addresses (n) (if (zp n) nil (cons (1- n) (make-descending-addresses (- n 1))))) (defun imem-custom-enum (n) (declare (xargs :mode :program)) (let* ((m (nth-imem-builtin n)) (vals (strip-cdrs m)) (keys (make-descending-addresses (len m)))) (pairlis$ keys vals))) (defun imem-customp (x) (or (null x) (and (consp x) (consp (car x)) (imem-customp (cdr x)) (instp (cdar x)) (p-addrp (caar x)) (or (and (null (cdr x)) (equal 0 (caar x))) (> (caar x) (caadr x)))))) (register-type imem-custom :predicate imem-customp :enumerator imem-custom-enum) (defdata-attach imem :test-enumerator imem-custom-enum) }) <h4>Recursive record types (similar to ML datatype facility)</h4> @({ (defdata tree (oneof 'Leaf (node (val . string) (left . tree) (right . tree)))) }) <h4>Polymorphic Type Signatures</h4> @({ (sig binary-append ((listof :a) (listof :a)) => (listof :a)) (sig nthcdr (nat (listof :a)) => (listof :a)) (sig nth (nat (listof :a)) => :a :satisfies (< x1 (len x2))) ;; xi corresponds to the ith type appearing in the sig form. For example, ;; x1 corresponds to nat and x2 to (listof :a) }) <h4>Registering a data constructor</h4> @({ (defun acons-caar (x) (caar x)) (defun acons-cdar (x) (cdar x)) (defun acons-cdr (x) (cdr x)) (defthm acons-acl2-count-lemma (equal (acl2-count (acons x1 x2 x3)) (+ 2 (acl2-count x1) (acl2-count x2) (acl2-count x3)))) (register-data-constructor (aconsp acons) ((allp acons-caar) (allp acons-cdar) (allp acons-cdr)) :rule-classes (:rewrite) :verbose t) }) <h4>Registering a type</h4> @({ (defun nth-even-builtin (n) (declare (xargs :guard (natp n))) (* 2 (nth-integer n))) (register-type even :predicate evenp :enumerator nth-even-builtin) }) <h4>Registering user-defined combinators</h4> @({ (register-user-combinator alistof :arity 2 :verbose t :aliases (acl2::alistof) :expansion (lambda (_name _args) `(OR nil (acons ,(car _args) ,(cadr _args) ,_name))) :syntax-restriction-fn proper-symbol-listp :polymorphic-type-form (alistof :a :b) :post-pred-hook-fns (user-alistof-theory-events)) }) <h3>Debugging</h3> <p> To debug a failed defdata form, you can proceed in multiple ways: <ul> <li> At the session editor (or emacs prompt), submit/evaluate @(':trans1 (defdata ...)') And from the output, evaluate the form that says @('(defdata-events ...)').</li> <li> Use keyword options @(':verbose t') and examine the ACL2 output.</li> </ul> </p> <h3>More details</h3> <p> Although most constructs of the defdata language are package-agnostic, some of the API functions/macros of the Defdata framework (like <tt>sig</tt>) reside in the ACL2S package. Use list (<tt>*acl2s-exports*</tt>) to import these symbols into your own package.</p> <p> For a comprehensive user guide and more details please refer to the following <a href="http://arxiv.org/abs/1406.1557">paper</a> </p> ")
other
(defxdoc register-type :parents (defdata cgen) :short "Register a name as a defdata type" :long " <h3>Example</h3> @({ (defun nth-odd-builtin (n) (if (evenp n) (1+ n) (- n))) (register-type odd :predicate oddp :enumerator nth-odd-builtin) }) <h3>Introduction</h3> <p> A defdata type expression can either be a typename, a quoted constant or a combinator or constructor expression. To serve as a typename, a symbol should either have been defined using the @('defdata') macro, or it should have been <i>registered</i> as a <i>defdata type</i> using the @('register-type') macro. </p> <p> As an example, after having <i>registered</i> @('odd') above, we can now use @('odd') to define other @('defdata') types, e.g., a list of odd numbers: </p> @({ (defdata odds (listof odd)) }) <h4>General Form:</h4> @({ (register-type name :predicate pred :enumerator enum [:enum/acc enum2] [:enum/test tenum] [:enum/test/acc tenum2] [:clique tnames] [:normalized-def nexp] [:prettyified-def pexp] ...) }) Consider the above call of @('register-type'). We expect the following guards to be satisfied. <ul> <li><i>pred</i> is a monadic boolean-valued function.</li> <li><i>enum, tenum</i> are 1-arity functions that take a natural number and return a value of the right type.</li> <li><i>enum2, tenum2</i> are 2-arity functions that take a natural number and a random seed and return (mv value seed).</li> <li> The rest of the options are for experts and are not yet documented. </li> </ul> ")
other
(defxdoc register-data-constructor :parents (defdata) :short "Register a data constructor (to extend the defdata language)" :long " <h3>Introduction</h3> <p> You must be familiar with compound data types specified by @('defdata') using @('cons'). Although @('cons') has a unique status in ACL2, it is not natively available in the @('defdata') language unlike built-in combinators such as @('oneof') and @('range'). In fact, advanced users can introduce custom notions of product data by using the @('register-data-constructor') macro, whose usage we show below. </p> <h3>Example</h3> <p> Consider the @('symbol-alist') type defined as @('(oneof nil (cons (cons symbol all) symbol-alist))'). We could have registered @('acons') as a data constructor, and alternatively defined @('symbol-alist') using @('acons') instead of @('cons'). </p> @({ (defun aconsp (x) (and (consp x) (consp (car x)))) (defun acons-caar (x) (caar x)) (defun acons-cdar (x) (cdar x)) (defun acons-cdr (x) (cdr x)) (register-data-constructor (aconsp acons) ((allp acons-caar) (allp acons-cdar) (allp acons-cdr))) (defdata symbol-alist (oneof nil (acons symbol all symbol-alist))) }) <p> In fact, this is how we setup the base environment in "defdata/base.lisp": we use @('register-data-constructor') to preregister all the primitive data constructors in ACL2. In particular, the following (primitive) constructors are available to build product types: @('cons'), @('intern$'), @('/') and @('complex'). </p> <h3>General Form:</h3> @({ (register-data-constructor (recognizer constructor) ((destructor-pred1 destructor1) ...) [:proper bool] [:hints hints] [:rule-classes rule-classes]) }) ")
other
(defxdoc sig :parents (defdata macro-libraries) :short "Specify type signatures for polymorphic functions" :long " <h3>Examples:</h3> @({ (sig nthcdr (nat (listof :a)) => (listof :a)) (sig binary-append ((listof :a) (listof :a)) => (listof :a)) (sig nth (nat (listof :a)) => :a :satisfies (< x1 (len x2))) ;; xi corresponds to the ith type appearing in the sig form. For example, ;; x1 corresponds to nat and x2 to (listof :a) }) <h3>General Form:</h3> @({ (sig fun-name arg-types => return-type [:satisfies hyp] [:hints hints] [:gen-rule-classes rule-classes] [:rule-classes rule-classes] [:verbose t]) }) <p> where <i>arg-types</i> is a list of @('defdata') type expressions and <i>return-type</i> is a @('defdata') type expression with the added capability of referring to type variables, which in our syntax are represented using keyword symbols @(':a, :b, :c, ...'). </p> <p> Note: @('sig') is currently limited to only 12 type variables. Check @(':pe *allowed-type-vars*') to view them. We have never encountered type signatures with more than 3 type variables, so we hope that this is not a limitation in practice. There are several other limitations we impose on @('sig'). If you would like more comprehensive support, contact Pete and Harsh. </p> <p> The following keyword arguments are supported by the @('sig') macro: <ul> <li> :satisfies -- specify additional dependent type hypotheses.</li> <li> :hints -- @(see acl2::hints) option to the generic type signature.</li> <li> :gen-rule-classes -- @(see acl2::rule-classes) option to the generic type signature.</li> <li> :rule-classes -- @(see acl2::rule-classes) option to the generated theorems.</li> <li> :verbose -- for debugging.</li> </ul> </p> ")
other
(defxdoc register-user-combinator :parents (defdata) :short "Register a user-defined combinator (to add sugar to defdata language)" :long " <h3>Introduction</h3> <p> User-defined combinators add syntactic sugar on top of the core defdata language. In addition it is also possible to specify the theory support for such a construct via the <tt>:post-pred-hook-fns</tt> keyword option. (See alistof.lisp for example usage).</p> <h3>Example</h3> Here is how we added <i>alistof</i> to the defdata language: @({ (register-user-combinator alistof :arity 2 :verbose t :aliases (acl2::alistof) :expansion (lambda (_name _args) `(OR nil (acons ,(car _args) ,(cadr _args) ,_name))) :syntax-restriction-fn proper-symbol-listp :polymorphic-type-form (alistof :a :b) :post-pred-hook-fns (user-alistof-theory-events)) }) <h3>General Form:</h3> @({ (register-user-combinator combinator-name :arity num :expansion term [optional args]) }) ")
other
(defxdoc defdata-attach :parents (defdata cgen) :short "Attach/modify metadata for a defdata type" :long " <h3>Examples:</h3> @({ (defdata-attach pos :enumerator nth-small-pos-testing) (defdata-attach imem :enum/acc imem-custom-enum2) }) <h3>General Form:</h3> @({ (defdata-attach typename [:enum/test enum-fn] [:equiv eq-rel] [:equiv-fixer eq-fix-fn] [:constraint ... ] [:sampling constant-list] [overridable metadata] ) }) <p> Defdata-attach can be used to attach custom test enumerators for certain types. This provides a method to customize Cgen's test data generation capability for certain scenarios.</p> <p> (Advanced) Type refinement : User can attach rules that specify (to Cgen) how to refine/expand a variable of this type when certain additional constraints match (or subterm-match). For those who are familar with dest-elim rules, the :rule field has a similar form. For example: </p> @({ (defdata-attach imemory :constraint (mget a x) ;x is the variable of this type :constraint-variable x :rule (implies (and (natp a) ;additional hyps (instp x.a) (imemoryp x1)) (equal x (mset a x.a x1))) ;refine/expand :meta-precondition (or (variablep a) (fquotep a)) :match-type :subterm-match) }) <p> Warning: Other optional keyword arguments are currently unsupported and the use of :override-ok can be unsound.</p> ")