+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
test-012.vala \
test-013.vala \
test-014.vala \
+ test-015.vala \
$(NULL)
--- /dev/null
+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;
+ }
+}
: 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);
| 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);
}
}
}
+
+ 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);
}
public override void visit_end_method (Method! m) {
+ current_symbol = current_symbol.parent_symbol;
+
if (m.name == "init") {
return;
}
}
- 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);
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 ();
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;
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);
}
}
}
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);
}
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);
}
}
}
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 ();
} 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;
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;
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) {
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) {
}
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;
}
} else if (msym.node is Method) {
var m = (Method) msym.node;
- ret_type = m.return_type;
params = m.parameters;
} else {
expr.error = true;
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;
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) {