3 # This is the API builder, it parses the C sources and build the
4 # API formal description in XML.
6 # See Copyright for the status of this software.
15 # C parser analysis code
18 "trio": "too many non standard macros",
19 "trio.c": "too many non standard macros",
20 "trionan.c": "too many non standard macros",
21 "triostr.c": "too many non standard macros",
22 "acconfig.h": "generated portability layer",
23 "config.h": "generated portability layer",
24 "libxml.h": "internal only",
25 "testOOM.c": "out of memory tester",
26 "testOOMlib.h": "out of memory tester",
27 "testOOMlib.c": "out of memory tester",
31 "WINAPI": (0, "Windows keyword"),
32 "LIBXML_DLL_IMPORT": (0, "Special macro to flag external keywords"),
33 "__declspec": (3, "Windows keyword"),
34 "ATTRIBUTE_UNUSED": (0, "macro keyword"),
35 "LIBEXSLT_PUBLIC": (0, "macro keyword"),
36 "X_IN_Y": (5, "macro function builder"),
40 raw = string.replace(raw, '&', '&')
41 raw = string.replace(raw, '<', '<')
42 raw = string.replace(raw, '>', '>')
43 raw = string.replace(raw, "'", ''')
44 raw = string.replace(raw, '"', '"')
48 def __init__(self, name, module=None, type=None, info=None, extra=None):
57 r = "%s %s:" % (self.type, self.name)
60 if self.module != None:
61 r = r + " from %s" % (self.module)
63 r = r + " " + `self.info`
64 if self.extra != None:
65 r = r + " " + `self.extra`
69 def set_module(self, module):
71 def set_type(self, type):
73 def set_info(self, info):
75 def set_extra(self, extra):
77 def set_static(self, static):
80 def update(self, module, type = None, info = None, extra=None):
81 if module != None and self.module == None:
82 self.set_module(module)
83 if type != None and self.type == None:
92 def __init__(self, name = "noname"):
104 def add(self, name, module, static, type, info=None, extra=None):
105 if name[0:2] == '__':
109 d = self.identifiers[name]
110 d.update(module, type, info, extra)
112 d = identifier(name, module, type, info, extra)
113 self.identifiers[name] = d
115 if d != None and static == 1:
118 if d != None and name != None and type != None:
119 if type == "function":
120 self.functions[name] = d
121 elif type == "functype":
122 self.functions[name] = d
123 elif type == "variable":
124 self.variables[name] = d
125 elif type == "include":
126 self.includes[name] = d
127 elif type == "struct":
128 self.structs[name] = d
131 elif type == "typedef":
132 self.typedefs[name] = d
133 elif type == "macro":
134 self.macros[name] = d
136 print "Unable to register type ", type
139 def merge(self, idx):
140 for id in idx.functions.keys():
142 # macro might be used to override functions or variables
145 if self.macros.has_key(id):
147 if self.functions.has_key(id):
148 print "function %s from %s redeclared in %s" % (
149 id, self.functions[id].module, idx.functions[id].module)
151 self.functions[id] = idx.functions[id]
152 self.identifiers[id] = idx.functions[id]
153 for id in idx.variables.keys():
155 # macro might be used to override functions or variables
158 if self.macros.has_key(id):
160 if self.variables.has_key(id):
161 print "variable %s from %s redeclared in %s" % (
162 id, self.variables[id].module, idx.variables[id].module)
164 self.variables[id] = idx.variables[id]
165 self.identifiers[id] = idx.variables[id]
166 for id in idx.structs.keys():
167 if self.structs.has_key(id):
168 print "struct %s from %s redeclared in %s" % (
169 id, self.structs[id].module, idx.structs[id].module)
171 self.structs[id] = idx.structs[id]
172 self.identifiers[id] = idx.structs[id]
173 for id in idx.typedefs.keys():
174 if self.typedefs.has_key(id):
175 print "typedef %s from %s redeclared in %s" % (
176 id, self.typedefs[id].module, idx.typedefs[id].module)
178 self.typedefs[id] = idx.typedefs[id]
179 self.identifiers[id] = idx.typedefs[id]
180 for id in idx.macros.keys():
182 # macro might be used to override functions or variables
185 if self.variables.has_key(id):
187 if self.functions.has_key(id):
189 if self.enums.has_key(id):
191 if self.macros.has_key(id):
192 print "macro %s from %s redeclared in %s" % (
193 id, self.macros[id].module, idx.macros[id].module)
195 self.macros[id] = idx.macros[id]
196 self.identifiers[id] = idx.macros[id]
197 for id in idx.enums.keys():
198 if self.enums.has_key(id):
199 print "enum %s from %s redeclared in %s" % (
200 id, self.enums[id].module, idx.enums[id].module)
202 self.enums[id] = idx.enums[id]
203 self.identifiers[id] = idx.enums[id]
205 def merge_public(self, idx):
206 for id in idx.functions.keys():
207 if self.functions.has_key(id):
208 up = idx.functions[id]
209 self.functions[id].update(None, up.type, up.info, up.extra)
211 # print "Function %s from %s is not declared in headers" % (
212 # id, idx.functions[id].module)
213 # TODO: do the same for variables.
215 def analyze_dict(self, type, dict):
218 for name in dict.keys():
224 print " %d %s , %d public" % (count, type, public)
226 print " %d public %s" % (count, type)
230 self.analyze_dict("functions", self.functions)
231 self.analyze_dict("variables", self.variables)
232 self.analyze_dict("structs", self.structs)
233 self.analyze_dict("typedefs", self.typedefs)
234 self.analyze_dict("macros", self.macros)
237 """A lexer for the C language, tokenize the input by reading and
238 analyzing it line by line"""
239 def __init__(self, input):
248 line = self.input.readline()
251 self.lineno = self.lineno + 1
252 line = string.lstrip(line)
253 line = string.rstrip(line)
256 while line[-1] == '\\':
258 n = self.input.readline()
259 self.lineno = self.lineno + 1
271 def push(self, token):
272 self.tokens.insert(0, token);
275 print "Last token: ", self.last
276 print "Token queue: ", self.tokens
277 print "Line %d end: " % (self.lineno), self.line
280 while self.tokens == []:
282 line = self.getline()
290 self.tokens = map((lambda x: ('preproc', x)),
294 if line[0] == '"' or line[0] == "'":
304 self.line = line[i+1:]
314 line = self.getline()
317 self.last = ('string', tok)
320 if l >= 2 and line[0] == '/' and line[1] == '*':
328 if line[i] == '*' and i+1 < l and line[i+1] == '/':
329 self.line = line[i+2:]
339 line = self.getline()
342 self.last = ('comment', tok)
344 if l >= 2 and line[0] == '/' and line[1] == '/':
346 self.last = ('comment', line)
350 if line[i] == '/' and i+1 < l and line[i+1] == '/':
354 if line[i] == '/' and i+1 < l and line[i+1] == '*':
358 if line[i] == '"' or line[i] == "'":
366 if line[i] == ' ' or line[i] == '\t':
370 if (o >= 97 and o <= 122) or (o >= 65 and o <= 90) or \
371 (o >= 48 and o <= 57):
375 if (o >= 97 and o <= 122) or (o >= 65 and o <= 90) or \
376 (o >= 48 and o <= 57) or string.find(
377 " \t(){}:;,+-*/%&!|[]=><", line[i]) == -1:
381 self.tokens.append(('name', line[s:i]))
383 if string.find("(){}:;,[]", line[i]) != -1:
384 # if line[i] == '(' or line[i] == ')' or line[i] == '{' or \
385 # line[i] == '}' or line[i] == ':' or line[i] == ';' or \
386 # line[i] == ',' or line[i] == '[' or line[i] == ']':
387 self.tokens.append(('sep', line[i]))
390 if string.find("+-*><=/%&!|.", line[i]) != -1:
391 # if line[i] == '+' or line[i] == '-' or line[i] == '*' or \
392 # line[i] == '>' or line[i] == '<' or line[i] == '=' or \
393 # line[i] == '/' or line[i] == '%' or line[i] == '&' or \
394 # line[i] == '!' or line[i] == '|' or line[i] == '.':
395 if line[i] == '.' and i + 2 < l and \
396 line[i+1] == '.' and line[i+2] == '.':
397 self.tokens.append(('name', '...'))
403 string.find("+-*><=/%&!|", line[j]) != -1):
404 # line[j] == '+' or line[j] == '-' or line[j] == '*' or \
405 # line[j] == '>' or line[j] == '<' or line[j] == '=' or \
406 # line[j] == '/' or line[j] == '%' or line[j] == '&' or \
407 # line[j] == '!' or line[j] == '|'):
408 self.tokens.append(('op', line[i:j+1]))
411 self.tokens.append(('op', line[i]))
417 if (o >= 97 and o <= 122) or (o >= 65 and o <= 90) or \
418 (o >= 48 and o <= 57) or (
419 string.find(" \t(){}:;,+-*/%&!|[]=><", line[i]) == -1):
420 # line[i] != ' ' and line[i] != '\t' and
421 # line[i] != '(' and line[i] != ')' and
422 # line[i] != '{' and line[i] != '}' and
423 # line[i] != ':' and line[i] != ';' and
424 # line[i] != ',' and line[i] != '+' and
425 # line[i] != '-' and line[i] != '*' and
426 # line[i] != '/' and line[i] != '%' and
427 # line[i] != '&' and line[i] != '!' and
428 # line[i] != '|' and line[i] != '[' and
429 # line[i] != ']' and line[i] != '=' and
430 # line[i] != '*' and line[i] != '>' and
435 self.tokens.append(('name', line[s:i]))
438 self.tokens = self.tokens[1:]
443 """The C module parser"""
444 def __init__(self, filename, idx = None):
445 self.filename = filename
446 if len(filename) > 2 and filename[-2:] == '.h':
450 self.input = open(filename)
451 self.lexer = CLexer(self.input)
456 self.top_comment = ""
457 self.last_comment = ""
461 return self.lexer.getlineno()
463 def error(self, msg, token=-1):
464 print "Parse Error: " + msg
466 print "Got token ", token
470 def debug(self, msg, token=-1):
471 print "Debug: " + msg
473 print "Got token ", token
476 def parseComment(self, token):
477 if self.top_comment == "":
478 self.top_comment = token[1]
479 if self.comment == None or token[1][0] == '*':
480 self.comment = token[1];
482 self.comment = self.comment + token[1]
483 token = self.lexer.token()
487 # Parse a comment block associate to a macro
489 def parseMacroComment(self, name, quiet = 0):
490 if name[0:2] == '__':
496 if self.comment == None:
498 print "Missing comment for macro %s" % (name)
500 if self.comment[0] != '*':
502 print "Missing * in macro comment for %s" % (name)
504 lines = string.split(self.comment, '\n')
507 if lines[0] != "* %s:" % (name):
509 print "Misformatted macro comment for %s" % (name)
510 print " Expecting '* %s:' got '%s'" % (name, lines[0])
513 while lines[0] == '*':
515 while len(lines) > 0 and lines[0][0:3] == '* @':
518 (arg, desc) = string.split(l, ':', 1)
519 desc=string.strip(desc)
520 arg=string.strip(arg)
523 print "Misformatted macro comment for %s" % (name)
524 print " problem with '%s'" % (lines[0])
528 l = string.strip(lines[0])
529 while len(l) > 2 and l[0:3] != '* @':
532 desc = desc + ' ' + string.strip(l)
537 args.append((arg, desc))
538 while len(lines) > 0 and lines[0] == '*':
541 while len(lines) > 0:
543 while len(l) > 0 and l[0] == '*':
546 desc = desc + " " + l
549 desc = string.strip(desc)
553 print "Macro comment for %s lack description of the macro" % (name)
558 # Parse a comment block and merge the informations found in the
559 # parameters descriptions, finally returns a block as complete
562 def mergeFunctionComment(self, name, description, quiet = 0):
565 if name[0:2] == '__':
568 (ret, args) = description
572 if self.comment == None:
574 print "Missing comment for function %s" % (name)
575 return(((ret[0], retdesc), args, desc))
576 if self.comment[0] != '*':
578 print "Missing * in function comment for %s" % (name)
579 return(((ret[0], retdesc), args, desc))
580 lines = string.split(self.comment, '\n')
583 if lines[0] != "* %s:" % (name):
585 print "Misformatted function comment for %s" % (name)
586 print " Expecting '* %s:' got '%s'" % (name, lines[0])
587 return(((ret[0], retdesc), args, desc))
589 while lines[0] == '*':
592 while len(lines) > 0 and lines[0][0:3] == '* @':
595 (arg, desc) = string.split(l, ':', 1)
596 desc=string.strip(desc)
597 arg=string.strip(arg)
600 print "Misformatted function comment for %s" % (name)
601 print " problem with '%s'" % (lines[0])
605 l = string.strip(lines[0])
606 while len(l) > 2 and l[0:3] != '* @':
609 desc = desc + ' ' + string.strip(l)
616 if args[i][1] == arg:
617 args[i] = (args[i][0], arg, desc)
622 print "Uname to find arg %s from function comment for %s" % (
624 while len(lines) > 0 and lines[0] == '*':
627 while len(lines) > 0:
629 while len(l) > 0 and l[0] == '*':
632 if len(l) >= 6 and l[0:6] == "return" or l[0:6] == "Return":
634 l = string.split(l, ' ', 1)[1]
637 retdesc = string.strip(l)
639 while len(lines) > 0:
641 while len(l) > 0 and l[0] == '*':
644 retdesc = retdesc + " " + l
647 desc = desc + " " + l
650 retdesc = string.strip(retdesc)
651 desc = string.strip(desc)
655 # report missing comments
659 if args[i][2] == None and args[i][0] != "void" and args[i][1] != None:
660 print "Function comment for %s lack description of arg %s" % (name, args[i][1])
662 if retdesc == "" and ret[0] != "void":
663 print "Function comment for %s lack description of return value" % (name)
665 print "Function comment for %s lack description of the function" % (name)
668 return(((ret[0], retdesc), args, desc))
670 def parsePreproc(self, token):
672 if name == "#include":
673 token = self.lexer.token()
676 if token[0] == 'preproc':
677 self.index.add(token[1], self.filename, not self.is_header,
679 return self.lexer.token()
681 if name == "#define":
682 token = self.lexer.token()
685 if token[0] == 'preproc':
686 # TODO macros with arguments
689 token = self.lexer.token()
690 while token != None and token[0] == 'preproc' and \
693 token = self.lexer.token()
695 name = string.split(name, '(') [0]
698 info = self.parseMacroComment(name, not self.is_header)
699 self.index.add(name, self.filename, not self.is_header,
702 token = self.lexer.token()
703 while token != None and token[0] == 'preproc' and \
705 token = self.lexer.token()
709 # token acquisition on top of the lexer, it handle internally
710 # preprocessor and comments since they are logically not part of
711 # the program structure.
716 token = self.lexer.token()
718 if token[0] == 'comment':
719 token = self.parseComment(token)
721 elif token[0] == 'preproc':
722 token = self.parsePreproc(token)
724 elif token[0] == "name" and ignored_words.has_key(token[1]):
725 (n, info) = ignored_words[token[1]]
728 token = self.lexer.token()
730 token = self.lexer.token()
738 # Parse a typedef, it records the type and its name.
740 def parseTypedef(self, token):
743 token = self.parseType(token)
745 self.error("parsing typedef")
747 base_type = self.type
749 #self.debug("end typedef type", token)
751 if token[0] == "name":
753 signature = self.signature
754 if signature != None:
755 type = string.split(type, '(')[0]
756 d = self.mergeFunctionComment(name,
757 ((type, None), signature), 1)
758 self.index.add(name, self.filename, not self.is_header,
761 if base_type == "struct":
762 self.index.add(name, self.filename, not self.is_header,
764 base_type = "struct " + name
766 self.index.add(name, self.filename, not self.is_header,
770 self.error("parsing typedef: expecting a name")
772 #self.debug("end typedef", token)
773 if token != None and token[0] == 'sep' and token[1] == ',':
776 while token != None and token[0] == "op":
777 type = type + token[1]
779 elif token != None and token[0] == 'sep' and token[1] == ';':
781 elif token != None and token[0] == 'name':
785 self.error("parsing typedef: expecting ';'", token)
791 # Parse a C code block, used for functions it parse till
792 # the balancing } included
794 def parseBlock(self, token):
796 if token[0] == "sep" and token[1] == "{":
798 token = self.parseBlock(token)
799 elif token[0] == "sep" and token[1] == "}":
808 # Parse a C struct definition till the balancing }
810 def parseStruct(self, token):
812 #self.debug("start parseStruct", token)
814 if token[0] == "sep" and token[1] == "{":
816 token = self.parseTypeBlock(token)
817 elif token[0] == "sep" and token[1] == "}":
818 self.struct_fields = fields
819 #self.debug("end parseStruct", token)
824 base_type = self.type
825 #self.debug("before parseType", token)
826 token = self.parseType(token)
827 #self.debug("after parseType", token)
828 if token != None and token[0] == "name":
831 if token[0] == "sep" and token[1] == ";":
834 fields.append((self.type, fname, self.comment))
837 self.error("parseStruct: expecting ;", token)
838 elif token != None and token[0] == "sep" and token[1] == "{":
840 token = self.parseTypeBlock(token)
841 if token != None and token[0] == "name":
843 if token != None and token[0] == "sep" and token[1] == ";":
846 self.error("parseStruct: expecting ;", token)
848 self.error("parseStruct: name", token)
850 self.type = base_type;
851 self.struct_fields = fields
852 #self.debug("end parseStruct", token)
857 # Parse a C enum block, parse till the balancing }
859 def parseEnumBlock(self, token):
866 if token[0] == "sep" and token[1] == "{":
868 token = self.parseTypeBlock(token)
869 elif token[0] == "sep" and token[1] == "}":
871 if self.comment != None:
872 comment = self.comment
874 self.enums.append((name, value, comment))
877 elif token[0] == "name":
879 if self.comment != None:
880 comment = string.strip(self.comment)
882 self.enums.append((name, value, comment))
886 if token[0] == "op" and token[1][0] == "=":
888 if len(token[1]) > 1:
891 while token[0] != "sep" or (token[1] != ',' and
893 value = value + token[1]
897 value = "%d" % (int(value) + 1)
899 print "Failed to compute value of enum %s" % (name)
901 if token[0] == "sep" and token[1] == ",":
908 # Parse a C definition block, used for structs it parse till
911 def parseTypeBlock(self, token):
913 if token[0] == "sep" and token[1] == "{":
915 token = self.parseTypeBlock(token)
916 elif token[0] == "sep" and token[1] == "}":
924 # Parse a type: the fact that the type name can either occur after
925 # the definition or within the definition makes it a little harder
926 # if inside, the name token is pushed back before returning
928 def parseType(self, token):
930 self.struct_fields = []
931 self.signature = None
935 while token[0] == "name" and (
936 token[1] == "const" or token[1] == "unsigned"):
940 self.type = self.type + " " + token[1]
943 if token[0] == "name" and (token[1] == "long" or token[1] == "short"):
947 self.type = self.type + " " + token[1]
948 if token[0] == "name" and token[1] == "int":
952 self.type = self.type + " " + tmp[1]
954 elif token[0] == "name" and token[1] == "struct":
958 self.type = self.type + " " + token[1]
961 if token[0] == "name":
964 if token != None and token[0] == "sep" and token[1] == "{":
966 token = self.parseStruct(token)
967 elif token != None and token[0] == "op" and token[1] == "*":
968 self.type = self.type + " " + nametok[1] + " *"
970 while token != None and token[0] == "op" and token[1] == "*":
971 self.type = self.type + " *"
973 if token[0] == "name":
977 self.error("struct : expecting name", token)
979 elif token != None and token[0] == "name" and nametok != None:
980 self.type = self.type + " " + nametok[1]
984 self.lexer.push(token)
988 elif token[0] == "name" and token[1] == "enum":
992 self.type = self.type + " " + token[1]
995 if token != None and token[0] == "sep" and token[1] == "{":
997 token = self.parseEnumBlock(token)
999 self.error("parsing enum: expecting '{'", token)
1001 if token != None and token[0] != "name":
1002 self.lexer.push(token)
1003 token = ("name", "enum")
1005 enum_type = token[1]
1006 for enum in self.enums:
1007 self.index.add(enum[0], self.filename,
1008 not self.is_header, "enum",
1009 (enum[1], enum[2], enum_type))
1012 elif token[0] == "name":
1014 self.type = token[1]
1016 self.type = self.type + " " + token[1]
1018 self.error("parsing type %s: expecting a name" % (self.type),
1021 token = self.token()
1022 while token != None and (token[0] == "op" or
1023 token[0] == "name" and token[1] == "const"):
1024 self.type = self.type + " " + token[1]
1025 token = self.token()
1028 # if there is a parenthesis here, this means a function type
1030 if token != None and token[0] == "sep" and token[1] == '(':
1031 self.type = self.type + token[1]
1032 token = self.token()
1033 while token != None and token[0] == "op" and token[1] == '*':
1034 self.type = self.type + token[1]
1035 token = self.token()
1036 if token == None or token[0] != "name" :
1037 self.error("parsing function type, name expected", token);
1039 self.type = self.type + token[1]
1041 token = self.token()
1042 if token != None and token[0] == "sep" and token[1] == ')':
1043 self.type = self.type + token[1]
1044 token = self.token()
1045 if token != None and token[0] == "sep" and token[1] == '(':
1046 token = self.token()
1048 token = self.parseSignature(token);
1051 self.error("parsing function type, '(' expected", token);
1054 self.error("parsing function type, ')' expected", token);
1056 self.lexer.push(token)
1061 # do some lookahead for arrays
1063 if token != None and token[0] == "name":
1065 token = self.token()
1066 if token != None and token[0] == "sep" and token[1] == '[':
1067 self.type = self.type + nametok[1]
1068 while token != None and token[0] == "sep" and token[1] == '[':
1069 self.type = self.type + token[1]
1070 token = self.token()
1071 while token != None and token[0] != 'sep' and \
1072 token[1] != ']' and token[1] != ';':
1073 self.type = self.type + token[1]
1074 token = self.token()
1075 if token != None and token[0] == 'sep' and token[1] == ']':
1076 self.type = self.type + token[1]
1077 token = self.token()
1079 self.error("parsing array type, ']' expected", token);
1081 elif token != None and token[0] == "sep" and token[1] == ':':
1082 # remove :12 in case it's a limited int size
1083 token = self.token()
1084 token = self.token()
1085 self.lexer.push(token)
1091 # Parse a signature: '(' has been parsed and we scan the type definition
1092 # up to the ')' included
1093 def parseSignature(self, token):
1095 if token != None and token[0] == "sep" and token[1] == ')':
1097 token = self.token()
1099 while token != None:
1100 token = self.parseType(token)
1101 if token != None and token[0] == "name":
1102 signature.append((self.type, token[1], None))
1103 token = self.token()
1104 elif token != None and token[0] == "sep" and token[1] == ',':
1105 token = self.token()
1107 elif token != None and token[0] == "sep" and token[1] == ')':
1108 # only the type was provided
1109 if self.type == "...":
1110 signature.append((self.type, "...", None))
1112 signature.append((self.type, None, None))
1113 if token != None and token[0] == "sep":
1115 token = self.token()
1117 elif token[1] == ')':
1118 token = self.token()
1120 self.signature = signature
1124 # Parse a global definition, be it a type, variable or function
1125 # the extern "C" blocks are a bit nasty and require it to recurse.
1127 def parseGlobal(self, token):
1129 if token[1] == 'extern':
1130 token = self.token()
1133 if token[0] == 'string':
1135 token = self.token()
1138 if token[0] == 'sep' and token[1] == "{":
1139 token = self.token()
1140 # print 'Entering extern "C line ', self.lineno()
1141 while token != None and (token[0] != 'sep' or
1143 if token[0] == 'name':
1144 token = self.parseGlobal(token)
1147 "token %s %s unexpected at the top level" % (
1148 token[0], token[1]))
1149 token = self.parseGlobal(token)
1150 # print 'Exiting extern "C" line', self.lineno()
1151 token = self.token()
1155 elif token[1] == 'static':
1157 token = self.token()
1158 if token == None or token[0] != 'name':
1161 if token[1] == 'typedef':
1162 token = self.token()
1163 return self.parseTypedef(token)
1165 token = self.parseType(token)
1166 type_orig = self.type
1167 if token == None or token[0] != "name":
1170 self.name = token[1]
1171 token = self.token()
1172 while token != None and (token[0] == "sep" or token[0] == "op"):
1173 if token[0] == "sep":
1175 type = type + token[1]
1176 token = self.token()
1177 while token != None and (token[0] != "sep" or \
1179 type = type + token[1]
1180 token = self.token()
1182 if token != None and token[0] == "op" and token[1] == "=":
1184 # Skip the initialization of the variable
1186 token = self.token()
1187 if token[0] == 'sep' and token[1] == '{':
1188 token = self.token()
1189 token = self.parseBlock(token)
1192 while token != None and (token[0] != "sep" or \
1193 (token[1] != ';' and token[1] != ',')):
1194 token = self.token()
1196 if token == None or token[0] != "sep" or (token[1] != ';' and
1198 self.error("missing ';' or ',' after value")
1200 if token != None and token[0] == "sep":
1203 token = self.token()
1204 if type == "struct":
1205 self.index.add(self.name, self.filename,
1206 not self.is_header, "struct", self.struct_fields)
1208 self.index.add(self.name, self.filename,
1209 not self.is_header, "variable", type)
1211 elif token[1] == "(":
1212 token = self.token()
1213 token = self.parseSignature(token)
1216 if token[0] == "sep" and token[1] == ";":
1217 d = self.mergeFunctionComment(self.name,
1218 ((type, None), self.signature), 1)
1219 self.index.add(self.name, self.filename, static,
1221 token = self.token()
1222 elif token[0] == "sep" and token[1] == "{":
1223 d = self.mergeFunctionComment(self.name,
1224 ((type, None), self.signature), static)
1225 self.index.add(self.name, self.filename, static,
1227 token = self.token()
1228 token = self.parseBlock(token);
1229 elif token[1] == ',':
1231 self.index.add(self.name, self.filename, static,
1234 token = self.token()
1235 while token != None and token[0] == "sep":
1236 type = type + token[1]
1237 token = self.token()
1238 if token != None and token[0] == "name":
1239 self.name = token[1]
1240 token = self.token()
1247 print "Parsing %s" % (self.filename)
1248 token = self.token()
1249 while token != None:
1250 if token[0] == 'name':
1251 token = self.parseGlobal(token)
1253 self.error("token %s %s unexpected at the top level" % (
1254 token[0], token[1]))
1255 token = self.parseGlobal(token)
1261 """A documentation builder"""
1262 def __init__(self, name, directories=['.'], excludes=[]):
1264 self.directories = directories
1265 self.excludes = excludes + ignored_files.keys()
1271 print "Project %s : %d headers, %d modules" % (self.name, len(self.headers.keys()), len(self.modules.keys()))
1274 def scanHeaders(self):
1275 for header in self.headers.keys():
1276 parser = CParser(header)
1277 idx = parser.parse()
1278 self.headers[header] = idx;
1281 def scanModules(self):
1282 for module in self.modules.keys():
1283 parser = CParser(module)
1284 idx = parser.parse()
1286 self.modules[module] = idx
1287 self.idx.merge_public(idx)
1290 for directory in self.directories:
1291 files = glob.glob(directory + "/*.c")
1294 for excl in self.excludes:
1295 if string.find(file, excl) != -1:
1299 self.modules[file] = None;
1300 files = glob.glob(directory + "/*.h")
1303 for excl in self.excludes:
1304 if string.find(file, excl) != -1:
1308 self.headers[file] = None;
1312 def modulename_file(self, file):
1313 module = os.path.basename(file)
1314 if module[-2:] == '.h':
1315 module = module[:-2]
1318 def serialize_enum(self, output, name):
1319 id = self.idx.enums[name]
1320 output.write(" <enum name='%s' file='%s'" % (name,
1321 self.modulename_file(id.module)))
1324 if info[0] != None and info[0] != '':
1325 output.write(" value='%s'" % info[0]);
1326 if info[2] != None and info[2] != '':
1327 output.write(" type='%s'" % info[2]);
1328 if info[1] != None and info[1] != '':
1329 output.write(" info='%s'" % escape(info[1]));
1330 output.write("/>\n")
1332 def serialize_macro(self, output, name):
1333 id = self.idx.macros[name]
1334 output.write(" <macro name='%s' file='%s'>\n" % (name,
1335 self.modulename_file(id.module)))
1338 (args, desc) = id.info
1339 if desc != None and desc != "":
1340 output.write(" <info>%s</info>\n" % (escape(desc)))
1343 if desc != None and desc != "":
1344 output.write(" <arg name='%s' info='%s'/>\n" % (
1345 name, escape(desc)))
1347 output.write(" <arg name='%s'/>\n" % (name))
1350 output.write(" </macro>\n")
1352 def serialize_typedef(self, output, name):
1353 id = self.idx.typedefs[name]
1354 if id.info[0:7] == 'struct ':
1355 output.write(" <struct name='%s' file='%s' type='%s'" % (
1356 name, self.modulename_file(id.module), id.info))
1358 if self.idx.structs.has_key(name) and ( \
1359 type(self.idx.structs[name].info) == type(()) or
1360 type(self.idx.structs[name].info) == type([])):
1361 output.write(">\n");
1363 for field in self.idx.structs[name].info:
1369 output.write(" <field name='%s' type='%s' info='%s'/>\n" % (field[1] , field[0], desc))
1371 print "Failed to serialize struct %s" % (name)
1372 output.write(" </struct>\n")
1374 output.write("/>\n");
1376 output.write(" <typedef name='%s' file='%s' type='%s'/>\n" % (
1377 name, self.modulename_file(id.module), id.info))
1379 def serialize_variable(self, output, name):
1380 id = self.idx.variables[name]
1382 output.write(" <variable name='%s' file='%s' type='%s'/>\n" % (
1383 name, self.modulename_file(id.module), id.info))
1385 output.write(" <variable name='%s' file='%s'/>\n" % (
1386 name, self.modulename_file(id.module)))
1388 def serialize_function(self, output, name):
1389 id = self.idx.functions[name]
1390 output.write(" <%s name='%s' file='%s'>\n" % (id.type, name,
1391 self.modulename_file(id.module)))
1393 (ret, params, desc) = id.info
1394 output.write(" <info>%s</info>\n" % (escape(desc)))
1396 if ret[0] == "void":
1397 output.write(" <return type='void'/>\n")
1399 output.write(" <return type='%s' info='%s'/>\n" % (
1400 ret[0], escape(ret[1])))
1401 for param in params:
1402 if param[0] == 'void':
1404 if param[2] == None:
1405 output.write(" <arg name='%s' type='%s' info=''/>\n" % (param[1], param[0]))
1407 output.write(" <arg name='%s' type='%s' info='%s'/>\n" % (param[1], param[0], escape(param[2])))
1409 print "Failed to save function %s info: " % name, `id.info`
1410 output.write(" </%s>\n" % (id.type))
1412 def serialize_exports(self, output, file):
1413 module = self.modulename_file(file)
1414 output.write(" <file name='%s'>\n" % (module))
1415 dict = self.headers[file]
1416 ids = dict.functions.keys() + dict.variables.keys() + \
1417 dict.macros.keys() + dict.typedefs.keys() + \
1418 dict.structs.keys() + dict.enums.keys()
1421 output.write(" <exports symbol='%s'/>\n" % (id))
1422 output.write(" </file>\n")
1425 def serialize(self, filename = None):
1426 if filename == None:
1427 filename = "%s-api.xml" % self.name
1428 print "Saving XML description %s" % (filename)
1429 output = open(filename, "w")
1430 output.write('<?xml version="1.0" encoding="ISO-8859-1"?>\n')
1431 output.write("<api name='%s'>\n" % self.name)
1432 output.write(" <files>\n")
1433 for file in self.headers.keys():
1434 self.serialize_exports(output, file)
1435 output.write(" </files>\n")
1436 output.write(" <symbols>\n")
1437 macros = self.idx.macros.keys()
1439 for macro in macros:
1440 self.serialize_macro(output, macro)
1441 enums = self.idx.enums.keys()
1444 self.serialize_enum(output, enum)
1445 typedefs = self.idx.typedefs.keys()
1447 for typedef in typedefs:
1448 self.serialize_typedef(output, typedef)
1449 variables = self.idx.variables.keys()
1451 for variable in variables:
1452 self.serialize_variable(output, variable)
1453 functions = self.idx.functions.keys()
1455 for function in functions:
1456 self.serialize_function(output, function)
1457 output.write(" </symbols>\n")
1458 output.write("</api>\n")
1464 if glob.glob("../parser.c") != [] :
1465 print "Rebuilding API description for libxml2"
1466 builder = docBuilder("libxml2", ["..", "../include/libxml"],
1467 ["xmlwin32version.h", "tst.c"])
1468 elif glob.glob("../libxslt/transform.c") != [] :
1469 print "Rebuilding API description for libxslt"
1470 builder = docBuilder("libxslt", ["../libxslt"],
1471 ["win32config.h", "libxslt.h", "tst.c"])
1473 print "rebuild() failed, unable to guess the module"
1478 if glob.glob("../libexslt/exslt.c") != [] :
1479 extra = docBuilder("libexslt", ["../libexslt"], ["libexslt.h"])
1486 # for debugging the parser
1488 def parse(filename):
1489 parser = CParser(filename)
1490 idx = parser.parse()
1493 if __name__ == "__main__":