generator: expressions can generate pre-code
authorChristian Linhart <chris@DemoRecorder.com>
Sun, 2 Nov 2014 12:45:29 +0000 (13:45 +0100)
committerChristian Linhart <chris@demorecorder.com>
Mon, 3 Nov 2014 10:23:16 +0000 (11:23 +0100)
This patch provides a mechanism for generating
preparatory code for expressions.

This is e.g. necessary when an expression needs computations
which cannot be done in a C-Expression, like for-loops.

This will be used for sumof expressions but may be useful
elsewhere.

Note: Patch 2 of this series has been removed during the review process.

V2: adapt to changes in previous patches

V3: some style and formatting changes according to suggestions from Ran Benita.

Signed-off-by: Christian Linhart <chris@DemoRecorder.com>
Reviewed-by: Ran Benita <ran234@gmail.com>
Message-ID: <54562769.3090405@DemoRecorder.com>
Patch-Thread-Subject: [Xcb] [PATCHSET] ListInputDevices revision 2
Patch-Set: ListInputDevices
Patch-Number: libxcb 3/9
Patch-Version: V3

src/c_client.py

index ac5b6dc..789b49e 100644 (file)
@@ -58,6 +58,107 @@ def _hc(fmt, *args):
     _h(fmt, *args)
     _c(fmt, *args)
 
+def _c_wr_stringlist(indent, strlist):
+    '''
+    Writes the given list of strings to the source file.
+    Each line is prepended by the indent string
+    '''
+    for str in strlist:
+        _c("%s%s", indent, str)
+
+
+class PreCode(object):
+    '''
+    For pre-code generated by expression generation
+    (for example, the for-loop of a sumof)
+    This has to account for recursiveness of the expression
+    generation, i.e., there may be pre-code for pre-code.
+    Therefore this is implemented as a stack of lists of lines.
+
+    If redirection is switched on, then all output is collected in
+    self.redirect_code and self.redirect_tempvars instead of
+    being sent to the output via _h und _c.
+    '''
+    def __init__(self):
+        self.nesting_level = 0
+        self.tempvars = []
+        self.codelines = []
+        self.redirect_code = None
+        self.redirect_tempvars = None
+        self.indent_str = '    '
+        self.indent_stack = []
+        self.tempvar_num = 0
+
+
+    # start and end of pre-code blocks
+    def start(self):
+        self.nesting_level += 1
+
+    def end(self):
+        self.nesting_level -= 1
+        if self.nesting_level == 0:
+            # lowest pre-code level is finished -> output to source
+            if self.redirect_tempvars is None:
+                _c_wr_stringlist('', self.tempvars)
+                self.tempvars = []
+            else:
+                self.redirect_tempvars.extend(self.tempvars)
+                self.tempvars = []
+            if self.redirect_code == None:
+                _c_wr_stringlist('', self.codelines)
+                self.codelines = []
+            else:
+                self.redirect_code.extend(self.codelines)
+                self.codelines = []
+
+
+    def output_tempvars(self):
+        if self.redirect_code == None:
+            _c_wr_stringlist('', self.tempvars)
+            self.tempvars = []
+
+    # output to precode
+    def code(self, fmt, *args):
+        self.codelines.append(self.indent_str + fmt % args)
+
+    def tempvar(self, fmt, *args):
+        self.tempvars.append('    ' + (fmt % args))
+
+    # get a unique name for a temporary variable
+    def get_tempvarname(self):
+        self.tempvar_num += 1
+        return "xcb_pre_tmp_%d" % self.tempvar_num
+
+    # indentation
+
+    def push_indent(self, indentstr):
+        self.indent_stack.append(self.indent_str)
+        self.indent_str = indentstr
+
+    def push_addindent(self, indent_add_str):
+        self.push_indent(self.indent_str + indent_add_str)
+
+    def indent(self):
+        self.push_addindent('    ')
+
+    def pop_indent(self):
+        self.indent_str = self.indent_stack.pop()
+
+    # redirection to lists
+    def redirect_start(self, redirect_code, redirect_tempvars=None):
+        self.redirect_code = redirect_code
+        self.redirect_tempvars = redirect_tempvars
+        if redirect_tempvars is not None:
+            self.tempvar_num = 0
+
+    def redirect_end(self):
+        self.redirect_code = None
+        self.redirect_tempvars = None
+
+# global PreCode handler
+_c_pre = PreCode()
+
+
 # XXX See if this level thing is really necessary.
 def _h_setlevel(idx):
     '''
@@ -1024,6 +1125,8 @@ def _c_serialize_helper_fields(context, self,
     need_padding = False
     prev_field_was_variable = False
 
+    _c_pre.push_indent(space + '    ')
+
     for field in self.fields:
         if not field.visible:
             if not ((field.wire and not field.auto) or 'unserialize' == context):
@@ -1112,6 +1215,8 @@ def _c_serialize_helper_fields(context, self,
         if self.c_var_followed_by_fixed_fields:
             need_padding = False
 
+    _c_pre.pop_indent()
+
     return count
 # _c_serialize_helper_fields()
 
@@ -1210,6 +1315,8 @@ def _c_serialize(context, self):
     temp_vars = []
     prefix = []
 
+    _c_pre.redirect_start(code_lines, temp_vars)
+
     if 'serialize' == context:
         if not self.is_switch and not self.c_var_followed_by_fixed_fields:
             _c('    %s *xcb_out = *_buffer;', self.c_type)
@@ -1255,11 +1362,13 @@ def _c_serialize(context, self):
             _c('    %s _aux;', self.c_type)
             _c('    return %s(%s, &_aux);', self.c_unpack_name, reduce(lambda x,y: "%s, %s" % (x, y), param_names))
             _c('}')
+            _c_pre.redirect_end()
             return
         elif self.c_var_followed_by_fixed_fields:
             # special case: call _unserialize()
             _c('    return %s(%s, NULL);', self.c_unserialize_name, reduce(lambda x,y: "%s, %s" % (x, y), param_names))
             _c('}')
+            _c_pre.redirect_end()
             return
         else:
             _c('    char *xcb_tmp = (char *)_buffer;')
@@ -1295,6 +1404,8 @@ def _c_serialize(context, self):
             _c('    unsigned int xcb_pad = 0;')
             _c('    unsigned int xcb_align_to = 0;')
 
+    _c_pre.redirect_end()
+
     _c('')
     for t in temp_vars:
         _c(t)
@@ -1684,12 +1795,11 @@ def _c_accessors_list(self, field):
         spacing = ' '*(len(field.c_length_name)+2)
         _h('%sconst %s *S /**< */);', spacing, S_obj.c_type)
         _c('%sconst %s *S  /**< */)', spacing, S_obj.c_type)
-        length = _c_accessor_get_expr(field.type.expr, fields)
     else:
         _h('%s (const %s *R  /**< */);', field.c_length_name, c_type)
         _c('%s (const %s *R  /**< */)', field.c_length_name, c_type)
-        length = _c_accessor_get_expr(field.type.expr, fields)
     _c('{')
+    length = _c_accessor_get_expr(field.type.expr, fields)
     _c('    return %s;', length)
     _c('}')
 
@@ -1739,10 +1849,15 @@ def _c_accessors_list(self, field):
         _c('{')
         _c('    %s i;', field.c_iterator_type)
 
+        _c_pre.start()
+        length_expr_str = _c_accessor_get_expr(field.type.expr, fields)
+
         if switch_obj is not None:
+            _c_pre.end()
             _c('    i.data = %s;', fields[field.c_field_name][0])
-            _c('    i.rem = %s;', _c_accessor_get_expr(field.type.expr, fields))
+            _c('    i.rem = %s;', length_expr_str)
         elif field.prev_varsized_field == None:
+            _c_pre.end()
             _c('    i.data = (%s *) (R + 1);', field.c_field_type)
         else:
             (prev_varsized_field, align_pad) = get_align_pad(field)
@@ -1753,11 +1868,12 @@ def _c_accessors_list(self, field):
 
             _c('    xcb_generic_iterator_t prev = %s;',
                 _c_iterator_get_end(prev_varsized_field, 'R'))
+            _c_pre.end()
             _c('    i.data = (%s *) ((char *) prev.data + %s);',
                 field.c_field_type, align_pad)
 
         if switch_obj is None:
-            _c('    i.rem = %s;', _c_accessor_get_expr(field.type.expr, fields))
+            _c('    i.rem = %s;', length_expr_str)
         _c('    i.index = (char *) i.data - (char *) %s;', 'R' if switch_obj is None else 'S' )
         _c('    return i;')
         _c('}')