1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <map>
6 
7 #include "banjo/raw_ast.h"
8 #include "banjo/tree_visitor.h"
9 
10 namespace banjo {
11 namespace raw {
12 
OnFile(std::unique_ptr<File> const & element)13 void DeclarationOrderTreeVisitor::OnFile(std::unique_ptr<File> const& element) {
14     OnSourceElementStart(*element);
15 
16     OnCompoundIdentifier(element->library_name);
17     for (auto i = element->using_list.begin();
18          i != element->using_list.end();
19          ++i) {
20         OnUsing(*i);
21     }
22 
23     auto const_decls_it = element->const_declaration_list.begin();
24     auto enum_decls_it = element->enum_declaration_list.begin();
25     auto interface_decls_it = element->interface_declaration_list.begin();
26     auto struct_decls_it = element->struct_declaration_list.begin();
27     auto union_decls_it = element->union_declaration_list.begin();
28 
29     enum Next {
30         const_t,
31         enum_t,
32         interface_t,
33         struct_t,
34         union_t
35     };
36 
37     std::map<const char*, Next> m;
38     do {
39         // We want to visit these in declaration order, rather than grouped
40         // by type of declaration.  std::map is sorted by key.  For each of
41         // these lists of declarations, we make a map where the key is "the
42         // next start location of the earliest element in the list" to a
43         // variable representing the type.  We then identify which type was
44         // put earliest in the map.  That will be the earliest declaration
45         // in the file.  We then visit the declaration accordingly.
46         m.clear();
47         if (const_decls_it != element->const_declaration_list.end()) {
48             m[(*const_decls_it)->start_.previous_end().data().data()] = const_t;
49         }
50         if (enum_decls_it != element->enum_declaration_list.end()) {
51             m[(*enum_decls_it)->start_.previous_end().data().data()] = enum_t;
52         }
53         if (interface_decls_it != element->interface_declaration_list.end()) {
54             if (*interface_decls_it == nullptr) {
55                 // Used to indicate empty, so let's wind it forward.
56                 interface_decls_it = element->interface_declaration_list.end();
57             } else {
58                 m[(*interface_decls_it)->start_.previous_end().data().data()] = interface_t;
59             }
60         }
61         if (struct_decls_it != element->struct_declaration_list.end()) {
62             m[(*struct_decls_it)->start_.previous_end().data().data()] = struct_t;
63         }
64         if (union_decls_it != element->union_declaration_list.end()) {
65             m[(*union_decls_it)->start_.previous_end().data().data()] = union_t;
66         }
67         if (m.size() == 0)
68             break;
69 
70         // And the earliest top level declaration is...
71         switch (m.begin()->second) {
72         case const_t:
73             OnConstDeclaration(*const_decls_it);
74             ++const_decls_it;
75             break;
76         case enum_t:
77             OnEnumDeclaration(*enum_decls_it);
78             ++enum_decls_it;
79             break;
80         case interface_t:
81             OnInterfaceDeclaration(*interface_decls_it);
82             ++interface_decls_it;
83             break;
84         case struct_t:
85             OnStructDeclaration(*struct_decls_it);
86             ++struct_decls_it;
87             break;
88         case union_t:
89             OnUnionDeclaration(*union_decls_it);
90             ++union_decls_it;
91             break;
92         }
93     } while (1);
94     OnSourceElementEnd(*element);
95 }
96 
97 } // namespace raw
98 } // namespace banjo
99