1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
|
import re
def slugify(value):
value = re.sub(r'[^\w]+', '_', value.lower()).strip('_')
if value[:1].isdigit():
value = '_' + value
return value
# A single variable (enclosed by the template)
class Var(object):
STRUCT_NAME = 'vars'
CSIZES = {
# This array doesn't have to be exact, it's only used for sorting
# struct members to save a few bytes of memory here and there
'int': 4,
'unsigned': 4,
'float': 4,
'ident_t': 2,
'uint8_t': 1,
'bool': 1,
}
def __init__(self, ctype, expr, name, csize=0, linenr=0):
self.ctype = ctype
self.csize = csize or Var.CSIZES[ctype]
self.expr = expr
self.name = name
self.linenr = linenr
def __str__(self):
return f'{Var.STRUCT_NAME}.{self.name}'
def is_literal(expr):
return expr.isnumeric() or expr in ['true', 'false']
# A (deduplicated) set of variables
class VarSet(object):
def __init__(self):
self.varmap = {} # expr -> cvar
def __iter__(self):
# Sort from largest to smallest variable to optimize struct padding
yield from sorted(self.varmap.values(),
reverse=True,
key=lambda v: v.csize,
)
def __bool__(self):
return True if self.varmap else False
def add_var_raw(self, var):
# Re-use existing entry for identical expression/type pairs
if old := self.varmap.get(var.expr):
if var.ctype != old.ctype:
raise SyntaxError(f'Conflicting types for expression {var.expr}, '
f'got {var.ctype}, expected {old.ctype}')
assert old.name == var.name
return old
names = [ v.name for v in self.varmap.values() ]
while var.name in names:
var.name += '_'
self.varmap[var.expr] = var
return var
# Returns the added variable
def add_var(self, ctype, expr, name=None, linenr=0):
assert expr
expr = expr.strip()
if is_literal(expr):
return expr
name = name or slugify(expr)
var = Var(ctype, expr=expr, name=name, linenr=linenr)
return self.add_var_raw(var)
def merge(self, other):
for var in other:
self.add_var_raw(var)
|