|
1 /****************************************************************************** |
|
2 * |
|
3 * Parser for syntax hightlighting and references for Fortran90 F subset |
|
4 * |
|
5 * Copyright (C) by Anke Visser |
|
6 * based on the work of Dimitri van Heesch. |
|
7 * |
|
8 * Permission to use, copy, modify, and distribute this software and its |
|
9 * documentation under the terms of the GNU General Public License is hereby |
|
10 * granted. No representations are made about the suitability of this software |
|
11 * for any purpose. It is provided "as is" without express or implied warranty. |
|
12 * See the GNU General Public License for more details. |
|
13 * |
|
14 * Documents produced by Doxygen are derivative works derived from the |
|
15 * input used in their production; they are not affected by this license. |
|
16 * |
|
17 */ |
|
18 |
|
19 /** |
|
20 @todo - continutation lines not always recognized |
|
21 - merging of use-statements with same module name and different only-names |
|
22 - rename part of use-statement |
|
23 - links to interface functions |
|
24 - references to variables |
|
25 **/ |
|
26 |
|
27 %{ |
|
28 |
|
29 /* |
|
30 * includes |
|
31 */ |
|
32 #include "qtbc.h" |
|
33 #include <stdio.h> |
|
34 #include <assert.h> |
|
35 #include <ctype.h> |
|
36 #include <qregexp.h> |
|
37 #include <qdir.h> |
|
38 #include <qstringlist.h> |
|
39 #include "entry.h" |
|
40 #include "doxygen.h" |
|
41 #include "message.h" |
|
42 #include "outputlist.h" |
|
43 #include "util.h" |
|
44 #include "membername.h" |
|
45 #include "searchindex.h" |
|
46 #include "defargs.h" |
|
47 |
|
48 #define YY_NEVER_INTERACTIVE 1 |
|
49 #define YY_NO_TOP_STATE 1 |
|
50 |
|
51 //-------------------------------------------------------------------------------- |
|
52 |
|
53 /** |
|
54 data of an use-statement |
|
55 */ |
|
56 class UseEntry |
|
57 { |
|
58 public: |
|
59 QCString module; // just for debug |
|
60 QStringList onlyNames; /* entries of the ONLY-part */ |
|
61 }; |
|
62 |
|
63 /** |
|
64 module name -> list of ONLY/remote entries |
|
65 (module name = name of the module, which can be accessed via use-directive) |
|
66 */ |
|
67 class UseSDict : public SDict<UseEntry> |
|
68 { |
|
69 public: |
|
70 UseSDict() : SDict<UseEntry>(17) {} |
|
71 }; |
|
72 |
|
73 /** |
|
74 Contains names of used modules and names of local variables. |
|
75 */ |
|
76 class Scope |
|
77 { |
|
78 public: |
|
79 QStringList useNames; //!< contains names of used modules |
|
80 QDict<void> localVars; //!< contains names of local variables |
|
81 |
|
82 Scope() : localVars(7, FALSE /*caseSensitive*/) {} |
|
83 }; |
|
84 |
|
85 /*===================================================================*/ |
|
86 /* |
|
87 * statics |
|
88 */ |
|
89 |
|
90 static QCString docBlock; //!< contents of all lines of a documentation block |
|
91 static QCString currentModule=0; //!< name of the current enclosing module |
|
92 static UseSDict *useMembers= new UseSDict; //!< info about used modules |
|
93 static UseEntry *useEntry = 0; //!< current use statement info |
|
94 static QList<Scope> scopeStack; |
|
95 // static QStringList *currentUseNames= new QStringList; //! contains names of used modules of current program unit |
|
96 static QCString str=""; //!> contents of fortran string |
|
97 |
|
98 static CodeOutputInterface * g_code; |
|
99 |
|
100 // TODO: is this still needed? if so, make it work |
|
101 static QCString g_parmType; |
|
102 static QCString g_parmName; |
|
103 |
|
104 static const char * g_inputString; //!< the code fragment as text |
|
105 static int g_inputPosition; //!< read offset during parsing |
|
106 static int g_inputLines; //!< number of line in the code fragment |
|
107 static int g_yyLineNr; //!< current line number |
|
108 static bool g_needsTermination; |
|
109 static bool g_isFixedForm; |
|
110 |
|
111 static bool g_insideBody; //!< inside subprog/program body? => create links |
|
112 static const char * g_currentFontClass; |
|
113 |
|
114 static bool g_exampleBlock; |
|
115 static QCString g_exampleName; |
|
116 static QCString g_exampleFile; |
|
117 |
|
118 static FileDef * g_sourceFileDef; |
|
119 static Definition * g_currentDefinition; |
|
120 static MemberDef * g_currentMemberDef; |
|
121 static bool g_includeCodeFragment; |
|
122 |
|
123 static char stringStartSymbol; // single or double quote |
|
124 // count in variable declaration to filter out |
|
125 // declared from referenced names |
|
126 static int bracketCount = 0; |
|
127 |
|
128 // simplified way to know if this is fixed form |
|
129 // duplicate in fortranscanner.l |
|
130 static bool recognizeFixedForm(const char* contents) |
|
131 { |
|
132 int column=0; |
|
133 bool skipLine=FALSE; |
|
134 |
|
135 for (int i=0;;i++) |
|
136 { |
|
137 column++; |
|
138 |
|
139 switch(contents[i]) |
|
140 { |
|
141 case '\n': |
|
142 column=0; |
|
143 skipLine=FALSE; |
|
144 break; |
|
145 case ' ': |
|
146 break; |
|
147 case '\000': |
|
148 return FALSE; |
|
149 case 'C': |
|
150 case 'c': |
|
151 case '*': |
|
152 if(column==1) return TRUE; |
|
153 if(skipLine) break; |
|
154 return FALSE; |
|
155 case '!': |
|
156 if(column>1 && column<7) return FALSE; |
|
157 skipLine=TRUE; |
|
158 break; |
|
159 default: |
|
160 if(skipLine) break; |
|
161 if(column==7) return TRUE; |
|
162 return FALSE; |
|
163 } |
|
164 } |
|
165 return FALSE; |
|
166 } |
|
167 |
|
168 static void endFontClass() |
|
169 { |
|
170 if (g_currentFontClass) |
|
171 { |
|
172 g_code->endFontClass(); |
|
173 g_currentFontClass=0; |
|
174 } |
|
175 } |
|
176 |
|
177 static void startFontClass(const char *s) |
|
178 { |
|
179 endFontClass(); |
|
180 g_code->startFontClass(s); |
|
181 g_currentFontClass=s; |
|
182 } |
|
183 |
|
184 static void setCurrentDoc(const QCString &name,const QCString &base,const QCString &anchor="") |
|
185 { |
|
186 if (Doxygen::searchIndex) |
|
187 { |
|
188 Doxygen::searchIndex->setCurrentDoc(name,base,anchor); |
|
189 } |
|
190 } |
|
191 |
|
192 static void addToSearchIndex(const char *text) |
|
193 { |
|
194 if (Doxygen::searchIndex) |
|
195 { |
|
196 Doxygen::searchIndex->addWord(text,FALSE); |
|
197 } |
|
198 } |
|
199 |
|
200 /*! start a new line of code, inserting a line number if g_sourceFileDef |
|
201 * is TRUE. If a definition starts at the current line, then the line |
|
202 * number is linked to the documentation of that definition. |
|
203 */ |
|
204 static void startCodeLine() |
|
205 { |
|
206 if (g_sourceFileDef) |
|
207 { |
|
208 //QCString lineNumber,lineAnchor; |
|
209 //lineNumber.sprintf("%05d",g_yyLineNr); |
|
210 //lineAnchor.sprintf("l%05d",g_yyLineNr); |
|
211 |
|
212 Definition *d = g_sourceFileDef->getSourceDefinition(g_yyLineNr); |
|
213 //printf("startCodeLine %d d=%s\n", g_yyLineNr,d ? d->name().data() : "<null>"); |
|
214 if (!g_includeCodeFragment && d) |
|
215 { |
|
216 g_currentDefinition = d; |
|
217 g_currentMemberDef = g_sourceFileDef->getSourceMember(g_yyLineNr); |
|
218 g_insideBody = FALSE; |
|
219 g_parmType.resize(0); |
|
220 g_parmName.resize(0); |
|
221 QCString lineAnchor; |
|
222 lineAnchor.sprintf("l%05d",g_yyLineNr); |
|
223 if (g_currentMemberDef) |
|
224 { |
|
225 g_code->writeLineNumber(g_currentMemberDef->getReference(), |
|
226 g_currentMemberDef->getOutputFileBase(), |
|
227 g_currentMemberDef->anchor(),g_yyLineNr); |
|
228 setCurrentDoc( |
|
229 g_currentMemberDef->qualifiedName(), |
|
230 g_sourceFileDef->getSourceFileBase(), |
|
231 lineAnchor); |
|
232 } |
|
233 else if (d->isLinkableInProject()) |
|
234 { |
|
235 g_code->writeLineNumber(d->getReference(), |
|
236 d->getOutputFileBase(), |
|
237 0,g_yyLineNr); |
|
238 setCurrentDoc( |
|
239 d->qualifiedName(), |
|
240 g_sourceFileDef->getSourceFileBase(), |
|
241 lineAnchor); |
|
242 } |
|
243 } |
|
244 else |
|
245 { |
|
246 g_code->writeLineNumber(0,0,0,g_yyLineNr); |
|
247 } |
|
248 } |
|
249 g_code->startCodeLine(); |
|
250 if (g_currentFontClass) |
|
251 { |
|
252 g_code->startFontClass(g_currentFontClass); |
|
253 } |
|
254 } |
|
255 |
|
256 |
|
257 static void endFontClass(); |
|
258 static void endCodeLine() |
|
259 { |
|
260 endFontClass(); |
|
261 g_code->endCodeLine(); |
|
262 } |
|
263 |
|
264 /*! write a code fragment `text' that may span multiple lines, inserting |
|
265 * line numbers for each line. |
|
266 */ |
|
267 static void codifyLines(char *text) |
|
268 { |
|
269 //printf("codifyLines(%d,\"%s\")\n",g_yyLineNr,text); |
|
270 char *p=text,*sp=p; |
|
271 char c; |
|
272 bool done=FALSE; |
|
273 while (!done) |
|
274 { |
|
275 sp=p; |
|
276 while ((c=*p++) && c!='\n') { } |
|
277 if (c=='\n') |
|
278 { |
|
279 g_yyLineNr++; |
|
280 *(p-1)='\0'; |
|
281 g_code->codify(sp); |
|
282 endCodeLine(); |
|
283 if (g_yyLineNr<g_inputLines) |
|
284 { |
|
285 startCodeLine(); |
|
286 } |
|
287 } |
|
288 else |
|
289 { |
|
290 g_code->codify(sp); |
|
291 done=TRUE; |
|
292 } |
|
293 } |
|
294 } |
|
295 |
|
296 static void codifyLines(QCString str) |
|
297 { |
|
298 char *tmp= (char *) malloc(str.length()+1); |
|
299 strcpy(tmp, str); |
|
300 codifyLines(tmp); |
|
301 free(tmp); |
|
302 } |
|
303 |
|
304 /*! writes a link to a fragment \a text that may span multiple lines, inserting |
|
305 * line numbers for each line. If \a text contains newlines, the link will be |
|
306 * split into multiple links with the same destination, one for each line. |
|
307 */ |
|
308 static void writeMultiLineCodeLink(CodeOutputInterface &ol, |
|
309 const char *ref,const char *file, |
|
310 const char *anchor,const char *text) |
|
311 { |
|
312 bool done=FALSE; |
|
313 char *p=(char *)text; |
|
314 while (!done) |
|
315 { |
|
316 char *sp=p; |
|
317 char c; |
|
318 while ((c=*p++) && c!='\n') { } |
|
319 if (c=='\n') |
|
320 { |
|
321 g_yyLineNr++; |
|
322 *(p-1)='\0'; |
|
323 //printf("writeCodeLink(%s,%s,%s,%s)\n",ref,file,anchor,sp); |
|
324 ol.writeCodeLink(ref,file,anchor,sp,0); |
|
325 endCodeLine(); |
|
326 if (g_yyLineNr<g_inputLines) |
|
327 { |
|
328 startCodeLine(); |
|
329 } |
|
330 } |
|
331 else |
|
332 { |
|
333 //printf("writeCodeLink(%s,%s,%s,%s)\n",ref,file,anchor,sp); |
|
334 ol.writeCodeLink(ref,file,anchor,sp,0); |
|
335 done=TRUE; |
|
336 } |
|
337 } |
|
338 } |
|
339 |
|
340 /** |
|
341 generates dictionay entries that are used if REFERENCED_BY_RELATION ... options are set |
|
342 (e.g. the "referenced by ..." list after the function documentation) |
|
343 */ |
|
344 |
|
345 static void addDocCrossReference(MemberDef *src, MemberDef *dst) |
|
346 { |
|
347 if (dst->isTypedef() || dst->isEnumerate()) return; // don't add types |
|
348 //printf("======= addDocCrossReference src=%s,dst=%s\n",src->name().data(),dst->name().data()); |
|
349 if ((Config_getBool("REFERENCED_BY_RELATION") || Config_getBool("CALLER_GRAPH")) && |
|
350 (src->isFunction())) |
|
351 { |
|
352 dst->addSourceReferencedBy(src); |
|
353 } |
|
354 if ((Config_getBool("REFERENCES_RELATION") || Config_getBool("CALL_GRAPH")) && (src->isFunction())) |
|
355 { |
|
356 src->addSourceReferences(dst); |
|
357 } |
|
358 } |
|
359 |
|
360 //------------------------------------------------------------------------------- |
|
361 /** |
|
362 searches for definition of a type |
|
363 @param tname the name of the type |
|
364 @param moduleName name of enclosing module or null, if global entry |
|
365 @param cd the entry, if found or null |
|
366 @param useDict dictionary of data of USE-statement |
|
367 @returns true, if type is found |
|
368 */ |
|
369 static bool getFortranTypeDefs(const QCString &tname, const QCString &moduleName, |
|
370 ClassDef *&cd, UseSDict *usedict=0) |
|
371 { |
|
372 if (tname.isEmpty()) return FALSE; /* empty name => nothing to link */ |
|
373 |
|
374 //cout << "=== search for type: " << tname << endl; |
|
375 |
|
376 // search for type |
|
377 if ((cd=Doxygen::classSDict->find(tname))) |
|
378 { |
|
379 //cout << "=== type found in global module" << endl; |
|
380 return TRUE; |
|
381 } |
|
382 else if (moduleName && (cd= Doxygen::classSDict->find(moduleName+"::"+tname))) |
|
383 { |
|
384 //cout << "=== type found in local module" << endl; |
|
385 return TRUE; |
|
386 } |
|
387 else |
|
388 { |
|
389 UseEntry *use; |
|
390 for (UseSDict::Iterator di(*usedict); (use=di.current()); ++di) |
|
391 if ((cd= Doxygen::classSDict->find(use->module+"::"+tname))) |
|
392 { |
|
393 //cout << "=== type found in used module" << endl; |
|
394 return TRUE; |
|
395 } |
|
396 } |
|
397 |
|
398 return FALSE; |
|
399 } |
|
400 |
|
401 /** |
|
402 searches for definition of function memberName |
|
403 @param memberName the name of the function/variable |
|
404 @param moduleName name of enclosing module or null, if global entry |
|
405 @param md the entry, if found or null |
|
406 @param usedict array of data of USE-statement |
|
407 @returns true, if found |
|
408 */ |
|
409 static bool getFortranDefs(const QCString &memberName, const QCString &moduleName, |
|
410 MemberDef *&md, UseSDict *usedict=0) |
|
411 { |
|
412 if (memberName.isEmpty()) return FALSE; /* empty name => nothing to link */ |
|
413 |
|
414 // look in local variables |
|
415 for (Scope *scope=scopeStack.last(); scope!=NULL; scope=scopeStack.prev()) |
|
416 { |
|
417 if(scope->localVars.find(memberName)) |
|
418 return FALSE; |
|
419 } |
|
420 |
|
421 // search for function |
|
422 MemberName *mn = Doxygen::functionNameSDict->find(memberName); |
|
423 |
|
424 if (mn) // name is known |
|
425 { |
|
426 MemberListIterator mli(*mn); |
|
427 for (mli.toFirst();(md=mli.current());++mli) // all found functions with given name |
|
428 { |
|
429 FileDef *fd=md->getFileDef(); |
|
430 GroupDef *gd=md->getGroupDef(); |
|
431 |
|
432 //cout << "found link with same name: " << fd->fileName() << " " << memberName; |
|
433 //if (md->getNamespaceDef() != 0) cout << " in namespace " << md->getNamespaceDef()->name();cout << endl; |
|
434 |
|
435 if ((gd && gd->isLinkable()) || (fd && fd->isLinkable())) |
|
436 { |
|
437 NamespaceDef *nspace= md->getNamespaceDef(); |
|
438 |
|
439 if (nspace == 0) |
|
440 { // found function in global scope |
|
441 return TRUE; |
|
442 } |
|
443 else if (moduleName == nspace->name()) |
|
444 { // found in local scope |
|
445 return TRUE; |
|
446 } |
|
447 else |
|
448 { // else search in used modules |
|
449 QCString moduleName= nspace->name(); |
|
450 UseEntry *ue= usedict->find(moduleName); |
|
451 if (ue) |
|
452 { |
|
453 // check if only-list exists and if current entry exists is this list |
|
454 QStringList &only= ue->onlyNames; |
|
455 if (only.isEmpty()) |
|
456 { |
|
457 //cout << " found in module " << moduleName << " entry " << memberName << endl; |
|
458 return TRUE; // whole module used |
|
459 } |
|
460 else |
|
461 { |
|
462 for ( QStringList::Iterator it = only.begin(); it != only.end(); ++it) |
|
463 { |
|
464 //cout << " search in only: " << moduleName << ":: " << memberName << "==" << (*it)<< endl; |
|
465 if (memberName == (QCString)(*it)) |
|
466 return TRUE; // found in ONLY-part of use list |
|
467 } |
|
468 } |
|
469 } |
|
470 } |
|
471 } // if linkable |
|
472 } // for |
|
473 } |
|
474 return FALSE; |
|
475 } |
|
476 |
|
477 /** |
|
478 gets the link to a generic procedure which depends not on the name, but on the parameter list |
|
479 @todo implementation |
|
480 */ |
|
481 static bool getGenericProcedureLink(const ClassDef *cd, |
|
482 const char *memberText, |
|
483 CodeOutputInterface &ol) |
|
484 { |
|
485 (void)cd; |
|
486 (void)memberText; |
|
487 (void)ol; |
|
488 return FALSE; |
|
489 } |
|
490 |
|
491 static bool getLink(UseSDict *usedict, // dictonary with used modules |
|
492 const char *memberText, // exact member text |
|
493 CodeOutputInterface &ol, |
|
494 const char *text) |
|
495 { |
|
496 MemberDef *md; |
|
497 QCString memberName= removeRedundantWhiteSpace(memberText); |
|
498 |
|
499 if (getFortranDefs(memberName, currentModule, md, usedict) && md->isLinkable()) |
|
500 { |
|
501 //if (md->isVariable()) return FALSE; // variables aren't handled yet |
|
502 |
|
503 Definition *d = md->getOuterScope()==Doxygen::globalScope ? |
|
504 md->getBodyDef() : md->getOuterScope(); |
|
505 if (md->getGroupDef()) d = md->getGroupDef(); |
|
506 if (d && d->isLinkable()) |
|
507 { |
|
508 if (g_currentDefinition && g_currentMemberDef && md!=g_currentMemberDef && g_insideBody) |
|
509 { |
|
510 addDocCrossReference(g_currentMemberDef,md); |
|
511 } |
|
512 ol.linkableSymbol(g_yyLineNr,md->name(),md, |
|
513 g_currentMemberDef ? g_currentMemberDef : g_currentDefinition); |
|
514 writeMultiLineCodeLink(ol,md->getReference(), |
|
515 md->getOutputFileBase(), |
|
516 md->anchor(), |
|
517 text ? text : memberText); |
|
518 addToSearchIndex(text ? text : memberText); |
|
519 return TRUE; |
|
520 } |
|
521 } |
|
522 return FALSE; |
|
523 } |
|
524 |
|
525 |
|
526 static void generateLink(CodeOutputInterface &ol, char *lname) |
|
527 { |
|
528 ClassDef *cd=0; |
|
529 |
|
530 // check if lname is a linkable type or interface |
|
531 if ( (getFortranTypeDefs(lname, currentModule, cd, useMembers)) && cd->isLinkable() ) |
|
532 { |
|
533 if ( (cd->compoundType() == ClassDef::Class) && // was Entry::INTERFACE_SEC) && |
|
534 (getGenericProcedureLink(cd, lname, ol)) ) |
|
535 { |
|
536 //cout << "=== generic procedure resolved" << endl; |
|
537 } |
|
538 else |
|
539 { // write type or interface link |
|
540 ol.linkableSymbol(g_yyLineNr, lname, cd, g_currentMemberDef?g_currentMemberDef:g_currentDefinition); |
|
541 writeMultiLineCodeLink(ol,cd->getReference(),cd->getOutputFileBase(),0,lname); |
|
542 addToSearchIndex(lname); |
|
543 } |
|
544 } |
|
545 // check for function/variable |
|
546 else if (getLink(useMembers, lname, ol, lname)) |
|
547 { |
|
548 //cout << "=== found link for " << lname << endl; |
|
549 } |
|
550 else |
|
551 { |
|
552 // nothing found, just write out the word |
|
553 ol.linkableSymbol(g_yyLineNr, lname, 0, g_currentMemberDef?g_currentMemberDef:g_currentDefinition); |
|
554 //startFontClass("charliteral"); //test |
|
555 codifyLines(lname); |
|
556 //endFontClass(); //test |
|
557 addToSearchIndex(lname); |
|
558 } |
|
559 } |
|
560 |
|
561 /*! counts the number of lines in the input */ |
|
562 static int countLines() |
|
563 { |
|
564 const char *p=g_inputString; |
|
565 char c; |
|
566 int count=1; |
|
567 while ((c=*p)) |
|
568 { |
|
569 p++ ; |
|
570 if (c=='\n') count++; |
|
571 } |
|
572 if (p>g_inputString && *(p-1)!='\n') |
|
573 { // last line does not end with a \n, so we add an extra |
|
574 // line and explicitly terminate the line after parsing. |
|
575 count++, |
|
576 g_needsTermination=TRUE; |
|
577 } |
|
578 return count; |
|
579 } |
|
580 |
|
581 //---------------------------------------------------------------------------- |
|
582 /** start scope */ |
|
583 void startScope() |
|
584 { |
|
585 // fprintf(stderr, "===> startScope %s",yytext); |
|
586 Scope *scope = new Scope; |
|
587 scopeStack.append(scope); |
|
588 } |
|
589 |
|
590 /** end scope */ |
|
591 void endScope() |
|
592 { |
|
593 // fprintf(stderr,"===> endScope %s",yytext); |
|
594 if (scopeStack.isEmpty()) |
|
595 { |
|
596 fprintf(stderr,"WARNING: fortrancode.l: stack empty!"); |
|
597 return; |
|
598 //exit(-1); |
|
599 } |
|
600 |
|
601 Scope *scope = scopeStack.getLast(); |
|
602 scopeStack.removeLast(); |
|
603 for ( QStringList::Iterator it = scope->useNames.begin(); it != scope->useNames.end(); ++it) |
|
604 { |
|
605 useMembers->remove(*it); |
|
606 } |
|
607 delete scope; |
|
608 } |
|
609 |
|
610 void addUse(QString moduleName) |
|
611 { |
|
612 if (!scopeStack.isEmpty()) |
|
613 scopeStack.last()->useNames.append(moduleName); |
|
614 } |
|
615 |
|
616 void addLocalVar(QString varName) |
|
617 { |
|
618 if (!scopeStack.isEmpty()) |
|
619 scopeStack.last()->localVars.insert(varName, (void*)1); |
|
620 } |
|
621 |
|
622 //---------------------------------------------------------------------------- |
|
623 |
|
624 /* -----------------------------------------------------------------*/ |
|
625 #undef YY_INPUT |
|
626 #define YY_INPUT(buf,result,max_size) result=yyread(buf,max_size); |
|
627 |
|
628 static int yyread(char *buf,int max_size) |
|
629 { |
|
630 int c=0; |
|
631 while( c < max_size && g_inputString[g_inputPosition] ) |
|
632 { |
|
633 *buf = g_inputString[g_inputPosition++] ; |
|
634 c++; buf++; |
|
635 } |
|
636 return c; |
|
637 } |
|
638 |
|
639 %} |
|
640 |
|
641 IDSYM [a-z_A-Z0-9] |
|
642 ID [a-z_A-Z]+{IDSYM}* |
|
643 SUBPROG (subroutine|function) |
|
644 B [ \t] |
|
645 BS [ \t]* |
|
646 BS_ [ \t]+ |
|
647 COMMA {BS},{BS} |
|
648 ARGS {BS}("("[^)]*")"){BS} |
|
649 |
|
650 NUM_TYPE (complex|integer|logical|real) |
|
651 KIND {ARGS} |
|
652 CHAR (CHARACTER{ARGS}?|CHARACTER{BS}"*"({BS}[0-9]+|{ARGS})) |
|
653 TYPE_SPEC (({NUM_TYPE}({BS}"*"{BS}[0-9]+)?)|({NUM_TYPE}{KIND})|DOUBLE{B}PRECISION|{CHAR}) |
|
654 |
|
655 INTENT_SPEC intent{BS}"("{BS}(in|out|in{BS}out){BS}")" |
|
656 ATTR_SPEC (ALLOCATABLE|DIMENSION{ARGS}|EXTERNAL|{INTENT_SPEC}|INTRINSIC|OPTIONAL|PARAMETER|POINTER|PRIVATE|PUBLIC|SAVE|TARGET|RECURSIVE|PURE|ELEMENTAL) |
|
657 ACCESS_SPEC (PRIVATE|PUBLIC) |
|
658 /* Assume that attribute statements are almost the same as attributes. */ |
|
659 ATTR_STMT {ATTR_SPEC}|DIMENSION |
|
660 COMMANDS (BLOCK{BS}DATA|DO|SELECT|CASE|WHERE|IF|THEN|ELSE|MODULE{BS_}PROCEDURE|CONTAINS|IMPLICIT{BS}NONE|CONTAINS|WRITE|READ|ALLOCATE|ALLOCATED|ASSOCIATED|DEALLOCATE|SIZE|END{BS}IF|END{BS}DO|WHILE|INQUIRE|OPEN|CLOSE|DATA) |
|
661 IGNORE (CALL) |
|
662 |
|
663 /* | */ |
|
664 |
|
665 %option noyywrap |
|
666 %option stack |
|
667 %option caseless |
|
668 /*%option debug*/ |
|
669 |
|
670 %x Start |
|
671 %x SubCall |
|
672 %x FuncDef |
|
673 %x ClassName |
|
674 %x ClassVar |
|
675 %x Subprog |
|
676 %x DocBlock |
|
677 %x Use |
|
678 %x UseOnly |
|
679 %x TypeDecl |
|
680 %x Declaration |
|
681 %x DeclContLine |
|
682 %x Parameterlist |
|
683 %x String |
|
684 |
|
685 %% |
|
686 /*==================================================================*/ |
|
687 |
|
688 /*-------- ignore ------------------------------------------------------------*/ |
|
689 |
|
690 <Start>{IGNORE}/{BS}"("? { // do not search keywords, intrinsics... TODO: complete list |
|
691 codifyLines(yytext); |
|
692 } |
|
693 /*-------- inner construct ---------------------------------------------------*/ |
|
694 |
|
695 <Start>{COMMANDS}/[,( \t\n].* { // hightlight rest of fortran statements |
|
696 /* font class is defined e.g. in doxygen.css */ |
|
697 startFontClass("keyword"); |
|
698 codifyLines(yytext); |
|
699 endFontClass(); |
|
700 } |
|
701 <Start>"end"({BS_}{COMMANDS})?/[ \t\n] { |
|
702 startFontClass("keyword"); |
|
703 codifyLines(yytext); |
|
704 endFontClass(); |
|
705 } |
|
706 |
|
707 /*-------- use statement -------------------------------------------*/ |
|
708 <Start>"use"{BS_} { |
|
709 codifyLines(yytext); |
|
710 yy_push_state(YY_START); |
|
711 BEGIN(Use); |
|
712 } |
|
713 <Use>{ID} { |
|
714 startFontClass("keywordflow"); |
|
715 codifyLines(yytext); |
|
716 endFontClass(); |
|
717 |
|
718 /* append module name to use dict */ |
|
719 useEntry = new UseEntry(); |
|
720 useEntry->module = yytext; |
|
721 useMembers->append(yytext, useEntry); |
|
722 addUse(yytext); |
|
723 } |
|
724 <Use>,{BS}"ONLY" { // TODO: rename |
|
725 codifyLines(yytext); |
|
726 yy_push_state(YY_START); |
|
727 BEGIN(UseOnly); |
|
728 } |
|
729 <UseOnly>{BS},{BS} { codifyLines(yytext); } |
|
730 <UseOnly>{ID} { |
|
731 codifyLines(yytext); |
|
732 useEntry->onlyNames.append(yytext); |
|
733 } |
|
734 <Use,UseOnly>"\n" { |
|
735 unput(*yytext); |
|
736 yy_pop_state(); |
|
737 } |
|
738 |
|
739 /*-------- fortran module -----------------------------------------*/ |
|
740 <Start>("program"|"module"|"type"|"interface")/{BS_}|({COMMA}{ACCESS_SPEC})|\n { // |
|
741 startScope(); |
|
742 startFontClass("keyword"); |
|
743 codifyLines(yytext); |
|
744 endFontClass(); |
|
745 yy_push_state(YY_START); |
|
746 BEGIN(ClassName); |
|
747 if (!stricmp(yytext,"module")) currentModule="module"; |
|
748 } |
|
749 <ClassName>{ID} { |
|
750 if (currentModule == "module") currentModule=yytext; |
|
751 generateLink(*g_code,yytext); |
|
752 yy_pop_state(); |
|
753 } |
|
754 <ClassName>\n { // interface may be without name |
|
755 yy_pop_state(); |
|
756 REJECT; |
|
757 } |
|
758 <Start>"end"({BS_}"module").* { // just reset currentModule, rest is done in following rule |
|
759 currentModule=0; |
|
760 REJECT; |
|
761 } |
|
762 <Start>^{BS}"end"({BS}("program"|"module"|"type"|"interface")({BS_}{ID})?)?{BS}/(\n|!) { // |
|
763 endScope(); |
|
764 startFontClass("keyword"); |
|
765 codifyLines(yytext); |
|
766 endFontClass(); |
|
767 } |
|
768 |
|
769 /*-------- subprog definition -------------------------------------*/ |
|
770 <Start>{TYPE_SPEC}{BS}/{SUBPROG}{BS_} { // TYPE_SPEC is for old function style function result |
|
771 startFontClass("keyword"); |
|
772 codifyLines(yytext); |
|
773 endFontClass(); |
|
774 } |
|
775 <Start>{SUBPROG}{BS_} { // Fortran subroutine or function found |
|
776 startFontClass("keyword"); |
|
777 codifyLines(yytext); |
|
778 endFontClass(); |
|
779 yy_push_state(YY_START); |
|
780 BEGIN(Subprog); |
|
781 } |
|
782 <Subprog>{ID} { // subroutine/function name |
|
783 // fprintf(stderr, "===> start subprogram %s\n", yytext); |
|
784 startScope(); |
|
785 generateLink(*g_code,yytext); |
|
786 } |
|
787 <Subprog>"(".* { // ignore rest of line |
|
788 codifyLines(yytext); |
|
789 } |
|
790 <Subprog>"\n" { codifyLines(yytext); |
|
791 yy_pop_state(); |
|
792 } |
|
793 <Start>^{BS}"end"({BS}{SUBPROG}({BS_}{ID})?)?{BS}/(\n|!) { // Fortran subroutine or function ends |
|
794 //cout << "===> end function " << yytext << endl; |
|
795 endScope(); |
|
796 startFontClass("keyword"); |
|
797 codifyLines(yytext); |
|
798 endFontClass(); |
|
799 } |
|
800 /*-------- variable declaration ----------------------------------*/ |
|
801 <Start>"TYPE"{BS}"(" { |
|
802 yy_push_state(YY_START); |
|
803 BEGIN(TypeDecl); |
|
804 startFontClass("keywordtype"); |
|
805 g_code->codify(yytext); |
|
806 endFontClass(); |
|
807 } |
|
808 <TypeDecl>{ID} { // link type |
|
809 g_insideBody=TRUE; |
|
810 generateLink(*g_code,yytext); |
|
811 g_insideBody=FALSE; |
|
812 } |
|
813 <TypeDecl>")" { |
|
814 BEGIN(Declaration); |
|
815 startFontClass("keywordtype"); |
|
816 g_code->codify(yytext); |
|
817 endFontClass(); |
|
818 } |
|
819 <Start>{TYPE_SPEC}/[,:( ] { |
|
820 yy_push_state(YY_START); |
|
821 BEGIN(Declaration); |
|
822 startFontClass("keywordtype"); |
|
823 g_code->codify(yytext); |
|
824 endFontClass(); |
|
825 } |
|
826 <Start>{ATTR_SPEC} { |
|
827 startFontClass("keywordtype"); |
|
828 g_code->codify(yytext); |
|
829 endFontClass(); |
|
830 } |
|
831 <Declaration>({TYPE_SPEC}|{ATTR_SPEC})/[,:( ] { //| variable deklaration |
|
832 startFontClass("keywordtype"); |
|
833 g_code->codify(yytext); |
|
834 endFontClass(); |
|
835 } |
|
836 <Declaration>{ID} { // local var |
|
837 g_code->codify(yytext); |
|
838 if (g_currentMemberDef && g_currentMemberDef->isFunction()) |
|
839 addLocalVar(yytext); |
|
840 } |
|
841 <Declaration>[(] { // start of array specification |
|
842 bracketCount++; |
|
843 g_code->codify(yytext); |
|
844 } |
|
845 |
|
846 <Declaration>[)] { // end array specification |
|
847 bracketCount--; |
|
848 g_code->codify(yytext); |
|
849 } |
|
850 |
|
851 <Declaration>"&" { // continuation line |
|
852 yy_push_state(YY_START); |
|
853 BEGIN(DeclContLine); |
|
854 } |
|
855 <DeclContLine>"\n" { // declaration not yet finished |
|
856 codifyLines(yytext); |
|
857 bracketCount = 0; |
|
858 yy_pop_state(); |
|
859 } |
|
860 <Declaration>"\n" { // end declaration line |
|
861 codifyLines(yytext); |
|
862 bracketCount = 0; |
|
863 yy_pop_state(); |
|
864 } |
|
865 |
|
866 /*-------- subprog calls -----------------------------------------*/ |
|
867 |
|
868 <Start>"call"{BS_} { |
|
869 codifyLines(yytext); |
|
870 yy_push_state(YY_START); |
|
871 BEGIN(SubCall); |
|
872 } |
|
873 <SubCall>{ID} { // subroutine call |
|
874 g_insideBody=TRUE; |
|
875 generateLink(*g_code, yytext); |
|
876 g_insideBody=FALSE; |
|
877 yy_pop_state(); |
|
878 } |
|
879 <Start>{ID}{BS}/"(" { // function call |
|
880 g_insideBody=TRUE; |
|
881 generateLink(*g_code, yytext); |
|
882 g_insideBody=FALSE; |
|
883 } |
|
884 |
|
885 /*-------- comments ---------------------------------------------------*/ |
|
886 <Start>\n?{BS}"!>" { // start comment line or comment block |
|
887 yy_push_state(YY_START); |
|
888 BEGIN(DocBlock); |
|
889 docBlock=yytext; |
|
890 } |
|
891 |
|
892 <DocBlock>.* { // contents of current comment line |
|
893 docBlock+=yytext; |
|
894 } |
|
895 <DocBlock>"\n"{BS}("!>"|"!"+) { //| comment block (next line is also comment line) |
|
896 docBlock+=yytext; |
|
897 } |
|
898 <DocBlock>"\n" { // comment block ends at the end of this line |
|
899 docBlock+=yytext; |
|
900 // remove special comment (default config) |
|
901 if (Config_getBool("STRIP_CODE_COMMENTS")) |
|
902 { |
|
903 g_yyLineNr+=((QCString)docBlock).contains('\n'); |
|
904 endCodeLine(); |
|
905 if (g_yyLineNr<g_inputLines) |
|
906 { |
|
907 startCodeLine(); |
|
908 } |
|
909 } |
|
910 else // do not remove comment |
|
911 { |
|
912 startFontClass("comment"); |
|
913 codifyLines(docBlock); |
|
914 endFontClass(); |
|
915 } |
|
916 yy_pop_state(); |
|
917 } |
|
918 |
|
919 <*>"!"[^>\n].*|"!"$ { // normal comment |
|
920 if(YY_START == String) REJECT; // ignore in strings |
|
921 startFontClass("comment"); |
|
922 codifyLines(yytext); |
|
923 endFontClass(); |
|
924 } |
|
925 |
|
926 <*>^[Cc*].* { // normal comment |
|
927 if(! g_isFixedForm) REJECT; |
|
928 |
|
929 startFontClass("comment"); |
|
930 codifyLines(yytext); |
|
931 endFontClass(); |
|
932 } |
|
933 |
|
934 /*------ preprocessor --------------------------------------------*/ |
|
935 <Start>"#".*\n { startFontClass("preprocessor"); |
|
936 codifyLines(yytext); |
|
937 endFontClass(); |
|
938 } |
|
939 /*------ variable references? -------------------------------------*/ |
|
940 |
|
941 <Start>"%"{BS}{ID} { // ignore references to elements |
|
942 g_code->codify(yytext); |
|
943 } |
|
944 <Start>{ID} { |
|
945 g_insideBody=TRUE; |
|
946 generateLink(*g_code, yytext); |
|
947 g_insideBody=FALSE; |
|
948 } |
|
949 /*------ strings --------------------------------------------------*/ |
|
950 <*>"\\\\" { str+=yytext; /* ignore \\ */} |
|
951 <*>"\\\""|\\\' { str+=yytext; /* ignore \" */} |
|
952 |
|
953 <String>\"|\' { // string ends with next quote without previous backspace |
|
954 if(yytext[0]!=stringStartSymbol) REJECT; // single vs double quote |
|
955 str+=yytext; |
|
956 startFontClass("stringliteral"); |
|
957 codifyLines(str); |
|
958 endFontClass(); |
|
959 yy_pop_state(); |
|
960 } |
|
961 <String>. {str+=yytext;} |
|
962 |
|
963 <*>\"|\' { /* string starts */ |
|
964 /* if(YY_START == StrIgnore) REJECT; // ignore in simple comments */ |
|
965 yy_push_state(YY_START); |
|
966 stringStartSymbol=yytext[0]; // single or double quote |
|
967 BEGIN(String); |
|
968 str=yytext; |
|
969 } |
|
970 /*-----------------------------------------------------------------------------*/ |
|
971 |
|
972 <*>\n { |
|
973 codifyLines(yytext); |
|
974 } |
|
975 <*>. { |
|
976 g_code->codify(yytext); |
|
977 } |
|
978 %% |
|
979 |
|
980 /*@ ---------------------------------------------------------------------------- |
|
981 */ |
|
982 |
|
983 /*===================================================================*/ |
|
984 |
|
985 |
|
986 void resetFortranCodeParserState() {} |
|
987 |
|
988 void parseFortranCode(CodeOutputInterface &od,const char *className,const QCString &s, |
|
989 bool exBlock, const char *exName,FileDef *fd, |
|
990 int startLine,int endLine,bool inlineFragment, |
|
991 MemberDef *memberDef) |
|
992 { |
|
993 //printf("***parseCode() exBlock=%d exName=%s fd=%p\n",exBlock,exName,fd); |
|
994 |
|
995 // used parameters |
|
996 (void)memberDef; |
|
997 (void)className; |
|
998 |
|
999 if (s.isEmpty()) return; |
|
1000 g_code = &od; |
|
1001 g_inputString = s; |
|
1002 g_inputPosition = 0; |
|
1003 g_isFixedForm = recognizeFixedForm((const char*)s); |
|
1004 g_currentFontClass = 0; |
|
1005 g_needsTermination = FALSE; |
|
1006 if (endLine!=-1) |
|
1007 g_inputLines = endLine+1; |
|
1008 else |
|
1009 g_inputLines = countLines(); |
|
1010 |
|
1011 if (startLine!=-1) |
|
1012 g_yyLineNr = startLine; |
|
1013 else |
|
1014 g_yyLineNr = 1; |
|
1015 |
|
1016 g_exampleBlock = exBlock; |
|
1017 g_exampleName = exName; |
|
1018 g_sourceFileDef = fd; |
|
1019 if (exBlock && fd==0) |
|
1020 { |
|
1021 // create a dummy filedef for the example |
|
1022 g_sourceFileDef = new FileDef("",exName); |
|
1023 } |
|
1024 if (g_sourceFileDef) |
|
1025 { |
|
1026 setCurrentDoc(g_sourceFileDef->name(),g_sourceFileDef->getSourceFileBase()); |
|
1027 } |
|
1028 g_currentDefinition = 0; |
|
1029 g_currentMemberDef = 0; |
|
1030 if (!g_exampleName.isEmpty()) |
|
1031 { |
|
1032 g_exampleFile = convertNameToFile(g_exampleName+"-example"); |
|
1033 } |
|
1034 g_includeCodeFragment = inlineFragment; |
|
1035 startCodeLine(); |
|
1036 g_parmName.resize(0); |
|
1037 g_parmType.resize(0); |
|
1038 fcodeYYrestart( fcodeYYin ); |
|
1039 BEGIN( Start ); |
|
1040 fcodeYYlex(); |
|
1041 if (g_needsTermination) |
|
1042 { |
|
1043 endFontClass(); |
|
1044 g_code->endCodeLine(); |
|
1045 } |
|
1046 if (exBlock && g_sourceFileDef) |
|
1047 { |
|
1048 // delete the temporary file definition used for this example |
|
1049 delete g_sourceFileDef; |
|
1050 g_sourceFileDef=0; |
|
1051 } |
|
1052 return; |
|
1053 } |
|
1054 |
|
1055 #if !defined(YY_FLEX_SUBMINOR_VERSION) |
|
1056 extern "C" { // some bogus code to keep the compiler happy |
|
1057 void fcodeYYdummy() { yy_flex_realloc(0,0); } |
|
1058 } |
|
1059 #elif YY_FLEX_SUBMINOR_VERSION<33 |
|
1060 #error "You seem to be using a version of flex newer than 2.5.4 but older than 2.5.33. These versions do NOT work with doxygen! Please use version <=2.5.4 or >=2.5.33 or expect things to be parsed wrongly!" |
|
1061 #else |
|
1062 extern "C" { // some bogus code to keep the compiler happy |
|
1063 void fcodeYYdummy() { yy_top_state(); } |
|
1064 } |
|
1065 #endif |
|
1066 |