xcbgen: support paramref in the parser
[free-sw/xcb/proto] / xcbgen / expr.py
1 '''
2 This module contains helper classes for structure fields and length expressions.
3 '''
4 class Field(object):
5     '''
6     Represents a field of a structure.
7
8     type is the datatype object for the field.
9     field_type is the name of the type (string tuple)
10     field_name is the name of the structure field.
11     visible is true iff the field should be in the request API.
12     wire is true iff the field should be in the request structure.
13     auto is true iff the field is on the wire but not in the request API (e.g. opcode)
14     enum is the enum name this field refers to, if any.
15     '''
16     def __init__(self, type, field_type, field_name, visible, wire, auto, enum=None, isfd=False):
17         self.type = type
18         self.field_type = field_type
19         self.field_name = field_name
20         self.enum = enum
21         self.visible = visible
22         self.wire = wire
23         self.auto = auto
24         self.isfd = isfd
25         self.parent = None
26
27
28 class Expression(object):
29     '''
30     Represents a mathematical expression for a list length or exprfield.
31
32     Public fields:
33     op is the operation (text +,*,/,<<,~) or None.
34     lhs and rhs are the sub-Expressions if op is set.
35     lenfield_name is the name of the length field, or None for request lists.
36     lenfield is the Field object for the length field, or None.
37     bitfield is True if the length field is a bitmask instead of a number.
38     nmemb is the fixed size (value)of the expression, or None
39     '''
40     def __init__(self, elt, parent):
41         self.parent = parent
42
43         self.nmemb = None
44
45         self.lenfield_name = None
46         self.lenfield_type = None
47         self.lenfield_parent = None
48         self.lenfield = None
49         self.lenwire = False
50         self.bitfield = False
51
52         self.op = None
53         self.lhs = None
54         self.rhs = None
55
56         self.contains_listelement_ref = False
57
58         if elt.tag == 'list':
59             # List going into a request, which has no length field (inferred by server)
60             self.lenfield_name = elt.get('name') + '_len'
61             self.lenfield_type = 'CARD32'
62
63         elif elt.tag == 'fieldref':
64             # Standard list with a fieldref
65             self.lenfield_name = elt.text
66
67         elif elt.tag == 'paramref':
68             self.lenfield_name = elt.text
69             self.lenfield_type = elt.get('type')
70
71         elif elt.tag == 'valueparam':
72             # Value-mask.  The length bitmask is described by attributes.
73             self.lenfield_name = elt.get('value-mask-name')
74             self.lenfield_type = elt.get('value-mask-type')
75             self.lenwire = True
76             self.bitfield = True
77
78         elif elt.tag == 'op':
79             # Op field.  Need to recurse.
80             self.op = elt.get('op')
81             self.lhs = Expression(list(elt)[0], parent)
82             self.rhs = Expression(list(elt)[1], parent)
83
84             # Hopefully we don't have two separate length fields...
85             self.lenfield_name = self.lhs.lenfield_name
86             if self.lenfield_name == None:
87                 self.lenfield_name = self.rhs.lenfield_name
88
89         elif elt.tag == 'unop':
90             # Op field.  Need to recurse.
91             self.op = elt.get('op')
92             self.rhs = Expression(list(elt)[0], parent)
93
94             self.lenfield_name = self.rhs.lenfield_name
95             
96         elif elt.tag == 'value':
97             # Constant expression
98             self.nmemb = int(elt.text, 0)
99
100         elif elt.tag == 'popcount':
101             self.op = 'popcount'
102             self.rhs = Expression(list(elt)[0], parent)
103             self.lenfield_name = self.rhs.lenfield_name
104             # xcb_popcount returns 'int' - handle the type in the language-specific part
105
106         elif elt.tag == 'enumref':
107             self.op = 'enumref'
108             self.lenfield_name = (elt.get('ref'), elt.text)
109             
110         elif elt.tag == 'sumof':
111             self.op = 'sumof'
112             self.lenfield_name = elt.get('ref')
113             subexpressions = list(elt)
114             if len(subexpressions) > 0:
115                 # sumof with a nested expression which is to be evaluated
116                 # for each list-element in the context of that list-element.
117                 # sumof then returns the sum of the results of these evaluations
118                 self.rhs = Expression(subexpressions[0], parent)
119
120         elif elt.tag == 'listelement-ref':
121             # current list element inside iterating expressions such as sumof
122             self.op = 'listelement-ref'
123             self.contains_listelement_ref = True
124
125         else:
126             # Notreached
127             raise Exception("undefined tag '%s'" % elt.tag)
128
129     def fixed_size(self):
130         return self.nmemb != None
131
132     def recursive_resolve_tasks(self, module, parents):
133         for subexpr in (self.lhs, self.rhs):
134             if subexpr != None:
135                 subexpr.recursive_resolve_tasks(module, parents)
136                 self.contains_listelement_ref |= subexpr.contains_listelement_ref
137
138     def resolve(self, module, parents):
139         if self.op == 'enumref':
140             self.lenfield_type = module.get_type(self.lenfield_name[0])
141             self.lenfield_name = self.lenfield_name[1]
142         elif self.op == 'sumof':
143             # need to find the field with lenfield_name
144             for p in reversed(parents): 
145                 fields = dict([(f.field_name, f) for f in p.fields])
146                 if self.lenfield_name in fields.keys():
147                     if p.is_case_or_bitcase:
148                         # switch is the anchestor 
149                         self.lenfield_parent = p.parents[-1]
150                     else:
151                         self.lenfield_parent = p
152                     self.lenfield_type = fields[self.lenfield_name].field_type
153                     break
154
155         self.recursive_resolve_tasks(module, parents)
156