Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelog
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@

43) PR #383. Fixes to various pylint errors.

44) PR #384 (working towards #354) Adds support for CASE constructs with
a DEFAULT clause when translating Fortran fparser2 to PSyIR.

release 1.7.0 20th December 2018

1) #172 and PR #173 Add support for logical declaration, the save
Expand Down
4 changes: 2 additions & 2 deletions doc/developer_guide/system_specific_setup.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
System-specific Developer Set-up
================================

Section :ref:`setup_user` in the PSyclone User Guide
Section :ref:`user_guide:system_specific_setup` in the PSyclone User Guide
describes the setup for a user of PSyclone. It includes all steps necessary
to be able to use PSyclone. And while you could obviously do
some development, none of the required tools for testing or
Expand All @@ -12,7 +12,7 @@ documentation creation will be installed.
This section adds software that is used to develop and test
PSyclone. It includes all packages for testing and creation of
documentation in html and pdf. We assume you have already installed
the software described in the :ref:`setup_user` section.
the software described in the :ref:`user_guide:system_specific_setup` section.

It contains detailed instructions for Ubuntu 16.04.2 and
OpenSUSE 42.2 - if you are working with a different Linux
Expand Down
Binary file modified psyclone.pdf
Binary file not shown.
51 changes: 42 additions & 9 deletions src/psyclone/psyGen.py
Original file line number Diff line number Diff line change
Expand Up @@ -5564,12 +5564,15 @@ def _case_construct_handler(self, node, parent):
:type node: :py:class:`fparser.two.Fortran2003.Case_Construct`
:param parent: Parent node of the PSyIR node we are constructing.
:type parent: :py:class:`psyclone.psyGen.Node`

:returns: PSyIR representation of node
:rtype: :py:class:`psyclone.psyGen.IfBlock`

:raises InternalError: If the fparser2 tree has an unexpected \
structure.
:raises NotImplementedError: If the fparser2 tree contains an \
unsupported structure and should be placed in a CodeBlock.

'''
from fparser.two import Fortran2003
# Check that the fparser2 parsetree has the expected structure
Expand All @@ -5582,24 +5585,33 @@ def _case_construct_handler(self, node, parent):
"Failed to find closing case statement in: "
"{0}".format(str(node)))

# Search for all the CASE clauses in the Case_Construct
# Search for all the CASE clauses in the Case_Construct. We do this
# because the fp2 parse tree has a flat structure at this point with
# the clauses being siblings of the contents of the clauses. The
# final index in this list will hold the position of the end-select
# statement.
clause_indices = []
selector = None
# The position of the 'case default' clause, if any
default_clause_idx = None
for idx, child in enumerate(node.content):
child._parent = node # Retrofit parent info
if isinstance(child, Fortran2003.Select_Case_Stmt):
selector = child.items[0]
if isinstance(child, Fortran2003.Case_Stmt):
# Case Default and value Ranges not supported yet, if found
# we raise a NotImplementedError that the process_node() will
# catch and generate a CodeBlock instead.
# Case value Ranges not supported yet, if found we
# raise a NotImplementedError that the process_node()
# will catch and generate a CodeBlock instead.
case_expression = child.items[0].items[0]
if isinstance(case_expression,
(Fortran2003.Case_Value_Range,
Fortran2003.Case_Value_Range_List)):
raise NotImplementedError("Case Value Range Statement")
elif case_expression is None:
raise NotImplementedError("Case Default Statement")
if case_expression is None:
# This is a 'case default' clause - store its position.
# We do this separately as this clause is special and
# will be added as a final 'else'.
default_clause_idx = idx
clause_indices.append(idx)
if isinstance(child, Fortran2003.End_Select_Stmt):
clause_indices.append(idx)
Expand All @@ -5609,6 +5621,9 @@ def _case_construct_handler(self, node, parent):
currentparent = parent
num_clauses = len(clause_indices) - 1
for idx in range(num_clauses):
# Skip the 'default' clause for now because we handle it last
if clause_indices[idx] == default_clause_idx:
continue
start_idx = clause_indices[idx]
end_idx = clause_indices[idx+1]
clause = node.content[start_idx]
Expand Down Expand Up @@ -5647,13 +5662,32 @@ def _case_construct_handler(self, node, parent):
elsebody = Schedule(parent=currentparent)
currentparent.addchild(elsebody)
elsebody.addchild(ifblock)
elsebody.ast = node.content[start_idx]
elsebody.ast_end = node.content[end_idx]
elsebody.ast = node.content[start_idx + 1]
elsebody.ast_end = node.content[end_idx - 1]
else:
rootif = ifblock

currentparent = ifblock

if default_clause_idx:
# Finally, add the content of the 'default' clause as a last
# 'else' clause.
elsebody = Schedule(parent=currentparent)
start_idx = default_clause_idx
# Find the next 'case' clause that occurs after 'case default'
# (if any)
end_idx = -1
for idx in clause_indices:
if idx > default_clause_idx:
end_idx = idx
break
self.process_nodes(parent=elsebody,
nodes=node.content[start_idx + 1:
end_idx],
nodes_parent=node)
currentparent.addchild(elsebody)
elsebody.ast = node.content[start_idx + 1]
elsebody.ast_end = node.content[end_idx - 1]
return rootif

def _return_handler(self, _, parent):
Expand Down Expand Up @@ -5845,7 +5879,6 @@ class Symbol(object):
:raises ValueError: Provided parameters contain invalid values.

'''

# Tuple with the valid values for the access attribute.
valid_scope_types = ('local', 'global_argument')
# Tuple with the valid datatypes.
Expand Down
56 changes: 40 additions & 16 deletions src/psyclone/tests/psyGen_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -4348,7 +4348,7 @@ def test_fparser2astprocessor_handling_complex_if_construct(f2008_parser):
assert nested_if2.children[1].children[0].children[0].name == 'found'


def test_fparser2astprocessor_handling_Case_construct(f2008_parser):
def test_fparser2astprocessor_handling_case_construct(f2008_parser):
''' Test that fparser2 Case_Construct is converted to the expected PSyIR
tree structure.
'''
Expand Down Expand Up @@ -4389,7 +4389,44 @@ def test_fparser2astprocessor_handling_Case_construct(f2008_parser):
assert len(ifnode.else_body[0].children) == 2 # SELECT CASE ends here


def test_fparser2astprocessor_handling_invalid_Case_construct(f2008_parser):
def test_fp2astproc_case_default(f2008_parser):
''' Check that the fparser2ASTProcessor handles SELECT blocks with
a default clause. '''
from fparser.common.readfortran import FortranStringReader
from fparser.two.Fortran2003 import Execution_Part, Assignment_Stmt
case_clauses = ["CASE default\nbranch3 = 1\nbranch3 = branch3 * 2\n",
"CASE (label1)\nbranch1 = 1\n",
"CASE (label2)\nbranch2 = 1\n"]
# Loop over the 3 possible locations for the 'default' clause
for idx1, idx2, idx3 in [(0, 1, 2), (1, 0, 2), (1, 2, 0)]:
fortran_text = (
"SELECT CASE (selector)\n"
"{0}{1}{2}"
"END SELECT\n".format(case_clauses[idx1], case_clauses[idx2],
case_clauses[idx3]))
reader = FortranStringReader(fortran_text)
fparser2case_construct = Execution_Part.match(reader)[0][0]

fake_parent = Node()
processor = Fparser2ASTProcessor()
processor.process_nodes(fake_parent, [fparser2case_construct], None)
assigns = fake_parent.walk(fake_parent.children, Assignment)
# Check that the assignment to 'branch 3' (in the default clause) is
# the deepest in the tree
assert "branch3" in str(assigns[2])
assert isinstance(assigns[2].ast, Assignment_Stmt)
assert isinstance(assigns[2].parent, Schedule)
assert isinstance(assigns[2].parent.ast, Assignment_Stmt)
assert "branch3 * 2" in str(assigns[2].parent.ast_end)
assert isinstance(assigns[2].parent.parent, IfBlock)
# Check that the if-body of the parent IfBlock also contains
# an Assignment
assert isinstance(assigns[2].parent.parent.children[1], Schedule)
assert isinstance(assigns[2].parent.parent.children[1].children[0],
Assignment)


def test_fp2astproc_handling_invalid_case_construct(f2008_parser):
''' Test that the Case_Construct handler raises the proper errors when
it parses invalid or unsupported fparser2 trees.
'''
Expand All @@ -4409,20 +4446,7 @@ def test_fparser2astprocessor_handling_invalid_Case_construct(f2008_parser):
processor.process_nodes(fake_parent, [fparser2case_construct], None)
assert isinstance(fake_parent.children[0], CodeBlock)

# CASE DEFAULT Statment not supported
reader = FortranStringReader(
'''SELECT CASE (selector)
CASE DEFAULT
branch3 = 1
END SELECT''')
fparser2case_construct = Execution_Part.match(reader)[0][0]

fake_parent = Node()
processor = Fparser2ASTProcessor()
processor.process_nodes(fake_parent, [fparser2case_construct], None)
assert isinstance(fake_parent.children[0], CodeBlock)

# but CASE (default) is just a regular symbol named default
# CASE (default) is just a regular symbol named default
reader = FortranStringReader(
'''SELECT CASE (selector)
CASE (default)
Expand Down