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
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
22 * Portions created by the Initial Developer are Copyright (C) 2005-2006
23 * the Initial Developer. All Rights Reserved.
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 ***** */
49 #include "jsversion.h"
51 const char * const keyword_list
[] = {
52 #define JS_KEYWORD(keyword, type, op, version) #keyword,
53 #include "jskeyword.tbl"
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
;
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
);
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
];
87 count_different_lengths(unsigned indexes
[], unsigned nelem
)
89 unsigned nlength
, current_length
, i
, l
;
93 for (i
= 0; i
!= nelem
; ++i
) {
94 l
= (unsigned)strlen(keyword_list
[indexes
[i
]]);
96 if (current_length
!= l
) {
105 find_char_span_and_count(unsigned indexes
[], unsigned nelem
, unsigned column
,
106 unsigned *span_result
, unsigned *count_result
)
109 unsigned char c
, prev
, minc
, maxc
;
112 minc
= maxc
= prev
= (unsigned char)keyword_list
[indexes
[0]][column
];
114 for (i
= 1; i
!= nelem
; ++i
) {
115 c
= (unsigned char)keyword_list
[indexes
[i
]][column
];
121 } else if (maxc
< c
) {
127 *span_result
= maxc
- minc
+ 1;
128 *count_result
= count
;
132 find_optimal_switch_column(struct gen_opt
*opt
,
133 unsigned indexes
[], unsigned nelem
,
134 unsigned columns
[], unsigned unprocessed_columns
,
138 unsigned span
, min_span
, min_span_index
;
139 unsigned nchar
, min_nchar
, min_nchar_index
;
141 assert(unprocessed_columns
!= 0);
143 min_nchar
= min_span
= (unsigned)-1;
144 min_nchar_index
= min_span_index
= 0;
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
,
157 if (min_span
> span
) {
161 if (min_nchar
> nchar
) {
165 } while (++i
!= unprocessed_columns
);
167 if (min_nchar
<= opt
->use_if_threshold
) {
176 * Restore order corresponding to i if it was destroyed by
179 if (i
!= unprocessed_columns
- 1) {
180 column_to_compare
= columns
[i
];
181 qsort(indexes
, nelem
, sizeof(indexes
[0]), column_comparator
);
189 p(struct gen_opt
*opt
, const char *format
, ...)
193 va_start(ap
, format
);
194 vfprintf(opt
->output
, format
, ap
);
198 /* Size for '\xxx' where xxx is octal escape */
199 #define MIN_QUOTED_CHAR_BUFFER 7
202 qchar(char c
, char *quoted_buffer
)
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
;
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
)));
229 assert(s
+ 1 <= quoted_buffer
+ MIN_QUOTED_CHAR_BUFFER
);
230 return quoted_buffer
;
234 nl(struct gen_opt
*opt
)
236 putc('\n', opt
->output
);
240 indent(struct gen_opt
*opt
)
242 unsigned n
= opt
->indent_level
;
245 fputs(" ", opt
->output
);
250 line(struct gen_opt
*opt
, const char *format
, ...)
255 va_start(ap
, format
);
256 vfprintf(opt
->output
, format
, ap
);
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
];
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
);
280 indent(opt
); p(opt
, "if (");
281 for (i
= 0; i
!= unprocessed_columns
; ++i
) {
283 qchar(keyword
[column
], qbuf
);
284 p(opt
, "%sJSKW_AT(%u)==%s", (i
== 0) ? "" : " && ",
287 p(opt
, ") {"); nl(opt
);
289 line(opt
, "JSKW_GOT_MATCH(%u) /* %s */", kw_index
, keyword
);
292 line(opt
, "JSKW_NO_MATCH()");
295 unsigned optimal_column_index
, optimal_column
;
300 assert(unprocessed_columns
!= 0);
301 optimal_column_index
= find_optimal_switch_column(opt
, indexes
, nelem
,
305 optimal_column
= columns
[optimal_column_index
];
306 columns
[optimal_column_index
] = columns
[unprocessed_columns
- 1];
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
;
316 for (++i
; i
!= nelem
; ++i
) {
317 next
= keyword_list
[indexes
[i
]][optimal_column
];
321 qchar(current
, qbuf
);
323 line(opt
, "if (JSKW_AT(%u) == %s) {", optimal_column
, qbuf
);
325 line(opt
, " case %s:", qbuf
);
328 generate_letter_switch_r(opt
, indexes
+ same_char_begin
,
330 columns
, unprocessed_columns
- 1);
342 columns
[optimal_column_index
] = optimal_column
;
344 line(opt
, "JSKW_NO_MATCH()");
349 generate_letter_switch(struct gen_opt
*opt
,
350 unsigned indexes
[], unsigned nelem
,
351 unsigned current_length
)
356 columns
= (unsigned *) malloc(sizeof(columns
[0]) * current_length
);
361 for (i
= 0; i
!= current_length
; ++i
) {
364 generate_letter_switch_r(opt
, indexes
, nelem
, columns
, current_length
);
370 generate_switch(struct gen_opt
*opt
)
378 nelem
= sizeof(keyword_list
)/sizeof(keyword_list
[0]);
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
]);
387 indexes
= (unsigned *) malloc(sizeof(indexes
[0]) * nelem
);
392 for (i
= 0; i
!= nelem
; ++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
);
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
]]);
413 line(opt
, "if (JSKW_LENGTH() == %u) {", current
);
415 line(opt
, " case %u:", current
);
418 generate_letter_switch(opt
, indexes
+ same_length_begin
,
419 i
- same_length_begin
,
429 line(opt
, "JSKW_NO_MATCH()");
433 int main(int argc
, char **argv
)
440 opt
.output
= fopen(argv
[1], "w");
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
)) {