xref: /btstack/port/arduino/docs/update_listings.py (revision df25739fc3ea5a0a90f0f5925e6461d653697d2e)
1#!/usr/bin/env python
2import getopt
3import re
4import sys
5
6# Defines the names of example groups. Preserves the order in which the example groups will be parsed.
7list_of_groups = ["iBeacon", "ANCS", "LE Central", "LE Peripheral"]
8
9# Defines which examples belong to a group. Example is defined as [example file, example title].
10list_of_examples = {
11    "iBeacon": [["iBeacon"], ["iBeaconScanner"]],
12    "ANCS": [["ANCS"]],
13    "LE Central": [["LECentral"]],
14    "LE Peripheral": [["LEPeripheral"]],
15}
16
17lst_header = """
18"""
19
20lst_ending = """
21"""
22
23examples_header = """
24"""
25
26example_item = """
27    - [EXAMPLE_TITLE](#section:EXAMPLE_LABEL): EXAMPLE_DESC.
28"""
29example_section = """
30
31## EXAMPLE_TITLE: EXAMPLE_DESC
32<a name="section:EXAMPLE_LABEL"></a>
33
34"""
35example_subsection = """
36### SECTION_TITLE
37"""
38
39listing_start = """
40
41<a name="FILE_NAME:LISTING_LABEL"></a>
42<!-- -->
43
44"""
45
46listing_ending = """
47
48"""
49
50
51def replacePlaceholder(template, title, lable):
52    snippet = template.replace("API_TITLE", title).replace("API_LABEL", lable)
53    return snippet
54
55
56def latexText(text, ref_prefix):
57    if not text:
58        return ""
59    brief = text.replace(" in the BTstack manual","")
60
61    refs = re.match('.*(Listing\s+)(\w+).*',brief)
62    if refs:
63        brief = brief.replace(refs.group(1), "[code snippet below]")
64        brief = brief.replace(refs.group(2), "(#"+ref_prefix+":" + refs.group(2)+")")
65
66    refs = re.match('.*(Section\s+)(\w+).*',brief)
67    if refs:
68        brief = brief.replace(refs.group(1), "[here]")
69        brief = brief.replace(refs.group(2), "(#section:"+refs.group(2)+")")
70
71    return brief
72
73
74def isEmptyCommentLine(line):
75    return re.match('(\s*\*\s*)\n',line)
76
77
78def isCommentLine(line):
79    return re.match('(\s*\*\s*).*',line)
80
81
82def isEndOfComment(line):
83    return re.match('\s*\*/.*', line)
84
85
86def isNewItem(line):
87    return re.match('(\s*\*\s*\-\s*)(.*)',line)
88
89
90def isTextTag(line):
91    return re.match('.*(@text).*', line)
92
93
94def isItemizeTag(line):
95    return re.match("(\s+\*\s+)(-\s)(.*)", line)
96
97
98def processTextLine(line, ref_prefix):
99    if isTextTag(line):
100        text_line_parts = re.match(".*(@text)(.*)", line)
101        return " " + latexText(text_line_parts.group(2), ref_prefix)
102
103    if isItemizeTag(line):
104        text_line_parts = re.match("(\s*\*\s*\-\s*)(.*)", line)
105        return "\n- " + latexText(text_line_parts.group(2), ref_prefix)
106
107    text_line_parts = re.match("(\s+\*\s+)(.*)", line)
108    if text_line_parts:
109        return " " + latexText(text_line_parts.group(2), ref_prefix)
110    return ""
111
112def getExampleTitle(example_path):
113    example_title = ''
114    with open(example_path, 'rb') as fin:
115        for line in fin:
116            parts = re.match('.*(EXAMPLE_START)\((.*)\):\s*(.*)(\*/)?\n',line)
117            if parts:
118                example_title = parts.group(3).replace("_","\_")
119                continue
120    return example_title
121
122class State:
123    SearchExampleStart = 0
124    SearchListingStart = 1
125    SearchListingPause = 2
126    SearchListingResume = 3
127    SearchListingEnd = 4
128    SearchItemizeEnd = 5
129    ReachedExampleEnd = 6
130
131text_block = ''
132itemize_block = ''
133
134def writeTextBlock(aout, lstStarted):
135    global text_block
136    if text_block and not lstStarted:
137        aout.write(text_block)
138        text_block = ''
139
140def writeItemizeBlock(aout, lstStarted):
141    global itemize_block
142    if itemize_block and not lstStarted:
143        aout.write(itemize_block + "\n\n")
144        itemize_block = ''
145
146def writeListings(aout, infile_name, ref_prefix):
147    global text_block, itemize_block
148    itemText = None
149    state = State.SearchExampleStart
150    code_in_listing = ""
151    code_identation = "    "
152    skip_code = 0
153
154    with open(infile_name, 'rb') as fin:
155        for line in fin:
156            if state == State.SearchExampleStart:
157                parts = re.match('.*(EXAMPLE_START)\((.*)\):\s*(.*)(\*/)?\n',line)
158                if parts:
159                    lable = parts.group(2).replace("_","")
160                    title = latexText(parts.group(2), ref_prefix)
161                    desc  = latexText(parts.group(3), ref_prefix)
162                    aout.write(example_section.replace("EXAMPLE_TITLE", title).replace("EXAMPLE_DESC", desc).replace("EXAMPLE_LABEL", lable))
163                    state = State.SearchListingStart
164                continue
165
166            # detect @section
167            section_parts = re.match('.*(@section)\s*(.*)(:?\s*.?)\*?/?\n',line)
168            if section_parts:
169                aout.write("\n" + example_subsection.replace("SECTION_TITLE", section_parts.group(2)))
170                continue
171
172            # detect @subsection
173            subsection_parts = re.match('.*(@section)\s*(.*)(:?\s*.?)\*?/?\n',line)
174            if section_parts:
175                subsubsection = example_subsection.replace("SECTION_TITLE", section_parts.group(2)).replace('section', 'subsection')
176                aout.write("\n" + subsubsection)
177                continue
178
179            if isTextTag(line):
180                text_block = text_block + "\n\n" + processTextLine(line, ref_prefix)
181                continue
182
183            skip_code = 0
184            lstStarted = state != State.SearchListingStart
185            if text_block or itemize_block:
186                if isEndOfComment(line) or isEmptyCommentLine(line):
187                    skip_code = 1
188                    if itemize_block:
189                        # finish itemize
190                        writeItemizeBlock(aout, lstStarted)
191                    else:
192                        if isEmptyCommentLine(line):
193                            text_block = text_block + "\n\n"
194
195                        else:
196                            writeTextBlock(aout, lstStarted)
197
198
199                else:
200                    if isNewItem(line) and not itemize_block:
201                        skip_code = 1
202                        # finish text, start itemize
203                        writeTextBlock(aout, lstStarted)
204                        itemize_block = "\n " + processTextLine(line, ref_prefix)
205                        continue
206                    if itemize_block:
207                        skip_code = 1
208                        itemize_block = itemize_block + processTextLine(line, ref_prefix)
209                    elif isCommentLine(line):
210                        # append text
211                        skip_code = 1
212                        text_block = text_block + processTextLine(line, ref_prefix)
213                    else:
214                        skip_code = 0
215                #continue
216
217            if state == State.SearchListingStart:
218                parts = re.match('.*(LISTING_START)\((.*)\):\s*(.*)(\s+\*/).*',line)
219
220                if parts:
221                    lst_lable = parts.group(2).replace("_","")
222                    lst_caption = latexText(parts.group(3), ref_prefix)
223                    listing = listing_start.replace("LISTING_CAPTION", lst_caption).replace("FILE_NAME", ref_prefix).replace("LISTING_LABEL", lst_lable)
224                    if listing:
225                        aout.write("\n" + listing)
226                    state = State.SearchListingEnd
227                continue
228
229            if state == State.SearchListingEnd:
230                parts_end = re.match('.*(LISTING_END).*',line)
231                parts_pause = re.match('.*(LISTING_PAUSE).*',line)
232                end_comment_parts = re.match('.*(\*/)\s*\n', line);
233
234                if parts_end:
235                    aout.write(code_in_listing)
236                    code_in_listing = ""
237                    aout.write(listing_ending)
238                    state = State.SearchListingStart
239                    writeItemizeBlock(aout, 0)
240                    writeTextBlock(aout, 0)
241                elif parts_pause:
242                    code_in_listing = code_in_listing + code_identation + "...\n"
243                    state = State.SearchListingResume
244                elif not end_comment_parts:
245                    # aout.write(line)
246                    if not skip_code:
247                        code_in_listing = code_in_listing + code_identation + line.replace("    ", "  ")
248                continue
249
250            if state == State.SearchListingResume:
251                parts = re.match('.*(LISTING_RESUME).*',line)
252                if parts:
253                    state = State.SearchListingEnd
254                continue
255
256            parts = re.match('.*(EXAMPLE_END).*',line)
257            if parts:
258                if state != State.SearchListingStart:
259                    print "Formating error detected"
260                writeItemizeBlock(aout, 0)
261                writeTextBlock(aout, 0)
262                state = State.ReachedExampleEnd
263                print "Reached end of the example"
264
265
266# write list of examples
267def processExamples(intro_file, examples_folder, examples_ofile):
268    with open(examples_ofile, 'w') as aout:
269
270        with open(intro_file, 'rb') as fin:
271            for line in fin:
272                aout.write(line)
273
274        for group_title in list_of_groups:
275            if not list_of_examples.has_key(group_title): continue
276            examples = list_of_examples[group_title]
277            for example in examples:
278                example_path  = examples_folder + example[0] + "/" + example[0] + ".ino"
279                example_title = getExampleTitle(example_path)
280                example.append(example_title)
281
282        aout.write(examples_header)
283        aout.write("\n\n");
284
285        for group_title in list_of_groups:
286            if not list_of_examples.has_key(group_title): continue
287            examples = list_of_examples[group_title]
288
289            group_title = group_title + " example"
290            if len(examples) > 1:
291                group_title = group_title + "s"
292            group_title = group_title + ":"
293
294            aout.write("- " + group_title + "\n");
295            for example in examples:
296                ref_prefix = example[0].replace("_", "")
297                title = latexText(example[0], ref_prefix)
298                desc  = latexText(example[1], ref_prefix)
299                aout.write(example_item.replace("EXAMPLE_TITLE", title).replace("EXAMPLE_DESC", desc).replace("EXAMPLE_LABEL", ref_prefix))
300            aout.write("\n")
301        aout.write("\n")
302
303        for group_title in list_of_groups:
304            if not list_of_examples.has_key(group_title): continue
305            examples = list_of_examples[group_title]
306
307            for example in examples:
308                file_name = examples_folder + example[0] + "/" + example[0] + ".ino"
309                writeListings(aout, file_name, example[0].replace("_",""))
310
311
312def main(argv):
313    btstack_folder = "../../../"
314    docs_folder = "docs/examples/"
315    inputfolder = btstack_folder + "port/arduino/examples/"
316    outputfile = docs_folder + "generated.md"
317    intro_file = "docs/examples/intro.md"
318
319    try:
320        opts, args = getopt.getopt(argv,"hiso:",["ifolder=","ofile="])
321    except getopt.GetoptError:
322        print 'update_listings.py [-i <inputfolder>] [-o <outputfile>]'
323        sys.exit(2)
324    for opt, arg in opts:
325        if opt == '-h':
326            print 'update_listings.py [-i <inputfolder>] [-s] [-o <outputfile>]'
327            sys.exit()
328        elif opt in ("-i", "--ifolder"):
329            inputfolder = arg
330        elif opt in ("-o", "--ofile"):
331            outputfile = arg
332    print 'Input folder is ', inputfolder
333    print 'Output file is ', outputfile
334    processExamples(intro_file, inputfolder, outputfile)
335
336if __name__ == "__main__":
337   main(sys.argv[1:])
338