Beispiel #1
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
Beispiel #2
0
def emit(g: Graph) -> Module:
    i = 0
    m = Module()
    variables: t.Dict[int, Symbol] = {}

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

            args = []
            for dep in node.depends:
                args.append(variables[dep.uid])
            m.letN
            return_type = node.metadata.get("return_type", "")
            if return_type == "":
                [variables[node.uid]] = m.letN([f"v{i}"],
                                               m.symbol(node.name)(*args))
            elif return_type == "with-err":
                variables[node.uid], err = m.letN((f"v{i}", "err"),
                                                  m.symbol(node.name)(*args))
                with m.if_("err != nil"):
                    m.return_("err")
            elif return_type == "with-cleanup":
                variables[node.uid], cleanup = m.letN(
                    (f"v{i}", "cleanup"),
                    m.symbol(node.name)(*args))
                m.stmt("defer cleanup()")
            elif return_type == "with-cleanup-err":
                variables[node.uid], cleanup, err = m.letN(
                    (f"v{i}", "cleanup", "err"),
                    m.symbol(node.name)(*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
Beispiel #3
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("}")
Beispiel #4
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("}")
Beispiel #5
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)
Beispiel #6
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("}")
Beispiel #7
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("}")
Beispiel #8
0
 def emit(self, m: Module, cleanup: Symbol) -> None:
     m.stmt(f"defer {cleanup}()")
Beispiel #9
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
Beispiel #10
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)
Beispiel #11
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
Beispiel #12
0
 def emit(self, m: Module, teardown: Symbol) -> None:
     m.stmt(f"defer {teardown}()")
Beispiel #13
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)