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" } ] } { Name = "Identifier" Fields = [ { Type = "Name"; Name = "Value" } ] } { Name = "List" Fields = [ { Type = "Expr[]"; Name = "Elements" } ] } { Name = "Variant" Fields = [ { Type = "Name"; Name = "Tag" }; { Type = "Expr?"; Name = "Argument" } ] } { Name = "Record" Fields = [ { Type = "Field[]" Name = "Extensions" } { Type = "BaseRecord?"; Name = "Base" } ] } { Name = "Selector" Fields = [ { Type = "Expr"; Name = "Left" }; { Type = "Name"; Name = "FieldName" } ] } { Name = "Indexer" Fields = [ { Type = "Expr"; Name = "Left" }; { Type = "Expr"; Name = "Index" } ] } { Name = "Call" Fields = [ { Type = "Expr"; Name = "Left" }; { Type = "Expr?[]"; Name = "Arguments" } ] } ] 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. // //////////////////////////////////////////////////////////// #nullable enable 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