@@ -993,6 +993,16 @@ def _resolvenameargspattern(line):
993993
994994
995995def analyzeline (m , case , line ):
996+ """
997+ Reads each line in the input file in sequence and updates global vars.
998+
999+ Effectively reads and collects information from the input file to the
1000+ global variable groupcache, a dictionary containing info about each part
8000
div>
1001+ of the fortran module.
1002+
1003+ At the end of analyzeline, information is filtered into the correct dict
1004+ keys, but parameter values and dimensions are not yet interpreted.
1005+ """
9961006 global groupcounter , groupname , groupcache , grouplist , filepositiontext
9971007 global currentfilename , f77modulename , neededinterface , neededmodule
9981008 global expectbegin , gotnextfile , previous_context
@@ -1679,10 +1689,18 @@ def markinnerspaces(line):
16791689
16801690
16811691def updatevars (typespec , selector , attrspec , entitydecl ):
1692+ """
1693+ Returns last_name, the variable name without special chars, parenthesis
1694+ or dimension specifiers.
1695+
1696+ Alters groupcache to add the name, typespec, attrspec (and possibly value)
1697+ of current variable.
1698+ """
16821699 global groupcache , groupcounter
16831700
16841701 last_name = None
1685
8000
1702 kindselect , charselect , typename = cracktypespec (typespec , selector )
1703+ # Clean up outer commas, whitespace and undesired chars from attrspec
16861704 if attrspec :
16871705 attrspec = [x .strip () for x in markoutercomma (attrspec ).split ('@,@' )]
16881706 l = []
@@ -2396,8 +2414,6 @@ def _calc_depend_dict(vars):
23962414
23972415
23982416def get_sorted_names (vars ):
2399- """
2400- """
24012417 depend_dict = _calc_depend_dict (vars )
24022418 names = []
24032419 for name in list (depend_dict .keys ()):
@@ -2450,7 +2466,7 @@ def _selected_real_kind_func(p, r=0, radix=0):
24502466 if p < 16 :
24512467 return 8
24522468 machine = platform .machine ().lower ()
2453- if machine .startswith (('aarch64' , 'alpha' , 'arm64' , 'loongarch' , 'power' , 'ppc' , 'riscv' , 's390x' , 'sparc' )):
2469+ if machine .startswith (('aarch64' , 'alpha' , 'arm64' , 'loongarch' , 'mips' , ' power' , 'ppc' , 'riscv' , 's390x' , 'sparc' )):
24542470 if p <= 33 :
24552471 return 16
24562472 else :
@@ -2489,6 +2505,7 @@ def get_parameters(vars, global_params={}):
24892505 # TODO: test .eq., .neq., etc replacements.
24902506 ]:
24912507 v = v .replace (* repl )
2508+
24922509 v = kind_re .sub (r'kind("\1")' , v )
24932510 v = selected_int_kind_re .sub (r'selected_int_kind(\1)' , v )
24942511
@@ -2497,14 +2514,17 @@ def get_parameters(vars, global_params={}):
24972514 # then we may easily remove those specifiers.
24982515 # However, it may be that the user uses other specifiers...(!)
24992516 is_replaced = False
2517+
25002518 if 'kindselector' in vars [n ]:
2519+ # Remove kind specifier (including those defined
2520+ # by parameters)
25012521 if 'kind' in vars [n ]['kindselector' ]:
25022522 orig_v_len = len (v )
25032523 v = v .replace ('_' + vars [n ]['kindselector' ]['kind' ], '' )
25042524 # Again, this will be true if even a single specifier
25052525 # has been replaced, see comment above.
25062526 is_replaced = len (v ) < orig_v_len
2507-
2527+
25082528 if not is_replaced :
25092529 if not selected_kind_re .match (v ):
25102530 v_ = v .split ('_' )
@@ -2531,27 +2551,30 @@ def get_parameters(vars, global_params={}):
25312551 outmess (f'get_parameters[TODO]: '
25322552 f'implement evaluation of complex expression { v } \n ' )
25332553
2554+ dimspec = ([s .lstrip ('dimension' ).strip ()
2555+ for s in vars [n ]['attrspec' ]
2556+ if s .startswith ('dimension' )] or [None ])[0 ]
2557+
25342558 # Handle _dp for gh-6624
25352559 # Also fixes gh-20460
25362560 if real16pattern .search (v ):
25372561 v = 8
25382562 elif real8pattern .search (v ):
25392563 v = 4
25402564 try :
2541- params [n ] = eval (v , g_params , params )
2542-
2565+ params [n ] = param_eval (v , g_params , params , dimspec = dimspec )
25432566 except Exception as msg :
25442567 params [n ] = v
2545- outmess ('get_parameters: got "%s" on %s\n ' % (msg , repr (v )))
2568+ outmess (f'get_parameters: got "{ msg } " on { n !r} \n ' )
2569+
25462570 if isstring (vars [n ]) and isinstance (params [n ], int ):
25472571 params [n ] = chr (params [n ])
25482572 nl = n .lower ()
25492573 if nl != n :
25502574 params [nl ] = params [n ]
25512575 else :
25522576 print (vars [n ])
2553- outmess (
2554- 'get_parameters:parameter %s does not have value?!\n ' % (repr (n )))
2577+ outmess (f'get_parameters:parameter { n !r} does not have value?!\n ' )
25552578 return params
25562579
25572580
@@ -2560,6 +2583,7 @@ def _eval_length(length, params):
25602583 return '(*)'
25612584 return _eval_scalar (length , params )
25622585
2586+
25632587_is_kind_number = re .compile (r'\d+_' ).match
25642588
25652589
@@ -2580,6 +2604,10 @@ def _eval_scalar(value, params):
25802604
25812605
25822606def analyzevars (block ):
2607+ """
2608+ Sets correct dimension information for each variable/parameter
2609+ """
2610+
25832611 global f90modulevars
25842612
25852613 setmesstext (block )
@@ -2608,7 +2636,8 @@ def analyzevars(block):
26082636 svars .append (n )
26092637
26102638 params = get_parameters (vars , get_useparameters (block ))
2611-
2639+ # At this point, params are read and interpreted, but
2640+ # the params used to define vars are not yet parsed
26122641 dep_matches = {}
26132642 name_match = re .compile (r'[A-Za-z][\w$]*' ).match
26142643 for v in list (vars .keys ()):
@@ -2707,27 +2736,30 @@ def analyzevars(block):
27072736 check = None
27082737 if dim and 'dimension' not in vars [n ]:
27092738 vars [n ]['dimension' ] = []
2710- for d in rmbadname ([x .strip () for x in markoutercomma (dim ).split ('@,@' )]):
2711- star = ':' if d == ':' else '*'
2739+ for d in rmbadname (
2740+ [x .strip () for x in markoutercomma (dim ).split ('@,@' )]
2741+ ):
2742+ # d is the expression inside the dimension declaration
27122743 # Evaluate `d` with respect to params
2713- if d in params :
2714- d = str (params [d ])
2715- for p in params :
2716- re_1 = re .compile (r'(?P<before>.*?)\b' + p + r'\b(?P<after>.*)' , re .I )
2717- m = re_1 .match (d )
2718- while m :
2719- d = m .group ('before' ) + \
2720- str (params [p ]) + m .group ('after' )
2721- m = re_1 .match (d )
2722-
2723- if d == star :
2724- dl = [star ]
2744+ try :
2745+ # the dimension for this variable depends on a
2746+ # previously defined parameter
2747+ d = param_parse (d , params )
2748+ except (ValueError , IndexError , KeyError ):
2749+ outmess (
2750+ ('analyzevars: could not parse dimension for '
2751+ f'variable { d !r} \n ' )
2752+ )
2753+
2754+ dim_char = ':' if d == ':' else '*'
2755+ if d == dim_char :
2756+ dl = [dim_char ]
27252757 else :
27262758 dl = markoutercomma (d , ':' ).split ('@:@' )
27272759 if len (dl ) == 2 and '*' in dl : # e.g. dimension(5:*)
27282760 dl = ['*' ]
27292761 d = '*'
2730- if len (dl ) == 1 and dl [0 ] != star :
2762+ if len (dl ) == 1 and dl [0 ] != dim_char :
27312763 dl = ['1' , dl [0 ]]
27322764 if len (dl ) == 2 :
27332765
67DE
d1 , d2 = map (symbolic .Expr .parse , dl )
@@ -2961,9 +2993,152 @@ def compute_deps(v, deps):
29612993 del vars [n ]
29622994 return vars
29632995
2996+
29642997analyzeargs_re_1 = re .compile (r'\A[a-z]+[\w$]*\Z' , re .I )
29652998
29662999
3000+ def param_eval (v , g_params , params , dimspec = None ):
3001+ """
3002+ Creates a dictionary of indices and values for each parameter in a
3003+ parameter array to be evaluated later.
3004+
3005+ WARNING: It is not possible to initialize multidimensional array
3006+ parameters e.g. dimension(-3:1, 4, 3:5) at this point. This is because in
3007+ Fortran initialization through array constructor requires the RESHAPE
3008+ intrinsic function. Since the right-hand side of the parameter declaration
65CE
td>3009+ is not executed in f2py, but rather at the compiled c/fortran extension,
3010+ later, it is not possible to execute a reshape of a parameter array.
3011+ One issue remains: if the user wants to access the array parameter from
3012+ python, we should either
3013+ 1) allow them to access the parameter array using python standard indexing
3014+ (which is often incompatible with the original fortran indexing)
3015+ 2) allow the parameter array to be accessed in python as a dictionary with
3016+ fortran indices as keys
3017+ We are choosing 2 for now.
3018+ """
3019+ if dimspec is None :
3020+ try :
3021+ p = eval (v , g_params , params )
3022+ except Exception as msg :
3023+ p = v
3024+ outmess (f'param_eval: got "{ msg } " on { v !r} \n ' )
3025+ return p
3026+
3027+ # This is an array parameter.
3028+ # First, we parse the dimension information
3029+ if len (dimspec ) < 2 or dimspec [::len (dimspec )- 1 ] != "()" :
3030+ raise ValueError (f'param_eval: dimension { dimspec } can\' t be parsed' )
3031+ dimrange = dimspec [1 :- 1 ].split (',' )
3032+ if len (dimrange ) == 1 :
3033+ # e.g. dimension(2) or dimension(-1:1)
3034+ dimrange = dimrange [0 ].split (':' )
3035+ # now, dimrange is a list of 1 or 2 elements
3036+ if len (dimrange ) == 1 :
3037+ bound = param_parse (dimrange [0 ], params )
3038+ dimrange = range (1 , int (bound )+ 1 )
3039+ else :
3040+ lbound = param_parse (dimrange [0 ], params )
3041+ ubound = param_parse (dimrange [1 ], params )
3042+ dimrange = range (int (lbound ), int (ubound )+ 1 )
3043+ else :
3044+ raise ValueError (f'param_eval: multidimensional array parameters '
3045+ '{dimspec} not supported' )
3046+
3047+ # Parse parameter value
3048+ v = (v [2 :- 2 ] if v .startswith ('(/' ) else v ).split (',' )
3049+ v_eval = []
3050+ for item in v :
3051+ try :
3052+ item = eval (item , g_params , params )
3053+ except Exception as msg :
3054+ outmess (f'param_eval: got "{ msg } " on { item !r} \n ' )
3055+ v_eval .append (item )
3056+
3057+ p = dict (zip (dimrange , v_eval ))
3058+
3059+ return p
3060+
3061+
3062+ def param_parse (d , params ):
3063+ """Recursively parse array dimensions.
3064+
3065+ Parses the declaration of an array variable or parameter
3066+ `dimension` keyword, and is called recursively if the
3067+ dimension for this array is a previously defined parameter
3068+ (found in `params`).
3069+
3070+ Parameters
3071+ ----------
3072+ d : str
3073+ Fortran expression describing the dimension of an array.
3074+ params : dict
3075+ Previously parsed parameters declared in the Fortran source file.
3076+
3077+ Returns
3078+ -------
3079+ out : str
3080+ Parsed dimension expression.
3081+
3082+ Examples
3083+ --------
3084+
3085+ * If the line being analyzed is
3086+
3087+ `integer, parameter, dimension(2) :: pa = (/ 3, 5 /)`
3088+
3089+ then `d = 2` and we return immediately, with
3090+
3091+ >>> d = '2'
3092+ >>> param_parse(d, params)
3093+ 2
3094+
3095+ * If the line being analyzed is
3096+
3097+ `integer, parameter, dimension(pa) :: pb = (/1, 2, 3/)`
3098+
3099+ then `d = 'pa'`; since `pa` is a previously parsed parameter,
3100+ and `pa = 3`, we call `param_parse` recursively, to obtain
3101+
3102+ >>> d = 'pa'
3103+ >>> params = {'pa': 3}
3104+ >>> param_parse(d, params)
3105+ 3
3106+
3107+ * If the line being analyzed is
3108+
3109+ `integer, parameter, dimension(pa(1)) :: pb = (/1, 2, 3/)`
3110+
3111+ then `d = 'pa(1)'`; since `pa` is a previously parsed parameter,
3112+ and `pa(1) = 3`, we call `param_parse` recursively, to obtain
3113+
3114+ >>> d = 'pa(1)'
3115+ >>> params = dict(pa={1: 3, 2: 5})
3116+ >>> param_parse(d, params)
3117+ 3
3118+ """
3119+ if "(" in d :
3120+ # this dimension expression is an array
3121+ dname = d [:d .find ("(" )]
3122+ ddims = d [d .find ("(" )+ 1 :d .rfind (")" )]
3123+ # this dimension expression is also a parameter;
3124+ # parse it recursively
3125+ index = int (param_parse (ddims , params ))
3126+ return str (params [dname ][index ])
3127+ elif d in params :
3128+ return str (params [d ])
3129+ else :
3130+ for p in params :
3131+ re_1 = re .compile (
3132+ r'(?P<before>.*?)\b' + p + r'\b(?P<after>.*)' , re .I
3133+ )
3134+ m = re_1 .match (d )
3135+ while m :
3136+ d = m .group ('before' ) + \
3137+ str (params [p ]) + m .group ('after' )
3138+ m = re_1 .match (d )
3139+ return d
3140+
3141+
29673142def expr2name (a , block , args = []):
29683143 orig_a = a
29693144 a_is_expr = not analyzeargs_re_1 .match (a )
@@ -3216,11 +3391,6 @@ def true_intent_list(var):
32163391
32173392
32183393def vars2fortran (block , vars , args , tab = '' , as_interface = False ):
3219- """
3220- TODO:
3221- public sub
3222- ...
3223- """
32243394 setmesstext (block )
32253395 ret = ''
32263396 nout = []
0 commit comments