mark.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. var TokenType = require('../token-types');
  2. module.exports = (function() {
  3. /**
  4. * Mark whitespaces and comments
  5. */
  6. function markSC(tokens) {
  7. var tokensLength = tokens.length,
  8. ws = -1, // flag for whitespaces
  9. sc = -1, // flag for whitespaces and comments
  10. t; // current token
  11. // For every token in the token list, mark spaces and line breaks
  12. // as spaces (set both `ws` and `sc` flags). Mark multiline comments
  13. // with `sc` flag.
  14. // If there are several spaces or tabs or line breaks or multiline
  15. // comments in a row, group them: take the last one's index number
  16. // and save it to the first token in the group as a reference
  17. // (e.g., `ws_last = 7` for a group of whitespaces or `sc_last = 9`
  18. // for a group of whitespaces and comments):
  19. for (var i = 0; i < tokensLength; i++) {
  20. t = tokens[i];
  21. switch (t.type) {
  22. case TokenType.Space:
  23. case TokenType.Tab:
  24. t.ws = true;
  25. t.sc = true;
  26. if (ws === -1) ws = i;
  27. if (sc === -1) sc = i;
  28. break;
  29. case TokenType.Newline:
  30. t.ws = true;
  31. t.sc = true;
  32. ws = ws === -1 ? i : ws;
  33. sc = sc === -1 ? i : ws;
  34. tokens[ws].ws_last = i - 1;
  35. tokens[sc].sc_last = i - 1;
  36. tokens[i].ws_last = i;
  37. tokens[i].sc_last = i;
  38. ws = -1;
  39. sc = -1;
  40. break;
  41. case TokenType.CommentML:
  42. case TokenType.CommentSL:
  43. if (ws !== -1) {
  44. tokens[ws].ws_last = i - 1;
  45. ws = -1;
  46. }
  47. t.sc = true;
  48. break;
  49. default:
  50. if (ws !== -1) {
  51. tokens[ws].ws_last = i - 1;
  52. ws = -1;
  53. }
  54. if (sc !== -1) {
  55. tokens[sc].sc_last = i - 1;
  56. sc = -1;
  57. }
  58. }
  59. }
  60. if (ws !== -1) tokens[ws].ws_last = i - 1;
  61. if (sc !== -1) tokens[sc].sc_last = i - 1;
  62. }
  63. /**
  64. * Pair brackets
  65. */
  66. function markBrackets(tokens) {
  67. var tokensLength = tokens.length;
  68. var ps = [], // parenthesis
  69. sbs = [], // square brackets
  70. cbs = [], // curly brackets
  71. t; // current token
  72. // For every token in the token list, if we meet an opening (left)
  73. // bracket, push its index number to a corresponding array.
  74. // If we then meet a closing (right) bracket, look at the corresponding
  75. // array. If there are any elements (records about previously met
  76. // left brackets), take a token of the last left bracket (take
  77. // the last index number from the array and find a token with
  78. // this index number) and save right bracket's index as a reference:
  79. for (var i = 0; i < tokens.length; i++) {
  80. t = tokens[i];
  81. switch(t.type) {
  82. case TokenType.LeftParenthesis:
  83. ps.push(i);
  84. break;
  85. case TokenType.RightParenthesis:
  86. if (ps.length) {
  87. t.left = ps.pop();
  88. tokens[t.left].right = i;
  89. }
  90. break;
  91. case TokenType.LeftSquareBracket:
  92. sbs.push(i);
  93. break;
  94. case TokenType.RightSquareBracket:
  95. if (sbs.length) {
  96. t.left = sbs.pop();
  97. tokens[t.left].right = i;
  98. }
  99. break;
  100. case TokenType.LeftCurlyBracket:
  101. cbs.push(i);
  102. break;
  103. case TokenType.RightCurlyBracket:
  104. if (cbs.length) {
  105. t.left = cbs.pop();
  106. tokens[t.left].right = i;
  107. }
  108. break;
  109. }
  110. }
  111. }
  112. function markBlocks(tokens) {
  113. var tokensLength = tokens.length;
  114. var blocks = [],
  115. currentLN = 1,
  116. currentIL = 0,
  117. prevIL = 0,
  118. i = 0,
  119. l = tokens.length,
  120. iw;
  121. for (; i != l; i++) {
  122. if (!tokens[i - 1]) continue;
  123. // Skip all tokens on current line:
  124. if (tokens[i].ln == currentLN) continue;
  125. else currentLN = tokens[i].ln;
  126. // Get indent level:
  127. prevIL = currentIL;
  128. if (tokens[i].type === TokenType.Newline) continue;
  129. else if (tokens[i].type === TokenType.Space &&
  130. tokens[i + 1] &&
  131. tokens[i + 1].type === TokenType.Newline) continue;
  132. else if (tokens[i].type !== TokenType.Space) currentIL = 0;
  133. else {
  134. // If we don't know ident width yet, count number of spaces:
  135. if (!iw) iw = tokens[i].value.length;
  136. prevIL = currentIL;
  137. currentIL = tokens[i].value.length / iw;
  138. }
  139. // Decide whether it's block's start or end:
  140. if (prevIL === currentIL) continue;
  141. else if (currentIL > prevIL) {
  142. blocks.push(i);
  143. continue;
  144. } else {
  145. var il = prevIL;
  146. while (blocks.length > 0 && il !== currentIL) {
  147. tokens[blocks.pop()].block_end = i - 1;
  148. il--;
  149. }
  150. }
  151. }
  152. while (blocks.length > 0) {
  153. tokens[blocks.pop()].block_end = i - 1;
  154. }
  155. }
  156. return function(tokens) {
  157. markBrackets(tokens);
  158. markSC(tokens);
  159. markBlocks(tokens);
  160. }
  161. })();