meowlang/visitorgenerator/Program.cs
2022-02-13 03:02:23 +01:00

134 lines
3.3 KiB
C#

using System.Reflection;
using meowlang.parser;
var className = args[0];
var destinatinDir = args[1] ?? ".";
var destinationPath = Path.Join(destinatinDir, $"{className}.cs");
var parserType = typeof(Parser);
var nameSpace = parserType.Namespace;
var modelTypes = parserType.Assembly.DefinedTypes.Where(t => t.IsAssignableTo(typeof(ModelBase))).Where(t => !t.IsAbstract);
string UnModelifyName(string name)
{
var nameWithoutModel = name;
var modelIndex = nameWithoutModel.LastIndexOf("Model", StringComparison.Ordinal);
if (modelIndex > -1)
{
nameWithoutModel = nameWithoutModel.Remove(modelIndex);
}
return nameWithoutModel;
}
string MakeVisitorMethod(TypeInfo model, bool isVoid)
{
if (model == null) throw new ArgumentNullException(nameof(model));
var typeName = model.Name;
var visitorName = $"Visit{UnModelifyName(typeName)}";
return isVoid ? @$"
public virtual void {visitorName}({typeName} m)
{{
foreach (var modelBase in m.GetChildren())
{{
Visit(modelBase);
}}
}}
" : @$"
public virtual T {visitorName}({typeName} m)
{{
T result = default;
foreach (var modelBase in m.GetChildren())
{{
result = Visit(modelBase);
}}
return result;
}}
";
}
string MakeSwitchArm(TypeInfo model, bool isVoid)
{
if (model == null) throw new ArgumentNullException(nameof(model));
var typeName = model.Name;
var visitorName = $"Visit{UnModelifyName(typeName)}";
return isVoid ? $@"
case {typeName} x: {visitorName}(x); break;" : $@"
case {typeName} x: return {visitorName}(x);";
}
var arms = new List<string>();
var voidArms = new List<string>();
var methods = new List<string>();
var voidMethods = new List<string>();
foreach (var modelType in modelTypes)
{
arms.Add(MakeSwitchArm(modelType, false));
voidArms.Add(MakeSwitchArm(modelType, true));
methods.Add(MakeVisitorMethod(modelType, false));
voidMethods.Add(MakeVisitorMethod(modelType, true));
}
var sourceCode = $@"// automatically generated
// ReSharper disable MemberCanBeProtected.Global
// ReSharper disable MemberCanBePrivate.Global
#pragma warning disable CS8603
#pragma warning disable CS8600
#pragma warning disable CS8509
using System.CodeDom.Compiler;
namespace {nameSpace};
[GeneratedCode(""meow-visitorgenerator"", ""1"")]
public class {className}<T>
{{
public T? TryVisit(ModelBase? m)
{{
return m == null ? default : Visit(m);
}}
public T Visit(ModelBase m)
{{
try {{
switch(m)
{{
{string.Join("", arms)}
}}
}} catch(Exception e) {{
if (e is MeowModelVisitorException) throw;
throw new MeowModelVisitorException(m, e);
}}
return default;
}}
{string.Join("", methods)}
}}
[GeneratedCode(""meow-visitorgenerator"", ""1"")]
public class {className}
{{
public void Visit(ModelBase m)
{{
try {{
switch(m)
{{
{string.Join("", voidArms)}
}}
}} catch(Exception e) {{
if (e is MeowModelVisitorException) throw;
throw new MeowModelVisitorException(m, e);
}}
}}
{string.Join("", voidMethods)}
}}
";
File.WriteAllText(destinationPath, sourceCode);
Console.WriteLine($"output written to {destinationPath}");