Source code for fluent.syntax.serializer

from __future__ import unicode_literals
from . import ast


def indent_except_first_line(content):
    return "    ".join(
        content.splitlines(True)
    )


def includes_new_line(elem):
    return isinstance(elem, ast.TextElement) and "\n" in elem.value


def is_select_expr(elem):
    return (
        isinstance(elem, ast.Placeable) and
        isinstance(elem.expression, ast.SelectExpression))


def should_start_on_new_line(pattern):
    is_multiline = any(is_select_expr(elem) for elem in pattern.elements) \
        or any(includes_new_line(elem) for elem in pattern.elements)

    if is_multiline:
        first_element = pattern.elements[0]
        if isinstance(first_element, ast.TextElement):
            first_char = first_element.value[0]
            if first_char in ("[", ".", "*"):
                return False
        return True
    return False


[docs]class FluentSerializer(object): """FluentSerializer converts :class:`.ast.SyntaxNode` objects to unicode strings. `with_junk` controls if parse errors are written back or not. """ HAS_ENTRIES = 1 def __init__(self, with_junk=False): self.with_junk = with_junk
[docs] def serialize(self, resource): "Serialize a :class:`.ast.Resource` to a string." if not isinstance(resource, ast.Resource): raise Exception('Unknown resource type: {}'.format(type(resource))) state = 0 parts = [] for entry in resource.body: if not isinstance(entry, ast.Junk) or self.with_junk: parts.append(self.serialize_entry(entry, state)) if not state & self.HAS_ENTRIES: state |= self.HAS_ENTRIES return "".join(parts)
[docs] def serialize_entry(self, entry, state=0): "Serialize an :class:`.ast.Entry` to a string." if isinstance(entry, ast.Message): return serialize_message(entry) if isinstance(entry, ast.Term): return serialize_term(entry) if isinstance(entry, ast.Comment): if state & self.HAS_ENTRIES: return "\n{}\n".format(serialize_comment(entry, "#")) return "{}\n".format(serialize_comment(entry, "#")) if isinstance(entry, ast.GroupComment): if state & self.HAS_ENTRIES: return "\n{}\n".format(serialize_comment(entry, "##")) return "{}\n".format(serialize_comment(entry, "##")) if isinstance(entry, ast.ResourceComment): if state & self.HAS_ENTRIES: return "\n{}\n".format(serialize_comment(entry, "###")) return "{}\n".format(serialize_comment(entry, "###")) if isinstance(entry, ast.Junk): return serialize_junk(entry) raise Exception('Unknown entry type: {}'.format(type(entry)))
def serialize_comment(comment, prefix="#"): prefixed = "\n".join([ prefix if len(line) == 0 else "{} {}".format(prefix, line) for line in comment.content.split("\n") ]) # Add the trailing line break. return '{}\n'.format(prefixed) def serialize_junk(junk): return junk.content def serialize_message(message): parts = [] if message.comment: parts.append(serialize_comment(message.comment)) parts.append("{} =".format(message.id.name)) if message.value: parts.append(serialize_pattern(message.value)) if message.attributes: for attribute in message.attributes: parts.append(serialize_attribute(attribute)) parts.append("\n") return ''.join(parts) def serialize_term(term): parts = [] if term.comment: parts.append(serialize_comment(term.comment)) parts.append("-{} =".format(term.id.name)) parts.append(serialize_pattern(term.value)) if term.attributes: for attribute in term.attributes: parts.append(serialize_attribute(attribute)) parts.append("\n") return ''.join(parts) def serialize_attribute(attribute): return "\n .{} ={}".format( attribute.id.name, indent_except_first_line(serialize_pattern(attribute.value)) ) def serialize_pattern(pattern): content = "".join(serialize_element(elem) for elem in pattern.elements) content = indent_except_first_line(content) if should_start_on_new_line(pattern): return '\n {}'.format(content) return ' {}'.format(content) def serialize_element(element): if isinstance(element, ast.TextElement): return element.value if isinstance(element, ast.Placeable): return serialize_placeable(element) raise Exception('Unknown element type: {}'.format(type(element))) def serialize_placeable(placeable): expr = placeable.expression if isinstance(expr, ast.Placeable): return "{{{}}}".format(serialize_placeable(expr)) if isinstance(expr, ast.SelectExpression): # Special-case select expressions to control the withespace around the # opening and the closing brace. return "{{ {}}}".format(serialize_expression(expr)) if isinstance(expr, ast.Expression): return "{{ {} }}".format(serialize_expression(expr)) def serialize_expression(expression): if isinstance(expression, ast.StringLiteral): return '"{}"'.format(expression.value) if isinstance(expression, ast.NumberLiteral): return expression.value if isinstance(expression, ast.VariableReference): return "${}".format(expression.id.name) if isinstance(expression, ast.TermReference): out = "-{}".format(expression.id.name) if expression.attribute is not None: out += ".{}".format(expression.attribute.name) if expression.arguments is not None: out += serialize_call_arguments(expression.arguments) return out if isinstance(expression, ast.MessageReference): out = expression.id.name if expression.attribute is not None: out += ".{}".format(expression.attribute.name) return out if isinstance(expression, ast.FunctionReference): args = serialize_call_arguments(expression.arguments) return "{}{}".format(expression.id.name, args) if isinstance(expression, ast.SelectExpression): out = "{} ->".format( serialize_expression(expression.selector)) for variant in expression.variants: out += serialize_variant(variant) return "{}\n".format(out) if isinstance(expression, ast.Placeable): return serialize_placeable(expression) raise Exception('Unknown expression type: {}'.format(type(expression))) def serialize_variant(variant): return "\n{}[{}]{}".format( " *" if variant.default else " ", serialize_variant_key(variant.key), indent_except_first_line(serialize_pattern(variant.value)) ) def serialize_call_arguments(expr): positional = ", ".join( serialize_expression(arg) for arg in expr.positional) named = ", ".join( serialize_named_argument(arg) for arg in expr.named) if len(expr.positional) > 0 and len(expr.named) > 0: return '({}, {})'.format(positional, named) return '({})'.format(positional or named) def serialize_named_argument(arg): return "{}: {}".format( arg.name.name, serialize_expression(arg.value) ) def serialize_variant_key(key): if isinstance(key, ast.Identifier): return key.name if isinstance(key, ast.NumberLiteral): return key.value raise Exception('Unknown variant key type: {}'.format(type(key)))