{"id":22318634,"url":"https://github.com/scymtym/architecture.builder-protocol","last_synced_at":"2025-07-29T13:31:26.425Z","repository":{"id":20113985,"uuid":"23383762","full_name":"scymtym/architecture.builder-protocol","owner":"scymtym","description":"Protocol for flexible construction and traversal of results (e.g. ASTs in case of parsers)","archived":false,"fork":false,"pushed_at":"2023-11-16T11:22:56.000Z","size":271,"stargazers_count":6,"open_issues_count":1,"forks_count":1,"subscribers_count":5,"default_branch":"master","last_synced_at":"2023-11-16T12:31:44.011Z","etag":null,"topics":["common-lisp","json","parsing","protocol","tree","xpath"],"latest_commit_sha":null,"homepage":"","language":"Common Lisp","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"firehol/packages","license":"lgpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/scymtym.png","metadata":{"files":{"readme":"README.org","changelog":null,"contributing":null,"funding":null,"license":"COPYING","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2014-08-27T09:14:46.000Z","updated_at":"2022-12-31T03:02:39.000Z","dependencies_parsed_at":"2023-01-11T20:43:23.288Z","dependency_job_id":"6f0a629a-3dc2-41de-b126-e622fdb753b7","html_url":"https://github.com/scymtym/architecture.builder-protocol","commit_stats":null,"previous_names":[],"tags_count":9,"template":null,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scymtym%2Farchitecture.builder-protocol","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scymtym%2Farchitecture.builder-protocol/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scymtym%2Farchitecture.builder-protocol/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scymtym%2Farchitecture.builder-protocol/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/scymtym","download_url":"https://codeload.github.com/scymtym/architecture.builder-protocol/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228017939,"owners_count":17856841,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["common-lisp","json","parsing","protocol","tree","xpath"],"created_at":"2024-12-03T23:42:05.682Z","updated_at":"2024-12-03T23:42:06.532Z","avatar_url":"https://github.com/scymtym.png","language":"Common Lisp","funding_links":[],"categories":[],"sub_categories":[],"readme":"#+TITLE:       architecture.builder-protocol README\n#+AUTHOR:      Jan Moringen\n#+EMAIL:       jmoringe@techfak.uni-bielefeld.de\n#+DESCRIPTION: A protocol for flexible result construction.\n#+KEYWORDS:    common lisp, architecture, protocol, framework, builder, pattern, parsing\n#+LANGUAGE:    en\n\n* STARTED Introduction\n  In tasks such as parsing there is often a need to construct a result\n  representation of some kind, e.g. a parse tree. This system is\n  concerned with flexible construction and processing of different\n  result representations while avoiding coupling between producers and\n  consumers of such results.\n\n  Staying with the parsing example, the result of a successful parse\n  is some sort of (abstract) syntax tree (AST). Most parsing code in\n  Common Lisp seems to do this in one of two ways: nested list\n  structures or a tree of (class or structure) instances. Both\n  approaches have advantages and disadvantages\n  + On the one hand, list-based parse results are well suited for\n    debugging since they pretty print nicely and unit tests since they\n    are =equal= comparable.\n  + On the other hand list-based results are not suitable for\n    CLOS-dispatch while instances are.\n  + Both kinds of results are well suited for AST processing using\n    pattern matching (e.g. with [[http://github.com/m2ym/optima][optima]]).\n  In practice, much parsing code seems to be written for one\n  particular consumer of the produced AST. This fact usually seems to\n  inform the choice of result representation.\n\n  This system employs the \"builder\" design pattern to enable a\n  flexible result representation with little effort for consumers and\n  producers. A \"builder protocol\" is concerned with the construction\n  of results while a \"un-builder protocol\" is concerned with\n  destructuring and traversing the constructed representations.\n\n  #+ATTR_HTML: :alt \"build status image\" :title Build Status :align right\n  [[https://travis-ci.org/scymtym/architecture.builder-protocol][https://travis-ci.org/scymtym/architecture.builder-protocol.svg]]\n\n* STARTED Tutorial\n  #+begin_src lisp :results none :exports none :session \"tutorial\"\n    #.(progn\n        #1=(ql:quickload '(:alexandria :architecture.builder-protocol\n                           :utilities.print-tree))\n        '#1#)\n  #+end_src\n\n** STARTED Build Protocol\n   Since this is a probably a common case, we will use the construction\n   of a simplistic AST from the output of an equally simplistic parser\n   as an example.\n\n   The example code in the following sections can be loaded into the\n   =cl-user= package and assumes that the =alexandria= system is\n   loaded.\n\n*** Implementing a Consumer of Results\n    The nodes of the AST we want to construct are either literals or\n    operator applications with two operands and are both expressions:\n    #+begin_src lisp :results none :exports code :session \"tutorial\"\n      (defclass expression () ())\n\n      (defclass literal (expression)\n        ((%value :initarg :value :reader literal-value)))\n\n      (defclass operator (expression)\n        ((%operands :accessor operator-operands :initform '())))\n    #+end_src\n    Note that the =value= slot of the =literal= is initialized using\n    the =:value= initarg while the =operands= slot of the =operator=\n    class is initialized to the empty lists but allows for later\n    mutation via =(setf operator-operands)=. The rationale is that\n    =literal= instances can be constructed in one =make-instance= call\n    while =operator= instance may be constructed before their operand\n    nodes, thus requiring mutation to attach these operand nodes once\n    they have been constructed.\n\n    A simple implementation of the builder protocol for these nodes\n    looks like this:\n    #+begin_src lisp :results none :exports code :session \"tutorial\"\n      (defclass ast-builder () ())\n\n      (defmethod architecture.builder-protocol:make-node\n          ((builder ast-builder)\n           (kind    (eql :literal))\n           \u0026key value)\n        (make-instance 'literal :value value))\n\n      (defmethod architecture.builder-protocol:make-node\n          ((builder ast-builder)\n           (kind    (eql :operator))\n           \u0026key)\n        (make-instance 'operator))\n\n      (defmethod architecture.builder-protocol:relate\n          ((builder  ast-builder)\n           (relation (eql :operand))\n           (left     operator)\n           (right    expression)\n           \u0026key)\n        (alexandria:appendf (operator-operands left) (list right))\n        left)\n    #+end_src\n    We can already use this builder without a parser:\n    #+begin_src lisp :exports both :session \"tutorial\"\n      (let* ((builder  (make-instance 'ast-builder))\n             (operands (list (architecture.builder-protocol:make+finish-node\n                              builder :literal :value 5)\n                             (architecture.builder-protocol:make+finish-node\n                              builder :literal :value 6)))\n             (operator (architecture.builder-protocol:make-node builder :operator)))\n        (architecture.builder-protocol:finish-node\n         builder :operator\n         (reduce (lambda (l r)\n                   (architecture.builder-protocol:relate\n                    builder :operand l r))\n                 operands :initial-value operator)))\n    #+end_src\n\n    #+RESULTS:\n    : #\u003cOPERATOR {100E5961}\u003e\n\n    The following is a more compact (but equivalent behind the scenes)\n    spelling of the above code:\n    #+begin_src lisp :exports both :session \"tutorial\"\n      (architecture.builder-protocol:with-builder ((make-instance 'ast-builder))\n        (architecture.builder-protocol:node* (:operator)\n          (* :operand (list (architecture.builder-protocol:node* (:literal :value 5))\n                            (architecture.builder-protocol:node* (:literal :value 6))))))\n    #+end_src\n\n    #+RESULTS:\n    : #\u003cOPERATOR {1019F0E013}\u003e\n\n*** Implementing a Producer of Results\n    We will use a parser for a very simple expressions in polish\n    notation:\n    #+begin_example\n    EXPRESSION ::= OPERATOR | LITERAL\n    LITERAL    ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'\n    OPERATOR   ::= '+' EXPRESSION EXPRESSION\n    #+end_example\n    The parser is straightforward: it has a local function for each\n    element of the grammar and uses the builder protocol like in the\n    previous example. Since we now parse an actual source text, source\n    locations of constructed result nodes can be recorded using the\n    =:bounds= initarg. Note that the =ast-builder= we defined in the\n    previous section receives the =:bounds= initarg in =make-node=\n    calls, but does not store it anywhere. If storing source locations\n    in AST nodes was desired, a =%source= slot could be added to the\n    =expression= class and the value of the =:bounds= keyword argument\n    could be passed to =make-instance= as the =:source= initarg.\n    #+begin_src lisp :results silent :exports code :session \"tutorial\"\n      (defun parse (stream builder)\n        (labels ((expression ()\n                   (let ((c (peek-char nil stream)))\n                     (cond ((char= c #\\+)\n                            (operator))\n                           ((digit-char-p c)\n                            (literal)))))\n                 (literal ()\n                   (let ((start (stream-file-position stream))\n                         (c     (read-char stream)))\n                     (architecture.builder-protocol:make-node\n                      builder :literal\n                      :value  (parse-integer (string c))\n                      :bounds (cons start (1+ start)))))\n                 (operator ()\n                   (let ((start    (stream-file-position stream))\n                         (c        (read-char stream))\n                         (operands (list (expression) (expression)))\n                         (end      (stream-file-position stream)))\n                     (declare (ignore c))\n                     (architecture.builder-protocol:finish-node\n                      builder :operator\n                      (reduce (lambda (l r)\n                                (architecture.builder-protocol:relate\n                                 builder :operator-operand l r))\n                              operands\n                              :initial-value (architecture.builder-protocol:make-node\n                                              builder :operator\n                                              :bounds (cons start end)))))))\n          (expression)))\n    #+end_src\n    As before, the various builder method calls can be written\n    compactly using the =node= macro:\n    #+begin_src lisp :results silent :exports code :session \"tutorial\"\n      (defun parse2 (stream builder)\n        (labels ((expression ()\n                   (let ((c (peek-char nil stream)))\n                     (cond ((char= c #\\+)\n                            (operator))\n                           ((digit-char-p c)\n                            (literal)))))\n                 (literal ()\n                   (let ((start (stream-file-position stream))\n                         (c     (read-char stream)))\n                     (architecture.builder-protocol:node\n                         (builder :literal :value  (parse-integer (string c))\n                                           :bounds (cons start (1+ start))))))\n                 (operator ()\n                   (let ((start    (stream-file-position stream))\n                         (c        (read-char stream))\n                         (operands (list (expression) (expression)))\n                         (end      (stream-file-position stream)))\n                     (declare (ignore c))\n                     (architecture.builder-protocol:node\n                         (builder :operator :bounds (cons start end))\n                       (* :operand operands)))))\n          (expression)))\n    #+end_src\n    The =with-builder= macro allows writing the =node= macro calls\n    without supplying the =builder= argument:\n    #+begin_src lisp\n      (architecture.builder-protocol:with-builder (BUILDER)\n        (architecture.builder-protocol:node* (:KIND :INITARG …)\n          (* :RELATION …)))\n    #+end_src\n\n\n*** The =list= Builder\n    When developing or testing result producers like parsers, it can be\n    convenient to produce a list-based result since it pretty-prints\n    nicely without any extra effort and can be =equal=-compared in unit\n    tests without depending on a more heavyweight representation such\n    as instances of AST node classes.\n\n    For these cases, the =architecture.builder-protocol= system\n    provides a builtin =list= builder:\n    #+begin_src lisp :results value code :exports both :session \"tutorial\"\n      (parse (make-string-input-stream \"++123\") 'list)\n    #+end_src\n\n    #+RESULTS:\n    #+BEGIN_SRC lisp\n\n    (:OPERATOR\n     (:OPERATOR-OPERAND\n      (((:OPERATOR\n         (:OPERATOR-OPERAND\n          (((:LITERAL NIL :VALUE 1 :BOUNDS (2 . 3)))\n           ((:LITERAL NIL :VALUE 2 :BOUNDS (3 . 4)))))\n         :BOUNDS (1 . 4)))\n       ((:LITERAL NIL :VALUE 3 :BOUNDS (4 . 5)))))\n     :BOUNDS (0 . 5))\n    #+END_SRC\n\n*** Printing Result Trees\n\n    This may be slightly off-topic, but a nice hack for printing\n    /arbitrary/ results produced by the =list= builder can be done\n    using the [[http://github.com/scymtym/utilities.print-tree][=utilities.print-tree= system]]:\n\n    #+begin_src lisp :exports code :session \"tutorial\"\n      (defun my-print-tree (tree \u0026optional (stream *standard-output*))\n        (utilities.print-tree:print-tree\n         stream tree\n         (utilities.print-tree:make-node-printer\n          (lambda (stream depth node)\n            (declare (ignore depth))\n            (destructuring-bind (kind relations \u0026rest slots) node\n              (declare (ignore relations))\n              (format stream \"~A~@[ @~A~]\"\n                      kind (getf slots :bounds))\n              (alexandria:remove-from-plist slots :bounds)))\n          (lambda (stream depth node)\n            (declare (ignore depth))\n            (destructuring-bind (kind relations \u0026rest slots) node\n              (declare (ignore kind relations))\n              (format stream \"~{~A: ~A~^~@:_~}\"\n                      (alexandria:remove-from-plist slots :bounds))))\n          (lambda (node)\n            (loop :for (relation nodes) :on (second node) :by #'cddr\n                  :appending (mapcar #'car nodes))))))\n    #+end_src\n\n    Putting these pieces together, we can achieve the following:\n    #+begin_src lisp :results output :exports both :session \"tutorial\"\n      (my-print-tree (parse (make-string-input-stream \"++123\") 'list))\n    #+end_src\n\n    #+RESULTS:\n    : OPERATOR @(0 . 5)\n    : ├─OPERATOR @(1 . 4)\n    : │ ├─LITERAL @(2 . 3)\n    : │ │   VALUE: 1\n    : │ └─LITERAL @(3 . 4)\n    : │     VALUE: 2\n    : └─LITERAL @(4 . 5)\n    :     VALUE: 3\n\n    The system =architecture.builder-protocol.print-tree= implements a\n    more complete version (not restricted to the =list= builder, among\n    other things) of this idea:\n\n    #+BEGIN_SRC lisp :results output :exports both :session \"tutorial\"\n      (defun print-tree (tree)\n        (fresh-line)\n        (architecture.builder-protocol.print-tree:print-tree\n         'list tree *standard-output*))\n\n      (print-tree (parse (make-string-input-stream \"++123\") 'list))\n    #+END_SRC\n\n    #+RESULTS:\n    : OPERATOR @(0 . 5)\n    : ├─OPERATOR-OPERAND: OPERATOR @(1 . 4)\n    : │ ├─OPERATOR-OPERAND: LITERAL @(2 . 3)\n    : │ │   VALUE: 1\n    : │ └─OPERATOR-OPERAND: LITERAL @(3 . 4)\n    : │     VALUE: 2\n    : └─OPERATOR-OPERAND: LITERAL @(4 . 5)\n    :     VALUE: 3\n\n** TODO \"Un-build\" Protocol\n\n*** STARTED The =walk-nodes= Function\n    The generic function =walk-nodes= can be used to traverse trees of\n    nodes built using the build protocol. It uses the \"un-build\"\n    protocol and can thus handle arbitrary tree representations.\n\n* STARTED Dictionary\n\n  #+begin_src lisp :results none :exports none :session \"doc\"\n    #.(progn\n        #1=(ql:quickload '(:architecture.builder-protocol :alexandria :split-sequence))\n        '#1#)\n    (defun doc (symbol kind)\n      (let* ((lambda-list (sb-introspect:function-lambda-list symbol))\n             (string      (documentation symbol kind))\n             (lines       (split-sequence:split-sequence #\\Newline string))\n             (trimmed     (mapcar (alexandria:curry #'string-left-trim '(#\\Space)) lines)))\n        (format nil \"~(~A~) ~\u003c~{~A~^ ~}~:@\u003e~2%~{~A~^~%~}\"\n                symbol (list lambda-list) trimmed)))\n  #+end_src\n\n** STARTED Build Protocol\n\n   #+begin_src lisp :exports results :session \"doc\"\n     (doc 'architecture.builder-protocol:prepare 'function)\n   #+end_src\n\n   #+RESULTS:\n   #+begin_example\n   prepare BUILDER\n\n   Prepare BUILDER for result construction, return a builder.\n\n   The default method just returns BUILDER.\n   #+end_example\n\n   #+begin_src lisp :exports results :session \"doc\"\n     (doc 'architecture.builder-protocol:finish 'function)\n   #+end_src\n\n   #+RESULTS:\n   #+begin_example\n   finish BUILDER VALUES\n\n   Finalize and return VALUES produced by BUILDER as multiple values.\n\n   VALUES is a list of objects that should be returned as multiple\n   values and constitute the overall result of an object tree\n   construction with BUILDER. The first element of VALUES which\n   becomes the first return value is the constructed tree\n   itself (which often coincides with the root node). Additional\n   values are optional and their presence and meaning depend on\n   BUILDER.\n\n   The default method just returns VALUES.\n   #+end_example\n\n   #+begin_src lisp :exports results :session \"doc\"\n     (doc 'architecture.builder-protocol:wrap 'function)\n   #+end_src\n\n   #+RESULTS:\n   #+begin_example\n   wrap BUILDER THUNK\n\n   Call THUNK with an appropriate dynamic environment for BUILDER.\n\n   A method on this generic function could, for example, bind special\n   variables around the construction of a result object tree.\n\n   The existing default methods do not specialize the BUILDER\n   parameter and specialize the THUNK parameter to `cl:function' and\n   `cl:symbol'. These default methods just call THUNK.\n   #+end_example\n\n   #+begin_src lisp :exports results :session \"doc\"\n     (doc 'architecture.builder-protocol:make-node 'function)\n   #+end_src\n\n   #+RESULTS:\n   #+begin_example\n   make-node BUILDER KIND \u0026REST INITARGS \u0026KEY \u0026ALLOW-OTHER-KEYS\n\n   Use BUILDER to make a result tree node of kind KIND and return it.\n\n   As a convention, when supplied, the value of the :bounds keyword\n   argument is of the form (START . END) and can be used to indicate\n   the input range for which the tree is constructed.\n   #+end_example\n\n   #+begin_src lisp :exports results :session \"doc\"\n     (doc 'architecture.builder-protocol:finish-node 'function)\n   #+end_src\n\n   #+RESULTS:\n   #+begin_example\n   finish-node BUILDER KIND NODE\n\n   Use BUILDER to perform finalization for NODE.\n\n   Return the modified NODE or an appropriate newly created object.\n   #+end_example\n\n   #+begin_src lisp :exports results :session \"doc\"\n     (doc 'architecture.builder-protocol:relate 'function)\n   #+end_src\n\n   #+RESULTS:\n   #+begin_example\n   relate BUILDER RELATION LEFT RIGHT \u0026REST ARGS \u0026KEY \u0026ALLOW-OTHER-KEYS\n\n   Establish RELATION between nodes LEFT and RIGHT and return the\n   resulting modified LEFT node (or an appropriate newly created\n   object).\n\n   ARGS can be used to supply additional information about the\n   relation that is available from neither LEFT nor RIGHT.\n\n   In a typical case, RELATION could be :child, LEFT being the parent\n   node and RIGHT being the child node.\n   #+end_example\n\n*** STARTED Convenience Functions\n\n    #+BEGIN_SRC lisp :exports results :session \"doc\"\n      (doc 'architecture.builder-protocol:add-relations 'function)\n    #+END_SRC\n\n    #+RESULTS:\n    #+begin_example\n    add-relations BUILDER NODE RELATIONS\n\n    Use BUILDER to add relations according to RELATIONS to NODE.\n\n    RELATIONS is a list of relation specifications of the form\n\n    (CARDINALITY RELATION-NAME RIGHT \u0026rest ARGS)\n\n    which are translated into `relate' calls in which NODE is the\n    \"left\" argument to `relate'. CARDINALITY has to be of type\n    `relation-cardinality' and is interpreted as follows:\n\n    ?            RIGHT is a single node or `nil'. If RIGHT is `nil',\n                 `relate' is not called.\n\n    1            RIGHT is a single node.\n\n    *            RIGHT is a (possibly empty) sequence of nodes. ARGS\n                 must be of the form\n\n                   :KEY₁ (VALUE₁₁ VALUE₁₂ …) :KEY₂ (VALUE₂₁ VALUE₂₂ …) …\n\n                 . The `relate' call for the k-th element of RIGHT will\n                 receive the keyword arguments\n                 :KEY₁ VALUEₖ₁ :KEY₂ VALUEₖ₂ …. If the value list for a\n                 given key would be a repetition of a particular value\n                 VALUE, the circular list #1=(VALUE . #1#) may be used\n                 as a replacement for that value list.\n\n    (:map . KEY) RIGHT is a (possible empty) sequence of nodes that\n                 should be \"zipped\" with a sequence of keys (see\n                 below) to form a set of key-values pair and thus a\n                 map. The sequence of keys is the value of the property\n                 whose indicator is KEY in the ARGS plist. The two\n                 sequences must be of the same length. Elements at\n                 corresponding positions will be paired in a\n                 \"zipping\" operation as described above.\n\n    RELATION-NAME does not have to be unique across the elements of\n    RELATIONS. This allows multiple \"right\" nodes to be related to\n    NODE via a given RELATION-NAME with CARDINALITY * in multiple\n    RELATIONS entries, potentially with different ARGS.\n\n    The modified NODE or a new node is returned.\n    #+end_example\n\n    #+BEGIN_SRC lisp :exports results :session \"doc\"\n      (doc 'architecture.builder-protocol:make+finish-node 'function)\n    #+END_SRC\n\n    #+RESULTS:\n    #+begin_example\n    make+finish-node BUILDER KIND \u0026REST INITARGS \u0026KEY \u0026ALLOW-OTHER-KEYS\n\n    Convenience function for constructing and immediately finishing a\n    node.\n    #+end_example\n\n    #+BEGIN_SRC lisp :exports results :session \"doc\"\n      (doc 'architecture.builder-protocol:make+finish-node+relations 'function)\n    #+END_SRC\n\n    #+RESULTS:\n    #+begin_example\n    make+finish-node+relations BUILDER KIND INITARGS RELATIONS\n\n    Use BUILDER to create a KIND, INITARGS node, relate it via RELATIONS.\n\n    RELATIONS is processed as described for `add-relations'.\n\n    `finish-node' is called on the created node. The created node is\n    returned.\n    #+end_example\n\n** STARTED \"Un-build\" Protocol\n\n   #+begin_src lisp :exports results :session \"doc\"\n     (doc 'architecture.builder-protocol:node-kind 'function)\n   #+end_src\n\n   #+RESULTS:\n   #+begin_example\n   node-kind BUILDER NODE\n\n   Return the kind of NODE w.r.t. BUILDER.\n\n   The return value is EQ to the KIND argument used to create NODE\n   with BUILDER.\n   #+end_example\n\n   #+begin_src lisp :exports results :session \"doc\"\n     (doc 'architecture.builder-protocol:node-initargs 'function)\n   #+end_src\n\n   #+RESULTS:\n   #+begin_example\n   node-initargs BUILDER NODE\n\n   Return a plist of initargs for NODE w.r.t. BUILDER.\n\n   The returned list is EQUAL to the list of keyword arguments pass\n   to the MAKE-NODE call that, using BUILDER, constructed NODE.\n   #+end_example\n\n   #+begin_src lisp :exports results :session \"doc\"\n     (doc 'architecture.builder-protocol:node-relations 'function)\n   #+end_src\n\n   #+RESULTS:\n   #+begin_example\n   node-relations BUILDER NODE\n\n   Return a list of relations of NODE w.r.t. BUILDER.\n\n   Each relation is of one of the forms\n\n   RELATION-NAME\n   (RELATION-NAME . CARDINALITY)\n\n   where RELATION-NAME names the relation and CARDINALITY is of type\n   `relation-cardinality'. When the first form is used,\n   i.e. CARDINALITY is not present, it is assumed to be\n   `*'. CARDINALITY values are interpreted as follows:\n\n   ?            The relation designated by RELATION-NAME with NODE\n   as the \"left\" node has zero or one \"right\"\n   nodes.\n\n   1            The relation designated by RELATION-NAME with NODE\n   as the \"left\" node has exactly one \"right\"\n   node.\n\n   *            The relation designated by RELATION-NAME with NODE\n   as the \"left\" node has zero or more \"right\"\n   nodes.\n\n   (:map . KEY) The relation designated by RELATION-NAME with NODE\n   as the \"left\" node has zero or more \"right\"\n   nodes with the additional constraint that the\n   relation parameters for each such node must contain\n   a unique value for the key KEY.\n\n   . This cardinality information is reflected by the return values\n   of (node-relation BUILDER RELATION-NAME NODE).\n   #+end_example\n\n   #+begin_src lisp :exports results :session \"doc\"\n     (doc 'architecture.builder-protocol:node-relation 'function)\n   #+end_src\n\n   #+RESULTS:\n   #+begin_example\n   node-relation BUILDER RELATION NODE\n\n   Return two values: 1) a single node or a sequence of nodes related to\n   NODE via RELATION w.r.t. BUILDER 2) `nil' or a same-length\n   sequence of arguments of the relations.\n\n   RELATION must be of one of the forms\n\n     RELATION-NAME\n     (RELATION-NAME . CARDINALITY)\n\n   where RELATION-NAME names the relation and CARDINALITY is of type\n   `relation-cardinality'. The second form is accepted for\n   convenience so that, for example, relation descriptions returned\n   by `node-relations' can be used as arguments to this\n   function. CARDINALITY is not processed by this function except\n   that a `type-error' may be signaled if CARDINALITY is not of type\n   `relation-cardinality'.\n\n   If the cardinality of RELATION is 1 or `?', the first return value\n   is a single node. Otherwise the first return value is a sequence\n   of nodes. Again, note that the cardinality of RELATION here refers\n   to the actual cardinality as known by BUILDER, not information\n   encoded in RELATION by the caller supplying RELATION\n   as (RELATION-NAME . CARDINALITY).\n\n   Each element in the sequence of relation arguments is EQUAL to the\n   list of arguments passed to the RELATE call that, using BUILDER,\n   established the relation between NODE and the related node.\n   #+end_example\n\n   #+begin_src lisp :exports results :session \"doc\"\n     (doc 'architecture.builder-protocol:walk-nodes 'function)\n   #+end_src\n\n   #+RESULTS:\n   #+begin_example\n   walk-nodes BUILDER FUNCTION ROOT\n\n   Call FUNCTION on nodes of the tree ROOT constructed by BUILDER.\n\n   Return whatever FUNCTION returns when called for ROOT.\n\n   The lambda-list of FUNCTION must be compatible to\n\n   (recurse relation relation-args node kind relations\n   \u0026rest initargs)\n\n   where RELATION and RELATION-ARGS are the relation and its\n   arguments connecting NODE to the previously visited node,\n\n   NODE is the node currently being visited,\n\n   KIND is the kind returned by `node-kind' for BUILDER and NODE.\n\n   RELATIONS are the relations returned by `node-relations' for\n   BUILDER and NODE.\n\n   INITARGS are the initargs returned by `node-initargs' for BUILDER\n   and NODE.\n\n   RECURSE is a function with the lambda-list\n\n   (\u0026key relations function)\n\n   that can be called, optionally with a list of relations, to\n   traverse the nodes related to NODE by that relation. If a list of\n   relations is not supplied via the :relations keyword parameter,\n   all relations are traversed. The :function keyword parameter\n   allows performing the traversal with a different function instead\n   of FUNCTION. Calls of this function return a list of elements each\n   of which is the result for the corresponding element of\n   RELATIONS. The result for a relation is either the return value of\n   FUNCTION if the cardinality of the relation is 1 or ? or a list of\n   such return values if the cardinality is * or :map.\n\n   If FUNCTION is an instance of `peeking', call the \"peeking\"\n   function stored in FUNCTION before the ordinary walk\n   function (also stored in FUNCTION) is called. The lambda-list of\n   the \"peeking\" function must be compatible to\n\n   (builder relation relation-args node)\n\n   (i.e. it does not receive kind, initargs or relations). This\n   function can control whether NODE should be processed normally,\n   replaced with something else, processed with a different builder\n   or ignored: Its return values are interpreted as follows:\n\n   NIL\n\n   Forego processing of NODE, in particular do not call\n   `node-kind', `node-relations', `node-initargs' or the walk\n   function for NODE.\n\n   T [* * * BUILDER]\n\n   Continue processing as if there was no \"peeking\" function.\n\n   If non-NIL, BUILDER specifies a builder that should be used\n   instead of the current builder to process the NODE and its\n   ancestors.\n\n   INSTEAD KIND INITARGS RELATIONS [BUILDER]\n\n   Continue processing as if NODE had been replaced by INSTEAD and\n   builder had returned KIND, INITARGS and RELATIONS. In particular\n   do not call `node-kind', `node-relations', `node-initargs' for\n   NODE.\n\n   If non-NIL, BUILDER specifies a builder that should be used\n   instead of the current builder to process INSTEAD and its\n   ancestors.\n\n   Depending on FUNCTION, potentially return a list-of-lists of the\n   same shape as the traversed tree containing return values of\n   FUNCTION.\n   #+end_example\n\n* Settings                                                         :noexport:\n\n#+OPTIONS: H:4 num:nil toc:t \\n:nil @:t ::t |:t ^:t -:t f:t *:t \u003c:t\n#+OPTIONS: TeX:t LaTeX:t skip:nil d:nil todo:t pri:nil tags:not-in-toc\n#+SEQ_TODO: TODO STARTED | DONE\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fscymtym%2Farchitecture.builder-protocol","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fscymtym%2Farchitecture.builder-protocol","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fscymtym%2Farchitecture.builder-protocol/lists"}