1 // Copyright 2009 The Archiveopteryx Developers <info@aox.org>
2 
3 #include "headerfile.h"
4 
5 #include "list.h"
6 #include "enum.h"
7 #include "class.h"
8 #include "error.h"
9 #include "parser.h"
10 #include "function.h"
11 
12 
13 static List<HeaderFile> * headers = 0;
14 
15 
16 /*! \class HeaderFile headerfile.h
17     The HeaderFile class models a header file.
18 
19     The HeaderFile file is viewed as a collection of class { ... }
20     statements, each of which is scanned for member functions and
21     superclass names. Other content is ignored (for now - enums may
22     one day be handled).
23 */
24 
25 
26 
27 /*! Constructs a HeaderFile for \a file, which is presumed to be in the
28     current directory.
29 
30     The file is parsed immediately.
31 */
32 
HeaderFile(const EString & file)33 HeaderFile::HeaderFile( const EString & file )
34     : File( file, Read )
35 {
36     if ( valid() ) {
37         if ( !headers )
38             headers = new List<HeaderFile>;
39         headers->append( this );
40         parse();
41     }
42 }
43 
44 
45 /*! Parses this header file and creates Class and Function objects as
46     appropriate.
47 
48     The parsing is minimalistic: All it does is look for a useful
49     subset of class declarations, and process those.
50 */
51 
parse()52 void HeaderFile::parse()
53 {
54     Parser p( contents() );
55     p.scan( "\nclass " );
56     while ( !p.atEnd() ) {
57         EString className = p.identifier();
58         EString superclass = 0;
59         p.whitespace();
60         if ( p.lookingAt( ":" ) ) {
61             p.step();
62             EString inheritance = p.word();
63             if ( inheritance != "public" ) {
64                 (void)new Error( this, p.line(),
65                                  "Non-public inheritance for class " +
66                                  className );
67                 return;
68             }
69             EString parent = p.identifier();
70             if ( parent.isEmpty() ) {
71                 (void)new Error( this, p.line(),
72                                  "Cannot parse superclass name for class " +
73                                  className );
74                 return;
75             }
76             superclass = parent;
77         }
78         p.whitespace();
79         if ( p.lookingAt( "{" ) ) {
80             Class * c = Class::find( className );
81             if ( !c )
82                 c = new Class( className, 0, 0 );
83             c->setParent( superclass );
84             if ( c && c->file() ) {
85                 (void) new Error( this, p.line(),
86                                   "Class " + className +
87                                   " conflicts with " + className + " at " +
88                                   c->file()->name() + ":" +
89                                   fn( c->line() ) );
90                 (void) new Error( c->file(), c->line(),
91                                   "Class " + className +
92                                   " conflicts with " + className + " at " +
93                                   name() + ":" +
94                                   fn( p.line() ) );
95             }
96             else {
97                 c->setSource( this, p.line() );
98             }
99             p.step();
100             bool ok = false;
101             do {
102                 ok = false;
103                 p.whitespace();
104                 while ( p.lookingAt( "public:" ) ||
105                         p.lookingAt( "private:" ) ||
106                         p.lookingAt( "protected:" ) ) {
107                     p.scan( ":" );
108                     p.step();
109                     p.whitespace();
110                 }
111                 if ( p.lookingAt( "virtual " ) )
112                     p.scan( " " );
113                 p.whitespace();
114                 EString t;
115                 EString n;
116                 uint l = p.line();
117                 if ( p.lookingAt( "operator " ) ) {
118                     n = p.identifier();
119                 }
120                 else if ( p.lookingAt( "enum " ) ) {
121                     p.scan( " " );
122                     Enum * e = new Enum( c, p.word(), this, l );
123                     p.whitespace();
124                     if ( p.lookingAt( "{" ) ) {
125                         bool again = true;
126                         while ( again ) {
127                             p.step();
128                             p.whitespace();
129                             EString v = p.word();
130                             if ( v.isEmpty() )
131                                 (void)new Error( this, p.line(),
132                                                  "Could not parse "
133                                                  "enum value" );
134                             else
135                                 e->addValue( v );
136                             p.whitespace();
137                             if ( p.lookingAt( "=" ) ) {
138                                 p.step();
139                                 p.whitespace();
140                                 (void)p.value();
141                                 p.whitespace();
142                             }
143                             again = p.lookingAt( "," );
144                         }
145                         if ( p.lookingAt( "}" ) ) {
146                             p.step();
147                             ok = true;
148                         }
149                         else {
150                             (void)new Error( this, p.line(),
151                                              "Enum definition for " +
152                                              className + "::" + n +
153                                              " does not end with '}'" );
154                         }
155                     }
156                     else if ( p.lookingAt( ";" ) ) {
157                         // senseless crap
158                         ok = true;
159                     }
160                     else {
161                         (void)new Error( this, l,
162                                          "Cannot parse enum " +
163                                          className + "::" + n );
164                     }
165                 }
166                 else if ( p.lookingAt( "typedef " ) ) {
167                     ok = true;
168                 }
169                 else {
170                     t = p.type();
171                     n = p.identifier();
172                     if ( n.isEmpty() ) {
173                         // constructor/destructor?
174                         if ( t == className || t == "~" + className ) {
175                             n = t;
176                             t = "";
177                         }
178                         else if ( t.isEmpty() && p.lookingAt( "~" ) ) {
179                             p.step();
180                             n = "~" + p.identifier();
181                         }
182                     }
183                 }
184                 if ( !n.isEmpty() ) {
185                     p.whitespace();
186                     if ( p.lookingAt( ";" ) )
187                         ok = true;
188                     EString a = p.argumentList();
189                     p.whitespace();
190                     bool fc = false;
191                     if ( p.lookingAt( "const" ) ) {
192                         fc = true;
193                         p.word();
194                     }
195                     if ( !n.isEmpty() && n.find( ':' ) < 0 &&
196                          !a.isEmpty() ) {
197                         n = className + "::" + n;
198                         Function * f = Function::find( n, a, fc );
199                         if ( !f )
200                             f = new Function( t, n, a, fc, this, l );
201                         ok = true;
202                     }
203                 }
204                 if ( ok ) {
205                     p.whitespace();
206                     if ( p.lookingAt( "{" ) ) {
207                         uint level = 0;
208                         while ( level > 0 || p.lookingAt( "{" ) ) {
209                             if ( p.lookingAt( "{" ) ) {
210                                 level++;
211                             }
212                             else if ( p.lookingAt( "}" ) ) {
213                                 level--;
214                             }
215                             p.step();
216                             p.whitespace();
217                         }
218                     }
219                     else {
220                         p.scan( ";" );
221                     }
222                 }
223             } while ( ok );
224         }
225         p.scan( "\nclass " );
226     }
227 }
228 
229 
230 /*! Returns a pointer to the HeaderFile whose unqualified file name is \a
231     s, or a null pointer if there is no such HeaderFile.
232 */
233 
find(const EString & s)234 HeaderFile * HeaderFile::find( const EString & s )
235 {
236     if ( !headers )
237         return 0;
238 
239     List<HeaderFile>::Iterator it( headers );
240     HeaderFile * h = 0;
241     EString hack = "/" + s;
242     while ( (h=it) != 0 &&
243             h->name() != s &&
244             !h->name().endsWith( hack ) )
245         ++it;
246     return h;
247 }
248