Skip to content

Commit 44bc44a

Browse files
committed
add python scripts
1 parent 1a53c3f commit 44bc44a

File tree

6 files changed

+303
-5
lines changed

6 files changed

+303
-5
lines changed

README.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Utilities
22

3-
This is a collection of useful functions for the Arduino Framerwork. All the functions are quite simple, you can see them in the [header](Utilities.h).
3+
This is a collection of useful functions for the Arduino Framework. All the functions are quite simple, you can see them in the [header](Utilities.h).
44

55
Upload the [example](/examples/basic/basic.ino) to see how to use them 🔥
66

@@ -39,4 +39,14 @@ Get the exact size of any array
3939
Convert Celsius to Fahrenheit
4040

4141
#### TO_CELSIUS
42-
Convert Fahrenheit to Celsius
42+
Convert Fahrenheit to Celsius
43+
44+
# Extras
45+
46+
in the [extras](/extras) folder there are two useful python scripts:
47+
48+
### keywords
49+
Automatically generate the keywords.txt from a header file or make an existing keywords.txt with a uniform indentation
50+
51+
### wrap
52+
Wrap a word around any symbol for documentation of source code

Utilities.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,4 +140,4 @@ char *splitString(char str[], uint8_t index, char delimiter[] = " ")
140140
return NULL;
141141
}
142142

143-
#endif // Utilities_h
143+
#endif // Utilities_h

examples/basic/basic.ino

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,4 @@ void loop()
7474
{
7575
Serial.println("Also from task2!");
7676
}
77-
}
77+
}

extras/keywords.py

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
#! python3
2+
"""
3+
Automatically generate the keywords.txt from a header file or make an existing
4+
keywords.txt with a uniform indentation
5+
6+
@author: aster94
7+
"""
8+
# Settings
9+
newline = '\n' # compatibility between unix, windows, mac?
10+
distance = 8
11+
12+
template_KEYWORD1 = """\
13+
#######################################
14+
# Syntax Coloring Map
15+
#######################################
16+
17+
#######################################
18+
# Datatypes (KEYWORD1)
19+
#######################################
20+
"""
21+
22+
template_KEYWORD2 = """\
23+
#######################################
24+
# Methods and Functions (KEYWORD2)
25+
#######################################
26+
"""
27+
28+
template_LITERAL1 = """\
29+
######################################
30+
# Constants (LITERAL1)
31+
######################################
32+
"""
33+
34+
# Modules
35+
import click, re, os, shutil, sys
36+
37+
############################################################
38+
###### Utilities #######
39+
############################################################
40+
def file_exist(file_path):
41+
return os.path.isfile(file_path)
42+
43+
def file_read(file_path):
44+
with open(file_path, 'r') as file:
45+
return file.read()
46+
47+
def file_write(file_path, content):
48+
with open(file_path, 'w') as file:
49+
file.write(content)
50+
51+
def file_copy(source, destination):
52+
shutil.copy(source, destination)
53+
54+
def file_create(file_path):
55+
with open(file_path, 'w+'): pass
56+
57+
58+
############################################################
59+
###### Functions #######
60+
############################################################
61+
def read_header(file_path, verbose, force):
62+
63+
block_comment = False
64+
polished = ""
65+
key = {'KEYWORD1': list(), 'KEYWORD2': list(), 'LITERAL1': list()}
66+
67+
raw = file_read (file_path)
68+
69+
# Integrity checks
70+
multiline_comment_start = raw.count('/*')
71+
multiline_comment_end = raw.count('*/')
72+
if (multiline_comment_start == 0 and multiline_comment_end == 0):
73+
pass
74+
elif (multiline_comment_start - multiline_comment_end != 0):
75+
print('check source file: multiline comment block problem')
76+
if not force: raise NameError('MultilineBlockUndefinied')
77+
78+
# Clean
79+
for line in raw.splitlines():
80+
line = line.strip()
81+
if (block_comment):
82+
# We are inside a multiline block comment
83+
if (line.find('*/') == -1):
84+
continue
85+
else:
86+
block_comment = False
87+
line = line[line.find('*/')+2:]
88+
89+
if (line.find('/*') != -1):
90+
# It is the start of a block comment
91+
block_comment = True
92+
line = line[:line.find('/*')]
93+
94+
if (line.find('//') != -1):
95+
line = line[:line.find('//')]
96+
97+
if (line.find('#') != -1):
98+
continue
99+
100+
polished += line + '\n'
101+
102+
# Removing empty lines
103+
polished = re.sub(r'\n\s*\n', '\n', polished.strip())
104+
105+
if ('public:' in polished):
106+
start = polished.find('public:') + 8
107+
else:
108+
start = polished.find('{') + 2
109+
110+
if ('private:' in polished):
111+
end = polished.find('private:')
112+
elif ('protected:' in polished):
113+
end = polished.find('protected:')
114+
else:
115+
end = polished.find('}')
116+
117+
if (start > end):
118+
print('Unexpected error')
119+
raise NameError('Unexpected error')
120+
121+
function_block = polished[start:end].strip()
122+
123+
for line in function_block.splitlines():
124+
line = line.strip()
125+
126+
start = line.find(' ') + 1
127+
end = line.find('(')
128+
129+
# Add them to list
130+
if (start > end):
131+
to_add = line[:end]
132+
if (not to_add in key['KEYWORD1']): key['KEYWORD1'].append(to_add)
133+
else:
134+
to_add = line[start:end]
135+
if (not to_add in key['KEYWORD2']): key['KEYWORD2'].append(to_add)
136+
137+
return key
138+
139+
def read_keywords(file_path, verbose, force):
140+
141+
key = {'KEYWORD1': list(), 'KEYWORD2': list(), 'LITERAL1': list()}
142+
raw = file_read(file_path)
143+
value = ''
144+
145+
for line in raw.splitlines():
146+
line = line.strip()
147+
if ('# ' in line):
148+
value = line[line.find('(')+1:line.find(')')]
149+
elif ('#' in line or not line):
150+
continue
151+
else:
152+
try:
153+
last = re.search('\s', line).start()
154+
except:
155+
last = len(line)
156+
key[value].append(line[:last])
157+
return key
158+
159+
def write_keywords(key_dict, file_path, verbose, soft):
160+
max_len = 0
161+
162+
# Get the longest item
163+
for k in key_dict.keys():
164+
for i in key_dict[k]:
165+
i_len = len (i)
166+
if (max_len < i_len): max_len = i_len
167+
168+
# Extend it
169+
while (max_len % 4 != 0): max_len += 1
170+
max_len += distance
171+
172+
output = template_KEYWORD1
173+
for n in key_dict['KEYWORD1']:
174+
pos = max_len - len(n)
175+
output += '{}{}{}\n'.format(n, ' ' * pos, 'KEYWORD1')
176+
177+
output += '\n\n' + template_KEYWORD2
178+
for n in key_dict['KEYWORD2']:
179+
pos = max_len - len(n)
180+
output += '{}{}{}\n'.format(n, ' ' * pos, 'KEYWORD2')
181+
182+
output += '\n\n' + template_LITERAL1
183+
for n in key_dict['LITERAL1']:
184+
pos = max_len - len(n)
185+
output += '{}{}{}\n'.format(n, ' ' * pos, 'LITERAL1')
186+
187+
if verbose: print('{} printed/wrote'.format(file_path))
188+
if soft: print(output.strip())
189+
else: file_write(file_path, output.strip())
190+
191+
192+
@click.command()
193+
@click.argument('file_path')
194+
@click.option('--backup' , '-b', is_flag=True, help='Create a backup with the original keywords.txt file.')
195+
@click.option('--verbose' , '-v', is_flag=True, help='Verbose output.')
196+
@click.option('--force' , '-f', is_flag=True, help='Overwrite existing files.')
197+
@click.option('--soft' , '-s', is_flag=True, help='Don\'t write files, print them in the console.')
198+
@click.option('--preserve' , '-p', is_flag=True, default=True, help='Don\'t preserve LITERAL1 even if keywords.txt already exists.')
199+
def keywords(file_path, backup, verbose, force, soft, preserve):
200+
"""
201+
Automatically generate the keywords.txt from a header file or make an existing
202+
keywords.txt with a uniform indentation
203+
"""
204+
205+
if verbose: print('Arguments\nfile_path: {}\nbackup: {}\nverbose: {}\
206+
\nforce: {}\nsoft: {}\npreserve: {}\n'.format(\
207+
file_path, backup, verbose, force, soft, preserve))
208+
209+
if not file_exist(file_path):
210+
print('File not found')
211+
raise NameError('FileNotExists')
212+
213+
key_dict = dict()
214+
extension = os.path.splitext(file_path)[1]
215+
216+
if (extension == '.h'):
217+
key_dict = read_header(file_path, verbose, force)
218+
keywords_path = os.path.dirname(file_path)+'\keywords.txt'
219+
if preserve:
220+
if file_exist(keywords_path):
221+
if verbose: print('Copying LITERAL1 from {}'.format(keywords_path))
222+
key_dict_old = read_keywords(keywords_path, verbose, force)
223+
key_dict['LITERAL1'] = key_dict_old['LITERAL1']
224+
else:
225+
if verbose: print('Can\'t copy LITERAL1, no keywords.txt found')
226+
elif (extension == '.txt'):
227+
key_dict = read_keywords(file_path, verbose, force)
228+
keywords_path = file_path
229+
else:
230+
print('File format not accepted')
231+
raise NameError('FileNotAccepted')
232+
233+
# Check if keywords.txt exist
234+
if(file_exist(keywords_path)):
235+
if force:
236+
if verbose: print('{} already exist, overwritten'.format(keywords_path))
237+
else:
238+
if verbose: print('{} already exist, not overwritten'.format(keywords_path))
239+
sys.exit(0)
240+
if (backup):
241+
backup_path = keywords_path + '.old'
242+
if file_exist(backup_path):
243+
if force:
244+
if verbose: print('{} already exist, overwritten'.format(backup_path))
245+
file_copy(keywords_path, backup_path)
246+
else:
247+
if verbose: print('{} already exist, not overwritten'.format(backup_path))
248+
else:
249+
if verbose: print('{} didn\'t exist, created'.format(backup_path))
250+
file_copy(keywords_path, backup_path)
251+
else:
252+
if verbose: print('{} created'.format(keywords_path))
253+
file_create(keywords_path)
254+
255+
write_keywords(key_dict, keywords_path, verbose, soft)
256+
257+
258+
if __name__ == '__main__':
259+
keywords()

