Commit 4244eab6 authored by Maxime Perrotin's avatar Maxime Perrotin
Browse files

Merge branch 'master' of https://github.com/esa/opengeode

parents 0be24fee 5c402339
......@@ -135,6 +135,19 @@ The background pattern was downloaded from www.subtlepatterns.com
Changelog
=========
2.0.14 (06/2018)
- fix numerical checks when setting timer parameters
2.0.13 (06/2018)
- Add taste-compatible cache mechanism when calling asn1scc
2.0.12 (06/2018)
- Fix resolution of ASN.1 constants - values were not use propertly when
a constant was referencing another constant (numerical operations only)
2.0.11 (06/2018)
- Ada backend: fix choice determinant issue leading to CHOICE_NOT_FOUND bug
2.0.10 (06/2018)
- Various fixes in statechart rendering, esp. from command line
......
......@@ -2433,8 +2433,10 @@ def _choiceitem(choice):
# is a namespace conflict)
basic = find_basic_type(choice.exprType)
prefix = 'CHOICE_NOT_FOUND'
search = choice.value['choice'].lower().replace('-', '_')
for each in basic.Children:
if each.lower() == choice.value['choice'].lower():
curr_choice = each.lower().replace('-', '_')
if curr_choice == search:
prefix = basic.Children[each].EnumID
break
ada_string = u'(Kind => {kind}, {opt} => {expr})'.format(
......
......@@ -4,7 +4,7 @@
"""
Python API for the ASN1Scc compiler
Copyright (c) 2013-2016 European Space Agency
Copyright (c) 2013-2018 European Space Agency
Designed and implemented by Maxime Perrotin
......@@ -81,20 +81,39 @@ def parse_asn1(*files, **options):
This function uses QProcess to launch the ASN.1 compiler because
the subprocess module from Python has issues on the Windows platform
'''
# use basic caching to avoid re-parsing when loading the model
project_cache = os.getenv ("PROJECT_CACHE")
if project_cache is not None and not os.path.isdir(project_cache):
raise TypeError (
"The configured cache folder \""
+ project_cache + "\" is not there!\n")
# make sure the same files are not parsed more than once if not modified
filehash = hashlib.md5()
file_list = list(*files)
file_list = sorted(list(*files))
try:
for each in file_list:
filehash.update(open(each).read())
# also hash the file path: it is used in the AST, so it is
# not enough to hash the content of the ASN.1 files, as two sets
# of input files may have the same hash
filehash.update(each)
except IOError as err:
raise TypeError (str(err))
new_hash = filehash.hexdigest()
fileset = "".join(file_list)
if fileset in AST.viewkeys() and AST[fileset]['hash'] == new_hash:
return AST[fileset]['ast']
else:
AST[fileset] = {'hash': new_hash}
# names of the files that will be generated by asn1scc and then parsed
out_py_name = new_hash + ".py"
out_html_name = new_hash + ".html"
if new_hash in AST.viewkeys():
return AST[new_hash]
elif project_cache is not None:
outdir = project_cache
elif project_cache is None:
outdir = tempfile.mkdtemp()
# to allow the import
sys.path.append(outdir)
ast_version = options.get('ast_version', ASN1.UniqueEnumeratedNames)
rename_policy = options.get('rename_policy', ASN1.NoRename)
......@@ -117,45 +136,46 @@ def parse_asn1(*files, **options):
binary = path_to_asn1scc
arg0 = ''
asn1scc_root = os.path.abspath(os.path.dirname(path_to_asn1scc))
# Create a temporary directory to store dataview.py and import it
tempdir = tempfile.mkdtemp()
sys.path.append(tempdir)
if os.name == 'nt':
# On windows, remove the drive letter, workaround to ASN1SCC bug
tempdir = tempdir[2:]
outdir = outdir[2:]
asn1scc_root = asn1scc_root[2:]
filename = str(uuid.uuid4()).replace('-', '_')
filepath = tempdir + os.sep + filename + '.py'
stg = asn1scc_root + os.sep + 'python.stg'
if pprint:
# Generate an html file with pretty-printed ASN.1 types
stg_qrc = QFile(':misc/pretty_print_asn1.stg')
stg_qrc.open(1)
content = stg_qrc.readAll()
stgfile = tempdir + os.sep + 'pretty_print_asn1.stg'
with open(stgfile, 'w') as tmpfile:
tmpfile.write(content.data())
out_html = tempdir + os.sep + 'dataview.html'
html = ['-customIcdUper', stgfile + '::' + out_html]
else:
html = []
args = [arg0, '-customStgAstVersion', str(ast_version.value),
'-customStg', stg + '::' + filepath,
'-renamePolicy', str(rename_policy.value)] + html + list(*files)
asn1scc = QProcess()
LOG.debug(os.getcwd())
LOG.debug(binary + ' ' + ' '.join(args))
asn1scc.start(binary, args)
_ = waitfor_qprocess(asn1scc, "ASN.1 Compiler")
ast = importlib.import_module(filename)
AST[fileset]['ast'] = ast
# The two possible files that can be generated with complete path:
py_filepath = outdir + os.sep + out_py_name
html_filepath = outdir + os.sep + out_html_name
# call the ASN.1 compiler only if there is no existing cached file
if project_cache is None or not os.path.exists(py_filepath):
stg = asn1scc_root + os.sep + 'python.stg'
if pprint:
# Generate an html file with pretty-printed ASN.1 types
stg_qrc = QFile(':misc/pretty_print_asn1.stg')
stg_qrc.open(1)
content = stg_qrc.readAll()
stgfile = outdir + os.sep + 'pretty_print_asn1.stg'
with open(stgfile, 'w') as tmpfile:
tmpfile.write(content.data())
html = ['-customIcdUper', stgfile + '::' + html_filepath]
else:
html = []
args = [arg0, '-customStgAstVersion', str(ast_version.value),
'-customStg', stg + '::' + py_filepath,
'-renamePolicy', str(rename_policy.value)] + html + file_list
asn1scc = QProcess()
LOG.debug(os.getcwd())
LOG.debug(binary + ' ' + ' '.join(args))
asn1scc.start(binary, args)
_ = waitfor_qprocess(asn1scc, "ASN.1 Compiler")
ast = importlib.import_module(new_hash)
AST[new_hash] = ast
if pprint:
# add the path to the optionally-gernated pretty-printed HTML file
ast.html = out_html
# add the path to the optionally-generated pretty-printed HTML file
ast.html = html_filepath
return ast
......
......@@ -86,6 +86,9 @@ TMPVAR = 0 # type: int
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'})
......@@ -122,7 +125,7 @@ SPECIAL_OPERATORS = {
'reset_timer': [{'type': TIMER, 'direction': 'in'}],
'round': [{'type': REAL, 'direction': 'in'}],
'set_timer': [
{'type': INTEGER, 'direction': 'in'},
{'type': UNSIGNED, 'direction': 'in'},
{'type': TIMER, 'direction': 'in'}
],
'sin': [{'type': REAL, 'direction': 'in'}],
......@@ -529,13 +532,17 @@ def check_call(name, params, context):
expr.right = param
try:
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)
warnings.extend(fix_expression_types(expr, context))
params[idx] = expr.right
except TypeError:
except TypeError as err:
expected = type_name(sign[idx]['type'])
received = type_name(expr.right.exprType)
raise TypeError('Expected type {} in call to {} ({} received)'.
format(expected, name, received))
raise TypeError('In call to {}: Type of parameter {} is incorrect'
' ({}) - {}'
.format(name, idx+1, received, str(err)))
if (warnings):
expected = type_name(sign[idx]['type'])
received = type_name(expr.right.exprType)
......@@ -933,7 +940,6 @@ def compare_types(type_a, type_b): # type -> [warnings]
return warnings
# Check if both types have basic compatibility
if type_a.kind == type_b.kind:
if type_a.kind == 'SequenceOfType':
if mismatch:
......@@ -956,12 +962,13 @@ def compare_types(type_a, type_b): # type -> [warnings]
# TODO: Check that OctetString types have compatible range
elif type_a.kind == 'SequenceType' and mismatch:
raise TypeError(mismatch)
elif type_a.kind == 'IntegerType' and mismatch:
elif type_a.kind == 'IntegerType':
# Detect Signed/Unsigned type mismatch
min_a, min_b = float(type_a.Min), float(type_b.Min)
if (min_a >= 0) != (min_b >= 0):
raise TypeError(mismatch + "(signed vs unsigned type)")
else:
raise TypeError("Signed vs Unsigned type mismatch " +
mismatch)
elif mismatch:
warnings.append(mismatch)
elif mismatch:
warnings.append(mismatch)
......@@ -1374,6 +1381,27 @@ def arithmetic_expression(root, context):
# accordingly with the kind of operation used between operands:
left = find_basic_type(expr.left.exprType)
right = find_basic_type(expr.right.exprType)
def get_constant_value(const_val):
# value may be a reference to another constant. In that case we
# must find the actual value by following the path until we find it
# however, stop after 20 trials to avoid looping forever in case
# there is some circular dependency or other weird asn1 construct
first_str = const_val
retry = 0
while retry < 20:
try:
return float(const_val)
except ValueError:
possible_constant = is_asn1constant(const_val)
if possible_constant is not None:
const_val = possible_constant.value
else:
# Exceptional case - should be caught by asn1scc
raise ValueError(str(first_str) + " could not be resolved")
retry += 1
raise ValueError(str(first_str) + " actual value not found" )
try:
minL = float(left.Min)
maxL = float(left.Max)
......@@ -1381,9 +1409,9 @@ def arithmetic_expression(root, context):
maxR = float(right.Max)
# Constants defined in ASN.1 : take their value for the range
if isinstance(expr.left, ogAST.PrimConstant):
minL = maxL = float (expr.left.constant_value)
minL = maxL = get_constant_value(expr.left.constant_value)
if isinstance(expr.right, ogAST.PrimConstant):
minR = maxR = float (expr.right.constant_value)
minL = maxL = get_constant_value(expr.right.constant_value)
# Type of the resulting expression depends on whether there are raw
# numbers on one side of the expression (PrInt). By default when they
# are parsed, they are set to 64 bits integers ; but if they are in an
......@@ -1421,6 +1449,7 @@ def arithmetic_expression(root, context):
except (ValueError, AttributeError):
msg = 'Check that all your numerical data types '\
'have a range constraint'
#print (traceback.format_exc())
errors.append(error(root, msg))
if root.type in (lexer.REM, lexer.MOD):
......
......@@ -141,7 +141,7 @@ except ImportError:
__all__ = ['opengeode', 'SDL_Scene', 'SDL_View', 'parse']
__version__ = '2.0.10'
__version__ = '2.0.14'
if hasattr(sys, 'frozen'):
# Detect if we are running on Windows (py2exe-generated)
......
......@@ -32,6 +32,8 @@ myBool MyBoolean ::= TRUE
myInt TASTE-Peek-id ::= 5
myInt2 TASTE-Peek-id ::= myInt
myString VariableString ::= 'DEADBEEF'H
mySeqBool SeqBool ::= { TRUE, FALSE }
......
/* CIF PROCESS (295, 56), (150, 75) */
PROCESS orchestrator
/* CIF COMMENT (405, 192), (71, 35) */
COMMENT 'Hello';
/* CIF TEXT (649, 323), (294, 140) */
process orchestrator
/* CIF comment (405, 192), (71, 35) */
comment 'Hello';
/* CIF TEXT (649, 323), (294, 143) */
-- Text area for declarations and comments
dcl toto NamedInt := one;
dcl tutu Enum_with_dash := first_one;
dcl titi NamedInt_with_dash := second_value;
dcl toto NamedInt := one;
dcl tutu Enum_with_dash := first_one;
dcl titi NamedInt_with_dash := second_value;
dcl recursive TASTE_Peek_id := myInt2;
/* CIF ENDTEXT */
/* CIF START (530, 150), (80, 36) */
START;
/* CIF NEXTSTATE (512, 201), (116, 33) */
NEXTSTATE Wait_for_GUI;
/* CIF STATE (1063, 119), (116, 33) */
STATE Wait_for_GUI;
ENDSTATE;
/* CIF STATE (512, 201), (116, 33) */
STATE Wait_for_GUI;
/* CIF INPUT (529, 254), (82, 35) */
INPUT pulse(titi);
/* CIF state (1063, 119), (116, 33) */
state Wait_for_GUI;
endstate;
/* CIF state (512, 201), (116, 33) */
state Wait_for_GUI;
/* CIF input (529, 254), (82, 35) */
input pulse(titi);
/* CIF PROCEDURECALL (522, 309), (96, 35) */
CALL writeln(titi);
/* CIF OUTPUT (508, 359), (123, 35) */
OUTPUT telemetry(tutu);
/* CIF TASK (501, 414), (138, 35) */
TASK tutu := second_one;
/* CIF NEXTSTATE (535, 464), (70, 35) */
call writeln(titi);
/* CIF output (508, 359), (123, 35) */
output telemetry(tutu);
/* CIF task (501, 414), (138, 35) */
task tutu := second_one;
/* CIF task (492, 469), (156, 35) */
task recursive := 1 + myInt2;
/* CIF NEXTSTATE (535, 519), (70, 35) */
NEXTSTATE -;
ENDSTATE;
ENDPROCESS orchestrator;
\ No newline at end of file
endstate;
endprocess orchestrator;
\ No newline at end of file
include ../shared.mk
ROOT_MODEL=test.pr
all: test-ada test-llvm
edit:
$(OPENGEODE) test.pr
test-parse:
$(OPENGEODE) test.pr --check
test-qgen-parse:
$(TESTQGEN_PARSE) $(ROOT_MODEL)
test-qgen-ada:
$(TESTQGEN_ADA) $(ROOT_MODEL)
test-qgen-c:
$(TESTQGEN_C) $(ROOT_MODEL)
test-qgen-gt-ada:
$(TESTQGEN_GT_ADA) $(ROOT_MODEL)
test-qgen-gt-c:
$(TESTQGEN_GT_C) $(ROOT_MODEL)
test-ada:
$(OPENGEODE) test.pr --toAda
mono $(ASN1SCC) -Ada -typePrefix asn1Scc -equal dataview.asn
cp toto_timer.adasource toto_timer.ads
cp toto_timer.adabody toto_timer.adb
gnat make test_ada.ada
simu:
$(OPENGEODE) test.pr --shared
./test_simu.sh
test-c:
$(OPENGEODE) test.pr --toC
mono $(ASN1SCC) -c -typePrefix asn1Scc -equal dataview.asn
$(CC) -O$(O) -c *.c
test-llvm:
$(OPENGEODE) test.pr --llvm -O$(O)
$(LLC) *.ll
$(CC) -O$(O) -c *.s
coverage:
coverage run -p $(OPENGEODE) test.pr --toAda
.PHONY: all edit test-parse test-ada test-llvm coverage simu
TASTE-Dataview DEFINITIONS ::=
BEGIN
T-UInt32 ::= INTEGER (0..65535)
Signed-Int ::= INTEGER (-1000..1000)
Unsigned-Int ::= INTEGER (0..100)
END
system test;
/* CIF TEXT (164, 303), (356, 219) */
-- Text area for declarations and comments
use datamodel comment 'dataview.asn';
signal blah;
/* CIF ENDTEXT */
channel c
from env to test with blah;
endchannel;
block test;
signalroute r
from env to test with blah;
connect c and r;
/* CIF PROCESS (202, 142), (150, 75) */
process test;
/* CIF TEXT (766, 271), (287, 140) */
timer toto;
dcl s Signed_Int := 10;
dcl us Unsigned_Int := 100;
/* CIF ENDTEXT */
/* CIF START (524, 139), (70, 35) */
START;
/* CIF NEXTSTATE (524, 194), (70, 35) */
NEXTSTATE wait;
/* CIF state (642, 470), (81, 35) */
state timer_run;
/* CIF input (648, 525), (70, 35) */
input toto;
/* CIF NEXTSTATE (648, 580), (70, 35) */
NEXTSTATE wait;
endstate;
/* CIF state (524, 194), (70, 35) */
state wait;
/* CIF input (524, 249), (70, 35) */
input blah;
/* CIF PROCEDURECALL (482, 304), (153, 35) */
call set_timer (100, toto);
/* CIF PROCEDURECALL (480, 359), (158, 35) */
call set_timer (-100, toto);
/* CIF PROCEDURECALL (488, 414), (142, 35) */
call set_timer(us, toto);
/* CIF NEXTSTATE (518, 469), (82, 35) */
NEXTSTATE timer_run;
endstate;
endprocess test;
endblock;
endsystem;
\ No newline at end of file
with text_io, test, taste_dataview, ada.real_time, toto_timer;
use text_io, test, taste_dataview, ada.real_time;
procedure test_ada is
begin
put_line ("hello");
test.blah;
end;
with Ada.Real_Time;
use Ada.Real_Time;
package body Toto_Timer is
procedure Set_Toto (Dur : asn1SccT_UInt32) is
begin
delay To_Duration (Milliseconds (Integer(Dur)));
end Set_Toto;
end Toto_Timer;
with Taste_Dataview;
use Taste_Dataview;
package Toto_Timer is
procedure Set_Toto (Dur : asn1SccT_UInt32)
with Export, Convention => C, Link_Name => "test_RI_set_toto";
end Toto_Timer;
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment