Source code

Revision control

Copy as Markdown

Other Tools

# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import re
import sys
def read_reserved_word_list(filename, enable_decorators):
macro_pat = re.compile(r"MACRO\(([^,]+), *[^,]+, *[^\)]+\)\s*\\?")
reserved_word_list = []
index = 0
with open(filename, "r") as f:
for line in f:
m = macro_pat.search(line)
if m:
reserved_word = m.group(1)
if reserved_word == "accessor" and not enable_decorators:
continue
reserved_word_list.append((index, reserved_word))
index += 1
assert len(reserved_word_list) != 0
return reserved_word_list
def line(opt, s):
opt["output"].write("{}{}\n".format(" " * opt["indent_level"], s))
def indent(opt):
opt["indent_level"] += 1
def dedent(opt):
opt["indent_level"] -= 1
def span_and_count_at(reserved_word_list, column):
assert len(reserved_word_list) != 0
chars_dict = {}
for index, word in reserved_word_list:
chars_dict[ord(word[column])] = True
chars = sorted(chars_dict.keys())
return chars[-1] - chars[0] + 1, len(chars)
def optimal_switch_column(opt, reserved_word_list, columns, unprocessed_columns):
assert len(reserved_word_list) != 0
assert unprocessed_columns != 0
min_count = 0
min_span = 0
min_count_index = 0
min_span_index = 0
for index in range(0, unprocessed_columns):
span, count = span_and_count_at(reserved_word_list, columns[index])
assert span != 0
if span == 1:
assert count == 1
return 1, True
assert count != 1
if index == 0 or min_span > span:
min_span = span
min_span_index = index
if index == 0 or min_count > count:
min_count = count
min_count_index = index
if min_count <= opt["use_if_threshold"]:
return min_count_index, True
return min_span_index, False
def split_list_per_column(reserved_word_list, column):
assert len(reserved_word_list) != 0
column_dict = {}
for item in reserved_word_list:
index, word = item
per_column = column_dict.setdefault(word[column], [])
per_column.append(item)
return sorted(column_dict.items())
def generate_letter_switch(opt, unprocessed_columns, reserved_word_list, columns=None):
assert len(reserved_word_list) != 0
if not columns:
columns = range(0, unprocessed_columns)
if len(reserved_word_list) == 1:
index, word = reserved_word_list[0]
if unprocessed_columns == 0:
line(opt, "JSRW_GOT_MATCH({}) /* {} */".format(index, word))
return
if unprocessed_columns > opt["char_tail_test_threshold"]:
line(opt, "JSRW_TEST_GUESS({}) /* {} */".format(index, word))
return
conds = []
for column in columns[0:unprocessed_columns]:
quoted = repr(word[column])
conds.append("JSRW_AT({})=={}".format(column, quoted))
line(opt, "if ({}) {{".format(" && ".join(conds)))
indent(opt)
line(opt, "JSRW_GOT_MATCH({}) /* {} */".format(index, word))
dedent(opt)
line(opt, "}")
line(opt, "JSRW_NO_MATCH()")
return
assert unprocessed_columns != 0
optimal_column_index, use_if = optimal_switch_column(
opt, reserved_word_list, columns, unprocessed_columns
)
optimal_column = columns[optimal_column_index]
# Make a copy to avoid breaking passed list.
columns = list(columns)
columns[optimal_column_index] = columns[unprocessed_columns - 1]
list_per_column = split_list_per_column(reserved_word_list, optimal_column)
if not use_if:
line(opt, "switch (JSRW_AT({})) {{".format(optimal_column))
for char, reserved_word_list_per_column in list_per_column:
quoted = repr(char)
if use_if:
line(opt, "if (JSRW_AT({}) == {}) {{".format(optimal_column, quoted))
else:
line(opt, " case {}:".format(quoted))
indent(opt)
generate_letter_switch(
opt, unprocessed_columns - 1, reserved_word_list_per_column, columns
)
dedent(opt)
if use_if:
line(opt, "}")
if not use_if:
line(opt, "}")
line(opt, "JSRW_NO_MATCH()")
def split_list_per_length(reserved_word_list):
assert len(reserved_word_list) != 0
length_dict = {}
for item in reserved_word_list:
index, word = item
per_length = length_dict.setdefault(len(word), [])
per_length.append(item)
return sorted(length_dict.items())
def generate_switch(opt, reserved_word_list):
assert len(reserved_word_list) != 0
line(opt, "/*")
line(
opt,
" * Generating switch for the list of {} entries:".format(
len(reserved_word_list)
),
)
for index, word in reserved_word_list:
line(opt, " * {}".format(word))
line(opt, " */")
list_per_length = split_list_per_length(reserved_word_list)
use_if = False
if len(list_per_length) < opt["use_if_threshold"]:
use_if = True
if not use_if:
line(opt, "switch (JSRW_LENGTH()) {")
for length, reserved_word_list_per_length in list_per_length:
if use_if:
line(opt, "if (JSRW_LENGTH() == {}) {{".format(length))
else:
line(opt, " case {}:".format(length))
indent(opt)
generate_letter_switch(opt, length, reserved_word_list_per_length)
dedent(opt)
if use_if:
line(opt, "}")
if not use_if:
line(opt, "}")
line(opt, "JSRW_NO_MATCH()")
def main(output, reserved_words_h, enable_decorators=False):
reserved_word_list = read_reserved_word_list(reserved_words_h, enable_decorators)
opt = {
"indent_level": 1,
"use_if_threshold": 3,
"char_tail_test_threshold": 4,
"output": output,
}
generate_switch(opt, reserved_word_list)
if __name__ == "__main__":
main(sys.stdout, *sys.argv[1:])