micropython_async_B_mapper.py 30.1 KB
Newer Older
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
#
# (C) Semantix Information Technologies.
#
# Semantix Information Technologies is licensing the code of the
# Data Modelling Tools (DMT) in the following dual-license mode:
#
# Commercial Developer License:
#       The DMT Commercial Developer License is the suggested version
# to use for the development of proprietary and/or commercial software.
# This version is for developers/companies who do not want to comply
# with the terms of the GNU Lesser General Public License version 2.1.
#
# GNU LGPL v. 2.1:
#       This version of DMT is the one to use for the development of
# applications, when you are willing to comply with the terms of the
# GNU Lesser General Public License version 2.1.
#
# Note that in both cases, there are no charges (royalties) for the
# generated code.
#
'''
This is the implementation of the code mapper for C code.
As initially envisioned, ASSERT technology is not supposed
to support manually-made systems. A migration path, however,
that allows legacy hand-written code and modelling-tool
generated code to co-exist, can be beneficial in allowing
for a smooth transition. To that end, this backend (as well as
the Ada one) are written.

This is a backend for Semantix's code generator B (aadl2glueC).

C is a member of the asynchronous "club" (SDL, etc);
The subsystem developer (or rather, the APLC developer) is using
native C/C++ code to work with code generated by modelling tools.
To that end, this backend creates "glue" functions for input and
output parameters, which have C callable interfaces.
'''

39
from typing import Tuple, List, Dict, Sequence  # noqa
40

41
from ..commonPy.utility import inform
42
from ..commonPy.asnAST import (
43
    sourceSequenceLimit, isSequenceVariable,
44
    AsnSequenceOrSet, AsnChoice, AsnSequenceOrSetOf, AsnEnumerated,
45
    AsnNode, AsnInt, AsnReal, AsnBool, AsnOctetString)
46
from ..commonPy.asnParser import AST_Lookup, AST_Leaftypes
47
from ..commonPy.recursiveMapper import RecursiveMapperGeneric
48 49 50
from .asynchronousTool import ASynchronousToolGlueGenerator
from .c_B_mapper import C_GlueGenerator

51 52 53 54
backend_C = None
backend_uPy = None
backends = None

55 56
# TODO replace most of the typedefs with an include of py/obj.h
h_header_str = """
57
#include <stdbool.h>
58
#include <stdint.h>
59 60
#include "C_ASN1_Types.h"

61 62 63 64 65 66
#ifdef MICROPY_INCLUDED_PY_OBJ_H

#include "py/objarray.h"

#else

67
typedef void *mp_obj_t;
68 69
typedef intptr_t mp_int_t;
typedef uintptr_t mp_uint_t;
70 71
typedef double mp_float_t;

72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
typedef struct _mp_obj_type_t mp_obj_type_t;

typedef struct _mp_obj_base_t {
    const mp_obj_type_t *type;
} mp_obj_base_t;

typedef struct _mp_obj_tuple_t {
    mp_obj_base_t base;
    size_t len;
    mp_obj_t items[];
} mp_obj_tuple_t;

typedef struct _mp_obj_list_t {
    mp_obj_base_t base;
    size_t alloc;
    size_t len;
    mp_obj_t *items;
} mp_obj_list_t;

#define BYTEARRAY_TYPECODE 1
typedef struct _mp_obj_array_t {
    mp_obj_base_t base;
    size_t typecode : 8;
    // free is number of unused elements after len used elements
    // alloc size = len + free
    size_t free : (8 * sizeof(size_t) - 8);
    size_t len; // in elements
    void *items;
} mp_obj_array_t;

#define MP_OBJ_TO_PTR(o) ((void*)o)
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
#define MP_OBJ_FROM_PTR(p) ((mp_obj_t)p)

// Constant objects, globally accessible
// The macros are for convenience only
#define mp_const_none (MP_OBJ_FROM_PTR(&mp_const_none_obj))
#define mp_const_false (MP_OBJ_FROM_PTR(&mp_const_false_obj))
#define mp_const_true (MP_OBJ_FROM_PTR(&mp_const_true_obj))
#define mp_const_empty_bytes (MP_OBJ_FROM_PTR(&mp_const_empty_bytes_obj))
#define mp_const_empty_tuple (MP_OBJ_FROM_PTR(&mp_const_empty_tuple_obj))
#define mp_const_notimplemented (MP_OBJ_FROM_PTR(&mp_const_notimplemented_obj))
extern const struct _mp_obj_none_t mp_const_none_obj;
extern const struct _mp_obj_bool_t mp_const_false_obj;
extern const struct _mp_obj_bool_t mp_const_true_obj;
extern const struct _mp_obj_str_t mp_const_empty_bytes_obj;
extern const struct _mp_obj_tuple_t mp_const_empty_tuple_obj;
extern const struct _mp_obj_singleton_t mp_const_ellipsis_obj;
extern const struct _mp_obj_singleton_t mp_const_notimplemented_obj;
extern const struct _mp_obj_exception_t mp_const_MemoryError_obj;
extern const struct _mp_obj_exception_t mp_const_GeneratorExit_obj;

123 124 125 126 127 128
// Types
extern const struct _mp_obj_type_t mp_type_float;
extern const struct _mp_obj_type_t mp_type_list;
extern const struct _mp_obj_type_t mp_type_bytearray;
extern const struct _mp_obj_type_t mp_type_mutable_attrtuple;

129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
// General API for objects

mp_obj_t mp_obj_new_none(void);
static inline mp_obj_t mp_obj_new_bool(mp_int_t x) { return x ? mp_const_true : mp_const_false; }
mp_obj_t mp_obj_new_cell(mp_obj_t obj);
mp_obj_t mp_obj_new_int(mp_int_t value);
mp_obj_t mp_obj_new_int_from_uint(mp_uint_t value);
mp_obj_t mp_obj_new_int_from_str_len(const char **str, size_t len, bool neg, unsigned int base);
mp_obj_t mp_obj_new_int_from_ll(long long val); // this must return a multi-precision integer object (or raise an overflow exception)
mp_obj_t mp_obj_new_int_from_ull(unsigned long long val); // this must return a multi-precision integer object (or raise an overflow exception)
mp_obj_t mp_obj_new_str(const char* data, size_t len, bool make_qstr_if_not_already);
mp_obj_t mp_obj_new_bytes(const byte* data, size_t len);
mp_obj_t mp_obj_new_bytearray(size_t n, void *items);
mp_obj_t mp_obj_new_bytearray_by_ref(size_t n, void *items);
mp_obj_t mp_obj_new_int_from_float(mp_float_t val);
144
mp_obj_t mp_obj_new_float(mp_float_t real);
145
mp_obj_t mp_obj_new_complex(mp_float_t real, mp_float_t imag);
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
mp_obj_t mp_obj_new_list(size_t len, mp_obj_t *items);

#endif

// Runtime functions

void mp_raise_NotImplementedError(const char *msg);

void mp_taste_types_init(void);

"""

c_header_str = """
typedef size_t qstr;
qstr qstr_from_str(const char *str);

bool mp_obj_is_true(mp_obj_t arg);
mp_int_t mp_obj_get_int(mp_obj_t arg);
mp_float_t mp_obj_get_float(mp_obj_t self_in);

166 167
"""

168 169
# pylint: disable=unused-argument
# pylint: disable=no-self-use
170

171

172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
class MapUPyObjData(RecursiveMapperGeneric[str, str]):
    def __init__(self) -> None:
        pass

    def MapInteger(self, srcVar: str, destVar: str, _: AsnInt, __: AST_Leaftypes, ___: AST_Lookup) -> List[str]:
        # No extra storage needed for this type
        # TODO handle big integers
        return []

    def MapReal(self, srcVar: str, destVar: str, _: AsnReal, __: AST_Leaftypes, ___: AST_Lookup) -> List[str]:
        # TODO if NaN boxing then we don't need any data
        return [
            'struct {',
            '    mp_obj_base_t base;',
            '    mp_float_t value;',
            '}',
        ]

    def MapBoolean(self, srcVar: str, destVar: str, _: AsnBool, __: AST_Leaftypes, ___: AST_Lookup) -> List[str]:
        # No extra storage needed for this type
        return []

    def MapOctetString(self, srcVar: str, destVar: str, node: AsnOctetString, __: AST_Leaftypes, ___: AST_Lookup) -> List[str]:
        return [
            'struct {',
            '   mp_obj_array_t array;',
            '   byte data[%u];' % node._range[-1],
            '}',
        ]

    def MapEnumerated(self, srcVar: str, destVar: str, _: AsnEnumerated, __: AST_Leaftypes, ___: AST_Lookup) -> List[str]:
        # No extra storage needed for this type
        return []

206
    def MapSequence(self, srcVar: str, destVar: str, node: AsnSequenceOrSet, leafTypeDict: AST_Leaftypes, names: AST_Lookup) -> List[str]:
207 208 209 210 211 212 213 214 215 216
        num_items = len(node._members)
        lines = [
            'struct {',
            '    mp_obj_tuple_t tuple;',
            '    mp_obj_t items[%u];' % (num_items + 1),
        ]
        for child in node._members:
            contained = self.Map(srcVar, destVar, child[1], leafTypeDict, names)
            if contained:
                lines.extend('    ' + l for l in contained[:-1])
217
                lines.append('    %s data_%s;' % (contained[-1], self.CleanName(child[0])))
218 219 220
        lines.append('}')
        return lines

221
    def MapSet(self, srcVar: str, destVar: str, node: AsnSequenceOrSet, leafTypeDict: AST_Leaftypes, names: AST_Lookup) -> List[str]:
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
        return self.MapSequence(srcVar, destVar, node, leafTypeDict, names)

    def MapChoice(self, srcVar: str, destVar: str, node: AsnChoice, leafTypeDict: AST_Leaftypes, names: AST_Lookup) -> List[str]:
        lines = [
            'struct {',
            '    mp_obj_tuple_t tuple;',
            '    mp_obj_t items[2];',
            '    union {',
        ]
        for child in node._members:
            contained = self.Map(srcVar, destVar, child[1], leafTypeDict, names)
            if contained:
                lines.extend('        ' + l for l in contained[:-1])
                lines.append('        %s %s;' % (contained[-1], self.CleanName(child[0])))
        lines.append('    } data;')
        lines.append('}')
        return lines

240
    def MapSequenceOf(self, srcVar: str, destVar: str, node: AsnSequenceOrSetOf, leafTypeDict: AST_Leaftypes, names: AST_Lookup) -> List[str]:
241 242 243 244 245 246 247 248 249 250 251 252 253
        num_items = node._range[-1]
        lines = [
            'struct {',
            '    mp_obj_list_t list;',
            '    mp_obj_t items[%u];' % num_items,
        ]
        contained = self.Map(srcVar, destVar, node._containedType, leafTypeDict, names)
        if contained:
            lines.extend('    ' + l for l in contained[:-1])
            lines.append('    %s data[%u];' % (contained[-1], num_items))
        lines.append('}')
        return lines

254
    def MapSetOf(self, srcVar: str, destVar: str, node: AsnSequenceOrSetOf, leafTypeDict: AST_Leaftypes, names: AST_Lookup) -> List[str]:
255 256 257 258
        return self.MapSequenceOf(srcVar, destVar, node, leafTypeDict, names)


class MapUPyObjEncode(RecursiveMapperGeneric[str, Tuple[str, str]]):
259
    def __init__(self, parent: 'MicroPython_GlueGenerator') -> None:
260 261 262 263 264 265 266
        self.parent = parent
        self.uniqueID = 0

    def UniqueID(self) -> int:
        self.uniqueID += 1
        return self.uniqueID

267
    def MapInteger(self, srcVar: str, destVar: Tuple[str, str], _: AsnInt, __: AST_Leaftypes, ___: AST_Lookup) -> List[str]:
268
        # TODO check range of integer
269
        dest, unused = destVar
270 271
        return ['%s = mp_obj_new_int(%s);' % (dest, srcVar)]

272
    def MapReal(self, srcVar: str, destVar: Tuple[str, str], _: AsnReal, __: AST_Leaftypes, ___: AST_Lookup) -> List[str]:
273 274 275 276 277 278 279 280 281
        # TODO check range of real
        # TODO if NaN boxing then we should just use mp_obj_new_float()
        dest, data = destVar
        return [
            '(%s)->base.type = &mp_type_float;' % data,
            '(%s)->value = %s;' % (data, srcVar),
            '%s = MP_OBJ_FROM_PTR(%s);' % (dest, data),
        ]

282
    def MapBoolean(self, srcVar: str, destVar: Tuple[str, str], _: AsnBool, __: AST_Leaftypes, ___: AST_Lookup) -> List[str]:
283
        dest, unused = destVar
284 285
        return ['%s = mp_obj_new_bool(%s);' % (dest, srcVar)]

286
    def MapOctetString(self, srcVar: str, destVar: Tuple[str, str], node: AsnOctetString, __: AST_Leaftypes, ___: AST_Lookup) -> List[str]:
287 288 289 290 291 292 293 294 295 296 297 298 299 300
        # OctetString maps to a MicroPython bytearray
        # TODO bytearray can be appended to, so perhaps make this a memoryview
        dest, data = destVar
        limit = sourceSequenceLimit(node, srcVar)
        return [
            '(%s)->array.base.type = &mp_type_bytearray;' % data,
            '(%s)->array.typecode = BYTEARRAY_TYPECODE;' % data,
            '(%s)->array.free = 0;' % data,
            '(%s)->array.len = %s;' % (data, limit),
            '(%s)->array.items = &(%s)->data[0];' % (data, data),
            'memcpy(&(%s)->data[0], &(%s).arr[0], %s);' % (data, srcVar, limit),
            '%s = MP_OBJ_FROM_PTR(%s);' % (dest, data),
        ]

301
    def MapEnumerated(self, srcVar: str, destVar: Tuple[str, str], _: AsnEnumerated, __: AST_Leaftypes, ___: AST_Lookup) -> List[str]:
302
        # TODO check the enum value fits in a small int and use MP_OBJ_NEW_SMALL_INT
303
        dest, unused = destVar
304 305
        return ['%s = mp_obj_new_int(%s);' % (dest, srcVar)]

306
    def MapSequence(self, srcVar: str, destVar: Tuple[str, str], node: AsnSequenceOrSet, leafTypeDict: AST_Leaftypes, names: AST_Lookup) -> List[str]:
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
        # Sequence maps to a MicroPython mutable-attrtuple
        dest, data = destVar
        limit = len(node._members)
        qstrs = tuple(self.CleanName(child[0]) for child in node._members)
        fields_name = self.parent.AddQstrFields(qstrs)
        lines = [
            'extern qstr %s[%u];' % (fields_name, limit),
            '(%s)->tuple.base.type = &mp_type_mutable_attrtuple;' % data,
            '(%s)->tuple.len = %s;' % (data, limit),
        ]
        for it, child in enumerate(node._members):
            lines.extend(
                self.Map(
                    "(%s).%s" % (srcVar, self.CleanName(child[0])),
                    ('(%s)->items[%s]' % (data, it), '&(%s)->data_%s' % (data, self.CleanName(child[0]))),
                    child[1],
                    leafTypeDict,
                    names))
        lines.append('(%s)->items[%u] = MP_OBJ_FROM_PTR(%s);' % (data, limit, fields_name))
326
        lines.append('%s = MP_OBJ_FROM_PTR(%s);' % (dest, data))
327 328
        return lines

329
    def MapSet(self, srcVar: str, destVar: Tuple[str, str], node: AsnSequenceOrSet, leafTypeDict: AST_Leaftypes, names: AST_Lookup) -> List[str]:
330 331
        return self.MapSequence(srcVar, destVar, node, leafTypeDict, names)

332
    def MapChoice(self, srcVar: str, destVar: Tuple[str, str], node: AsnChoice, leafTypeDict: AST_Leaftypes, names: AST_Lookup) -> List[str]:
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349
        # Choice maps to a MicroPython mutable-attrtuple with a single member
        # The choice itself is indicated by the array of qstrs that items[1] points to
        dest, data = destVar
        lines = []
        fields_names = []
        for child in node._members:
            qstrs = (self.CleanName(child[0]),)
            fields_names.append(self.parent.AddQstrFields(qstrs))
            lines.append('extern qstr %s[1];' % fields_names[-1])
        self.parent.AddChoiceFields(node, fields_names)
        lines.extend([
            '(%s)->tuple.base.type = &mp_type_mutable_attrtuple;' % data,
            '(%s)->tuple.len = 1;' % data,
        ])
        for it, child in enumerate(node._members):
            lines.append('%sif ((%s).kind == %s) {' % ('else ' if it else '', srcVar, self.CleanName(child[2])))
            lines.extend('    ' + l
350 351 352 353 354 355
                         for l in self.Map(
                             "(%s).u.%s" % (srcVar, self.CleanName(child[0])),
                             ('(%s)->items[0]' % data, '&(%s)->data.%s' % (data, self.CleanName(child[0]))),
                             child[1],
                             leafTypeDict,
                             names))
356 357
            lines.append('    (%s)->items[1] = MP_OBJ_FROM_PTR(%s);' % (data, fields_names[it]))
            lines.append('}')
358
        lines.append('%s = MP_OBJ_FROM_PTR(%s);' % (dest, data))
359 360
        return lines

361
    def MapSequenceOf(self, srcVar: str, destVar: Tuple[str, str], node: AsnSequenceOrSetOf, leafTypeDict: AST_Leaftypes, names: AST_Lookup) -> List[str]:
362 363 364 365 366 367 368 369 370 371 372 373
        # SequenceOf maps to a MicroPython list
        dest, data = destVar
        it = 'i%u' % self.UniqueID()
        limit = sourceSequenceLimit(node, srcVar)
        lines = [
            '(%s)->list.base.type = &mp_type_list;' % data,
            '(%s)->list.alloc = %s;' % (data, limit),
            '(%s)->list.len = %s;' % (data, limit),
            '(%s)->list.items = &(%s)->items[0];' % (data, data),
            'for (size_t %s = 0; %s < %s; ++%s) {' % (it, it, limit, it),
        ]
        lines.extend('    ' + l
374 375 376 377
                     for l in self.Map(
                         '(%s).arr[%s]' % (srcVar, it),
                         ('(%s)->items[%s]' % (data, it), '&(%s)->data[%s]' % (data, it)),
                         node._containedType, leafTypeDict, names))
378
        lines.append('}')
379
        lines.append('%s = MP_OBJ_FROM_PTR(%s);' % (dest, data))
380 381
        return lines

382
    def MapSetOf(self, srcVar: str, destVar: Tuple[str, str], node: AsnSequenceOrSetOf, leafTypeDict: AST_Leaftypes, names: AST_Lookup) -> List[str]:
383 384 385
        return self.MapSequenceOf(srcVar, destVar, node, leafTypeDict, names)


386
class MapUPyObjDecode(RecursiveMapperGeneric[str, str]):
387
    def __init__(self, parent: 'MicroPython_GlueGenerator') -> None:
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
        self.parent = parent
        self.uniqueID = 0

    def UniqueID(self) -> int:
        self.uniqueID += 1
        return self.uniqueID

    def MapInteger(self, srcVar: str, destVar: str, _: AsnInt, __: AST_Leaftypes, ___: AST_Lookup) -> List[str]:
        # TODO check range of integer
        return ['%s = mp_obj_get_int(%s);' % (destVar, srcVar)]

    def MapReal(self, srcVar: str, destVar: str, _: AsnReal, __: AST_Leaftypes, ___: AST_Lookup) -> List[str]:
        # TODO check range of real
        return ['%s = mp_obj_get_float(%s);' % (destVar, srcVar)]

    def MapBoolean(self, srcVar: str, destVar: str, _: AsnBool, __: AST_Leaftypes, ___: AST_Lookup) -> List[str]:
        return ['%s = mp_obj_is_true(%s);' % (destVar, srcVar)]

    def MapOctetString(self, srcVar: str, destVar: str, node: AsnOctetString, __: AST_Leaftypes, ___: AST_Lookup) -> List[str]:
        # OctetString maps to a MicroPython bytearray
        limit = sourceSequenceLimit(node, srcVar)
        lines = [
            'memcpy(&(%s).arr[0], ((mp_obj_array_t*)MP_OBJ_TO_PTR(%s))->items, %s);' % (destVar, srcVar, limit),
        ]
        if isSequenceVariable(node):
            lines.append('(%s).nCount = %s\n' % (destVar, limit))
        return lines

    def MapEnumerated(self, srcVar: str, destVar: str, _: AsnEnumerated, __: AST_Leaftypes, ___: AST_Lookup) -> List[str]:
        # TODO check the enum value fits in a small int and use MP_OBJ_SMALL_INT_VALUE
        return ['%s = mp_obj_get_int(%s);' % (destVar, srcVar)]

420
    def MapSequence(self, srcVar: str, destVar: str, node: AsnSequenceOrSet, leafTypeDict: AST_Leaftypes, names: AST_Lookup) -> List[str]:
421 422 423
        # Sequence maps to a MicroPython mutable-attrtuple
        lines = []
        # TODO verify incoming object has correct type and length (and qstr fields?)
424 425 426
        # limit = len(node._members)
        # '%s->tuple.base.type == &mp_type_mutable_attrtuple;' % data,
        # '%s->tuple.len == %s;' % (data, limit),
427 428 429 430 431 432 433 434 435 436
        for it, child in enumerate(node._members):
            lines.extend(
                self.Map(
                    '((mp_obj_tuple_t*)MP_OBJ_TO_PTR(%s))->items[%u]' % (srcVar, it),
                    '(%s).%s' % (destVar, self.CleanName(child[0])),
                    child[1],
                    leafTypeDict,
                    names))
        return lines

437
    def MapSet(self, srcVar: str, destVar: str, node: AsnSequenceOrSet, leafTypeDict: AST_Leaftypes, names: AST_Lookup) -> List[str]:
438 439 440 441 442 443 444 445 446 447 448 449
        return self.MapSequence(srcVar, destVar, node, leafTypeDict, names)

    def MapChoice(self, srcVar: str, destVar: str, node: AsnChoice, leafTypeDict: AST_Leaftypes, names: AST_Lookup) -> List[str]:
        # Choice maps to a MicroPython mutable-attrtuple with a single member
        lines = []
        # TODO verify incoming object has correct type and length (and qstr fields?)
        fields_names = self.parent.LookupChoiceFields(node)
        for f in fields_names:
            lines.append('extern qstr %s[1];' % f)
        for it, child in enumerate(node._members):
            lines.append('%sif (MP_OBJ_TO_PTR(((mp_obj_tuple_t*)MP_OBJ_TO_PTR(%s))->items[1]) == %s) {' % ('else ' if it else '', srcVar, fields_names[it]))
            lines.extend('    ' + l
450 451 452 453 454 455
                         for l in self.Map(
                             '((mp_obj_tuple_t*)MP_OBJ_TO_PTR(%s))->items[0]' % srcVar,
                             '(%s).u.%s' % (destVar, self.CleanName(child[0])),
                             child[1],
                             leafTypeDict,
                             names))
456 457 458 459
            lines.append('    (%s).kind = %s;' % (destVar, self.CleanName(child[2])))
            lines.append('}')
        return lines

460
    def MapSequenceOf(self, srcVar: str, destVar: str, node: AsnSequenceOrSetOf, leafTypeDict: AST_Leaftypes, names: AST_Lookup) -> List[str]:
461 462 463 464 465 466
        # SequenceOf maps to a MicroPython list
        it = 'i%u' % self.UniqueID()
        limit = sourceSequenceLimit(node, srcVar)
        # TODO verify incoming object has correct type and length
        #    '%s->list.base.type = &mp_type_list;' % data,
        #    '%s->list.len = %s;' % (data, limit),
467
        lines = [
468 469 470
            'for (size_t %s = 0; %s < %s; ++%s) {' % (it, it, limit, it),
        ]
        lines.extend('    ' + l
471 472 473 474
                     for l in self.Map(
                         '((mp_obj_list_t*)MP_OBJ_TO_PTR(%s))->items[%s]' % (srcVar, it),
                         '%s.arr[%s]' % (destVar, it),
                         node._containedType, leafTypeDict, names))
475 476 477
        lines.append('}')
        return lines

478
    def MapSetOf(self, srcVar: str, destVar: str, node: AsnSequenceOrSetOf, leafTypeDict: AST_Leaftypes, names: AST_Lookup) -> List[str]:
479 480 481
        return self.MapSequenceOf(srcVar, destVar, node, leafTypeDict, names)


482
class MicroPython_GlueGenerator(ASynchronousToolGlueGenerator):
483 484
    def __init__(self) -> None:
        ASynchronousToolGlueGenerator.__init__(self)
485 486 487
        self.neededQstrFields = {}  # type: Dict[Sequence[str], Tuple[str, Sequence[str]]]
        self.choiceNodes = {}  # type: Dict[AsnChoice, List[str]]
        self.allAsnTypes = []  # type: List[Tuple[str, str]]
488 489 490 491
        self.MapUPyObjData = MapUPyObjData()
        self.MapUPyObjEncode = MapUPyObjEncode(self)
        self.MapUPyObjDecode = MapUPyObjDecode(self)

492
    def AddQstrFields(self, fields: Sequence[str]) -> str:
493 494 495
        try:
            return self.neededQstrFields[fields][0]
        except KeyError:
496 497 498
            ide = 'upython_qstr_fields%u' % len(self.neededQstrFields)
            self.neededQstrFields[fields] = (ide, fields)
            return ide
499

500
    def AddChoiceFields(self, choiceNode: AsnChoice, fields: List[str]) -> None:
501 502
        self.choiceNodes[choiceNode] = fields

503
    def LookupChoiceFields(self, choiceNode: AsnChoice) -> List[str]:
504 505
        return self.choiceNodes[choiceNode]

506
    def Version(self) -> None:  # pylint: disable=no-self-use
507
        print("Code generator: " + "$Id: micropython_async_B_mapper.py 2390 2017-08-00 12:00:00Z dpgeorge $")  # pragma: no cover
508 509 510 511 512

    def HeadersOnStartup(self,  # pylint: disable=no-self-use
                         asnFile: str,
                         outputDir: str,
                         maybeFVname: str) -> None:
513
        # print('headers', asnFile, outputDir, maybeFVname)
514 515
        self.C_HeaderFile.write(h_header_str)
        self.C_SourceFile.write(c_header_str)
516 517 518 519 520 521 522 523

    def Encoder(self,  # pylint: disable=no-self-use
                nodeTypename: str,
                node: AsnNode,
                leafTypeDict: AST_Leaftypes,
                names: AST_Lookup,
                encoding: str) -> None:
        # we get called for each nodeTypename, and each encoding (uper, acn, native)
524
        # print('encoder', nodeTypename, encoding)
525
        if encoding == 'native':
526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
            tname = self.CleanNameAsToolWants(nodeTypename)

            # Generate the MicroPython typedef for the ASN type, and the encode declaration
            lines = []
            data = self.MapUPyObjData.Map('', '', node, leafTypeDict, names)
            if data:
                dataType = 'mp_obj_asn1Scc%s_t' % tname
                lines.append('#define MICROPY_TASTE_NEED_DATA_FOR_%s (1)' % tname)
                if len(data) == 1:
                    lines.append('typedef %s %s;' % (data[0], dataType))
                else:
                    lines.append('typedef %s' % data[0])
                    lines.extend(data[1:-1])
                    lines.append('%s %s;' % (data[-1], dataType))
            else:
                dataType = 'void'
            lines.append('mp_obj_t mp_obj_encode_asn1Scc%s(const asn1Scc%s *pVal, %s *pData);' % (tname, tname, dataType))
            self.C_HeaderFile.write('\n'.join(lines) + '\n\n')

            # Generate the encode function definition
            lines = []
            lines.append('mp_obj_t mp_obj_encode_asn1Scc%s(const asn1Scc%s *pVal, %s *pData) {' % (tname, tname, dataType))
            if dataType == 'void':
549
                lines.append('    (void)pData;')  # suppress C compiler warning
550 551 552 553 554 555 556 557
            lines.append('    mp_obj_t o;')
            lines.extend('    ' + l for l in self.MapUPyObjEncode.Map('*pVal', ('o', 'pData'), node, leafTypeDict, names))
            lines.append('    return o;')
            lines.append('}')
            self.C_SourceFile.write('\n'.join(lines) + '\n\n')

            # Append to the list of all ASN types
            self.allAsnTypes.append((tname, dataType))
558 559 560 561 562 563 564 565

    def Decoder(self,  # pylint: disable=no-self-use
                nodeTypename: str,
                node: AsnNode,
                leafTypeDict: AST_Leaftypes,
                names: AST_Lookup,
                encoding: str) -> None:
        # we get called for each nodeTypename, and each encoding (uper, acn, native)
566
        # print('decoder', nodeTypename, encoding)
567 568 569 570 571 572 573 574 575 576 577 578 579 580
        if encoding == 'native':
            tname = self.CleanNameAsToolWants(nodeTypename)

            lines = []
            lines.append('void mp_obj_decode_asn1Scc%s(mp_obj_t obj, asn1Scc%s *pVal);' % (tname, tname))
            self.C_HeaderFile.write('\n'.join(lines) + '\n\n')

            lines = []
            lines.append('void mp_obj_decode_asn1Scc%s(mp_obj_t obj, asn1Scc%s *pVal) {' % (tname, tname))
            lines.extend('    ' + l for l in self.MapUPyObjDecode.Map('obj', '(*pVal)', node, leafTypeDict, names))
            lines.append('}')
            self.C_SourceFile.write('\n'.join(lines) + '\n\n')

    # We completely override this method so we can add some code at the end of the C file
581
    def OnShutdown(self, modelingLanguage: str, asnFile: str, maybeFVname: str) -> None:
582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619
        # This is what ASynchronousToolGlueGenerator would do
        for nodeTypename, value in self.typesToWorkOn.items():
            inform(str(self.__class__) + "Really working on " + nodeTypename)
            (node, leafTypeDict, names) = value
            self.Common(nodeTypename, node, leafTypeDict, names)

        # Generate the MicroPython ASN types that will go in the MicroPython "taste" module
        lines = []
        lines.append('#define MICROPY_TASTE_ASN_CONSTRUCTORS \\')
        for tname, dataType in self.allAsnTypes:
            asn = 'asn1Scc%s' % tname
            lines.extend([
                'mp_obj_t mp_obj_new_%s(size_t n_args, const mp_obj_t *args) { \\' % asn,
                '    %s val; \\' % asn,
                '    if (n_args == 0) { \\',
                '        %s_Initialize(&val); \\' % asn,
                '    } else { \\',
                '        mp_obj_decode_%s(args[0], &val); \\' % asn,
                '    } \\',
            ])
            if dataType == 'void':
                lines.append('    return mp_obj_encode_%s(&val, NULL); \\' % asn)
            else:
                # Note: mp_obj_encode_XXX must return a pointer to "data" for the following
                # code to be safe and the GC not reclaim the object.
                lines.append('    %s *data = m_new_obj(%s); \\' % (dataType, dataType))
                lines.append('    return mp_obj_encode_%s(&val, data); \\' % asn)
            lines.append('} \\')
            lines.append('MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_obj_new_%s_obj, 0, 1, mp_obj_new_%s); \\' % (asn, asn))
        lines.append('')
        lines.append('#define MICROPY_TASTE_ASN_MAP_ENTRIES \\')
        for tname, dataType in self.allAsnTypes:
            lines.append('    { MP_ROM_QSTR(MP_QSTR_asn1Scc%s), MP_ROM_PTR(&mp_obj_new_asn1Scc%s_obj) }, \\' % (tname, tname))
        lines.append('')
        self.C_HeaderFile.write('\n'.join(lines) + '\n\n')

        # Generate the init function
        lines = []
620 621
        for ide, fields in self.neededQstrFields.values():
            lines.append('qstr %s[%u];' % (ide, len(fields)))
622 623 624 625 626 627 628
        lines.append('')
        lines.append('void mp_taste_types_init(void) {')
        lines.append('    static int inited = 0;')
        lines.append('    if (inited) {')
        lines.append('        return;')
        lines.append('    }')
        lines.append('    inited = 1;')
629
        for ide, fields in self.neededQstrFields.values():
630
            for i, f in enumerate(fields):
631
                lines.append('    %s[%u] = qstr_from_str("%s");' % (ide, i, f))
632 633
        lines.append('}')
        self.C_SourceFile.write('\n'.join(lines) + '\n\n')
634

635 636 637 638
        # Finish and close files
        self.C_HeaderFile.write("\n#endif\n")
        self.C_HeaderFile.close()
        self.C_SourceFile.close()
639

640

641
def OnStartup(modelingLanguage: str, asnFile: str, outputDir: str, maybeFVname: str, useOSS: bool) -> None:
642 643 644 645 646 647
    global backend_C, backend_uPy, backends
    backend_C = C_GlueGenerator()
    backend_uPy = MicroPython_GlueGenerator()
    backends = [backend_C, backend_uPy]
    backend_C.OnStartup("C", asnFile, outputDir, maybeFVname, useOSS)
    backend_uPy.OnStartup("MicroPython", asnFile, outputDir, maybeFVname, useOSS)
648 649 650


def OnBasic(nodeTypename: str, node: AsnNode, leafTypeDict: AST_Leaftypes, names: AST_Lookup) -> None:
651 652
    for b in backends:
        b.OnBasic(nodeTypename, node, leafTypeDict, names)
653 654 655


def OnSequence(nodeTypename: str, node: AsnNode, leafTypeDict: AST_Leaftypes, names: AST_Lookup) -> None:
656 657
    for b in backends:
        b.OnSequence(nodeTypename, node, leafTypeDict, names)
658 659 660


def OnSet(nodeTypename: str, node: AsnNode, leafTypeDict: AST_Leaftypes, names: AST_Lookup) -> None:
661 662
    for b in backends:
        b.OnSet(nodeTypename, node, leafTypeDict, names)
663 664 665


def OnEnumerated(nodeTypename: str, node: AsnNode, leafTypeDict: AST_Leaftypes, names: AST_Lookup) -> None:
666 667
    for b in backends:
        b.OnEnumerated(nodeTypename, node, leafTypeDict, names)
668 669 670


def OnSequenceOf(nodeTypename: str, node: AsnNode, leafTypeDict: AST_Leaftypes, names: AST_Lookup) -> None:
671 672
    for b in backends:
        b.OnSequenceOf(nodeTypename, node, leafTypeDict, names)
673 674 675


def OnSetOf(nodeTypename: str, node: AsnNode, leafTypeDict: AST_Leaftypes, names: AST_Lookup) -> None:
676 677
    for b in backends:
        b.OnSetOf(nodeTypename, node, leafTypeDict, names)
678 679 680


def OnChoice(nodeTypename: str, node: AsnNode, leafTypeDict: AST_Leaftypes, names: AST_Lookup) -> None:
681 682
    for b in backends:
        b.OnChoice(nodeTypename, node, leafTypeDict, names)
683 684 685


def OnShutdown(modelingLanguage: str, asnFile: str, maybeFVname: str) -> None:
686 687
    for b in backends:
        b.OnShutdown(modelingLanguage, asnFile, maybeFVname)