Skip to content

Instantly share code, notes, and snippets.

@mrange
Last active June 12, 2022 05:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mrange/560db9c0331ed3e4161cad5901b722db to your computer and use it in GitHub Desktop.
Save mrange/560db9c0331ed3e4161cad5901b722db to your computer and use it in GitHub Desktop.
T4 template to generate all version of a model
namespace CsGenAll
{
using System.Diagnostics;
// BooleanValue - BEGIN
// Kind: IsEnum
public enum BooleanValue
{
False
, True
}
// BooleanValue - END
// NumberValue - BEGIN
// Kind: IsEnum
public enum NumberValue
{
Zero
, One
, PI
}
// NumberValue - END
// StringValue - BEGIN
// Kind: IsEnum
public enum StringValue
{
Empty
, Hello
, Escaped
}
// StringValue - END
// ArrayValue - BEGIN
// Kind: IsUnion
public abstract partial record ArrayValue();
public sealed partial record ArrayValue_Empty(
) : ArrayValue();
public sealed partial record ArrayValue_One(
JsonValue V0
) : ArrayValue();
public sealed partial record ArrayValue_Two(
JsonValue V0
, JsonValue V1
) : ArrayValue();
// ArrayValue - END
// MemberValue - BEGIN
// Kind: IsRecord
public sealed partial record MemberValue(
StringValue Key
, JsonValue Value
);
// MemberValue - END
// ObjectValue - BEGIN
// Kind: IsUnion
public abstract partial record ObjectValue();
public sealed partial record ObjectValue_Empty(
) : ObjectValue();
public sealed partial record ObjectValue_One(
MemberValue V0
) : ObjectValue();
public sealed partial record ObjectValue_Two(
MemberValue V0
, MemberValue V1
) : ObjectValue();
// ObjectValue - END
// JsonValue - BEGIN
// Kind: IsUnion
public abstract partial record JsonValue();
public sealed partial record JsonValue_Null(
) : JsonValue();
public sealed partial record JsonValue_String(
StringValue Value
) : JsonValue();
public sealed partial record JsonValue_Number(
NumberValue Value
) : JsonValue();
public sealed partial record JsonValue_Boolean(
BooleanValue Value
) : JsonValue();
public sealed partial record JsonValue_Array(
ArrayValue Value
) : JsonValue();
public sealed partial record JsonValue_Object(
ObjectValue Value
) : JsonValue();
// JsonValue - END
static partial class Generators
{
// BooleanValue - BEGIN
// Kind: IsEnum
public static BooleanValue[] Generate_BooleanValue(int rem)
{
if (rem <= 0)
{
return Array.Empty<BooleanValue>();
}
return new []
{
BooleanValue.False
, BooleanValue.True
};
}
// BooleanValue - END
// NumberValue - BEGIN
// Kind: IsEnum
public static NumberValue[] Generate_NumberValue(int rem)
{
if (rem <= 0)
{
return Array.Empty<NumberValue>();
}
return new []
{
NumberValue.Zero
, NumberValue.One
, NumberValue.PI
};
}
// NumberValue - END
// StringValue - BEGIN
// Kind: IsEnum
public static StringValue[] Generate_StringValue(int rem)
{
if (rem <= 0)
{
return Array.Empty<StringValue>();
}
return new []
{
StringValue.Empty
, StringValue.Hello
, StringValue.Escaped
};
}
// StringValue - END
// ArrayValue - BEGIN
// Kind: IsUnion
public static ArrayValue[] Generate_ArrayValue(int rem)
{
if (rem <= 0)
{
return Array.Empty<ArrayValue>();
}
var vs_JsonValue = Generate_JsonValue(rem - 1);
var values = new ArrayValue[
0
// Case: ArrayValue.Empty - BEGIN
+( 1
)
// Case: ArrayValue.Empty - END
// Case: ArrayValue.One - BEGIN
+( 1
* vs_JsonValue.Length
)
// Case: ArrayValue.One - END
// Case: ArrayValue.Two - BEGIN
+( 1
* vs_JsonValue.Length
* vs_JsonValue.Length
)
// Case: ArrayValue.Two - END
];
var i = 0;
// Case: ArrayValue.Empty - BEGIN
values[i] = new ArrayValue_Empty(
);
++i;
// Case: ArrayValue.Empty - END
// Case: ArrayValue.One - BEGIN
foreach (var v_V0 in vs_JsonValue) {
values[i] = new ArrayValue_One(
v_V0
);
++i;
} // i_V0
// Case: ArrayValue.One - END
// Case: ArrayValue.Two - BEGIN
foreach (var v_V0 in vs_JsonValue) {
foreach (var v_V1 in vs_JsonValue) {
values[i] = new ArrayValue_Two(
v_V0
, v_V1
);
++i;
} // i_V0
} // i_V1
// Case: ArrayValue.Two - END
Debug.Assert(i == values.Length);
return values;
}
// ArrayValue - END
// MemberValue - BEGIN
// Kind: IsRecord
public static MemberValue[] Generate_MemberValue(int rem)
{
if (rem <= 0)
{
return Array.Empty<MemberValue>();
}
var vs_StringValue = Generate_StringValue(rem - 1);
var vs_JsonValue = Generate_JsonValue(rem - 1);
var values = new MemberValue[
1
* vs_StringValue.Length
* vs_JsonValue.Length
];
var i = 0;
foreach (var v_Key in vs_StringValue) {
foreach (var v_Value in vs_JsonValue) {
values[i] = new (
v_Key
, v_Value
);
++i;
} // v_Key
} // v_Value
Debug.Assert(i == values.Length);
return values;
}
// MemberValue - END
// ObjectValue - BEGIN
// Kind: IsUnion
public static ObjectValue[] Generate_ObjectValue(int rem)
{
if (rem <= 0)
{
return Array.Empty<ObjectValue>();
}
var vs_MemberValue = Generate_MemberValue(rem - 1);
var values = new ObjectValue[
0
// Case: ObjectValue.Empty - BEGIN
+( 1
)
// Case: ObjectValue.Empty - END
// Case: ObjectValue.One - BEGIN
+( 1
* vs_MemberValue.Length
)
// Case: ObjectValue.One - END
// Case: ObjectValue.Two - BEGIN
+( 1
* vs_MemberValue.Length
* vs_MemberValue.Length
)
// Case: ObjectValue.Two - END
];
var i = 0;
// Case: ObjectValue.Empty - BEGIN
values[i] = new ObjectValue_Empty(
);
++i;
// Case: ObjectValue.Empty - END
// Case: ObjectValue.One - BEGIN
foreach (var v_V0 in vs_MemberValue) {
values[i] = new ObjectValue_One(
v_V0
);
++i;
} // i_V0
// Case: ObjectValue.One - END
// Case: ObjectValue.Two - BEGIN
foreach (var v_V0 in vs_MemberValue) {
foreach (var v_V1 in vs_MemberValue) {
values[i] = new ObjectValue_Two(
v_V0
, v_V1
);
++i;
} // i_V0
} // i_V1
// Case: ObjectValue.Two - END
Debug.Assert(i == values.Length);
return values;
}
// ObjectValue - END
// JsonValue - BEGIN
// Kind: IsUnion
public static JsonValue[] Generate_JsonValue(int rem)
{
if (rem <= 0)
{
return Array.Empty<JsonValue>();
}
var vs_StringValue = Generate_StringValue(rem - 1);
var vs_NumberValue = Generate_NumberValue(rem - 1);
var vs_BooleanValue = Generate_BooleanValue(rem - 1);
var vs_ArrayValue = Generate_ArrayValue(rem - 1);
var vs_ObjectValue = Generate_ObjectValue(rem - 1);
var values = new JsonValue[
0
// Case: JsonValue.Null - BEGIN
+( 1
)
// Case: JsonValue.Null - END
// Case: JsonValue.String - BEGIN
+( 1
* vs_StringValue.Length
)
// Case: JsonValue.String - END
// Case: JsonValue.Number - BEGIN
+( 1
* vs_NumberValue.Length
)
// Case: JsonValue.Number - END
// Case: JsonValue.Boolean - BEGIN
+( 1
* vs_BooleanValue.Length
)
// Case: JsonValue.Boolean - END
// Case: JsonValue.Array - BEGIN
+( 1
* vs_ArrayValue.Length
)
// Case: JsonValue.Array - END
// Case: JsonValue.Object - BEGIN
+( 1
* vs_ObjectValue.Length
)
// Case: JsonValue.Object - END
];
var i = 0;
// Case: JsonValue.Null - BEGIN
values[i] = new JsonValue_Null(
);
++i;
// Case: JsonValue.Null - END
// Case: JsonValue.String - BEGIN
foreach (var v_Value in vs_StringValue) {
values[i] = new JsonValue_String(
v_Value
);
++i;
} // i_Value
// Case: JsonValue.String - END
// Case: JsonValue.Number - BEGIN
foreach (var v_Value in vs_NumberValue) {
values[i] = new JsonValue_Number(
v_Value
);
++i;
} // i_Value
// Case: JsonValue.Number - END
// Case: JsonValue.Boolean - BEGIN
foreach (var v_Value in vs_BooleanValue) {
values[i] = new JsonValue_Boolean(
v_Value
);
++i;
} // i_Value
// Case: JsonValue.Boolean - END
// Case: JsonValue.Array - BEGIN
foreach (var v_Value in vs_ArrayValue) {
values[i] = new JsonValue_Array(
v_Value
);
++i;
} // i_Value
// Case: JsonValue.Array - END
// Case: JsonValue.Object - BEGIN
foreach (var v_Value in vs_ObjectValue) {
values[i] = new JsonValue_Object(
v_Value
);
++i;
} // i_Value
// Case: JsonValue.Object - END
Debug.Assert(i == values.Length);
return values;
}
// JsonValue - END
// BooleanValue - BEGIN
// Kind: IsEnum
public static int SizeOf_BooleanValue(int rem)
{
if (rem <= 0)
{
return 0;
}
return 2;
}
// BooleanValue - END
// NumberValue - BEGIN
// Kind: IsEnum
public static int SizeOf_NumberValue(int rem)
{
if (rem <= 0)
{
return 0;
}
return 3;
}
// NumberValue - END
// StringValue - BEGIN
// Kind: IsEnum
public static int SizeOf_StringValue(int rem)
{
if (rem <= 0)
{
return 0;
}
return 3;
}
// StringValue - END
// ArrayValue - BEGIN
// Kind: IsUnion
public static int SizeOf_ArrayValue(int rem)
{
if (rem <= 0)
{
return 0;
}
var sizeOf_JsonValue = SizeOf_JsonValue(rem - 1);
return
0
// Case: ArrayValue.Empty - BEGIN
+ (
1
)
// Case: ArrayValue.One - BEGIN
+ (
1
* sizeOf_JsonValue
)
// Case: ArrayValue.Two - BEGIN
+ (
1
* sizeOf_JsonValue
* sizeOf_JsonValue
)
;
}
// ArrayValue - END
// MemberValue - BEGIN
// Kind: IsRecord
public static int SizeOf_MemberValue(int rem)
{
if (rem <= 0)
{
return 0;
}
var sizeOf_StringValue = SizeOf_StringValue(rem - 1);
var sizeOf_JsonValue = SizeOf_JsonValue(rem - 1);
return
1
* sizeOf_StringValue
* sizeOf_JsonValue
;
}
// MemberValue - END
// ObjectValue - BEGIN
// Kind: IsUnion
public static int SizeOf_ObjectValue(int rem)
{
if (rem <= 0)
{
return 0;
}
var sizeOf_MemberValue = SizeOf_MemberValue(rem - 1);
return
0
// Case: ObjectValue.Empty - BEGIN
+ (
1
)
// Case: ObjectValue.One - BEGIN
+ (
1
* sizeOf_MemberValue
)
// Case: ObjectValue.Two - BEGIN
+ (
1
* sizeOf_MemberValue
* sizeOf_MemberValue
)
;
}
// ObjectValue - END
// JsonValue - BEGIN
// Kind: IsUnion
public static int SizeOf_JsonValue(int rem)
{
if (rem <= 0)
{
return 0;
}
var sizeOf_StringValue = SizeOf_StringValue(rem - 1);
var sizeOf_NumberValue = SizeOf_NumberValue(rem - 1);
var sizeOf_BooleanValue = SizeOf_BooleanValue(rem - 1);
var sizeOf_ArrayValue = SizeOf_ArrayValue(rem - 1);
var sizeOf_ObjectValue = SizeOf_ObjectValue(rem - 1);
return
0
// Case: JsonValue.Null - BEGIN
+ (
1
)
// Case: JsonValue.String - BEGIN
+ (
1
* sizeOf_StringValue
)
// Case: JsonValue.Number - BEGIN
+ (
1
* sizeOf_NumberValue
)
// Case: JsonValue.Boolean - BEGIN
+ (
1
* sizeOf_BooleanValue
)
// Case: JsonValue.Array - BEGIN
+ (
1
* sizeOf_ArrayValue
)
// Case: JsonValue.Object - BEGIN
+ (
1
* sizeOf_ObjectValue
)
;
}
// JsonValue - END
}
}
<#@ assembly name ="System.Core" #>
<#@ import namespace ="System.Linq" #>
<#
var makePublic = true;
var model = new []
{
Union(
"BooleanValue"
, Case("False")
, Case("True")
)
, Union(
"NumberValue"
, Case("Zero")
, Case("One")
, Case("PI")
)
, Union(
"StringValue"
, Case("Empty")
, Case("Hello")
, Case("Escaped")
)
, Union(
"ArrayValue"
, Case("Empty")
, Case("One", ("JsonValue", "V0"))
, Case("Two", ("JsonValue", "V0"), ("JsonValue", "V1"))
)
, Record("MemberValue", ("StringValue", "Key"), ("JsonValue", "Value"))
, Union(
"ObjectValue"
, Case("Empty")
, Case("One", ("MemberValue", "V0"))
, Case("Two", ("MemberValue", "V0"), ("MemberValue", "V1"))
)
, Union(
"JsonValue"
, Case("Null" )
, Case("String" , ("StringValue" , "Value"))
, Case("Number" , ("NumberValue" , "Value"))
, Case("Boolean", ("BooleanValue" , "Value"))
, Case("Array" , ("ArrayValue" , "Value"))
, Case("Object" , ("ObjectValue" , "Value"))
)
};
#>
namespace CsGenAll
{
using System.Diagnostics;
<#
var decorator = makePublic ? "public " : "";
var prefix = "";
#>
<# foreach (var td in model) { #>
<#
var kind = WhatKind(td);
var (bnm, cs) = td;
#>
// <#=bnm#> - BEGIN
// Kind: <#=kind#>
<#
switch(kind)
{
default:
case Kind.IsEmpty: { #>
<#=decorator#>sealed partial record <#=bnm#>();
<# }
break;
case Kind.IsRecord: { #>
<#
var (_, args) = cs[0];
#>
<#=decorator#>sealed partial record <#=bnm#>(
<# prefix = " "; #>
<# foreach (var (tp, nm) in args) { #>
<#=prefix#><#=RightPad(tp, Pad)#> <#=nm#>
<# prefix = ", "; #>
<# } #>
);
<# }
break;
case Kind.IsEnum: { #>
<#=decorator#>enum <#=bnm#>
{
<# prefix = " "; #>
<# foreach (var (cnm, _) in cs) { #>
<#=prefix#><#=cnm#>
<# prefix = ", "; #>
<# } #>
}
<# }
break;
case Kind.IsUnion: { #>
<#=decorator#>abstract partial record <#=bnm#>();
<# foreach (var (cnm, args) in cs) { #>
<#=decorator#>sealed partial record <#=bnm#>_<#=cnm#>(
<# prefix = " "; #>
<# foreach (var (tp, nm) in args) { #>
<#=prefix#><#=RightPad(tp, Pad)#> <#=nm#>
<# prefix = ", "; #>
<# } #>
) : <#=bnm#>();
<# } #>
<# }
break;
}
#>
// <#=bnm#> - END
<# } #>
static partial class Generators
{
<# foreach (var td in model) { #>
<#
var kind = WhatKind(td);
var (bnm, cs) = td;
#>
// <#=bnm#> - BEGIN
// Kind: <#=kind#>
public static <#=bnm#>[] Generate_<#=bnm#>(int rem)
{
if (rem <= 0)
{
return Array.Empty<<#=bnm#>>();
}
<#
switch(kind)
{
default:
case Kind.IsEmpty: { #>
return new [] { new() };
<# }
break;
case Kind.IsRecord: { #>
<#
var (_, args) = cs[0];
#>
<# foreach (var tp in args.Select(x => x.Item1).Distinct()) { #>
var vs_<#=tp#> = Generate_<#=tp#>(rem - 1);
<# } #>
var values = new <#=bnm#>[
1
<# foreach (var tp in args.Select(x => x.Item1).Distinct()) { #>
* vs_<#=tp#>.Length
<# } #>
];
var i = 0;
<# foreach (var (tp, nm) in args) { #>
foreach (var v_<#=nm#> in vs_<#=tp#>) {
<# } #>
values[i] = new (
<# prefix = " "; #>
<# foreach (var (tp, nm) in args) { #>
<#=prefix#>v_<#=nm#>
<# prefix = ", "; #>
<# } #>
);
++i;
<# foreach (var (tp, nm) in args) { #>
} // v_<#=nm#>
<# } #>
Debug.Assert(i == values.Length);
return values;
<# }
break;
case Kind.IsEnum: { #>
<# prefix = " "; #>
return new []
{
<# foreach (var (cnm, _) in cs) { #>
<#=prefix#><#=bnm#>.<#=cnm#>
<# prefix = ", "; #>
<# } #>
};
<# }
break;
case Kind.IsUnion: { #>
<# foreach (var tp in cs.SelectMany(x => x.Item2).Select(x => x.Item1).Distinct()) { #>
var vs_<#=tp#> = Generate_<#=tp#>(rem - 1);
<# } #>
var values = new <#=bnm#>[
0
<# foreach (var (cnm, args) in cs) { #>
// Case: <#=bnm#>.<#=cnm#> - BEGIN
+( 1
<# foreach (var (tp, nm) in args) { #>
* vs_<#=tp#>.Length
<# } #>
)
// Case: <#=bnm#>.<#=cnm#> - END
<# } #>
];
var i = 0;
<# foreach (var (cnm, args) in cs) { #>
// Case: <#=bnm#>.<#=cnm#> - BEGIN
<# foreach (var (tp, nm) in args) { #>
foreach (var v_<#=nm#> in vs_<#=tp#>) {
<# } #>
values[i] = new <#=bnm#>_<#=cnm#>(
<# prefix = " "; #>
<# foreach (var (tp, nm) in args) { #>
<#=prefix#>v_<#=nm#>
<# prefix = ", "; #>
<# } #>
);
++i;
<# foreach (var (tp, nm) in args) { #>
} // i_<#=nm#>
<# } #>
// Case: <#=bnm#>.<#=cnm#> - END
<# } #>
Debug.Assert(i == values.Length);
return values;
<# }
break;
}
#>
}
// <#=bnm#> - END
<# } #>
<# foreach (var td in model) { #>
<#
var kind = WhatKind(td);
var (bnm, cs) = td;
#>
// <#=bnm#> - BEGIN
// Kind: <#=kind#>
public static int SizeOf_<#=bnm#>(int rem)
{
if (rem <= 0)
{
return 0;
}
<#
switch(kind)
{
default:
case Kind.IsEmpty: { #>
return 1;
<# }
break;
case Kind.IsRecord: { #>
<#
var (cnm, args) = cs[0];
#>
<# foreach (var (tp,_) in args) { #>
var sizeOf_<#=tp#> = SizeOf_<#=tp#>(rem - 1);
<# } #>
return
1
<# foreach (var (tp,_) in args) { #>
* sizeOf_<#=tp#>
<# } #>
;
<# }
break;
case Kind.IsEnum: { #>
return <#=cs.Length#>;
<# }
break;
case Kind.IsUnion: { #>
<# foreach (var tp in cs.SelectMany(x => x.Item2).Select(x => x.Item1).Distinct()) { #>
var sizeOf_<#=tp#> = SizeOf_<#=tp#>(rem - 1);
<# } #>
return
0
<# foreach (var (cnm, args) in cs) { #>
// Case: <#=bnm#>.<#=cnm#> - BEGIN
+ (
1
<# foreach (var (tp, nm) in args) { #>
* sizeOf_<#=tp#>
<# } #>
)
<# } #>
;
<# }
break;
}
#>
}
// <#=bnm#> - END
<# } #>
}
}
<#+
const int Pad = 30;
enum Kind
{
IsEnum ,
IsRecord ,
IsUnion ,
IsEmpty ,
}
static Kind WhatKind((string, (string, (string, string)[])[]) i)
{
var (bnm, cs) = i;
if (cs.Length == 0)
{
return Kind.IsEmpty;
}
else if (cs.Length == 1 && cs[0].Item1 == bnm)
{
return Kind.IsRecord;
}
else
{
var isEnum = cs.All(c => c.Item2.Length == 0);
if (isEnum)
{
return Kind.IsEnum;
}
else
{
return Kind.IsUnion;
}
}
}
static string RightPad(string s, int n)
{
s ??= "";
n = System.Math.Max(n, 0);
if (s.Length < n)
{
return s + new string(' ', n - s.Length);
}
else
{
return s;
}
}
(string, (string, (string, string)[])[]) Record(string name, params (string, string)[] attributes)
{
return (name, new [] { (name, attributes) });
}
(string, (string, (string, string)[])[]) Union(string name, params (string, (string, string)[])[] cases)
{
return (name, cases);
}
(string, (string, string)[]) Case(string name, params (string, string)[] attributes)
{
return (name, attributes);
}
#>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment