eli.bendersky | fe5c2bb | 2010-12-04 17:38:11 +0200 | [diff] [blame] | 1 | #----------------------------------------------------------------- |
| 2 | # pycparser: cdecl.py |
| 3 | # |
| 4 | # Example of the CDECL tool using pycparser. CDECL "explains" |
| 5 | # C type declarations in plain English. |
| 6 | # |
| 7 | # The AST generated by pycparser from the given declaration is |
| 8 | # traversed recursively to build the explanation. |
| 9 | # Note that the declaration must be a valid external declaration |
| 10 | # in C. All the types used in it must be defined with typedef, |
| 11 | # or parsing will fail. The definition can be arbitrary, it isn't |
| 12 | # really used - by pycparser must know which tokens are types. |
| 13 | # |
| 14 | # For example: |
| 15 | # |
| 16 | # 'typedef int Node; const Node* (*ar)[10];' |
| 17 | # => |
| 18 | # ar is a pointer to array[10] of pointer to const Node |
| 19 | # |
eli.bendersky | 84a6a63 | 2011-04-29 09:00:43 +0300 | [diff] [blame] | 20 | # Copyright (C) 2008-2011, Eli Bendersky |
| 21 | # License: BSD |
eli.bendersky | fe5c2bb | 2010-12-04 17:38:11 +0200 | [diff] [blame] | 22 | #----------------------------------------------------------------- |
| 23 | import sys |
| 24 | |
| 25 | # This is not required if you've installed pycparser into |
| 26 | # your site-packages/ with setup.py |
| 27 | # |
Ben | f86dea1 | 2012-02-03 06:24:55 +0200 | [diff] [blame] | 28 | sys.path.extend(['.', '..']) |
eli.bendersky | fe5c2bb | 2010-12-04 17:38:11 +0200 | [diff] [blame] | 29 | |
| 30 | from pycparser import c_parser, c_ast |
| 31 | |
| 32 | |
Eli Bendersky | 3921e8e | 2010-05-21 09:05:39 +0300 | [diff] [blame] | 33 | def explain_c_declaration(c_decl): |
eli.bendersky | fe5c2bb | 2010-12-04 17:38:11 +0200 | [diff] [blame] | 34 | """ Parses the declaration in c_decl and returns a text |
| 35 | explanation as a string. |
| 36 | |
| 37 | The last external node of the string is used, to allow |
Eli Bendersky | 3921e8e | 2010-05-21 09:05:39 +0300 | [diff] [blame] | 38 | earlier typedefs for used types. |
eli.bendersky | fe5c2bb | 2010-12-04 17:38:11 +0200 | [diff] [blame] | 39 | """ |
| 40 | parser = c_parser.CParser() |
| 41 | |
| 42 | try: |
| 43 | node = parser.parse(c_decl, filename='<stdin>') |
| 44 | except c_parser.ParseError: |
| 45 | e = sys.exc_info()[1] |
| 46 | return "Parse error:" + str(e) |
| 47 | |
| 48 | if ( not isinstance(node, c_ast.FileAST) or |
| 49 | not isinstance(node.ext[-1], c_ast.Decl)): |
| 50 | return "Not a valid declaration" |
| 51 | |
| 52 | return _explain_decl_node(node.ext[-1]) |
| 53 | |
| 54 | |
Eli Bendersky | 3921e8e | 2010-05-21 09:05:39 +0300 | [diff] [blame] | 55 | def _explain_decl_node(decl_node): |
eli.bendersky | fe5c2bb | 2010-12-04 17:38:11 +0200 | [diff] [blame] | 56 | """ Receives a c_ast.Decl note and returns its explanation in |
Eli Bendersky | 3921e8e | 2010-05-21 09:05:39 +0300 | [diff] [blame] | 57 | English. |
eli.bendersky | fe5c2bb | 2010-12-04 17:38:11 +0200 | [diff] [blame] | 58 | """ |
| 59 | #~ print decl_node.show() |
| 60 | storage = ' '.join(decl_node.storage) + ' ' if decl_node.storage else '' |
| 61 | |
| 62 | return (decl_node.name + |
| 63 | " is a " + |
| 64 | storage + |
| 65 | _explain_type(decl_node.type)) |
| 66 | |
| 67 | |
Eli Bendersky | 3921e8e | 2010-05-21 09:05:39 +0300 | [diff] [blame] | 68 | def _explain_type(decl): |
| 69 | """ Recursively explains a type decl node |
eli.bendersky | fe5c2bb | 2010-12-04 17:38:11 +0200 | [diff] [blame] | 70 | """ |
| 71 | typ = type(decl) |
| 72 | |
| 73 | if typ == c_ast.TypeDecl: |
| 74 | quals = ' '.join(decl.quals) + ' ' if decl.quals else '' |
| 75 | return quals + _explain_type(decl.type) |
| 76 | elif typ == c_ast.Typename or typ == c_ast.Decl: |
| 77 | return _explain_type(decl.type) |
| 78 | elif typ == c_ast.IdentifierType: |
| 79 | return ' '.join(decl.names) |
| 80 | elif typ == c_ast.PtrDecl: |
| 81 | quals = ' '.join(decl.quals) + ' ' if decl.quals else '' |
| 82 | return quals + 'pointer to ' + _explain_type(decl.type) |
| 83 | elif typ == c_ast.ArrayDecl: |
| 84 | arr = 'array' |
| 85 | if decl.dim: arr += '[%s]' % decl.dim.value |
| 86 | |
| 87 | return arr + " of " + _explain_type(decl.type) |
| 88 | |
| 89 | elif typ == c_ast.FuncDecl: |
| 90 | if decl.args: |
| 91 | params = [_explain_type(param) for param in decl.args.params] |
| 92 | args = ', '.join(params) |
| 93 | else: |
| 94 | args = '' |
| 95 | |
| 96 | return ('function(%s) returning ' % (args) + |
| 97 | _explain_type(decl.type)) |
| 98 | |
| 99 | |
| 100 | if __name__ == "__main__": |
| 101 | if len(sys.argv) > 1: |
| 102 | c_decl = sys.argv[1] |
| 103 | else: |
| 104 | c_decl = "char *(*(**foo[][8])())[];" |
| 105 | |
| 106 | print("Explaining the declaration: " + c_decl + "\n") |
| 107 | print(explain_c_declaration(c_decl) + "\n") |
| 108 | |