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
Changelog
=========
**3.4.0 (03/2021)**
- Support exported/reference procedures (allowing remote synchronous calls)
**3.3.7 (03/2021)**
- Support IA5String type
......
......@@ -712,8 +712,13 @@ package body {process_name}_RI is''']
pi_header = procedure_header(proc)
ads_template.append(f'{pi_header};')
if not proc.external and not generic:
ads_template.append(
f'pragma Export (C, p{SEPARATOR}{proc.inputString}, "_{proc.inputString}");')
if not proc.exported:
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
taste_template.extend(process_level_decl)
......
......@@ -125,8 +125,8 @@ def _automaton(ast, scene):
# Render procedures symbols
top_level_symbols.extend(
[render(proc, scene)
for proc in ast.inner_procedures
if not proc.external])
for proc in ast.inner_procedures
if not proc.external and not proc.textual_procedure])
# Render the start symbol
if ast.start:
......
......@@ -842,6 +842,8 @@ class Procedure:
self.external = False
# Determine if a procedure is a remote procedure
self.exported = False
# Determine if a procedure only has a textual definition
self.textual_procedure = False
# Optional comment
self.comment = None
# Set of symbols contained in the procedure (type Automaton)
......@@ -851,6 +853,10 @@ class Procedure:
self.output_signals = []
# The "DECISION ANY" construct requires random number generators
self.random_generator = set()
# Procedure declared as EXPORTED
self.exported : bool = False
# procedure declared as REFERENCED
self.referenced : bool = False
class Process:
......@@ -1032,6 +1038,7 @@ class System:
self.filename = None
# Reference to top-level AST
self.ast = None
# self.parent = None
# list of SIGNAL declarations: [{'name': str, 'type': asn1type}]
# (Supporting only one parameter)
self.signals = []
......@@ -1051,6 +1058,8 @@ class AST:
AST entries are systems, processes, and USE clauses
(cf. ANTLR grammar, production "pr_file")
'''
# Top-level node: parent remains None
self.parent = None
# Set of input files the AST was created from
self.pr_files = set()
# USE clauses: list of strings (eg. "DataView")
......
......@@ -2813,13 +2813,10 @@ def composite_state(root, parent=None, context=None):
elif new_proc.inputString.strip().lower() == 'exit':
comp.exit_procedure = new_proc
# check for duplicate declaration
if any(each.inputString.lower() == new_proc.inputString.lower()
for each in chain(comp.content.inner_procedures,
context.procedures)
if each.inputString.lower() not in ('entry', 'exit')):
errors.append(['Duplicate procedure Declaration: {}'
.format(new_proc.inputString),
[new_proc.pos_x, new_proc.pos_y], []])
dupl_errs = []
check_duplicate_procedures(comp, new_proc, dupl_errs)
for err_str in dupl_errs:
errors.append([err_str, [new_proc.pos_x, new_proc.pos_y], []])
# Add procedure to the context, to make it visible at scope level
comp.content.inner_procedures.append(new_proc)
context.procedures.append(new_proc)
......@@ -2968,6 +2965,10 @@ def procedure_pre(root, parent=None, context=None):
elif child.type in (lexer.PROCEDURE, lexer.START,
lexer.STATE, lexer.FLOATING_LABEL):
content.append(child)
elif child.type == lexer.EXPORTED:
proc.exported = True
elif child.type == lexer.REFERENCED:
proc.referenced = True
else:
warnings.append([
'Unsupported construct in procedure, type: ' +
......@@ -2987,6 +2988,51 @@ def procedure_returns(root):
return sdl_to_asn1(root.getChild(1).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):
''' Parse the content of a procedure '''
......@@ -3016,16 +3062,13 @@ def procedure_post(proc, content, parent=None, context=None):
errors.extend(err)
warnings.extend(warn)
# check for duplicate declaration
err = any(each.inputString.lower() == new_proc.inputString.lower()
for each in chain(proc.content.inner_procedures,
context.procedures))
dupl_errs = []
check_duplicate_procedures(context, new_proc, dupl_errs)
# Add procedure to the context, to make it visible at scope level
context.procedures.append(new_proc)
proc.content.inner_procedures.append(new_proc)
if err:
errors.append(['Duplicate declaration of procedure {}'
.format(new_proc.inputString),
[0, 0], []])
for err_str in dupl_errs:
errors.append([err_str, [0, 0], []])
elif child.type == lexer.START:
# START transition (fills the mapping structure)
proc.content.start, err, warn = start(child, context=proc)
......@@ -3369,6 +3412,8 @@ def text_area_content(root, ta_ast, context):
for each in procedures:
# Procedure textual declarations need ASN.1 types
proc, err, warn = procedure(each, context=context)
# set the flag to avoid rendering a graphical procedure
proc.textual_procedure = True
errors.extend(err)
warnings.extend(warn)
try:
......@@ -3376,12 +3421,7 @@ def text_area_content(root, ta_ast, context):
except AttributeError:
# May not be any content in current context (eg System)
content = []
# check for duplicates
if any(each.inputString.lower() == proc.inputString.lower()
for each in chain(content, context.procedures)):
errors.append('Duplicate Procedure Declaration: {}'
.format(proc.inputString))
check_duplicate_procedures(context, proc, errors)
# Add procedure to the container (process or procedure) if any
content.append(proc)
# Add to context to make it visible at scope level
......@@ -3546,6 +3586,7 @@ def system_definition(root, parent):
# Store the name of the file where the system is defined
system.filename = node_filename(root)
system.ast = parent
#system.parent = parent
asn1_files = []
signals, procedures, blocks = [], [], []
for child in root.getChildren():
......@@ -3571,7 +3612,7 @@ def system_definition(root, parent):
# Update list of ASN.1 files - if any
if not 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.extend(err)
warnings.extend(warn)
......@@ -3598,6 +3639,8 @@ def system_definition(root, parent):
for each in procedures:
proc, err, warn = procedure(
each, parent=None, context=system)
# Procedure defined at system level are only textual
proc.textual_procedure = True
errors.extend(err)
warnings.extend(warn)
system.procedures.append(proc)
......@@ -3641,12 +3684,10 @@ def process_definition(root, parent=None, context=None):
errors.extend(err)
warnings.extend(warn)
# check for duplicate declaration
if any(each.inputString.lower() == proc.inputString.lower()
for each in chain(process.content.inner_procedures,
process.procedures)):
errors.append(['Duplicate Procedure declaration: {}'
.format(proc.inputString),
[proc.pos_x, proc.pos_y], []])
dupl_errs = []
check_duplicate_procedures(process, proc, dupl_errs)
for err_str in dupl_errs:
errors.append([err_str, [proc.pos_x, proc.pos_y], []])
# Add it at process level so that it is in the scope
process.content.inner_procedures.append(proc)
process.procedures.append(proc)
......
......@@ -141,7 +141,7 @@ except ImportError:
__all__ = ['opengeode', 'SDL_Scene', 'SDL_View', 'parse']
__version__ = '3.3.8'
__version__ = '3.4.0'
if hasattr(sys, 'frozen'):
# 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
from antlr3 import *
......@@ -10,13 +10,13 @@ HIDDEN = BaseRecognizer.HIDDEN
# token types
EOF=-1
T__224=224
T__225=225
T__226=226
T__227=227
T__228=228
T__229=229
T__230=230
T__231=231
A=4
ACTION=5
ACTIVE=6
......@@ -84,165 +84,166 @@ ENTRY_POINT=67
EQ=68
EXPONENT=69
EXPORT=70
EXPRESSION=71
EXTERNAL=72
Exponent=73
F=74
FALSE=75
FI=76
FIELD=77
FIELDS=78
FIELD_NAME=79
FLOAT=80
FLOAT2=81
FLOATING_LABEL=82
FOR=83
FPAR=84
FROM=85
G=86
GE=87
GEODE=88
GROUND=89
GT=90
H=91
HYPERLINK=92
I=93
ID=94
IF=95
IFTHENELSE=96
IMPLIES=97
IMPORT=98
IN=99
INFORMAL_TEXT=100
INOUT=101
INPUT=102
INPUTLIST=103
INPUT_NONE=104
INT=105
J=106
JOIN=107
K=108
KEEP=109
L=110
LABEL=111
LE=112
LITERAL=113
LT=114
L_BRACKET=115
L_PAREN=116
M=117
MANTISSA=118
MINUS_INFINITY=119
MKSTRING=120
MOD=121
N=122
NEG=123
NEQ=124
NEWTYPE=125
NEXTSTATE=126
NONE=127
NOT=128
NUMBER_OF_INSTANCES=129
O=130
OCTSTR=131
OPEN_RANGE=132
OR=133
OUT=134
OUTPUT=135
OUTPUT_BODY=136
P=137
PARAM=138
PARAMNAMES=139
PARAMS=140
PAREN=141
PFPAR=142
PLUS=143
PLUS_INFINITY=144
POINT=145
PRIMARY=146
PRIORITY=147
PROCEDURE=148
PROCEDURE_CALL=149
PROCEDURE_NAME=150
PROCESS=151
PROVIDED=152
Q=153
QUESTION=154
R=155
RANGE=156
REFERENCED=157
REM=158
RESET=159
RETURN=160
RETURNS=161
ROUTE=162
R_BRACKET=163
R_PAREN=164
S=165
SAVE=166
SELECTOR=167
SEMI=168
SEQOF=169
SEQUENCE=170
SET=171
SIGNAL=172
SIGNALROUTE=173
SIGNAL_LIST=174
SORT=175
SPECIFIC=176
START=177
STATE=178
STATELIST=179
STATE_AGGREGATION=180
STATE_PARTITION_CONNECTION=181
STIMULUS=182
STOP=183
STOPIF=184
STR=185
STRING=186
STRUCT=187
SUBSTRUCTURE=188
SYNONYM=189
SYNONYM_LIST=190
SYNTYPE=191
SYSTEM=192
T=193
TASK=194
TASK_BODY=195
TERMINATOR=196
TEXT=197
TEXTAREA=198
TEXTAREA_CONTENT=199
THEN=200
THIS=201
TIMER=202
TO=203
TRANSITION=204
TRUE=205
TYPE=206
TYPE_INSTANCE=207
U=208
USE=209
V=210
VALUE=211
VARIABLE=212
VARIABLES=213
VIA=214
VIAPATH=215
VIEW=216
W=217
WITH=218
WS=219
X=220
XOR=221
Y=222
Z=223
EXPORTED=71
EXPRESSION=72
EXTERNAL=73
Exponent=74
F=75
FALSE=76
FI=77
FIELD=78
FIELDS=79
FIELD_NAME=80
FLOAT=81
FLOAT2=82
FLOATING_LABEL=83
FOR=84
FPAR=85
FROM=86
G=87
GE=88
GEODE=89
GROUND=90
GT=91
H=92
HYPERLINK=93
I=94
ID=95
IF=96
IFTHENELSE=97
IMPLIES=98
IMPORT=99
IN=100
INFORMAL_TEXT=101
INOUT=102
INPUT=103
INPUTLIST=104
INPUT_NONE=105
INT=106
J=107
JOIN=108
K=109
KEEP=110
L=111
LABEL=112
LE=113
LITERAL=114
LT=115
L_BRACKET=116
L_PAREN=117
M=118
MANTISSA=119
MINUS_INFINITY=120
MKSTRING=121
MOD=122
N=123
NEG=124
NEQ=125
NEWTYPE=126
NEXTSTATE=127
NONE=128
NOT=129
NUMBER_OF_INSTANCES=130
O=131
OCTSTR=132
OPEN_RANGE=133
OR=134
OUT=135
OUTPUT=136
OUTPUT_BODY=137
P=138
PARAM=139
PARAMNAMES=140
PARAMS=141
PAREN=142
PFPAR=143
PLUS=144
PLUS_INFINITY=145
POINT=146
PRIMARY=147
PRIORITY=148
PROCEDURE=149
PROCEDURE_CALL=150
PROCEDURE_NAME=151
PROCESS=152
PROVIDED=153
Q=154
QUESTION=155
R=156
RANGE=157
REFERENCED=158
REM=159
RESET=160
RETURN=161
RETURNS=162
ROUTE=163
R_BRACKET=164
R_PAREN=165
S=166
SAVE=167
SELECTOR=168
SEMI=169
SEQOF=170
SEQUENCE=171
SET=172
SIGNAL=173
SIGNALROUTE=174
SIGNAL_LIST=175
SORT=176
SPECIFIC=177
START=178
STATE=179
STATELIST=180
STATE_AGGREGATION=181
STATE_PARTITION_CONNECTION=182
STIMULUS=183
STOP=184
STOPIF=185
STR=186
STRING=187
STRUCT=188
SUBSTRUCTURE=189
SYNONYM=190
SYNONYM_LIST=191
SYNTYPE=192
SYSTEM=193
T=194
TASK=195
TASK_BODY=196
TERMINATOR=197
TEXT=198
TEXTAREA=199
TEXTAREA_CONTENT=200
THEN=201
THIS=202
TIMER=203
TO=204
TRANSITION=205
TRUE=206
TYPE=207
TYPE_INSTANCE=208
U=209
USE=210
V=211
VALUE=212
VARIABLE=213
VARIABLES=214
VIA=215
VIAPATH=216
VIEW=217
W=218
WITH=219
WS=220
X=221
XOR=222
Y=223
Z=224
# token names
tokenNamesMap = {
0: "<invalid>", 1: "<EOR>", 2: "<DOWN>", 3: "<UP>",
-1: "EOF", 224: "T__224", 225: "T__225", 226: "T__226", 227: "T__227",
228: "T__228", 229: "T__229", 230: "T__230", 4: "A", 5: "ACTION", 6: "ACTIVE",
-1: "EOF", 225: "T__225", 226: "T__226", 227: "T__227", 228: "T__228",
229: "T__229", 230: "T__230", 231: "T__231", 4: "A", 5: "ACTION", 6: "ACTIVE",
7: "AGGREGATION", 8: "ALL", 9: "ALPHA", 10: "ALTERNATIVE", 11: "AND",
12: "ANSWER", 13: "ANY", 14: "APPEND", 15: "ARRAY", 16: "ASN1", 17: "ASNFILENAME",
18: "ASSIGN", 19: "ASSIG_OP", 20: "ASTERISK", 21: "B", 22: "BASE", 23: "BITSTR",
......@@ -255,37 +256,37 @@ tokenNamesMap = {
56: "ENDCONNECTION", 57: "ENDDECISION", 58: "ENDFOR", 59: "ENDNEWTYPE",
60: "ENDPROCEDURE", 61: "ENDPROCESS", 62: "ENDSTATE", 63: "ENDSUBSTRUCTURE",
64: "ENDSYNTYPE", 65: "ENDSYSTEM", 66: "ENDTEXT", 67: "ENTRY_POINT",
68: "EQ", 69: "EXPONENT", 70: "EXPORT", 71: "EXPRESSION", 72: "EXTERNAL",
73: "Exponent", 74: "F", 75: "FALSE", 76: "FI", 77: "FIELD", 78: "FIELDS",
79: "FIELD_NAME", 80: "FLOAT", 81: "FLOAT2", 82: "FLOATING_LABEL", 83: "FOR",
84: "FPAR", 85: "FROM", 86: "G", 87: "GE", 88: "GEODE", 89: "GROUND",
90: "GT", 91: "H", 92: "HYPERLINK", 93: "I", 94: "ID", 95: "IF", 96: "IFTHENELSE",
97: "IMPLIES", 98: "IMPORT", 99: "IN", 100: "INFORMAL_TEXT", 101: "INOUT",
102: "INPUT", 103: "INPUTLIST", 104: "INPUT_NONE", 105: "INT", 106: "J",
107: "JOIN", 108: "K", 109: "KEEP", 110: "L", 111: "LABEL", 112: "LE",
113: "LITERAL", 114: "LT", 115: "L_BRACKET", 116: "L_PAREN", 117: "M",
118: "MANTISSA", 119: "MINUS_INFINITY", 120: "MKSTRING", 121: "MOD",
122: "N", 123: "NEG", 124: "NEQ", 125: "NEWTYPE", 126: "NEXTSTATE",
127: "NONE", 128: "NOT", 129: "NUMBER_OF_INSTANCES", 130: "O", 131: "OCTSTR",
132: "OPEN_RANGE", 133: "OR", 134: "OUT", 135: "OUTPUT", 136: "OUTPUT_BODY",
137: "P", 138: "PARAM", 139: "PARAMNAMES", 140: "PARAMS", 141: "PAREN",
142: "PFPAR", 143: "PLUS", 144: "PLUS_INFINITY", 145: "POINT", 146: "PRIMARY",
147: "PRIORITY", 148: "PROCEDURE", 149: "PROCEDURE_CALL", 150: "PROCEDURE_NAME",
151: "PROCESS", 152: "PROVIDED", 153: "Q", 154: "QUESTION", 155: "R",
156: "RANGE", 157: "REFERENCED", 158: "REM", 159: "RESET", 160: "RETURN",
161: "RETURNS", 162