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