1760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org#!/usr/bin/python
2760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org#
3760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org# Copyright (C) 2009 Chia-I Wu <olv@0xlab.org>
4760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org#
5760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org# Permission is hereby granted, free of charge, to any person obtaining a
6760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org# copy of this software and associated documentation files (the "Software"),
7760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org# to deal in the Software without restriction, including without limitation
8760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org# on the rights to use, copy, modify, merge, publish, distribute, sub
9760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org# license, and/or sell copies of the Software, and to permit persons to whom
10760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org# the Software is furnished to do so, subject to the following conditions:
11760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org#
12760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org# The above copyright notice and this permission notice (including the next
13760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org# paragraph) shall be included in all copies or substantial portions of the
14760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org# Software.
15760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org#
16760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
19760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org# IBM AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org# IN THE SOFTWARE.
23760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org"""
24760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.orgA parser for APIspec.
25760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org"""
26760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
27760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.orgclass SpecError(Exception):
28760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    """Error in the spec file."""
29760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
30760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
31760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.orgclass Spec(object):
32760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    """A Spec is an abstraction of the API spec."""
33760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
34760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    def __init__(self, doc):
35760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self.doc = doc
36760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
37760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self.spec_node = doc.getRootElement()
38760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self.tmpl_nodes = {}
39760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self.api_nodes = {}
40760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self.impl_node = None
41760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
42760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        # parse <apispec>
43760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        node = self.spec_node.children
44760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        while node:
45760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            if node.type == "element":
46760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                if node.name == "template":
47760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    self.tmpl_nodes[node.prop("name")] = node
48760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                elif node.name == "api":
49760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    self.api_nodes[node.prop("name")] = node
50760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                else:
51760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    raise SpecError("unexpected node %s in apispec" %
52760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                            node.name)
53760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            node = node.next
54760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
55760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        # find an implementation
56760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        for name, node in self.api_nodes.iteritems():
57760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            if node.prop("implementation") == "true":
58760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                self.impl_node = node
59760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                break
60760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        if not self.impl_node:
61760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            raise SpecError("unable to find an implementation")
62760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
63760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    def get_impl(self):
64760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        """Return the implementation."""
65760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        return API(self, self.impl_node)
66760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
67760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    def get_api(self, name):
68760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        """Return an API."""
69760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        return API(self, self.api_nodes[name])
70760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
71760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
72760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.orgclass API(object):
73760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    """An API consists of categories and functions."""
74760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
75760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    def __init__(self, spec, api_node):
76760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self.name = api_node.prop("name")
77760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self.is_impl = (api_node.prop("implementation") == "true")
78760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
79760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self.categories = []
80760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self.functions = []
81760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
82760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        # parse <api>
83760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        func_nodes = []
84760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        node = api_node.children
85760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        while node:
86760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            if node.type == "element":
87760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                if node.name == "category":
88760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    cat = node.prop("name")
89760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    self.categories.append(cat)
90760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                elif node.name == "function":
91760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    func_nodes.append(node)
92760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                else:
93760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    raise SpecError("unexpected node %s in api" % node.name)
94760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            node = node.next
95760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
96760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        # realize functions
97760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        for func_node in func_nodes:
98760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            tmpl_node = spec.tmpl_nodes[func_node.prop("template")]
99760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            try:
100760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                func = Function(tmpl_node, func_node, self.is_impl,
101760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                                self.categories)
102760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            except SpecError, e:
103760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                func_name = func_node.prop("name")
104760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                raise SpecError("failed to parse %s: %s" % (func_name, e))
105760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            self.functions.append(func)
106760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
107760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    def match(self, func, conversions={}):
108760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        """Find a matching function in the API."""
109760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        match = None
110760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        need_conv = False
111760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        for f in self.functions:
112760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            matched, conv = f.match(func, conversions)
113760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            if matched:
114760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                match = f
115760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                need_conv = conv
116760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                # exact match
117760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                if not need_conv:
118760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    break
119760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        return (match, need_conv)
120760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
121760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
122760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.orgclass Function(object):
123760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    """Parse and realize a <template> node."""
124760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
125760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    def __init__(self, tmpl_node, func_node, force_skip_desc=False, categories=[]):
126760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self.tmpl_name = tmpl_node.prop("name")
127760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self.direction = tmpl_node.prop("direction")
128760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
129760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self.name = func_node.prop("name")
130760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self.prefix = func_node.prop("default_prefix")
131760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self.is_external = (func_node.prop("external") == "true")
132760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
133760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        if force_skip_desc:
134760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            self._skip_desc = True
135760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        else:
136760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            self._skip_desc = (func_node.prop("skip_desc") == "true")
137760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
138760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self._categories = categories
139760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
140760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        # these attributes decide how the template is realized
141760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self._gltype = func_node.prop("gltype")
142760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        if func_node.hasProp("vector_size"):
143760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            self._vector_size = int(func_node.prop("vector_size"))
144760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        else:
145760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            self._vector_size = 0
146760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self._expand_vector = (func_node.prop("expand_vector") == "true")
147760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
148760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self.return_type = "void"
149760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        param_nodes = []
150760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
151760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        # find <proto>
152760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        proto_node = tmpl_node.children
153760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        while proto_node:
154760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            if proto_node.type == "element" and proto_node.name == "proto":
155760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                break
156760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            proto_node = proto_node.next
157760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        if not proto_node:
158760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            raise SpecError("no proto")
159760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        # and parse it
160760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        node = proto_node.children
161760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        while node:
162760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            if node.type == "element":
163760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                if node.name == "return":
164760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    self.return_type = node.prop("type")
165760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                elif node.name == "param" or node.name == "vector":
166760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    if self.support_node(node):
167760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                        # make sure the node is not hidden
168760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                        if not (self._expand_vector and
169760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                                (node.prop("hide_if_expanded") == "true")):
170760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                            param_nodes.append(node)
171760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                else:
172760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    raise SpecError("unexpected node %s in proto" % node.name)
173760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            node = node.next
174760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
175760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self._init_params(param_nodes)
176760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self._init_descs(tmpl_node, param_nodes)
177760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
178760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    def __str__(self):
179760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        return "%s %s%s(%s)" % (self.return_type, self.prefix, self.name,
180760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                self.param_string(True))
181760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
182760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    def _init_params(self, param_nodes):
183760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        """Parse and initialize parameters."""
184760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self.params = []
185760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
186760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        for param_node in param_nodes:
187760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            size = self.param_node_size(param_node)
188760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            # when no expansion, vector is just like param
189760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            if param_node.name == "param" or not self._expand_vector:
190760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                param = Parameter(param_node, self._gltype, size)
191760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                self.params.append(param)
192760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                continue
193760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
194760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            if not size or size > param_node.lsCountNode():
195760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                raise SpecError("could not expand %s with unknown or "
196760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                                "mismatch sizes" % param.name)
197760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
198760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            # expand the vector
199760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            expanded_params = []
200760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            child = param_node.children
201760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            while child:
202760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                if (child.type == "element" and child.name == "param" and
203760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    self.support_node(child)):
204760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    expanded_params.append(Parameter(child, self._gltype))
205760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    if len(expanded_params) == size:
206760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                        break
207760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                child = child.next
208760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            # just in case that lsCountNode counts unknown nodes
209760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            if len(expanded_params) < size:
210760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                raise SpecError("not enough named parameters")
211760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
212760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            self.params.extend(expanded_params)
213760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
214760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    def _init_descs(self, tmpl_node, param_nodes):
215760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        """Parse and initialize parameter descriptions."""
216760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self.checker = Checker()
217760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        if self._skip_desc:
218760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            return
219760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
220760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        node = tmpl_node.children
221760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        while node:
222760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            if node.type == "element" and node.name == "desc":
223760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                if self.support_node(node):
224760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    # parse <desc>
225760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    desc = Description(node, self._categories)
226760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    self.checker.add_desc(desc)
227760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            node = node.next
228760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
229760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self.checker.validate(self, param_nodes)
230760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
231760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    def support_node(self, node):
232760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        """Return true if a node is in the supported category."""
233760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        return (not node.hasProp("category") or
234760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                node.prop("category") in self._categories)
235760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
236760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    def get_param(self, name):
237760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        """Return the named parameter."""
238760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        for param in self.params:
239760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            if param.name == name:
240760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                return param
241760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        return None
242760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
243760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    def param_node_size(self, param):
244760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        """Return the size of a vector."""
245760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        if param.name != "vector":
246760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            return 0
247760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
248760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        size = param.prop("size")
249760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        if size.isdigit():
250760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            size = int(size)
251760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        else:
252760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            size = 0
253760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        if not size:
254760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            size = self._vector_size
255760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            if not size and self._expand_vector:
256760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                # return the number of named parameters
257760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                size = param.lsCountNode()
258760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        return size
259760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
260760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    def param_string(self, declaration):
261760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        """Return the C code of the parameters."""
262760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        args = []
263760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        if declaration:
264760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            for param in self.params:
265760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                sep = "" if param.type.endswith("*") else " "
266760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                args.append("%s%s%s" % (param.type, sep, param.name))
267760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            if not args:
268760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                args.append("void")
269760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        else:
270760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            for param in self.params:
271760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                args.append(param.name)
272760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        return ", ".join(args)
273760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
274760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    def match(self, other, conversions={}):
275760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        """Return true if the functions match, probably with a conversion."""
276760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        if (self.tmpl_name != other.tmpl_name or
277760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            self.return_type != other.return_type or
278760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            len(self.params) != len(other.params)):
279760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            return (False, False)
280760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
281760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        need_conv = False
282760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        for i in xrange(len(self.params)):
283760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            src = other.params[i]
284760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            dst = self.params[i]
285760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            if (src.is_vector != dst.is_vector or src.size != dst.size):
286760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                return (False, False)
287760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            if src.type != dst.type:
288760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                if dst.base_type() in conversions.get(src.base_type(), []):
289760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    need_conv = True
290760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                else:
291760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    # unable to convert
292760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    return (False, False)
293760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
294760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        return (True, need_conv)
295760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
296760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
297760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.orgclass Parameter(object):
298760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    """A parameter of a function."""
299760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
300760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    def __init__(self, param_node, gltype=None, size=0):
301760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self.is_vector = (param_node.name == "vector")
302760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
303760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self.name = param_node.prop("name")
304760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self.size = size
305760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
306760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        type = param_node.prop("type")
307760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        if gltype:
308760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            type = type.replace("GLtype", gltype)
309760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        elif type.find("GLtype") != -1:
310760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            raise SpecError("parameter %s has unresolved type" % self.name)
311760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
312760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self.type = type
313760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
314760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    def base_type(self):
315760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        """Return the base GL type by stripping qualifiers."""
316760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        return [t for t in self.type.split(" ") if t.startswith("GL")][0]
317760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
318760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
319760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.orgclass Checker(object):
320760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    """A checker is the collection of all descriptions on the same level.
321760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    Descriptions of the same parameter are concatenated.
322760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    """
323760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
324760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    def __init__(self):
325760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self.switches = {}
326760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self.switch_constants = {}
327760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
328760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    def add_desc(self, desc):
329760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        """Add a description."""
330760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        # TODO allow index to vary
331760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        const_attrs = ["index", "error", "convert", "size_str"]
332760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        if desc.name not in self.switches:
333760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            self.switches[desc.name] = []
334760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            self.switch_constants[desc.name] = {}
335760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            for attr in const_attrs:
336760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                self.switch_constants[desc.name][attr] = None
337760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
338760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        # some attributes, like error code, should be the same for all descs
339760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        consts = self.switch_constants[desc.name]
340760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        for attr in const_attrs:
341760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            if getattr(desc, attr) is not None:
342760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                if (consts[attr] is not None and
343760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    consts[attr] != getattr(desc, attr)):
344760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    raise SpecError("mismatch %s for %s" % (attr, desc.name))
345760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                consts[attr] = getattr(desc, attr)
346760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
347760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self.switches[desc.name].append(desc)
348760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
349760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    def validate(self, func, param_nodes):
350760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        """Validate the checker against a function."""
351760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        tmp = Checker()
352760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
353760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        for switch in self.switches.itervalues():
354760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            valid_descs = []
355760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            for desc in switch:
356760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                if desc.validate(func, param_nodes):
357760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    valid_descs.append(desc)
358760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            # no possible values
359760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            if not valid_descs:
360760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                return False
361760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            for desc in valid_descs:
362760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                if not desc._is_noop:
363760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    tmp.add_desc(desc)
364760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
365760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self.switches = tmp.switches
366760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self.switch_constants = tmp.switch_constants
367760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        return True
368760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
369760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    def flatten(self, name=None):
370760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        """Return a flat list of all descriptions of the named parameter."""
371760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        flat_list = []
372760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        for switch in self.switches.itervalues():
373760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            for desc in switch:
374760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                if not name or desc.name == name:
375760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    flat_list.append(desc)
376760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                flat_list.extend(desc.checker.flatten(name))
377760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        return flat_list
378760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
379760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    def always_check(self, name):
380760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        """Return true if the parameter is checked in all possible pathes."""
381760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        if name in self.switches:
382760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            return True
383760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
384760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        # a param is always checked if any of the switch always checks it
385760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        for switch in self.switches.itervalues():
386760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            # a switch always checks it if all of the descs always check it
387760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            always = True
388760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            for desc in switch:
389760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                if not desc.checker.always_check(name):
390760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    always = False
391760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    break
392760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            if always:
393760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                return True
394760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        return False
395760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
396760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    def _c_switch(self, name, indent="\t"):
397760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        """Output C switch-statement for the named parameter, for debug."""
398760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        switch = self.switches.get(name, [])
399760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        # make sure there are valid values
400760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        need_switch = False
401760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        for desc in switch:
402760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            if desc.values:
403760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                need_switch = True
404760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        if not need_switch:
405760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            return []
406760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
407760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        stmts = []
408760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        var = switch[0].name
409760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        if switch[0].index >= 0:
410760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            var += "[%d]" % switch[0].index
411760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        stmts.append("switch (%s) { /* assume GLenum */" % var)
412760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
413760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        for desc in switch:
414760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            if desc.values:
415760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                for val in desc.values:
416760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    stmts.append("case %s:" % val)
417760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                for dep_name in desc.checker.switches.iterkeys():
418760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    dep_stmts = [indent + s for s in desc.checker._c_switch(dep_name, indent)]
419760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    stmts.extend(dep_stmts)
420760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                stmts.append(indent + "break;")
421760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
422760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        stmts.append("default:")
423760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        stmts.append(indent + "ON_ERROR(%s);" % switch[0].error);
424760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        stmts.append(indent + "break;")
425760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        stmts.append("}")
426760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
427760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        return stmts
428760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
429760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    def dump(self, indent="\t"):
430760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        """Dump the descriptions in C code."""
431760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        stmts = []
432760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        for name in self.switches.iterkeys():
433760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            c_switch = self._c_switch(name)
434760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            print "\n".join(c_switch)
435760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
436760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
437760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.orgclass Description(object):
438760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    """A description desribes a parameter and its relationship with other
439760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    parameters.
440760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    """
441760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
442760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    def __init__(self, desc_node, categories=[]):
443760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self._categories = categories
444760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self._is_noop = False
445760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
446760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self.name = desc_node.prop("name")
447760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self.index = -1
448760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
449760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self.error = desc_node.prop("error") or "GL_INVALID_ENUM"
450760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        # vector_size may be C code
451760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self.size_str = desc_node.prop("vector_size")
452760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
453760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self._has_enum = False
454760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self.values = []
455760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        dep_nodes = []
456760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
457760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        # parse <desc>
458760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        valid_names = ["value", "range", "desc"]
459760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        node = desc_node.children
460760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        while node:
461760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            if node.type == "element":
462760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                if node.name in valid_names:
463760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    # ignore nodes that require unsupported categories
464760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    if (node.prop("category") and
465760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                        node.prop("category") not in self._categories):
466760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                        node = node.next
467760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                        continue
468760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                else:
469760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    raise SpecError("unexpected node %s in desc" % node.name)
470760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
471760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                if node.name == "value":
472760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    val = node.prop("name")
473760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    if not self._has_enum and val.startswith("GL_"):
474760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                        self._has_enum = True
475760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    self.values.append(val)
476760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                elif node.name == "range":
477760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    first = int(node.prop("from"))
478760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    last = int(node.prop("to"))
479760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    base = node.prop("base") or ""
480760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    if not self._has_enum and base.startswith("GL_"):
481760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                        self._has_enum = True
482760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    # expand range
483760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    for i in xrange(first, last + 1):
484760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                        self.values.append("%s%d" % (base, i))
485760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                else: # dependent desc
486760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    dep_nodes.append(node)
487760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            node = node.next
488760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
489760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        # default to convert if there is no enum
490760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self.convert = not self._has_enum
491760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        if desc_node.hasProp("convert"):
492760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            self.convert = (desc_node.prop("convert") == "true")
493760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
494760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self._init_deps(dep_nodes)
495760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
496760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    def _init_deps(self, dep_nodes):
497760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        """Parse and initialize dependents."""
498760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self.checker = Checker()
499760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
500760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        for dep_node in dep_nodes:
501760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            # recursion!
502760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            dep = Description(dep_node, self._categories)
503760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            self.checker.add_desc(dep)
504760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
505760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    def _search_param_node(self, param_nodes, name=None):
506760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        """Search the template parameters for the named node."""
507760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        param_node = None
508760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        param_index = -1
509760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
510760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        if not name:
511760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            name = self.name
512760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        for node in param_nodes:
513760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            if name == node.prop("name"):
514760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                param_node = node
515760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            elif node.name == "vector":
516760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                child = node.children
517760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                idx = 0
518760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                while child:
519760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    if child.type == "element" and child.name == "param":
520760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                        if name == child.prop("name"):
521760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                            param_node = node
522760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                            param_index = idx
523760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                            break
524760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                        idx += 1
525760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    child = child.next
526760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            if param_node:
527760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                break
528760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        return (param_node, param_index)
529760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
530760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    def _find_final(self, func, param_nodes):
531760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        """Find the final parameter."""
532760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        param = func.get_param(self.name)
533760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        param_index = -1
534760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
535760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        # the described param is not in the final function
536760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        if not param:
537760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            # search the template parameters
538760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            node, index = self._search_param_node(param_nodes)
539760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            if not node:
540760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                raise SpecError("invalid desc %s in %s" %
541760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                        (self.name, func.name))
542760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
543760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            # a named parameter of a vector
544760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            if index >= 0:
545760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                param = func.get_param(node.prop("name"))
546760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                param_index = index
547760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            elif node.name == "vector":
548760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                # must be an expanded vector, check its size
549760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                if self.size_str and self.size_str.isdigit():
550760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    size = int(self.size_str)
551760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    expanded_size = func.param_node_size(node)
552760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                    if size != expanded_size:
553760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                        return (False, None, -1)
554760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            # otherwise, it is a valid, but no-op, description
555760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
556760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        return (True, param, param_index)
557760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
558760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    def validate(self, func, param_nodes):
559760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        """Validate a description against certain function."""
560760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        if self.checker.switches and not self.values:
561760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            raise SpecError("no valid values for %s" % self.name)
562760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
563760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        valid, param, param_index = self._find_final(func, param_nodes)
564760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        if not valid:
565760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            return False
566760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
567760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        # the description is valid, but the param is gone
568760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        # mark it no-op so that it will be skipped
569760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        if not param:
570760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            self._is_noop = True
571760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            return True
572760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
573760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        if param.is_vector:
574760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            # if param was known, this should have been done in __init__
575760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            if self._has_enum:
576760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                self.size_str = "1"
577760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            # size mismatch
578760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            if (param.size and self.size_str and self.size_str.isdigit() and
579760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                param.size != int(self.size_str)):
580760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                return False
581760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        elif self.size_str:
582760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            # only vector accepts vector_size
583760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            raise SpecError("vector_size is invalid for %s" % param.name)
584760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
585760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        if not self.checker.validate(func, param_nodes):
586760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            return False
587760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
588760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        # update the description
589760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self.name = param.name
590760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        self.index = param_index
591760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
592760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        return True
593760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
594760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
595760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.orgdef main():
596760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    import libxml2
597760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
598760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    filename = "APIspec.xml"
599760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    apinames = ["GLES1.1", "GLES2.0"]
600760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
601760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    doc = libxml2.readFile(filename, None,
602760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            libxml2.XML_PARSE_DTDLOAD +
603760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            libxml2.XML_PARSE_DTDVALID +
604760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org            libxml2.XML_PARSE_NOBLANKS)
605760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
606760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    spec = Spec(doc)
607760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    impl = spec.get_impl()
608760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    for apiname in apinames:
609760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org        spec.get_api(apiname)
610760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
611760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    doc.freeDoc()
612760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
613760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    print "%s is successfully parsed" % filename
614760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
615760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
616760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.orgif __name__ == "__main__":
617760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    main()
618