示例#1
0
def lex(text, textpos, filepath):
    offset = pos = 0
    end = len(text)
    escaped = False

    while 1:
        if escaped:
            offset = text.find(PREFIX, offset + 2)
            escaped = False
        else:
            offset = text.find(PREFIX, pos)
        if offset < 0 or offset == end - 1:
            break
        next = text[offset + 1]

        if next == '{':
            if offset > pos:
                yield False, text[pos:offset]
            pos = offset + 2
            level = 1
            while level:
                match = tokenprog.match(text, pos)
                if match is None:
                    raise TemplateSyntaxError('invalid syntax', filepath,
                                              *textpos[1:])
                pos = match.end()
                tstart, tend = match.regs[3]
                token = text[tstart:tend]
                if token == '{':
                    level += 1
                elif token == '}':
                    level -= 1
            yield True, text[offset + 2:pos - 1]

        elif next in NAMESTART:
            if offset > pos:
                yield False, text[pos:offset]
                pos = offset
            pos += 1
            while pos < end:
                char = text[pos]
                if char not in NAMECHARS:
                    break
                pos += 1
            yield True, text[offset + 1:pos].strip()

        elif not escaped and next == PREFIX:
            if offset > pos:
                yield False, text[pos:offset]
                pos = offset
            escaped = True
            pos = offset + 1

        else:
            yield False, text[pos:offset + 1]
            pos = offset + 1

    if pos < end:
        yield False, text[pos:]
示例#2
0
 def __init__(self, value, template, namespaces=None, lineno=-1, offset=-1):
     if ' in ' not in value:
         raise TemplateSyntaxError(
             '"in" keyword missing in "for" directive', template.filepath,
             lineno, offset)
     assign, value = value.split(' in ', 1)
     ast = _parse(assign, 'exec')
     self.assign = _assignment(ast.node.nodes[0].expr)
     self.filename = template.filepath
     Directive.__init__(self, value.strip(), template, namespaces, lineno,
                        offset)
示例#3
0
 def _parse_expr(cls, expr, template, lineno=-1, offset=-1):
     """Parses the given expression, raising a useful error message when a
     syntax error is encountered.
     """
     try:
         return expr and Expression(
             expr, template.filepath, lineno,
             lookup=template.lookup) or None
     except SyntaxError, err:
         err.msg += ' in expression "%s" of "%s" directive' % (expr,
                                                               cls.tagname)
         raise TemplateSyntaxError(err, template.filepath, lineno,
                                   offset + (err.offset or 0))
示例#4
0
 def __init__(self, value, template, namespaces=None, lineno=-1, offset=-1):
     Directive.__init__(self, None, template, namespaces, lineno, offset)
     self.vars = []
     value = value.strip()
     try:
         ast = _parse(value, 'exec').node
         for node in ast.nodes:
             if isinstance(node, compiler.ast.Discard):
                 continue
             elif not isinstance(node, compiler.ast.Assign):
                 raise TemplateSyntaxError(
                     'only assignment allowed in '
                     'value of the "with" directive', template.filepath,
                     lineno, offset)
             self.vars.append(([_assignment(n) for n in node.nodes],
                               Expression(node.expr,
                                          template.filepath,
                                          lineno,
                                          lookup=template.lookup)))
     except SyntaxError, err:
         err.msg += ' in expression "%s" of "%s" directive' % (value,
                                                               self.tagname)
         raise TemplateSyntaxError(err, template.filepath, lineno,
                                   offset + (err.offset or 0))
示例#5
0
 def attach(cls, template, stream, value, namespaces, pos):
     if not value:
         raise TemplateSyntaxError('missing value for "replace" directive',
                                   template.filepath, *pos[1:])
     expr = cls._parse_expr(value, template, *pos[1:])
     return None, [(EXPR, expr, pos)]
示例#6
0
def interpolate(text,
                basedir=None,
                filename=None,
                lineno=-1,
                offset=0,
                lookup='lenient'):
    """Parse the given string and extract expressions.
    
    This function is a generator that yields `TEXT` events for literal strings,
    and `EXPR` events for expressions, depending on the results of parsing the
    string.
    
    >>> for kind, data, pos in interpolate("hey ${foo}bar"):
    ...     print kind, `data`
    TEXT u'hey '
    EXPR Expression('foo')
    TEXT u'bar'
    
    :param text: the text to parse
    :param basedir: base directory of the file in which the text was found
                    (optional)
    :param filename: basename of the file in which the text was found (optional)
    :param lineno: the line number at which the text was found (optional)
    :param offset: the column number at which the text starts in the source
                   (optional)
    :param lookup: the variable lookup mechanism; either "lenient" (the
                   default), "strict", or a custom lookup class
    :return: a list of `TEXT` and `EXPR` events
    :raise TemplateSyntaxError: when a syntax error in an expression is
                                encountered
    """
    filepath = filename
    if filepath and basedir:
        filepath = os.path.join(basedir, filepath)
    pos = [filepath, lineno, offset]

    textbuf = []
    textpos = None
    for is_expr, chunk in chain(lex(text, pos, filepath), [(True, '')]):
        if is_expr:
            if textbuf:
                yield TEXT, u''.join(textbuf), textpos
                del textbuf[:]
                textpos = None
            if chunk:
                try:
                    expr = Expression(chunk.strip(),
                                      pos[0],
                                      pos[1],
                                      lookup=lookup)
                    yield EXPR, expr, tuple(pos)
                except SyntaxError, err:
                    raise TemplateSyntaxError(err, filepath, pos[1],
                                              pos[2] + (err.offset or 0))
        else:
            textbuf.append(chunk)
            if textpos is None:
                textpos = tuple(pos)

        if '\n' in chunk:
            lines = chunk.splitlines()
            pos[1] += len(lines) - 1
            pos[2] += len(lines[-1])
        else:
            pos[2] += len(chunk)
示例#7
0
文件: markup.py 项目: tsondt/Canvas
    def _parse(self, source, encoding):
        streams = [[]]  # stacked lists of events of the "compiled" template
        dirmap = {}  # temporary mapping of directives to elements
        ns_prefix = {}
        depth = 0
        fallbacks = []
        includes = []

        if not isinstance(source, Stream):
            source = XMLParser(source,
                               filename=self.filename,
                               encoding=encoding)

        for kind, data, pos in source:
            stream = streams[-1]

            if kind is START_NS:
                # Strip out the namespace declaration for template directives
                prefix, uri = data
                ns_prefix[prefix] = uri
                if uri not in (self.DIRECTIVE_NAMESPACE,
                               self.XINCLUDE_NAMESPACE):
                    stream.append((kind, data, pos))

            elif kind is END_NS:
                uri = ns_prefix.pop(data, None)
                if uri and uri not in (self.DIRECTIVE_NAMESPACE,
                                       self.XINCLUDE_NAMESPACE):
                    stream.append((kind, data, pos))

            elif kind is START:
                # Record any directive attributes in start tags
                tag, attrs = data
                directives = []
                strip = False

                if tag in self.DIRECTIVE_NAMESPACE:
                    cls = self._dir_by_name.get(tag.localname)
                    if cls is None:
                        raise BadDirectiveError(tag.localname, self.filepath,
                                                pos[1])
                    value = attrs.get(getattr(cls, 'ATTRIBUTE', None), '')
                    directives.append((cls, value, ns_prefix.copy(), pos))
                    strip = True

                new_attrs = []
                for name, value in attrs:
                    if name in self.DIRECTIVE_NAMESPACE:
                        cls = self._dir_by_name.get(name.localname)
                        if cls is None:
                            raise BadDirectiveError(name.localname,
                                                    self.filepath, pos[1])
                        directives.append((cls, value, ns_prefix.copy(), pos))
                    else:
                        if value:
                            value = list(
                                interpolate(value,
                                            self.basedir,
                                            pos[0],
                                            pos[1],
                                            pos[2],
                                            lookup=self.lookup))
                            if len(value) == 1 and value[0][0] is TEXT:
                                value = value[0][1]
                        else:
                            value = [(TEXT, u'', pos)]
                        new_attrs.append((name, value))
                new_attrs = Attrs(new_attrs)

                if directives:
                    index = self._dir_order.index
                    directives.sort(lambda a, b: cmp(index(a[0]), index(b[0])))
                    dirmap[(depth, tag)] = (directives, len(stream), strip)

                if tag in self.XINCLUDE_NAMESPACE:
                    if tag.localname == 'include':
                        include_href = new_attrs.get('href')
                        if not include_href:
                            raise TemplateSyntaxError(
                                'Include misses required '
                                'attribute "href"', self.filepath, *pos[1:])
                        includes.append(include_href)
                        streams.append([])
                    elif tag.localname == 'fallback':
                        streams.append([])
                        fallbacks.append(streams[-1])

                else:
                    stream.append((kind, (tag, new_attrs), pos))

                depth += 1

            elif kind is END:
                depth -= 1

                if fallbacks and data == self.XINCLUDE_NAMESPACE['fallback']:
                    assert streams.pop() is fallbacks[-1]
                elif data == self.XINCLUDE_NAMESPACE['include']:
                    fallback = None
                    if len(fallbacks) == len(includes):
                        fallback = fallbacks.pop()
                    streams.pop()  # discard anything between the include tags
                    # and the fallback element
                    stream = streams[-1]
                    stream.append((INCLUDE, (includes.pop(), fallback), pos))
                else:
                    stream.append((kind, data, pos))

                # If there have have directive attributes with the corresponding
                # start tag, move the events inbetween into a "subprogram"
                if (depth, data) in dirmap:
                    directives, start_offset, strip = dirmap.pop((depth, data))
                    substream = stream[start_offset:]
                    if strip:
                        substream = substream[1:-1]
                    stream[start_offset:] = [(SUB, (directives, substream),
                                              pos)]

            elif kind is PI and data[0] == 'python':
                try:
                    # As Expat doesn't report whitespace between the PI target
                    # and the data, we have to jump through some hoops here to
                    # get correctly indented Python code
                    # Unfortunately, we'll still probably not get the line
                    # number quite right
                    lines = [
                        line.expandtabs() for line in data[1].splitlines()
                    ]
                    first = lines[0]
                    rest = dedent('\n'.join(lines[1:])).rstrip()
                    if first.rstrip().endswith(':') and not rest[0].isspace():
                        rest = '\n'.join(
                            ['    ' + line for line in rest.splitlines()])
                    source = '\n'.join([first, rest])
                    suite = Suite(source,
                                  self.filepath,
                                  pos[1],
                                  lookup=self.lookup)
                except SyntaxError, err:
                    raise TemplateSyntaxError(err, self.filepath,
                                              pos[1] + (err.lineno or 1) - 1,
                                              pos[2] + (err.offset or 0))
                stream.append((EXEC, suite, pos))

            elif kind is TEXT:
                for kind, data, pos in interpolate(data,
                                                   self.basedir,
                                                   pos[0],
                                                   pos[1],
                                                   pos[2],
                                                   lookup=self.lookup):
                    stream.append((kind, data, pos))