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)
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
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 = ''
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 = ''