CMakeDoxygenFilter.cpp 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. /*=============================================================================
  2. Library: CTK
  3. Copyright (c) German Cancer Research Center,
  4. Division of Medical and Biological Informatics
  5. Licensed under the Apache License, Version 2.0 (the "License");
  6. you may not use this file except in compliance with the License.
  7. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. =============================================================================*/
  15. #include <cstdlib>
  16. #include <string>
  17. #include <fstream>
  18. #include <iostream>
  19. #include <assert.h>
  20. //--------------------------------------
  21. // Utilitiy classes and functions
  22. //--------------------------------------
  23. struct ci_char_traits : public std::char_traits<char>
  24. // just inherit all the other functions
  25. // that we don't need to override
  26. {
  27. static bool eq(char c1, char c2)
  28. { return toupper(c1) == toupper(c2); }
  29. static bool ne(char c1, char c2)
  30. { return toupper(c1) != toupper(c2); }
  31. static bool lt(char c1, char c2)
  32. { return toupper(c1) < toupper(c2); }
  33. static bool gt(char c1, char c2)
  34. { return toupper(c1) > toupper(c2); }
  35. static int compare(const char* s1, const char* s2, std::size_t n)
  36. {
  37. while (n-- > 0)
  38. {
  39. if (lt(*s1, *s2)) return -1;
  40. if (gt(*s1, *s2)) return 1;
  41. ++s1; ++s2;
  42. }
  43. return 0;
  44. }
  45. static const char* find(const char* s, int n, char a)
  46. {
  47. while (n-- > 0 && toupper(*s) != toupper(a))
  48. {
  49. ++s;
  50. }
  51. return s;
  52. }
  53. };
  54. typedef std::basic_string<char, ci_char_traits> ci_string;
  55. //--------------------------------------
  56. // Lexer
  57. //--------------------------------------
  58. class CMakeLexer
  59. {
  60. public:
  61. enum Token {
  62. TOK_EOF = -1,
  63. // commands
  64. TOK_MACRO = -2, TOK_ENDMACRO = -3,
  65. TOK_FUNCTION = -4, TOK_ENDFUNCTION = -5,
  66. TOK_DOXYGEN_COMMENT = -6,
  67. TOK_STRING_LITERAL = -100,
  68. // primary
  69. TOK_IDENTIFIER = -200
  70. };
  71. CMakeLexer(std::istream& is)
  72. : _lastChar(' '), _is(is), _line(1), _col(1)
  73. {}
  74. int getToken()
  75. {
  76. // skip whitespace
  77. while (isspace(_lastChar))
  78. {
  79. _lastChar = getChar();
  80. }
  81. if (isalpha(_lastChar) || _lastChar == '_')
  82. {
  83. _identifier = _lastChar;
  84. while (isalnum(_lastChar = getChar()) || _lastChar == '-' || _lastChar == '_')
  85. {
  86. _identifier += _lastChar;
  87. }
  88. if (_identifier == "function")
  89. return TOK_FUNCTION;
  90. if (_identifier == "macro")
  91. return TOK_MACRO;
  92. if (_identifier == "endfunction")
  93. return TOK_ENDFUNCTION;
  94. if (_identifier == "endmacro")
  95. return TOK_ENDMACRO;
  96. return TOK_IDENTIFIER;
  97. }
  98. if (_lastChar == '#')
  99. {
  100. _lastChar = getChar();
  101. if (_lastChar == '!')
  102. {
  103. // found a doxygen comment marker
  104. _identifier.clear();
  105. _lastChar = getChar();
  106. while (_lastChar != EOF && _lastChar != '\n' && _lastChar != '\r')
  107. {
  108. _identifier += _lastChar;
  109. _lastChar = getChar();
  110. }
  111. return TOK_DOXYGEN_COMMENT;
  112. }
  113. // skip the comment
  114. while (_lastChar != EOF && _lastChar != '\n' && _lastChar != '\r')
  115. {
  116. _lastChar = getChar();
  117. }
  118. }
  119. if (_lastChar == '"')
  120. {
  121. _lastChar = getChar();
  122. _identifier.clear();
  123. while (_lastChar != EOF && _lastChar != '"')
  124. {
  125. _identifier += _lastChar;
  126. _lastChar = getChar();
  127. }
  128. // eat the closing "
  129. _lastChar = getChar();
  130. return TOK_STRING_LITERAL;
  131. }
  132. // don't eat the EOF
  133. if (_lastChar == EOF) return TOK_EOF;
  134. // return the character as its ascii value
  135. int thisChar = _lastChar;
  136. _lastChar = getChar();
  137. return thisChar;
  138. }
  139. std::string getIdentifier() const
  140. {
  141. return std::string(_identifier.c_str());
  142. }
  143. int curLine() const
  144. { return _line; }
  145. int curCol() const
  146. { return _col; }
  147. int getChar()
  148. {
  149. int c = _is.get();
  150. updateLoc(c);
  151. return c;
  152. }
  153. private:
  154. void updateLoc(int c)
  155. {
  156. if (c == '\n' || c == '\r')
  157. {
  158. ++_line;
  159. _col = 1;
  160. }
  161. else
  162. {
  163. ++_col;
  164. }
  165. }
  166. ci_string _identifier;
  167. int _lastChar;
  168. std::istream& _is;
  169. int _line;
  170. int _col;
  171. };
  172. //--------------------------------------
  173. // Parser
  174. //--------------------------------------
  175. class CMakeParser
  176. {
  177. public:
  178. CMakeParser(std::istream& is, std::ostream& os)
  179. : _is(is), _os(os), _lexer(is), _curToken(CMakeLexer::TOK_EOF)
  180. { }
  181. int curToken()
  182. {
  183. return _curToken;
  184. }
  185. int nextToken()
  186. {
  187. return _curToken = _lexer.getToken();
  188. }
  189. void handleMacro()
  190. {
  191. if(!parseMacro())
  192. {
  193. // skip token for error recovery
  194. nextToken();
  195. }
  196. }
  197. void handleFunction()
  198. {
  199. if(!parseFunction())
  200. {
  201. // skip token for error recovery
  202. nextToken();
  203. }
  204. }
  205. void handleDoxygenComment()
  206. {
  207. _os << "///" << _lexer.getIdentifier() << std::endl;
  208. nextToken();
  209. }
  210. void handleTopLevelExpression()
  211. {
  212. // skip token
  213. nextToken();
  214. }
  215. private:
  216. void printError(const char* str)
  217. {
  218. std::cerr << "Error: " << str << " (at line " << _lexer.curLine() << ", col " << _lexer.curCol() << ")\n";
  219. }
  220. bool parseMacro()
  221. {
  222. if (nextToken() != '(')
  223. {
  224. printError("Expected '(' after MACRO");
  225. return false;
  226. }
  227. nextToken();
  228. std::string macroName = _lexer.getIdentifier();
  229. if (curToken() != CMakeLexer::TOK_IDENTIFIER || macroName.empty())
  230. {
  231. printError("Expected macro name");
  232. return false;
  233. }
  234. _os << macroName << '(';
  235. if (nextToken() == CMakeLexer::TOK_IDENTIFIER)
  236. {
  237. _os << _lexer.getIdentifier();
  238. while (nextToken() == CMakeLexer::TOK_IDENTIFIER)
  239. {
  240. _os << ", " << _lexer.getIdentifier();
  241. }
  242. }
  243. if (curToken() != ')')
  244. {
  245. printError("Missing expected ')'");
  246. }
  247. else
  248. {
  249. _os << ");" << std::endl;
  250. }
  251. // eat the ')'
  252. nextToken();
  253. return true;
  254. }
  255. bool parseFunction()
  256. {
  257. if (nextToken() != '(')
  258. {
  259. printError("Expected '(' after FUNCTION");
  260. return false;
  261. }
  262. nextToken();
  263. std::string funcName = _lexer.getIdentifier();
  264. if (curToken() != CMakeLexer::TOK_IDENTIFIER || funcName.empty())
  265. {
  266. printError("Expected function name");
  267. return false;
  268. }
  269. _os << funcName << '(';
  270. if (nextToken() == CMakeLexer::TOK_IDENTIFIER)
  271. {
  272. _os << _lexer.getIdentifier();
  273. while (nextToken() == CMakeLexer::TOK_IDENTIFIER)
  274. {
  275. _os << ", " << _lexer.getIdentifier();
  276. }
  277. }
  278. if (curToken() != ')')
  279. {
  280. printError("Missing expected ')'");
  281. }
  282. else
  283. {
  284. _os << ");" << std::endl;
  285. }
  286. // eat the ')'
  287. nextToken();
  288. return true;
  289. }
  290. std::istream& _is;
  291. std::ostream& _os;
  292. CMakeLexer _lexer;
  293. int _curToken;
  294. };
  295. #define STRINGIFY(a) #a
  296. #define DOUBLESTRINGIFY(a) STRINGIFY(a)
  297. int main(int argc, char** argv)
  298. {
  299. assert(argc > 1);
  300. for (int i = 1; i < argc; ++i)
  301. {
  302. std::ifstream ifs(argv[i]);
  303. std::ostream& os = std::cout;
  304. #ifdef USE_NAMESPACE
  305. os << "namespace " << DOUBLESTRINGIFY(USE_NAMESPACE) << " {\n";
  306. #endif
  307. CMakeParser parser(ifs, os);
  308. parser.nextToken();
  309. while (ifs.good())
  310. {
  311. switch (parser.curToken())
  312. {
  313. case CMakeLexer::TOK_EOF:
  314. return ifs.get(); // eat EOF
  315. case CMakeLexer::TOK_MACRO:
  316. parser.handleMacro();
  317. break;
  318. case CMakeLexer::TOK_FUNCTION:
  319. parser.handleFunction();
  320. break;
  321. case CMakeLexer::TOK_DOXYGEN_COMMENT:
  322. parser.handleDoxygenComment();
  323. break;
  324. default:
  325. parser.handleTopLevelExpression();
  326. break;
  327. }
  328. }
  329. #ifdef USE_NAMESPACE
  330. os << "}\n";
  331. #endif
  332. }
  333. return EXIT_SUCCESS;
  334. }