mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2026-05-06 05:08:08 +02:00
mesa/es: Add APIspec.xml and its parser.
APIspec.xml is based on APIspec.txt. The new format has less code duplications and should be easier to read. Signed-off-by: Chia-I Wu <olvaffe@gmail.com>
This commit is contained in:
parent
9f38553018
commit
0c1a7bbe0d
4 changed files with 5320 additions and 0 deletions
52
src/mesa/es/main/APIspec.dtd
Normal file
52
src/mesa/es/main/APIspec.dtd
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
<!ELEMENT apispec (template|api)+>
|
||||
|
||||
<!ELEMENT api (category*, function*)>
|
||||
<!ELEMENT category EMPTY>
|
||||
<!ELEMENT function EMPTY>
|
||||
|
||||
<!ELEMENT template (proto, desc*)>
|
||||
<!ELEMENT proto (return, (param|vector)*)>
|
||||
<!ELEMENT return EMPTY>
|
||||
<!ELEMENT param EMPTY>
|
||||
<!ELEMENT vector (param*)>
|
||||
<!ELEMENT desc ((value|range)*, desc*)>
|
||||
<!ELEMENT value EMPTY>
|
||||
<!ELEMENT range EMPTY>
|
||||
|
||||
<!ATTLIST api name NMTOKEN #REQUIRED
|
||||
implementation (true | false) "false">
|
||||
<!ATTLIST category name NMTOKEN #REQUIRED>
|
||||
<!ATTLIST function name NMTOKEN #REQUIRED
|
||||
default_prefix NMTOKEN "_mesa_"
|
||||
external (true | false) "false"
|
||||
template NMTOKEN #REQUIRED
|
||||
gltype CDATA #IMPLIED
|
||||
vector_size NMTOKEN #IMPLIED
|
||||
expand_vector (true | false) "false"
|
||||
skip_desc (true | false) "false">
|
||||
|
||||
<!ATTLIST template name NMTOKEN #REQUIRED
|
||||
direction (set | get) "set">
|
||||
|
||||
<!ATTLIST return type CDATA #REQUIRED>
|
||||
<!ATTLIST param name NMTOKEN #REQUIRED
|
||||
type CDATA #REQUIRED
|
||||
hide_if_expanded (true | false) "false"
|
||||
category NMTOKEN #IMPLIED>
|
||||
<!ATTLIST vector name NMTOKEN #REQUIRED
|
||||
type CDATA #REQUIRED
|
||||
size NMTOKEN #REQUIRED
|
||||
category NMTOKEN #IMPLIED>
|
||||
|
||||
<!ATTLIST desc name NMTOKEN #REQUIRED
|
||||
vector_size CDATA #IMPLIED
|
||||
convert (true | false) #IMPLIED
|
||||
error NMTOKEN "GL_INVALID_ENUM"
|
||||
category NMTOKEN #IMPLIED>
|
||||
|
||||
<!ATTLIST value name CDATA #REQUIRED
|
||||
category NMTOKEN #IMPLIED>
|
||||
<!ATTLIST range from NMTOKEN #REQUIRED
|
||||
to NMTOKEN #REQUIRED
|
||||
base NMTOKEN #IMPLIED
|
||||
category NMTOKEN #IMPLIED>
|
||||
601
src/mesa/es/main/APIspec.py
Normal file
601
src/mesa/es/main/APIspec.py
Normal file
|
|
@ -0,0 +1,601 @@
|
|||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (C) 2009 Chia-I Wu <olv@0xlab.org>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a
|
||||
# copy of this software and associated documentation files (the "Software"),
|
||||
# to deal in the Software without restriction, including without limitation
|
||||
# on the rights to use, copy, modify, merge, publish, distribute, sub
|
||||
# license, and/or sell copies of the Software, and to permit persons to whom
|
||||
# the Software is furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice (including the next
|
||||
# paragraph) shall be included in all copies or substantial portions of the
|
||||
# Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
# IBM AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
# IN THE SOFTWARE.
|
||||
"""
|
||||
A parser for APIspec.
|
||||
"""
|
||||
|
||||
class SpecError(Exception):
|
||||
"""Error in the spec file."""
|
||||
|
||||
|
||||
class Spec(object):
|
||||
"""A Spec is an abstraction of the API spec."""
|
||||
|
||||
def __init__(self, doc):
|
||||
self.doc = doc
|
||||
|
||||
self.spec_node = doc.getRootElement()
|
||||
self.tmpl_nodes = {}
|
||||
self.api_nodes = {}
|
||||
self.impl_node = None
|
||||
|
||||
# parse <apispec>
|
||||
node = self.spec_node.children
|
||||
while node:
|
||||
if node.type == "element":
|
||||
if node.name == "template":
|
||||
self.tmpl_nodes[node.prop("name")] = node
|
||||
elif node.name == "api":
|
||||
self.api_nodes[node.prop("name")] = node
|
||||
else:
|
||||
raise SpecError("unexpected node %s in apispec" %
|
||||
node.name)
|
||||
node = node.next
|
||||
|
||||
# find an implementation
|
||||
for name, node in self.api_nodes.iteritems():
|
||||
if node.prop("implementation") == "true":
|
||||
self.impl_node = node
|
||||
break
|
||||
if not self.impl_node:
|
||||
raise SpecError("unable to find an implementation")
|
||||
|
||||
def get_impl(self):
|
||||
"""Return the implementation."""
|
||||
return API(self, self.impl_node)
|
||||
|
||||
def get_api(self, name):
|
||||
"""Return an API."""
|
||||
return API(self, self.api_nodes[name])
|
||||
|
||||
|
||||
class API(object):
|
||||
"""An API consists of categories and functions."""
|
||||
|
||||
def __init__(self, spec, api_node):
|
||||
self.name = api_node.prop("name")
|
||||
self.is_impl = (api_node.prop("implementation") == "true")
|
||||
|
||||
self.categories = []
|
||||
self.functions = []
|
||||
|
||||
# parse <api>
|
||||
func_nodes = []
|
||||
node = api_node.children
|
||||
while node:
|
||||
if node.type == "element":
|
||||
if node.name == "category":
|
||||
cat = node.prop("name")
|
||||
self.categories.append(cat)
|
||||
elif node.name == "function":
|
||||
func_nodes.append(node)
|
||||
else:
|
||||
raise SpecError("unexpected node %s in api" % node.name)
|
||||
node = node.next
|
||||
|
||||
# realize functions
|
||||
for func_node in func_nodes:
|
||||
tmpl_node = spec.tmpl_nodes[func_node.prop("template")]
|
||||
try:
|
||||
func = Function(tmpl_node, func_node, self.is_impl,
|
||||
self.categories)
|
||||
except SpecError, e:
|
||||
func_name = func_node.prop("name")
|
||||
raise SpecError("failed to parse %s: %s" % (func_name, e))
|
||||
self.functions.append(func)
|
||||
|
||||
def match(self, func, conversions={}):
|
||||
"""Find a matching function in the API."""
|
||||
match = None
|
||||
need_conv = False
|
||||
for f in self.functions:
|
||||
matched, conv = f.match(func, conversions)
|
||||
if matched:
|
||||
match = f
|
||||
need_conv = conv
|
||||
# exact match
|
||||
if not need_conv:
|
||||
break
|
||||
return (match, need_conv)
|
||||
|
||||
|
||||
class Function(object):
|
||||
"""Parse and realize a <template> node."""
|
||||
|
||||
def __init__(self, tmpl_node, func_node, force_skip_desc=False, categories=[]):
|
||||
self.tmpl_name = tmpl_node.prop("name")
|
||||
self.direction = tmpl_node.prop("direction")
|
||||
|
||||
self.name = func_node.prop("name")
|
||||
self.prefix = func_node.prop("default_prefix")
|
||||
self.is_external = (func_node.prop("external") == "true")
|
||||
|
||||
if force_skip_desc:
|
||||
self._skip_desc = True
|
||||
else:
|
||||
self._skip_desc = (self.is_external or func_node.prop("skip_desc") == "true")
|
||||
|
||||
self._categories = categories
|
||||
|
||||
# these attributes decide how the template is realized
|
||||
self._gltype = func_node.prop("gltype")
|
||||
if func_node.hasProp("vector_size"):
|
||||
self._vector_size = int(func_node.prop("vector_size"))
|
||||
else:
|
||||
self._vector_size = 0
|
||||
self._expand_vector = (func_node.prop("expand_vector") == "true")
|
||||
|
||||
self.return_type = "void"
|
||||
param_nodes = []
|
||||
|
||||
# find <proto>
|
||||
proto_node = tmpl_node.children
|
||||
while proto_node:
|
||||
if proto_node.type == "element" and proto_node.name == "proto":
|
||||
break
|
||||
proto_node = proto_node.next
|
||||
if not proto_node:
|
||||
raise SpecError("no proto")
|
||||
# and parse it
|
||||
node = proto_node.children
|
||||
while node:
|
||||
if node.type == "element":
|
||||
if node.name == "return":
|
||||
self.return_type = node.prop("type")
|
||||
elif node.name == "param" or node.name == "vector":
|
||||
if self.support_node(node):
|
||||
# make sure the node is not hidden
|
||||
if not (self._expand_vector and
|
||||
(node.prop("hide_if_expanded") == "true")):
|
||||
param_nodes.append(node)
|
||||
else:
|
||||
raise SpecError("unexpected node %s in proto" % node.name)
|
||||
node = node.next
|
||||
|
||||
self._init_params(param_nodes)
|
||||
self._init_descs(tmpl_node, param_nodes)
|
||||
|
||||
def __str__(self):
|
||||
return "%s %s%s(%s)" % (self.return_type, self.prefix, self.name,
|
||||
self.param_string(True))
|
||||
|
||||
def _init_params(self, param_nodes):
|
||||
"""Parse and initialize parameters."""
|
||||
self.params = []
|
||||
|
||||
for param_node in param_nodes:
|
||||
size = self.param_node_size(param_node)
|
||||
# when no expansion, vector is just like param
|
||||
if param_node.name == "param" or not self._expand_vector:
|
||||
param = Parameter(param_node, self._gltype, size)
|
||||
self.params.append(param)
|
||||
continue
|
||||
|
||||
if not size or size > param_node.lsCountNode():
|
||||
raise SpecError("could not expand %s with unknown or "
|
||||
"mismatch sizes" % param.name)
|
||||
|
||||
# expand the vector
|
||||
expanded_params = []
|
||||
child = param_node.children
|
||||
while child:
|
||||
if (child.type == "element" and child.name == "param" and
|
||||
self.support_node(child)):
|
||||
expanded_params.append(Parameter(child, self._gltype))
|
||||
if len(expanded_params) == size:
|
||||
break
|
||||
child = child.next
|
||||
# just in case that lsCountNode counts unknown nodes
|
||||
if len(expanded_params) < size:
|
||||
raise SpecError("not enough named parameters")
|
||||
|
||||
self.params.extend(expanded_params)
|
||||
|
||||
def _init_descs(self, tmpl_node, param_nodes):
|
||||
"""Parse and initialize parameter descriptions."""
|
||||
self.checker = Checker()
|
||||
if self._skip_desc:
|
||||
return
|
||||
|
||||
node = tmpl_node.children
|
||||
while node:
|
||||
if node.type == "element" and node.name == "desc":
|
||||
if self.support_node(node):
|
||||
# parse <desc>
|
||||
desc = Description(node, self._categories)
|
||||
self.checker.add_desc(desc)
|
||||
node = node.next
|
||||
|
||||
self.checker.validate(self, param_nodes)
|
||||
|
||||
def support_node(self, node):
|
||||
"""Return true if a node is in the supported category."""
|
||||
return (not node.hasProp("category") or
|
||||
node.prop("category") in self._categories)
|
||||
|
||||
def get_param(self, name):
|
||||
"""Return the named parameter."""
|
||||
for param in self.params:
|
||||
if param.name == name:
|
||||
return param
|
||||
return None
|
||||
|
||||
def param_node_size(self, param):
|
||||
"""Return the size of a vector."""
|
||||
if param.name != "vector":
|
||||
return 0
|
||||
|
||||
size = param.prop("size")
|
||||
if size.isdigit():
|
||||
size = int(size)
|
||||
else:
|
||||
size = 0
|
||||
if not size:
|
||||
size = self._vector_size
|
||||
if not size and self._expand_vector:
|
||||
# return the number of named parameters
|
||||
size = param.lsCountNode()
|
||||
return size
|
||||
|
||||
def param_string(self, declaration):
|
||||
"""Return the C code of the parameters."""
|
||||
args = []
|
||||
if declaration:
|
||||
for param in self.params:
|
||||
sep = "" if param.type.endswith("*") else " "
|
||||
args.append("%s%s%s" % (param.type, sep, param.name))
|
||||
if not args:
|
||||
args.append("void")
|
||||
else:
|
||||
for param in self.params:
|
||||
args.append(param.name)
|
||||
return ", ".join(args)
|
||||
|
||||
def match(self, other, conversions={}):
|
||||
"""Return true if the functions match, probably with a conversion."""
|
||||
if (self.tmpl_name != other.tmpl_name or
|
||||
self.return_type != other.return_type or
|
||||
len(self.params) != len(other.params)):
|
||||
return (False, False)
|
||||
|
||||
need_conv = False
|
||||
for i in xrange(len(self.params)):
|
||||
src = other.params[i]
|
||||
dst = self.params[i]
|
||||
if (src.is_vector != dst.is_vector or src.size != dst.size):
|
||||
return (False, False)
|
||||
if src.type != dst.type:
|
||||
if dst.base_type() in conversions.get(src.base_type(), []):
|
||||
need_conv = True
|
||||
else:
|
||||
# unable to convert
|
||||
return (False, False)
|
||||
|
||||
return (True, need_conv)
|
||||
|
||||
|
||||
class Parameter(object):
|
||||
"""A parameter of a function."""
|
||||
|
||||
def __init__(self, param_node, gltype=None, size=0):
|
||||
self.is_vector = (param_node.name == "vector")
|
||||
|
||||
self.name = param_node.prop("name")
|
||||
self.size = size
|
||||
|
||||
type = param_node.prop("type")
|
||||
if gltype:
|
||||
type = type.replace("GLtype", gltype)
|
||||
elif type.find("GLtype") != -1:
|
||||
raise SpecError("parameter %s has unresolved type" % self.name)
|
||||
|
||||
self.type = type
|
||||
|
||||
def base_type(self):
|
||||
"""Return the base GL type by stripping qualifiers."""
|
||||
return [t for t in self.type.split(" ") if t.startswith("GL")][0]
|
||||
|
||||
|
||||
class Checker(object):
|
||||
"""A checker is the collection of all descriptions on the same level.
|
||||
Descriptions of the same parameter are concatenated.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.switches = {}
|
||||
|
||||
def add_desc(self, desc):
|
||||
"""Add a description."""
|
||||
# TODO take index into consideration
|
||||
if desc.name not in self.switches:
|
||||
self.switches[desc.name] = []
|
||||
self.switches[desc.name].append(desc)
|
||||
|
||||
def validate(self, func, param_nodes):
|
||||
"""Validate the checker against a function."""
|
||||
tmp = Checker()
|
||||
|
||||
for switch in self.switches.itervalues():
|
||||
valid_descs = []
|
||||
for desc in switch:
|
||||
if desc.validate(func, param_nodes):
|
||||
valid_descs.append(desc)
|
||||
# no possible values
|
||||
if not valid_descs:
|
||||
return False
|
||||
for desc in valid_descs:
|
||||
if not desc._is_noop:
|
||||
tmp.add_desc(desc)
|
||||
|
||||
self.switches = tmp.switches
|
||||
return True
|
||||
|
||||
def flatten(self, name=None):
|
||||
"""Return a flat list of all descriptions of the named parameter."""
|
||||
flat_list = []
|
||||
for switch in self.switches.itervalues():
|
||||
for desc in switch:
|
||||
if not name or desc.name == name:
|
||||
flat_list.append(desc)
|
||||
flat_list.extend(desc.checker.flatten(name))
|
||||
return flat_list
|
||||
|
||||
def always_check(self, name):
|
||||
"""Return true if the parameter is checked in all possible pathes."""
|
||||
if name in self.switches:
|
||||
return True
|
||||
|
||||
# a param is always checked if any of the switch always checks it
|
||||
for switch in self.switches.itervalues():
|
||||
# a switch always checks it if all of the descs always check it
|
||||
always = True
|
||||
for desc in switch:
|
||||
if not desc.checker.always_check(name):
|
||||
always = False
|
||||
break
|
||||
if always:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _c_switch(self, name, indent="\t"):
|
||||
"""Output C switch-statement for the named parameter, for debug."""
|
||||
switch = self.switches.get(name, [])
|
||||
# make sure there are valid values
|
||||
need_switch = False
|
||||
for desc in switch:
|
||||
if desc.values:
|
||||
need_switch = True
|
||||
if not need_switch:
|
||||
return []
|
||||
|
||||
stmts = []
|
||||
var = switch[0].name
|
||||
if switch[0].index >= 0:
|
||||
var += "[%d]" % switch[0].index
|
||||
stmts.append("switch (%s) { /* assume GLenum */" % var)
|
||||
|
||||
for desc in switch:
|
||||
if desc.values:
|
||||
for val in desc.values:
|
||||
stmts.append("case %s:" % val)
|
||||
for dep_name in desc.checker.switches.iterkeys():
|
||||
dep_stmts = [indent + s for s in desc.checker._c_switch(dep_name, indent)]
|
||||
stmts.extend(dep_stmts)
|
||||
stmts.append(indent + "break;")
|
||||
|
||||
stmts.append("default:")
|
||||
stmts.append(indent + "ON_ERROR(%s);" % switch[0].error);
|
||||
stmts.append(indent + "break;")
|
||||
stmts.append("}")
|
||||
|
||||
return stmts
|
||||
|
||||
def dump(self, indent="\t"):
|
||||
"""Dump the descriptions in C code."""
|
||||
stmts = []
|
||||
for name in self.switches.iterkeys():
|
||||
c_switch = self._c_switch(name)
|
||||
print "\n".join(c_switch)
|
||||
|
||||
|
||||
class Description(object):
|
||||
"""A description desribes a parameter and its relationship with other
|
||||
parameters.
|
||||
"""
|
||||
|
||||
def __init__(self, desc_node, categories=[]):
|
||||
self._categories = categories
|
||||
self._is_noop = False
|
||||
|
||||
self.name = desc_node.prop("name")
|
||||
self.index = -1
|
||||
|
||||
self.error = desc_node.prop("error") or "GL_INVALID_ENUM"
|
||||
# vector_size may be C code
|
||||
self.size_str = desc_node.prop("vector_size")
|
||||
|
||||
self._has_enum = False
|
||||
self.values = []
|
||||
dep_nodes = []
|
||||
|
||||
# parse <desc>
|
||||
valid_names = ["value", "range", "desc"]
|
||||
node = desc_node.children
|
||||
while node:
|
||||
if node.type == "element":
|
||||
if node.name in valid_names:
|
||||
# ignore nodes that require unsupported categories
|
||||
if (node.prop("category") and
|
||||
node.prop("category") not in self._categories):
|
||||
node = node.next
|
||||
continue
|
||||
else:
|
||||
raise SpecError("unexpected node %s in desc" % node.name)
|
||||
|
||||
if node.name == "value":
|
||||
val = node.prop("name")
|
||||
if not self._has_enum and val.startswith("GL_"):
|
||||
self._has_enum = True
|
||||
self.values.append(val)
|
||||
elif node.name == "range":
|
||||
first = int(node.prop("from"))
|
||||
last = int(node.prop("to"))
|
||||
base = node.prop("base") or ""
|
||||
if not self._has_enum and base.startswith("GL_"):
|
||||
self._has_enum = True
|
||||
# expand range
|
||||
for i in xrange(first, last + 1):
|
||||
self.values.append("%s%d" % (base, i))
|
||||
else: # dependent desc
|
||||
dep_nodes.append(node)
|
||||
node = node.next
|
||||
|
||||
# default to convert if there is no enum
|
||||
self.convert = not self._has_enum
|
||||
if desc_node.hasProp("convert"):
|
||||
self.convert = (desc_node.prop("convert") == "true")
|
||||
|
||||
self._init_deps(dep_nodes)
|
||||
|
||||
def _init_deps(self, dep_nodes):
|
||||
"""Parse and initialize dependents."""
|
||||
self.checker = Checker()
|
||||
|
||||
for dep_node in dep_nodes:
|
||||
# recursion!
|
||||
dep = Description(dep_node, self._categories)
|
||||
self.checker.add_desc(dep)
|
||||
|
||||
def _search_param_node(self, param_nodes, name=None):
|
||||
"""Search the template parameters for the named node."""
|
||||
param_node = None
|
||||
param_index = -1
|
||||
|
||||
if not name:
|
||||
name = self.name
|
||||
for node in param_nodes:
|
||||
if name == node.prop("name"):
|
||||
param_node = node
|
||||
elif node.name == "vector":
|
||||
child = node.children
|
||||
idx = 0
|
||||
while child:
|
||||
if child.type == "element" and child.name == "param":
|
||||
if name == child.prop("name"):
|
||||
param_node = node
|
||||
param_index = idx
|
||||
break
|
||||
idx += 1
|
||||
child = child.next
|
||||
if param_node:
|
||||
break
|
||||
return (param_node, param_index)
|
||||
|
||||
def _find_final(self, func, param_nodes):
|
||||
"""Find the final parameter."""
|
||||
param = func.get_param(self.name)
|
||||
param_index = -1
|
||||
|
||||
# the described param is not in the final function
|
||||
if not param:
|
||||
# search the template parameters
|
||||
node, index = self._search_param_node(param_nodes)
|
||||
if not node:
|
||||
raise SpecError("invalid desc %s in %s" %
|
||||
(self.name, func.name))
|
||||
|
||||
# a named parameter of a vector
|
||||
if index >= 0:
|
||||
param = func.get_param(node.prop("name"))
|
||||
param_index = index
|
||||
elif node.name == "vector":
|
||||
# must be an expanded vector, check its size
|
||||
if self.size_str and self.size_str.isdigit():
|
||||
size = int(self.size_str)
|
||||
expanded_size = func.param_node_size(node)
|
||||
if size != expanded_size:
|
||||
return (False, None, -1)
|
||||
# otherwise, it is a valid, but no-op, description
|
||||
|
||||
return (True, param, param_index)
|
||||
|
||||
def validate(self, func, param_nodes):
|
||||
"""Validate a description against certain function."""
|
||||
if self.checker.switches and not self.values:
|
||||
raise SpecError("no valid values for %s" % self.name)
|
||||
|
||||
valid, param, param_index = self._find_final(func, param_nodes)
|
||||
if not valid:
|
||||
return False
|
||||
|
||||
# the description is valid, but the param is gone
|
||||
# mark it no-op so that it will be skipped
|
||||
if not param:
|
||||
self._is_noop = True
|
||||
return True
|
||||
|
||||
if param.is_vector:
|
||||
# if param was known, this should have been done in __init__
|
||||
if self._has_enum:
|
||||
self.size_str = "1"
|
||||
# size mismatch
|
||||
if (param.size and self.size_str and self.size_str.isdigit() and
|
||||
param.size != int(self.size_str)):
|
||||
return False
|
||||
elif self.size_str:
|
||||
# only vector accepts vector_size
|
||||
raise SpecError("vector_size is invalid for %s" % param.name)
|
||||
|
||||
if not self.checker.validate(func, param_nodes):
|
||||
return False
|
||||
|
||||
# update the description
|
||||
self.name = param.name
|
||||
self.index = param_index
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def main():
|
||||
import libxml2
|
||||
|
||||
filename = "APIspec.xml"
|
||||
apinames = ["GLES1.1", "GLES2.0"]
|
||||
|
||||
doc = libxml2.readFile(filename, None,
|
||||
libxml2.XML_PARSE_DTDLOAD +
|
||||
libxml2.XML_PARSE_DTDVALID +
|
||||
libxml2.XML_PARSE_NOBLANKS)
|
||||
|
||||
spec = Spec(doc)
|
||||
impl = spec.get_impl()
|
||||
for apiname in apinames:
|
||||
spec.get_api(apiname)
|
||||
|
||||
doc.freeDoc()
|
||||
|
||||
print "%s is successfully parsed" % filename
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
4405
src/mesa/es/main/APIspec.xml
Normal file
4405
src/mesa/es/main/APIspec.xml
Normal file
File diff suppressed because it is too large
Load diff
262
src/mesa/es/main/APIspecutil.py
Normal file
262
src/mesa/es/main/APIspecutil.py
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (C) 2009 Chia-I Wu <olv@0xlab.org>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a
|
||||
# copy of this software and associated documentation files (the "Software"),
|
||||
# to deal in the Software without restriction, including without limitation
|
||||
# on the rights to use, copy, modify, merge, publish, distribute, sub
|
||||
# license, and/or sell copies of the Software, and to permit persons to whom
|
||||
# the Software is furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice (including the next
|
||||
# paragraph) shall be included in all copies or substantial portions of the
|
||||
# Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
# IBM AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
# IN THE SOFTWARE.
|
||||
"""
|
||||
Minimal apiutil.py interface for use by es_generator.py.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import libxml2
|
||||
|
||||
import APIspec
|
||||
|
||||
__spec = {}
|
||||
__functions = {}
|
||||
__aliases = {}
|
||||
|
||||
def _ParseXML(filename, apiname):
|
||||
conversions = {
|
||||
# from to
|
||||
'GLfloat': [ 'GLdouble' ],
|
||||
'GLclampf': [ 'GLclampd' ],
|
||||
'GLubyte': [ 'GLfloat', 'GLdouble' ],
|
||||
'GLint': [ 'GLfloat', 'GLdouble' ],
|
||||
'GLfixed': [ 'GLfloat', 'GLdouble' ],
|
||||
'GLclampx': [ 'GLclampf', 'GLclampd' ],
|
||||
}
|
||||
|
||||
doc = libxml2.readFile(filename, None,
|
||||
libxml2.XML_PARSE_DTDLOAD +
|
||||
libxml2.XML_PARSE_DTDVALID +
|
||||
libxml2.XML_PARSE_NOBLANKS)
|
||||
spec = APIspec.Spec(doc)
|
||||
impl = spec.get_impl()
|
||||
api = spec.get_api(apiname)
|
||||
doc.freeDoc()
|
||||
|
||||
__spec["impl"] = impl
|
||||
__spec["api"] = api
|
||||
|
||||
for func in api.functions:
|
||||
alias, need_conv = impl.match(func, conversions)
|
||||
if not alias:
|
||||
print >>sys.stderr, "Error: unable to dispatch %s" % func.name
|
||||
alias = func
|
||||
|
||||
__functions[func.name] = func
|
||||
__aliases[func.name] = (alias, need_conv)
|
||||
|
||||
|
||||
def AllSpecials(notused=None):
|
||||
"""Return a list of all external functions in the API."""
|
||||
api = __spec["api"]
|
||||
|
||||
specials = []
|
||||
for func in api.functions:
|
||||
if func.is_external:
|
||||
specials.append(func.name)
|
||||
|
||||
return specials
|
||||
|
||||
|
||||
def GetAllFunctions(filename, api):
|
||||
"""Return sorted list of all functions in the API."""
|
||||
if not __spec:
|
||||
_ParseXML(filename, api)
|
||||
|
||||
api = __spec["api"]
|
||||
names = []
|
||||
for func in api.functions:
|
||||
names.append(func.name)
|
||||
names.sort()
|
||||
return names
|
||||
|
||||
|
||||
def ReturnType(funcname):
|
||||
"""Return the C return type of named function."""
|
||||
func = __functions[funcname]
|
||||
return func.return_type
|
||||
|
||||
|
||||
def Properties(funcname):
|
||||
"""Return list of properties of the named GL function."""
|
||||
func = __functions[funcname]
|
||||
return [func.direction]
|
||||
|
||||
|
||||
def _ValidValues(func, param):
|
||||
"""Return the valid values of a parameter."""
|
||||
valid_values = []
|
||||
switch = func.checker.switches.get(param.name, [])
|
||||
for desc in switch:
|
||||
# no dependent vector
|
||||
if not desc.checker.switches:
|
||||
for val in desc.values:
|
||||
valid_values.append((val, None, None, [], desc.error, None))
|
||||
continue
|
||||
|
||||
items = desc.checker.switches.items()
|
||||
if len(items) > 1:
|
||||
print >>sys.stderr, "%s: more than one parameter depend on %s" % \
|
||||
(func.name, desc.name)
|
||||
dep_name, dep_switch = items[0]
|
||||
|
||||
for dep_desc in dep_switch:
|
||||
if dep_desc.index >= 0 and dep_desc.index != 0:
|
||||
print >>sys.stderr, "%s: not first element of a vector" % func.name
|
||||
if dep_desc.checker.switches:
|
||||
print >>sys.stderr, "%s: deep nested dependence" % func.name
|
||||
|
||||
convert = None if dep_desc.convert else "noconvert"
|
||||
for val in desc.values:
|
||||
valid_values.append((val, dep_desc.size_str, dep_desc.name,
|
||||
dep_desc.values, dep_desc.error, convert))
|
||||
return valid_values
|
||||
|
||||
|
||||
def _Conversion(func, src_param):
|
||||
"""Return the destination type of the conversion, or None."""
|
||||
alias, need_conv = __aliases[func.name]
|
||||
if need_conv:
|
||||
dst_param = alias.get_param(src_param.name)
|
||||
if src_param.type == dst_param.type:
|
||||
need_conv = False
|
||||
if not need_conv:
|
||||
return (None, "none")
|
||||
|
||||
converts = { True: 0, False: 0 }
|
||||
|
||||
# In Fogx, for example, pname may be GL_FOG_DENSITY/GL_FOG_START/GL_FOG_END
|
||||
# or GL_FOG_MODE. In the former three cases, param is not checked and the
|
||||
# default is to convert.
|
||||
if not func.checker.always_check(src_param.name):
|
||||
converts[True] += 1
|
||||
|
||||
for desc in func.checker.flatten(src_param.name):
|
||||
converts[desc.convert] += 1
|
||||
if converts[True] and converts[False]:
|
||||
break
|
||||
|
||||
# it should be "never", "sometimes", and "always"...
|
||||
if converts[False]:
|
||||
if converts[True]:
|
||||
conversion = "some"
|
||||
else:
|
||||
conversion = "none"
|
||||
else:
|
||||
conversion = "all"
|
||||
|
||||
return (dst_param.base_type(), conversion)
|
||||
|
||||
|
||||
def _MaxVecSize(func, param):
|
||||
"""Return the largest possible size of a vector."""
|
||||
if not param.is_vector:
|
||||
return 0
|
||||
if param.size:
|
||||
return param.size
|
||||
|
||||
# need to look at all descriptions
|
||||
size = 0
|
||||
for desc in func.checker.flatten(param.name):
|
||||
if desc.size_str and desc.size_str.isdigit():
|
||||
s = int(desc.size_str)
|
||||
if s > size:
|
||||
size = s
|
||||
if not size:
|
||||
need_conv = __aliases[func.name][1]
|
||||
if need_conv:
|
||||
print >>sys.stderr, \
|
||||
"Error: unable to dicide the max size of %s in %s" % \
|
||||
(param.name, func.name)
|
||||
return size
|
||||
|
||||
|
||||
def _ParameterTuple(func, param):
|
||||
"""Return a parameter tuple.
|
||||
|
||||
[0] -- parameter name
|
||||
[1] -- parameter type
|
||||
[2] -- max vector size or 0
|
||||
[3] -- dest type the parameter converts to, or None
|
||||
[4] -- valid values
|
||||
[5] -- how often does the conversion happen
|
||||
|
||||
"""
|
||||
vec_size = _MaxVecSize(func, param)
|
||||
dst_type, conversion = _Conversion(func, param)
|
||||
valid_values = _ValidValues(func, param)
|
||||
|
||||
return (param.name, param.type, vec_size, dst_type, valid_values, conversion)
|
||||
|
||||
|
||||
def Parameters(funcname):
|
||||
"""Return list of tuples of function parameters."""
|
||||
func = __functions[funcname]
|
||||
params = []
|
||||
for param in func.params:
|
||||
params.append(_ParameterTuple(func, param))
|
||||
|
||||
return params
|
||||
|
||||
|
||||
def FindParamIndex(params, paramname):
|
||||
"""Find the index of a named parameter."""
|
||||
for i in xrange(len(params)):
|
||||
if params[i][0] == paramname:
|
||||
return i
|
||||
return None
|
||||
|
||||
|
||||
def MakeDeclarationString(params):
|
||||
"""Return a C-style parameter declaration string."""
|
||||
string = []
|
||||
for p in params:
|
||||
sep = "" if p[1].endswith("*") else " "
|
||||
string.append("%s%s%s" % (p[1], sep, p[0]))
|
||||
if not string:
|
||||
return "void"
|
||||
return ", ".join(string)
|
||||
|
||||
|
||||
def AliasPrefix(funcname):
|
||||
"""Return the prefix of the function the named function is an alias of."""
|
||||
alias = __aliases[funcname][0]
|
||||
return alias.prefix
|
||||
|
||||
|
||||
def Alias(funcname):
|
||||
"""Return the name of the function the named function is an alias of."""
|
||||
alias, need_conv = __aliases[funcname]
|
||||
return alias.name if not need_conv else None
|
||||
|
||||
|
||||
def ConversionFunction(funcname):
|
||||
"""Return the name of the function the named function converts to."""
|
||||
alias, need_conv = __aliases[funcname]
|
||||
return alias.name if need_conv else None
|
||||
|
||||
|
||||
def Categories(funcname):
|
||||
"""Return all the categories of the named GL function."""
|
||||
api = __spec["api"]
|
||||
return [api.name]
|
||||
Loading…
Add table
Reference in a new issue