Hi there,

Attached you can find a tryout for adding simple support for extension
methods to valac.

I'm nonetheless mailing this unfinished work to the mailing list for
feedback already. Especially how I'm finding the Class to add the method
to concerns me (I dislike the find_in_tree which I added to Namespace).

I'm also unsure about just using add_method on the class, instead of
adding a add_extension_method instead (and keeping the list separate).
That or perhaps have a is_extension bool on Vala.Method?

Right now the compiler will not use the parameter's identifier to refer
to the this object. Instead only 'this' works. The Code writer must I
guess also do something like TestA *a = this at the top of the function?

The syntax that I'm aiming for is very much like extension methods in
C#:

public class Test {
        public void Existing() {
        }
}

public void Method (this Test a) {
        // a.Existing(); doesn't work yet
        this.Existing(); // works atm, but should use a here
}

Hoping for input and assistance from more experience Valac developers ;-)

Kind regards,

Philip


-- 
Philip Van Hoof
freelance software developer
Codeminded BVBA - http://codeminded.be



>From 6c101e3de51675265e064453acf46f99b68e102c Mon Sep 17 00:00:00 2001
From: Philip Van Hoof <phi...@codeminded.be>
Date: Mon, 14 Oct 2013 17:41:17 +0200
Subject: [PATCH] Adds support for simple extension methods

For the moment you can't use the parameter's identifier in the method,
you must use this (which isn't correct yet).
---
 tests/Makefile.am                   |  1 +
 tests/methods/extensionmethods.vala | 32 ++++++++++++++++++++++++++++++++
 vala/valamethod.vala                |  2 ++
 vala/valanamespace.vala             | 15 +++++++++++++++
 vala/valaparser.vala                | 16 +++++++++++++++-
 5 files changed, 65 insertions(+), 1 deletion(-)
 create mode 100644 tests/methods/extensionmethods.vala

diff --git a/tests/Makefile.am b/tests/Makefile.am
index de7e823..5b88123 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -57,6 +57,7 @@ TESTS = \
 	methods/bug653908.vala \
 	methods/bug663210.vala \
 	methods/generics.vala \
+	methods/extensionmethods.vala \
 	control-flow/break.vala \
 	control-flow/expressions-conditional.vala \
 	control-flow/for.vala \
diff --git a/tests/methods/extensionmethods.vala b/tests/methods/extensionmethods.vala
new file mode 100644
index 0000000..073a8de
--- /dev/null
+++ b/tests/methods/extensionmethods.vala
@@ -0,0 +1,32 @@
+
+public class TestB
+{
+	public void Hello() {
+		print ("Hello\n");
+	}
+}
+
+public class TestA
+{
+	public void Test(TestB b) {
+		b.Hello();
+	}
+}
+
+public void Hallo(this TestA a, string msg)
+{
+	print ("Hallo %s\n", msg);
+	TestB b = new TestB();
+	this.Test(b);
+}
+
+public class Program {
+	static void main ()
+	{
+		TestA a = new TestA();
+		TestB b = new TestB();
+		a.Test(b);
+		a.Hallo("hihi");
+
+	}
+}
diff --git a/vala/valamethod.vala b/vala/valamethod.vala
index 663ae6f..3b5df12 100644
--- a/vala/valamethod.vala
+++ b/vala/valamethod.vala
@@ -41,6 +41,8 @@ public class Vala.Method : Subroutine {
 		}
 	}
 
+	public Parameter extension_for { get; set; }
+
 	public override bool has_result {
 		get { return !(return_type is VoidType); }
 	}
diff --git a/vala/valanamespace.vala b/vala/valanamespace.vala
index 47dd4ee..d8d6770 100644
--- a/vala/valanamespace.vala
+++ b/vala/valanamespace.vala
@@ -54,6 +54,21 @@ public class Vala.Namespace : Symbol {
 		access = SymbolAccessibility.PUBLIC;
 	}
 
+	public Class? find_in_tree (string f_name) {
+		foreach (Class cl in classes) {
+			if (cl.name == f_name)
+				return cl;
+		}
+		foreach (Namespace ns in namespaces) {
+			foreach (Class cl in ns.classes) {
+				if (cl.name == f_name)
+					return cl;
+			}
+			return ns.find_in_tree (f_name);
+		}
+		return null;
+	}
+
 	/**
 	 * Adds a new using directive with the specified namespace.
 	 *
diff --git a/vala/valaparser.vala b/vala/valaparser.vala
index a7ca9c0..22e708c 100644
--- a/vala/valaparser.vala
+++ b/vala/valaparser.vala
@@ -2615,6 +2615,10 @@ public class Vala.Parser : CodeVisitor {
 		expect (TokenType.OPEN_PARENS);
 		if (current () != TokenType.CLOSE_PARENS) {
 			do {
+				if (accept (TokenType.THIS)) {
+					method.extension_for = parse_parameter ();
+					continue;
+				}
 				var param = parse_parameter ();
 				method.add_parameter (param);
 			} while (accept (TokenType.COMMA));
@@ -2641,7 +2645,16 @@ public class Vala.Parser : CodeVisitor {
 			method.external = true;
 		}
 
-		parent.add_method (method);
+		if (method.extension_for != null) {
+			var cl = context.root.find_in_tree (method.extension_for.variable_type.to_string());
+			if (cl != null) {
+				cl.add_method (method);
+			} else {
+				throw new ParseError.SYNTAX (get_error ("Could not find class `%s' to add extension method `%s' to".printf(method.extension_for.variable_type.to_string(), method.name)));
+			}
+		} else {
+			parent.add_method (method);
+		}
 	}
 
 	void parse_property_declaration (Symbol parent, List<Attribute>? attrs) throws ParseError {
@@ -3181,6 +3194,7 @@ public class Vala.Parser : CodeVisitor {
 		if (accept (TokenType.ASSIGN)) {
 			param.initializer = parse_expression ();
 		}
+
 		return param;
 	}
 
-- 
1.8.4.rc3

_______________________________________________
vala-list mailing list
vala-list@gnome.org
https://mail.gnome.org/mailman/listinfo/vala-list

Reply via email to