summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRico Tzschichholz <ricotz@ubuntu.com>2021-09-30 16:42:14 +0200
committerRico Tzschichholz <ricotz@ubuntu.com>2021-10-30 13:54:25 +0200
commit048aaba0cd88b784fe9fa421dbad494019abd42e (patch)
treea88abce391ffb3a667359e41c34c04fd4f2be0da
parent554011b393cf9635e2990a589c6323925fe53090 (diff)
downloadvala-048aaba0cd88b784fe9fa421dbad494019abd42e.tar.gz
vala: Add support for nested methods (local functions)
Fixes https://gitlab.gnome.org/GNOME/vala/issues/1232
-rw-r--r--tests/Makefile.am1
-rw-r--r--tests/methods/local-functions.c-expected203
-rw-r--r--tests/methods/local-functions.vala34
-rw-r--r--vala/valaparser.vala61
4 files changed, 298 insertions, 1 deletions
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 518a22f43..2628320bb 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -174,6 +174,7 @@ TESTS = \
methods/ellipsis-preceding.test \
methods/extern.vala \
methods/iterator.vala \
+ methods/local-functions.vala \
methods/parameter-fixed-array-initializer.vala \
methods/parameter-out-free-on-error.vala \
methods/parameter-ref-array-resize.vala \
diff --git a/tests/methods/local-functions.c-expected b/tests/methods/local-functions.c-expected
new file mode 100644
index 000000000..6e49f842b
--- /dev/null
+++ b/tests/methods/local-functions.c-expected
@@ -0,0 +1,203 @@
+/* methods_local_functions.c generated by valac, the Vala compiler
+ * generated from methods_local_functions.vala, do not modify */
+
+#include <glib.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef gint (*_LocalFunc0_) (gpointer user_data);
+typedef gboolean (*_LocalFunc1_) (gint a, gint b, gpointer user_data);
+typedef void (*_LocalFunc2_) (gchar* * s, gpointer user_data);
+typedef const gchar* (*_LocalFunc3_) (gchar* * s, gpointer user_data);
+typedef struct _Block1Data Block1Data;
+#define _g_free0(var) (var = (g_free (var), NULL))
+#define _vala_assert(expr, msg) if G_LIKELY (expr) ; else g_assertion_message_expr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, msg);
+#define _vala_return_if_fail(expr, msg) if G_LIKELY (expr) ; else { g_return_if_fail_warning (G_LOG_DOMAIN, G_STRFUNC, msg); return; }
+#define _vala_return_val_if_fail(expr, msg, val) if G_LIKELY (expr) ; else { g_return_if_fail_warning (G_LOG_DOMAIN, G_STRFUNC, msg); return val; }
+#define _vala_warn_if_fail(expr, msg) if G_LIKELY (expr) ; else g_warn_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, msg);
+
+struct _Block1Data {
+ int _ref_count_;
+ gint x;
+ gint y;
+};
+
+static void _vala_main (void);
+static Block1Data* block1_data_ref (Block1Data* _data1_);
+static void block1_data_unref (void * _userdata_);
+static gint ___lambda4_ (Block1Data* _data1_);
+static gint ____lambda4___localfunc0_ (gpointer self);
+static gboolean ___lambda5_ (gint a,
+ gint b);
+static gboolean ____lambda5___localfunc1_ (gint a,
+ gint b,
+ gpointer self);
+static void ___lambda6_ (gchar* * s);
+static void ____lambda6___localfunc2_ (gchar* * s,
+ gpointer self);
+static const gchar* ___lambda7_ (gchar* * s);
+static const gchar* ____lambda7___localfunc3_ (gchar* * s,
+ gpointer self);
+
+static Block1Data*
+block1_data_ref (Block1Data* _data1_)
+{
+ g_atomic_int_inc (&_data1_->_ref_count_);
+ return _data1_;
+}
+
+static void
+block1_data_unref (void * _userdata_)
+{
+ Block1Data* _data1_;
+ _data1_ = (Block1Data*) _userdata_;
+ if (g_atomic_int_dec_and_test (&_data1_->_ref_count_)) {
+ g_slice_free (Block1Data, _data1_);
+ }
+}
+
+static gint
+___lambda4_ (Block1Data* _data1_)
+{
+ gint result = 0;
+ result = _data1_->x + _data1_->y;
+ return result;
+}
+
+static gint
+____lambda4___localfunc0_ (gpointer self)
+{
+ gint result;
+ result = ___lambda4_ (self);
+ return result;
+}
+
+static gboolean
+___lambda5_ (gint a,
+ gint b)
+{
+ gboolean result = FALSE;
+ _vala_assert ((a * b) == 966, "a * b == 966");
+ result = TRUE;
+ return result;
+}
+
+static gboolean
+____lambda5___localfunc1_ (gint a,
+ gint b,
+ gpointer self)
+{
+ gboolean result;
+ result = ___lambda5_ (a, b);
+ return result;
+}
+
+static void
+___lambda6_ (gchar* * s)
+{
+ gchar* _vala_s = NULL;
+ gchar* _tmp0_;
+ _tmp0_ = g_strdup ("foo");
+ _g_free0 (_vala_s);
+ _vala_s = _tmp0_;
+ if (s) {
+ *s = _vala_s;
+ } else {
+ _g_free0 (_vala_s);
+ }
+}
+
+static void
+____lambda6___localfunc2_ (gchar* * s,
+ gpointer self)
+{
+ ___lambda6_ (s);
+}
+
+static const gchar*
+___lambda7_ (gchar* * s)
+{
+ gchar* _tmp0_;
+ const gchar* result = NULL;
+ g_return_val_if_fail (*s != NULL, NULL);
+ _vala_assert (g_strcmp0 (*s, "foo") == 0, "s == \"foo\"");
+ _tmp0_ = g_strdup ("bar");
+ _g_free0 (*s);
+ *s = _tmp0_;
+ result = *s;
+ return result;
+}
+
+static const gchar*
+____lambda7___localfunc3_ (gchar* * s,
+ gpointer self)
+{
+ const gchar* result;
+ result = ___lambda7_ (s);
+ return result;
+}
+
+static void
+_vala_main (void)
+{
+ {
+ Block1Data* _data1_;
+ _LocalFunc0_ foo = NULL;
+ gpointer foo_target;
+ _data1_ = g_slice_new0 (Block1Data);
+ _data1_->_ref_count_ = 1;
+ _data1_->x = 23;
+ _data1_->y = 42;
+ foo = ____lambda4___localfunc0_;
+ foo_target = _data1_;
+ _vala_assert (foo (foo_target) == 65, "foo () == 65");
+ block1_data_unref (_data1_);
+ _data1_ = NULL;
+ }
+ {
+ _LocalFunc1_ foo = NULL;
+ gpointer foo_target;
+ foo = ____lambda5___localfunc1_;
+ foo_target = NULL;
+ _vala_assert (foo (23, 42, foo_target), "foo (23, 42)");
+ }
+ {
+ _LocalFunc2_ foo = NULL;
+ gpointer foo_target;
+ gchar* s = NULL;
+ gchar* _tmp0_ = NULL;
+ foo = ____lambda6___localfunc2_;
+ foo_target = NULL;
+ foo (&_tmp0_, foo_target);
+ _g_free0 (s);
+ s = _tmp0_;
+ _vala_assert (g_strcmp0 (s, "foo") == 0, "s == \"foo\"");
+ _g_free0 (s);
+ }
+ {
+ _LocalFunc3_ foo = NULL;
+ gpointer foo_target;
+ gchar* s = NULL;
+ gchar* _tmp1_;
+ const gchar* _tmp2_;
+ const gchar* _tmp3_;
+ foo = ____lambda7___localfunc3_;
+ foo_target = NULL;
+ _tmp1_ = g_strdup ("foo");
+ s = _tmp1_;
+ _tmp2_ = foo (&s, foo_target);
+ _vala_assert (g_strcmp0 (_tmp2_, "bar") == 0, "foo (ref s) == \"bar\"");
+ _tmp3_ = s;
+ _vala_assert (g_strcmp0 (_tmp3_, "bar") == 0, "s == \"bar\"");
+ _g_free0 (s);
+ }
+}
+
+int
+main (int argc,
+ char ** argv)
+{
+ _vala_main ();
+ return 0;
+}
+
diff --git a/tests/methods/local-functions.vala b/tests/methods/local-functions.vala
new file mode 100644
index 000000000..02b9b0164
--- /dev/null
+++ b/tests/methods/local-functions.vala
@@ -0,0 +1,34 @@
+void main () {
+ {
+ int x = 23, y = 42;
+ int foo () {
+ return x + y;
+ }
+ assert (foo () == 65);
+ }
+ {
+ bool foo (int a, int b) {
+ assert (a * b == 966);
+ return true;
+ }
+ assert (foo (23, 42));
+ }
+ {
+ void foo (out string s) {
+ s = "foo";
+ }
+ string s;
+ foo (out s);
+ assert (s == "foo");
+ }
+ {
+ unowned string foo (ref string s) {
+ assert (s == "foo");
+ s = "bar";
+ return s;
+ }
+ string s = "foo";
+ assert (foo (ref s) == "bar");
+ assert (s == "bar");
+ }
+}
diff --git a/vala/valaparser.vala b/vala/valaparser.vala
index a2b99f9ed..93031cee1 100644
--- a/vala/valaparser.vala
+++ b/vala/valaparser.vala
@@ -42,6 +42,7 @@ public class Vala.Parser : CodeVisitor {
const int BUFFER_SIZE = 32;
static List<TypeParameter> _empty_type_parameter_list;
+ static int next_local_func_id = 0;
struct TokenInfo {
public TokenType type;
@@ -1754,7 +1755,12 @@ public class Vala.Parser : CodeVisitor {
stmt = parse_expression_statement ();
} else {
is_decl = true;
- parse_local_variable_declarations (block);
+ bool is_local_func = is_local_function ();
+ if (is_local_func) {
+ parse_local_function_declaration (block);
+ } else {
+ parse_local_variable_declarations (block);
+ }
}
break;
}
@@ -1871,6 +1877,22 @@ public class Vala.Parser : CodeVisitor {
return false;
}
+ bool is_local_function () {
+ var begin = get_location ();
+
+ try {
+ skip_type ();
+ if (accept (TokenType.IDENTIFIER) && accept (TokenType.OPEN_PARENS)) {
+ rollback (begin);
+ return true;
+ }
+ } catch {
+ }
+
+ rollback (begin);
+ return false;
+ }
+
Block parse_embedded_statement (string statement_name, bool accept_empty_body = true) throws ParseError {
if (current () == TokenType.OPEN_BRACE) {
var block = parse_block ();
@@ -2084,6 +2106,43 @@ public class Vala.Parser : CodeVisitor {
return new Constant (id, type, initializer, src);
}
+ void parse_local_function_declaration (Block block) throws ParseError {
+ var begin = get_location ();
+ var type = parse_type (true, false);
+ var sym = parse_symbol_name ();
+
+ expect (TokenType.OPEN_PARENS);
+ List<Parameter> params = new ArrayList<Parameter> ();
+ if (current () != TokenType.CLOSE_PARENS) {
+ do {
+ params.add (parse_parameter ());
+ } while (accept (TokenType.COMMA));
+ }
+ expect (TokenType.CLOSE_PARENS);
+
+ var src = get_src (begin);
+
+ var d = new Delegate ("_LocalFunc%i_".printf (next_local_func_id++), type, src);
+ foreach (var param in params) {
+ d.add_parameter (param);
+ }
+ context.root.add_delegate (d);
+
+ var lambda = new LambdaExpression.with_statement_body (parse_block (), src);
+ foreach (var p in params) {
+ var param = new Parameter (p.name, null, p.source_reference);
+ param.direction = p.direction;
+ lambda.add_parameter (param);
+ }
+
+ if (!context.experimental) {
+ Report.warning (src, "local functions are experimental");
+ }
+
+ var local = new LocalVariable (new DelegateType (d), sym.name, lambda, src);
+ block.add_statement (new DeclarationStatement (local, src));
+ }
+
Statement parse_expression_statement () throws ParseError {
var begin = get_location ();
var expr = parse_statement_expression ();