diff --git a/blueprintcompiler/language/__init__.py b/blueprintcompiler/language/__init__.py index feeb301..3f87faf 100644 --- a/blueprintcompiler/language/__init__.py +++ b/blueprintcompiler/language/__init__.py @@ -16,6 +16,7 @@ from .gtk_string_list import Strings from .gtk_styles import Styles from .gtkbuilder_child import Child from .gtkbuilder_template import Template +from .lambdas import Lambda from .imports import GtkDirective, Import from .ui import UI from .values import IdentValue, TranslatedStringValue, FlagsValue, LiteralValue @@ -47,4 +48,5 @@ VALUE_HOOKS.children = [ FlagsValue, IdentValue, LiteralValue, + Lambda, ] diff --git a/blueprintcompiler/language/common.py b/blueprintcompiler/language/common.py index deeb5fc..8cf07ce 100644 --- a/blueprintcompiler/language/common.py +++ b/blueprintcompiler/language/common.py @@ -33,3 +33,24 @@ from ..xml_emitter import XmlEmitter OBJECT_HOOKS = AnyOf() OBJECT_CONTENT_HOOKS = AnyOf() VALUE_HOOKS = AnyOf() + + +class Scope: + def get_variables(self) -> T.Iterator[str]: + yield from self.get_objects().keys() + + def get_objects(self) -> T.Dict[str, T.Any]: + raise NotImplementedError() + + @property + def this_name(self) -> T.Optional[str]: + return None + + @property + def this_type(self) -> T.Optional[str]: + return None + + @property + def this_type_glib_name(self) -> T.Optional[str]: + return None + diff --git a/blueprintcompiler/language/expression.py b/blueprintcompiler/language/expression.py index a684b39..8f3bf1b 100644 --- a/blueprintcompiler/language/expression.py +++ b/blueprintcompiler/language/expression.py @@ -41,7 +41,14 @@ class InfixExpr(AstNode): class IdentExpr(AstNode): grammar = UseIdent("ident") + @property + def is_this(self): + return self.parent_by_type(Scope).this_name == self.tokens["ident"] + def emit_xml(self, xml: XmlEmitter): + if self.is_this: + raise CompilerBugError() + xml.start_tag("constant") xml.put_text(self.tokens["ident"]) xml.end_tag() @@ -51,9 +58,12 @@ class LookupOp(InfixExpr): grammar = [".", UseIdent("property")] def emit_xml(self, xml: XmlEmitter): - xml.start_tag("lookup", name=self.tokens["property"]) - self.lhs.emit_xml(xml) - xml.end_tag() + if isinstance(self.lhs, IdentExpr) and self.lhs.is_this: + xml.put_self_closing("lookup", name=self.tokens["property"], type=self.parent_by_type(Scope).this_type) + else: + xml.start_tag("lookup", name=self.tokens["property"]) + self.lhs.emit_xml(xml) + xml.end_tag() expr.children = [ diff --git a/blueprintcompiler/language/lambdas.py b/blueprintcompiler/language/lambdas.py new file mode 100644 index 0000000..495659b --- /dev/null +++ b/blueprintcompiler/language/lambdas.py @@ -0,0 +1,54 @@ +# lambdas.py +# +# Copyright 2022 James Westman +# +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This file is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program. If not, see . +# +# SPDX-License-Identifier: LGPL-3.0-or-later + + +from .types import TypeName +from .expression import Expr +from .values import Value +from .common import * + + +class Lambda(Value, Scope): + grammar = [ + "(", + TypeName, + UseIdent("argument"), + ")", + "=>", + Expr, + ] + + def emit_xml(self, xml: XmlEmitter): + for child in self.children: + child.emit_xml(xml) + + def get_objects(self): + return { + **self.parent.parent_by_type(Scope).get_objects(), + self.tokens["argument"]: None, + } + + @property + def this_name(self) -> str: + return self.tokens["argument"] + + @property + def this_type(self) -> str: + return self.children[TypeName][0].gir_type + diff --git a/blueprintcompiler/language/ui.py b/blueprintcompiler/language/ui.py index 9123bf8..9191120 100644 --- a/blueprintcompiler/language/ui.py +++ b/blueprintcompiler/language/ui.py @@ -24,7 +24,7 @@ from .gtkbuilder_template import Template from .common import * -class UI(AstNode): +class UI(AstNode, Scope): """ The AST node for the entire file """ grammar = [ @@ -62,6 +62,8 @@ class UI(AstNode): def objects_by_id(self): return { obj.tokens["id"]: obj for obj in self.iterate_children_recursive() if obj.tokens["id"] is not None } + def get_objects(self): + return self.objects_by_id @validate() def gir_errors(self): diff --git a/tests/samples/lambda.blp b/tests/samples/lambda.blp new file mode 100644 index 0000000..1db7003 --- /dev/null +++ b/tests/samples/lambda.blp @@ -0,0 +1,9 @@ +using Gtk 4.0; + +Gtk.BoolFilter filter { + expression: (Gtk.Label object) => object.visible; +} + +Gtk.BoolFilter { + expression: (Gtk.Label object) => filter.invert; +} diff --git a/tests/samples/lambda.ui b/tests/samples/lambda.ui new file mode 100644 index 0000000..177449b --- /dev/null +++ b/tests/samples/lambda.ui @@ -0,0 +1,16 @@ + + + + + + + + + + + + filter + + + + diff --git a/tests/test_samples.py b/tests/test_samples.py index 4fdb5c0..7da4ae9 100644 --- a/tests/test_samples.py +++ b/tests/test_samples.py @@ -141,6 +141,7 @@ class TestSamples(unittest.TestCase): self.assert_sample("flags") self.assert_sample("id_prop") self.assert_sample("inline_menu") + self.assert_sample("lambda") self.assert_sample("layout") self.assert_sample("menu") self.assert_sample("numbers")