Commit bee2e120 authored by dbarbera's avatar dbarbera
Browse files

Merge remote-tracking branch 'upstream/master' into llvm

parents 556f732c d614520e
......@@ -11,15 +11,50 @@ Developer documentation is provided in docstrings in the Python modules.
License is LGPL (see file LICENSE)
You need Pyside and ANTLR 3.1.3 runtimes to be able to use this Python module
On Debian, Ubuntu:
$ apt-get install python-pyside pip
$ pip install antlr_python_runtime
You need to install the following dependencies before you can run Opengeode:
- Python 2.7
installed on all Linux distributions ; on Windows you need to install it manually
- PySide
on debian-based Linux run:
$ sudo apt-get install python-pyside
on windows and mac, download and run the automatic installer from Qt
- pip for Python2
on debian-based Linux run:
$ sudo apt-get install python-pip
on windows and mac, download and use the automatic installer
- enum34, singledispatch, graphviz, antlr runtime
graphviz core needs to be installed from the Linux repos first:
$ sudo apt-get install graphviz
Then using pip, install the python libraries:
$ sudo pip install --upgrade graphviz enum34 singledispatch
$ sudo pip install antlr_python_runtime --allow-external antlr_python_runtime --allow-unverified antlr_python_runtime
* Note: graphviz for python cannot be installed on Windows
- ASN1SCC - the ASN.1 compiler
Download here: http://www.semantix.gr/asn1scc/asn1Comp.tar.gz
It is a .NET application, it can be installed on Windows and Linux (you need mono)
Make sure asn1.exe is in the path
On linux :
$ cd /opt
$ sudo wget http://www.semantix.gr/asn1scc/asn1Comp.tar.gz
$ sudo tar zxvf asn1Comp.tar.gz
$ sudo apt-get install mono-runtime libmono-system-runtime4.0-cil libmono-i18n-west2.0-cil libmono-posix2.0-cil libmono-security2.0-cil
libmono-system-runtime-serialization4.0-cil
Check that it works:
$ asn1Comp/bin/asn1.exe
Then add to your PATH the bin directory in e.g. your .bashrc file
$ echo 'export PATH=$PATH:/opt/asn1Comp/bin' >> ~/.bashrc
- gnat - if you want to build the code you generate in Ada
$ sudo apt-get install gnat
Also tested on Windows and FreeBSD - use pip to install antlr-python-runtime.
Install Pyside from the Windows binary or from FreeBSD ports.
On Windows, you also need to install Python 2.7 manually.
To make a static binary on Linux with pyinstaller 2.0:
python pyinstaller.py ../../opengeode.py --onefile
......@@ -23,8 +23,8 @@ Features
- Graphical editor of SDL processes and procedures.
- SDL2010 features: FOR loops in task symbols, hierarchical states
- Works on pure PR+CIF files (textual SDL notation) - no fancy proprietary save format
- Supports ASN.1 data types, including the Value notation - check this page to know more about our ASN.1 compiler and tools
- Generates Ada code with ASN.1 types using TASTE ASN.1 "space-certified" (SPARK) compiler
- Full supports ASN.1 data types - using ESA Space Certified compiler (www.github.com/ttsiodras/asn1scc)
- Generates Ada code
- Extensive syntax and semantic checks
- Automatic conversion to Statechart diagrams
- Save the complete or parts of the model to PNG/SVG/PDF files
......@@ -34,6 +34,7 @@ Features
- Syntax highlighting
- Undo/Redo, Copy-Paste
- (Limited) VIM mode - You can use :wq or :%s,search,replace,g, and /search pattern
- (In progress) SDL to LLVM code generation
Installation
============
......@@ -41,46 +42,46 @@ Installation
Pre-requisites
--------------
There are three major dependencies for OpenGEODE:
There are several dependencies for OpenGEODE:
Apart from pygraphviz, all of them exist for Linux, Windows, FreeBSD, and most likely Mac OSX
- Python 2.7 with pip
- Pyside (the Qt bindings for Python)
- Python ANTLR Runtime
- PyGraphviz (Linux only - not available on Windows)
- enum34, singledispatch
- ASN1SCC
- (optional) GNAT to build the generated Ada code
- mono
If you use pip to install OpenGEODE, these dependencies should be installed
automatically. However, note that installing PySide from pip requires some
work and is not straightforward.
If you are using a Linux Debian-based distribution (including Ubuntu),
I would recommended to install PySide using your package manager:
You should also install pygraphviz using the same method, for convenience.
On Debian, Ubuntu, and probably other distributions:
```bash
$ sudo apt-get install python-pyside pyside-tools python-pygraphviz pip
$ sudo apt-get install python-pyside pyside-tools graphviz pip gnat mono-runtime libmono-system-runtime4.0-cil libmono-i18n-west2.0-cil libmono-posix2.0-cil libmono-security2.0-cil
libmono-system-runtime-serialization4.0-cil
$ sudo pip install --upgrade graphviz enum34 singledispatch
$ sudo pip install antlr_python_runtime --allow-external antlr_python_runtime --allow-unverified antlr_python_runtime
```
The Python 2.7 ANTLR 3.1.3 runtime is not part of Debian packages. Install
it with pip (or download and install manually the package):
To install the ASN.1 compiler:
```bash
$ pip install antlr_python_runtime singledispatch
$ cd /opt
$ sudo wget http://www.semantix.gr/asn1scc/asn1Comp.tar.gz
$ sudo tar zxvf asn1Comp.tar.gz
$ echo 'export PATH=$PATH:/opt/asn1Comp/bin' >> ~/.bashrc
```
On Windows:
You need to download and install Python, Pyside, and pip (binaries are
available on respective websites)
Check that it works:
On FreeBSD:
PySide is available through the ports.
You can also use easy_install to install it.
Use pip to install the ANTLR runtime (see above)
```bash
$ asn1.exe
```
Automatic installation (recommended)
------------------------------------
OpenGEODE installation
----------------------
To install the application on your machine:
To install the application on your machine, once all dependencies are met:
```bash
$ pip install --upgrade opengeode
......@@ -88,30 +89,13 @@ $ pip install --upgrade opengeode
This is sufficient to get opengeode running
In addition OpenGEODE is capable of generating code for embedded, real-
time systems in the Ada programming language, with compact and efficient
data manipulation and binary encoding using the ASN.1 notation.
To get the full benefits of SDL and OpenGEODE, consider installing
TASTE, that is a complete development environment dedicated to
real-time, embedded systems from the European Space Agency.
TASTE also allows the transparent integration and communication between
models developed by commercial tools such as Matlab-Simulink and
Real-Time Developer Studio.
Installation from source
------------------------
You can get the source from the TASTE repositories or from GitHub
```bash
$ svn co https://tecsw.estec.esa.int/svn/taste/trunk/misc/opengeode opengeode
```
Or
```bash
$ git clone https://github.com/maxime-esa/opengeode.git opengeode
$ git clone https://github.com/maxime-esa/opengeode.git
```
Then enter the opengeode directory and as root, type:
......@@ -120,6 +104,8 @@ Then enter the opengeode directory and as root, type:
$ make install
```
Installation is optional. You can simply run opengeode.py to get it work.
Information and contact
=======================
......@@ -127,9 +113,16 @@ OpenGEODE is part of the TASTE project.
Find more information and download at http://taste.tuxfamily.org
OpenGEODE is developed and maintained by Maxime Perrotin
TASTE allows to create embedded software systems that combine SDL models with C, Ada,
Matlab-Simulink and a few other languages or tools.
OpenGEODE is mainly designed, developed and maintained by Maxime Perrotin
maxime (dot) perrotin (at) esa (dot) int
The LLVM backend was designed and implemented by Diego Barbera during the ESA Summer of Code 2014
Some parts have been implemented by Laurent Meyer (native SDL type support in the parser)
The background pattern was downloaded from www.subtlepatterns.com
The ASN.1 compiler (ASN1Scc) that OpenGEODE is based on was
......
......@@ -1452,12 +1452,15 @@ def primary_index(root, context):
if not is_integer(idx_bty):
errors.append(error(root, 'Index is not an integer'))
else:
if float(idx_bty.Min) < float(r_min) or\
float(idx_bty.Max) >= float(r_max):
if float(idx_bty.Max) >= float(r_max):
errors.append(error(root,
'Index outside of range [{} .. <{}]'
.format(r_min,
r_max)))
'Index range [{id1} .. {id2}] '
'outside of range [0 .. <{r2}]'
.format(id1=idx_bty.Min, id2=idx_bty.Max,
r2=r_max)))
elif float(idx_bty.Min) > float(r_min):
warnings.append(warning(root,
'Index higher than range min value'))
else:
msg = 'Index can only be applied to type SequenceOf'
errors.append(error(root, msg))
......@@ -1527,12 +1530,18 @@ def selector_expression(root, context):
field_name = root.children[1].text.replace('_', '-').lower()
try:
if receiver_bty.kind == 'ChoiceType':
warnings.append(error(root, 'Wrong syntax for a CHOICE selector. '
'Use "var := {field}: value" instead of '
'"var!{field} := value"'
.format(field=field_name)))
for n, f in receiver_bty.Children.viewitems():
if n.lower() == field_name:
node.exprType = f.type
break
else:
msg = 'Field "{}" not found in expression {}'.format(field_name)
msg = 'Field "{}" not found in expression {}'.format(field_name,
receiver.inputString)
errors.append(error(root, msg))
except AttributeError:
# When parsing for syntax or copy-paste, receiver_bty may
......@@ -1782,6 +1791,7 @@ def composite_state(root, parent=None, context=None):
comp.operators = dict(context.operators)
except AttributeError:
LOG.debug('Procedure context is undefined')
inner_proc = []
# Gather the list of states defined in the composite state
# and map a list of transitionsi to each state
comp.mapping = {name: [] for name in get_state_list(root)}
......@@ -1807,7 +1817,8 @@ def composite_state(root, parent=None, context=None):
warnings.extend(warn)
comp.content.textAreas.append(textarea)
elif child.type == lexer.PROCEDURE:
new_proc, err, warn = procedure(child, context=comp)
new_proc, content, err, warn = procedure_pre(child, context=comp)
inner_proc.append((new_proc, content))
errors.extend(err)
warnings.extend(warn)
if new_proc.inputString.strip().lower() == 'entry':
......@@ -1855,6 +1866,11 @@ def composite_state(root, parent=None, context=None):
errors.extend(err)
warnings.extend(warn)
comp.content.floating_labels.append(lab)
for proc, content in inner_proc:
# parse content of procedures - all scopes are set
err, warn = procedure_post(proc, content, context=comp)
errors.extend(err)
warnings.extend(warn)
for each in states:
# And parse the states after inner states to make sure all CONNECTS
# are properly defined.
......@@ -1875,27 +1891,14 @@ def composite_state(root, parent=None, context=None):
return comp, errors, warnings
def procedure(root, parent=None, context=None):
''' Parse a procedure definition '''
proc = ogAST.Procedure()
def procedure_pre(root, parent=None, context=None):
''' Parse a procedure interface - the content has to be parsed after
all procedure interfaces are known, to prevent missing references '''
errors = []
warnings = []
# Create a list of all inherited data
try:
proc.global_variables = dict(context.variables)
proc.global_variables.update(context.global_variables)
proc.global_timers = list(context.timers)
proc.global_timers.extend(list(context.global_timers))
proc.input_signals = context.input_signals
proc.output_signals = context.output_signals
proc.procedures = context.procedures
proc.operators = dict(context.operators)
except AttributeError:
LOG.debug('Procedure context is undefined')
# Gather the list of states defined in the procedure
# and create a mapping of transitions to each state
# (Note, procedures in OG currently do NOT support states)
proc.mapping = {name: [] for name in get_state_list(root)}
proc = ogAST.Procedure()
content = []
for child in root.getChildren():
if child.type == lexer.CIF:
# Get symbol coordinates
......@@ -1911,13 +1914,6 @@ def procedure(root, parent=None, context=None):
errors.extend(err)
warnings.extend(warn)
proc.content.textAreas.append(textarea)
elif child.type == lexer.PROCEDURE:
new_proc, err, warn = procedure(child, context=proc)
errors.extend(err)
warnings.extend(warn)
proc.content.inner_procedures.append(new_proc)
# Add procedure to the context, to make it visible at scope level
context.procedures.append(new_proc)
elif child.type == lexer.EXTERNAL:
proc.external = True
elif child.type == lexer.FPAR:
......@@ -1925,27 +1921,68 @@ def procedure(root, parent=None, context=None):
errors.extend(err)
warnings.extend(warn)
proc.fpar = params
elif child.type in (lexer.PROCEDURE, lexer.START,
lexer.STATE, lexer.FLOATING_LABEL):
content.append(child)
else:
warnings.append(
'Unsupported construct in procedure, type: ' +
str(child.type) + ' - line ' + str(child.getLine()) +
' - string: ' + str(proc.inputString))
return proc, content, errors, warnings
def procedure_post(proc, content, parent=None, context=None):
''' Parse the content of a procedure '''
errors = []
warnings = []
# Create a list of all inherited data
try:
proc.global_variables = dict(context.variables)
proc.global_variables.update(context.global_variables)
proc.global_timers = list(context.timers)
proc.global_timers.extend(list(context.global_timers))
proc.input_signals = context.input_signals
proc.output_signals = context.output_signals
proc.procedures = context.procedures
proc.operators = dict(context.operators)
except AttributeError:
LOG.debug('Procedure context is undefined')
# Gather the list of states defined in the procedure
# and create a mapping of transitions to each state
# (Note, procedures in OG currently do NOT support states)
# proc.mapping = {name: [] for name in get_state_list(root)}
inner_proc = []
for child in content:
if child.type == lexer.PROCEDURE:
new_proc, content, err, warn = procedure_pre(child, context=proc)
inner_proc.append((new_proc, content))
errors.extend(err)
warnings.extend(warn)
proc.content.inner_procedures.append(new_proc)
# Add procedure to the context, to make it visible at scope level
context.procedures.append(new_proc)
elif child.type == lexer.START:
# START transition (fills the mapping structure)
proc.content.start, err, warn = start(child, context=proc)
errors.extend(err)
warnings.extend(warn)
elif child.type == lexer.STATE:
# STATE - fills up the 'mapping' structure.
newstate, err, warn = state(child, parent=None, context=proc)
errors.extend(err)
warnings.extend(warn)
proc.content.states.append(newstate)
# elif child.type == lexer.STATE:
# # STATE - fills up the 'mapping' structure.
# newstate, err, warn = state(child, parent=None, context=proc)
# errors.extend(err)
# warnings.extend(warn)
# proc.content.states.append(newstate)
elif child.type == lexer.FLOATING_LABEL:
lab, err, warn = floating_label(child, parent=None, context=proc)
errors.extend(err)
warnings.extend(warn)
proc.content.floating_labels.append(lab)
else:
warnings.append(
'Unsupported construct in procedure, type: ' +
str(child.type) + ' - line ' + str(child.getLine()) +
' - string: ' + str(proc.inputString))
for new_proc, content in inner_proc:
# parse content of procedures
err, warn = procedure_post(new_proc, content, context=proc)
errors.extend(err)
warnings.extend(warn)
for each in proc.terminators:
# check that RETURN statements type is correct
if not proc.return_type and each.return_expr:
......@@ -1969,6 +2006,17 @@ def procedure(root, parent=None, context=None):
continue
for each in chain(errors, warnings):
each[2].insert(0, 'PROCEDURE {}'.format(proc.inputString))
return errors, warnings
def procedure(root, parent=None, context=None):
''' Parse a procedure - call sequentially the pre- and post- functions
This function is called by the syntax checker only '''
proc, content, errors, warnings = procedure_pre(root, parent, context)
err, warn = procedure_post(proc, content, parent, context)
errors.extend(err)
warnings.extend(warn)
return proc, errors, warnings
......@@ -2360,6 +2408,7 @@ def process_definition(root, parent=None, context=None):
process.filename = node_filename(root)
process.parent = parent
proc_x, proc_y = 0, 0
inner_proc = []
# Prepare the transition/state mapping
process.mapping = {name: [] for name in get_state_list(root)}
for child in root.getChildren():
......@@ -2408,8 +2457,9 @@ def process_definition(root, parent=None, context=None):
# Number of instances - discarded (working on a single process)
pass
elif child.type == lexer.PROCEDURE:
proc, err, warn = procedure(
proc, content, err, warn = procedure_pre(
child, parent=None, context=process)
inner_proc.append((proc, content))
errors.extend(err)
warnings.extend(warn)
process.content.inner_procedures.append(proc)
......@@ -2433,11 +2483,20 @@ def process_definition(root, parent=None, context=None):
elif child.type == lexer.COMMENT:
process.comment, _, _ = end(child)
else:
warnings.append('Unsupported process definition child: ' +
warnings.append(['Unsupported process definition child: ' +
sdl92Parser.tokenNames[child.type] +
' - line ' + str(child.getLine()))
' - line ' + str(child.getLine()),
[proc_x, proc_y], []])
for proc, content in inner_proc:
err, warn = procedure_post(proc, content, context=process)
errors.extend(err)
warnings.extend(warn)
for each in chain(errors, warnings):
try:
each[2].insert(0, 'PROCESS {}'.format(process.processName))
except AttributeError as err:
LOG.debug(str(err))
LOG.error('Internal error - please report "{}"'.format(str(each)))
errors.extend(perr)
return process, errors, warnings
......@@ -3423,6 +3482,9 @@ def assign(root, context):
)
expr.kind = 'assign'
if len(root.children) != 2:
errors.append('Syntax error: {}'.format(expr.inputString))
if root.children[0].type == lexer.CALL:
expr.left, err, warn = call_expression(root.children[0], context)
elif root.children[0].type == lexer.SELECTOR:
......@@ -3535,7 +3597,8 @@ def for_loop(root, context):
basic = find_basic_type(start_expr.exprType)
r_min = basic.Min if basic != UNKNOWN_TYPE else '0'
basic = find_basic_type(stop_expr.exprType)
r_max = basic.Max if basic != UNKNOWN_TYPE else '4294967295'
r_max = str(int(float(basic.Max) - 1)) \
if basic != UNKNOWN_TYPE else '4294967295'
# basic may be UNKNOWN_TYPE if the expression is a
# reference to an ASN.1 constant - their values are not
# currently visible to the SDL parser
......@@ -3709,6 +3772,9 @@ def pr_file(root):
# Can happen if DataView.py is not there
LOG.info('USE Clause did not contain ASN.1 filename')
LOG.debug(str(err))
except TypeError as err:
errors.append('ASN.1 compiler execution failed')
LOG.debug(str(err))
for child in systems:
LOG.debug('found SYSTEM')
......
......@@ -22,7 +22,7 @@ dcl VAR5 mychoice;
DECISION var1
/* CIF COMMENT (615, 128), (191, 35) */
COMMENT 'Check range overlapping';
/* CIF ANSWER (370, 191), (90, 24) */
/* CIF ANSWER (366, 191), (90, 24) */
(-500:500):
/* CIF ANSWER (480, 191), (70, 24) */
(=0):
......@@ -31,68 +31,81 @@ dcl VAR5 mychoice;
/* CIF ANSWER (660, 191), (70, 24) */
(>0):
ENDDECISION;
/* CIF DECISION (525, 245), (70, 50) */
/* CIF DECISION (525, 230), (70, 50) */
DECISION var2
/* CIF COMMENT (615, 252), (241, 35) */
/* CIF COMMENT (615, 237), (241, 35) */
COMMENT 'Check unreachable branch check';
/* CIF ANSWER (389, 315), (53, 33) */
/* CIF ANSWER (385, 300), (53, 33) */
(<0):
/* CIF ANSWER (471, 315), (68, 33) */
/* CIF ANSWER (467, 300), (68, 33) */
(>300):
/* CIF ANSWER (568, 315), (72, 33) */
/* CIF ANSWER (557, 300), (72, 33) */
(-10:-5):
/* CIF ANSWER (642, 315), (86, 24) */
/* CIF ANSWER (638, 300), (86, 24) */
(256:300):
ENDDECISION;
/* CIF DECISION (525, 405), (71, 50) */
/* CIF DECISION (524, 348), (71, 50) */
DECISION var3;
/* CIF ANSWER (470, 475), (71, 24) */
/* CIF ANSWER (466, 418), (71, 24) */
(10:20):
/* CIF ANSWER (570, 475), (70, 24) */
/* CIF ANSWER (570, 418), (70, 24) */
(/=50):
ENDDECISION;
/* CIF DECISION (499, 529), (123, 50) */
/* CIF DECISION (498, 457), (123, 50) */
DECISION present(var4)
/* CIF COMMENT (643, 536), (252, 35) */
/* CIF COMMENT (643, 464), (252, 35) */
COMMENT 'Check coverage of CHOICE answers';
/* CIF ANSWER (527, 599), (66, 34) */
/* CIF ANSWER (527, 527), (66, 34) */
(a):
ENDDECISION;
/* CIF DECISION (523, 693), (74, 50) */
/* CIF DECISION (523, 576), (74, 50) */
DECISION var5
/* CIF COMMENT (611, 691), (241, 50) */
/* CIF COMMENT (611, 574), (241, 50) */
COMMENT 'Check duplicates and
coverage of ENUMERATED values';
/* CIF ANSWER (390, 763), (70, 24) */
/* CIF ANSWER (390, 646), (70, 24) */
(hello):
/* CIF ANSWER (470, 763), (72, 24) */
/* CIF ANSWER (466, 646), (72, 24) */
(world):
/* CIF ANSWER (562, 763), (72, 24) */
/* CIF ANSWER (558, 646), (72, 24) */
(world):
/* CIF ANSWER (661, 763), (70, 24) */
/* CIF ANSWER (661, 646), (70, 24) */
(hello):
ENDDECISION;
/* CIF DECISION (525, 837), (70, 50) */
/* CIF DECISION (525, 686), (70, 50) */
DECISION var5
/* CIF COMMENT (615, 844), (224, 35) */
/* CIF COMMENT (615, 693), (224, 35) */
COMMENT 'should be no error due to ELSE';
/* CIF ANSWER (412, 907), (105, 24) */
/* CIF ANSWER (408, 756), (105, 24) */
(howareyou):
/* CIF ANSWER (608, 907), (70, 24) */
/* CIF ANSWER (608, 756), (70, 24) */
else:
ENDDECISION;
/* CIF NEXTSTATE (525, 981), (70, 35) */
/* CIF TASK (496, 795), (127, 35) */
TASK var4!uia:= true
/* CIF COMMENT (630, 795), (160, 40) */
COMMENT 'incorrect syntax and
non-existent field';
/* CIF TASK (500, 845), (120, 35) */
TASK var4!a := true
/* CIF COMMENT (640, 842), (135, 40) */
COMMENT 'incorrect syntax';
/* CIF TASK (469, 895), (180, 35) */
TASK var5 := howareyou:true;
/* CIF TASK (507, 945), (105, 35) */
TASK tutu!i!y := 5;
/* CIF NEXTSTATE (525, 995), (70, 35) */
NEXTSTATE wait;
/* CIF STATE (1324, 193), (80, 35) */
/* CIF STATE (1320, 193), (80, 35) */
STATE *(wait);
/* CIF INPUT (1277, 248), (173, 35) */
/* CIF INPUT (1273, 248), (173, 35) */
INPUT start_something(var2);
/* CIF NEXTSTATE (1329, 298), (70, 35) */
NEXTSTATE -;
ENDSTATE;
/* CIF STATE (1215, 52), (70, 35) */
STATE wait;
/* CIF INPUT (1166, 107), (137, 40) */
/* CIF INPUT (1162, 107), (137, 40) */
INPUT start_something
(var2);
/* CIF NEXTSTATE (1203, 162), (70, 35) */
......@@ -100,12 +113,12 @@ coverage of ENUMERATED values';
ENDSTATE;
/* CIF STATE (958, 325), (70, 35) */
STATE *;
/* CIF INPUT (905, 380), (173, 35) */
/* CIF INPUT (901, 380), (173, 35) */
INPUT start_something(var2);
/* CIF NEXTSTATE (958, 430), (70, 35) */
NEXTSTATE wait;
ENDSTATE;
/* CIF STATE (1129, 294), (97, 35) */
/* CIF STATE (1125, 294), (97, 35) */
STATE wait, toto;
/* CIF INPUT (1089, 349), (178, 35) */
INPUT start_something (var2);
......
all: test-ada test-llvm
edit:
../../../opengeode.py challenge.pr system_structure.pr
test-parse:
../../../opengeode.py challenge.pr system_structure.pr --check
......