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.

561 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", 0 };
  38. static const char* const keywords3Char[] =
  39. { "for", "int", "new", "try", "xor", "and", "asm", "not", 0 };
  40. static const char* const keywords4Char[] =
  41. { "bool", "void", "this", "true", "long", "else", "char",
  42. "enum", "case", "goto", "auto", 0 };
  43. static const char* const keywords5Char[] =
  44. { "while", "bitor", "break", "catch", "class", "compl", "const", "false",
  45. "float", "short", "throw", "union", "using", "or_eq", 0 };
  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", 0};
  50. static const char* const keywords7Char[] =
  51. { "default", "mutable", "private", "typedef", "nullptr", "virtual", 0 };
  52. static const char* const keywordsOther[] =
  53. { "noexcept", "const_cast", "continue", "explicit", "namespace",
  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", 0 };
  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<class 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<class 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<class 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<class 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<class 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<class 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<class 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<class 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<class 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<class 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<class Iterator>
  291. static void skipIfNextCharMatches (Iterator& source, const juce_wchar c) noexcept
  292. {
  293. if (source.peekNextChar() == c)
  294. source.skip();
  295. }
  296. template<class 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<class Iterator>
  304. static int readNextToken (Iterator& source)
  305. {
  306. int result = CPlusPlusCodeTokeniser::tokenType_error;
  307. source.skipWhitespace();
  308. const juce_wchar firstChar = source.peekNextChar();
  309. switch (firstChar)
  310. {
  311. case 0:
  312. source.skip();
  313. break;
  314. case '0':
  315. case '1':
  316. case '2':
  317. case '3':
  318. case '4':
  319. case '5':
  320. case '6':
  321. case '7':
  322. case '8':
  323. case '9':
  324. case '.':
  325. result = parseNumber (source);
  326. if (result == CPlusPlusCodeTokeniser::tokenType_error)
  327. {
  328. source.skip();
  329. if (firstChar == '.')
  330. result = CPlusPlusCodeTokeniser::tokenType_punctuation;
  331. }
  332. break;
  333. case ',':
  334. case ';':
  335. case ':':
  336. source.skip();
  337. result = CPlusPlusCodeTokeniser::tokenType_punctuation;
  338. break;
  339. case '(':
  340. case ')':
  341. case '{':
  342. case '}':
  343. case '[':
  344. case ']':
  345. source.skip();
  346. result = CPlusPlusCodeTokeniser::tokenType_bracket;
  347. break;
  348. case '"':
  349. case '\'':
  350. skipQuotedString (source);
  351. result = CPlusPlusCodeTokeniser::tokenType_string;
  352. break;
  353. case '+':
  354. result = CPlusPlusCodeTokeniser::tokenType_operator;
  355. source.skip();
  356. skipIfNextCharMatches (source, '+', '=');
  357. break;
  358. case '-':
  359. source.skip();
  360. result = parseNumber (source);
  361. if (result == CPlusPlusCodeTokeniser::tokenType_error)
  362. {
  363. result = CPlusPlusCodeTokeniser::tokenType_operator;
  364. skipIfNextCharMatches (source, '-', '=');
  365. }
  366. break;
  367. case '*':
  368. case '%':
  369. case '=':
  370. case '!':
  371. result = CPlusPlusCodeTokeniser::tokenType_operator;
  372. source.skip();
  373. skipIfNextCharMatches (source, '=');
  374. break;
  375. case '/':
  376. result = CPlusPlusCodeTokeniser::tokenType_operator;
  377. source.skip();
  378. if (source.peekNextChar() == '=')
  379. {
  380. source.skip();
  381. }
  382. else if (source.peekNextChar() == '/')
  383. {
  384. result = CPlusPlusCodeTokeniser::tokenType_comment;
  385. source.skipToEndOfLine();
  386. }
  387. else if (source.peekNextChar() == '*')
  388. {
  389. source.skip();
  390. result = CPlusPlusCodeTokeniser::tokenType_comment;
  391. skipComment (source);
  392. }
  393. break;
  394. case '?':
  395. case '~':
  396. source.skip();
  397. result = CPlusPlusCodeTokeniser::tokenType_operator;
  398. break;
  399. case '<':
  400. case '>':
  401. case '|':
  402. case '&':
  403. case '^':
  404. source.skip();
  405. result = CPlusPlusCodeTokeniser::tokenType_operator;
  406. skipIfNextCharMatches (source, firstChar);
  407. skipIfNextCharMatches (source, '=');
  408. break;
  409. case '#':
  410. result = CPlusPlusCodeTokeniser::tokenType_preprocessor;
  411. skipPreprocessorLine (source);
  412. break;
  413. default:
  414. if (isIdentifierStart (firstChar))
  415. result = parseIdentifier (source);
  416. else
  417. source.skip();
  418. break;
  419. }
  420. return result;
  421. }
  422. /** A class that can be passed to the CppTokeniserFunctions functions in order to
  423. parse a String.
  424. */
  425. struct StringIterator
  426. {
  427. StringIterator (const String& s) noexcept : t (s.getCharPointer()), numChars (0) {}
  428. StringIterator (String::CharPointerType s) noexcept : t (s), numChars (0) {}
  429. juce_wchar nextChar() noexcept { if (isEOF()) return 0; ++numChars; return t.getAndAdvance(); }
  430. juce_wchar peekNextChar()noexcept { return *t; }
  431. void skip() noexcept { if (! isEOF()) { ++t; ++numChars; } }
  432. void skipWhitespace() noexcept { while (t.isWhitespace()) skip(); }
  433. void skipToEndOfLine() noexcept { while (*t != '\r' && *t != '\n' && *t != 0) skip(); }
  434. bool isEOF() const noexcept { return t.isEmpty(); }
  435. String::CharPointerType t;
  436. int numChars;
  437. };
  438. };
  439. #endif // JUCE_CPLUSPLUSCODETOKENISERFUNCTIONS_H_INCLUDED