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.

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