Tasmota/lib/libesp32/berry/tools/coc/str_build.py

172 lines
5.8 KiB
Python

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