rocks/mkeithers

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&lt;double, string&gt;.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);
}