def test_parser_invokeinfo_nocode(tmpdir): '''Check that the invoke_info() method in the Parser() class raises the expected exception if no relevant code (subroutine, module etc.) is found in the supplied fparser2 tree. ''' parser = Parser() alg_filename = str(tmpdir.join("empty.f90")) with open(alg_filename, "w") as ffile: ffile.write("") alg_parse_tree = parse_fp2(alg_filename) with pytest.raises(ParseError) as info: parser.invoke_info(alg_parse_tree) assert ("Program, module, function or subroutine not found in fparser2 " "parse tree.") in str(info.value)
def test_parser_invokeinfo_internalerror(): '''Test that the invoke_info method in the Parser class raises the expected exception if an unexpected child of Type_Declaration_Stmt or Data_Component_Def_Stmt is found. ''' alg_filename = os.path.join( LFRIC_TEST_PATH, "26.1_mixed_precision.f90") parser = Parser(kernel_path=LFRIC_TEST_PATH) alg_parse_tree = parse_fp2(alg_filename) # Modify parse tree to make it invalid alg_parse_tree.children[0].children[1].children[9].items = ["hello"] with pytest.raises(InternalError) as info: parser.invoke_info(alg_parse_tree) assert ( "Expected first child of Type_Declaration_Stmt or " "Data_Component_Def_Stmt to be Declaration_Type_Spec or " "Intrinsic_Type_Spec but found 'str'" in str(info.value))
def test_parser_invokeinfo_datatypes_clash(): '''Test that the invoke_info method in the Parser class allows multiple symbols with the same name and type but raises an exception if a symbol has the same name but a different type. This is simply a limitation of the current implementation as we do not capture the context of a symbol so do not deal with variable scope. This limitation will disapear when the PSyIR is used to determine datatypes, see issue #753. ''' alg_filename = os.path.join( LFRIC_TEST_PATH, "26.3_mixed_precision_error.f90") parser = Parser(kernel_path=LFRIC_TEST_PATH) alg_parse_tree = parse_fp2(alg_filename) with pytest.raises(NotImplementedError) as info: parser.invoke_info(alg_parse_tree) assert ("The same symbol 'a' is used for different datatypes, 'real, " "r_solver' and 'real, None'. This is not currently supported." in str(info.value))
def test_parser_invokeinfo_containers(tmpdir, code, name): '''Check that the invoke_info() method in the Parser() class works with program, module, subroutine and function. ''' parser = Parser() alg_filename = str(tmpdir.join("container.f90")) with open(alg_filename, "w") as ffile: ffile.write(code) alg_parse_tree = parse_fp2(alg_filename) res = parser.invoke_info(alg_parse_tree) assert isinstance(res, FileInfo) assert res.name == name
def test_parser_invokeinfo_datatypes_self(): '''Test that the invoke_info method in the Parser class captures the required datatype information when the argument is part of a class and is referenced via self. ''' alg_filename = os.path.join( LFRIC_TEST_PATH, "26.2_mixed_precision_self.f90") parser = Parser(kernel_path=LFRIC_TEST_PATH) alg_parse_tree = parse_fp2(alg_filename) info = parser.invoke_info(alg_parse_tree) args = info.calls[0].kcalls[0].args assert args[0]._datatype == ("r_solver_operator_type", None) assert args[1]._datatype == ("r_solver_field_type", None) assert args[2]._datatype == ("real", "r_solver") assert args[3]._datatype == ("quadrature_xyoz_type", None)
def test_parser_invokeinfo_datatypes(): '''Test that the invoke_info method in the Parser class captures the required datatype information for "standard" fields, operators and scalars i.e. defined as field_type, operator_type and r_def respectively. We also capture the datatype of quadrature but don't care. field_type is actually a vector which shows that the code works with arrays as well as individual types. ''' alg_filename = os.path.join(LFRIC_TEST_PATH, "10_operator.f90") parser = Parser(kernel_path=LFRIC_TEST_PATH) alg_parse_tree = parse_fp2(alg_filename) info = parser.invoke_info(alg_parse_tree) args = info.calls[0].kcalls[0].args assert args[0]._datatype == ("operator_type", None) assert args[1]._datatype == ("field_type", None) assert args[2]._datatype == ("real", "r_def") assert args[3]._datatype == ("quadrature_xyoz_type", None)
def test_parser_invokeinfo_first(tmpdir): '''Check that the invoke_info() method in the Parser() class evaluates the first subroutine, module, etc if more than one exist in the supplied fparser2 tree. ''' parser = Parser() alg_filename = str(tmpdir.join("two_routines.f90")) with open(alg_filename, "w") as ffile: ffile.write( "subroutine first()\n" "end subroutine first\n" "subroutine second()\n" "end subroutine second\n") alg_parse_tree = parse_fp2(alg_filename) res = parser.invoke_info(alg_parse_tree) assert isinstance(res, FileInfo) assert res.name == "first"
def test_parser_invokeinfo_structure_error(): '''Test that the invoke_info method in the Parser class provides None as the datatype to the associated Arg class if an argument to an invoke is a structure that comes from a use statement (as we then do not know its datatype), but that the datatype for a structure is found if the structure is declared within the code. ''' alg_filename = os.path.join( LFRIC_TEST_PATH, "26.5_mixed_precision_structure.f90") parser = Parser(kernel_path=LFRIC_TEST_PATH) alg_parse_tree = parse_fp2(alg_filename) info = parser.invoke_info(alg_parse_tree) args = info.calls[0].kcalls[0].args assert args[0]._datatype is None assert args[1]._datatype == ("r_solver_field_type", None) assert args[2]._datatype == ("real", "r_def") assert args[3]._datatype == ("quadrature_xyoz_type", None)
def test_parser_invokeinfo_use_error(): '''Test that the invoke_info method in the Parser class provides None as the datatype to the associated Arg class if an argument to an invoke comes from a use statement (as we then do not know its datatype). Also check for the same behaviour if the variable is not declared at all i.e. is included via a wildcard use statement, or implicit none is not specified. ''' alg_filename = os.path.join( LFRIC_TEST_PATH, "26.4_mixed_precision_use.f90") parser = Parser(kernel_path=LFRIC_TEST_PATH) alg_parse_tree = parse_fp2(alg_filename) info = parser.invoke_info(alg_parse_tree) args = info.calls[0].kcalls[0].args assert args[0]._datatype is None assert args[1]._datatype == ("r_solver_field_type", None) assert args[2]._datatype is None assert args[3]._datatype == ("quadrature_xyoz_type", None)
def test_parser_invokeinfo_datatypes_mixed(): '''Test that the invoke_info method in the Parser class captures the required datatype information with mixed-precision fields, operators and scalars e.g. defined as r_solver_field_type, r_solver_operator_type and r_solver respectively. Also tests that the datatype information is always lower case irrespective of the case of the declaration and argument. This covers the situation where the variable is declared and used with different case e.g. real a\n call invoke(kern(A)) ''' alg_filename = os.path.join( LFRIC_TEST_PATH, "26.1_mixed_precision.f90") parser = Parser(kernel_path=LFRIC_TEST_PATH) alg_parse_tree = parse_fp2(alg_filename) info = parser.invoke_info(alg_parse_tree) args = info.calls[0].kcalls[0].args assert args[0]._datatype == ("r_solver_operator_type", None) assert args[1]._datatype == ("r_solver_field_type", None) assert args[2]._datatype == ("real", "r_solver") assert args[3]._datatype == ("quadrature_xyoz_type", None)