Commit 67073155 authored by Maxime Perrotin's avatar Maxime Perrotin
Browse files

Add support for synchronous PIs (exported procedures)

parent baef5f9e
...@@ -124,6 +124,9 @@ The background pattern was downloaded from www.subtlepatterns.com ...@@ -124,6 +124,9 @@ The background pattern was downloaded from www.subtlepatterns.com
Changelog Changelog
========= =========
**3.4.0 (03/2021)**
- Support exported/reference procedures (allowing remote synchronous calls)
**3.3.7 (03/2021)** **3.3.7 (03/2021)**
- Support IA5String type - Support IA5String type
......
...@@ -712,8 +712,13 @@ package body {process_name}_RI is'''] ...@@ -712,8 +712,13 @@ package body {process_name}_RI is''']
pi_header = procedure_header(proc) pi_header = procedure_header(proc)
ads_template.append(f'{pi_header};') ads_template.append(f'{pi_header};')
if not proc.external and not generic: if not proc.external and not generic:
ads_template.append( if not proc.exported:
f'pragma Export (C, p{SEPARATOR}{proc.inputString}, "_{proc.inputString}");') ads_template.append(
f'pragma Export (C, p{SEPARATOR}{proc.inputString}, "_{proc.inputString}");')
else:
# Export for TASTE as a synchronous PI
ads_template.append(
f'pragma Export (C, p{SEPARATOR}{proc.inputString}, "{process_name.lower()}_PI_{proc.inputString}");')
# Generate the code for the process-level variable declarations # Generate the code for the process-level variable declarations
taste_template.extend(process_level_decl) taste_template.extend(process_level_decl)
......
...@@ -125,8 +125,8 @@ def _automaton(ast, scene): ...@@ -125,8 +125,8 @@ def _automaton(ast, scene):
# Render procedures symbols # Render procedures symbols
top_level_symbols.extend( top_level_symbols.extend(
[render(proc, scene) [render(proc, scene)
for proc in ast.inner_procedures for proc in ast.inner_procedures
if not proc.external]) if not proc.external and not proc.textual_procedure])
# Render the start symbol # Render the start symbol
if ast.start: if ast.start:
......
...@@ -842,6 +842,8 @@ class Procedure: ...@@ -842,6 +842,8 @@ class Procedure:
self.external = False self.external = False
# Determine if a procedure is a remote procedure # Determine if a procedure is a remote procedure
self.exported = False self.exported = False
# Determine if a procedure only has a textual definition
self.textual_procedure = False
# Optional comment # Optional comment
self.comment = None self.comment = None
# Set of symbols contained in the procedure (type Automaton) # Set of symbols contained in the procedure (type Automaton)
...@@ -851,6 +853,10 @@ class Procedure: ...@@ -851,6 +853,10 @@ class Procedure:
self.output_signals = [] self.output_signals = []
# The "DECISION ANY" construct requires random number generators # The "DECISION ANY" construct requires random number generators
self.random_generator = set() self.random_generator = set()
# Procedure declared as EXPORTED
self.exported : bool = False
# procedure declared as REFERENCED
self.referenced : bool = False
class Process: class Process:
...@@ -1032,6 +1038,7 @@ class System: ...@@ -1032,6 +1038,7 @@ class System:
self.filename = None self.filename = None
# Reference to top-level AST # Reference to top-level AST
self.ast = None self.ast = None
# self.parent = None
# list of SIGNAL declarations: [{'name': str, 'type': asn1type}] # list of SIGNAL declarations: [{'name': str, 'type': asn1type}]
# (Supporting only one parameter) # (Supporting only one parameter)
self.signals = [] self.signals = []
...@@ -1051,6 +1058,8 @@ class AST: ...@@ -1051,6 +1058,8 @@ class AST:
AST entries are systems, processes, and USE clauses AST entries are systems, processes, and USE clauses
(cf. ANTLR grammar, production "pr_file") (cf. ANTLR grammar, production "pr_file")
''' '''
# Top-level node: parent remains None
self.parent = None
# Set of input files the AST was created from # Set of input files the AST was created from
self.pr_files = set() self.pr_files = set()
# USE clauses: list of strings (eg. "DataView") # USE clauses: list of strings (eg. "DataView")
......
...@@ -2813,13 +2813,10 @@ def composite_state(root, parent=None, context=None): ...@@ -2813,13 +2813,10 @@ def composite_state(root, parent=None, context=None):
elif new_proc.inputString.strip().lower() == 'exit': elif new_proc.inputString.strip().lower() == 'exit':
comp.exit_procedure = new_proc comp.exit_procedure = new_proc
# check for duplicate declaration # check for duplicate declaration
if any(each.inputString.lower() == new_proc.inputString.lower() dupl_errs = []
for each in chain(comp.content.inner_procedures, check_duplicate_procedures(comp, new_proc, dupl_errs)
context.procedures) for err_str in dupl_errs:
if each.inputString.lower() not in ('entry', 'exit')): errors.append([err_str, [new_proc.pos_x, new_proc.pos_y], []])
errors.append(['Duplicate procedure Declaration: {}'
.format(new_proc.inputString),
[new_proc.pos_x, new_proc.pos_y], []])
# Add procedure to the context, to make it visible at scope level # Add procedure to the context, to make it visible at scope level
comp.content.inner_procedures.append(new_proc) comp.content.inner_procedures.append(new_proc)
context.procedures.append(new_proc) context.procedures.append(new_proc)
...@@ -2968,6 +2965,10 @@ def procedure_pre(root, parent=None, context=None): ...@@ -2968,6 +2965,10 @@ def procedure_pre(root, parent=None, context=None):
elif child.type in (lexer.PROCEDURE, lexer.START, elif child.type in (lexer.PROCEDURE, lexer.START,
lexer.STATE, lexer.FLOATING_LABEL): lexer.STATE, lexer.FLOATING_LABEL):
content.append(child) content.append(child)
elif child.type == lexer.EXPORTED:
proc.exported = True
elif child.type == lexer.REFERENCED:
proc.referenced = True
else: else:
warnings.append([ warnings.append([
'Unsupported construct in procedure, type: ' + 'Unsupported construct in procedure, type: ' +
...@@ -2987,6 +2988,51 @@ def procedure_returns(root): ...@@ -2987,6 +2988,51 @@ def procedure_returns(root):
return sdl_to_asn1(root.getChild(1).getChild(0).text),\ return sdl_to_asn1(root.getChild(1).getChild(0).text),\
root.getChild(0).text root.getChild(0).text
def check_duplicate_procedures(ctxt, proc, errors=[]):
''' Check for duplicates procedure declarations in a given context
and recursively in contexts above
If the proceduure in the context is declared as referenced,
then report an error only if the signature is different
Procedure named "entry" and "exit" are ignored
'''
name = proc.inputString.lower()
if not isinstance(ctxt, ogAST.System) and ctxt.parent != None:
check_duplicate_procedures(ctxt.parent, proc, errors)
if isinstance(ctxt, ogAST.AST):
# If we are at AST top level there are no declarations -> ignore
return
if name in ('entry', 'exit'):
# ignore special entry/exit procedures
return
try:
content = ctxt.content.inner_procedures
except AttributeError:
content = []
for each in chain(content, ctxt.procedures):
if each.inputString.lower() == name:
if not each.referenced and not proc.referenced:
errors.append('Duplicate procedure declaration: {}'
.format(name))
else:
mismatch = False
# at least one is referenced -> compare signatures
left, right = proc.fpar, each.fpar
if len(left) != len(right):
mismatch = True
for idx, val in enumerate(left):
if(right[idx]['name'] != val['name']
or type_name(right[idx]['type']) != type_name(val['type'])
or right[idx]['direction'] != val['direction']):
mismatch = True
break
if mismatch:
errors.append(f'Procedure {proc.inputString}: '
'declaration and definition interface mismatch')
if proc.exported != each.exported:
# If declared exported, definition must be exported too
# Setting the flag here, this will ease code generators
proc.exported = each.exported = True
def procedure_post(proc, content, parent=None, context=None): def procedure_post(proc, content, parent=None, context=None):
''' Parse the content of a procedure ''' ''' Parse the content of a procedure '''
...@@ -3016,16 +3062,13 @@ def procedure_post(proc, content, parent=None, context=None): ...@@ -3016,16 +3062,13 @@ def procedure_post(proc, content, parent=None, context=None):
errors.extend(err) errors.extend(err)
warnings.extend(warn) warnings.extend(warn)
# check for duplicate declaration # check for duplicate declaration
err = any(each.inputString.lower() == new_proc.inputString.lower() dupl_errs = []
for each in chain(proc.content.inner_procedures, check_duplicate_procedures(context, new_proc, dupl_errs)
context.procedures))
# Add procedure to the context, to make it visible at scope level # Add procedure to the context, to make it visible at scope level
context.procedures.append(new_proc) context.procedures.append(new_proc)
proc.content.inner_procedures.append(new_proc) proc.content.inner_procedures.append(new_proc)
if err: for err_str in dupl_errs:
errors.append(['Duplicate declaration of procedure {}' errors.append([err_str, [0, 0], []])
.format(new_proc.inputString),
[0, 0], []])
elif child.type == lexer.START: elif child.type == lexer.START:
# START transition (fills the mapping structure) # START transition (fills the mapping structure)
proc.content.start, err, warn = start(child, context=proc) proc.content.start, err, warn = start(child, context=proc)
...@@ -3369,6 +3412,8 @@ def text_area_content(root, ta_ast, context): ...@@ -3369,6 +3412,8 @@ def text_area_content(root, ta_ast, context):
for each in procedures: for each in procedures:
# Procedure textual declarations need ASN.1 types # Procedure textual declarations need ASN.1 types
proc, err, warn = procedure(each, context=context) proc, err, warn = procedure(each, context=context)
# set the flag to avoid rendering a graphical procedure
proc.textual_procedure = True
errors.extend(err) errors.extend(err)
warnings.extend(warn) warnings.extend(warn)
try: try:
...@@ -3376,12 +3421,7 @@ def text_area_content(root, ta_ast, context): ...@@ -3376,12 +3421,7 @@ def text_area_content(root, ta_ast, context):
except AttributeError: except AttributeError:
# May not be any content in current context (eg System) # May not be any content in current context (eg System)
content = [] content = []
# check for duplicates check_duplicate_procedures(context, proc, errors)
if any(each.inputString.lower() == proc.inputString.lower()
for each in chain(content, context.procedures)):
errors.append('Duplicate Procedure Declaration: {}'
.format(proc.inputString))
# Add procedure to the container (process or procedure) if any # Add procedure to the container (process or procedure) if any
content.append(proc) content.append(proc)
# Add to context to make it visible at scope level # Add to context to make it visible at scope level
...@@ -3546,6 +3586,7 @@ def system_definition(root, parent): ...@@ -3546,6 +3586,7 @@ def system_definition(root, parent):
# Store the name of the file where the system is defined # Store the name of the file where the system is defined
system.filename = node_filename(root) system.filename = node_filename(root)
system.ast = parent system.ast = parent
#system.parent = parent
asn1_files = [] asn1_files = []
signals, procedures, blocks = [], [], [] signals, procedures, blocks = [], [], []
for child in root.getChildren(): for child in root.getChildren():
...@@ -3571,7 +3612,7 @@ def system_definition(root, parent): ...@@ -3571,7 +3612,7 @@ def system_definition(root, parent):
# Update list of ASN.1 files - if any # Update list of ASN.1 files - if any
if not asn1_files: if not asn1_files:
asn1_files = textarea.asn1_files asn1_files = textarea.asn1_files
else: elif textarea.asn1_files:
errors.append('ASN.1 Files must be set in a single text area') errors.append('ASN.1 Files must be set in a single text area')
errors.extend(err) errors.extend(err)
warnings.extend(warn) warnings.extend(warn)
...@@ -3598,6 +3639,8 @@ def system_definition(root, parent): ...@@ -3598,6 +3639,8 @@ def system_definition(root, parent):
for each in procedures: for each in procedures:
proc, err, warn = procedure( proc, err, warn = procedure(
each, parent=None, context=system) each, parent=None, context=system)
# Procedure defined at system level are only textual
proc.textual_procedure = True
errors.extend(err) errors.extend(err)
warnings.extend(warn) warnings.extend(warn)
system.procedures.append(proc) system.procedures.append(proc)
...@@ -3641,12 +3684,10 @@ def process_definition(root, parent=None, context=None): ...@@ -3641,12 +3684,10 @@ def process_definition(root, parent=None, context=None):
errors.extend(err) errors.extend(err)
warnings.extend(warn) warnings.extend(warn)
# check for duplicate declaration # check for duplicate declaration
if any(each.inputString.lower() == proc.inputString.lower() dupl_errs = []
for each in chain(process.content.inner_procedures, check_duplicate_procedures(process, proc, dupl_errs)
process.procedures)): for err_str in dupl_errs:
errors.append(['Duplicate Procedure declaration: {}' errors.append([err_str, [proc.pos_x, proc.pos_y], []])
.format(proc.inputString),
[proc.pos_x, proc.pos_y], []])
# Add it at process level so that it is in the scope # Add it at process level so that it is in the scope
process.content.inner_procedures.append(proc) process.content.inner_procedures.append(proc)
process.procedures.append(proc) process.procedures.append(proc)
......
...@@ -141,7 +141,7 @@ except ImportError: ...@@ -141,7 +141,7 @@ except ImportError:
__all__ = ['opengeode', 'SDL_Scene', 'SDL_View', 'parse'] __all__ = ['opengeode', 'SDL_Scene', 'SDL_View', 'parse']
__version__ = '3.3.8' __version__ = '3.4.0'
if hasattr(sys, 'frozen'): if hasattr(sys, 'frozen'):
# Detect if we are running on Windows (py2exe-generated) # Detect if we are running on Windows (py2exe-generated)
......
# $ANTLR 3.5.2 sdl92.g 2020-07-09 17:54:52 # $ANTLR 3.5.2 sdl92.g 2021-03-28 16:21:31
import sys import sys
from antlr3 import * from antlr3 import *
...@@ -10,13 +10,13 @@ HIDDEN = BaseRecognizer.HIDDEN ...@@ -10,13 +10,13 @@ HIDDEN = BaseRecognizer.HIDDEN
# token types # token types
EOF=-1 EOF=-1
T__224=224
T__225=225 T__225=225
T__226=226 T__226=226
T__227=227 T__227=227
T__228=228 T__228=228
T__229=229 T__229=229
T__230=230 T__230=230
T__231=231
A=4 A=4
ACTION=5 ACTION=5
ACTIVE=6 ACTIVE=6
...@@ -84,165 +84,166 @@ ENTRY_POINT=67 ...@@ -84,165 +84,166 @@ ENTRY_POINT=67
EQ=68 EQ=68
EXPONENT=69 EXPONENT=69
EXPORT=70 EXPORT=70
EXPRESSION=71 EXPORTED=71
EXTERNAL=72 EXPRESSION=72
Exponent=73 EXTERNAL=73
F=74 Exponent=74
FALSE=75 F=75
FI=76 FALSE=76
FIELD=77 FI=77
FIELDS=78 FIELD=78
FIELD_NAME=79 FIELDS=79
FLOAT=80 FIELD_NAME=80
FLOAT2=81 FLOAT=81
FLOATING_LABEL=82 FLOAT2=82
FOR=83 FLOATING_LABEL=83
FPAR=84 FOR=84
FROM=85 FPAR=85
G=86 FROM=86
GE=87 G=87
GEODE=88 GE=88
GROUND=89 GEODE=89
GT=90 GROUND=90
H=91 GT=91
HYPERLINK=92 H=92
I=93 HYPERLINK=93
ID=94 I=94
IF=95 ID=95
IFTHENELSE=96 IF=96
IMPLIES=97 IFTHENELSE=97
IMPORT=98 IMPLIES=98
IN=99 IMPORT=99
INFORMAL_TEXT=100 IN=100
INOUT=101 INFORMAL_TEXT=101
INPUT=102 INOUT=102
INPUTLIST=103 INPUT=103
INPUT_NONE=104 INPUTLIST=104
INT=105 INPUT_NONE=105
J=106 INT=106
JOIN=107 J=107
K=108 JOIN=108
KEEP=109 K=109
L=110 KEEP=110
LABEL=111 L=111
LE=112 LABEL=112
LITERAL=113 LE=113
LT=114 LITERAL=114
L_BRACKET=115 LT=115
L_PAREN=116 L_BRACKET=116
M=117 L_PAREN=117
MANTISSA=118 M=118
MINUS_INFINITY=119 MANTISSA=119
MKSTRING=120 MINUS_INFINITY=120
MOD=121 MKSTRING=121
N=122 MOD=122
NEG=123 N=123
NEQ=124 NEG=124
NEWTYPE=125 NEQ=125
NEXTSTATE=126 NEWTYPE=126
NONE=127 NEXTSTATE=127
NOT=128 NONE=128
NUMBER_OF_INSTANCES=129 NOT=129
O=130 NUMBER_OF_INSTANCES=130
OCTSTR=131 O=131
OPEN_RANGE=132 OCTSTR=132
OR=133 OPEN_RANGE=133
OUT=134 OR=134
OUTPUT=135 OUT=135
OUTPUT_BODY=136 OUTPUT=136
P=137 OUTPUT_BODY=137
PARAM=138 P=138
PARAMNAMES=139 PARAM=139
PARAMS=140 PARAMNAMES=140
PAREN=141 PARAMS=141
PFPAR=142 PAREN=142
PLUS=143 PFPAR=143
PLUS_INFINITY=144 PLUS=144
POINT=145 PLUS_INFINITY=145
PRIMARY=146 POINT=146
PRIORITY=147 PRIMARY=147
PROCEDURE=148 PRIORITY=148
PROCEDURE_CALL=149 PROCEDURE=149
PROCEDURE_NAME=150 PROCEDURE_CALL=150
PROCESS=151 PROCEDURE_NAME=151
PROVIDED=152 PROCESS=152
Q=153 PROVIDED=153
QUESTION=154 Q=154
R=155 QUESTION=155
RANGE=156 R=156
REFERENCED=157 RANGE=157
REM=158 REFERENCED=158
RESET=159 REM=159
RETURN=160 RESET=160
RETURNS=161 RETURN=161
ROUTE=162 RETURNS=162
R_BRACKET=163 ROUTE=163
R_PAREN=164 R_BRACKET=164
S=165 R_PAREN=165
SAVE=166 S=166
SELECTOR=167 SAVE=167
SEMI=168 SELECTOR=168
SEQOF=169 SEMI=169
SEQUENCE=170 SEQOF=170
SET=171 SEQUENCE=171
SIGNAL=172 SET=172
SIGNALROUTE=173 SIGNAL=173
SIGNAL_LIST=174 SIGNALROUTE=174
SORT=175 SIGNAL_LIST=175
SPECIFIC=176 SORT=176
START=177 SPECIFIC=177
STATE=178 START=178
STATELIST=179 STATE=179
STATE_AGGREGATION=180 STATELIST=180
STATE_PARTITION_CONNECTION=181 STATE_AGGREGATION=181
STIMULUS=182 STATE_PARTITION_CONNECTION=182
STOP=183 STIMULUS=183
STOPIF=184 STOP=184
STR=185 STOPIF=185
STRING=186 STR=186
STRUCT=187 STRING=187
SUBSTRUCTURE=188 STRUCT=188
SYNONYM=189 SUBSTRUCTURE=189
SYNONYM_LIST=190 SYNONYM=190
SYNTYPE=191 SYNONYM_LIST=191
SYSTEM=192 SYNTYPE=192
T=193 SYSTEM=193
TASK=194 T=194
TASK_BODY=195 TASK=195
TERMINATOR=196 TASK_BODY=196
TEXT=197 TERMINATOR=197
TEXTAREA=198 TEXT=198
TEXTAREA_CONTENT=199 TEXTAREA=199
THEN=200 TEXTAREA_CONTENT=200
THIS=201 THEN=201
TIMER=202 THIS=202
TO=203 TIMER=203
TRANSITION=204 TO=204
TRUE=205 TRANSITION=205
TYPE=206 TRUE=206