diff options
author | Rico Tzschichholz <ricotz@ubuntu.com> | 2021-09-30 16:42:14 +0200 |
---|---|---|
committer | Rico Tzschichholz <ricotz@ubuntu.com> | 2021-10-30 13:54:25 +0200 |
commit | 048aaba0cd88b784fe9fa421dbad494019abd42e (patch) | |
tree | a88abce391ffb3a667359e41c34c04fd4f2be0da | |
parent | 554011b393cf9635e2990a589c6323925fe53090 (diff) | |
download | vala-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.am | 1 | ||||
-rw-r--r-- | tests/methods/local-functions.c-expected | 203 | ||||
-rw-r--r-- | tests/methods/local-functions.vala | 34 | ||||
-rw-r--r-- | vala/valaparser.vala | 61 |
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 (); |