Commit 1755877f authored by Maxime Perrotin's avatar Maxime Perrotin
Browse files

Merge https://github.com/esa/opengeode into feature_buster

parents e9ae37d3 7263b97c
......@@ -124,6 +124,12 @@ The background pattern was downloaded from www.subtlepatterns.com
Changelog
=========
**3.4.1 (03/2021)**
- Fix support of remote synchronous calls
**3.4.0 (03/2021)**
- Support exported/reference procedures (allowing remote synchronous calls)
**3.3.7 (03/2021)**
- Support IA5String type
......
......@@ -712,8 +712,9 @@ package body {process_name}_RI is''']
pi_header = procedure_header(proc)
ads_template.append(f'{pi_header};')
if not proc.external and not generic:
# Export for TASTE as a synchronous PI
ads_template.append(
f'pragma Export (C, p{SEPARATOR}{proc.inputString}, "_{proc.inputString}");')
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)
......@@ -1518,10 +1519,10 @@ def _call_external_function(output, **kwargs):
code.append(u'{tmp}.Length := {app_len};'
.format(tmp=tmp_id, app_len=app_len))
else:
code.append(u'{} := {};'.format(tmp_id, p_id))
code.append(f'{tmp_id} := {p_id};')
list_of_params.append(u"{}{}"
.format(tmp_id,
u", {}'Size".format(tmp_id)
f", {tmp_id}'Size"
if is_out_sig else ""))
else:
# Output parameters/local variables
......@@ -3091,7 +3092,8 @@ def procedure_header(proc):
typename = type_name(fpar['type'])
params.append(u'{name}: in{out} {ptype}'.format(
name=fpar.get('name'),
out=' out' if fpar.get('direction') == 'out' else '',
# out: exported procedures always use in out for taste (C code) compatibility
out=' out' if (fpar.get('direction') == 'out' or proc.exported) else '',
ptype=typename))
pi_header += ';'.join(params)
pi_header += ')'
......@@ -3133,10 +3135,8 @@ def _inner_procedure(proc, **kwargs):
# Inner procedures declared external by the user: pragma import
# the C symbol with the same name. Overrules the pragma import from
# taste for required interfaces.
local_decl.append(u'pragma import(C, p{sep}{proc_name}, '
u'"{proc_name}");'
.format(sep=SEPARATOR,
proc_name=proc.inputString))
local_decl.append(f'pragma Import (C, p{SEPARATOR}{proc.inputString}, '
f'"{proc.inputString}");')
else:
# Generate the code for the procedure itself
# local variables and code of the START transition
......
......@@ -79,10 +79,12 @@ def _block(ast, scene):
param=('(' + sig['type'].ReferencedTypeName.replace('-', '_') + ')')
if 'type' in sig else '')
for sig in ast.parent.signals]
procedures = ["procedure {proc.inputString};\n{optfpar}external;\n"
procedures = ["{exported}procedure {proc.inputString};\n{optfpar}{external};\n"
.format(proc=proc,
optfpar="fpar\n " + u",\n ".join
([u"{direc} {fp[name]} {asn1}"
exported="exported " if proc.exported else "",
external="referenced" if proc.referenced else "external",
optfpar="fpar\n " + ",\n ".join
(["{direc} {fp[name]} {asn1}"
.format(fp=fpar,
direc="in"
if fpar['direction']=='in'
......@@ -125,8 +127,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:
......
......@@ -840,8 +840,8 @@ class Procedure:
self.transitions = []
# Determine if a procedure is externally defined
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 +851,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 +1036,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 +1056,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)
......@@ -2963,16 +2960,21 @@ def procedure_pre(root, parent=None, context=None):
errors.append([f"In procdure {proc.inputString}: {str(err)}",
[0, 0], []])
if proc.return_var:
warnings.append('Procedure return variable not supported')
warnings.append([f'Procedure {proc.inputString}:'
' return variable not supported', [0, 0], []])
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(
warnings.append([
'Unsupported construct in procedure, type: ' +
sdl92Parser.tokenNamesMap[child.type] +
' - line ' + str(child.getLine()) +
' - in procedure ' + str(proc.inputString))
' - in procedure ' + proc.inputString, [0, 0], []])
for each in chain(errors, warnings):
each[2].insert(0, f'PROCEDURE {proc.inputString}')
return proc, content, errors, warnings
......@@ -2986,6 +2988,52 @@ 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()
#breakpoint()
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 '''
......@@ -3015,16 +3063,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)
......@@ -3368,6 +3413,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:
......@@ -3375,12 +3422,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
......@@ -3424,9 +3466,24 @@ def text_area(root, parent=None, context=None):
warnings.append('Unsupported construct in text area, type: ' +
str(child.type))
# Report errors with symbol coordinates
errors = [[e, [ta.pos_x or 0, ta.pos_y or 0], []] for e in errors]
warnings = [[w, [ta.pos_x or 0, ta.pos_y or 0], []] for w in warnings]
return ta, errors, warnings
clean_errs, clean_warns = [], []
# some errors and warnings may already have the list structure because
# they were parsed by a rule that added them (e.g. procedures)
# in that case we must reuse this structure. otherwise, we add the
# missing parts
for err in errors:
if type(err) == list:
err[1] = [ta.pos_x or 0, ta.pos_y or 0]
clean_errs.append(err)
else: # string
clean_errs.append([err, [ta.pos_x or 0, ta.pos_y or 0], []])
for warn in warnings:
if type(warn) == list:
warn[1] = [ta.pos_x or 0, ta.pos_y or 0]
clean_warns.append(warn)
else: # string
clean_warns.append([warn, [ta.pos_x or 0, ta.pos_y or 0], []])
return ta, clean_errs, clean_warns
def signal(root):
......@@ -3530,6 +3587,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():
......@@ -3555,7 +3613,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)
......@@ -3582,6 +3640,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)
......@@ -3625,12 +3685,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)
......@@ -5423,7 +5481,27 @@ def pr_file(root):
ast.process_types.extend(p_types)
for child in processes:
# process definition at root level (can be a process type)
process, err, warn = process_definition(child, parent=ast)
# parse the process name to find the scope in which it is declared
for node in child.getChildren():
if node.type == lexer.ID:
processName = node.text
def rec_find_process_parent(block, proc_name : str):
# to define the parent of the process, find the block where it is
# specified with a "referenced" flag.
res = None
for nested in block.blocks:
res = rec_find_process_parent(nested, proc_name)
if res:
return nested
for proc in block.processes:
if proc.processName.lower() == proc_name.lower():
return block
proc_parent = ast
for system in ast.systems:
proc_parent = rec_find_process_parent(system, processName)
if proc_parent:
break
process, err, warn = process_definition(child, parent=proc_parent)
# check if the process was declared as referenced with FPAR
for each in ref_p_with_fpar:
if each.processName == process.processName:
......
......@@ -141,7 +141,7 @@ except ImportError:
__all__ = ['opengeode', 'SDL_Scene', 'SDL_View', 'parse']
__version__ = '3.3.8'
__version__ = '3.4.1'
if hasattr(sys, 'frozen'):
# Detect if we are running on Windows (py2exe-generated)
......
This diff is collapsed.
This diff is collapsed.
......@@ -51,6 +51,7 @@ SDL_BLACKBOLD = ['\\b{word}\\b'.format(word=word) for word in (
'SET_TIMER', 'RESET_TIMER', 'VIA', 'ENTRY', 'EXIT', 'PRIORITY',
'SYNTYPE', 'ENDSYNTYPE', 'CONSTANTS', 'ENDPROCEDURE', 'FOR',
'COMMENT', 'SIGNAL', 'SIGNALLIST', 'USE', 'RETURNS', 'ANY',
'EXPORTED', 'REFERENCED',
'NEWTYPE', 'ENDNEWTYPE', 'ARRAY', 'STRUCT', 'SYNONYM')]
SDL_REDBOLD = ['\\b{word}\\b'.format(word=word) for word in (
......
......@@ -284,16 +284,19 @@ parameters_of_sort
// procedure
// 2021-03 Added EXPORTED and REFEERENCED keywords
// needed to declare a synchronous provided interface at system level
procedure
: cif?
PROCEDURE procedure_id (e1=end | SEMI)
EXPORTED? PROCEDURE procedure_id (e1=end | SEMI)
fpar?
res=procedure_result?
(text_area | procedure)*
((processBody? ENDPROCEDURE procedure_id?) | EXTERNAL)
((processBody? ENDPROCEDURE procedure_id?)
| EXTERNAL | REFERENCED)
e2=end
-> ^(PROCEDURE cif? procedure_id $e1? $e2? fpar? $res?
text_area* procedure* processBody? EXTERNAL?)
text_area* procedure* processBody? EXTERNAL? EXPORTED? REFERENCED?)
;
// Procedure result / optional return type
......@@ -1530,6 +1533,7 @@ DECISION : D E C I S I O N;
ENDDECISION : E N D D E C I S I O N;
EXPORT : E X P O R T;
EXTERNAL : E X T E R N A L;
EXPORTED : E X P O R T E D;
REFERENCED : R E F E R E N C E D;
CONNECTION : C O N N E C T I O N;
ENDCONNECTION : E N D C O N N E C T I O N;
......
Supports Markdown
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