Exemplo n.º 1
0
    def visit_Start(self, node):
        try:
            line, column = node.prefix.location
        except AttributeError:
            line, column = 0, 0

        yield Comment(
            " %s%s ... (%d:%d)\n"
            " --------------------------------------------------------" %
            (node.prefix, node.name, line, column))

        if not hasattr(node, "repeatable"):
            print "ea"
        if node.repeatable:
            push = template("STACK.repeat(N)",
                            STACK=self._current_stack,
                            N=ast.Str(s=node.name))
        elif node.replayable:
            push = template("STACK.replay(N)",
                            STACK=self._current_stack,
                            N=ast.Str(s=node.name))
        else:
            push = template("STACK.u(N)",
                            STACK=self._current_stack,
                            N=ast.Str(s=node.name))
        for stmt in push:
            yield stmt

        if node.attributes:
            for attribute in node.attributes:
                for stmt in self.visit(attribute):
                    yield stmt
Exemplo n.º 2
0
    def visit_Macro(self, node):
        body = []

        # Resolve defaults
        for name in self.defaults:
            body += template("NAME = econtext[KEY]",
                             NAME=name,
                             KEY=ast.Str(s="__" + name))

        # Visit macro body
        nodes = itertools.chain(*tuple(map(self.visit, node.body)))

        # Append visited nodes
        body += template("__i18n_domain = None")
        body += nodes

        function_name = "render" if node.name is None else \
                        "render_%s" % mangle(node.name)

        function = ast.FunctionDef(name=function_name,
                                   args=ast.arguments(
                                       args=[
                                           param(self._current_stack),
                                           param(self._defined_models[""]),
                                           param("econtext"),
                                           param("rcontext"),
                                       ],
                                       defaults=[],
                                   ),
                                   body=body)

        yield function
Exemplo n.º 3
0
    def visit_Assignment(self, node):
        for name in node.names:
            if name in COMPILER_INTERNALS_OR_DISALLOWED:
                raise TranslationError("Name disallowed by compiler.", name)

            if name.startswith('__'):
                raise TranslationError(
                    "Name disallowed by compiler (double underscore).", name)

        assignment = self._engine(node.expression, store("__value"))

        if len(node.names) != 1:
            target = ast.Tuple(
                elts=[store_econtext(name) for name in node.names],
                ctx=ast.Store(),
            )
        else:
            target = store_econtext(node.names[0])

        assignment.append(ast.Assign(targets=[target], value=load("__value")))

        for name in node.names:
            if not node.local:
                assignment += template("rcontext[KEY] = __value",
                                       KEY=ast.Str(s=native_string(name)))

        return assignment
Exemplo n.º 4
0
    def __call__(self, expression, target):
        if isinstance(target, string_type):
            target = store(target)

        try:
            stmts = self.translate(expression, target)
        except ExpressionError:
            if self.strict:
                raise

            exc = sys.exc_info()[1]
            p = pickle.dumps(exc)

            stmts = template("__exc = loads(p)",
                             loads=self.loads_symbol,
                             p=ast.Str(s=p))

            token = Token(exc.token, exc.offset, filename=exc.filename)

            stmts += set_error(token, load("__exc"))
            stmts += [ast.Raise(exc=load("__exc"))]

        # Apply visitor to each statement
        for stmt in stmts:
            self.visitor(stmt)

        return stmts
Exemplo n.º 5
0
 def visit_Translate(self, node, target):
     if node.msgid is not None:
         msgid = ast.Str(s=node.msgid)
     else:
         msgid = target
     return self.translate(node.node, target) + \
            emit_translate(target, msgid, default=target)
Exemplo n.º 6
0
    def visit_BindChange(self, node):
        body = []

        body.append(Comment("start model-binding"))

        new_stack = identifier("stack", id(node))
        old_stack = self._current_stack
        body += template("CAPTURED_STACK = STACK.capture()",
                         CAPTURED_STACK=new_stack,
                         STACK=old_stack)

        self._current_stack = new_stack
        self._aliases.append(self._aliases[-1].copy())

        inner = self.visit(node.node)

        self._aliases.pop()
        self._current_stack = old_stack

        on_change_name = identifier("on_change", id(node))
        on_change_func = [
            ast.FunctionDef(name=on_change_name,
                            args=ast.arguments(
                                args=[],
                                defaults=(),
                            ),
                            body=inner)
        ]
        if node.model_name not in self._defined_models:
            raise TranslationError(
                "Cannot find bind model on current context.", node.model_name)

        body += on_change_func
        bind_attrs = ast.Tuple(
            elts=[ast.Str(s=attr) for attr in node.bind_attrs], ctx=ast.Load())
        bind_ons = ast.Tuple(elts=[ast.Str(s=attr) for attr in node.bind_ons],
                             ctx=ast.Load())
        body += template("BIND_CHANGE(MODEL, BIND_ONS, BIND_ATTRS, ON_CHANGE)",
                         BIND_CHANGE=Symbol(bind_change),
                         MODEL=load(self._defined_models[node.model_name]),
                         BIND_ATTRS=bind_attrs,
                         BIND_ONS=bind_ons,
                         ON_CHANGE=on_change_name)

        body.append(Comment("end model-binding"))
        return body
Exemplo n.º 7
0
    def visit_Module(self, node):
        body = []

        body += template("__marker = object()")
        body += template("__filename = f", f=ast.Str(s=self.filename))
        body += self.visit(node.program)

        return body
Exemplo n.º 8
0
 def _leave_assignment(self, names):
     for name in names:
         for stmt in template(
                 "deleteitem(econtext, KEY, BACKUP, __marker)",
                 deleteitem=Symbol(deleteitem),
                 BACKUP=identifier("backup_%s" % name, id(names)),
                 KEY=ast.Str(s=native_string(name)),
         ):
             yield stmt
Exemplo n.º 9
0
    def _convert_bool(self, target, s):
        """Converts value given by ``target`` to a string ``s`` if the
        target is a true value, otherwise ``None``.
        """

        return emit_bool(target,
                         ast.Str(s=s),
                         default=self._default,
                         default_marker=self._default_marker)
Exemplo n.º 10
0
    def visit_Attribute(self, node):
        f = node.space + "%s," + node.quote + "%s" + node.quote

        # Static attributes are just outputted directly
        if isinstance(node.expression, ast.Str):
            s = f % (node.name, node.expression.s)
            return template("STACK.a(N,S)",
                            STACK=self._current_stack,
                            N=ast.Str(s=node.name),
                            S=ast.Str(s=node.expression.s))

        target = identifier("attr", node.name)
        body = self._engine(node.expression, store(target))
        return body + template(
            "if VALUE is not None: STACK.a(NAME,  VALUE)",
            STACK=self._current_stack,
            NAME=ast.Str(s=node.name),
            VALUE=target,
        )
Exemplo n.º 11
0
def set_error(token, exception):
    try:
        line, column = token.location
        filename = token.filename
    except AttributeError:
        line, column = 0, 0
        filename = "<string>"

    string = safe_native(token)

    return template(
        "rcontext.setdefault('__error__', [])."
        "append((string, line, col, src, exc))",
        string=ast.Str(s=string),
        line=ast.Num(n=line),
        col=ast.Num(n=column),
        src=ast.Str(s=filename),
        sys=Symbol(sys),
        exc=exception,
    )
Exemplo n.º 12
0
    def _convert_text(self, target, body=None):
        """Converts value given by ``target`` to text."""

        if self._char_escape:
            # This is a cop-out - we really only support a very select
            # set of escape characters
            other = set(self._char_escape) - self.supported_char_escape_set

            if other:
                for supported in '"', '\'', '':
                    if supported in self._char_escape:
                        quote = supported
                        break
                else:
                    raise RuntimeError("Unsupported escape set: %s." %
                                       repr(self._char_escape))
            else:
                quote = '\0'

            entity = char2entity(quote or '\0')

            result = []
            if body is not None:
                result += template("T = F", F=body, T=target)

            return result + emit_convert_and_escape(
                target,
                quote=ast.Str(s=quote),
                quote_entity=ast.Str(s=entity),
                default=self._default,
                default_marker=self._default_marker,
            )

        return emit_convert(
            target,
            default=self._default,
            default_marker=self._default_marker,
        )
Exemplo n.º 13
0
    def __call__(self, node):
        name = node.id

        # Don't rewrite names that begin with an underscore; they are
        # internal and can be assumed to be locally defined. This
        # policy really should be part of the template program, not
        # defined here in the compiler.

        if isinstance(node.ctx, ast.Load):
            if self.validate_model and name.startswith("__model_"):
                name = node.id[8:]
                if not name in self.defined_models:
                    err = ParseError(
                        "Cannot find model",
                        Token(name,
                              pos=node.lineno,
                              source=self.source,
                              filename=self.filename))
                    raise err
                node.id = self.defined_models[name]
                return node

        if name.startswith('__') or name in self.internals:
            return node

        if name == "el":
            return Builtin("None")

        if isinstance(node.ctx, ast.Store):
            return store_econtext(name)

        aliased = self.aliases.get(name)
        if aliased is not None:
            return load(aliased)

        # If the name is a Python global, first try acquiring it from
        # the dynamic context, then fall back to the global.
        if name in self.builtins:
            return template(
                "getitem(econtext, key, name)",
                getitem=Symbol(getitem),
                mode="eval",
                key=ast.Str(s=name),
                name=load(name),
            )

        # Otherwise, simply acquire it from the dynamic context.
        return load_econtext(name)
Exemplo n.º 14
0
    def visit_BindReplay(self, node):
        body = []

        body.append(Comment("start replay-binding"))

        new_stack = identifier("stack", id(node))
        old_stack = self._current_stack
        body += template("CAPTURED_STACK = STACK.capture()",
                         CAPTURED_STACK=new_stack,
                         STACK=old_stack)

        self._current_stack = new_stack
        self._aliases.append(self._aliases[-1].copy())

        inner = self.visit(node.node)

        self._aliases.pop()
        self._current_stack = old_stack

        on_event_name = identifier("on_event", id(node))
        on_event_func = [
            ast.FunctionDef(name=on_event_name,
                            args=ast.arguments(
                                args=[],
                                defaults=(),
                            ),
                            body=inner)
        ]

        body += on_event_func

        bindable = "__bindable"
        body += self._engine(node.expression, store(bindable))
        events = ast.Tuple(elts=[ast.Str(s=attr) for attr in node.events],
                           ctx=ast.Load())

        body += template("BIND_REPLAY(BINDABLE, EVENTS, ON_EVENT)",
                         BIND_REPLAY=Symbol(bind_replay),
                         BINDABLE=bindable,
                         EVENTS=events,
                         ON_EVENT=on_event_name)

        body.append(Comment("end model-binding"))
        return body
Exemplo n.º 15
0
    def visit_OnError(self, node):
        body = []

        fallback = identifier("__fallback")
        body += template("fallback = len(__stream)", fallback=fallback)

        self._enter_assignment((node.name, ))
        fallback_body = self.visit(node.fallback)
        self._leave_assignment((node.name, ))

        error_assignment = template(
            "econtext[key] = cls(__exc, rcontext['__error__'][-1][1:3])",
            cls=ErrorInfo,
            key=ast.Str(s=node.name),
        )

        body += self.visit(node.node)

        return body
Exemplo n.º 16
0
 def visit_Text(self, node):
     #remove xml comment
     text = comment_re.sub("", node.value)
     return emit_node(ast.Str(s=text), STACK=self._current_stack)
Exemplo n.º 17
0
 def visit_Domain(self, node):
     backup = "__previous_i18n_domain_%d" % id(node)
     return template("BACKUP = __i18n_domain", BACKUP=backup) + \
            template("__i18n_domain = NAME", NAME=ast.Str(s=node.name)) + \
            self.visit(node.node) + \
            template("__i18n_domain = BACKUP", BACKUP=backup)
Exemplo n.º 18
0
def load_econtext(name):
    return template("econtext[KEY]", KEY=ast.Str(s=name), mode="eval")
Exemplo n.º 19
0
    def __call__(self, name, engine):
        """The strategy is to find possible expression strings and
        call the ``validate`` function of the parser to validate.

        For every possible starting point, the longest possible
        expression is tried first, then the second longest and so
        forth.

        Example 1:

          ${'expressions use the ${<expression>} format'}

        The entire expression is attempted first and it is also the
        only one that validates.

        Example 2:

          ${'Hello'} ${'world!'}

        Validation of the longest possible expression (the entire
        string) will fail, while the second round of attempts,
        ``${'Hello'}`` and ``${'world!'}`` respectively, validate.

        """

        body = []
        nodes = []
        text = self.expression

        expr_map = {}
        translate = self.translate

        while text:
            matched = text
            m = self.regex.search(matched)
            if m is None:
                nodes.append(ast.Str(s=text))
                break

            part = text[:m.start()]
            text = text[m.start():]

            if part:
                node = ast.Str(s=part)
                nodes.append(node)

            if not body:
                target = name
            else:
                target = store("%s_%d" % (name.id, text.pos))

            while True:
                d = groupdict(m, matched)
                string = d["expression"] or d["variable"] or ""

                string = decode_htmlentities(string)

                try:
                    compiler = engine.parse(string)
                    body += compiler.assign_text(target)
                except ExpressionError:
                    matched = matched[m.start():m.end() - 1]
                    m = self.regex.search(matched)
                    if m is None:
                        raise
                else:
                    break

            # If one or more expressions are not simple names, we
            # disable translation.
            if RE_NAME.match(string) is None:
                translate = False

            # if this is the first expression, use the provided
            # assignment name; otherwise, generate one (here based
            # on the string position)
            node = load(target.id)
            nodes.append(node)

            expr_map[node] = safe_native(string)

            text = text[len(m.group()):]

        if len(nodes) == 1:
            target = nodes[0]

            if translate and isinstance(target, ast.Str):
                target = template(
                    "translate(msgid, domain=__i18n_domain)",
                    msgid=target,
                    mode="eval",
                )
        else:
            if translate:
                formatting_string = ""
                keys = []
                values = []

                for node in nodes:
                    if isinstance(node, ast.Str):
                        formatting_string += node.s
                    else:
                        string = expr_map[node]
                        formatting_string += "${%s}" % string
                        keys.append(ast.Str(s=string))
                        values.append(node)

                target = template(
                    "translate(msgid, mapping=mapping, domain=__i18n_domain)",
                    msgid=ast.Str(s=formatting_string),
                    mapping=ast.Dict(keys=keys, values=values),
                    mode="eval")
            else:
                nodes = [
                    template("NODE", NODE=node, mode="eval") for node in nodes
                ]

                target = ast.BinOp(left=ast.Str(s="%s" * len(nodes)),
                                   op=ast.Mod(),
                                   right=ast.Tuple(elts=nodes, ctx=ast.Load()))
            body += [ast.Assign(targets=[name], value=target)]

        return body
Exemplo n.º 20
0
    def visit_Translate(self, node):
        """Translation.

        Visit items and assign output to a default value.

        Finally, compile a translation expression and use either
        result or default.
        """

        body = []

        # Track the blocks of this translation
        self._translations.append(set())

        # Prepare new stream
        append = identifier("append", id(node))
        stream = identifier("stream", id(node))
        body += template("s = new_list", s=stream, new_list=LIST) + \
                template("a = s.append", a=append, s=stream)

        # Visit body to generate the message body
        code = self.visit(node.node)
        swap(ast.Suite(body=code), load(append), "__append")
        body += code

        # Reduce white space and assign as message id
        msgid = identifier("msgid", id(node))
        body += template("msgid = __re_whitespace(''.join(stream)).strip()",
                         msgid=msgid,
                         stream=stream)

        default = msgid

        # Compute translation block mapping if applicable
        names = self._translations[-1]
        if names:
            keys = []
            values = []

            for name in names:
                stream, append = self._get_translation_identifiers(name)
                keys.append(ast.Str(s=name))
                values.append(load(stream))

                # Initialize value
                body.insert(
                    0,
                    ast.Assign(targets=[store(stream)],
                               value=ast.Str(s=native_string(""))))

            mapping = ast.Dict(keys=keys, values=values)
        else:
            mapping = None

        # if this translation node has a name, use it as the message id
        if node.msgid:
            msgid = ast.Str(s=node.msgid)

        # emit the translation expression
        body += template(
            "STACK.t(translate("
            "msgid, mapping=mapping, default=default, domain=__i18n_domain))",
            stack=self._current_stack,
            msgid=msgid,
            default=default,
            mapping=mapping)

        # pop away translation block reference
        self._translations.pop()

        return body
Exemplo n.º 21
0
    def visit_Repeat(self, node):
        # Used for loop variable definition and restore
        self._scopes.append(set())

        # Variable assignment and repeat key for single- and
        # multi-variable repeat clause
        if node.local:
            contexts = "econtext",
        else:
            contexts = "econtext", "rcontext"

        for name in node.names:
            if name in COMPILER_INTERNALS_OR_DISALLOWED:
                raise TranslationError("Name disallowed by compiler.", name)

        if len(node.names) > 1:
            targets = [
                ast.Tuple(elts=[
                    subscript(native_string(name), load(context), ast.Store())
                    for name in node.names
                ],
                          ctx=ast.Store()) for context in contexts
            ]

            key = ast.Tuple(elts=[ast.Str(s=name) for name in node.names],
                            ctx=ast.Load())
        else:
            name = node.names[0]
            targets = [
                subscript(native_string(name), load(context), ast.Store())
                for context in contexts
            ]

            key = ast.Str(s=node.names[0])

        repeat = identifier("__repeat", id(node))
        index = identifier("__index", id(node))
        iterator = identifier("__iterator", id(node))
        assignment = [ast.Assign(targets=targets, value=load("__item"))]

        # Make repeat assignment in outer loop
        names = node.names
        local = node.local

        outer = self._engine(node.expression, store(iterator))

        if local:
            outer[:] = list(self._enter_assignment(names)) + outer

        outer += template("REPEAT = econtext['repeat'](key, ITERATOR)",
                          key=key,
                          INDEX=index,
                          REPEAT=repeat,
                          ITERATOR=iterator)
        outer += template("INDEX = REPEAT.length", INDEX=index, REPEAT=repeat)

        # Set a trivial default value for each name assigned to make
        # sure we assign a value even if the iteration is empty
        for name in node.names:
            outer += [
                ast.Assign(targets=[store_econtext(name)], value=load("None"))
            ]

        inner = template("REPEAT._next()", REPEAT=repeat)
        # Compute inner body
        inner += self.visit(node.node)

        # After each iteration, decrease the index
        inner += template("index -= 1", index=index)

        # For items up to N - 1, emit repeat whitespace
        inner += template("if INDEX > 0: STACK.t(WHITESPACE)",
                          STACK=self._current_stack,
                          INDEX=index,
                          WHITESPACE=ast.Str(s=node.whitespace))

        # Main repeat loop
        outer += [
            ast.For(
                target=store("__item"),
                iter=load(iterator),
                body=assignment + inner,
            )
        ]

        # Finally, clean up assignment if it's local
        if outer:
            outer += self._leave_assignment(names)

        self._scopes.pop()

        return outer