mirror of
https://gitlab.gnome.org/jwestman/blueprint-compiler.git
synced 2025-05-04 15:59:08 -04:00
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:
parent
ee2b9b2950
commit
c0c40b1577
12 changed files with 140 additions and 26 deletions
|
@ -18,7 +18,7 @@ build:
|
|||
- ninja -C _build docs/en
|
||||
- git clone https://gitlab.gnome.org/jwestman/blueprint-regression-tests.git
|
||||
- cd blueprint-regression-tests
|
||||
- git checkout c61f425d5df359261f1369722e791f947bc3ede1
|
||||
- git checkout d14b95b6c1fc0cddd4b0ad21d224b05edee2d01f
|
||||
- ./test.sh
|
||||
- cd ..
|
||||
coverage: '/TOTAL.*\s([.\d]+)%/'
|
||||
|
|
7
NEWS.md
7
NEWS.md
|
@ -1,3 +1,10 @@
|
|||
# v0.6.0 (unreleased)
|
||||
|
||||
## Breaking Changes
|
||||
- Quoted and numeric literals are no longer interchangeable (e.g. `"800"` is
|
||||
no longer an accepted value for an integer type).
|
||||
- Boxed types are now type checked.
|
||||
|
||||
# v0.4.0
|
||||
|
||||
## Added
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
]
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1 +1 @@
|
|||
5,18,1,Cannot convert 1 to Gtk.Orientation
|
||||
5,18,1,Cannot convert number to Gtk.Orientation
|
||||
|
|
|
@ -1,5 +1,14 @@
|
|||
using Gtk 4.0;
|
||||
using Gio 2.0;
|
||||
|
||||
Gtk.Shortcut {
|
||||
trigger: "Escape";
|
||||
}
|
||||
|
||||
Picture {
|
||||
paintable: "/path/to/paintable";
|
||||
}
|
||||
|
||||
ColorButton {
|
||||
rgba: "rgb(0, 0, 0)";
|
||||
}
|
|
@ -4,4 +4,10 @@
|
|||
<object class="GtkShortcut">
|
||||
<property name="trigger">Escape</property>
|
||||
</object>
|
||||
<object class="GtkPicture">
|
||||
<property name="paintable">/path/to/paintable</property>
|
||||
</object>
|
||||
<object class="GtkColorButton">
|
||||
<property name="rgba">rgb(0, 0, 0)</property>
|
||||
</object>
|
||||
</interface>
|
||||
|
|
11
tests/samples/typeof.blp
Normal file
11
tests/samples/typeof.blp
Normal file
|
@ -0,0 +1,11 @@
|
|||
using Gtk 4.0;
|
||||
using GObject 2.0;
|
||||
using Gio 2.0;
|
||||
|
||||
Gio.ListStore {
|
||||
item-type: typeof(GObject.Object);
|
||||
}
|
||||
|
||||
Gio.ListStore {
|
||||
item-type: typeof(.MyObject);
|
||||
}
|
10
tests/samples/typeof.ui
Normal file
10
tests/samples/typeof.ui
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<requires lib="gtk" version="4.0"/>
|
||||
<object class="GListStore">
|
||||
<property name="item-type">GObject</property>
|
||||
</object>
|
||||
<object class="GListStore">
|
||||
<property name="item-type">MyObject</property>
|
||||
</object>
|
||||
</interface>
|
|
@ -155,6 +155,7 @@ class TestSamples(unittest.TestCase):
|
|||
self.assert_sample("template")
|
||||
self.assert_sample("template_no_parent")
|
||||
self.assert_sample("translated")
|
||||
self.assert_sample("typeof")
|
||||
self.assert_sample("uint")
|
||||
self.assert_sample("unchecked_class")
|
||||
self.assert_sample("using")
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue