Skip to content
Open
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
7 changes: 2 additions & 5 deletions lint_rules/lint_rules/ifs_arpege_coding_standards.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
"""

from collections import defaultdict
import re

try:
from fparser.two.Fortran2003 import Intrinsic_Name
Expand Down Expand Up @@ -48,15 +47,13 @@ class MissingImplicitNoneRule(GenericRule):
),
}

_regex = re.compile(r'implicit\s+none\b', re.I)

@classmethod
def check_for_implicit_none(cls, ir_):
"""
Check for intrinsic nodes that match the regex.
"""
for intr in FindNodes(ir.Intrinsic).visit(ir_):
if cls._regex.match(intr.text):
for intr in FindNodes(ir.ImplicitStmt).visit(ir_):
if not intr.text or intr.text.lower() == 'none':
break
else:
return False
Expand Down
14 changes: 6 additions & 8 deletions lint_rules/lint_rules/ifs_coding_standards_2011.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ class LimitSubroutineStatementsRule(GenericRule): # Coding standards 2.2

# List of nodes that are considered executable statements
exec_nodes = (
ir.Assignment, ir.MaskedStatement, ir.Intrinsic, ir.Allocation,
ir.Assignment, ir.MaskedStatement, ir.GenericStmt, ir.Allocation,
ir.Deallocation, ir.Nullify, ir.CallStatement
)

Expand All @@ -231,7 +231,7 @@ def check_subroutine(cls, subroutine, rule_report, config, **kwargs):
nodes = FindNodes(cls.exec_nodes).visit(subroutine.ir)
num_nodes = len(nodes)
# Subtract number of non-exec intrinsic nodes
intrinsic_nodes = filter(lambda node: isinstance(node, ir.Intrinsic), nodes)
intrinsic_nodes = filter(lambda node: isinstance(node, ir.GenericStmt), nodes)
num_nodes -= sum(1 for _ in filter(
lambda node: cls.match_non_exec_intrinsic_node.match(node.text), intrinsic_nodes))

Expand Down Expand Up @@ -298,15 +298,13 @@ class ImplicitNoneRule(GenericRule): # Coding standards 4.4
'title': '"IMPLICIT NONE" is mandatory in all routines.',
}

_regex = re.compile(r'implicit\s+none\b', re.I)

@staticmethod
def check_for_implicit_none(ast):
"""
Check for intrinsic nodes that match the regex.
"""
for intr in FindNodes(ir.Intrinsic).visit(ast):
if ImplicitNoneRule._regex.match(intr.text):
for intr in FindNodes(ir.ImplicitStmt).visit(ast):
if not intr.text or intr.text.lower() == 'none':
break
else:
return False
Expand Down Expand Up @@ -450,9 +448,9 @@ class BannedStatementsRule(GenericRule): # Coding standards 4.11
@classmethod
def check_subroutine(cls, subroutine, rule_report, config, **kwargs):
'''Check for banned statements in intrinsic nodes.'''
for intr in FindNodes(ir.Intrinsic).visit(subroutine.ir):
for intr in FindNodes(ir.GenericStmt).visit(subroutine.ir):
for keyword in config['banned']:
if keyword.lower() in intr.text.lower():
if keyword.upper() in intr.text.upper() or keyword.upper() == intr.keyword:
rule_report.add(f'Banned keyword "{keyword}"', intr)


Expand Down
2 changes: 1 addition & 1 deletion loki/backend/cgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ def visit_Node(self, o, **kwargs):

# Handler for IR nodes

def visit_Intrinsic(self, o, **kwargs): # pylint: disable=unused-argument
def visit_GenericStmt(self, o, **kwargs): # pylint: disable=unused-argument
"""
Format intrinsic nodes.
"""
Expand Down
6 changes: 4 additions & 2 deletions loki/backend/fgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,11 +323,13 @@ def visit_str(self, o, **kwargs):

# Handler for IR nodes

def visit_Intrinsic(self, o, **kwargs):
def visit_GenericStmt(self, o, **kwargs):
"""
Format intrinsic nodes.
"""
return self.format_line(str(o.text).lstrip())
keyword = f'{o.keyword} ' if o.keyword else ''
text = ', '.join(self.visit_all(as_tuple(o.text), **kwargs)) if o.text else ''
return self.format_line(keyword, str(text).lstrip())

def visit_RawSource(self, o, **kwargs):
"""
Expand Down
2 changes: 1 addition & 1 deletion loki/backend/pygen.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ def <name>(<args>):

