don't pass parameter list to lambda expression contructor support static
authorJürg Billeter <j@bitron.ch>
Wed, 5 Jul 2006 14:49:34 +0000 (14:49 +0000)
committerJürg Billeter <juergbi@src.gnome.org>
Wed, 5 Jul 2006 14:49:34 +0000 (14:49 +0000)
2006-07-05  Jürg Billeter  <j@bitron.ch>

* vala/parser.y: don't pass parameter list to lambda expression
  contructor
* vala/valasemanticanalyzer.vala, vala/valamemorymanager.vala,
  vala/valacodegenerator.vala: support static lambda expressions and
  nested methods
* vala/valainvocationexpression.vala: visit call node before visiting
  begin of invocation expression
* vala/valaformalparameter.vala, vala/valalambdaexpression.vala: add
  interface documentation, use implicit namespace specification
* vala/valamethod.vala: allow nested methods
* tests/test-015.vala: test lambda expressions
* tests/Makefile.am: update

svn path=/trunk/; revision=65

vala/ChangeLog
vala/tests/Makefile.am
vala/tests/test-015.vala [new file with mode: 0644]
vala/vala/parser.y
vala/vala/valacodegenerator.vala
vala/vala/valaformalparameter.vala
vala/vala/valainvocationexpression.vala
vala/vala/valalambdaexpression.vala
vala/vala/valamemorymanager.vala
vala/vala/valamethod.vala
vala/vala/valasemanticanalyzer.vala

index 9a6fcdf..d18be5e 100644 (file)
@@ -1,3 +1,18 @@
+2006-07-05  Jürg Billeter  <j@bitron.ch>
+
+       * vala/parser.y: don't pass parameter list to lambda expression
+         contructor
+       * vala/valasemanticanalyzer.vala, vala/valamemorymanager.vala,
+         vala/valacodegenerator.vala: support static lambda expressions and
+         nested methods
+       * vala/valainvocationexpression.vala: visit call node before visiting
+         begin of invocation expression
+       * vala/valaformalparameter.vala, vala/valalambdaexpression.vala: add
+         interface documentation, use implicit namespace specification
+       * vala/valamethod.vala: allow nested methods
+       * tests/test-015.vala: test lambda expressions
+       * tests/Makefile.am: update
+
 2006-07-04  Jürg Billeter  <j@bitron.ch>
 
        * vala/parser.y: support implicit namespace specification in callback
index 1a684b0..2297438 100644 (file)
@@ -15,4 +15,5 @@ EXTRA_DIST = \
        test-012.vala \
        test-013.vala \
        test-014.vala \
+       test-015.vala \
        $(NULL)
diff --git a/vala/tests/test-015.vala b/vala/tests/test-015.vala
new file mode 100644 (file)
index 0000000..1280e4f
--- /dev/null
@@ -0,0 +1,19 @@
+using GLib;
+
+callback int Maman.ActionCallback (int i);
+
+class Maman.Bar {
+       static int do_action (ActionCallback cb) {
+               return cb (1);
+       }
+
+       static int main (int argc, string[] argv) {
+               stdout.printf ("Lambda Test: 1");
+               
+               stdout.printf (" %d", do_action (i => i * 2));
+               
+               stdout.printf (" 3\n");
+               
+               return 0;
+       }
+}
index b6a9eb9..e5365bd 100644 (file)
@@ -863,8 +863,13 @@ lambda_expression
        : OPEN_PARENS opt_lambda_parameter_list CLOSE_PARENS LAMBDA expression
          {
                ValaSourceReference *src = src(@4);
-               $$ = VALA_EXPRESSION (vala_lambda_expression_new ($2, $5, src));
+               $$ = VALA_EXPRESSION (vala_lambda_expression_new ($5, src));
                if ($2 != NULL) {
+                       GList *l;
+                       for (l = $2; l != NULL; l = l->next) {
+                               vala_lambda_expression_add_parameter (VALA_LAMBDA_EXPRESSION ($$), l->data);
+                               g_free (l->data);
+                       }
                        g_list_free ($2);
                }
                g_object_unref ($5);
@@ -873,7 +878,7 @@ lambda_expression
        | IDENTIFIER LAMBDA expression
          {
                ValaSourceReference *src = src(@2);
-               $$ = VALA_EXPRESSION (vala_lambda_expression_new (NULL, $3, src));
+               $$ = VALA_EXPRESSION (vala_lambda_expression_new ($3, src));
                g_object_unref ($3);
                g_object_unref (src);
                vala_lambda_expression_add_parameter (VALA_LAMBDA_EXPRESSION ($$), $1);
index 263a67a..cd63fb5 100644 (file)
@@ -684,6 +684,10 @@ namespace Vala {
                                }
                        }
                }
+
+               public override void visit_begin_method (Method! m) {
+                       current_symbol = m.symbol;
+               }
                
                private ref CCodeStatement create_method_type_check_statement (Method! m, DataType! t, bool non_null, string! var_name) {
                        return create_type_check_statement (m, m.return_type.type, t, non_null, var_name);
@@ -733,6 +737,8 @@ namespace Vala {
                }
                
                public override void visit_end_method (Method! m) {
+                       current_symbol = current_symbol.parent_symbol;
+
                        if (m.name == "init") {
                                
                                return;
@@ -815,7 +821,7 @@ namespace Vala {
                                                }
                                        }
 
-                                       if (m.source_reference.comment != null) {
+                                       if (m.source_reference != null && m.source_reference.comment != null) {
                                                source_type_member_definition.append (new CCodeComment (text = m.source_reference.comment));
                                        }
                                        source_type_member_definition.append (function);
@@ -1573,6 +1579,10 @@ namespace Vala {
                                var decl = (VariableDeclarator) expr.call.symbol_reference.node;
                                var cb = (Callback) decl.type_reference.type;
                                params = cb.get_parameters ();
+                       } else if (expr.call.symbol_reference.node is FormalParameter) {
+                               var param = (FormalParameter) expr.call.symbol_reference.node;
+                               var cb = (Callback) param.type_reference.type;
+                               params = cb.get_parameters ();
                        } else {
                                m = (Method) expr.call.symbol_reference.node;
                                params = m.get_parameters ();
@@ -1807,6 +1817,10 @@ namespace Vala {
                        expr.ccodenode = ccheck;
                }
 
+               public override void visit_end_lambda_expression (LambdaExpression! l) {
+                       l.ccodenode = new CCodeIdentifier (name = l.method.get_cname ());
+               }
+
                public override void visit_assignment (Assignment! a) {
                        if (a.left.symbol_reference.node is Property) {
                                var prop = (Property) a.left.symbol_reference.node;
index a3df820..103c7c1 100644 (file)
 
 using GLib;
 
-namespace Vala {
-       public class FormalParameter : CodeNode {
-               public string name { get; construct; }
-               public TypeReference type_reference { get; construct; }
-               public bool ellipsis { get; construct; }
-               public Expression default_expression { get; set construct; }
-               
-               public static ref FormalParameter new (string name, TypeReference type, SourceReference source) {
-                       return (new FormalParameter (name = name, type_reference = type, source_reference = source));
-               }
-               
-               public static ref FormalParameter new_ellipsis (SourceReference source) {
-                       return (new FormalParameter (ellipsis = true, source_reference = source));
+/**
+ * Represents a formal parameter in method and callback signatures.
+ */
+public class Vala.FormalParameter : CodeNode {
+       /**
+        * The parameter name.
+        */
+       public string! name { get; set construct; }
+       
+       /**
+        * The parameter type.
+        */
+       public TypeReference type_reference { get; set; }
+       
+       /**
+        * Specifies whether the methods accepts an indefinite number of
+        * parameters.
+        */
+       public bool ellipsis { get; set; }
+       
+       /**
+        * Specifies the expression used when the caller doesn't supply an
+        * argument for this parameter.
+        */
+       public Expression default_expression { get; set; }
+       
+       /**
+        * Creates a new formal parameter.
+        *
+        * @param name   parameter name
+        * @param type   parameter type
+        * @param source reference to source code
+        * @return       newly created formal parameter
+        */
+       public static ref FormalParameter new (string! name, TypeReference type, SourceReference source) {
+               return (new FormalParameter (name = name, type_reference = type, source_reference = source));
+       }
+       
+       /**
+        * Creates a new ellipsis parameter representing an indefinite number of
+        * parameters.
+        */
+       public static ref FormalParameter new_ellipsis (SourceReference source) {
+               return (new FormalParameter (ellipsis = true, source_reference = source));
+       }
+       
+       public override void accept (CodeVisitor visitor) {
+               if (!ellipsis) {
+                       type_reference.accept (visitor);
                }
                
-               public override void accept (CodeVisitor visitor) {
-                       if (!ellipsis) {
-                               type_reference.accept (visitor);
-                       }
-                       
-                       visitor.visit_formal_parameter (this);
-               }
+               visitor.visit_formal_parameter (this);
        }
 }
index 4f1bb1c..fb9a836 100644 (file)
@@ -36,9 +36,10 @@ namespace Vala {
                }
                
                public override void accept (CodeVisitor visitor) {
+                       call.accept (visitor);
+
                        visitor.visit_begin_invocation_expression (this);
 
-                       call.accept (visitor);
                        foreach (Expression expr in argument_list) {
                                expr.accept (visitor);
                        }
index ee46855..5e34544 100644 (file)
 
 using GLib;
 
-namespace Vala {
-       public class LambdaExpression : Expression {
-               public List<string> parameters { get; set; }
-               public Expression inner { get; set; }
-               
-               /* generated anonymous method */
-               public Method method;
-               
-               public static ref LambdaExpression new (List<String> params, Expression! inner, SourceReference source) {
-                       return new LambdaExpression (parameters = params, inner = inner, source_reference = source);
-               }
-               
-               public void add_parameter (string! param) {
-                       _parameters.append (param);
-               }
-               
-               public override void accept (CodeVisitor! visitor) {
-                       visitor.visit_begin_lambda_expression (this);
+/**
+ * Represents a lambda expression in the source code. Lambda expressions are
+ * anonymous methods with implicitly typed parameters.
+ */
+public class Vala.LambdaExpression : Expression {
+       /**
+        * The body of this lambda expression.
+        */
+       public Expression! inner { get; set construct; }
+       
+       /**
+        * The generated method.
+        */
+       public Method method { get; set; }
 
+       private List<string> parameters;
+
+       /**
+        * Creates a new lambda expression.
+        *
+        * @param inner  expression body
+        * @param source reference to source code
+        * @return       newly created lambda expression
+        */
+       public static ref LambdaExpression new (Expression! inner, SourceReference source) {
+               return new LambdaExpression (inner = inner, source_reference = source);
+       }
+       
+       /**
+        * Appends implicitly typed parameter.
+        *
+        * @param param parameter name
+        */
+       public void add_parameter (string! param) {
+               parameters.append (param);
+       }
+       
+       /**
+        * Returns copy of parameter list.
+        *
+        * @return parameter list
+        */
+       public ref List<string> get_parameters () {
+               return parameters.copy ();
+       }
+       
+       public override void accept (CodeVisitor! visitor) {
+               visitor.visit_begin_lambda_expression (this);
+
+               if (method == null) {
                        inner.accept (visitor);
                        visitor.visit_end_full_expression (inner);
+               }
 
-                       visitor.visit_end_lambda_expression (this);
-                       
-                       if (method != null) {
-                               method.accept (visitor);
-                       }
+               visitor.visit_end_lambda_expression (this);
+               
+               if (method != null) {
+                       method.accept (visitor);
                }
        }
 }
index 39eca3f..63fa2a4 100644 (file)
@@ -103,6 +103,10 @@ namespace Vala {
                                var decl = (VariableDeclarator) msym.node;
                                var cb = (Callback) decl.type_reference.type;
                                params = cb.get_parameters ();
+                       } else if (msym.node is FormalParameter) {
+                               var param = (FormalParameter) msym.node;
+                               var cb = (Callback) param.type_reference.type;
+                               params = cb.get_parameters ();
                        } else {
                                var m = (Method) msym.node;
                                params = m.get_parameters ();
index 1793282..d318cab 100644 (file)
@@ -86,7 +86,7 @@ namespace Vala {
                                } else if (parent is Namespace) {
                                        cname = "%s%s".printf (((Namespace) parent).get_lower_case_cprefix (), name);
                                } else {
-                                       Report.error (source_reference, "method is neither in struct nor in namespace\n");
+                                       cname = name;
                                }
                        }
                        return cname;
index 5a364ab..58b162f 100644 (file)
@@ -36,6 +36,8 @@ namespace Vala {
                TypeReference bool_type;
                TypeReference string_type;
                DataType initially_unowned_type;
+
+               private int next_lambda_id = 0;
                
                public void analyze (CodeContext context) {
                        root_symbol = context.root;
@@ -57,6 +59,8 @@ namespace Vala {
                public override void visit_begin_source_file (SourceFile! file) {
                        current_source_file = file;
                        current_using_directives = file.get_using_directives ();
+                       
+                       next_lambda_id = 0;
                }
 
                public override void visit_end_source_file (SourceFile! file) {
@@ -118,6 +122,12 @@ namespace Vala {
                public override void visit_end_method (Method! m) {
                        current_symbol = current_symbol.parent_symbol;
                        current_return_type = null;
+
+                       if (current_symbol.parent_symbol.node is Method) {
+                               /* lambda expressions produce nested methods */
+                               var up_method = (Method) current_symbol.parent_symbol.node;
+                               current_return_type = up_method.return_type;
+                       }
                        
                        if (m.is_virtual || m.is_override) {
                                if (current_symbol.node is Class) {
@@ -537,24 +547,30 @@ namespace Vala {
                }
 
                public override void visit_begin_invocation_expression (InvocationExpression! expr) {
-               }
-
-               public override void visit_end_invocation_expression (InvocationExpression! expr) {
                        if (expr.call.symbol_reference == null) {
                                /* if method resolving didn't succeed, skip this check */
+                               expr.error = true;
                                return;
                        }
                        
                        var msym = expr.call.symbol_reference;
                        
-                       TypeReference ret_type;
                        List<FormalParameter> params;
                        
                        if (msym.node is VariableDeclarator) {
                                var decl = (VariableDeclarator) msym.node;
                                if (decl.type_reference.type is Callback) {
                                        var cb = (Callback) decl.type_reference.type;
-                                       ret_type = cb.return_type;
+                                       params = cb.get_parameters ();
+                               } else {
+                                       expr.error = true;
+                                       Report.error (expr.source_reference, "invocation not supported in this context");
+                                       return;
+                               }
+                       } else if (msym.node is FormalParameter) {
+                               var param = (FormalParameter) msym.node;
+                               if (param.type_reference.type is Callback) {
+                                       var cb = (Callback) param.type_reference.type;
                                        params = cb.get_parameters ();
                                } else {
                                        expr.error = true;
@@ -563,7 +579,6 @@ namespace Vala {
                                }
                        } else if (msym.node is Method) {
                                var m = (Method) msym.node;
-                               ret_type = m.return_type;
                                params = m.parameters;
                        } else {
                                expr.error = true;
@@ -571,6 +586,49 @@ namespace Vala {
                                return;
                        }
                
+                       List arg_it = expr.argument_list;
+                       foreach (FormalParameter param in params) {
+                               if (param.ellipsis) {
+                                       break;
+                               }
+                               
+                               if (arg_it != null) {
+                                       var arg = (Expression) arg_it.data;
+
+                                       /* store expected type for callback parameters */
+                                       arg.expected_type = param.type_reference;
+                                       
+                                       arg_it = arg_it.next;
+                               }
+                       }
+               }
+
+               public override void visit_end_invocation_expression (InvocationExpression! expr) {
+                       if (expr.error) {
+                               return;
+                       }
+                       
+                       var msym = expr.call.symbol_reference;
+                       
+                       TypeReference ret_type;
+                       List<FormalParameter> params;
+                       
+                       if (msym.node is VariableDeclarator) {
+                               var decl = (VariableDeclarator) msym.node;
+                               var cb = (Callback) decl.type_reference.type;
+                               ret_type = cb.return_type;
+                               params = cb.get_parameters ();
+                       } else if (msym.node is FormalParameter) {
+                               var param = (FormalParameter) msym.node;
+                               var cb = (Callback) param.type_reference.type;
+                               ret_type = cb.return_type;
+                               params = cb.get_parameters ();
+                       } else if (msym.node is Method) {
+                               var m = (Method) msym.node;
+                               ret_type = m.return_type;
+                               params = m.parameters;
+                       }
+               
                        expr.static_type = ret_type;
                        
                        List arg_it = expr.argument_list;
@@ -798,6 +856,62 @@ namespace Vala {
 
                        expr.static_type = bool_type;
                }
+               
+               private ref string get_lambda_name () {
+                       var result = "__lambda%d".printf (next_lambda_id);
+
+                       next_lambda_id++;
+                       
+                       return result;
+               }
+
+               public override void visit_begin_lambda_expression (LambdaExpression! l) {
+                       if (l.expected_type == null || !(l.expected_type.type is Callback)) {
+                               l.error = true;
+                               Report.error (l.source_reference, "lambda expression not allowed in this context");
+                               return;
+                       }
+                       
+                       var cb = (Callback) l.expected_type.type;
+                       l.method = new Method (name = get_lambda_name (), return_type = cb.return_type);
+                       l.method.instance = false;
+                       l.method.symbol = new Symbol (node = l.method);
+                       l.method.symbol.parent_symbol = current_symbol;
+                       
+                       var lambda_params = l.get_parameters ();
+                       var lambda_param_it = lambda_params;
+                       foreach (FormalParameter cb_param in cb.get_parameters ()) {
+                               if (lambda_param_it == null) {
+                                       /* lambda expressions are allowed to have less parameters */
+                                       break;
+                               }
+                               
+                               var lambda_param = (string) lambda_param_it.data;
+                               
+                               var param = new FormalParameter (name = lambda_param);
+                               param.type_reference = cb_param.type_reference;
+                               param.symbol = new Symbol (node = param);
+                               l.method.symbol.add (param.name, param.symbol);
+                               
+                               l.method.add_parameter (param);
+                               
+                               lambda_param_it = lambda_param_it.next;
+                       }
+                       
+                       if (lambda_param_it != null) {
+                               /* lambda expressions may not expect more parameters */
+                               l.error = true;
+                               Report.error (l.source_reference, "lambda expression: too many parameters");
+                               return;
+                       }
+                       
+                       var block = new Block ();
+                       block.symbol = new Symbol (node = block);
+                       block.symbol.parent_symbol = l.method.symbol;
+                       block.add_statement (new ReturnStatement (return_expression = l.inner));
+                       
+                       l.method.body = block;
+               }
 
                public override void visit_assignment (Assignment! a) {
                        if (a.left.symbol_reference.node is Signal) {