type Field = { Type: string; Name: string } type Type = { Name: string; Fields: list } let exprTypes = [ { 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" } ] } { Name = "Let" Fields = [ { Type = "Binding[]" Name = "Bindings" } { Type = "Expr"; Name = "Body" } ] } ] let patternTypes = [ { Name = "SimplePattern" Fields = [ { Type = "Name?"; Name = "Identifier" } ] } { Name = "VariantPattern" Fields = [ { Type = "Name"; Name = "Tag" }; { Type = "Pattern?"; Name = "Argument" } ] } { Name = "FieldPattern" Fields = [ { Type = "Name"; Name = "Name" }; { Type = "Pattern?"; Name = "Pattern" } ] } { Name = "RecordPattern" Fields = [ { Type = "FieldPattern[]" Name = "Fields" } { Type = "SimplePattern?" Name = "Rest" } ] } ] let visitorMethod baseName t = $"visit{t.Name}{baseName}" let renderIVisitor (sw: System.IO.StreamWriter) (baseName: string) types = sw.Write($"public interface I{baseName}Visitor {{\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 = let writeField i field = if i > 0 then sw.Write(", ") sw.Write $"{field.Type} {field.Name}" sw.Write $"public partial record {t.Name}(" List.iteri writeField t.Fields sw.Write $") : {baseName}() {{\n" sw.Write $"\tpublic override T accept(I{baseName}Visitor 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 record {baseName}() {{ \tpublic abstract T accept(I{baseName}Visitor visitor); }}\n" renderIVisitor sw baseName types List.iter (renderType sw baseName) types renderAST "." "Expr" exprTypes renderAST "." "Pattern" patternTypes