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