class Parser(object): def __init__(self, fail_gracefully=True): self.fail_gracefully = fail_gracefully def parse(self, template_name, templates): """ Creates an AST for the given template. Returns a Template object. """ self.templates = templates # Maps template names to template sources. self.root = Template(template_name) self.stack = [self.root] self.current = self.root self.tokens = Lexer(self.templates[template_name], 'django-pancake').tokenize() _TOKEN_TEXT, _TOKEN_VAR, _TOKEN_BLOCK = TOKEN_TEXT, TOKEN_VAR, TOKEN_BLOCK while self.tokens: token = self.next_token() if token.token_type == _TOKEN_TEXT: self.current.leaves.append(token.contents) elif token.token_type == _TOKEN_VAR: if token.contents == 'block.super': if self.root.parent is None: raise PancakeFail('Got {{ block.super }} in a template that has no parent') super_block_name = self.stack[-1].name current_par = self.root.parent while current_par is not None: if super_block_name in current_par.blocks: self.current.leaves.extend(current_par.blocks[super_block_name].leaves) break current_par = current_par.parent else: self.current.leaves.append('{{ %s }}' % token.contents) elif token.token_type == _TOKEN_BLOCK: try: tag_name, arg = token.contents.split(None, 1) except ValueError: tag_name, arg = token.contents.strip(), None method_name = 'do_%s' % tag_name if hasattr(self, method_name): getattr(self, method_name)(arg) else: self.current.leaves.append('{%% %s %%}' % token.contents) return self.root def next_token(self): return self.tokens.pop(0) def do_block(self, text): if not text: raise PancakeFail('{% block %} without a name') self.current.leaves.append(Block(text)) self.root.blocks[text] = self.current = self.current.leaves[-1] self.stack.append(self.current) def do_endblock(self, text): self.stack.pop() self.current = self.stack[-1] def do_extends(self, text): if not text: raise PancakeFail('{%% extends %%} without an argument (file: %r)' % self.root.name) if text[0] in ('"', "'"): parent_name = text[1:-1] self.root.parent = Parser().parse(parent_name, self.templates) else: raise PancakeFail('Variable {%% extends %%} tags are not supported (file: %r)' % self.root.name) def do_comment(self, text): # Consume all tokens until 'endcomment' while self.tokens: token = self.next_token() if token.token_type == TOKEN_BLOCK: try: tag_name, arg = token.contents.split(None, 1) except ValueError: tag_name, arg = token.contents.strip(), None if tag_name == 'endcomment': break def do_load(self, text): # Keep track of which template libraries have been loaded, # so that we can pass them up to the root. self.root.loads.update(text.split()) def do_include(self, text): if ' only' in text: if self.fail_gracefully: self.current.leaves.append('{%% include %s %%}' % text) return else: raise PancakeFail('{%% include %%} tags containing "only" are not supported (file: %r)' % self.root.name) try: template_name, rest = text.split(None, 1) except ValueError: template_name, rest = text, '' if not template_name[0] in ('"', "'"): if self.fail_gracefully: self.current.leaves.append('{%% include %s %%}' % text) return else: raise PancakeFail('Variable {%% include %%} tags are not supported (file: %r)' % self.root.name) template_name = template_name[1:-1] if rest.startswith('with '): rest = rest[5:] include_node = Parser().parse(template_name, self.templates) # Add {% load %} tags from the included template. self.root.loads.update(include_node.loads) if rest: self.current.leaves.append('{%% with %s %%}' % rest) self.current.leaves.extend(include_node.leaves) if rest: self.current.leaves.append('{% endwith %}')
class Parser(object): def __init__(self, fail_gracefully=True): self.fail_gracefully = fail_gracefully def parse(self, template_name, templates): """ Creates an AST for the given template. Returns a Template object. """ self.templates = templates # Maps template names to template sources. self.root = Template(template_name) self.stack = [self.root] self.current = self.root self.tokens = Lexer(self.templates[template_name]).tokenize() _TOKEN_TEXT, _TOKEN_VAR, _TOKEN_BLOCK = TokenType.TEXT, TokenType.VAR, TokenType.BLOCK while self.tokens: token = self.next_token() if token.token_type == _TOKEN_TEXT: self.current.leaves.append(token.contents) elif token.token_type == _TOKEN_VAR: if token.contents == 'block.super': if self.root.parent is None: raise PancakeFail( 'Got {{ block.super }} in a template that has no parent' ) super_block_name = self.stack[-1].name current_par = self.root.parent while current_par is not None: if super_block_name in current_par.blocks: self.current.leaves.extend( current_par.blocks[super_block_name].leaves) break current_par = current_par.parent else: self.current.leaves.append('{{ %s }}' % token.contents) elif token.token_type == _TOKEN_BLOCK: try: tag_name, arg = token.contents.split(None, 1) except ValueError: tag_name, arg = token.contents.strip(), None method_name = 'do_%s' % tag_name if hasattr(self, method_name): getattr(self, method_name)(arg) else: self.current.leaves.append('{%% %s %%}' % token.contents) return self.root def next_token(self): return self.tokens.pop(0) def do_block(self, text): if not text: raise PancakeFail('{% block %} without a name') self.current.leaves.append(Block(text)) self.root.blocks[text] = self.current = self.current.leaves[-1] self.stack.append(self.current) def do_endblock(self, text): self.stack.pop() self.current = self.stack[-1] def do_extends(self, text): if not text: raise PancakeFail( '{%% extends %%} without an argument (file: %r)' % self.root.name) if text[0] in ('"', "'"): parent_name = text[1:-1] self.root.parent = Parser().parse(parent_name, self.templates) else: raise PancakeFail( 'Variable {%% extends %%} tags are not supported (file: %r)' % self.root.name) def do_comment(self, text): # Consume all tokens until 'endcomment' while self.tokens: token = self.next_token() if token.token_type == TokenType.BLOCK: try: tag_name, arg = token.contents.split(None, 1) except ValueError: tag_name, arg = token.contents.strip(), None if tag_name == 'endcomment': break def do_load(self, text): # Keep track of which template libraries have been loaded, # so that we can pass them up to the root. self.root.loads.update(text.split()) def do_include(self, text): if ' only' in text: if self.fail_gracefully: self.current.leaves.append('{%% include %s %%}' % text) return else: raise PancakeFail( '{%% include %%} tags containing "only" are not supported (file: %r)' % self.root.name) try: template_name, rest = text.split(None, 1) except ValueError: template_name, rest = text, '' if not template_name[0] in ('"', "'"): if self.fail_gracefully: self.current.leaves.append('{%% include %s %%}' % text) return else: raise PancakeFail( 'Variable {%% include %%} tags are not supported (file: %r)' % self.root.name) template_name = template_name[1:-1] if rest.startswith('with '): rest = rest[5:] include_node = Parser().parse(template_name, self.templates) # Add {% load %} tags from the included template. self.root.loads.update(include_node.loads) if rest: self.current.leaves.append('{%% with %s %%}' % rest) self.current.leaves.extend(include_node.leaves) if rest: self.current.leaves.append('{% endwith %}')