Commit fc96395c authored by Laurent MEYER's avatar Laurent MEYER
Browse files

Merge remote-tracking branch 'remotes/upstream/master'

Conflicts:
	sdl92.g
	sdl92Lexer.py
	sdl92Parser.py
parents 92e381f2 44a59893
...@@ -17,11 +17,11 @@ import subprocess ...@@ -17,11 +17,11 @@ import subprocess
import tempfile import tempfile
import uuid import uuid
import os import os
import distutils.spawn as spawn
import sys import sys
import importlib import importlib
import logging import logging
import PySide.QtCore as Qt import PySide.QtCore as Qt
import icons
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
...@@ -57,20 +57,21 @@ def parse_asn1(*files, **options): ...@@ -57,20 +57,21 @@ def parse_asn1(*files, **options):
assert isinstance(ast_version, ASN1) assert isinstance(ast_version, ASN1)
assert isinstance(flags, list) assert isinstance(flags, list)
asn1scc_root = os.path.dirname(spawn.find_executable('asn1.exe'))
tempdir = tempfile.mkdtemp() tempdir = tempfile.mkdtemp()
filename = str(uuid.uuid4()).replace('-', '_') filename = str(uuid.uuid4()).replace('-', '_')
filepath = tempdir + os.sep + filename + '.py' filepath = tempdir + os.sep + filename + '.py'
# dump python.stg in the temp directory # dump python.stg in the temp directory
stg = Qt.QFile(':misc/python.stg') #stg = Qt.QFile(':misc/python.stg')
stg_data = stg.readData(stg.size()) #stg_data = stg.readData(stg.size())
tmp_stg = tempdir + os.sep + 'python.stg' stg = asn1scc_root + os.sep + 'python.stg'
with open(tmp_stg, 'w') as fd: #with open(tmp_stg, 'w') as fd:
fd.write(stg_data) # fd.write(stg_data)
args = ['asn1.exe', args = ['asn1.exe',
'-customStgAstVerion', str(ast_version.value), '-customStgAstVerion', str(ast_version.value),
'-customStg', tmp_stg + ':' + filepath] + list(*files) '-customStg', stg + ':' + filepath] + list(*files)
LOG.debug('Calling: ' + ' '.join(args)) LOG.debug('Calling: ' + ' '.join(args))
try: try:
......
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
OpenGEODE - A tiny SDL Editor for TASTE
This module generates textual SDL code (PR format)
by parsing the graphical symbols.
Copyright (c) 2012-2014 European Space Agency
Designed and implemented by Maxime Perrotin
Contact: maxime.perrotin@esa.int
"""
import logging
from singledispatch import singledispatch
import genericSymbols, sdlSymbols
LOG = logging.getLogger(__name__)
__all__ = ['generate']
def cif_coord(name, symbol):
''' PR string for the CIF coordinates/size of a symbol '''
return u'/* CIF {symb} ({x}, {y}), ({w}, {h}) */'.format(
symb=name,
x=int(symbol.scenePos().x()), y=int(symbol.scenePos().y()),
w=int(symbol.boundingRect().width()),
h=int(symbol.boundingRect().height()))
def hyperlink(symbol):
''' PR string for the optional hyperlink associated to a symbol '''
return u"/* CIF Keep Specific Geode HyperLink '{}' */".format(
symbol.hyperlink)
def common(name, symbol):
''' PR string format that is shared by most symbols '''
result = [cif_coord(name, symbol)]
if symbol.hyperlink:
result.append(hyperlink(symbol))
result.append(u'{} {}{}'.format(name, unicode(symbol.text), ';'
if not symbol.comment else ''))
if symbol.comment:
result.extend(generate(symbol.comment))
return result
def recursive_aligned(symbol):
''' Get the branch following symbol '''
result = []
next_symbol = symbol.next_aligned_symbol()
while next_symbol:
result.extend(generate(next_symbol))
next_symbol = next_symbol.next_aligned_symbol()
return result
@singledispatch
def generate(symbol, recursive=True):
''' Generate text for a symbol, recursively or not - return a list of
strings '''
_, _ = symbol, recursive
raise NotImplementedError('[PR Generator] Unsupported AST construct')
return []
@generate.register(genericSymbols.Comment)
def _comment(symbol):
''' Optional comment linked to a symbol '''
result = [cif_coord('COMMENT', symbol)]
if symbol.hyperlink:
result.append(hyperlink(symbol))
result.append('COMMENT \'{}\';'.format(unicode(symbol.text)))
return result
@generate.register(sdlSymbols.Input)
def _input(symbol, recursive=True):
''' Input symbol or branch if recursive is set '''
result = common('INPUT', symbol)
if recursive:
result.extend(recursive_aligned(symbol))
return result
@generate.register(sdlSymbols.Connect)
def _connect(symbol, recursive=True):
''' Connect symbol or branch if recursive is set '''
result = common('CONNECT', symbol)
if recursive:
result.extend(recursive_aligned(symbol))
return result
@generate.register(sdlSymbols.Output)
def _output(symbol):
''' Output symbol '''
return common('OUTPUT', symbol)
@generate.register(sdlSymbols.Decision)
def _decision(symbol, recursive=True):
''' Decision symbol or branch if recursive is set '''
result = common('DECISION', symbol)
if recursive:
else_branch = None
for answer in symbol.branches():
if unicode(answer).lower().strip() == 'else':
else_branch = generate(answer)
else:
result.extend(generate(answer))
if else_branch:
result.extend(else_branch)
result.append('ENDDECISION;')
@generate.register(sdlSymbols.DecisionAnswer)
def _decisionanswer(symbol, recursive=True):
''' Decision Answer symbol or branch if recursive is set '''
ans = unicode(symbol)
if ans.lower().strip() != u'else':
ans = u'({})'.format(ans)
result = [cif_coord('ANSWER', symbol)]
if symbol.hyperlink:
result.append(hyperlink(symbol))
result.append('{}:'.format(ans))
if recursive:
result.extend(recursive_aligned(symbol))
return result
@generate.register(sdlSymbols.Join)
def _join(symbol):
''' Join symbol '''
return common('JOIN', symbol)
@generate.register(sdlSymbols.ProcedureStop)
def _procedurestop(symbol):
''' Procedure Stop symbol '''
return common('RETURN', symbol)
@generate.register(sdlSymbols.Label)
def _label(symbol, recursive=True):
''' Label symbol or branch if recursive is set '''
_, _ = symbol, recursive
@generate.register(sdlSymbols.Task)
def _task(symbol):
''' Task symbol '''
return common('TASK', symbol)
@generate.register(sdlSymbols.ProcedureCall)
def _procedurecall(symbol):
''' Procedure call symbol '''
result = [cif_coord('PROCEDURECALL', symbol)]
if symbol.hyperlink:
result.append(hyperlink(symbol))
result.append(u'CALL {}{}'.format(unicode(symbol.text), ';'
if not symbol.comment else ''))
return result
@generate.register(sdlSymbols.TextSymbol)
def _textsymbol(symbol):
''' Text Area symbol '''
result = [cif_coord('TEXT', symbol)]
if symbol.hyperlink:
result.append(hyperlink(symbol))
result.append(unicode(symbol.text))
result.append('/* CIF ENDTEXT */')
return result
@generate.register(sdlSymbols.State)
def _state(symbol, recursive=True):
''' State symbol or branch if recursive is set '''
_, _ = symbol, recursive
@generate.register(sdlSymbols.Process)
def _process(symbol, recursive=True):
''' Process symbol or branch if recursive is set '''
_, _ = symbol, recursive
@generate.register(sdlSymbols.Procedure)
def _procedure(symbol, recursive=True):
''' Procedure symbol or branch if recursive is set '''
_, _ = symbol, recursive
@generate.register(sdlSymbols.Start)
def _start(symbol, recursive=True):
''' START symbol or branch if recursive is set '''
_, _ = symbol, recursive
This diff is collapsed.
group python;
//delimiters "$", "$"
RootXml(arrsFiles) ::= <<
#!/usr/bin/env python
# ASN.1 Data model
asn1Files = []
asn1Modules = []
exportedTypes = {}
exportedVariables = {}
importedModules = {}
types = {}
$arrsFiles;separator="\n"$
>>
FileXml(sFileName, arrsModules) ::= <<
asn1Files.append("$sFileName$")
$arrsModules;separator="\n"$
>>
ModuleXml(sName, arrsImportedModules, arrsExpTypes, arrsExpVars, arrsTases) ::=<<
asn1Modules.append("$sName$")
exportedTypes["$sName$"] = [$arrsExpTypes:{x|"$x$"};separator=", "$]
exportedVariables["$sName$"] = [$arrsExpVars:{x|"$x$"};separator=", "$]
importedModules["$sName$"] = [$arrsImportedModules:{x|$x$};separator=", "$]
$arrsTases;separator="\n"$
>>
ImportedMod(sName, arrsTypes, arrsVars) ::= <<
{"$sName$":{"ImportedTypes": [$arrsTypes:{t|"$t$"};separator=","$], "ImportedVariables": [$arrsVars:{t|"$t$"};separator=","$]}}
>>
TasXml(sName, nLine, nPos, sType) ::= <<
types["$sName$"] = type("$sName$", (object,), {
"Line": $nLine$, "CharPositionInLine": $nPos$, "type": type("$sName$_type", (object,), {
$sType$
})
})
>>
TypeGeneric(nLine, nPos, sSubType) ::= <<
"Line": $nLine$, "CharPositionInLine": $nPos$, $sSubType$
>>
MinMaxType(sName, sMin, sMax) ::= <<
"kind": "$sName$", "Min": "$sMin$", "Max": "$sMax$"
>>
MinMaxType2(sName, sMin, sMax) ::= <<
"kind": "$sName$", "Min": "$sMin$", "Max": "$sMax$"
>>
BooleanType () ::= <<"kind": "BooleanType">>
NullType () ::= <<"kind": "NullType">>
EnumItem (sName, nVal, nLine, nPos, sCID) ::= <<
"$sName$": type("$sCID$", (object,), {
"IntValue": $nVal$, "Line": $nLine$, "CharPositionInLine": $nPos$, "EnumID": "$sCID$"
})
>>
EnumType(arrsItems) ::= <<
"kind": "EnumeratedType", "Extensible": "False", "ValuesAutoCalculated": "False", "EnumValues": {
$arrsItems;separator=",\n"$
}
>>
ChoiceChild(sName, nLine, nPos, sChildContent, sNamePresent ) ::= <<
"$sName$": type("$sNamePresent$", (object,), {
"Line": $nLine$, "CharPositionInLine": $nPos$, "EnumID": "$sNamePresent$", "type": type("$sNamePresent$_type", (object,), {
$sChildContent$
})
})
>>
ChoiceType(arrsChildren) ::= <<
"kind": "ChoiceType", "Children": {
$arrsChildren;separator=",\n"$
}
>>
SequenceChild(sName, bOptional, sDefVal, nLine, nPos, sChildContent ) ::= <<
"$sName$": type("$sName$", (object,), {
"Optional": "$bOptional$"$if(sDefVal)$, "DefaultValue": "$sDefVal$"$endif$, "Line": $nLine$, "CharPositionInLine": $nPos$, "type": type("$sName$_type", (object,), {
$sChildContent$
})
})
>>
SequenceType(arrsChildren) ::= <<
"kind": "SequenceType", "Children": {
$arrsChildren;separator=",\n"$
}
>>
SequenceOfType(sMin, sMax, sChild) ::= <<
"kind": "SequenceOfType", "Min": "$sMin$", "Max": "$sMax$", "type": type("SeqOf_type", (object,), {
$sChild$
})
>>
RefTypeMinMax(sMin, sMax, sName, sModName) ::= <<
"kind": "ReferenceType", "ReferencedTypeName": "$sName$", "Min": "$sMin$", "Max": "$sMax$"$if(sModName)$, "ReferencedModName": "$sModName$"$endif$
>>
RefType(sName, sModName) ::= <<
"kind": "ReferenceType", "ReferencedTypeName": "$sName$"$if(sModName)$, "ReferencedModName": "$sModName$"$endif$
>>
...@@ -119,7 +119,7 @@ class ExprMod(Expression): ...@@ -119,7 +119,7 @@ class ExprMod(Expression):
class ExprRem(Expression): class ExprRem(Expression):
operand = 'mod' operand = 'rem'
class ExprAssign(Expression): class ExprAssign(Expression):
......
...@@ -75,7 +75,9 @@ TMPVAR = 0 ...@@ -75,7 +75,9 @@ TMPVAR = 0
# ASN.1 types used to support the signature of special operators # ASN.1 types used to support the signature of special operators
INTEGER = type('IntegerType', (object,), {'kind': 'IntegerType'}) INTEGER = type('IntegerType', (object,), {'kind': 'IntegerType'})
INT32 = type('Integer32Type', (object,), {'kind': 'Integer32Type'}) INT32 = type('Integer32Type', (object,), {'kind': 'Integer32Type',
'Min':'-2147483648',
'Max':'2147483647'})
NUMERICAL = type('NumericalType', (object,), {'kind': 'Numerical'}) NUMERICAL = type('NumericalType', (object,), {'kind': 'Numerical'})
TIMER = type('TimerType', (object,), {'kind': 'TimerType'}) TIMER = type('TimerType', (object,), {'kind': 'TimerType'})
REAL = type('RealType', (object,), {'kind': 'RealType'}) REAL = type('RealType', (object,), {'kind': 'RealType'})
...@@ -427,6 +429,22 @@ def check_and_fix_op_params(op_name, expr_list, context): ...@@ -427,6 +429,22 @@ def check_and_fix_op_params(op_name, expr_list, context):
.format(expr.right.inputString)) .format(expr.right.inputString))
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
both types assumed to be basic types
'''
try:
if float(type_to_check.Min) < float(typeref.Min) \
or float(type_to_check.Max) > float(typeref.Max):
raise TypeError('Expression evaluation in range [{}..{}], '
'outside expected range [{}..{}]'
.format(type_to_check.Min, type_to_check.Max,
typeref.Min, typeref.Max))
except (AttributeError, ValueError):
raise TypeError('Missing range')
def check_type_compatibility(primary, typeRef, context): def check_type_compatibility(primary, typeRef, context):
''' '''
Check if an ogAST.Primary (raw value, enumerated, ASN.1 Value...) Check if an ogAST.Primary (raw value, enumerated, ASN.1 Value...)
...@@ -486,9 +504,11 @@ def check_type_compatibility(primary, typeRef, context): ...@@ -486,9 +504,11 @@ def check_type_compatibility(primary, typeRef, context):
elif isinstance(primary, ogAST.PrimInteger) \ elif isinstance(primary, ogAST.PrimInteger) \
and actual_type.kind.startswith('Integer'): and actual_type.kind.startswith('Integer'):
return return
elif isinstance(primary, ogAST.PrimReal) \ elif isinstance(primary, ogAST.PrimReal) \
and actual_type.kind.startswith('Real'): and actual_type.kind.startswith('Real'):
return return
elif isinstance(primary, ogAST.PrimBoolean) \ elif isinstance(primary, ogAST.PrimBoolean) \
and actual_type.kind.startswith('Boolean'): and actual_type.kind.startswith('Boolean'):
return return
...@@ -631,9 +651,18 @@ def compare_types(type_a, type_b): ...@@ -631,9 +651,18 @@ def compare_types(type_a, type_b):
'StringType'): 'StringType'):
# Allow Octet String values to be printable strings.. for convenience # Allow Octet String values to be printable strings.. for convenience
return return
elif not(type_a.kind in ('IntegerType', 'Integer32Type', 'RealType') and elif not(type_a.kind in ('IntegerType', 'Integer32Type') and
type_b.kind in ('IntegerType', 'Integer32Type', 'RealType')): type_b.kind in ('IntegerType', 'Integer32Type')):
raise TypeError('Int/Real mismatch') raise TypeError('One type is an integer, not the other one')
elif any(side.kind == 'RealType' for side in (type_a, type_b)):
raise TypeError('One type is an REAL, not the other one')
elif all(side.kind.startswith('Integer') for side in (type_a, type_b)) \
or all(side.kind == 'RealType' for side in (type_a, type_b)):
pass # XXX no need for type check here, only at assignments
# if float(type_b.Min) < float(type_a.Min) \
# or float(type_b.Max) > float(type_a.Max):
# raise TypeError('Range [{}..{}] incompatible with range [{}..{}]'
# .format(type_b.Min, type_b.Max, type_a.Min, type_a.Max))
else: else:
return return
...@@ -697,6 +726,17 @@ def find_type(path, context): ...@@ -697,6 +726,17 @@ def find_type(path, context):
break break
else: else:
if main.lower() in SPECIAL_OPERATORS: if main.lower() in SPECIAL_OPERATORS:
param = path[1].get('procParams') or []
if not param:
raise TypeError('Missing parameter in {} operator call'
.format(main))
# Retrieve type of first parameter - all operators need it
first_param = param[0]
first_type = find_variable(first_param.inputString, context)\
if first_param.exprType == UNKNOWN_TYPE else \
first_param.exprType
first_basic = find_basic_type(first_type)
# Special operators require type elaboration # Special operators require type elaboration
if main.lower() == 'present': if main.lower() == 'present':
result = type('present', (object,), result = type('present', (object,),
...@@ -704,33 +744,45 @@ def find_type(path, context): ...@@ -704,33 +744,45 @@ def find_type(path, context):
'EnumValues': {}}) 'EnumValues': {}})
# present(choiceType): must return an enum # present(choiceType): must return an enum
# with elements being the choice options # with elements being the choice options
param, = path[1].get('procParams') or [None] if len(param) != 1:
if not param: raise TypeError('Wrong number of parameters in '
raise TypeError('Missing parameter in PRESENT clause') 'PRESENT clause (only one expected)')
if first_basic.kind != 'ChoiceType':
raise TypeError('PRESENT parameter'
' must be a CHOICE type:' + str(path))
else: else:
check_type = find_variable(param.inputString, context)\ result.EnumValues = first_basic.Children
if param.exprType == UNKNOWN_TYPE else \ elif main.lower() in ('length', 'fix'):
param.exprType if len(param) != 1:
param_type = find_basic_type(check_type) raise TypeError('Wrong number of parameters in {} '
if param_type.kind != 'ChoiceType': 'operator'.format(main))
raise TypeError('PRESENT parameter' # result is an integer type with range of the param type
' must be a CHOICE type:' + str(path)) result = type('fix', (INTEGER,), {'Min': first_basic.Min,
else: 'Max': first_basic.Max})
result.EnumValues = param_type.Children
elif main.lower() in ('length', 'abs', 'fix'):
# XXX length et abs: we must set Min and Max
# and abs may return a RealType, not always integer
result = INTEGER
# type('lenabs', (object,), {'kind': 'IntegerType'})
elif main.lower() == 'float': elif main.lower() == 'float':
result = REAL if len(param) != 1:
raise TypeError('Wrong number of parameters in {} '
'operator'.format(main))
result = type('float_op', (REAL,), {'Min': first_basic.Min,
'Max': first_basic.Max})
elif main.lower() == 'power': elif main.lower() == 'power':
# Result can be int or real, depending on first param second = param[1]
param = path[1].get('procParams')[0] second_type = find_variable(second.inputString, context)\
check_type = find_variable(param.inputString, context) \ if second.exprType == UNKNOWN_TYPE else \
if param.exprType == UNKNOWN_TYPE else \ second.exprType
param.exprType second_basic = find_basic_type(second_type)
result = find_basic_type(check_type) try:
result = type('Power', (first_basic,),
{'Min':str(pow(float(first_basic.Min),
float(second_basic.Min))),
'Max':str(pow(float(first_basic.Max),
float(second_basic.Max)))})
except OverflowError:
raise TypeError('Result can exceeds 64-bits')
elif main.lower() == 'abs':
result = type('Abs', (first_basic,),
{'Min':str(max(float(first_basic.Min), 0)),
'Max':str(max(float(first_basic.Max), 0))})
else: # write and writeln return void else: # write and writeln return void
pass pass
if result.kind == 'ReferenceType': if result.kind == 'ReferenceType':
...@@ -793,7 +845,6 @@ def fix_expression_types(expr, context): ...@@ -793,7 +845,6 @@ def fix_expression_types(expr, context):
and isinstance(uk_expr, ogAST.PrimPath) \ and isinstance(uk_expr, ogAST.PrimPath) \
and len(uk_expr.value) == 1: and len(uk_expr.value) == 1:
try: try:
#exprType = find_variable(uk_expr.inputString, context)
exprType = find_variable(uk_expr.value[0], context) exprType = find_variable(uk_expr.value[0], context)
# Differentiate DCL and FPAR variables # Differentiate DCL and FPAR variables
use_type = ogAST.PrimVariable use_type = ogAST.PrimVariable
...@@ -901,8 +952,6 @@ def fix_expression_types(expr, context): ...@@ -901,8 +952,6 @@ def fix_expression_types(expr, context):
expr.right.value['value'] = check_expr.right expr.right.value['value'] = check_expr.right
elif isinstance(expr.right, ogAST.PrimIfThenElse): elif isinstance(expr.right, ogAST.PrimIfThenElse):
for det in ('then', 'else'): for det in ('then', 'else'):
#if expr.right.value[det].exprType == UNKNOWN_TYPE:
# expr.right.value[det].exprType = expr.left.exprType
# Recursively fix possibly missing types in the expression # Recursively fix possibly missing types in the expression
check_expr = ogAST.ExprAssign() check_expr = ogAST.ExprAssign()
check_expr.left = ogAST.PrimPath() check_expr.left = ogAST.PrimPath()
...@@ -934,12 +983,23 @@ def fix_expression_types(expr, context): ...@@ -934,12 +983,23 @@ def fix_expression_types(expr, context):
else: else:
raise TypeError('Bitwise operators only work with ' raise TypeError('Bitwise operators only work with '
'booleans and arrays of booleans') 'booleans and arrays of booleans')
if expr.right.is_raw != expr.left.is_raw: if expr.right.is_raw != expr.left.is_raw:
check_type_compatibility(raw_expr, ref_type, context) check_type_compatibility(raw_expr, ref_type, context)
raw_expr.exprType = ref_type if not raw_expr.exprType.kind.startswith(('Integer', 'Real')):
# Raw int/real must keep their type because of the range
# that can be computed
raw_expr.exprType = ref_type
else: else:
compare_types(expr.left.exprType, expr.right.exprType) compare_types(expr.left.exprType, expr.right.exprType)
if isinstance(expr, ogAST.ExprAssign):
# Assignment with numerical value: check range