Replaced Array::new with [ ]
[kakapo:kakapo.git] / src / DependencyQueue.nqp
1 # Copyright (C) 2009-2010, Austin Hastings. See accompanying LICENSE file, or
2 # http://www.opensource.org/licenses/artistic-license-2.0.php for license.
3
4 class Exception::DependencyQueue::AlreadyDone
5         is Exception::Wrapper;
6
7 # A queue that orders its entries according to their prerequisites.
8 module DependencyQueue;
9
10 has %!added;
11 has %!already_done;
12 has %!cycle;
13 has @!cycle_keys;
14 has $!locked;
15 has %!pending;
16 has @!queue;
17
18 sub _pre_initload() {
19
20         has(    '%!added',
21                 '%!already_done',
22                 '%!cycle',
23                 '@!cycle_keys',
24                 '$!locked',
25                 '%!pending',
26                 '@!queue'
27         );
28 }
29
30 our method add_entry($name, $value, :@requires?) {
31         if self.already_done.contains($name) {
32                 Exception::DependencyQueue::AlreadyDone.new(
33                         :message("Added already-done $name to DependencyQueue")
34                 ).throw;
35         }
36
37         if @requires.isa('String') { @requires := [ @requires ]; }
38         my @entry := [ $name, $value, @requires ];
39         self.pending{$name} := @entry;
40 }
41
42 my method already_added($name) {
43         return self.already_done.contains($name)
44                 || self.added.contains($name);
45 }
46
47 method _init_obj(*@pos, *%named) {
48         self.locked(0);
49
50         while @pos {
51                 self.mark_as_done: @pos.shift;
52         }
53
54         self._init_args(|@pos, |%named);
55 }
56
57 our method is_empty() {
58         return self.locked
59                 ?? self.queue.elems == 0
60                 !! self.pending.elems == 0;
61 }
62
63 our method mark_as_done($label) {
64         self.already_done{$label} := 1;
65 }
66
67 our method next_entry() {
68         unless self.locked {
69                 self.tsort_queue();
70         }
71
72         if self.queue.elems {
73                 my $node := self.queue.shift;
74                 self.mark_as_done($node[0]);
75                 $node[1];
76         }
77         else {
78                 Undef.new;
79         }
80 }
81
82 our method reset() {
83         self.locked(0);
84         self.pending(Hash.new);
85 }
86
87 method tsort_queue() {
88         self.locked(1);
89         self.cycle_keys( [] );
90         self.cycle(Hash.new());
91         self.added(Hash.new());
92         self.tsort_add_pending_entries(self.pending.keys);
93 }
94
95 my method tsort_add_pending_entries(@list) {
96 # Visits a list of keys, adding the attached calls to the queue in topological order.
97
98         for @list {
99                 my $key := $_;
100
101                 if self.already_added($key) {
102                         next;
103                 }
104
105                 ## Check for cycles in the graph.
106
107                 my $next_index := self.cycle_keys.elems;
108                 self.cycle_keys.push($key);
109
110                 if self.cycle.contains($key) {
111                         my @slice := self.cycle_keys.slice(:from(self.cycle{$key}));
112
113                         die("Cycle detected in dependencies: ", @slice.join(', '));
114                 }
115
116                 self.cycle{$key} := $next_index;
117
118                 ## Put everything $key depends on ahead of $key
119
120                 if self.pending.contains($key) {
121                         my $node := self.pending{$key};
122                         my @prerequisites := $node[2];
123
124                         if +@prerequisites {
125                                 self.tsort_add_pending_entries(@prerequisites);
126                         }
127
128                         ## Finally, it's my turn.
129                         self.added{$key} := 1;
130                         self.queue.push($node);
131                 }
132                 else {
133                         die("$key is a requirement, but is not marked done, and not in the pending queue.");
134                 }
135         }
136 }