1#!/usr/bin/python
2#
3# Copyright (C) 2009 Chia-I Wu <olv@0xlab.org>
4#
5# Permission is hereby granted, free of charge, to any person obtaining a
6# copy of this software and associated documentation files (the "Software"),
7# to deal in the Software without restriction, including without limitation
8# on the rights to use, copy, modify, merge, publish, distribute, sub
9# license, and/or sell copies of the Software, and to permit persons to whom
10# the Software is furnished to do so, subject to the following conditions:
11#
12# The above copyright notice and this permission notice (including the next
13# paragraph) shall be included in all copies or substantial portions of the
14# Software.
15#
16# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
19# IBM AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22# IN THE SOFTWARE.
23"""
24Minimal apiutil.py interface for use by es_generator.py.
25"""
26
27import sys
28import libxml2
29
30import APIspec
31
32__spec = {}
33__functions = {}
34__aliases = {}
35
36def _ParseXML(filename, apiname):
37    conversions = {
38        # from           to
39        'GLfloat':  [ 'GLdouble' ],
40        'GLclampf': [ 'GLclampd' ],
41        'GLubyte':  [ 'GLfloat', 'GLdouble' ],
42        'GLint':    [ 'GLfloat', 'GLdouble' ],
43        'GLfixed':  [ 'GLfloat', 'GLdouble' ],
44        'GLclampx': [ 'GLclampf', 'GLclampd' ],
45    }
46
47    doc = libxml2.readFile(filename, None,
48            libxml2.XML_PARSE_DTDLOAD +
49            libxml2.XML_PARSE_DTDVALID +
50            libxml2.XML_PARSE_NOBLANKS)
51    spec = APIspec.Spec(doc)
52    impl = spec.get_impl()
53    api = spec.get_api(apiname)
54    doc.freeDoc()
55
56    __spec["impl"] = impl
57    __spec["api"] = api
58
59    for func in api.functions:
60        alias, need_conv = impl.match(func, conversions)
61        if not alias:
62            # external functions are manually dispatched
63            if not func.is_external:
64                print >>sys.stderr, "Error: unable to dispatch %s" % func.name
65            alias = func
66            need_conv = False
67
68        __functions[func.name] = func
69        __aliases[func.name] = (alias, need_conv)
70
71
72def AllSpecials(notused=None):
73    """Return a list of all external functions in the API."""
74    api = __spec["api"]
75
76    specials = []
77    for func in api.functions:
78        if func.is_external:
79            specials.append(func.name)
80
81    return specials
82
83
84def GetAllFunctions(filename, api):
85    """Return sorted list of all functions in the API."""
86    if not __spec:
87        _ParseXML(filename, api)
88
89    api = __spec["api"]
90    names = []
91    for func in api.functions:
92        names.append(func.name)
93    names.sort()
94    return names
95
96
97def ReturnType(funcname):
98    """Return the C return type of named function."""
99    func = __functions[funcname]
100    return func.return_type
101
102
103def Properties(funcname):
104    """Return list of properties of the named GL function."""
105    func = __functions[funcname]
106    return [func.direction]
107
108
109def _ValidValues(func, param):
110    """Return the valid values of a parameter."""
111    valid_values = []
112    switch = func.checker.switches.get(param.name, [])
113    for desc in switch:
114        # no dependent vector
115        if not desc.checker.switches:
116            for val in desc.values:
117                valid_values.append((val, None, None, [], desc.error, None))
118            continue
119
120        items = desc.checker.switches.items()
121        if len(items) > 1:
122            print >>sys.stderr, "%s: more than one parameter depend on %s" % \
123                    (func.name, desc.name)
124        dep_name, dep_switch = items[0]
125
126        for dep_desc in dep_switch:
127            if dep_desc.index >= 0 and dep_desc.index != 0:
128                print >>sys.stderr, "%s: not first element of a vector" % func.name
129            if dep_desc.checker.switches:
130                print >>sys.stderr, "%s: deep nested dependence" % func.name
131
132            convert = None if dep_desc.convert else "noconvert"
133            for val in desc.values:
134                valid_values.append((val, dep_desc.size_str, dep_desc.name,
135                                     dep_desc.values, dep_desc.error, convert))
136    return valid_values
137
138
139def _Conversion(func, src_param):
140    """Return the destination type of the conversion, or None."""
141    alias, need_conv = __aliases[func.name]
142    if need_conv:
143        dst_param = alias.get_param(src_param.name)
144        if src_param.type == dst_param.type:
145            need_conv = False
146    if not need_conv:
147        return (None, "none")
148
149    converts = { True: 0, False: 0 }
150
151    # In Fogx, for example,  pname may be GL_FOG_DENSITY/GL_FOG_START/GL_FOG_END
152    # or GL_FOG_MODE.  In the former three cases, param is not checked and the
153    # default is to convert.
154    if not func.checker.always_check(src_param.name):
155        converts[True] += 1
156
157    for desc in func.checker.flatten(src_param.name):
158        converts[desc.convert] += 1
159        if converts[True] and converts[False]:
160            break
161
162    # it should be "never", "sometimes", and "always"...
163    if converts[False]:
164        if converts[True]:
165            conversion = "some"
166        else:
167            conversion = "none"
168    else:
169        conversion = "all"
170
171    return (dst_param.base_type(), conversion)
172
173
174def _MaxVecSize(func, param):
175    """Return the largest possible size of a vector."""
176    if not param.is_vector:
177        return 0
178    if param.size:
179        return param.size
180
181    # need to look at all descriptions
182    size = 0
183    for desc in func.checker.flatten(param.name):
184        if desc.size_str and desc.size_str.isdigit():
185            s = int(desc.size_str)
186            if s > size:
187                size = s
188    if not size:
189        need_conv = __aliases[func.name][1]
190        if need_conv:
191            print >>sys.stderr, \
192                    "Error: unable to dicide the max size of %s in %s" % \
193                    (param.name, func.name)
194    return size
195
196
197def _ParameterTuple(func, param):
198    """Return a parameter tuple.
199
200    [0] -- parameter name
201    [1] -- parameter type
202    [2] -- max vector size or 0
203    [3] -- dest type the parameter converts to, or None
204    [4] -- valid values
205    [5] -- how often does the conversion happen
206
207    """
208    vec_size = _MaxVecSize(func, param)
209    dst_type, conversion = _Conversion(func, param)
210    valid_values = _ValidValues(func, param)
211
212    return (param.name, param.type, vec_size, dst_type, valid_values, conversion)
213
214
215def Parameters(funcname):
216    """Return list of tuples of function parameters."""
217    func = __functions[funcname]
218    params = []
219    for param in func.params:
220        params.append(_ParameterTuple(func, param))
221
222    return params
223
224
225def FunctionPrefix(funcname):
226    """Return function specific prefix."""
227    func = __functions[funcname]
228
229    return func.prefix
230
231
232def FindParamIndex(params, paramname):
233    """Find the index of a named parameter."""
234    for i in xrange(len(params)):
235        if params[i][0] == paramname:
236            return i
237    return None
238
239
240def MakeDeclarationString(params):
241    """Return a C-style parameter declaration string."""
242    string = []
243    for p in params:
244        sep = "" if p[1].endswith("*") else " "
245        string.append("%s%s%s" % (p[1], sep, p[0]))
246    if not string:
247        return "void"
248    return ", ".join(string)
249
250
251def AliasPrefix(funcname):
252    """Return the prefix of the function the named function is an alias of."""
253    alias = __aliases[funcname][0]
254    return alias.prefix
255
256
257def Alias(funcname):
258    """Return the name of the function the named function is an alias of."""
259    alias, need_conv = __aliases[funcname]
260    return alias.name if not need_conv else None
261
262
263def ConversionFunction(funcname):
264    """Return the name of the function the named function converts to."""
265    alias, need_conv = __aliases[funcname]
266    return alias.name if need_conv else None
267
268
269def Categories(funcname):
270    """Return all the categories of the named GL function."""
271    api = __spec["api"]
272    return [api.name]
273