10c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu#!/usr/bin/python
20c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu#
30c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu# Copyright (C) 2009 Chia-I Wu <olv@0xlab.org>
40c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu#
50c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu# Permission is hereby granted, free of charge, to any person obtaining a
60c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu# copy of this software and associated documentation files (the "Software"),
70c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu# to deal in the Software without restriction, including without limitation
80c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu# on the rights to use, copy, modify, merge, publish, distribute, sub
90c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu# license, and/or sell copies of the Software, and to permit persons to whom
100c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu# the Software is furnished to do so, subject to the following conditions:
110c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu#
120c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu# The above copyright notice and this permission notice (including the next
130c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu# paragraph) shall be included in all copies or substantial portions of the
140c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu# Software.
150c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu#
160c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
170c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
180c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
190c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu# IBM AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
200c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
210c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
220c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu# IN THE SOFTWARE.
230c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu"""
240c1a7bbe0d0c6727a432890164032188787e7e26Chia-I WuA parser for APIspec.
250c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu"""
260c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
270c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wuclass SpecError(Exception):
280c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    """Error in the spec file."""
290c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
300c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
310c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wuclass Spec(object):
320c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    """A Spec is an abstraction of the API spec."""
330c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
340c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    def __init__(self, doc):
350c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self.doc = doc
360c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
370c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self.spec_node = doc.getRootElement()
380c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self.tmpl_nodes = {}
390c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self.api_nodes = {}
400c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self.impl_node = None
410c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
420c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        # parse <apispec>
430c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        node = self.spec_node.children
440c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        while node:
450c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            if node.type == "element":
460c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                if node.name == "template":
470c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    self.tmpl_nodes[node.prop("name")] = node
480c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                elif node.name == "api":
490c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    self.api_nodes[node.prop("name")] = node
500c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                else:
510c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    raise SpecError("unexpected node %s in apispec" %
520c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                            node.name)
530c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            node = node.next
540c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
550c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        # find an implementation
560c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        for name, node in self.api_nodes.iteritems():
570c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            if node.prop("implementation") == "true":
580c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                self.impl_node = node
590c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                break
600c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        if not self.impl_node:
610c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            raise SpecError("unable to find an implementation")
620c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
630c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    def get_impl(self):
640c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        """Return the implementation."""
650c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        return API(self, self.impl_node)
660c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
670c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    def get_api(self, name):
680c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        """Return an API."""
690c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        return API(self, self.api_nodes[name])
700c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
710c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
720c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wuclass API(object):
730c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    """An API consists of categories and functions."""
740c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
750c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    def __init__(self, spec, api_node):
760c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self.name = api_node.prop("name")
770c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self.is_impl = (api_node.prop("implementation") == "true")
780c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
790c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self.categories = []
800c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self.functions = []
810c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
820c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        # parse <api>
830c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        func_nodes = []
840c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        node = api_node.children
850c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        while node:
860c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            if node.type == "element":
870c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                if node.name == "category":
880c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    cat = node.prop("name")
890c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    self.categories.append(cat)
900c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                elif node.name == "function":
910c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    func_nodes.append(node)
920c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                else:
930c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    raise SpecError("unexpected node %s in api" % node.name)
940c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            node = node.next
950c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
960c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        # realize functions
970c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        for func_node in func_nodes:
980c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            tmpl_node = spec.tmpl_nodes[func_node.prop("template")]
990c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            try:
1000c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                func = Function(tmpl_node, func_node, self.is_impl,
1010c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                                self.categories)
1020c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            except SpecError, e:
1030c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                func_name = func_node.prop("name")
1040c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                raise SpecError("failed to parse %s: %s" % (func_name, e))
1050c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            self.functions.append(func)
1060c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
1070c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    def match(self, func, conversions={}):
1080c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        """Find a matching function in the API."""
1090c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        match = None
1100c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        need_conv = False
1110c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        for f in self.functions:
1120c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            matched, conv = f.match(func, conversions)
1130c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            if matched:
1140c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                match = f
1150c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                need_conv = conv
1160c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                # exact match
1170c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                if not need_conv:
1180c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    break
1190c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        return (match, need_conv)
1200c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
1210c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
1220c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wuclass Function(object):
1230c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    """Parse and realize a <template> node."""
1240c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
1250c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    def __init__(self, tmpl_node, func_node, force_skip_desc=False, categories=[]):
1260c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self.tmpl_name = tmpl_node.prop("name")
1270c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self.direction = tmpl_node.prop("direction")
1280c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
1290c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self.name = func_node.prop("name")
1300c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self.prefix = func_node.prop("default_prefix")
1310c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self.is_external = (func_node.prop("external") == "true")
1320c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
1330c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        if force_skip_desc:
1340c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            self._skip_desc = True
1350c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        else:
136a2d21f67f901ec34799819ba47b9579ac78bf112Chia-I Wu            self._skip_desc = (func_node.prop("skip_desc") == "true")
1370c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
1380c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self._categories = categories
1390c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
1400c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        # these attributes decide how the template is realized
1410c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self._gltype = func_node.prop("gltype")
1420c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        if func_node.hasProp("vector_size"):
1430c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            self._vector_size = int(func_node.prop("vector_size"))
1440c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        else:
1450c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            self._vector_size = 0
1460c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self._expand_vector = (func_node.prop("expand_vector") == "true")
1470c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
1480c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self.return_type = "void"
1490c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        param_nodes = []
1500c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
1510c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        # find <proto>
1520c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        proto_node = tmpl_node.children
1530c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        while proto_node:
1540c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            if proto_node.type == "element" and proto_node.name == "proto":
1550c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                break
1560c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            proto_node = proto_node.next
1570c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        if not proto_node:
1580c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            raise SpecError("no proto")
1590c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        # and parse it
1600c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        node = proto_node.children
1610c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        while node:
1620c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            if node.type == "element":
1630c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                if node.name == "return":
1640c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    self.return_type = node.prop("type")
1650c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                elif node.name == "param" or node.name == "vector":
1660c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    if self.support_node(node):
1670c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                        # make sure the node is not hidden
1680c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                        if not (self._expand_vector and
1690c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                                (node.prop("hide_if_expanded") == "true")):
1700c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                            param_nodes.append(node)
1710c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                else:
1720c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    raise SpecError("unexpected node %s in proto" % node.name)
1730c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            node = node.next
1740c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
1750c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self._init_params(param_nodes)
1760c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self._init_descs(tmpl_node, param_nodes)
1770c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
1780c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    def __str__(self):
1790c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        return "%s %s%s(%s)" % (self.return_type, self.prefix, self.name,
1800c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                self.param_string(True))
1810c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
1820c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    def _init_params(self, param_nodes):
1830c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        """Parse and initialize parameters."""
1840c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self.params = []
1850c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
1860c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        for param_node in param_nodes:
1870c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            size = self.param_node_size(param_node)
1880c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            # when no expansion, vector is just like param
1890c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            if param_node.name == "param" or not self._expand_vector:
1900c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                param = Parameter(param_node, self._gltype, size)
1910c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                self.params.append(param)
1920c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                continue
1930c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
1940c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            if not size or size > param_node.lsCountNode():
1950c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                raise SpecError("could not expand %s with unknown or "
1960c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                                "mismatch sizes" % param.name)
1970c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
1980c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            # expand the vector
1990c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            expanded_params = []
2000c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            child = param_node.children
2010c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            while child:
2020c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                if (child.type == "element" and child.name == "param" and
2030c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    self.support_node(child)):
2040c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    expanded_params.append(Parameter(child, self._gltype))
2050c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    if len(expanded_params) == size:
2060c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                        break
2070c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                child = child.next
2080c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            # just in case that lsCountNode counts unknown nodes
2090c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            if len(expanded_params) < size:
2100c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                raise SpecError("not enough named parameters")
2110c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
2120c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            self.params.extend(expanded_params)
2130c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
2140c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    def _init_descs(self, tmpl_node, param_nodes):
2150c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        """Parse and initialize parameter descriptions."""
2160c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self.checker = Checker()
2170c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        if self._skip_desc:
2180c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            return
2190c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
2200c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        node = tmpl_node.children
2210c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        while node:
2220c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            if node.type == "element" and node.name == "desc":
2230c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                if self.support_node(node):
2240c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    # parse <desc>
2250c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    desc = Description(node, self._categories)
2260c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    self.checker.add_desc(desc)
2270c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            node = node.next
2280c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
2290c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self.checker.validate(self, param_nodes)
2300c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
2310c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    def support_node(self, node):
2320c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        """Return true if a node is in the supported category."""
2330c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        return (not node.hasProp("category") or
2340c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                node.prop("category") in self._categories)
2350c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
2360c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    def get_param(self, name):
2370c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        """Return the named parameter."""
2380c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        for param in self.params:
2390c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            if param.name == name:
2400c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                return param
2410c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        return None
2420c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
2430c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    def param_node_size(self, param):
2440c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        """Return the size of a vector."""
2450c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        if param.name != "vector":
2460c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            return 0
2470c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
2480c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        size = param.prop("size")
2490c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        if size.isdigit():
2500c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            size = int(size)
2510c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        else:
2520c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            size = 0
2530c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        if not size:
2540c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            size = self._vector_size
2550c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            if not size and self._expand_vector:
2560c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                # return the number of named parameters
2570c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                size = param.lsCountNode()
2580c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        return size
2590c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
2600c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    def param_string(self, declaration):
2610c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        """Return the C code of the parameters."""
2620c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        args = []
2630c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        if declaration:
2640c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            for param in self.params:
2650c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                sep = "" if param.type.endswith("*") else " "
2660c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                args.append("%s%s%s" % (param.type, sep, param.name))
2670c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            if not args:
2680c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                args.append("void")
2690c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        else:
2700c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            for param in self.params:
2710c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                args.append(param.name)
2720c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        return ", ".join(args)
2730c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
2740c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    def match(self, other, conversions={}):
2750c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        """Return true if the functions match, probably with a conversion."""
2760c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        if (self.tmpl_name != other.tmpl_name or
2770c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            self.return_type != other.return_type or
2780c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            len(self.params) != len(other.params)):
2790c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            return (False, False)
2800c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
2810c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        need_conv = False
2820c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        for i in xrange(len(self.params)):
2830c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            src = other.params[i]
2840c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            dst = self.params[i]
2850c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            if (src.is_vector != dst.is_vector or src.size != dst.size):
2860c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                return (False, False)
2870c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            if src.type != dst.type:
2880c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                if dst.base_type() in conversions.get(src.base_type(), []):
2890c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    need_conv = True
2900c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                else:
2910c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    # unable to convert
2920c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    return (False, False)
2930c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
2940c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        return (True, need_conv)
2950c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
2960c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
2970c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wuclass Parameter(object):
2980c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    """A parameter of a function."""
2990c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
3000c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    def __init__(self, param_node, gltype=None, size=0):
3010c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self.is_vector = (param_node.name == "vector")
3020c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
3030c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self.name = param_node.prop("name")
3040c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self.size = size
3050c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
3060c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        type = param_node.prop("type")
3070c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        if gltype:
3080c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            type = type.replace("GLtype", gltype)
3090c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        elif type.find("GLtype") != -1:
3100c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            raise SpecError("parameter %s has unresolved type" % self.name)
3110c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
3120c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self.type = type
3130c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
3140c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    def base_type(self):
3150c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        """Return the base GL type by stripping qualifiers."""
3160c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        return [t for t in self.type.split(" ") if t.startswith("GL")][0]
3170c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
3180c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
3190c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wuclass Checker(object):
3200c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    """A checker is the collection of all descriptions on the same level.
3210c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    Descriptions of the same parameter are concatenated.
3220c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    """
3230c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
3240c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    def __init__(self):
3250c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self.switches = {}
326c3bd85791766e4a6f3896ea724e18640e56c6808Chia-I Wu        self.switch_constants = {}
3270c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
3280c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    def add_desc(self, desc):
3290c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        """Add a description."""
330c3bd85791766e4a6f3896ea724e18640e56c6808Chia-I Wu        # TODO allow index to vary
331c3bd85791766e4a6f3896ea724e18640e56c6808Chia-I Wu        const_attrs = ["index", "error", "convert", "size_str"]
3320c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        if desc.name not in self.switches:
3330c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            self.switches[desc.name] = []
334c3bd85791766e4a6f3896ea724e18640e56c6808Chia-I Wu            self.switch_constants[desc.name] = {}
335c3bd85791766e4a6f3896ea724e18640e56c6808Chia-I Wu            for attr in const_attrs:
336c3bd85791766e4a6f3896ea724e18640e56c6808Chia-I Wu                self.switch_constants[desc.name][attr] = None
337c3bd85791766e4a6f3896ea724e18640e56c6808Chia-I Wu
338c3bd85791766e4a6f3896ea724e18640e56c6808Chia-I Wu        # some attributes, like error code, should be the same for all descs
339c3bd85791766e4a6f3896ea724e18640e56c6808Chia-I Wu        consts = self.switch_constants[desc.name]
340c3bd85791766e4a6f3896ea724e18640e56c6808Chia-I Wu        for attr in const_attrs:
341c3bd85791766e4a6f3896ea724e18640e56c6808Chia-I Wu            if getattr(desc, attr) is not None:
342c3bd85791766e4a6f3896ea724e18640e56c6808Chia-I Wu                if (consts[attr] is not None and
343c3bd85791766e4a6f3896ea724e18640e56c6808Chia-I Wu                    consts[attr] != getattr(desc, attr)):
344c3bd85791766e4a6f3896ea724e18640e56c6808Chia-I Wu                    raise SpecError("mismatch %s for %s" % (attr, desc.name))
345c3bd85791766e4a6f3896ea724e18640e56c6808Chia-I Wu                consts[attr] = getattr(desc, attr)
346c3bd85791766e4a6f3896ea724e18640e56c6808Chia-I Wu
3470c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self.switches[desc.name].append(desc)
3480c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
3490c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    def validate(self, func, param_nodes):
3500c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        """Validate the checker against a function."""
3510c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        tmp = Checker()
3520c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
3530c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        for switch in self.switches.itervalues():
3540c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            valid_descs = []
3550c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            for desc in switch:
3560c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                if desc.validate(func, param_nodes):
3570c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    valid_descs.append(desc)
3580c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            # no possible values
3590c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            if not valid_descs:
3600c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                return False
3610c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            for desc in valid_descs:
3620c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                if not desc._is_noop:
3630c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    tmp.add_desc(desc)
3640c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
3650c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self.switches = tmp.switches
366c3bd85791766e4a6f3896ea724e18640e56c6808Chia-I Wu        self.switch_constants = tmp.switch_constants
3670c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        return True
3680c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
3690c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    def flatten(self, name=None):
3700c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        """Return a flat list of all descriptions of the named parameter."""
3710c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        flat_list = []
3720c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        for switch in self.switches.itervalues():
3730c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            for desc in switch:
3740c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                if not name or desc.name == name:
3750c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    flat_list.append(desc)
3760c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                flat_list.extend(desc.checker.flatten(name))
3770c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        return flat_list
3780c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
3790c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    def always_check(self, name):
3800c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        """Return true if the parameter is checked in all possible pathes."""
3810c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        if name in self.switches:
3820c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            return True
3830c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
3840c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        # a param is always checked if any of the switch always checks it
3850c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        for switch in self.switches.itervalues():
3860c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            # a switch always checks it if all of the descs always check it
3870c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            always = True
3880c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            for desc in switch:
3890c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                if not desc.checker.always_check(name):
3900c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    always = False
3910c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    break
3920c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            if always:
3930c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                return True
3940c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        return False
3950c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
3960c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    def _c_switch(self, name, indent="\t"):
3970c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        """Output C switch-statement for the named parameter, for debug."""
3980c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        switch = self.switches.get(name, [])
3990c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        # make sure there are valid values
4000c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        need_switch = False
4010c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        for desc in switch:
4020c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            if desc.values:
4030c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                need_switch = True
4040c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        if not need_switch:
4050c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            return []
4060c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
4070c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        stmts = []
4080c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        var = switch[0].name
4090c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        if switch[0].index >= 0:
4100c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            var += "[%d]" % switch[0].index
4110c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        stmts.append("switch (%s) { /* assume GLenum */" % var)
4120c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
4130c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        for desc in switch:
4140c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            if desc.values:
4150c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                for val in desc.values:
4160c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    stmts.append("case %s:" % val)
4170c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                for dep_name in desc.checker.switches.iterkeys():
4180c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    dep_stmts = [indent + s for s in desc.checker._c_switch(dep_name, indent)]
4190c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    stmts.extend(dep_stmts)
4200c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                stmts.append(indent + "break;")
4210c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
4220c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        stmts.append("default:")
4230c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        stmts.append(indent + "ON_ERROR(%s);" % switch[0].error);
4240c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        stmts.append(indent + "break;")
4250c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        stmts.append("}")
4260c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
4270c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        return stmts
4280c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
4290c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    def dump(self, indent="\t"):
4300c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        """Dump the descriptions in C code."""
4310c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        stmts = []
4320c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        for name in self.switches.iterkeys():
4330c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            c_switch = self._c_switch(name)
4340c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            print "\n".join(c_switch)
4350c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
4360c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
4370c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wuclass Description(object):
4380c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    """A description desribes a parameter and its relationship with other
4390c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    parameters.
4400c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    """
4410c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
4420c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    def __init__(self, desc_node, categories=[]):
4430c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self._categories = categories
4440c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self._is_noop = False
4450c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
4460c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self.name = desc_node.prop("name")
4470c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self.index = -1
4480c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
4490c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self.error = desc_node.prop("error") or "GL_INVALID_ENUM"
4500c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        # vector_size may be C code
4510c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self.size_str = desc_node.prop("vector_size")
4520c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
4530c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self._has_enum = False
4540c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self.values = []
4550c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        dep_nodes = []
4560c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
4570c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        # parse <desc>
4580c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        valid_names = ["value", "range", "desc"]
4590c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        node = desc_node.children
4600c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        while node:
4610c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            if node.type == "element":
4620c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                if node.name in valid_names:
4630c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    # ignore nodes that require unsupported categories
4640c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    if (node.prop("category") and
4650c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                        node.prop("category") not in self._categories):
4660c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                        node = node.next
4670c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                        continue
4680c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                else:
4690c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    raise SpecError("unexpected node %s in desc" % node.name)
4700c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
4710c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                if node.name == "value":
4720c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    val = node.prop("name")
4730c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    if not self._has_enum and val.startswith("GL_"):
4740c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                        self._has_enum = True
4750c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    self.values.append(val)
4760c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                elif node.name == "range":
4770c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    first = int(node.prop("from"))
4780c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    last = int(node.prop("to"))
4790c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    base = node.prop("base") or ""
4800c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    if not self._has_enum and base.startswith("GL_"):
4810c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                        self._has_enum = True
4820c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    # expand range
4830c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    for i in xrange(first, last + 1):
4840c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                        self.values.append("%s%d" % (base, i))
4850c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                else: # dependent desc
4860c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    dep_nodes.append(node)
4870c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            node = node.next
4880c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
4890c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        # default to convert if there is no enum
4900c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self.convert = not self._has_enum
4910c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        if desc_node.hasProp("convert"):
4920c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            self.convert = (desc_node.prop("convert") == "true")
4930c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
4940c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self._init_deps(dep_nodes)
4950c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
4960c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    def _init_deps(self, dep_nodes):
4970c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        """Parse and initialize dependents."""
4980c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self.checker = Checker()
4990c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
5000c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        for dep_node in dep_nodes:
5010c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            # recursion!
5020c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            dep = Description(dep_node, self._categories)
5030c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            self.checker.add_desc(dep)
5040c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
5050c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    def _search_param_node(self, param_nodes, name=None):
5060c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        """Search the template parameters for the named node."""
5070c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        param_node = None
5080c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        param_index = -1
5090c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
5100c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        if not name:
5110c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            name = self.name
5120c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        for node in param_nodes:
5130c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            if name == node.prop("name"):
5140c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                param_node = node
5150c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            elif node.name == "vector":
5160c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                child = node.children
5170c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                idx = 0
5180c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                while child:
5190c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    if child.type == "element" and child.name == "param":
5200c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                        if name == child.prop("name"):
5210c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                            param_node = node
5220c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                            param_index = idx
5230c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                            break
5240c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                        idx += 1
5250c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    child = child.next
5260c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            if param_node:
5270c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                break
5280c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        return (param_node, param_index)
5290c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
5300c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    def _find_final(self, func, param_nodes):
5310c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        """Find the final parameter."""
5320c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        param = func.get_param(self.name)
5330c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        param_index = -1
5340c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
5350c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        # the described param is not in the final function
5360c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        if not param:
5370c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            # search the template parameters
5380c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            node, index = self._search_param_node(param_nodes)
5390c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            if not node:
5400c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                raise SpecError("invalid desc %s in %s" %
5410c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                        (self.name, func.name))
5420c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
5430c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            # a named parameter of a vector
5440c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            if index >= 0:
5450c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                param = func.get_param(node.prop("name"))
5460c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                param_index = index
5470c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            elif node.name == "vector":
5480c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                # must be an expanded vector, check its size
5490c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                if self.size_str and self.size_str.isdigit():
5500c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    size = int(self.size_str)
5510c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    expanded_size = func.param_node_size(node)
5520c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                    if size != expanded_size:
5530c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                        return (False, None, -1)
5540c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            # otherwise, it is a valid, but no-op, description
5550c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
5560c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        return (True, param, param_index)
5570c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
5580c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    def validate(self, func, param_nodes):
5590c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        """Validate a description against certain function."""
5600c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        if self.checker.switches and not self.values:
5610c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            raise SpecError("no valid values for %s" % self.name)
5620c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
5630c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        valid, param, param_index = self._find_final(func, param_nodes)
5640c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        if not valid:
5650c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            return False
5660c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
5670c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        # the description is valid, but the param is gone
5680c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        # mark it no-op so that it will be skipped
5690c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        if not param:
5700c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            self._is_noop = True
5710c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            return True
5720c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
5730c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        if param.is_vector:
5740c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            # if param was known, this should have been done in __init__
5750c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            if self._has_enum:
5760c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                self.size_str = "1"
5770c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            # size mismatch
5780c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            if (param.size and self.size_str and self.size_str.isdigit() and
5790c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                param.size != int(self.size_str)):
5800c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu                return False
5810c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        elif self.size_str:
5820c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            # only vector accepts vector_size
5830c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            raise SpecError("vector_size is invalid for %s" % param.name)
5840c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
5850c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        if not self.checker.validate(func, param_nodes):
5860c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            return False
5870c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
5880c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        # update the description
5890c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self.name = param.name
5900c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        self.index = param_index
5910c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
5920c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        return True
5930c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
5940c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
5950c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wudef main():
5960c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    import libxml2
5970c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
5980c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    filename = "APIspec.xml"
5990c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    apinames = ["GLES1.1", "GLES2.0"]
6000c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
6010c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    doc = libxml2.readFile(filename, None,
6020c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            libxml2.XML_PARSE_DTDLOAD +
6030c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            libxml2.XML_PARSE_DTDVALID +
6040c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu            libxml2.XML_PARSE_NOBLANKS)
6050c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
6060c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    spec = Spec(doc)
6070c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    impl = spec.get_impl()
6080c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    for apiname in apinames:
6090c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu        spec.get_api(apiname)
6100c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
6110c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    doc.freeDoc()
6120c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
6130c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    print "%s is successfully parsed" % filename
6140c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
6150c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu
6160c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wuif __name__ == "__main__":
6170c1a7bbe0d0c6727a432890164032188787e7e26Chia-I Wu    main()
618