Commit 299496c6 authored by Maxime Perrotin's avatar Maxime Perrotin

Merge https://github.com/esa/opengeode into python3-pyside2

parents 9559e025 2c9f5c60
......@@ -58,7 +58,9 @@ full-install: update
$(MAKE) install
publish:
@python3 setup.py sdist upload
@rm -f dist/*
@python3 setup.py sdist bdist_wheel
@twine upload dist/*
pytest:
# make sure you have installed pytest-qt:
......
......@@ -53,50 +53,17 @@ Debian 10 (buster) is the baseline. Recent versions of Ubuntu (20.x) should work
Using TASTE
-----------
__Important: OpenGEODE is already installed in the TASTE Virtual Machine, and fully integrated with the toolset, however, the current TASTE VM is a bit old - it is based on Debian 9 which does not have important dependencies to support the latest version of OpenGEODE. It is missing Python 3.7+ and PySide2. An upgrade is in the works, but it's not ready yet. If you are familiar enough with Linux, you can manually upgrade by completing the following steps.__
__Important: OpenGEODE is already installed in the TASTE10 Virtual Machine (based on Debian Buster), and fully integrated with the toolset. It is the easiest way to get started with OpenGEODE__
_Install the [TASTE VM](https://taste.tools/#install). Once logged in, enter a new shell and:_
To start a new project run:
```
# Switch to root
sudo su
# Make sure VM image is up to date
apt-get update
apt-get upgrade
apt-get dist-upgrade
# Point APT to Buster to prepare for upgrade
sed -i 's/stretch/buster/g' /etc/apt/sources.list
# Upgrade packages to Buster; when prompted allow services to be restarted automatically
apt-get upgrade
# Upgrade distribution to Buster
apt-get dist-upgrade
# Remove packages no longer needed
apt-get autoremove
# Return to taste user; exit root
exit
# Navigate to TASTE source; upgrade
cd ~/tool-src
git pull
git checkout feature_buster
./Update-TASTE.sh
$ taste
```
After this upgrade, you can work with the latest version of the tools, in particular the new Kazoo build system and Opengeode 3.xx (which you can update at any time). The Quick Reference Card has not been updated to reflect this yet.
The main differences to create/edit a project is that you must just run `taste<` (and not `taste-create-project/taste-edit-project` anymore). To build, run `make`.
Select a project name and the graphical editor will pop-up shortly after. You can add functions to the system and specify the imnplementation language to __SDL__. When you edit the function, the OpenGEODE editor will start.
You can check an example of a system using Opengeode if you go in `~/tool-src/kazoo/tests/Demo_ABB_Opengeode` and run `make` to build it. Then `taste` to edit.
In the interface view, select the SDL language for the implementation of the blocks you want to model using OpenGEODE.
Then when you right-click on the SDL block you can select the option "Open SDL Editor".
The code is automatically generated when you exit the tool.
Manual
......@@ -138,7 +105,7 @@ Once you have the dependencies installed you can update the tool by running the
```
$ git pull
$ make install # alternatively: pip3 install --user --upgrade .
$ make install # alternatively: pip3 install --user --upgrade opengeode
```
OpenGEODE Website
......@@ -175,6 +142,18 @@ The background pattern was downloaded from www.subtlepatterns.com
Changelog
=========
**3.3.2 (10/2020)**
- Fix reporting of semantic errors in procedures
**3.3.1 (09/2020)**
- Fix issue with type synonyms
- Update installation procedure
- Enable pip3 installations from PyPI
**3.3.0 (08/2020)**
- Save the state as an ASN.1 model instead of a native Ada type
- Ada backend basic support for "decision any"
**3.2.3 (09/2020)
- Fix type checks when a type inherits another type with different constraints
......
This diff is collapsed.
......@@ -849,6 +849,8 @@ class Procedure:
# input/output signal lists - unused but for context information
self.input_signals = []
self.output_signals = []
# The "DECISION ANY" construct requires random number generators
self.random_generator = set()
class Process:
......@@ -935,6 +937,9 @@ class Process:
# list of processes used for context management of the GUI
self.processes = []
# The "DECISION ANY" construct requires random number generators
self.random_generator = set()
class CompositeState(Process):
'''
......
......@@ -1194,9 +1194,6 @@ def check_type_compatibility(primary, type_ref, context):
raise TypeError(f'Type of array element {primary.inputString} '
f'does not match expected type "{type_name(type_ref)}"')
else:
print( isinstance(primary, ogAST.PrimIndex))
print( primary.exprType != type_ref)
print (f"{primary} {primary.exprType} vs {type_ref}")
raise TypeError('{prim} does not match type {t1}'
.format(prim=primary.inputString,
t1=type_name(type_ref)))
......@@ -1864,7 +1861,7 @@ def arithmetic_expression(root, context):
if (not is_number(basic_left)
and basic_left.kind == 'Integer32Type') or (not is_number
(basic_right) and basic_right.kind == 'Integer32Type'):
# One of the operand is a loop index (SIGNED)
# One of the operands is a loop index (SIGNED)
kind = 'Integer32Type'
expr.exprType = type("Computed_Range",
(basic_right if not is_number(basic_right)
......@@ -1902,7 +1899,14 @@ def arithmetic_expression(root, context):
bound_max = str(float(bounds['Max']))
attrs = {'Min': bound_min, 'Max': bound_max}
expr.exprType = type('Computed_Range_2', (basic_right,), attrs)
# If a side is an Integer32 and the other side a Integer64
# then the base type should be Integer64
if (basic_right.kind == basic_left.kind
or basic_right.kind == "IntegerType"):
base_type = basic_right # Any is ok
else:
base_type = basic_left
expr.exprType = type('Computed_Range_2', (base_type,), attrs)
if expr.exprType is not UNKNOWN_TYPE:
expr.expected_type = expr.exprType
......@@ -2284,6 +2288,7 @@ def primary_index(root, context, pos):
expression(root.children[0], context, pos)
receiver_bty = find_basic_type(receiver.exprType)
errors.extend(receiver_err)
warnings.extend(receiver_warn)
......@@ -2921,7 +2926,9 @@ def procedure_pre(root, parent=None, context=None):
elif child.type == lexer.TEXTAREA:
textarea, err, warn = text_area(child, context=proc)
if textarea.signals:
errors.append('Signals shall not be declared in a procedure')
errors.append([f'In procedure {proc.inputString}:'
' signals shall not be declared in a procedure',
[textarea.pos_x or 0, textarea.pos_y or 0], []])
errors.extend(err)
warnings.extend(warn)
proc.content.textAreas.append(textarea)
......@@ -2933,10 +2940,15 @@ def procedure_pre(root, parent=None, context=None):
warnings.extend(warn)
proc.fpar = params
elif child.type == lexer.RETURNS:
# Declaration not in a text area...
if proc.return_type is not None:
errors.append([f'In procedure {proc.inputString}: '
'duplicate "returns" statement', [0, 0], []])
try:
proc.return_type, proc.return_var = procedure_returns(child)
except TypeError as err:
errors.append(str(err))
errors.append([f"In procdure {proc.inputString}: {str(err)}",
[0, 0], []])
if proc.return_var:
warnings.append('Procedure return variable not supported')
elif child.type in (lexer.PROCEDURE, lexer.START,
......@@ -2948,6 +2960,8 @@ def procedure_pre(root, parent=None, context=None):
sdl92Parser.tokenNamesMap[child.type] +
' - line ' + str(child.getLine()) +
' - in procedure ' + str(proc.inputString))
for each in chain(errors, warnings):
each[2].insert(0, f'PROCEDURE {proc.inputString}')
return proc, content, errors, warnings
......@@ -3033,18 +3047,17 @@ def procedure_post(proc, content, parent=None, context=None):
try:
warnings.extend(fix_expression_types(check_expr, context))
except (TypeError, AttributeError) as err:
errors.append(str(err))
errors.append([f"In procedure {proc.inputString}: {str(err)}",
[0, 0], []])
# Id of fd_expr may have changed (enumerated, choice)
each.return_expr = check_expr.right
elif proc.return_type and not each.return_expr:
errors.append(['Missing return value in procedure {}'
.format(proc.inputString),
elif proc.return_type and each.kind == 'return' and not each.return_expr:
errors.append([f'Missing return value in procedure {proc.inputString}',
[0, 0], []])
else:
continue
for each in chain(errors, warnings):
each[2].insert(0, 'PROCEDURE {}'.format(proc.inputString))
each[2].insert(0, f'PROCEDURE {proc.inputString}')
return errors, warnings
......@@ -3288,6 +3301,8 @@ def text_area_content(root, ta_ast, context):
except AttributeError:
errors.append('Entity cannot have an FPAR section')
elif child.type == lexer.RETURNS:
if context.return_type is not None:
errors.append('Duplicate "returns" statement')
try:
context.return_type, context.return_var =\
procedure_returns(child)
......@@ -4398,6 +4413,8 @@ def decision(root, parent, context):
dec.tmpVar = tmp()
has_else = False
dec_x, dec_y = 0, 0
# To support the "decision any" construct:
need_random_generator = False
for child in root.getChildren():
if child.type == lexer.CIF:
# Get symbol coordinates
......@@ -4419,6 +4436,7 @@ def decision(root, parent, context):
warnings.append(['Use of "ANY" introduces non-determinism ',
[dec.pos_x, dec.pos_y], []])
dec.kind = 'any'
need_random_generator = True
dec.inputString = get_input_string(child)
dec.question = ogAST.PrimStringLiteral()
dec.question.value = 'ANY'
......@@ -4451,6 +4469,10 @@ def decision(root, parent, context):
else:
warnings.append(['Unsupported DECISION child type: ' +
str(child.type), [dec.pos_x, dec.pos_y], []])
if need_random_generator:
# If there are N answers, code generators will need a random
# number from 0 to N.
context.random_generator.add(len(dec.answers))
# Make type checks to be sure that question and answers are compatible
covered_ranges = defaultdict(list)
qmin, qmax = 0, 0
......@@ -5128,8 +5150,16 @@ def for_loop(root, context):
r_min = '0'
else:
basic = find_basic_type(start_expr.exprType)
# ASN.1 constant -> use the constant value
if isinstance(start_expr, ogAST.PrimConstant):
basic.Min = \
get_asn1_constant_value(start_expr.constant_value)
r_min = basic.Min if basic != UNKNOWN_TYPE else '0'
basic = find_basic_type(stop_expr.exprType)
# ASN.1 constant -> get the value in place of the type's Max
if isinstance(stop_expr, ogAST.PrimConstant):
basic.Max = \
get_asn1_constant_value(stop_expr.constant_value)
r_max = str(int(float(basic.Max) - 1)) \
if basic != UNKNOWN_TYPE else '4294967295'
result_type = type('for_range', (INT32,), {'Min': r_min,
......
......@@ -140,7 +140,7 @@ except ImportError:
__all__ = ['opengeode', 'SDL_Scene', 'SDL_View', 'parse']
__version__ = '3.2.3'
__version__ = '3.3.2'
if hasattr(sys, 'frozen'):
# Detect if we are running on Windows (py2exe-generated)
......
......@@ -2,8 +2,8 @@
# -*- coding: utf-8 -*-
'''
Setup file for Linux distribution
Usage: python setup.py sdist --> to create a tarball
python setup.py install --> to install in python directory
Usage: python3 setup.py sdist bdist_wheel --> to create a tarball
pip3 install --user --upgrade . --> to install in ~/.local
'''
# from distutils.core import setup
......@@ -19,15 +19,17 @@ setup(
author_email='maxime.perrotin@esa.int',
description='A free SDL editor for TASTE',
long_description=open('README.md').read(),
long_description_content_type="text/markdown",
install_requires=[],
tests_require=['tabulate'],
include_package_data=True,
url='http://opengeode.net',
python_requires='>=3.7',
classifiers=[
'Programming Language :: Python',
'License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)',
'Operating System :: OS Independent',
'Programming Language :: Python :: 3.7'
'Programming Language :: Python :: 3'
],
entry_points={
'console_scripts': [
......
......@@ -52,14 +52,14 @@ endsystem;
assert False
def test_system_with_error_2():
''' Detect the syntax error (missing SEMI after "procedure entry") '''
''' no error '''
string='''system huuh;
block huuh;
process huuh;
/* CIF START (171, 77), (70, 35) */
START;
/* CIF NEXTSTATE (171, 132), (70, 35) */
NEXTSTATE a-;
NEXTSTATE -;
/* CIF state (386, 90), (70, 35) */
state a;
endstate;
......@@ -79,7 +79,7 @@ endsystem;
err = "\n".join(test.error_list)
raise SyntaxError(err)
else:
assert False
pass
if __name__ == '__main__':
for name, value in dict(globals()).viewitems():
......
......@@ -6,7 +6,7 @@
**/tmp/
**/build/
logs/
*/code/
*/GPS_project.gpr
*/*generated
......@@ -28,3 +28,7 @@
**/*.log
**actual
*/*.xml
**/*.pr.ini
**/asn1_x86.gpr
**/*_datamodel.asn
**/*_ada.gpr
project controller_Ada is
for Languages use ("Ada");
for Source_Dirs use (".") & External_As_List ("CODE_PATH", ":");
for Object_Dir use "../obj";
end controller_Ada;
\ No newline at end of file
......@@ -28,7 +28,7 @@ test-qgen-gt-c:
test-ada:
#make -f Makefile.og
$(OPENGEODE) og.pr --toAda
mono $(ASN1SCC) -Ada -typePrefix asn1Scc -equal dataview.asn
mono $(ASN1SCC) -Ada -typePrefix asn1Scc -equal *.asn
gnat make titi
test-c:
......
project DataView_Ada is
for Languages use ("ASN1");
for Source_Dirs use (".");
for Source_Files use ("dataview.asn");
for Object_Dir use "code";
package Naming is
for Body_Suffix ("ASN1") use ".asn";
end Naming;
package Compiler is
for Driver ("ASN1") use "mono";
for Leading_Required_Switches ("ASN1") use
(external("ASN1SCC"),
"-Ada",
"-typePrefix",
"Asn1Scc");
end Compiler;
end DataView_Ada;
\ No newline at end of file
......@@ -13,11 +13,12 @@ TESTQGEN_GT_ADA=../testqgen.py test-qgen-gt-ada
TESTQGEN_GT_C=../testqgen.py test-qgen-gt-c
clean:
gnat clean *.adb
rm -rf *.adb *.ads *.pyc runSpark.sh spark.idx *.o *.so *.ali gnat.cfg \
examiner bin *.wrn GPS_project.gpr *.ll *.s dataview-uniq.c dataview-uniq.h \
real.c xer.c ber.c acn.c asn1crt.c asn1crt.h test_ada test_llvm \
*.autosave *_simu.sh *_interface.aadl *.lst *.gcno *.gcda *.gcov \
check obj src code
check obj src code *_datamodel.asn asn1_x86.gpr *_ada.gpr
%.o: %.pr FORCE
$(OPENGEODE) $< system_structure.pr --llvm -O$(O)
......@@ -30,8 +31,8 @@ clean:
%.ali: %.pr FORCE
$(OPENGEODE) $< system_structure.pr --toAda && \
mono $(ASN1SCC) -Ada -typePrefix asn1Scc -equal dataview-uniq.asn && \
$(GNATMAKE) -O$(O) -gnat2012 -c -g -fprofile-arcs -ftest-coverage *.adb
mono $(ASN1SCC) -Ada -typePrefix asn1Scc -equal *.asn && \
$(GNATMAKE) -O$(O) -c -g -fprofile-arcs -ftest-coverage *.adb
%.o: %.asn FORCE
mono $(ASN1SCC) -c -typePrefix asn1Scc -equal $<
......
......@@ -27,7 +27,7 @@ test-qgen-gt-c:
test-ada:
$(OPENGEODE) og.pr --toAda
mono $(ASN1SCC) -Ada -typePrefix asn1Scc -equal dataview-uniq.asn
mono $(ASN1SCC) -Ada -typePrefix asn1Scc -equal *.asn
$(GNATMAKE) -O$(O) -c *.adb
$(GNATBIND) -n og.ali
......
......@@ -28,7 +28,7 @@ test-qgen-gt-c:
test-ada:
#make -f Makefile.og
$(OPENGEODE) og.pr --toAda
mono $(ASN1SCC) -Ada -typePrefix asn1Scc -equal dataview-uniq.asn
mono $(ASN1SCC) -Ada -typePrefix asn1Scc -equal *.asn
gnat make og
test-c:
......
project DataView_Ada is
for Languages use ("ASN1");
for Source_Dirs use (".");
for Source_Files use ("dataview-uniq.asn");
for Object_Dir use "code";
package Naming is
for Body_Suffix ("ASN1") use ".asn";
end Naming;
package Compiler is
for Driver ("ASN1") use "mono";
for Leading_Required_Switches ("ASN1") use
(external("ASN1SCC"),
"-Ada",
"-typePrefix",
"Asn1Scc");
end Compiler;
end DataView_Ada;
\ No newline at end of file
project og_Ada is
for Languages use ("Ada");
for Source_Dirs use (".") & External_As_List ("CODE_PATH", ":");
for Object_Dir use "../obj";
end og_Ada;
\ No newline at end of file
......@@ -27,7 +27,7 @@ test-qgen-gt-c:
test-ada:
$(OPENGEODE) og.pr --toAda
mono $(ASN1SCC) -Ada -typePrefix asn1Scc -equal dataview-uniq.asn
mono $(ASN1SCC) -Ada -typePrefix asn1Scc -equal *.asn
$(GNATMAKE) -O$(O) -c *.adb
$(GNATBIND) -n car.ali
......
......@@ -28,7 +28,7 @@ test-qgen-gt-c:
test-ada:
mkdir -p src && cd src && $(OPENGEODE) ../toto.pr -g --toAda && \
cp ../test_toto.ada test_toto.adb && \
mono $(ASN1SCC) -Ada -typePrefix asn1Scc -equal ../dataview.asn
mono $(ASN1SCC) -Ada -typePrefix asn1Scc -equal ../*.asn *.asn
mkdir -p obj && cd obj && gnat make -O$(O) ../src/test_toto && \
./test_toto | diff ../expected -
......
......@@ -27,7 +27,7 @@ test-qgen-gt-c:
test-ada:
$(OPENGEODE) og.pr --toAda
mono $(ASN1SCC) -Ada -typePrefix asn1Scc -equal dataview.asn
mono $(ASN1SCC) -Ada -typePrefix asn1Scc -equal *.asn
$(GNATMAKE) -O$(O) -c *.adb
$(GNATBIND) -n og.ali
......
/* CIF PROCESS (295, 56), (150, 75) */
PROCESS orchestrator
/* CIF COMMENT (405, 192), (71, 35) */
COMMENT 'Hello';
/* CIF TEXT (0, 94), (450, 353) */
process orchestrator
/* CIF comment (405, 192), (71, 35) */
comment 'Hello';
/* CIF TEXT (0, 94), (450, 376) */
dcl seq tastE_Peek_id_list := { 1,2,3,4,5};
dcl fixed FixedString := 'Hello';
dcl variable VariableString := 'Hello';
dcl seqboolean SeqBool := { true, false };
dcl seqbool2 SeqBoolFix := { true, false };
dcl seqen SeqEnum := { hello2, world2};
dcl seqen2 SeqEnumFix := { hello, world };
dcl myenum MyEnum := a;
dcl turlututu MyEnum := a;
dcl check tasTE_Peek_id := 42;
dcl choice Mychoice := c1:TRUE ;
dcl opt SeqOpt := { A TRUE, b FALSE }; -- test optional fields
dcl somebool boolean := true;
-- dcl opt2 SeqOpt := { a somebool, b false }; SHOULD REPORT AND ERROR
dcl fixed FixedString := 'Hello';
dcl variable VariableString := 'Hello';
dcl seqboolean SeqBool := { true, false };
dcl seqbool2 SeqBoolFix := { true, false };
dcl seqen SeqEnum := { hello2, world2};
dcl seqen2 SeqEnumFix := { hello, world };
dcl myenum MyEnum := a;
dcl turlututu MyEnum := a;
dcl check tasTE_Peek_id := 42;
dcl choice Mychoice := c1:TRUE ;
dcl opt SeqOpt := { A TRUE, b FALSE }; -- test optional fields
dcl somebool boolean := true;
-- dcl opt2 SeqOpt := { a somebool, b false }; SHOULD REPORT AND ERROR
/* CIF ENDTEXT */
/* CIF PROCEDURE (1306, 371), (91, 35) */
PROCEDURE numProc;
/* CIF procedure (1273, 175), (106, 35) */
procedure emptyproc;
endprocedure;
/* CIF procedure (1306, 371), (91, 35) */
procedure numProc;
/* CIF START (161, 82), (70, 35) */
START;
/* CIF DECISION (135, 132), (122, 50) */
DECISION num(myenum);
/* CIF ANSWER (163, 202), (70, 23) */
else:
ENDDECISION;
/* CIF DECISION (135, 240), (122, 50) */
DECISION num(myenum);
/* CIF ANSWER (163, 310), (70, 23) */
else:
ENDDECISION;
/* CIF RETURN (178, 348), (35, 35) */
RETURN ;
ENDPROCEDURE;
/* CIF PROCEDURE (1273, 175), (106, 35) */
PROCEDURE emptyproc;
ENDPROCEDURE;
/* CIF START (596, 224), (80, 36) */
START;
/* CIF TASK (564, 275), (144, 35) */
TASK opt := { B TRUE }
/* CIF COMMENT (728, 275), (155, 35) */
COMMENT 'Test optional fields';
/* CIF DECISION (575, 325), (122, 50) */
DECISION num(myenum);
/* CIF ANSWER (563, 395), (70, 23) */
(0):
/* CIF ANSWER (635, 395), (70, 23) */
/* CIF decision (161, 137), (70, 50) */
decision any;
/* CIF ANSWER (71, 207), (70, 24) */
('a'):
/* CIF ANSWER (161, 207), (70, 24) */
('b'):
/* CIF ANSWER (251, 207), (70, 24) */
('c'):
enddecision;
/* CIF decision (135, 247), (122, 50) */
decision num(myenum);
/* CIF ANSWER (163, 317), (70, 24) */
else:
ENDDECISION;
/* CIF DECISION (575, 433), (122, 50) */
DECISION num(myenum);
/* CIF ANSWER (564, 503), (70, 23) */
(0):
/* CIF ANSWER (644, 503), (70, 23) */
enddecision;
/* CIF decision (135, 357), (122, 50) */
decision num(myenum);
/* CIF ANSWER (163, 427), (70, 24) */
else:
ENDDECISION;
/* CIF DECISION (586, 541), (99, 50) */
DECISION 'informal'
/* CIF COMMENT (746, 540), (179, 53) */
COMMENT 'Informal decision -
enddecision;
/* CIF return (178, 467), (35, 35) */
return ;
endprocedure;
/* CIF START (596, 224), (80, 36) */
START;
/* CIF task (564, 275), (144, 35) */
task opt := { B TRUE }
/* CIF comment (728, 275), (155, 35) */
comment 'Test optional fields';
/* CIF decision (575, 325), (122, 50) */
decision num(myenum);
/* CIF ANSWER (563, 395), (70, 24) */