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