scade6_A_mapper.py 16.3 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 Union, Set, Dict  # NOQA pylint: disable=unused-import
32

33 34
from ..commonPy.utility import inform, panic
from ..commonPy.asnAST import (
35
    AsnBasicNode, AsnString, AsnEnumerated, AsnMetaMember, AsnSet,
36 37
    AsnSetOf, AsnSequence, AsnSequenceOf, AsnChoice,
    AsnSequenceOrSet, AsnSequenceOrSetOf
38
)
39
from ..commonPy import asnParser
Thanassis Tsiodras's avatar
Thanassis Tsiodras committed
40
from ..commonPy.cleanupNodes import SetOfBadTypenames
41
from ..commonPy.asnParser import AST_Leaftypes
42 43 44 45 46 47 48 49 50 51 52

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

# The file written to
g_outputFile = None

# The assigned OIDs
53
g_oid = {}  # type: Dict[str, str]
54 55 56 57 58 59 60 61

# The main OID for this module
g_mainOid = ""

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

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

# The DOM elements
g_doc = None
g_Declarations = None


Thanassis Tsiodras's avatar
Thanassis Tsiodras committed
69
def Version() -> None:
70
    print("Code generator: " + "$Id: scade612_A_mapper.py 1842 2010-03-10 14:16:42Z ttsiodras $")  # pragma: no cover
71 72


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


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


Thanassis Tsiodras's avatar
Thanassis Tsiodras committed
84
def FixupNestedStringsAndEnumerated() -> None:
85 86
    names = asnParser.g_names
    leafTypeDict = asnParser.g_leafTypeDict
87
    for nodeTypename in list(names.keys()):
88
        node = names[nodeTypename]
89
        if isinstance(node, (AsnSequence, AsnChoice, AsnSet)):
90
            for child in node._members:
91
                if isinstance(child[1], (AsnString, AsnEnumerated)):
92 93 94 95 96 97
                    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
98 99
        elif isinstance(node, (AsnSequenceOf, AsnSetOf)):
            if isinstance(node._containedType, (AsnString, AsnEnumerated)):
100 101 102 103 104 105
                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
106 107


Thanassis Tsiodras's avatar
Thanassis Tsiodras committed
108
def OnStartup(unused_modelingLanguage: str, asnFile: str, outputDir: str, unused_badTypes: SetOfBadTypenames) -> None:
109 110 111 112 113 114
    outputFilename = CleanNameAsScadeWants(os.path.basename(os.path.splitext(asnFile)[0])) + ".xscade"

    FixupNestedStringsAndEnumerated()

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

    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)


137
def RenderElements(controlString: str) -> None:
138
    if controlString.endswith(","):
139 140
        controlString = controlString[:-1]
    createdElements = {}  # type: Dict[str, Node]
141
    parent = g_Declarations  # type: Node
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
    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:
159 160
            # This is a bug in pylint - scheduled to be fixed in next release, by:
            # https://github.com/PyCQA/pylint/commit/6d31776454b5e308e4b869a1893b39083dca3146
161
            newElement = g_doc.createElement(finalElementName)
162
        if attributes:
163
            for atr in attributes:
164 165 166
                # This is a bug in pylint - scheduled to be fixed in next release, by:
                # https://github.com/PyCQA/pylint/commit/6d31776454b5e308e4b869a1893b39083dca3146
                newElement.setAttribute(atr.split('=')[0], atr.split('=')[1])  # pylint: disable=no-member
167 168 169 170
        if under is not None:
            parent = createdElements[under]
        parent.appendChild(newElement)
        createdElements[finalElementName] = newElement
171
        parent = newElement
172 173


174
def GetOID(nodeTypename: str) -> str:
175 176 177 178 179 180 181 182 183 184
    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


185
def CheckPrerequisites(nodeTypename: str) -> None:
186 187
    names = asnParser.g_names
    leafTypeDict = asnParser.g_leafTypeDict
188 189 190 191
    if nodeTypename not in g_declaredTypes:
        node = names[nodeTypename]
        leafType = leafTypeDict[nodeTypename]
        # If it is a base type,
192
        if isinstance(node, AsnBasicNode):
193 194
            OnBasic(nodeTypename, node, leafTypeDict)
        # if it is a complex type
195
        elif isinstance(node, (AsnSequence, AsnSet, AsnChoice, AsnSequenceOf, AsnSetOf, AsnEnumerated)):
196 197 198 199 200 201 202 203 204
            # make sure we have mapping instructions for the element
            mappedName = {
                'SEQUENCE': 'OnSequence',
                'SET': 'OnSet',
                'CHOICE': 'OnChoice',
                'SEQUENCEOF': 'OnSequenceOf',
                'SETOF': 'OnSetOf',
                'ENUMERATED': 'OnEnumerated'
            }
205
            if mappedName[leafType] not in list(globals().keys()):
206 207 208 209 210 211 212
                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
213
        g_declaredTypes.add(nodeTypename)
214 215


216
def HandleTypedef(nodeTypename: str) -> bool:
217
    if nodeTypename not in asnParser.g_metatypes:
218 219
        return False
    controlString = 'Type$name=%s,definition,NamedType,type,TypeRef$name=%s' % \
220
        (CleanNameAsScadeWants(nodeTypename), CleanNameAsScadeWants(asnParser.g_metatypes[nodeTypename]))
221 222 223 224
    RenderElements(controlString)
    return True


225
def OnBasic(nodeTypename: str, node: AsnBasicNode, unused_leafTypeDict: AST_Leaftypes) -> None:
226
    assert isinstance(node, AsnBasicNode)
227 228
    if nodeTypename in g_declaredTypes:
        return
229
    g_declaredTypes.add(nodeTypename)
230 231 232 233 234 235 236 237 238 239 240 241 242
    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!
243
        if not node._range:
244
            panic(("Scade612_A_mapper: string (in %s) must have a SIZE constraint inside ASN.1,\n" +  # pragma: no cover
245
                   "or else SCADE can't generate C code!") % node.Location())  # pragma: no cover
246 247 248 249 250 251 252 253 254
        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

255
    controlString += 'pragmas`Type,ed:Type$oid=!ed/%(oid)s' % {"oid": oid}
256 257 258
    RenderElements(controlString)


259 260 261
def CommonSeqSetChoice(nodeTypename: str,
                       node: Union[AsnSequence, AsnSet, AsnChoice],
                       unused_leafTypeDict: AST_Leaftypes,
262
                       isChoice: bool = False) -> None:
263 264
    if nodeTypename in g_declaredTypes:
        return
265
    g_declaredTypes.add(nodeTypename)
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
    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)


291 292 293 294
def OnSequence(nodeTypename: str, node: AsnSequenceOrSet, leafTypeDict: AST_Leaftypes) -> None:
    CommonSeqSetChoice(nodeTypename, node, leafTypeDict)  # pragma: nocover


295
def OnSet(nodeTypename: str, node: AsnSequenceOrSet, leafTypeDict: AST_Leaftypes) -> None:
296 297 298 299 300
    CommonSeqSetChoice(nodeTypename, node, leafTypeDict)  # pragma: nocover


def OnChoice(nodeTypename: str, node: AsnChoice, leafTypeDict: AST_Leaftypes) -> None:
    CommonSeqSetChoice(nodeTypename, node, leafTypeDict, isChoice=True)
301 302


303
def OnEnumerated(nodeTypename: str, node: AsnEnumerated, unused_leafTypeDict: AST_Leaftypes) -> None:
304 305
    if nodeTypename in g_declaredTypes:
        return
306
    g_declaredTypes.add(nodeTypename)
307 308 309 310 311 312 313 314 315 316 317 318
    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]),
319
                "oid": GetOID(nodeTypename + "_" + member[0]),
320 321 322 323 324 325
                "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
326
                    "oid": GetOID(nodeTypename + "_" + member[0])  # pragma: no cover
327 328 329 330 331
                }  # pragma: no cover
    controlString += 'pragmas`Type,ed:Type$oid=!ed/%(oid)s' % {"oid": oid}
    RenderElements(controlString)


332
def OnSequenceOf(nodeTypename: str, node: AsnSequenceOrSetOf, unused_leafTypeDict: AST_Leaftypes) -> None:
333 334
    if nodeTypename in g_declaredTypes:
        return
335
    g_declaredTypes.add(nodeTypename)
336 337
    if HandleTypedef(nodeTypename):
        return
338
    if not node._range:
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
        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)


357
def OnSetOf(nodeTypename: str, node: AsnSequenceOrSetOf, leafTypeDict: AST_Leaftypes) -> None:
358 359 360
    OnSequenceOf(nodeTypename, node, leafTypeDict)  # pragma: nocover


361
def OnShutdown(unused_badTypes: SetOfBadTypenames) -> None:
362
    g_outputFile.write(g_doc.toprettyxml(indent="    ", encoding="UTF-8"))
Thanassis Tsiodras's avatar
Thanassis Tsiodras committed
363 364

# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4