mirror of
https://gitlab.gnome.org/jwestman/blueprint-compiler.git
synced 2025-05-05 16:09:07 -04:00
Validate object types
This commit is contained in:
parent
a0ba59af77
commit
d89f2356b4
11 changed files with 126 additions and 33 deletions
|
@ -57,7 +57,7 @@ class UI(AstNode):
|
|||
|
||||
@lazy_prop
|
||||
def objects_by_id(self):
|
||||
return { obj.id: obj for obj in self.iterate_children_recursive() if hasattr(obj, "id") }
|
||||
return { obj.tokens["id"]: obj for obj in self.iterate_children_recursive() if obj.tokens["id"] is not None }
|
||||
|
||||
|
||||
@validate()
|
||||
|
@ -137,12 +137,12 @@ class Import(AstNode):
|
|||
|
||||
class Template(AstNode):
|
||||
@validate("namespace", "class_name")
|
||||
def gir_parent_exists(self):
|
||||
def gir_class_exists(self):
|
||||
if not self.tokens["ignore_gir"]:
|
||||
self.root.gir.validate_class(self.tokens["class_name"], self.tokens["namespace"])
|
||||
|
||||
@property
|
||||
def gir_parent(self):
|
||||
def gir_class(self):
|
||||
return self.root.gir.get_class(self.tokens["class_name"], self.tokens["namespace"])
|
||||
|
||||
|
||||
|
@ -152,14 +152,14 @@ class Template(AstNode):
|
|||
|
||||
@docs("class_name")
|
||||
def class_docs(self):
|
||||
if self.gir_parent:
|
||||
return self.gir_parent.doc
|
||||
if self.gir_class:
|
||||
return self.gir_class.doc
|
||||
|
||||
|
||||
def emit_xml(self, xml: XmlEmitter):
|
||||
xml.start_tag("template", **{
|
||||
"class": self.tokens["name"],
|
||||
"parent": self.gir_parent.glib_type_name if self.gir_parent else self.tokens["class_name"],
|
||||
"parent": self.gir_class.glib_type_name if self.gir_class else self.tokens["class_name"],
|
||||
})
|
||||
for child in self.children:
|
||||
child.emit_xml(xml)
|
||||
|
@ -210,12 +210,7 @@ class Child(AstNode):
|
|||
class ObjectContent(AstNode):
|
||||
@property
|
||||
def gir_class(self):
|
||||
if isinstance(self.parent, Template):
|
||||
return self.parent.gir_parent
|
||||
elif isinstance(self.parent, Object):
|
||||
return self.parent.gir_class
|
||||
else:
|
||||
raise CompilerBugError()
|
||||
return self.parent.gir_class
|
||||
|
||||
# @validate()
|
||||
# def only_one_style_class(self):
|
||||
|
@ -233,13 +228,7 @@ class ObjectContent(AstNode):
|
|||
class Property(AstNode):
|
||||
@property
|
||||
def gir_class(self):
|
||||
parent = self.parent.parent
|
||||
if isinstance(parent, Template):
|
||||
return parent.gir_parent
|
||||
elif isinstance(parent, Object):
|
||||
return parent.gir_class
|
||||
else:
|
||||
raise CompilerBugError()
|
||||
return self.parent.parent.gir_class
|
||||
|
||||
|
||||
@property
|
||||
|
@ -273,6 +262,19 @@ class Property(AstNode):
|
|||
)
|
||||
|
||||
|
||||
@validate()
|
||||
def obj_property_type(self):
|
||||
if len(self.children[Object]) == 0:
|
||||
return
|
||||
|
||||
object = self.children[Object][0]
|
||||
type = self.value_type
|
||||
if object and type and object.gir_class and not object.gir_class.assignable_to(type):
|
||||
raise CompileError(
|
||||
f"Cannot assign {object.gir_class.full_name} to {type.full_name}"
|
||||
)
|
||||
|
||||
|
||||
@docs("name")
|
||||
def property_docs(self):
|
||||
if self.gir_property is not None:
|
||||
|
@ -323,13 +325,7 @@ class Signal(AstNode):
|
|||
|
||||
@property
|
||||
def gir_class(self):
|
||||
parent = self.parent.parent
|
||||
if isinstance(parent, Template):
|
||||
return parent.gir_parent
|
||||
elif isinstance(parent, Object):
|
||||
return parent.gir_class
|
||||
else:
|
||||
raise CompilerBugError()
|
||||
return self.parent.parent.gir_class
|
||||
|
||||
|
||||
@validate("name")
|
||||
|
@ -397,12 +393,14 @@ class IdentValue(Value):
|
|||
@validate()
|
||||
def validate_for_type(self):
|
||||
type = self.parent.value_type
|
||||
|
||||
if isinstance(type, gir.Enumeration):
|
||||
if self.tokens["value"] not in type.members:
|
||||
raise CompileError(
|
||||
f"{self.tokens['value']} is not a member of {type.full_name}",
|
||||
did_you_mean=type.members.keys(),
|
||||
)
|
||||
|
||||
elif isinstance(type, gir.BoolType):
|
||||
# would have been parsed as a LiteralValue if it was correct
|
||||
raise CompileError(
|
||||
|
@ -410,6 +408,18 @@ class IdentValue(Value):
|
|||
did_you_mean=["true", "false"],
|
||||
)
|
||||
|
||||
else:
|
||||
object = self.root.objects_by_id.get(self.tokens["value"])
|
||||
if object is None:
|
||||
raise CompileError(
|
||||
f"Could not find object with ID {self.tokens['value']}",
|
||||
did_you_mean=(self.tokens['value'], self.root.objects_by_id.keys()),
|
||||
)
|
||||
elif object.gir_class and not object.gir_class.assignable_to(type):
|
||||
raise CompileError(
|
||||
f"Cannot assign {object.gir_class.full_name} to {type.full_name}"
|
||||
)
|
||||
|
||||
|
||||
@docs()
|
||||
def docs(self):
|
||||
|
|
|
@ -55,23 +55,47 @@ def get_namespace(namespace, version):
|
|||
return _namespace_cache[filename]
|
||||
|
||||
|
||||
class BasicType:
|
||||
class GirType:
|
||||
pass
|
||||
|
||||
class BasicType:
|
||||
name: str = "unknown type"
|
||||
|
||||
def assignable_to(self, other) -> bool:
|
||||
raise NotImplementedError()
|
||||
|
||||
@property
|
||||
def full_name(self) -> str:
|
||||
return self.name
|
||||
|
||||
class BoolType(BasicType):
|
||||
pass
|
||||
name = "bool"
|
||||
def assignable_to(self, other) -> bool:
|
||||
return isinstance(other, BoolType)
|
||||
|
||||
class IntType(BasicType):
|
||||
pass
|
||||
name = "int"
|
||||
def assignable_to(self, other) -> bool:
|
||||
return isinstance(other, IntType) or isinstance(other, UIntType) or isinstance(other, FloatType)
|
||||
|
||||
class UIntType(BasicType):
|
||||
pass
|
||||
name = "uint"
|
||||
def assignable_to(self, other) -> bool:
|
||||
return isinstance(other, IntType) or isinstance(other, UIntType) or isinstance(other, FloatType)
|
||||
|
||||
class FloatType(BasicType):
|
||||
pass
|
||||
name = "float"
|
||||
def assignable_to(self, other) -> bool:
|
||||
return isinstance(other, FloatType)
|
||||
|
||||
class StringType(BasicType):
|
||||
name = "string"
|
||||
def assignable_to(self, other) -> bool:
|
||||
return isinstance(other, StringType)
|
||||
|
||||
_BASIC_TYPES = {
|
||||
"gboolean": BoolType,
|
||||
"int": IntType,
|
||||
"gint": IntType,
|
||||
"gint64": IntType,
|
||||
"guint": UIntType,
|
||||
|
@ -80,6 +104,7 @@ _BASIC_TYPES = {
|
|||
"gdouble": FloatType,
|
||||
"float": FloatType,
|
||||
"double": FloatType,
|
||||
"utf8": StringType,
|
||||
}
|
||||
|
||||
class GirNode:
|
||||
|
@ -172,14 +197,23 @@ class Signal(GirNode):
|
|||
return f"signal {self.container.name}.{self.name} ({args})"
|
||||
|
||||
|
||||
class Interface(GirNode):
|
||||
class Interface(GirNode, GirType):
|
||||
def __init__(self, ns, xml: xml_reader.Element):
|
||||
super().__init__(ns, xml)
|
||||
self.properties = {child["name"]: Property(self, child) for child in xml.get_elements("property")}
|
||||
self.signals = {child["name"]: Signal(self, child) for child in xml.get_elements("glib:signal")}
|
||||
self.prerequisites = [child["name"] for child in xml.get_elements("prerequisite")]
|
||||
|
||||
def assignable_to(self, other) -> bool:
|
||||
if self == other:
|
||||
return True
|
||||
for pre in self.prerequisites:
|
||||
if self.get_containing(Namespace).lookup_type(pre).assignable_to(other):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class Class(GirNode):
|
||||
class Class(GirNode, GirType):
|
||||
def __init__(self, ns, xml: xml_reader.Element):
|
||||
super().__init__(ns, xml)
|
||||
self._parent = xml["parent"]
|
||||
|
@ -211,6 +245,17 @@ class Class(GirNode):
|
|||
return self.get_containing(Namespace).lookup_type(self._parent)
|
||||
|
||||
|
||||
def assignable_to(self, other) -> bool:
|
||||
if self == other:
|
||||
return True
|
||||
elif self.parent and self.parent.assignable_to(other):
|
||||
return True
|
||||
elif other in self.implements:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def _enum_properties(self):
|
||||
yield from self.own_properties.values()
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue