/******************************************************************************
*
* Copyright (C) 2014 by M. Kreis
*
* Permission to use, copy, modify, and distribute this software and its
* documentation under the terms of the GNU General Public License is hereby
* granted. No representations are made about the suitability of this software
* for any purpose. It is provided "as is" without express or implied warranty.
* See the GNU General Public License for more details.
*
*/
#include <qcstring.h>
#include <qfileinfo.h>
#include <qstringlist.h>
#include "vhdljjparser.h"
#include "vhdlcode.h"
#include "vhdldocgen.h"
#include "message.h"
#include "config.h"
#include "doxygen.h"
#include "util.h"
#include "language.h"
#include "commentscan.h"
#include "index.h"
#include "definition.h"
#include "searchindex.h"
#include "outputlist.h"
#include "arguments.h"
#include "types.h"
#include "VhdlParserIF.h"
using namespace vhdl::parser;
using namespace std;
static ParserInterface *g_thisParser;
static QCString yyFileName;
static int yyLineNr = 1;
static int* lineParse;
static int iDocLine = -1;
static QCString inputString;
static Entry* gBlock = 0;
static Entry* previous = 0;
//-------------------------------------------------------
static Entry* oldEntry;
static bool varr=FALSE;
static QCString varName;
static QList<Entry> instFiles;
static QList<Entry> libUse;
static QList<Entry> lineEntry;
Entry* VhdlParser::currentCompound=0;
Entry* VhdlParser::tempEntry=0;
Entry* VhdlParser::lastEntity=0 ;
Entry* VhdlParser::lastCompound=0 ;
Entry* VhdlParser::current=0;
Entry* VhdlParser::current_root = 0;
QCString VhdlParser::compSpec;
QCString VhdlParser::currName;
QCString VhdlParser::confName;
QCString VhdlParser::genLabels;
QCString VhdlParser::lab;
QCString VhdlParser::forL;
int VhdlParser::param_sec = 0;
int VhdlParser::parse_sec=0;
int VhdlParser::currP=0;
int VhdlParser::levelCounter;
static QList<VhdlConfNode> configL;
static struct
{
QCString doc;
bool brief;
bool pending;
int iDocLine;
} str_doc;
static bool doxComment=FALSE; // doxygen comment ?
static QCString strComment;
static int iCodeLen;
bool checkMultiComment(QCString& qcs,int line);
QList<Entry>* getEntryAtLine(const Entry* ce,int line);
//-------------------------------------
QList<VhdlConfNode>& getVhdlConfiguration() { return configL; }
QList<Entry>& getVhdlInstList() { return instFiles; }
Entry* getVhdlCompound()
{
if (VhdlParser::lastEntity) return VhdlParser::lastEntity;
if (VhdlParser::lastCompound) return VhdlParser::lastCompound;
return NULL;
}
void startCodeBlock(int index)
{
int ll=strComment.length();
if (!gBlock) gBlock = new Entry;
iCodeLen=inputString.findRev(strComment.data())+ll;
// fprintf(stderr,"\n startin code..%d %d %d\n",iCodeLen,num_chars,ll);
gBlock->reset();
int len=strComment.length();
QCString name=strComment.right(len-index);//
name=VhdlDocGen::getIndexWord(name.data(),1);
if (!name)
gBlock->name="misc"+ VhdlDocGen::getRecordNumber();
else
gBlock->name=name;
gBlock->startLine=yyLineNr;
gBlock->bodyLine=yyLineNr;
strComment=strComment.left(index);
VhdlDocGen::prepareComment(strComment);
gBlock->brief+=strComment;
}
void makeInlineDoc(int endCode)
{
int len=endCode-iCodeLen;
if (!gBlock) gBlock = new Entry;
QCString par=inputString.mid(iCodeLen,len);
//fprintf(stderr,"\n inline code: \n<%s>",par.data());
gBlock->doc=par;
gBlock->inbodyDocs=par;
gBlock->section=Entry::VARIABLE_SEC;
gBlock->spec=VhdlDocGen::MISCELLANEOUS;
gBlock->fileName = yyFileName;
gBlock->endBodyLine=yyLineNr-1;
gBlock->lang=SrcLangExt_VHDL;
Entry *temp=new Entry(*gBlock);
Entry* compound=getVhdlCompound();
if (compound)
{
compound->addSubEntry(temp);
}
else
{
temp->type="misc"; // global code like library ieee...
VhdlParser::current_root->addSubEntry(temp);
}
strComment.resize(0);
gBlock->reset();
}// makeInlineDoc
bool isConstraintFile(const QCString &fileName,const QCString &ext)
{
return fileName.right(ext.length())==ext;
}
void VHDLLanguageScanner::parseInput(const char *fileName,const char *fileBuf,Entry *root,
bool ,QStrList&)
{
g_thisParser=this;
bool inLine=false;
inputString=fileBuf;
// fprintf(stderr,"\n ============= %s\n ==========\n",fileBuf);
if (strlen(fileName)==0)
{
inLine=true;
}
yyFileName+=fileName;
bool xilinx_ucf=isConstraintFile(yyFileName,".ucf");
bool altera_qsf=isConstraintFile(yyFileName,".qsf");
// support XILINX(ucf) and ALTERA (qsf) file
if (xilinx_ucf)
{
VhdlDocGen::parseUCF(fileBuf,root,yyFileName,FALSE);
return;
}
if (altera_qsf)
{
VhdlDocGen::parseUCF(fileBuf,root,yyFileName,TRUE);
return;
}
libUse.setAutoDelete(true);
yyLineNr=1;
VhdlParser::current_root=root;
VhdlParser::lastCompound=0;
VhdlParser::lastEntity=0;
VhdlParser::currentCompound=0;
VhdlParser::lastEntity=0;
oldEntry = 0;
VhdlParser::current=new Entry();
VhdlParser::initEntry(VhdlParser::current);
groupEnterFile(fileName,yyLineNr);
lineParse=new int[200]; // Dimitri: dangerous constant: should be bigger than largest token id in VhdlParserConstants.h
VhdlParserIF::parseVhdlfile(fileBuf,inLine);
delete VhdlParser::current;
VhdlParser::current=0;
if (!inLine)
VhdlParser::mapLibPackage(root);
delete[] lineParse;
yyFileName.resize(0);
libUse.clear();
VhdlDocGen::resetCodeVhdlParserState();
}
void VhdlParser::lineCount()
{
yyLineNr++;
}
void VhdlParser::lineCount(const char* text)
{
for (const char* c=text ; *c ; ++c )
{
yyLineNr += (*c == '\n') ;
}
}
void isVhdlDocPending()
{
if (!str_doc.pending) return;
str_doc.pending=FALSE;
oldEntry=0; // prevents endless recursion
iDocLine=str_doc.iDocLine;
VhdlParser::handleCommentBlock(str_doc.doc,str_doc.brief);
iDocLine=-1;
}
void VhdlParser::initEntry(Entry *e)
{
e->fileName = yyFileName;
e->lang = SrcLangExt_VHDL;
isVhdlDocPending();
initGroupInfo(e);
}
void VhdlParser::newEntry()
{
if (current->spec==VhdlDocGen::ENTITY ||
current->spec==VhdlDocGen::PACKAGE ||
current->spec==VhdlDocGen::ARCHITECTURE ||
current->spec==VhdlDocGen::PACKAGE_BODY)
{
current_root->addSubEntry(current);
}
else
{
if (lastCompound)
{
lastCompound->addSubEntry(current);
}
else
{
if (lastEntity)
{
lastEntity->addSubEntry(current);
}
else
{
current_root->addSubEntry(current);
}
}
}
previous = current;
current = new Entry ;
initEntry(current);
}
bool checkInlineCode(QCString & doc)
{
int index=doc.find("\\code");
if (index>0)
{
strComment+=doc;
startCodeBlock(index);
doxComment=TRUE;
return true;
}
return false;
}
void VhdlParser::handleFlowComment(const char* doc)
{
lineCount(doc);
if (VhdlDocGen::getFlowMember())
{
QCString qcs(doc);
qcs=qcs.stripWhiteSpace();
qcs.stripPrefix("--#");
FlowChart::addFlowChart(FlowChart::COMMENT_NO,0,0,qcs.data());
}
}
void VhdlParser::handleCommentBlock(const char* doc1,bool brief)
{
int position=0;
static bool isIn;
QCString doc;
doc.append(doc1);
// fprintf(stderr,"\n %s",doc.data());
if (doc.isEmpty()) return;
if (checkMultiComment(doc,yyLineNr))
{
strComment.resize(0);
return;
}
isIn=checkInlineCode(doc);
bool isEndCode=doc.contains("\\endcode");
// empty comment --!
if (isEndCode)
{
int end=inputString.find(doc.data(),iCodeLen);
makeInlineDoc(end);
strComment.resize(0);
isIn=false;
}
if (isIn)
{
isIn=false;
return;
}
VhdlDocGen::prepareComment(doc);
bool needsEntry=FALSE;
Protection protection=Public;
if (oldEntry==current)
{
//printf("\n find pending message < %s > at line: %d \n ",doc.data(),iDocLine);
str_doc.doc=doc;
str_doc.iDocLine=iDocLine;
str_doc.brief=brief;
str_doc.pending=TRUE;
return;
}
oldEntry=current;
if (brief)
{
current->briefLine = yyLineNr;
}
else
{
current->docLine = yyLineNr;
}
// printf("parseCommentBlock file<%s>\n [%s]\n at line [%d] \n ",yyFileName.data(),doc.data(),iDocLine);
int j=doc.find("[plant]");
if (j>=0)
{
doc=doc.remove(j,7);
current->stat=true;
}
while (parseCommentBlock(
g_thisParser,
current,
doc, // text
yyFileName, // file
iDocLine, // line of block start
brief,
0,
FALSE,
protection,
position,
needsEntry
)
)
{
//printf("parseCommentBlock position=%d [%s]\n",position,doc.data()+position);
if (needsEntry) newEntry();
}
if (needsEntry)
{
if (varr)
{
varr=FALSE;
current->name=varName;
current->section=Entry::VARIABLEDOC_SEC;
varName="";
}
newEntry();
}
iDocLine=-1;
strComment.resize(0);
}
void VHDLLanguageScanner::parsePrototype(const char *text)
{
varName=text;
varr=TRUE;
}
void VhdlParser::addCompInst(const char *n, const char* instName, const char* comp,int iLine)
{
current->spec=VhdlDocGen::INSTANTIATION;
current->section=Entry::VARIABLE_SEC;
current->startLine=iLine;
current->bodyLine=iLine;
current->type=instName; // foo:instname e.g proto or work. proto(ttt)
current->exception=genLabels.lower(); // |arch|label1:label2...
current->name=n; // foo
if (lastCompound)
{
current->args=lastCompound->name; // architecture name
}
current->includeName=comp; // component/enity/configuration
int u=genLabels.find("|",1);
if (u>0)
{
current->write=genLabels.right(genLabels.length()-u);
current->read=genLabels.left(u);
}
//printf (" \n genlable: [%s] inst: [%s] name: [%s] %d\n",n,instName,comp,iLine);
if (lastCompound)
{
current->args=lastCompound->name;
if (true) // !findInstant(current->type))
{
initEntry(current);
instFiles.append(new Entry(*current));
}
Entry *temp=current; // hold current pointer (temp=oldEntry)
current=new Entry; // (oldEntry != current)
delete temp;
}
else
{
newEntry();
}
}
void VhdlParser::addVhdlType(const char *n,int startLine,int section,
uint64 spec,const char* args,const char* type,Protection prot)
{
QCString name(n);
if (isFuncProcProced() || VhdlDocGen::getFlowMember()) return;
if (parse_sec==GEN_SEC)
{
spec= VhdlDocGen::GENERIC;
}
QStringList ql=QStringList::split(",",name,FALSE);
for (uint u=0;u<ql.count();u++)
{
current->name=ql[u].utf8();
current->startLine=startLine;
current->bodyLine=startLine;
current->section=section;
current->spec=spec;
current->fileName=yyFileName;
if (current->args.isEmpty())
{
current->args=args;
}
current->type=type;
current->protection=prot;
if (!lastCompound && (section==Entry::VARIABLE_SEC) && (spec == VhdlDocGen::USE || spec == VhdlDocGen::LIBRARY) )
{
libUse.append(new Entry(*current));
current->reset();
}
newEntry();
}
}
void VhdlParser::createFunction(const char *imp,uint64 spec,const char *fn)
{
QCString impure(imp);
QCString fname(fn);
current->spec=spec;
current->section=Entry::FUNCTION_SEC;
if (impure=="impure" || impure=="pure")
{
current->exception=impure;
}
if (parse_sec==GEN_SEC)
{
current->spec= VhdlDocGen::GENERIC;
current->section=Entry::FUNCTION_SEC;
}
if (currP==VhdlDocGen::PROCEDURE)
{
current->name=impure;
current->exception="";
}
else
{
current->name=fname;
}
if (spec==VhdlDocGen::PROCESS)
{
current->args=fname;
current->name=impure;
VhdlDocGen::deleteAllChars(current->args,' ');
if (!fname.isEmpty())
{
QStringList q1=QStringList::split(",",fname);
for (uint ii=0;ii<q1.count();ii++)
{
Argument *arg=new Argument;
arg->name=q1[ii].utf8();
current->argList->append(arg);
}
}
return;
}
}
bool VhdlParser::isFuncProcProced()
{
if (currP==VhdlDocGen::FUNCTION ||
currP==VhdlDocGen::PROCEDURE ||
currP==VhdlDocGen::PROCESS
)
{
return TRUE;
}
return FALSE;
}
void VhdlParser::pushLabel( QCString &label,QCString & val)
{
label+="|";
label+=val;
}
QCString VhdlParser::popLabel(QCString & q)
{
int i=q.findRev("|");
if (i<0) return "";
q = q.left(i);
return q;
}
void VhdlParser::addConfigureNode(const char* a,const char*b, bool,bool isLeaf,bool inlineConf)
{
VhdlConfNode* co=0;
QCString ent;
ent=a;
if (b)
{
ent=b;
}
int level=0;
if (!configL.isEmpty())
{
VhdlConfNode* vc=configL.getLast();
level=vc->level;
if (levelCounter==0)
{
pushLabel(forL,ent);
}
else if (level<levelCounter)
{
if (!isLeaf)
{
pushLabel(forL,ent);
}
}
else if (level>levelCounter)
{
forL=popLabel(forL);
}
}
else
{
pushLabel(forL,ent);
}
if (inlineConf)
{
confName=lastCompound->name;
}
//fprintf(stderr,"\n[%s %d %d]\n",forL.data(),levelCounter,level);
co=new VhdlConfNode(a,b,confName.lower().data(),forL.lower().data(),isLeaf);
if (inlineConf)
{
co->isInlineConf=TRUE;
}
configL.append(co);
}
void VhdlParser::addProto(const char *s1,const char *s2,const char *s3,
const char *s4,const char *s5,const char *s6)
{
(void)s5; // avoid unused warning
QCString name=s2;
QStringList ql=QStringList::split(",",name,FALSE);
for (uint u=0;u<ql.count();u++)
{
Argument *arg=new Argument;
arg->name=ql[u].utf8();
if (s3)
{
arg->type=s3;
}
arg->type+=" ";
arg->type+=s4;
if (s6)
{
arg->type+=s6;
}
if (parse_sec==GEN_SEC && param_sec==0)
{
arg->defval="gen!";
}
if (parse_sec==PARAM_SEC)
{
// assert(false);
}
arg->defval+=s1;
arg->attrib="";//s6;
current->argList->append(arg);
current->args+=s2;
current->args+=",";
}
}
/*
* adds the library|use statements to the next class (entity|package|architecture|package body
* library ieee
* entity xxx
* .....
* library
* package
* enity zzz
* .....
* and so on..
*/
void VhdlParser::mapLibPackage( Entry* root)
{
QList<Entry> epp=libUse;
EntryListIterator eli(epp);
Entry *rt;
for (;(rt=eli.current());++eli)
{
if (addLibUseClause(rt->name))
{
Entry *current;
EntryListIterator eLib(*root->children());
bool bFound=FALSE;
for (eLib.toFirst();(current=eLib.current());++eLib)
{
if (VhdlDocGen::isVhdlClass(current))
{
if (current->startLine > rt->startLine)
{
bFound=TRUE;
current->addSubEntry(new Entry(*rt));
break;
}
}
}//for
if (!bFound)
{
root->addSubEntry(new Entry(*rt));
}
} //if
}// for
}//MapLib
bool VhdlParser::addLibUseClause(const QCString &type)
{
static bool showIEEESTD=Config_getBool(FORCE_LOCAL_INCLUDES);
if (showIEEESTD) // all standard packages and libraries will not be shown
{
if (type.lower().stripPrefix("ieee")) return FALSE;
if (type.lower().stripPrefix("std")) return FALSE;
}
return TRUE;
}
int VhdlParser::getLine()
{
return yyLineNr;
}
void VhdlParser::setLineParsed(int tok)
{
lineParse[tok]=yyLineNr;
}
int VhdlParser::getLine(int tok)
{
int val=lineParse[tok];
if (val<0) val=0;
//assert(val>=0 && val<=yyLineNr);
return val;
}
void VhdlParser::createFlow()
{
if (!VhdlDocGen::getFlowMember())
{
return;
}
QCString q,ret;
if (currP==VhdlDocGen::FUNCTION)
{
q=":function( ";
FlowChart::alignFuncProc(q,tempEntry->argList,true);
q+=")";
}
else if (currP==VhdlDocGen::PROCEDURE)
{
q=":procedure (";
FlowChart::alignFuncProc(q,tempEntry->argList,false);
q+=")";
}
else
{
q=":process( "+tempEntry->args;
q+=")";
}
q.prepend(VhdlDocGen::getFlowMember()->name().data());
FlowChart::addFlowChart(FlowChart::START_NO,q,0);
if (currP==VhdlDocGen::FUNCTION)
{
ret="end function ";
}
else if (currP==VhdlDocGen::PROCEDURE)
{
ret="end procedure";
}
else
{
ret="end process ";
}
FlowChart::addFlowChart(FlowChart::END_NO,ret,0);
// FlowChart::printFlowList();
FlowChart::writeFlowChart();
currP=0;
}
void VhdlParser::setMultCommentLine()
{
iDocLine=yyLineNr;
}
void VhdlParser::oneLineComment(QCString qcs)
{
bool isEndCode=qcs.contains("\\endcode");
int index = qcs.find("\\code");
if (isEndCode)
{
int end = inputString.find(qcs.data(),iCodeLen);
makeInlineDoc(end);
}
else if (index > 0)
{
// assert(false);
strComment=qcs;
startCodeBlock(index);
strComment.resize(0);
}
if (!isEndCode && index==-1)
{
int j=qcs.find("--!");
qcs=qcs.right(qcs.length()-3-j);
if (!checkMultiComment(qcs,iDocLine))
{
handleCommentBlock(qcs,TRUE);
}
}
}
bool checkMultiComment(QCString& qcs,int line)
{
QList<Entry> *pTemp=getEntryAtLine(VhdlParser::current_root,line);
if (pTemp->isEmpty()) return false;
VhdlDocGen::prepareComment(qcs);
while (!pTemp->isEmpty())
{
Entry *e=(Entry*)pTemp->getFirst();
e->briefLine=line;
e->brief+=qcs;
pTemp->removeFirst();
}
return true;
}
// returns the vhdl parsed types at line xxx
QList<Entry>* getEntryAtLine(const Entry* ce,int line)
{
EntryListIterator eli(*ce->children());
Entry *rt;
for (;(rt=eli.current());++eli)
{
if (rt->bodyLine==line)
{
lineEntry.insert(0,rt);
}
getEntryAtLine(rt,line);
}
return &lineEntry;
}