ogParser.py 238 KB
Newer Older
Maxime Perrotin's avatar
Maxime Perrotin committed
1 2 3 4
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
5
    OpenGEODE SDL parser
Maxime Perrotin's avatar
Maxime Perrotin committed
6 7 8 9 10 11 12 13 14 15 16 17 18

    This library builds the SDL AST (described in ogAST.py)
    The AST can then be used to build SDL backends such as the
    diagram editor (placing symbols in a graphical canvas for editition)
    or code generators, etc.

    The AST build is based on the ANTLR-grammar and generated lexer and parser
    (the grammar is in the file sdl92.g and requires antlr 3.1.3 for Python
    to be compiled and used).

    During the build of the AST this library makes a number of semantic
    checks on the SDL input mode.

19
    Copyright (c) 2012-2020 European Space Agency
Maxime Perrotin's avatar
Maxime Perrotin committed
20 21 22 23 24 25 26 27 28 29

    Designed and implemented by Maxime Perrotin

    Contact: maxime.perrotin@esa.int
"""

__author__ = 'Maxime Perrotin'

import sys
import os
dbarbera's avatar
dbarbera committed
30
import math
31
import operator
Maxime Perrotin's avatar
Maxime Perrotin committed
32 33
import logging
import traceback
Maxime Perrotin's avatar
Maxime Perrotin committed
34
from functools import partial
35
import codecs
Maxime Perrotin's avatar
Maxime Perrotin committed
36
from typing import Dict, Tuple
37
from inspect import currentframe, getframeinfo
38
import binascii
39
from textwrap import dedent
Maxime Perrotin's avatar
Maxime Perrotin committed
40
from itertools import chain, permutations, combinations
41
from collections import defaultdict, Counter
Maxime Perrotin's avatar
Maxime Perrotin committed
42

Maxime Perrotin's avatar
Maxime Perrotin committed
43
import antlr3
Maxime Perrotin's avatar
Maxime Perrotin committed
44
from antlr3 import tree
Maxime Perrotin's avatar
Maxime Perrotin committed
45

Maxime Perrotin's avatar
Maxime Perrotin committed
46
from . import sdl92Lexer as lexer
47
from . import sdl92Parser
Maxime Perrotin's avatar
Maxime Perrotin committed
48

Maxime Perrotin's avatar
Maxime Perrotin committed
49
from . import ogAST
Maxime Perrotin's avatar
Maxime Perrotin committed
50
from .Asn1scc import parse_asn1, ASN1, create_choice_determinant_types
Maxime Perrotin's avatar
Maxime Perrotin committed
51 52 53

LOG = logging.getLogger(__name__)

Maxime Perrotin's avatar
Maxime Perrotin committed
54
EXPR_NODE: Dict[int, ogAST.Expression] = {
55
    lexer.PLUS:     ogAST.ExprPlus,
56
    lexer.ASTERISK: ogAST.ExprMul,
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
    lexer.IMPLIES:  ogAST.ExprImplies,
    lexer.DASH:     ogAST.ExprMinus,
    lexer.OR:       ogAST.ExprOr,
    lexer.AND:      ogAST.ExprAnd,
    lexer.XOR:      ogAST.ExprXor,
    lexer.EQ:       ogAST.ExprEq,
    lexer.NEQ:      ogAST.ExprNeq,
    lexer.GT:       ogAST.ExprGt,
    lexer.GE:       ogAST.ExprGe,
    lexer.LT:       ogAST.ExprLt,
    lexer.LE:       ogAST.ExprLe,
    lexer.DIV:      ogAST.ExprDiv,
    lexer.MOD:      ogAST.ExprMod,
    lexer.APPEND:   ogAST.ExprAppend,
    lexer.IN:       ogAST.ExprIn,
    lexer.REM:      ogAST.ExprRem,
    lexer.NOT:      ogAST.ExprNot,
    lexer.NEG:      ogAST.ExprNeg,
    lexer.PRIMARY:  ogAST.Primary,
Maxime Perrotin's avatar
Maxime Perrotin committed
76
}
Maxime Perrotin's avatar
Maxime Perrotin committed
77 78 79 80

# Insert current path in the search list for importing modules
sys.path.insert(0, '.')

Maxime Perrotin's avatar
Maxime Perrotin committed
81
DV = None  # type: module
Maxime Perrotin's avatar
Maxime Perrotin committed
82 83 84 85

# Code generator backends may need some intemediate variables to process
# expressions. For convenience and to avoid multiple pass parsing, the parser
# tries to guess where they may be useful, and adds a hint in the AST.
Maxime Perrotin's avatar
Maxime Perrotin committed
86
TMPVAR = 0  # type: int
Maxime Perrotin's avatar
Maxime Perrotin committed
87 88

# ASN.1 types used to support the signature of special operators
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
INTEGER      = type('IntegerType',    (object,), {'kind': 'IntegerType',
                                                  'Min' : str(-(2 ** 63)),
                                                  'Max' : str(2 ** 63 - 1)})
UNSIGNED     = type('IntegerType',    (object,), {'kind': 'IntegerType',
                                                  'Min' : "0",
                                                  'Max' : str(2 ** 64 - 1)})
INT32        = type('Integer32Type',  (object,), {'kind': 'Integer32Type',
                                                  'Min' : '-2147483648',
                                                  'Max' : '2147483647'})
NUMERICAL    = type('NumericalType',  (object,), {'kind': 'Numerical'})
TIMER        = type('TimerType',      (object,), {'kind': 'TimerType'})
REAL         = type('RealType',       (object,), {'kind': 'RealType',
                                                  'Min' : str(1e-308),
                                                  'Max' : str(1e308)})
LIST         = type('ListType',       (object,), {'kind': 'ListType'})
ANY_TYPE     = type('AnyType',        (object,), {'kind': 'AnyType'})
CHOICE       = type('ChoiceType',     (object,), {'kind': 'ChoiceType'})
BOOLEAN      = type('BooleanType',    (object,), {'kind': 'BooleanType'})
RAWSTRING    = type('RawString',      (object,), {'kind': 'StandardStringType',
                                                  'Min' : '0',
                                                  'Max' : '255'})
OCTETSTRING  = type('OctetString',    (object,), {'kind': 'OctetStringType'})
ENUMERATED   = type('EnumeratedType', (object,), {'kind': 'EnumeratedType'})
UNKNOWN_TYPE = type('UnknownType',    (object,), {'kind': 'UnknownType'})
Maxime Perrotin's avatar
Maxime Perrotin committed
113

114
SPECIAL_OPERATORS = {
115 116 117 118 119 120 121 122 123 124
    'abs'        : [{'type': NUMERICAL,  'direction': 'in'}],
    'ceil'       : [{'type': REAL,       'direction': 'in'}],
    'cos'        : [{'type': REAL,       'direction': 'in'}],
    'fix'        : [{'type': NUMERICAL,  'direction': 'in'}],
    'float'      : [{'type': NUMERICAL,  'direction': 'in'}],
    'floor'      : [{'type': REAL,       'direction': 'in'}],
    'length'     : [{'type': LIST,       'direction': 'in'}],
    'num'        : [{'type': ENUMERATED, 'direction': 'in'}],
    'power'      : [
                    {'type': NUMERICAL,  'direction': 'in'},
125
                    {'type': NUMERICAL,  'direction': 'in'}
126 127
                   ],
    'present'    : [{'type': CHOICE,     'direction': 'in'}],
128
    'exist'      : [{'type': ANY_TYPE,   'direction': 'in'}],
129 130 131 132 133 134 135 136 137 138 139
    'reset_timer': [{'type': TIMER,      'direction': 'in'}],
    'round'      : [{'type': REAL,       'direction': 'in'}],
    'set_timer'  : [
                    {'type': UNSIGNED,   'direction': 'in'},
                    {'type': TIMER,      'direction': 'in'}
                   ],
    'sin'        : [{'type': REAL,       'direction': 'in'}],
    'sqrt'       : [{'type': REAL,       'direction': 'in'}],
    'trunc'      : [{'type': REAL,       'direction': 'in'}],
    'write'      : [{'type': ANY_TYPE,   'direction': 'in'}],
    'writeln'    : [{'type': ANY_TYPE,   'direction': 'in'}],
140 141 142 143 144 145 146 147
    'to_selector': [  #  to convert an enum to a choice discriminant
                    {'type': ENUMERATED, 'direction': 'in'},
                    {'type': ANY_TYPE,   'direction': 'in'}
                   ],
    'to_enum'    : [  #  to convert a choice discriminant to an enum
                    {'type': ENUMERATED, 'direction': 'in'},
                    {'type': ANY_TYPE,   'direction': 'in'}
                   ],
148 149 150 151
    'val'    : [  #  to convert a number to an enumerated type value
                    {'type': UNSIGNED,   'direction': 'in'},  # eg. 1
                    {'type': ANY_TYPE,   'direction': 'in'}   # eg. MyChoice
                   ],
152 153 154 155 156
    'choice_to_int' : [  #  to return the value of a numerical choice item
                    {'type': CHOICE,     'direction': 'in'},  # choice variable
                    {'type': NUMERICAL,  'direction': 'in'}   # default value
                   ],

157
}
Maxime Perrotin's avatar
Maxime Perrotin committed
158 159 160 161

# Container to keep a list of types mapped from ANTLR Tokens
# (Used with singledispatch/visitor pattern)
ANTLR_TOKEN_TYPES = {a: type(a, (antlr3.tree.CommonTree,), {})
Maxime Perrotin's avatar
Maxime Perrotin committed
162
                    for a, b in lexer.__dict__.items() if type(b) == int}
Maxime Perrotin's avatar
Maxime Perrotin committed
163 164 165 166 167 168 169 170 171 172 173 174


# Shortcut to create a new referenced ASN.1 type
new_ref_type = lambda refname: \
        type(str(refname), (object,),
                {'kind': 'ReferenceType',
                 'ReferencedTypeName': refname.replace('_', '-')})

# Shortcut to return a type name (Reference name or basic type)
type_name = lambda t: \
                t.kind if t.kind != 'ReferenceType' else t.ReferencedTypeName

175 176 177
# return the line number of this python module, useful for debugging
lineno = lambda : currentframe().f_back.f_lineno

178 179 180 181 182
# user may create SDL (non-asn1) types with the newtype keyword
# they are stored in a dedicated dictionary with the same structure
# as the ASN1SCC generated python AST
USER_DEFINED_TYPES = dict()

183

184 185 186 187 188 189
def types():
    ''' Return all ASN.1 and user defined types '''
    ret = getattr(DV, 'types', {}).copy()
    ret.update(USER_DEFINED_TYPES)
    return ret
#types = lambda: getattr(DV, 'types', {}) or USER_DEFINED_TYPES
Maxime Perrotin's avatar
Maxime Perrotin committed
190 191


192
def set_global_DV(asn1_filenames):
Maxime Perrotin's avatar
Maxime Perrotin committed
193
    # type: (List[str]) -> None
194 195
    ''' Call ASN.1 parser and set the global dataview AST entry (DV) '''
    global DV
196 197 198 199
    if '--toC' in sys.argv:
        rename_policy = ASN1.RenameOnlyConflicting
    else:
        rename_policy = ASN1.NoRename
200 201 202
    try:
        DV = parse_asn1(tuple(asn1_filenames),
                        ast_version=ASN1.UniqueEnumeratedNames,
203
                        rename_policy=rename_policy,
Maxime Perrotin's avatar
Maxime Perrotin committed
204 205
                        flags=[ASN1.AstOnly],
                        pretty_print=True)
206 207 208
        # Create new types corresponding to CHOICE determinants as enum
        choice_selectors = create_choice_determinant_types (DV)
        DV.types.update(choice_selectors)
209 210 211 212 213 214 215 216 217
    except (ImportError, NameError) as err:
        # Can happen if DataView.py is not there
        LOG.error('Error loading ASN.1 model')
        LOG.debug(str(err))
    except TypeError as err:
        LOG.debug(traceback.format_exc())
        raise TypeError('ASN.1 compiler failed - {}'.format(str(err)))


Maxime Perrotin's avatar
Maxime Perrotin committed
218
def substring_range(substring: ogAST.PrimSubstring) -> Tuple[str, str]:
219 220 221 222 223 224 225
    ''' Return the range of a substring '''
    left, right = substring.value[1]['substring']
    left_bty = find_basic_type(left.exprType)
    right_bty = find_basic_type(right.exprType)
    return left_bty.Min, right_bty.Max


Maxime Perrotin's avatar
Maxime Perrotin committed
226
def is_number(basic_ty) -> bool:
Maxime Perrotin's avatar
Maxime Perrotin committed
227 228 229 230
    ''' Return true if basic type is a raw number (i.e. not a variable) '''
    return basic_ty.__name__ in ('Universal_Integer', 'PrReal')


Maxime Perrotin's avatar
Maxime Perrotin committed
231
def is_integer(ty) -> bool:
dbarbera's avatar
dbarbera committed
232 233 234 235 236 237 238
    ''' Return true if a type is an Integer Type '''
    return find_basic_type(ty).kind in (
        'IntegerType',
        'Integer32Type'
    )


Maxime Perrotin's avatar
Maxime Perrotin committed
239
def is_real(ty) -> bool:
240 241 242 243
    ''' Return true if a type is a Real Type '''
    return find_basic_type(ty).kind == 'RealType'


Maxime Perrotin's avatar
Maxime Perrotin committed
244
def is_numeric(ty) -> bool:
dbarbera's avatar
dbarbera committed
245
    ''' Return true if a type is a Numeric Type '''
246 247 248 249 250 251 252 253
    return find_basic_type(ty).kind in (
        'IntegerType',
        'Integer32Type',
        'Numerical',
        'RealType'
    )


Maxime Perrotin's avatar
Maxime Perrotin committed
254
def is_boolean(ty) -> bool:
255 256 257 258
    ''' Return true if a type is a Boolean Type '''
    return find_basic_type(ty).kind == 'BooleanType'


Maxime Perrotin's avatar
Maxime Perrotin committed
259
def is_null(ty) -> bool:
260 261 262 263
    ''' Return true if a type is a NULL Type '''
    return find_basic_type(ty).kind == 'NullType'


Maxime Perrotin's avatar
Maxime Perrotin committed
264
def is_string(ty) -> bool:
dbarbera's avatar
dbarbera committed
265 266 267
    ''' Return true if a type is a String Type '''
    return find_basic_type(ty).kind in (
        'StandardStringType',
268 269
        'OctetStringType',
        'StringType'
dbarbera's avatar
dbarbera committed
270 271 272
    )


Maxime Perrotin's avatar
Maxime Perrotin committed
273
def is_sequenceof(ty) -> bool:
274 275 276 277
    ''' Return true if a type is a SequenceOf Type '''
    return find_basic_type(ty).kind == 'SequenceOfType'


Maxime Perrotin's avatar
Maxime Perrotin committed
278
def is_list(ty) -> bool:
279 280 281 282
    ''' Return true if a type is a List Type '''
    return is_string(ty) or is_sequenceof(ty) or ty == LIST


Maxime Perrotin's avatar
Maxime Perrotin committed
283
def is_enumerated(ty) -> bool:
284 285 286 287
    ''' Return true if a type is an Enumerated Type '''
    return find_basic_type(ty).kind == 'EnumeratedType' or ty == ENUMERATED


Maxime Perrotin's avatar
Maxime Perrotin committed
288
def is_sequence(ty) -> bool:
289 290 291 292
    ''' Return true if a type is a Sequence Type '''
    return find_basic_type(ty).kind == 'SequenceType'


Maxime Perrotin's avatar
Maxime Perrotin committed
293
def is_choice(ty) -> bool:
294 295 296 297
    ''' Return true if a type is a Choice Type '''
    return find_basic_type(ty).kind == 'ChoiceType' or ty == CHOICE


Maxime Perrotin's avatar
Maxime Perrotin committed
298
def is_timer(ty) -> bool:
299 300 301 302
    ''' Return true if a type is a Timer Type '''
    return find_basic_type(ty).kind == 'TimerType'


Maxime Perrotin's avatar
Maxime Perrotin committed
303
def sdl_to_asn1(sort: str):
Maxime Perrotin's avatar
Maxime Perrotin committed
304 305 306 307
    '''
        Convert case insensitive type reference to the actual type as found
        in the ASN.1 datamodel
    '''
Maxime Perrotin's avatar
Maxime Perrotin committed
308
    for asn1_type in types().keys():
Maxime Perrotin's avatar
Maxime Perrotin committed
309 310 311 312 313 314 315 316 317 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 347 348 349 350 351 352 353 354 355 356
        if sort.replace('_', '-').lower() == asn1_type.lower():
            break
    else:
        raise TypeError('Type {} not found in ASN.1 model'.format(sort))
    return new_ref_type(asn1_type)


def node_filename(node):
    ''' Return the filename associated to the stream of this node '''
    parent = node
    while parent:
        try:
            return parent.getToken().getInputStream().fileName
        except AttributeError:
            parent = parent.getParent()
    return None


def token_stream(node):
    '''
        Return the token stream associated to a tree node
        It is set at the root of the tree by the parser
    '''
    parent = node
    while parent:
        try:
            return parent.token_stream
        except AttributeError:
            parent = parent.getParent()


def signals_in_system(ast):
    ''' Recursively find signal definitions in a nested SDL model '''
    all_signals = []
    for block in ast.blocks:
        all_signals.extend(signals_in_system(block))
    all_signals.extend(ast.signals)
    return all_signals


def find_process_declaration(ast, process_name):
    ''' Recursively search for a process declaration in a nested SDL model '''
    for block in ast.blocks:
        result = find_process_declaration(block, process_name)
        if result:
            return result
    try:
        for process in ast.processes:
357 358 359
            if process.processName.lower() == process_name.lower():
                return process
            elif process.instance_of_name.lower() == process_name.lower():
Maxime Perrotin's avatar
Maxime Perrotin committed
360 361 362 363 364 365 366 367 368 369 370 371 372
                return process
    except AttributeError:
        return None
    return None


def valid_output(scope):
    '''
        Yields the output, procedures, and operators names,
        that is all the elements that can be valid in an OUTPUT symbol
        (does not mean it IS valid - caller still has to check it)
    '''
    for out_sig in scope.output_signals:
373
        yield out_sig['name']
Maxime Perrotin's avatar
Maxime Perrotin committed
374
    for proc in scope.procedures:
375
        yield proc.inputString
Maxime Perrotin's avatar
Maxime Perrotin committed
376
    for special_op in SPECIAL_OPERATORS:
377
        yield special_op
Maxime Perrotin's avatar
Maxime Perrotin committed
378 379 380 381 382 383


def get_interfaces(ast, process_name):
    '''
        Search for the list of input and output signals (async PI/RI)
        and procedures (sync RI) of a process in a given top-level AST
384 385 386
        process_name can be the name of a process type, in which case the
        interfaces can only be found by looking at an instance of the type
        that is actually connected in the system
Maxime Perrotin's avatar
Maxime Perrotin committed
387
    '''
388
    all_signals, async_signals, errors = [], [], set()
389
    system = ast
Maxime Perrotin's avatar
Maxime Perrotin committed
390 391 392 393 394 395 396 397 398 399 400 401

    # Move up to the system level, in case process is nested in a block
    # and not defined at root level as it is the case when it is referenced
    while hasattr(system, 'parent'):
        system = system.parent

    # If we are at AST level, check in all systems, otherwise in current one
    iterator = ast.systems if hasattr(ast, 'systems') else (system,)

    for system in iterator:
        all_signals.extend(signals_in_system(system))
        process_ref = find_process_declaration(system, process_name)
402 403
        # Update process name with the name of the instance if we are parsing
        # a process type.
Maxime Perrotin's avatar
Maxime Perrotin committed
404
        if process_ref:
405
            process_name = process_ref.processName
406 407
            # Go to the block where the process is defined
            process_parent = process_ref.parent
Maxime Perrotin's avatar
Maxime Perrotin committed
408 409 410
            break
    else:
        if isinstance(ast, ogAST.Block):
411
            process_parent = ast
Maxime Perrotin's avatar
Maxime Perrotin committed
412 413
        else:
            raise TypeError('Process ' + process_name +
414
                        ' is defined but not declared in a system')
Maxime Perrotin's avatar
Maxime Perrotin committed
415
    # Find in and out signals names using the signalroutes
416
    undeclared_signals = []
417 418
    for each in process_parent.signalroutes:
        for route in each['routes']:
Maxime Perrotin's avatar
Maxime Perrotin committed
419 420 421 422 423 424 425 426
            if route['source'] == process_name:
                direction = 'out'
            elif route['dest'] == process_name:
                direction = 'in'
            else:
                continue
            for sig_id in route['signals']:
                # Copy the signal to the result dict
427 428
                try:
                    found, = [dict(sig) for sig in all_signals
429
                              if sig['name'].lower() == sig_id.lower()]
430 431 432
                    found['direction'] = direction
                    async_signals.append(found)
                except ValueError:
433
                    undeclared_signals.append(sig_id)
434
                except (KeyError, AttributeError) as err:
435 436 437 438 439 440
                    # Exceptions raised if a signal is not defined, i.e. there
                    # if an empty signal entry in the list. This can happen
                    # if the name of the signal is a reserved keyword, such as
                    # "stop", "reset"...
                    errors.add('Check the names of your signals against'
                               ' reserved keywords')
441
    if undeclared_signals:
442 443
        errors.add('Missing declaration for signal(s) {}'
                   .format(', '.join(undeclared_signals)))
444
    return async_signals, system.procedures, errors
Maxime Perrotin's avatar
Maxime Perrotin committed
445 446 447 448


def get_input_string(root):
    ''' Return the input string of a tree node '''
449 450 451 452 453 454 455
    try:
       res = token_stream(root).toString(root.getTokenStartIndex(),
                root.getTokenStopIndex())
       return res
    except AttributeError as err:
        # in case there is no token_strem(root)
        return ""
Maxime Perrotin's avatar
Maxime Perrotin committed
456 457


Maxime Perrotin's avatar
Maxime Perrotin committed
458
def error(root, msg: str) -> str:
dbarbera's avatar
dbarbera committed
459
    ''' Return an error message '''
460
    return '{} - "{}"'.format(msg, get_input_string(root))
dbarbera's avatar
dbarbera committed
461 462


Maxime Perrotin's avatar
Maxime Perrotin committed
463
def warning(root, msg: str) -> str:
dbarbera's avatar
dbarbera committed
464
    ''' Return a warning message '''
465
    return '{} - "{}"'.format(msg, get_input_string(root))
dbarbera's avatar
dbarbera committed
466 467


468
def check_syntax(node: antlr3.tree.CommonTree,
Maxime Perrotin's avatar
Maxime Perrotin committed
469 470 471
                 recursive:bool = False,
                 filename:str = "",
                 input_string:str = "") -> None:
472 473 474 475 476 477 478 479 480
    ''' Check if the ANTLR node is valid, otherwise raise an excption,
    meaning there is a syntax error, and report the string that could not be
    parsed '''
    def check(root: antlr3.tree.CommonTree,
              parent: antlr3.tree.CommonTree,
              rec: bool) -> None:
        if rec:
            for child in root.getChildren():
                check(child, parent, rec)
Maxime Perrotin's avatar
Maxime Perrotin committed
481 482 483
        if isinstance(root, antlr3.tree.CommonErrorNode):  #  in tree.py
            exc = root.trappedException    # in exceptions.py
            token = exc.token
484 485 486
            token_str = token.text
            line = token.line
            pos = token.charPositionInLine + 1
Maxime Perrotin's avatar
Maxime Perrotin committed
487 488
            if filename:
                text = open(filename, 'r').readlines()
489
            else:
Maxime Perrotin's avatar
Maxime Perrotin committed
490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508
                text = input_string.split('\n')
            if isinstance(exc, antlr3.exceptions.MismatchedTokenException):
                err_msg = f'{" "*(pos-1)}^__ Expected ' \
                          f'"{lexer.tokenNamesMap[exc.expecting]}", ' \
                          f'got "{token_str}"'
            else:
                err_msg = f'{" "*(pos-1)}^__ Unexpected "{token_str}"'
            if len(text) >= line:
                syntax_error = f'{text[line-1]}\n{err_msg}'
            else:
                LOG.error("Unrecoverable Error with text input")
                syntax_error = f'{err_msg} (line {line} offset {pos}) {len(text)} {text} {str(exc)}'
            se_exc          = SyntaxError(syntax_error)
            se_exc.filename = filename
            se_exc.lineno   = line
            se_exc.offset   = pos
            se_exc.text     = syntax_error
            raise se_exc
            #raise SyntaxError(syntax_error)
509 510 511
    check(node, parent=node, rec=recursive)


Maxime Perrotin's avatar
Maxime Perrotin committed
512
def tmp() -> int:
dbarbera's avatar
dbarbera committed
513 514 515 516 517 518 519
    ''' Return a temporary variable name '''
    global TMPVAR
    varname = TMPVAR
    TMPVAR += 1
    return varname


Maxime Perrotin's avatar
Maxime Perrotin committed
520 521 522 523 524 525 526 527 528 529 530 531 532 533 534
def get_state_list(process_root):
    ''' Return the list of states of a process '''
    # 1) get all STATE statements
    states = (child for child in process_root.getChildren()
            if child.type == lexer.STATE)
    # 2) keep only the ones containing a STATELIST token (i.e. no ASTERISK)
    relevant = (child for state in states for child in state.getChildren()
            if child.type == lexer.STATELIST)
    # 3) extract the state list from each of them
    state_list = [s.text.lower() for r in relevant for s in r.getChildren()]
    # state_list.append('START')
    # 4) create a set to remove duplicates
    return set(state_list)


535
def find_basic_type(a_type, pool=None):
Maxime Perrotin's avatar
Maxime Perrotin committed
536 537
    ''' Return the ASN.1 basic type of a_type '''
    basic_type = a_type or UNKNOWN_TYPE
538
    pool = pool or types()
Maxime Perrotin's avatar
Maxime Perrotin committed
539
    while basic_type.kind == 'ReferenceType':
540 541 542
        Min = getattr(basic_type, "Min", None)
        Max = getattr(basic_type, "Max", None)

Maxime Perrotin's avatar
Maxime Perrotin committed
543
        # Find type with proper case in the data view
Maxime Perrotin's avatar
Maxime Perrotin committed
544
        for typename in pool.keys():
Maxime Perrotin's avatar
Maxime Perrotin committed
545
            if typename.lower() == basic_type.ReferencedTypeName.lower():
546
                basic_type = pool[typename].type
547
                if Min is not None and Max is not None \
548 549
                        and (is_numeric(basic_type) or is_string(basic_type)
                                or is_sequenceof(basic_type)):
550 551 552 553 554 555
                    # Subtypes may have defined subranges
                    new_type = type('Subtype',  basic_type.__bases__,
                            dict (basic_type.__dict__))
                    new_type.Min = Min
                    new_type.Max = Max
                    basic_type = new_type
Maxime Perrotin's avatar
Maxime Perrotin committed
556 557 558 559 560 561
                break
        else:
            raise TypeError('Type "' + type_name(basic_type) +
                            '" was not found in Dataview')
    return basic_type

562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584
def find_type_name(a_type, pool=None) -> str:
    ''' A Type may be an alias of another type, if it has a ReferencedTypeName
    and the same constraints as the referenced type. This function finds the
    name of the original type definition '''
    if a_type.kind != 'ReferenceType':
        raise TypeError('Not a referenced type')
    pool = pool or types()
    refname = a_type.ReferencedTypeName
    Min = getattr(a_type, "Min", None)
    Max = getattr(a_type, "Max", None)
    for typename in pool.keys():
        # Find the referenced type in the dataview
        if refname.lower() == typename.lower():
            parent = pool[typename].type
            parent_Min = getattr(parent, "Min", None)
            parent_Max = getattr(parent, "Max", None)
            if((Min != None and parent_Min != Min)
                    or (Max != None and parent_Max != Max)
                    or parent.kind != 'ReferenceType'):
                # Parent has different constraints or is a basic type
                return refname
            else:
                return find_type_name(parent)
Maxime Perrotin's avatar
Maxime Perrotin committed
585

586 587 588 589 590
def signature(name, context):
    ''' Return the signature of a procecure/output/operator '''
    name = name.lower()
    if name in SPECIAL_OPERATORS:
        return SPECIAL_OPERATORS[name]
Maxime Perrotin's avatar
Maxime Perrotin committed
591

592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611
    for out_sig in context.output_signals:
        if out_sig['name'].lower() == name:
            signature = []
            if out_sig.get('type'):
                # output signals: one single parameter
                signature.append({
                    'type': out_sig.get('type'),
                    'name': out_sig.get('param_name' or ''),
                    'direction': 'in',
                })
            return signature

    for inner_proc in context.procedures:
        proc_name = inner_proc.inputString
        if proc_name.lower() == name:
            return inner_proc.fpar

    raise AttributeError('Operator/output/procedure not found: ' + name)


612 613
def check_call(name, params, context):
    ''' Check the parameter types of a procedure/output/operator call,
Maxime Perrotin's avatar
Maxime Perrotin committed
614 615
        returning the type of its result (value-returning functions only,
        i.e not signal sending '''
616

617
    # Special case for write/writeln functions
618
    if name.lower() in ('write', 'writeln'):
619
        def check_one_param(p, name):
620
            p_ty = p.exprType
621 622
            if is_numeric(p_ty) or is_boolean(p_ty) or is_string(p_ty) or \
                    is_enumerated(p_ty):
623
                return
624 625
            raise TypeError('Type {} not supported in call to {}'.
                format(type_name(p.exprType), name))
626 627 628 629 630 631
        for p in params:
            if not isinstance(p, ogAST.PrimConditional):
                check_one_param(p, name)
            else:
                for each in (p.value['then'], p.value['else']):
                    check_one_param(each, name)
632
                # check that both "then" and "else" are of a similar type
633 634 635 636 637 638 639 640 641 642 643 644 645 646
                # (string, int, or enumerated), this is necessary for the
                # backends
                if (is_numeric(p.value['then'].exprType) ==
                   is_numeric(p.value['else'].exprType) == True) or \
                   (is_boolean(p.value['then'].exprType) ==
                   is_boolean(p.value['else'].exprType) == True) or \
                   (is_string(p.value['then'].exprType) ==
                   is_string(p.value['else'].exprType) == True) or \
                   (is_enumerated(p.value['then'].exprType) ==
                   is_enumerated(p.value['else'].exprType) == True):
                      p.exprType = p.value['then'].exprType
                else:
                    raise TypeError('{}: both options must have the type type.'
                                    .format(name))
Maxime Perrotin's avatar
Maxime Perrotin committed
647
        return UNKNOWN_TYPE
Maxime Perrotin's avatar
Maxime Perrotin committed
648

649 650 651 652 653 654 655 656 657
    # Special case for "exist" function
    elif name == 'exist':
        # "exist" shall return true if an optional SEQUENCE field is present
        # We have to check that the parameter is actually an optional field
        # So we check first that there is only one param
        # then that this is a PrimSelector (at least "a.b")
        # Then we analyse from the variable to the last field if that is
        # actually an optional field, using the ASN.1 data model
        if len(params) != 1:
658
            raise TypeError('"exist" operator takes only one parameter')
659 660
        param, = params
        if not isinstance(param, ogAST.PrimSelector):
661
            raise TypeError('"exist" operator only works on optional fields')
662 663 664 665 666 667 668 669 670 671 672 673 674 675
        left = param.value[0] # Can be a variable or another PrimSelector
        field_list = [param.value[1]] # string of the field name
        while isinstance(left, ogAST.PrimSelector):
            field_list.append(left.value[1])
            left = left.value[0]
        sort = find_basic_type(left.exprType)  # must have Children
        if sort.kind == 'UnknownType':
            raise TypeError('Variable not found in call to "exist" operator')
        # At this point we know that the expression is correct, so we will
        # not miss any child in the dataview. We can follow the children
        # in the ASN.1 model until we reach the last one, which shall be
        # optional
        while field_list:
            child_name = field_list.pop().replace('_', '-').lower()
Maxime Perrotin's avatar
Maxime Perrotin committed
676
            for child in sort.Children.keys():
677 678 679 680 681 682 683 684 685 686 687 688
                if child.lower() == child_name:
                    break
            optional = sort.Children[child].Optional
            sort = sort.Children[child].type
            if sort.kind == 'ReferenceType':
                sort = find_basic_type (sort)
        # At this point we should have found the last type
        if optional != "True":
            raise TypeError('Field is not optional in call to "exist"')
        return type('Exist', (object,), {
            'kind': 'BooleanType'
        })
689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709
    elif name == 'val':
        # val converts a positive number to a enumeration literal
        # the first parameter is the number and the second is the type name
        # of the enumeration. It is equivalent in Ada to EnumType'Val(number)
        if len(params) != 2:
            raise TypeError(name + " takes 2 parameters: number, type")
        variable, target_type = params
        variable_sort = find_basic_type(variable.exprType)
        if variable_sort.kind != 'IntegerType':
            raise TypeError(name + ': First parameter is not an number')
        sort_name = target_type.value[0]  #  raw string of the type to cast
        for sort in types().keys():
            if sort.lower().replace('-', '_') == \
                    sort_name.lower().replace('-', '_'):
                break
        else:
            raise TypeError(name + ': type ' + sort_name + 'not found')
        # we could check if the range of the number is compatible with the
        # number of values...
        return_type = types()[sort].type
        return return_type
710

711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729
    elif name in ('to_selector', 'to_enum'):
        if len(params) != 2:
            raise TypeError(name + " takes 2 parameters: variable, type")
        variable, target_type = params
        variable_sort = find_basic_type(variable.exprType)
        if variable_sort.kind == 'UnknownType':
            raise TypeError(name + ': variable not found (parameter 1)')
        if variable_sort.kind != 'EnumeratedType':
            raise TypeError(name + ': First parameter is not an enumerated')
        sort_name = target_type.value[0]  #  raw string of the type to cast
        for sort in types().keys():
            if sort.lower().replace('-', '_') == \
                    sort_name.lower().replace('-', '_'):
                break
        else:
            raise TypeError(name + ': type ' + sort_name + 'not found')
        # check that the list of enumerants are identical. unfortunately we
        # cannot check the ordering, as it is an unordered dict
        if name == 'to_selector':
730 731 732
            # if the sort is a subtype, it may not have a -selection suffix
            # and an exception may be raised. FIXME : check "present" operator
            # as the issue is already fixed there
733 734 735 736 737 738 739 740 741 742 743
            return_type = types()[sort + '-selection'].type
        else:
            return_type = types()[sort].type
        if return_type.kind != 'EnumeratedType':
            raise TypeError(name + ': Second parameter is incorrect')
        return_type_keys = return_type.EnumValues.keys()
        variable_sort_keys = variable_sort.EnumValues.keys()
        if return_type_keys != variable_sort_keys:
            raise TypeError(name + ': Enumerated type are not equivalent')
        return return_type

Maxime Perrotin's avatar
Maxime Perrotin committed
744 745
    # (1) Find the signature of the function
    # signature will hold the list of parameters for the function
746
    sign = signature(name, context)
Maxime Perrotin's avatar
Maxime Perrotin committed
747 748

    # (2) Check that the number of given parameters matches the signature
749 750 751
    if len(sign) != len(params):
        raise TypeError('Expected {} arguments in call to {} ({} received)'.
            format(len(sign), name, len(params)))
Maxime Perrotin's avatar
Maxime Perrotin committed
752

753 754
    # (3) Check each individual parameter type
    for idx, param in enumerate(params):
Maxime Perrotin's avatar
Maxime Perrotin committed
755
        warnings = []
Maxime Perrotin's avatar
Maxime Perrotin committed
756
        expr               = ogAST.ExprAssign()
757
        expr.left          = ogAST.PrimVariable(debugLine=lineno())
758
        expr.left.exprType = sign[idx]['type']
Maxime Perrotin's avatar
Maxime Perrotin committed
759
        expr.right         = param
760 761

        try:
762 763 764
            basic_left  = find_basic_type(expr.left.exprType)
            basic_right = find_basic_type(expr.right.exprType)
            #print getattr(basic_left, "Min", 0), getattr(basic_right, "Min", 0)
Maxime Perrotin's avatar
Maxime Perrotin committed
765
            warnings.extend(fix_expression_types(expr, context))
766
            params[idx] = expr.right
767
        except TypeError as err:
768 769
            expected = type_name(sign[idx]['type'])
            received = type_name(expr.right.exprType)
770 771 772
            raise TypeError('In call to {}: Type of parameter {} is incorrect'
                            ' ({}) - {}'
                            .format(name, idx+1, received, str(err)))
Maxime Perrotin's avatar
Maxime Perrotin committed
773
        if (warnings):
774 775 776 777
            expected = type_name(sign[idx]['type'])
            received = type_name(expr.right.exprType)
            raise Warning('Expected type {} in call to {} ({} received)'.
                format(expected, name, received))
778 779

        if sign[idx].get('direction') != 'in' \
780 781
                and not isinstance(expr.right, ogAST.PrimVariable):
            raise TypeError('OUT parameter "{}" is not a variable'
782
                .format(expr.right.inputString))
Maxime Perrotin's avatar
Maxime Perrotin committed
783

784 785 786
    # (4) Compute the type of the result
    param_btys = [find_basic_type(p.exprType) for p in params]
    if name == 'abs':
Maxime Perrotin's avatar
Maxime Perrotin committed
787
        # The implementation of abs in *all* programming languages returns
Maxime Perrotin's avatar
Maxime Perrotin committed
788 789
        # a type that is the same as the type of the parameter. The returned
        # value is *not* unsigned. abs(integer'Min) returns a NEGATIVE number
Maxime Perrotin's avatar
Maxime Perrotin committed
790 791
        # this is an issue in an assign statement, if the recipient is
        # unsigned .. A cast is necessary if the parameter of abs is negative
792 793
        Min = float(param_btys[0].Min)
        Max = float(param_btys[0].Max)
Maxime Perrotin's avatar
Maxime Perrotin committed
794
        if params[0].exprType.kind != 'ReferenceType' and Min == Max:
795 796 797 798 799 800 801 802
            # if param is a raw number, return a positive range
            # otherwise return the same type as the variable
            return type('Universal_Integer', (param_btys[0],), {
               'Min': str(max(Min, 0)),
               'Max': str(max(Max, 0))
           })
        else:
            return type('Abs', (param_btys[0],), {})
803 804 805 806 807 808 809

    elif name == 'ceil':
        return type('Ceil', (REAL,), {
            'Min': str(math.ceil(float(param_btys[0].Min))),
            'Max': str(math.ceil(float(param_btys[0].Max)))
        })

dbarbera's avatar
dbarbera committed
810 811 812 813 814 815
    elif name == 'cos':
        return type('Cos', (REAL,), {
            'Min': str(-1.0),
            'Max': str(1.0)
        })

816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834
    elif name == 'fix':
        return type('Fix', (INTEGER,), {
            'Min': param_btys[0].Min,
            'Max': param_btys[0].Max
        })

    elif name == 'float':
        return type('Float', (REAL,), {
            'Min': param_btys[0].Min,
            'Max': param_btys[0].Max
        })

    elif name == 'floor':
        return type('Floor', (REAL,), {
            'Min': str(math.floor(float(param_btys[0].Min))),
            'Max': str(math.floor(float(param_btys[0].Max)))
        })

    elif name == 'length':
835
        return type('Length', (INT32,), {
836 837 838 839 840 841
            'Min': param_btys[0].Min,
            'Max': param_btys[0].Max
        })

    elif name == 'num':
        enum_values = [int(each.IntValue)
Maxime Perrotin's avatar
Maxime Perrotin committed
842
                       for each in param_btys[0].EnumValues.values()]
843 844 845 846 847 848 849 850 851 852 853 854 855 856
        return type('Num', (INTEGER,), {
            'Min': str(min(enum_values)),
            'Max': str(max(enum_values))
        })

    elif name == 'power':
        return type('Power', (param_btys[0],), {
            'Min': str(pow(float(param_btys[0].Min),
                           float(param_btys[1].Min))),
            'Max': str(pow(float(param_btys[0].Max),
                           float(param_btys[1].Max)))
        })

    elif name == 'present':
857
        p, = params
858 859 860 861 862 863 864 865 866 867 868 869 870 871 872
        # When we get there, the parameter has been checked already and we
        # know it is a CHOICE type.
        # However it may be a subtype, and in the AST the choices are defined
        # in the supertype, so we must find it and and the -selection suffix
        # This suffix is added by the Asn1scc module.
        sort = p.exprType
        sort_name = type_name (sort)
        try:
            while sort.kind == "ReferenceType":
                sort_name = sort.ReferencedTypeName
                sort = types()[sort.ReferencedTypeName].type
        except AttributeError:
            # Native choice types don't have the kind field here
            pass
        return types()[sort_name + "-selection"].type
873

874 875 876 877 878 879 880 881 882 883
    # choice_to_int: returns an integer corresponding to either the currently
    # selected choice value (e.g. foo in CHOICE { foo INTEGER (..), ... } when
    # foo is the current choice). or a default, user-defined value if the
    # current choice is not numerical. The first parameter is an instance of
    # a CHOICE type, and the second parameter is the default value.
    elif name == 'choice_to_int':
        p1, _ = params
        #sort = type_name (p1.exprType)
        return type('choice_to_int', (INTEGER,), {})

dbarbera's avatar
dbarbera committed
884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901
    elif name == 'round':
        return type('Round', (REAL,), {
            'Min': str(round(float(param_btys[0].Min))),
            'Max': str(round(float(param_btys[0].Max)))
        })

    elif name == 'sin':
        return type('Sin', (REAL,), {
            'Min': str(-1.0),
            'Max': str(1.0)
        })

    elif name == 'sqrt':
        return type('Sqrt', (REAL,), {
            'Min': str(0.0),
            'Max': str(math.sqrt(float(param_btys[0].Max)))
        })

902 903 904 905 906 907
    elif name == 'trunc':
        return type('Trunc', (REAL,), {
            'Min': str(math.trunc(float(param_btys[0].Min))),
            'Max': str(math.trunc(float(param_btys[0].Max)))
        })

908 909 910 911 912 913 914
    else:
        # check if procedure is declared to return a type
        for inner_proc in context.procedures:
            proc_name = inner_proc.inputString
            if proc_name.lower() == name.lower() and inner_proc.return_type:
                return inner_proc.return_type

915 916
    return UNKNOWN_TYPE

Maxime Perrotin's avatar
Maxime Perrotin committed
917

Maxime Perrotin's avatar
Maxime Perrotin committed
918 919 920
def check_range(typeref, type_to_check):
    ''' Verify the that the Min/Max bounds of two types are compatible
        Called to test that assignments are withing allowed range
dbarbera's avatar
dbarbera committed
921
        both types assumed to be basic types
Maxime Perrotin's avatar
Maxime Perrotin committed
922 923
    '''
    try:
924 925 926 927 928 929 930 931 932 933 934 935
        min1, max1 = float(type_to_check.Min), float(type_to_check.Max)
        min2, max2 = float(typeref.Min), float(typeref.Max)
        error   = min1 > max2 or max1 < min2
        warning = min1 < min2 or max1 > max2
        if error:
            raise TypeError(
                    'Expression in range {} .. {} is outside range {} .. {}'
                    .format(type_to_check.Min, type_to_check.Max,
                            typeref.Min, typeref.Max))
        elif warning:
            raise Warning('Expression in range {} .. {}, '
                          'could be outside expected range {} .. {}'
Maxime Perrotin's avatar
Maxime Perrotin committed
936 937
                    .format(type_to_check.Min, type_to_check.Max,
                            typeref.Min, typeref.Max))
938
    except (AttributeError, ValueError):
Maxime Perrotin's avatar
Maxime Perrotin committed
939 940 941
        raise TypeError('Missing range')


Maxime Perrotin's avatar
Maxime Perrotin committed
942 943
def fix_append_expression_type(expr, expected_type):
    ''' In an Append expression, all components must be of the same type,
944
        which is the type expected by the user of the append, for example
Maxime Perrotin's avatar
Maxime Perrotin committed
945 946 947 948 949 950 951
        the left part of an assign expression.
        We must recursively fix the Append type, in case we have a//b//c
        that is handled as (a//b)//c
        Inputs:
           expr: the append expression (possibly recursive)
           expected_type : the type to assign to the expression
    '''
Maxime Perrotin's avatar
Maxime Perrotin committed
952
    #print "[DEBUG] Fix append expression: ", expr.inputString
Maxime Perrotin's avatar
Maxime Perrotin committed
953 954 955 956
    def rec_append(inner_expr, set_type):
        for each in (inner_expr.left, inner_expr.right):
            if isinstance(each, ogAST.ExprAppend):
                rec_append(each, set_type)
957 958 959
            if each.exprType == UNKNOWN_TYPE:
                # eg. if the side is a PrimConditional (ternary)
                each.exprType = set_type
Maxime Perrotin's avatar
Maxime Perrotin committed
960 961 962 963 964 965
            each.expected_type = set_type
    rec_append(expr, expected_type)
    expr.exprType      = expected_type
    expr.expected_type = expected_type


Maxime Perrotin's avatar
Maxime Perrotin committed
966
def check_type_compatibility(primary, type_ref, context):
Maxime Perrotin's avatar
Maxime Perrotin committed
967 968
    '''
        Check if an ogAST.Primary (raw value, enumerated, ASN.1 Value...)
969
        is compatible with a given type (type_ref is an ASN1Scc type)
970
        Possibly returns a list of warnings; can raises TypeError
Maxime Perrotin's avatar
Maxime Perrotin committed
971
    '''
Maxime Perrotin's avatar
Maxime Perrotin committed
972
    warnings = []    # function returns a list of warnings
Maxime Perrotin's avatar
Maxime Perrotin committed
973 974
    # assert type_ref is not None
    if type_ref is UNKNOWN_TYPE or type_ref is None:
975
        #print traceback.print_stack()
Maxime Perrotin's avatar
Maxime Perrotin committed
976
        raise TypeError('Type reference is unknown')
dbarbera's avatar
dbarbera committed
977

Maxime Perrotin's avatar
Maxime Perrotin committed
978 979 980
    #if primary.exprType == type_ref:
    #    return warnings

981
    basic_type = find_basic_type(type_ref)
982 983 984 985 986 987 988 989 990 991
    # watch out: type_ref may be a subtype of basic_type with different
    # min/max constraint, in particular in case of substrings
    if type_ref.__name__ == 'SubStr':
        minR, maxR = type_ref.Min, type_ref.Max
    else:
        try:
            minR, maxR = basic_type.Min, basic_type.Max
        except AttributeError:
            # some types do not have Min/Max ranges
            pass
Maxime Perrotin's avatar
Maxime Perrotin committed
992 993

    if (isinstance(primary, ogAST.PrimEnumeratedValue)
994
            and basic_type.kind.endswith('EnumeratedType')):
Maxime Perrotin's avatar
Maxime Perrotin committed
995 996 997
        # If type ref is an enumeration, check that the value is valid
        # Note, when using the "present" operator of a CHOICE type, the
        # resulting value is actually an EnumeratedType
998 999 1000 1001
        enumerant = primary.inputString.replace('_', '-').lower()
        for each in basic_type.EnumValues.keys():
            if each.lower() == enumerant:
                # Found -> all OK
Maxime Perrotin's avatar
Maxime Perrotin committed
1002
                return warnings
Maxime Perrotin's avatar
Maxime Perrotin committed
1003
        else:
1004
            err = ('Value "' + primary.inputString +
Maxime Perrotin's avatar
Maxime Perrotin committed
1005
                   '" not in this enumeration: ' +
1006
                   str(basic_type.EnumValues.keys()))
Maxime Perrotin's avatar
Maxime Perrotin committed
1007
            raise TypeError(err)
1008 1009 1010 1011 1012 1013
    elif isinstance(primary, ogAST.PrimConditional):
        then_expr = primary.value['then']
        else_expr = primary.value['else']

        for expr in (then_expr, else_expr):
            if expr.is_raw:
Maxime Perrotin's avatar
Maxime Perrotin committed
1014 1015 1016 1017
                warnings.extend(check_type_compatibility(expr,
                                                         type_ref,
                                                         context))
        return warnings
1018

1019
    elif isinstance(primary, (ogAST.PrimVariable, ogAST.PrimSelector)):
Maxime Perrotin's avatar
Maxime Perrotin committed
1020
        try:
Maxime Perrotin's avatar
Maxime Perrotin committed
1021
            warnings.extend(compare_types(primary.exprType, type_ref))
Maxime Perrotin's avatar
Maxime Perrotin committed
1022 1023 1024
        except TypeError as err:
            raise TypeError('{expr} should be of type {ty} - {err}'
                            .format(expr=primary.inputString,
1025
                                    ty=type_name(type_ref),
Maxime Perrotin's avatar
Maxime Perrotin committed
1026
                                    err=str(err)))
Maxime Perrotin's avatar
Maxime Perrotin committed
1027
        return warnings
1028

Maxime Perrotin's avatar
Maxime Perrotin committed
1029
    elif isinstance(primary, (ogAST.PrimInteger, ogAST.ExprMod)) \
dbarbera's avatar
dbarbera committed
1030
            and (is_integer(type_ref) or type_ref == NUMERICAL):
Maxime Perrotin's avatar
Maxime Perrotin committed
1031
        return warnings
Maxime Perrotin's avatar
Maxime Perrotin committed
1032

Maxime Perrotin's avatar
Maxime Perrotin committed
1033
    elif isinstance(primary, ogAST.PrimReal) \
dbarbera's avatar
dbarbera committed
1034
            and (is_real(type_ref) or type_ref == NUMERICAL):
Maxime Perrotin's avatar
Maxime Perrotin committed
1035
        return warnings
Maxime Perrotin's avatar
Maxime Perrotin committed
1036

1037
    elif isinstance(primary, ogAST.ExprNeg):
Maxime Perrotin's avatar
Maxime Perrotin committed
1038 1039 1040
        warnings.extend(check_type_compatibility(primary.expr,
                                                 type_ref,
                                                 context))
1041

dbarbera's avatar
dbarbera committed
1042
    elif isinstance(primary, ogAST.PrimBoolean) and is_boolean(type_ref):
Maxime Perrotin's avatar
Maxime Perrotin committed
1043
        return warnings
1044

1045
    elif isinstance(primary, ogAST.PrimNull) and is_null(type_ref):
Maxime Perrotin's avatar
Maxime Perrotin committed
1046
        return warnings
1047

1048 1049 1050
    elif isinstance(primary, ogAST.PrimEmptyString):
        # Empty strings ("{ }") can be used for arrays and empty records
        if basic_type.kind == 'SequenceOfType':
1051
            if int(minR) == 0:
Maxime Perrotin's avatar
Maxime Perrotin committed
1052
                return warnings
1053 1054
            else:
                raise TypeError('SEQUENCE OF has a minimum size of '
1055
                                + minR + ')')
1056 1057 1058
        elif