зеркало из https://github.com/mono/rocks.git
360 строки
12 KiB
Perl
Executable File
360 строки
12 KiB
Perl
Executable File
#!/usr/bin/perl
|
|
use strict;
|
|
use Generator;
|
|
use Getopt::Long;
|
|
|
|
my $n = 2;
|
|
my $namespace = "Mono.Rocks";
|
|
my $output = "Eithers.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 N Either types.
|
|
|
|
Options:
|
|
-n, --count=N The number of types to generate. Defaults 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 < 2) {
|
|
print STDERR "error: n must be > 1, was $n\n";
|
|
exit 1;
|
|
}
|
|
|
|
my $handle = *STDOUT;
|
|
|
|
my $g = Generator->new ($handle);
|
|
|
|
my $header = <<EOF;
|
|
//
|
|
// $output: Either types.
|
|
//
|
|
// 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.Collections.Generic;\n\n");
|
|
|
|
$g->Namespace ($namespace, sub {
|
|
|
|
for (my $i = 2; $i <= $n; ++$i) {
|
|
create_either ($g, $i);
|
|
}
|
|
|
|
});
|
|
|
|
sub create_either {
|
|
my ($g, $n) = @_;
|
|
|
|
my $type = "Mono.Rocks.Either{" . Generator::GetTypeParameterList ($n) . "}";
|
|
my $typeN = "Mono.Rocks.Either`$n";
|
|
my $fold = "$type.Fold``1(" . create_xmldoc_fold_args ($n) . ")";
|
|
|
|
$g->Write ("\n");
|
|
for (my $i = 1; $i <= $n; ++$i) {
|
|
$g->XmlTypeparam (Generator::GetTypeParameter ($n, $i),
|
|
"The " . Generator::Nth ($i) . " value type.");
|
|
}
|
|
$g->XmlSummary ("A union of $n values.");
|
|
$g->XmlRemarks (
|
|
qq(<para>
|
|
An <c>Either</c> is an immutable, strongly typed union of variously
|
|
typed values with each value lacking an otherwise meaningful name aside
|
|
from its position, which is not exposed. It stores only one (non-null)
|
|
value from a set of types (as determined by the type parameter list).
|
|
</para>
|
|
<para>
|
|
The value held by a <see cref="T:$type" /> instance
|
|
can be converted into a value by using the
|
|
<see cref="M:$fold" /> method.
|
|
<c>Fold</c> takes a list of delegates to perform the conversion; the
|
|
delegate used to perform the conversion is based upon the internal
|
|
position of the value stored.
|
|
</para>
|
|
<para>
|
|
<c>Either</c> instances are created through one of the following
|
|
creation methods:
|
|
</para>
|
|
<list type="bullet">
|
|
@{[ create_xmldoc_creators ($n, $type) ]}
|
|
</list>
|
|
<code lang="C#">
|
|
var a = Either<double, string>.A (Math.PI); // value stored in 1st position
|
|
|
|
int r = a.Fold (
|
|
v => (int) v, // 1st position converter
|
|
v => v.Length); // 2nd position converter
|
|
|
|
Console.WriteLine (r); // prints 3</code>));
|
|
$g->Type (
|
|
sub {
|
|
$g->Write ("public abstract class Either<")->TypeParameterList ($n)->Write (">\n")
|
|
->Write ("\t: IEquatable<Either<")->TypeParameterList ($n)->Write (">>\n");
|
|
},
|
|
sub {
|
|
$g->Method ("private ", "Either", sub {}, sub {});
|
|
|
|
for (my $i = 0; $i < $n; ++$i) {
|
|
$g->Write ("\n");
|
|
|
|
$g->XmlParam ("value",
|
|
qq(A <typeparamref name="T@{[ $i+1 ]}" /> containing the value
|
|
to provide to the @{[ Generator::Nth ($i+1) ]}
|
|
<see cref="M:$fold" />
|
|
delegate.));
|
|
$g->XmlSummary (
|
|
qq(Creates a <see cref="T:$type" /> instance which
|
|
holds a <typeparamref name="@{[ Generator::GetTypeParameter ($n, $i+1) ]}" /> value.));
|
|
$g->XmlReturns (
|
|
qq(A <see cref="T:$type" /> instance which holds a
|
|
holds a <typeparamref name="@{[ Generator::GetTypeParameter ($n, $i+1) ]}" /> value.));
|
|
$g->XmlRemarks (qq(<para>
|
|
When
|
|
<see cref="M:$fold" />
|
|
is invoked,
|
|
the returned <see cref="T:$type" /> instance
|
|
will invoke the @{[ Generator::Nth ($i+1) ]} delegate
|
|
for conversions.
|
|
</para>));
|
|
$g->XmlException("System.ArgumentNullException",
|
|
"<paramref name=\"value\" /> is <see langword=\"null\" />.");
|
|
$g->Method (
|
|
sub {
|
|
$g->Write ("public static Either<")
|
|
->TypeParameterList ($n)->Write ("> ");
|
|
},
|
|
sub {
|
|
$g->Write (get_creator ($i));
|
|
},
|
|
sub { $g->TypeParameter ($n, $i+1)->Write (" value"); },
|
|
sub {
|
|
$g->Write ("if (value == null)\n")
|
|
->Write ("\tthrow new ArgumentNullException (\"value\");\n");
|
|
$g->Write ("return new ")
|
|
->Write (get_creator ($i))->Write ("Handler (value);\n")
|
|
}
|
|
);
|
|
}
|
|
|
|
$g->Write ("\n");
|
|
$g->XmlTypeparam ("TResult", qq(The type to convert the <see cref="T:$type" /> to.));
|
|
for (my $i = 0; $i < $n; ++$i) {
|
|
my $t = Generator::GetTypeParameter ($n, $i+1);
|
|
$g->XmlParam (get_creator ($i, 'a'),
|
|
qq(A <see cref="T:System.Func{$t,TResult}" />
|
|
used if the <see cref="T:$type" /> stores a
|
|
<typeparamref name="$t" /> value into a
|
|
<typeparamref name="TResult" /> value.));
|
|
}
|
|
$g->XmlSummary (qq(Converts a <see cref="T:$type" /> into a <typeparamref name="TResult" /> value.));
|
|
$g->XmlReturns (qq(A <typeparamref name="TResult" /> as generated by one
|
|
of the conversion delegate parameters.));
|
|
$g->XmlRemarks (qq(<para>
|
|
Converts a <see cref="T:$type" /> into a <typeparamref name="TResult" />
|
|
by invoking one of the provided delegate parameters.
|
|
</para>
|
|
<para>
|
|
The parameter which is invoked is predicated upon the internal position of
|
|
the value held. For example, if the internal value is in the first position
|
|
(i.e. <see cref="M:$type.A(`0)" />
|
|
was used to create the <see cref="T:$type" /> instance), then
|
|
<paramref name="a" /> (the first delegate parameter) will be invoked to
|
|
convert the <typeparamref name="T1" /> into a
|
|
<typeparamref name="TResult" />.
|
|
</para>));
|
|
my $ane_docs = "<para>\n" .
|
|
" <paramref name=\"a\" /> is <see langword=\"null\" />.\n" .
|
|
"</para>\n";
|
|
for (my $i = 1; $i < $n; ++$i) {
|
|
$ane_docs .= "<para>\n -or-\n</para>\n<para>\n " .
|
|
"<paramref name=\"" . get_creator ($i, 'a') .
|
|
"\" /> is <see langword=\"null\" />.\n" .
|
|
"</para>";
|
|
}
|
|
$g->XmlException ("System.ArgumentNullException", $ane_docs);
|
|
$g->Write ("public abstract TResult Fold<TResult> (");
|
|
$g->Write ("Func<")->TypeParameter ($n, 1)->Write (", TResult> ")
|
|
->Write (get_creator (0, 'a'));
|
|
for (my $i = 1; $i < $n; ++$i) {
|
|
$g->Write (", Func<")->TypeParameter ($n, $i+1)->Write (", TResult> ")
|
|
->Write (get_creator ($i, 'a'));
|
|
}
|
|
$g->Write (");\n");
|
|
|
|
$g->Write ("\n");
|
|
$g->Method ("private static void ", "CheckFolders<TResult>",
|
|
sub {
|
|
$g->Write ("Func<")->TypeParameter ($n, 1)->Write (", TResult> ")
|
|
->Write (get_creator (0, 'a'));
|
|
for (my $i = 1; $i < $n; ++$i) {
|
|
$g->Write (", Func<")->TypeParameter ($n, $i+1)->Write (", TResult> ")
|
|
->Write (get_creator ($i, 'a'));
|
|
}
|
|
},
|
|
sub {
|
|
for (my $i = 0; $i < $n; ++$i) {
|
|
$g->Write ("if (")->Write (get_creator ($i, 'a'))
|
|
->Write (" == null)\n\tthrow new ArgumentNullException (\"")
|
|
->Write (get_creator ($i, 'a'))->Write ("\");\n");
|
|
}
|
|
});
|
|
|
|
$g->Write ("\n");
|
|
$g->XmlParam ("obj", "A <see cref=\"T:System.Object\"/> to compare this instance against.");
|
|
$g->XmlSummary ("Determines whether the current instance and the specified object have the same value.");
|
|
$g->XmlReturns (qq(<para>
|
|
<see langword="true"/> if <paramref name="obj"/> is a
|
|
<see cref="T:$type"/> and each member of <paramref name="obj"/>
|
|
and the current instance have the same value (according to
|
|
<see cref="M:System.Object.Equals(System.Object)"/>); otherwise
|
|
<see langword="false"/> is returned.
|
|
</para>));
|
|
$g->XmlRemarks (qq(<para>
|
|
This method checks for value equality
|
|
(<see cref="M:System.Object.Equals(System.Object)"/>), as defined by each
|
|
value type.
|
|
</para>
|
|
<para>
|
|
<block subset="none" type="note">
|
|
This method overrides <see cref="M:System.Object.Equals(System.Object)"/>.
|
|
</block>
|
|
</para>));
|
|
$g->Write ("public override abstract bool Equals (object obj);\n");
|
|
|
|
$g->Write ("\n");
|
|
$g->XmlParam ("obj", "A <see cref=\"T:$type\"/> to compare this instance against.");
|
|
$g->XmlSummary ("Determines whether the current instance and the specified <see cref=\"T:$type\"/> have the same value.");
|
|
$g->XmlReturns (qq(<para>
|
|
<see langword="true"/> if each member of <paramref name="obj"/>
|
|
and the current instance have the same value (according to
|
|
<see cref="M:System.Object.Equals(System.Object)"/>); otherwise
|
|
<see langword="false"/> is returned.
|
|
</para>));
|
|
$g->XmlRemarks (qq(<para>
|
|
This method checks for value equality
|
|
(<see cref="M:System.Object.Equals(System.Object)"/>), as defined by each
|
|
value type.
|
|
</para>));
|
|
$g->Write ("public abstract bool Equals (Either<")
|
|
->TypeParameterList ($n)->Write ("> obj);\n");
|
|
|
|
$g->Write ("\n");
|
|
$g->XmlSummary ("Generates a hash code for the current instance.");
|
|
$g->XmlReturns ("A <see cref=\"T:System.Int32\"/> containing the hash code for this instance.");
|
|
$g->XmlRemarks (
|
|
qq(<para>
|
|
<block subset="none" type="note">
|
|
This method overrides <see cref="M:System.Object.GetHashCode"/>.
|
|
</block>
|
|
</para>));
|
|
$g->Write ("public override abstract int GetHashCode ();\n");
|
|
|
|
for (my $i = 0; $i < $n; ++$i) {
|
|
$g->Write ("\n");
|
|
my $typeHandler = get_creator ($i) . "Handler";
|
|
$g->Type (
|
|
sub {
|
|
$g->Write ("private class ")->Write ($typeHandler)
|
|
->Write (" : Either<")->TypeParameterList ($n)->Write (">\n");
|
|
},
|
|
sub {
|
|
$g->Write ("private readonly ")->TypeParameter ($n, $i+1)->Write (" value;\n\n");
|
|
$g->Method ("public ", $typeHandler,
|
|
sub {
|
|
$g->TypeParameter ($n, $i+1)->Write (" value");
|
|
},
|
|
sub {
|
|
$g->Write ("this.value = value;\n");
|
|
});
|
|
|
|
$g->Write ("\n");
|
|
$g->Method ("public override int ", "GetHashCode", "", "return value.GetHashCode();\n");
|
|
$g->Write ("\n");
|
|
$g->Method ("public override bool ", "Equals", "object obj", sub {
|
|
$g->Write ("$typeHandler o = obj as $typeHandler;\n");
|
|
$g->Write ("if (o == null)\n")
|
|
->Write ("\treturn false;\n")
|
|
->Write ("return Equals (o);\n");
|
|
});
|
|
|
|
$g->Write ("\n");
|
|
$g->Method ("public override bool ", "Equals",
|
|
sub { $g->Write ("Either<")->TypeParameterList ($n)->Write ("> obj"); },
|
|
sub {
|
|
$g->Write ("$typeHandler o = obj as $typeHandler;\n");
|
|
$g->Write ("if (o == null)\n")
|
|
->Write ("\treturn false;\n");
|
|
$g->Write ("return EqualityComparer<")->TypeParameter ($n, $i+1)
|
|
->Write (">.Default.Equals (this.value, o.value);\n");
|
|
});
|
|
|
|
$g->Write ("\n");
|
|
$g->Method ("public override TResult ", "Fold<TResult>",
|
|
sub {
|
|
$g->Write ("Func<")->TypeParameter ($n, 1)->Write (", TResult> ")
|
|
->Write (get_creator (0, 'a'));
|
|
for (my $j = 1; $j < $n; ++$j) {
|
|
$g->Write (", Func<")->TypeParameter ($n, $j+1)->Write (", TResult> ")
|
|
->Write (get_creator ($j, 'a'));
|
|
}
|
|
},
|
|
sub {
|
|
$g->Write ("CheckFolders (a");
|
|
for (my $j = 1; $j < $n; ++$j) {
|
|
$g->Write (", ")->Write (get_creator ($j, 'a'));
|
|
}
|
|
$g->Write (");\n");
|
|
$g->Write ("return ")->Write (get_creator ($i, 'a'))
|
|
->Write (" (value);\n");
|
|
});
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
sub create_xmldoc_fold_args {
|
|
my ($n) = @_;
|
|
|
|
my $a = "System.Func{`0,``0}";
|
|
for (my $i = 1; $i < $n; ++$i) {
|
|
$a .= ",System.Func{`$i,``0}";
|
|
}
|
|
return $a;
|
|
}
|
|
|
|
sub create_xmldoc_creators {
|
|
my ($n, $type) = @_;
|
|
my $d = "";
|
|
for (my $i = 0; $i < $n; ++$i) {
|
|
$d .= qq( <item><term><see cref="M:$type.@{[ get_creator ($i) ]}(`$i)" /></term></item>\n);
|
|
}
|
|
return $d;
|
|
}
|
|
|
|
sub get_creator {
|
|
my ($n, $start) = @_;
|
|
|
|
$start ||= 'A';
|
|
|
|
return chr (ord ($start)+$n);
|
|
}
|