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