decompiler: Add more decompilable tags

Add more tags to the list of things the decompiler can handle. This
required some changes to track the containing object class in the
DecompileCtx, since some objects use the same tag names.

The improved support means we can test the decompiler on most of the
test suite. Any new test samples will by default be tested to ensure the
decompiler produces the original blueprint file.

Also, updated the decompiler to always use double quotes.
This commit is contained in:
James Westman 2024-04-06 13:36:22 -05:00
parent ea4c7245be
commit c1a82a034b
49 changed files with 396 additions and 151 deletions

View file

@ -107,3 +107,9 @@ class SimpleBinding:
no_sync_create: bool = False
bidirectional: bool = False
inverted: bool = False
@decompiler("binding")
def decompile_binding(ctx: DecompileCtx, gir: gir.GirContext, name: str):
ctx.end_block_with(";")
ctx.print(f"{name}: bind ")

View file

@ -18,9 +18,9 @@
# SPDX-License-Identifier: LGPL-3.0-or-later
from ..decompiler import decompile_element
from .common import *
from .contexts import ScopeCtx, ValueTypeCtx
from .gtkbuilder_template import Template
from .types import TypeName
expr = Sequence()
@ -274,3 +274,69 @@ expr.children = [
AnyOf(ClosureExpr, LiteralExpr, ["(", Expression, ")"]),
ZeroOrMore(AnyOf(LookupOp, CastExpr)),
]
@decompiler("lookup", skip_children=True, cdata=True)
def decompile_lookup(
ctx: DecompileCtx, gir: gir.GirContext, cdata: str, name: str, type: str
):
if t := ctx.type_by_cname(type):
type = decompile.full_name(t)
else:
type = "$" + type
assert ctx.current_node is not None
constant = None
if len(ctx.current_node.children) == 0:
constant = cdata
elif (
len(ctx.current_node.children) == 1
and ctx.current_node.children[0].tag == "constant"
):
constant = ctx.current_node.children[0].cdata
if constant is not None:
if constant == ctx.template_class:
ctx.print("template." + name)
else:
ctx.print(constant + "." + name)
return
else:
for child in ctx.current_node.children:
decompile.decompile_element(ctx, gir, child)
ctx.print(f" as <{type}>.{name}")
@decompiler("constant", cdata=True)
def decompile_constant(
ctx: DecompileCtx, gir: gir.GirContext, cdata: str, type: T.Optional[str] = None
):
if type is None:
if cdata == ctx.template_class:
ctx.print("template")
else:
ctx.print(cdata)
else:
ctx.print_value(cdata, ctx.type_by_cname(type))
@decompiler("closure", skip_children=True)
def decompile_closure(ctx: DecompileCtx, gir: gir.GirContext, function: str, type: str):
if t := ctx.type_by_cname(type):
type = decompile.full_name(t)
else:
type = "$" + type
ctx.print(f"${function}(")
assert ctx.current_node is not None
for i, node in enumerate(ctx.current_node.children):
decompile_element(ctx, gir, node)
assert ctx.current_node is not None
if i < len(ctx.current_node.children) - 1:
ctx.print(", ")
ctx.end_block_with(f") as <{type}>")

View file

@ -115,7 +115,7 @@ def validate_parent_type(node, ns: str, name: str, err_msg: str):
@decompiler("object")
def decompile_object(ctx, gir, klass, id=None):
def decompile_object(ctx: DecompileCtx, gir, klass, id=None):
gir_class = ctx.type_by_cname(klass)
klass_name = (
decompile.full_name(gir_class) if gir_class is not None else "$" + klass
@ -124,4 +124,5 @@ def decompile_object(ctx, gir, klass, id=None):
ctx.print(f"{klass_name} {{")
else:
ctx.print(f"{klass_name} {id} {{")
ctx.push_obj_type(gir_class)
return gir_class

View file

@ -89,3 +89,29 @@ class ExtComboBoxItems(AstNode):
)
def items_completer(lsp, ast_node, match_variables):
yield Completion("items", CompletionItemKind.Snippet, snippet="items [$0]")
@decompiler("items", parent_type="Gtk.ComboBoxText")
def decompile_items(ctx: DecompileCtx, gir: gir.GirContext):
ctx.print("items [")
@decompiler("item", parent_type="Gtk.ComboBoxText", cdata=True)
def decompile_item(
ctx: DecompileCtx,
gir: gir.GirContext,
cdata: str,
id: T.Optional[str] = None,
translatable="false",
comments=None,
context=None,
):
comments, translatable = decompile_translatable(
cdata, translatable, context, comments
)
if comments:
ctx.print(comments)
if id:
ctx.print(f"{id}: ")
ctx.print(translatable)
ctx.print(",")

View file

@ -266,7 +266,7 @@ def decompile_submenu(ctx, gir, id=None):
ctx.print("submenu {")
@decompiler("item")
@decompiler("item", parent_tag="menu")
def decompile_item(ctx, gir, id=None):
if id:
ctx.print(f"item {id} {{")

View file

@ -96,3 +96,13 @@ class ExtSizeGroupWidgets(AstNode):
)
def size_group_completer(lsp, ast_node, match_variables):
yield Completion("widgets", CompletionItemKind.Snippet, snippet="widgets [$0]")
@decompiler("widgets")
def size_group_decompiler(ctx, gir: gir.GirContext):
ctx.print("widgets [")
@decompiler("widget")
def widget_decompiler(ctx, gir: gir.GirContext, name: str):
ctx.print(name + ",")

View file

@ -73,3 +73,25 @@ class ExtStringListStrings(AstNode):
)
def strings_completer(lsp, ast_node, match_variables):
yield Completion("strings", CompletionItemKind.Snippet, snippet="strings [$0]")
@decompiler("items", parent_type="Gtk.StringList")
def decompile_strings(ctx: DecompileCtx, gir: gir.GirContext):
ctx.print("strings [")
@decompiler("item", cdata=True, parent_type="Gtk.StringList")
def decompile_item(
ctx: DecompileCtx,
gir: gir.GirContext,
translatable="false",
comments=None,
context=None,
cdata=None,
):
comments, translatable = decompile_translatable(
cdata, translatable, context, comments
)
if comments is not None:
ctx.print(comments)
ctx.print(translatable + ",")