-
Notifications
You must be signed in to change notification settings - Fork 644
Expand file tree
/
Copy pathtyped_usage.py
More file actions
77 lines (56 loc) · 2.36 KB
/
typed_usage.py
File metadata and controls
77 lines (56 loc) · 2.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# ------------------------------------------------------------------------------
# pycparser: typed_usage.py
#
# A minimal example of how static type checking helps use pycparser correctly.
#
# The important parts are the narrowings:
# - ast.ext[...] entries start out as generic AST nodes
# - Decl.coord is optional and must be checked before use
# - Decl.type must be narrowed before accessing node-specific attributes
#
# Run it from the root directory of pycparser.
# ------------------------------------------------------------------------------
import sys
# This is not required if you've installed pycparser into
# your site-packages/ with setup.py
sys.path.extend([".", ".."])
from pycparser import c_ast, c_parser
SAMPLE_DECL = "const unsigned int *value;"
def parse_single_decl(source: str) -> c_ast.Decl:
ast = c_parser.CParser().parse(source, filename="<stdin>")
node = ast.ext[-1]
# Without this guard, type checkers only know that ext entries are Nodes.
if not isinstance(node, c_ast.Decl):
raise TypeError("expected a declaration")
return node
def require_coord(node: c_ast.Node) -> c_parser.Coord:
coord = node.coord
# Without this guard, `coord.line` is a type error because coord may be None.
if coord is None:
raise ValueError("node has no source coordinate")
return coord
def describe_type(type_node: c_ast.Node) -> str:
# Without these isinstance checks, attributes such as `.type`, `.quals`,
# and `.names` are not safe to access.
if isinstance(type_node, c_ast.TypeDecl):
quals = " ".join(type_node.quals)
base = describe_type(type_node.type)
return f"{quals} {base}".strip()
if isinstance(type_node, c_ast.PtrDecl):
quals = " ".join(type_node.quals)
pointer = "pointer"
if quals:
pointer = f"{quals} {pointer}"
return f"{pointer} to {describe_type(type_node.type)}"
if isinstance(type_node, c_ast.IdentifierType):
return " ".join(type_node.names)
raise TypeError(f"unsupported declaration type: {type_node.__class__.__name__}")
def show_decl(source: str) -> None:
decl = parse_single_decl(source)
coord = require_coord(decl)
print(f"{decl.name}: {describe_type(decl.type)} at {coord}")
if __name__ == "__main__":
if len(sys.argv) > 1:
show_decl(sys.argv[1])
else:
show_decl(SAMPLE_DECL)