Source code for emlearn.cgen


"""
C code generation utilities
=========================
"""


import re

VALID_IDENTIFIER_REGEX = r'^[_a-zA-Z][_a-zA-Z0-9]*$' # valid C identifiers
from .creserved import RESERVED_WORDS

from .fixedpoint import FixedPointFormat, from_float

[docs]def struct_init(*args): """Struct initializer >>> from emlearn import cgen >>> cgen.struct_init([ 1, 2, 3 ]) "{ 1, 2, 3 }" """ return '{ ' + ', '.join(str(a) for a in args) + ' }'
[docs]def struct_declare(name, type_name, values=[], modifiers='static const'): """ Declaration and initialization of a (typedef) struct >>> from emlearn import cgen >>> cgen.struct_declare('myinstance', 'SomeStruct', [2, 'foo_array']) 'static const SomeStruct myinstance = { 2, foo_array };' """ init = '' if values is not None: init = f'= {struct_init(*values)}' out = f'{type_name} {name}{init};' return out
[docs]def constant(val, dtype='float'): """A literal value >>> from emlearn import cgen >>> cgen.constant(3.14) "3.14f" """ if dtype == 'float': return "{:.6f}f".format(val) elif 'int' in dtype: return "{:d}".format(int(val)) else: return str(val)
[docs]def constant_declare(name, val, dtype='int'): """ Declaration and initialization of a constant value >>> from emlearn import cgen >>> cgen.constant_declare('myvariable', 3) 'static const int myvariable = 3; ' Floating point instead >>> from emlearn import cgen >>> cgen.constant_declare('myfloat', 3.14, dtype='float') 'static const float myfloat = 3.140000f; ' """ v = constant(val, dtype=dtype) return f'static const {dtype} {name} = {v}; '
[docs]def array_declare(name, size=None, dtype='float', modifiers='static const', values=None, end='', indent=''): """ Declare and optionally initialize an array. >>> from emlearn import cgen >>> cgen.array_declare("declareonly", 10) "static const float declareonly[10];" Also intialize it. >>> from emlearn import cgen >>> cgen.array_declare("initialized", 3, dtype='int', modifiers='const') "const int initialized[3] = { 1, 2, 3 };" """ if values is not None: if size is None: size = len(values) assert size == len(values), 'size does not match length' init = '' if values is not None: init_values = ', '.join(constant(v, dtype) for v in values) init = ' = {{ {init_values} }}'.format(**locals()) return '{indent}{modifiers} {dtype} {name}[{size}]{init};{end}'.format(**locals())
[docs]def array_declare_fixedpoint(name, fixedpoint : FixedPointFormat = None, values=None, **kwargs): """ Declare and optionally initialize an fixed-point array. Handles conversion of floating-point values to fixed-point. """ if fixedpoint is None: # use float return array_declare(name, values=values, **kwargs) assert fixedpoint.total_bits == 32, ('code assumes 32 bit size for the fixed-point format') converted = from_float(values, fmt=fixedpoint) return array_declare(name, values=converted, dtype=fixedpoint.ctype, **kwargs)
[docs]def identifier_is_valid(s): """ Check whether identifier consists only of valid characters for C >>> from emlearn import cgen >>> cgen.identifier_is_reserved("_++") True """ match = re.match(VALID_IDENTIFIER_REGEX, s) return match is not None
[docs]def identifier_is_reserved(s): """ Check whether identifier is a reserved keyword in C >>> from emlearn import cgen >>> cgen.identifier_is_reserved("for") True """ reserved = s in RESERVED_WORDS return reserved
[docs]def assert_valid_identifier(s): """ Check whether a identifier is a valid in C :raises ValueError: In case this is *not* a valid C string """ valid = identifier_is_valid(s) if not valid: raise ValueError(f"'{s}' is not a valid C identifier") reserved = identifier_is_reserved(s) if reserved: raise ValueError(f"'{s}' is a reserved word in C")