예제 #1
0
def visit_core(m: Module, *, name: str) -> None:
    accessor_name, name = name.split(".", 2)
    a = globals()[accessor_name]
    m.stmt(f"func {name}() error {{")
    with m.scope():
        m.stmt(a.new(m, name=name))
    m.stmt("}")
예제 #2
0
def visit_wrap(m: Module, *, name: str, prev: str) -> None:
    accessor_name, name = name.split(".", 2)
    a = globals()[accessor_name]
    m.stmt(f"func {name}() error {{")
    with m.scope():
        m.stmt(a.wrap(m, name=name, prev=prev))
    m.stmt("}")
예제 #3
0
def emit(m: Module, g: Graph) -> Symbol:
    # TODO: name
    # TODO: import_
    i = 0
    variables: t.Dict[int, Symbol] = {}

    for node in topological_sorted(g):
        if node.is_primitive:
            variables[node.uid] = m.symbol(node.name)
            continue

        metadata = t.cast(Metadata, node.metadata)
        return_type = metadata.get("return_type", "")

        return_types = list(t.get_args(return_type) or [return_type])
        var_names = [
            f"v{i}",
            *[getattr(typ, "name", typ.__name__) for typ in return_types[1:]],
        ]

        spec: Fnspec = metadata.get("fnspec")
        provider_callable: t.Optional[Symbol] = None
        if spec is not None:
            pkg_prefix = m.import_(metadata["component_type"].gopackage)
            provider_callable = m.symbol(f"{pkg_prefix}.{spec.name}")
        if provider_callable is None:
            provider_callable = m.symbol(spec.name if spec else node.name)

        args = [variables[dep.uid] for dep in node.depends]
        variables[node.uid], *extra_vars = m.letN(var_names,
                                                  provider_callable(*args))

        if extra_vars:
            for sym, typ in sorted(
                    zip(extra_vars, return_types[1:]),
                    key=lambda pair: getattr(pair[1], "priority", 5),
                    reverse=True,
            ):
                if hasattr(typ, "emit"):
                    typ.emit(m, sym)

        i += 1
    return variables[node.uid]
예제 #4
0
def visit(m: Module, *, names: t.List[str]) -> None:
    m.stmt("func run() error {")
    with m.scope():
        m.stmt(f"return {names[0]}()")
    m.stmt("}")

    for name, prev in itertools.zip_longest(names, names[1:]):
        if prev is None:
            visit_core(m, name=name)
        else:
            visit_wrap(m, name=name, prev=prev)
예제 #5
0
def emit(g: Graph) -> Module:
    # TODO: name
    # TODO: import_
    i = 0
    m = Module()
    variables: t.Dict[int, Symbol] = {}

    with m.func("run", *_get_args(g), return_="error"):
        for node in topological_sorted(g):
            if node.is_primitive:
                variables[node.uid] = m.symbol(node.name)
                continue

            metadata = t.cast(Metadata, node.metadata)
            return_type = metadata.get("return_type", "")

            return_types = list(t.get_args(return_type) or [return_type])
            var_names = [
                f"v{i}",
                *[
                    getattr(typ, "name", typ.__name__)
                    for typ in return_types[1:]
                ],
            ]
            provider_callable = m.symbol(metadata.get("provider") or node.name)

            args = [variables[dep.uid] for dep in node.depends]
            variables[node.uid], *extra_vars = m.letN(var_names,
                                                      provider_callable(*args))
            if extra_vars:
                for sym, typ in sorted(
                        zip(extra_vars, return_types[1:]),
                        key=lambda pair: getattr(pair[1], "priority", 5),
                        reverse=True,
                ):
                    if hasattr(typ, "emit"):
                        typ.emit(m, sym)

            i += 1
        m.return_("nil")
    return m
예제 #6
0
def gen(*, m: t.Optional[Module] = None, names: t.List[str]) -> Module:
    m = m or Module()

    m.stmt("package main")
    m.sep()
    m.toplevel = m.submodule()
    m.sep()
    m.stmt("func main() {")
    with m.scope():
        m.stmt("if err := run(); err != nil {")
        with m.scope():
            log = m.toplevel.import_("log")
            m.stmt(f'{log.Fatalf}("!!%+v", err)')
        m.stmt("}")
    m.stmt("}")
    m.sep()

    visit(m, names=names)
    return m
