示例#1
0
 def createAction(self, statementGroup):
     '''Tries to create an action based on p_statementGroup. If the statement
        is not correct, r_ is -1. Else, r_ is the index of the element within
        the buffer that is the object of the action.'''
     res = -1
     try:
         # Check the whole statement group
         if not statementGroup or (len(statementGroup) > 2):
             raise ParsingError(BAD_STATEMENT_GROUP % str(statementGroup))
         # Check the statement
         statement = statementGroup[0]
         aRes = self.actionRex.match(statement)
         if not aRes:
             raise ParsingError(BAD_STATEMENT % statement)
         statementName, podElem, minus, actionType, subExpr = aRes.groups()
         if not (podElem in PodElement.POD_ELEMS):
             raise ParsingError(BAD_ELEMENT % podElem)
         if minus and (not podElem in PodElement.MINUS_ELEMS):
             raise ParsingError(BAD_MINUS %
                                (podElem, PodElement.MINUS_ELEMS))
         indexPodElem = self.getIndex(podElem)
         if indexPodElem == -1:
             raise ParsingError(
                 ELEMENT_NOT_FOUND % (podElem, str([
                     e.__class__.__name__.lower() \
                     for e in self.elements.values()])))
         podElem = self.elements[indexPodElem]
         # Check the 'from' clause
         fromClause = None
         source = 'buffer'
         if len(statementGroup) > 1:
             fromClause = statementGroup[1]
             source = 'from'
             if not fromClause.startswith('from '):
                 raise ParsingError(BAD_FROM_CLAUSE % fromClause)
             fromClause = fromClause[5:]
         # Create the action
         if actionType == 'if':
             self.action = IfAction(statementName, self, subExpr, podElem,
                                    minus, source, fromClause)
             self.env.ifActions.append(self.action)
             if self.action.name:
                 # We must register this action as a named action
                 if self.env.namedIfActions.has_key(self.action.name):
                     raise ParsingError(DUPLICATE_NAMED_IF)
                 self.env.namedIfActions[self.action.name] = self.action
         elif actionType == 'else':
             if not self.env.ifActions:
                 raise ParsingError(ELSE_WITHOUT_IF)
             # Does the "else" action reference a named "if" action?
             ifReference = subExpr.strip()
             if ifReference:
                 if not self.env.namedIfActions.has_key(ifReference):
                     raise ParsingError(ELSE_WITHOUT_NAMED_IF % ifReference)
                 linkedIfAction = self.env.namedIfActions[ifReference]
                 # This "else" action "consumes" the "if" action: this way,
                 # it is not possible to define two "else" actions related to
                 # the same "if".
                 del self.env.namedIfActions[ifReference]
                 self.env.ifActions.remove(linkedIfAction)
             else:
                 linkedIfAction = self.env.ifActions.pop()
             self.action = ElseAction(statementName, self, None, podElem,
                                      minus, source, fromClause,
                                      linkedIfAction)
         elif actionType == 'for':
             forRes = MemoryBuffer.forRex.match(subExpr.strip())
             if not forRes:
                 raise ParsingError(BAD_FOR_EXPRESSION % subExpr)
             iter, subExpr = forRes.groups()
             self.action = ForAction(statementName, self, subExpr, podElem,
                                     minus, iter, source, fromClause)
         elif actionType == 'with':
             variables = self._getVariables(subExpr)
             self.action = VariablesAction(statementName, self, podElem,
                                           minus, variables, source,
                                           fromClause)
         else:  # null action
             if not fromClause:
                 raise ParsingError(NULL_ACTION_ERROR)
             self.action = NullAction(statementName, self, None, podElem,
                                      None, source, fromClause)
         res = indexPodElem
     except ParsingError, ppe:
         PodError.dump(self, ppe, removeFirstLine=True)
示例#2
0
 def createAction(self, statementGroup):
     '''Tries to create an action based on p_statementGroup. If the statement
        is not correct, r_ is -1. Else, r_ is the index of the element within
        the buffer that is the object of the action.'''
     res = -1
     try:
         # Check the whole statement group
         if not statementGroup or (len(statementGroup) > 2):
             raise ParsingError(BAD_STATEMENT_GROUP % str(statementGroup))
         # Check the statement
         statement = statementGroup[0]
         aRes = self.actionRex.match(statement)
         if not aRes:
             raise ParsingError(BAD_STATEMENT % statement)
         statementName, podElem, minus, actionType, subExpr = aRes.groups()
         if not (podElem in PodElement.POD_ELEMS):
             raise ParsingError(BAD_ELEMENT % podElem)
         if minus and (not podElem in PodElement.MINUS_ELEMS):
             raise ParsingError(
                 BAD_MINUS % (podElem, PodElement.MINUS_ELEMS))
         indexPodElem = self.getIndex(podElem)
         if indexPodElem == -1:
             raise ParsingError(
                 ELEMENT_NOT_FOUND % (podElem, str([
                     e.__class__.__name__.lower() \
                     for e in list(self.elements.values())])))
         podElem = self.elements[indexPodElem]
         # Check the 'from' clause
         fromClause = None
         source = 'buffer'
         if len(statementGroup) > 1:
             fromClause = statementGroup[1]
             source = 'from'
             if not fromClause.startswith('from '):
                 raise ParsingError(BAD_FROM_CLAUSE % fromClause)
             fromClause = fromClause[5:]
         # Create the action
         if actionType == 'if':
             self.action = IfAction(statementName, self, subExpr, podElem,
                                    minus, source, fromClause)
             self.env.ifActions.append(self.action)
             if self.action.name:
                 # We must register this action as a named action
                 if self.action.name in self.env.namedIfActions:
                     raise ParsingError(DUPLICATE_NAMED_IF)
                 self.env.namedIfActions[self.action.name] = self.action
         elif actionType == 'else':
             if not self.env.ifActions:
                 raise ParsingError(ELSE_WITHOUT_IF)
             # Does the "else" action reference a named "if" action?
             ifReference = subExpr.strip()
             if ifReference:
                 if ifReference not in self.env.namedIfActions:
                     raise ParsingError(ELSE_WITHOUT_NAMED_IF % ifReference)
                 linkedIfAction = self.env.namedIfActions[ifReference]
                 # This "else" action "consumes" the "if" action: this way,
                 # it is not possible to define two "else" actions related to
                 # the same "if".
                 del self.env.namedIfActions[ifReference]
                 self.env.ifActions.remove(linkedIfAction)
             else:
                 linkedIfAction = self.env.ifActions.pop()
             self.action = ElseAction(statementName, self, None, podElem,
                                      minus, source, fromClause,
                                      linkedIfAction)
         elif actionType == 'for':
             forRes = MemoryBuffer.forRex.match(subExpr.strip())
             if not forRes:
                 raise ParsingError(BAD_FOR_EXPRESSION % subExpr)
             iter, subExpr = forRes.groups()
             self.action = ForAction(statementName, self, subExpr, podElem,
                                     minus, iter, source, fromClause)
         elif actionType == 'with':
             variables = self._getVariables(subExpr)
             self.action = VariablesAction(statementName, self, podElem,
                                        minus, variables, source, fromClause)
         else: # null action
             if not fromClause:
                 raise ParsingError(NULL_ACTION_ERROR)
             self.action = NullAction(statementName, self, None, podElem,
                                      None, source, fromClause)
         res = indexPodElem
     except ParsingError as ppe:
         PodError.dump(self, ppe, removeFirstLine=True)
     return res
示例#3
0
class MemoryBuffer(Buffer):
    actionRex = re.compile('(?:(\w+)\s*\:\s*)?do\s+(\w+)(-)?' \
                           '(?:\s+(for|if|else|with)\s*(.*))?')
    forRex = re.compile('\s*([\w\-_]+)\s+in\s+(.*)')
    varRex = re.compile('\s*(@?[\w\-_]+)\s*=\s*(.*)')

    def __init__(self, env, parent):
        Buffer.__init__(self, env, parent)
        self.content = ''
        self.elements = {}
        self.action = None

    def clone(self):
        '''Produces an empty buffer that is a clone of this one.'''
        return MemoryBuffer(self.env, self.parent)

    def addSubBuffer(self, subBuffer=None):
        sb = Buffer.addSubBuffer(self, subBuffer)
        self.content += ' '  # To avoid having several subbuffers referenced at
        # the same place within this buffer.
        return sb

    def getRootBuffer(self):
        '''Returns the root buffer. For POD it is always a FileBuffer. For PX,
           it is a MemoryBuffer.'''
        if self.parent: return self.parent.getRootBuffer()
        return self

    def getLength(self):
        return len(self.content)

    def write(self, thing):
        self.content += thing

    def getIndex(self, podElemName):
        res = -1
        for index, podElem in self.elements.items():
            if podElem.__class__.__name__.lower() == podElemName:
                if index > res:
                    res = index
        return res

    def getMainElement(self):
        res = None
        if 0 in self.elements:
            res = self.elements[0]
        return res

    def isMainElement(self, elem):
        '''Is p_elem the main element within this buffer?'''
        mainElem = self.getMainElement()
        if not mainElem: return
        if hasattr(mainElem, 'OD'): mainElem = mainElem.OD.elem
        if elem != mainElem: return
        # elem is the same as the main elem. But is it really the main elem, or
        # the same elem, found deeper in the buffer?
        for index, iElem in self.elements.items():
            foundElem = None
            if hasattr(iElem, 'OD'):
                if iElem.OD:
                    foundElem = iElem.OD.elem
            else:
                foundElem = iElem
            if (foundElem == mainElem) and (index != 0):
                return
        return True

    def unreferenceElement(self, elem):
        # Find last occurrence of this element
        elemIndex = -1
        for index, iElem in self.elements.items():
            foundElem = None
            if hasattr(iElem, 'OD'):
                # A POD element
                if iElem.OD:
                    foundElem = iElem.OD.elem
            else:
                # A PX elem
                foundElem = iElem
            if (foundElem == elem) and (index > elemIndex):
                elemIndex = index
        del self.elements[elemIndex]

    def pushSubBuffer(self, subBuffer):
        '''Sets p_subBuffer at the very end of the buffer.'''
        subIndex = None
        for index, aSubBuffer in self.subBuffers.items():
            if aSubBuffer == subBuffer:
                subIndex = index
                break
        if subIndex != None:
            # Indeed, it is possible that this buffer is not referenced
            # in the parent (if it is a temp buffer generated from a cut)
            del self.subBuffers[subIndex]
            self.subBuffers[self.getLength()] = subBuffer
            self.content += ' '

    def transferAllContent(self):
        '''Transfer all content to parent.'''
        if isinstance(self.parent, FileBuffer):
            # First unreference all elements
            for index in self.getElementIndexes(expressions=False):
                del self.elements[index]
            self.evaluate(self.parent, self.env.context)
        else:
            # Transfer content in itself
            oldParentLength = self.parent.getLength()
            self.parent.write(self.content)
            # Transfer elements
            for index, podElem in self.elements.items():
                self.parent.elements[oldParentLength + index] = podElem
            # Transfer sub-buffers
            for index, buf in self.subBuffers.items():
                self.parent.subBuffers[oldParentLength + index] = buf
        # Empty the buffer
        MemoryBuffer.__init__(self, self.env, self.parent)
        # Change buffer position wrt parent
        self.parent.pushSubBuffer(self)

    def addElement(self, elem, elemType='pod'):
        if elemType == 'pod':
            elem = PodElement.create(elem)
        self.elements[self.getLength()] = elem
        if isinstance(elem, Cell) or isinstance(elem, Table):
            elem.tableInfo = self.env.getTable()
            if isinstance(elem, Cell):
                # Remember where this cell is in the table
                elem.colIndex = elem.tableInfo.curColIndex
        if elem == 'x':
            # See comment on similar statement in the method below.
            self.content += ' '

    def addExpression(self, expression, tiedHook=None):
        # Create the POD expression
        expr = Expression(expression, self.pod)
        if tiedHook: tiedHook.tiedExpression = expr
        self.elements[self.getLength()] = expr
        # To be sure that an expr and an elem can't be found at the same index
        # in the buffer.
        self.content += ' '

    def addAttributes(self):
        '''pod-only: adds an Attributes instance into this buffer.'''
        attrs = Attributes(self.env)
        self.elements[self.getLength()] = attrs
        self.content += ' '
        return attrs

    def addAttribute(self, name, expr):
        '''px-only: adds an Attribute instance into this buffer.'''
        attr = Attribute(name, expr)
        self.elements[self.getLength()] = attr
        self.content += ' '
        return attr

    def _getVariables(self, expr):
        '''Returns variable definitions in p_expr as a list
           ~[(s_varName, s_expr)]~.'''
        exprs = expr.strip().split(';')
        res = []
        for sub in exprs:
            varRes = MemoryBuffer.varRex.match(sub)
            if not varRes:
                raise ParsingError(BAD_VAR_EXPRESSION % sub)
            res.append(varRes.groups())
        return res

    def createAction(self, statementGroup):
        '''Tries to create an action based on p_statementGroup. If the statement
           is not correct, r_ is -1. Else, r_ is the index of the element within
           the buffer that is the object of the action.'''
        res = -1
        try:
            # Check the whole statement group
            if not statementGroup or (len(statementGroup) > 2):
                raise ParsingError(BAD_STATEMENT_GROUP % str(statementGroup))
            # Check the statement
            statement = statementGroup[0]
            aRes = self.actionRex.match(statement)
            if not aRes:
                raise ParsingError(BAD_STATEMENT % statement)
            statementName, podElem, minus, actionType, subExpr = aRes.groups()
            if not (podElem in PodElement.POD_ELEMS):
                raise ParsingError(BAD_ELEMENT % podElem)
            if minus and (not podElem in PodElement.MINUS_ELEMS):
                raise ParsingError(BAD_MINUS %
                                   (podElem, PodElement.MINUS_ELEMS))
            indexPodElem = self.getIndex(podElem)
            if indexPodElem == -1:
                raise ParsingError(
                    ELEMENT_NOT_FOUND % (podElem, str([
                        e.__class__.__name__.lower() \
                        for e in list(self.elements.values())])))
            podElem = self.elements[indexPodElem]
            # Check the 'from' clause
            fromClause = None
            source = 'buffer'
            if len(statementGroup) > 1:
                fromClause = statementGroup[1]
                source = 'from'
                if not fromClause.startswith('from '):
                    raise ParsingError(BAD_FROM_CLAUSE % fromClause)
                fromClause = fromClause[5:]
            # Create the action
            if actionType == 'if':
                self.action = IfAction(statementName, self, subExpr, podElem,
                                       minus, source, fromClause)
                self.env.ifActions.append(self.action)
                if self.action.name:
                    # We must register this action as a named action
                    if self.action.name in self.env.namedIfActions:
                        raise ParsingError(DUPLICATE_NAMED_IF)
                    self.env.namedIfActions[self.action.name] = self.action
            elif actionType == 'else':
                if not self.env.ifActions:
                    raise ParsingError(ELSE_WITHOUT_IF)
                # Does the "else" action reference a named "if" action?
                ifReference = subExpr.strip()
                if ifReference:
                    if ifReference not in self.env.namedIfActions:
                        raise ParsingError(ELSE_WITHOUT_NAMED_IF % ifReference)
                    linkedIfAction = self.env.namedIfActions[ifReference]
                    # This "else" action "consumes" the "if" action: this way,
                    # it is not possible to define two "else" actions related to
                    # the same "if".
                    del self.env.namedIfActions[ifReference]
                    self.env.ifActions.remove(linkedIfAction)
                else:
                    linkedIfAction = self.env.ifActions.pop()
                self.action = ElseAction(statementName, self, None, podElem,
                                         minus, source, fromClause,
                                         linkedIfAction)
            elif actionType == 'for':
                forRes = MemoryBuffer.forRex.match(subExpr.strip())
                if not forRes:
                    raise ParsingError(BAD_FOR_EXPRESSION % subExpr)
                iter, subExpr = forRes.groups()
                self.action = ForAction(statementName, self, subExpr, podElem,
                                        minus, iter, source, fromClause)
            elif actionType == 'with':
                variables = self._getVariables(subExpr)
                self.action = VariablesAction(statementName, self, podElem,
                                              minus, variables, source,
                                              fromClause)
            else:  # null action
                if not fromClause:
                    raise ParsingError(NULL_ACTION_ERROR)
                self.action = NullAction(statementName, self, None, podElem,
                                         None, source, fromClause)
            res = indexPodElem
        except ParsingError as ppe:
            PodError.dump(self, ppe, removeFirstLine=True)
        return res

    def createPxAction(self, elem, actionType, statement):
        '''Creates a PX action and link it to this buffer. If an action is
           already linked to this buffer (in self.action), this action is
           chained behind the last action via self.action.subAction.'''
        res = 0
        statement = statement.strip()
        if actionType == 'for':
            forRes = MemoryBuffer.forRex.match(statement)
            if not forRes:
                raise ParsingError(BAD_FOR_EXPRESSION % statement)
            iter, subExpr = forRes.groups()
            action = ForAction('for', self, subExpr, elem, False, iter,
                               'buffer', None)
        elif actionType == 'if':
            action = IfAction('if', self, statement, elem, False, 'buffer',
                              None)
        elif actionType in ('var', 'var2'):
            variables = self._getVariables(statement)
            action = VariablesAction('var', self, elem, False, variables,
                                     'buffer', None)
        # Is it the first action for this buffer or not?
        if not self.action:
            self.action = action
        else:
            self.action.addSubAction(action)
        return res

    def cut(self, index, keepFirstPart):
        '''Cuts this buffer into 2 parts. Depending on p_keepFirstPart, the 1st
        (from 0 to index-1) or the second (from index to the end) part of the
        buffer is returned as a MemoryBuffer instance without parent; the other
        part is self.'''
        res = MemoryBuffer(self.env, None)
        # Manage buffer meta-info (elements, expressions, subbuffers)
        iter = BufferIterator(self)
        subBuffersToDelete = []
        elementsToDelete = []
        mustShift = False
        while iter.hasNext():
            itemIndex, item = next(iter)
            if keepFirstPart:
                if itemIndex >= index:
                    newIndex = itemIndex - index
                    if isinstance(item, MemoryBuffer):
                        res.subBuffers[newIndex] = item
                        subBuffersToDelete.append(itemIndex)
                    else:
                        res.elements[newIndex] = item
                        elementsToDelete.append(itemIndex)
            else:
                if itemIndex < index:
                    if isinstance(item, MemoryBuffer):
                        res.subBuffers[itemIndex] = item
                        subBuffersToDelete.append(itemIndex)
                    else:
                        res.elements[itemIndex] = item
                        elementsToDelete.append(itemIndex)
                else:
                    mustShift = True
        if elementsToDelete:
            for elemIndex in elementsToDelete:
                del self.elements[elemIndex]
        if subBuffersToDelete:
            for subIndex in subBuffersToDelete:
                del self.subBuffers[subIndex]
        if mustShift:
            elements = {}
            for elemIndex, elem in self.elements.items():
                elements[elemIndex - index] = elem
            self.elements = elements
            subBuffers = {}
            for subIndex, buf in self.subBuffers.items():
                subBuffers[subIndex - index] = buf
            self.subBuffers = subBuffers
        # Manage content
        if keepFirstPart:
            res.write(self.content[index:])
            self.content = self.content[:index]
        else:
            res.write(self.content[:index])
            self.content = self.content[index:]
        return res

    def getElementIndexes(self, expressions=True):
        res = []
        for index, elem in self.elements.items():
            condition = isinstance(elem, Expression) or \
                        isinstance(elem, Attributes)
            if not expressions:
                condition = not condition
            if condition:
                res.append(index)
        return res

    def transferActionIndependentContent(self, actionElemIndex):
        # Manage content to transfer to parent buffer
        if actionElemIndex != 0:
            actionIndependentBuffer = self.cut(actionElemIndex,
                                               keepFirstPart=False)
            actionIndependentBuffer.parent = self.parent
            actionIndependentBuffer.transferAllContent()
            self.parent.pushSubBuffer(self)
        # Manage content to transfer to a child buffer
        actionElemIndex = self.getIndex(
            self.action.elem.__class__.__name__.lower())
        # We recompute actionElemIndex because after cut it may have changed
        elemIndexes = self.getElementIndexes(expressions=False)
        elemIndexes.sort()
        if elemIndexes.index(actionElemIndex) != (len(elemIndexes) - 1):
            # I must create a sub-buffer with the impactable elements after
            # the action-related element
            childBuffer = self.cut(
                elemIndexes[elemIndexes.index(actionElemIndex) + 1],
                keepFirstPart=True)
            self.addSubBuffer(childBuffer)
            res = childBuffer
        else:
            res = self
        return res

    def getStartIndex(self, removeMainElems):
        '''When I must dump the buffer, sometimes (if p_removeMainElems is
        True), I must dump only a subset of it. This method returns the start
        index of the buffer part I must dump.'''
        if not removeMainElems: return 0
        # Find the start position of the deepest element to remove
        deepestElem = self.action.elem.DEEPEST_TO_REMOVE
        pos = self.content.find('<%s' % deepestElem.elem)
        pos = pos + len(deepestElem.elem)
        # Now we must find the position of the end of this start tag,
        # skipping potential attributes.
        inAttrValue = False  # Are we parsing an attribute value ?
        endTagFound = False  # Have we found the end of this tag ?
        while not endTagFound:
            pos += 1
            nextChar = self.content[pos]
            if (nextChar == '>') and not inAttrValue:
                # Yes we have it
                endTagFound = True
            elif nextChar == '"':
                inAttrValue = not inAttrValue
        return pos + 1

    def getStopIndex(self, removeMainElems):
        '''This method returns the stop index of the buffer part I must dump.'''
        if removeMainElems:
            ns = self.env.namespaces
            deepestElem = self.action.elem.DEEPEST_TO_REMOVE
            pos = self.content.rfind('</%s>' % deepestElem.getFullName(ns))
            res = pos
        else:
            res = self.getLength()
        return res

    def removeAutomaticExpressions(self):
        '''When a buffer has an action with minus=True, we must remove the
           "columnsRepeat" expressions automatically inserted by pod. Else, we
           will have problems when computing the index of the part to keep
           (m_getStartIndex).'''
        # Find the start position of the deepest element to remove
        deepestElem = self.action.elem.DEEPEST_TO_REMOVE
        pos = self.content.find('<%s' % deepestElem.elem)
        for index in list(self.elements.keys()):
            if index < pos: del self.elements[index]

    reTagContent = re.compile('<(?P<p>[\w-]+):(?P<f>[\w-]+)(.*?)>.*</(?P=p):' \
                              '(?P=f)>', re.S)

    def evaluate(self,
                 result,
                 context,
                 subElements=True,
                 removeMainElems=False):
        '''Evaluates this buffer given the current p_context and add the result
           into p_result. With pod, p_result is the root file buffer; with px
           it is a memory buffer.'''
        if not subElements:
            # Dump the root tag in this buffer, but not its content
            res = self.reTagContent.match(self.content.strip())
            if not res: result.write(self.content)
            else:
                g = res.group
                result.write('<%s:%s%s></%s:%s>' %
                             (g(1), g(2), g(3), g(1), g(2)))
        else:
            if removeMainElems: self.removeAutomaticExpressions()
            iter = BufferIterator(self)
            currentIndex = self.getStartIndex(removeMainElems)
            while iter.hasNext():
                index, evalEntry = next(iter)
                result.write(self.content[currentIndex:index])
                currentIndex = index + 1
                if isinstance(evalEntry, Expression):
                    try:
                        res, escape = evalEntry.evaluate(context)
                        if escape: result.dumpContent(res)
                        else: result.write(res)
                    except EvaluationError as e:
                        # This exception has already been treated (see the
                        # "except" block below). Simply re-raise it when needed.
                        if self.env.raiseOnError: raise e
                    except Exception as e:
                        if not self.env.raiseOnError:
                            PodError.dump(
                                result, EVAL_EXPR_ERROR % (evalEntry.expr, e))
                        else:
                            raise EvaluationError(EVAL_EXPR_ERROR % \
                                        (evalEntry.expr, '\n'+Traceback.get(5)))
                elif isinstance(evalEntry, Attributes) or \
                     isinstance(evalEntry, Attribute):
                    result.write(evalEntry.evaluate(context))
                else:  # It is a subBuffer
                    if evalEntry.action:
                        evalEntry.action.execute(result, context)
                    else:
                        result.write(evalEntry.content)
            stopIndex = self.getStopIndex(removeMainElems)
            if currentIndex < (stopIndex - 1):
                result.write(self.content[currentIndex:stopIndex])

    def clean(self):
        '''Cleans the buffer content.'''
        self.content = ''
示例#4
0
class MemoryBuffer(Buffer):
    actionRex = re.compile('(?:(\w+)\s*\:\s*)?do\s+(\w+)(-)?' \
                           '(?:\s+(for|if|else|with)\s*(.*))?')
    forRex = re.compile('\s*([\w\-_]+)\s+in\s+(.*)')
    varRex = re.compile('\s*(@?[\w\-_]+)\s*=\s*(.*)')

    def __init__(self, env, parent):
        Buffer.__init__(self, env, parent)
        self.content = ''
        self.elements = {}
        self.action = None

    def clone(self):
        '''Produces an empty buffer that is a clone of this one.'''
        return MemoryBuffer(self.env, self.parent)

    def addSubBuffer(self, subBuffer=None):
        sb = Buffer.addSubBuffer(self, subBuffer)
        self.content += ' ' # To avoid having several subbuffers referenced at
                            # the same place within this buffer.
        return sb

    def getRootBuffer(self):
        '''Returns the root buffer. For POD it is always a FileBuffer. For PX,
           it is a MemoryBuffer.'''
        if self.parent: return self.parent.getRootBuffer()
        return self

    def getLength(self): return len(self.content)

    def write(self, thing): self.content += thing

    def getIndex(self, podElemName):
        res = -1
        for index, podElem in self.elements.items():
            if podElem.__class__.__name__.lower() == podElemName:
                if index > res:
                    res = index
        return res

    def getMainElement(self):
        res = None
        if 0 in self.elements:
            res = self.elements[0]
        return res

    def isMainElement(self, elem):
        '''Is p_elem the main element within this buffer?'''
        mainElem = self.getMainElement()
        if not mainElem: return
        if hasattr(mainElem, 'OD'): mainElem = mainElem.OD.elem
        if elem != mainElem: return
        # elem is the same as the main elem. But is it really the main elem, or
        # the same elem, found deeper in the buffer?
        for index, iElem in self.elements.items():
            foundElem = None
            if hasattr(iElem, 'OD'):
                if iElem.OD:
                    foundElem = iElem.OD.elem
            else:
                foundElem = iElem
            if (foundElem == mainElem) and (index != 0):
                return
        return True

    def unreferenceElement(self, elem):
        # Find last occurrence of this element
        elemIndex = -1
        for index, iElem in self.elements.items():
            foundElem = None
            if hasattr(iElem, 'OD'):
                # A POD element
                if iElem.OD:
                    foundElem = iElem.OD.elem
            else:
                # A PX elem
                foundElem = iElem
            if (foundElem == elem) and (index > elemIndex):
                elemIndex = index
        del self.elements[elemIndex]

    def pushSubBuffer(self, subBuffer):
        '''Sets p_subBuffer at the very end of the buffer.'''
        subIndex = None
        for index, aSubBuffer in self.subBuffers.items():
            if aSubBuffer == subBuffer:
                subIndex = index
                break
        if subIndex != None:
            # Indeed, it is possible that this buffer is not referenced
            # in the parent (if it is a temp buffer generated from a cut)
            del self.subBuffers[subIndex]
            self.subBuffers[self.getLength()] = subBuffer
            self.content += ' '

    def transferAllContent(self):
        '''Transfer all content to parent.'''
        if isinstance(self.parent, FileBuffer):
            # First unreference all elements
            for index in self.getElementIndexes(expressions=False):
                del self.elements[index]
            self.evaluate(self.parent, self.env.context)
        else:
            # Transfer content in itself
            oldParentLength = self.parent.getLength()
            self.parent.write(self.content)
            # Transfer elements
            for index, podElem in self.elements.items():
                self.parent.elements[oldParentLength + index] = podElem
            # Transfer sub-buffers
            for index, buf in self.subBuffers.items():
                self.parent.subBuffers[oldParentLength + index] = buf
        # Empty the buffer
        MemoryBuffer.__init__(self, self.env, self.parent)
        # Change buffer position wrt parent
        self.parent.pushSubBuffer(self)

    def addElement(self, elem, elemType='pod'):
        if elemType == 'pod':
            elem = PodElement.create(elem)
        self.elements[self.getLength()] = elem
        if isinstance(elem, Cell) or isinstance(elem, Table):
            elem.tableInfo = self.env.getTable()
            if isinstance(elem, Cell):
                # Remember where this cell is in the table
                elem.colIndex = elem.tableInfo.curColIndex
        if elem == 'x':
            # See comment on similar statement in the method below.
            self.content += ' '

    def addExpression(self, expression, tiedHook=None):
        # Create the POD expression
        expr = Expression(expression, self.pod)
        if tiedHook: tiedHook.tiedExpression = expr
        self.elements[self.getLength()] = expr
        # To be sure that an expr and an elem can't be found at the same index
        # in the buffer.
        self.content += ' '

    def addAttributes(self):
        '''pod-only: adds an Attributes instance into this buffer.'''
        attrs = Attributes(self.env)
        self.elements[self.getLength()] = attrs
        self.content += ' '
        return attrs

    def addAttribute(self, name, expr):
        '''px-only: adds an Attribute instance into this buffer.'''
        attr = Attribute(name, expr)
        self.elements[self.getLength()] = attr
        self.content += ' '
        return attr

    def _getVariables(self, expr):
        '''Returns variable definitions in p_expr as a list
           ~[(s_varName, s_expr)]~.'''
        exprs = expr.strip().split(';')
        res = []
        for sub in exprs:
            varRes = MemoryBuffer.varRex.match(sub)
            if not varRes:
                raise ParsingError(BAD_VAR_EXPRESSION % sub)
            res.append(varRes.groups())
        return res

    def createAction(self, statementGroup):
        '''Tries to create an action based on p_statementGroup. If the statement
           is not correct, r_ is -1. Else, r_ is the index of the element within
           the buffer that is the object of the action.'''
        res = -1
        try:
            # Check the whole statement group
            if not statementGroup or (len(statementGroup) > 2):
                raise ParsingError(BAD_STATEMENT_GROUP % str(statementGroup))
            # Check the statement
            statement = statementGroup[0]
            aRes = self.actionRex.match(statement)
            if not aRes:
                raise ParsingError(BAD_STATEMENT % statement)
            statementName, podElem, minus, actionType, subExpr = aRes.groups()
            if not (podElem in PodElement.POD_ELEMS):
                raise ParsingError(BAD_ELEMENT % podElem)
            if minus and (not podElem in PodElement.MINUS_ELEMS):
                raise ParsingError(
                    BAD_MINUS % (podElem, PodElement.MINUS_ELEMS))
            indexPodElem = self.getIndex(podElem)
            if indexPodElem == -1:
                raise ParsingError(
                    ELEMENT_NOT_FOUND % (podElem, str([
                        e.__class__.__name__.lower() \
                        for e in list(self.elements.values())])))
            podElem = self.elements[indexPodElem]
            # Check the 'from' clause
            fromClause = None
            source = 'buffer'
            if len(statementGroup) > 1:
                fromClause = statementGroup[1]
                source = 'from'
                if not fromClause.startswith('from '):
                    raise ParsingError(BAD_FROM_CLAUSE % fromClause)
                fromClause = fromClause[5:]
            # Create the action
            if actionType == 'if':
                self.action = IfAction(statementName, self, subExpr, podElem,
                                       minus, source, fromClause)
                self.env.ifActions.append(self.action)
                if self.action.name:
                    # We must register this action as a named action
                    if self.action.name in self.env.namedIfActions:
                        raise ParsingError(DUPLICATE_NAMED_IF)
                    self.env.namedIfActions[self.action.name] = self.action
            elif actionType == 'else':
                if not self.env.ifActions:
                    raise ParsingError(ELSE_WITHOUT_IF)
                # Does the "else" action reference a named "if" action?
                ifReference = subExpr.strip()
                if ifReference:
                    if ifReference not in self.env.namedIfActions:
                        raise ParsingError(ELSE_WITHOUT_NAMED_IF % ifReference)
                    linkedIfAction = self.env.namedIfActions[ifReference]
                    # This "else" action "consumes" the "if" action: this way,
                    # it is not possible to define two "else" actions related to
                    # the same "if".
                    del self.env.namedIfActions[ifReference]
                    self.env.ifActions.remove(linkedIfAction)
                else:
                    linkedIfAction = self.env.ifActions.pop()
                self.action = ElseAction(statementName, self, None, podElem,
                                         minus, source, fromClause,
                                         linkedIfAction)
            elif actionType == 'for':
                forRes = MemoryBuffer.forRex.match(subExpr.strip())
                if not forRes:
                    raise ParsingError(BAD_FOR_EXPRESSION % subExpr)
                iter, subExpr = forRes.groups()
                self.action = ForAction(statementName, self, subExpr, podElem,
                                        minus, iter, source, fromClause)
            elif actionType == 'with':
                variables = self._getVariables(subExpr)
                self.action = VariablesAction(statementName, self, podElem,
                                           minus, variables, source, fromClause)
            else: # null action
                if not fromClause:
                    raise ParsingError(NULL_ACTION_ERROR)
                self.action = NullAction(statementName, self, None, podElem,
                                         None, source, fromClause)
            res = indexPodElem
        except ParsingError as ppe:
            PodError.dump(self, ppe, removeFirstLine=True)
        return res

    def createPxAction(self, elem, actionType, statement):
        '''Creates a PX action and link it to this buffer. If an action is
           already linked to this buffer (in self.action), this action is
           chained behind the last action via self.action.subAction.'''
        res = 0
        statement = statement.strip()
        if actionType == 'for':
            forRes = MemoryBuffer.forRex.match(statement)
            if not forRes:
                raise ParsingError(BAD_FOR_EXPRESSION % statement)
            iter, subExpr = forRes.groups()
            action = ForAction('for', self, subExpr, elem, False, iter,
                               'buffer', None)
        elif actionType == 'if':
            action= IfAction('if', self, statement, elem, False, 'buffer', None)
        elif actionType in ('var', 'var2'):
            variables = self._getVariables(statement)
            action = VariablesAction('var', self, elem, False, variables,
                                     'buffer', None)
        # Is it the first action for this buffer or not?
        if not self.action:
            self.action = action
        else:
            self.action.addSubAction(action)
        return res

    def cut(self, index, keepFirstPart):
        '''Cuts this buffer into 2 parts. Depending on p_keepFirstPart, the 1st
        (from 0 to index-1) or the second (from index to the end) part of the
        buffer is returned as a MemoryBuffer instance without parent; the other
        part is self.'''
        res = MemoryBuffer(self.env, None)
        # Manage buffer meta-info (elements, expressions, subbuffers)
        iter = BufferIterator(self)
        subBuffersToDelete = []
        elementsToDelete = []
        mustShift = False
        while iter.hasNext():
            itemIndex, item = next(iter)
            if keepFirstPart:
                if itemIndex >= index:
                    newIndex = itemIndex-index
                    if isinstance(item, MemoryBuffer):
                        res.subBuffers[newIndex] = item
                        subBuffersToDelete.append(itemIndex)
                    else:
                        res.elements[newIndex] = item
                        elementsToDelete.append(itemIndex)
            else:
                if itemIndex < index:
                    if isinstance(item, MemoryBuffer):
                        res.subBuffers[itemIndex] = item
                        subBuffersToDelete.append(itemIndex)
                    else:
                        res.elements[itemIndex] = item
                        elementsToDelete.append(itemIndex)
                else:
                    mustShift = True
        if elementsToDelete:
            for elemIndex in elementsToDelete:
                del self.elements[elemIndex]
        if subBuffersToDelete:
            for subIndex in subBuffersToDelete:
                del self.subBuffers[subIndex]
        if mustShift:
            elements = {}
            for elemIndex, elem in self.elements.items():
                elements[elemIndex-index] = elem
            self.elements = elements
            subBuffers = {}
            for subIndex, buf in self.subBuffers.items():
                subBuffers[subIndex-index] = buf
            self.subBuffers = subBuffers
        # Manage content
        if keepFirstPart:
            res.write(self.content[index:])
            self.content = self.content[:index]
        else:
            res.write(self.content[:index])
            self.content = self.content[index:]
        return res

    def getElementIndexes(self, expressions=True):
        res = []
        for index, elem in self.elements.items():
            condition = isinstance(elem, Expression) or \
                        isinstance(elem, Attributes)
            if not expressions:
                condition = not condition
            if condition:
                res.append(index)
        return res

    def transferActionIndependentContent(self, actionElemIndex):
        # Manage content to transfer to parent buffer
        if actionElemIndex != 0:
            actionIndependentBuffer = self.cut(actionElemIndex,
                                               keepFirstPart=False)
            actionIndependentBuffer.parent = self.parent
            actionIndependentBuffer.transferAllContent()
            self.parent.pushSubBuffer(self)
        # Manage content to transfer to a child buffer
        actionElemIndex = self.getIndex(
            self.action.elem.__class__.__name__.lower())
        # We recompute actionElemIndex because after cut it may have changed
        elemIndexes = self.getElementIndexes(expressions=False)
        elemIndexes.sort()
        if elemIndexes.index(actionElemIndex) != (len(elemIndexes)-1):
            # I must create a sub-buffer with the impactable elements after
            # the action-related element
            childBuffer = self.cut(elemIndexes[elemIndexes.index(
                actionElemIndex)+1], keepFirstPart=True)
            self.addSubBuffer(childBuffer)
            res = childBuffer
        else:
            res = self
        return res

    def getStartIndex(self, removeMainElems):
        '''When I must dump the buffer, sometimes (if p_removeMainElems is
        True), I must dump only a subset of it. This method returns the start
        index of the buffer part I must dump.'''
        if not removeMainElems: return 0
        # Find the start position of the deepest element to remove
        deepestElem = self.action.elem.DEEPEST_TO_REMOVE
        pos = self.content.find('<%s' % deepestElem.elem)
        pos = pos + len(deepestElem.elem)
        # Now we must find the position of the end of this start tag,
        # skipping potential attributes.
        inAttrValue = False # Are we parsing an attribute value ?
        endTagFound = False # Have we found the end of this tag ?
        while not endTagFound:
            pos += 1
            nextChar = self.content[pos]
            if (nextChar == '>') and not inAttrValue:
                # Yes we have it
                endTagFound = True
            elif nextChar == '"':
                inAttrValue = not inAttrValue
        return pos + 1

    def getStopIndex(self, removeMainElems):
        '''This method returns the stop index of the buffer part I must dump.'''
        if removeMainElems:
            ns = self.env.namespaces
            deepestElem = self.action.elem.DEEPEST_TO_REMOVE
            pos = self.content.rfind('</%s>' % deepestElem.getFullName(ns))
            res = pos
        else:
            res = self.getLength()
        return res

    def removeAutomaticExpressions(self):
        '''When a buffer has an action with minus=True, we must remove the
           "columnsRepeat" expressions automatically inserted by pod. Else, we
           will have problems when computing the index of the part to keep
           (m_getStartIndex).'''
        # Find the start position of the deepest element to remove
        deepestElem = self.action.elem.DEEPEST_TO_REMOVE
        pos = self.content.find('<%s' % deepestElem.elem)
        for index in list(self.elements.keys()):
            if index < pos: del self.elements[index]

    reTagContent = re.compile('<(?P<p>[\w-]+):(?P<f>[\w-]+)(.*?)>.*</(?P=p):' \
                              '(?P=f)>', re.S)
    def evaluate(self, result, context, subElements=True,
                 removeMainElems=False):
        '''Evaluates this buffer given the current p_context and add the result
           into p_result. With pod, p_result is the root file buffer; with px
           it is a memory buffer.'''
        if not subElements:
            # Dump the root tag in this buffer, but not its content
            res = self.reTagContent.match(self.content.strip())
            if not res: result.write(self.content)
            else:
                g = res.group
                result.write('<%s:%s%s></%s:%s>' % (g(1),g(2),g(3),g(1),g(2)))
        else:
            if removeMainElems: self.removeAutomaticExpressions()
            iter = BufferIterator(self)
            currentIndex = self.getStartIndex(removeMainElems)
            while iter.hasNext():
                index, evalEntry = next(iter)
                result.write(self.content[currentIndex:index])
                currentIndex = index + 1
                if isinstance(evalEntry, Expression):
                    try:
                        res, escape = evalEntry.evaluate(context)
                        if escape: result.dumpContent(res)
                        else: result.write(res)
                    except EvaluationError as e:
                        # This exception has already been treated (see the 
                        # "except" block below). Simply re-raise it when needed.
                        if self.env.raiseOnError: raise e
                    except Exception as e:
                        if not self.env.raiseOnError:
                            PodError.dump(result, EVAL_EXPR_ERROR % (
                                          evalEntry.expr, e))
                        else:
                            raise EvaluationError(EVAL_EXPR_ERROR % \
                                        (evalEntry.expr, '\n'+Traceback.get(5)))
                elif isinstance(evalEntry, Attributes) or \
                     isinstance(evalEntry, Attribute):
                    result.write(evalEntry.evaluate(context))
                else: # It is a subBuffer
                    if evalEntry.action:
                        evalEntry.action.execute(result, context)
                    else:
                        result.write(evalEntry.content)
            stopIndex = self.getStopIndex(removeMainElems)
            if currentIndex < (stopIndex-1):
                result.write(self.content[currentIndex:stopIndex])

    def clean(self):
        '''Cleans the buffer content.'''
        self.content = ''