1import sys
2
3PASS_BY_VALUE = 1
4PASS_BY_REFERENCE = 2
5
6DIR_NONE = 0
7DIR_IN   = 1
8DIR_OUT  = 2
9DIR_BOTH = 3
10
11_default_namespace = ""
12def namespace(s):
13    if type(s) != str:
14        raise TypeError, "Require a string for the default namespace."
15    global _default_namespace
16    _default_namespace = s
17
18def _get_default_namespace():
19    global _default_namespace
20    return _default_namespace
21
22_default_hidden = False
23def hidden(b):
24    global _default_hidden
25    _default_hidden = b
26
27def _get_default_hidden():
28    global _default_hidden
29    return _default_hidden
30
31class Type(object):
32    def __init__(self, typename, **kwargs):
33        self.namespace = kwargs.setdefault('namespace',
34                _get_default_namespace())
35        self._hidden = kwargs.setdefault('hidden', _get_default_hidden())
36        self.dir = kwargs.setdefault('dir', DIR_BOTH)
37        if self.dir not in [DIR_NONE, DIR_IN, DIR_OUT, DIR_BOTH]:
38            raise ValueError
39
40        self.passby = kwargs.setdefault('passby', PASS_BY_VALUE)
41        if self.passby not in [PASS_BY_VALUE, PASS_BY_REFERENCE]:
42            raise ValueError
43
44        self.private = kwargs.setdefault('private', False)
45
46        if typename is None: # Anonymous type
47            self.typename = None
48            self.rawname = None
49        elif self.namespace is None: # e.g. system provided types
50            self.typename = typename
51            self.rawname = typename
52        else:
53            self.typename = self.namespace + typename
54            self.rawname = typename
55
56        if self.typename is not None:
57            self.dispose_fn = kwargs.setdefault('dispose_fn', self.typename + "_dispose")
58        else:
59            self.dispose_fn = kwargs.setdefault('dispose_fn', None)
60
61        self.autogenerate_dispose_fn = kwargs.setdefault('autogenerate_dispose_fn', True)
62
63        if self.typename is not None:
64            self.copy_fn = kwargs.setdefault('copy_fn', self.typename + "_copy")
65        else:
66            self.copy_fn = kwargs.setdefault('copy_fn', None)
67
68        self.autogenerate_copy_fn = kwargs.setdefault('autogenerate_copy_fn', True)
69
70        self.init_fn = kwargs.setdefault('init_fn', None)
71        self.init_val = kwargs.setdefault('init_val', None)
72        self.autogenerate_init_fn = kwargs.setdefault('autogenerate_init_fn', False)
73
74        self.check_default_fn = kwargs.setdefault('check_default_fn', None)
75        self.copy_deprecated_fn = kwargs.setdefault('copy_deprecated_fn',
76                                                    None)
77
78        if self.typename is not None and not self.private:
79            self.json_gen_fn = kwargs.setdefault('json_gen_fn', self.typename + "_gen_json")
80            self.json_parse_type = kwargs.setdefault('json_parse_type', "JSON_ANY")
81            if self.namespace is not None:
82                self.json_parse_fn = kwargs.setdefault('json_parse_fn',
83                                                       self.namespace + "_" + self.rawname  + "_parse_json")
84            else:
85                self.json_parse_fn = kwargs.setdefault('json_parse_fn',
86                                                       self.typename + "_parse_json")
87        else:
88            self.json_gen_fn = kwargs.setdefault('json_gen_fn', None)
89            self.json_parse_type = kwargs.setdefault('json_parse_type', None)
90            self.json_parse_fn = kwargs.setdefault('json_parse_fn', None)
91
92        self.autogenerate_json = kwargs.setdefault('autogenerate_json', True)
93
94    def marshal_in(self):
95        return self.dir in [DIR_IN, DIR_BOTH]
96    def marshal_out(self):
97        return self.dir in [DIR_OUT, DIR_BOTH]
98
99    def hidden(self):
100        if self._hidden:
101            return "_hidden "
102        else:
103            return ""
104
105    def make_arg(self, n, passby=None):
106        if passby is None: passby = self.passby
107
108        if passby == PASS_BY_REFERENCE:
109            return "%s *%s" % (self.typename, n)
110        else:
111            return "%s %s" % (self.typename, n)
112
113    def pass_arg(self, n, isref=None, passby=None):
114        if passby is None: passby = self.passby
115        if isref is None: isref = self.passby == PASS_BY_REFERENCE
116
117        if passby == PASS_BY_REFERENCE:
118            if isref:
119                return "%s" % (n)
120            else:
121                return "&%s" % (n)
122        else:
123            if isref:
124                return "*%s" % (n)
125            else:
126                return "%s" % (n)
127
128class Builtin(Type):
129    """Builtin type"""
130    def __init__(self, typename, **kwargs):
131        kwargs.setdefault('dispose_fn', None)
132        kwargs.setdefault('autogenerate_dispose_fn', False)
133        kwargs.setdefault('autogenerate_json', False)
134        Type.__init__(self, typename, **kwargs)
135
136class Number(Builtin):
137    def __init__(self, ctype, **kwargs):
138        kwargs.setdefault('namespace', None)
139        kwargs.setdefault('dispose_fn', None)
140        kwargs.setdefault('copy_fn', None)
141        kwargs.setdefault('signed', False)
142        kwargs.setdefault('json_gen_fn', "yajl_gen_integer")
143        kwargs.setdefault('json_parse_type', "JSON_INTEGER")
144        # json_parse_fn might be overriden on specific type
145        kwargs.setdefault('json_parse_fn', "libxl__int_parse_json")
146        self.signed = kwargs['signed']
147        Builtin.__init__(self, ctype, **kwargs)
148
149class UInt(Number):
150    def __init__(self, w, **kwargs):
151        kwargs.setdefault('namespace', None)
152        kwargs.setdefault('dispose_fn', None)
153        kwargs.setdefault('json_parse_fn', "libxl__uint%d_parse_json" % w)
154        kwargs.setdefault('copy_fn', None)
155        Number.__init__(self, "uint%d_t" % w, **kwargs)
156
157        self.width = w
158
159class EnumerationValue(object):
160    def __init__(self, enum, value, name, **kwargs):
161        self.enum = enum
162
163        self.valuename = str.upper(name)
164        self.rawname = str.upper(enum.rawname) + "_" + self.valuename
165        self.name = str.upper(enum.value_namespace) + self.rawname
166        self.value = value
167
168class Enumeration(Type):
169    def __init__(self, typename, values, **kwargs):
170        kwargs.setdefault('dispose_fn', None)
171        kwargs.setdefault('copy_fn', None)
172        kwargs.setdefault('json_parse_type', "JSON_STRING")
173        Type.__init__(self, typename, **kwargs)
174
175        self.value_namespace = kwargs.setdefault('value_namespace',
176            self.namespace)
177
178        self.values = []
179        for v in values:
180            # (value, name)
181            (num,name) = v
182            self.values.append(EnumerationValue(self, num, name,
183                                                typename=self.rawname))
184    def lookup(self, name):
185        for v in self.values:
186            if v.valuename == str.upper(name):
187                return v
188        return ValueError
189
190class Field(object):
191    """An element of an Aggregate type"""
192    def __init__(self, type, name, **kwargs):
193        self.type = type
194        self.name = name
195        self.const = kwargs.setdefault('const', False)
196        self.enumname = kwargs.setdefault('enumname', None)
197        self.init_val = kwargs.setdefault('init_val', None)
198        self.deprecated_by = kwargs.setdefault('deprecated_by', None)
199
200class Aggregate(Type):
201    """A type containing a collection of other types"""
202    def __init__(self, kind, typename, fields, **kwargs):
203        kwargs.setdefault('json_parse_type', "JSON_MAP")
204        Type.__init__(self, typename, **kwargs)
205
206        if self.typename is not None:
207            self.init_fn = kwargs.setdefault('init_fn', self.typename + "_init")
208        else:
209            self.init_fn = kwargs.setdefault('init_fn', None)
210
211        self.autogenerate_init_fn = kwargs.setdefault('autogenerate_init_fn', True)
212
213        self.kind = kind
214
215        self.fields = []
216        for f in fields:
217            # (name, type[, {kw args}])
218            if len(f) == 2:
219                n,t = f
220                kw = {}
221            elif len(f) == 3:
222                n,t,kw = f
223            else:
224                raise ValueError
225            if n is None:
226                raise ValueError
227            self.fields.append(Field(t,n,**kw))
228
229    # Returns a tuple (stem, field-expr)
230    #
231    # field-expr is a C expression for a field "f" within the struct
232    # "v".
233    #
234    # stem is the stem common to both "f" and any other sibbling field
235    # within the "v".
236    def member(self, v, f, isref):
237        if isref:
238            deref = v + "->"
239        else:
240            deref = v + "."
241
242        if f.name is None: # Anonymous
243            return (deref, deref)
244        else:
245            return (deref, deref + f.name)
246
247class Struct(Aggregate):
248    def __init__(self, name, fields, **kwargs):
249        kwargs.setdefault('passby', PASS_BY_REFERENCE)
250        Aggregate.__init__(self, "struct", name, fields, **kwargs)
251
252    def has_fields(self):
253        return len(self.fields) != 0
254
255class Union(Aggregate):
256    def __init__(self, name, fields, **kwargs):
257        # Generally speaking some intelligence is required to free a
258        # union therefore any specific instance of this class will
259        # need to provide an explicit destructor function.
260        kwargs.setdefault('passby', PASS_BY_REFERENCE)
261        kwargs.setdefault('dispose_fn', None)
262        Aggregate.__init__(self, "union", name, fields, **kwargs)
263
264class KeyedUnion(Aggregate):
265    """A union which is keyed of another variable in the parent structure"""
266    def __init__(self, name, keyvar_type, keyvar_name, fields, **kwargs):
267        Aggregate.__init__(self, "union", name, [], **kwargs)
268
269        if not isinstance(keyvar_type, Enumeration):
270            raise ValueError
271
272        kv_kwargs = dict([(x.lstrip('keyvar_'),y) for (x,y) in kwargs.items() if x.startswith('keyvar_')])
273
274        self.keyvar = Field(keyvar_type, keyvar_name, **kv_kwargs)
275
276        for f in fields:
277            # (name, enum, type)
278            e, ty = f
279            ev = keyvar_type.lookup(e)
280            en = ev.name
281            self.fields.append(Field(ty, e, enumname=en))
282
283#
284# Standard Types
285#
286
287void = Builtin("void *", namespace = None)
288bool = Builtin("bool", namespace = None,
289               copy_fn=None,
290               json_gen_fn = "yajl_gen_bool",
291               json_parse_type = "JSON_BOOL",
292               json_parse_fn = "libxl__bool_parse_json",
293               autogenerate_json = False)
294
295size_t = Number("size_t", namespace = None)
296
297integer = Number("int", namespace = None, signed = True)
298
299uint8 = UInt(8)
300uint16 = UInt(16)
301uint32 = UInt(32)
302uint64 = UInt(64, json_gen_fn = "libxl__uint64_gen_json")
303
304string = Builtin("char *", namespace = None, copy_fn = "libxl_string_copy", dispose_fn = "free",
305                 json_gen_fn = "libxl__string_gen_json",
306                 json_parse_type = "JSON_STRING | JSON_NULL",
307                 json_parse_fn = "libxl__string_parse_json",
308                 autogenerate_json = False,
309                 check_default_fn="libxl__string_is_default")
310
311class Array(Type):
312    """An array of the same type"""
313    def __init__(self, elem_type, lenvar_name, **kwargs):
314        kwargs.setdefault('dispose_fn', 'free')
315        kwargs.setdefault('json_parse_type', 'JSON_ARRAY')
316        Type.__init__(self, namespace=elem_type.namespace, typename=elem_type.rawname + " *", **kwargs)
317
318        lv_kwargs = dict([(x.lstrip('lenvar_'),y) for (x,y) in kwargs.items() if x.startswith('lenvar_')])
319
320        self.lenvar = Field(integer, lenvar_name, **lv_kwargs)
321        self.elem_type = elem_type
322
323class OrderedDict(dict):
324    """A dictionary which remembers insertion order.
325
326       push to back on duplicate insertion"""
327
328    def __init__(self):
329        dict.__init__(self)
330        self.__ordered = []
331
332    def __setitem__(self, key, value):
333        try:
334            self.__ordered.remove(key)
335        except ValueError:
336            pass
337
338        self.__ordered.append(key)
339        dict.__setitem__(self, key, value)
340
341    def ordered_keys(self):
342        return self.__ordered
343    def ordered_values(self):
344        return [self[x] for x in self.__ordered]
345    def ordered_items(self):
346        return [(x,self[x]) for x in self.__ordered]
347
348def parse(f):
349    print >>sys.stderr, "Parsing %s" % f
350
351    globs = {}
352    locs = OrderedDict()
353
354    for n,t in globals().items():
355        if isinstance(t, Type):
356            globs[n] = t
357        elif isinstance(t,type(object)) and issubclass(t, Type):
358            globs[n] = t
359        elif n in ['PASS_BY_REFERENCE', 'PASS_BY_VALUE',
360                   'DIR_NONE', 'DIR_IN', 'DIR_OUT', 'DIR_BOTH',
361                   'namespace', 'hidden']:
362            globs[n] = t
363
364    try:
365        execfile(f, globs, locs)
366    except SyntaxError,e:
367        raise SyntaxError, \
368              "Errors were found at line %d while processing %s:\n\t%s"\
369              %(e.lineno,f,e.text)
370
371    types = [t for t in locs.ordered_values() if isinstance(t,Type)]
372
373    builtins = [t for t in types if isinstance(t,Builtin)]
374    types = [t for t in types if not isinstance(t,Builtin)]
375
376    return (builtins,types)
377