(require 'cl) (load (concat (getenv "SWARMDOCS") "common.el")) (defvar *protocol-hash-table* (make-hash-table :test #'equal)) (defvar *module-hash-table* (make-hash-table)) (defconst *phases* '(:creating :setting :using)) (defvar *protocol-list*) (defvar *method-signature-hash-table* (make-hash-table :test #'equal)) (defvar *method-signature-list*) (defvar *general-example-counter-hash-table* (make-hash-table :test #'eq)) (defvar *method-example-counter-hash-table* (make-hash-table :test #'equal)) (defvar *macro-name-hash-table* (make-hash-table :test #'equal)) (defstruct module sym summary description-list function-list global-list macro-list typedef-list example-list) (defstruct protocol module name summary description-list included-protocol-list macro-list function-list global-list typedef-list example-list method-list expanded-methodinfo-list) (defstruct method phase factory-flag return-type arguments description-list example-list) (defstruct global name module protocol type description-list) (defstruct macro name module protocol arguments description-list) (defstruct typedef name module protocol type description-list) (defstruct function name module protocol return-type arguments description-list example-list) (defstruct parse-state tag last-tag phase line buf summary-doc description-doc-list global-type global-name global-names global-list function-return-type function-name function-list macro-name macro-list item-doc-list method-list typedef-list scratch-example-list example-list) (defconst *doc-types* '(:method-doc :summary-doc :description-doc :function-doc :macro-doc :typedef-doc :global-doc :global-begin :global-end :global-break :example-doc)) (defconst *protocol-regexp* "^@\\(protocol\\|deftype\\)") (defconst *funcptr-regexp* "\\([^;()]*(\\s-*[*]*\\s-*\\([^*);]+\\))[^;]*\\);") (defun find-protocol () (interactive) (re-search-forward *protocol-regexp* nil t)) (defun skip-whitespace () (skip-chars-forward " \t\r\n")) (defun skip-whitespace-backward () (skip-chars-backward " \t\r\n")) (defun skip-backward-to-nonwhite () (when (looking-at "\\s-") (skip-whitespace-backward)) (when (looking-at "\\s-") (backward-char))) (defun skip-name () (skip-chars-forward "[a-zA-Z_.][a-zA-Z0-9_.]") (point)) (defun next-paren-expr () (when (looking-at "(") (let ((beg (point))) (forward-sexp) (buffer-substring (1+ beg) (- (point) 1))))) (defun next-expr () (list (progn (skip-whitespace) (next-paren-expr)) (progn (skip-whitespace) (next-name)))) (defun end-of-line-position () (save-excursion (end-of-line) (point))) (defun parse-included-protocol-list () (let ((eolpos (end-of-line-position))) (loop with beg = (search-forward "<" eolpos t) while beg for end = (re-search-forward "[ \t,>]" eolpos t) unless end do (error "Bad protocol syntax") do (backward-char) for next = (cond ((looking-at "[ \t,>]") (skip-chars-forward ", \t") (if (looking-at ">") nil (point))) (t (point))) collect (buffer-substring beg (- end 1)) and do (setq beg next)))) (defun next-name () (let* ((beg (point)) (end (skip-name))) (prog1 (buffer-substring beg end) (skip-whitespace)))) (defun parse-method (protocol factory-flag parse-state) (let ((phase (parse-state-phase parse-state)) (method-description-list (reverse (parse-state-item-doc-list parse-state))) (method-example-list (reverse (parse-state-scratch-example-list parse-state)))) (forward-char) (skip-whitespace) (let* ((return-type (next-paren-expr)) arguments name) (loop unless (looking-at ":") do (setq name (next-name)) (when (looking-at ";") (push (cons name nil) arguments)) when (looking-at ":") do (forward-char) (push (cons name (next-expr)) arguments) (setq name nil) (while (looking-at ",") (forward-char) (push (cons nil (next-expr)) arguments)) (when (looking-at "//") (beginning-of-line 2)) until (looking-at ";")) (unless phase (error "No phase in protocol: %s" (protocol-name protocol))) (make-method :phase phase :factory-flag factory-flag :arguments (reverse arguments) :return-type return-type :description-list method-description-list :example-list method-example-list)))) (defun parse-function (module protocol parse-state) (prog1 (make-function :name (parse-state-function-name parse-state) :module module :protocol protocol :return-type (parse-state-function-return-type parse-state) :description-list (reverse (parse-state-item-doc-list parse-state)) :example-list (reverse (parse-state-scratch-example-list parse-state)) :arguments (progn (search-forward "(") (loop do (skip-whitespace) for start-pos = (point) do (re-search-forward "[),]") (backward-char 2) (skip-backward-to-nonwhite) for arg = (buffer-substring start-pos (1+ (point))) collect (if (string-match "\\(.*[^a-zA-Z_]\\)\\([a-zA-Z_]+\\)" arg) (cons (match-string 1 arg) (match-string 2 arg)) (cons arg nil)) do (re-search-forward "[),]") (backward-char) until (looking-at ")") do (forward-char)))) (search-forward ";"))) (defun handle-function (module protocol parse-state) (prog1 (push (parse-function module protocol parse-state) (parse-state-function-list parse-state)) (setf (parse-state-item-doc-list parse-state) nil))) (defun line-text () (buffer-substring (point) (end-of-line-position))) (defun general-example-counter (protocol) (let ((index (let ((val (gethash protocol *general-example-counter-hash-table*))) (if val (progn (incf (gethash protocol *general-example-counter-hash-table*)) val) (progn (setf (gethash protocol *general-example-counter-hash-table*) 1) 0))))) (1+ index))) (defun method-example-counter (protocol method) (let ((index (let* ((key (cons protocol method)) (val (gethash key *general-example-counter-hash-table*))) (if val (progn (incf (gethash key *general-example-counter-hash-table*)) val) (progn (setf (gethash key *general-example-counter-hash-table*) 1) 0))))) (1+ index))) (defun extract-doc-string (str) (if (> (length str) 5) (substring str 5) "")) (defun parse-global-using-parse-state (module protocol parse-state) (prog1 (make-global :name (parse-state-global-name parse-state) :module module :protocol protocol :type (parse-state-global-type parse-state) :description-list (if (eq (parse-state-tag parse-state) :global) (parse-state-item-doc-list parse-state) (list (extract-doc-string (parse-state-line parse-state))))) (setf (parse-state-item-doc-list parse-state) nil))) (defun immediate-global-tag-processed (module protocol parse-state) (when (member (parse-state-tag parse-state) '(:global-begin :global-end :global-break)) (parse-global-using-parse-state module protocol parse-state))) (defun is-doc-type (parse-state) (member (parse-state-tag parse-state) *doc-types*)) (defun set-buf (parse-state) (setf (parse-state-buf parse-state) (extract-doc-string (parse-state-line parse-state)))) (defun append-buf (parse-state) (let ((tag (parse-state-tag parse-state))) (when (is-doc-type parse-state) (let ((buf (parse-state-buf parse-state))) (setf (parse-state-buf parse-state) (let ((line (parse-state-line parse-state))) (if (eq tag :example-doc) (concat buf "\n" (extract-doc-string line)) (concat (if (string-match " $" buf) buf (concat buf " ")) (extract-doc-string line))))))))) (defun extract-funcptr-name () (save-excursion (forward-char) (backward-sexp) (let ((end (save-excursion (search-backward ")") (point)))) (backward-sexp) (search-forward "*") (buffer-substring (point) end)))) (defun extract-funcptr-type () (let ((name (extract-funcptr-name))) (save-excursion (forward-char) (let ((end (point))) (backward-sexp 3) (strip-regexp (buffer-substring (point) end) name))))) (defun update-global-state (parse-state) (unless (save-excursion (beginning-of-line) (looking-at "\\s-+//G:")) (let ((is-terminated (looking-at ";"))) (backward-char) (skip-backward-to-nonwhite) (if (looking-at ")") (let ((name (extract-funcptr-type)) (type (extract-funcptr-name))) (setf (parse-state-global-type parse-state) name) (setf (parse-state-global-name parse-state) type)) (progn (backward-sexp) (let ((name-beg (point))) (forward-sexp) (let ((name (buffer-substring name-beg (point)))) (setf (parse-state-global-name parse-state) name)) (when is-terminated (beginning-of-line) (forward-sexp) (skip-whitespace) (let ((type-beg (point))) (goto-char name-beg) (backward-char) (skip-backward-to-nonwhite) (let ((type (buffer-substring type-beg (1+ (point))))) (setf (parse-state-global-type parse-state) type)))))))) (unless (parse-state-global-name parse-state) (error "nil name (non-funcptr)")) (unless (parse-state-global-type parse-state) (error "nil type (non-funcptr)")))) (defun check-global-doc-post (parse-state) (search-forward "//G:") (backward-char 4) (save-excursion (let ((last-extern (parse-state-global-name parse-state))) (update-global-state parse-state) (cond ((save-excursion (beginning-of-line) (looking-at ".*;.*//G:")) :global-end) ((not (string= last-extern (parse-state-global-name parse-state))) :global-break) (t :global-doc))))) (defun globaldocp () (save-excursion (search-forward "//G:" (end-of-line-position) t))) (defun check-global (parse-state) (let ((tag :global)) (re-search-forward "\\s-+") (let ((not-eol-flag (re-search-forward "\\(;\\|//G:\\|,\\)" (end-of-line-position) t))) (when not-eol-flag (backward-char)) (cond ((or (not not-eol-flag) (looking-at ":")) (save-excursion (let ((end (if not-eol-flag (point) (end-of-line-position)))) (beginning-of-line) (forward-sexp) (skip-whitespace) (let ((beg (point))) (goto-char end) (skip-whitespace-backward) (setf (parse-state-global-type parse-state) (buffer-substring beg (point)))))) (if not-eol-flag (progn (setq tag :global-begin) (forward-char 2)) (setq tag :global-type-only))) ((looking-at ",") (if (globaldocp) ; implies only one (update-global-state parse-state) (progn (let ((start (point))) (setf (parse-state-global-name parse-state) nil) (setq tag :global-names) (save-excursion (backward-char) (backward-sexp) (let ((name-beg (point))) (search-forward ";") (backward-char 2) (skip-backward-to-nonwhite) (let ((names (buffer-substring name-beg (1+ (point))))) (setf (parse-state-global-names parse-state) names)) (goto-char start) (beginning-of-line) (forward-sexp) (forward-char) (let* ((type-beg (point)) (type (buffer-substring type-beg (save-excursion (goto-char name-beg) (backward-char) (skip-backward-to-nonwhite) (1+ (point)))))) (setf (parse-state-global-type parse-state) type)))))))) ((looking-at ";") (update-global-state parse-state) (when (globaldocp) (setq tag :global-end)))) (case tag ((:global-begin :global-end) (search-forward "//G: ") (backward-char 5)) (:global-names (search-forward ";") (setq tag :global)) ((:global :global-type-only)) (otherwise (error "unknown global tag: %s" tag)))) tag)) (defun skip-c-comment () (interactive) (search-forward "*/") (beginning-of-line 2)) (defun check-common-tags (parse-state) (cond ((looking-at "[ \t]*$") :newline) ((looking-at "//S:") :summary-doc) ((looking-at "//D:") :description-doc) ((looking-at "//#:") :macro-doc) ((looking-at "//F:") :function-doc) ((looking-at "//T:") :typedef-doc) ((looking-at "extern\\s-\\([^(]*\\|[^()]*(\\s-*[*][^)]+)([^)]*)[^()]*\\)$") (check-global parse-state)) ((looking-at "//G:") :global-doc) ((looking-at ".+//G:") (check-global-doc-post parse-state)) ((looking-at "#if 0") (c-forward-conditional 1) (beginning-of-line 0) :ifdef-comment) ((looking-at "#ifndef") :ifndef) ((looking-at "#if [^0]") :if) ((looking-at "#endif") :endif) ((looking-at "#else") :else) ((looking-at "#undef") :undef) ((looking-at "/\\*") (skip-c-comment) :c-comment) ((looking-at "#import") :import) ((looking-at " ") :page-break) ((looking-at "typedef\\s-+") :typedef) ((or (looking-at "// ") (looking-at "//$")) :objc-comment) ((looking-at "#define\\s-+\\([^() \t\n]+\\)\\(\\s-+[^()\n]+\\)?$") (setf (parse-state-macro-name parse-state) (match-string 1)) :define) ((looking-at "#define\\s-+\\([^(\n]+\\)\\(([^)\n]*)\\)") (setf (parse-state-macro-name parse-state) (match-string 1)) :macro) ((looking-at "@class") :class) ((looking-at "extern\\s-+\\(Class\\s-+\\|int\\s-+\\|void\\s-\\|id\\s-+<.*\\s-\\|id\\s-\\|const\\s-+char\\s-*\\*\\|BOOL\\)\\s-*\\([^ (]+\\)\\s-*(") (let ((return-type (match-string 1)) (function-name (match-string 2))) (setf (parse-state-function-return-type parse-state) (strip-regexp return-type "\\s+$")) (setf (parse-state-function-name parse-state) function-name)) :function) (t nil))) (defun check-protocol-tags (parse-state) (let ((tag (cond ((looking-at "CREATING") :creating) ((looking-at "SETTING") :setting) ((looking-at "USING") :using) ((looking-at "-") :method) ((looking-at "+") :factory-method) ((looking-at "//M:") :method-doc) ((looking-at "@end") :protocol-end) ((looking-at "//E:") :example-doc) ((looking-at "///M:") :bogus-method) (t nil)))) (when (member tag '(:creating :setting :using)) (setf (parse-state-phase parse-state) tag)) tag)) (defun protocol-tag-change (parse-state) (let ((buf (parse-state-buf parse-state))) (case (parse-state-last-tag parse-state) (:example-doc (push (concat buf "\n") (parse-state-scratch-example-list parse-state)) (unless (or (parse-state-method-list parse-state) (parse-state-item-doc-list parse-state)) (setf (parse-state-example-list parse-state) (parse-state-scratch-example-list parse-state)) (setf (parse-state-scratch-example-list parse-state) nil))) (:method-doc (push buf (parse-state-item-doc-list parse-state)))))) (defun common-tag-change (parse-state) (let ((buf (parse-state-buf parse-state))) (case (parse-state-last-tag parse-state) ((:function-doc :macro-doc :global-doc :typedef-doc) (push buf (parse-state-item-doc-list parse-state))) (:summary-doc (if (parse-state-summary-doc parse-state) (error "summary already set") (setf (parse-state-summary-doc parse-state) buf))) (:description-doc (push buf (parse-state-description-doc-list parse-state)))))) (defun handle-method (protocol factory-flag parse-state) (push (parse-method protocol factory-flag parse-state) (parse-state-method-list parse-state)) (setf (parse-state-scratch-example-list parse-state) nil) (setf (parse-state-item-doc-list parse-state) nil) t) (defun parse-typedef (module protocol parse-state) (forward-char 7) (skip-whitespace) (let ((type-beg (point)) (description-list (reverse (parse-state-item-doc-list parse-state)))) (if (looking-at ".*\\(union\\|struct\\)") (progn (search-forward "{") (backward-char) (forward-sexp) (let ((type-end (point))) (skip-whitespace) (let ((name-beg (point))) (search-forward ";") (backward-char 2) (skip-backward-to-nonwhite) (make-typedef :name (buffer-substring name-beg (1+ (point))) :module module :protocol protocol :type (buffer-substring type-beg type-end) :description-list description-list)))) (if (looking-at *funcptr-regexp*) (let* ((name (match-string 2)) (type (strip-regexp (match-string 1) name))) (make-typedef :name name :module module :protocol protocol :type type :description-list description-list)) (progn (search-forward ";") (backward-char 2) (skip-backward-to-nonwhite) (let ((name-end (1+ (point)))) (backward-sexp) (let ((name-beg (point))) (skip-whitespace-backward) (let ((type-end (point))) (make-typedef :name (buffer-substring name-beg name-end) :module module :protocol protocol :type (buffer-substring type-beg type-end) :description-list description-list))))))))) (defun handle-typedef (module protocol parse-state) (push (parse-typedef module protocol parse-state) (parse-state-typedef-list parse-state)) (setf (parse-state-item-doc-list parse-state) nil) t) (defun parse-macro (module protocol parse-state) (prog1 (let ((dl (parse-state-item-doc-list parse-state)) (name (parse-state-macro-name parse-state))) (search-forward "(") (let ((arguments (loop do (skip-whitespace) for start-pos = (point) do (re-search-forward "[),]") (backward-char 2) (skip-backward-to-nonwhite) for arg = (buffer-substring start-pos (1+ (point))) collect arg do (re-search-forward "[),]") (backward-char) until (looking-at ")") do (forward-char)))) (make-macro :name name :module module :protocol protocol :arguments arguments :description-list dl))) (while (looking-at ".*\\\\\\s-*$") (forward-line)))) (defun parse-define (module protocol parse-state) (prog1 (let ((dl (parse-state-item-doc-list parse-state)) (name (parse-state-macro-name parse-state))) (let ((macro (gethash name *macro-name-hash-table*))) (if macro (progn (message "duplicate #define: %s" name) nil) (setf (gethash name *macro-name-hash-table*) (make-macro :name name :module module :protocol protocol :arguments :no-arguments :description-list dl))))) (while (looking-at ".*\\\\\\s-*$") (forward-line)))) (defun handle-macro (module protocol parse-state) (push (parse-macro module protocol parse-state) (parse-state-macro-list parse-state)) (setf (parse-state-item-doc-list parse-state) nil) t) (defun handle-define (module protocol parse-state) (let ((macro (parse-define module protocol parse-state))) (when macro (push macro (parse-state-macro-list parse-state)))) (setf (parse-state-item-doc-list parse-state) nil) t) (defun handle-global (module protocol parse-state) (let ((names (parse-state-global-names parse-state))) (if names (progn (loop for name in (split-string names ",") for stripped-name = (strip-regexp name "\\s-+") do (push (make-global :name stripped-name :module module :protocol protocol :type (parse-state-global-type parse-state) :description-list (reverse (parse-state-item-doc-list parse-state))) (parse-state-global-list parse-state))) (setf (parse-state-global-names parse-state) nil)) (push (parse-global-using-parse-state module protocol parse-state) (parse-state-global-list parse-state))) (setf (parse-state-item-doc-list parse-state) nil)) t) (defun handle-protocol-tag (protocol parse-state) (let ((tag (parse-state-tag parse-state)) (module (protocol-module protocol))) (case tag ((:method :factory-method) (handle-method protocol (eq (parse-state-tag parse-state) :factory-method) parse-state)) (:global (handle-global module protocol parse-state)) (:macro (handle-macro module protocol parse-state)) (:define (handle-define module protocol parse-state)) (:typedef (handle-typedef module protocol parse-state)) (:function (handle-function module protocol parse-state)) (:protocol-end t) (otherwise nil)))) (defun handle-common-tag (module protocol parse-state) (let ((tag (parse-state-tag parse-state))) (case tag (:global (handle-global module protocol parse-state)) (:macro (handle-macro module protocol parse-state)) (:typedef (handle-typedef module protocol parse-state)) (:define (handle-define module protocol parse-state)) (:function (handle-function module protocol parse-state)) (otherwise nil)))) (defun same-tag-p (parse-state) (eq (parse-state-tag parse-state) (parse-state-last-tag parse-state))) (defun end-tag-p (parse-state) (eq (parse-state-tag parse-state) :protocol-end)) (defun process-header-file (module protocol) (let ((parse-state (make-parse-state))) (beginning-of-line 1) (while (and (zerop (forward-line 1)) (not (and protocol (end-tag-p parse-state)))) (beginning-of-line) (let ((tag (check-common-tags parse-state))) (unless tag (if protocol (progn (setq tag (check-protocol-tags parse-state)) (unless tag (error "Unrecognized text (protocol): [%s]" (line-text)))) (if (looking-at *protocol-regexp*) (progn (re-search-forward "^@end") (setq tag :skipped-protocol)) (error "Unrecognized text (non-protocol): [%s]" (line-text))))) (setf (parse-state-tag parse-state) tag)) (setf (parse-state-line parse-state) (line-text)) (let ((immediate-object (immediate-global-tag-processed module protocol parse-state))) (if immediate-object (push immediate-object (parse-state-global-list parse-state)) (progn (if (same-tag-p parse-state) (append-buf parse-state) (progn (if protocol (unless (protocol-tag-change parse-state) (common-tag-change parse-state)) (common-tag-change parse-state)) (when (is-doc-type parse-state) (set-buf parse-state))))))) (if protocol (unless (handle-protocol-tag protocol parse-state) (handle-common-tag module protocol parse-state)) (handle-common-tag module protocol parse-state)) (setf (parse-state-last-tag parse-state) (parse-state-tag parse-state))) parse-state)) (defun load-protocol (module) (interactive) (skip-whitespace) (let* ((protocol-name (let ((beg (point))) (skip-name) (buffer-substring beg (point)))) (included-protocol-list (parse-included-protocol-list)) (protocol (make-protocol :module module :name protocol-name :included-protocol-list included-protocol-list))) (let ((parse-state (process-header-file module protocol))) (setf (protocol-summary protocol) (parse-state-summary-doc parse-state) (protocol-description-list protocol) (reverse (parse-state-description-doc-list parse-state)) (protocol-macro-list protocol) (reverse (parse-state-macro-list parse-state)) (protocol-global-list protocol) (reverse (parse-state-global-list parse-state)) (protocol-method-list protocol) (reverse (parse-state-method-list parse-state)) (protocol-typedef-list protocol) (reverse (parse-state-typedef-list parse-state)) (protocol-example-list protocol) (reverse (parse-state-example-list parse-state)))) protocol)) (defun load-protocols (module) (interactive) (goto-char (point-min)) (loop while (find-protocol) collect (load-protocol module))) (defun load-module (module-sym) (goto-char (point-min)) (let* ((module (make-module :sym module-sym)) (parse-state (process-header-file module nil))) (setf (module-summary module) (parse-state-summary-doc parse-state)) (setf (module-description-list module) (reverse (parse-state-description-doc-list parse-state))) (setf (module-example-list module) (reverse (parse-state-example-list parse-state))) (setf (module-function-list module) (reverse (parse-state-function-list parse-state))) (setf (module-global-list module) (reverse (parse-state-global-list parse-state))) (setf (module-macro-list module) (reverse (parse-state-macro-list parse-state))) (setf (module-typedef-list module) (reverse (parse-state-typedef-list parse-state))) module)) (defun create-included-protocol-list (protocol) (loop for included-protocol-name in (protocol-included-protocol-list protocol) for included-protocol = (lookup-protocol included-protocol-name) unless included-protocol do (error "Could not find protocol %s" included-protocol-name) collect included-protocol)) (defun lookup-module (module-sym) (car (remove-if-not #'module-p (gethash module-sym *module-hash-table*)))) (defun lookup-protocol (name) (gethash name *protocol-hash-table*)) (defun CREATABLE-protocol () (let ((description "Declare that a defined type supports creation.")) (make-protocol :name "CREATABLE" :module (lookup-module 'defobj) :included-protocol-list nil :summary description :description-list (list description) :method-list nil))) (defun add-protocol (module-sym protocol) (setf (gethash (protocol-name protocol) *protocol-hash-table*) protocol) (push protocol (gethash module-sym *module-hash-table*))) (defun module-sym-from-spec (module-spec) (if (consp module-spec) (car module-spec) module-spec)) (defun ensure-module (module-sym) (let ((module (lookup-module module-sym))) (if module module (progn (setq module (load-module module-sym)) (push module (gethash module-sym *module-hash-table*)))) module)) (defun load-all-modules () (interactive) (let ((old-push-mark (symbol-function 'push-mark))) (when noninteractive (setf (symbol-function 'push-mark) #'(lambda () (funcall old-push-mark nil t)))) (clrhash *protocol-hash-table*) (clrhash *module-hash-table*) (loop for module-spec in *swarm-modules* for module-sym = (module-sym-from-spec module-spec) do (if (consp module-spec) (find-file-read-only (pathname-for-module-sym module-sym (cdr module-spec))) (find-file-read-only (pathname-for-module-sym module-sym))) (let ((module (ensure-module module-sym))) (loop for protocol in (load-protocols module) for name = (protocol-name protocol) for exist = (gethash name *protocol-hash-table*) when exist do (error "Protocol %s already exists" name) do (add-protocol module-sym protocol))) (kill-buffer (current-buffer))) (add-protocol 'defobj (CREATABLE-protocol)) (when noninteractive (setf (symbol-function 'push-mark) old-push-mark)) (loop for protocol being each hash-value of *protocol-hash-table* do (setf (protocol-included-protocol-list protocol) (create-included-protocol-list protocol))))) (defun compare-string-lists (a b) (let ((diff (loop for a-arg in a for b-arg in b if (string< a-arg b-arg) return -1 else if (not (string= a-arg b-arg)) return 1 finally return 0))) (if (zerop diff) (< (length a) (length b)) diff))) (defun generate-expanded-methodinfo-list (protocol) (let ((expanded-protocols-hash-table (make-hash-table)) (method-hash-table (make-hash-table))) (flet ((expand-protocol-level (protocol level) (setf (gethash protocol expanded-protocols-hash-table) t) (loop for method in (protocol-method-list protocol) do (setf (gethash method method-hash-table) (cons level protocol))) (loop for included-protocol in (protocol-included-protocol-list protocol) do (unless (gethash included-protocol expanded-protocols-hash-table) (expand-protocol-level included-protocol (1+ level)))))) (expand-protocol-level protocol 0)) (sort (loop for method being each hash-key of method-hash-table using (hash-value level.protocol) collect (list (car level.protocol) (cdr level.protocol) method)) #'(lambda (a b) (flet ((phase-pos (phase) (case phase (:creating 0) (:setting 1) (:using 2))) (compare-arguments (a b) (flet ((get-key-list (item) (mapcar #'first item))) (compare-string-lists (get-key-list a) (get-key-list b))))) (let ((level-diff (- (first a) (first b)))) (if (zerop level-diff) (let* ((method-a (third a)) (method-b (third b)) (phase-diff (- (phase-pos (method-phase method-a)) (phase-pos (method-phase method-b))))) (if (zerop phase-diff) (compare-arguments (method-arguments method-a) (method-arguments method-b)) (< phase-diff 0))) (< level-diff 0)))))))) (defun generate-expanded-methodinfo-lists () (interactive) (loop for protocol being each hash-value of *protocol-hash-table* do (setf (protocol-expanded-methodinfo-list protocol) (generate-expanded-methodinfo-list protocol)))) (defun external-protocol-name (protocol) (let ((raw-protocol-name (protocol-name protocol))) (if (internal-protocol-p protocol) (substring raw-protocol-name 1) raw-protocol-name))) (defun get-method-signature (method) (with-output-to-string (print-method-signature method))) (defun protocol-index (protocol) (position protocol *protocol-list*)) (defun method-signature-index (method-signature) (position method-signature *method-signature-list* :test #'string=)) (defun module-name (module) (symbol-name (module-sym module))) (defun sgml-object-id (type module protocol &optional name) (cook-id (let* ((type-str (upcase (symbol-name type))) (base-id (if protocol (let* ((cooked-protocol-name (external-protocol-name protocol))) (concat "SWARM." (upcase (module-name (protocol-module protocol))) "." (upcase cooked-protocol-name) "." type-str)) (concat "SWARM." (upcase (module-name module)) ".GENERIC." type-str)))) (if name (concat base-id "." (upcase name)) base-id)))) (defun sgml-protocol-id (protocol) (sgml-object-id 'protocol (protocol-module protocol) protocol)) (defun sgml-method-signature-id (protocol phase method-signature) (sgml-object-id 'method (protocol-module protocol) protocol (format "P%s.M%d" (case phase (:creating "C") (:setting "S") (:using "U") (otherwise (error "bad phase"))) (method-signature-index method-signature)))) (defun sgml-module-id (module) (sgml-object-id 'module module nil)) (defun object-type (object) (cond ((protocol-p object) 'protocol) ((module-p object) 'module) ((global-p object) 'global) ((function-p object) 'function) ((macro-p object) 'macro) ((typedef-p object) 'typedef) (t (error "unknown object type")))) (defun generic-module (object) (let ((type (object-type object))) (case type (protocol (protocol-module object)) (module object) (function (function-module object)) (global (global-module object)) (macro (macro-module object)) (typedef (typedef-module object)) (otherwise (error "unknown type: %s" type))))) (defun generic-summary (object) (cond ((protocol-p object) (protocol-summary object)) ((module-p object) (module-summary object)) (t (error "unknown object")))) (defun generic-description-list (object) (reverse (cond ((protocol-p object) (protocol-description-list object)) ((module-p object) (module-description-list object)) (t (error "unknown object"))))) (defun generic-protocol (object) (case (object-type object) (function (function-protocol object)) (global (global-protocol object)) (macro (macro-protocol object)) (typedef (typedef-protocol object)) (otherwise (error "unknown type")))) (defun generic-name (object) (case (object-type object) (protocol (protocol-name object)) (module (module-name object)) (function (function-name object)) (global (global-name object)) (macro (macro-name object)) (typedef (typedef-name object)) (otherwise (error "unknown type")))) (defun generic-macro-list (object) (case (object-type object) (protocol (protocol-macro-list object)) (module (module-macro-list object)))) (defun generic-typedef-list (object) (case (object-type object) (protocol (protocol-typedef-list object)) (module (module-typedef-list object)))) (defun generic-function-list (object) (case (object-type object) (protocol (protocol-function-list object)) (module (module-function-list object)))) (defun generic-global-list (object) (case (object-type object) (protocol (protocol-global-list object)) (module (module-global-list object)))) (defun sgml-id (object) (sgml-object-id (object-type object) (generic-module object) (generic-protocol object) (generic-name object))) (defun sgml-refentry-start (obj) (insert "\n")) (defun sgml-refmeta (object) (let (title module-name) (cond ((protocol-p object) (setq title (protocol-name object)) (setq module-name (module-name (protocol-module object)))) ((module-p object) (setq title "General") (setq module-name (module-name object))) (t (error "unknown object"))) (insert "\n") (insert "") (insert title) (insert "\n") (insert "") (insert module-name) (insert "\n") (insert "\n"))) (defun sgml-namediv (object) (insert "\n") (insert "") (insert (generic-name object)) (insert "\n") (insert "\n") (insert (generic-summary object)) (insert "\n\n") (insert "\n")) (defun sgml-refsect1-text-list (title text-list) (when text-list (insert "\n") (insert "") (insert-text title) (insert "\n") (loop for text in text-list do (insert "\n") (insert-text text) (insert "\n\n")) (insert "\n"))) (defun sgml-refsect1-description (object) (sgml-refsect1-text-list "Description" (generic-description-list object))) (defun sgml-funcsynopsisinfo (class-name description-list) (insert "\n") (insert "") (insert class-name) (insert "\n") (loop for description in description-list do (insert-text description) (insert "\n")) (insert "\n")) (defun print-method-signature (method &optional stream) (if (method-factory-flag method) (princ "+" stream) (princ "-" stream)) (loop for arguments in (method-arguments method) for key = (first arguments) when key do (princ key stream) when (third arguments) do (princ ":" stream))) (defun sgml-method-funcsynopsis (owner-protocol method) (insert "\n") (insert "\n") (insert "") (let ((return-type (method-return-type method))) (when return-type (insert-text return-type))) (insert "") (print-method-signature method (current-buffer)) (insert "") (insert "\n") (let ((arguments (method-arguments method))) (if (and (eql (length arguments) 1) (null (third (first arguments)))) (insert "\n") (loop for arg in arguments for type = (second arg) do (insert "") (when type (insert-text type)) (insert "") (insert-text (third arg)) (insert "") (insert "\n")))) (insert "\n") (sgml-funcsynopsisinfo (protocol-name owner-protocol) (method-description-list method)) (insert "\n")) (defun sgml-link-to-protocol (protocol) (insert "") (insert (external-protocol-name protocol)) (insert "")) (defun methodinfo-list-for-phase (protocol phase) (loop for methodinfo in (protocol-expanded-methodinfo-list protocol) when (eq (method-phase (third methodinfo)) phase) collect methodinfo)) (defun include-p (level protocol owner-protocol) (or (zerop level) (let ((owner-protocol-name (protocol-name owner-protocol))) (when (internal-protocol-p owner-protocol) (string= (substring (protocol-name owner-protocol) 1) (protocol-name protocol)))))) (defun count-included-methodinfo-entries (protocol phase) (loop for methodinfo in (methodinfo-list-for-phase protocol phase) count (include-p (first methodinfo) protocol (second methodinfo)))) (defun count-included-methodinfo-entries-for-all-phases (protocol) (loop for phase in *phases* sum (count-included-methodinfo-entries protocol phase))) (defun sgml-method-definitions (protocol phase &optional protocol-listitem-flag) (unless (zerop (count-included-methodinfo-entries protocol phase)) (let ((methodinfo-list (methodinfo-list-for-phase protocol phase)) have-list have-item) (when protocol-listitem-flag (insert "\n")) (loop with last-protocol = nil for methodinfo in methodinfo-list for level = (first methodinfo) for owner-protocol = (second methodinfo) for method = (third methodinfo) for new-group-flag = (not (eq owner-protocol last-protocol)) when new-group-flag do (when have-list (insert "\n") (setq have-list nil)) (when have-item (insert "\n") (setq have-item nil)) (when protocol-listitem-flag (when (include-p level protocol owner-protocol) (insert "\n") (setq have-item t) (insert "") (insert (external-protocol-name owner-protocol)) (insert "\n"))) (when (include-p level protocol owner-protocol) (setq have-list t) (insert "\n")) do (when (include-p level protocol owner-protocol) (insert "\n") (sgml-method-funcsynopsis owner-protocol method) (sgml-method-examples owner-protocol method) (insert "\n")) for last-protocol = owner-protocol) (when have-list (insert "\n")) (when protocol-listitem-flag (when have-item (insert "\n")) (insert "\n"))))) (defun sgml-macro (macro) (if (eq :no-arguments (macro-arguments macro)) (progn (insert "\n") (insert-text (macro-name macro)) (insert "\n\n") (loop for text in (macro-description-list macro) do (insert "\n") (insert-text text) (insert "\n"))) (progn (insert "\n") (insert "\n") (insert "") (insert "") (insert-text (macro-name macro)) (insert "") (insert "\n") (loop for arg in (macro-arguments macro) do (when arg (insert "") (insert "") (insert arg) (insert "") (insert "\n"))) (insert "\n") (sgml-funcsynopsisinfo "(MACRO)" (macro-description-list macro)) (insert "\n")))) (defun sgml-function (function) (insert "\n") (insert "\n") (insert "") (insert-text (function-return-type function)) (insert "") (insert-text (function-name function)) (insert "") (insert "\n") (loop for type.name in (function-arguments function) do (insert "") (insert-text (car type.name)) (insert "") (let ((name (cdr type.name))) (when name (insert-text name))) (insert "") (insert "\n")) (insert "\n") (sgml-funcsynopsisinfo "(FUNCTION)" (function-description-list function)) (insert "\n")) (defun sgml-typedef (typedef) (insert "\n") (insert (typedef-name typedef)) (insert "\n\n") (insert (typedef-type typedef)) (insert "\n") (insert "\n")) (defun name< (a b) (string< (generic-name a) (generic-name b))) (defun sgml-refsect1-object-list (title object-list print-object-func) (when object-list (insert "\n") (insert "") (insert-text title) (insert "\n") (insert "\n") (loop for object in (sort object-list #'name<) do (insert "\n") (funcall print-object-func object) (insert "\n")) (insert "\n") (insert "\n"))) (defun sgml-refsect1-macro-list (object) (sgml-refsect1-object-list "Macros" (generic-macro-list object) #'sgml-macro)) (defun sgml-refsect1-typedef-list (object) (sgml-refsect1-object-list "Typedefs" (generic-typedef-list object) #'sgml-typedef)) (defun sgml-refsect1-function-list (object) (sgml-refsect1-object-list "Functions" (generic-function-list object) #'sgml-function)) (defun sgml-refsect1-global-list (object) (let ((global-list (generic-global-list object))) (when global-list (insert "\n") (insert "") (insert "Globals") (insert "\n") (insert "\n") (loop for global in global-list do (insert "\n") (insert "") (insert-text (global-type global)) (insert "\n") (insert "") (insert-text (global-name global)) (insert "\n") (let ((description-list (global-description-list global))) (if description-list (progn (insert "\n") (loop for text in description-list do (insert "\n") (insert-text text) (insert "\n")) (insert "\n")) (insert "No description available.\n"))) (insert "\n")) (insert "\n") (insert "\n")))) (defun sgml-examples (object) (let ((example-list (protocol-example-list object))) (when example-list (insert "") (insert "\n") (insert "\n") (loop for example in example-list do (insert "\n\n\n")) (insert "\n")))) (defun count-method-examples (protocol phase) (loop for methodinfo in (methodinfo-list-for-phase protocol phase) for method = (third methodinfo) count (method-example-list method))) (defun count-noninternal-protocols (protocol) (loop for included-protocol in (protocol-included-protocol-list protocol) count (not (internal-protocol-p included-protocol)))) (defun compare-method-signatures (method-a method-b) (let* ((method-a-signature (get-method-signature method-a)) (method-b-signature (get-method-signature method-b))) (string< method-a-signature method-b-signature))) (defun compare-methodinfo (a b) (let ((protocol-name-a (protocol-name (second a))) (protocol-name-b (protocol-name (second b)))) (if (string= protocol-name-a protocol-name-b) (compare-method-signatures (third a) (third b)) (string< protocol-name-a protocol-name-b)))) (defun sgml-method-examples (protocol method) (when (method-example-list method) (insert "") (insert "") (insert "\n") (insert "\n") (loop for example in (method-example-list method) do (insert example) (insert "\n")) (insert "\n") (insert "\n"))) (defun sgml-methods-for-phase (protocol phase) (unless (zerop (count-included-methodinfo-entries protocol phase)) (insert "\n") (insert "Phase: ") (insert (capitalize (substring (prin1-to-string phase) 1))) (insert "\n") (sgml-method-definitions protocol phase) (insert "\n"))) (defun sgml-refsect1-protocol-list (protocol &optional expand-flag) (insert "\n") (insert "Protocols adopted by ") (insert (protocol-name protocol)) (insert "\n") (if (zerop (count-noninternal-protocols protocol)) (insert "None\n") (flet ((print-expanded-protocol-list (protocol) (insert "\n") (loop for included-protocol in (protocol-included-protocol-list protocol) do (unless (internal-protocol-p protocol) (insert "\n") (insert "") (sgml-link-to-protocol included-protocol) (insert "\n") (print-expanded-protocol-list included-protocol) (insert "\n"))) (insert "\n")) (print-unexpanded-protocol-list (protocol) (insert "") (loop for included-protocol in (protocol-included-protocol-list protocol) do (unless (internal-protocol-p protocol) (insert " ") (sgml-link-to-protocol included-protocol))) (insert "\n"))) (if expand-flag (print-expanded-protocol-list protocol) (print-unexpanded-protocol-list protocol)))) (insert "\n")) (defun sgml-refsect1-method-list (protocol) (insert "Methods\n") (if (zerop (count-included-methodinfo-entries-for-all-phases protocol)) (insert "None\n") (loop for phase in *phases* do (sgml-methods-for-phase protocol phase))) (insert "\n")) (defun sgml-refsect1-examples (protocol) (when (protocol-example-list protocol) (insert "Examples\n") (sgml-examples protocol) (insert "\n"))) (defun internal-protocol-p (protocol) (string= (substring (protocol-name protocol) 0 1) "_")) (defun generate-refentry (object) (unless (and (protocol-p object) (internal-protocol-p object)) (sgml-refentry-start object) (sgml-refmeta object) (sgml-namediv object) (sgml-refsect1-description object) (when (protocol-p object) (sgml-refsect1-protocol-list object) (sgml-refsect1-method-list object)) (sgml-refsect1-macro-list object) (sgml-refsect1-function-list object) (sgml-refsect1-typedef-list object) (sgml-refsect1-global-list object) (when (protocol-p object) (sgml-refsect1-examples object)) (insert "\n"))) (defun sgml-generate-refentries-for-module (module-sym) (loop for object in (sort (gethash module-sym *module-hash-table*) #'name<) do (generate-refentry object))) (defun sgml-create-refentries-for-module (module-sym) (let ((module-name (symbol-name module-sym))) (with-temp-file (pathname-for-swarmdocs-pages-output module-sym) (sgml-generate-refentries-for-module module-sym)))) (defun sgml-create-refentries-for-all-modules () (interactive) (loop for module-sym being each hash-key of *module-hash-table* do (sgml-create-refentries-for-module module-sym))) (defun build-method-signature-hash-table () (loop for protocol being each hash-value of *protocol-hash-table* do (loop for method in (protocol-method-list protocol) do (push (cons protocol method) (gethash (get-method-signature method) *method-signature-hash-table*))))) (defun build-protocol-vector () (setq *protocol-list* (sort (loop for protocol being each hash-value of *protocol-hash-table* collect protocol) #'name<))) (defun build-method-signature-vector () (setq *method-signature-list* (sort (loop for method-signature being each hash-key of *method-signature-hash-table* collect method-signature) #'string<))) (defun sgml-protocol-indexentry (protocol) (insert "\n") (insert "") (insert (external-protocol-name protocol)) (insert "\n") (insert "\n")) (defun sgml-generate-protocol-index () (insert "\n") (insert "Protocol Index\n") (loop for protocol in *protocol-list* unless (internal-protocol-p protocol) do (sgml-protocol-indexentry protocol)) (insert "\n")) (defun sgml-method-signature-indexentry (method-signature) (insert "\n") (insert "") (insert method-signature) (insert "\n") (insert "\n")) (defun sgml-generate-method-signature-index () (insert "\n") (insert "Method Index\n") (loop for method-signature in *method-signature-list* do (sgml-method-signature-indexentry method-signature)) (insert "\n")) (defun collect-objects-of-type (type) (let ((object-accessor (case type (function #'generic-function-list) (global #'generic-global-list) (macro #'generic-macro-list) (typedef #'generic-typedef-list) (otherwise (error "unknown type"))))) (loop for module-sym being each hash-key of *module-hash-table* append (loop for object in (gethash module-sym *module-hash-table*) append (funcall object-accessor object))))) (defun sgml-indexentry (object) (insert "\n") (insert "") (insert (generic-name object)) (insert "\n") (insert "\n")) (defun sgml-generate-index-of-type (type) (insert "\n") (insert "") (insert (capitalize (symbol-name type))) (insert " Index") (insert "\n") (loop for object in (sort (collect-objects-of-type type) #'name<) do (sgml-indexentry object)) (insert "\n")) (defun sgml-generate-indices () (with-temp-file (concat (get-swarmdocs-build-area) "refbook/refindex.sgml") (sgml-generate-protocol-index) (sgml-generate-method-signature-index) (loop for type in '(function global macro typedef) do (sgml-generate-index-of-type type)))) (defun load-and-process-modules () (interactive) (load-all-modules) (generate-expanded-methodinfo-lists) (build-method-signature-hash-table) (build-protocol-vector) (build-method-signature-vector)) (defun run-all () (interactive) (load-and-process-modules) (sgml-create-refentries-for-all-modules) (sgml-generate-indices) nil) (defun vcg-graph-all-protocols () (loop for module-sym being each hash-key of *module-hash-table* using (hash-values module-items) for module-name = (concat "M:" (symbol-name module-sym)) do (insert "graph: {\nfolding: 1 color: blue title: \"") (insert (capitalize module-name)) (insert "\"\n") (insert "node: { title: \"") (insert module-name) (insert "\" }\n") (loop for module-item in module-items do (cond ((and (protocol-p module-item) (not (internal-protocol-p module-item))) (let ((protocol-name (protocol-name module-item))) (insert "node: { title: \"") (insert protocol-name) (insert "\" shape: ellipse color: red }\n") (insert "edge: { targetname: \"") (insert protocol-name) (insert "\" sourcename: \"") (insert module-name) (insert "\" }\n")) (loop for included-protocol in (protocol-included-protocol-list module-item) unless (internal-protocol-p included-protocol) do (insert "backedge: { targetname: \"") (insert (protocol-name module-item)) (insert "\" sourcename: \"") (insert (protocol-name included-protocol)) (insert "\" }\n"))))) (insert "}\n"))) (defun vcg-output-protocols-graph () (interactive) (with-temp-file (concat (get-swarmdocs-build-area) "protocols.vcg") (insert "graph: {\n") (insert "orientation: left_to_right\n") (vcg-graph-all-protocols) (insert "}\n"))) (defun dot-graph-all-protocols () (loop for module-sym being each hash-key of *module-hash-table* using (hash-values module-items) for module-name = (symbol-name module-sym) do (loop for module-item in module-items do (cond ((and (protocol-p module-item) (not (internal-protocol-p module-item))) (loop for included-protocol in (protocol-included-protocol-list module-item) unless (internal-protocol-p included-protocol) do (insert "\"") (insert (protocol-name module-item)) (insert "\" -> \"") (insert (protocol-name included-protocol)) (insert "\"\n"))))) (insert "subgraph cluster_") (insert module-name) (insert " { label=\"") (insert (capitalize module-name)) (insert "\"\n") (loop for module-item in module-items do (cond ((and (protocol-p module-item) (not (internal-protocol-p module-item))) (insert "\"") (insert (protocol-name module-item)) (insert "\"; ")))) (insert "}\n"))) (defun dot-output-protocols-graph () (interactive) (with-temp-file (concat (get-swarmdocs-build-area) "protocols.dot") (insert "digraph \"Swarm Protocols\" {\n"); (insert "page=\"10,7.5\"\n") (insert "ratio=auto\n") (dot-graph-all-protocols) (insert "}\n"))) (defun dot-graph-module (edge-hash-table module-sym &optional module-items) (unless module-items (setq module-items (gethash module-sym *module-hash-table*))) (let ((module-name (symbol-name module-sym)) (included-module-hash-table (make-hash-table)) local-protocols) (loop for module-item in module-items do (cond ((and (protocol-p module-item) (not (internal-protocol-p module-item))) (loop for included-protocol in (protocol-included-protocol-list module-item) for included-module-sym = (module-sym (protocol-module included-protocol)) unless (internal-protocol-p included-protocol) do (let ((edge-key (cons module-item included-protocol))) (unless (gethash edge-key edge-hash-table) (insert "\"") (insert (protocol-name module-item)) (insert "\" -> \"") (insert (protocol-name included-protocol)) (insert "\"\n") (setf (gethash edge-key edge-hash-table) t))) (if (eq included-module-sym module-sym) (push included-protocol local-protocols) (push included-protocol (gethash included-module-sym included-module-hash-table)) ))))) (insert "subgraph cluster_") (insert module-name) (insert " { label=\"") (insert (capitalize module-name)) (insert "\"\n") (loop for module-item in (append module-items local-protocols) do (cond ((and (protocol-p module-item) (not (internal-protocol-p module-item))) (insert "\"") (insert (protocol-name module-item)) (insert "\"; ")))) (insert "}\n") (loop for included-module-sym being each hash-key of included-module-hash-table do (dot-graph-module edge-hash-table included-module-sym (gethash included-module-sym included-module-hash-table))))) (defun dot-output-module-graph (module-sym) (let ((edge-hash-table (make-hash-table :test #'equal))) (with-temp-file (concat (get-swarmdocs-build-area) (symbol-name module-sym) ".dot") (insert "digraph ") (insert (capitalize (symbol-name module-sym))) (insert " {\n") (insert "size=\"10,7.5\"\n") (insert "ratio=compress\n") (insert "rotate=90\n") (dot-graph-module edge-hash-table module-sym) (insert "}\n")))) (defun dot-output-each-module-graph () (interactive) (loop for module-sym being each hash-key of *module-hash-table* do (dot-output-module-graph module-sym)))