def check_as_closer(self, node, active_node): #print node #print self # The node passed in should be a MakoNode or a MakoLeaf at the same indentation level # Who is closing? if self is active_node: # I am the active node, so I am the unambiguous choice to be closed at this time return potentially_closed = active_node.parent while potentially_closed is not None: #print 'Checking: %s' % potentially_closed if potentially_closed.depth == node.depth: # <potentially_closed> is definitely being closed by <node>, and all is well # Todo: Perform type checking to make sure MakoNodes only close against other MakoNodes return elif potentially_closed.depth < node.depth: # How am is <node> closing someone at a lower depth than it? raise NemoException('\nIncorrect indentation\n' + \ 'at:\n\t%s\n' % node + \ 'Tried to close against::\n\t%s\n' % self + \ 'Within active scope of:\n\t%s' % active_node ) potentially_closed = potentially_closed.parent
def _add_control_leaf_to_tree(self, node, active_node): if node.depth > active_node.depth: active_node.add_child(node) return active_node # The following check is disabled # -------- # Leafs cannot appear on a higher indentation point than the active node # That would be ambiguous #raise NemoException('\nIncorrect indentation\n' + \ # 'at:\n\t%s\n' % active_node + \ # 'Followed by:\n\t%s\n' % node + \ # 'Parent:\n\t%s' % active_node.parent ) else: """Try to assign node to one of the ancestors of active_node""" testing_node = active_node while testing_node is not None: # Close against the first element in the tree that is inline with you if testing_node.depth == node.depth: # We are trying to close against the root element if not testing_node.parent: raise NemoException('\nIncorrect indentation\n' + \ 'at:\n\t%s\n' % node + \ 'attempted to close against:\n\t%s' % testing_node ) else: parent = testing_node.parent parent.add_child(node) # Todo: Remove this check testing_node.check_as_closer(node, active_node) return testing_node.parent elif testing_node.depth < node.depth: raise NemoException('\nIncorrect indentation\n' + \ 'at:\n\t%s\n' % node + \ 'attempted to close against:\n\t%s' % testing_node ) testing_node = testing_node.parent else: # This should never be reached because NemoRoot has a depth of -1 raise NemoException('\nIncorrect indentation\n' + \ 'at:\n\t%s\n' % active_node + \ 'Followed by:\n\t%s\n' % node + \ 'Parent:\n\t%s' % parent )
def check_as_closer(self, node, active_node): """ The passed in node was added as your child, and is attempting to close your scope. Is this allowed? """ raise NemoException('\nIncorrect indentation\n' + \ 'at:\n\t%s\n' % node + \ 'Tried to close against:\n\t%s\n' % self + \ 'Within active scope of:\n\t%s' % active_node )
def check_open_close_on_mako_nodes(self, children): open_mako_context = None for child in children: child_type = type(child) # Check child nodes for open/close semantics if child_type is MakoNode and open_mako_context is None: open_mako_context = child if child_type is MakoEndTag: if open_mako_context is None: # Closer w/o an open context raise NemoException('\nEnd tag without open context\n' + \ 'at:\n\t%s\n' % child + \ 'within:\n\t%s\n' % self ) # Close context open_mako_context = None yield child if open_mako_context is not None: # Open context without a closer raise NemoException('\nOpen tag without a closer found:\n' + \ 'at:\n\t%s\n' % open_mako_context + \ 'within:\n\t%s\n' % self )
def _place_in_ancestor(self, node, active_node): """Try to assign node to one of the ancestors of active_node""" parent = active_node while parent is not None: if parent.depth < node.depth: parent.add_child(node) return parent parent = parent.parent else: # This should never be reached because NemoRoot has a depth of -1 raise NemoException('\nIncorrect indentation\n' + \ 'at:\n\t%s\n' % active_node + \ 'Followed by:\n\t%s\n' % node + \ 'Parent:\n\t%s' % parent )
def check_indentation_rules(self, children): depth_seen = None for child in children: # Ensure child is at correct depth # If this is disabled then depth.failure and inner_tag_indentation.failure will both succeed # It is dubious if we want this # Todo: Permissive mode if child.follows_indentation_rules and not PERMISSIVE: if depth_seen is None: depth_seen = child.depth elif child.depth is not depth_seen: raise NemoException('\nIncorrect indentation\n' + \ 'at:\n\t%s\n' % child + \ 'within:\n\t%s\n' % self + \ 'expected indentation of %d ' % depth_seen) yield child
def parse(self, source): self.buffer = Buffer() self._init(raw=iter(source)) quotes = ['\'', '"'] expect = None while True: try: self._next() except StopIteration: break if expect is None: # Match class token (.) or id token (#) if self._c == ' ': self.buffer.write(self._c) elif self._c == '.': expect = quotes self.buffer.write('class=') elif self._c == '#': expect = quotes self.buffer.write('id=') else: # Slurp up attribute name til we see a quote self.buffer.write(self._c) self._slurp_till(quotes) # Slurp up attribute value till we see another of the same quote delimiter_found = self._c self._slurp_till(delimiter_found) else: if self._c not in expect: raise NemoException('Expected one of: %s but received %s' % (expect, self._c)) if expect is quotes: delimiter_found = self._c # Write quote to buffer, then slurp up til the next quote self.buffer.write(delimiter_found) arg = self._slurp_till(delimiter_found) # We have a keyword/arg combination complete so go back to expecting nothing expect = None return self.buffer.getvalue()
def check_as_closer(self, node, active_node): """ Originally this was slated to be removed because it only provided security against bugs we hadn't tested against. In practice (the last 4 years), it proved to be invaluable in providing better error messages than otherwise would be available. It didn't uncover any real bugs, but it showed incorrect indentation at a better level than would otherwise be provided. Technically removing this wouldn't result in invalid code immediately, but it'll let you write poorly Nemo and forget about it. Then later on, you'll end up writing more seemingly valid code which will caused an error in previously written statements. Unlike in HAML, we've chosen to cause an error as soon as possible, rather than implicitly swallowing the error node. """ # Debugging #print node #print self # The node passed in should be a MakoNode or a MakoLeaf at the same indentation level # Who is closing? if self is active_node: # I am the active node, so I am the unambiguous choice to be closed at this time return potentially_closed = active_node.parent while potentially_closed is not None: #print 'Checking: %s' % potentially_closed if potentially_closed.depth == node.depth: # <potentially_closed> is definitely being closed by <node>, and all is well # Todo: Perform type checking to make sure MakoNodes only close against other MakoNodes return elif potentially_closed.depth < node.depth: # How am is <node> closing someone at a lower depth than it? raise NemoException('\nIncorrect indentation\n' + \ 'at:\n\t%s\n' % node + \ 'Tried to close against::\n\t%s\n' % self + \ 'Within active scope of:\n\t%s' % active_node ) potentially_closed = potentially_closed.parent
def _slurp_till(self, end_delimiters): """ Read until a given delimiter is found in a string iterator. Keeps white space, and ignores the end_delimiter if it is preceded by \ Return a string """ cc = None if type(end_delimiters) in [str, unicode]: end_delimiters = [end_delimiters] while True: try: self._next() except StopIteration: raise NemoException('Expected delimiter %s but EOL found instead' % end_delimiters) self.buffer.write(self._c) for end in end_delimiters: if self._c == end and self._last_c != '\\': return
def add_child(self, node): # This should never be called raise NemoException('Parser error. Tried to add node:\n\t%s to leaf: \n\t%s' % (node, self))