/js/lib/Socket.IO-node/support/expresso/deps/jscoverage/js/jskwgen.cpp

http://github.com/onedayitwillmake/RealtimeMultiplayerNodeJs · C++ · 460 lines · 358 code · 54 blank · 48 comment · 80 complexity · f31d63498a6e44d2033e0ac37f9bc276 MD5 · raw file

  1. /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2. * vim: set sw=4 ts=8 et tw=80:
  3. *
  4. * ***** BEGIN LICENSE BLOCK *****
  5. * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  6. *
  7. * The contents of this file are subject to the Mozilla Public License Version
  8. * 1.1 (the "License"); you may not use this file except in compliance with
  9. * the License. You may obtain a copy of the License at
  10. * http://www.mozilla.org/MPL/
  11. *
  12. * Software distributed under the License is distributed on an "AS IS" basis,
  13. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  14. * for the specific language governing rights and limitations under the
  15. * License.
  16. *
  17. * The Original Code is String Switch Generator for JavaScript Keywords,
  18. * released 2005-12-09.
  19. *
  20. * The Initial Developer of the Original Code is
  21. * Igor Bukanov.
  22. * Portions created by the Initial Developer are Copyright (C) 2005-2006
  23. * the Initial Developer. All Rights Reserved.
  24. *
  25. * Contributor(s):
  26. *
  27. * Alternatively, the contents of this file may be used under the terms of
  28. * either of the GNU General Public License Version 2 or later (the "GPL"),
  29. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  30. * in which case the provisions of the GPL or the LGPL are applicable instead
  31. * of those above. If you wish to allow use of your version of this file only
  32. * under the terms of either the GPL or the LGPL, and not to allow others to
  33. * use your version of this file under the terms of the MPL, indicate your
  34. * decision by deleting the provisions above and replace them with the notice
  35. * and other provisions required by the GPL or the LGPL. If you do not delete
  36. * the provisions above, a recipient may use your version of this file under
  37. * the terms of any one of the MPL, the GPL or the LGPL.
  38. *
  39. * ***** END LICENSE BLOCK ***** */
  40. #include "jsstddef.h"
  41. #include <assert.h>
  42. #include <stdio.h>
  43. #include <stdlib.h>
  44. #include <string.h>
  45. #include <stdarg.h>
  46. #include <ctype.h>
  47. #include "jsversion.h"
  48. const char * const keyword_list[] = {
  49. #define JS_KEYWORD(keyword, type, op, version) #keyword,
  50. #include "jskeyword.tbl"
  51. #undef JS_KEYWORD
  52. };
  53. struct gen_opt {
  54. FILE *output; /* output file for generated source */
  55. unsigned use_if_threshold; /* max number of choices to generate
  56. "if" selector instead of "switch" */
  57. unsigned char_tail_test_threshold; /* max number of unprocessed columns
  58. to use inlined char compare
  59. for remaining chars and not generic
  60. string compare code */
  61. unsigned indent_level; /* current source identation level */
  62. };
  63. static unsigned column_to_compare;
  64. static int
  65. length_comparator(const void *a, const void *b)
  66. {
  67. const char *str1 = keyword_list[*(unsigned *)a];
  68. const char *str2 = keyword_list[*(unsigned *)b];
  69. return (int)strlen(str1) - (int)strlen(str2);
  70. }
  71. static int
  72. column_comparator(const void *a, const void *b)
  73. {
  74. const char *str1 = keyword_list[*(unsigned *)a];
  75. const char *str2 = keyword_list[*(unsigned *)b];
  76. return (int)str1[column_to_compare] - (int)str2[column_to_compare];
  77. }
  78. static unsigned
  79. count_different_lengths(unsigned indexes[], unsigned nelem)
  80. {
  81. unsigned nlength, current_length, i, l;
  82. current_length = 0;
  83. nlength = 0;
  84. for (i = 0; i != nelem; ++i) {
  85. l = (unsigned)strlen(keyword_list[indexes[i]]);
  86. assert(l != 0);
  87. if (current_length != l) {
  88. ++nlength;
  89. current_length = l;
  90. }
  91. }
  92. return nlength;
  93. }
  94. static void
  95. find_char_span_and_count(unsigned indexes[], unsigned nelem, unsigned column,
  96. unsigned *span_result, unsigned *count_result)
  97. {
  98. unsigned i, count;
  99. unsigned char c, prev, minc, maxc;
  100. assert(nelem != 0);
  101. minc = maxc = prev = (unsigned char)keyword_list[indexes[0]][column];
  102. count = 1;
  103. for (i = 1; i != nelem; ++i) {
  104. c = (unsigned char)keyword_list[indexes[i]][column];
  105. if (prev != c) {
  106. prev = c;
  107. ++count;
  108. if (minc > c) {
  109. minc = c;
  110. } else if (maxc < c) {
  111. maxc = c;
  112. }
  113. }
  114. }
  115. *span_result = maxc - minc + 1;
  116. *count_result = count;
  117. }
  118. static unsigned
  119. find_optimal_switch_column(struct gen_opt *opt,
  120. unsigned indexes[], unsigned nelem,
  121. unsigned columns[], unsigned unprocessed_columns,
  122. int *use_if_result)
  123. {
  124. unsigned i;
  125. unsigned span, min_span, min_span_index;
  126. unsigned nchar, min_nchar, min_nchar_index;
  127. assert(unprocessed_columns != 0);
  128. i = 0;
  129. min_nchar = min_span = (unsigned)-1;
  130. min_nchar_index = min_span_index = 0;
  131. do {
  132. column_to_compare = columns[i];
  133. qsort(indexes, nelem, sizeof(indexes[0]), column_comparator);
  134. find_char_span_and_count(indexes, nelem, column_to_compare,
  135. &span, &nchar);
  136. assert(span != 0);
  137. if (span == 1) {
  138. assert(nchar == 1);
  139. *use_if_result = 1;
  140. return 1;
  141. }
  142. assert(nchar != 1);
  143. if (min_span > span) {
  144. min_span = span;
  145. min_span_index = i;
  146. }
  147. if (min_nchar > nchar) {
  148. min_nchar = nchar;
  149. min_nchar_index = i;
  150. }
  151. } while (++i != unprocessed_columns);
  152. if (min_nchar <= opt->use_if_threshold) {
  153. *use_if_result = 1;
  154. i = min_nchar_index;
  155. } else {
  156. *use_if_result = 0;
  157. i = min_span_index;
  158. }
  159. /*
  160. * Restore order corresponding to i if it was destroyed by
  161. * subsequent sort.
  162. */
  163. if (i != unprocessed_columns - 1) {
  164. column_to_compare = columns[i];
  165. qsort(indexes, nelem, sizeof(indexes[0]), column_comparator);
  166. }
  167. return i;
  168. }
  169. static void
  170. p(struct gen_opt *opt, const char *format, ...)
  171. {
  172. va_list ap;
  173. va_start(ap, format);
  174. vfprintf(opt->output, format, ap);
  175. va_end(ap);
  176. }
  177. /* Size for '\xxx' where xxx is octal escape */
  178. #define MIN_QUOTED_CHAR_BUFFER 7
  179. static char *
  180. qchar(char c, char *quoted_buffer)
  181. {
  182. char *s;
  183. s = quoted_buffer;
  184. *s++ = '\'';
  185. switch (c) {
  186. case '\n': c = 'n'; goto one_char_escape;
  187. case '\r': c = 'r'; goto one_char_escape;
  188. case '\t': c = 't'; goto one_char_escape;
  189. case '\f': c = 't'; goto one_char_escape;
  190. case '\0': c = '0'; goto one_char_escape;
  191. case '\'': goto one_char_escape;
  192. one_char_escape:
  193. *s++ = '\\';
  194. break;
  195. default:
  196. if (!isprint(c)) {
  197. *s++ = '\\';
  198. *s++ = (char)('0' + (0x3 & (((unsigned char)c) >> 6)));
  199. *s++ = (char)('0' + (0x7 & (((unsigned char)c) >> 3)));
  200. c = (char)('0' + (0x7 & ((unsigned char)c)));
  201. }
  202. }
  203. *s++ = c;
  204. *s++ = '\'';
  205. *s = '\0';
  206. assert(s + 1 <= quoted_buffer + MIN_QUOTED_CHAR_BUFFER);
  207. return quoted_buffer;
  208. }
  209. static void
  210. nl(struct gen_opt *opt)
  211. {
  212. putc('\n', opt->output);
  213. }
  214. static void
  215. indent(struct gen_opt *opt)
  216. {
  217. unsigned n = opt->indent_level;
  218. while (n != 0) {
  219. --n;
  220. fputs(" ", opt->output);
  221. }
  222. }
  223. static void
  224. line(struct gen_opt *opt, const char *format, ...)
  225. {
  226. va_list ap;
  227. indent(opt);
  228. va_start(ap, format);
  229. vfprintf(opt->output, format, ap);
  230. va_end(ap);
  231. nl(opt);
  232. }
  233. static void
  234. generate_letter_switch_r(struct gen_opt *opt,
  235. unsigned indexes[], unsigned nelem,
  236. unsigned columns[], unsigned unprocessed_columns)
  237. {
  238. char qbuf[MIN_QUOTED_CHAR_BUFFER];
  239. assert(nelem != 0);
  240. if (nelem == 1) {
  241. unsigned kw_index = indexes[0];
  242. const char *keyword = keyword_list[kw_index];
  243. if (unprocessed_columns == 0) {
  244. line(opt, "JSKW_GOT_MATCH(%u) /* %s */", kw_index, keyword);
  245. } else if (unprocessed_columns > opt->char_tail_test_threshold) {
  246. line(opt, "JSKW_TEST_GUESS(%u) /* %s */", kw_index, keyword);
  247. } else {
  248. unsigned i, column;
  249. indent(opt); p(opt, "if (");
  250. for (i = 0; i != unprocessed_columns; ++i) {
  251. column = columns[i];
  252. qchar(keyword[column], qbuf);
  253. p(opt, "%sJSKW_AT(%u)==%s", (i == 0) ? "" : " && ",
  254. column, qbuf);
  255. }
  256. p(opt, ") {"); nl(opt);
  257. ++opt->indent_level;
  258. line(opt, "JSKW_GOT_MATCH(%u) /* %s */", kw_index, keyword);
  259. --opt->indent_level;
  260. line(opt, "}");
  261. line(opt, "JSKW_NO_MATCH()");
  262. }
  263. } else {
  264. unsigned optimal_column_index, optimal_column;
  265. unsigned i;
  266. int use_if;
  267. char current;
  268. assert(unprocessed_columns != 0);
  269. optimal_column_index = find_optimal_switch_column(opt, indexes, nelem,
  270. columns,
  271. unprocessed_columns,
  272. &use_if);
  273. optimal_column = columns[optimal_column_index];
  274. columns[optimal_column_index] = columns[unprocessed_columns - 1];
  275. if (!use_if)
  276. line(opt, "switch (JSKW_AT(%u)) {", optimal_column);
  277. current = keyword_list[indexes[0]][optimal_column];
  278. for (i = 0; i != nelem;) {
  279. unsigned same_char_begin = i;
  280. char next = current;
  281. for (++i; i != nelem; ++i) {
  282. next = keyword_list[indexes[i]][optimal_column];
  283. if (next != current)
  284. break;
  285. }
  286. qchar(current, qbuf);
  287. if (use_if) {
  288. line(opt, "if (JSKW_AT(%u) == %s) {", optimal_column, qbuf);
  289. } else {
  290. line(opt, " case %s:", qbuf);
  291. }
  292. ++opt->indent_level;
  293. generate_letter_switch_r(opt, indexes + same_char_begin,
  294. i - same_char_begin,
  295. columns, unprocessed_columns - 1);
  296. --opt->indent_level;
  297. if (use_if) {
  298. line(opt, "}");
  299. }
  300. current = next;
  301. }
  302. if (!use_if) {
  303. line(opt, "}");
  304. }
  305. columns[optimal_column_index] = optimal_column;
  306. line(opt, "JSKW_NO_MATCH()");
  307. }
  308. }
  309. static void
  310. generate_letter_switch(struct gen_opt *opt,
  311. unsigned indexes[], unsigned nelem,
  312. unsigned current_length)
  313. {
  314. unsigned *columns;
  315. unsigned i;
  316. columns = (unsigned *) malloc(sizeof(columns[0]) * current_length);
  317. if (!columns) {
  318. perror("malloc");
  319. exit(EXIT_FAILURE);
  320. }
  321. for (i = 0; i != current_length; ++i) {
  322. columns[i] = i;
  323. }
  324. generate_letter_switch_r(opt, indexes, nelem, columns, current_length);
  325. free(columns);
  326. }
  327. static void
  328. generate_switch(struct gen_opt *opt)
  329. {
  330. unsigned *indexes;
  331. unsigned nlength;
  332. unsigned i, current;
  333. int use_if;
  334. unsigned nelem;
  335. nelem = sizeof(keyword_list)/sizeof(keyword_list[0]);
  336. line(opt, "/*");
  337. line(opt, " * Generating switch for the list of %u entries:", nelem);
  338. for (i = 0; i != nelem; ++i) {
  339. line(opt, " * %s", keyword_list[i]);
  340. }
  341. line(opt, " */");
  342. indexes = (unsigned *) malloc(sizeof(indexes[0]) * nelem);
  343. if (!indexes) {
  344. perror("malloc");
  345. exit(EXIT_FAILURE);
  346. }
  347. for (i = 0; i != nelem; ++i)
  348. indexes[i] = i;
  349. qsort(indexes, nelem, sizeof(indexes[i]), length_comparator);
  350. nlength = count_different_lengths(indexes, nelem);
  351. use_if = (nlength <= opt->use_if_threshold);
  352. if (!use_if)
  353. line(opt, "switch (JSKW_LENGTH()) {");
  354. current = (unsigned)strlen(keyword_list[indexes[0]]);
  355. for (i = 0; i != nelem;) {
  356. unsigned same_length_begin = i;
  357. unsigned next = current;
  358. for (++i; i != nelem; ++i) {
  359. next = (unsigned)strlen(keyword_list[indexes[i]]);
  360. if (next != current)
  361. break;
  362. }
  363. if (use_if) {
  364. line(opt, "if (JSKW_LENGTH() == %u) {", current);
  365. } else {
  366. line(opt, " case %u:", current);
  367. }
  368. ++opt->indent_level;
  369. generate_letter_switch(opt, indexes + same_length_begin,
  370. i - same_length_begin,
  371. current);
  372. --opt->indent_level;
  373. if (use_if) {
  374. line(opt, "}");
  375. }
  376. current = next;
  377. }
  378. if (!use_if)
  379. line(opt, "}");
  380. line(opt, "JSKW_NO_MATCH()");
  381. free(indexes);
  382. }
  383. int main(int argc, char **argv)
  384. {
  385. struct gen_opt opt;
  386. if (argc < 2) {
  387. opt.output = stdout;
  388. } else {
  389. opt.output = fopen(argv[1], "w");
  390. if (!opt.output) {
  391. perror("fopen");
  392. exit(EXIT_FAILURE);
  393. }
  394. }
  395. opt.indent_level = 1;
  396. opt.use_if_threshold = 3;
  397. opt.char_tail_test_threshold = 4;
  398. generate_switch(&opt);
  399. if (opt.output != stdout) {
  400. if (fclose(opt.output)) {
  401. perror("fclose");
  402. exit(EXIT_FAILURE);
  403. }
  404. }
  405. return EXIT_SUCCESS;
  406. }