diff --git a/buildroot/share/scripts/pinsformat.py b/buildroot/share/scripts/pinsformat.py new file mode 100755 index 0000000000..3a376ecc7e --- /dev/null +++ b/buildroot/share/scripts/pinsformat.py @@ -0,0 +1,265 @@ +#!/usr/bin/env python3 + +# +# Formatter script for pins_MYPINS.h files +# +# Usage: pinsformat.py [infile] [outfile] +# +# With no parameters convert STDIN to STDOUT +# + +import sys, re + +do_log = False +def logmsg(msg, line): + if do_log: print(msg, line) + +col_comment = 50 + +# String lpad / rpad +def lpad(astr, fill, c=None): + if not fill: return astr + if c == None: c = ' ' + need = fill - len(astr) + return astr if need <= 0 else (need * c) + astr + +def rpad(astr, fill, c=None): + if not fill: return astr + if c == None: c = ' ' + need = fill - len(astr) + return astr if need <= 0 else astr + (need * c) + +# Pin patterns +mpatt = [ r'-?\d{1,3}', r'P[A-I]\d+', r'P\d_\d+', r'Pin[A-Z]\d\b' ] +mstr = '|'.join(mpatt) +mexpr = [ re.compile(f'^{m}$') for m in mpatt ] + +# Corrsponding padding for each pattern +ppad = [ 3, 4, 5, 5 ] + +# Match a define line +definePatt = re.compile(rf'^\s*(//)?#define\s+[A-Z_][A-Z0-9_]+\s+({mstr})\s*(//.*)?$') + +def format_pins(argv): + src_file = 'stdin' + dst_file = None + + scnt = 0 + for arg in argv: + if arg == '-v': + do_log = True + elif scnt == 0: + # Get a source file if specified. Default destination is the same file + src_file = dst_file = arg + scnt += 1 + elif scnt == 1: + # Get destination file if specified + dst_file = arg + scnt += 1 + + # No text to process yet + file_text = '' + + if src_file == 'stdin': + # If no source file specified read from STDIN + file_text = sys.stdin.read() + else: + # Open the file src_file + with open(src_file, 'r') as rf: + file_text = rf.read() + + if len(file_text) == 0: + print('No text to process') + return + + # Read from file or STDIN until it terminates + filtered = process_text(file_text) + if dst_file: + with open(dst_file, 'w') as wf: + wf.write(filtered) + else: + print(filtered) + +# Find the pin pattern so non-pin defines can be skipped +def get_pin_pattern(txt): + r = '' + m = 0 + match_count = [ 0, 0, 0, 0 ] + + # Find the most common matching pattern + match_threshold = 5 + for line in txt.split('\n'): + r = definePatt.match(line) + if r == None: continue + ind = -1 + for p in mexpr: + ind += 1 + if not p.match(r[2]): continue + match_count[ind] += 1 + if match_count[ind] >= match_threshold: + return { 'match': mpatt[ind], 'pad':ppad[ind] } + return None + +def process_text(txt): + if len(txt) == 0: return '(no text)' + patt = get_pin_pattern(txt) + if patt == None: return txt + + pindefPatt = re.compile(r'^(\s*(//)?#define)\s+([A-Z_][A-Z0-9_]+)\s+(' + patt['match'] + r')\s*(//.*)?$') + noPinPatt = re.compile(r'^(\s*(//)?#define)\s+([A-Z_][A-Z0-9_]+)\s+(-1)\s*(//.*)?$') + skipPatt1 = re.compile(r'^(\s*(//)?#define)\s+(AT90USB|USBCON|(BOARD|DAC|FLASH|HAS|IS|USE)_.+|.+_(ADDRESS|AVAILABLE|BAUDRATE|CLOCK|CONNECTION|DEFAULT|FREQ|ITEM|MODULE|NAME|ONLY|PERIOD|RANGE|RATE|SERIAL|SIZE|SPI|STATE|STEP|TIMER))\s+(.+)\s*(//.*)?$') + skipPatt2 = re.compile(r'^(\s*(//)?#define)\s+([A-Z_][A-Z0-9_]+)\s+(0x[0-9A-Fa-f]+|\d+|.+[a-z].+)\s*(//.*)?$') + aliasPatt = re.compile(r'^(\s*(//)?#define)\s+([A-Z_][A-Z0-9_]+)\s+([A-Z_][A-Z0-9_()]+)\s*(//.*)?$') + switchPatt = re.compile(r'^(\s*(//)?#define)\s+([A-Z_][A-Z0-9_]+)\s*(//.*)?$') + undefPatt = re.compile(r'^(\s*(//)?#undef)\s+([A-Z_][A-Z0-9_]+)\s*(//.*)?$') + defPatt = re.compile(r'^(\s*(//)?#define)\s+([A-Z_][A-Z0-9_]+)\s+([-_\w]+)\s*(//.*)?$') + condPatt = re.compile(r'^(\s*(//)?#(if|ifn?def|else|elif)(\s+\S+)*)\s+(//.*)$') + commPatt = re.compile(r'^\s{20,}(//.*)?$') + + col_value_lj = col_comment - patt['pad'] - 2 + col_value_rj = col_comment - 3 + + # + # #define SKIP_ME + # + def trySkip1(d): + if skipPatt1.match(d['line']) == None: return False + logmsg("skip:", d['line']) + return True + + # + # #define MY_PIN [pin] + # + def tryPindef(d): + line = d['line'] + r = pindefPatt.match(line) + if r == None: return False + logmsg("pin:", line) + pinnum = r[4] if r[4][0] == 'P' else lpad(r[4], patt['pad']) + line = r[1] + ' ' + r[3] + line = rpad(line, col_value_lj) + pinnum + if r[5]: line = rpad(line, col_comment) + r[5] + d['line'] = line + return True + + # + # #define MY_PIN -1 + # + def tryNoPin(d): + line = d['line'] + r = noPinPatt.match(line) + if r == None: return False + logmsg("pin -1:", line) + line = r[1] + ' ' + r[3] + line = rpad(line, col_value_lj) + '-1' + if r[5]: line = rpad(line, col_comment) + r[5] + d['line'] = line + return True + + # + # #define SKIP_ME_TOO + # + def trySkip2(d): + if skipPatt2.match( d['line']) == None: return False + logmsg("skip:", d['line']) + return True + + # + # #define ALIAS OTHER + # + def tryAlias(d): + line = d['line'] + r = aliasPatt.match(line) + if r == None: return False + logmsg("alias:", line) + line = f'{r[1]} {r[3]}' + line += lpad(r[4], col_value_rj + 1 - len(line)) + if r[5]: line = rpad(line, col_comment) + r[5] + d['line'] = line + return True + + # + # #define SWITCH + # + def trySwitch(d): + line = d['line'] + r = switchPatt.match(line) + if r == None: return False + logmsg("switch:", line) + line = f'{r[1]} {r[3]}' + if r[4]: line = rpad(line, col_comment) + r[4] + d['line'] = line + d['check_comment_next'] = True + return True + + # + # #define ... + # + def tryDef(d): + line = d['line'] + r = defPatt.match(line) + if r == None: return False + logmsg("def:", line) + line = f'{r[1]} {r[3]} ' + line += lpad(r[4], col_value_rj + 1 - len(line)) + if r[5]: line = rpad(line, col_comment - 1) + ' ' + r[5] + d['line'] = line + return True + + # + # #undef ... + # + def tryUndef(d): + line = d['line'] + r = undefPatt.match(line) + if r == None: return False + logmsg("undef:", line) + line = f'{r[1]} {r[3]}' + if r[4]: line = rpad(line, col_comment) + r[4] + d['line'] = line + return True + + # + # #if ... + # + def tryCond(d): + line = d['line'] + r = condPatt.match(line) + if r == None: return False + logmsg("cond:", line) + line = rpad(r[1], col_comment) + r[5] + d['line'] = line + d['check_comment_next'] = True + return True + + out = '' + wDict = { 'check_comment_next': False } + + # Transform each line and add it to the output + for line in txt.split('\n'): + wDict['line'] = line + if wDict['check_comment_next']: + r = commPatt.match(line) + wDict['check_comment_next'] = (r != None) + + if wDict['check_comment_next']: + # Comments in column 45 + line = rpad('', col_comment) + r[1] + + elif trySkip1(wDict): pass #define SKIP_ME + elif tryPindef(wDict): pass #define MY_PIN [pin] + elif tryNoPin(wDict): pass #define MY_PIN -1 + elif trySkip2(wDict): pass #define SKIP_ME_TOO + elif tryAlias(wDict): pass #define ALIAS OTHER + elif trySwitch(wDict): pass #define SWITCH + elif tryDef(wDict): pass #define ... + elif tryUndef(wDict): pass #undef ... + elif tryCond(wDict): pass #if ... + + out += wDict['line'] + '\n' + + return re.sub('\n\n$', '\n', re.sub(r'\n\n+', '\n\n', out)) + +# Python standard startup for command line with arguments +if __name__ == '__main__': + format_pins(sys.argv[1:])