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.

281 lines
8.5KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. struct CppParserHelpers
  20. {
  21. static bool parseHexInt (const String& text, int64& result)
  22. {
  23. CppTokeniserFunctions::StringIterator i (text);
  24. if (CppTokeniserFunctions::parseHexLiteral (i))
  25. {
  26. result = text.fromFirstOccurrenceOf ("x", false, true).getHexValue64();
  27. return true;
  28. }
  29. return false;
  30. }
  31. static bool parseOctalInt (const String& text, int64& result)
  32. {
  33. CppTokeniserFunctions::StringIterator it (text);
  34. if (CppTokeniserFunctions::parseOctalLiteral (it))
  35. {
  36. result = 0;
  37. for (int i = 0; i < text.length(); ++i)
  38. {
  39. const int digit = text[i] - '0';
  40. if (digit < 0 || digit > 7)
  41. break;
  42. result = result * 8 + digit;
  43. }
  44. return true;
  45. }
  46. return false;
  47. }
  48. static bool parseDecimalInt (const String& text, int64& result)
  49. {
  50. CppTokeniserFunctions::StringIterator i (text);
  51. if (CppTokeniserFunctions::parseDecimalLiteral (i))
  52. {
  53. result = text.getLargeIntValue();
  54. return true;
  55. }
  56. return false;
  57. }
  58. static bool parseInt (const String& text, int64& result)
  59. {
  60. return parseHexInt (text, result)
  61. || parseOctalInt (text, result)
  62. || parseDecimalInt (text, result);
  63. }
  64. static bool parseFloat (const String& text, double& result)
  65. {
  66. CppTokeniserFunctions::StringIterator i (text);
  67. if (CppTokeniserFunctions::parseFloatLiteral (i))
  68. {
  69. result = text.getDoubleValue();
  70. return true;
  71. }
  72. return false;
  73. }
  74. static int parseSingleToken (const String& text)
  75. {
  76. if (text.isEmpty())
  77. return CPlusPlusCodeTokeniser::tokenType_error;
  78. CppTokeniserFunctions::StringIterator i (text);
  79. i.skipWhitespace();
  80. const int tok = CppTokeniserFunctions::readNextToken (i);
  81. i.skipWhitespace();
  82. i.skip();
  83. return i.isEOF() ? tok : CPlusPlusCodeTokeniser::tokenType_error;
  84. }
  85. static String getIntegerSuffix (const String& s) { return s.retainCharacters ("lLuU"); }
  86. static String getFloatSuffix (const String& s) { return s.retainCharacters ("fF"); }
  87. static String getReplacementStringInSameFormat (const String& old, double newValue)
  88. {
  89. {
  90. CppTokeniserFunctions::StringIterator i (old);
  91. if (CppTokeniserFunctions::parseFloatLiteral (i))
  92. {
  93. String s (newValue);
  94. if (! s.containsChar ('.'))
  95. s += ".0";
  96. return s + getFloatSuffix (old);
  97. }
  98. }
  99. return getReplacementStringInSameFormat (old, (int64) newValue);
  100. }
  101. static String getReplacementStringInSameFormat (const String& old, int64 newValue)
  102. {
  103. {
  104. CppTokeniserFunctions::StringIterator i (old);
  105. if (CppTokeniserFunctions::parseHexLiteral (i))
  106. {
  107. String s ("0x" + String::toHexString (newValue) + getIntegerSuffix (old));
  108. if (old.toUpperCase() == old)
  109. s = s.toUpperCase();
  110. return s;
  111. }
  112. }
  113. {
  114. CppTokeniserFunctions::StringIterator i (old);
  115. if (CppTokeniserFunctions::parseDecimalLiteral (i))
  116. return String (newValue) + getIntegerSuffix (old);
  117. }
  118. return old;
  119. }
  120. // Given a type name which could be a smart pointer or other pointer/ref, this extracts
  121. // the essential class name of the thing that it points to.
  122. static String getSignificantClass (String cls)
  123. {
  124. int firstAngleBracket = cls.indexOfChar ('<');
  125. if (firstAngleBracket > 0)
  126. cls = cls.substring (firstAngleBracket + 1).upToLastOccurrenceOf (">", false, false).trim();
  127. while (cls.endsWithChar ('*') || cls.endsWithChar ('&'))
  128. cls = cls.dropLastCharacters (1).trim();
  129. return cls;
  130. }
  131. //==============================================================================
  132. struct ValidCppIdentifierRestriction : public TextEditor::InputFilter
  133. {
  134. ValidCppIdentifierRestriction (bool allowTemplatesAndNamespaces)
  135. : className (allowTemplatesAndNamespaces) {}
  136. String filterNewText (TextEditor& ed, const String& text)
  137. {
  138. String allowedChars ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_");
  139. if (className)
  140. allowedChars += "<>:";
  141. if (ed.getHighlightedRegion().getStart() > 0)
  142. allowedChars += "0123456789";
  143. String s = text.retainCharacters (allowedChars);
  144. if (CPlusPlusCodeTokeniser::isReservedKeyword (ed.getText().replaceSection (ed.getHighlightedRegion().getStart(),
  145. ed.getHighlightedRegion().getLength(),
  146. s)))
  147. return String();
  148. return s;
  149. }
  150. bool className;
  151. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValidCppIdentifierRestriction)
  152. };
  153. };
  154. //==============================================================================
  155. struct CodeChange
  156. {
  157. CodeChange (Range<int> r, const String& t) : range (r), text (t)
  158. {
  159. }
  160. bool mergeWith (const CodeChange& next)
  161. {
  162. if (text.isEmpty())
  163. {
  164. if (next.text.isNotEmpty()
  165. && next.range.isEmpty()
  166. && next.range.getStart() == range.getStart())
  167. {
  168. text = next.text;
  169. return true;
  170. }
  171. if (next.text.isEmpty())
  172. {
  173. Range<int> nextRange (next.range);
  174. if (nextRange.getStart() >= range.getStart())
  175. nextRange += range.getLength();
  176. else if (nextRange.getEnd() > range.getStart())
  177. nextRange.setEnd (nextRange.getEnd() + range.getLength());
  178. if (range.intersects (nextRange)
  179. || range.getEnd() == nextRange.getStart()
  180. || range.getStart() == nextRange.getEnd())
  181. {
  182. range = range.getUnionWith (nextRange);
  183. return true;
  184. }
  185. }
  186. }
  187. else if (next.text.isEmpty())
  188. {
  189. if (next.range.getEnd() == range.getStart())
  190. {
  191. range.setStart (next.range.getStart());
  192. return true;
  193. }
  194. if (next.range.getStart() == range.getStart() + text.length())
  195. {
  196. range.setLength (range.getLength() + next.range.getLength());
  197. return true;
  198. }
  199. }
  200. return false;
  201. }
  202. void addToList (Array<CodeChange>& list) const
  203. {
  204. if (list.size() == 0 || ! list.getReference (list.size() - 1).mergeWith (*this))
  205. list.add (*this);
  206. }
  207. Range<int> range;
  208. String text;
  209. };
  210. //==============================================================================
  211. static inline String concatenateListOfStrings (const StringArray& s)
  212. {
  213. return s.joinIntoString ("\x01");
  214. }
  215. static inline StringArray separateJoinedStrings (const String& s)
  216. {
  217. return StringArray::fromTokens (s, "\x01", juce::StringRef());
  218. }