type Field = { Type: string; Name: string } type Type = { Name: string; Fields: list } let types = [ { Name = "Sequence" Fields = [ { Type = "Expr"; Name = "Left" }; { Type = "Expr"; Name = "Right" } ] } { Name = "Binary" Fields = [ { Type = "Expr"; Name = "Left" } { Type = "Token"; Name = "Op" } { Type = "Expr"; Name = "Right" } ] } { Name = "Grouping" Fields = [ { Type = "Expr"; Name = "Expression" } ] } { Name = "Literal" Fields = [ { Type = "System.Object" Name = "Value" } ] } { Name = "Unary" Fields = [ { Type = "Token"; Name = "Op" }; { Type = "Expr"; Name = "Right" } ] } { Name = "If" Fields = [ { Type = "Expr"; Name = "Condition" } { Type = "Expr"; Name = "Then" } { Type = "Expr"; Name = "Else" } ] } ] let visitorMethod baseName t = $"visit{t.Name}{baseName}" let renderIVisitor (sw: System.IO.StreamWriter) (baseName: string) types = sw.Write($"public interface IVisitor {{\n") for t in types do sw.Write($"\tT {visitorMethod baseName t}({t.Name} {baseName.ToLower()});\n") sw.Write("}\n") let renderType (sw: System.IO.StreamWriter) baseName t = sw.Write $"public class {t.Name} : {baseName} {{\n" for f in t.Fields do sw.Write $"\tpublic required {f.Type} {f.Name} {{ get; init; }}\n" sw.Write $"\tpublic override T accept(IVisitor visitor) \t{{ \t\treturn visitor.{visitorMethod baseName t}(this); \t}}\n" sw.Write("}\n") let renderAST outputDir baseName types = let path = System.IO.Path.Combine(outputDir, baseName + ".g.cs") use sw = new System.IO.StreamWriter(path) sw.Write $"//////////////////////////////////////////////////////////// // THIS FILE IS GENERATED. // // DO NOT EDIT. // //////////////////////////////////////////////////////////// using System; namespace Finn.AST; public abstract class {baseName} {{ \tpublic abstract T accept(IVisitor visitor); }}\n" renderIVisitor sw baseName types List.iter (renderType sw baseName) types renderAST "." "Expr" types