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.

541 lines
16KB

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