From cc1d2ab78f4a682e6756db6d19580ded70efb30b Mon Sep 17 00:00:00 2001 From: James Westman Date: Sat, 30 Apr 2022 13:27:54 -0500 Subject: [PATCH] expressions: Add closure expressions --- blueprintcompiler/language/expression.py | 25 ++++++++++++++++++++++++ tests/sample_errors/closure_not_cast.blp | 5 +++++ tests/sample_errors/closure_not_cast.err | 1 + tests/samples/expr_closure.blp | 11 +++++++++++ tests/samples/expr_closure.ui | 25 ++++++++++++++++++++++++ tests/test_samples.py | 2 ++ 6 files changed, 69 insertions(+) create mode 100644 tests/sample_errors/closure_not_cast.blp create mode 100644 tests/sample_errors/closure_not_cast.err create mode 100644 tests/samples/expr_closure.blp create mode 100644 tests/samples/expr_closure.ui diff --git a/blueprintcompiler/language/expression.py b/blueprintcompiler/language/expression.py index 736657f..31473de 100644 --- a/blueprintcompiler/language/expression.py +++ b/blueprintcompiler/language/expression.py @@ -64,6 +64,30 @@ class IdentExpr(AstNode): self.parent_by_type(Scope).variables[self.tokens["ident"]].emit_xml(xml) +class ClosureExpr(AstNode): + grammar = [ + UseIdent("function"), + "(", + Delimited(Expr, ",").expected("closure arguments"), + Match(")").expected(), + ] + + @validate() + def is_cast_to_return_val(self): + if not isinstance(self.parent.parent, CastExpr): + raise CompileError(f"Closure expression needs to be cast to {self.tokens['function']}'s return type") + + @property + def gir_type(self): + return self.parent.parent.gir_type + + def emit_xml(self, xml: XmlEmitter): + xml.start_tag("closure", function=self.tokens["function"], type=self.gir_type) + for child in self.children[Expr]: + child.emit_xml(xml) + xml.end_tag() + + class LookupOp(InfixExpr): grammar = [".", UseIdent("property")] @@ -92,6 +116,7 @@ class CastExpr(AstNode): expr.children = [ + Prefix(ClosureExpr), Prefix(IdentExpr), Prefix(CastExpr), Prefix(["(", Expr, ")"]), diff --git a/tests/sample_errors/closure_not_cast.blp b/tests/sample_errors/closure_not_cast.blp new file mode 100644 index 0000000..ed5949d --- /dev/null +++ b/tests/sample_errors/closure_not_cast.blp @@ -0,0 +1,5 @@ +using Gtk 4.0; + +Label { + label: bind my_func(); +} diff --git a/tests/sample_errors/closure_not_cast.err b/tests/sample_errors/closure_not_cast.err new file mode 100644 index 0000000..6d55835 --- /dev/null +++ b/tests/sample_errors/closure_not_cast.err @@ -0,0 +1 @@ +4,15,9,Closure expression needs to be cast to my_func's return type \ No newline at end of file diff --git a/tests/samples/expr_closure.blp b/tests/samples/expr_closure.blp new file mode 100644 index 0000000..f6035dd --- /dev/null +++ b/tests/samples/expr_closure.blp @@ -0,0 +1,11 @@ +using Gtk 4.0; + +Box { + Box box { + } +} + +Stack { + visible-child: bind ((Gtk.Button)my_closure(box.parent)).parent; + visible: bind (bool) my_other_closure(box); +} diff --git a/tests/samples/expr_closure.ui b/tests/samples/expr_closure.ui new file mode 100644 index 0000000..27249e6 --- /dev/null +++ b/tests/samples/expr_closure.ui @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + box + + + + + + + box + + + + diff --git a/tests/test_samples.py b/tests/test_samples.py index 7da4ae9..f80625b 100644 --- a/tests/test_samples.py +++ b/tests/test_samples.py @@ -136,6 +136,7 @@ class TestSamples(unittest.TestCase): self.assert_sample("combo_box_text") self.assert_sample("comments") self.assert_sample("enum") + self.assert_sample("expr_closure") self.assert_sample("expr_lookup") self.assert_sample("file_filter") self.assert_sample("flags") @@ -179,6 +180,7 @@ class TestSamples(unittest.TestCase): self.assert_sample_error("children") self.assert_sample_error("class_assign") self.assert_sample_error("class_dne") + self.assert_sample_error("closure_not_cast") self.assert_sample_error("consecutive_unexpected_tokens") self.assert_sample_error("does_not_implement") self.assert_sample_error("duplicate_obj_id")