topological-sort.scm 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2019 Ludovic Courtès <ludo@gnu.org>
  3. ;;; Copyright © 2022 Maxime Devos <maximedevos@telenet.be>
  4. ;;;
  5. ;;; This file is part of GNU Guix.
  6. ;;;
  7. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  8. ;;; under the terms of the GNU General Public License as published by
  9. ;;; the Free Software Foundation; either version 3 of the License, or (at
  10. ;;; your option) any later version.
  11. ;;;
  12. ;;; GNU Guix is distributed in the hope that it will be useful, but
  13. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  14. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. ;;; GNU General Public License for more details.
  16. ;;;
  17. ;;; You should have received a copy of the GNU General Public License
  18. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  19. ;; To be used by the implementation of workspaces.
  20. ;; Extracted from (guix import utils), and changed from (guix sets)
  21. ;; to a guile-pfds equivalent.
  22. (define-module (topological-sort)
  23. #:export (topological-sort topological-sort*)
  24. #:use-module (srfi srfi-1)
  25. #:use-module ((srfi srfi-69) #:select (hash))
  26. #:use-module ((ice-9 match) #:select (match))
  27. ;; XXX: Cuirass compiles even build-side only modules.
  28. #:autoload (pfds hamts) (make-hamt hamt-ref hamt-set))
  29. (define (topological-sort nodes
  30. node-dependencies
  31. node-name)
  32. "Perform a breadth-first traversal of the graph rooted at NODES, a list of
  33. nodes, and return the list of nodes sorted in topological order. Call
  34. NODE-DEPENDENCIES to obtain the dependencies of a node, and NODE-NAME to
  35. obtain a node's uniquely identifying \"key\"."
  36. (define (node=? x y)
  37. (equal? (node-name x) (node-name y)))
  38. (define tag (make-prompt-tag 'topological-sort))
  39. (define (maybe-visit node recurse)
  40. (abort-to-prompt tag node recurse))
  41. (define (done)
  42. (abort-to-prompt tag))
  43. (define (visit-recursively node)
  44. (maybe-visit
  45. node
  46. (lambda ()
  47. (for-each visit-recursively (node-dependencies node)))))
  48. (define (initial-thunk)
  49. (for-each visit-recursively nodes)
  50. (done))
  51. (let loop ((continue initial-thunk)
  52. (visiting '()) ; call stack, for detecting cycles
  53. (visited '())) ; set represented as a list
  54. (call-with-prompt
  55. tag continue
  56. (case-lambda
  57. ((k) visited) ; done
  58. ((k node recurse)
  59. (cond ((member node visiting node=?)
  60. (pk (cons node visiting))
  61. (error "cycle detected"))
  62. ((member node visited node=?)
  63. ;; Nothing to do
  64. (loop k visiting visited))
  65. (#true
  66. ;; Push it on the stack, recurse,
  67. ;; then continue (without having it on the stack).
  68. (loop k
  69. visiting
  70. (cons node
  71. (loop (lambda ()
  72. (recurse)
  73. (done))
  74. (cons node visiting)
  75. visited))))))))))
  76. (define (topological-sort* nodes node-dependencies node-name)
  77. "Like TOPOLOGICAL-SORT, but don't assume that NODES are roots. Instead,
  78. consider all nodes in the closure of NODES."
  79. (define artificial-root (make-symbol "root")) ; uninterned, fresh symbol
  80. (define nodes* (list artificial-root))
  81. (define (node-dependencies* node*)
  82. (if (eq? node* artificial-root)
  83. nodes
  84. (node-dependencies node*)))
  85. (define (node-name* node*)
  86. (if (eq? node* artificial-root)
  87. artificial-root
  88. (node-name node*)))
  89. (define (proper-node? node*)
  90. (not (eq? node* artificial-root)))
  91. (filter proper-node?
  92. (topological-sort nodes* node-dependencies* node-name*)))