mirror of
https://gitlab.gnome.org/jwestman/blueprint-compiler.git
synced 2025-05-04 15:59:08 -04:00
Refactor child types
Didn't change the actual syntax, but changed the rules around to be less confusing.
This commit is contained in:
parent
9dcd06de51
commit
71f52d350a
11 changed files with 74 additions and 55 deletions
|
@ -109,9 +109,9 @@ class AstNode:
|
||||||
else:
|
else:
|
||||||
return self.parent.root
|
return self.parent.root
|
||||||
|
|
||||||
def parent_by_type(self, type):
|
def parent_by_type(self, type: T.Type[TType]) -> TType:
|
||||||
if self.parent is None:
|
if self.parent is None:
|
||||||
return None
|
raise CompilerBugError()
|
||||||
elif isinstance(self.parent, type):
|
elif isinstance(self.parent, type):
|
||||||
return self.parent
|
return self.parent
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -23,7 +23,7 @@ from .gtk_menu import menu, Menu, MenuAttribute
|
||||||
from .gtk_size_group import Widgets
|
from .gtk_size_group import Widgets
|
||||||
from .gtk_string_list import Strings
|
from .gtk_string_list import Strings
|
||||||
from .gtk_styles import Styles
|
from .gtk_styles import Styles
|
||||||
from .gtkbuilder_child import Child
|
from .gtkbuilder_child import Child, ChildType, ChildInternal, ChildExtension
|
||||||
from .gtkbuilder_template import Template
|
from .gtkbuilder_template import Template
|
||||||
from .imports import GtkDirective, Import
|
from .imports import GtkDirective, Import
|
||||||
from .property_binding import PropertyBinding
|
from .property_binding import PropertyBinding
|
||||||
|
|
|
@ -22,7 +22,7 @@ import typing as T
|
||||||
from functools import cached_property
|
from functools import cached_property
|
||||||
|
|
||||||
from .common import *
|
from .common import *
|
||||||
from .response_id import ResponseId
|
from .response_id import ExtResponse
|
||||||
from .types import ClassName, ConcreteClassName
|
from .types import ClassName, ConcreteClassName
|
||||||
|
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ class Object(AstNode):
|
||||||
return self.class_name.gir_type
|
return self.class_name.gir_type
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def action_widgets(self) -> T.List[ResponseId]:
|
def action_widgets(self) -> T.List[ExtResponse]:
|
||||||
"""Get list of widget's action widgets.
|
"""Get list of widget's action widgets.
|
||||||
|
|
||||||
Empty if object doesn't have action widgets.
|
Empty if object doesn't have action widgets.
|
||||||
|
@ -69,7 +69,7 @@ class Object(AstNode):
|
||||||
|
|
||||||
return [
|
return [
|
||||||
child.response_id
|
child.response_id
|
||||||
for child in self.children[ObjectContent][0].children[Child]
|
for child in self.content.children[Child]
|
||||||
if child.response_id
|
if child.response_id
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
from functools import cached_property
|
from functools import cached_property
|
||||||
|
|
||||||
from .gobject_object import Object
|
from .gobject_object import Object
|
||||||
from .response_id import ResponseId
|
from .response_id import ExtResponse
|
||||||
from .common import *
|
from .common import *
|
||||||
|
|
||||||
ALLOWED_PARENTS: T.List[T.Tuple[str, str]] = [
|
ALLOWED_PARENTS: T.List[T.Tuple[str, str]] = [
|
||||||
|
@ -30,20 +30,49 @@ ALLOWED_PARENTS: T.List[T.Tuple[str, str]] = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ChildInternal(AstNode):
|
||||||
|
grammar = ["internal-child", UseIdent("internal_child")]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def internal_child(self) -> str:
|
||||||
|
return self.tokens["internal_child"]
|
||||||
|
|
||||||
|
|
||||||
|
class ChildType(AstNode):
|
||||||
|
grammar = UseIdent("child_type").expected("a child type")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def child_type(self) -> str:
|
||||||
|
return self.tokens["child_type"]
|
||||||
|
|
||||||
|
|
||||||
|
class ChildExtension(AstNode):
|
||||||
|
grammar = ExtResponse
|
||||||
|
|
||||||
|
@property
|
||||||
|
def child(self) -> ExtResponse:
|
||||||
|
return self.children[0]
|
||||||
|
|
||||||
|
|
||||||
|
class ChildAnnotation(AstNode):
|
||||||
|
grammar = ["[", AnyOf(ChildInternal, ChildExtension, ChildType), "]"]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def child(self) -> T.Union[ChildInternal, ChildExtension, ChildType]:
|
||||||
|
return self.children[0]
|
||||||
|
|
||||||
|
|
||||||
class Child(AstNode):
|
class Child(AstNode):
|
||||||
grammar = [
|
grammar = [
|
||||||
Optional(
|
Optional(ChildAnnotation),
|
||||||
[
|
|
||||||
"[",
|
|
||||||
Optional(["internal-child", UseLiteral("internal_child", True)]),
|
|
||||||
UseIdent("child_type").expected("a child type"),
|
|
||||||
Optional(ResponseId),
|
|
||||||
"]",
|
|
||||||
]
|
|
||||||
),
|
|
||||||
Object,
|
Object,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def annotation(self) -> T.Optional[ChildAnnotation]:
|
||||||
|
annotations = self.children[ChildAnnotation]
|
||||||
|
return annotations[0] if len(annotations) else None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def object(self) -> Object:
|
def object(self) -> Object:
|
||||||
return self.children[Object][0]
|
return self.children[Object][0]
|
||||||
|
@ -69,15 +98,17 @@ class Child(AstNode):
|
||||||
)
|
)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def response_id(self) -> T.Optional[ResponseId]:
|
def response_id(self) -> T.Optional[ExtResponse]:
|
||||||
"""Get action widget's response ID.
|
"""Get action widget's response ID.
|
||||||
|
|
||||||
If child is not action widget, returns `None`.
|
If child is not action widget, returns `None`.
|
||||||
"""
|
"""
|
||||||
response_ids = self.children[ResponseId]
|
if (
|
||||||
|
self.annotation is not None
|
||||||
if response_ids:
|
and isinstance(self.annotation.child, ChildExtension)
|
||||||
return response_ids[0]
|
and isinstance(self.annotation.child.child, ExtResponse)
|
||||||
|
):
|
||||||
|
return self.annotation.child.child
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
|
@ -23,12 +23,13 @@ import typing as T
|
||||||
from .common import *
|
from .common import *
|
||||||
|
|
||||||
|
|
||||||
class ResponseId(AstNode):
|
class ExtResponse(AstNode):
|
||||||
"""Response ID of action widget."""
|
"""Response ID of action widget."""
|
||||||
|
|
||||||
ALLOWED_PARENTS: T.List[T.Tuple[str, str]] = [("Gtk", "Dialog"), ("Gtk", "InfoBar")]
|
ALLOWED_PARENTS: T.List[T.Tuple[str, str]] = [("Gtk", "Dialog"), ("Gtk", "InfoBar")]
|
||||||
|
|
||||||
grammar = [
|
grammar = [
|
||||||
|
Keyword("action"),
|
||||||
Keyword("response"),
|
Keyword("response"),
|
||||||
"=",
|
"=",
|
||||||
AnyOf(
|
AnyOf(
|
||||||
|
@ -41,13 +42,6 @@ class ResponseId(AstNode):
|
||||||
Optional([Keyword("default"), UseLiteral("is_default", True)]),
|
Optional([Keyword("default"), UseLiteral("is_default", True)]),
|
||||||
]
|
]
|
||||||
|
|
||||||
@validate()
|
|
||||||
def child_type_is_action(self) -> None:
|
|
||||||
"""Check that child type is "action"."""
|
|
||||||
child_type = self.parent.tokens["child_type"]
|
|
||||||
if child_type != "action":
|
|
||||||
raise CompileError(f"Only action widget can have response ID")
|
|
||||||
|
|
||||||
@validate()
|
@validate()
|
||||||
def parent_has_action_widgets(self) -> None:
|
def parent_has_action_widgets(self) -> None:
|
||||||
"""Chech that parent widget has allowed type."""
|
"""Chech that parent widget has allowed type."""
|
||||||
|
@ -59,7 +53,7 @@ class ResponseId(AstNode):
|
||||||
|
|
||||||
gir = self.root.gir
|
gir = self.root.gir
|
||||||
|
|
||||||
for namespace, name in ResponseId.ALLOWED_PARENTS:
|
for namespace, name in ExtResponse.ALLOWED_PARENTS:
|
||||||
parent_type = gir.get_type(name, namespace)
|
parent_type = gir.get_type(name, namespace)
|
||||||
if container_type.assignable_to(parent_type):
|
if container_type.assignable_to(parent_type):
|
||||||
break
|
break
|
||||||
|
@ -71,10 +65,10 @@ class ResponseId(AstNode):
|
||||||
@validate()
|
@validate()
|
||||||
def widget_have_id(self) -> None:
|
def widget_have_id(self) -> None:
|
||||||
"""Check that action widget have ID."""
|
"""Check that action widget have ID."""
|
||||||
from .gobject_object import Object
|
from .gtkbuilder_child import Child
|
||||||
|
|
||||||
_object = self.parent.children[Object][0]
|
object = self.parent_by_type(Child).object
|
||||||
if _object.tokens["id"] is None:
|
if object.id is None:
|
||||||
raise CompileError(f"Action widget must have ID")
|
raise CompileError(f"Action widget must have ID")
|
||||||
|
|
||||||
@validate("response_id")
|
@validate("response_id")
|
||||||
|
@ -102,10 +96,9 @@ class ResponseId(AstNode):
|
||||||
@validate("default")
|
@validate("default")
|
||||||
def no_multiple_default(self) -> None:
|
def no_multiple_default(self) -> None:
|
||||||
"""Only one action widget in dialog can be default."""
|
"""Only one action widget in dialog can be default."""
|
||||||
from .gtkbuilder_child import Child
|
|
||||||
from .gobject_object import Object
|
from .gobject_object import Object
|
||||||
|
|
||||||
if not self.tokens["is_default"]:
|
if not self.is_default:
|
||||||
return
|
return
|
||||||
|
|
||||||
action_widgets = self.parent_by_type(Object).action_widgets
|
action_widgets = self.parent_by_type(Object).action_widgets
|
||||||
|
@ -126,7 +119,7 @@ class ResponseId(AstNode):
|
||||||
@property
|
@property
|
||||||
def widget_id(self) -> str:
|
def widget_id(self) -> str:
|
||||||
"""Get action widget ID."""
|
"""Get action widget ID."""
|
||||||
from .gobject_object import Object
|
from .gtkbuilder_child import Child
|
||||||
|
|
||||||
_object: Object = self.parent.children[Object][0]
|
object = self.parent_by_type(Child).object
|
||||||
return _object.tokens["id"]
|
return object.id
|
||||||
|
|
|
@ -170,11 +170,16 @@ class XmlOutput(OutputFormat):
|
||||||
|
|
||||||
def _emit_child(self, child: Child, xml: XmlEmitter):
|
def _emit_child(self, child: Child, xml: XmlEmitter):
|
||||||
child_type = internal_child = None
|
child_type = internal_child = None
|
||||||
|
if child.annotation is not None:
|
||||||
if child.tokens["internal_child"]:
|
annotation = child.annotation.child
|
||||||
internal_child = child.tokens["child_type"]
|
if isinstance(annotation, ChildType):
|
||||||
else:
|
child_type = annotation.child_type
|
||||||
child_type = child.tokens["child_type"]
|
elif isinstance(annotation, ChildInternal):
|
||||||
|
internal_child = annotation.internal_child
|
||||||
|
elif isinstance(annotation, ChildExtension):
|
||||||
|
child_type = "action"
|
||||||
|
else:
|
||||||
|
raise CompilerBugError()
|
||||||
|
|
||||||
xml.start_tag("child", type=child_type, internal_child=internal_child)
|
xml.start_tag("child", type=child_type, internal_child=internal_child)
|
||||||
self._emit_object(child.object, xml)
|
self._emit_object(child.object, xml)
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
4,13,15,Action widget must have ID
|
4,6,22,Action widget must have ID
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
4,13,11,Gtk.Box doesn't have action widgets
|
4,6,18,Gtk.Box doesn't have action widgets
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
using Gtk 4.0;
|
|
||||||
|
|
||||||
Dialog {
|
|
||||||
[some_type response=ok]
|
|
||||||
Button ok_button {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
4,16,11,Only action widget can have response ID
|
|
|
@ -216,7 +216,6 @@ class TestSamples(unittest.TestCase):
|
||||||
self.assert_sample_error("action_widget_float_response")
|
self.assert_sample_error("action_widget_float_response")
|
||||||
self.assert_sample_error("action_widget_have_no_id")
|
self.assert_sample_error("action_widget_have_no_id")
|
||||||
self.assert_sample_error("action_widget_multiple_default")
|
self.assert_sample_error("action_widget_multiple_default")
|
||||||
self.assert_sample_error("action_widget_not_action")
|
|
||||||
self.assert_sample_error("action_widget_in_invalid_container")
|
self.assert_sample_error("action_widget_in_invalid_container")
|
||||||
self.assert_sample_error("action_widget_response_dne")
|
self.assert_sample_error("action_widget_response_dne")
|
||||||
self.assert_sample_error("action_widget_negative_response")
|
self.assert_sample_error("action_widget_negative_response")
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue