Skip to content

Instantly share code, notes, and snippets.

@mrange
Last active May 24, 2016 21:16
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mrange/34341f15612e0799a67d50b7350f478d to your computer and use it in GitHub Desktop.
Save mrange/34341f15612e0799a67d50b7350f478d to your computer and use it in GitHub Desktop.
Reusable ADT generator for C#/T4
<#
var model = new Model ()
{
Namespace = "MyNameSpace" ,
Unions = new []
{
U ( "CustomerKind"
, UC ("Person" , F ("string", "FullName"), F ("string", "SocialNo" ), F ("DateTime", "BirthDate" ))
, UC ("Company", F ("string", "Name" ), F ("string", "CompanyNo"), F ("string" , "TaxNo" ))
),
},
Tuples = new []
{
T ("Address", F ("string", "Street"), F ("string", "Zip"), F ("string", "City"), F ("string", "State")) ,
},
};
#>
namespace <#=model.Namespace#>
{
using System;
using System.Text;
<#
foreach (var tuple in model.Tuples)
{
#>
// Tuple: <#=tuple.Name#>
partial struct <#=tuple.Name#>
: IEquatable<<#=tuple.Name#>>
, IComparable<<#=tuple.Name#>>
, IComparable
{
<#
TupleMembers (4, false, "tuple", tuple.Name, tuple.Fields);
#>
public int CompareTo (object other)
{
return other is <#=tuple.Name#>
? CompareTo ((<#=tuple.Name#>) other)
: 1
;
}
}
// Tuple: <#=tuple.Name#>
<#
}
#>
<#
foreach (var union in model.Unions)
{
var tag = 0;
#>
// Union: <#=union.Name#>
abstract partial class <#=union.Name#>
: IEquatable<<#=union.Name#>>
, IComparable<<#=union.Name#>>
, IComparable
{
public abstract int Tag { get; }
public abstract bool Equals (<#=union.Name#> other);
public abstract int CompareTo (<#=union.Name#> other);
public abstract int CompareTo (object other);
public TResult Match<TResult> (
TResult defaultValue
<#
foreach (var unionCase in union.UnionCases)
{
#>
, Func<<#=unionCase.Name#>, TResult> <#=LowerCaseFirst (unionCase.Name)#> = null
<#
}
#>
)
{
switch (Tag)
{
<#
tag = 0;
foreach (var unionCase in union.UnionCases)
{
++tag;
var lucn = LowerCaseFirst (unionCase.Name);
#>
case <#=tag#>:
return <#=lucn#> != null
? <#=lucn#> ((<#=unionCase.Name#>)this)
: defaultValue
;
<#
}
#>
default:
throw new Exception ("union/<#=union.Name#>: Invalid union case");
}
}
<#
tag = 0;
foreach (var unionCase in union.UnionCases)
{
++tag;
#>
// UnionCase: <#=unionCase.Name#>
public partial class <#=unionCase.Name#>
: <#=union.Name#>
, IEquatable<<#=unionCase.Name#>>
, IComparable<<#=unionCase.Name#>>, IComparable
{
<#
TupleMembers (6, true, "union/" + union.Name, unionCase.Name, unionCase.Fields);
#>
public override int Tag
{
get
{
return <#=tag#>;
}
}
public override bool Equals (<#=union.Name#> other)
{
return other is <#=unionCase.Name#>
? Equals ((<#=unionCase.Name#>) other)
: false
;
}
public override int CompareTo (<#=union.Name#> other)
{
return other is <#=unionCase.Name#>
? CompareTo ((<#=unionCase.Name#>) other)
: <#=tag#> < ((<#=union.Name#>)other).Tag ? -1 : 1
;
}
public override int CompareTo (object other)
{
if (other is <#=unionCase.Name#>)
{
return CompareTo ((<#=unionCase.Name#>) other);
}
else if (other is <#=union.Name#>)
{
return <#=tag#> < ((<#=union.Name#>)other).Tag ? -1 : 1;
}
else
{
return 1;
}
}
}
// UnionCase: <#=unionCase.Name#>
<#
}
#>
}
// Union: <#=union.Name#>
<#
}
#>
}
<#+
static FieldDefinition F (string type, string name)
{
return new FieldDefinition
{
Type = type ?? NoType ,
Name = name ?? NoName ,
};
}
static UnionDefinition U (string name, params UnionCaseDefinition[] unionCases)
{
return new UnionDefinition
{
Name = name ?? NoName ,
UnionCases = unionCases ?? new UnionCaseDefinition[0],
};
}
static UnionCaseDefinition UC (string name, params FieldDefinition[] fields)
{
return new UnionCaseDefinition
{
Name = name ?? NoName ,
Fields = fields ?? new FieldDefinition[0],
};
}
static TupleDefinition T (string name, params FieldDefinition[] fields)
{
return new TupleDefinition
{
Name = name ?? NoName ,
Fields = fields ?? new FieldDefinition[0],
};
}
class Model
{
public string Namespace = NoName ;
public UnionDefinition[] Unions = new UnionDefinition[0] ;
public TupleDefinition[] Tuples = new TupleDefinition[0] ;
}
class FieldDefinition
{
public string Type = NoType ;
public string Name = NoName ;
}
class UnionCaseDefinition
{
public string Name = NoName ;
public FieldDefinition[] Fields = new FieldDefinition[0] ;
}
class UnionDefinition
{
public string Name = NoName ;
public UnionCaseDefinition[] UnionCases = new UnionCaseDefinition[0];
}
class TupleDefinition
{
public string Name = NoName ;
public FieldDefinition[] Fields = new FieldDefinition[0] ;
}
const string NoName = "<NoName>";
const string NoType = "<NoType>";
static string LowerCaseFirst (string name)
{
return string.IsNullOrEmpty (name)
? ""
: char.ToLowerInvariant (name[0]) + name.Substring (1)
;
}
void PushIndent (int i)
{
PushIndent (new string (' ', i));
}
void ApplyTo<T> (Action<bool, T> transform, params T[] vs)
{
var first = true;
foreach (var v in vs)
{
transform (first, v);
first = false;
}
}
void FormatArgument (bool first, FieldDefinition field)
{
#>
<#=first ? " " : ","#> <#=LowerCaseFirst (field.Name)#>
<#+
}
void FormatParameter (bool first, FieldDefinition field)
{
#>
<#=first ? " " : ","#> <#=field.Type#> <#=LowerCaseFirst (field.Name)#>
<#+
}
void TupleMembers (int indent, bool refType, string prelude, string name, FieldDefinition[] fields)
{
PushIndent (indent);
foreach (var field in fields)
{
#>
public <#=field.Type#> <#=field.Name#>;
<#+
}
#>
public <#=name#> (
<#+
ApplyTo (FormatParameter, fields);
#>
)
{
<#+
foreach (var field in fields)
{
#>
<#=field.Name#> = <#=LowerCaseFirst (field.Name)#>;
<#+
}
#>
}
public static <#=name#> Create (
<#+
ApplyTo (FormatParameter, fields);
#>
)
{
return new <#=name#> (
<#+
ApplyTo (FormatArgument, fields);
#>
);
}
public override string ToString ()
{
var sb = new StringBuilder (16);
sb.Append ("(<#=prelude#>/<#=name#>");
<#+
foreach (var field in fields)
{
#>
sb.AppendFormat (", {0}", <#=field.Name#>);
<#+
}
#>
sb.Append (")");
return sb.ToString ();
}
public bool Equals (<#=name#> other)
{
<#+
if (refType)
{
#>
if (other == null)
{
return false;
}
<#+
}
#>
return
true
<#+
foreach (var field in fields)
{
#>
&& (<#=field.Name#> == default (<#=field.Type#>) ? other.<#=field.Name#> == default (<#=field.Type#>) : <#=field.Name#>.Equals (other.<#=field.Name#>))
<#+
}
#>
;
}
public override bool Equals (object other)
{
return other is <#=name#>
? Equals ((<#=name#>) other)
: false
;
}
public override int GetHashCode ()
{
var hashCode = 17;
<#+
foreach (var field in fields)
{
#>
hashCode = hashCode * 23 + (<#=field.Name#> != default (<#=field.Type#>) ? <#=field.Name#>.GetHashCode () : 17);
<#+
}
#>
return hashCode;
}
public int CompareTo (<#=name#> other)
{
<#+
if (refType)
{
#>
if (other == null)
{
return 1;
}
<#+
}
#>
<#+
foreach (var field in fields)
{
#>
{
var c = <#=field.Name#> == default (<#=field.Type#>)
? (other.<#=field.Name#> == default (<#=field.Type#>) ? 0 : -1)
: <#=field.Name#>.CompareTo (other.<#=field.Name#>)
;
if (c != 0)
{
return c;
}
}
<#+
}
#>
return 0;
}
<#+
PopIndent ();
}
#>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment