From 14be727777a4eee2be260e3d4d77045ae6afd789 Mon Sep 17 00:00:00 2001 From: "Diego Augusto S. C" Date: Fri, 3 Nov 2023 19:10:12 -0300 Subject: [PATCH] Implement array value Adds ArrayValue docs, and tests for diagnostics. --- blueprintcompiler/gir.py | 4 ++ blueprintcompiler/language/__init__.py | 1 + .../language/gobject_property.py | 8 +-- blueprintcompiler/language/values.py | 50 +++++++++++++++++++ blueprintcompiler/outputs/xml/__init__.py | 9 ++++ docs/reference/values.rst | 12 ++++- .../sample_errors/newline_in_string_array.blp | 6 +++ .../sample_errors/newline_in_string_array.err | 1 + .../newline_in_string_array_multi.blp | 6 +++ .../newline_in_string_array_multi.err | 2 + tests/samples/string_array.blp | 6 +++ tests/samples/string_array.ui | 14 ++++++ 12 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 tests/sample_errors/newline_in_string_array.blp create mode 100644 tests/sample_errors/newline_in_string_array.err create mode 100644 tests/sample_errors/newline_in_string_array_multi.blp create mode 100644 tests/sample_errors/newline_in_string_array_multi.err create mode 100644 tests/samples/string_array.blp create mode 100644 tests/samples/string_array.ui diff --git a/blueprintcompiler/gir.py b/blueprintcompiler/gir.py index 142bbd2..35be84e 100644 --- a/blueprintcompiler/gir.py +++ b/blueprintcompiler/gir.py @@ -200,6 +200,10 @@ class ArrayType(GirType): def assignable_to(self, other: GirType) -> bool: return isinstance(other, ArrayType) and self._inner.assignable_to(other._inner) + @property + def inner(self) -> GirType: + return self._inner + @property def name(self) -> str: return self._inner.name + "[]" diff --git a/blueprintcompiler/language/__init__.py b/blueprintcompiler/language/__init__.py index 1e183c9..b302686 100644 --- a/blueprintcompiler/language/__init__.py +++ b/blueprintcompiler/language/__init__.py @@ -41,6 +41,7 @@ from .imports import GtkDirective, Import from .types import ClassName from .ui import UI from .values import ( + ArrayValue, Flag, Flags, IdentLiteral, diff --git a/blueprintcompiler/language/gobject_property.py b/blueprintcompiler/language/gobject_property.py index 3d2ac5b..5d0c867 100644 --- a/blueprintcompiler/language/gobject_property.py +++ b/blueprintcompiler/language/gobject_property.py @@ -22,18 +22,20 @@ from .binding import Binding from .common import * from .contexts import ValueTypeCtx from .gtkbuilder_template import Template -from .values import ObjectValue, Value +from .values import ArrayValue, ObjectValue, Value class Property(AstNode): - grammar = Statement(UseIdent("name"), ":", AnyOf(Binding, ObjectValue, Value)) + grammar = Statement( + UseIdent("name"), ":", AnyOf(Binding, ObjectValue, Value, ArrayValue) + ) @property def name(self) -> str: return self.tokens["name"] @property - def value(self) -> T.Union[Binding, ObjectValue, Value]: + def value(self) -> T.Union[Binding, ObjectValue, Value, ArrayValue]: return self.children[0] @property diff --git a/blueprintcompiler/language/values.py b/blueprintcompiler/language/values.py index b9d719f..c1f0b21 100644 --- a/blueprintcompiler/language/values.py +++ b/blueprintcompiler/language/values.py @@ -19,6 +19,8 @@ import typing as T +from blueprintcompiler.gir import ArrayType + from .common import * from .contexts import ScopeCtx, ValueTypeCtx from .gobject_object import Object @@ -395,6 +397,54 @@ class Value(AstNode): return self.children[0] +class ArrayValue(AstNode): + grammar = ["[", Delimited(Value, ","), "]"] + + @validate() + def validate_for_type(self) -> None: + expected_type = self.gir_type + if expected_type is not None and not isinstance(expected_type, gir.ArrayType): + raise CompileError(f"Cannot assign array to {expected_type.full_name}") + + if expected_type is not None and not isinstance( + expected_type.inner, StringType + ): + raise CompileError("Only string arrays are supported") + + @validate() + def validate_invalid_newline(self) -> None: + expected_type = self.gir_type + if expected_type is not None and isinstance(expected_type.inner, StringType): + errors = [] + for value in self.values: + if isinstance(value.child, Literal) and isinstance( + value.child.value, QuotedLiteral + ): + quoted_literal = value.child.value + literal_value = quoted_literal.value + if "\n" in literal_value: + errors.append( + CompileError( + "String literals inside arrays can't contain newlines", + range=quoted_literal.range, + ) + ) + if len(errors) > 0: + raise MultipleErrors(errors) + + @property + def values(self) -> T.List[Value]: + return self.children + + @property + def gir_type(self): + return self.parent.context[ValueTypeCtx].value_type + + @context(ValueTypeCtx) + def child_value(self): + return ValueTypeCtx(self.gir_type.inner) + + class StringValue(AstNode): grammar = AnyOf(Translated, QuotedLiteral) diff --git a/blueprintcompiler/outputs/xml/__init__.py b/blueprintcompiler/outputs/xml/__init__.py index f10fadf..a7f4b4b 100644 --- a/blueprintcompiler/outputs/xml/__init__.py +++ b/blueprintcompiler/outputs/xml/__init__.py @@ -139,6 +139,15 @@ class XmlOutput(OutputFormat): self._emit_object(value.object, xml) xml.end_tag() + elif isinstance(value, ArrayValue): + xml.start_tag("property", **props) + values = list(value.values) + for value in values[:-1]: + self._emit_value(value, xml) + xml.put_text("\n") + self._emit_value(values[-1], xml) + xml.end_tag() + else: raise CompilerBugError() diff --git a/docs/reference/values.rst b/docs/reference/values.rst index 7c61d78..95f6bc8 100644 --- a/docs/reference/values.rst +++ b/docs/reference/values.rst @@ -145,7 +145,6 @@ Example label: _("This is an advanced feature. Use with caution!"); } - .. _Syntax ObjectValue: Object Values @@ -170,3 +169,14 @@ String Values StringValue = :ref:`Translated` | :ref:`QuotedLiteral` Menus, as well as some :ref:`extensions`, have properties that can only be string literals or translated strings. + +.. _Syntax ArrayValue: + +Array Values +------------- + +.. rst-class:: grammar-block + + ArrayValue = '[' (:ref:`StringValue`),* ']' + +For now, it only supports :ref:`Strings`. This is because Gtk.Builder only supports string arrays. diff --git a/tests/sample_errors/newline_in_string_array.blp b/tests/sample_errors/newline_in_string_array.blp new file mode 100644 index 0000000..5a6278b --- /dev/null +++ b/tests/sample_errors/newline_in_string_array.blp @@ -0,0 +1,6 @@ +using Gtk 4.0; + +AboutDialog about { + valign: center; + authors: ["Olimar\n"]; +} diff --git a/tests/sample_errors/newline_in_string_array.err b/tests/sample_errors/newline_in_string_array.err new file mode 100644 index 0000000..451f28e --- /dev/null +++ b/tests/sample_errors/newline_in_string_array.err @@ -0,0 +1 @@ +5,15,10,String literals inside arrays can't contain newlines diff --git a/tests/sample_errors/newline_in_string_array_multi.blp b/tests/sample_errors/newline_in_string_array_multi.blp new file mode 100644 index 0000000..627f886 --- /dev/null +++ b/tests/sample_errors/newline_in_string_array_multi.blp @@ -0,0 +1,6 @@ +using Gtk 4.0; + +AboutDialog about { + valign: center; + authors: ["Olimar\n", "Luie\nPresident"]; +} diff --git a/tests/sample_errors/newline_in_string_array_multi.err b/tests/sample_errors/newline_in_string_array_multi.err new file mode 100644 index 0000000..6f37d31 --- /dev/null +++ b/tests/sample_errors/newline_in_string_array_multi.err @@ -0,0 +1,2 @@ +5,15,10,String literals inside arrays can't contain newlines +5,27,17,String literals inside arrays can't contain newlines diff --git a/tests/samples/string_array.blp b/tests/samples/string_array.blp new file mode 100644 index 0000000..4f8ac5b --- /dev/null +++ b/tests/samples/string_array.blp @@ -0,0 +1,6 @@ +using Gtk 4.0; + +AboutDialog about { + valign: center; + authors: ["Jane doe ", "Jhonny D "]; +} diff --git a/tests/samples/string_array.ui b/tests/samples/string_array.ui new file mode 100644 index 0000000..03f60ac --- /dev/null +++ b/tests/samples/string_array.ui @@ -0,0 +1,14 @@ + + + + + + 3 + Jane doe <jane-doe@email.com> +Jhonny D <jd@email.com> + +