# Handler for IR nodes

def visit_Intrinsic(self, o, **kwargs): # pylint: disable=unused-argument
def visit_GenericStmt(self, o, **kwargs): # pylint: disable=unused-argument
"""
Format intrinsic nodes.
"""
Expand Down
2 changes: 1 addition & 1 deletion loki/backend/tests/test_fgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def test_fgen_literal_list_linebreak(frontend, tmp_path):
# Make sure all lines are continued correctly
code = module.to_fortran()
code_lines = code.splitlines()
assert len(code_lines) in (35, 36) # OMNI produces an extra line
assert len(code_lines) in (35, 37) # OMNI produces an extra line
assert all(line.strip(' &\n') for line in code_lines)
assert all(len(line) < 132 for line in code_lines)

Expand Down
22 changes: 12 additions & 10 deletions loki/backend/tests/test_stringifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ def test_stringifier(frontend, tmp_path):
"""
fcode = """
MODULE some_mod
IMPLICIT NONE
INTEGER :: n
!$loki dimension(klon)
REAL :: arr(:)
Expand Down Expand Up @@ -83,13 +84,14 @@ def test_stringifier(frontend, tmp_path):
ref_lines = [
"<Module:: some_mod>", # l. 1
"#<Section::>",
"##<Implicit:: NONE>",
"##<VariableDeclaration:: n>",
"##<Pragma:: loki dimension(klon)>",
"##<VariableDeclaration:: arr(:)>",
"#<Subroutine:: some_routine>",
"##<Comment:: ! This is a b...>",
"##<Section::>",
"###<Intrinsic:: IMPLICIT NONE>",
"###<Implicit:: NONE>",
"###<VariableDeclaration:: x>", # l. 10
"###<VariableDeclaration:: y>",
"###<VariableDeclaration:: i>",
Expand All @@ -112,15 +114,15 @@ def test_stringifier(frontend, tmp_path):
"... 1. + 1.>",
"#<Function:: my_sqrt>", # l. 30
"##<Section::>",
"###<Intrinsic:: IMPLICIT NONE>",
"###<Implicit:: NONE>",
"###<VariableDeclaration:: arg>",
"###<VariableDeclaration:: my_sqrt>",
"##<Section::>",
"###<Assignment:: my_sqrt = SQRT(arg)>",
"#<Subroutine:: other_routine>",
"##<CommentBlock:: ! This is jus...>",
"##<Section::>",
"###<Intrinsic:: IMPLICIT NONE>", # l. 40
"###<Implicit:: NONE>", # l. 40
"###<VariableDeclaration:: m>",
"###<VariableDeclaration:: var(:)>",
"##<Section::>",
Expand All @@ -129,11 +131,11 @@ def test_stringifier(frontend, tmp_path):
"####<Case (0)>",
"#####<Assignment:: m = 1>",
"####<Case (1:10)>",
"#####<Intrinsic:: PRINT *, '1 t...>",
"#####<GenericStmt:: PRINT *, '1 t...>",
"####<Case (-1, -2)>", # l. 50
"#####<Assignment:: m = 10>",
"####<Default>",
"#####<Intrinsic:: PRINT *, 'Def...>",
"#####<GenericStmt:: PRINT *, 'Def...>",
"###<Associate:: arr(m)=x>",
"####<Assignment:: x = x*2.>",
"###<Allocation:: var>",
Expand All @@ -144,12 +146,12 @@ def test_stringifier(frontend, tmp_path):

if frontend == OMNI:
# Some string inconsistencies
ref_lines[15] = ref_lines[15].replace('1E-8', '1e-8')
ref_lines[35] = ref_lines[35].replace('SQRT', 'sqrt')
ref_lines[48] = ref_lines[48].replace('PRINT', 'print')
ref_lines[52] = ref_lines[52].replace('PRINT', 'print')
ref_lines[16] = ref_lines[16].replace('1E-8', '1e-8')
ref_lines[36] = ref_lines[36].replace('SQRT', 'sqrt')
ref_lines[49] = ref_lines[49].replace('PRINT', 'print')
ref_lines[53] = ref_lines[53].replace('PRINT', 'print')

cont_index = 27 # line number where line continuation is happening
cont_index = 28 # line number where line continuation is happening
ref = '\n'.join(ref_lines)
module = Module.from_source(fcode, frontend=frontend, xmods=[tmp_path])

Expand Down
2 changes: 1 addition & 1 deletion loki/expression/tests/test_expression.py
Original file line number Diff line number Diff line change
Expand Up @@ -602,7 +602,7 @@ def test_output_intrinsics(frontend):
ref[1] = ref[1].replace(' * ', '*')
ref[1] = ref[1].replace('- 1', '-1')

intrinsics = FindNodes(ir.Intrinsic).visit(routine.body)
intrinsics = FindNodes(ir.GenericStmt).visit(routine.body)
assert len(intrinsics) == 2
assert intrinsics[0].text.lower() == ref[0]
assert intrinsics[1].text.lower() == ref[1]
Expand Down
90 changes: 57 additions & 33 deletions loki/frontend/fparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,13 +322,13 @@ def visit_List(self, o, **kwargs):
"""
return tuple(self.visit(i, **kwargs) for i in o.children)

def visit_Intrinsic_Stmt(self, o, **kwargs):
def visit_Generic_Stmt(self, o, **kwargs):
"""
Universal routine to capture nodes as plain string in the IR
"""
label = kwargs.get('label')
label = str(label) if label else label # Ensure srting labels
return ir.Intrinsic(text=o.tostr(), label=label, source=kwargs.get('source'))
return ir.GenericStmt(text=o.tostr(), label=label, source=kwargs.get('source'))

#
# Base blocks
Expand Down Expand Up @@ -1526,12 +1526,18 @@ def visit_Final_Binding(self, o, **kwargs):
symbols=symbols, final=True, source=kwargs.get('source'), label=kwargs.get('label')
)

def visit_Contains_Stmt(self, o, **kwargs):
return ir.ContainsStmt(source=kwargs.get('source'))

def visit_Private_Components_Stmt(self, o, **kwargs):
return ir.PrivateStmt(source=kwargs.get('source'))

def visit_Binding_Private_Stmt(self, o, **kwargs):
return ir.PrivateStmt(source=kwargs.get('source'))

visit_Binding_Name_List = visit_List
visit_Final_Subroutine_Name_List = visit_List
visit_Contains_Stmt = visit_Intrinsic_Stmt
visit_Binding_Private_Stmt = visit_Intrinsic_Stmt
visit_Private_Components_Stmt = visit_Intrinsic_Stmt
visit_Sequence_Stmt = visit_Intrinsic_Stmt
visit_Sequence_Stmt = visit_Generic_Stmt

#
# ASSOCIATE blocks
Expand Down Expand Up @@ -3245,14 +3251,18 @@ def visit_Include_Stmt(self, o, **kwargs):
label=kwargs.get('label'))

def visit_Implicit_Stmt(self, o, **kwargs):
return ir.Intrinsic(text=f'IMPLICIT {o.items[0]}', source=kwargs.get('source'),
label=kwargs.get('label'))
if len(o.items) == 1 and isinstance(o.items[0], str):
return ir.ImplicitStmt(text=o.items[0], **kwargs)
content = tuple(i if isinstance(i, str) else self.visit(i, **kwargs) for i in o.items)
return ir.ImplicitStmt(text=content, **kwargs)

def visit_Print_Stmt(self, o, **kwargs):
# NOTE: fparser returns None for an empty print (`PRINT *`) instead of
# the usual `Output_Item_List` entity.
return ir.Intrinsic(text=f'PRINT {", ".join(str(i) for i in o.items if i is not None)}',
source=kwargs.get('source'), label=kwargs.get('label'))
return ir.GenericStmt(
text=f'PRINT {", ".join(str(i) for i in o.items if i is not None)}',
source=kwargs.get('source'), label=kwargs.get('label')
)

# TODO: Deal with line-continuation pragmas!
_re_pragma = re.compile(r'^\s*\!\$(?P<keyword>\w+)\s*(?P<content>.*)', re.IGNORECASE)
Expand Down Expand Up @@ -3491,29 +3501,43 @@ def visit_Parenthesis(self, o, **kwargs):
expression = ParenthesisedPow(expression.base, expression.exponent)
return expression

visit_Format_Stmt = visit_Intrinsic_Stmt
visit_Write_Stmt = visit_Intrinsic_Stmt
visit_Goto_Stmt = visit_Intrinsic_Stmt
visit_Return_Stmt = visit_Intrinsic_Stmt
visit_Continue_Stmt = visit_Intrinsic_Stmt
visit_Cycle_Stmt = visit_Intrinsic_Stmt
visit_Exit_Stmt = visit_Intrinsic_Stmt
visit_Save_Stmt = visit_Intrinsic_Stmt
visit_Read_Stmt = visit_Intrinsic_Stmt
visit_Open_Stmt = visit_Intrinsic_Stmt
visit_Close_Stmt = visit_Intrinsic_Stmt
visit_Inquire_Stmt = visit_Intrinsic_Stmt
visit_Namelist_Stmt = visit_Intrinsic_Stmt
visit_Parameter_Stmt = visit_Intrinsic_Stmt
visit_Dimension_Stmt = visit_Intrinsic_Stmt
visit_Equivalence_Stmt = visit_Intrinsic_Stmt
visit_Common_Stmt = visit_Intrinsic_Stmt
visit_Stop_Stmt = visit_Intrinsic_Stmt
visit_Error_Stop_Stmt = visit_Intrinsic_Stmt
visit_Backspace_Stmt = visit_Intrinsic_Stmt
visit_Rewind_Stmt = visit_Intrinsic_Stmt
visit_Entry_Stmt = visit_Intrinsic_Stmt
visit_Cray_Pointer_Stmt = visit_Intrinsic_Stmt
#
# Remaining internal Fortran statements
#

def visit_Save_Stmt(self, o, **kwargs):
return ir.SaveStmt(source=kwargs.get('source'))

def visit_Return_Stmt(self, o, **kwargs):
return ir.ReturnStmt(source=kwargs.get('source'))

def visit_Cycle_Stmt(self, o, **kwargs):
return ir.CycleStmt(source=kwargs.get('source'))

def visit_Goto_Stmt(self, o, **kwargs):
label = o.items[0].tostr()
return ir.GotoStmt(text=label, source=kwargs.get('source'))

visit_Intrinsic_Stmt = visit_Generic_Stmt
visit_Format_Stmt = visit_Generic_Stmt
visit_Write_Stmt = visit_Generic_Stmt
visit_Continue_Stmt = visit_Generic_Stmt
visit_Exit_Stmt = visit_Generic_Stmt
visit_Read_Stmt = visit_Generic_Stmt
visit_Open_Stmt = visit_Generic_Stmt
visit_Close_Stmt = visit_Generic_Stmt
visit_Inquire_Stmt = visit_Generic_Stmt
visit_Namelist_Stmt = visit_Generic_Stmt
visit_Parameter_Stmt = visit_Generic_Stmt
visit_Dimension_Stmt = visit_Generic_Stmt
visit_Equivalence_Stmt = visit_Generic_Stmt
visit_Common_Stmt = visit_Generic_Stmt
visit_Stop_Stmt = visit_Generic_Stmt
visit_Error_Stop_Stmt = visit_Generic_Stmt
visit_Backspace_Stmt = visit_Generic_Stmt
visit_Rewind_Stmt = visit_Generic_Stmt
visit_Entry_Stmt = visit_Generic_Stmt
visit_Cray_Pointer_Stmt = visit_Generic_Stmt

def visit_Cpp_If_Stmt(self, o, **kwargs):
return ir.PreprocessorDirective(text=o.tostr(), source=kwargs.get('source'))
Expand Down
Loading
Loading