language: Support boxed types and GType

- Add support for type checking boxed types
- Remove support for converting string and number literals
- Add the `typeof()` operator for GType literals
This commit is contained in:
James Westman 2022-09-16 22:33:49 -05:00
parent ee2b9b2950
commit c0c40b1577
No known key found for this signature in database
GPG key ID: CE2DBA0ADB654EA6
12 changed files with 140 additions and 26 deletions

View file

@ -129,6 +129,11 @@ class StringType(BasicType):
def assignable_to(self, other) -> bool:
return isinstance(other, StringType)
class TypeType(BasicType):
name = "GType"
def assignable_to(self, other) -> bool:
return isinstance(other, TypeType)
_BASIC_TYPES = {
"gboolean": BoolType,
"int": IntType,
@ -141,6 +146,8 @@ _BASIC_TYPES = {
"float": FloatType,
"double": FloatType,
"utf8": StringType,
"gtype": TypeType,
"type": TypeType,
}
class GirNode:
@ -213,7 +220,7 @@ class GirNode:
@property
def type(self):
return self.get_containing(Namespace).lookup_type(self.type_name)
raise NotImplementedError()
class Property(GirNode):
@ -464,6 +471,18 @@ class Enumeration(GirNode, GirType):
return type == self
class Boxed(GirNode, GirType):
def __init__(self, ns, tl: typelib.Typelib):
super().__init__(ns, tl)
@property
def signature(self):
return f"boxed {self.full_name}"
def assignable_to(self, type):
return type == self
class Bitfield(Enumeration):
def __init__(self, ns, tl: typelib.Typelib):
super().__init__(ns, tl)
@ -491,6 +510,8 @@ class Namespace(GirNode):
self.entries[entry_name] = Class(self, entry_blob)
elif entry_type == typelib.BLOB_TYPE_INTERFACE:
self.entries[entry_name] = Interface(self, entry_blob)
elif entry_type == typelib.BLOB_TYPE_BOXED or entry_type == typelib.BLOB_TYPE_STRUCT:
self.entries[entry_name] = Boxed(self, entry_blob)
@cached_property
def xml(self):
@ -595,6 +616,8 @@ class Repository(GirNode):
return UIntType()
elif type_id == typelib.TYPE_UTF8:
return StringType()
elif type_id == typelib.TYPE_GTYPE:
return TypeType()
else:
raise CompilerBugError("Unknown type ID", type_id)
else:

View file

@ -18,7 +18,7 @@ from .gtkbuilder_child import Child
from .gtkbuilder_template import Template
from .imports import GtkDirective, Import
from .ui import UI
from .values import IdentValue, TranslatedStringValue, FlagsValue, LiteralValue
from .values import TypeValue, IdentValue, TranslatedStringValue, FlagsValue, QuotedValue, NumberValue
from .common import *
@ -43,8 +43,10 @@ OBJECT_CONTENT_HOOKS.children = [
]
VALUE_HOOKS.children = [
TypeValue,
TranslatedStringValue,
FlagsValue,
IdentValue,
LiteralValue,
QuotedValue,
NumberValue,
]

View file

@ -19,6 +19,7 @@
from .common import *
from .types import TypeName
class Value(AstNode):
@ -55,11 +56,68 @@ class TranslatedStringValue(Value):
xml.put_text(self.tokens["value"])
class LiteralValue(Value):
grammar = AnyOf(
UseNumber("value"),
UseQuoted("value"),
)
class TypeValue(Value):
grammar = [
"typeof",
"(",
to_parse_node(TypeName).expected("type name"),
Match(")").expected(),
]
@property
def type_name(self):
return self.children[TypeName][0]
def emit_xml(self, xml: XmlEmitter):
xml.put_text(self.type_name.glib_type_name)
@validate()
def validate_for_type(self):
type = self.parent.value_type
if type is not None and not isinstance(type, gir.TypeType):
raise CompileError(f"Cannot convert GType to {type.full_name}")
class QuotedValue(Value):
grammar = UseQuoted("value")
def emit_xml(self, xml: XmlEmitter):
xml.put_text(self.tokens["value"])
@validate()
def validate_for_type(self):
type = self.parent.value_type
if isinstance(type, gir.IntType) or isinstance(type, gir.UIntType) or isinstance(type, gir.FloatType):
raise CompileError(f"Cannot convert string to number")
elif isinstance(type, gir.StringType):
pass
elif isinstance(type, gir.Class) or isinstance(type, gir.Interface) or isinstance(type, gir.Boxed):
parseable_types = [
"Gdk.Paintable",
"Gdk.Texture",
"Gdk.Pixbuf",
"GLib.File",
"Gtk.ShortcutTrigger",
"Gtk.ShortcutAction",
"Gdk.RGBA",
"Gdk.ContentFormats",
"Gsk.Transform",
"GLib.Variant",
]
if type.full_name not in parseable_types:
hints = []
if isinstance(type, gir.TypeType):
hints.append(f"use the typeof operator: 'typeof({self.tokens('value')})'")
raise CompileError(f"Cannot convert string to {type.full_name}", hints=hints)
elif type is not None:
raise CompileError(f"Cannot convert string to {type.full_name}")
class NumberValue(Value):
grammar = UseNumber("value")
def emit_xml(self, xml: XmlEmitter):
xml.put_text(self.tokens["value"])
@ -87,23 +145,8 @@ class LiteralValue(Value):
except:
raise CompileError(f"Cannot convert {self.group.tokens['value']} to float")
elif isinstance(type, gir.StringType):
pass
elif isinstance(type, gir.Class) or isinstance(type, gir.Interface):
parseable_types = [
"Gdk.Paintable",
"Gdk.Texture",
"Gdk.Pixbuf",
"GLib.File",
"Gtk.ShortcutTrigger",
"Gtk.ShortcutAction",
]
if type.full_name not in parseable_types:
raise CompileError(f"Cannot convert {self.group.tokens['value']} to {type.full_name}")
elif type is not None:
raise CompileError(f"Cannot convert {self.group.tokens['value']} to {type.full_name}")
raise CompileError(f"Cannot convert number to {type.full_name}")
class Flag(AstNode):

View file

@ -26,6 +26,8 @@ import mmap, os
from .errors import CompilerBugError
BLOB_TYPE_STRUCT = 3
BLOB_TYPE_BOXED = 4
BLOB_TYPE_ENUM = 5
BLOB_TYPE_FLAGS = 6
BLOB_TYPE_OBJECT = 7