예제 #7
0
def emit(g: Graph) -> Module:
    i = 0
    m = Module()
    variables: t.Dict[int, Symbol] = {}

    # TODO: with type
    # TODO: name
    root_args = [
        f"{node.name} string" for node in g.nodes if node.is_primitive
    ]
    with m.func("run", *root_args, return_="error"):
        for node in topological_sorted(g):
            if node.is_primitive:
                variables[node.uid] = m.symbol(node.name)
                continue

            metadata = t.cast(Metadata, node.metadata)
            return_type = metadata.get("return_type", "")

            return_types = list(t.get_args(return_type) or [return_type])
            var_names = [
                f"v{i}",
                *[
                    getattr(typ, "name", typ.__name__)
                    for typ in return_types[1:]
                ],
            ]
            provider_callable = m.symbol(metadata.get("provider") or node.name)

            args = [variables[dep.uid] for dep in node.depends]
            variables[node.uid], *extra_vars = m.letN(var_names,
                                                      provider_callable(*args))
            if extra_vars:
                for sym, typ in zip(extra_vars, return_types[1:]):
                    if hasattr(typ, "emit"):
                        typ.emit(m, sym)

            i += 1
        m.return_("nil")
    return m
예제 #8
0
def gen(*, m=None):
    m = m or Module()

    m.stmt("package main")
    m.sep()
    m.stmt("import (")
    with m.scope():
        m.stmt('"fmt"')
        m.stmt('"log"')
        m.sep()
        m.stmt('"github.com/pkg/errors"')
    m.stmt(")")
    m.sep()
    m.stmt("func main() {")
    with m.scope():
        m.stmt("if err := run(); err != nil {")
        with m.scope():
            m.stmt('log.Fatalf("!!%+v", err)')
        m.stmt("}")
    m.stmt("}")
    m.sep()

    visit(m, names=["f", "g"])
    return m
예제 #9
0
def visit_wrap(m: Module, *, name: str, prev: str) -> None:
    m.stmt(f"func {name}() error {{")
    with m.scope():
        m.stmt(f'return errors.Wrap({prev}(), "on {name}")')
    m.stmt("}")
예제 #10
0
def visit_core(m: Module, *, name: str) -> None:
    m.stmt(f"func {name}() error {{")
    with m.scope():
        m.stmt('return fmt.Errorf("xxx")')
    m.stmt("}")
예제 #11
0
def inject(
    m: Module,
    g: Graph,
    *,
    variables: t.Dict[int, Symbol],
    levels: t.Optional[t.Dict[int, int]] = None,
    nodes: t.Optional[t.List[Node]] = None,
    seen: t.Optional[t.Set[int]] = None,
    strict: bool = True,
) -> Symbol:
    # TODO: name
    i = len(variables)
    if levels is None:
        levels = defaultdict(int)
    if nodes:
        node = nodes[0]

    nodes = topological_sorted(g, seen=seen, nodes=nodes)
    for node in nodes:
        if node.is_primitive:
            if strict:
                assert node.uid in variables
            else:
                variables[node.uid] = m.symbol(node.name)
            continue

        metadata = t.cast(Metadata, node.metadata)
        return_type = metadata.get("return_type", "")

        # handling provider callable
        return_types = list(typing_get_args(return_type) or [return_type])
        var_names = [
            f"v{i}",
            *[getattr(typ, "name", typ.__name__) for typ in return_types[1:]],
        ]

        spec: t.Optional[Fnspec] = metadata.get("fnspec")
        provider_callable: t.Optional[Symbol] = None
        if spec is not None:
            provider = spec.name
            pkg = get_gopackage(metadata["fnspec"].body)
            if pkg is not None:
                pkg_prefix = m.import_(pkg)
                provider = f"{pkg_prefix}.{provider}"
            provider_callable = m.symbol(provider)
        if provider_callable is None:
            provider_callable = m.symbol(spec.name if spec else node.name)

        # handling arguments (pointer)
        if spec is None:
            args = [variables[dep.uid] for dep in node.depends]  # todo: remove
        else:
            args = []
            assert len(node.depends) == len(spec.arguments), (
                len(node.depends),
                len(spec.arguments),
            )
            for dep, (name, typ, _) in zip(node.depends,
                                           spec.arguments):  # parameters?
                sym = variables[dep.uid]

                current_level = levels[dep.uid]
                need_level = metadata["levels"].get(name, 0)  # more strict?
                level_diff = need_level - current_level

                if level_diff == 0:
                    pass
                elif level_diff > 0:
                    sym = Symbol("&" * level_diff + str(sym))
                else:
                    sym = Symbol("*" * -level_diff + str(sym))
                args.append(sym)
            levels[node.uid] = metadata["levels"]["return"]

        variables[node.uid], *extra_vars = m.letN(var_names,
                                                  provider_callable(*args))

        # handling error and cleanup:
        if extra_vars:
            for sym, typ in sorted(
                    zip(extra_vars, return_types[1:]),
                    key=lambda pair: getattr(  # type:ignore
                        pair[1], "priority", priority.NORMAL),
                    reverse=True,
            ):
                if hasattr(typ, "emit"):
                    typ.emit(m, sym)

        i += 1
    return variables[node.uid]
예제 #12
0
def emit(g: Graph) -> Module:
    i = 0
    m = Module()
    variables: t.Dict[int, Symbol] = {}

    # TODO: with type
    # TODO: name
    root_args = [f"{node.name} string" for node in g.nodes if node.is_primitive]
    with m.func("run", *root_args, return_="error"):
        for node in topological_sorted(g):
            if node.is_primitive:
                variables[node.uid] = m.symbol(node.name)
                continue

            args = [variables[dep.uid] for dep in node.depends]

            metadata = t.cast(Metadata, node.metadata)
            return_type = metadata.get("return_type", "")
            provider = m.symbol(metadata.get("provider") or node.name)

            if return_type == "":
                (variables[node.uid],) = m.letN([f"v{i}"], provider(*args))
            elif return_type == "with-err":
                variables[node.uid], err = m.letN([f"v{i}", "err"], provider(*args))
                with m.if_("err != nil"):
                    m.return_("err")
            elif return_type == "with-cleanup":
                variables[node.uid], cleanup = m.letN(
                    [f"v{i}", "cleanup"], provider(*args)
                )
                m.stmt("defer cleanup()")
            elif return_type == "with-cleanup-err":
                variables[node.uid], cleanup, err = m.letN(
                    [f"v{i}", "cleanup", "err"], provider(*args)
                )
                with m.if_("err != nil"):
                    m.return_("err")
                m.stmt("defer cleanup()")
            else:
                raise ValueError(f"unexpected return_type {return_type}")
            i += 1
        m.return_("nil")
    return m
예제 #13
0
 def emit(self, m: Module, cleanup: Symbol) -> None:
     m.stmt(f"defer {cleanup}()")
예제 #14
0
 def emit(self, m: Module, err: Symbol) -> None:
     with m.if_(f"{err} != nil"):
         m.return_(err)
예제 #15
0
                returns=return_type,
        ):
            emit_fn(m.symbol(self.receiver), *args)
        return m.symbol(methodname)


def _literal(s: str) -> t.Any:
    import json
    from prestring.utils import UnRepr

    return UnRepr(json.dumps(s))


gomethod = staticmethod


class Greeter:
    @gomethod
    def emitHello(self, message: str) -> str:
        global m
        fmt = m.import_("fmt")
        m.return_(fmt.Printf(_literal("Hello %s\n"), message))


m = Module()
m.package("greeter")
m.import_("")
emitter = MethodEmitter(Greeter)
emitter.emit(Greeter.emitHello)
print(m)
예제 #16
0
from prestring.go.codeobject import Module
from prestring import codeobject
import json

# print("**switch")
# codeobject.default_as_value = json.dumps

m = Module()
fmt = m.import_("fmt")

m.stmt("fmt.Println({})", "foo")
m.stmt("fmt.Println({!r})", "foo")

m.stmt(fmt.Println("foo"))
m.stmt("foo")
print(m)
예제 #17
0
def emit_union(m: Module, item: Item, *, resolver: Resolver) -> Definition:
    typename = goname(item.type_.__name__)
    kind_typename = typename + "Kind"

    # type <typename> {
    #     Kind string `json:"$kind"`
    # ...
    # }
    m.stmt(f"type {typename} struct {{")
    with m.scope():
        m.stmt(f'Kind {kind_typename} `json:"$kind"` // discriminator')
        for subtype in item.args:
            gotype: str = resolver.resolve_gotype(subtype)
            m.append(f"{gotype} *{gotype}")
            m.stmt(
                f' `json:"{untitleize(str(gotype)).rstrip("_")},omitempty"`')

    m.stmt("}")
    m.sep()

    # UnmarshalJSON
    discriminator_field = ("$kind", typeinfo.typeinfo(str), metadata())
    discriminator_field[-1]["_override_type"] = kind_typename

    pseudo_fields = [(sub_type.__name__, typeinfo.typeinfo(sub_type),
                      metadata(required=False)) for sub_type in item.args]
    pseudo_item = Item(
        type_=item.type_,
        fields=[discriminator_field] + pseudo_fields,
        args=[],
    )

    unmarshalJSON_definition = emit_unmarshalJSON(m,
                                                  pseudo_item,
                                                  resolver=resolver)
    m.sep()

    # one-of validation
    assert unmarshalJSON_definition.code_module is not None
    this = m.symbol(f"{item.type_.__name__[0].lower()}")
    maperr_pkg = m.import_("github.com/podhmo/maperr")

    sm = unmarshalJSON_definition.code_module
    sm.stmt("// one-of?")
    sm.stmt("{")
    with sm.scope():
        for go_name, info, _ in pseudo_item.fields[1:]:
            with sm.if_(
                    f'{this}.Kind == "{go_name}" && {this}.{go_name} == nil'):
                sm.stmt(
                    f'err = err.Add("{go_name}", {maperr_pkg}.Message{{Text: "treated as {go_name}, but no data"}})'
                )
    sm.stmt("}")

    # enums
    emit_enums(m, item.type_, resolver=resolver, name=kind_typename)

    definition = Definition(name=typename, code_module=None)
    return definition
예제 #18
0
def emit_struct(m: Module, item: Item, *, resolver: Resolver) -> Definition:
    gopackage = get_gopackage(item.type_)
    if gopackage is not None:
        return ""

    typename = str(resolver.resolve_gotype(item.type_))

    # // <typename> ...
    doc = inspect.getdoc(item.type_)
    if doc:
        lines = doc.split("\n")
        m.stmt(f"// {typename} {lines[0]}")
        for line in lines[1:]:
            m.stmt(f"// {line}")

    # type <typename> struct {
    # ...
    # }
    m.stmt(f"type {typename} struct {{")
    with m.scope():
        for name, info, metadata in item.fields:
            gotype: str = resolver.resolve_gotype(info.raw)

            # handling field (private field?, embedded?)
            if metadata.get("inline", False):
                m.append(gotype)
            elif name.startswith("_"):
                m.append(f"{untitleize(goname(name))} {gotype}")
            else:
                m.append(f"{goname(name)} {gotype}")

            # todo: handling tags
            if "tags" not in metadata:
                metadata["tags"] = {}
            metadata["tags"] = {"json": [name.rstrip("_")]}
            m.append(f" `{build_gotags(metadata['tags'])}`")

            # handling comments
            if metadata.get("inline", False):
                m.stmt(f"  // {metadata}")
            else:
                comment = metadata.get("comment", "")
                m.stmt(
                    f"  // {comment.split(_NEWLINE, 1)[0]}" if comment else "")

    definition = Definition(name=typename, code_module=None)
    m.stmt("}")
    return definition
예제 #19
0
def emit_unmarshalJSON(m: Module, item: Item, *,
                       resolver: Resolver) -> Definition:
    this = m.symbol(f"{item.type_.__name__[0].lower()}")
    this_type = f"{resolver.resolve_gotype(item.type_)}"
    this_type_pointer = f"*{this_type}"

    # func (ob *Ob) UnmarshalJSON(b []byte) error {
    b = m.symbol("b")
    m.stmt(
        f"func ({this} {this_type_pointer}) UnmarshalJSON({b} []byte) error {{"
    )
    with m.scope():

        # var err *maperr.Error
        err = m.symbol("err")
        maperr_pkg = m.import_("github.com/podhmo/maperr")
        m.stmt(f"var {err} *{maperr_pkg}.Error")
        m.sep()

        # var inner struct {
        #   ...
        # }
        m.stmt("// loading internal data")
        inner = m.symbol("inner")
        m.stmt(f"var {inner} struct {{")
        with m.scope():
            for name, info, metadata in item.fields:
                if name.startswith("_"):
                    continue  # xxx:

                if "_override_type" in metadata:
                    gotype: str = metadata["_override_type"]
                elif has_class_object(info):
                    json_pkg = m.import_("encoding/json")
                    gotype: str = str(json_pkg.RawMessage)
                else:
                    gotype: str = resolver.resolve_gotype(info.raw)

                m.append(f'{goname(name)} *{gotype} `json:"{name}"`')
                m.stmt("// required" if metadata["required"] else "")
        m.stmt("}")

        # if rawErr := json.Unmarshal(b, &inner); rawErr != nil {
        # ...
        # }
        json_pkg = m.import_("encoding/json")
        raw_err = m.symbol("rawErr")
        with m.if_(
                f"{raw_err} := {json_pkg}.Unmarshal(b, &{inner}); {raw_err} != nil"
        ):
            m.return_(err.AddSummary(raw_err.Error()))
        m.sep()

        # if <field> != nil {
        #     ob.<field> = *<field>
        # } else {
        #     m.add(<field>, "required")
        # }
        rawerr = m.symbol("rawerr")
        m.stmt("// binding field value and required check")
        with m.block():
            for name, info, metadata in item.fields:
                field = m.symbol(goname(name))
                with m.if_(f"{inner}.{field} != nil"):
                    if has_class_object(info):
                        # pointer
                        if info.is_optional:
                            gotype: str = resolver.resolve_gotype(
                                info.normalized)
                            m.stmt(f"{this}.{goname(name)} = &{gotype}{{}}")
                            ref = f"{this}.{field}"
                        elif hasattr(info, "args"):  # xxx
                            gotype: str = resolver.resolve_gotype(
                                info.normalized)
                            m.stmt(f"{this}.{goname(name)} = {gotype}{{}}")
                            ref = f"&{this}.{field}"
                        else:
                            ref = f"&{this}.{field}"

                        with m.if_(
                                f"{rawerr} := json.Unmarshal(*{inner}.{field}, {ref}); {rawerr} != nil"
                        ):
                            m.stmt(
                                f'{err} = {err}.Add("{name}", {maperr_pkg}.Message{{Error: {rawerr}}})'
                            )
                    else:
                        m.stmt(f"{this}.{field} = *{inner}.{field}")
                if metadata["required"]:
                    with m.else_():
                        m.stmt(
                            f'{err} = err.Add("{name}", {maperr_pkg}.Message{{Text: "required"}})'
                        )
        m.sep()

        # NOTE: for injecting code from extrnal area
        code_module = m.submodule("", newline=False)

        # return err.Untyped()
        m.return_(err.Untyped())

    m.stmt("}")
    return Definition(name="UnmarshalJSON", code_module=code_module)
예제 #20
0
def emit_enums(
    m: Module,
    literal_type: t.Type[t.Any],
    *,
    resolver: Resolver,
    name: t.Optional[str] = None,
) -> str:
    # literal_type or union_type
    go_type = name or f"{resolver.resolve_gotype(literal_type)}"

    first_of_args = t.get_args(literal_type)[0]
    base_go_type = resolver.resolve_gotype(
        type(getattr(first_of_args, "__name__", first_of_args)))

    const_names = [getattr(x, "__name__", x) for x in t.get_args(literal_type)]
    const_members = {name: f"{go_type}{goname(name)}" for name in const_names}
    this = m.symbol("v")
    as_literal = resolver.resolve_default

    # type <enum> string
    m.stmt(f"type {go_type} {base_go_type}")
    m.sep()

    # const (
    #     <enum>xxx <enum> = "xxx"
    # ...
    # )
    with m.const_group() as cg:
        for name in const_names:
            cg(f"{const_members[name]} {go_type} = {as_literal(type(name), name)}"
               )
    m.sep()

    # func (v <enum>) Valid() error {
    # ...
    # }
    with m.method(f"{this} {go_type}", "Valid", returns="error"):
        with m.switch(this) as sm:
            with sm.case(", ".join(const_members.values())):
                sm.return_("nil")
            with sm.default() as sm:
                fmt_pkg = m.import_("fmt")
                candidates = ", ".join([str(x) for x in const_names])
                sm.return_(
                    fmt_pkg.Errorf(
                        as_literal(
                            str,
                            f"%q is invalid enum value of ({candidates})"),
                        this,
                    ))
        sm.unnewline()

    # func (v <enum>) UnmarshalJSON(b []byte) error {
    # ...
    # }
    with m.method(f"{this} *{go_type}",
                  "UnmarshalJSON",
                  f"b []byte",
                  returns="error"):
        strings_pkg = m.import_("strings")
        m.stmt(f'*{this} = {go_type}({strings_pkg}.Trim(string(b), `"`))')
        m.return_(this.Valid())

    definition = Definition(name=go_type, code_module=None)
    return definition
예제 #21
0
        methodname = goname(spec.name)
        self = m.symbol(receiver.lstrip("* ")[0].lower())

        # todo: type -> gotype
        args = [m.symbol(name) for name, _, _, in spec.arguments]
        return_type = "string"

        with m.method(
                f"{self} *{receiver}",
                methodname,
                *[f"{x} string" for x in args],
                returns=return_type,
        ):
            emit_fn(self, *args)
        return m.symbol(methodname)

    return _emit


@codeobject
def emitHello(self, name: str) -> str:
    global m
    fmt = m.import_("fmt")
    m.return_(fmt.Printf(_literal("Hello %s\n"), name))


m = Module()
m.import_("")
emitHello(receiver="Greeter")
print(m)
예제 #22
0
 def emit(self, m: Module, teardown: Symbol) -> None:
     m.stmt(f"defer {teardown}()")