In the previous parts, I've discussed the fact that we use an input filter to translate our LPC into something a little more doxygen-friendly. This is a bit of a kludge, really--I hope you'll report in if you find improvements on this method. I should thank Sebastian Schaefer for this filter which, while entirely unrelated to LPC, gave me a starting point for understanding what my Doxygen filter needed to do, and a skeleton to build it on. The first section of this post explains what the filter needs to do for us, and the second part contains the filter code.
The primary tasks we need to undertake are:
- Communicating to Doxygen that it should think of our files as classes.
- Converting our inheritance statements into a syntax Doxygen can understand
To that end, the LPC filter checks the file to make sure it's a .c file, strips out all of our inherit statements, and then inserts a new class statement, with explicit visibility modifiers, before our first .h include.
I should note that the source file contains a few things that don't work as described; I don't have access to update the file on the server frequently, and generally only tweak it when necessary. The code, for example, claims it wants to place the class statement after our last .h include, but it will put it before the first.
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#
4
5# Copyright notice:
6# This program is free software; you can redistribute it and/or
7# modify it under the terms of the GNU General Public License
8# as published by the Free Software Foundation; either version 2
9# of the License, or (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program; if not, write to the Free Software
18# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20##
21# @brief A Doxygen filter for LPC
22# @details Based (loosely) on glslfilter by Sebastian Schäfer
23# @copyright GNU Public License.
24
25import getopt # get command-line options
26import os.path # getting extension from file
27import string # string manipulation
28import sys # output and stuff
29import re # for regular expressions
30
31# modifiers that could show up in front of an inherit statement...
32valid_modifiers = ["private", "nosave", "static", "public", "virtual", "inherit"]
33
34def writeLine(txt):
35 sys.stdout.write(txt + "\n")
36
37# strips inherits and converts them to a class statement following the last #include X.h
38def parseInherit(filename, txt):
39 temp = lambda x: x.find("inherit ") != -1
40 inherits = filter(temp, txt)
41 temp = lambda x: x.split(" ")[0] in valid_modifiers
42 inherits = filter(temp, inherits)
43 removed = len(txt)
44 [txt.remove(x) for x in inherits]
45 removed = removed - len(txt)
46 temp = lambda x: x.find("#include ") == 0 and x.rfind(".h") #find the last .h include so we can start our class after
47 includes = filter(temp, txt)
48
49 #save the line of our last include
50 try:
51 first_include = txt.index(includes[0]) + 1
52 except IndexError:
53 #set this to 1, as we knock a line off on include and that'll send us negative otherwise
54 first_include = 1
55
56 inherit_replacements = []
57
58 #strip basename from our inherit statements
59 for inherit_str in inherits:
60 pair = inherit_str.split("inherit")
61 modifiers = pair[0]
62 if not len(modifiers):
63 modifiers = "public "
64 inherit_class = pair[1].split("/")[-1].split(".")[0].rstrip("\"\';\n \r")
65 inherit_replacements.append(modifiers + inherit_class)
66
67 ## construct the class statement
68 if len(inherit_replacements):
69 replacement_text = " : "+", ".join(inherit_replacements)
70 else:
71 replacement_text = ""
72
73 ## re-pad the file to keep our line numbers the same, so we can reliably cross-refer
74 while removed > 1:
75 removed = removed - 1
76 txt.insert(first_include, "\n")
77
78 ## insert the class-style inheritance statement doxygen wants after our last header include
79 txt.insert(first_include-1, "public class "+os.path.basename(filename)+ replacement_text + " {\n")
80
81 ##check how the file ends, end the class statement appropriately
82 if txt[-1].startswith("/"):
83 txt.insert(len(txt)-1, "};\n")
84 else:
85 txt.append("\n};")
86
87 showLines(txt)
88 #fake(txt)
89
90## @returns the complete file content as an array of lines
91def readFile(filename):
92 with open(filename) as f:
93 r = f.readlines()
94 return r
95
96## dump all lines to stdout
97def showLines(r):
98 for s in r:
99 try:
100 sys.stdout.write(s)
101 except:
102 print sys.exc_info()[0]
103
104## main method - open a file and see what can be done
105def inherit_filter(filename):
106
107 try:
108 root, ext = os.path.splitext(filename)
109 txt = readFile(filename)
110 if (ext.lower() == ".c" or ext.lower() == ".c"):
111 parseInherit(root, txt)
112 else:
113 showLines(txt)
114 except IOError,e:
115 pass
116 #sys.stderr.write(e[1]+"\n")
117
118if len(sys.argv) != 2:
119 print "usage: ", sys.argv[0], " filename"
120 sys.exit(1)
121
122filename = sys.argv[1]
123inherit_filter(filename)
124
125try:
126 sys.stdout.close()
127except:
128 pass
129try:
130 sys.stderr.close()
131except:
132 pass
133
134sys.exit(0)