Create a template that says how to make visitor functions.
This is used in combination with defvisitors and defvisitor to automatically generate "visitor" functions, i.e. functions that traverse a data structure and do something at specified locations in it. E.g., they can be used to transform all fields of a certain type, or to collect some information about all occurrences of a certain product field, etc. The types that these visitors may traverse are those defined by deftypes and related macros defprod, deftagsum, deflist, defalist, defoption, deftranssum, and defflexsum.
Visitor templates can be used by defvisitor, defvisitors, and defvisitor-multi to automatically generate immense amounts of boilerplate code for traversing complicated datatypes, especially when the operation you want to do only really has to do with a few fields or component types.
Here is a simple example from visitor-tests.lisp, annotated:
(defvisitor-template ;; Name of the template. This gets referred to later when this template is ;; used by defvisitor/defvisitors. collect-strings ;; Formals, similar to the formals in std::define. Here :object stands for ;; the type predicate of whatever kind of object we're currently visiting; we'll ;; typically instantiate this template with several different :object types. ((x :object)) ;; Return specifiers. These are also like in std::define, but after each return name ;; is a description of how the return value gets constructed. The names here are ;; a "join" value, which means they get constructed by combining, ;; pairwise, the corresponding values returned by sub-calls. In this case, the ;; value (names1) returned from the most recent subcall is appended onto the ;; previous value (names). The initial value is nil, i.e. this is what a ;; visitor function returns when run on an empty list or an object with no fields. :returns (names (:join (append names1 names) :tmp-var names1 :initial nil) string-listp) ;; Now we describe what is a base case and what we return in base cases. ;; This says, for any string field x, just produce (list x). (The value ;; bound in the alist is a lambda or function that gets applied to the ;; formals, in this case just x.) :type-fns ((string list)) ;; Describes how the functions we produce should be named. Here, <type> gets ;; replaced by the type of object each separate visitor function operates on. :fnname-template collect-strings-in-<type>)
Besides join values, there are two other kinds of visitor return values: accumulators and updaters. The following example shows how to use an accumulator:
(defvisitor-template collect-names-acc ;; template name ;; formals: ((x :object) (names string-listp)) ;; accumulator formal ;; Names the return value and declares it to be an accumulator, which ;; corresponds to the formal NAMES. The :fix is optional but is needed if ;; the return type of your accumulator output is unconditional. :returns (names-out (:acc names :fix (string-list-fix names)) string-listp) ;; Base case specification. This says that when visiting a ;; simple-tree-leaf product, use the function CONS as the visitor for the ;; NAME field. That is, instead of recurring on name, use (cons x names), ;; i.e., add the name to the accumulator. :prod-fns ((simple-tree-leaf (name cons))))
This shows how to use an updater return value:
(defvisitor-template incr-val ((x :object) (incr-amount natp)) ;; In an :update return value, the type is implicitly the same as :object. ;; It can optionally be specified differently. This means that new-x gets ;; created by updating all the fields that we recurred on (or that were base ;; cases) with the corresponding results. :returns (new-x :update) ;; This says that when we visit a simple-tree-leaf, we replace its value field with ;; the field's previous value plus (lnfix incr-amount). (We could just use ;; + here instead of the lambda, but this would violate the fixing convention for ;; incr-amount.) :prod-fns ((simple-tree-leaf (value (lambda (x incr-amount) (+ x (lnfix incr-amount)))))))
The general form of a
(defvisitor-template template-name formals ... keyword-args)
where the accepted keywords are as follows: