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
import tempfile
import uuid
import os
import distutils.spawn as spawn
import sys
import importlib
import logging
import PySide.QtCore as Qt
import icons
LOG = logging.getLogger(__name__)
......@@ -57,20 +57,21 @@ def parse_asn1(*files, **options):
assert isinstance(ast_version, ASN1)
assert isinstance(flags, list)
asn1scc_root = os.path.dirname(spawn.find_executable('asn1.exe'))
tempdir = tempfile.mkdtemp()
filename = str(uuid.uuid4()).replace('-', '_')
filepath = tempdir + os.sep + filename + '.py'
# dump python.stg in the temp directory
stg = Qt.QFile(':misc/python.stg')
stg_data = stg.readData(stg.size())
tmp_stg = tempdir + os.sep + 'python.stg'
with open(tmp_stg, 'w') as fd:
fd.write(stg_data)
#stg = Qt.QFile(':misc/python.stg')
#stg_data = stg.readData(stg.size())
stg = asn1scc_root + os.sep + 'python.stg'
#with open(tmp_stg, 'w') as fd:
# fd.write(stg_data)
args = ['asn1.exe',
'-customStgAstVerion', str(ast_version.value),
'-customStg', tmp_stg + ':' + filepath] + list(*files)
'-customStg', stg + ':' + filepath] + list(*files)
LOG.debug('Calling: ' + ' '.join(args))
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):
class ExprRem(Expression):
operand = 'mod'
operand = 'rem'
class ExprAssign(Expression):
......
......@@ -75,7 +75,9 @@ TMPVAR = 0
# ASN.1 types used to support the signature of special operators
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'})
TIMER = type('TimerType', (object,), {'kind': 'TimerType'})
REAL = type('RealType', (object,), {'kind': 'RealType'})
......@@ -427,6 +429,22 @@ def check_and_fix_op_params(op_name, expr_list, context):
.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):
'''
Check if an ogAST.Primary (raw value, enumerated, ASN.1 Value...)
......@@ -486,9 +504,11 @@ def check_type_compatibility(primary, typeRef, context):
elif isinstance(primary, ogAST.PrimInteger) \
and actual_type.kind.startswith('Integer'):
return
elif isinstance(primary, ogAST.PrimReal) \
and actual_type.kind.startswith('Real'):
return
elif isinstance(primary, ogAST.PrimBoolean) \
and actual_type.kind.startswith('Boolean'):
return
......@@ -631,9 +651,18 @@ def compare_types(type_a, type_b):
'StringType'):
# Allow Octet String values to be printable strings.. for convenience
return
elif not(type_a.kind in ('IntegerType', 'Integer32Type', 'RealType') and
type_b.kind in ('IntegerType', 'Integer32Type', 'RealType')):
raise TypeError('Int/Real mismatch')
elif not(type_a.kind in ('IntegerType', 'Integer32Type') and
type_b.kind in ('IntegerType', 'Integer32Type')):
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:
return
......@@ -697,6 +726,17 @@ def find_type(path, context):
break
else:
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
if main.lower() == 'present':
result = type('present', (object,),
......@@ -704,33 +744,45 @@ def find_type(path, context):
'EnumValues': {}})
# present(choiceType): must return an enum
# with elements being the choice options
param, = path[1].get('procParams') or [None]
if not param:
raise TypeError('Missing parameter in PRESENT clause')
if len(param) != 1:
raise TypeError('Wrong number of parameters in '
'PRESENT clause (only one expected)')
if first_basic.kind != 'ChoiceType':
raise TypeError('PRESENT parameter'
' must be a CHOICE type:' + str(path))
else:
check_type = find_variable(param.inputString, context)\
if param.exprType == UNKNOWN_TYPE else \
param.exprType
param_type = find_basic_type(check_type)
if param_type.kind != 'ChoiceType':
raise TypeError('PRESENT parameter'
' must be a CHOICE type:' + str(path))
else:
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'})
result.EnumValues = first_basic.Children
elif main.lower() in ('length', 'fix'):
if len(param) != 1:
raise TypeError('Wrong number of parameters in {} '
'operator'.format(main))
# result is an integer type with range of the param type
result = type('fix', (INTEGER,), {'Min': first_basic.Min,
'Max': first_basic.Max})
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':
# Result can be int or real, depending on first param
param = path[1].get('procParams')[0]
check_type = find_variable(param.inputString, context) \
if param.exprType == UNKNOWN_TYPE else \
param.exprType
result = find_basic_type(check_type)
second = param[1]
second_type = find_variable(second.inputString, context)\
if second.exprType == UNKNOWN_TYPE else \
second.exprType
second_basic = find_basic_type(second_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
pass
if result.kind == 'ReferenceType':
......@@ -793,7 +845,6 @@ def fix_expression_types(expr, context):
and isinstance(uk_expr, ogAST.PrimPath) \
and len(uk_expr.value) == 1:
try:
#exprType = find_variable(uk_expr.inputString, context)
exprType = find_variable(uk_expr.value[0], context)
# Differentiate DCL and FPAR variables
use_type = ogAST.PrimVariable
......@@ -901,8 +952,6 @@ def fix_expression_types(expr, context):
expr.right.value['value'] = check_expr.right
elif isinstance(expr.right, ogAST.PrimIfThenElse):
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
check_expr = ogAST.ExprAssign()
check_expr.left = ogAST.PrimPath()
......@@ -934,12 +983,23 @@ def fix_expression_types(expr, context):
else:
raise TypeError('Bitwise operators only work with '
'booleans and arrays of booleans')
if expr.right.is_raw != expr.left.is_raw:
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:
compare_types(expr.left.exprType, expr.right.exprType)
if isinstance(expr, ogAST.ExprAssign):
# Assignment with numerical value: check range
basic = find_basic_type(expr.left.exprType)
if basic.kind.startswith(('Integer', 'Real')):
check_range(basic, find_basic_type(expr.right.exprType))
def expression_list(root, context):
''' Parse a list of expression parameters '''
......@@ -1231,14 +1291,39 @@ def expression(root, context):
lexer.LE,
lexer.IN):
expr.exprType = BOOLEAN
# elif root.type == lexer.PLUS:
# Adjust type constraint upper range with sum of expressions
# Create a new type to store the new constraint
# INCORRECT TO DO IT HERE - we must check right and left types FIRST
# expr.exprType = type('Plus_Type',
# (find_basic_type(expr.left.exprType),), {})
# expr.exprType.Max = str(int(find_basic_type(expr.left.exprType).Max) +
# int(find_basic_type(expr.right.exprType).Max))
# Expressions returning a numerical type must have their range defined
# accordingly with the kind of opration used between operand:
elif root.type in (lexer.PLUS, lexer.ASTERISK, lexer.DASH,
lexer.DIV, lexer.MOD, lexer.REM):
basic = find_basic_type(expr.left.exprType)
left = find_basic_type(expr.left.exprType)
right = find_basic_type(expr.right.exprType)
try:
if isinstance(expr, ogAST.ExprPlus):
attrs = {'Min': str(float(left.Min) + float(right.Min)),
'Max': str(float(left.Max) + float(right.Max))}
expr.exprType = type('Plus', (basic,), attrs)
elif isinstance(expr, ogAST.ExprMul):
attrs = {'Min': str(float(left.Min) * float(right.Min)),
'Max': str(float(left.Max) * float(right.Max))}
expr.exprType = type('Mul', (basic,), attrs)
elif isinstance(expr, ogAST.ExprMinus):
attrs = {'Min': str(float(left.Min) - float(right.Min)),
'Max': str(float(left.Max) - float(right.Max))}
expr.exprType = type('Minus', (basic,), attrs)
elif isinstance(expr, ogAST.ExprDiv):
attrs = {'Min': str(float(left.Min) / (float(right.Min) or 1)),
'Max': str(float(left.Max) / (float(right.Max) or 1))}
expr.exprType = type('Div', (basic,), attrs)
elif isinstance(expr, (ogAST.ExprMod, ogAST.ExprRem)):
attrs = {'Min': right.Min, 'Max': right.Max}
expr.exprType = type('Mod', (basic,), attrs)
except ValueError:
errors.append('Check that all your numerical data types have '
'a range constraint')
elif root.type in (lexer.OR, lexer.AND, lexer.XOR):
# in the case of bitwise operators, if both sides are arrays,
# then the result is an array too
......@@ -2842,7 +2927,6 @@ def for_loop(root, context):
forloop['var'] = child.text
# Implicit variable declaration for the iterator
context_scope = dict(context.variables)
#context.variables[child.text] = (INT32, 0)
elif child.type == lexer.VARIABLE:
list_var, err, warn = primary_value(child, context)
forloop['list'] = ogAST