示例#1
0
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 %}')
示例#2
0
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 %}')