Skip to content

Commit 1d61571

Browse files
committed
πŸ§‘β€πŸ’» Python version of pins formatting script
1 parent 99c5702 commit 1d61571

File tree

1 file changed

+265
-0
lines changed

1 file changed

+265
-0
lines changed
+265
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
#!/usr/bin/env python3
2+
3+
#
4+
# Formatter script for pins_MYPINS.h files
5+
#
6+
# Usage: pinsformat.py [infile] [outfile]
7+
#
8+
# With no parameters convert STDIN to STDOUT
9+
#
10+
11+
import sys, re
12+
13+
do_log = False
14+
def logmsg(msg, line):
15+
if do_log: print(msg, line)
16+
17+
col_comment = 50
18+
19+
# String lpad / rpad
20+
def lpad(astr, fill, c=None):
21+
if not fill: return astr
22+
if c == None: c = ' '
23+
need = fill - len(astr)
24+
return astr if need <= 0 else (need * c) + astr
25+
26+
def rpad(astr, fill, c=None):
27+
if not fill: return astr
28+
if c == None: c = ' '
29+
need = fill - len(astr)
30+
return astr if need <= 0 else astr + (need * c)
31+
32+
# Pin patterns
33+
mpatt = [ r'-?\d{1,3}', r'P[A-I]\d+', r'P\d_\d+', r'Pin[A-Z]\d\b' ]
34+
mstr = '|'.join(mpatt)
35+
mexpr = [ re.compile(f'^{m}$') for m in mpatt ]
36+
37+
# Corrsponding padding for each pattern
38+
ppad = [ 3, 4, 5, 5 ]
39+
40+
# Match a define line
41+
definePatt = re.compile(rf'^\s*(//)?#define\s+[A-Z_][A-Z0-9_]+\s+({mstr})\s*(//.*)?$')
42+
43+
def format_pins(argv):
44+
src_file = 'stdin'
45+
dst_file = None
46+
47+
scnt = 0
48+
for arg in argv:
49+
if arg == '-v':
50+
do_log = True
51+
elif scnt == 0:
52+
# Get a source file if specified. Default destination is the same file
53+
src_file = dst_file = arg
54+
scnt += 1
55+
elif scnt == 1:
56+
# Get destination file if specified
57+
dst_file = arg
58+
scnt += 1
59+
60+
# No text to process yet
61+
file_text = ''
62+
63+
if src_file == 'stdin':
64+
# If no source file specified read from STDIN
65+
file_text = sys.stdin.read()
66+
else:
67+
# Open the file src_file
68+
with open(src_file, 'r') as rf:
69+
file_text = rf.read()
70+
71+
if len(file_text) == 0:
72+
print('No text to process')
73+
return
74+
75+
# Read from file or STDIN until it terminates
76+
filtered = process_text(file_text)
77+
if dst_file:
78+
with open(dst_file, 'w') as wf:
79+
wf.write(filtered)
80+
else:
81+
print(filtered)
82+
83+
# Find the pin pattern so non-pin defines can be skipped
84+
def get_pin_pattern(txt):
85+
r = ''
86+
m = 0
87+
match_count = [ 0, 0, 0, 0 ]
88+
89+
# Find the most common matching pattern
90+
match_threshold = 5
91+
for line in txt.split('\n'):
92+
r = definePatt.match(line)
93+
if r == None: continue
94+
ind = -1
95+
for p in mexpr:
96+
ind += 1
97+
if not p.match(r[2]): continue
98+
match_count[ind] += 1
99+
if match_count[ind] >= match_threshold:
100+
return { 'match': mpatt[ind], 'pad':ppad[ind] }
101+
return None
102+
103+
def process_text(txt):
104+
if len(txt) == 0: return '(no text)'
105+
patt = get_pin_pattern(txt)
106+
if patt == None: return txt
107+
108+
pindefPatt = re.compile(r'^(\s*(//)?#define)\s+([A-Z_][A-Z0-9_]+)\s+(' + patt['match'] + r')\s*(//.*)?$')
109+
noPinPatt = re.compile(r'^(\s*(//)?#define)\s+([A-Z_][A-Z0-9_]+)\s+(-1)\s*(//.*)?$')
110+
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*(//.*)?$')
111+
skipPatt2 = re.compile(r'^(\s*(//)?#define)\s+([A-Z_][A-Z0-9_]+)\s+(0x[0-9A-Fa-f]+|\d+|.+[a-z].+)\s*(//.*)?$')
112+
aliasPatt = re.compile(r'^(\s*(//)?#define)\s+([A-Z_][A-Z0-9_]+)\s+([A-Z_][A-Z0-9_()]+)\s*(//.*)?$')
113+
switchPatt = re.compile(r'^(\s*(//)?#define)\s+([A-Z_][A-Z0-9_]+)\s*(//.*)?$')
114+
undefPatt = re.compile(r'^(\s*(//)?#undef)\s+([A-Z_][A-Z0-9_]+)\s*(//.*)?$')
115+
defPatt = re.compile(r'^(\s*(//)?#define)\s+([A-Z_][A-Z0-9_]+)\s+([-_\w]+)\s*(//.*)?$')
116+
condPatt = re.compile(r'^(\s*(//)?#(if|ifn?def|else|elif)(\s+\S+)*)\s+(//.*)$')
117+
commPatt = re.compile(r'^\s{20,}(//.*)?$')
118+
119+
col_value_lj = col_comment - patt['pad'] - 2
120+
col_value_rj = col_comment - 3
121+
122+
#
123+
# #define SKIP_ME
124+
#
125+
def trySkip1(d):
126+
if skipPatt1.match(d['line']) == None: return False
127+
logmsg("skip:", d['line'])
128+
return True
129+
130+
#
131+
# #define MY_PIN [pin]
132+
#
133+
def tryPindef(d):
134+
line = d['line']
135+
r = pindefPatt.match(line)
136+
if r == None: return False
137+
logmsg("pin:", line)
138+
pinnum = r[4] if r[4][0] == 'P' else lpad(r[4], patt['pad'])
139+
line = r[1] + ' ' + r[3]
140+
line = rpad(line, col_value_lj) + pinnum
141+
if r[5]: line = rpad(line, col_comment) + r[5]
142+
d['line'] = line
143+
return True
144+
145+
#
146+
# #define MY_PIN -1
147+
#
148+
def tryNoPin(d):
149+
line = d['line']
150+
r = noPinPatt.match(line)
151+
if r == None: return False
152+
logmsg("pin -1:", line)
153+
line = r[1] + ' ' + r[3]
154+
line = rpad(line, col_value_lj) + '-1'
155+
if r[5]: line = rpad(line, col_comment) + r[5]
156+
d['line'] = line
157+
return True
158+
159+
#
160+
# #define SKIP_ME_TOO
161+
#
162+
def trySkip2(d):
163+
if skipPatt2.match( d['line']) == None: return False
164+
logmsg("skip:", d['line'])
165+
return True
166+
167+
#
168+
# #define ALIAS OTHER
169+
#
170+
def tryAlias(d):
171+
line = d['line']
172+
r = aliasPatt.match(line)
173+
if r == None: return False
174+
logmsg("alias:", line)
175+
line = f'{r[1]} {r[3]}'
176+
line += lpad(r[4], col_value_rj + 1 - len(line))
177+
if r[5]: line = rpad(line, col_comment) + r[5]
178+
d['line'] = line
179+
return True
180+
181+
#
182+
# #define SWITCH
183+
#
184+
def trySwitch(d):
185+
line = d['line']
186+
r = switchPatt.match(line)
187+
if r == None: return False
188+
logmsg("switch:", line)
189+
line = f'{r[1]} {r[3]}'
190+
if r[4]: line = rpad(line, col_comment) + r[4]
191+
d['line'] = line
192+
d['check_comment_next'] = True
193+
return True
194+
195+
#
196+
# #define ...
197+
#
198+
def tryDef(d):
199+
line = d['line']
200+
r = defPatt.match(line)
201+
if r == None: return False
202+
logmsg("def:", line)
203+
line = f'{r[1]} {r[3]} '
204+
line += lpad(r[4], col_value_rj + 1 - len(line))
205+
if r[5]: line = rpad(line, col_comment - 1) + ' ' + r[5]
206+
d['line'] = line
207+
return True
208+
209+
#
210+
# #undef ...
211+
#
212+
def tryUndef(d):
213+
line = d['line']
214+
r = undefPatt.match(line)
215+
if r == None: return False
216+
logmsg("undef:", line)
217+
line = f'{r[1]} {r[3]}'
218+
if r[4]: line = rpad(line, col_comment) + r[4]
219+
d['line'] = line
220+
return True
221+
222+
#
223+
# #if ...
224+
#
225+
def tryCond(d):
226+
line = d['line']
227+
r = condPatt.match(line)
228+
if r == None: return False
229+
logmsg("cond:", line)
230+
line = rpad(r[1], col_comment) + r[5]
231+
d['line'] = line
232+
d['check_comment_next'] = True
233+
return True
234+
235+
out = ''
236+
wDict = { 'check_comment_next': False }
237+
238+
# Transform each line and add it to the output
239+
for line in txt.split('\n'):
240+
wDict['line'] = line
241+
if wDict['check_comment_next']:
242+
r = commPatt.match(line)
243+
wDict['check_comment_next'] = (r != None)
244+
245+
if wDict['check_comment_next']:
246+
# Comments in column 45
247+
line = rpad('', col_comment) + r[1]
248+
249+
elif trySkip1(wDict): pass #define SKIP_ME
250+
elif tryPindef(wDict): pass #define MY_PIN [pin]
251+
elif tryNoPin(wDict): pass #define MY_PIN -1
252+
elif trySkip2(wDict): pass #define SKIP_ME_TOO
253+
elif tryAlias(wDict): pass #define ALIAS OTHER
254+
elif trySwitch(wDict): pass #define SWITCH
255+
elif tryDef(wDict): pass #define ...
256+
elif tryUndef(wDict): pass #undef ...
257+
elif tryCond(wDict): pass #if ...
258+
259+
out += wDict['line'] + '\n'
260+
261+
return re.sub('\n\n$', '\n', re.sub(r'\n\n+', '\n\n', out))
262+
263+
# Python standard startup for command line with arguments
264+
if __name__ == '__main__':
265+
format_pins(sys.argv[1:])

0 commit comments

Comments
Β (0)