#!/usr/bin/env python # # Public Domain 2014-2016 MongoDB, Inc. # Public Domain 2008-2014 WiredTiger, Inc. # # This is free and unencumbered software released into the public domain. # # Anyone is free to copy, modify, publish, use, compile, sell, or # distribute this software, either in source code form or as a compiled # binary, for any purpose, commercial or non-commercial, and by any # means. # # In jurisdictions that recognize copyright laws, the author or authors # of this software dedicate any and all copyright interest in the # software to the public domain. We make this dedication for the benefit # of the public at large and to the detriment of our heirs and # successors. We intend this dedication to be an overt act of # relinquishment in perpetuity of all present and future rights to this # software under copyright law. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. # This Python script is run as part of generating the documentation for the # WiredTiger reference manual. It changes comments to Javadoc style # (i.e., from "/*!" to "/**"), because the latter are configured to not # search for brief descriptions at the beginning of pages. # It also processes any page marked with @m_page specially to create # multiple per-language versions of the page. import re, sys progname = 'doxfilter.py' linenum = 0 filename = '' def err(arg): sys.stderr.write(filename + ':' + str(linenum) + ': ERROR: ' + arg + '\n') sys.exit(1) def java_post_substitutions(source): result = source for datatype in [['WT_CONNECTION', 'Connection'], ['WT_CURSOR', 'Cursor'], ['WT_SESSION', 'Session'], ['WT_ASYNC_OPTYPE', 'AsyncOpType'], ['WT_ASYNC_OP', 'AsyncOp']]: fromdt = datatype[0] todt = datatype[1] # e.g. replace("WT_CONNECTION::", "Connection."). # replace("WT_CONNECTION", "Connection") # replace("::WT_CONNECTION", "Connection") # etc. result = result.replace(fromdt + '::', todt + '.') result = re.sub(r':*' + fromdt, todt, result) # We fix back any 'ref' entries, since we don't have # many java versions of these refered-to pages. # # Ideally, we'd have a way ('@m_ref'?) to indicate which # ones could refer to a Java version. result = result.replace('ref ' + todt + '.', 'ref ' + fromdt + '::') result = result.replace('::wiredtiger_open', '\c wiredtiger.open') return result def process_lang(lang, lines): result = '' lang_ext = '.' + lang class_suffix = None if lang == 'c': lang_suffix = "" lang_desc="" elif lang == 'java': lang_suffix = "_lang_java" lang_desc=" in Java" else: err('@m_page contains illegal lang: ' + lang) condstack = [True] linenum = 0 mif_pat = re.compile(r'^\s*@m_if{([^,}]*)}') melse_pat = re.compile(r'^\s*@m_else\s*$') mendif_pat = re.compile(r'^\s*@m_endif\s*$') mpage_pat = re.compile(r'@m_page{{([^}]*)},([^,}]*),([^}]*)}') mpage_rep = r'@page \2' + lang_suffix + r' \3 ' + lang_desc ref_pat = re.compile(r'@ref\s+(\w*)') ref_rep = r'@ref \1' + lang_suffix snip_pat = re.compile(r'@snippet ex_([^.]*)[.]c\s+(.*)') snip_rep = r'@snippet ex_\1' + lang_ext + r' \2' section_pat = re.compile(r'(^@\w*section)\s+(\w*)') section_rep = r'\1 \2' + lang_suffix subpage_pat = re.compile(r'@subpage\s+(\w*)') subpage_rep = r'@subpage \1' + lang_suffix exref_pat = re.compile(r'@ex_ref{ex_([^.]*)[.]c}') # Add some ability to have non-language references x_ref_pat = re.compile(r'@x_ref\s+(\w*)') x_ref_rep = r'@ref \1' if lang == 'c': exref_rep = r'@ex_ref{ex_\1' + lang_ext + '}' else: # Though we have java examples, we don't have references # to them working yet, so strip the @ex_ref. exref_rep = r'ex_\1' + lang_ext # Any remaining @m_foo{...} aliases are # diverted to @c_foo{...} or @java_foo{...} mgeneric_pat = re.compile(r'@m_([^ }]*)') mgeneric_rep = r'@' + lang + r'_\1' for line in lines: linenum += 1 if lang != 'c': line = re.sub(exref_pat, exref_rep, line) line = re.sub(ref_pat, ref_rep, line) line = re.sub(section_pat, section_rep, line) line = re.sub(snip_pat, snip_rep, line) line = re.sub(mpage_pat, mpage_rep, line) line = re.sub(subpage_pat, subpage_rep, line) line = re.sub(x_ref_pat, x_ref_rep, line) if '@m_if' in line: m = re.search(mif_pat, line) if not m: err('@m_if incorrect syntax') iflang = m.groups()[0] if iflang != 'java' and iflang != 'c': err('@m_if unknown language') condstack.append(iflang == lang) elif '@m_else' in line: if not re.search(melse_pat, line): err('@m_else has extraneous stuff') if len(condstack) <= 1: err('@m_else missing if') condstack[-1] = not condstack[-1] elif '@m_endif' in line: if not re.search(mendif_pat, line): err('@m_endif has extraneous stuff') if len(condstack) <= 1: err('@m_endif missing if') condstack.pop() else: if condstack[-1]: # Do generic @m_... macros last line = re.sub(mgeneric_pat, mgeneric_rep, line) result += line + '\n' if lang == 'java': result = java_post_substitutions(result) if len(condstack) != 1: err('non matching @m_if/@m_endif') return result # Collect all lines between @m_page and the comment end to # be processed multiple times, once for each language. def process_multilang(source): result = '' in_mpage = False mpage_content = [] mpage_pat = re.compile(r'@m_page{{([^}]*)},([^,}]*),([^}]*)}') mpage_end_pat = re.compile(r'\*/') for line in source.split('\n'): m = re.search(mpage_pat, line) if line.count('@m_page') > 0 and not m: err('@m_page incorrect syntax') if m: if in_mpage: err('multiple @m_page without end of comment') else: in_mpage = True langs = m.groups()[0].split(',') if in_mpage: mpage_content.append(line) else: result += line + '\n' if re.search(mpage_end_pat, line): if in_mpage: in_mpage = False for lang in langs: result += process_lang(lang, mpage_content) mpage_content = [] return result def process(source): source = source.replace(r'/*!', r'/**') if '@m_' in source: source = process_multilang(source) return source if __name__ == '__main__': for f in sys.argv[1:]: filename = f with open(f, 'r') as infile: sys.stdout.write(process(infile.read())) sys.exit(0)