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(); var voidArms = new List(); var methods = new List(); var voidMethods = new List(); 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} {{ 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}");