parsing.cpp 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. /*
  2. ktigcc - TIGCC IDE for KDE
  3. Copyright (C) 2006 Kevin Kofler
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software Foundation,
  14. Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  15. */
  16. // This file handles parsing of source files for the function list and for
  17. // completion purposes.
  18. #include "parsing.h"
  19. #include "ktigcc.h"
  20. #include <qstring.h>
  21. #include <qstringlist.h>
  22. #include <qregexp.h>
  23. #include <qtextcodec.h>
  24. #include <qapplication.h>
  25. #include <qeventloop.h>
  26. #include <qdir.h>
  27. #include <kprocio.h>
  28. #include <kmessagebox.h>
  29. #include <unistd.h>
  30. SourceFileFunctions getCFunctions(const QString &text)
  31. {
  32. // Parse C using Exuberant Ctags (http://ctags.sourceforge.net).
  33. SourceFileFunctions result;
  34. write_temp_file("parser_temp_source.c",text,0);
  35. {
  36. // The QTextCodec has to be passed explicitly, or it will default to
  37. // ISO-8859-1 regardless of the locale, which is just broken.
  38. KProcIO procio(QTextCodec::codecForLocale());
  39. // Use MergedStderr instead of Stderr so the messages get ordered
  40. // properly.
  41. procio.setComm(static_cast<KProcess::Communication>(
  42. KProcess::Stdout|KProcess::MergedStderr));
  43. procio.setWorkingDirectory(tempdir);
  44. procio<<"ctags"<<"-f"<<"-"<<"-n"<<"-u"<<"-h"<<".h"<<"--language-force=C"
  45. <<"--C-kinds=pf"<<"--fields=k"<<"-I"<<"CALLBACK,__ATTR_TIOS__,"
  46. "__ATTR_TIOS_NORETURN__,__ATTR_TIOS_CALLBACK__,__ATTR_GCC__,"
  47. "__ATTR_LIB_C__,__ATTR_LIB_ASM__,__ATTR_LIB_ASM_NORETURN__,"
  48. "__ATTR_LIB_CALLBACK_C__,__ATTR_LIB_CALLBACK_ASM__"
  49. <<"parser_temp_source.c";
  50. if (!procio.start()) {
  51. delete_temp_file("parser_temp_source.c");
  52. KMessageBox::error(0,"Could not run ctags.\nThis feature requires "
  53. "Exuberant Ctags, which can be obtained from: "
  54. "http://ctags.sourceforge.net");
  55. return result;
  56. }
  57. QString line;
  58. int ret;
  59. while ((ret=procio.readln(line))>=0 || procio.isRunning()) {
  60. if (ret>=0) {
  61. QStringList columns=QStringList::split('\t',line,TRUE);
  62. QString identifier=columns[0];
  63. QString linenoString=columns[2];
  64. int semicolonPos=linenoString.find(';');
  65. if (semicolonPos>=0) linenoString.truncate(semicolonPos);
  66. int lineno=linenoString.toInt()-1;
  67. QString kind=columns[3];
  68. SourceFileFunctions::Iterator it=result.find(identifier);
  69. if (kind=="p") {
  70. if (it==result.end())
  71. result.append(SourceFileFunction(identifier,lineno,-1));
  72. } else if (kind=="f") {
  73. if (it==result.end())
  74. result.append(SourceFileFunction(identifier,-1,lineno));
  75. else
  76. (*it).implementationLine=lineno;
  77. } else qWarning("Invalid result from ctags.");
  78. } else {
  79. usleep(10000);
  80. QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput,10);
  81. }
  82. }
  83. }
  84. delete_temp_file("parser_temp_source.c");
  85. return result;
  86. }
  87. SourceFileFunctions getASMFunctions(const QString &text)
  88. {
  89. // Parse ASM by hand.
  90. QStringList lines=QStringList::split('\n',text,TRUE);
  91. unsigned lineno=0;
  92. SourceFileFunctions result;
  93. for (QStringList::Iterator it=lines.begin(); it!=lines.end(); ++it,++lineno) {
  94. QString line=*it;
  95. if (line.isEmpty()) continue;
  96. QString identifier;
  97. unsigned col=0, l=line.length();
  98. while (col<l) {
  99. QChar c=line[col++];
  100. if ((c>='A'&&c<='Z')||(c>='a'&&c<='z')||(c>='0'&&c<='9')||c=='_'||c=='$')
  101. identifier.append(c);
  102. else
  103. break;
  104. }
  105. if (line[col-1]==':') result.append(SourceFileFunction(identifier,-1,lineno));
  106. }
  107. return result;
  108. }
  109. CompletionInfo parseFileCompletion(const QString &fileText,
  110. const QString &pathInProject,
  111. CompletionInfo result)
  112. {
  113. // Parse included files.
  114. // Empty lines can be ignored here.
  115. QStringList lines=QStringList::split('\n',fileText);
  116. bool inComment=false;
  117. bool isSystemHeader=pathInProject.isNull();
  118. for (QStringList::ConstIterator it=lines.begin(); it!=lines.end(); ++it) {
  119. const QString &line=(*it);
  120. if (!inComment) {
  121. QString strippedLine=line.stripWhiteSpace();
  122. if (strippedLine.startsWith("#include")) {
  123. QString includedName=strippedLine.mid(8).stripWhiteSpace();
  124. if (includedName[0]=='<') {
  125. int pos=includedName.find('>',1);
  126. if (pos>=0)
  127. result.includedSystem.append(includedName.mid(1,pos-1));
  128. } else if (includedName[0]=='\"') {
  129. int pos=includedName.find('\"',1);
  130. if (pos>=0) {
  131. if (isSystemHeader)
  132. // A system header can only include another system header.
  133. result.includedSystem.append(includedName.mid(1,pos-1));
  134. else
  135. result.included.append(QDir::cleanDirPath(pathInProject+"/"
  136. +includedName.mid(1,pos-1)));
  137. }
  138. } // else ignore
  139. }
  140. }
  141. int pos=0;
  142. if (inComment) {
  143. in_comment:
  144. pos=line.find("*/",pos);
  145. if (pos<0) continue; // Comment line only, next line.
  146. pos+=2;
  147. inComment=false;
  148. }
  149. pos=line.find("/*",pos);
  150. if (pos>=0) {
  151. pos+=2;
  152. inComment=true;
  153. goto in_comment;
  154. }
  155. }
  156. // Parse for prototypes etc. using ctags.
  157. QString fileTextCleaned=fileText;
  158. // ctags doesn't like asmspecs.
  159. fileTextCleaned.remove(QRegExp("\\b(asm|_asm|__asm)\\(\"%?[adAD][0-7]\"\\)"));
  160. write_temp_file("parser_temp_source.c",fileTextCleaned,0);
  161. {
  162. // The QTextCodec has to be passed explicitly, or it will default to
  163. // ISO-8859-1 regardless of the locale, which is just broken.
  164. KProcIO procio(QTextCodec::codecForLocale());
  165. // Use MergedStderr instead of Stderr so the messages get ordered
  166. // properly.
  167. procio.setComm(static_cast<KProcess::Communication>(
  168. KProcess::Stdout|KProcess::MergedStderr));
  169. procio.setWorkingDirectory(tempdir);
  170. procio<<"ctags"<<"-f"<<"-"<<"-n"<<"-u"<<"-h"<<".h"<<"--language-force=C"
  171. <<"--C-kinds=defgpstuvx"<<"--fields=kS"<<"-I"<<"CALLBACK,__ATTR_TIOS__,"
  172. "__ATTR_TIOS_NORETURN__,__ATTR_TIOS_CALLBACK__,__ATTR_GCC__,"
  173. "__ATTR_LIB_C__,__ATTR_LIB_ASM__,__ATTR_LIB_ASM_NORETURN__,"
  174. "__ATTR_LIB_CALLBACK_C__,__ATTR_LIB_CALLBACK_ASM__"
  175. <<"parser_temp_source.c";
  176. if (!procio.start()) {
  177. delete_temp_file("parser_temp_source.c");
  178. KMessageBox::error(0,"Could not run ctags.\nThis feature requires "
  179. "Exuberant Ctags, which can be obtained from: "
  180. "http://ctags.sourceforge.net");
  181. result.dirty=true;
  182. return result;
  183. }
  184. QString line;
  185. int ret;
  186. while ((ret=procio.readln(line))>=0 || procio.isRunning()) {
  187. if (ret>=0) {
  188. QStringList columns=QStringList::split('\t',line,TRUE);
  189. QString identifier=columns[0];
  190. QString linenoString=columns[2];
  191. int semicolonPos=linenoString.find(';');
  192. if (semicolonPos>=0) linenoString.truncate(semicolonPos);
  193. int lineno=linenoString.toInt()-1;
  194. QString kind=columns[3];
  195. QString type=(kind=="d")?"macro"
  196. :(kind=="e")?"enum"
  197. :(kind=="f" || kind=="p")?"func"
  198. :(kind=="v" || kind=="x")?"var"
  199. :"type";
  200. QString signature=columns[4];
  201. if (signature.startsWith("signature:")) signature.remove(0,10);
  202. signature.replace(QRegExp("\\s*,"),",").replace(QRegExp("\\s*\\)"),")");
  203. bool alreadyKnown=result.lineNumbers.contains(identifier);
  204. // This has to be done for system headers because there may already be
  205. // better information extracted from the .hsf files. However, .hsf files
  206. // obviously don't contain line number information.
  207. if (isSystemHeader) {
  208. for (QValueList<KTextEditor::CompletionEntry>::ConstIterator it
  209. =result.entries.begin(); it!=result.entries.end(); ++it) {
  210. if ((*it).text==identifier) {
  211. alreadyKnown=true;
  212. break;
  213. }
  214. }
  215. }
  216. if (lineno>=0)
  217. result.lineNumbers.insert(identifier,lineno,(kind!="p" && kind!="x"));
  218. if (!alreadyKnown) {
  219. KTextEditor::CompletionEntry entry;
  220. entry.text=identifier;
  221. entry.prefix=type;
  222. entry.postfix=signature;
  223. result.entries.append(entry);
  224. }
  225. } else {
  226. usleep(10000);
  227. QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput,10);
  228. }
  229. }
  230. }
  231. delete_temp_file("parser_temp_source.c");
  232. return result;
  233. }