/js/lib/Socket.IO-node/support/expresso/deps/jscoverage/js/jskwgen.cpp
C++ | 460 lines | 358 code | 54 blank | 48 comment | 80 complexity | f31d63498a6e44d2033e0ac37f9bc276 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-3-Clause
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 41#include "jsstddef.h" 42#include <assert.h> 43#include <stdio.h> 44#include <stdlib.h> 45#include <string.h> 46#include <stdarg.h> 47#include <ctype.h> 48 49#include "jsversion.h" 50 51const char * const keyword_list[] = { 52#define JS_KEYWORD(keyword, type, op, version) #keyword, 53#include "jskeyword.tbl" 54#undef JS_KEYWORD 55}; 56 57struct gen_opt { 58 FILE *output; /* output file for generated source */ 59 unsigned use_if_threshold; /* max number of choices to generate 60 "if" selector instead of "switch" */ 61 unsigned char_tail_test_threshold; /* max number of unprocessed columns 62 to use inlined char compare 63 for remaining chars and not generic 64 string compare code */ 65 unsigned indent_level; /* current source identation level */ 66}; 67 68static unsigned column_to_compare; 69 70static int 71length_comparator(const void *a, const void *b) 72{ 73 const char *str1 = keyword_list[*(unsigned *)a]; 74 const char *str2 = keyword_list[*(unsigned *)b]; 75 return (int)strlen(str1) - (int)strlen(str2); 76} 77 78static int 79column_comparator(const void *a, const void *b) 80{ 81 const char *str1 = keyword_list[*(unsigned *)a]; 82 const char *str2 = keyword_list[*(unsigned *)b]; 83 return (int)str1[column_to_compare] - (int)str2[column_to_compare]; 84} 85 86static unsigned 87count_different_lengths(unsigned indexes[], unsigned nelem) 88{ 89 unsigned nlength, current_length, i, l; 90 91 current_length = 0; 92 nlength = 0; 93 for (i = 0; i != nelem; ++i) { 94 l = (unsigned)strlen(keyword_list[indexes[i]]); 95 assert(l != 0); 96 if (current_length != l) { 97 ++nlength; 98 current_length = l; 99 } 100 } 101 return nlength; 102} 103 104static void 105find_char_span_and_count(unsigned indexes[], unsigned nelem, unsigned column, 106 unsigned *span_result, unsigned *count_result) 107{ 108 unsigned i, count; 109 unsigned char c, prev, minc, maxc; 110 111 assert(nelem != 0); 112 minc = maxc = prev = (unsigned char)keyword_list[indexes[0]][column]; 113 count = 1; 114 for (i = 1; i != nelem; ++i) { 115 c = (unsigned char)keyword_list[indexes[i]][column]; 116 if (prev != c) { 117 prev = c; 118 ++count; 119 if (minc > c) { 120 minc = c; 121 } else if (maxc < c) { 122 maxc = c; 123 } 124 } 125 } 126 127 *span_result = maxc - minc + 1; 128 *count_result = count; 129} 130 131static unsigned 132find_optimal_switch_column(struct gen_opt *opt, 133 unsigned indexes[], unsigned nelem, 134 unsigned columns[], unsigned unprocessed_columns, 135 int *use_if_result) 136{ 137 unsigned i; 138 unsigned span, min_span, min_span_index; 139 unsigned nchar, min_nchar, min_nchar_index; 140 141 assert(unprocessed_columns != 0); 142 i = 0; 143 min_nchar = min_span = (unsigned)-1; 144 min_nchar_index = min_span_index = 0; 145 do { 146 column_to_compare = columns[i]; 147 qsort(indexes, nelem, sizeof(indexes[0]), column_comparator); 148 find_char_span_and_count(indexes, nelem, column_to_compare, 149 &span, &nchar); 150 assert(span != 0); 151 if (span == 1) { 152 assert(nchar == 1); 153 *use_if_result = 1; 154 return 1; 155 } 156 assert(nchar != 1); 157 if (min_span > span) { 158 min_span = span; 159 min_span_index = i; 160 } 161 if (min_nchar > nchar) { 162 min_nchar = nchar; 163 min_nchar_index = i; 164 } 165 } while (++i != unprocessed_columns); 166 167 if (min_nchar <= opt->use_if_threshold) { 168 *use_if_result = 1; 169 i = min_nchar_index; 170 } else { 171 *use_if_result = 0; 172 i = min_span_index; 173 } 174 175 /* 176 * Restore order corresponding to i if it was destroyed by 177 * subsequent sort. 178 */ 179 if (i != unprocessed_columns - 1) { 180 column_to_compare = columns[i]; 181 qsort(indexes, nelem, sizeof(indexes[0]), column_comparator); 182 } 183 184 return i; 185} 186 187 188static void 189p(struct gen_opt *opt, const char *format, ...) 190{ 191 va_list ap; 192 193 va_start(ap, format); 194 vfprintf(opt->output, format, ap); 195 va_end(ap); 196} 197 198/* Size for '\xxx' where xxx is octal escape */ 199#define MIN_QUOTED_CHAR_BUFFER 7 200 201static char * 202qchar(char c, char *quoted_buffer) 203{ 204 char *s; 205 206 s = quoted_buffer; 207 *s++ = '\''; 208 switch (c) { 209 case '\n': c = 'n'; goto one_char_escape; 210 case '\r': c = 'r'; goto one_char_escape; 211 case '\t': c = 't'; goto one_char_escape; 212 case '\f': c = 't'; goto one_char_escape; 213 case '\0': c = '0'; goto one_char_escape; 214 case '\'': goto one_char_escape; 215 one_char_escape: 216 *s++ = '\\'; 217 break; 218 default: 219 if (!isprint(c)) { 220 *s++ = '\\'; 221 *s++ = (char)('0' + (0x3 & (((unsigned char)c) >> 6))); 222 *s++ = (char)('0' + (0x7 & (((unsigned char)c) >> 3))); 223 c = (char)('0' + (0x7 & ((unsigned char)c))); 224 } 225 } 226 *s++ = c; 227 *s++ = '\''; 228 *s = '\0'; 229 assert(s + 1 <= quoted_buffer + MIN_QUOTED_CHAR_BUFFER); 230 return quoted_buffer; 231} 232 233static void 234nl(struct gen_opt *opt) 235{ 236 putc('\n', opt->output); 237} 238 239static void 240indent(struct gen_opt *opt) 241{ 242 unsigned n = opt->indent_level; 243 while (n != 0) { 244 --n; 245 fputs(" ", opt->output); 246 } 247} 248 249static void 250line(struct gen_opt *opt, const char *format, ...) 251{ 252 va_list ap; 253 254 indent(opt); 255 va_start(ap, format); 256 vfprintf(opt->output, format, ap); 257 va_end(ap); 258 nl(opt); 259} 260 261static void 262generate_letter_switch_r(struct gen_opt *opt, 263 unsigned indexes[], unsigned nelem, 264 unsigned columns[], unsigned unprocessed_columns) 265{ 266 char qbuf[MIN_QUOTED_CHAR_BUFFER]; 267 268 assert(nelem != 0); 269 if (nelem == 1) { 270 unsigned kw_index = indexes[0]; 271 const char *keyword = keyword_list[kw_index]; 272 273 if (unprocessed_columns == 0) { 274 line(opt, "JSKW_GOT_MATCH(%u) /* %s */", kw_index, keyword); 275 } else if (unprocessed_columns > opt->char_tail_test_threshold) { 276 line(opt, "JSKW_TEST_GUESS(%u) /* %s */", kw_index, keyword); 277 } else { 278 unsigned i, column; 279 280 indent(opt); p(opt, "if ("); 281 for (i = 0; i != unprocessed_columns; ++i) { 282 column = columns[i]; 283 qchar(keyword[column], qbuf); 284 p(opt, "%sJSKW_AT(%u)==%s", (i == 0) ? "" : " && ", 285 column, qbuf); 286 } 287 p(opt, ") {"); nl(opt); 288 ++opt->indent_level; 289 line(opt, "JSKW_GOT_MATCH(%u) /* %s */", kw_index, keyword); 290 --opt->indent_level; 291 line(opt, "}"); 292 line(opt, "JSKW_NO_MATCH()"); 293 } 294 } else { 295 unsigned optimal_column_index, optimal_column; 296 unsigned i; 297 int use_if; 298 char current; 299 300 assert(unprocessed_columns != 0); 301 optimal_column_index = find_optimal_switch_column(opt, indexes, nelem, 302 columns, 303 unprocessed_columns, 304 &use_if); 305 optimal_column = columns[optimal_column_index]; 306 columns[optimal_column_index] = columns[unprocessed_columns - 1]; 307 308 if (!use_if) 309 line(opt, "switch (JSKW_AT(%u)) {", optimal_column); 310 311 current = keyword_list[indexes[0]][optimal_column]; 312 for (i = 0; i != nelem;) { 313 unsigned same_char_begin = i; 314 char next = current; 315 316 for (++i; i != nelem; ++i) { 317 next = keyword_list[indexes[i]][optimal_column]; 318 if (next != current) 319 break; 320 } 321 qchar(current, qbuf); 322 if (use_if) { 323 line(opt, "if (JSKW_AT(%u) == %s) {", optimal_column, qbuf); 324 } else { 325 line(opt, " case %s:", qbuf); 326 } 327 ++opt->indent_level; 328 generate_letter_switch_r(opt, indexes + same_char_begin, 329 i - same_char_begin, 330 columns, unprocessed_columns - 1); 331 --opt->indent_level; 332 if (use_if) { 333 line(opt, "}"); 334 } 335 current = next; 336 } 337 338 if (!use_if) { 339 line(opt, "}"); 340 } 341 342 columns[optimal_column_index] = optimal_column; 343 344 line(opt, "JSKW_NO_MATCH()"); 345 } 346} 347 348static void 349generate_letter_switch(struct gen_opt *opt, 350 unsigned indexes[], unsigned nelem, 351 unsigned current_length) 352{ 353 unsigned *columns; 354 unsigned i; 355 356 columns = (unsigned *) malloc(sizeof(columns[0]) * current_length); 357 if (!columns) { 358 perror("malloc"); 359 exit(EXIT_FAILURE); 360 } 361 for (i = 0; i != current_length; ++i) { 362 columns[i] = i; 363 } 364 generate_letter_switch_r(opt, indexes, nelem, columns, current_length); 365 free(columns); 366} 367 368 369static void 370generate_switch(struct gen_opt *opt) 371{ 372 unsigned *indexes; 373 unsigned nlength; 374 unsigned i, current; 375 int use_if; 376 unsigned nelem; 377 378 nelem = sizeof(keyword_list)/sizeof(keyword_list[0]); 379 380 line(opt, "/*"); 381 line(opt, " * Generating switch for the list of %u entries:", nelem); 382 for (i = 0; i != nelem; ++i) { 383 line(opt, " * %s", keyword_list[i]); 384 } 385 line(opt, " */"); 386 387 indexes = (unsigned *) malloc(sizeof(indexes[0]) * nelem); 388 if (!indexes) { 389 perror("malloc"); 390 exit(EXIT_FAILURE); 391 } 392 for (i = 0; i != nelem; ++i) 393 indexes[i] = i; 394 qsort(indexes, nelem, sizeof(indexes[i]), length_comparator); 395 nlength = count_different_lengths(indexes, nelem); 396 397 use_if = (nlength <= opt->use_if_threshold); 398 399 if (!use_if) 400 line(opt, "switch (JSKW_LENGTH()) {"); 401 402 current = (unsigned)strlen(keyword_list[indexes[0]]); 403 for (i = 0; i != nelem;) { 404 unsigned same_length_begin = i; 405 unsigned next = current; 406 407 for (++i; i != nelem; ++i) { 408 next = (unsigned)strlen(keyword_list[indexes[i]]); 409 if (next != current) 410 break; 411 } 412 if (use_if) { 413 line(opt, "if (JSKW_LENGTH() == %u) {", current); 414 } else { 415 line(opt, " case %u:", current); 416 } 417 ++opt->indent_level; 418 generate_letter_switch(opt, indexes + same_length_begin, 419 i - same_length_begin, 420 current); 421 --opt->indent_level; 422 if (use_if) { 423 line(opt, "}"); 424 } 425 current = next; 426 } 427 if (!use_if) 428 line(opt, "}"); 429 line(opt, "JSKW_NO_MATCH()"); 430 free(indexes); 431} 432 433int main(int argc, char **argv) 434{ 435 struct gen_opt opt; 436 437 if (argc < 2) { 438 opt.output = stdout; 439 } else { 440 opt.output = fopen(argv[1], "w"); 441 if (!opt.output) { 442 perror("fopen"); 443 exit(EXIT_FAILURE); 444 } 445 } 446 opt.indent_level = 1; 447 opt.use_if_threshold = 3; 448 opt.char_tail_test_threshold = 4; 449 450 generate_switch(&opt); 451 452 if (opt.output != stdout) { 453 if (fclose(opt.output)) { 454 perror("fclose"); 455 exit(EXIT_FAILURE); 456 } 457 } 458 459 return EXIT_SUCCESS; 460}