PageRenderTime 84ms CodeModel.GetById 30ms app.highlight 48ms RepoModel.GetById 1ms app.codeStats 0ms

/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
 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}