Move bindings out of the Value syntax

They're only valid in properties, so they should just be there. Same
with object values.
This commit is contained in:
James Westman 2023-04-09 18:18:01 -05:00
parent ac2a7d9282
commit 75055ac967
7 changed files with 45 additions and 47 deletions

View file

@ -47,11 +47,6 @@ class Binding(AstNode):
) )
return None return None
@validate("bind")
def not_bindable(self) -> None:
if binding_error := self.context[ValueTypeCtx].binding_error:
raise binding_error
@dataclass @dataclass
class SimpleBinding: class SimpleBinding:

View file

@ -25,4 +25,3 @@ from .common import *
@dataclass @dataclass
class ValueTypeCtx: class ValueTypeCtx:
value_type: T.Optional[GirType] value_type: T.Optional[GirType]
binding_error: T.Optional[CompileError] = None

View file

@ -22,7 +22,7 @@ from dataclasses import dataclass
from .expression import ExprChain from .expression import ExprChain
from .gobject_object import Object from .gobject_object import Object
from .gtkbuilder_template import Template from .gtkbuilder_template import Template
from .values import Value, Translated from .values import Value, ObjectValue
from .common import * from .common import *
from .contexts import ValueTypeCtx from .contexts import ValueTypeCtx
from .property_binding import PropertyBinding from .property_binding import PropertyBinding
@ -30,14 +30,16 @@ from .binding import Binding
class Property(AstNode): class Property(AstNode):
grammar = Statement(UseIdent("name"), ":", Value) grammar = Statement(
UseIdent("name"), ":", AnyOf(PropertyBinding, Binding, ObjectValue, Value)
)
@property @property
def name(self) -> str: def name(self) -> str:
return self.tokens["name"] return self.tokens["name"]
@property @property
def value(self) -> Value: def value(self) -> T.Union[PropertyBinding, Binding, ObjectValue, Value]:
return self.children[0] return self.children[0]
@property @property
@ -49,29 +51,26 @@ class Property(AstNode):
if self.gir_class is not None and not isinstance(self.gir_class, ExternType): if self.gir_class is not None and not isinstance(self.gir_class, ExternType):
return self.gir_class.properties.get(self.tokens["name"]) return self.gir_class.properties.get(self.tokens["name"])
@context(ValueTypeCtx) @validate()
def value_type(self) -> ValueTypeCtx: def binding_valid(self):
if ( if (
( (isinstance(self.value, PropertyBinding) or isinstance(self.value, Binding))
isinstance(self.value.child, PropertyBinding)
or isinstance(self.value.child, Binding)
)
and self.gir_property is not None and self.gir_property is not None
and self.gir_property.construct_only 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", 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"], 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: if self.gir_property is not None:
type = self.gir_property.type type = self.gir_property.type
else: else:
type = None type = None
return ValueTypeCtx(type, binding_error) return ValueTypeCtx(type)
@validate("name") @validate("name")
def property_exists(self): def property_exists(self):

View file

@ -121,11 +121,6 @@ class PropertyBinding(AstNode):
f"{gir_class.full_name} does not have a property called {self.property_name}" 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") @validate("bind")
def old_bind(self): def old_bind(self):
if self.tokens["bind"]: if self.tokens["bind"]:

View file

@ -361,12 +361,12 @@ class ObjectValue(AstNode):
class Value(AstNode): class Value(AstNode):
grammar = AnyOf(PropertyBinding, Binding, Translated, ObjectValue, Flags, Literal) grammar = AnyOf(Translated, Flags, Literal)
@property @property
def child( def child(
self, self,
) -> T.Union[PropertyBinding, Binding, Translated, ObjectValue, Flags, Literal]: ) -> T.Union[Translated, Flags, Literal]:
return self.children[0] return self.children[0]

View file

@ -91,48 +91,58 @@ class XmlOutput(OutputFormat):
def _emit_property(self, property: Property, xml: XmlEmitter): def _emit_property(self, property: Property, xml: XmlEmitter):
value = property.value value = property.value
child = value.child
props: T.Dict[str, T.Optional[str]] = { props: T.Dict[str, T.Optional[str]] = {
"name": property.name, "name": property.name,
} }
if isinstance(value, Value):
child = value.child
if isinstance(child, Translated): if isinstance(child, Translated):
xml.start_tag("property", **props, **self._translated_string_attrs(child)) xml.start_tag(
"property", **props, **self._translated_string_attrs(child)
)
xml.put_text(child.child.string) xml.put_text(child.child.string)
xml.end_tag() xml.end_tag()
elif isinstance(child, Object): else:
xml.start_tag("property", **props) xml.start_tag("property", **props)
self._emit_object(child, xml) self._emit_value(value, xml)
xml.end_tag() xml.end_tag()
elif isinstance(child, Binding):
if simple := child.simple_binding: elif isinstance(value, Binding):
if simple := value.simple_binding:
props["bind-source"] = simple.source props["bind-source"] = simple.source
props["bind-property"] = simple.property_name props["bind-property"] = simple.property_name
props["bind-flags"] = "sync-create" props["bind-flags"] = "sync-create"
xml.put_self_closing("property", **props) xml.put_self_closing("property", **props)
else: else:
xml.start_tag("binding", **props) xml.start_tag("binding", **props)
self._emit_expression(child.expression, xml) self._emit_expression(value.expression, xml)
xml.end_tag() xml.end_tag()
elif isinstance(child, PropertyBinding):
elif isinstance(value, PropertyBinding):
bind_flags = [] bind_flags = []
if not child.no_sync_create: if not value.no_sync_create:
bind_flags.append("sync-create") bind_flags.append("sync-create")
if child.inverted: if value.inverted:
bind_flags.append("invert-boolean") bind_flags.append("invert-boolean")
if child.bidirectional: if value.bidirectional:
bind_flags.append("bidirectional") bind_flags.append("bidirectional")
props["bind-source"] = child.source props["bind-source"] = value.source
props["bind-property"] = child.property_name props["bind-property"] = value.property_name
props["bind-flags"] = "|".join(bind_flags) or None props["bind-flags"] = "|".join(bind_flags) or None
xml.put_self_closing("property", **props) xml.put_self_closing("property", **props)
else:
elif isinstance(value, ObjectValue):
xml.start_tag("property", **props) xml.start_tag("property", **props)
self._emit_value(value, xml) self._emit_object(value.object, xml)
xml.end_tag() xml.end_tag()
else:
raise CompilerBugError()
def _translated_string_attrs( def _translated_string_attrs(
self, translated: T.Union[QuotedLiteral, Translated] self, translated: T.Union[QuotedLiteral, Translated]
) -> T.Dict[str, T.Optional[str]]: ) -> T.Dict[str, T.Optional[str]]:

View file

@ -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 5,3,12,Widget.scale-factor is not writable