Bug 618017: Encountering XML should not override the version. (r=lw)
[mozilla-central.git] / js / src / jskwgen.cpp
blobf808b92a708bb0e35249c799b5a2a93803bfbf66
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set sw=4 ts=8 et tw=80:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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.
17 * The Original Code is String Switch Generator for JavaScript Keywords,
18 * released 2005-12-09.
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.
25 * Contributor(s):
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.
39 * ***** END LICENSE BLOCK ***** */
41 #include <stddef.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>
49 #include "jsversion.h"
51 const char * const keyword_list[] = {
52 #define JS_KEYWORD(keyword, type, op, version) #keyword,
53 #include "jskeyword.tbl"
54 #undef JS_KEYWORD
57 struct 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 */
68 static unsigned column_to_compare;
70 static int
71 length_comparator(const void *a, const void *b)
73 const char *str1 = keyword_list[*(unsigned *)a];
74 const char *str2 = keyword_list[*(unsigned *)b];
75 return (int)strlen(str1) - (int)strlen(str2);
78 static int
79 column_comparator(const void *a, const void *b)
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];
86 static unsigned
87 count_different_lengths(unsigned indexes[], unsigned nelem)
89 unsigned nlength, current_length, i, l;
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;
101 return nlength;
104 static void
105 find_char_span_and_count(unsigned indexes[], unsigned nelem, unsigned column,
106 unsigned *span_result, unsigned *count_result)
108 unsigned i, count;
109 unsigned char c, prev, minc, maxc;
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;
127 *span_result = maxc - minc + 1;
128 *count_result = count;
131 static unsigned
132 find_optimal_switch_column(struct gen_opt *opt,
133 unsigned indexes[], unsigned nelem,
134 unsigned columns[], unsigned unprocessed_columns,
135 int *use_if_result)
137 unsigned i;
138 unsigned span, min_span, min_span_index;
139 unsigned nchar, min_nchar, min_nchar_index;
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;
156 assert(nchar != 1);
157 if (min_span > span) {
158 min_span = span;
159 min_span_index = i;
161 if (min_nchar > nchar) {
162 min_nchar = nchar;
163 min_nchar_index = i;
165 } while (++i != unprocessed_columns);
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;
176 * Restore order corresponding to i if it was destroyed by
177 * subsequent sort.
179 if (i != unprocessed_columns - 1) {
180 column_to_compare = columns[i];
181 qsort(indexes, nelem, sizeof(indexes[0]), column_comparator);
184 return i;
188 static void
189 p(struct gen_opt *opt, const char *format, ...)
191 va_list ap;
193 va_start(ap, format);
194 vfprintf(opt->output, format, ap);
195 va_end(ap);
198 /* Size for '\xxx' where xxx is octal escape */
199 #define MIN_QUOTED_CHAR_BUFFER 7
201 static char *
202 qchar(char c, char *quoted_buffer)
204 char *s;
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)));
226 *s++ = c;
227 *s++ = '\'';
228 *s = '\0';
229 assert(s + 1 <= quoted_buffer + MIN_QUOTED_CHAR_BUFFER);
230 return quoted_buffer;
233 static void
234 nl(struct gen_opt *opt)
236 putc('\n', opt->output);
239 static void
240 indent(struct gen_opt *opt)
242 unsigned n = opt->indent_level;
243 while (n != 0) {
244 --n;
245 fputs(" ", opt->output);
249 static void
250 line(struct gen_opt *opt, const char *format, ...)
252 va_list ap;
254 indent(opt);
255 va_start(ap, format);
256 vfprintf(opt->output, format, ap);
257 va_end(ap);
258 nl(opt);
261 static void
262 generate_letter_switch_r(struct gen_opt *opt,
263 unsigned indexes[], unsigned nelem,
264 unsigned columns[], unsigned unprocessed_columns)
266 char qbuf[MIN_QUOTED_CHAR_BUFFER];
268 assert(nelem != 0);
269 if (nelem == 1) {
270 unsigned kw_index = indexes[0];
271 const char *keyword = keyword_list[kw_index];
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;
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);
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()");
294 } else {
295 unsigned optimal_column_index, optimal_column;
296 unsigned i;
297 int use_if;
298 char current;
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];
308 if (!use_if)
309 line(opt, "switch (JSKW_AT(%u)) {", optimal_column);
311 current = keyword_list[indexes[0]][optimal_column];
312 for (i = 0; i != nelem;) {
313 unsigned same_char_begin = i;
314 char next = current;
316 for (++i; i != nelem; ++i) {
317 next = keyword_list[indexes[i]][optimal_column];
318 if (next != current)
319 break;
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);
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, "}");
335 current = next;
338 if (!use_if) {
339 line(opt, "}");
342 columns[optimal_column_index] = optimal_column;
344 line(opt, "JSKW_NO_MATCH()");
348 static void
349 generate_letter_switch(struct gen_opt *opt,
350 unsigned indexes[], unsigned nelem,
351 unsigned current_length)
353 unsigned *columns;
354 unsigned i;
356 columns = (unsigned *) malloc(sizeof(columns[0]) * current_length);
357 if (!columns) {
358 perror("malloc");
359 exit(EXIT_FAILURE);
361 for (i = 0; i != current_length; ++i) {
362 columns[i] = i;
364 generate_letter_switch_r(opt, indexes, nelem, columns, current_length);
365 free(columns);
369 static void
370 generate_switch(struct gen_opt *opt)
372 unsigned *indexes;
373 unsigned nlength;
374 unsigned i, current;
375 int use_if;
376 unsigned nelem;
378 nelem = sizeof(keyword_list)/sizeof(keyword_list[0]);
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]);
385 line(opt, " */");
387 indexes = (unsigned *) malloc(sizeof(indexes[0]) * nelem);
388 if (!indexes) {
389 perror("malloc");
390 exit(EXIT_FAILURE);
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);
397 use_if = (nlength <= opt->use_if_threshold);
399 if (!use_if)
400 line(opt, "switch (JSKW_LENGTH()) {");
402 current = (unsigned)strlen(keyword_list[indexes[0]]);
403 for (i = 0; i != nelem;) {
404 unsigned same_length_begin = i;
405 unsigned next = current;
407 for (++i; i != nelem; ++i) {
408 next = (unsigned)strlen(keyword_list[indexes[i]]);
409 if (next != current)
410 break;
412 if (use_if) {
413 line(opt, "if (JSKW_LENGTH() == %u) {", current);
414 } else {
415 line(opt, " case %u:", current);
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, "}");
425 current = next;
427 if (!use_if)
428 line(opt, "}");
429 line(opt, "JSKW_NO_MATCH()");
430 free(indexes);
433 int main(int argc, char **argv)
435 struct gen_opt opt;
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);
446 opt.indent_level = 1;
447 opt.use_if_threshold = 3;
448 opt.char_tail_test_threshold = 4;
450 generate_switch(&opt);
452 if (opt.output != stdout) {
453 if (fclose(opt.output)) {
454 perror("fclose");
455 exit(EXIT_FAILURE);
459 return EXIT_SUCCESS;