parsing.cpp 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. /*
  2. ktigcc - TIGCC IDE for KDE
  3. Copyright (C) 2006-2007 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>
  21. #include <QStringList>
  22. #include <QRegExp>
  23. #include <QTextCodec>
  24. #include <QApplication>
  25. #include <QEventLoop>
  26. #include <QDir>
  27. #include <Q3ValueList>
  28. #include <kprocio.h>
  29. #include <kmessagebox.h>
  30. #include <unistd.h>
  31. SourceFileFunctions getCFunctions(const QString &text)
  32. {
  33. // Parse C using Exuberant Ctags (http://ctags.sourceforge.net).
  34. SourceFileFunctions result;
  35. write_temp_file("parser_temp_source.c",text,0);
  36. {
  37. // The QTextCodec has to be passed explicitly, or it will default to
  38. // ISO-8859-1 regardless of the locale, which is just broken.
  39. KProcIO procio(QTextCodec::codecForLocale());
  40. // Use MergedStderr instead of Stderr so the messages get ordered
  41. // properly.
  42. procio.setComm(static_cast<KProcess::Communication>(
  43. KProcess::Stdout|KProcess::MergedStderr));
  44. procio.setWorkingDirectory(tempdir);
  45. procio<<"ctags"<<"-f"<<"-"<<"-n"<<"-u"<<"-h"<<".h"<<"--language-force=C"
  46. <<"--C-kinds=pf"<<"--fields=k"<<"-I"<<"CALLBACK,__ATTR_TIOS__,"
  47. "__ATTR_TIOS_NORETURN__,__ATTR_TIOS_CALLBACK__,__ATTR_GCC__,"
  48. "__ATTR_LIB_C__,__ATTR_LIB_ASM__,__ATTR_LIB_ASM_NORETURN__,"
  49. "__ATTR_LIB_CALLBACK_C__,__ATTR_LIB_CALLBACK_ASM__"
  50. <<"parser_temp_source.c";
  51. if (!procio.start()) {
  52. delete_temp_file("parser_temp_source.c");
  53. KMessageBox::error(0,"Could not run ctags.\nThis feature requires "
  54. "Exuberant Ctags, which can be obtained from: "
  55. "http://ctags.sourceforge.net");
  56. return result;
  57. }
  58. QString line;
  59. int ret;
  60. while ((ret=procio.readln(line))>=0 || procio.isRunning()) {
  61. if (ret>=0) {
  62. QStringList columns=line.split('\t');
  63. int numColumns=columns.count();
  64. QString identifier;
  65. if (numColumns) identifier=columns[0];
  66. QString linenoString;
  67. if (numColumns>2) linenoString=columns[2];
  68. int semicolonPos=linenoString.find(';');
  69. if (semicolonPos>=0) linenoString.truncate(semicolonPos);
  70. int lineno=linenoString.toInt()-1;
  71. QString kind;
  72. if (numColumns>3) kind=columns[3];
  73. SourceFileFunctions::Iterator it=result.find(identifier);
  74. if (kind=="p") {
  75. if (it==result.end())
  76. result.append(SourceFileFunction(identifier,lineno,-1));
  77. } else if (kind=="f") {
  78. if (it==result.end())
  79. result.append(SourceFileFunction(identifier,-1,lineno));
  80. else
  81. (*it).implementationLine=lineno;
  82. } else qWarning("Invalid result from ctags.");
  83. } else {
  84. usleep(10000);
  85. QCoreApplication::processEvents(QEventLoop::ExcludeUserInput,10);
  86. }
  87. }
  88. }
  89. delete_temp_file("parser_temp_source.c");
  90. return result;
  91. }
  92. SourceFileFunctions getASMFunctions(const QString &text)
  93. {
  94. // Parse ASM by hand.
  95. QStringList lines=text.split('\n');
  96. unsigned lineno=0;
  97. SourceFileFunctions result;
  98. for (QStringList::Iterator it=lines.begin(); it!=lines.end(); ++it,++lineno) {
  99. QString line=*it;
  100. if (line.isEmpty()) continue;
  101. QString identifier;
  102. unsigned col=0, l=line.length();
  103. while (col<l) {
  104. QChar c=line[col++];
  105. if ((c>='A'&&c<='Z')||(c>='a'&&c<='z')||(c>='0'&&c<='9')||c=='_'||c=='$')
  106. identifier.append(c);
  107. else
  108. break;
  109. }
  110. if (line[col-1]==':') result.append(SourceFileFunction(identifier,-1,lineno));
  111. }
  112. return result;
  113. }
  114. CompletionInfo parseFileCompletion(const QString &fileText,
  115. const QString &pathInProject,
  116. CompletionInfo result)
  117. {
  118. // Parse included files.
  119. // Empty lines can be ignored here.
  120. QStringList lines=fileText.split('\n',QString::SkipEmptyParts);
  121. bool inComment=false;
  122. bool isSystemHeader=pathInProject.isNull();
  123. for (QStringList::ConstIterator it=lines.begin(); it!=lines.end(); ++it) {
  124. const QString &line=(*it);
  125. if (!inComment) {
  126. QString strippedLine=line.trimmed();
  127. if (strippedLine.startsWith("#include")) {
  128. QString includedName=strippedLine.mid(8).trimmed();
  129. if (!includedName.isEmpty()) {
  130. if (includedName[0]=='<') {
  131. int pos=includedName.find('>',1);
  132. if (pos>=0)
  133. result.includedSystem.append(includedName.mid(1,pos-1));
  134. } else if (includedName[0]=='\"') {
  135. int pos=includedName.find('\"',1);
  136. if (pos>=0) {
  137. if (isSystemHeader)
  138. // A system header can only include another system header.
  139. result.includedSystem.append(includedName.mid(1,pos-1));
  140. else
  141. result.included.append(QDir::cleanPath(pathInProject+"/"
  142. +includedName.mid(1,pos-1)));
  143. }
  144. } // else ignore
  145. }
  146. }
  147. }
  148. int pos=0;
  149. if (inComment) {
  150. in_comment:
  151. pos=line.find("*/",pos);
  152. if (pos<0) continue; // Comment line only, next line.
  153. pos+=2;
  154. inComment=false;
  155. }
  156. pos=line.find("/*",pos);
  157. if (pos>=0) {
  158. pos+=2;
  159. inComment=true;
  160. goto in_comment;
  161. }
  162. }
  163. // Parse for prototypes etc. using ctags.
  164. QString fileTextCleaned=fileText;
  165. // ctags doesn't like asmspecs.
  166. fileTextCleaned.remove(QRegExp("\\b(asm|_asm|__asm)\\(\"%?[adAD][0-7]\"\\)"));
  167. write_temp_file("parser_temp_source.c",fileTextCleaned,0);
  168. {
  169. // The QTextCodec has to be passed explicitly, or it will default to
  170. // ISO-8859-1 regardless of the locale, which is just broken.
  171. KProcIO procio(QTextCodec::codecForLocale());
  172. // Use MergedStderr instead of Stderr so the messages get ordered
  173. // properly.
  174. procio.setComm(static_cast<KProcess::Communication>(
  175. KProcess::Stdout|KProcess::MergedStderr));
  176. procio.setWorkingDirectory(tempdir);
  177. procio<<"ctags"<<"-f"<<"-"<<"-n"<<"-u"<<"-h"<<".h"<<"--language-force=C"
  178. <<"--C-kinds=defgpstuvx"<<"--fields=kS"<<"-I"<<"CALLBACK,__ATTR_TIOS__,"
  179. "__ATTR_TIOS_NORETURN__,__ATTR_TIOS_CALLBACK__,__ATTR_GCC__,"
  180. "__ATTR_LIB_C__,__ATTR_LIB_ASM__,__ATTR_LIB_ASM_NORETURN__,"
  181. "__ATTR_LIB_CALLBACK_C__,__ATTR_LIB_CALLBACK_ASM__"
  182. <<"parser_temp_source.c";
  183. if (!procio.start()) {
  184. delete_temp_file("parser_temp_source.c");
  185. KMessageBox::error(0,"Could not run ctags.\nThis feature requires "
  186. "Exuberant Ctags, which can be obtained from: "
  187. "http://ctags.sourceforge.net");
  188. result.dirty=true;
  189. return result;
  190. }
  191. QString line;
  192. int ret;
  193. while ((ret=procio.readln(line))>=0 || procio.isRunning()) {
  194. if (ret>=0) {
  195. QStringList columns=line.split('\t');
  196. int numColumns=columns.count();
  197. QString identifier;
  198. if (numColumns) identifier=columns[0];
  199. QString linenoString;
  200. if (numColumns>2) linenoString=columns[2];
  201. int semicolonPos=linenoString.find(';');
  202. if (semicolonPos>=0) linenoString.truncate(semicolonPos);
  203. int lineno=linenoString.toInt()-1;
  204. QString kind;
  205. if (numColumns>3) kind=columns[3];
  206. QString type=(kind=="d")?"macro"
  207. :(kind=="e")?"enum"
  208. :(kind=="f" || kind=="p")?"func"
  209. :(kind=="v" || kind=="x")?"var"
  210. :"type";
  211. QString signature;
  212. if (numColumns>4) signature=columns[4];
  213. if (signature.startsWith("signature:")) signature.remove(0,10);
  214. signature.replace(QRegExp("\\s*,"),",").replace(QRegExp("\\s*\\)"),")");
  215. bool alreadyKnown=result.lineNumbers.contains(identifier);
  216. // This has to be done for system headers because there may already be
  217. // better information extracted from the .hsf files. However, .hsf files
  218. // obviously don't contain line number information.
  219. if (isSystemHeader) {
  220. for (Q3ValueList<CompletionEntry>::ConstIterator it
  221. =result.entries.begin(); it!=result.entries.end(); ++it) {
  222. if ((*it).text==identifier) {
  223. alreadyKnown=true;
  224. break;
  225. }
  226. }
  227. }
  228. if (lineno>=0)
  229. result.lineNumbers.insert(identifier,lineno,(kind!="p" && kind!="x"));
  230. if (!alreadyKnown) {
  231. CompletionEntry entry;
  232. entry.text=identifier;
  233. entry.prefix=type;
  234. entry.postfix=signature;
  235. result.entries.append(entry);
  236. }
  237. } else {
  238. usleep(10000);
  239. QCoreApplication::processEvents(QEventLoop::ExcludeUserInput,10);
  240. }
  241. }
  242. }
  243. delete_temp_file("parser_temp_source.c");
  244. return result;
  245. }