mirror of
https://gitlab.gnome.org/jwestman/blueprint-compiler.git
synced 2025-05-04 15:59:08 -04:00
language: Add closure expressions
This commit is contained in:
parent
5cf9b63547
commit
59aa054c4c
10 changed files with 96 additions and 7 deletions
|
@ -189,7 +189,10 @@ class TypeType(BasicType):
|
||||||
|
|
||||||
|
|
||||||
_BASIC_TYPES = {
|
_BASIC_TYPES = {
|
||||||
|
"bool": BoolType,
|
||||||
"gboolean": BoolType,
|
"gboolean": BoolType,
|
||||||
|
"string": StringType,
|
||||||
|
"gchararray": StringType,
|
||||||
"int": IntType,
|
"int": IntType,
|
||||||
"gint": IntType,
|
"gint": IntType,
|
||||||
"gint64": IntType,
|
"gint64": IntType,
|
||||||
|
@ -730,6 +733,9 @@ class GirContext:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_type(self, name: str, ns: str) -> T.Optional[GirNode]:
|
def get_type(self, name: str, ns: str) -> T.Optional[GirNode]:
|
||||||
|
if ns is None and name in _BASIC_TYPES:
|
||||||
|
return _BASIC_TYPES[name]()
|
||||||
|
|
||||||
ns = ns or "Gtk"
|
ns = ns or "Gtk"
|
||||||
|
|
||||||
if ns not in self.namespaces:
|
if ns not in self.namespaces:
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from .attributes import BaseAttribute, BaseTypedAttribute
|
from .attributes import BaseAttribute, BaseTypedAttribute
|
||||||
from .expression import CastExpr, IdentExpr, LookupOp, ExprChain
|
from .expression import CastExpr, ClosureExpr, Expr, ExprChain, IdentExpr, LookupOp
|
||||||
from .gobject_object import Object, ObjectContent
|
from .gobject_object import Object, ObjectContent
|
||||||
from .gobject_property import Property
|
from .gobject_property import Property
|
||||||
from .gobject_signal import Signal
|
from .gobject_signal import Signal
|
||||||
|
|
|
@ -25,13 +25,24 @@ from .types import TypeName
|
||||||
expr = Pratt()
|
expr = Pratt()
|
||||||
|
|
||||||
|
|
||||||
class Expr:
|
class Expr(AstNode):
|
||||||
@property
|
@property
|
||||||
def type(self) -> T.Optional[GirType]:
|
def type(self) -> T.Optional[GirType]:
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def rhs(self) -> T.Optional["Expr"]:
|
||||||
|
if isinstance(self.parent, ExprChain):
|
||||||
|
children = list(self.parent.children)
|
||||||
|
if children.index(self) + 1 < len(children):
|
||||||
|
return children[children.index(self) + 1]
|
||||||
|
else:
|
||||||
|
return self.parent.rhs
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
class ExprChain(Expr, AstNode):
|
|
||||||
|
class ExprChain(Expr):
|
||||||
grammar = expr
|
grammar = expr
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -43,14 +54,14 @@ class ExprChain(Expr, AstNode):
|
||||||
return self.last.type
|
return self.last.type
|
||||||
|
|
||||||
|
|
||||||
class InfixExpr(Expr, AstNode):
|
class InfixExpr(Expr):
|
||||||
@property
|
@property
|
||||||
def lhs(self):
|
def lhs(self):
|
||||||
children = list(self.parent_by_type(ExprChain).children)
|
children = list(self.parent_by_type(ExprChain).children)
|
||||||
return children[children.index(self) - 1]
|
return children[children.index(self) - 1]
|
||||||
|
|
||||||
|
|
||||||
class IdentExpr(Expr, AstNode):
|
class IdentExpr(Expr):
|
||||||
grammar = UseIdent("ident")
|
grammar = UseIdent("ident")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -124,7 +135,45 @@ class CastExpr(InfixExpr):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ClosureExpr(Expr):
|
||||||
|
grammar = [
|
||||||
|
Optional(["$", UseLiteral("extern", True)]),
|
||||||
|
UseIdent("name"),
|
||||||
|
"(",
|
||||||
|
Delimited(ExprChain, ","),
|
||||||
|
")",
|
||||||
|
]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self) -> T.Optional[GirType]:
|
||||||
|
if isinstance(self.rhs, CastExpr):
|
||||||
|
return self.rhs.type
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def closure_name(self) -> str:
|
||||||
|
return self.tokens["name"]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def args(self) -> T.List[ExprChain]:
|
||||||
|
return self.children[ExprChain]
|
||||||
|
|
||||||
|
@validate()
|
||||||
|
def cast_to_return_type(self):
|
||||||
|
if not isinstance(self.rhs, CastExpr):
|
||||||
|
raise CompileError(
|
||||||
|
"Closure expression must be cast to the closure's return type"
|
||||||
|
)
|
||||||
|
|
||||||
|
@validate()
|
||||||
|
def builtin_exists(self):
|
||||||
|
if not self.tokens["extern"]:
|
||||||
|
raise CompileError(f"{self.closure_name} is not a builtin function")
|
||||||
|
|
||||||
|
|
||||||
expr.children = [
|
expr.children = [
|
||||||
|
Prefix(ClosureExpr),
|
||||||
Prefix(IdentExpr),
|
Prefix(IdentExpr),
|
||||||
Prefix(["(", ExprChain, ")"]),
|
Prefix(["(", ExprChain, ")"]),
|
||||||
Infix(10, LookupOp),
|
Infix(10, LookupOp),
|
||||||
|
|
|
@ -179,7 +179,7 @@ class XmlOutput(OutputFormat):
|
||||||
def _emit_expression(self, expression: ExprChain, xml: XmlEmitter):
|
def _emit_expression(self, expression: ExprChain, xml: XmlEmitter):
|
||||||
self._emit_expression_part(expression.children[-1], xml)
|
self._emit_expression_part(expression.children[-1], xml)
|
||||||
|
|
||||||
def _emit_expression_part(self, expression, xml: XmlEmitter):
|
def _emit_expression_part(self, expression: Expr, xml: XmlEmitter):
|
||||||
if isinstance(expression, IdentExpr):
|
if isinstance(expression, IdentExpr):
|
||||||
self._emit_ident_expr(expression, xml)
|
self._emit_ident_expr(expression, xml)
|
||||||
elif isinstance(expression, LookupOp):
|
elif isinstance(expression, LookupOp):
|
||||||
|
@ -188,6 +188,8 @@ class XmlOutput(OutputFormat):
|
||||||
self._emit_expression(expression, xml)
|
self._emit_expression(expression, xml)
|
||||||
elif isinstance(expression, CastExpr):
|
elif isinstance(expression, CastExpr):
|
||||||
self._emit_cast_expr(expression, xml)
|
self._emit_cast_expr(expression, xml)
|
||||||
|
elif isinstance(expression, ClosureExpr):
|
||||||
|
self._emit_closure_expr(expression, xml)
|
||||||
else:
|
else:
|
||||||
raise CompilerBugError()
|
raise CompilerBugError()
|
||||||
|
|
||||||
|
@ -204,6 +206,12 @@ class XmlOutput(OutputFormat):
|
||||||
def _emit_cast_expr(self, expr: CastExpr, xml: XmlEmitter):
|
def _emit_cast_expr(self, expr: CastExpr, xml: XmlEmitter):
|
||||||
self._emit_expression_part(expr.lhs, xml)
|
self._emit_expression_part(expr.lhs, xml)
|
||||||
|
|
||||||
|
def _emit_closure_expr(self, expr: ClosureExpr, xml: XmlEmitter):
|
||||||
|
xml.start_tag("closure", function=expr.closure_name, type=expr.type)
|
||||||
|
for arg in expr.args:
|
||||||
|
self._emit_expression_part(arg, xml)
|
||||||
|
xml.end_tag()
|
||||||
|
|
||||||
def _emit_attribute(
|
def _emit_attribute(
|
||||||
self, tag: str, attr: str, name: str, value: Value, xml: XmlEmitter
|
self, tag: str, attr: str, name: str, value: Value, xml: XmlEmitter
|
||||||
):
|
):
|
||||||
|
|
|
@ -46,7 +46,7 @@ _tokens = [
|
||||||
(TokenType.WHITESPACE, r"\s+"),
|
(TokenType.WHITESPACE, r"\s+"),
|
||||||
(TokenType.COMMENT, r"\/\*[\s\S]*?\*\/"),
|
(TokenType.COMMENT, r"\/\*[\s\S]*?\*\/"),
|
||||||
(TokenType.COMMENT, r"\/\/[^\n]*"),
|
(TokenType.COMMENT, r"\/\/[^\n]*"),
|
||||||
(TokenType.OP, r"<<|>>|=>|::|<|>|:=|\.|\|\||\||\+|\-|\*|=|:|/"),
|
(TokenType.OP, r"\$|<<|>>|=>|::|<|>|:=|\.|\|\||\||\+|\-|\*|=|:|/"),
|
||||||
(TokenType.PUNCTUATION, r"\(|\)|\{|\}|;|\[|\]|\,"),
|
(TokenType.PUNCTUATION, r"\(|\)|\{|\}|;|\[|\]|\,"),
|
||||||
]
|
]
|
||||||
_TOKENS = [(type, re.compile(regex)) for (type, regex) in _tokens]
|
_TOKENS = [(type, re.compile(regex)) for (type, regex) in _tokens]
|
||||||
|
|
5
tests/sample_errors/expr_closure_not_cast.blp
Normal file
5
tests/sample_errors/expr_closure_not_cast.blp
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
using Gtk 4.0;
|
||||||
|
|
||||||
|
Label {
|
||||||
|
label: bind $closure();
|
||||||
|
}
|
1
tests/sample_errors/expr_closure_not_cast.err
Normal file
1
tests/sample_errors/expr_closure_not_cast.err
Normal file
|
@ -0,0 +1 @@
|
||||||
|
4,15,10,Closure expression must be cast to the closure's return type
|
5
tests/samples/expr_closure.blp
Normal file
5
tests/samples/expr_closure.blp
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
using Gtk 4.0;
|
||||||
|
|
||||||
|
Label my-label {
|
||||||
|
label: bind ($my-closure(my-label.margin-bottom)) as (string);
|
||||||
|
}
|
13
tests/samples/expr_closure.ui
Normal file
13
tests/samples/expr_closure.ui
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<interface>
|
||||||
|
<requires lib="gtk" version="4.0"/>
|
||||||
|
<object class="GtkLabel" id="my-label">
|
||||||
|
<binding name="label">
|
||||||
|
<closure function="my-closure" type="gchararray">
|
||||||
|
<lookup name="margin-bottom" type="GtkLabel">
|
||||||
|
<constant>my-label</constant>
|
||||||
|
</lookup>
|
||||||
|
</closure>
|
||||||
|
</binding>
|
||||||
|
</object>
|
||||||
|
</interface>
|
|
@ -151,6 +151,7 @@ class TestSamples(unittest.TestCase):
|
||||||
self.assert_sample("combo_box_text")
|
self.assert_sample("combo_box_text")
|
||||||
self.assert_sample("comments")
|
self.assert_sample("comments")
|
||||||
self.assert_sample("enum")
|
self.assert_sample("enum")
|
||||||
|
self.assert_sample("expr_closure", skip_run=True) # The closure doesn't exist
|
||||||
self.assert_sample("expr_lookup")
|
self.assert_sample("expr_lookup")
|
||||||
self.assert_sample("file_filter")
|
self.assert_sample("file_filter")
|
||||||
self.assert_sample("flags")
|
self.assert_sample("flags")
|
||||||
|
@ -208,6 +209,7 @@ class TestSamples(unittest.TestCase):
|
||||||
self.assert_sample_error("empty")
|
self.assert_sample_error("empty")
|
||||||
self.assert_sample_error("enum_member_dne")
|
self.assert_sample_error("enum_member_dne")
|
||||||
self.assert_sample_error("expr_cast_conversion")
|
self.assert_sample_error("expr_cast_conversion")
|
||||||
|
self.assert_sample_error("expr_closure_not_cast")
|
||||||
self.assert_sample_error("expr_lookup_dne")
|
self.assert_sample_error("expr_lookup_dne")
|
||||||
self.assert_sample_error("expr_lookup_no_properties")
|
self.assert_sample_error("expr_lookup_no_properties")
|
||||||
self.assert_sample_error("filters_in_non_file_filter")
|
self.assert_sample_error("filters_in_non_file_filter")
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue