Skip to content

Commit

Permalink
Add Python parser language-independent parts.
Browse files Browse the repository at this point in the history
  • Loading branch information
Eamon Walsh authored and Eamon F Walsh committed Apr 19, 2008
1 parent b15d25b commit 7820273
Show file tree
Hide file tree
Showing 10 changed files with 859 additions and 2 deletions.
2 changes: 1 addition & 1 deletion Makefile.am
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
SUBDIRS=src
SUBDIRS = src xcbgen

pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = xcb-proto.pc
Expand Down
13 changes: 13 additions & 0 deletions README
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,19 @@ the XML-XCB protocol descriptions, client-side support for a new
extension requires only an XML description of the extension, and not a
single line of code.

Python libraries: xcb-proto also contains language-independent Python
libraries that are used to parse an XML description and create objects
used by Python code generators in individual language bindings. These
libraries are installed into $(prefix)/lib/pythonX.X/site-packages. If
this location is not on your system's Python path, scripts that import
them will fail with import errors. In this case you must add the
install location to your Python path by creating a file with a `.pth'
extension in a directory that _is_ on the Python path, and put the
path to the install location in that file. For example, on my system
there is a file named 'local.pth' in /usr/lib/python2.5/site-packages,
which contains '/usr/local/lib/python2.5/site-packages'. Note that
this is only necessary on machines where XCB is being built.

Please report any issues you find to the freedesktop.org bug tracker,
at:

Expand Down
4 changes: 3 additions & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ if test "$XMLLINT" = "no"; then
AC_MSG_WARN([xmllint not found; unable to validate against schema.])
fi

AM_PATH_PYTHON([2.5])

xcbincludedir='${datadir}/xcb'
AC_SUBST(xcbincludedir)

AC_OUTPUT([Makefile src/Makefile xcb-proto.pc])
AC_OUTPUT([Makefile src/Makefile xcbgen/Makefile xcb-proto.pc])
3 changes: 3 additions & 0 deletions xcbgen/Makefile.am
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pkgpythondir = $(pythondir)/xcbgen

pkgpython_PYTHON = __init__.py error.py expr.py matcher.py state.py types.py
1 change: 1 addition & 0 deletions xcbgen/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

5 changes: 5 additions & 0 deletions xcbgen/error.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class ResolveException(Exception):
'''
Gets thrown when a type doesn't resolve in the XML.
'''
pass
88 changes: 88 additions & 0 deletions xcbgen/expr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
'''
This module contains helper classes for structure fields and length expressions.
'''
class Field(object):
'''
Represents a field of a structure.
type is the datatype object for the field.
field_type is the name of the type (string tuple)
field_name is the name of the structure field.
visible is true iff the field should be in the request API.
wire is true iff the field should be in the request structure.
auto is true iff the field is on the wire but not in the request API (e.g. opcode)
'''
def __init__(self, type, field_type, field_name, visible, wire, auto):
self.type = type
self.field_type = field_type
self.field_name = field_name
self.visible = visible
self.wire = wire
self.auto = auto


class Expression(object):
'''
Represents a mathematical expression for a list length or exprfield.
Public fields:
op is the operation (text +,*,/,<<) or None.
lhs and rhs are the sub-Expressions if op is set.
lenfield_name is the name of the length field, or None for request lists.
lenfield is the Field object for the length field, or None.
bitfield is True if the length field is a bitmask instead of a number.
nmemb is the fixed size (value)of the expression, or None
'''
def __init__(self, elt, parent):
self.parent = parent

self.nmemb = None

self.lenfield_name = None
self.lenfield_type = None
self.lenfield = None
self.lenwire = False
self.bitfield = False

self.op = None
self.lhs = None
self.rhs = None

if elt.tag == 'list':
# List going into a request, which has no length field (inferred by server)
self.lenfield_name = elt.get('name') + '_len'
self.lenfield_type = 'CARD32'

elif elt.tag == 'fieldref':
# Standard list with a fieldref
self.lenfield_name = elt.text

elif elt.tag == 'valueparam':
# Value-mask. The length bitmask is described by attributes.
self.lenfield_name = elt.get('value-mask-name')
self.lenfield_type = elt.get('value-mask-type')
self.lenwire = True
self.bitfield = True

elif elt.tag == 'op':
# Op field. Need to recurse.
self.op = elt.get('op')
self.lhs = Expression(list(elt)[0], parent)
self.rhs = Expression(list(elt)[1], parent)

# Hopefully we don't have two separate length fields...
self.lenfield_name = self.lhs.lenfield_name
if self.lenfield_name == None:
self.lenfield_name = self.rhs.lenfield_name

elif elt.tag == 'value':
# Constant expression
self.nmemb = int(elt.text)

else:
# Notreached
raise Exception('XXX')


def fixed_size(self):
return self.nmemb != None
112 changes: 112 additions & 0 deletions xcbgen/matcher.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
'''
XML parser. One function for each top-level element in the schema.
Most functions just declare a new object and add it to the module.
For typedefs, eventcopies, xidtypes, and other aliases though,
we do not create a new type object, we just record the existing one under a new name.
'''

from os.path import join
from xml.etree.cElementTree import parse

import state
from types import *

def import_(node, module, namespace):
'''
For imports, we load the file, create a new namespace object,
execute recursively, then record the import (for header files, etc.)
'''
new_file = join(namespace.dir, '%s.xml' % node.text)
new_root = parse(new_file).getroot()
new_namespace = state.Namespace(new_file)
execute(module, new_namespace)
if not module.has_import(node.text):
module.add_import(node.text, new_namespace)

def typedef(node, module, namespace):
id = node.get('newname')
name = namespace.prefix + (id,)
type = module.get_type(node.get('oldname'))
module.add_type(id, namespace.ns, name, type)

def xidtype(node, module, namespace):
id = node.get('name')
name = namespace.prefix + (id,)
type = module.get_type('CARD32')
module.add_type(id, namespace.ns, name, type)

def xidunion(node, module, namespace):
id = node.get('name')
name = namespace.prefix + (id,)
type = module.get_type('CARD32')
module.add_type(id, namespace.ns, name, type)

def enum(node, module, namespace):
id = node.get('name')
name = namespace.prefix + (id,)
type = Enum(name, node)
module.add_type(id, namespace.ns, name, type)

def struct(node, module, namespace):
id = node.get('name')
name = namespace.prefix + (id,)
type = Struct(name, node)
module.add_type(id, namespace.ns, name, type)

def union(node, module, namespace):
id = node.get('name')
name = namespace.prefix + (id,)
type = Union(name, node)
module.add_type(id, namespace.ns, name, type)

def request(node, module, namespace):
id = node.get('name')
name = namespace.prefix + (id,)
type = Request(name, node)
module.add_request(id, name, type)

def event(node, module, namespace):
id = node.get('name')
name = namespace.prefix + (id,)
event = Event(name, node)
event.add_opcode(node.get('number'), name, True)
module.add_event(id, name, event)

def eventcopy(node, module, namespace):
id = node.get('name')
name = namespace.prefix + (id,)
event = module.get_event(node.get('ref'))
event.add_opcode(node.get('number'), name, False)
module.add_event(id, name, event)

def error(node, module, namespace):
id = node.get('name')
name = namespace.prefix + (id,)
error = Error(name, node)
error.add_opcode(node.get('number'), name, True)
module.add_error(id, name, error)

def errorcopy(node, module, namespace):
id = node.get('name')
name = namespace.prefix + (id,)
error = module.get_error(node.get('ref'))
error.add_opcode(node.get('number'), name, False)
module.add_error(id, name, error)

funcs = {'import' : import_,
'typedef' : typedef,
'xidtype' : xidtype,
'xidunion' : xidunion,
'enum' : enum,
'struct' : struct,
'union' : union,
'request' : request,
'event' : event,
'eventcopy' : eventcopy,
'error' : error,
'errorcopy' : errorcopy}

def execute(module, namespace):
for elt in list(namespace.root):
funcs[elt.tag](elt, module, namespace)
Loading

0 comments on commit 7820273

Please sign in to comment.