import json from coc_string import * # from https://stackoverflow.com/questions/14945095/how-to-escape-string-for-generated-c (simplified) def escape_c(s, encoding='ascii'): result = '' for c in s: if not (32 <= ord(c) < 127): result += '\\%03o' % ord(c) elif c == '\\': result += "\\\\" elif c == '"': result += "\\\"" else: result += c return '"' + result + '"' class str_info: def __init__(self): self.hash = 0 self.str = "" self.extra = 0 class str_build: def __init__(self, map, map_weak, map_long): self.map = map.copy() self.str_weak = [] self.str_long = [] size = int(len(self.map) / 2) # voluntarily reduce hash size to half if size < 4: size = 4 self.buckets = [] for i in range(size): self.buckets.append([]) self.keywords() # add keywords to self.map self.make_ceil("", 0) # add empty string as it is always useful self.count = len(self.map) + 1 # TODO it is not actually accurate since keywords are not counted for k in sorted(self.map.keys()): self.make_ceil(k, self.map[k]) # handle weak strings for k in sorted(map_weak.keys()): if not k in self.map: self.str_weak.append(k) # handle long strings for k in sorted(map_long.keys()): if not k in self.map: self.str_long.append(k) def build(self, path): prefix = path + "/be_const_strtab" self.writefile(prefix + "_def.h", self.build_table_def()) self.writefile(prefix + ".h", self.build_table_ext()) def get_count(self): # compute the total size by adding sizes of each bucket size = 0 for bucket in self.buckets: size += len(bucket) return size def keywords(self): opif = 50 tab = { "if": opif, "elif": opif + 1 , "else": opif + 2 , "while": opif + 3 , "for": opif + 4 , "def": opif + 5 , "end": opif + 6 , "class": opif + 7 , "break": opif + 8 , "continue": opif + 9 , "return": opif + 10 , "true": opif + 11 , "false": opif + 12 , "nil": opif + 13 , "var": opif + 14 , "do": opif + 15 , "import": opif + 16 , "as": opif + 17 , "try": opif + 18 , "except": opif + 19 , "raise": opif + 20 , "static": opif + 21, } for key, v in tab.items(): self.map[key] = v def make_ceil(self, name, extra): info = str_info() info.hash = hashcode(name) info.str = name info.extra = extra self.buckets[info.hash % len(self.buckets)].append(info) def writefile(self, filename, text): buf = "" try: with open(filename) as f: buf = f.read() except FileNotFoundError: pass if buf != text: with open(filename, "w") as f: f.write(text) def build_table_def(self): strings = {} for bucket in self.buckets: # print(f"> Bucket= {[x.str for x in bucket]}") size = len(bucket) for i in range(size): info = bucket[i] node = escape_operator(info.str) istr = "" if i < size - 1: next = "&be_const_str_" + escape_operator(bucket[i + 1].str) else: next = "NULL" istr += "be_define_const_str(" istr += node + ", " + escape_c(info.str) + ", " istr += str(info.hash) + "u, " + str(info.extra) + ", " istr += str(len(info.str)) + ", " + next + ");\n" strings[info.str] = istr ostr = "" for s in sorted(strings.keys()): ostr += strings[s] ostr += "\n" ostr += "\n/* weak strings */\n" for k in self.str_weak: ostr += "be_define_const_str(" ostr += escape_operator(k) + ", " + escape_c(k) + ", " ostr += "0u, 0, " + str(len(k)) + ", NULL);\n" for k in self.str_long: ostr += "be_define_const_str_long(" ostr += escape_operator(k) + ", " + escape_c(k) + ", " + str(len(k)) + ");\n" ostr += "\n" ostr += "static const bstring* const m_string_table[] = {\n" size = len(self.buckets) for i in range(size): bucket = self.buckets[i] if len(bucket) > 0: ostr += " (const bstring *)&be_const_str_" + escape_operator(bucket[0].str) else: ostr += " NULL" if i < size - 1: ostr += "," ostr += "\n" ostr += "};\n\n" ostr += "static const struct bconststrtab m_const_string_table = {\n" ostr += " .size = " + str(size) + ",\n" ostr += " .count = " + str(self.get_count()) + ",\n" ostr += " .table = m_string_table\n" ostr += "};\n" return ostr def build_table_ext(self): ostr = "" # put all in a sorted sed all = set() for bucket in self.buckets: for info in bucket: all.add(escape_operator(info.str)) for s in sorted(all): ostr += "extern const bcstring be_const_str_" + s + ";\n" # ostr += "#define BE_CONST_STR_" + s + "\n" # weak strings ostr += "\n/* weak strings */\n" for s in self.str_weak: ostr += "extern const bcstring be_const_str_" + escape_operator(s) + ";\n" for s in self.str_long: ostr += "extern const bclstring be_const_str_" + escape_operator(s) + ";\n" return ostr