extras/wrap.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#! python3
2+
"""
3+
Wrap a word around any symbol for documentation of source code
4+
5+
@author: aster94
6+
"""
7+
import pyperclip, click
8+
9+
@click.command()
10+
@click.argument('word')
11+
@click.option('--wrapper' , '-w', default='/', help='Choose the symbol for the wrap')
12+
def wrap(word, wrapper):
13+
"""
14+
Wrap a word around any symbol for documentation of source code
15+
"""
16+
total_lenght = 60
17+
side_hashtag = 6
18+
spaces = int((total_lenght - side_hashtag * 2 - len(word)) / 2)
19+
if (len(word) % 2 == 1): extra = ' '
20+
else: extra = ''
21+
22+
string = '{0}\n{1}{2}{3}{2}{4}{1}\n{0}\n'.format(wrapper * total_lenght, wrapper * side_hashtag, ' ' * spaces, word, extra)
23+
24+
pyperclip.copy(string)
25+
print(string)
26+
print('Text already copied in the clipboad!')
27+
28+
if __name__ == '__main__':
29+
wrap()

library.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name=Utilities
2-
version=0.2.0
2+
version=0.3.0
33
author=Vincenzo G.
44
maintainer=Vincenzo G.
55
sentence=A library that makes using Arduino a breeze.

0 commit comments

Comments
 (0)