scade6_A_mapper.py 15.1 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#
# (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.
#
21
22
23
24
'''
Implementation of mapping ASN.1 constructs to SCADE's modeling language,
using .xscade files.
'''
25
26
27
28

import re
import os
import random
29

30
31
from xml.dom.minidom import Document, Node  # type: ignore  # NOQA  pylint: disable=unused-import
from typing import Set  # NOQA pylint: disable=unused-import
32
33

from commonPy.utility import inform, panic
34
35
36
37
from commonPy.asnAST import (
    AsnBasicNode, AsnString, AsnEnumerated, AsnMetaMember, AsnSet,
    AsnSetOf, AsnSequence, AsnSequenceOf, AsnChoice
)
38
39
40
41
42
43
44
45
46
47
48
49
import commonPy.asnParser

g_lookup = {
    "INTEGER": "int",
    "REAL": "real",
    "BOOLEAN": "bool"
}

# The file written to
g_outputFile = None

# The assigned OIDs
50
g_oid = {}  # type: Dict[str, str]
51
52
53
54
55
56
57
58

# The main OID for this module
g_mainOid = ""

# The counter for OIDs in this module
g_currOid = 0x1f00

# The types declared so far
59
g_declaredTypes = set()  # type: Set[str]
60
61
62
63
64
65
66

# The DOM elements
g_doc = None
g_Declarations = None


def Version():
67
    print("Code generator: " + "$Id: scade612_A_mapper.py 1842 2010-03-10 14:16:42Z ttsiodras $")  # pragma: no cover
68
69


70
def CleanNameAsScadeWants(name: str) -> str:
71
72
73
    return re.sub(r'[^a-zA-Z0-9_]', '_', name)


74
def RandomHex(digits: int) -> str:
75
    result = ""
76
    for _ in range(0, digits):
77
78
79
80
81
82
83
        result += random.choice('0123456789abcdef')
    return result


def FixupNestedStringsAndEnumerated():
    names = commonPy.asnParser.g_names
    leafTypeDict = commonPy.asnParser.g_leafTypeDict
84
    for nodeTypename in list(names.keys()):
85
        node = names[nodeTypename]
86
        if isinstance(node, (AsnSequence, AsnChoice, AsnSet)):
87
88
            for child in node._members:
                if isinstance(child[1], AsnString) or isinstance(child[1], AsnEnumerated):
89
90
91
92
93
94
                    newName = nodeTypename + "_" + child[0]                                                      # pragma: no cover
                    while newName in names:                                                                      # pragma: no cover
                        newName += "_t"                                                                          # pragma: no cover
                    names[newName] = child[1]                                                                    # pragma: no cover
                    leafTypeDict[newName] = 'OCTET STRING' if isinstance(child[1], AsnString) else 'ENUMERATED'  # pragma: no cover
                    child[1] = AsnMetaMember(asnFilename=child[1]._asnFilename, containedType=newName)           # pragma: no cover
95
96
        elif isinstance(node, (AsnSequenceOf, AsnSetOf)):
            if isinstance(node._containedType, (AsnString, AsnEnumerated)):
97
98
99
100
101
102
                newName = nodeTypename + "_contained"                                                                   # pragma: no cover
                while newName in names:                                                                                 # pragma: no cover
                    newName += "_t"                                                                                     # pragma: no cover
                names[newName] = node._containedType                                                                    # pragma: no cover
                leafTypeDict[newName] = 'OCTET STRING' if isinstance(node._containedType, AsnString) else 'ENUMERATED'  # pragma: no cover
                node._containedType = newName                                                                           # pragma: no cover
103
104


105
def OnStartup(unused_modelingLanguage, asnFile: str, outputDir: str, unused_badTypes) -> None:
106
107
108
109
110
111
    outputFilename = CleanNameAsScadeWants(os.path.basename(os.path.splitext(asnFile)[0])) + ".xscade"

    FixupNestedStringsAndEnumerated()

    inform("Scade612_A_mapper: Creating file '%s'...", outputFilename)
    global g_outputFile
112
    g_outputFile = open(outputDir + outputFilename, 'wb')
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133

    global g_mainOid
    g_mainOid = "/" + RandomHex(4) + "/" + RandomHex(3) + "/"

    global g_currOid
    g_currOid = 0x1f00

    global g_doc
    g_doc = Document()

    File = g_doc.createElement("File")
    File.setAttribute("xmlns", "http://www.esterel-technologies.com/ns/scade/3")
    File.setAttribute("xmlns:ed", "http://www.esterel-technologies.com/ns/scade/pragmas/editor/2")
    File.setAttribute("xmlns:kcg", "http://www.esterel-technologies.com/ns/scade/pragmas/codegen/1")
    g_doc.appendChild(File)

    global g_Declarations
    g_Declarations = g_doc.createElement("declarations")
    File.appendChild(g_Declarations)


134
def RenderElements(controlString: str):
135
    if controlString.endswith(","):
136
137
        controlString = controlString[:-1]
    createdElements = {}  # type: Dict[str, Node]
138
    parent = g_Declarations  # type: Node
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
    for elem in controlString.split(","):
        if '`' in elem:
            element = elem.split("`")[0]
            under = elem.split("`")[1]
        else:
            element = elem
            under = None
        if '$' in element:
            data = element.split("$")
            finalElementName = data[0]
            attributes = data[1:]
        else:
            finalElementName = element
            attributes = []
        if finalElementName.startswith("TEXT"):
            newElement = g_doc.createTextNode(finalElementName[4:])
        else:
            newElement = g_doc.createElement(finalElementName)
        if attributes != []:
            for atr in attributes:
                newElement.setAttribute(atr.split('=')[0], atr.split('=')[1])
        if under is not None:
            parent = createdElements[under]
        parent.appendChild(newElement)
        createdElements[finalElementName] = newElement
164
        parent = newElement  # pylint: disable=redefined-variable-type
165
166
167
168
169
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


def GetOID(nodeTypename):
    global g_currOid
    if nodeTypename not in g_oid:
        oid = hex(g_currOid)[2:] + g_mainOid + RandomHex(12)
        g_oid[nodeTypename] = oid
        g_currOid += 1
    else:                          # pragma: no cover
        oid = g_oid[nodeTypename]  # pragma: no cover
    return oid


def CheckPrerequisites(nodeTypename):
    names = commonPy.asnParser.g_names
    leafTypeDict = commonPy.asnParser.g_leafTypeDict
    if nodeTypename not in g_declaredTypes:
        node = names[nodeTypename]
        leafType = leafTypeDict[nodeTypename]
        # If it is a base type,
        if leafType in ['BOOLEAN', 'INTEGER', 'REAL', 'OCTET STRING']:
            OnBasic(nodeTypename, node, leafTypeDict)
        # if it is a complex type
        elif leafType in ['SEQUENCE', 'SET', 'CHOICE', 'SEQUENCEOF', 'SETOF', 'ENUMERATED']:
            # make sure we have mapping instructions for the element
            mappedName = {
                'SEQUENCE': 'OnSequence',
                'SET': 'OnSet',
                'CHOICE': 'OnChoice',
                'SEQUENCEOF': 'OnSequenceOf',
                'SETOF': 'OnSetOf',
                'ENUMERATED': 'OnEnumerated'
            }
198
            if mappedName[leafType] not in list(globals().keys()):
199
200
201
202
203
204
205
                panic("ASN.1 grammar contains %s but no %s section found in the backend! Contact Semantix." %  # pragma: no cover
                      (nodeTypename, mappedName[leafType]))  # pragma: no cover
            processor = globals()[mappedName[leafType]]
            processor(nodeTypename, node, leafTypeDict)
        # what type is this?
        else:  # pragma: no cover
            panic("Unexpected type of element: %s" % leafTypeDict[nodeTypename])  # pragma: no cover
206
        g_declaredTypes.add(nodeTypename)
207
208


209
def HandleTypedef(nodeTypename: str) -> bool:
210
211
212
213
214
215
216
217
218
    if nodeTypename not in commonPy.asnParser.g_metatypes:
        return False
    controlString = 'Type$name=%s,definition,NamedType,type,TypeRef$name=%s' % \
        (CleanNameAsScadeWants(nodeTypename), CleanNameAsScadeWants(commonPy.asnParser.g_metatypes[nodeTypename]))
    RenderElements(controlString)
    return True


def OnBasic(nodeTypename, node, unused_leafTypeDict):
219
    assert isinstance(node, AsnBasicNode)
220
221
    if nodeTypename in g_declaredTypes:
        return
222
    g_declaredTypes.add(nodeTypename)
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
    if HandleTypedef(nodeTypename):
        return
    oid = GetOID(nodeTypename)

    # Make the type name SCADE-compliant
    nodeTypename = CleanNameAsScadeWants(nodeTypename)

    controlString = 'Type$name=%(nodeTypename)s,definition,' % {"nodeTypename": nodeTypename}

    # Check to see the real leaf type of this node
    if isinstance(node, AsnString):
        # An OCTET STRING must always include a range definition,
        # otherwise SCADE will not be able to create C code!
        if node._range == []:
            panic(("Scade612_A_mapper: string (in %s) must have a SIZE constraint inside ASN.1,\n" +  # pragma: no cover
238
                   "or else SCADE can't generate C code!") % node.Location())  # pragma: no cover
239
240
241
242
243
244
245
246
247
        controlString += 'Table,type,NamedType,type,TypeRef$name=char,size`Table,ConstValue$value=%d,' % node._range[-1]
    else:
        # For the rest of the simple types, use the lookup table defined in g_lookup
        realLeafType = node._leafType
        try:
            controlString += 'NamedType,type,TypeRef$name=%s,' % g_lookup[realLeafType]
        except KeyError:  # pragma: no cover
            panic("Scade612_A_mapper: Unsupported literal: %s (%s)\n" % (realLeafType, node.Location()))  # pragma: no cover

248
    controlString += 'pragmas`Type,ed:Type$oid=!ed/%(oid)s' % {"oid": oid}
249
250
251
252
253
254
    RenderElements(controlString)


def OnSequence(nodeTypename, node, unused_leafTypeDict, isChoice=False):
    if nodeTypename in g_declaredTypes:
        return
255
    g_declaredTypes.add(nodeTypename)
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
    if HandleTypedef(nodeTypename):
        return
    oid = GetOID(nodeTypename)
    controlString = 'Type$name=%s,definition,Struct,fields,' % CleanNameAsScadeWants(nodeTypename)
    if isChoice:
        controlString += 'Field$name=choiceIdx`fields,type,NamedType,type,TypeRef$name=int,'
    for child in node._members:
        realLeafType = child[1]._leafType
        controlString += 'Field$name=%s`fields,' % CleanNameAsScadeWants(child[0])
        if isinstance(node, AsnString):
            controlString += 'Table,type,NamedType,type,TypeRef$name=char,size`Table,ConstValue$value=%d,' % node._range[-1]  # pragma: no cover because of FixupNestedStringsAndEnumerated
        elif isinstance(child[1], AsnBasicNode):
            controlString += 'type,NamedType,type,TypeRef$name=%s,' % g_lookup[realLeafType]
        elif isinstance(child[1], AsnEnumerated):
            panic("Scade612_A_mapper: Don't use naked ENUMERATED in CHOICE/SEQUENCE, SCADE doesn't support them (%s)!\n" % node.Location())  # pragma: no cover because of FixupNestedStringsAndEnumerated
        elif isinstance(child[1], AsnMetaMember):
            CheckPrerequisites(child[1]._containedType)
            controlString += 'type,NamedType,type,TypeRef$name=%s,' % CleanNameAsScadeWants(child[1]._containedType)
        else:  # pragma: no cover
            panic("Scade612_A_mapper: Unexpected member %s (%s)!\n" % (child[0], node.Location()))  # pragma: no cover
        controlString += 'pragmas`Field,ed:Field$oid=!ed/%s,' % GetOID(nodeTypename + "_" + child[0])
    controlString += 'pragmas`Type,ed:Type$oid=!ed/%s,' % oid
    RenderElements(controlString)


def OnSet(nodeTypename, node, leafTypeDict):
    OnSequence(nodeTypename, node, leafTypeDict)  # pragma: nocover


def OnEnumerated(nodeTypename, node, unused_leafTypeDict):
    if nodeTypename in g_declaredTypes:
        return
288
    g_declaredTypes.add(nodeTypename)
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
    if HandleTypedef(nodeTypename):
        return
    oid = GetOID(nodeTypename)
    controlString = 'Type$name=%(nodeTypename)s,definition,Enum,values,' % {"nodeTypename": CleanNameAsScadeWants(nodeTypename)}
    for member in node._members:
        # member[0] is the enumerant name
        # member[1] is the integer value used (or None)
        if member[1] is not None:
            # Alain, what do I need to do with pragmas here?
            # g_outputFile.write("\t    %s[%s]" % (CleanNameAsScadeWants(member[0]), member[1]))
            controlString += 'Value$name=%(enumerant)s`values,pragmas,ed:Value$oid=%(oid)s,kcg:Pragma`pragmas,TEXTenum_val %(value)s,' % {
                "enumerant": CleanNameAsScadeWants(member[0]),
                "oid": GetOID(nodeTypename+"_"+member[0]),
                "value": member[1]
            }
        else:  # pragma: no cover
            controlString += 'Value$name=%(enumerant)s`values,pragmas,ed:Value$oid=%(oid)s,' % \
                {  # pragma: no cover
                    "enumerant": CleanNameAsScadeWants(member[0]),  # pragma: no cover
                    "oid": GetOID(nodeTypename+"_"+member[0])  # pragma: no cover
                }  # pragma: no cover
    controlString += 'pragmas`Type,ed:Type$oid=!ed/%(oid)s' % {"oid": oid}
    RenderElements(controlString)


def OnSequenceOf(nodeTypename, node, unused_leafTypeDict):
    if nodeTypename in g_declaredTypes:
        return
317
    g_declaredTypes.add(nodeTypename)
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
    if HandleTypedef(nodeTypename):
        return
    if node._range == []:
        panic("Scade612_A_mapper: must have a SIZE constraint or else SCADE can't generate C code (in %s)!\n" %  # pragma: no cover
              node.Location())  # pragma: no cover
    oid = GetOID(nodeTypename)
    controlString = 'Type$name=%s,definition,Table,type,NamedType,type,' % CleanNameAsScadeWants(nodeTypename)
    if isinstance(node._containedType, AsnBasicNode):
        controlString += 'TypeRef$name=%s,' % g_lookup[node._containedType._leafType]
    elif isinstance(node._containedType, AsnEnumerated):
        panic("Scade612_A_mapper: Don't use naked ENUMERATED in SEQUENCEOF, SCADE doesn't support them (%s)!\n" % node.Location())  # pragma: no cover because of FixupNestedStringsAndEnumerated
    elif isinstance(node._containedType, str):
        CheckPrerequisites(node._containedType)
        controlString += 'TypeRef$name=%s,' % CleanNameAsScadeWants(node._containedType)
    else:  # pragma: no cover
        panic("Scade612_A_mapper: Unexpected contained %s (%s)!\n" % (str(node._containedType), node.Location()))  # pragma: no cover
    controlString += 'size`Table,ConstValue$value=%s,' % node._range[-1]
    controlString += 'pragmas`Type,ed:Type$oid=!ed/%s' % oid
    RenderElements(controlString)


def OnSetOf(nodeTypename, node, leafTypeDict):
    OnSequenceOf(nodeTypename, node, leafTypeDict)  # pragma: nocover


def OnChoice(nodeTypename, node, leafTypeDict):
    OnSequence(nodeTypename, node, leafTypeDict, isChoice=True)


347
def OnShutdown(unused_badTypes):
348
    g_outputFile.write(g_doc.toprettyxml(indent="    ", encoding="UTF-8"))