codeStringParsingTools.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. /**
  2. * Extracts the characters between two markers (for eg, between "(" and ")"). The function handles nested markers as well as markers inside strings (delimited by ", ' or `) and comments
  3. * @param markerOpen opening marker
  4. * @param markerClose closing marker
  5. * @param block code block to parse
  6. * @param startIndex starting index in block where the extraction must start. The character at block[startIndex] should be the markerOpen character!
  7. * @returns index of the last character for the extraction (or -1 if the string is invalid - no matching closing marker found). The string to extract (without the markers) is the string between startIndex + 1 and the returned value (exclusive)
  8. */
  9. export function ExtractBetweenMarkers(markerOpen, markerClose, block, startIndex) {
  10. let currPos = startIndex, openMarkers = 0, waitForChar = "";
  11. while (currPos < block.length) {
  12. const currChar = block.charAt(currPos);
  13. if (!waitForChar) {
  14. switch (currChar) {
  15. case markerOpen:
  16. openMarkers++;
  17. break;
  18. case markerClose:
  19. openMarkers--;
  20. break;
  21. case '"':
  22. case "'":
  23. case "`":
  24. waitForChar = currChar;
  25. break;
  26. case "/":
  27. if (currPos + 1 < block.length) {
  28. const nextChar = block.charAt(currPos + 1);
  29. if (nextChar === "/") {
  30. waitForChar = "\n";
  31. }
  32. else if (nextChar === "*") {
  33. waitForChar = "*/";
  34. }
  35. }
  36. break;
  37. }
  38. }
  39. else {
  40. if (currChar === waitForChar) {
  41. if (waitForChar === '"' || waitForChar === "'") {
  42. block.charAt(currPos - 1) !== "\\" && (waitForChar = "");
  43. }
  44. else {
  45. waitForChar = "";
  46. }
  47. }
  48. else if (waitForChar === "*/" && currChar === "*" && currPos + 1 < block.length) {
  49. block.charAt(currPos + 1) === "/" && (waitForChar = "");
  50. if (waitForChar === "") {
  51. currPos++;
  52. }
  53. }
  54. }
  55. currPos++;
  56. if (openMarkers === 0) {
  57. break;
  58. }
  59. }
  60. return openMarkers === 0 ? currPos - 1 : -1;
  61. }
  62. /**
  63. * Parses a string and skip whitespaces
  64. * @param s string to parse
  65. * @param index index where to start parsing
  66. * @returns the index after all whitespaces have been skipped
  67. */
  68. export function SkipWhitespaces(s, index) {
  69. while (index < s.length) {
  70. const c = s[index];
  71. if (c !== " " && c !== "\n" && c !== "\r" && c !== "\t" && c !== "\u000a" && c !== "\u00a0") {
  72. break;
  73. }
  74. index++;
  75. }
  76. return index;
  77. }
  78. /**
  79. * Checks if a character is an identifier character (meaning, if it is 0-9, A-Z, a-z or _)
  80. * @param c character to check
  81. * @returns true if the character is an identifier character
  82. */
  83. export function IsIdentifierChar(c) {
  84. const v = c.charCodeAt(0);
  85. return ((v >= 48 && v <= 57) || // 0-9
  86. (v >= 65 && v <= 90) || // A-Z
  87. (v >= 97 && v <= 122) || // a-z
  88. v == 95); // _
  89. }
  90. /**
  91. * Removes the comments of a code block
  92. * @param block code block to parse
  93. * @returns block with the comments removed
  94. */
  95. export function RemoveComments(block) {
  96. let currPos = 0, waitForChar = "", inComments = false;
  97. const s = [];
  98. while (currPos < block.length) {
  99. const currChar = block.charAt(currPos);
  100. if (!waitForChar) {
  101. switch (currChar) {
  102. case '"':
  103. case "'":
  104. case "`":
  105. waitForChar = currChar;
  106. break;
  107. case "/":
  108. if (currPos + 1 < block.length) {
  109. const nextChar = block.charAt(currPos + 1);
  110. if (nextChar === "/") {
  111. waitForChar = "\n";
  112. inComments = true;
  113. }
  114. else if (nextChar === "*") {
  115. waitForChar = "*/";
  116. inComments = true;
  117. }
  118. }
  119. break;
  120. }
  121. if (!inComments) {
  122. s.push(currChar);
  123. }
  124. }
  125. else {
  126. if (currChar === waitForChar) {
  127. if (waitForChar === '"' || waitForChar === "'") {
  128. block.charAt(currPos - 1) !== "\\" && (waitForChar = "");
  129. s.push(currChar);
  130. }
  131. else {
  132. waitForChar = "";
  133. inComments = false;
  134. }
  135. }
  136. else if (waitForChar === "*/" && currChar === "*" && currPos + 1 < block.length) {
  137. block.charAt(currPos + 1) === "/" && (waitForChar = "");
  138. if (waitForChar === "") {
  139. inComments = false;
  140. currPos++;
  141. }
  142. }
  143. else {
  144. if (!inComments) {
  145. s.push(currChar);
  146. }
  147. }
  148. }
  149. currPos++;
  150. }
  151. return s.join("");
  152. }
  153. /**
  154. * Finds the first occurrence of a character in a string going backward
  155. * @param s the string to parse
  156. * @param index starting index in the string
  157. * @param c the character to find
  158. * @param c2 an optional second character to find
  159. * @returns the index of the character if found, else -1
  160. */
  161. export function FindBackward(s, index, c, c2) {
  162. while (index >= 0 && s.charAt(index) !== c && (!c2 || s.charAt(index) !== c2)) {
  163. index--;
  164. }
  165. return index;
  166. }
  167. /**
  168. * Escapes a string so that it is usable as a regular expression
  169. * @param s string to escape
  170. * @returns escaped string
  171. */
  172. export function EscapeRegExp(s) {
  173. return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
  174. }
  175. //# sourceMappingURL=codeStringParsingTools.js.map