rocks/mklambda

320 строки
11 KiB
Perl
Executable File

#!/usr/bin/perl
use strict;
use Generator;
use Getopt::Long;
my $n = 1;
my $namespace = "Mono.Rocks";
my $output = "Lambdas.cs";
my $help = undef;
GetOptions (
"n|count=i", \$n,
"o|output=s", \$output,
"namespace=s", \$namespace,
"h|?|help", \$help
);
if ($help) {
print <<EOF;
$0 [-n N] [-o FILE] [-h]
Create a C# file containing C# lambda helper methods.
Options:
-n, --count=N The number of types to generate. Default is 1.
-o, --output=FILE The file to create. Default is `Tuples.cs'.
--namespace=NS Place the types within the namespace NS.
Default is `Mono.Rocks'.
-h, -?, --help Show this message and exit.
EOF
exit 0;
}
if ($n < 1) {
print STDERR "error: n must be >= 1, was $n\n";
exit 1;
}
my $handle = *STDOUT;
my $g = Generator->new ($handle);
my $header = <<EOF;
//
// $output: C# Lambda Expression Helpers.
//
// GENERATED CODE: DO NOT EDIT.
//
// To regenerate this code, execute: $0 -n $n -o $output
//
// Copyright (c) 2008 Novell, Inc. (http://www.novell.com)
EOF
$g->Write ($header);
$g->WriteLicense ();
$g->Write ("using System;\n");
$g->Write ("using System.Linq.Expressions;\n\n");
$g->Namespace ($namespace, sub {
create_actions ($g, $n);
$g->Write ("\n");
$g->XmlSummary (
qq(Provides static utility methods to generate anonymous delegates
or expression trees of pre-determined types.));
$g->XmlRemarks (
qq(<para>
C# lambda methods and anonymous delegates are a curious
1.5-class citizen: They are implicitly convertable to any
delegate type, but have no type by themselves. Thus,
the following code fails to compile:
</para>
<code lang="C#">
((int x) => Console.WriteLine (x))(5);</code>
<para>It would instead need:</para>
<code lang="C#">
// either:
Action&lt;int&gt; a = x => Console.WriteLine (x);
a (5);
//
// or
//
((Action&lt;int&gt;) (x => Console.WriteLine (x)))(5);</code>
<para>
So you'd either need to assign the lambda to an actual
delegate type, or insert a cast.
</para>
<para>
<see cref="M:Mono.Rocks.Lambda.A" /> allows you to
provide a lambda body for the <see cref="T:System.Action"/>
builtin delegate type, and <see cref="M:Mono.Rocks.Lambda.F" />
allows you to provide a lambda body for the
<see cref="T:System.Func{TResult}"/> delegate type,
thus removing the need for a cast or an extra variable:
</para>
<code lang="C#">
Lambda.F ((int x) => Console.WriteLine (x)) (5);</code>
<para>
<see cref="T:Mono.Rocks.Lambda"/> provides the following sets of
functionality:
</para>
<list type="bullet">
<item><term>Delegate creation methods, which return
<see cref="T:System.Action"/>-like delegates:
<see cref="M:Mono.Rocks.Lambda.A(System.Action)"/>,
<see cref="M:Mono.Rocks.Lambda.A``1(System.Action{``0})"/>,
<see cref="M:Mono.Rocks.Lambda.A``2(System.Action{``0,``1})"/>,
<see cref="M:Mono.Rocks.Lambda.A``3(System.Action{``0,``1,``2})"/>, and
<see cref="M:Mono.Rocks.Lambda.A``4(System.Action{``0,``1,``2,``3})"/>.
</term></item>
<item><term>Delegate creation methods which return
return <see cref="T:System.Func{TResult}"/>-like delegates
<see cref="M:Mono.Rocks.Lambda.F``1(System.Func{``0})"/>,
<see cref="M:Mono.Rocks.Lambda.F``2(System.Func{``0,``1})"/>,
<see cref="M:Mono.Rocks.Lambda.F``3(System.Func{``0,``1,``2})"/>,
<see cref="M:Mono.Rocks.Lambda.F``4(System.Func{``0,``1,``2,``3})"/>, and
<see cref="M:Mono.Rocks.Lambda.F``5(System.Func{``0,``1,``2,``3,``4})"/>.
</term></item>
<item><term><see cref="T:System.Linq.Expressions.Expression"/>-creating methods:
<see cref="M:Mono.Rocks.Lambda.XA(System.Linq.Expressions.Expression{System.Action})"/>,
<see cref="M:Mono.Rocks.Lambda.XA``1(System.Linq.Expressions.Expression{System.Action{``0}})"/>,
<see cref="M:Mono.Rocks.Lambda.XA``2(System.Linq.Expressions.Expression{System.Action{``0,``1}})"/>,
<see cref="M:Mono.Rocks.Lambda.XA``3(System.Linq.Expressions.Expression{System.Action{``0,``1,``2}})"/>,
<see cref="M:Mono.Rocks.Lambda.XA``4(System.Linq.Expressions.Expression{System.Action{``0,``1,``2,``3}})"/>, and
<see cref="M:Mono.Rocks.Lambda.XF``1(System.Linq.Expressions.Expression{System.Func{``0}})"/>,
<see cref="M:Mono.Rocks.Lambda.XF``2(System.Linq.Expressions.Expression{System.Func{``0,``1}})"/>,
<see cref="M:Mono.Rocks.Lambda.XF``3(System.Linq.Expressions.Expression{System.Func{``0,``1,``2}})"/>,
<see cref="M:Mono.Rocks.Lambda.XF``4(System.Linq.Expressions.Expression{System.Func{``0,``1,``2,``3}})"/>,
<see cref="M:Mono.Rocks.Lambda.XF``5(System.Linq.Expressions.Expression{System.Func{``0,``1,``2,``3,``4}})"/>.
</term></item>
<item><term>Y-Combinators, which permit writing recursive lambdas:
<see cref="M:Mono.Rocks.Lambda.RecFunc``2(System.Func{System.Func{``0,``1},System.Func{``0,``1}})"/>,
<see cref="M:Mono.Rocks.Lambda.RecFunc``3(System.Func{System.Func{``0,``1,``2},System.Func{``0,``1,``2}})"/>,
<see cref="M:Mono.Rocks.Lambda.RecFunc``4(System.Func{System.Func{``0,``1,``2,``3},System.Func{``0,``1,``2,``3}})"/>, and
<see cref="M:Mono.Rocks.Lambda.RecFunc``5(System.Func{System.Func{``0,``1,``2,``3,``4},System.Func{``0,``1,``2,``3,``4}})"/>.
</term></item>
</list>
));
$g->Type ("public static partial class Lambda", sub {
$g->Write ("\n");
create_func_doc ($g, 0, Generator::GetDocAction (0));
$g->Method (
"public static Action ", "A", "Action lambda", "return lambda;\n"
);
$g->Write ("\n");
create_func_doc ($g, 0, Generator::GetDocFunc (0));
$g->Method (
"public static Func<TResult> ", "F<TResult>", "Func<TResult> lambda", "return lambda;\n"
);
$g->Write ("\n");
create_expr_doc ($g, 0, Generator::GetDocAction (0));
$g->Method (
"public static Expression<Action> ", "XA", "Expression<Action> expr", "return expr;\n"
);
$g->Write ("\n");
create_expr_doc ($g, 0, Generator::GetDocFunc (0));
$g->Method (
"public static Expression<Func<TResult>> ", "XF<TResult>", "Expression<Func<TResult>> expr", "return expr;\n"
);
for (my $i = 1; $i <= $n; ++$i) {
$g->Write ("\n");
create_func_doc ($g, $i, Generator::GetDocAction ($i));
$g->Method (
sub {
$g->Write ("public static ")->Action ($i)
->Write ("<")->TypeParameterList ($i)->Write (">");
},
sub { $g->Write ("\n\tA<")->TypeParameterList ($i)->Write (">"); },
sub { $g->Action ($i)->Write ("<")->TypeParameterList ($i)->Write ("> lambda"); },
"return lambda;\n"
);
$g->Write ("\n");
create_func_doc ($g, $i, Generator::GetDocFunc ($i));
$g->Method (
sub {
$g->Write ("public static ")->Func ($i)
->Write ("<")->TypeParameterList ($i)->Write (", TResult>");
},
sub { $g->Write ("\n\tF<")->TypeParameterList ($i)->Write (", TResult>"); },
sub { $g->Func ($i)->Write ("<")->TypeParameterList ($i)->Write (", TResult> lambda"); },
"return lambda;\n"
);
$g->Write ("\n");
create_expr_doc ($g, $i, Generator::GetDocAction ($i));
$g->Method (
sub {
$g->Write ("public static Expression<")->Action ($i)
->Write ("<")->TypeParameterList ($i)->Write (">>");
},
sub { $g->Write ("\n\tXA<")->TypeParameterList ($i)->Write (">"); },
sub { $g->Write ("Expression<")->Action ($i)->Write ("<")->TypeParameterList ($i)->Write (">> expr"); },
"return expr;\n"
);
$g->Write ("\n");
create_expr_doc ($g, $i, Generator::GetDocFunc ($i));
$g->Method (
sub {
$g->Write ("public static Expression<")->Func ($i)
->Write ("<")->TypeParameterList ($i)->Write (", TResult>>");
},
sub { $g->Write ("\n\tXF<")->TypeParameterList ($i)->Write (", TResult>"); },
sub { $g->Write ("Expression<")->Func ($i)->Write ("<")->TypeParameterList ($i)->Write (", TResult>> expr"); },
"return expr;\n"
);
}
$g->Write ("\n//\n");
$g->Write ("// Y-Combinators\n");
$g->Write ("// http://blogs.msdn.com/madst/archive/2007/05/11/recursive-lambda-expressions.aspx\n");
$g->Write ("//\n");
for (my $i = 1; $i <= $n; ++$i) {
$g->Write ("\n");
create_recfunc_doc ($g, $i, Generator::GetDocFunc ($i));
$g->Method (
sub {
$g->Write ("public static ")->Func ($i)
->Write ("<")->TypeParameterList ($i)->Write (", TResult>");
},
sub { $g->Write ("\n\tRecFunc<")->TypeParameterList ($i)->Write (", TResult>"); },
sub { $g->Func ($i)->Write ("<")
->Func ($i)->Write ("<")->TypeParameterList ($i)->Write (", TResult>, ")
->Func ($i)->Write ("<")->TypeParameterList ($i)->Write (", TResult>> lambda");
},
sub {
$g->Write ("if (lambda == null)\n")
->Write ("\tthrow new ArgumentNullException (\"lambda\");\n\n");
$g->Write ("return (")->ValueList ($i)->Write (") => ")
->Write ("lambda (RecFunc (lambda))(")->ValueList ($i)->Write (");\n");
}
);
}
});
});
sub create_actions {
my ($g, $n) = @_;
if ($n > $g->MaxSystemFuncs) {
for (my $i = $g->MaxSystemFuncs+1; $i <= $n; ++$i) {
$g->Write ("\npublic delegate void RocksAction<");
$g->TypeParameterList ($i);
$g->Write ("> (");
$g->MethodParameterList ($i);
$g->Write (");");
}
$g->Write ("\n");
}
}
sub create_func_doc {
my ($g, $n, $t) = @_;
for (my $i = 1; $i <= $n; ++$i) {
$g->XmlTypeparam (Generator::GetTypeParameter ($n, $i),
"A <see cref=\"T:$t\"/> parameter type.");
}
if ($t =~ m/Func/) {
$g->XmlTypeparam ("TResult", "The <see cref=\"T:$t\"/> return type.");
}
$g->XmlParam ("lambda",
"The <see cref=\"T:$t\"/> to return.");
$g->XmlSummary ("Creates a <see cref=\"T:$t\"/> delegate.");
$g->XmlReturns ("Returns <paramref name=\"lambda\"/>.");
# $g->XmlRemarks ("remarks", "");
}
sub create_expr_doc {
my ($g, $n, $t) = @_;
my $e = "System.Linq.Expressions.Expression";
$t = "$e\{$t\}";
for (my $i = 1; $i <= $n; ++$i) {
$g->XmlTypeparam (Generator::GetTypeParameter ($n, $i),
"A <see cref=\"T:$t\"/> parameter type.");
}
if ($t =~ m/Func/) {
$g->XmlTypeparam ("TResult", "The <see cref=\"T:$t\"/> return type.");
}
$g->XmlParam ("expr",
"The <see cref=\"T:$t\"/> to return.");
$g->XmlSummary ("Creates a <see cref=\"T:$t\"/> expression tree.");
$g->XmlReturns ("Returns <paramref name=\"expr\"/>.");
# $g->XmlRemarks ("remarks", "");
}
sub create_recfunc_doc {
my ($g, $n, $t) = @_;
for (my $i = 1; $i <= $n; ++$i) {
$g->XmlTypeparam (Generator::GetTypeParameter ($n, $i),
"A <see cref=\"T:$t\"/> parameter type.");
}
if ($t =~ m/Func/) {
$g->XmlTypeparam ("TResult", "The <see cref=\"T:$t\"/> return type.");
}
$g->XmlParam ("lambda",
"The <see cref=\"T:System.Func{$t,$t}\"/> to use.");
$g->XmlSummary ("Creates a <see cref=\"T:$t\"/> delegate, which may be recursive.");
$g->XmlReturns (
qq(Returns a <see cref=\"T:$t\"/> which (eventually) invokes
<paramref name="lambda"/>.));
$g->XmlException ("System.ArgumentNullException",
"if <paramref name=\"lambda\"/> is <see langword=\"null\"/>.");
$g->XmlRemarks(
qq(<para>
The following example makes use of a recursive lambda:
</para>
<code lang="C#">
Func&lt;int, int&gt; factorial = Lambda.RecFunc&lt;int, int&gt; (
fac => x => x == 0 ? 1 : x * fac (x-1));
Console.WriteLine (factorial (5)); // prints "120"</code>
));
# $g->XmlRemarks ("remarks", "");
}