#!/usr/bin/env python3 import os import sys qmckl_h = sys.argv[1] collect = False process = False get_name = False block = [] res_str = '' func_name = '' arrays = {} numbers = {} qmckl_public_api = [] qmckl_errors = [] with open(qmckl_h, 'r') as f_in: for line in f_in: # get the errors but without the type cast because SWIG does not recognize it if '#define' in line and 'qmckl_exit_code' in line: qmckl_errors.append(line.strip().replace('(qmckl_exit_code)','')) continue if get_name: words = line.strip().split() if '(' in words[0]: func_name = words[0].split('(')[0] else: func_name = words[0] if 'get' in func_name or 'set' in func_name: qmckl_public_api.append(func_name) get_name = False if 'qmckl_exit_code' in line: words = line.strip().split() if len(words) > 1 and 'qmckl_exit_code' in words[0]: # this means that the function name is on the same line as `qmckl_exit_code` func_name = words[1].split('(')[0] if 'get' in func_name or 'set' in func_name: qmckl_public_api.append(func_name) elif len(words) == 1: # this means that the function name is the first element on the next line get_name = True #continue # do not `continue` here otherwise collect is not True for some functions # process functions - oneliners (for arrays) if 'size_max' in line and ';' in line: tmp_list = line.split(',') for i,s in enumerate(tmp_list): if 'size_max' in s: end_str = tmp_list[i].replace(';','').replace('\n','') pattern = f"({tmp_list[i-1]} ,{end_str}" datatype = tmp_list[i-1].replace('const','').replace('*','').split()[0] arrays[func_name] = { 'datatype' : datatype, 'pattern' : pattern } #if 'qmckl_get_jastrow_type_nucl_vector' in func_name: # print(line) # print(pattern) continue # if size_max is not provided then the function should deal with numbers or string #elif 'num' in line and 'get' in func_name: elif ';' in line and 'get' in func_name: # special case if 'size_max' in line: continue #print(line) tmp_str = line.split(',')[-1].strip() pattern = tmp_str.replace(')','').replace(';','') datatype = pattern.replace('const','').replace('*','').split()[0] numbers[func_name] = { 'datatype' : datatype, 'pattern' : pattern } continue # for multilne functions - append line by line to the list else: block.append(line) collect = True continue # if size_max is encountered within the multiline function if 'size_max' in line and collect: #if 'qmckl_get_electron_rescale_factor_en' in func_name: # print("LOL") # this will not work for 2-line functions where array argument is on the same line as # func name and size_max argument is on the next line if not 'qmckl_exit_code' in block[-1] and not '*/' in line: pattern = '(' + block[-1].strip() + line.strip().replace(';','') datatype = pattern.replace('const','').replace('*','').replace('(','').split()[0] collect = False block = [] arrays[func_name] = { 'datatype' : datatype, 'pattern' : pattern } continue #if 'num' in line and 'get' in func_name and not 'qmckl_get' in line and collect: if 'get' in func_name and not 'qmckl_get' in line and collect and ';' in line: #print(func_name) #print(line) pattern = line.replace(';','').replace(')','').strip() datatype = pattern.replace('const','').replace('*','').split()[0] collect = False block = [] numbers[func_name] = { 'datatype' : datatype, 'pattern' : pattern } continue # stop/continue multiline function analyzer if collect and ')' in line: collect = False block = [] continue else: block.append(line) continue # remove buggy qmckl_get_electron_rescale_factor_en key #arrays.pop('qmckl_get_electron_rescale_factor_en') processed = list(arrays.keys()) + list(numbers.keys()) #for pub_func in qmckl_public_api: #if pub_func not in processed and 'set' not in pub_func: #print("TODO", pub_func) #print(v['datatype']) #for k,v in numbers.items(): # print(v) with open("qmckl_include.i", 'w') as f_out: # write the list of errors as constants without the type cast for e in qmckl_errors: line = e.replace('#define', '%constant qmckl_exit_code').replace('(','=').replace(')',';') f_out.write(line + '\n') swig_type = '' for v in numbers.values(): if 'int' in v['datatype']: swig_type = 'int' elif 'float' in v['datatype'] or 'double' in v['datatype']: swig_type = 'float' elif 'char' in v['datatype'] or 'bool' in v['datatype']: #print('SWIG, skipping', v['datatype'], v['pattern']) continue else: raise TypeError(f"Unknown datatype for swig conversion: {v['datatype']}") f_out.write(f"%apply {swig_type} *OUTPUT {{ {v['pattern']} }};\n") for k,v in arrays.items(): if 'char' in v['datatype']: #print("String type", k, v) pass if len(v['pattern'].split(',')) != 2: print('Problemo', k, v) continue if 'get' in k: f_out.write(f"%apply ( {v['datatype']}* ARGOUT_ARRAY1 , int64_t DIM1 ) {{ {v['pattern']} }};\n") elif 'set' in k: f_out.write(f"%apply ( {v['datatype']}* IN_ARRAY1 , int64_t DIM1 ) {{ {v['pattern']} }};\n") #else: #print("HOW-TO ?", k)