1
by Colin Watson
Import upstream version 5.5.4 |
1 |
/*
|
1.1.3
by Colin Watson
Import upstream version 5.8 |
2 |
* $Id: eiffel.c 706 2009-06-28 23:09:30Z dhiebert $
|
1
by Colin Watson
Import upstream version 5.5.4 |
3 |
*
|
4 |
* Copyright (c) 1998-2002, Darren Hiebert
|
|
5 |
*
|
|
6 |
* This source code is released for free distribution under the terms of the
|
|
7 |
* GNU General Public License.
|
|
8 |
*
|
|
9 |
* This module contains functions for generating tags for Eiffel language
|
|
10 |
* files.
|
|
11 |
*/
|
|
12 |
||
13 |
/*
|
|
14 |
* INCLUDE FILES
|
|
15 |
*/
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
16 |
#include "general.h" /* must always come first */ |
1
by Colin Watson
Import upstream version 5.5.4 |
17 |
|
18 |
#ifdef TYPE_REFERENCE_TOOL
|
|
19 |
#include <stdio.h> |
|
20 |
#endif
|
|
21 |
#include <string.h> |
|
22 |
#include <limits.h> |
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
23 |
#include <ctype.h> /* to define tolower () */ |
1
by Colin Watson
Import upstream version 5.5.4 |
24 |
#include <setjmp.h> |
25 |
||
26 |
#include "debug.h" |
|
27 |
#include "keyword.h" |
|
28 |
#include "routines.h" |
|
29 |
#include "vstring.h" |
|
30 |
#ifndef TYPE_REFERENCE_TOOL
|
|
31 |
#include "entry.h" |
|
32 |
#include "options.h" |
|
33 |
#include "parse.h" |
|
34 |
#include "read.h" |
|
35 |
#endif
|
|
36 |
||
37 |
/*
|
|
38 |
* MACROS
|
|
39 |
*/
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
40 |
#define isident(c) (isalnum(c) || (c) == '_')
|
41 |
#define isFreeOperatorChar(c) ((c) == '@' || (c) == '#' || \
|
|
42 |
(c) == '|' || (c) == '&')
|
|
43 |
#define isType(token,t) (boolean) ((token)->type == (t))
|
|
44 |
#define isKeyword(token,k) (boolean) ((token)->keyword == (k))
|
|
1
by Colin Watson
Import upstream version 5.5.4 |
45 |
|
46 |
/*
|
|
47 |
* DATA DECLARATIONS
|
|
48 |
*/
|
|
49 |
||
50 |
typedef enum eException { ExceptionNone, ExceptionEOF } exception_t; |
|
51 |
||
52 |
/* Used to specify type of keyword.
|
|
53 |
*/
|
|
54 |
typedef enum eKeywordId { |
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
55 |
KEYWORD_NONE = -1, |
1.1.3
by Colin Watson
Import upstream version 5.8 |
56 |
KEYWORD_alias, KEYWORD_all, KEYWORD_and, KEYWORD_as, KEYWORD_assign, |
57 |
KEYWORD_check, KEYWORD_class, KEYWORD_convert, KEYWORD_create, |
|
58 |
KEYWORD_creation, KEYWORD_Current, |
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
59 |
KEYWORD_debug, KEYWORD_deferred, KEYWORD_do, KEYWORD_else, |
60 |
KEYWORD_elseif, KEYWORD_end, KEYWORD_ensure, KEYWORD_expanded, |
|
61 |
KEYWORD_export, KEYWORD_external, KEYWORD_false, KEYWORD_feature, |
|
62 |
KEYWORD_from, KEYWORD_frozen, KEYWORD_if, KEYWORD_implies, |
|
63 |
KEYWORD_indexing, KEYWORD_infix, KEYWORD_inherit, KEYWORD_inspect, |
|
64 |
KEYWORD_invariant, KEYWORD_is, KEYWORD_like, KEYWORD_local, |
|
65 |
KEYWORD_loop, KEYWORD_not, KEYWORD_obsolete, KEYWORD_old, KEYWORD_once, |
|
66 |
KEYWORD_or, KEYWORD_prefix, KEYWORD_redefine, KEYWORD_rename, |
|
67 |
KEYWORD_require, KEYWORD_rescue, KEYWORD_Result, KEYWORD_retry, |
|
68 |
KEYWORD_select, KEYWORD_separate, KEYWORD_strip, KEYWORD_then, |
|
69 |
KEYWORD_true, KEYWORD_undefine, KEYWORD_unique, KEYWORD_until, |
|
70 |
KEYWORD_variant, KEYWORD_when, KEYWORD_xor |
|
1
by Colin Watson
Import upstream version 5.5.4 |
71 |
} keywordId; |
72 |
||
73 |
/* Used to determine whether keyword is valid for the token language and
|
|
74 |
* what its ID is.
|
|
75 |
*/
|
|
76 |
typedef struct sKeywordDesc { |
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
77 |
const char *name; |
78 |
keywordId id; |
|
1
by Colin Watson
Import upstream version 5.5.4 |
79 |
} keywordDesc; |
80 |
||
81 |
typedef enum eTokenType { |
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
82 |
TOKEN_UNDEFINED, |
83 |
TOKEN_BANG, |
|
84 |
TOKEN_CHARACTER, |
|
85 |
TOKEN_CLOSE_BRACE, |
|
86 |
TOKEN_CLOSE_BRACKET, |
|
87 |
TOKEN_CLOSE_PAREN, |
|
88 |
TOKEN_COLON, |
|
89 |
TOKEN_COMMA, |
|
90 |
TOKEN_CONSTRAINT, |
|
91 |
TOKEN_DOT, |
|
92 |
TOKEN_DOLLAR, |
|
93 |
TOKEN_IDENTIFIER, |
|
94 |
TOKEN_KEYWORD, |
|
95 |
TOKEN_NUMERIC, |
|
96 |
TOKEN_OPEN_BRACE, |
|
97 |
TOKEN_OPEN_BRACKET, |
|
98 |
TOKEN_OPEN_PAREN, |
|
99 |
TOKEN_OPERATOR, |
|
100 |
TOKEN_OTHER, |
|
1.1.3
by Colin Watson
Import upstream version 5.8 |
101 |
TOKEN_QUESTION, |
102 |
TOKEN_SEMICOLON, |
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
103 |
TOKEN_SEPARATOR, |
104 |
TOKEN_STRING, |
|
105 |
TOKEN_TILDE
|
|
1
by Colin Watson
Import upstream version 5.5.4 |
106 |
} tokenType; |
107 |
||
108 |
typedef struct sTokenInfo { |
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
109 |
tokenType type; |
110 |
keywordId keyword; |
|
111 |
boolean isExported; |
|
112 |
vString* string; |
|
113 |
vString* className; |
|
114 |
vString* featureName; |
|
1
by Colin Watson
Import upstream version 5.5.4 |
115 |
} tokenInfo; |
116 |
||
117 |
/*
|
|
118 |
* DATA DEFINITIONS
|
|
119 |
*/
|
|
120 |
||
121 |
static langType Lang_eiffel; |
|
122 |
||
123 |
#ifdef TYPE_REFERENCE_TOOL
|
|
124 |
||
125 |
static const char *FileName; |
|
126 |
static FILE *File; |
|
127 |
static int PrintClass; |
|
128 |
static int PrintReferences; |
|
129 |
static int SelfReferences; |
|
130 |
static int Debug; |
|
131 |
static stringList *GenericNames; |
|
132 |
static stringList *ReferencedTypes; |
|
133 |
||
134 |
#else
|
|
135 |
||
136 |
typedef enum { |
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
137 |
EKIND_CLASS, EKIND_FEATURE, EKIND_LOCAL, EKIND_QUALIFIED_TAGS |
1
by Colin Watson
Import upstream version 5.5.4 |
138 |
} eiffelKind; |
139 |
||
140 |
static kindOption EiffelKinds [] = { |
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
141 |
{ TRUE, 'c', "class", "classes"}, |
142 |
{ TRUE, 'f', "feature", "features"}, |
|
143 |
{ FALSE, 'l', "local", "local entities"} |
|
1
by Colin Watson
Import upstream version 5.5.4 |
144 |
};
|
145 |
||
146 |
#endif
|
|
147 |
||
148 |
static jmp_buf Exception; |
|
149 |
||
150 |
static const keywordDesc EiffelKeywordTable [] = { |
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
151 |
/* keyword keyword ID */
|
152 |
{ "alias", KEYWORD_alias }, |
|
153 |
{ "all", KEYWORD_all }, |
|
154 |
{ "and", KEYWORD_and }, |
|
155 |
{ "as", KEYWORD_as }, |
|
1.1.3
by Colin Watson
Import upstream version 5.8 |
156 |
{ "assign", KEYWORD_assign }, |
1.1.1
by Colin Watson
Import upstream version 5.6 |
157 |
{ "check", KEYWORD_check }, |
158 |
{ "class", KEYWORD_class }, |
|
1.1.2
by Colin Watson
Import upstream version 5.7 |
159 |
{ "convert", KEYWORD_convert }, |
1.1.1
by Colin Watson
Import upstream version 5.6 |
160 |
{ "create", KEYWORD_create }, |
161 |
{ "creation", KEYWORD_creation }, |
|
162 |
{ "current", KEYWORD_Current }, |
|
163 |
{ "debug", KEYWORD_debug }, |
|
164 |
{ "deferred", KEYWORD_deferred }, |
|
165 |
{ "do", KEYWORD_do }, |
|
166 |
{ "else", KEYWORD_else }, |
|
167 |
{ "elseif", KEYWORD_elseif }, |
|
168 |
{ "end", KEYWORD_end }, |
|
169 |
{ "ensure", KEYWORD_ensure }, |
|
170 |
{ "expanded", KEYWORD_expanded }, |
|
171 |
{ "export", KEYWORD_export }, |
|
172 |
{ "external", KEYWORD_external }, |
|
173 |
{ "false", KEYWORD_false }, |
|
174 |
{ "feature", KEYWORD_feature }, |
|
175 |
{ "from", KEYWORD_from }, |
|
176 |
{ "frozen", KEYWORD_frozen }, |
|
177 |
{ "if", KEYWORD_if }, |
|
178 |
{ "implies", KEYWORD_implies }, |
|
179 |
{ "indexing", KEYWORD_indexing }, |
|
180 |
{ "infix", KEYWORD_infix }, |
|
181 |
{ "inherit", KEYWORD_inherit }, |
|
182 |
{ "inspect", KEYWORD_inspect }, |
|
183 |
{ "invariant", KEYWORD_invariant }, |
|
184 |
{ "is", KEYWORD_is }, |
|
185 |
{ "like", KEYWORD_like }, |
|
186 |
{ "local", KEYWORD_local }, |
|
187 |
{ "loop", KEYWORD_loop }, |
|
188 |
{ "not", KEYWORD_not }, |
|
189 |
{ "obsolete", KEYWORD_obsolete }, |
|
190 |
{ "old", KEYWORD_old }, |
|
191 |
{ "once", KEYWORD_once }, |
|
192 |
{ "or", KEYWORD_or }, |
|
193 |
{ "prefix", KEYWORD_prefix }, |
|
194 |
{ "redefine", KEYWORD_redefine }, |
|
195 |
{ "rename", KEYWORD_rename }, |
|
196 |
{ "require", KEYWORD_require }, |
|
197 |
{ "rescue", KEYWORD_rescue }, |
|
198 |
{ "result", KEYWORD_Result }, |
|
199 |
{ "retry", KEYWORD_retry }, |
|
200 |
{ "select", KEYWORD_select }, |
|
201 |
{ "separate", KEYWORD_separate }, |
|
202 |
{ "strip", KEYWORD_strip }, |
|
203 |
{ "then", KEYWORD_then }, |
|
204 |
{ "true", KEYWORD_true }, |
|
205 |
{ "undefine", KEYWORD_undefine }, |
|
206 |
{ "unique", KEYWORD_unique }, |
|
207 |
{ "until", KEYWORD_until }, |
|
208 |
{ "variant", KEYWORD_variant }, |
|
209 |
{ "when", KEYWORD_when }, |
|
210 |
{ "xor", KEYWORD_xor } |
|
1
by Colin Watson
Import upstream version 5.5.4 |
211 |
};
|
212 |
||
213 |
/*
|
|
214 |
* FUNCTION DEFINITIONS
|
|
215 |
*/
|
|
216 |
||
217 |
static void buildEiffelKeywordHash (void) |
|
218 |
{
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
219 |
const size_t count = sizeof (EiffelKeywordTable) / |
220 |
sizeof (EiffelKeywordTable [0]); |
|
221 |
size_t i; |
|
222 |
for (i = 0 ; i < count ; ++i) |
|
223 |
{
|
|
224 |
const keywordDesc* const p = &EiffelKeywordTable [i]; |
|
225 |
addKeyword (p->name, Lang_eiffel, (int) p->id); |
|
226 |
}
|
|
1
by Colin Watson
Import upstream version 5.5.4 |
227 |
}
|
228 |
||
229 |
#ifdef TYPE_REFERENCE_TOOL
|
|
230 |
||
231 |
static void addGenericName (tokenInfo *const token) |
|
232 |
{
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
233 |
vStringUpper (token->string); |
234 |
if (vStringLength (token->string) > 0) |
|
235 |
stringListAdd (GenericNames, vStringNewCopy (token->string)); |
|
1
by Colin Watson
Import upstream version 5.5.4 |
236 |
}
|
237 |
||
238 |
static boolean isGeneric (tokenInfo *const token) |
|
239 |
{
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
240 |
return (boolean) stringListHas (GenericNames, vStringValue (token->string)); |
1
by Colin Watson
Import upstream version 5.5.4 |
241 |
}
|
242 |
||
243 |
static void reportType (tokenInfo *const token) |
|
244 |
{
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
245 |
vStringUpper (token->string); |
246 |
if (vStringLength (token->string) > 0 && ! isGeneric (token) && |
|
247 |
(SelfReferences || strcmp (vStringValue ( |
|
248 |
token->string), vStringValue (token->className)) != 0) && |
|
249 |
! stringListHas (ReferencedTypes, vStringValue (token->string))) |
|
250 |
{
|
|
251 |
printf ("%s\n", vStringValue (token->string)); |
|
252 |
stringListAdd (ReferencedTypes, vStringNewCopy (token->string)); |
|
253 |
}
|
|
1
by Colin Watson
Import upstream version 5.5.4 |
254 |
}
|
255 |
||
256 |
static int fileGetc (void) |
|
257 |
{
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
258 |
int c = getc (File); |
259 |
if (c == '\r') |
|
1
by Colin Watson
Import upstream version 5.5.4 |
260 |
{
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
261 |
c = getc (File); |
262 |
if (c != '\n') |
|
263 |
{
|
|
264 |
ungetc (c, File); |
|
265 |
c = '\n'; |
|
266 |
}
|
|
1
by Colin Watson
Import upstream version 5.5.4 |
267 |
}
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
268 |
if (Debug > 0 && c != EOF) |
269 |
putc (c, errout); |
|
270 |
return c; |
|
1
by Colin Watson
Import upstream version 5.5.4 |
271 |
}
|
272 |
||
273 |
static int fileUngetc (c) |
|
274 |
{
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
275 |
return ungetc (c, File); |
1
by Colin Watson
Import upstream version 5.5.4 |
276 |
}
|
277 |
||
278 |
extern char *readLine (vString *const vLine, FILE *const fp) |
|
279 |
{
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
280 |
return NULL; |
1
by Colin Watson
Import upstream version 5.5.4 |
281 |
}
|
282 |
||
283 |
#else
|
|
284 |
||
285 |
/*
|
|
286 |
* Tag generation functions
|
|
287 |
*/
|
|
288 |
||
289 |
static void makeEiffelClassTag (tokenInfo *const token) |
|
290 |
{
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
291 |
if (EiffelKinds [EKIND_CLASS].enabled) |
292 |
{
|
|
293 |
const char *const name = vStringValue (token->string); |
|
294 |
tagEntryInfo e; |
|
295 |
||
296 |
initTagEntry (&e, name); |
|
297 |
||
298 |
e.kindName = EiffelKinds [EKIND_CLASS].name; |
|
299 |
e.kind = EiffelKinds [EKIND_CLASS].letter; |
|
300 |
||
301 |
makeTagEntry (&e); |
|
302 |
}
|
|
303 |
vStringCopy (token->className, token->string); |
|
1
by Colin Watson
Import upstream version 5.5.4 |
304 |
}
|
305 |
||
306 |
static void makeEiffelFeatureTag (tokenInfo *const token) |
|
307 |
{
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
308 |
if (EiffelKinds [EKIND_FEATURE].enabled && |
309 |
(token->isExported || Option.include.fileScope)) |
|
1
by Colin Watson
Import upstream version 5.5.4 |
310 |
{
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
311 |
const char *const name = vStringValue (token->string); |
312 |
tagEntryInfo e; |
|
313 |
||
314 |
initTagEntry (&e, name); |
|
315 |
||
316 |
e.isFileScope = (boolean) (! token->isExported); |
|
317 |
e.kindName = EiffelKinds [EKIND_FEATURE].name; |
|
318 |
e.kind = EiffelKinds [EKIND_FEATURE].letter; |
|
319 |
e.extensionFields.scope [0] = EiffelKinds [EKIND_CLASS].name; |
|
320 |
e.extensionFields.scope [1] = vStringValue (token->className); |
|
321 |
||
322 |
makeTagEntry (&e); |
|
323 |
||
324 |
if (Option.include.qualifiedTags) |
|
325 |
{
|
|
326 |
vString* qualified = vStringNewInit (vStringValue (token->className)); |
|
327 |
vStringPut (qualified, '.'); |
|
328 |
vStringCat (qualified, token->string); |
|
329 |
e.name = vStringValue (qualified); |
|
330 |
makeTagEntry (&e); |
|
331 |
vStringDelete (qualified); |
|
332 |
}
|
|
1
by Colin Watson
Import upstream version 5.5.4 |
333 |
}
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
334 |
vStringCopy (token->featureName, token->string); |
1
by Colin Watson
Import upstream version 5.5.4 |
335 |
}
|
336 |
||
337 |
static void makeEiffelLocalTag (tokenInfo *const token) |
|
338 |
{
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
339 |
if (EiffelKinds [EKIND_LOCAL].enabled && Option.include.fileScope) |
340 |
{
|
|
341 |
const char *const name = vStringValue (token->string); |
|
342 |
vString* scope = vStringNew (); |
|
343 |
tagEntryInfo e; |
|
344 |
||
345 |
initTagEntry (&e, name); |
|
346 |
||
347 |
e.isFileScope = TRUE; |
|
348 |
e.kindName = EiffelKinds [EKIND_LOCAL].name; |
|
349 |
e.kind = EiffelKinds [EKIND_LOCAL].letter; |
|
350 |
||
351 |
vStringCopy (scope, token->className); |
|
352 |
vStringPut (scope, '.'); |
|
353 |
vStringCat (scope, token->featureName); |
|
354 |
||
355 |
e.extensionFields.scope [0] = EiffelKinds [EKIND_FEATURE].name; |
|
356 |
e.extensionFields.scope [1] = vStringValue (scope); |
|
357 |
||
358 |
makeTagEntry (&e); |
|
359 |
vStringDelete (scope); |
|
360 |
}
|
|
1
by Colin Watson
Import upstream version 5.5.4 |
361 |
}
|
362 |
||
363 |
#endif
|
|
364 |
||
365 |
/*
|
|
366 |
* Parsing functions
|
|
367 |
*/
|
|
368 |
||
369 |
static int skipToCharacter (const int c) |
|
370 |
{
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
371 |
int d; |
372 |
||
373 |
do
|
|
374 |
{
|
|
375 |
d = fileGetc (); |
|
376 |
} while (d != EOF && d != c); |
|
377 |
||
378 |
return d; |
|
1
by Colin Watson
Import upstream version 5.5.4 |
379 |
}
|
380 |
||
381 |
/* If a numeric is passed in 'c', this is used as the first digit of the
|
|
382 |
* numeric being parsed.
|
|
383 |
*/
|
|
384 |
static vString *parseInteger (int c) |
|
385 |
{
|
|
1.1.2
by Colin Watson
Import upstream version 5.7 |
386 |
vString *string = vStringNew (); |
1.1.1
by Colin Watson
Import upstream version 5.6 |
387 |
|
388 |
if (c == '\0') |
|
389 |
c = fileGetc (); |
|
390 |
if (c == '-') |
|
391 |
{
|
|
392 |
vStringPut (string, c); |
|
393 |
c = fileGetc (); |
|
394 |
}
|
|
395 |
else if (! isdigit (c)) |
|
396 |
c = fileGetc (); |
|
397 |
while (c != EOF && (isdigit (c) || c == '_')) |
|
398 |
{
|
|
399 |
vStringPut (string, c); |
|
400 |
c = fileGetc (); |
|
401 |
}
|
|
402 |
vStringTerminate (string); |
|
403 |
fileUngetc (c); |
|
404 |
||
405 |
return string; |
|
1
by Colin Watson
Import upstream version 5.5.4 |
406 |
}
|
407 |
||
408 |
static vString *parseNumeric (int c) |
|
409 |
{
|
|
1.1.2
by Colin Watson
Import upstream version 5.7 |
410 |
vString *string = vStringNew (); |
411 |
vString *integer = parseInteger (c); |
|
412 |
vStringCopy (string, integer); |
|
413 |
vStringDelete (integer); |
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
414 |
|
1
by Colin Watson
Import upstream version 5.5.4 |
415 |
c = fileGetc (); |
1.1.1
by Colin Watson
Import upstream version 5.6 |
416 |
if (c == '.') |
417 |
{
|
|
1.1.2
by Colin Watson
Import upstream version 5.7 |
418 |
integer = parseInteger ('\0'); |
1.1.1
by Colin Watson
Import upstream version 5.6 |
419 |
vStringPut (string, c); |
1.1.2
by Colin Watson
Import upstream version 5.7 |
420 |
vStringCat (string, integer); |
421 |
vStringDelete (integer); |
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
422 |
c = fileGetc (); |
423 |
}
|
|
424 |
if (tolower (c) == 'e') |
|
425 |
{
|
|
1.1.2
by Colin Watson
Import upstream version 5.7 |
426 |
integer = parseInteger ('\0'); |
1.1.1
by Colin Watson
Import upstream version 5.6 |
427 |
vStringPut (string, c); |
1.1.2
by Colin Watson
Import upstream version 5.7 |
428 |
vStringCat (string, integer); |
429 |
vStringDelete (integer); |
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
430 |
}
|
431 |
else if (!isspace (c)) |
|
432 |
fileUngetc (c); |
|
433 |
||
434 |
vStringTerminate (string); |
|
435 |
||
436 |
return string; |
|
1
by Colin Watson
Import upstream version 5.5.4 |
437 |
}
|
438 |
||
439 |
static int parseEscapedCharacter (void) |
|
440 |
{
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
441 |
int d = '\0'; |
442 |
int c = fileGetc (); |
|
1
by Colin Watson
Import upstream version 5.5.4 |
443 |
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
444 |
switch (c) |
445 |
{
|
|
446 |
case 'A': d = '@'; break; |
|
447 |
case 'B': d = '\b'; break; |
|
448 |
case 'C': d = '^'; break; |
|
449 |
case 'D': d = '$'; break; |
|
450 |
case 'F': d = '\f'; break; |
|
451 |
case 'H': d = '\\'; break; |
|
452 |
case 'L': d = '~'; break; |
|
453 |
case 'N': d = '\n'; break; |
|
1
by Colin Watson
Import upstream version 5.5.4 |
454 |
#ifdef QDOS
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
455 |
case 'Q': d = 0x9F; break; |
1
by Colin Watson
Import upstream version 5.5.4 |
456 |
#else
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
457 |
case 'Q': d = '`'; break; |
1
by Colin Watson
Import upstream version 5.5.4 |
458 |
#endif
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
459 |
case 'R': d = '\r'; break; |
460 |
case 'S': d = '#'; break; |
|
461 |
case 'T': d = '\t'; break; |
|
462 |
case 'U': d = '\0'; break; |
|
463 |
case 'V': d = '|'; break; |
|
464 |
case '%': d = '%'; break; |
|
465 |
case '\'': d = '\''; break; |
|
466 |
case '"': d = '"'; break; |
|
467 |
case '(': d = '['; break; |
|
468 |
case ')': d = ']'; break; |
|
469 |
case '<': d = '{'; break; |
|
470 |
case '>': d = '}'; break; |
|
471 |
||
472 |
case '\n': skipToCharacter ('%'); break; |
|
473 |
||
474 |
case '/': |
|
475 |
{
|
|
476 |
vString *string = parseInteger ('\0'); |
|
477 |
const char *value = vStringValue (string); |
|
478 |
const unsigned long ascii = atol (value); |
|
1.1.2
by Colin Watson
Import upstream version 5.7 |
479 |
vStringDelete (string); |
1.1.1
by Colin Watson
Import upstream version 5.6 |
480 |
|
481 |
c = fileGetc (); |
|
482 |
if (c == '/' && ascii < 256) |
|
483 |
d = ascii; |
|
484 |
break; |
|
485 |
}
|
|
486 |
||
487 |
default: break; |
|
1
by Colin Watson
Import upstream version 5.5.4 |
488 |
}
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
489 |
return d; |
1
by Colin Watson
Import upstream version 5.5.4 |
490 |
}
|
491 |
||
492 |
static int parseCharacter (void) |
|
493 |
{
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
494 |
int c = fileGetc (); |
495 |
int result = c; |
|
496 |
||
497 |
if (c == '%') |
|
498 |
result = parseEscapedCharacter (); |
|
499 |
||
500 |
c = fileGetc (); |
|
501 |
if (c != '\'') |
|
502 |
skipToCharacter ('\n'); |
|
503 |
||
504 |
return result; |
|
1
by Colin Watson
Import upstream version 5.5.4 |
505 |
}
|
506 |
||
507 |
static void parseString (vString *const string) |
|
508 |
{
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
509 |
boolean verbatim = FALSE; |
510 |
boolean align = FALSE; |
|
511 |
boolean end = FALSE; |
|
1.1.2
by Colin Watson
Import upstream version 5.7 |
512 |
vString *verbatimCloser = vStringNew (); |
513 |
vString *lastLine = vStringNew (); |
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
514 |
int prev = '\0'; |
515 |
int c; |
|
1
by Colin Watson
Import upstream version 5.5.4 |
516 |
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
517 |
while (! end) |
518 |
{
|
|
519 |
c = fileGetc (); |
|
520 |
if (c == EOF) |
|
521 |
end = TRUE; |
|
522 |
else if (c == '"') |
|
523 |
{
|
|
524 |
if (! verbatim) |
|
525 |
end = TRUE; |
|
526 |
else
|
|
527 |
end = (boolean) (strcmp (vStringValue (lastLine), |
|
528 |
vStringValue (verbatimCloser)) == 0); |
|
529 |
}
|
|
530 |
else if (c == '\n') |
|
531 |
{
|
|
532 |
if (verbatim) |
|
533 |
vStringClear (lastLine); |
|
534 |
if (prev == '[' /* || prev == '{' */) |
|
535 |
{
|
|
536 |
verbatim = TRUE; |
|
1.1.2
by Colin Watson
Import upstream version 5.7 |
537 |
vStringClear (verbatimCloser); |
538 |
vStringClear (lastLine); |
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
539 |
if (prev == '{') |
540 |
vStringPut (verbatimCloser, '}'); |
|
541 |
else
|
|
542 |
{
|
|
543 |
vStringPut (verbatimCloser, ']'); |
|
544 |
align = TRUE; |
|
545 |
}
|
|
546 |
vStringNCat (verbatimCloser, string, vStringLength (string) - 1); |
|
547 |
vStringClear (string); |
|
548 |
}
|
|
549 |
if (verbatim && align) |
|
550 |
{
|
|
551 |
do
|
|
552 |
c = fileGetc (); |
|
553 |
while (isspace (c)); |
|
554 |
}
|
|
555 |
}
|
|
556 |
else if (c == '%') |
|
557 |
c = parseEscapedCharacter (); |
|
558 |
if (! end) |
|
559 |
{
|
|
560 |
vStringPut (string, c); |
|
561 |
if (verbatim) |
|
562 |
{
|
|
563 |
vStringPut (lastLine, c); |
|
564 |
vStringTerminate (lastLine); |
|
565 |
}
|
|
566 |
prev = c; |
|
567 |
}
|
|
568 |
}
|
|
569 |
vStringTerminate (string); |
|
1.1.2
by Colin Watson
Import upstream version 5.7 |
570 |
vStringDelete (lastLine); |
571 |
vStringDelete (verbatimCloser); |
|
1
by Colin Watson
Import upstream version 5.5.4 |
572 |
}
|
573 |
||
574 |
/* Read a C identifier beginning with "firstChar" and places it into "name".
|
|
575 |
*/
|
|
576 |
static void parseIdentifier (vString *const string, const int firstChar) |
|
577 |
{
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
578 |
int c = firstChar; |
579 |
||
580 |
do
|
|
581 |
{
|
|
582 |
vStringPut (string, c); |
|
583 |
c = fileGetc (); |
|
584 |
} while (isident (c)); |
|
585 |
||
586 |
vStringTerminate (string); |
|
587 |
if (!isspace (c)) |
|
588 |
fileUngetc (c); /* unget non-identifier character */ |
|
1
by Colin Watson
Import upstream version 5.5.4 |
589 |
}
|
590 |
||
591 |
static void parseFreeOperator (vString *const string, const int firstChar) |
|
592 |
{
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
593 |
int c = firstChar; |
594 |
||
595 |
do
|
|
596 |
{
|
|
597 |
vStringPut (string, c); |
|
598 |
c = fileGetc (); |
|
599 |
} while (c > ' '); |
|
600 |
||
601 |
vStringTerminate (string); |
|
602 |
if (!isspace (c)) |
|
603 |
fileUngetc (c); /* unget non-identifier character */ |
|
1
by Colin Watson
Import upstream version 5.5.4 |
604 |
}
|
605 |
||
1.1.3
by Colin Watson
Import upstream version 5.8 |
606 |
static void copyToken (tokenInfo* dst, const tokenInfo *src) |
607 |
{
|
|
608 |
dst->type = src->type; |
|
609 |
dst->keyword = src->keyword; |
|
610 |
dst->isExported = src->isExported; |
|
611 |
||
612 |
vStringCopy (dst->string, src->string); |
|
613 |
vStringCopy (dst->className, src->className); |
|
614 |
vStringCopy (dst->featureName, src->featureName); |
|
615 |
}
|
|
616 |
||
617 |
static tokenInfo *newToken (void) |
|
618 |
{
|
|
619 |
tokenInfo *const token = xMalloc (1, tokenInfo); |
|
620 |
||
621 |
token->type = TOKEN_UNDEFINED; |
|
622 |
token->keyword = KEYWORD_NONE; |
|
623 |
token->isExported = TRUE; |
|
624 |
||
625 |
token->string = vStringNew (); |
|
626 |
token->className = vStringNew (); |
|
627 |
token->featureName = vStringNew (); |
|
628 |
||
629 |
return token; |
|
630 |
}
|
|
631 |
||
632 |
static void deleteToken (tokenInfo *const token) |
|
633 |
{
|
|
634 |
vStringDelete (token->string); |
|
635 |
vStringDelete (token->className); |
|
636 |
vStringDelete (token->featureName); |
|
637 |
||
638 |
eFree (token); |
|
1
by Colin Watson
Import upstream version 5.5.4 |
639 |
}
|
640 |
||
641 |
static void readToken (tokenInfo *const token) |
|
642 |
{
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
643 |
int c; |
1
by Colin Watson
Import upstream version 5.5.4 |
644 |
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
645 |
token->type = TOKEN_UNDEFINED; |
646 |
token->keyword = KEYWORD_NONE; |
|
647 |
vStringClear (token->string); |
|
1
by Colin Watson
Import upstream version 5.5.4 |
648 |
|
649 |
getNextChar: |
|
650 |
||
1.1.1
by Colin Watson
Import upstream version 5.6 |
651 |
do
|
652 |
c = fileGetc (); |
|
653 |
while (c == '\t' || c == ' ' || c == '\n'); |
|
654 |
||
655 |
switch (c) |
|
656 |
{
|
|
657 |
case EOF: longjmp (Exception, (int)ExceptionEOF); break; |
|
1.1.3
by Colin Watson
Import upstream version 5.8 |
658 |
case ';': token->type = TOKEN_SEMICOLON; break; |
1.1.1
by Colin Watson
Import upstream version 5.6 |
659 |
case '!': token->type = TOKEN_BANG; break; |
1.1.3
by Colin Watson
Import upstream version 5.8 |
660 |
case '}': token->type = TOKEN_CLOSE_BRACE; break; |
661 |
case ']': token->type = TOKEN_CLOSE_BRACKET; break; |
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
662 |
case ')': token->type = TOKEN_CLOSE_PAREN; break; |
663 |
case ',': token->type = TOKEN_COMMA; break; |
|
1.1.3
by Colin Watson
Import upstream version 5.8 |
664 |
case '$': token->type = TOKEN_DOLLAR; break; |
1.1.1
by Colin Watson
Import upstream version 5.6 |
665 |
case '.': token->type = TOKEN_DOT; break; |
1.1.3
by Colin Watson
Import upstream version 5.8 |
666 |
case '{': token->type = TOKEN_OPEN_BRACE; break; |
1.1.1
by Colin Watson
Import upstream version 5.6 |
667 |
case '[': token->type = TOKEN_OPEN_BRACKET; break; |
1.1.3
by Colin Watson
Import upstream version 5.8 |
668 |
case '(': token->type = TOKEN_OPEN_PAREN; break; |
1.1.1
by Colin Watson
Import upstream version 5.6 |
669 |
case '~': token->type = TOKEN_TILDE; break; |
670 |
||
671 |
||
672 |
case '+': |
|
673 |
case '*': |
|
674 |
case '^': |
|
675 |
case '=': token->type = TOKEN_OPERATOR; break; |
|
676 |
||
677 |
case '-': |
|
678 |
c = fileGetc (); |
|
679 |
if (c == '>') |
|
680 |
token->type = TOKEN_CONSTRAINT; |
|
681 |
else if (c == '-') /* is this the start of a comment? */ |
|
682 |
{
|
|
683 |
skipToCharacter ('\n'); |
|
684 |
goto getNextChar; |
|
685 |
}
|
|
686 |
else
|
|
687 |
{
|
|
688 |
if (!isspace (c)) |
|
689 |
fileUngetc (c); |
|
690 |
token->type = TOKEN_OPERATOR; |
|
691 |
}
|
|
692 |
break; |
|
693 |
||
694 |
case '?': |
|
695 |
case ':': |
|
1.1.3
by Colin Watson
Import upstream version 5.8 |
696 |
{
|
697 |
int c2 = fileGetc (); |
|
698 |
if (c2 == '=') |
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
699 |
token->type = TOKEN_OPERATOR; |
700 |
else
|
|
701 |
{
|
|
1.1.3
by Colin Watson
Import upstream version 5.8 |
702 |
if (!isspace (c2)) |
703 |
fileUngetc (c2); |
|
704 |
if (c == ':') |
|
705 |
token->type = TOKEN_COLON; |
|
706 |
else
|
|
707 |
token->type = TOKEN_QUESTION; |
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
708 |
}
|
709 |
break; |
|
1.1.3
by Colin Watson
Import upstream version 5.8 |
710 |
}
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
711 |
|
712 |
case '<': |
|
713 |
c = fileGetc (); |
|
714 |
if (c != '=' && c != '>' && !isspace (c)) |
|
715 |
fileUngetc (c); |
|
716 |
token->type = TOKEN_OPERATOR; |
|
717 |
break; |
|
718 |
||
719 |
case '>': |
|
720 |
c = fileGetc (); |
|
721 |
if (c != '=' && c != '>' && !isspace (c)) |
|
722 |
fileUngetc (c); |
|
723 |
token->type = TOKEN_OPERATOR; |
|
724 |
break; |
|
725 |
||
726 |
case '/': |
|
727 |
c = fileGetc (); |
|
728 |
if (c != '/' && c != '=' && !isspace (c)) |
|
729 |
fileUngetc (c); |
|
730 |
token->type = TOKEN_OPERATOR; |
|
731 |
break; |
|
732 |
||
733 |
case '\\': |
|
734 |
c = fileGetc (); |
|
735 |
if (c != '\\' && !isspace (c)) |
|
736 |
fileUngetc (c); |
|
737 |
token->type = TOKEN_OPERATOR; |
|
738 |
break; |
|
739 |
||
740 |
case '"': |
|
741 |
token->type = TOKEN_STRING; |
|
742 |
parseString (token->string); |
|
743 |
break; |
|
744 |
||
745 |
case '\'': |
|
746 |
token->type = TOKEN_CHARACTER; |
|
747 |
parseCharacter (); |
|
748 |
break; |
|
749 |
||
750 |
default: |
|
751 |
if (isalpha (c)) |
|
752 |
{
|
|
753 |
parseIdentifier (token->string, c); |
|
1.1.3
by Colin Watson
Import upstream version 5.8 |
754 |
token->keyword = analyzeToken (token->string, Lang_eiffel); |
1.1.1
by Colin Watson
Import upstream version 5.6 |
755 |
if (isKeyword (token, KEYWORD_NONE)) |
756 |
token->type = TOKEN_IDENTIFIER; |
|
757 |
else
|
|
758 |
token->type = TOKEN_KEYWORD; |
|
759 |
}
|
|
760 |
else if (isdigit (c)) |
|
761 |
{
|
|
1.1.2
by Colin Watson
Import upstream version 5.7 |
762 |
vString* numeric = parseNumeric (c); |
763 |
vStringCat (token->string, numeric); |
|
764 |
vStringDelete (numeric); |
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
765 |
token->type = TOKEN_NUMERIC; |
766 |
}
|
|
767 |
else if (isFreeOperatorChar (c)) |
|
768 |
{
|
|
769 |
parseFreeOperator (token->string, c); |
|
770 |
token->type = TOKEN_OPERATOR; |
|
771 |
}
|
|
772 |
else
|
|
773 |
{
|
|
774 |
token->type = TOKEN_UNDEFINED; |
|
775 |
Assert (! isType (token, TOKEN_UNDEFINED)); |
|
776 |
}
|
|
777 |
break; |
|
778 |
}
|
|
1
by Colin Watson
Import upstream version 5.5.4 |
779 |
}
|
780 |
||
781 |
/*
|
|
782 |
* Scanning functions
|
|
783 |
*/
|
|
784 |
||
1.1.1
by Colin Watson
Import upstream version 5.6 |
785 |
static boolean isIdentifierMatch ( |
786 |
const tokenInfo *const token, const char *const name) |
|
1
by Colin Watson
Import upstream version 5.5.4 |
787 |
{
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
788 |
return (boolean) (isType (token, TOKEN_IDENTIFIER) && |
789 |
strcasecmp (vStringValue (token->string), name) == 0); |
|
1
by Colin Watson
Import upstream version 5.5.4 |
790 |
}
|
791 |
||
792 |
static void findToken (tokenInfo *const token, const tokenType type) |
|
793 |
{
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
794 |
while (! isType (token, type)) |
795 |
readToken (token); |
|
1
by Colin Watson
Import upstream version 5.5.4 |
796 |
}
|
797 |
||
798 |
static void findKeyword (tokenInfo *const token, const keywordId keyword) |
|
799 |
{
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
800 |
while (! isKeyword (token, keyword)) |
801 |
readToken (token); |
|
1
by Colin Watson
Import upstream version 5.5.4 |
802 |
}
|
803 |
||
1.1.3
by Colin Watson
Import upstream version 5.8 |
804 |
static boolean parseType (tokenInfo *const token); |
805 |
||
1
by Colin Watson
Import upstream version 5.5.4 |
806 |
static void parseGeneric (tokenInfo *const token, boolean declaration __unused__) |
807 |
{
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
808 |
unsigned int depth = 0; |
809 |
#ifdef TYPE_REFERENCE_TOOL
|
|
810 |
boolean constraint = FALSE; |
|
811 |
#endif
|
|
812 |
Assert (isType (token, TOKEN_OPEN_BRACKET)); |
|
813 |
do
|
|
814 |
{
|
|
815 |
if (isType (token, TOKEN_OPEN_BRACKET)) |
|
1.1.3
by Colin Watson
Import upstream version 5.8 |
816 |
{
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
817 |
++depth; |
1.1.3
by Colin Watson
Import upstream version 5.8 |
818 |
readToken (token); |
819 |
}
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
820 |
else if (isType (token, TOKEN_CLOSE_BRACKET)) |
1.1.3
by Colin Watson
Import upstream version 5.8 |
821 |
{
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
822 |
--depth; |
1.1.3
by Colin Watson
Import upstream version 5.8 |
823 |
readToken (token); |
824 |
}
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
825 |
#ifdef TYPE_REFERENCE_TOOL
|
826 |
else if (declaration) |
|
827 |
{
|
|
1.1.3
by Colin Watson
Import upstream version 5.8 |
828 |
boolean advanced = FALSE; |
1.1.1
by Colin Watson
Import upstream version 5.6 |
829 |
if (depth == 1) |
830 |
{
|
|
831 |
if (isType (token, TOKEN_CONSTRAINT)) |
|
832 |
constraint = TRUE; |
|
833 |
else if (isKeyword (token, KEYWORD_create)) |
|
834 |
findKeyword (token, KEYWORD_end); |
|
835 |
else if (isType (token, TOKEN_IDENTIFIER)) |
|
836 |
{
|
|
837 |
if (constraint) |
|
1.1.3
by Colin Watson
Import upstream version 5.8 |
838 |
advanced = parseType (token); |
1.1.1
by Colin Watson
Import upstream version 5.6 |
839 |
else
|
840 |
addGenericName (token); |
|
841 |
constraint = FALSE; |
|
842 |
}
|
|
843 |
}
|
|
1.1.3
by Colin Watson
Import upstream version 5.8 |
844 |
else if (isType (token, TOKEN_IDENTIFIER)) |
845 |
advanced = parseType (token); |
|
846 |
if (! advanced) |
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
847 |
readToken (token); |
848 |
}
|
|
1.1.3
by Colin Watson
Import upstream version 5.8 |
849 |
#endif
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
850 |
else
|
1.1.3
by Colin Watson
Import upstream version 5.8 |
851 |
parseType (token); |
852 |
} while (depth > 0); |
|
853 |
}
|
|
854 |
||
855 |
static boolean parseType (tokenInfo *const token) |
|
856 |
{
|
|
857 |
tokenInfo* const id = newToken (); |
|
858 |
copyToken (id, token); |
|
859 |
readToken (token); |
|
860 |
if (isType (token, TOKEN_COLON)) /* check for "{entity: TYPE}" */ |
|
861 |
{
|
|
862 |
readToken (id); |
|
863 |
readToken (token); |
|
864 |
}
|
|
865 |
if (isKeyword (id, KEYWORD_like)) |
|
866 |
{
|
|
867 |
if (isType (token, TOKEN_IDENTIFIER) || |
|
868 |
isKeyword (token, KEYWORD_Current)) |
|
869 |
readToken (token); |
|
870 |
}
|
|
871 |
else
|
|
872 |
{
|
|
873 |
if (isKeyword (id, KEYWORD_expanded)) |
|
874 |
{
|
|
875 |
copyToken (id, token); |
|
876 |
readToken (token); |
|
877 |
}
|
|
878 |
if (isType (id, TOKEN_IDENTIFIER)) |
|
879 |
{
|
|
880 |
#ifdef TYPE_REFERENCE_TOOL
|
|
881 |
reportType (id); |
|
882 |
#endif
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
883 |
if (isType (token, TOKEN_OPEN_BRACKET)) |
1.1.3
by Colin Watson
Import upstream version 5.8 |
884 |
parseGeneric (token, FALSE); |
885 |
else if ((strcmp ("BIT", vStringValue (id->string)) == 0)) |
|
886 |
readToken (token); /* read token after number of bits */ |
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
887 |
}
|
1.1.3
by Colin Watson
Import upstream version 5.8 |
888 |
}
|
889 |
deleteToken (id); |
|
890 |
return TRUE; |
|
1
by Colin Watson
Import upstream version 5.5.4 |
891 |
}
|
892 |
||
893 |
static void parseEntityType (tokenInfo *const token) |
|
894 |
{
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
895 |
Assert (isType (token, TOKEN_COLON)); |
896 |
readToken (token); |
|
897 |
||
1.1.3
by Colin Watson
Import upstream version 5.8 |
898 |
if (isType (token, TOKEN_BANG) || isType (token, TOKEN_QUESTION)) |
899 |
readToken (token); /* skip over '!' or '?' */ |
|
900 |
parseType (token); |
|
1
by Colin Watson
Import upstream version 5.5.4 |
901 |
}
|
902 |
||
903 |
static void parseLocal (tokenInfo *const token) |
|
904 |
{
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
905 |
Assert (isKeyword (token, KEYWORD_local)); |
906 |
readToken (token); |
|
1
by Colin Watson
Import upstream version 5.5.4 |
907 |
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
908 |
/* Check keyword first in case local clause is empty
|
909 |
*/
|
|
910 |
while (! isKeyword (token, KEYWORD_do) && |
|
911 |
! isKeyword (token, KEYWORD_once)) |
|
912 |
{
|
|
1
by Colin Watson
Import upstream version 5.5.4 |
913 |
#ifndef TYPE_REFERENCE_TOOL
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
914 |
if (isType (token, TOKEN_IDENTIFIER)) |
915 |
makeEiffelLocalTag (token); |
|
1
by Colin Watson
Import upstream version 5.5.4 |
916 |
#endif
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
917 |
readToken (token); |
918 |
if (isType (token, TOKEN_COLON)) |
|
1.1.3
by Colin Watson
Import upstream version 5.8 |
919 |
parseEntityType (token); |
1
by Colin Watson
Import upstream version 5.5.4 |
920 |
}
|
921 |
}
|
|
922 |
||
923 |
static void findFeatureEnd (tokenInfo *const token) |
|
924 |
{
|
|
1.1.3
by Colin Watson
Import upstream version 5.8 |
925 |
boolean isFound = isKeyword (token, KEYWORD_is); |
926 |
if (isFound) |
|
927 |
readToken (token); |
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
928 |
switch (token->keyword) |
1
by Colin Watson
Import upstream version 5.5.4 |
929 |
{
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
930 |
case KEYWORD_deferred: |
931 |
case KEYWORD_do: |
|
932 |
case KEYWORD_external: |
|
933 |
case KEYWORD_local: |
|
934 |
case KEYWORD_obsolete: |
|
935 |
case KEYWORD_once: |
|
936 |
case KEYWORD_require: |
|
937 |
{
|
|
938 |
int depth = 1; |
|
939 |
||
940 |
while (depth > 0) |
|
941 |
{
|
|
1
by Colin Watson
Import upstream version 5.5.4 |
942 |
#ifdef TYPE_REFERENCE_TOOL
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
943 |
if (isType (token, TOKEN_OPEN_BRACE)) |
944 |
{
|
|
945 |
readToken (token); |
|
946 |
if (isType (token, TOKEN_IDENTIFIER)) |
|
947 |
parseType (token); |
|
948 |
}
|
|
949 |
else if (isType (token, TOKEN_BANG)) |
|
950 |
{
|
|
951 |
readToken (token); |
|
952 |
if (isType (token, TOKEN_IDENTIFIER)) |
|
953 |
parseType (token); |
|
954 |
if (isType (token, TOKEN_BANG)) |
|
955 |
readToken (token); |
|
956 |
}
|
|
957 |
else
|
|
1
by Colin Watson
Import upstream version 5.5.4 |
958 |
#endif
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
959 |
switch (token->keyword) |
960 |
{
|
|
961 |
case KEYWORD_check: |
|
962 |
case KEYWORD_debug: |
|
963 |
case KEYWORD_from: |
|
964 |
case KEYWORD_if: |
|
965 |
case KEYWORD_inspect: |
|
966 |
++depth; |
|
967 |
break; |
|
968 |
||
969 |
case KEYWORD_local: |
|
970 |
parseLocal (token); |
|
971 |
break; |
|
972 |
||
973 |
case KEYWORD_end: |
|
974 |
--depth; |
|
975 |
break; |
|
976 |
||
977 |
default: |
|
978 |
break; |
|
979 |
}
|
|
980 |
readToken (token); |
|
981 |
}
|
|
1
by Colin Watson
Import upstream version 5.5.4 |
982 |
break; |
983 |
}
|
|
1.1.3
by Colin Watson
Import upstream version 5.8 |
984 |
|
985 |
default: |
|
986 |
/* is this a manifest constant? */
|
|
987 |
if (isFound || isType (token, TOKEN_OPERATOR)) { |
|
988 |
if (isType (token, TOKEN_OPERATOR)) |
|
989 |
readToken (token); |
|
990 |
readToken (token); |
|
991 |
}
|
|
992 |
break; |
|
1
by Colin Watson
Import upstream version 5.5.4 |
993 |
}
|
994 |
}
|
|
995 |
||
996 |
static boolean readFeatureName (tokenInfo *const token) |
|
997 |
{
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
998 |
boolean isFeatureName = FALSE; |
1
by Colin Watson
Import upstream version 5.5.4 |
999 |
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
1000 |
if (isKeyword (token, KEYWORD_frozen)) |
1001 |
readToken (token); |
|
1002 |
if (isType (token, TOKEN_IDENTIFIER)) |
|
1003 |
isFeatureName = TRUE; |
|
1.1.3
by Colin Watson
Import upstream version 5.8 |
1004 |
else if (isKeyword (token, KEYWORD_assign)) /* legacy code */ |
1005 |
isFeatureName = TRUE; |
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
1006 |
else if (isKeyword (token, KEYWORD_infix) || |
1007 |
isKeyword (token, KEYWORD_prefix)) |
|
1008 |
{
|
|
1009 |
readToken (token); |
|
1010 |
if (isType (token, TOKEN_STRING)) |
|
1011 |
isFeatureName = TRUE; |
|
1012 |
}
|
|
1013 |
return isFeatureName; |
|
1
by Colin Watson
Import upstream version 5.5.4 |
1014 |
}
|
1015 |
||
1016 |
static void parseArguments (tokenInfo *const token) |
|
1017 |
{
|
|
1018 |
#ifndef TYPE_REFERENCE_TOOL
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
1019 |
findToken (token, TOKEN_CLOSE_PAREN); |
1020 |
readToken (token); |
|
1
by Colin Watson
Import upstream version 5.5.4 |
1021 |
#else
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
1022 |
Assert (isType (token, TOKEN_OPEN_PAREN)); |
1023 |
readToken (token); |
|
1024 |
do
|
|
1
by Colin Watson
Import upstream version 5.5.4 |
1025 |
{
|
1.1.3
by Colin Watson
Import upstream version 5.8 |
1026 |
if (isType (token, TOKEN_COLON)) |
1027 |
parseEntityType (token); |
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
1028 |
else
|
1029 |
readToken (token); |
|
1030 |
} while (! isType (token, TOKEN_CLOSE_PAREN)); |
|
1031 |
readToken (token); |
|
1
by Colin Watson
Import upstream version 5.5.4 |
1032 |
#endif
|
1033 |
}
|
|
1034 |
||
1035 |
static boolean parseFeature (tokenInfo *const token) |
|
1036 |
{
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
1037 |
boolean found = FALSE; |
1038 |
while (readFeatureName (token)) |
|
1039 |
{
|
|
1040 |
found = TRUE; |
|
1
by Colin Watson
Import upstream version 5.5.4 |
1041 |
#ifndef TYPE_REFERENCE_TOOL
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
1042 |
makeEiffelFeatureTag (token); |
1
by Colin Watson
Import upstream version 5.5.4 |
1043 |
#endif
|
1044 |
readToken (token); |
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
1045 |
if (isType (token, TOKEN_COMMA)) |
1046 |
readToken (token); |
|
1047 |
}
|
|
1048 |
if (found) |
|
1049 |
{
|
|
1.1.3
by Colin Watson
Import upstream version 5.8 |
1050 |
if (isKeyword (token, KEYWORD_alias)) { |
1051 |
readToken (token); |
|
1052 |
#ifndef TYPE_REFERENCE_TOOL
|
|
1053 |
if (isType (token, TOKEN_STRING)) |
|
1054 |
makeEiffelFeatureTag (token); |
|
1055 |
#endif
|
|
1056 |
readToken (token); |
|
1057 |
}
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
1058 |
if (isType (token, TOKEN_OPEN_PAREN)) /* arguments? */ |
1059 |
parseArguments (token); |
|
1060 |
if (isType (token, TOKEN_COLON)) /* a query? */ |
|
1061 |
parseEntityType (token); |
|
1.1.3
by Colin Watson
Import upstream version 5.8 |
1062 |
if (isKeyword (token, KEYWORD_assign)) |
1063 |
{
|
|
1064 |
readToken (token); |
|
1065 |
readToken (token); |
|
1066 |
}
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
1067 |
if (isKeyword (token, KEYWORD_obsolete)) |
1068 |
{
|
|
1069 |
readToken (token); |
|
1070 |
if (isType (token, TOKEN_STRING)) |
|
1071 |
readToken (token); |
|
1072 |
}
|
|
1.1.3
by Colin Watson
Import upstream version 5.8 |
1073 |
findFeatureEnd (token); |
1.1.1
by Colin Watson
Import upstream version 5.6 |
1074 |
}
|
1075 |
return found; |
|
1
by Colin Watson
Import upstream version 5.5.4 |
1076 |
}
|
1077 |
||
1078 |
static void parseExport (tokenInfo *const token) |
|
1079 |
{
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
1080 |
token->isExported = TRUE; |
1081 |
readToken (token); |
|
1082 |
if (isType (token, TOKEN_OPEN_BRACE)) |
|
1
by Colin Watson
Import upstream version 5.5.4 |
1083 |
{
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
1084 |
token->isExported = FALSE; |
1085 |
while (! isType (token, TOKEN_CLOSE_BRACE)) |
|
1086 |
{
|
|
1087 |
if (isType (token, TOKEN_IDENTIFIER)) |
|
1088 |
token->isExported |= !isIdentifierMatch (token, "NONE"); |
|
1089 |
readToken (token); |
|
1090 |
}
|
|
1091 |
readToken (token); |
|
1
by Colin Watson
Import upstream version 5.5.4 |
1092 |
}
|
1093 |
}
|
|
1094 |
||
1095 |
static void parseFeatureClauses (tokenInfo *const token) |
|
1096 |
{
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
1097 |
Assert (isKeyword (token, KEYWORD_feature)); |
1098 |
do
|
|
1
by Colin Watson
Import upstream version 5.5.4 |
1099 |
{
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
1100 |
if (isKeyword (token, KEYWORD_feature)) |
1101 |
parseExport (token); |
|
1102 |
if (! isKeyword (token, KEYWORD_feature) && |
|
1103 |
! isKeyword (token, KEYWORD_invariant) && |
|
1104 |
! isKeyword (token, KEYWORD_indexing)) |
|
1105 |
{
|
|
1106 |
if (! parseFeature (token)) |
|
1107 |
readToken (token); |
|
1108 |
}
|
|
1109 |
} while (! isKeyword (token, KEYWORD_end) && |
|
1110 |
! isKeyword (token, KEYWORD_invariant) && |
|
1111 |
! isKeyword (token, KEYWORD_indexing)); |
|
1
by Colin Watson
Import upstream version 5.5.4 |
1112 |
}
|
1113 |
||
1114 |
static void parseRename (tokenInfo *const token) |
|
1115 |
{
|
|
1.1.3
by Colin Watson
Import upstream version 5.8 |
1116 |
Assert (isKeyword (token, KEYWORD_rename)); |
1.1.1
by Colin Watson
Import upstream version 5.6 |
1117 |
do { |
1
by Colin Watson
Import upstream version 5.5.4 |
1118 |
readToken (token); |
1119 |
if (readFeatureName (token)) |
|
1120 |
{
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
1121 |
readToken (token); |
1122 |
if (isKeyword (token, KEYWORD_as)) |
|
1123 |
{
|
|
1124 |
readToken (token); |
|
1125 |
if (readFeatureName (token)) |
|
1126 |
{
|
|
1
by Colin Watson
Import upstream version 5.5.4 |
1127 |
#ifndef TYPE_REFERENCE_TOOL
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
1128 |
makeEiffelFeatureTag (token); /* renamed feature */ |
1
by Colin Watson
Import upstream version 5.5.4 |
1129 |
#endif
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
1130 |
readToken (token); |
1131 |
}
|
|
1132 |
}
|
|
1
by Colin Watson
Import upstream version 5.5.4 |
1133 |
}
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
1134 |
} while (isType (token, TOKEN_COMMA)); |
1
by Colin Watson
Import upstream version 5.5.4 |
1135 |
}
|
1136 |
||
1137 |
static void parseInherit (tokenInfo *const token) |
|
1138 |
{
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
1139 |
Assert (isKeyword (token, KEYWORD_inherit)); |
1140 |
readToken (token); |
|
1141 |
while (isType (token, TOKEN_IDENTIFIER)) |
|
1
by Colin Watson
Import upstream version 5.5.4 |
1142 |
{
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
1143 |
parseType (token); |
1144 |
if (isType (token, TOKEN_KEYWORD)) |
|
1145 |
{
|
|
1146 |
switch (token->keyword) /* check for feature adaptation */ |
|
1147 |
{
|
|
1148 |
case KEYWORD_rename: |
|
1.1.3
by Colin Watson
Import upstream version 5.8 |
1149 |
parseRename (token); |
1.1.1
by Colin Watson
Import upstream version 5.6 |
1150 |
case KEYWORD_export: |
1151 |
case KEYWORD_undefine: |
|
1152 |
case KEYWORD_redefine: |
|
1153 |
case KEYWORD_select: |
|
1154 |
findKeyword (token, KEYWORD_end); |
|
1155 |
readToken (token); |
|
1.1.3
by Colin Watson
Import upstream version 5.8 |
1156 |
break; |
1157 |
||
1158 |
case KEYWORD_end: |
|
1159 |
readToken (token); |
|
1160 |
break; |
|
1161 |
||
1.1.1
by Colin Watson
Import upstream version 5.6 |
1162 |
default: break; |
1163 |
}
|
|
1164 |
}
|
|
1.1.3
by Colin Watson
Import upstream version 5.8 |
1165 |
if (isType (token, TOKEN_SEMICOLON)) |
1166 |
readToken (token); |
|
1167 |
}
|
|
1
by Colin Watson
Import upstream version 5.5.4 |
1168 |
}
|
1169 |
||
1.1.2
by Colin Watson
Import upstream version 5.7 |
1170 |
static void parseConvert (tokenInfo *const token) |
1171 |
{
|
|
1172 |
Assert (isKeyword (token, KEYWORD_convert)); |
|
1173 |
do
|
|
1174 |
{
|
|
1175 |
readToken (token); |
|
1176 |
if (! isType (token, TOKEN_IDENTIFIER)) |
|
1177 |
break; |
|
1178 |
else if (isType (token, TOKEN_OPEN_PAREN)) |
|
1179 |
{
|
|
1180 |
while (! isType (token, TOKEN_CLOSE_PAREN)) |
|
1181 |
readToken (token); |
|
1182 |
}
|
|
1183 |
else if (isType (token, TOKEN_COLON)) |
|
1184 |
{
|
|
1185 |
readToken (token); |
|
1186 |
if (! isType (token, TOKEN_OPEN_BRACE)) |
|
1187 |
break; |
|
1188 |
else while (! isType (token, TOKEN_CLOSE_BRACE)) |
|
1189 |
readToken (token); |
|
1190 |
}
|
|
1191 |
} while (isType (token, TOKEN_COMMA)); |
|
1192 |
}
|
|
1193 |
||
1
by Colin Watson
Import upstream version 5.5.4 |
1194 |
static void parseClass (tokenInfo *const token) |
1195 |
{
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
1196 |
Assert (isKeyword (token, KEYWORD_class)); |
1197 |
readToken (token); |
|
1198 |
if (isType (token, TOKEN_IDENTIFIER)) |
|
1199 |
{
|
|
1
by Colin Watson
Import upstream version 5.5.4 |
1200 |
#ifndef TYPE_REFERENCE_TOOL
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
1201 |
makeEiffelClassTag (token); |
1202 |
readToken (token); |
|
1
by Colin Watson
Import upstream version 5.5.4 |
1203 |
#else
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
1204 |
vStringCopy (token->className, token->string); |
1205 |
vStringUpper (token->className); |
|
1206 |
if (PrintClass) |
|
1207 |
puts (vStringValue (token->className)); |
|
1208 |
if (! PrintReferences) |
|
1209 |
exit (0); |
|
1210 |
readToken (token); |
|
1
by Colin Watson
Import upstream version 5.5.4 |
1211 |
#endif
|
1212 |
}
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
1213 |
|
1214 |
do
|
|
1215 |
{
|
|
1216 |
if (isType (token, TOKEN_OPEN_BRACKET)) |
|
1217 |
parseGeneric (token, TRUE); |
|
1218 |
else if (! isType (token, TOKEN_KEYWORD)) |
|
1219 |
readToken (token); |
|
1220 |
else switch (token->keyword) |
|
1221 |
{
|
|
1222 |
case KEYWORD_inherit: parseInherit (token); break; |
|
1223 |
case KEYWORD_feature: parseFeatureClauses (token); break; |
|
1.1.2
by Colin Watson
Import upstream version 5.7 |
1224 |
case KEYWORD_convert: parseConvert (token); break; |
1.1.1
by Colin Watson
Import upstream version 5.6 |
1225 |
default: readToken (token); break; |
1226 |
}
|
|
1227 |
} while (! isKeyword (token, KEYWORD_end)); |
|
1
by Colin Watson
Import upstream version 5.5.4 |
1228 |
}
|
1229 |
||
1230 |
static void initialize (const langType language) |
|
1231 |
{
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
1232 |
Lang_eiffel = language; |
1233 |
buildEiffelKeywordHash (); |
|
1
by Colin Watson
Import upstream version 5.5.4 |
1234 |
}
|
1235 |
||
1236 |
static void findEiffelTags (void) |
|
1237 |
{
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
1238 |
tokenInfo *const token = newToken (); |
1239 |
exception_t exception; |
|
1
by Colin Watson
Import upstream version 5.5.4 |
1240 |
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
1241 |
exception = (exception_t) (setjmp (Exception)); |
1242 |
while (exception == ExceptionNone) |
|
1243 |
{
|
|
1244 |
findKeyword (token, KEYWORD_class); |
|
1245 |
parseClass (token); |
|
1246 |
}
|
|
1247 |
deleteToken (token); |
|
1
by Colin Watson
Import upstream version 5.5.4 |
1248 |
}
|
1249 |
||
1250 |
#ifndef TYPE_REFERENCE_TOOL
|
|
1251 |
||
1252 |
extern parserDefinition* EiffelParser (void) |
|
1253 |
{
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
1254 |
static const char *const extensions [] = { "e", NULL }; |
1255 |
parserDefinition* def = parserNew ("Eiffel"); |
|
1256 |
def->kinds = EiffelKinds; |
|
1257 |
def->kindCount = KIND_COUNT (EiffelKinds); |
|
1258 |
def->extensions = extensions; |
|
1259 |
def->parser = findEiffelTags; |
|
1260 |
def->initialize = initialize; |
|
1261 |
return def; |
|
1
by Colin Watson
Import upstream version 5.5.4 |
1262 |
}
|
1263 |
||
1264 |
#else
|
|
1265 |
||
1266 |
static void findReferences (void) |
|
1267 |
{
|
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
1268 |
ReferencedTypes = stringListNew (); |
1269 |
GenericNames = stringListNew (); |
|
1270 |
initialize (0); |
|
1271 |
||
1272 |
findEiffelTags (); |
|
1273 |
||
1274 |
stringListDelete (GenericNames); |
|
1275 |
GenericNames = NULL; |
|
1276 |
stringListDelete (ReferencedTypes); |
|
1277 |
ReferencedTypes = NULL; |
|
1
by Colin Watson
Import upstream version 5.5.4 |
1278 |
}
|
1279 |
||
1280 |
static const char *const Usage = |
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
1281 |
"Prints names of types referenced by an Eiffel language file.\n" |
1282 |
"\n" |
|
1283 |
"Usage: %s [-cdrs] [file_name | -]\n" |
|
1284 |
"\n" |
|
1285 |
"Options:\n" |
|
1286 |
" -c Print class name of current file (on first line of output).\n" |
|
1287 |
" -d Enable debug output.\n" |
|
1288 |
" -r Print types referenced by current file (default unless -c).\n" |
|
1289 |
" -s Include self-references.\n" |
|
1290 |
"\n"; |
|
1
by Colin Watson
Import upstream version 5.5.4 |
1291 |
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
1292 |
extern int main (int argc, char** argv) |
1
by Colin Watson
Import upstream version 5.5.4 |
1293 |
{
|
1.1.1
by Colin Watson
Import upstream version 5.6 |
1294 |
int i; |
1295 |
for (i = 1 ; argv [i] != NULL ; ++i) |
|
1296 |
{
|
|
1297 |
const char *const arg = argv [i]; |
|
1298 |
if (arg [0] == '-') |
|
1299 |
{
|
|
1300 |
int j; |
|
1301 |
if (arg [1] == '\0') |
|
1302 |
{
|
|
1303 |
File = stdin; |
|
1304 |
FileName = "stdin"; |
|
1305 |
}
|
|
1306 |
else for (j = 1 ; arg [j] != '\0' ; ++j) switch (arg [j]) |
|
1307 |
{
|
|
1308 |
case 'c': PrintClass = 1; break; |
|
1309 |
case 'r': PrintReferences = 1; break; |
|
1310 |
case 's': SelfReferences = 1; break; |
|
1311 |
case 'd': Debug = 1; break; |
|
1312 |
default: |
|
1313 |
fprintf (errout, "%s: unknown option: %c\n", argv [0], arg [1]); |
|
1314 |
fprintf (errout, Usage, argv [0]); |
|
1315 |
exit (1); |
|
1316 |
break; |
|
1317 |
}
|
|
1318 |
}
|
|
1319 |
else if (File != NULL) |
|
1320 |
{
|
|
1321 |
fprintf (errout, Usage, argv [0]); |
|
1322 |
exit (1); |
|
1323 |
}
|
|
1324 |
else
|
|
1325 |
{
|
|
1326 |
FileName = arg; |
|
1327 |
File = fopen (FileName, "r"); |
|
1328 |
if (File == NULL) |
|
1329 |
{
|
|
1330 |
perror (argv [0]); |
|
1331 |
exit (1); |
|
1332 |
}
|
|
1333 |
}
|
|
1334 |
}
|
|
1335 |
if (! PrintClass) |
|
1336 |
PrintReferences = 1; |
|
1337 |
if (File == NULL) |
|
1338 |
{
|
|
1339 |
fprintf (errout, Usage, argv [0]); |
|
1
by Colin Watson
Import upstream version 5.5.4 |
1340 |
exit (1); |
1.1.1
by Colin Watson
Import upstream version 5.6 |
1341 |
}
|
1342 |
else
|
|
1343 |
{
|
|
1344 |
findReferences (); |
|
1345 |
fclose (File); |
|
1346 |
}
|
|
1347 |
return 0; |
|
1
by Colin Watson
Import upstream version 5.5.4 |
1348 |
}
|
1349 |
||
1350 |
#endif
|
|
1351 |
||
1.1.1
by Colin Watson
Import upstream version 5.6 |
1352 |
/* vi:set tabstop=4 shiftwidth=4: */
|