PageRenderTime 42ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/js/lib/Socket.IO-node/support/expresso/deps/jscoverage/highlight.c

http://github.com/onedayitwillmake/RealtimeMultiplayerNodeJs
C | 478 lines | 422 code | 31 blank | 25 comment | 96 complexity | 1239bf7c575a6c4ccc803b04032b21f6 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-3-Clause
  1. /*
  2. highlight.c - JavaScript syntax highlighting
  3. Copyright (C) 2008 siliconforks.com
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License along
  13. with this program; if not, write to the Free Software Foundation, Inc.,
  14. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  15. */
  16. #include <config.h>
  17. #include "highlight.h"
  18. #include <assert.h>
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #include <jslock.h>
  22. #include <jsscan.h>
  23. #include "util.h"
  24. enum Class {
  25. CLASS_NONE,
  26. CLASS_COMMENT,
  27. CLASS_REGEXP,
  28. CLASS_NUMBER,
  29. CLASS_STRING,
  30. CLASS_SPECIALCHAR,
  31. CLASS_KEYWORD,
  32. CLASS_TYPE,
  33. CLASS_SYMBOL,
  34. CLASS_CBRACKET
  35. };
  36. static const char * get_class_name(enum Class class) {
  37. switch (class) {
  38. case CLASS_NONE:
  39. abort();
  40. break;
  41. case CLASS_COMMENT:
  42. return "c";
  43. break;
  44. case CLASS_REGEXP:
  45. return "s";
  46. break;
  47. case CLASS_NUMBER:
  48. return "s";
  49. break;
  50. case CLASS_STRING:
  51. return "s";
  52. break;
  53. case CLASS_SPECIALCHAR:
  54. return "t";
  55. break;
  56. case CLASS_KEYWORD:
  57. return "k";
  58. break;
  59. case CLASS_TYPE:
  60. return "k";
  61. break;
  62. case CLASS_SYMBOL:
  63. return "k";
  64. break;
  65. case CLASS_CBRACKET:
  66. return "k";
  67. break;
  68. default:
  69. abort();
  70. break;
  71. }
  72. }
  73. static const char * g_id;
  74. static const jschar * g_characters;
  75. static size_t g_num_characters;
  76. static Stream * g_output;
  77. static size_t character_offset;
  78. static uint16_t line_num;
  79. static uint16_t column_num;
  80. static enum Class current_class;
  81. static void output_character(jschar c, enum Class class) {
  82. if (c == '\r' || c == '\n' || c == 0x2028 || c == 0x2029) {
  83. class = CLASS_NONE;
  84. }
  85. if (class != current_class) {
  86. /* output the end tag */
  87. if (current_class != CLASS_NONE) {
  88. Stream_write_string(g_output, "</span>");
  89. }
  90. current_class = class;
  91. /* output the start tag */
  92. if (current_class != CLASS_NONE) {
  93. Stream_printf(g_output, "<span class=\"%s\">", get_class_name(class));
  94. }
  95. }
  96. if (column_num == UINT16_MAX) {
  97. fatal("%s: script contains a line with more than 65,535 columns", g_id);
  98. }
  99. column_num++;
  100. switch (c) {
  101. case '&':
  102. Stream_write_string(g_output, "&amp;");
  103. break;
  104. case '<':
  105. Stream_write_string(g_output, "&lt;");
  106. break;
  107. case '>':
  108. Stream_write_string(g_output, "&gt;");
  109. break;
  110. case '\t':
  111. Stream_write_char(g_output, c);
  112. break;
  113. case '\r':
  114. case '\n':
  115. case 0x2028:
  116. case 0x2029:
  117. if (c == '\r' && character_offset + 1 < g_num_characters && g_characters[character_offset + 1] == '\n') {
  118. break;
  119. }
  120. Stream_write_char(g_output, '\n');
  121. column_num = 0;
  122. if (line_num == UINT16_MAX) {
  123. fatal("%s: script contains more than 65,535 lines", g_id);
  124. }
  125. line_num++;
  126. break;
  127. default:
  128. if (32 <= c && c <= 126) {
  129. Stream_write_char(g_output, c);
  130. }
  131. else {
  132. Stream_printf(g_output, "&#%d;", c);
  133. }
  134. break;
  135. }
  136. character_offset++;
  137. }
  138. static void mark_nontoken_chars(uint16_t end_line, uint16_t end_column) {
  139. enum State {
  140. STATE_NORMAL,
  141. STATE_LINE_COMMENT,
  142. STATE_MULTILINE_COMMENT
  143. };
  144. enum State state = STATE_NORMAL;
  145. while (character_offset < g_num_characters) {
  146. if (end_line != 0 && line_num > end_line) {
  147. break;
  148. }
  149. else if (line_num == end_line && column_num >= end_column) {
  150. break;
  151. }
  152. jschar c = g_characters[character_offset];
  153. if (c == '\0') {
  154. fatal("%s: script contains NULL character", g_id);
  155. }
  156. switch (state) {
  157. case STATE_NORMAL:
  158. if (c == '/' && character_offset + 1 < g_num_characters && g_characters[character_offset + 1] == '/') {
  159. state = STATE_LINE_COMMENT;
  160. }
  161. else if (c == '/' && character_offset + 1 < g_num_characters && g_characters[character_offset + 1] == '*') {
  162. state = STATE_MULTILINE_COMMENT;
  163. output_character('/', CLASS_COMMENT);
  164. output_character('*', CLASS_COMMENT);
  165. continue;
  166. }
  167. break;
  168. case STATE_LINE_COMMENT:
  169. if (c == '\r' || c == '\n' || c == 0x2028 || c == 0x2029) {
  170. state = STATE_NORMAL;
  171. }
  172. break;
  173. case STATE_MULTILINE_COMMENT:
  174. if (c == '*' && character_offset + 1 < g_num_characters && g_characters[character_offset + 1] == '/') {
  175. output_character('*', CLASS_COMMENT);
  176. output_character('/', CLASS_COMMENT);
  177. state = STATE_NORMAL;
  178. continue;
  179. }
  180. break;
  181. }
  182. if (state == STATE_NORMAL) {
  183. output_character(c, CLASS_NONE);
  184. }
  185. else {
  186. output_character(c, CLASS_COMMENT);
  187. }
  188. }
  189. }
  190. void jscoverage_highlight_js(JSContext * context, const char * id, const jschar * characters, size_t num_characters, Stream * output) {
  191. g_id = id;
  192. g_characters = characters;
  193. g_num_characters = num_characters;
  194. g_output = output;
  195. character_offset = 0;
  196. line_num = 1;
  197. column_num = 0;
  198. current_class = CLASS_NONE;
  199. /* tokenize the JavaScript */
  200. JSTokenStream token_stream;
  201. if (! js_InitTokenStream(context, &token_stream, characters, num_characters, NULL, NULL, 1)) {
  202. fatal("cannot create token stream from JavaScript file %s", id);
  203. }
  204. for (;;) {
  205. JSTokenType tt = js_GetToken(context, &token_stream);
  206. if (tt == TOK_ERROR) {
  207. fatal("JavaScript parse error: %s: line = %d, col = %d\n", id, line_num, column_num);
  208. }
  209. if (tt == TOK_EOF) {
  210. mark_nontoken_chars(0, 0);
  211. break;
  212. }
  213. /* mark the chars before the token */
  214. JSToken t = CURRENT_TOKEN(&token_stream);
  215. mark_nontoken_chars(t.pos.begin.lineno, t.pos.begin.index);
  216. /* mark the token */
  217. enum Class class;
  218. switch (tt) {
  219. case TOK_ERROR:
  220. case TOK_EOF:
  221. abort();
  222. case TOK_EOL:
  223. class = CLASS_NONE;
  224. token_stream.flags |= TSF_OPERAND;
  225. break;
  226. case TOK_SEMI:
  227. case TOK_COMMA:
  228. case TOK_ASSIGN:
  229. case TOK_HOOK:
  230. case TOK_COLON:
  231. case TOK_OR:
  232. case TOK_AND:
  233. case TOK_BITOR:
  234. case TOK_BITXOR:
  235. case TOK_BITAND:
  236. case TOK_EQOP:
  237. case TOK_RELOP:
  238. case TOK_SHOP:
  239. case TOK_PLUS:
  240. case TOK_MINUS:
  241. case TOK_STAR:
  242. case TOK_DIVOP:
  243. class = CLASS_SYMBOL;
  244. token_stream.flags |= TSF_OPERAND;
  245. break;
  246. case TOK_UNARYOP:
  247. switch (t.t_op) {
  248. case JSOP_NEG:
  249. case JSOP_POS:
  250. case JSOP_NOT:
  251. case JSOP_BITNOT:
  252. class = CLASS_SYMBOL;
  253. token_stream.flags |= TSF_OPERAND;
  254. break;
  255. case JSOP_TYPEOF:
  256. class = CLASS_KEYWORD;
  257. token_stream.flags |= TSF_OPERAND;
  258. break;
  259. case JSOP_VOID:
  260. class = CLASS_TYPE;
  261. token_stream.flags |= TSF_OPERAND;
  262. break;
  263. default:
  264. abort();
  265. }
  266. break;
  267. case TOK_INC:
  268. case TOK_DEC:
  269. class = CLASS_SYMBOL;
  270. /* token_stream.flags does not change w.r.t. TSF_OPERAND */
  271. break;
  272. case TOK_DOT:
  273. case TOK_LB:
  274. class = CLASS_SYMBOL;
  275. token_stream.flags |= TSF_OPERAND;
  276. break;
  277. case TOK_RB:
  278. class = CLASS_SYMBOL;
  279. token_stream.flags &= ~TSF_OPERAND;
  280. break;
  281. case TOK_LC:
  282. class = CLASS_CBRACKET;
  283. token_stream.flags |= TSF_OPERAND;
  284. break;
  285. case TOK_RC:
  286. class = CLASS_CBRACKET;
  287. token_stream.flags &= ~TSF_OPERAND;
  288. break;
  289. case TOK_LP:
  290. class = CLASS_SYMBOL;
  291. token_stream.flags |= TSF_OPERAND;
  292. break;
  293. case TOK_RP:
  294. class = CLASS_SYMBOL;
  295. token_stream.flags &= ~TSF_OPERAND;
  296. break;
  297. case TOK_NAME:
  298. class = CLASS_NONE;
  299. token_stream.flags &= ~TSF_OPERAND;
  300. if (js_PeekToken(context, &token_stream) == TOK_LP) {
  301. /* function */
  302. class = CLASS_NONE;
  303. }
  304. break;
  305. case TOK_NUMBER:
  306. class = CLASS_NUMBER;
  307. token_stream.flags &= ~TSF_OPERAND;
  308. break;
  309. case TOK_STRING:
  310. class = CLASS_STRING;
  311. token_stream.flags &= ~TSF_OPERAND;
  312. break;
  313. case TOK_REGEXP:
  314. class = CLASS_REGEXP;
  315. token_stream.flags &= ~TSF_OPERAND;
  316. break;
  317. case TOK_PRIMARY:
  318. switch (t.t_op) {
  319. case JSOP_TRUE:
  320. case JSOP_FALSE:
  321. case JSOP_NULL:
  322. case JSOP_THIS:
  323. class = CLASS_KEYWORD;
  324. token_stream.flags &= ~TSF_OPERAND;
  325. break;
  326. default:
  327. abort();
  328. }
  329. break;
  330. case TOK_FUNCTION:
  331. class = CLASS_KEYWORD;
  332. token_stream.flags |= TSF_OPERAND;
  333. break;
  334. case TOK_IF:
  335. case TOK_ELSE:
  336. case TOK_SWITCH:
  337. case TOK_CASE:
  338. case TOK_DEFAULT:
  339. case TOK_WHILE:
  340. case TOK_DO:
  341. case TOK_FOR:
  342. case TOK_BREAK:
  343. case TOK_CONTINUE:
  344. case TOK_IN:
  345. case TOK_VAR:
  346. case TOK_WITH:
  347. case TOK_RETURN:
  348. case TOK_NEW:
  349. case TOK_DELETE:
  350. token_stream.flags |= TSF_OPERAND;
  351. class = CLASS_KEYWORD;
  352. break;
  353. case TOK_DEFSHARP:
  354. case TOK_USESHARP:
  355. abort();
  356. break;
  357. case TOK_TRY:
  358. case TOK_CATCH:
  359. case TOK_FINALLY:
  360. case TOK_THROW:
  361. case TOK_INSTANCEOF:
  362. case TOK_DEBUGGER:
  363. token_stream.flags |= TSF_OPERAND;
  364. class = CLASS_KEYWORD;
  365. break;
  366. case TOK_XMLSTAGO:
  367. case TOK_XMLETAGO:
  368. case TOK_XMLPTAGC:
  369. case TOK_XMLTAGC:
  370. case TOK_XMLNAME:
  371. case TOK_XMLATTR:
  372. case TOK_XMLSPACE:
  373. case TOK_XMLTEXT:
  374. case TOK_XMLCOMMENT:
  375. case TOK_XMLCDATA:
  376. case TOK_XMLPI:
  377. case TOK_AT:
  378. case TOK_DBLCOLON:
  379. case TOK_ANYNAME:
  380. case TOK_DBLDOT:
  381. case TOK_FILTER:
  382. case TOK_XMLELEM:
  383. case TOK_XMLLIST:
  384. abort();
  385. break;
  386. case TOK_YIELD:
  387. token_stream.flags |= TSF_OPERAND;
  388. class = CLASS_KEYWORD;
  389. break;
  390. case TOK_ARRAYCOMP:
  391. case TOK_ARRAYPUSH:
  392. case TOK_LEXICALSCOPE:
  393. abort();
  394. break;
  395. case TOK_LET:
  396. token_stream.flags |= TSF_OPERAND;
  397. class = CLASS_KEYWORD;
  398. break;
  399. case TOK_SEQ:
  400. case TOK_FORHEAD:
  401. case TOK_RESERVED:
  402. case TOK_LIMIT:
  403. abort();
  404. break;
  405. default:
  406. abort();
  407. break;
  408. }
  409. uint16_t start_line = t.pos.begin.lineno;
  410. uint16_t end_line = t.pos.end.lineno;
  411. uint16_t start_column = t.pos.begin.index;
  412. uint16_t end_column = t.pos.end.index;
  413. assert(line_num == start_line);
  414. assert(column_num == start_column);
  415. if (start_line == end_line && start_column >= end_column) {
  416. fatal("%s: script contains line with more than 65,535 characters", id);
  417. }
  418. for (;;) {
  419. assert(character_offset < num_characters);
  420. jschar c = characters[character_offset];
  421. if (tt == TOK_STRING && c == '\\') {
  422. output_character(c, CLASS_SPECIALCHAR);
  423. assert(character_offset < num_characters);
  424. c = characters[character_offset];
  425. output_character(c, CLASS_SPECIALCHAR);
  426. }
  427. else {
  428. output_character(c, class);
  429. }
  430. if (line_num > end_line) {
  431. break;
  432. }
  433. else if (line_num == end_line && column_num >= end_column) {
  434. break;
  435. }
  436. }
  437. assert(line_num == end_line);
  438. assert(column_num = end_column);
  439. }
  440. if (current_class != CLASS_NONE) {
  441. output_character('\n', CLASS_NONE);
  442. }
  443. js_CloseTokenStream(context, &token_stream);
  444. }