blob: afb104b6ef7549ec81bfc4a3b09f25c111b0024e [file] [log] [blame]
Martin v. Löwis4d0d4712010-12-03 20:14:31 +00001# This script converts a C file to use the PEP 384 type definition API
2# Usage: abitype.py < old_code > new_code
3import re, sys
4
5############ Simplistic C scanner ##################################
6tokenizer = re.compile(
7 r"(?P<preproc>#.*\n)"
8 r"|(?P<comment>/\*.*?\*/)"
9 r"|(?P<ident>[a-zA-Z_][a-zA-Z0-9_]*)"
10 r"|(?P<ws>[ \t\n]+)"
11 r"|(?P<other>.)",
12 re.MULTILINE)
13
14tokens = []
15source = sys.stdin.read()
16pos = 0
17while pos != len(source):
18 m = tokenizer.match(source, pos)
19 tokens.append([m.lastgroup, m.group()])
20 pos += len(tokens[-1][1])
21 if tokens[-1][0] == 'preproc':
22 # continuation lines are considered
23 # only in preprocess statements
24 while tokens[-1][1].endswith('\\\n'):
25 nl = source.find('\n', pos)
26 if nl == -1:
27 line = source[pos:]
28 else:
29 line = source[pos:nl+1]
30 tokens[-1][1] += line
31 pos += len(line)
32
33###### Replacement of PyTypeObject static instances ##############
34
35# classify each token, giving it a one-letter code:
36# S: static
37# T: PyTypeObject
38# I: ident
39# W: whitespace
40# =, {, }, ; : themselves
41def classify():
42 res = []
43 for t,v in tokens:
44 if t == 'other' and v in "={};":
45 res.append(v)
46 elif t == 'ident':
47 if v == 'PyTypeObject':
48 res.append('T')
49 elif v == 'static':
50 res.append('S')
51 else:
52 res.append('I')
53 elif t == 'ws':
54 res.append('W')
55 else:
56 res.append('.')
57 return ''.join(res)
58
59# Obtain a list of fields of a PyTypeObject, in declaration order,
60# skipping ob_base
61# All comments are dropped from the variable (which are typically
62# just the slot names, anyway), and information is discarded whether
63# the original type was static.
64def get_fields(start, real_end):
65 pos = start
66 # static?
67 if tokens[pos][1] == 'static':
68 pos += 2
69 # PyTypeObject
70 pos += 2
71 # name
72 name = tokens[pos][1]
73 pos += 1
74 while tokens[pos][1] != '{':
75 pos += 1
76 pos += 1
77 # PyVarObject_HEAD_INIT
78 while tokens[pos][0] in ('ws', 'comment'):
79 pos += 1
80 if tokens[pos][1] != 'PyVarObject_HEAD_INIT':
81 raise Exception, '%s has no PyVarObject_HEAD_INIT' % name
82 while tokens[pos][1] != ')':
83 pos += 1
84 pos += 1
85 # field definitions: various tokens, comma-separated
86 fields = []
87 while True:
88 while tokens[pos][0] in ('ws', 'comment'):
89 pos += 1
90 end = pos
91 while tokens[end][1] not in ',}':
92 if tokens[end][1] == '(':
93 nesting = 1
94 while nesting:
95 end += 1
96 if tokens[end][1] == '(': nesting+=1
97 if tokens[end][1] == ')': nesting-=1
98 end += 1
99 assert end < real_end
100 # join field, excluding separator and trailing ws
101 end1 = end-1
102 while tokens[end1][0] in ('ws', 'comment'):
103 end1 -= 1
104 fields.append(''.join(t[1] for t in tokens[pos:end1+1]))
105 if tokens[end][1] == '}':
106 break
107 pos = end+1
108 return name, fields
109
110# List of type slots as of Python 3.2, omitting ob_base
111typeslots = [
112 'tp_name',
113 'tp_basicsize',
114 'tp_itemsize',
115 'tp_dealloc',
116 'tp_print',
117 'tp_getattr',
118 'tp_setattr',
119 'tp_reserved',
120 'tp_repr',
121 'tp_as_number',
122 'tp_as_sequence',
123 'tp_as_mapping',
124 'tp_hash',
125 'tp_call',
126 'tp_str',
127 'tp_getattro',
128 'tp_setattro',
129 'tp_as_buffer',
130 'tp_flags',
131 'tp_doc',
132 'tp_traverse',
133 'tp_clear',
134 'tp_richcompare',
135 'tp_weaklistoffset',
136 'tp_iter',
137 'iternextfunc',
138 'tp_methods',
139 'tp_members',
140 'tp_getset',
141 'tp_base',
142 'tp_dict',
143 'tp_descr_get',
144 'tp_descr_set',
145 'tp_dictoffset',
146 'tp_init',
147 'tp_alloc',
148 'tp_new',
149 'tp_free',
150 'tp_is_gc',
151 'tp_bases',
152 'tp_mro',
153 'tp_cache',
154 'tp_subclasses',
155 'tp_weaklist',
156 'tp_del'
157 'tp_version_tag'
158]
159
160# Generate a PyType_Spec definition
161def make_slots(name, fields):
162 res = []
163 res.append('static PyType_Slot %s_slots[] = {' % name)
164 # defaults for spec
165 spec = { 'tp_doc':'NULL', 'tp_itemsize':'0' }
166 for i, val in enumerate(fields):
167 if val.endswith('0'):
168 continue
169 if typeslots[i] in ('tp_name', 'tp_doc', 'tp_basicsize',
170 'tp_itemsize', 'tp_flags'):
171 spec[typeslots[i]] = val
172 continue
173 res.append(' {Py_%s, %s},' % (typeslots[i], val))
174 res.append('};')
175 res.append('static PyType_Spec %s_spec = {' % name)
176 res.append(' %s,' % spec['tp_name'])
177 res.append(' %s,' % spec['tp_doc'])
178 res.append(' %s,' % spec['tp_basicsize'])
179 res.append(' %s,' % spec['tp_itemsize'])
180 res.append(' %s,' % spec['tp_flags'])
181 res.append(' %s_slots,' % name)
182 res.append('};\n')
183 return '\n'.join(res)
184
185
186# Main loop: replace all static PyTypeObjects until
187# there are none left.
188while 1:
189 c = classify()
190 m = re.search('(SW)?TWIW?=W?{.*?};', c)
191 if not m:
192 break
193 start = m.start()
194 end = m.end()
195 name, fields = get_fields(start, m)
196 tokens[start:end] = [('',make_slots(name, fields))]
197
198# Output result to stdout
199for t, v in tokens:
200 sys.stdout.write(v)