diff --git a/blueprintcompiler/decompiler.py b/blueprintcompiler/decompiler.py index 2663a09..ac5ca96 100644 --- a/blueprintcompiler/decompiler.py +++ b/blueprintcompiler/decompiler.py @@ -42,6 +42,19 @@ _NAMESPACES = [ ] +@dataclass +class TextFragment: + text: str + newline_after: bool = True + indent: bool = True + + +@dataclass +class BlockInfo: + terminator: T.Optional[TextFragment] = None + separator: T.Optional[TextFragment] = None + + class LineType(Enum): NONE = 1 STMT = 2 @@ -54,7 +67,7 @@ class DecompileCtx: self._result: str = "" self.gir = GirContext() self._indent: int = 0 - self._blocks_need_end: T.List[str] = [] + self._blocks: T.List[BlockInfo] = [] self._last_line_type: LineType = LineType.NONE self.template_class: T.Optional[str] = None @@ -86,16 +99,25 @@ class DecompileCtx: return None def start_block(self) -> None: - self._blocks_need_end.append("") + self._blocks.append(BlockInfo()) def end_block(self) -> None: - if close := self._blocks_need_end.pop(): - self.print(close) + block = self._blocks.pop() + if terminator := block.terminator: + self.print(terminator.text, terminator.newline_after, terminator.indent) - def end_block_with(self, text: str) -> None: - self._blocks_need_end[-1] = text + def separate_block_elements(self) -> None: + block = self._blocks[-1] + if separator := block.separator: + self.print(separator.text, separator.newline_after, separator.indent) - def print(self, line: str, newline: bool = True) -> None: + def end_block_with(self, text: str, newline_after: bool = True, indent: bool = True) -> None: + self._blocks[-1].terminator = TextFragment(text, newline_after, indent) + + def separate_block_elements_with(self, text: str, newline_after: bool = False, indent: bool = False) -> None: + self._blocks[-1].separator = TextFragment(text, newline_after, indent) + + def print(self, line: str, newline: bool = True, indent: bool = True) -> None: if line == "}" or line == "]": self._indent -= 1 @@ -117,13 +139,13 @@ class DecompileCtx: self._result += "\n" self._last_line_type = line_type - self._result += (" " * self._indent) + line + self._result += (" " * self._indent * indent) + line if newline: self._result += "\n" if line.endswith("{") or line.endswith("["): - if len(self._blocks_need_end): - self._blocks_need_end[-1] = _CLOSING[line[-1]] + if len(self._blocks): + self._blocks[-1].terminator = TextFragment(_CLOSING[line[-1]]) self._indent += 1 def print_attribute(self, name: str, value: str, type: GirType) -> None: @@ -193,7 +215,11 @@ def _decompile_element( ctx.start_block() gir = decompiler(ctx, gir, **args) + first = True for child in xml.children: + if not first: + ctx.separate_block_elements() + first = False _decompile_element(ctx, gir, child) ctx.end_block() @@ -331,6 +357,45 @@ def decompile_property( return gir +@decompiler("binding") +def decompile_binding(ctx: DecompileCtx, gir, name): + name = name.replace("_", "-") + ctx.print(f"{name}: bind ", newline=False) + ctx.end_block_with(";") + return gir + + +@decompiler("lookup", cdata=True) +def decompile_lookup(ctx: DecompileCtx, gir, name, cdata, type=None): + name = name.replace("_", "-") + + if cdata is not None: + ctx.print(cdata, newline=False) + + cast = f" as <{type}>" if type is not None else "" + ctx.end_block_with(f"{cast}.{name}", newline_after=False, indent=False) + + return gir + + +@decompiler("constant", cdata=True) +def decompile_constant(ctx: DecompileCtx, gir, cdata, type=None): + cast = f" as <{type}>" if type is not None else "" + ctx.print(f"{cdata}{cast}", newline=False, indent=False) + + return gir + + +@decompiler("closure") +def decompile_closure(ctx: DecompileCtx, gir, function, type): + ctx.print(f"{function}(", newline=False) + ctx.separate_block_elements_with(f", ") + cast = f" as <{type}>" + ctx.end_block_with(f"){cast}", newline_after=False, indent=False) + + return gir + + @decompiler("attribute", cdata=True) def decompile_attribute( ctx, gir, name, cdata, translatable="false", comments=None, context=None diff --git a/tests/samples/binding1.blp b/tests/samples/binding1.blp new file mode 100644 index 0000000..a73b83a --- /dev/null +++ b/tests/samples/binding1.blp @@ -0,0 +1,5 @@ +using Gtk 4.0; + +DropDown { + expression: bla as ; +} diff --git a/tests/samples/binding1.ui b/tests/samples/binding1.ui new file mode 100644 index 0000000..f1af768 --- /dev/null +++ b/tests/samples/binding1.ui @@ -0,0 +1,7 @@ + + + + bla + + + diff --git a/tests/samples/binding2.blp b/tests/samples/binding2.blp new file mode 100644 index 0000000..7ccc7b9 --- /dev/null +++ b/tests/samples/binding2.blp @@ -0,0 +1,5 @@ +using Gtk 4.0; + +DropDown { + expression: bla as ; +} diff --git a/tests/samples/binding2.ui b/tests/samples/binding2.ui new file mode 100644 index 0000000..2fe929d --- /dev/null +++ b/tests/samples/binding2.ui @@ -0,0 +1,7 @@ + + + + bla + + + diff --git a/tests/samples/binding3.blp b/tests/samples/binding3.blp new file mode 100644 index 0000000..8f64e2c --- /dev/null +++ b/tests/samples/binding3.blp @@ -0,0 +1,5 @@ +using Gtk 4.0; + +DropDown { + expression: bla ; +} diff --git a/tests/samples/binding3.ui b/tests/samples/binding3.ui new file mode 100644 index 0000000..1e3180d --- /dev/null +++ b/tests/samples/binding3.ui @@ -0,0 +1,7 @@ + + + + bla + + + diff --git a/tests/samples/binding4.blp b/tests/samples/binding4.blp new file mode 100644 index 0000000..5cfb03f --- /dev/null +++ b/tests/samples/binding4.blp @@ -0,0 +1,5 @@ +using Gtk 4.0; + +DropDown { + expression: bla as ; +} diff --git a/tests/samples/binding4.ui b/tests/samples/binding4.ui new file mode 100644 index 0000000..ba810ea --- /dev/null +++ b/tests/samples/binding4.ui @@ -0,0 +1,7 @@ + + + + bla + + + diff --git a/tests/samples/binding5.blp b/tests/samples/binding5.blp new file mode 100644 index 0000000..a52700c --- /dev/null +++ b/tests/samples/binding5.blp @@ -0,0 +1,8 @@ +using Gtk 4.0; + +Label bla { +} + +DropDown { + expression: bla.label ; +} diff --git a/tests/samples/binding5.ui b/tests/samples/binding5.ui new file mode 100644 index 0000000..1020b4e --- /dev/null +++ b/tests/samples/binding5.ui @@ -0,0 +1,8 @@ + + + + + bla + + + diff --git a/tests/samples/binding6.blp b/tests/samples/binding6.blp new file mode 100644 index 0000000..9e72a31 --- /dev/null +++ b/tests/samples/binding6.blp @@ -0,0 +1,8 @@ +using Gtk 4.0; + +Label bla { +} + +DropDown { + expression: bla as .label ; +} diff --git a/tests/samples/binding6.ui b/tests/samples/binding6.ui new file mode 100644 index 0000000..e2fc43c --- /dev/null +++ b/tests/samples/binding6.ui @@ -0,0 +1,8 @@ + + + + + bla + + + diff --git a/tests/samples/binding7.blp b/tests/samples/binding7.blp new file mode 100644 index 0000000..86d8df3 --- /dev/null +++ b/tests/samples/binding7.blp @@ -0,0 +1,8 @@ +using Gtk 4.0; + +Label bla { +} + +DropDown { + expression: bla as .label ; +} diff --git a/tests/samples/binding7.ui b/tests/samples/binding7.ui new file mode 100644 index 0000000..93516b9 --- /dev/null +++ b/tests/samples/binding7.ui @@ -0,0 +1,10 @@ + + + + + + bla + + + + diff --git a/tests/samples/binding8.blp b/tests/samples/binding8.blp new file mode 100644 index 0000000..442b7b8 --- /dev/null +++ b/tests/samples/binding8.blp @@ -0,0 +1,8 @@ +using Gtk 4.0; + +Label bla { +} + +DropDown { + expression: strcmp('File size:', bla as .max-width-chars) as ; +} diff --git a/tests/samples/binding8.ui b/tests/samples/binding8.ui new file mode 100644 index 0000000..46f85a4 --- /dev/null +++ b/tests/samples/binding8.ui @@ -0,0 +1,11 @@ + + + + + + File size: + bla + + + + diff --git a/tests/test_samples.py b/tests/test_samples.py index 38b72d6..6289f60 100644 --- a/tests/test_samples.py +++ b/tests/test_samples.py @@ -174,6 +174,14 @@ class TestSamples(unittest.TestCase): REQUIRE_ADW_1_4 = ["adw_breakpoint"] SKIP_RUN = [ + "binding1", + "binding2", + "binding3", + "binding4", + "binding5", + "binding6", + "binding7", + "binding8", "expr_closure", "expr_closure_args", "parseable", @@ -211,6 +219,14 @@ class TestSamples(unittest.TestCase): def test_decompiler(self): self.assert_decompile("accessibility_dec") + self.assert_decompile("binding1") + self.assert_decompile("binding2") + self.assert_decompile("binding3") + self.assert_decompile("binding4") + self.assert_decompile("binding5") + self.assert_decompile("binding6") + self.assert_decompile("binding7") + self.assert_decompile("binding8") self.assert_decompile("child_type") self.assert_decompile("file_filter") self.assert_decompile("flags")