The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

551 lines
16KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. /** Some basic functions for simple tokenising of C++ code. */
  19. struct CppTokeniserFunctions
  20. {
  21. static bool isIdentifierStart (const juce_wchar c) noexcept
  22. {
  23. return CharacterFunctions::isLetter (c)
  24. || c == '_' || c == '@';
  25. }
  26. static bool isIdentifierBody (const juce_wchar c) noexcept
  27. {
  28. return CharacterFunctions::isLetterOrDigit (c)
  29. || c == '_' || c == '@';
  30. }
  31. static bool isReservedKeyword (String::CharPointerType token, const int tokenLength) noexcept
  32. {
  33. static const char* const keywords2Char[] =
  34. { "if", "do", "or", "id", 0 };
  35. static const char* const keywords3Char[] =
  36. { "for", "int", "new", "try", "xor", "and", "asm", "not", 0 };
  37. static const char* const keywords4Char[] =
  38. { "bool", "void", "this", "true", "long", "else", "char",
  39. "enum", "case", "goto", "auto", 0 };
  40. static const char* const keywords5Char[] =
  41. { "while", "bitor", "break", "catch", "class", "compl", "const", "false",
  42. "float", "short", "throw", "union", "using", "or_eq", 0 };
  43. static const char* const keywords6Char[] =
  44. { "return", "struct", "and_eq", "bitand", "delete", "double", "extern",
  45. "friend", "inline", "not_eq", "public", "sizeof", "static", "signed",
  46. "switch", "typeid", "wchar_t", "xor_eq", 0};
  47. static const char* const keywords7Char[] =
  48. { "default", "mutable", "private", "typedef", "nullptr", "virtual", 0 };
  49. static const char* const keywordsOther[] =
  50. { "noexcept", "const_cast", "continue", "explicit", "namespace",
  51. "operator", "protected", "register", "reinterpret_cast", "static_cast",
  52. "template", "typename", "unsigned", "volatile", "constexpr",
  53. "@implementation", "@interface", "@end", "@synthesize", "@dynamic", "@public",
  54. "@private", "@property", "@protected", "@class", 0 };
  55. const char* const* k;
  56. switch (tokenLength)
  57. {
  58. case 2: k = keywords2Char; break;
  59. case 3: k = keywords3Char; break;
  60. case 4: k = keywords4Char; break;
  61. case 5: k = keywords5Char; break;
  62. case 6: k = keywords6Char; break;
  63. case 7: k = keywords7Char; break;
  64. default:
  65. if (tokenLength < 2 || tokenLength > 16)
  66. return false;
  67. k = keywordsOther;
  68. break;
  69. }
  70. for (int i = 0; k[i] != 0; ++i)
  71. if (token.compare (CharPointer_ASCII (k[i])) == 0)
  72. return true;
  73. return false;
  74. }
  75. template<class Iterator>
  76. static int parseIdentifier (Iterator& source) noexcept
  77. {
  78. int tokenLength = 0;
  79. String::CharPointerType::CharType possibleIdentifier [100];
  80. String::CharPointerType possible (possibleIdentifier);
  81. while (isIdentifierBody (source.peekNextChar()))
  82. {
  83. const juce_wchar c = source.nextChar();
  84. if (tokenLength < 20)
  85. possible.write (c);
  86. ++tokenLength;
  87. }
  88. if (tokenLength > 1 && tokenLength <= 16)
  89. {
  90. possible.writeNull();
  91. if (isReservedKeyword (String::CharPointerType (possibleIdentifier), tokenLength))
  92. return CPlusPlusCodeTokeniser::tokenType_keyword;
  93. }
  94. return CPlusPlusCodeTokeniser::tokenType_identifier;
  95. }
  96. template<class Iterator>
  97. static bool skipNumberSuffix (Iterator& source)
  98. {
  99. const juce_wchar c = source.peekNextChar();
  100. if (c == 'l' || c == 'L' || c == 'u' || c == 'U')
  101. source.skip();
  102. if (CharacterFunctions::isLetterOrDigit (source.peekNextChar()))
  103. return false;
  104. return true;
  105. }
  106. static bool isHexDigit (const juce_wchar c) noexcept
  107. {
  108. return (c >= '0' && c <= '9')
  109. || (c >= 'a' && c <= 'f')
  110. || (c >= 'A' && c <= 'F');
  111. }
  112. template<class Iterator>
  113. static bool parseHexLiteral (Iterator& source) noexcept
  114. {
  115. if (source.peekNextChar() == '-')
  116. source.skip();
  117. if (source.nextChar() != '0')
  118. return false;
  119. juce_wchar c = source.nextChar();
  120. if (c != 'x' && c != 'X')
  121. return false;
  122. int numDigits = 0;
  123. while (isHexDigit (source.peekNextChar()))
  124. {
  125. ++numDigits;
  126. source.skip();
  127. }
  128. if (numDigits == 0)
  129. return false;
  130. return skipNumberSuffix (source);
  131. }
  132. static bool isOctalDigit (const juce_wchar c) noexcept
  133. {
  134. return c >= '0' && c <= '7';
  135. }
  136. template<class Iterator>
  137. static bool parseOctalLiteral (Iterator& source) noexcept
  138. {
  139. if (source.peekNextChar() == '-')
  140. source.skip();
  141. if (source.nextChar() != '0')
  142. return false;
  143. if (! isOctalDigit (source.nextChar()))
  144. return false;
  145. while (isOctalDigit (source.peekNextChar()))
  146. source.skip();
  147. return skipNumberSuffix (source);
  148. }
  149. static bool isDecimalDigit (const juce_wchar c) noexcept
  150. {
  151. return c >= '0' && c <= '9';
  152. }
  153. template<class Iterator>
  154. static bool parseDecimalLiteral (Iterator& source) noexcept
  155. {
  156. if (source.peekNextChar() == '-')
  157. source.skip();
  158. int numChars = 0;
  159. while (isDecimalDigit (source.peekNextChar()))
  160. {
  161. ++numChars;
  162. source.skip();
  163. }
  164. if (numChars == 0)
  165. return false;
  166. return skipNumberSuffix (source);
  167. }
  168. template<class Iterator>
  169. static bool parseFloatLiteral (Iterator& source) noexcept
  170. {
  171. if (source.peekNextChar() == '-')
  172. source.skip();
  173. int numDigits = 0;
  174. while (isDecimalDigit (source.peekNextChar()))
  175. {
  176. source.skip();
  177. ++numDigits;
  178. }
  179. const bool hasPoint = (source.peekNextChar() == '.');
  180. if (hasPoint)
  181. {
  182. source.skip();
  183. while (isDecimalDigit (source.peekNextChar()))
  184. {
  185. source.skip();
  186. ++numDigits;
  187. }
  188. }
  189. if (numDigits == 0)
  190. return false;
  191. juce_wchar c = source.peekNextChar();
  192. const bool hasExponent = (c == 'e' || c == 'E');
  193. if (hasExponent)
  194. {
  195. source.skip();
  196. c = source.peekNextChar();
  197. if (c == '+' || c == '-')
  198. source.skip();
  199. int numExpDigits = 0;
  200. while (isDecimalDigit (source.peekNextChar()))
  201. {
  202. source.skip();
  203. ++numExpDigits;
  204. }
  205. if (numExpDigits == 0)
  206. return false;
  207. }
  208. c = source.peekNextChar();
  209. if (c == 'f' || c == 'F')
  210. source.skip();
  211. else if (! (hasExponent || hasPoint))
  212. return false;
  213. return true;
  214. }
  215. template<class Iterator>
  216. static int parseNumber (Iterator& source)
  217. {
  218. const Iterator original (source);
  219. if (parseFloatLiteral (source)) return CPlusPlusCodeTokeniser::tokenType_float;
  220. source = original;
  221. if (parseHexLiteral (source)) return CPlusPlusCodeTokeniser::tokenType_integer;
  222. source = original;
  223. if (parseOctalLiteral (source)) return CPlusPlusCodeTokeniser::tokenType_integer;
  224. source = original;
  225. if (parseDecimalLiteral (source)) return CPlusPlusCodeTokeniser::tokenType_integer;
  226. source = original;
  227. return CPlusPlusCodeTokeniser::tokenType_error;
  228. }
  229. template<class Iterator>
  230. static void skipQuotedString (Iterator& source) noexcept
  231. {
  232. const juce_wchar quote = source.nextChar();
  233. for (;;)
  234. {
  235. const juce_wchar c = source.nextChar();
  236. if (c == quote || c == 0)
  237. break;
  238. if (c == '\\')
  239. source.skip();
  240. }
  241. }
  242. template<class Iterator>
  243. static void skipComment (Iterator& source) noexcept
  244. {
  245. bool lastWasStar = false;
  246. for (;;)
  247. {
  248. const juce_wchar c = source.nextChar();
  249. if (c == 0 || (c == '/' && lastWasStar))
  250. break;
  251. lastWasStar = (c == '*');
  252. }
  253. }
  254. template<class Iterator>
  255. static void skipPreprocessorLine (Iterator& source) noexcept
  256. {
  257. bool lastWasBackslash = false;
  258. for (;;)
  259. {
  260. const juce_wchar c = source.peekNextChar();
  261. if (c == '"')
  262. {
  263. skipQuotedString (source);
  264. continue;
  265. }
  266. if (c == '/')
  267. {
  268. Iterator next (source);
  269. next.skip();
  270. const juce_wchar c2 = next.peekNextChar();
  271. if (c2 == '/' || c2 == '*')
  272. return;
  273. }
  274. if (c == 0)
  275. break;
  276. if (c == '\n' || c == '\r')
  277. {
  278. source.skipToEndOfLine();
  279. if (lastWasBackslash)
  280. skipPreprocessorLine (source);
  281. break;
  282. }
  283. lastWasBackslash = (c == '\\');
  284. source.skip();
  285. }
  286. }
  287. template<class Iterator>
  288. static void skipIfNextCharMatches (Iterator& source, const juce_wchar c) noexcept
  289. {
  290. if (source.peekNextChar() == c)
  291. source.skip();
  292. }
  293. template<class Iterator>
  294. static void skipIfNextCharMatches (Iterator& source, const juce_wchar c1, const juce_wchar c2) noexcept
  295. {
  296. const juce_wchar c = source.peekNextChar();
  297. if (c == c1 || c == c2)
  298. source.skip();
  299. }
  300. template<class Iterator>
  301. static int readNextToken (Iterator& source)
  302. {
  303. int result = CPlusPlusCodeTokeniser::tokenType_error;
  304. source.skipWhitespace();
  305. const juce_wchar firstChar = source.peekNextChar();
  306. switch (firstChar)
  307. {
  308. case 0:
  309. source.skip();
  310. break;
  311. case '0':
  312. case '1':
  313. case '2':
  314. case '3':
  315. case '4':
  316. case '5':
  317. case '6':
  318. case '7':
  319. case '8':
  320. case '9':
  321. case '.':
  322. result = parseNumber (source);
  323. if (result == CPlusPlusCodeTokeniser::tokenType_error)
  324. {
  325. source.skip();
  326. if (firstChar == '.')
  327. result = CPlusPlusCodeTokeniser::tokenType_punctuation;
  328. }
  329. break;
  330. case ',':
  331. case ';':
  332. case ':':
  333. source.skip();
  334. result = CPlusPlusCodeTokeniser::tokenType_punctuation;
  335. break;
  336. case '(':
  337. case ')':
  338. case '{':
  339. case '}':
  340. case '[':
  341. case ']':
  342. source.skip();
  343. result = CPlusPlusCodeTokeniser::tokenType_bracket;
  344. break;
  345. case '"':
  346. case '\'':
  347. skipQuotedString (source);
  348. result = CPlusPlusCodeTokeniser::tokenType_string;
  349. break;
  350. case '+':
  351. result = CPlusPlusCodeTokeniser::tokenType_operator;
  352. source.skip();
  353. skipIfNextCharMatches (source, '+', '=');
  354. break;
  355. case '-':
  356. source.skip();
  357. result = parseNumber (source);
  358. if (result == CPlusPlusCodeTokeniser::tokenType_error)
  359. {
  360. result = CPlusPlusCodeTokeniser::tokenType_operator;
  361. skipIfNextCharMatches (source, '-', '=');
  362. }
  363. break;
  364. case '*':
  365. case '%':
  366. case '=':
  367. case '!':
  368. result = CPlusPlusCodeTokeniser::tokenType_operator;
  369. source.skip();
  370. skipIfNextCharMatches (source, '=');
  371. break;
  372. case '/':
  373. result = CPlusPlusCodeTokeniser::tokenType_operator;
  374. source.skip();
  375. if (source.peekNextChar() == '=')
  376. {
  377. source.skip();
  378. }
  379. else if (source.peekNextChar() == '/')
  380. {
  381. result = CPlusPlusCodeTokeniser::tokenType_comment;
  382. source.skipToEndOfLine();
  383. }
  384. else if (source.peekNextChar() == '*')
  385. {
  386. source.skip();
  387. result = CPlusPlusCodeTokeniser::tokenType_comment;
  388. skipComment (source);
  389. }
  390. break;
  391. case '?':
  392. case '~':
  393. source.skip();
  394. result = CPlusPlusCodeTokeniser::tokenType_operator;
  395. break;
  396. case '<':
  397. case '>':
  398. case '|':
  399. case '&':
  400. case '^':
  401. source.skip();
  402. result = CPlusPlusCodeTokeniser::tokenType_operator;
  403. skipIfNextCharMatches (source, firstChar);
  404. skipIfNextCharMatches (source, '=');
  405. break;
  406. case '#':
  407. result = CPlusPlusCodeTokeniser::tokenType_preprocessor;
  408. skipPreprocessorLine (source);
  409. break;
  410. default:
  411. if (isIdentifierStart (firstChar))
  412. result = parseIdentifier (source);
  413. else
  414. source.skip();
  415. break;
  416. }
  417. return result;
  418. }
  419. /** A class that the tokeniser can use to parse a string. */
  420. struct StringIterator
  421. {
  422. StringIterator (const String& s) noexcept : t (s.getCharPointer()), numChars (0) {}
  423. StringIterator (const String::CharPointerType& s) noexcept : t (s), numChars (0) {}
  424. juce_wchar nextChar() noexcept { if (isEOF()) return 0; ++numChars; return t.getAndAdvance(); }
  425. juce_wchar peekNextChar()noexcept { return *t; }
  426. void skip() noexcept { if (! isEOF()) { ++t; ++numChars; } }
  427. void skipWhitespace() noexcept { while (t.isWhitespace()) skip(); }
  428. void skipToEndOfLine() noexcept { while (*t != '\r' && *t != '\n' && *t != 0) skip(); }
  429. bool isEOF() const noexcept { return t.isEmpty(); }
  430. String::CharPointerType t;
  431. int numChars;
  432. };
  433. };