diff --git a/blueprintcompiler/language/binding.py b/blueprintcompiler/language/binding.py index ab544ca..b2dee10 100644 --- a/blueprintcompiler/language/binding.py +++ b/blueprintcompiler/language/binding.py @@ -47,11 +47,6 @@ class Binding(AstNode): ) return None - @validate("bind") - def not_bindable(self) -> None: - if binding_error := self.context[ValueTypeCtx].binding_error: - raise binding_error - @dataclass class SimpleBinding: diff --git a/blueprintcompiler/language/contexts.py b/blueprintcompiler/language/contexts.py index f5b92c2..fec38f7 100644 --- a/blueprintcompiler/language/contexts.py +++ b/blueprintcompiler/language/contexts.py @@ -25,4 +25,3 @@ from .common import * @dataclass class ValueTypeCtx: value_type: T.Optional[GirType] - binding_error: T.Optional[CompileError] = None diff --git a/blueprintcompiler/language/gobject_property.py b/blueprintcompiler/language/gobject_property.py index 18b91ae..6beb738 100644 --- a/blueprintcompiler/language/gobject_property.py +++ b/blueprintcompiler/language/gobject_property.py @@ -22,7 +22,7 @@ from dataclasses import dataclass from .expression import ExprChain from .gobject_object import Object from .gtkbuilder_template import Template -from .values import Value, Translated +from .values import Value, ObjectValue from .common import * from .contexts import ValueTypeCtx from .property_binding import PropertyBinding @@ -30,14 +30,16 @@ from .binding import Binding class Property(AstNode): - grammar = Statement(UseIdent("name"), ":", Value) + grammar = Statement( + UseIdent("name"), ":", AnyOf(PropertyBinding, Binding, ObjectValue, Value) + ) @property def name(self) -> str: return self.tokens["name"] @property - def value(self) -> Value: + def value(self) -> T.Union[PropertyBinding, Binding, ObjectValue, Value]: return self.children[0] @property @@ -49,29 +51,26 @@ class Property(AstNode): if self.gir_class is not None and not isinstance(self.gir_class, ExternType): return self.gir_class.properties.get(self.tokens["name"]) - @context(ValueTypeCtx) - def value_type(self) -> ValueTypeCtx: + @validate() + def binding_valid(self): if ( - ( - isinstance(self.value.child, PropertyBinding) - or isinstance(self.value.child, Binding) - ) + (isinstance(self.value, PropertyBinding) or isinstance(self.value, Binding)) and self.gir_property is not None and self.gir_property.construct_only ): - binding_error = CompileError( + raise CompileError( f"{self.gir_property.full_name} can't be bound because it is construct-only", hints=["construct-only properties may only be set to a static value"], ) - else: - binding_error = None + @context(ValueTypeCtx) + def value_type(self) -> ValueTypeCtx: if self.gir_property is not None: type = self.gir_property.type else: type = None - return ValueTypeCtx(type, binding_error) + return ValueTypeCtx(type) @validate("name") def property_exists(self): diff --git a/blueprintcompiler/language/property_binding.py b/blueprintcompiler/language/property_binding.py index 5314934..ba8a29d 100644 --- a/blueprintcompiler/language/property_binding.py +++ b/blueprintcompiler/language/property_binding.py @@ -121,11 +121,6 @@ class PropertyBinding(AstNode): f"{gir_class.full_name} does not have a property called {self.property_name}" ) - @validate("bind-property") - def not_bindable(self) -> None: - if binding_error := self.context[ValueTypeCtx].binding_error: - raise binding_error - @validate("bind") def old_bind(self): if self.tokens["bind"]: diff --git a/blueprintcompiler/language/values.py b/blueprintcompiler/language/values.py index bfc17c6..a1666cc 100644 --- a/blueprintcompiler/language/values.py +++ b/blueprintcompiler/language/values.py @@ -361,12 +361,12 @@ class ObjectValue(AstNode): class Value(AstNode): - grammar = AnyOf(PropertyBinding, Binding, Translated, ObjectValue, Flags, Literal) + grammar = AnyOf(Translated, Flags, Literal) @property def child( self, - ) -> T.Union[PropertyBinding, Binding, Translated, ObjectValue, Flags, Literal]: + ) -> T.Union[Translated, Flags, Literal]: return self.children[0] diff --git a/blueprintcompiler/outputs/xml/__init__.py b/blueprintcompiler/outputs/xml/__init__.py index 0daae44..42d9b21 100644 --- a/blueprintcompiler/outputs/xml/__init__.py +++ b/blueprintcompiler/outputs/xml/__init__.py @@ -91,48 +91,58 @@ class XmlOutput(OutputFormat): def _emit_property(self, property: Property, xml: XmlEmitter): value = property.value - child = value.child props: T.Dict[str, T.Optional[str]] = { "name": property.name, } - if isinstance(child, Translated): - xml.start_tag("property", **props, **self._translated_string_attrs(child)) - xml.put_text(child.child.string) - xml.end_tag() - elif isinstance(child, Object): - xml.start_tag("property", **props) - self._emit_object(child, xml) - xml.end_tag() - elif isinstance(child, Binding): - if simple := child.simple_binding: + if isinstance(value, Value): + child = value.child + + if isinstance(child, Translated): + xml.start_tag( + "property", **props, **self._translated_string_attrs(child) + ) + xml.put_text(child.child.string) + xml.end_tag() + else: + xml.start_tag("property", **props) + self._emit_value(value, xml) + xml.end_tag() + + elif isinstance(value, Binding): + if simple := value.simple_binding: props["bind-source"] = simple.source props["bind-property"] = simple.property_name props["bind-flags"] = "sync-create" xml.put_self_closing("property", **props) else: xml.start_tag("binding", **props) - self._emit_expression(child.expression, xml) + self._emit_expression(value.expression, xml) xml.end_tag() - elif isinstance(child, PropertyBinding): + + elif isinstance(value, PropertyBinding): bind_flags = [] - if not child.no_sync_create: + if not value.no_sync_create: bind_flags.append("sync-create") - if child.inverted: + if value.inverted: bind_flags.append("invert-boolean") - if child.bidirectional: + if value.bidirectional: bind_flags.append("bidirectional") - props["bind-source"] = child.source - props["bind-property"] = child.property_name + props["bind-source"] = value.source + props["bind-property"] = value.property_name props["bind-flags"] = "|".join(bind_flags) or None xml.put_self_closing("property", **props) - else: + + elif isinstance(value, ObjectValue): xml.start_tag("property", **props) - self._emit_value(value, xml) + self._emit_object(value.object, xml) xml.end_tag() + else: + raise CompilerBugError() + def _translated_string_attrs( self, translated: T.Union[QuotedLiteral, Translated] ) -> T.Dict[str, T.Optional[str]]: diff --git a/tests/sample_errors/read_only_properties.err b/tests/sample_errors/read_only_properties.err index dc03297..e30e801 100644 --- a/tests/sample_errors/read_only_properties.err +++ b/tests/sample_errors/read_only_properties.err @@ -1,2 +1,2 @@ -4,14,4,ComboBox.has-entry can't be bound because it is construct-only +4,3,34,ComboBox.has-entry can't be bound because it is construct-only 5,3,12,Widget.scale-factor is not writable