953cd6bdd44962c423efa0ddf08fb9f6817f7968
[platform/upstream/doxygen.git] / src / vhdljjparser.cpp
1 /******************************************************************************
2  *
3  * Copyright (C) 2014 by M. Kreis
4  *
5  * Permission to use, copy, modify, and distribute this software and its
6  * documentation under the terms of the GNU General Public License is hereby
7  * granted. No representations are made about the suitability of this software
8  * for any purpose. It is provided "as is" without express or implied warranty.
9  * See the GNU General Public License for more details.
10  *
11  */
12
13 #include <qcstring.h>
14 #include <qfileinfo.h>
15 #include <qstringlist.h>
16 #include "vhdljjparser.h"
17 #include "vhdlcode.h"
18 #include "vhdldocgen.h"
19 #include "message.h"
20 #include "config.h"
21 #include "doxygen.h"
22 #include "util.h"
23 #include "language.h"
24 #include "commentscan.h"
25 #include "index.h"
26 #include "definition.h"
27 #include "searchindex.h"
28 #include "outputlist.h"
29 #include "arguments.h"
30 #include "types.h"
31 #include "VhdlParserIF.h"
32
33 using namespace vhdl::parser;
34 using namespace std;
35
36 static ParserInterface *g_thisParser;
37
38 static QCString         yyFileName;
39 static int              yyLineNr      = 1;
40 static int*             lineParse;
41 static int              iDocLine      = -1;
42 static QCString         inputString;
43 static Entry*           gBlock        = 0;
44 static Entry*           previous      = 0;
45 //-------------------------------------------------------
46
47 static Entry* oldEntry;
48 static bool varr=FALSE;
49 static QCString varName;
50
51 static QList<Entry> instFiles;
52 static QList<Entry> libUse;
53 static QList<Entry> lineEntry;
54
55 Entry*   VhdlParser::currentCompound=0;
56 Entry*   VhdlParser::tempEntry=0;
57 Entry*   VhdlParser::lastEntity=0  ;
58 Entry*   VhdlParser::lastCompound=0  ;
59 Entry*   VhdlParser::current=0;
60 Entry*   VhdlParser::current_root  = 0;
61 QCString VhdlParser::compSpec;
62 QCString VhdlParser::currName;
63 QCString VhdlParser::confName;
64 QCString VhdlParser::genLabels;
65 QCString VhdlParser::lab;
66 QCString VhdlParser::forL;
67
68 int VhdlParser::param_sec = 0;
69 int VhdlParser::parse_sec=0;
70 int VhdlParser::currP=0;
71 int VhdlParser::levelCounter;
72
73 static QList<VhdlConfNode> configL;
74
75 static struct
76 {
77   QCString doc;
78   bool brief;
79   bool pending;
80   int iDocLine;
81 } str_doc;
82
83 static bool doxComment=FALSE; // doxygen comment ?
84 static QCString strComment;
85 static int iCodeLen;
86
87 bool  checkMultiComment(QCString& qcs,int line);
88 QList<Entry>* getEntryAtLine(const Entry* ce,int line);
89
90 //-------------------------------------
91
92 QList<VhdlConfNode>& getVhdlConfiguration() { return  configL; }
93 QList<Entry>& getVhdlInstList() { return  instFiles; }
94
95 Entry* getVhdlCompound()
96 {
97   if (VhdlParser::lastEntity) return VhdlParser::lastEntity;
98   if (VhdlParser::lastCompound) return VhdlParser::lastCompound;
99   return NULL;
100 }
101
102 void startCodeBlock(int index)
103 {
104   int ll=strComment.length();
105   if (!gBlock) gBlock = new Entry;
106   iCodeLen=inputString.findRev(strComment.data())+ll;
107   // fprintf(stderr,"\n startin code..%d %d %d\n",iCodeLen,num_chars,ll);
108   gBlock->reset();
109   int len=strComment.length();
110   QCString name=strComment.right(len-index);//
111   name=VhdlDocGen::getIndexWord(name.data(),1);
112   if (!name)
113     gBlock->name="misc"+ VhdlDocGen::getRecordNumber();
114   else
115     gBlock->name=name;
116
117   gBlock->startLine=yyLineNr;
118   gBlock->bodyLine=yyLineNr;
119
120   strComment=strComment.left(index);
121   VhdlDocGen::prepareComment(strComment);
122   gBlock->brief+=strComment;
123 }
124
125 void makeInlineDoc(int endCode)
126 {
127   int len=endCode-iCodeLen;
128   if (!gBlock) gBlock = new Entry;
129   QCString par=inputString.mid(iCodeLen,len);
130   //fprintf(stderr,"\n inline code: \n<%s>",par.data());
131   gBlock->doc=par;
132   gBlock->inbodyDocs=par;
133   gBlock->section=Entry::VARIABLE_SEC;
134   gBlock->spec=VhdlDocGen::MISCELLANEOUS;
135   gBlock->fileName = yyFileName;
136   gBlock->endBodyLine=yyLineNr-1;
137   gBlock->lang=SrcLangExt_VHDL;
138   Entry *temp=new Entry(*gBlock);
139   Entry* compound=getVhdlCompound();
140
141   if (compound)
142   {
143     compound->addSubEntry(temp);
144   }
145   else
146   {
147     temp->type="misc"; // global code like library ieee...
148     VhdlParser::current_root->addSubEntry(temp);
149   }
150   strComment.resize(0);
151   gBlock->reset();
152 }// makeInlineDoc
153
154
155 bool isConstraintFile(const QCString &fileName,const QCString &ext)
156 {
157   return fileName.right(ext.length())==ext;
158 }
159
160
161 void VHDLLanguageScanner::parseInput(const char *fileName,const char *fileBuf,Entry *root,
162                           bool ,QStrList&)
163 {
164   g_thisParser=this;
165   bool inLine=false;
166   inputString=fileBuf;
167
168  // fprintf(stderr,"\n ============= %s\n ==========\n",fileBuf);
169
170   if (strlen(fileName)==0)
171   {
172     inLine=true;
173   }
174
175   yyFileName+=fileName;
176
177   bool xilinx_ucf=isConstraintFile(yyFileName,".ucf");
178   bool altera_qsf=isConstraintFile(yyFileName,".qsf");
179
180   // support XILINX(ucf) and ALTERA (qsf) file
181
182   if (xilinx_ucf)
183   {
184     VhdlDocGen::parseUCF(fileBuf,root,yyFileName,FALSE);
185     return;
186   }
187   if (altera_qsf)
188   {
189     VhdlDocGen::parseUCF(fileBuf,root,yyFileName,TRUE);
190     return;
191   }
192   libUse.setAutoDelete(true);
193   yyLineNr=1;
194   VhdlParser::current_root=root;
195   VhdlParser::lastCompound=0;
196   VhdlParser::lastEntity=0;
197   VhdlParser::currentCompound=0;
198   VhdlParser::lastEntity=0;
199   oldEntry = 0;
200   VhdlParser::current=new Entry();
201   VhdlParser::initEntry(VhdlParser::current);
202   groupEnterFile(fileName,yyLineNr);
203   lineParse=new int[200]; // Dimitri: dangerous constant: should be bigger than largest token id in VhdlParserConstants.h
204   VhdlParserIF::parseVhdlfile(fileBuf,inLine);
205
206   delete VhdlParser::current;
207   VhdlParser::current=0;
208
209   if (!inLine)
210   VhdlParser::mapLibPackage(root);
211
212   delete[] lineParse;
213   yyFileName.resize(0);
214   libUse.clear();
215   VhdlDocGen::resetCodeVhdlParserState();
216 }
217
218 void VhdlParser::lineCount()
219 {
220   yyLineNr++;
221 }
222
223 void VhdlParser::lineCount(const char* text)
224 {
225   for (const char* c=text ; *c ; ++c )
226   {
227     yyLineNr += (*c == '\n') ;
228   }
229 }
230
231 void isVhdlDocPending()
232 {
233   if (!str_doc.pending) return;
234
235   str_doc.pending=FALSE;
236   oldEntry=0; // prevents endless recursion
237   iDocLine=str_doc.iDocLine;
238   VhdlParser::handleCommentBlock(str_doc.doc,str_doc.brief);
239   iDocLine=-1;
240 }
241
242 void VhdlParser::initEntry(Entry *e)
243 {
244   e->fileName = yyFileName;
245   e->lang     = SrcLangExt_VHDL;
246   isVhdlDocPending();
247   initGroupInfo(e);
248 }
249
250 void VhdlParser::newEntry()
251 {
252   if (current->spec==VhdlDocGen::ENTITY ||
253       current->spec==VhdlDocGen::PACKAGE ||
254       current->spec==VhdlDocGen::ARCHITECTURE ||
255       current->spec==VhdlDocGen::PACKAGE_BODY)
256   {
257     current_root->addSubEntry(current);
258   }
259   else
260   {
261     if (lastCompound)
262     {
263       lastCompound->addSubEntry(current);
264     }
265     else
266     {
267       if (lastEntity)
268       {
269         lastEntity->addSubEntry(current);
270       }
271       else
272       {
273         current_root->addSubEntry(current);
274       }
275     }
276   }
277   previous = current;
278   current = new Entry ;
279   initEntry(current);
280 }
281
282 bool checkInlineCode(QCString & doc)
283 {
284   int index=doc.find("\\code");
285
286   if (index>0)
287   {
288      strComment+=doc;
289          startCodeBlock(index);
290          doxComment=TRUE;
291          return true;
292   }
293   return false;
294 }
295
296 void VhdlParser::handleFlowComment(const char* doc)
297 {
298         lineCount(doc);
299
300   if (VhdlDocGen::getFlowMember())
301   {
302     QCString qcs(doc);
303     qcs=qcs.stripWhiteSpace();
304     qcs.stripPrefix("--#");
305     FlowChart::addFlowChart(FlowChart::COMMENT_NO,0,0,qcs.data());
306   }
307 }
308
309
310 void VhdlParser::handleCommentBlock(const char* doc1,bool brief)
311 {
312   int position=0;
313   static bool isIn;
314   QCString doc;
315   doc.append(doc1);
316  // fprintf(stderr,"\n %s",doc.data());
317   if (doc.isEmpty()) return;
318
319   if (checkMultiComment(doc,yyLineNr))
320   {
321     strComment.resize(0);
322     return;
323   }
324
325   isIn=checkInlineCode(doc);
326   bool isEndCode=doc.contains("\\endcode");
327   // empty comment  --!
328   if (isEndCode)
329   {
330     int end=inputString.find(doc.data(),iCodeLen);
331     makeInlineDoc(end);
332     strComment.resize(0);
333     isIn=false;
334   }
335   if (isIn)
336   {
337     isIn=false;
338     return;
339   }
340
341   VhdlDocGen::prepareComment(doc);
342
343   bool needsEntry=FALSE;
344   Protection protection=Public;
345
346   if (oldEntry==current)
347   {
348     //printf("\n find pending message  < %s > at line: %d \n ",doc.data(),iDocLine);
349     str_doc.doc=doc;
350     str_doc.iDocLine=iDocLine;
351     str_doc.brief=brief;
352     str_doc.pending=TRUE;
353     return;
354   }
355
356   oldEntry=current;
357
358   if (brief)
359   {
360     current->briefLine = yyLineNr;
361   }
362   else
363   {
364     current->docLine = yyLineNr;
365   }
366   //  printf("parseCommentBlock file<%s>\n [%s]\n at line [%d] \n ",yyFileName.data(),doc.data(),iDocLine);
367
368   int j=doc.find("[plant]");
369   if (j>=0)
370   {
371     doc=doc.remove(j,7);
372     current->stat=true;
373   }
374
375   while (parseCommentBlock(
376         g_thisParser,
377         current,
378         doc,        // text
379         yyFileName, // file
380         iDocLine,   // line of block start
381         brief,
382         0,
383         FALSE,
384         protection,
385         position,
386         needsEntry
387         )
388       )
389   {
390     //printf("parseCommentBlock position=%d [%s]\n",position,doc.data()+position);
391     if (needsEntry) newEntry();
392   }
393   if (needsEntry)
394   {
395     if (varr)
396     {
397       varr=FALSE;
398       current->name=varName;
399       current->section=Entry::VARIABLEDOC_SEC;
400       varName="";
401     }
402     newEntry();
403   }
404   iDocLine=-1;
405   strComment.resize(0);
406 }
407
408 void VHDLLanguageScanner::parsePrototype(const char *text)
409 {
410   varName=text;
411   varr=TRUE;
412 }
413
414 void VhdlParser::addCompInst(const char *n, const char* instName, const char* comp,int iLine)
415 {
416   current->spec=VhdlDocGen::INSTANTIATION;
417   current->section=Entry::VARIABLE_SEC;
418   current->startLine=iLine;
419   current->bodyLine=iLine;
420   current->type=instName;                       // foo:instname e.g proto or work. proto(ttt)
421   current->exception=genLabels.lower();         // |arch|label1:label2...
422   current->name=n;                              // foo
423   if (lastCompound)
424   {
425     current->args=lastCompound->name;             // architecture name
426   }
427   current->includeName=comp;                    // component/enity/configuration
428   int u=genLabels.find("|",1);
429   if (u>0)
430   {
431     current->write=genLabels.right(genLabels.length()-u);
432     current->read=genLabels.left(u);
433   }
434   //printf  (" \n genlable: [%s]  inst: [%s]  name: [%s] %d\n",n,instName,comp,iLine);
435
436   if (lastCompound)
437   {
438     current->args=lastCompound->name;
439     if (true) // !findInstant(current->type))
440     {
441       initEntry(current);
442       instFiles.append(new Entry(*current));
443     }
444
445     Entry *temp=current;  // hold  current pointer  (temp=oldEntry)
446     current=new Entry;     // (oldEntry != current)
447     delete  temp;
448   }
449   else
450   {
451     newEntry();
452   }
453 }
454
455 void VhdlParser::addVhdlType(const char *n,int startLine,int section,
456     uint64 spec,const char* args,const char* type,Protection prot)
457 {
458   QCString name(n);
459   if (isFuncProcProced() || VhdlDocGen::getFlowMember())  return;
460
461   if (parse_sec==GEN_SEC)
462   {
463     spec= VhdlDocGen::GENERIC;
464   }
465
466   QStringList ql=QStringList::split(",",name,FALSE);
467
468   for (uint u=0;u<ql.count();u++)
469   {
470     current->name=ql[u].utf8();
471     current->startLine=startLine;
472     current->bodyLine=startLine;
473     current->section=section;
474     current->spec=spec;
475     current->fileName=yyFileName;
476     if (current->args.isEmpty())
477     {
478       current->args=args;
479     }
480     current->type=type;
481     current->protection=prot;
482
483     if (!lastCompound && (section==Entry::VARIABLE_SEC) &&  (spec == VhdlDocGen::USE || spec == VhdlDocGen::LIBRARY) )
484     {
485       libUse.append(new Entry(*current));
486       current->reset();
487     }
488     newEntry();
489   }
490 }
491
492 void VhdlParser::createFunction(const char *imp,uint64 spec,const char *fn)
493 {
494   QCString impure(imp);
495   QCString fname(fn);
496   current->spec=spec;
497   current->section=Entry::FUNCTION_SEC;
498
499   if (impure=="impure" || impure=="pure")
500   {
501     current->exception=impure;
502   }
503
504   if (parse_sec==GEN_SEC)
505   {
506     current->spec= VhdlDocGen::GENERIC;
507     current->section=Entry::FUNCTION_SEC;
508   }
509
510   if (currP==VhdlDocGen::PROCEDURE)
511   {
512     current->name=impure;
513     current->exception="";
514   }
515   else
516   {
517     current->name=fname;
518   }
519
520   if (spec==VhdlDocGen::PROCESS)
521   {
522     current->args=fname;
523     current->name=impure;
524     VhdlDocGen::deleteAllChars(current->args,' ');
525     if (!fname.isEmpty())
526     {
527       QStringList q1=QStringList::split(",",fname);
528       for (uint ii=0;ii<q1.count();ii++)
529       {
530         Argument *arg=new Argument;
531         arg->name=q1[ii].utf8();
532         current->argList->append(arg);
533       }
534     }
535     return;
536   }
537  }
538
539
540 bool VhdlParser::isFuncProcProced()
541 {
542   if (currP==VhdlDocGen::FUNCTION  ||
543       currP==VhdlDocGen::PROCEDURE ||
544       currP==VhdlDocGen::PROCESS
545      )
546   {
547     return TRUE;
548   }
549   return FALSE;
550 }
551
552 void VhdlParser::pushLabel( QCString &label,QCString & val)
553 {
554   label+="|";
555   label+=val;
556 }
557
558  QCString  VhdlParser::popLabel(QCString & q)
559 {
560   int i=q.findRev("|");
561   if (i<0) return "";
562   q = q.left(i);
563   return q;
564 }
565
566 void VhdlParser::addConfigureNode(const char* a,const char*b, bool,bool isLeaf,bool inlineConf)
567 {
568   VhdlConfNode* co=0;
569   QCString ent;
570   ent=a;
571
572   if (b)
573   {
574     ent=b;
575   }
576   int level=0;
577
578   if (!configL.isEmpty())
579   {
580     VhdlConfNode* vc=configL.getLast();
581     level=vc->level;
582     if (levelCounter==0)
583     {
584       pushLabel(forL,ent);
585     }
586     else if (level<levelCounter)
587     {
588       if (!isLeaf)
589       {
590         pushLabel(forL,ent);
591       }
592     }
593     else if (level>levelCounter)
594     {
595       forL=popLabel(forL);
596     }
597   }
598   else
599   {
600     pushLabel(forL,ent);
601   }
602
603   if (inlineConf)
604   {
605     confName=lastCompound->name;
606   }
607
608   //fprintf(stderr,"\n[%s %d %d]\n",forL.data(),levelCounter,level);
609   co=new VhdlConfNode(a,b,confName.lower().data(),forL.lower().data(),isLeaf);
610
611   if (inlineConf)
612   {
613     co->isInlineConf=TRUE;
614   }
615
616   configL.append(co);
617 }
618
619
620 void VhdlParser::addProto(const char *s1,const char *s2,const char *s3,
621     const char *s4,const char *s5,const char *s6)
622 {
623   (void)s5; // avoid unused warning
624   QCString name=s2;
625   QStringList ql=QStringList::split(",",name,FALSE);
626
627   for (uint u=0;u<ql.count();u++)
628   {
629     Argument *arg=new Argument;
630     arg->name=ql[u].utf8();
631     if (s3)
632     {
633       arg->type=s3;
634     }
635     arg->type+=" ";
636     arg->type+=s4;
637     if (s6)
638     {
639       arg->type+=s6;
640     }
641     if (parse_sec==GEN_SEC && param_sec==0)
642     {
643       arg->defval="gen!";
644     }
645
646     if (parse_sec==PARAM_SEC)
647     {
648     //  assert(false);
649     }
650
651     arg->defval+=s1;
652     arg->attrib="";//s6;
653
654     current->argList->append(arg);
655     current->args+=s2;
656     current->args+=",";
657   }
658 }
659
660
661 /*
662  * adds the library|use statements to the next class (entity|package|architecture|package body
663  * library ieee
664  * entity xxx
665  * .....
666  * library
667  * package
668  * enity zzz
669  * .....
670  * and so on..
671  */
672 void VhdlParser::mapLibPackage( Entry* root)
673 {
674   QList<Entry> epp=libUse;
675   EntryListIterator eli(epp);
676   Entry *rt;
677   for (;(rt=eli.current());++eli)
678   {
679     if (addLibUseClause(rt->name))
680     {
681       Entry *current;
682       EntryListIterator eLib(*root->children());
683       bool bFound=FALSE;
684       for (eLib.toFirst();(current=eLib.current());++eLib)
685       {
686         if (VhdlDocGen::isVhdlClass(current))
687         {
688           if (current->startLine > rt->startLine)
689           {
690             bFound=TRUE;
691             current->addSubEntry(new Entry(*rt));
692             break;
693           }
694         }
695       }//for
696       if (!bFound)
697       {
698         root->addSubEntry(new Entry(*rt));
699       }
700     } //if
701   }// for
702 }//MapLib
703
704 bool VhdlParser::addLibUseClause(const QCString &type)
705 {
706   static bool showIEEESTD=Config_getBool(FORCE_LOCAL_INCLUDES);
707
708   if (showIEEESTD) // all standard packages and libraries will not be shown
709   {
710     if (type.lower().stripPrefix("ieee")) return FALSE;
711     if (type.lower().stripPrefix("std")) return FALSE;
712   }
713   return TRUE;
714 }
715
716 int VhdlParser::getLine()
717 {
718   return yyLineNr;
719 }
720
721 void VhdlParser::setLineParsed(int tok)
722 {
723   lineParse[tok]=yyLineNr;
724 }
725
726 int VhdlParser::getLine(int tok)
727 {
728   int val=lineParse[tok];
729   if (val<0) val=0;
730   //assert(val>=0 && val<=yyLineNr);
731   return val;
732 }
733
734
735 void VhdlParser::createFlow()
736 {
737   if (!VhdlDocGen::getFlowMember())
738   {
739     return;
740   }
741   QCString q,ret;
742
743   if (currP==VhdlDocGen::FUNCTION)
744   {
745     q=":function( ";
746     FlowChart::alignFuncProc(q,tempEntry->argList,true);
747     q+=")";
748   }
749   else if (currP==VhdlDocGen::PROCEDURE)
750   {
751     q=":procedure (";
752     FlowChart::alignFuncProc(q,tempEntry->argList,false);
753     q+=")";
754   }
755   else
756   {
757     q=":process( "+tempEntry->args;
758     q+=")";
759   }
760
761   q.prepend(VhdlDocGen::getFlowMember()->name().data());
762
763   FlowChart::addFlowChart(FlowChart::START_NO,q,0);
764
765   if (currP==VhdlDocGen::FUNCTION)
766   {
767     ret="end function ";
768   }
769   else if (currP==VhdlDocGen::PROCEDURE)
770   {
771     ret="end procedure";
772   }
773   else
774   {
775     ret="end process ";
776   }
777
778   FlowChart::addFlowChart(FlowChart::END_NO,ret,0);
779   //  FlowChart::printFlowList();
780   FlowChart::writeFlowChart();
781   currP=0;
782 }
783
784 void VhdlParser::setMultCommentLine()
785 {
786   iDocLine=yyLineNr;
787 }
788
789 void VhdlParser::oneLineComment(QCString qcs)
790 {
791   bool isEndCode=qcs.contains("\\endcode");
792
793   int index = qcs.find("\\code");
794   if (isEndCode)
795   {
796     int end = inputString.find(qcs.data(),iCodeLen);
797     makeInlineDoc(end);
798   }
799   else if (index > 0)
800   {
801     // assert(false);
802     strComment=qcs;
803     startCodeBlock(index);
804     strComment.resize(0);
805   }
806
807   if (!isEndCode && index==-1)
808   {
809     int j=qcs.find("--!");
810     qcs=qcs.right(qcs.length()-3-j);
811     if (!checkMultiComment(qcs,iDocLine))
812     {
813       handleCommentBlock(qcs,TRUE);
814     }
815   }
816 }
817
818
819 bool  checkMultiComment(QCString& qcs,int line)
820 {
821   QList<Entry> *pTemp=getEntryAtLine(VhdlParser::current_root,line);
822
823   if (pTemp->isEmpty()) return false;
824
825   VhdlDocGen::prepareComment(qcs);
826   while (!pTemp->isEmpty())
827   {
828     Entry *e=(Entry*)pTemp->getFirst();
829     e->briefLine=line;
830     e->brief+=qcs;
831
832     pTemp->removeFirst();
833   }
834   return true;
835 }
836
837 // returns the vhdl parsed types at line xxx
838 QList<Entry>* getEntryAtLine(const Entry* ce,int line)
839 {
840   EntryListIterator eli(*ce->children());
841   Entry *rt;
842   for (;(rt=eli.current());++eli)
843   {
844     if (rt->bodyLine==line)
845     {
846       lineEntry.insert(0,rt);
847     }
848
849     getEntryAtLine(rt,line);
850   }
851   return &lineEntry;
852 }
853