From 8834d885dee8a2a7dc5187e45283433c8f0260e8 Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Fri, 4 Oct 2024 15:13:49 +0100 Subject: [PATCH] Initial import of Fantomas client library (#6) --- .envrc | 1 + .github/workflows/dotnet.yaml | 13 + WoofWare.Whippet.Fantomas.Test/TestSurface.fs | 28 + .../WoofWare.Whippet.Fantomas.Test.fsproj | 25 + WoofWare.Whippet.Fantomas/Ast.fs | 47 ++ WoofWare.Whippet.Fantomas/CompExpr.fs | 55 ++ WoofWare.Whippet.Fantomas/Ident.fs | 72 ++ WoofWare.Whippet.Fantomas/PreXmlDoc.fs | 20 + WoofWare.Whippet.Fantomas/Primitives.fs | 33 + WoofWare.Whippet.Fantomas/README.md | 9 + WoofWare.Whippet.Fantomas/SurfaceBaseline.txt | 350 ++++++++++ WoofWare.Whippet.Fantomas/SynArgInfo.fs | 10 + WoofWare.Whippet.Fantomas/SynArgPats.fs | 36 + WoofWare.Whippet.Fantomas/SynAttribute.fs | 32 + WoofWare.Whippet.Fantomas/SynAttributes.fs | 19 + WoofWare.Whippet.Fantomas/SynBinding.fs | 253 +++++++ WoofWare.Whippet.Fantomas/SynComponentInfo.fs | 63 ++ WoofWare.Whippet.Fantomas/SynConst.fs | 12 + WoofWare.Whippet.Fantomas/SynExpr.fs | 415 ++++++++++++ .../SynExprLetOrUseTrivia.fs | 13 + WoofWare.Whippet.Fantomas/SynField.fs | 80 +++ WoofWare.Whippet.Fantomas/SynIdent.fs | 14 + WoofWare.Whippet.Fantomas/SynLongIdent.fs | 175 +++++ WoofWare.Whippet.Fantomas/SynMatchClause.fs | 28 + WoofWare.Whippet.Fantomas/SynMemberDefn.fs | 78 +++ WoofWare.Whippet.Fantomas/SynModuleDecl.fs | 37 ++ .../SynModuleOrNamespace.fs | 26 + WoofWare.Whippet.Fantomas/SynPat.fs | 73 ++ WoofWare.Whippet.Fantomas/SynSimplePat.fs | 13 + WoofWare.Whippet.Fantomas/SynSimplePats.fs | 15 + WoofWare.Whippet.Fantomas/SynType.fs | 624 ++++++++++++++++++ WoofWare.Whippet.Fantomas/SynTypeDefn.fs | 53 ++ WoofWare.Whippet.Fantomas/SynTypeDefnRepr.fs | 32 + WoofWare.Whippet.Fantomas/SynUnionCase.fs | 57 ++ WoofWare.Whippet.Fantomas/SynValInfo.fs | 9 + .../WoofWare.Whippet.Fantomas.fsproj | 65 ++ WoofWare.Whippet.Fantomas/version.json | 12 + WoofWare.Whippet.Test/TestSurface.fs | 2 +- WoofWare.Whippet.sln | 12 + WoofWare.Whippet/Program.fs | 8 +- WoofWare.Whippet/WoofWare.Whippet.fsproj | 1 - 41 files changed, 2915 insertions(+), 5 deletions(-) create mode 100644 .envrc create mode 100644 WoofWare.Whippet.Fantomas.Test/TestSurface.fs create mode 100644 WoofWare.Whippet.Fantomas.Test/WoofWare.Whippet.Fantomas.Test.fsproj create mode 100644 WoofWare.Whippet.Fantomas/Ast.fs create mode 100644 WoofWare.Whippet.Fantomas/CompExpr.fs create mode 100644 WoofWare.Whippet.Fantomas/Ident.fs create mode 100644 WoofWare.Whippet.Fantomas/PreXmlDoc.fs create mode 100644 WoofWare.Whippet.Fantomas/Primitives.fs create mode 100644 WoofWare.Whippet.Fantomas/README.md create mode 100644 WoofWare.Whippet.Fantomas/SurfaceBaseline.txt create mode 100644 WoofWare.Whippet.Fantomas/SynArgInfo.fs create mode 100644 WoofWare.Whippet.Fantomas/SynArgPats.fs create mode 100644 WoofWare.Whippet.Fantomas/SynAttribute.fs create mode 100644 WoofWare.Whippet.Fantomas/SynAttributes.fs create mode 100644 WoofWare.Whippet.Fantomas/SynBinding.fs create mode 100644 WoofWare.Whippet.Fantomas/SynComponentInfo.fs create mode 100644 WoofWare.Whippet.Fantomas/SynConst.fs create mode 100644 WoofWare.Whippet.Fantomas/SynExpr.fs create mode 100644 WoofWare.Whippet.Fantomas/SynExprLetOrUseTrivia.fs create mode 100644 WoofWare.Whippet.Fantomas/SynField.fs create mode 100644 WoofWare.Whippet.Fantomas/SynIdent.fs create mode 100644 WoofWare.Whippet.Fantomas/SynLongIdent.fs create mode 100644 WoofWare.Whippet.Fantomas/SynMatchClause.fs create mode 100644 WoofWare.Whippet.Fantomas/SynMemberDefn.fs create mode 100644 WoofWare.Whippet.Fantomas/SynModuleDecl.fs create mode 100644 WoofWare.Whippet.Fantomas/SynModuleOrNamespace.fs create mode 100644 WoofWare.Whippet.Fantomas/SynPat.fs create mode 100644 WoofWare.Whippet.Fantomas/SynSimplePat.fs create mode 100644 WoofWare.Whippet.Fantomas/SynSimplePats.fs create mode 100644 WoofWare.Whippet.Fantomas/SynType.fs create mode 100644 WoofWare.Whippet.Fantomas/SynTypeDefn.fs create mode 100644 WoofWare.Whippet.Fantomas/SynTypeDefnRepr.fs create mode 100644 WoofWare.Whippet.Fantomas/SynUnionCase.fs create mode 100644 WoofWare.Whippet.Fantomas/SynValInfo.fs create mode 100644 WoofWare.Whippet.Fantomas/WoofWare.Whippet.Fantomas.fsproj create mode 100644 WoofWare.Whippet.Fantomas/version.json diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.github/workflows/dotnet.yaml b/.github/workflows/dotnet.yaml index d61f4d6..d6e4858 100644 --- a/.github/workflows/dotnet.yaml +++ b/.github/workflows/dotnet.yaml @@ -155,6 +155,11 @@ jobs: with: name: nuget-package-core path: WoofWare.Whippet.Core/bin/Release/WoofWare.Whippet.Core.*.nupkg + - name: Upload NuGet artifact (Fantomas) + uses: actions/upload-artifact@v4 + with: + name: nuget-package-fantomas + path: WoofWare.Whippet.Fantomas/bin/Release/WoofWare.Whippet.Fantomas.*.nupkg expected-pack: needs: [nuget-pack] @@ -176,6 +181,14 @@ jobs: - name: Check NuGet contents # Verify that there is exactly one nupkg in the artifact that would be NuGet published run: if [[ $(find packed-core -maxdepth 1 -name 'WoofWare.Whippet.Core.*.nupkg' -printf c | wc -c) -ne "1" ]]; then exit 1; fi + - name: Download NuGet artifact (Fantomas) + uses: actions/download-artifact@v4 + with: + name: nuget-package-fantomas + path: packed-fantomas + - name: Check NuGet contents + # Verify that there is exactly one nupkg in the artifact that would be NuGet published + run: if [[ $(find packed-fantomas -maxdepth 1 -name 'WoofWare.Whippet.Fantomas.*.nupkg' -printf c | wc -c) -ne "1" ]]; then exit 1; fi all-required-checks-complete: needs: [check-dotnet-format, check-nix-format, build, build-nix, linkcheck, flake-check, nuget-pack, expected-pack, analyzers] diff --git a/WoofWare.Whippet.Fantomas.Test/TestSurface.fs b/WoofWare.Whippet.Fantomas.Test/TestSurface.fs new file mode 100644 index 0000000..1c0f826 --- /dev/null +++ b/WoofWare.Whippet.Fantomas.Test/TestSurface.fs @@ -0,0 +1,28 @@ +namespace WoofWare.Whippet.Fantomas.Test + +open NUnit.Framework +open WoofWare.Whippet.Fantomas +open ApiSurface + +[] +module TestSurface = + + let coreAssembly = typeof.Assembly + + [] + let ``Ensure API surface has not been modified`` () = ApiSurface.assertIdentical coreAssembly + + (* + [] + // https://github.com/nunit/nunit3-vs-adapter/issues/876 + let CheckVersionAgainstRemote () = + MonotonicVersion.validate assembly "WoofWare.Whippet.Fantomas" + *) + + [] + let ``Update API surface`` () = + ApiSurface.writeAssemblyBaseline coreAssembly + + [] + let ``Ensure public API is fully documented`` () = + DocCoverage.assertFullyDocumented coreAssembly diff --git a/WoofWare.Whippet.Fantomas.Test/WoofWare.Whippet.Fantomas.Test.fsproj b/WoofWare.Whippet.Fantomas.Test/WoofWare.Whippet.Fantomas.Test.fsproj new file mode 100644 index 0000000..1c148c4 --- /dev/null +++ b/WoofWare.Whippet.Fantomas.Test/WoofWare.Whippet.Fantomas.Test.fsproj @@ -0,0 +1,25 @@ + + + + net8.0 + + false + true + + + + + + + + + + + + + + + + + + diff --git a/WoofWare.Whippet.Fantomas/Ast.fs b/WoofWare.Whippet.Fantomas/Ast.fs new file mode 100644 index 0000000..6dea48c --- /dev/null +++ b/WoofWare.Whippet.Fantomas/Ast.fs @@ -0,0 +1,47 @@ +namespace WoofWare.Whippet.Fantomas + +open Fantomas.Core +open Fantomas.FCS.Syntax +open Fantomas.FCS.SyntaxTrivia + +/// Helper methods to convert between source code and FCS ASTs. +[] +module Ast = + /// Given the contents of an F# source file, parse it into an AST. This is sync-over-async internally, which is + /// naughty. + let parse (fileContents : string) : ParsedInput = + CodeFormatter.ParseAsync (false, fileContents) + |> Async.RunSynchronously + |> Array.head + |> fst + + /// Concatenate the input modules/namespaces and render them as a single F# source file. + /// + /// This can return `None`, if the input was empty. + /// This is sync-over-async internally, which is naughty. + let render (contents : SynModuleOrNamespace list) : string option = + if contents.IsEmpty then + None + else + + let parseTree = + ParsedInput.ImplFile ( + ParsedImplFileInput.ParsedImplFileInput ( + "file.fs", + false, + QualifiedNameOfFile.QualifiedNameOfFile (Ident.create "file"), + [], + [], + contents, + (false, false), + { + ParsedImplFileInputTrivia.CodeComments = [] + ConditionalDirectives = [] + }, + Set.empty + ) + ) + + let cfg = FormatConfig.Default + + CodeFormatter.FormatASTAsync (parseTree, cfg) |> Async.RunSynchronously |> Some diff --git a/WoofWare.Whippet.Fantomas/CompExpr.fs b/WoofWare.Whippet.Fantomas/CompExpr.fs new file mode 100644 index 0000000..53b7c3b --- /dev/null +++ b/WoofWare.Whippet.Fantomas/CompExpr.fs @@ -0,0 +1,55 @@ +namespace WoofWare.Whippet.Fantomas + +open Fantomas.FCS.Syntax + +/// A little DSL for defining the contents of a computation expression. +/// Pass these to `SynExpr.createCompExpr`. +type CompExprBinding = + /// `let! {varName} = {rhs}` + | LetBang of varName : string * rhs : SynExpr + /// `let {varName} = {rhs}` + | Let of varName : string * rhs : SynExpr + /// `use {varName} = {rhs}` + | Use of varName : string * rhs : SynExpr + /// `do {body}` + | Do of body : SynExpr + +(* +Potential API! +type internal CompExprBindings = + private + { + /// These are stored in reverse. + Bindings : CompExprBinding list + CompExprName : string + } + +[] +module internal CompExprBindings = + let make (name : string) : CompExprBindings = + { + Bindings = [] + CompExprName = name + } + + let thenDo (body : SynExpr) (bindings : CompExprBindings) = + { bindings with + Bindings = (Do body :: bindings.Bindings) + } + + let thenLet (varName : string) (value : SynExpr) (bindings : CompExprBindings) = + { bindings with + Bindings = (Let (varName, value) :: bindings.Bindings) + } + + let thenLetBang (varName : string) (value : SynExpr) (bindings : CompExprBindings) = + { bindings with + Bindings = (LetBang (varName, value) :: bindings.Bindings) + } + + + let thenUse (varName : string) (value : SynExpr) (bindings : CompExprBindings) = + { bindings with + Bindings = (LetBang (varName, value) :: bindings.Bindings) + } +*) diff --git a/WoofWare.Whippet.Fantomas/Ident.fs b/WoofWare.Whippet.Fantomas/Ident.fs new file mode 100644 index 0000000..91e5c30 --- /dev/null +++ b/WoofWare.Whippet.Fantomas/Ident.fs @@ -0,0 +1,72 @@ +namespace WoofWare.Whippet.Fantomas + +open System +open System.Text +open System.Text.RegularExpressions +open Fantomas.FCS.Syntax +open Fantomas.FCS.Text.Range + +/// Methods for manipulating the `Ident` type. This type indicates a variable or type identifier in many contexts: +/// whenever the AST needs to name something, it is usually with the `Ident` type. +[] +module Ident = + /// "Cast" a string into an `Ident`. Probably don't put any weird characters in this; I don't know how well it will + /// work! + let inline create (s : string) = Ident (s, range0) + + /// Fantomas bug, perhaps? "type" is not rendered as ``type``, although the ASTs are identical + /// apart from the ranges? + /// Awful hack: here is a function that does this sort of thing. + let createSanitisedParamName (s : string) = + match s with + | "type" -> create "type'" + | "private" -> create "private'" + | _ -> + + let result = StringBuilder () + + for i = 0 to s.Length - 1 do + if Char.IsLetter s.[i] then + result.Append s.[i] |> ignore + elif Char.IsNumber s.[i] then + if result.Length > 0 then + result.Append s.[i] |> ignore + elif s.[i] = '_' || s.[i] = '-' then + result.Append '_' |> ignore + else + failwith $"could not convert to ident: %s{s}" + + create (result.ToString ()) + + let private alnum = Regex @"^[a-zA-Z][a-zA-Z0-9]*$" + + /// Create an `Ident` for this string, suitable for use as a type name. So, for example, we'll put it into + /// PascalCase if it was in snake_case, we'll remove non-alphanumeric characters, and so on. + let createSanitisedTypeName (s : string) : Ident = + let result = StringBuilder () + let mutable capitalize = true + + for i = 0 to s.Length - 1 do + if Char.IsLetter s.[i] then + if capitalize then + result.Append (Char.ToUpperInvariant s.[i]) |> ignore + capitalize <- false + else + result.Append s.[i] |> ignore + elif Char.IsNumber s.[i] then + if result.Length > 0 then + result.Append s.[i] |> ignore + elif s.[i] = '_' then + capitalize <- true + + if result.Length = 0 then + failwith $"String %s{s} was not suitable as a type identifier" + + Ident (result.ToString (), range0) + + /// Create an Ident which is the same as the input but which has its initial char lowercased if it's a letter. + let lowerFirstLetter (x : Ident) : Ident = + let result = StringBuilder x.idText.Length + result.Append (Char.ToLowerInvariant x.idText.[0]) |> ignore + result.Append x.idText.[1..] |> ignore + create ((result : StringBuilder).ToString ()) diff --git a/WoofWare.Whippet.Fantomas/PreXmlDoc.fs b/WoofWare.Whippet.Fantomas/PreXmlDoc.fs new file mode 100644 index 0000000..69a17cd --- /dev/null +++ b/WoofWare.Whippet.Fantomas/PreXmlDoc.fs @@ -0,0 +1,20 @@ +namespace WoofWare.Whippet.Fantomas + +open Fantomas.FCS.Xml +open Fantomas.FCS.Text.Range + +/// Module for manipulating the PreXmlDoc AST type (which represents XML docstrings). +[] +module PreXmlDoc = + /// Pass an arbitrary string in here, with newlines if you like, and we'll make sure it appears correctly. + let create (s : string) : PreXmlDoc = + let s = s.Split "\n" + + for i = 0 to s.Length - 1 do + s.[i] <- " " + s.[i] + + PreXmlDoc.Create (s, range0) + + /// This is a light wrapper around Fantomas's `PreXmlDoc`; you should probably use `create` instead. + let create' (s : string seq) : PreXmlDoc = + PreXmlDoc.Create (Array.ofSeq s, range0) diff --git a/WoofWare.Whippet.Fantomas/Primitives.fs b/WoofWare.Whippet.Fantomas/Primitives.fs new file mode 100644 index 0000000..10b5029 --- /dev/null +++ b/WoofWare.Whippet.Fantomas/Primitives.fs @@ -0,0 +1,33 @@ +namespace WoofWare.Whippet.Fantomas + +open Fantomas.FCS.Syntax +open Fantomas.FCS.Text.Range + +/// Module for manipulating primitive types like `int` or `string`. +[] +module Primitives = + /// Given e.g. "byte", returns "System.Byte". + let qualifyType (typeName : string) : LongIdent option = + match typeName with + | "float32" + | "single" -> [ "System" ; "Single" ] |> Some + | "float" + | "double" -> [ "System" ; "Double" ] |> Some + | "byte" + | "uint8" -> [ "System" ; "Byte" ] |> Some + | "sbyte" + | "int8" -> [ "System" ; "SByte" ] |> Some + | "int16" -> [ "System" ; "Int16" ] |> Some + | "int" + | "int32" -> [ "System" ; "Int32" ] |> Some + | "int64" -> [ "System" ; "Int64" ] |> Some + | "uint16" -> [ "System" ; "UInt16" ] |> Some + | "uint" + | "uint32" -> [ "System" ; "UInt32" ] |> Some + | "uint64" -> [ "System" ; "UInt64" ] |> Some + | "char" -> [ "System" ; "Char" ] |> Some + | "decimal" -> [ "System" ; "Decimal" ] |> Some + | "string" -> [ "System" ; "String" ] |> Some + | "bool" -> [ "System" ; "Boolean" ] |> Some + | _ -> None + |> Option.map (List.map (fun i -> (Ident (i, range0)))) diff --git a/WoofWare.Whippet.Fantomas/README.md b/WoofWare.Whippet.Fantomas/README.md new file mode 100644 index 0000000..9da613d --- /dev/null +++ b/WoofWare.Whippet.Fantomas/README.md @@ -0,0 +1,9 @@ +# WoofWare.Whippet.Fantomas + +Helper methods to access [Fantomas](https://github.com/fsprojects/fantomas) from within a [Whippet](https://github.com/Smaug123/WoofWare.Whippet) plugin (or elsewhere). + +This is *not* intended to be the long-term future of Whippet plugins. +I have created it for compatibility with my existing [Myriad](https://github.com/MoiraeSoftware/myriad) plugins ([WoofWare.Myriad](https://github.com/Smaug123/WoofWare.Myriad/)), to help the transition to Whippet. + +As such, the version of Fantomas and the F# compiler is currently pinned to a rather old version. +Once I have migrated WoofWare.Myriad to Whippet, I will upgrade Fantomas here, and there will be breaking changes to the API. diff --git a/WoofWare.Whippet.Fantomas/SurfaceBaseline.txt b/WoofWare.Whippet.Fantomas/SurfaceBaseline.txt new file mode 100644 index 0000000..6013bdf --- /dev/null +++ b/WoofWare.Whippet.Fantomas/SurfaceBaseline.txt @@ -0,0 +1,350 @@ +WoofWare.Whippet.Fantomas.Ast inherit obj +WoofWare.Whippet.Fantomas.Ast.parse [static method]: string -> Fantomas.FCS.Syntax.ParsedInput +WoofWare.Whippet.Fantomas.Ast.render [static method]: Fantomas.FCS.Syntax.SynModuleOrNamespace list -> string option +WoofWare.Whippet.Fantomas.CompExprBinding inherit obj - union type with 4 cases +WoofWare.Whippet.Fantomas.CompExprBinding+Do inherit WoofWare.Whippet.Fantomas.CompExprBinding +WoofWare.Whippet.Fantomas.CompExprBinding+Do.body [property]: [read-only] Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.CompExprBinding+Do.get_body [method]: unit -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.CompExprBinding+Let inherit WoofWare.Whippet.Fantomas.CompExprBinding +WoofWare.Whippet.Fantomas.CompExprBinding+Let.get_rhs [method]: unit -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.CompExprBinding+Let.get_varName [method]: unit -> string +WoofWare.Whippet.Fantomas.CompExprBinding+Let.rhs [property]: [read-only] Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.CompExprBinding+Let.varName [property]: [read-only] string +WoofWare.Whippet.Fantomas.CompExprBinding+LetBang inherit WoofWare.Whippet.Fantomas.CompExprBinding +WoofWare.Whippet.Fantomas.CompExprBinding+LetBang.get_rhs [method]: unit -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.CompExprBinding+LetBang.get_varName [method]: unit -> string +WoofWare.Whippet.Fantomas.CompExprBinding+LetBang.rhs [property]: [read-only] Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.CompExprBinding+LetBang.varName [property]: [read-only] string +WoofWare.Whippet.Fantomas.CompExprBinding+Tags inherit obj +WoofWare.Whippet.Fantomas.CompExprBinding+Tags.Do [static field]: int = 3 +WoofWare.Whippet.Fantomas.CompExprBinding+Tags.Let [static field]: int = 1 +WoofWare.Whippet.Fantomas.CompExprBinding+Tags.LetBang [static field]: int = 0 +WoofWare.Whippet.Fantomas.CompExprBinding+Tags.Use [static field]: int = 2 +WoofWare.Whippet.Fantomas.CompExprBinding+Use inherit WoofWare.Whippet.Fantomas.CompExprBinding +WoofWare.Whippet.Fantomas.CompExprBinding+Use.get_rhs [method]: unit -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.CompExprBinding+Use.get_varName [method]: unit -> string +WoofWare.Whippet.Fantomas.CompExprBinding+Use.rhs [property]: [read-only] Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.CompExprBinding+Use.varName [property]: [read-only] string +WoofWare.Whippet.Fantomas.CompExprBinding.get_IsDo [method]: unit -> bool +WoofWare.Whippet.Fantomas.CompExprBinding.get_IsLet [method]: unit -> bool +WoofWare.Whippet.Fantomas.CompExprBinding.get_IsLetBang [method]: unit -> bool +WoofWare.Whippet.Fantomas.CompExprBinding.get_IsUse [method]: unit -> bool +WoofWare.Whippet.Fantomas.CompExprBinding.get_Tag [method]: unit -> int +WoofWare.Whippet.Fantomas.CompExprBinding.IsDo [property]: [read-only] bool +WoofWare.Whippet.Fantomas.CompExprBinding.IsLet [property]: [read-only] bool +WoofWare.Whippet.Fantomas.CompExprBinding.IsLetBang [property]: [read-only] bool +WoofWare.Whippet.Fantomas.CompExprBinding.IsUse [property]: [read-only] bool +WoofWare.Whippet.Fantomas.CompExprBinding.NewDo [static method]: Fantomas.FCS.Syntax.SynExpr -> WoofWare.Whippet.Fantomas.CompExprBinding +WoofWare.Whippet.Fantomas.CompExprBinding.NewLet [static method]: (string, Fantomas.FCS.Syntax.SynExpr) -> WoofWare.Whippet.Fantomas.CompExprBinding +WoofWare.Whippet.Fantomas.CompExprBinding.NewLetBang [static method]: (string, Fantomas.FCS.Syntax.SynExpr) -> WoofWare.Whippet.Fantomas.CompExprBinding +WoofWare.Whippet.Fantomas.CompExprBinding.NewUse [static method]: (string, Fantomas.FCS.Syntax.SynExpr) -> WoofWare.Whippet.Fantomas.CompExprBinding +WoofWare.Whippet.Fantomas.CompExprBinding.Tag [property]: [read-only] int +WoofWare.Whippet.Fantomas.Ident inherit obj +WoofWare.Whippet.Fantomas.Ident.create [static method]: string -> Fantomas.FCS.Syntax.Ident +WoofWare.Whippet.Fantomas.Ident.createSanitisedParamName [static method]: string -> Fantomas.FCS.Syntax.Ident +WoofWare.Whippet.Fantomas.Ident.createSanitisedTypeName [static method]: string -> Fantomas.FCS.Syntax.Ident +WoofWare.Whippet.Fantomas.Ident.lowerFirstLetter [static method]: Fantomas.FCS.Syntax.Ident -> Fantomas.FCS.Syntax.Ident +WoofWare.Whippet.Fantomas.PreXmlDoc inherit obj +WoofWare.Whippet.Fantomas.PreXmlDoc.create [static method]: string -> Fantomas.FCS.Xml.PreXmlDoc +WoofWare.Whippet.Fantomas.PreXmlDoc.create' [static method]: string seq -> Fantomas.FCS.Xml.PreXmlDoc +WoofWare.Whippet.Fantomas.Primitives inherit obj +WoofWare.Whippet.Fantomas.Primitives.qualifyType [static method]: string -> Fantomas.FCS.Syntax.Ident list option +WoofWare.Whippet.Fantomas.SynArgInfo inherit obj +WoofWare.Whippet.Fantomas.SynArgInfo.empty [static property]: [read-only] Fantomas.FCS.Syntax.SynArgInfo +WoofWare.Whippet.Fantomas.SynArgInfo.get_empty [static method]: unit -> Fantomas.FCS.Syntax.SynArgInfo +WoofWare.Whippet.Fantomas.SynArgPats inherit obj +WoofWare.Whippet.Fantomas.SynArgPats.create [static method]: Fantomas.FCS.Syntax.SynPat list -> Fantomas.FCS.Syntax.SynArgPats +WoofWare.Whippet.Fantomas.SynArgPats.createNamed [static method]: string list -> Fantomas.FCS.Syntax.SynArgPats +WoofWare.Whippet.Fantomas.SynAttribute inherit obj +WoofWare.Whippet.Fantomas.SynAttribute.autoOpen [static property]: [read-only] Fantomas.FCS.Syntax.SynAttribute +WoofWare.Whippet.Fantomas.SynAttribute.compilationRepresentation [static property]: [read-only] Fantomas.FCS.Syntax.SynAttribute +WoofWare.Whippet.Fantomas.SynAttribute.create [static method]: Fantomas.FCS.Syntax.SynLongIdent -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynAttribute +WoofWare.Whippet.Fantomas.SynAttribute.get_autoOpen [static method]: unit -> Fantomas.FCS.Syntax.SynAttribute +WoofWare.Whippet.Fantomas.SynAttribute.get_compilationRepresentation [static method]: unit -> Fantomas.FCS.Syntax.SynAttribute +WoofWare.Whippet.Fantomas.SynAttribute.get_requireQualifiedAccess [static method]: unit -> Fantomas.FCS.Syntax.SynAttribute +WoofWare.Whippet.Fantomas.SynAttribute.requireQualifiedAccess [static property]: [read-only] Fantomas.FCS.Syntax.SynAttribute +WoofWare.Whippet.Fantomas.SynAttributes inherit obj +WoofWare.Whippet.Fantomas.SynAttributes.ofAttrs [static method]: Fantomas.FCS.Syntax.SynAttribute list -> Fantomas.FCS.Syntax.SynAttributeList list +WoofWare.Whippet.Fantomas.SynBinding inherit obj +WoofWare.Whippet.Fantomas.SynBinding.basic [static method]: Fantomas.FCS.Syntax.Ident list -> Fantomas.FCS.Syntax.SynPat list -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynBinding +WoofWare.Whippet.Fantomas.SynBinding.makeInline [static method]: Fantomas.FCS.Syntax.SynBinding -> Fantomas.FCS.Syntax.SynBinding +WoofWare.Whippet.Fantomas.SynBinding.makeInstanceMember [static method]: Fantomas.FCS.Syntax.SynBinding -> Fantomas.FCS.Syntax.SynBinding +WoofWare.Whippet.Fantomas.SynBinding.makeNotInline [static method]: Fantomas.FCS.Syntax.SynBinding -> Fantomas.FCS.Syntax.SynBinding +WoofWare.Whippet.Fantomas.SynBinding.makeStaticMember [static method]: Fantomas.FCS.Syntax.SynBinding -> Fantomas.FCS.Syntax.SynBinding +WoofWare.Whippet.Fantomas.SynBinding.setInline [static method]: bool -> Fantomas.FCS.Syntax.SynBinding -> Fantomas.FCS.Syntax.SynBinding +WoofWare.Whippet.Fantomas.SynBinding.triviaZero [static method]: bool -> Fantomas.FCS.SyntaxTrivia.SynBindingTrivia +WoofWare.Whippet.Fantomas.SynBinding.withAccessibility [static method]: Fantomas.FCS.Syntax.SynAccess option -> Fantomas.FCS.Syntax.SynBinding -> Fantomas.FCS.Syntax.SynBinding +WoofWare.Whippet.Fantomas.SynBinding.withMutability [static method]: bool -> Fantomas.FCS.Syntax.SynBinding -> Fantomas.FCS.Syntax.SynBinding +WoofWare.Whippet.Fantomas.SynBinding.withRecursion [static method]: bool -> Fantomas.FCS.Syntax.SynBinding -> Fantomas.FCS.Syntax.SynBinding +WoofWare.Whippet.Fantomas.SynBinding.withReturnAnnotation [static method]: Fantomas.FCS.Syntax.SynType -> Fantomas.FCS.Syntax.SynBinding -> Fantomas.FCS.Syntax.SynBinding +WoofWare.Whippet.Fantomas.SynBinding.withXmlDoc [static method]: Fantomas.FCS.Xml.PreXmlDoc -> Fantomas.FCS.Syntax.SynBinding -> Fantomas.FCS.Syntax.SynBinding +WoofWare.Whippet.Fantomas.SynComponentInfo inherit obj +WoofWare.Whippet.Fantomas.SynComponentInfo.addAttributes [static method]: Fantomas.FCS.Syntax.SynAttribute list -> Fantomas.FCS.Syntax.SynComponentInfo -> Fantomas.FCS.Syntax.SynComponentInfo +WoofWare.Whippet.Fantomas.SynComponentInfo.create [static method]: Fantomas.FCS.Syntax.Ident -> Fantomas.FCS.Syntax.SynComponentInfo +WoofWare.Whippet.Fantomas.SynComponentInfo.createLong [static method]: Fantomas.FCS.Syntax.Ident list -> Fantomas.FCS.Syntax.SynComponentInfo +WoofWare.Whippet.Fantomas.SynComponentInfo.setAccessibility [static method]: Fantomas.FCS.Syntax.SynAccess option -> Fantomas.FCS.Syntax.SynComponentInfo -> Fantomas.FCS.Syntax.SynComponentInfo +WoofWare.Whippet.Fantomas.SynComponentInfo.setGenerics [static method]: Fantomas.FCS.Syntax.SynTyparDecls option -> Fantomas.FCS.Syntax.SynComponentInfo -> Fantomas.FCS.Syntax.SynComponentInfo +WoofWare.Whippet.Fantomas.SynComponentInfo.withAccessibility [static method]: Fantomas.FCS.Syntax.SynAccess -> Fantomas.FCS.Syntax.SynComponentInfo -> Fantomas.FCS.Syntax.SynComponentInfo +WoofWare.Whippet.Fantomas.SynComponentInfo.withDocString [static method]: Fantomas.FCS.Xml.PreXmlDoc -> Fantomas.FCS.Syntax.SynComponentInfo -> Fantomas.FCS.Syntax.SynComponentInfo +WoofWare.Whippet.Fantomas.SynComponentInfo.withGenerics [static method]: Fantomas.FCS.Syntax.SynTyparDecl list -> Fantomas.FCS.Syntax.SynComponentInfo -> Fantomas.FCS.Syntax.SynComponentInfo +WoofWare.Whippet.Fantomas.SynConstExt inherit obj +WoofWare.Whippet.Fantomas.SynConstExt.SynConst.Create.Static [static method]: string -> Fantomas.FCS.Syntax.SynConst +WoofWare.Whippet.Fantomas.SynExpr inherit obj +WoofWare.Whippet.Fantomas.SynExpr.applyFunction [static method]: Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.applyTo [static method]: Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.arrayIndexRange [static method]: Fantomas.FCS.Syntax.SynExpr option -> Fantomas.FCS.Syntax.SynExpr option -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.arrayLiteral [static method]: Fantomas.FCS.Syntax.SynExpr list -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.assign [static method]: Fantomas.FCS.Syntax.SynLongIdent -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.assignIndex [static method]: Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.awaitTask [static method]: Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.booleanAnd [static method]: Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.booleanOr [static method]: Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.callGenericMethod [static method]: Fantomas.FCS.Syntax.SynLongIdent -> Fantomas.FCS.Syntax.SynType list -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.callGenericMethod' [static method]: string -> string -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.callMethod [static method]: string -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.callMethodArg [static method]: string -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.createCompExpr [static method]: string -> Fantomas.FCS.Syntax.SynExpr -> WoofWare.Whippet.Fantomas.CompExprBinding list -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.createDo [static method]: Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.createForEach [static method]: Fantomas.FCS.Syntax.SynPat -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.createIdent [static method]: string -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.createIdent' [static method]: Fantomas.FCS.Syntax.Ident -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.createLambda [static method]: string -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.createLet [static method]: Fantomas.FCS.Syntax.SynBinding list -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.createLongIdent [static method]: string list -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.createLongIdent' [static method]: Fantomas.FCS.Syntax.Ident list -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.createLongIdent'' [static method]: Fantomas.FCS.Syntax.SynLongIdent -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.createMatch [static method]: Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynMatchClause list -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.createNew [static method]: Fantomas.FCS.Syntax.SynType -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.createNull [static method]: unit -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.createThunk [static method]: Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.createWhile [static method]: Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.dotGet [static method]: string -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.equals [static method]: Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.get_reraise [static method]: unit -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.greaterThan [static method]: Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.greaterThanOrEqual [static method]: Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.ifThenElse [static method]: Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.index [static method]: Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.lessThan [static method]: Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.lessThanOrEqual [static method]: Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.listCons [static method]: Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.listLiteral [static method]: Fantomas.FCS.Syntax.SynExpr list -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.minus [static method]: Fantomas.FCS.Syntax.SynLongIdent -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.minusN [static method]: Fantomas.FCS.Syntax.SynLongIdent -> int -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.paren [static method]: Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.pipeThroughFunction [static method]: Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.pipeThroughTryWith [static method]: Fantomas.FCS.Syntax.SynPat -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.plus [static method]: Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.reraise [static property]: [read-only] Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.sequential [static method]: Fantomas.FCS.Syntax.SynExpr list -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.startAsTask [static method]: Fantomas.FCS.Syntax.Ident -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.stripOptionalParen [static method]: Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.times [static method]: Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.toString [static method]: Fantomas.FCS.Syntax.SynType -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.tuple [static method]: Fantomas.FCS.Syntax.SynExpr list -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.tupleNoParen [static method]: Fantomas.FCS.Syntax.SynExpr list -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.typeAnnotate [static method]: Fantomas.FCS.Syntax.SynType -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.typeApp [static method]: Fantomas.FCS.Syntax.SynType list -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExpr.upcast' [static method]: Fantomas.FCS.Syntax.SynType -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExprExtensions inherit obj +WoofWare.Whippet.Fantomas.SynExprExtensions.SynExpr.CreateConst.Static [static method]: bool -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExprExtensions.SynExpr.CreateConst.Static [static method]: char -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExprExtensions.SynExpr.CreateConst.Static [static method]: int -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExprExtensions.SynExpr.CreateConst.Static [static method]: string -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExprExtensions.SynExpr.CreateConst.Static [static method]: unit -> Fantomas.FCS.Syntax.SynExpr +WoofWare.Whippet.Fantomas.SynExprLetOrUseTrivia inherit obj +WoofWare.Whippet.Fantomas.SynExprLetOrUseTrivia.empty [static property]: [read-only] Fantomas.FCS.SyntaxTrivia.SynExprLetOrUseTrivia +WoofWare.Whippet.Fantomas.SynExprLetOrUseTrivia.get_empty [static method]: unit -> Fantomas.FCS.SyntaxTrivia.SynExprLetOrUseTrivia +WoofWare.Whippet.Fantomas.SynField inherit obj +WoofWare.Whippet.Fantomas.SynField.extract [static method]: Fantomas.FCS.Syntax.SynField -> Fantomas.FCS.Syntax.Ident option WoofWare.Whippet.Fantomas.SynFieldData +WoofWare.Whippet.Fantomas.SynField.extractWithIdent [static method]: Fantomas.FCS.Syntax.SynField -> Fantomas.FCS.Syntax.Ident WoofWare.Whippet.Fantomas.SynFieldData +WoofWare.Whippet.Fantomas.SynField.make [static method]: Fantomas.FCS.Syntax.Ident option WoofWare.Whippet.Fantomas.SynFieldData -> Fantomas.FCS.Syntax.SynField +WoofWare.Whippet.Fantomas.SynField.mapIdent [static method]: ('a -> 'b) -> 'a WoofWare.Whippet.Fantomas.SynFieldData -> 'b WoofWare.Whippet.Fantomas.SynFieldData +WoofWare.Whippet.Fantomas.SynField.withDocString [static method]: Fantomas.FCS.Xml.PreXmlDoc -> Fantomas.FCS.Syntax.SynField -> Fantomas.FCS.Syntax.SynField +WoofWare.Whippet.Fantomas.SynFieldData`1 inherit obj +WoofWare.Whippet.Fantomas.SynFieldData`1..ctor [constructor]: (Fantomas.FCS.Syntax.SynAttribute list, 'Ident, Fantomas.FCS.Syntax.SynType) +WoofWare.Whippet.Fantomas.SynFieldData`1.Attrs [property]: [read-only] Fantomas.FCS.Syntax.SynAttribute list +WoofWare.Whippet.Fantomas.SynFieldData`1.get_Attrs [method]: unit -> Fantomas.FCS.Syntax.SynAttribute list +WoofWare.Whippet.Fantomas.SynFieldData`1.get_Ident [method]: unit -> 'Ident +WoofWare.Whippet.Fantomas.SynFieldData`1.get_Type [method]: unit -> Fantomas.FCS.Syntax.SynType +WoofWare.Whippet.Fantomas.SynFieldData`1.Ident [property]: [read-only] 'Ident +WoofWare.Whippet.Fantomas.SynFieldData`1.Type [property]: [read-only] Fantomas.FCS.Syntax.SynType +WoofWare.Whippet.Fantomas.SynIdent inherit obj +WoofWare.Whippet.Fantomas.SynIdent.createI [static method]: Fantomas.FCS.Syntax.Ident -> Fantomas.FCS.Syntax.SynIdent +WoofWare.Whippet.Fantomas.SynIdent.createS [static method]: string -> Fantomas.FCS.Syntax.SynIdent +WoofWare.Whippet.Fantomas.SynLongIdent inherit obj +WoofWare.Whippet.Fantomas.SynLongIdent.booleanAnd [static property]: [read-only] Fantomas.FCS.Syntax.SynLongIdent +WoofWare.Whippet.Fantomas.SynLongIdent.booleanOr [static property]: [read-only] Fantomas.FCS.Syntax.SynLongIdent +WoofWare.Whippet.Fantomas.SynLongIdent.create [static method]: Fantomas.FCS.Syntax.Ident list -> Fantomas.FCS.Syntax.SynLongIdent +WoofWare.Whippet.Fantomas.SynLongIdent.createI [static method]: Fantomas.FCS.Syntax.Ident -> Fantomas.FCS.Syntax.SynLongIdent +WoofWare.Whippet.Fantomas.SynLongIdent.createS [static method]: string -> Fantomas.FCS.Syntax.SynLongIdent +WoofWare.Whippet.Fantomas.SynLongIdent.createS' [static method]: string list -> Fantomas.FCS.Syntax.SynLongIdent +WoofWare.Whippet.Fantomas.SynLongIdent.eq [static property]: [read-only] Fantomas.FCS.Syntax.SynLongIdent +WoofWare.Whippet.Fantomas.SynLongIdent.geq [static property]: [read-only] Fantomas.FCS.Syntax.SynLongIdent +WoofWare.Whippet.Fantomas.SynLongIdent.get_booleanAnd [static method]: unit -> Fantomas.FCS.Syntax.SynLongIdent +WoofWare.Whippet.Fantomas.SynLongIdent.get_booleanOr [static method]: unit -> Fantomas.FCS.Syntax.SynLongIdent +WoofWare.Whippet.Fantomas.SynLongIdent.get_eq [static method]: unit -> Fantomas.FCS.Syntax.SynLongIdent +WoofWare.Whippet.Fantomas.SynLongIdent.get_geq [static method]: unit -> Fantomas.FCS.Syntax.SynLongIdent +WoofWare.Whippet.Fantomas.SynLongIdent.get_gt [static method]: unit -> Fantomas.FCS.Syntax.SynLongIdent +WoofWare.Whippet.Fantomas.SynLongIdent.get_leq [static method]: unit -> Fantomas.FCS.Syntax.SynLongIdent +WoofWare.Whippet.Fantomas.SynLongIdent.get_lt [static method]: unit -> Fantomas.FCS.Syntax.SynLongIdent +WoofWare.Whippet.Fantomas.SynLongIdent.get_pipe [static method]: unit -> Fantomas.FCS.Syntax.SynLongIdent +WoofWare.Whippet.Fantomas.SynLongIdent.get_plus [static method]: unit -> Fantomas.FCS.Syntax.SynLongIdent +WoofWare.Whippet.Fantomas.SynLongIdent.get_sub [static method]: unit -> Fantomas.FCS.Syntax.SynLongIdent +WoofWare.Whippet.Fantomas.SynLongIdent.get_times [static method]: unit -> Fantomas.FCS.Syntax.SynLongIdent +WoofWare.Whippet.Fantomas.SynLongIdent.gt [static property]: [read-only] Fantomas.FCS.Syntax.SynLongIdent +WoofWare.Whippet.Fantomas.SynLongIdent.isArray [static method]: Fantomas.FCS.Syntax.SynLongIdent -> bool +WoofWare.Whippet.Fantomas.SynLongIdent.isChoice [static method]: Fantomas.FCS.Syntax.SynLongIdent -> bool +WoofWare.Whippet.Fantomas.SynLongIdent.isDictionary [static method]: Fantomas.FCS.Syntax.SynLongIdent -> bool +WoofWare.Whippet.Fantomas.SynLongIdent.isIDictionary [static method]: Fantomas.FCS.Syntax.SynLongIdent -> bool +WoofWare.Whippet.Fantomas.SynLongIdent.isList [static method]: Fantomas.FCS.Syntax.SynLongIdent -> bool +WoofWare.Whippet.Fantomas.SynLongIdent.isMap [static method]: Fantomas.FCS.Syntax.SynLongIdent -> bool +WoofWare.Whippet.Fantomas.SynLongIdent.isNullable [static method]: Fantomas.FCS.Syntax.SynLongIdent -> bool +WoofWare.Whippet.Fantomas.SynLongIdent.isOption [static method]: Fantomas.FCS.Syntax.SynLongIdent -> bool +WoofWare.Whippet.Fantomas.SynLongIdent.isReadOnlyDictionary [static method]: Fantomas.FCS.Syntax.SynLongIdent -> bool +WoofWare.Whippet.Fantomas.SynLongIdent.isResponse [static method]: Fantomas.FCS.Syntax.SynLongIdent -> bool +WoofWare.Whippet.Fantomas.SynLongIdent.isUnit [static method]: Fantomas.FCS.Syntax.SynLongIdent -> bool +WoofWare.Whippet.Fantomas.SynLongIdent.leq [static property]: [read-only] Fantomas.FCS.Syntax.SynLongIdent +WoofWare.Whippet.Fantomas.SynLongIdent.lt [static property]: [read-only] Fantomas.FCS.Syntax.SynLongIdent +WoofWare.Whippet.Fantomas.SynLongIdent.pipe [static property]: [read-only] Fantomas.FCS.Syntax.SynLongIdent +WoofWare.Whippet.Fantomas.SynLongIdent.plus [static property]: [read-only] Fantomas.FCS.Syntax.SynLongIdent +WoofWare.Whippet.Fantomas.SynLongIdent.sub [static property]: [read-only] Fantomas.FCS.Syntax.SynLongIdent +WoofWare.Whippet.Fantomas.SynLongIdent.times [static property]: [read-only] Fantomas.FCS.Syntax.SynLongIdent +WoofWare.Whippet.Fantomas.SynLongIdent.toString [static method]: Fantomas.FCS.Syntax.SynLongIdent -> string +WoofWare.Whippet.Fantomas.SynMatchClause inherit obj +WoofWare.Whippet.Fantomas.SynMatchClause.create [static method]: Fantomas.FCS.Syntax.SynPat -> Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynMatchClause +WoofWare.Whippet.Fantomas.SynMatchClause.withWhere [static method]: Fantomas.FCS.Syntax.SynExpr -> Fantomas.FCS.Syntax.SynMatchClause -> Fantomas.FCS.Syntax.SynMatchClause +WoofWare.Whippet.Fantomas.SynMemberDefn inherit obj +WoofWare.Whippet.Fantomas.SynMemberDefn.abstractMember [static method]: Fantomas.FCS.Syntax.SynAttribute list -> Fantomas.FCS.Syntax.SynIdent -> Fantomas.FCS.Syntax.SynTyparDecls option -> Fantomas.FCS.Syntax.SynValInfo -> Fantomas.FCS.Xml.PreXmlDoc -> Fantomas.FCS.Syntax.SynType -> Fantomas.FCS.Syntax.SynMemberDefn +WoofWare.Whippet.Fantomas.SynMemberDefn.memberImplementation [static method]: Fantomas.FCS.Syntax.SynBinding -> Fantomas.FCS.Syntax.SynMemberDefn +WoofWare.Whippet.Fantomas.SynMemberDefn.staticMember [static method]: Fantomas.FCS.Syntax.SynBinding -> Fantomas.FCS.Syntax.SynMemberDefn +WoofWare.Whippet.Fantomas.SynModuleDecl inherit obj +WoofWare.Whippet.Fantomas.SynModuleDecl.createLet [static method]: Fantomas.FCS.Syntax.SynBinding -> Fantomas.FCS.Syntax.SynModuleDecl +WoofWare.Whippet.Fantomas.SynModuleDecl.createLets [static method]: Fantomas.FCS.Syntax.SynBinding list -> Fantomas.FCS.Syntax.SynModuleDecl +WoofWare.Whippet.Fantomas.SynModuleDecl.createTypes [static method]: Fantomas.FCS.Syntax.SynTypeDefn list -> Fantomas.FCS.Syntax.SynModuleDecl +WoofWare.Whippet.Fantomas.SynModuleDecl.nestedModule [static method]: Fantomas.FCS.Syntax.SynComponentInfo -> Fantomas.FCS.Syntax.SynModuleDecl list -> Fantomas.FCS.Syntax.SynModuleDecl +WoofWare.Whippet.Fantomas.SynModuleDecl.openAny [static method]: Fantomas.FCS.Syntax.SynOpenDeclTarget -> Fantomas.FCS.Syntax.SynModuleDecl +WoofWare.Whippet.Fantomas.SynModuleOrNamespace inherit obj +WoofWare.Whippet.Fantomas.SynModuleOrNamespace.createNamespace [static method]: Fantomas.FCS.Syntax.Ident list -> Fantomas.FCS.Syntax.SynModuleDecl list -> Fantomas.FCS.Syntax.SynModuleOrNamespace +WoofWare.Whippet.Fantomas.SynPat inherit obj +WoofWare.Whippet.Fantomas.SynPat.annotateType [static method]: Fantomas.FCS.Syntax.SynType -> Fantomas.FCS.Syntax.SynPat -> Fantomas.FCS.Syntax.SynPat +WoofWare.Whippet.Fantomas.SynPat.annotateTypeNoParen [static method]: Fantomas.FCS.Syntax.SynType -> Fantomas.FCS.Syntax.SynPat -> Fantomas.FCS.Syntax.SynPat +WoofWare.Whippet.Fantomas.SynPat.anon [static property]: [read-only] Fantomas.FCS.Syntax.SynPat +WoofWare.Whippet.Fantomas.SynPat.createConst [static method]: Fantomas.FCS.Syntax.SynConst -> Fantomas.FCS.Syntax.SynPat +WoofWare.Whippet.Fantomas.SynPat.createNull [static property]: [read-only] Fantomas.FCS.Syntax.SynPat +WoofWare.Whippet.Fantomas.SynPat.emptyArray [static property]: [read-only] Fantomas.FCS.Syntax.SynPat +WoofWare.Whippet.Fantomas.SynPat.emptyList [static property]: [read-only] Fantomas.FCS.Syntax.SynPat +WoofWare.Whippet.Fantomas.SynPat.get_anon [static method]: unit -> Fantomas.FCS.Syntax.SynPat +WoofWare.Whippet.Fantomas.SynPat.get_createNull [static method]: unit -> Fantomas.FCS.Syntax.SynPat +WoofWare.Whippet.Fantomas.SynPat.get_emptyArray [static method]: unit -> Fantomas.FCS.Syntax.SynPat +WoofWare.Whippet.Fantomas.SynPat.get_emptyList [static method]: unit -> Fantomas.FCS.Syntax.SynPat +WoofWare.Whippet.Fantomas.SynPat.get_unit [static method]: unit -> Fantomas.FCS.Syntax.SynPat +WoofWare.Whippet.Fantomas.SynPat.identWithArgs [static method]: Fantomas.FCS.Syntax.Ident list -> Fantomas.FCS.Syntax.SynArgPats -> Fantomas.FCS.Syntax.SynPat +WoofWare.Whippet.Fantomas.SynPat.listCons [static method]: Fantomas.FCS.Syntax.SynPat -> Fantomas.FCS.Syntax.SynPat -> Fantomas.FCS.Syntax.SynPat +WoofWare.Whippet.Fantomas.SynPat.named [static method]: string -> Fantomas.FCS.Syntax.SynPat +WoofWare.Whippet.Fantomas.SynPat.namedI [static method]: Fantomas.FCS.Syntax.Ident -> Fantomas.FCS.Syntax.SynPat +WoofWare.Whippet.Fantomas.SynPat.nameWithArgs [static method]: string -> Fantomas.FCS.Syntax.SynPat list -> Fantomas.FCS.Syntax.SynPat +WoofWare.Whippet.Fantomas.SynPat.paren [static method]: Fantomas.FCS.Syntax.SynPat -> Fantomas.FCS.Syntax.SynPat +WoofWare.Whippet.Fantomas.SynPat.tuple [static method]: Fantomas.FCS.Syntax.SynPat list -> Fantomas.FCS.Syntax.SynPat +WoofWare.Whippet.Fantomas.SynPat.tupleNoParen [static method]: Fantomas.FCS.Syntax.SynPat list -> Fantomas.FCS.Syntax.SynPat +WoofWare.Whippet.Fantomas.SynPat.unit [static property]: [read-only] Fantomas.FCS.Syntax.SynPat +WoofWare.Whippet.Fantomas.SynSimplePat inherit obj +WoofWare.Whippet.Fantomas.SynSimplePat.createId [static method]: Fantomas.FCS.Syntax.Ident -> Fantomas.FCS.Syntax.SynSimplePat +WoofWare.Whippet.Fantomas.SynSimplePats inherit obj +WoofWare.Whippet.Fantomas.SynSimplePats.create [static method]: Fantomas.FCS.Syntax.SynSimplePat list -> Fantomas.FCS.Syntax.SynSimplePats +WoofWare.Whippet.Fantomas.SynType inherit obj +WoofWare.Whippet.Fantomas.SynType.anon [static property]: [read-only] Fantomas.FCS.Syntax.SynType +WoofWare.Whippet.Fantomas.SynType.app [static method]: string -> Fantomas.FCS.Syntax.SynType list -> Fantomas.FCS.Syntax.SynType +WoofWare.Whippet.Fantomas.SynType.app' [static method]: Fantomas.FCS.Syntax.SynType -> Fantomas.FCS.Syntax.SynType list -> Fantomas.FCS.Syntax.SynType +WoofWare.Whippet.Fantomas.SynType.appPostfix [static method]: string -> Fantomas.FCS.Syntax.SynType -> Fantomas.FCS.Syntax.SynType +WoofWare.Whippet.Fantomas.SynType.appPostfix' [static method]: string list -> Fantomas.FCS.Syntax.SynType -> Fantomas.FCS.Syntax.SynType +WoofWare.Whippet.Fantomas.SynType.array [static method]: Fantomas.FCS.Syntax.SynType -> Fantomas.FCS.Syntax.SynType +WoofWare.Whippet.Fantomas.SynType.bool [static property]: [read-only] Fantomas.FCS.Syntax.SynType +WoofWare.Whippet.Fantomas.SynType.createLongIdent [static method]: Fantomas.FCS.Syntax.Ident list -> Fantomas.FCS.Syntax.SynType +WoofWare.Whippet.Fantomas.SynType.createLongIdent' [static method]: string list -> Fantomas.FCS.Syntax.SynType +WoofWare.Whippet.Fantomas.SynType.funFromDomain [static method]: Fantomas.FCS.Syntax.SynType -> Fantomas.FCS.Syntax.SynType -> Fantomas.FCS.Syntax.SynType +WoofWare.Whippet.Fantomas.SynType.get_anon [static method]: unit -> Fantomas.FCS.Syntax.SynType +WoofWare.Whippet.Fantomas.SynType.get_bool [static method]: unit -> Fantomas.FCS.Syntax.SynType +WoofWare.Whippet.Fantomas.SynType.get_int [static method]: unit -> Fantomas.FCS.Syntax.SynType +WoofWare.Whippet.Fantomas.SynType.get_obj [static method]: unit -> Fantomas.FCS.Syntax.SynType +WoofWare.Whippet.Fantomas.SynType.get_string [static method]: unit -> Fantomas.FCS.Syntax.SynType +WoofWare.Whippet.Fantomas.SynType.get_unit [static method]: unit -> Fantomas.FCS.Syntax.SynType +WoofWare.Whippet.Fantomas.SynType.getType [static method]: Fantomas.FCS.Syntax.SynType -> ((Fantomas.FCS.Syntax.SynType * bool) list * Fantomas.FCS.Syntax.SynType) +WoofWare.Whippet.Fantomas.SynType.int [static property]: [read-only] Fantomas.FCS.Syntax.SynType +WoofWare.Whippet.Fantomas.SynType.list [static method]: Fantomas.FCS.Syntax.SynType -> Fantomas.FCS.Syntax.SynType +WoofWare.Whippet.Fantomas.SynType.named [static method]: string -> Fantomas.FCS.Syntax.SynType +WoofWare.Whippet.Fantomas.SynType.obj [static property]: [read-only] Fantomas.FCS.Syntax.SynType +WoofWare.Whippet.Fantomas.SynType.option [static method]: Fantomas.FCS.Syntax.SynType -> Fantomas.FCS.Syntax.SynType +WoofWare.Whippet.Fantomas.SynType.paren [static method]: Fantomas.FCS.Syntax.SynType -> Fantomas.FCS.Syntax.SynType +WoofWare.Whippet.Fantomas.SynType.primitiveToHumanReadableString [static method]: Fantomas.FCS.Syntax.Ident list -> string +WoofWare.Whippet.Fantomas.SynType.provablyEqual [static method]: Fantomas.FCS.Syntax.SynType -> Fantomas.FCS.Syntax.SynType -> bool +WoofWare.Whippet.Fantomas.SynType.signatureParamOfType [static method]: Fantomas.FCS.Syntax.SynAttribute list -> Fantomas.FCS.Syntax.SynType -> bool -> Fantomas.FCS.Syntax.Ident option -> Fantomas.FCS.Syntax.SynType +WoofWare.Whippet.Fantomas.SynType.string [static property]: [read-only] Fantomas.FCS.Syntax.SynType +WoofWare.Whippet.Fantomas.SynType.stripOptionalParen [static method]: Fantomas.FCS.Syntax.SynType -> Fantomas.FCS.Syntax.SynType +WoofWare.Whippet.Fantomas.SynType.task [static method]: Fantomas.FCS.Syntax.SynType -> Fantomas.FCS.Syntax.SynType +WoofWare.Whippet.Fantomas.SynType.toFun [static method]: Fantomas.FCS.Syntax.SynType list -> Fantomas.FCS.Syntax.SynType -> Fantomas.FCS.Syntax.SynType +WoofWare.Whippet.Fantomas.SynType.toHumanReadableString [static method]: Fantomas.FCS.Syntax.SynType -> string +WoofWare.Whippet.Fantomas.SynType.tupleNoParen [static method]: Fantomas.FCS.Syntax.SynType list -> Fantomas.FCS.Syntax.SynType option +WoofWare.Whippet.Fantomas.SynType.unit [static property]: [read-only] Fantomas.FCS.Syntax.SynType +WoofWare.Whippet.Fantomas.SynType.var [static method]: Fantomas.FCS.Syntax.SynTypar -> Fantomas.FCS.Syntax.SynType +WoofWare.Whippet.Fantomas.SynTypeDefn inherit obj +WoofWare.Whippet.Fantomas.SynTypeDefn.create [static method]: Fantomas.FCS.Syntax.SynComponentInfo -> Fantomas.FCS.Syntax.SynTypeDefnRepr -> Fantomas.FCS.Syntax.SynTypeDefn +WoofWare.Whippet.Fantomas.SynTypeDefn.getAttribute [static method]: string -> Fantomas.FCS.Syntax.SynTypeDefn -> Fantomas.FCS.Syntax.SynAttribute option +WoofWare.Whippet.Fantomas.SynTypeDefn.getName [static method]: Fantomas.FCS.Syntax.SynTypeDefn -> Fantomas.FCS.Syntax.Ident list +WoofWare.Whippet.Fantomas.SynTypeDefn.hasAttribute [static method]: string -> Fantomas.FCS.Syntax.SynTypeDefn -> bool +WoofWare.Whippet.Fantomas.SynTypeDefn.withMemberDefns [static method]: Fantomas.FCS.Syntax.SynMemberDefn list -> Fantomas.FCS.Syntax.SynTypeDefn -> Fantomas.FCS.Syntax.SynTypeDefn +WoofWare.Whippet.Fantomas.SynTypeDefnRepr inherit obj +WoofWare.Whippet.Fantomas.SynTypeDefnRepr.augmentation [static method]: unit -> Fantomas.FCS.Syntax.SynTypeDefnRepr +WoofWare.Whippet.Fantomas.SynTypeDefnRepr.interfaceType [static method]: Fantomas.FCS.Syntax.SynMemberDefn list -> Fantomas.FCS.Syntax.SynTypeDefnRepr +WoofWare.Whippet.Fantomas.SynTypeDefnRepr.record [static method]: Fantomas.FCS.Syntax.SynField list -> Fantomas.FCS.Syntax.SynTypeDefnRepr +WoofWare.Whippet.Fantomas.SynTypeDefnRepr.recordWithAccess [static method]: Fantomas.FCS.Syntax.SynAccess option -> Fantomas.FCS.Syntax.SynField list -> Fantomas.FCS.Syntax.SynTypeDefnRepr +WoofWare.Whippet.Fantomas.SynTypeDefnRepr.union [static method]: Fantomas.FCS.Syntax.SynUnionCase list -> Fantomas.FCS.Syntax.SynTypeDefnRepr +WoofWare.Whippet.Fantomas.SynTypeDefnRepr.unionWithAccess [static method]: Fantomas.FCS.Syntax.SynAccess option -> Fantomas.FCS.Syntax.SynUnionCase list -> Fantomas.FCS.Syntax.SynTypeDefnRepr +WoofWare.Whippet.Fantomas.SynTypePatterns inherit obj +WoofWare.Whippet.Fantomas.SynTypePatterns.|ArrayType|_| [static method]: Fantomas.FCS.Syntax.SynType -> Fantomas.FCS.Syntax.SynType option +WoofWare.Whippet.Fantomas.SynTypePatterns.|BigInt|_| [static method]: Fantomas.FCS.Syntax.SynType -> unit option +WoofWare.Whippet.Fantomas.SynTypePatterns.|Byte|_| [static method]: Fantomas.FCS.Syntax.SynType -> unit option +WoofWare.Whippet.Fantomas.SynTypePatterns.|ChoiceType|_| [static method]: Fantomas.FCS.Syntax.SynType -> Fantomas.FCS.Syntax.SynType list option +WoofWare.Whippet.Fantomas.SynTypePatterns.|DateOnly|_| [static method]: Fantomas.FCS.Syntax.SynType -> unit option +WoofWare.Whippet.Fantomas.SynTypePatterns.|DateTimeOffset|_| [static method]: Fantomas.FCS.Syntax.SynType -> unit option +WoofWare.Whippet.Fantomas.SynTypePatterns.|DateTime|_| [static method]: Fantomas.FCS.Syntax.SynType -> unit option +WoofWare.Whippet.Fantomas.SynTypePatterns.|DictionaryType|_| [static method]: Fantomas.FCS.Syntax.SynType -> (Fantomas.FCS.Syntax.SynType * Fantomas.FCS.Syntax.SynType) option +WoofWare.Whippet.Fantomas.SynTypePatterns.|DirectoryInfo|_| [static method]: Fantomas.FCS.Syntax.SynType -> unit option +WoofWare.Whippet.Fantomas.SynTypePatterns.|FileInfo|_| [static method]: Fantomas.FCS.Syntax.SynType -> unit option +WoofWare.Whippet.Fantomas.SynTypePatterns.|Guid|_| [static method]: Fantomas.FCS.Syntax.SynType -> unit option +WoofWare.Whippet.Fantomas.SynTypePatterns.|HttpContent|_| [static method]: Fantomas.FCS.Syntax.SynType -> unit option +WoofWare.Whippet.Fantomas.SynTypePatterns.|HttpResponseMessage|_| [static method]: Fantomas.FCS.Syntax.SynType -> unit option +WoofWare.Whippet.Fantomas.SynTypePatterns.|IDictionaryType|_| [static method]: Fantomas.FCS.Syntax.SynType -> (Fantomas.FCS.Syntax.SynType * Fantomas.FCS.Syntax.SynType) option +WoofWare.Whippet.Fantomas.SynTypePatterns.|IReadOnlyDictionaryType|_| [static method]: Fantomas.FCS.Syntax.SynType -> (Fantomas.FCS.Syntax.SynType * Fantomas.FCS.Syntax.SynType) option +WoofWare.Whippet.Fantomas.SynTypePatterns.|JsonNode|_| [static method]: Fantomas.FCS.Syntax.SynType -> unit option +WoofWare.Whippet.Fantomas.SynTypePatterns.|ListType|_| [static method]: Fantomas.FCS.Syntax.SynType -> Fantomas.FCS.Syntax.SynType option +WoofWare.Whippet.Fantomas.SynTypePatterns.|MapType|_| [static method]: Fantomas.FCS.Syntax.SynType -> (Fantomas.FCS.Syntax.SynType * Fantomas.FCS.Syntax.SynType) option +WoofWare.Whippet.Fantomas.SynTypePatterns.|Measure|_| [static method]: Fantomas.FCS.Syntax.SynType -> (Fantomas.FCS.Syntax.Ident * Fantomas.FCS.Syntax.Ident list) option +WoofWare.Whippet.Fantomas.SynTypePatterns.|NullableType|_| [static method]: Fantomas.FCS.Syntax.SynType -> Fantomas.FCS.Syntax.SynType option +WoofWare.Whippet.Fantomas.SynTypePatterns.|NumberType|_| [static method]: Fantomas.FCS.Syntax.SynType -> Fantomas.FCS.Syntax.Ident list option +WoofWare.Whippet.Fantomas.SynTypePatterns.|OptionType|_| [static method]: Fantomas.FCS.Syntax.SynType -> Fantomas.FCS.Syntax.SynType option +WoofWare.Whippet.Fantomas.SynTypePatterns.|PrimitiveType|_| [static method]: Fantomas.FCS.Syntax.SynType -> Fantomas.FCS.Syntax.Ident list option +WoofWare.Whippet.Fantomas.SynTypePatterns.|RestEaseResponseType|_| [static method]: Fantomas.FCS.Syntax.SynType -> Fantomas.FCS.Syntax.SynType option +WoofWare.Whippet.Fantomas.SynTypePatterns.|Stream|_| [static method]: Fantomas.FCS.Syntax.SynType -> unit option +WoofWare.Whippet.Fantomas.SynTypePatterns.|String|_| [static method]: Fantomas.FCS.Syntax.SynType -> unit option +WoofWare.Whippet.Fantomas.SynTypePatterns.|Task|_| [static method]: Fantomas.FCS.Syntax.SynType -> Fantomas.FCS.Syntax.SynType option +WoofWare.Whippet.Fantomas.SynTypePatterns.|TimeSpan|_| [static method]: Fantomas.FCS.Syntax.SynType -> unit option +WoofWare.Whippet.Fantomas.SynTypePatterns.|UnitType|_| [static method]: Fantomas.FCS.Syntax.SynType -> unit option +WoofWare.Whippet.Fantomas.SynTypePatterns.|Uri|_| [static method]: Fantomas.FCS.Syntax.SynType -> unit option +WoofWare.Whippet.Fantomas.SynUnionCase inherit obj +WoofWare.Whippet.Fantomas.SynUnionCase.create [static method]: Fantomas.FCS.Syntax.Ident option WoofWare.Whippet.Fantomas.UnionCase -> Fantomas.FCS.Syntax.SynUnionCase +WoofWare.Whippet.Fantomas.SynValInfo inherit obj +WoofWare.Whippet.Fantomas.SynValInfo.empty [static property]: [read-only] Fantomas.FCS.Syntax.SynValInfo +WoofWare.Whippet.Fantomas.SynValInfo.get_empty [static method]: unit -> Fantomas.FCS.Syntax.SynValInfo +WoofWare.Whippet.Fantomas.UnionCase`1 inherit obj +WoofWare.Whippet.Fantomas.UnionCase`1..ctor [constructor]: (Fantomas.FCS.Syntax.Ident, Fantomas.FCS.Xml.PreXmlDoc option, Fantomas.FCS.Syntax.SynAccess option, Fantomas.FCS.Syntax.SynAttribute list, 'ident WoofWare.Whippet.Fantomas.SynFieldData list) +WoofWare.Whippet.Fantomas.UnionCase`1.Access [property]: [read-only] Fantomas.FCS.Syntax.SynAccess option +WoofWare.Whippet.Fantomas.UnionCase`1.Attributes [property]: [read-only] Fantomas.FCS.Syntax.SynAttribute list +WoofWare.Whippet.Fantomas.UnionCase`1.Fields [property]: [read-only] 'ident WoofWare.Whippet.Fantomas.SynFieldData list +WoofWare.Whippet.Fantomas.UnionCase`1.get_Access [method]: unit -> Fantomas.FCS.Syntax.SynAccess option +WoofWare.Whippet.Fantomas.UnionCase`1.get_Attributes [method]: unit -> Fantomas.FCS.Syntax.SynAttribute list +WoofWare.Whippet.Fantomas.UnionCase`1.get_Fields [method]: unit -> 'ident WoofWare.Whippet.Fantomas.SynFieldData list +WoofWare.Whippet.Fantomas.UnionCase`1.get_Name [method]: unit -> Fantomas.FCS.Syntax.Ident +WoofWare.Whippet.Fantomas.UnionCase`1.get_XmlDoc [method]: unit -> Fantomas.FCS.Xml.PreXmlDoc option +WoofWare.Whippet.Fantomas.UnionCase`1.Name [property]: [read-only] Fantomas.FCS.Syntax.Ident +WoofWare.Whippet.Fantomas.UnionCase`1.XmlDoc [property]: [read-only] Fantomas.FCS.Xml.PreXmlDoc option \ No newline at end of file diff --git a/WoofWare.Whippet.Fantomas/SynArgInfo.fs b/WoofWare.Whippet.Fantomas/SynArgInfo.fs new file mode 100644 index 0000000..50b6797 --- /dev/null +++ b/WoofWare.Whippet.Fantomas/SynArgInfo.fs @@ -0,0 +1,10 @@ +namespace WoofWare.Whippet.Fantomas + +open Fantomas.FCS.Syntax + +/// Module for manipulating the SynArgInfo AST type (which represents a single argument, possibly within a tuple, +/// in a `let` binding). +[] +module SynArgInfo = + /// No arguments. + let empty = SynArgInfo.SynArgInfo ([], false, None) diff --git a/WoofWare.Whippet.Fantomas/SynArgPats.fs b/WoofWare.Whippet.Fantomas/SynArgPats.fs new file mode 100644 index 0000000..4a65d1b --- /dev/null +++ b/WoofWare.Whippet.Fantomas/SynArgPats.fs @@ -0,0 +1,36 @@ +namespace WoofWare.Whippet.Fantomas + +open Fantomas.FCS.Syntax +open Fantomas.FCS.Text.Range + +/// Methods for manipulating SynArgPats, a type which represents the arguments to a pattern. +[] +module SynArgPats = + /// Create a SynArgPats which represents the tuple of the given case names. + /// For example, this specifies the `(a, b, c)` in the pattern match arm `| Foo (a, b, c)`. + let createNamed (caseNames : string list) : SynArgPats = + match caseNames.Length with + | 0 -> SynArgPats.Pats [] + | 1 -> + SynPat.Named (SynIdent.createS caseNames.[0], false, None, range0) + |> List.singleton + |> SynArgPats.Pats + | len -> + caseNames + |> List.map (fun name -> SynPat.Named (SynIdent.createS name, false, None, range0)) + |> fun t -> SynPat.Tuple (false, t, List.replicate (len - 1) range0, range0) + |> fun t -> SynPat.Paren (t, range0) + |> List.singleton + |> SynArgPats.Pats + + /// Create a SynArgPats representing the tuple of the given patterns. + /// For example, if the input `SynPat`s are `[Named "a" ; Named "b" ; Named "c"]`, then this would + /// specify the `(a, b, c)` in the pattern match arm `| Foo (a, b, c)`. + let create (pats : SynPat list) : SynArgPats = + match pats.Length with + | 0 -> SynArgPats.Pats [] + | 1 -> [ pats.[0] ] |> SynArgPats.Pats + | len -> + SynPat.Paren (SynPat.Tuple (false, pats, List.replicate (len - 1) range0, range0), range0) + |> List.singleton + |> SynArgPats.Pats diff --git a/WoofWare.Whippet.Fantomas/SynAttribute.fs b/WoofWare.Whippet.Fantomas/SynAttribute.fs new file mode 100644 index 0000000..6e72c6c --- /dev/null +++ b/WoofWare.Whippet.Fantomas/SynAttribute.fs @@ -0,0 +1,32 @@ +namespace WoofWare.Whippet.Fantomas + +open Fantomas.FCS.Syntax +open Fantomas.FCS.Text.Range + +/// Methods for manipulating `SynAttribute`, which represents a single attribute such as `[]`. +[] +module SynAttribute = + /// Create an attribute. If you want no arguments, use `SynExpr.CreateConst ()` as the `arg`. + let inline create (typeName : SynLongIdent) (arg : SynExpr) : SynAttribute = + { + TypeName = typeName + ArgExpr = arg + Target = None + AppliesToGetterAndSetter = false + Range = range0 + } + + /// The `[]` attribute, for use on modules. + let compilationRepresentation : SynAttribute = + [ "CompilationRepresentationFlags" ; "ModuleSuffix" ] + |> SynExpr.createLongIdent + |> SynExpr.paren + |> create (SynLongIdent.createS "CompilationRepresentation") + + /// The `[]` attribute. + let requireQualifiedAccess : SynAttribute = + create (SynLongIdent.createS "RequireQualifiedAccess") (SynExpr.CreateConst ()) + + /// The `[]` attribute. + let autoOpen : SynAttribute = + create (SynLongIdent.createS "AutoOpen") (SynExpr.CreateConst ()) diff --git a/WoofWare.Whippet.Fantomas/SynAttributes.fs b/WoofWare.Whippet.Fantomas/SynAttributes.fs new file mode 100644 index 0000000..2531fcf --- /dev/null +++ b/WoofWare.Whippet.Fantomas/SynAttributes.fs @@ -0,0 +1,19 @@ +namespace WoofWare.Whippet.Fantomas + +open Fantomas.FCS.Syntax +open Fantomas.FCS.Text.Range + +/// Module for manipulating the AST `SynAttributes` type. This represents a collection of `[]` objects: +/// if you have `[] [] foo` then the SynAttributes contains two elements, `[A]` and `[B ; C]`, each of which +/// is a list of attributes. +[] +module SynAttributes = + /// Build a SynAttributes of the form `[] [] []`, i.e. one `[<>]` for each input attribute. + let ofAttrs (attrs : SynAttribute list) : SynAttributes = + attrs + |> List.map (fun a -> + { + Attributes = [ a ] + Range = range0 + } + ) diff --git a/WoofWare.Whippet.Fantomas/SynBinding.fs b/WoofWare.Whippet.Fantomas/SynBinding.fs new file mode 100644 index 0000000..3403413 --- /dev/null +++ b/WoofWare.Whippet.Fantomas/SynBinding.fs @@ -0,0 +1,253 @@ +namespace WoofWare.Whippet.Fantomas + +open Fantomas.FCS.Syntax +open Fantomas.FCS.SyntaxTrivia +open Fantomas.FCS.Xml +open Fantomas.FCS.Text.Range + +/// Methods for manipulating `SynBinding`, which represents a `let`-binding or a member definition. +[] +module SynBinding = + + let rec private stripParen (pat : SynPat) = + match pat with + | SynPat.Paren (p, _) -> stripParen p + | _ -> pat + + let rec private getName (pat : SynPat) : Ident option = + match stripParen pat with + | SynPat.Named (SynIdent.SynIdent (name, _), _, _, _) -> Some name + | SynPat.Typed (pat, _, _) -> getName pat + | SynPat.LongIdent (SynLongIdent.SynLongIdent (longIdent, _, _), _, _, _, _, _) -> + match longIdent with + | [ x ] -> Some x + | _ -> failwithf "got long ident %O ; can only get the name of a long ident with one component" longIdent + | _ -> None + + let private getArgInfo (pat : SynPat) : SynArgInfo list = + // TODO: this only copes with one layer of tupling + match stripParen pat with + | SynPat.Tuple (_, pats, _, _) -> pats |> List.map (fun pat -> SynArgInfo.SynArgInfo ([], false, getName pat)) + | pat -> [ SynArgInfo.SynArgInfo (SynAttributes.Empty, false, getName pat) ] + + /// The basic `SynBindingTrivia` which means "there's nothing special about this binding". + /// You tell us whether this is a `member Foo = ...` versus a `let foo = ...`. + let triviaZero (isMember : bool) : SynBindingTrivia = + { + SynBindingTrivia.EqualsRange = Some range0 + InlineKeyword = None + LeadingKeyword = + if isMember then + SynLeadingKeyword.Member range0 + else + SynLeadingKeyword.Let range0 + } + + /// A simple binding: + /// `let {name} {args} = {body}` + /// + /// If you want this to become an instance member, you need to make sure the `this.` is present as a component + /// of the `name`. + let basic (name : LongIdent) (args : SynPat list) (body : SynExpr) : SynBinding = + let valInfo : SynValInfo = + args + |> List.map getArgInfo + |> fun x -> SynValInfo.SynValInfo (x, SynArgInfo.SynArgInfo ([], false, None)) + + SynBinding.SynBinding ( + None, + SynBindingKind.Normal, + false, + false, + [], + PreXmlDoc.Empty, + SynValData.SynValData (None, valInfo, None), + SynPat.identWithArgs name (SynArgPats.Pats args), + None, + body, + range0, + DebugPointAtBinding.Yes range0, + triviaZero false + ) + + /// Set the mutability of this binding: `let mutable i = ...` (or remove the word `mutable`, if `mut` is false). + let withMutability (mut : bool) (binding : SynBinding) : SynBinding = + match binding with + | SynBinding (pat, kind, inl, _, attrs, xml, valData, headPat, returnInfo, expr, range, debugPoint, trivia) -> + SynBinding (pat, kind, inl, mut, attrs, xml, valData, headPat, returnInfo, expr, range, debugPoint, trivia) + + /// Set the `rec` keyword on this binding: `let rec foo = ...` (or remove the word `rec`, if `isRec` is false). + let withRecursion (isRec : bool) (binding : SynBinding) : SynBinding = + match binding with + | SynBinding (pat, kind, inl, mut, attrs, xml, valData, headPat, returnInfo, expr, range, debugPoint, trivia) -> + let trivia = + { trivia with + LeadingKeyword = + match trivia.LeadingKeyword with + | SynLeadingKeyword.Let _ -> + if isRec then + SynLeadingKeyword.LetRec (range0, range0) + else + trivia.LeadingKeyword + | SynLeadingKeyword.LetRec _ -> + if isRec then + trivia.LeadingKeyword + else + trivia.LeadingKeyword + | existing -> + failwith + $"WoofWare.Whippet.Fantomas doesn't yet let you adjust the recursion modifier on a binding with modifier %O{existing}" + } + + SynBinding (pat, kind, inl, mut, attrs, xml, valData, headPat, returnInfo, expr, range, debugPoint, trivia) + + /// Override the accessibility modifier on this binding, or clear it: `let private foo = ...` + let withAccessibility (acc : SynAccess option) (binding : SynBinding) : SynBinding = + match binding with + | SynBinding (_, kind, inl, mut, attrs, xml, valData, headPat, returnInfo, expr, range, debugPoint, trivia) -> + let headPat = + match headPat with + | SynPat.LongIdent (ident, extra, options, argPats, _, range) -> + SynPat.LongIdent (ident, extra, options, argPats, acc, range) + | _ -> failwithf "unrecognised head pattern: %O" headPat + + SynBinding (acc, kind, inl, mut, attrs, xml, valData, headPat, returnInfo, expr, range, debugPoint, trivia) + + /// Set the XML docstring on this binding: `/// blah\nlet foo = ...` + let withXmlDoc (doc : PreXmlDoc) (binding : SynBinding) : SynBinding = + match binding with + | SynBinding (acc, kind, inl, mut, attrs, _, valData, headPat, returnInfo, expr, range, debugPoint, trivia) -> + SynBinding (acc, kind, inl, mut, attrs, doc, valData, headPat, returnInfo, expr, range, debugPoint, trivia) + + /// Set the return type annotation: `let foo : int = ...` + let withReturnAnnotation (ty : SynType) (binding : SynBinding) : SynBinding = + match binding with + | SynBinding (acc, kind, inl, mut, attrs, doc, valData, headPat, _, expr, range, debugPoint, trivia) -> + let retInfo = + SynBindingReturnInfo.SynBindingReturnInfo ( + ty, + range0, + [], + { + ColonRange = Some range0 + } + ) + + SynBinding ( + acc, + kind, + inl, + mut, + attrs, + doc, + valData, + headPat, + Some retInfo, + expr, + range, + debugPoint, + trivia + ) + + /// Make the definition an `inline` definition: `let inline foo = ...` + let inline makeInline (binding : SynBinding) : SynBinding = + match binding with + | SynBinding (acc, kind, _, mut, attrs, doc, valData, headPat, ret, expr, range, debugPoint, trivia) -> + SynBinding ( + acc, + kind, + true, + mut, + attrs, + doc, + valData, + headPat, + ret, + expr, + range, + debugPoint, + { trivia with + InlineKeyword = Some range0 + } + ) + + /// Make the definition not be an `inline` definition: that is, turn `let inline foo = ...` into `let foo = ...`. + /// This is a no-op if the binding is already not inline. + let inline makeNotInline (binding : SynBinding) : SynBinding = + match binding with + | SynBinding (acc, kind, _, mut, attrs, doc, valData, headPat, ret, expr, range, debugPoint, trivia) -> + SynBinding ( + acc, + kind, + false, + mut, + attrs, + doc, + valData, + headPat, + ret, + expr, + range, + debugPoint, + { trivia with + InlineKeyword = None + } + ) + + /// Set or remove the `inline` keyword on the given binding. + let inline setInline (isInline : bool) (binding : SynBinding) : SynBinding = + if isInline then + makeInline binding + else + makeNotInline binding + + /// Convert this member definition to a `static` member. + let makeStaticMember (binding : SynBinding) : SynBinding = + let memberFlags = + { + SynMemberFlags.IsInstance = false + SynMemberFlags.IsDispatchSlot = false + SynMemberFlags.IsOverrideOrExplicitImpl = false + SynMemberFlags.IsFinal = false + SynMemberFlags.GetterOrSetterIsCompilerGenerated = false + SynMemberFlags.MemberKind = SynMemberKind.Member + } + + match binding with + | SynBinding (acc, kind, inl, mut, attrs, doc, valData, headPat, ret, expr, range, debugPoint, trivia) -> + let valData = + match valData with + | SynValData.SynValData (_, valInfo, _) -> SynValData.SynValData (Some memberFlags, valInfo, None) + + let trivia = + { trivia with + LeadingKeyword = SynLeadingKeyword.StaticMember (range0, range0) + } + + SynBinding (acc, kind, inl, mut, attrs, doc, valData, headPat, ret, expr, range, debugPoint, trivia) + + /// Convert this member definition to an instance member, `member this.Foo = ...`. + /// You need to make sure the `this` is present in the name of the binding. + let makeInstanceMember (binding : SynBinding) : SynBinding = + let memberFlags = + { + SynMemberFlags.IsInstance = true + SynMemberFlags.IsDispatchSlot = false + SynMemberFlags.IsOverrideOrExplicitImpl = true + SynMemberFlags.IsFinal = false + SynMemberFlags.GetterOrSetterIsCompilerGenerated = false + SynMemberFlags.MemberKind = SynMemberKind.Member + } + + match binding with + | SynBinding (acc, kind, inl, mut, attrs, doc, valData, headPat, ret, expr, range, debugPoint, trivia) -> + let valData = + match valData with + | SynValData.SynValData (_, valInfo, _) -> SynValData.SynValData (Some memberFlags, valInfo, None) + + let trivia = + { trivia with + LeadingKeyword = SynLeadingKeyword.Member range0 + } + + SynBinding (acc, kind, inl, mut, attrs, doc, valData, headPat, ret, expr, range, debugPoint, trivia) diff --git a/WoofWare.Whippet.Fantomas/SynComponentInfo.fs b/WoofWare.Whippet.Fantomas/SynComponentInfo.fs new file mode 100644 index 0000000..f9fc445 --- /dev/null +++ b/WoofWare.Whippet.Fantomas/SynComponentInfo.fs @@ -0,0 +1,63 @@ +namespace WoofWare.Whippet.Fantomas + +open Fantomas.FCS.Syntax +open Fantomas.FCS.Xml +open Fantomas.FCS.Text.Range + +/// Methods for manipulating `SynComponentInfo`, which is like the "front matter" for a type definition. +/// It specifies type parameters, docstrings, name, etc. +[] +module SynComponentInfo = + /// Create the most basic possible SynComponentInfo, specifying only its name. + let inline createLong (name : LongIdent) = + SynComponentInfo.SynComponentInfo ([], None, [], name, PreXmlDoc.Empty, false, None, range0) + + /// Create the most basic possible SynComponentInfo, specifying only its name. + /// (Don't put full stops in this `name`; use `createLong` if you want to qualify the name further.) + let inline create (name : Ident) = createLong [ name ] + + /// Add a docstring to this type definition front matter. + let inline withDocString (doc : PreXmlDoc) (i : SynComponentInfo) : SynComponentInfo = + match i with + | SynComponentInfo.SynComponentInfo (attrs, typars, constraints, name, _, postfix, access, range) -> + SynComponentInfo (attrs, typars, constraints, name, doc, postfix, access, range) + + /// Specify that this type definition has generic type parameters. + /// This is the fully general method; you might find it more ergonomic to use the more specialised `withGenerics` + /// instead. + let inline setGenerics (typars : SynTyparDecls option) (i : SynComponentInfo) : SynComponentInfo = + match i with + | SynComponentInfo.SynComponentInfo (attrs, _, constraints, name, doc, postfix, access, range) -> + SynComponentInfo (attrs, typars, constraints, name, doc, postfix, access, range) + + /// Specify that this type definition has these generic type parameters. + let inline withGenerics (typars : SynTyparDecl list) (i : SynComponentInfo) : SynComponentInfo = + let inner = + if typars.IsEmpty then + None + else + Some (SynTyparDecls.PostfixList (typars, [], range0)) + + setGenerics inner i + + /// Specify that this type definition has this accessibility modifier (or no accessibility modifier). + let inline setAccessibility (acc : SynAccess option) (i : SynComponentInfo) : SynComponentInfo = + match i with + | SynComponentInfo.SynComponentInfo (attrs, typars, constraints, name, doc, postfix, _, range) -> + SynComponentInfo.SynComponentInfo (attrs, typars, constraints, name, doc, postfix, acc, range) + + /// Specify that this type definition has this accessibility modifier. + let inline withAccessibility (acc : SynAccess) (i : SynComponentInfo) : SynComponentInfo = + setAccessibility (Some acc) i + + /// Prepend these attributes to the type definition: `[] type Blah = ...`. + let inline addAttributes (attrs : SynAttribute list) (i : SynComponentInfo) : SynComponentInfo = + match i with + | SynComponentInfo.SynComponentInfo (oldAttrs, typars, constraints, name, doc, postfix, acc, range) -> + let attrs = + { + SynAttributeList.Attributes = attrs + SynAttributeList.Range = range0 + } + + SynComponentInfo.SynComponentInfo ((attrs :: oldAttrs), typars, constraints, name, doc, postfix, acc, range) diff --git a/WoofWare.Whippet.Fantomas/SynConst.fs b/WoofWare.Whippet.Fantomas/SynConst.fs new file mode 100644 index 0000000..48f50f7 --- /dev/null +++ b/WoofWare.Whippet.Fantomas/SynConst.fs @@ -0,0 +1,12 @@ +namespace WoofWare.Whippet.Fantomas + +open Fantomas.FCS.Syntax +open Fantomas.FCS.Text.Range + +/// Extension methods for creating SynConst AST objects (which represent literal constants such as `3` or `"hi"`). +[] +module SynConstExt = + type SynConst with + /// Create the constant string with this value. + static member Create (s : string) : SynConst = + SynConst.String (s, SynStringKind.Regular, range0) diff --git a/WoofWare.Whippet.Fantomas/SynExpr.fs b/WoofWare.Whippet.Fantomas/SynExpr.fs new file mode 100644 index 0000000..1716c4f --- /dev/null +++ b/WoofWare.Whippet.Fantomas/SynExpr.fs @@ -0,0 +1,415 @@ +namespace WoofWare.Whippet.Fantomas + +open Fantomas.FCS.Syntax +open Fantomas.FCS.SyntaxTrivia +open Fantomas.FCS.Text.Range + +/// Extension methods to hold `SynExpr.CreateConst`. +[] +module SynExprExtensions = + type SynExpr with + /// Create the constant expression which is the given string. + /// For example, create the F# expression `"hi"` by calling `SynExpr.CreateConst "hi"`. + static member CreateConst (s : string) : SynExpr = + SynExpr.Const (SynConst.Create s, range0) + + /// Create the constant unit expression. + static member CreateConst () : SynExpr = SynExpr.Const (SynConst.Unit, range0) + + /// Create the constant expression which is the given bool. + /// For example, create the F# expression `false` by calling `SynExpr.CreateConst false`. + static member CreateConst (b : bool) : SynExpr = SynExpr.Const (SynConst.Bool b, range0) + + /// Create the constant expression which is the given char. + /// For example, create the F# expression `'c'` by calling `SynExpr.CreateConst 'c'`. + /// + /// There appears to be a bug in Fantomas in at least one version, causing incorrect formatting when this is + /// done naively; so this function instead gives you `(char {appropriate-integer})`. + static member CreateConst (c : char) : SynExpr = + // apparent Fantomas bug: `IndexOf '?'` gets formatted as `IndexOf ?` which is clearly wrong + SynExpr.App ( + ExprAtomicFlag.NonAtomic, + false, + SynExpr.Ident (Ident.create "char"), + SynExpr.CreateConst (int c), + range0 + ) + |> fun e -> SynExpr.Paren (e, range0, Some range0, range0) + + /// Create the constant expression which is the given int. + /// For example, create the F# expression `3` by calling `SynExpr.CreateConst 3`. + static member CreateConst (i : int32) : SynExpr = + SynExpr.Const (SynConst.Int32 i, range0) + +/// Methods for manipulating `SynExpr`, which represents an F# expression. +[] +module SynExpr = + + /// {f} {x} + let applyFunction (f : SynExpr) (x : SynExpr) : SynExpr = + SynExpr.App (ExprAtomicFlag.NonAtomic, false, f, x, range0) + + /// {f} {x} + let inline applyTo (x : SynExpr) (f : SynExpr) : SynExpr = applyFunction f x + + let inline private createAppInfix (f : SynExpr) (x : SynExpr) = + SynExpr.App (ExprAtomicFlag.NonAtomic, true, f, x, range0) + + /// An expression which is just "this name". + /// For example, `Foo.Blah`. + let inline createLongIdent'' (ident : SynLongIdent) : SynExpr = + SynExpr.LongIdent (false, ident, None, range0) + + /// An expression which is just "this name". + /// For example, `Foo.Blah`. + let inline createLongIdent' (ident : Ident list) : SynExpr = + createLongIdent'' (SynLongIdent.create ident) + + /// An expression which is just "this name". + /// For example, `Foo.Blah`. + let inline createLongIdent (ident : string list) : SynExpr = + createLongIdent' (ident |> List.map Ident.create) + + /// {expr} |> {func} + let pipeThroughFunction (func : SynExpr) (expr : SynExpr) : SynExpr = + createAppInfix (createLongIdent'' SynLongIdent.pipe) expr |> applyTo func + + /// if {cond} then {trueBranch} else {falseBranch} + /// Note that this function puts the trueBranch last, for pipelining convenience: + /// we assume that the `else` branch is more like an error case and is less interesting. + let ifThenElse (cond : SynExpr) (falseBranch : SynExpr) (trueBranch : SynExpr) : SynExpr = + SynExpr.IfThenElse ( + cond, + trueBranch, + Some falseBranch, + DebugPointAtBinding.Yes range0, + false, + range0, + { + IfKeyword = range0 + IsElif = false + ThenKeyword = range0 + ElseKeyword = Some range0 + IfToThenRange = range0 + } + ) + + /// try {body} with | {exc} as exc -> {handler} + let pipeThroughTryWith (exc : SynPat) (handler : SynExpr) (body : SynExpr) : SynExpr = + let clause = + SynMatchClause.create (SynPat.As (exc, SynPat.named "exc", range0)) handler + + SynExpr.TryWith ( + body, + [ clause ], + range0, + DebugPointAtTry.Yes range0, + DebugPointAtWith.Yes range0, + { + TryKeyword = range0 + TryToWithRange = range0 + WithKeyword = range0 + WithToEndRange = range0 + } + ) + + /// {a} = {b} + let equals (a : SynExpr) (b : SynExpr) = + createAppInfix (createLongIdent'' SynLongIdent.eq) a |> applyTo b + + /// {a} && {b} + let booleanAnd (a : SynExpr) (b : SynExpr) = + createAppInfix (createLongIdent'' SynLongIdent.booleanAnd) a |> applyTo b + + /// {a} || {b} + let booleanOr (a : SynExpr) (b : SynExpr) = + createAppInfix (createLongIdent'' SynLongIdent.booleanOr) a |> applyTo b + + /// {a} + {b} + let plus (a : SynExpr) (b : SynExpr) = + createAppInfix (createLongIdent'' SynLongIdent.plus) a |> applyTo b + + /// {a} * {b} + let times (a : SynExpr) (b : SynExpr) = + createAppInfix (createLongIdent'' SynLongIdent.times) a |> applyTo b + + /// Strip all outer parentheses from the expression: `((e))` -> `e`. + let rec stripOptionalParen (expr : SynExpr) : SynExpr = + match expr with + | SynExpr.Paren (expr, _, _, _) -> stripOptionalParen expr + | expr -> expr + + /// {obj}.{field} + let dotGet (field : string) (obj : SynExpr) : SynExpr = + SynExpr.DotGet ( + obj, + range0, + SynLongIdent.SynLongIdent (id = [ Ident.create field ], dotRanges = [], trivia = [ None ]), + range0 + ) + + /// {obj}.{meth} {arg} + let callMethodArg (meth : string) (arg : SynExpr) (obj : SynExpr) : SynExpr = dotGet meth obj |> applyTo arg + + /// {obj}.{meth}() + let callMethod (meth : string) (obj : SynExpr) : SynExpr = + callMethodArg meth (SynExpr.CreateConst ()) obj + + /// `{operand}<{types}>` + let typeApp (types : SynType list) (operand : SynExpr) = + SynExpr.TypeApp (operand, range0, types, List.replicate (types.Length - 1) range0, Some range0, range0, range0) + + /// {obj}.{meth}() + let callGenericMethod (meth : SynLongIdent) (types : SynType list) (obj : SynExpr) : SynExpr = + SynExpr.DotGet (obj, range0, meth, range0) + |> typeApp types + |> applyTo (SynExpr.CreateConst ()) + + /// {obj}.{meth}() + let callGenericMethod' (meth : string) (ty : string) (obj : SynExpr) : SynExpr = + callGenericMethod (SynLongIdent.createS meth) [ SynType.createLongIdent' [ ty ] ] obj + + /// {obj}.[{property}] + let inline index (property : SynExpr) (obj : SynExpr) : SynExpr = + SynExpr.DotIndexedGet (obj, property, range0, range0) + + /// `{arr}.[{start} .. {end}]` + /// + /// You can set either the start or end to None to omit them from the range. + let inline arrayIndexRange (start : SynExpr option) (endRange : SynExpr option) (arr : SynExpr) : SynExpr = + SynExpr.DotIndexedGet ( + arr, + (SynExpr.IndexRange (start, range0, endRange, range0, range0, range0)), + range0, + range0 + ) + + /// Wraps the expression in parentheses: `({e})` + let inline paren (e : SynExpr) : SynExpr = + SynExpr.Paren (e, range0, Some range0, range0) + + /// (fun {varName} -> {body}) + let createLambda (varName : string) (body : SynExpr) : SynExpr = + let parsedDataPat = [ SynPat.named varName ] + + SynExpr.Lambda ( + false, + false, + SynSimplePats.create [ SynSimplePat.createId (Ident.create varName) ], + body, + Some (parsedDataPat, body), + range0, + { + ArrowRange = Some range0 + } + ) + |> paren + + /// `(fun () -> {body})` + let createThunk (body : SynExpr) : SynExpr = + SynExpr.Lambda ( + false, + false, + SynSimplePats.create [], + body, + Some ([ SynPat.unit ], body), + range0, + { + ArrowRange = Some range0 + } + ) + |> paren + + /// Just the plain expression `s`, referring to a variable. + let inline createIdent (s : string) : SynExpr = SynExpr.Ident (Ident (s, range0)) + + /// Just the plain expression `x`, referring to a variable. + let inline createIdent' (i : Ident) : SynExpr = SynExpr.Ident i + + /// `{arg1}, {arg2}, ...` + let tupleNoParen (args : SynExpr list) : SynExpr = + SynExpr.Tuple (false, args, List.replicate (args.Length - 1) range0, range0) + + /// `({arg1}, {arg2}, ...)` + let inline tuple (args : SynExpr list) = args |> tupleNoParen |> paren + + /// {body} |> fun a -> Async.StartAsTask (a, ?cancellationToken=ct) + let startAsTask (ct : Ident) (body : SynExpr) = + let lambda = + [ + createIdent "a" + equals + (SynExpr.LongIdent (true, SynLongIdent.createS "cancellationToken", None, range0)) + (createIdent' ct) + ] + |> tuple + |> applyFunction (createLongIdent [ "Async" ; "StartAsTask" ]) + |> createLambda "a" + + pipeThroughFunction lambda body + + /// `for {pat} in {enumExpr} do {body}` + let inline createForEach (pat : SynPat) (enumExpr : SynExpr) (body : SynExpr) : SynExpr = + SynExpr.ForEach ( + DebugPointAtFor.No, + DebugPointAtInOrTo.No, + SeqExprOnly.SeqExprOnly false, + true, + pat, + enumExpr, + body, + range0 + ) + + /// `let {binding1 = binding1}; let {binding2 = binding2}; {body} + let inline createLet (bindings : SynBinding list) (body : SynExpr) : SynExpr = + SynExpr.LetOrUse (false, false, bindings, body, range0, SynExprLetOrUseTrivia.empty) + + /// `do {body}` + let inline createDo (body : SynExpr) : SynExpr = SynExpr.Do (body, range0) + + /// `match {matchOn} with {cases}` + let inline createMatch (matchOn : SynExpr) (cases : SynMatchClause list) : SynExpr = + SynExpr.Match ( + DebugPointAtBinding.Yes range0, + matchOn, + cases, + range0, + { + MatchKeyword = range0 + WithKeyword = range0 + } + ) + + /// `{expr} : {ty}` + let typeAnnotate (ty : SynType) (expr : SynExpr) : SynExpr = SynExpr.Typed (expr, ty, range0) + + /// `new {ty} ({args})` + let inline createNew (ty : SynType) (args : SynExpr) : SynExpr = + SynExpr.New (false, ty, paren args, range0) + + /// `while {cond} do {body}` + let inline createWhile (cond : SynExpr) (body : SynExpr) : SynExpr = + SynExpr.While (DebugPointAtWhile.Yes range0, cond, body, range0) + + /// `null` + let inline createNull () : SynExpr = SynExpr.Null range0 + + /// `reraise ()` + let reraise : SynExpr = createIdent "reraise" |> applyTo (SynExpr.CreateConst ()) + + /// Semantically this is: + /// {elt1} ; {elt2} ; ... + /// except we probably end up formatting without the semicolons. + let sequential (exprs : SynExpr list) : SynExpr = + exprs + |> List.reduce (fun a b -> SynExpr.Sequential (DebugPointAtSequential.SuppressNeither, false, a, b, range0)) + + /// [ {elt1} ; {elt2} ; ... ] + let listLiteral (elts : SynExpr list) : SynExpr = + SynExpr.ArrayOrListComputed (false, sequential elts, range0) + + /// [| {elt1} ; {elt2} ; ... |] + let arrayLiteral (elts : SynExpr list) : SynExpr = + SynExpr.ArrayOrListComputed (true, sequential elts, range0) + + /// {compExpr} { {lets} ; return {ret} } + let createCompExpr (compExpr : string) (retBody : SynExpr) (lets : CompExprBinding list) : SynExpr = + let retStatement = SynExpr.YieldOrReturn ((false, true), retBody, range0) + + let contents : SynExpr = + (retStatement, List.rev lets) + ||> List.fold (fun state binding -> + match binding with + | LetBang (lhs, rhs) -> + SynExpr.LetOrUseBang ( + DebugPointAtBinding.Yes range0, + false, + true, + SynPat.named lhs, + rhs, + [], + state, + range0, + { + EqualsRange = Some range0 + } + ) + | Let (lhs, rhs) -> createLet [ SynBinding.basic [ Ident.create lhs ] [] rhs ] state + | Use (lhs, rhs) -> + SynExpr.LetOrUse ( + false, + true, + [ SynBinding.basic [ Ident.create lhs ] [] rhs ], + state, + range0, + { + SynExprLetOrUseTrivia.InKeyword = None + } + ) + | Do body -> sequential [ SynExpr.Do (body, range0) ; state ] + ) + + applyFunction (createIdent compExpr) (SynExpr.ComputationExpr (false, contents, range0)) + + /// {expr} |> Async.AwaitTask + let awaitTask (expr : SynExpr) : SynExpr = + expr |> pipeThroughFunction (createLongIdent [ "Async" ; "AwaitTask" ]) + + /// {ident}.ToString () + /// with special casing for some types like DateTime + let toString (ty : SynType) (ident : SynExpr) = + match ty with + | DateOnly -> ident |> callMethodArg "ToString" (SynExpr.CreateConst "yyyy-MM-dd") + | DateTime -> ident |> callMethodArg "ToString" (SynExpr.CreateConst "yyyy-MM-ddTHH:mm:ss") + | _ -> callMethod "ToString" ident + + /// {e} :> {ty} + let upcast' (ty : SynType) (e : SynExpr) = SynExpr.Upcast (e, ty, range0) + + /// {ident} - {rhs} + let minus (ident : SynLongIdent) (rhs : SynExpr) : SynExpr = + createAppInfix (createLongIdent'' SynLongIdent.sub) (createLongIdent'' ident) + |> applyTo rhs + + /// {ident} - {n} + let minusN (ident : SynLongIdent) (n : int) : SynExpr = minus ident (SynExpr.CreateConst n) + + /// {y} > {x} + let greaterThan (x : SynExpr) (y : SynExpr) : SynExpr = + createAppInfix (createLongIdent'' SynLongIdent.gt) y |> applyTo x + + /// {y} < {x} + let lessThan (x : SynExpr) (y : SynExpr) : SynExpr = + createAppInfix (createLongIdent'' SynLongIdent.lt) y |> applyTo x + + /// {y} >= {x} + let greaterThanOrEqual (x : SynExpr) (y : SynExpr) : SynExpr = + createAppInfix (createLongIdent'' SynLongIdent.geq) y |> applyTo x + + /// {y} <= {x} + let lessThanOrEqual (x : SynExpr) (y : SynExpr) : SynExpr = + createAppInfix (createLongIdent'' SynLongIdent.leq) y |> applyTo x + + /// {x} :: {y} + let listCons (x : SynExpr) (y : SynExpr) : SynExpr = + createAppInfix + (SynExpr.LongIdent ( + false, + SynLongIdent.SynLongIdent ( + [ Ident.create "op_ColonColon" ], + [], + [ Some (IdentTrivia.OriginalNotation "::") ] + ), + None, + range0 + )) + (tupleNoParen [ x ; y ]) + |> paren + + /// `{lhs} <- {rhs}` + let assign (lhs : SynLongIdent) (rhs : SynExpr) : SynExpr = SynExpr.LongIdentSet (lhs, rhs, range0) + + /// `{lhs}.[{index}] <- {rhs}` + let assignIndex (lhs : SynExpr) (index : SynExpr) (rhs : SynExpr) : SynExpr = + SynExpr.DotIndexedSet (lhs, index, rhs, range0, range0, range0) diff --git a/WoofWare.Whippet.Fantomas/SynExprLetOrUseTrivia.fs b/WoofWare.Whippet.Fantomas/SynExprLetOrUseTrivia.fs new file mode 100644 index 0000000..c977000 --- /dev/null +++ b/WoofWare.Whippet.Fantomas/SynExprLetOrUseTrivia.fs @@ -0,0 +1,13 @@ +namespace WoofWare.Whippet.Fantomas + +open Fantomas.FCS.SyntaxTrivia + +/// Methods for manipulating SynExprLetOrUseTrivia, which gives non-semantically-relevant information about the +/// formatting of a `let` or `use` binding. +[] +module SynExprLetOrUseTrivia = + /// Specify that this is just a plain `let` or `use` binding. You basically always want this. + let empty : SynExprLetOrUseTrivia = + { + InKeyword = None + } diff --git a/WoofWare.Whippet.Fantomas/SynField.fs b/WoofWare.Whippet.Fantomas/SynField.fs new file mode 100644 index 0000000..4c927d1 --- /dev/null +++ b/WoofWare.Whippet.Fantomas/SynField.fs @@ -0,0 +1,80 @@ +namespace WoofWare.Whippet.Fantomas + +open Fantomas.FCS.Text.Range +open Fantomas.FCS.Syntax +open Fantomas.FCS.SyntaxTrivia +open Fantomas.FCS.Xml + +/// The data needed to reconstitute a single piece of data within a union field, or a single record field. +/// This is generic on whether the field is identified. For example, in `type Foo = Blah of int`, the `int` +/// field is not identified; whereas in `type Foo = Blah of baz : int`, it is identified. +type SynFieldData<'Ident> = + { + /// Attributes on this field. I think you can only get these if this is a *record* field. + Attrs : SynAttribute list + /// The identifier of this field (see docstring for SynFieldData). + Ident : 'Ident + /// The type of the data contained in this field. For example, `type Foo = { Blah : int }` + /// has this being `int`. + Type : SynType + } + +/// Methods for manipulating `SynField`, which represents a field of a record or union. +[] +module SynField = + /// Get the useful information out of a SynField. + let extract (SynField (attrs, _, id, fieldType, _, _, _, _, _)) : SynFieldData = + { + Attrs = attrs |> List.collect (fun l -> l.Attributes) + Ident = id + Type = fieldType + } + + /// Functorial `map`. + let mapIdent<'a, 'b> (f : 'a -> 'b) (x : SynFieldData<'a>) : SynFieldData<'b> = + let ident = f x.Ident + + { + Attrs = x.Attrs + Ident = ident + Type = x.Type + } + + /// Convert a `SynField` into our structured `SynFieldData` type. Throws if the field has no identifier. + let extractWithIdent (f : SynField) : SynFieldData = + f + |> extract + |> mapIdent (fun ident -> + match ident with + | None -> failwith "expected field identifier to have a value, but it did not" + | Some i -> i + ) + + /// Convert our structured `SynFieldData` type into an AST `SynField`. + let make (data : SynFieldData) : SynField = + let attrs : SynAttributeList list = + data.Attrs + |> List.map (fun l -> + { + Attributes = [ l ] + Range = range0 + } + ) + + SynField.SynField ( + attrs, + false, + data.Ident, + data.Type, + false, + PreXmlDoc.Empty, + None, + range0, + SynFieldTrivia.Zero + ) + + /// Set the docstring of this `SynField`. + let withDocString (doc : PreXmlDoc) (f : SynField) : SynField = + match f with + | SynField (attributes, isStatic, idOpt, fieldType, isMutable, _, accessibility, range, trivia) -> + SynField (attributes, isStatic, idOpt, fieldType, isMutable, doc, accessibility, range, trivia) diff --git a/WoofWare.Whippet.Fantomas/SynIdent.fs b/WoofWare.Whippet.Fantomas/SynIdent.fs new file mode 100644 index 0000000..9459493 --- /dev/null +++ b/WoofWare.Whippet.Fantomas/SynIdent.fs @@ -0,0 +1,14 @@ +namespace WoofWare.Whippet.Fantomas + +open Fantomas.FCS.Syntax + +/// Methods for manipulating the SynIdent AST type. This is basically an Ident, but specifically one which is appearing +/// in the AST at some position. +[] +module SynIdent = + /// Create a SynIdent from an Ident. + let inline createI (i : Ident) : SynIdent = SynIdent.SynIdent (i, None) + + /// Create a SynIdent from a string (which we'll convert into an Ident for you en route). + let inline createS (i : string) : SynIdent = + SynIdent.SynIdent (Ident.create i, None) diff --git a/WoofWare.Whippet.Fantomas/SynLongIdent.fs b/WoofWare.Whippet.Fantomas/SynLongIdent.fs new file mode 100644 index 0000000..90932d5 --- /dev/null +++ b/WoofWare.Whippet.Fantomas/SynLongIdent.fs @@ -0,0 +1,175 @@ +namespace WoofWare.Whippet.Fantomas + +open Fantomas.FCS.SyntaxTrivia +open Fantomas.FCS.Text.Range +open Fantomas.FCS.Syntax + +/// Methods for manipulating the SynLongIdent type. This is a LongIdent, but specifically one which is appearing in the +/// AST somewhere. +[] +module SynLongIdent = + + /// The ">=" identifier. + let geq = + SynLongIdent.SynLongIdent ( + [ Ident.create "op_GreaterThanOrEqual" ], + [], + [ Some (IdentTrivia.OriginalNotation ">=") ] + ) + + /// The "<=" identifier. + let leq = + SynLongIdent.SynLongIdent ( + [ Ident.create "op_LessThanOrEqual" ], + [], + [ Some (IdentTrivia.OriginalNotation "<=") ] + ) + + /// The ">" identifier. + let gt = + SynLongIdent.SynLongIdent ([ Ident.create "op_GreaterThan" ], [], [ Some (IdentTrivia.OriginalNotation ">") ]) + + /// The "<" identifier. + let lt = + SynLongIdent.SynLongIdent ([ Ident.create "op_LessThan" ], [], [ Some (IdentTrivia.OriginalNotation "<") ]) + + /// The "-" identifier. + let sub = + SynLongIdent.SynLongIdent ([ Ident.create "op_Subtraction" ], [], [ Some (IdentTrivia.OriginalNotation "-") ]) + + /// The "=" identifier. + let eq = + SynLongIdent.SynLongIdent ([ Ident.create "op_Equality" ], [], [ Some (IdentTrivia.OriginalNotation "=") ]) + + /// The "&&" identifier. + let booleanAnd = + SynLongIdent.SynLongIdent ([ Ident.create "op_BooleanAnd" ], [], [ Some (IdentTrivia.OriginalNotation "&&") ]) + + /// The "||" identifier. + let booleanOr = + SynLongIdent.SynLongIdent ([ Ident.create "op_BooleanOr" ], [], [ Some (IdentTrivia.OriginalNotation "||") ]) + + /// The "+" identifier. + let plus = + SynLongIdent.SynLongIdent ([ Ident.create "op_Addition" ], [], [ Some (IdentTrivia.OriginalNotation "+") ]) + + /// The "*" identifier. + let times = + SynLongIdent.SynLongIdent ([ Ident.create "op_Multiply" ], [], [ Some (IdentTrivia.OriginalNotation "*") ]) + + /// The "|>" identifier. + let pipe = + SynLongIdent.SynLongIdent ([ Ident.create "op_PipeRight" ], [], [ Some (IdentTrivia.OriginalNotation "|>") ]) + + /// Convert this SynLongIdent into a human-readable string for display. + /// This is not quite round-trippable, but it should be round-trippable if your identifiers are not pathological + /// (e.g. you should not have a full stop in your identifiers; that's the job of LongIdent, not Ident). + let toString (sli : SynLongIdent) : string = + sli.LongIdent |> List.map _.idText |> String.concat "." + + /// Build a SynLongIdent from a LongIdent (by attaching dummy location information to it). + let create (ident : LongIdent) : SynLongIdent = + let commas = + match ident with + | [] -> [] + | _ :: commas -> commas |> List.map (fun _ -> range0) + + SynLongIdent.SynLongIdent (ident, commas, List.replicate ident.Length None) + + /// Build a SynLongIdent from an Ident. Please don't put full stops in the argument, I don't know what will happen; + /// use `create` if you have multiple components. + let inline createI (i : Ident) : SynLongIdent = create [ i ] + + /// Build a SynLongIdent from a string which is expected to be "basically an Ident" - no full stops, for example. + /// Use `createS'` if you have multiple components. + let inline createS (s : string) : SynLongIdent = createI (Ident (s, range0)) + + /// Build a SynLongIdent from its components. + let inline createS' (s : string list) : SynLongIdent = + create (s |> List.map (fun i -> Ident (i, range0))) + + /// Determine whether this ident is the identifier of the `unit` type. + let isUnit (ident : SynLongIdent) : bool = + match ident.LongIdent with + | [ i ] when System.String.Equals (i.idText, "unit", System.StringComparison.OrdinalIgnoreCase) -> true + | _ -> false + + /// Determine whether this ident is the identifier of the `list` (F# list) type. + let isList (ident : SynLongIdent) : bool = + match ident.LongIdent with + | [ i ] when System.String.Equals (i.idText, "list", System.StringComparison.OrdinalIgnoreCase) -> true + // TODO: consider FSharpList or whatever it is + | _ -> false + + /// Determine whether this ident is an identifier of the array type. + let isArray (ident : SynLongIdent) : bool = + match ident.LongIdent with + | [ i ] when + System.String.Equals (i.idText, "array", System.StringComparison.OrdinalIgnoreCase) + || System.String.Equals (i.idText, "[]", System.StringComparison.Ordinal) + -> + true + | _ -> false + + /// Determine whether this ident is an identifier of the F# option type. + let isOption (ident : SynLongIdent) : bool = + match ident.LongIdent with + | [ i ] when System.String.Equals (i.idText, "option", System.StringComparison.OrdinalIgnoreCase) -> true + // TODO: consider Microsoft.FSharp.Option or whatever it is + | _ -> false + + /// Determine whether this ident is an identifier of the F# Choice type. + let isChoice (ident : SynLongIdent) : bool = + match ident.LongIdent with + | [ i ] when System.String.Equals (i.idText, "Choice", System.StringComparison.Ordinal) -> true + // TODO: consider Microsoft.FSharp.Choice or whatever it is + | _ -> false + + /// Determine whether this ident is an identifier of the System.Nullable type. + let isNullable (ident : SynLongIdent) : bool = + match ident.LongIdent |> List.map _.idText with + | [ "System" ; "Nullable" ] + | [ "Nullable" ] -> true + | _ -> false + + /// Determine whether this ident is an identifier of the RestEase.Response type. + let isResponse (ident : SynLongIdent) : bool = + match ident.LongIdent |> List.map _.idText with + | [ "Response" ] + | [ "RestEase" ; "Response" ] -> true + | _ -> false + + /// Determine whether this ident is an identifier of the F# Map type. + let isMap (ident : SynLongIdent) : bool = + match ident.LongIdent |> List.map _.idText with + | [ "Map" ] -> true + | _ -> false + + /// Determine whether this ident is an identifier of the System.Collections.Generic.IReadOnlyDictionary type. + /// This is purely syntactic: it will not say that e.g. the Dictionary type is an IReadOnlyDictionary. + let isReadOnlyDictionary (ident : SynLongIdent) : bool = + match ident.LongIdent |> List.map _.idText with + | [ "IReadOnlyDictionary" ] + | [ "Generic" ; "IReadOnlyDictionary" ] + | [ "Collections" ; "Generic" ; "IReadOnlyDictionary" ] + | [ "System" ; "Collections" ; "Generic" ; "IReadOnlyDictionary" ] -> true + | _ -> false + + /// Determine whether this ident is an identifier of the System.Collections.Generic.Dictionary type. + let isDictionary (ident : SynLongIdent) : bool = + match ident.LongIdent |> List.map _.idText with + | [ "Dictionary" ] + | [ "Generic" ; "Dictionary" ] + | [ "Collections" ; "Generic" ; "Dictionary" ] + | [ "System" ; "Collections" ; "Generic" ; "Dictionary" ] -> true + | _ -> false + + /// Determine whether this ident is an identifier of the System.Collections.Generic.IDictionary type. + /// This is purely syntactic: it will not say that e.g. the Dictionary type is an IDictionary. + let isIDictionary (ident : SynLongIdent) : bool = + match ident.LongIdent |> List.map _.idText with + | [ "IDictionary" ] + | [ "Generic" ; "IDictionary" ] + | [ "Collections" ; "Generic" ; "IDictionary" ] + | [ "System" ; "Collections" ; "Generic" ; "IDictionary" ] -> true + | _ -> false diff --git a/WoofWare.Whippet.Fantomas/SynMatchClause.fs b/WoofWare.Whippet.Fantomas/SynMatchClause.fs new file mode 100644 index 0000000..b2ccd94 --- /dev/null +++ b/WoofWare.Whippet.Fantomas/SynMatchClause.fs @@ -0,0 +1,28 @@ +namespace WoofWare.Whippet.Fantomas + +open Fantomas.FCS.Syntax +open Fantomas.FCS.Text.Range + +/// Methods for manipulating SynMatchClause, a type representing a single arm of a `match` pattern match. +/// For example, the entire line of code `| Foo (a, b) -> bar` is represented by one of these. +[] +module SynMatchClause = + /// Represents the `match` arm `| {lhs} -> {rhs}`. + let create (lhs : SynPat) (rhs : SynExpr) : SynMatchClause = + SynMatchClause.SynMatchClause ( + lhs, + None, + rhs, + range0, + DebugPointAtTarget.Yes, + { + ArrowRange = Some range0 + BarRange = Some range0 + } + ) + + /// Replace the `where` clause (or add a new one, if there isn't one already) in this `match` arm. + let withWhere (where : SynExpr) (m : SynMatchClause) : SynMatchClause = + match m with + | SynMatchClause (synPat, _, resultExpr, range, debugPointAtTarget, synMatchClauseTrivia) -> + SynMatchClause (synPat, Some where, resultExpr, range, debugPointAtTarget, synMatchClauseTrivia) diff --git a/WoofWare.Whippet.Fantomas/SynMemberDefn.fs b/WoofWare.Whippet.Fantomas/SynMemberDefn.fs new file mode 100644 index 0000000..0ef415e --- /dev/null +++ b/WoofWare.Whippet.Fantomas/SynMemberDefn.fs @@ -0,0 +1,78 @@ +namespace WoofWare.Whippet.Fantomas + +open Fantomas.FCS.Syntax +open Fantomas.FCS.SyntaxTrivia +open Fantomas.FCS.Text.Range +open Fantomas.FCS.Xml + +/// Methods for manipulating SynMemberDefn, which specifies interface members and other such fields in a type declaration. +[] +module SynMemberDefn = + let private interfaceMemberSlotFlags = + { + SynMemberFlags.IsInstance = true + SynMemberFlags.IsDispatchSlot = true + SynMemberFlags.IsOverrideOrExplicitImpl = false + SynMemberFlags.IsFinal = false + SynMemberFlags.GetterOrSetterIsCompilerGenerated = false + SynMemberFlags.MemberKind = SynMemberKind.Member + } + + /// An `abstract Foo : blah`. + /// You specify the shape of any arguments via the input `arity`. + let abstractMember + (attrs : SynAttribute list) + (ident : SynIdent) + (typars : SynTyparDecls option) + (arity : SynValInfo) + (xmlDoc : PreXmlDoc) + (returnType : SynType) + : SynMemberDefn + = + let slot = + SynValSig.SynValSig ( + attrs + |> List.map (fun attr -> + { + Attributes = [ attr ] + Range = range0 + } + ), + ident, + SynValTyparDecls.SynValTyparDecls (typars, true), + returnType, + arity, + false, + false, + xmlDoc, + None, + None, + range0, + { + EqualsRange = None + WithKeyword = None + InlineKeyword = None + LeadingKeyword = SynLeadingKeyword.Abstract range0 + } + ) + + SynMemberDefn.AbstractSlot ( + slot, + interfaceMemberSlotFlags, + range0, + { + GetSetKeywords = None + } + ) + + /// `static member Foo = ...` + let staticMember (binding : SynBinding) : SynMemberDefn = + let binding = SynBinding.makeStaticMember binding + SynMemberDefn.Member (binding, range0) + + /// `member this.Foo = ...` + /// + /// You need to make sure the `this` is present in the name of the binding. + let memberImplementation (binding : SynBinding) : SynMemberDefn = + let binding = SynBinding.makeInstanceMember binding + SynMemberDefn.Member (binding, range0) diff --git a/WoofWare.Whippet.Fantomas/SynModuleDecl.fs b/WoofWare.Whippet.Fantomas/SynModuleDecl.fs new file mode 100644 index 0000000..4f42ce6 --- /dev/null +++ b/WoofWare.Whippet.Fantomas/SynModuleDecl.fs @@ -0,0 +1,37 @@ +namespace WoofWare.Whippet.Fantomas + +open Fantomas.FCS.Syntax +open Fantomas.FCS.SyntaxTrivia +open Fantomas.FCS.Text.Range + +/// Methods for manipulating `SynModuleDecl`, the type representing a single definition within a module. +/// For example, an `open` statement, a `let` binding, a type definition, etc. +[] +module SynModuleDecl = + + /// Open this target (e.g. `open MyModule` or `open MyNamespace`). + let inline openAny (ident : SynOpenDeclTarget) : SynModuleDecl = SynModuleDecl.Open (ident, range0) + + /// Add consecutive `let`-bindings, non-recursive. + let inline createLets (bindings : SynBinding list) : SynModuleDecl = + SynModuleDecl.Let (false, bindings, range0) + + /// Add a single `let`-binding. (Use `createLets` for *multiple* bindings.) + let inline createLet (binding : SynBinding) : SynModuleDecl = createLets [ binding ] + + /// Add type definitions. + let inline createTypes (tys : SynTypeDefn list) : SynModuleDecl = SynModuleDecl.Types (tys, range0) + + /// Add a nested module definition. + let nestedModule (info : SynComponentInfo) (decls : SynModuleDecl list) : SynModuleDecl = + SynModuleDecl.NestedModule ( + info, + false, + decls, + false, + range0, + { + ModuleKeyword = Some range0 + EqualsRange = Some range0 + } + ) diff --git a/WoofWare.Whippet.Fantomas/SynModuleOrNamespace.fs b/WoofWare.Whippet.Fantomas/SynModuleOrNamespace.fs new file mode 100644 index 0000000..1c714ee --- /dev/null +++ b/WoofWare.Whippet.Fantomas/SynModuleOrNamespace.fs @@ -0,0 +1,26 @@ +namespace WoofWare.Whippet.Fantomas + +open Fantomas.FCS.Syntax +open Fantomas.FCS.SyntaxTrivia +open Fantomas.FCS.Xml +open Fantomas.FCS.Text.Range + +/// Methods for manipulating SynModuleOrNamespace, which defines a module or namespace definition. +[] +module SynModuleOrNamespace = + + /// Create a namespace with the given name and with the given declarations inside it. + let createNamespace (name : LongIdent) (decls : SynModuleDecl list) : SynModuleOrNamespace = + SynModuleOrNamespace.SynModuleOrNamespace ( + name, + false, + SynModuleOrNamespaceKind.DeclaredNamespace, + decls, + PreXmlDoc.Empty, + [], + None, + range0, + { + LeadingKeyword = SynModuleOrNamespaceLeadingKeyword.Namespace range0 + } + ) diff --git a/WoofWare.Whippet.Fantomas/SynPat.fs b/WoofWare.Whippet.Fantomas/SynPat.fs new file mode 100644 index 0000000..181a7ad --- /dev/null +++ b/WoofWare.Whippet.Fantomas/SynPat.fs @@ -0,0 +1,73 @@ +namespace WoofWare.Whippet.Fantomas + +open Fantomas.FCS.Syntax +open Fantomas.FCS.Text.Range + +/// Methods for manipulating `SynPat`, which represents a pattern being matched on in any context. +[] +module SynPat = + /// Wrap this pattern in parentheses. + let inline paren (pat : SynPat) : SynPat = SynPat.Paren (pat, range0) + + /// The anonymous pattern, `_`. + let anon : SynPat = SynPat.Wild range0 + + /// `{pat} : {ty}` + let inline annotateTypeNoParen (ty : SynType) (pat : SynPat) = SynPat.Typed (pat, ty, range0) + + /// `({pat} : {ty})` + let inline annotateType (ty : SynType) (pat : SynPat) = paren (annotateTypeNoParen ty pat) + + /// The pattern that is just the given name, `x` (e.g. in `match "hi" with | x -> ...`) + let inline named (s : string) : SynPat = + SynPat.Named (SynIdent.SynIdent (Ident (s, range0), None), false, None, range0) + + /// The pattern that is just the given name, `i` (e.g. in `match "hi" with | i -> ...`) + let inline namedI (i : Ident) : SynPat = + SynPat.Named (SynIdent.SynIdent (i, None), false, None, range0) + + /// The pattern that is the given name applied to these arguments, e.g. in `match "hi" with | Foo (a, b) -> ...` + let inline identWithArgs (i : LongIdent) (args : SynArgPats) : SynPat = + SynPat.LongIdent (SynLongIdent.create i, None, None, args, None, range0) + + /// The pattern that is the given name applied to these arguments, e.g. in `match "hi" with | Foo (a, b) -> ...` + let inline nameWithArgs (i : string) (args : SynPat list) : SynPat = + identWithArgs [ Ident.create i ] (SynArgPats.create args) + + /// The tuple pattern, `{el1}, {el2}, ...` + let inline tupleNoParen (elements : SynPat list) : SynPat = + match elements with + | [] -> failwith "Can't tuple no elements in a pattern" + | [ p ] -> p + | elements -> SynPat.Tuple (false, elements, List.replicate (elements.Length - 1) range0, range0) + + /// The tuple pattern, `({el1}, {el2}, ...)` + /// + /// Consider `tupleNoParen` if you don't want parentheses. + let inline tuple (elements : SynPat list) : SynPat = tupleNoParen elements |> paren + + /// A constant pattern, e.g. in `match foo with | "hi" -> ...` + let inline createConst (c : SynConst) = SynPat.Const (c, range0) + + /// The unit pattern, as in e.g. `let foo () = ...` + let unit = createConst SynConst.Unit + + /// The null pattern, as in e.g. `match foo with | null -> ...` + let createNull = SynPat.Null range0 + + /// The empty list pattern, as in e.g. `match foo with | [] -> ...` + let emptyList = SynPat.ArrayOrList (false, [], range0) + + /// The list cons pattern, as in e.g. `match foo with | lhs :: rhs -> ...` + let listCons (lhs : SynPat) (rhs : SynPat) = + SynPat.ListCons ( + lhs, + rhs, + range0, + { + ColonColonRange = range0 + } + ) + + /// The empty array pattern, as in e.g. `match foo with | [||] -> ...` + let emptyArray = SynPat.ArrayOrList (true, [], range0) diff --git a/WoofWare.Whippet.Fantomas/SynSimplePat.fs b/WoofWare.Whippet.Fantomas/SynSimplePat.fs new file mode 100644 index 0000000..b2f8b0a --- /dev/null +++ b/WoofWare.Whippet.Fantomas/SynSimplePat.fs @@ -0,0 +1,13 @@ +namespace WoofWare.Whippet.Fantomas + +open Fantomas.FCS.Syntax +open Fantomas.FCS.Text.Range + +/// Methods for manipulating the SynSimplePat AST type. This type represents the left-hand side of a pattern match, +/// e.g. the `x` in `fun x -> foo`. +[] +module SynSimplePat = + + /// Create a SynSimplePat that is a bare identifier, no type information, non-optional. + let createId (id : Ident) : SynSimplePat = + SynSimplePat.Id (id, None, false, false, false, range0) diff --git a/WoofWare.Whippet.Fantomas/SynSimplePats.fs b/WoofWare.Whippet.Fantomas/SynSimplePats.fs new file mode 100644 index 0000000..dbd31ac --- /dev/null +++ b/WoofWare.Whippet.Fantomas/SynSimplePats.fs @@ -0,0 +1,15 @@ +namespace WoofWare.Whippet.Fantomas + +open Fantomas.FCS.Syntax +open Fantomas.FCS.Text.Range + +/// Methods for manipulating the SynSimplePats AST type. This type represents a collection of SynSimplePat entries, +/// all tupled together (e.g. as in `fun (a, b : int) -> ...`). +[] +module SynSimplePats = + + /// Build a SynSimplePats by tupling together a bunch of individual patterns. + let create (pats : SynSimplePat list) : SynSimplePats = + match pats with + | [] -> SynSimplePats.SimplePats ([], [], range0) + | pats -> SynSimplePats.SimplePats (pats, List.replicate (pats.Length - 1) range0, range0) diff --git a/WoofWare.Whippet.Fantomas/SynType.fs b/WoofWare.Whippet.Fantomas/SynType.fs new file mode 100644 index 0000000..80006df --- /dev/null +++ b/WoofWare.Whippet.Fantomas/SynType.fs @@ -0,0 +1,624 @@ +namespace WoofWare.Whippet.Fantomas + +open System +open Fantomas.FCS.Syntax +open Fantomas.FCS.Text.Range + +/// Patterns to let you match on a `SynType` to discover whether it's one of a well-known variety. +[] +module SynTypePatterns = + /// An `option` type. You get access to the type argument of `option`. + let (|OptionType|_|) (fieldType : SynType) = + match fieldType with + | SynType.App (SynType.LongIdent ident, _, [ innerType ], _, _, _, _) when SynLongIdent.isOption ident -> + Some innerType + | _ -> None + + /// A `Choice` type. You get access to the type arguments; for example, a two-case `Choice` would match + /// `ChoiceType [PrimitiveType "System.Int32" ; PrimitiveType "System.String"]`. + let (|ChoiceType|_|) (fieldType : SynType) = + match fieldType with + | SynType.App (SynType.LongIdent ident, _, inner, _, _, _, _) when SynLongIdent.isChoice ident -> Some inner + | _ -> None + + /// A `System.Nullable` type. You get access to its type argument. + let (|NullableType|_|) (fieldType : SynType) = + match fieldType with + | SynType.App (SynType.LongIdent ident, _, [ innerType ], _, _, _, _) when SynLongIdent.isNullable ident -> + Some innerType + | _ -> None + + /// An F# list type. You get access to its type argument. + let (|ListType|_|) (fieldType : SynType) = + match fieldType with + | SynType.App (SynType.LongIdent ident, _, [ innerType ], _, _, _, _) when SynLongIdent.isList ident -> + Some innerType + | _ -> None + + /// An array type. You get access to its type argument. + let (|ArrayType|_|) (fieldType : SynType) = + match fieldType with + | SynType.App (SynType.LongIdent ident, _, [ innerType ], _, _, _, _) when SynLongIdent.isArray ident -> + Some innerType + | SynType.Array (1, innerType, _) -> Some innerType + | _ -> None + + /// The `RestEase.Response` type. + let (|RestEaseResponseType|_|) (fieldType : SynType) = + match fieldType with + | SynType.App (SynType.LongIdent ident, _, [ innerType ], _, _, _, _) when SynLongIdent.isResponse ident -> + Some innerType + | _ -> None + + /// A System.Collections.Generic.Dictionary<_,_> type. You get access to its key and value argument types. + let (|DictionaryType|_|) (fieldType : SynType) = + match fieldType with + | SynType.App (SynType.LongIdent ident, _, [ key ; value ], _, _, _, _) when SynLongIdent.isDictionary ident -> + Some (key, value) + | _ -> None + + /// A System.Collections.Generic.IDictionary<_,_> type. You get access to its key and value argument types. + /// Note that this is purely syntactic: a plain `Dictionary<_, _>` won't match this. + let (|IDictionaryType|_|) (fieldType : SynType) = + match fieldType with + | SynType.App (SynType.LongIdent ident, _, [ key ; value ], _, _, _, _) when SynLongIdent.isIDictionary ident -> + Some (key, value) + | _ -> None + + /// A System.Collections.Generic.IReadOnlyDictionary<_,_> type. You get access to its key and value argument types. + /// Note that this is purely syntactic: a plain `Dictionary<_, _>` won't match this. + let (|IReadOnlyDictionaryType|_|) (fieldType : SynType) = + match fieldType with + | SynType.App (SynType.LongIdent ident, _, [ key ; value ], _, _, _, _) when + SynLongIdent.isReadOnlyDictionary ident + -> + Some (key, value) + | _ -> None + + /// An F# Map<_, _> type. You get access to its key and value argument types. + let (|MapType|_|) (fieldType : SynType) = + match fieldType with + | SynType.App (SynType.LongIdent ident, _, [ key ; value ], _, _, _, _) when SynLongIdent.isMap ident -> + Some (key, value) + | _ -> None + + /// A System.Numerics.BigInteger type (which can be denoted `bigint`). + let (|BigInt|_|) (fieldType : SynType) : unit option = + match fieldType with + | SynType.LongIdent ident -> + match ident.LongIdent |> List.map _.idText with + | [ "bigint" ] + | [ "BigInteger" ] + | [ "Numerics" ; "BigInteger" ] + | [ "System" ; "Numerics" ; "BigInteger" ] -> Some () + | _ -> None + | _ -> None + + /// Returns the type, qualified as in e.g. `System.Boolean`. + let (|PrimitiveType|_|) (fieldType : SynType) : LongIdent option = + match fieldType with + | SynType.LongIdent ident -> + match ident.LongIdent with + | [ i ] -> Primitives.qualifyType i.idText + | _ -> None + | _ -> None + + /// The `string` type. + let (|String|_|) (fieldType : SynType) : unit option = + match fieldType with + | SynType.LongIdent ident -> + match ident.LongIdent with + | [ i ] -> + [ "string" ] + |> List.tryFind (fun s -> s = i.idText) + |> Option.map ignore + | _ -> None + | _ -> None + + /// The `byte` type. + let (|Byte|_|) (fieldType : SynType) : unit option = + match fieldType with + | SynType.LongIdent ident -> + match ident.LongIdent with + | [ i ] -> [ "byte" ] |> List.tryFind (fun s -> s = i.idText) |> Option.map ignore + | _ -> None + | _ -> None + + /// The `System.Guid` type. + let (|Guid|_|) (fieldType : SynType) : unit option = + match fieldType with + | SynType.LongIdent ident -> + match ident.LongIdent |> List.map (fun i -> i.idText) with + | [ "System" ; "Guid" ] + | [ "Guid" ] -> Some () + | _ -> None + | _ -> None + + /// The `System.Net.Http.HttpResponseMessage` type. + let (|HttpResponseMessage|_|) (fieldType : SynType) : unit option = + match fieldType with + | SynType.LongIdent ident -> + match ident.LongIdent |> List.map (fun i -> i.idText) with + | [ "System" ; "Net" ; "Http" ; "HttpResponseMessage" ] + | [ "Net" ; "Http" ; "HttpResponseMessage" ] + | [ "Http" ; "HttpResponseMessage" ] + | [ "HttpResponseMessage" ] -> Some () + | _ -> None + | _ -> None + + /// The `System.Net.Http.HttpContent` type. + let (|HttpContent|_|) (fieldType : SynType) : unit option = + match fieldType with + | SynType.LongIdent ident -> + match ident.LongIdent |> List.map (fun i -> i.idText) with + | [ "System" ; "Net" ; "Http" ; "HttpContent" ] + | [ "Net" ; "Http" ; "HttpContent" ] + | [ "Http" ; "HttpContent" ] + | [ "HttpContent" ] -> Some () + | _ -> None + | _ -> None + + /// The `System.IO.Stream` type. + let (|Stream|_|) (fieldType : SynType) : unit option = + match fieldType with + | SynType.LongIdent ident -> + match ident.LongIdent |> List.map (fun i -> i.idText) with + | [ "System" ; "IO" ; "Stream" ] + | [ "IO" ; "Stream" ] + | [ "Stream" ] -> Some () + | _ -> None + | _ -> None + + /// A numeric primitive type, like `byte` or `float` but not `char`. + /// You get access to the fully-qualified type name, like `System.Int32`. + let (|NumberType|_|) (fieldType : SynType) = + match fieldType with + | SynType.LongIdent ident -> + match ident.LongIdent with + | [ i ] -> + // We won't bother with the case that the user has done e.g. `Single` (relying on `System` being open). + match Primitives.qualifyType i.idText with + | Some qualified -> + match i.idText with + | "char" + | "string" -> None + | _ -> Some qualified + | None -> None + | _ -> None + | _ -> None + + /// A type with a unit of measure. Returns the name of the measure, and the outer type to which the measure was + /// applied. + let (|Measure|_|) (fieldType : SynType) : (Ident * LongIdent) option = + match fieldType with + | SynType.App (NumberType outer, + _, + [ SynType.LongIdent (SynLongIdent.SynLongIdent ([ ident ], _, _)) ], + _, + _, + _, + _) -> Some (ident, outer) + | _ -> None + + /// The `System.Text.Json.Nodes.JsonNode` type. + let (|JsonNode|_|) (fieldType : SynType) : unit option = + match fieldType with + | SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) -> + match ident |> List.map (fun i -> i.idText) with + | [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ] + | [ "Text" ; "Json" ; "Nodes" ; "JsonNode" ] + | [ "Json" ; "Nodes" ; "JsonNode" ] + | [ "Nodes" ; "JsonNode" ] + | [ "JsonNode" ] -> Some () + | _ -> None + | _ -> None + + /// The `unit` type. + let (|UnitType|_|) (fieldType : SynType) : unit option = + match fieldType with + | SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) -> + match ident |> List.map (fun i -> i.idText.ToLowerInvariant ()) with + | [ "microsoft" ; "fsharp" ; "core" ; "unit" ] + | [ "fsharp" ; "core" ; "unit" ] + | [ "core" ; "unit" ] + | [ "unit" ] -> Some () + | _ -> None + | _ -> None + + /// The `System.DateOnly` type. + let (|DateOnly|_|) (fieldType : SynType) = + match fieldType with + | SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) -> + match ident |> List.map (fun i -> i.idText) with + | [ "System" ; "DateOnly" ] + | [ "DateOnly" ] -> Some () + | _ -> None + | _ -> None + + /// The `System.DateTime` type. + let (|DateTime|_|) (fieldType : SynType) = + match fieldType with + | SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) -> + match ident |> List.map (fun i -> i.idText) with + | [ "System" ; "DateTime" ] + | [ "DateTime" ] -> Some () + | _ -> None + | _ -> None + + /// The `System.DateTimeOffset` type. + let (|DateTimeOffset|_|) (fieldType : SynType) = + match fieldType with + | SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) -> + match ident |> List.map (fun i -> i.idText) with + | [ "System" ; "DateTimeOffset" ] + | [ "DateTimeOffset" ] -> Some () + | _ -> None + | _ -> None + + /// The `System.Uri` type. + let (|Uri|_|) (fieldType : SynType) = + match fieldType with + | SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) -> + match ident |> List.map (fun i -> i.idText) with + | [ "System" ; "Uri" ] + | [ "Uri" ] -> Some () + | _ -> None + | _ -> None + + /// The `System.Threading.Tasks.Task<_>` type. You get access to the generic argument. + /// Due to a design error which I haven't yet fixed, this throws on the non-generic Task; please raise an issue + /// if you run into this. + let (|Task|_|) (fieldType : SynType) : SynType option = + match fieldType with + | SynType.App (SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)), _, args, _, _, _, _) -> + match ident |> List.map (fun i -> i.idText) with + | [ "Task" ] + | [ "Tasks" ; "Task" ] + | [ "Threading" ; "Tasks" ; "Task" ] + | [ "System" ; "Threading" ; "Tasks" ; "Task" ] -> + match args with + | [ arg ] -> Some arg + | _ -> failwithf "Expected Task to be applied to exactly one arg, but got: %+A" args + | _ -> None + | _ -> None + + /// The `System.IO.DirectoryInfo` type. + let (|DirectoryInfo|_|) (fieldType : SynType) = + match fieldType with + | SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) -> + match ident |> List.map (fun i -> i.idText) with + | [ "System" ; "IO" ; "DirectoryInfo" ] + | [ "IO" ; "DirectoryInfo" ] + | [ "DirectoryInfo" ] -> Some () + | _ -> None + | _ -> None + + /// The `System.IO.FieldInfo` type. + let (|FileInfo|_|) (fieldType : SynType) = + match fieldType with + | SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) -> + match ident |> List.map (fun i -> i.idText) with + | [ "System" ; "IO" ; "FileInfo" ] + | [ "IO" ; "FileInfo" ] + | [ "FileInfo" ] -> Some () + | _ -> None + | _ -> None + + /// The `System.TimeSpan` type. + let (|TimeSpan|_|) (fieldType : SynType) = + match fieldType with + | SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) -> + match ident |> List.map (fun i -> i.idText) with + | [ "System" ; "TimeSpan" ] + | [ "TimeSpan" ] -> Some () + | _ -> None + | _ -> None + +/// Methods for manipulating SynType, which represents a type as might appear e.g. in a type annotation. +[] +module SynType = + + /// Strip all outer parentheses from this SynType. + /// + /// Most functions by default will not strip parentheses. Note, for example, that `let foo : (int) = 3` is fine. + /// Use `stripOptionalParen` to turn that `(int)` into `int`, so that e.g. you can match on it using the patterns + /// in `SynTypePatterns`. + let rec stripOptionalParen (ty : SynType) : SynType = + match ty with + | SynType.Paren (ty, _) -> stripOptionalParen ty + | ty -> ty + + /// Wrap this type in parentheses. + let inline paren (ty : SynType) : SynType = SynType.Paren (ty, range0) + + /// Define a SynType by just a name. + let inline createLongIdent (ident : LongIdent) : SynType = + SynType.LongIdent (SynLongIdent.create ident) + + /// Define a SynType by just a name. + let inline createLongIdent' (ident : string list) : SynType = + SynType.LongIdent (SynLongIdent.createS' ident) + + /// Define a SynType by just a name. Use `createLongIdent'` if you want more components in this name, + /// so e.g. don't pass `System.Collections.Generic.Dictionary` to `named`. + let inline named (name : string) = createLongIdent' [ name ] + + /// {name}<{args}> + let inline app' (name : SynType) (args : SynType list) : SynType = + if args.IsEmpty then + failwith "Type cannot be applied to no arguments" + + SynType.App (name, Some range0, args, List.replicate (args.Length - 1) range0, Some range0, false, range0) + + /// {name}<{args}> + let inline app (name : string) (args : SynType list) : SynType = app' (named name) args + + /// Returns None if the input list was empty. + let inline tupleNoParen (ty : SynType list) : SynType option = + match List.rev ty with + | [] -> None + | [ t ] -> Some t + | t :: rest -> + ([ SynTupleTypeSegment.Type t ], rest) + ||> List.fold (fun ty nextArg -> SynTupleTypeSegment.Type nextArg :: SynTupleTypeSegment.Star range0 :: ty) + |> fun segs -> SynType.Tuple (false, segs, range0) + |> Some + + /// `{arg} {name}`, e.g. `int option`. + let inline appPostfix (name : string) (arg : SynType) : SynType = + SynType.App (named name, None, [ arg ], [], None, true, range0) + + /// `{arg} {name1.name2.name3}`, e.g. `int System.Nullable`. + let inline appPostfix' (name : string list) (arg : SynType) : SynType = + SynType.App (createLongIdent' name, None, [ arg ], [], None, true, range0) + + /// The type `{domain} -> {range}`. + let inline funFromDomain (domain : SynType) (range : SynType) : SynType = + SynType.Fun ( + domain, + range, + range0, + { + ArrowRange = range0 + } + ) + + /// In an abstract type definition like `type Foo = abstract Blah : x : int -> string`, + /// this represents one single `x : int` part. + let inline signatureParamOfType + (attrs : SynAttribute list) + (ty : SynType) + (optional : bool) + (name : Ident option) + : SynType + = + SynType.SignatureParameter ( + attrs + |> List.map (fun attr -> + { + Attributes = [ attr ] + Range = range0 + } + ), + optional, + name, + ty, + range0 + ) + + /// Create a type which refers to a generic type parameter. For example, `'a` (assuming there's already + /// some language construct causing the generic `'a` to be in scope). + let inline var (ty : SynTypar) : SynType = SynType.Var (ty, range0) + + /// The `unit` type. + let unit : SynType = named "unit" + + /// The `obj` type. + let obj : SynType = named "obj" + + /// The `bool` type. + let bool : SynType = named "bool" + + /// The `int` type. + let int : SynType = named "int" + + /// The type `{elt} array`. + let array (elt : SynType) : SynType = SynType.Array (1, elt, range0) + + /// The type `{elt} list` (i.e. an F# list). + let list (elt : SynType) : SynType = + SynType.App (named "list", None, [ elt ], [], None, true, range0) + + /// The type `{elt} option`. + let option (elt : SynType) : SynType = + SynType.App (named "option", None, [ elt ], [], None, true, range0) + + /// The anonymous type, i.e. the `_` in the type-annotated `x : _`. + let anon : SynType = SynType.Anon range0 + + /// The type `System.Threading.Tasks.Task<{elt}>`. + let task (elt : SynType) : SynType = + SynType.App ( + createLongIdent' [ "System" ; "Threading" ; "Tasks" ; "Task" ], + None, + [ elt ], + [], + None, + true, + range0 + ) + + /// The type `string`. + let string : SynType = named "string" + + /// Given ['a1, 'a2] and 'ret, returns 'a1 -> 'a2 -> 'ret. + let toFun (inputs : SynType list) (ret : SynType) : SynType = + (ret, List.rev inputs) ||> List.fold (fun ty input -> funFromDomain input ty) + + /// Convert a canonical form like `System.Int32` to a human-readable form like `int32`. + /// Throws on unrecognised inputs. + let primitiveToHumanReadableString (name : LongIdent) : string = + match name |> List.map _.idText with + | [ "System" ; "Single" ] -> "single" + | [ "System" ; "Double" ] -> "double" + | [ "System" ; "Byte" ] -> "byte" + | [ "System" ; "SByte" ] -> "signed byte" + | [ "System" ; "Int16" ] -> "int16" + | [ "System" ; "Int32" ] -> "int32" + | [ "System" ; "Int64" ] -> "int64" + | [ "System" ; "UInt16" ] -> "uint16" + | [ "System" ; "UInt32" ] -> "uint32" + | [ "System" ; "UInt64" ] -> "uint64" + | [ "System" ; "Char" ] -> "char" + | [ "System" ; "Decimal" ] -> "decimal" + | [ "System" ; "String" ] -> "string" + | [ "System" ; "Boolean" ] -> "bool" + | ty -> + ty + |> String.concat "." + |> failwithf "could not create human-readable string for primitive type %s" + + /// Attempt to create a human-readable representation of this type, for use in error messages. + /// This function throws if we couldn't decide on a human-readable representation. + let rec toHumanReadableString (ty : SynType) : string = + match ty with + | PrimitiveType t1 -> primitiveToHumanReadableString t1 + | OptionType t1 -> toHumanReadableString t1 + " option" + | NullableType t1 -> toHumanReadableString t1 + " Nullable" + | ChoiceType ts -> + ts + |> List.map toHumanReadableString + |> String.concat ", " + |> sprintf "Choice<%s>" + | MapType (k, v) + | DictionaryType (k, v) + | IDictionaryType (k, v) + | IReadOnlyDictionaryType (k, v) -> sprintf "map<%s, %s>" (toHumanReadableString k) (toHumanReadableString v) + | ListType t1 -> toHumanReadableString t1 + " list" + | ArrayType t1 -> toHumanReadableString t1 + " array" + | Task t1 -> toHumanReadableString t1 + " Task" + | UnitType -> "unit" + | FileInfo -> "FileInfo" + | DirectoryInfo -> "DirectoryInfo" + | Uri -> "URI" + | Stream -> "Stream" + | Guid -> "GUID" + | BigInt -> "bigint" + | DateTimeOffset -> "DateTimeOffset" + | DateOnly -> "DateOnly" + | TimeSpan -> "TimeSpan" + | SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) -> ident |> List.map _.idText |> String.concat "." + | ty -> failwithf "could not compute human-readable string for type: %O" ty + + /// Guess whether the types are equal. We err on the side of saying "no, they're different". + let rec provablyEqual (ty1 : SynType) (ty2 : SynType) : bool = + if Object.ReferenceEquals (ty1, ty2) then + true + else + + match ty1 with + | PrimitiveType t1 -> + match ty2 with + | PrimitiveType t2 -> (t1 |> List.map _.idText) = (t2 |> List.map _.idText) + | _ -> false + | OptionType t1 -> + match ty2 with + | OptionType t2 -> provablyEqual t1 t2 + | _ -> false + | NullableType t1 -> + match ty2 with + | NullableType t2 -> provablyEqual t1 t2 + | _ -> false + | ChoiceType t1 -> + match ty2 with + | ChoiceType t2 -> + t1.Length = t2.Length + && List.forall (fun (a, b) -> provablyEqual a b) (List.zip t1 t2) + | _ -> false + | DictionaryType (k1, v1) -> + match ty2 with + | DictionaryType (k2, v2) -> provablyEqual k1 k2 && provablyEqual v1 v2 + | _ -> false + | IDictionaryType (k1, v1) -> + match ty2 with + | IDictionaryType (k2, v2) -> provablyEqual k1 k2 && provablyEqual v1 v2 + | _ -> false + | IReadOnlyDictionaryType (k1, v1) -> + match ty2 with + | IReadOnlyDictionaryType (k2, v2) -> provablyEqual k1 k2 && provablyEqual v1 v2 + | _ -> false + | MapType (k1, v1) -> + match ty2 with + | MapType (k2, v2) -> provablyEqual k1 k2 && provablyEqual v1 v2 + | _ -> false + | ListType t1 -> + match ty2 with + | ListType t2 -> provablyEqual t1 t2 + | _ -> false + | ArrayType t1 -> + match ty2 with + | ArrayType t2 -> provablyEqual t1 t2 + | _ -> false + | Task t1 -> + match ty2 with + | Task t2 -> provablyEqual t1 t2 + | _ -> false + | UnitType -> + match ty2 with + | UnitType -> true + | _ -> false + | FileInfo -> + match ty2 with + | FileInfo -> true + | _ -> false + | DirectoryInfo -> + match ty2 with + | DirectoryInfo -> true + | _ -> false + | Uri -> + match ty2 with + | Uri -> true + | _ -> false + | Stream -> + match ty2 with + | Stream -> true + | _ -> false + | Guid -> + match ty2 with + | Guid -> true + | _ -> false + | BigInt -> + match ty2 with + | BigInt -> true + | _ -> false + | DateTimeOffset -> + match ty2 with + | DateTimeOffset -> true + | _ -> false + | DateOnly -> + match ty2 with + | DateOnly -> true + | _ -> false + | _ -> + + match ty1, ty2 with + | SynType.LongIdent (SynLongIdent (ident1, _, _)), SynType.LongIdent (SynLongIdent (ident2, _, _)) -> + let ident1 = ident1 |> List.map _.idText + let ident2 = ident2 |> List.map _.idText + ident1 = ident2 + | _, _ -> false + + /// Returns the args (where these are tuple types if curried) in order, and the return type. + let rec getType (ty : SynType) : (SynType * bool) list * SynType = + match ty with + | SynType.Paren (ty, _) -> getType ty + | SynType.Fun (argType, returnType, _, _) -> + let args, ret = getType returnType + // TODO this code is clearly wrong + let (inputArgs, inputRet), hasParen = + match argType with + | SynType.Paren (argType, _) -> getType argType, true + | _ -> getType argType, false + + ((toFun (List.map fst inputArgs) inputRet), hasParen) :: args, ret + | _ -> [], ty diff --git a/WoofWare.Whippet.Fantomas/SynTypeDefn.fs b/WoofWare.Whippet.Fantomas/SynTypeDefn.fs new file mode 100644 index 0000000..87744d5 --- /dev/null +++ b/WoofWare.Whippet.Fantomas/SynTypeDefn.fs @@ -0,0 +1,53 @@ +namespace WoofWare.Whippet.Fantomas + +open Fantomas.FCS.Syntax +open Fantomas.FCS.SyntaxTrivia +open Fantomas.FCS.Text.Range + +/// Methods for manipulating `SynTypeDefn`, which represents any type definition like `type Foo = ...`. +[] +module SynTypeDefn = + + /// Build a `SynTypeDefn` from its components: + /// the "front matter" `SynComponentInfo`, and the "body" `SynTypeDefnRepr`. + let inline create (componentInfo : SynComponentInfo) (repr : SynTypeDefnRepr) : SynTypeDefn = + SynTypeDefn.SynTypeDefn ( + componentInfo, + repr, + [], + None, + range0, + { + LeadingKeyword = SynTypeDefnLeadingKeyword.Type range0 + EqualsRange = Some range0 + WithKeyword = None + } + ) + + /// Add member definitions to this type: `type Foo = ... with member Blah = ...` + let inline withMemberDefns (members : SynMemberDefn list) (r : SynTypeDefn) : SynTypeDefn = + match r with + | SynTypeDefn (typeInfo, typeRepr, _, ctor, range, trivia) -> + SynTypeDefn.SynTypeDefn (typeInfo, typeRepr, members, ctor, range, trivia) + + /// Get the name of this type as it appears in the source. + let getName (defn : SynTypeDefn) : LongIdent = + match defn with + | SynTypeDefn (SynComponentInfo.SynComponentInfo (_, _, _, id, _, _, _, _), _, _, _, _, _) -> id + + /// Select from this type definition the first attribute with the given name: `[] type Blah = ...` + let getAttribute (attrName : string) (defn : SynTypeDefn) : SynAttribute option = + match defn with + | SynTypeDefn (SynComponentInfo.SynComponentInfo (attrs, _, _, _, _, _, _, _), _, _, _, _, _) -> + attrs + |> List.collect (fun a -> a.Attributes) + |> List.tryFind (fun i -> + match i.TypeName with + | SynLongIdent.SynLongIdent (id, _, _) -> + let name = List.last(id).idText + name = attrName || name + "Attribute" = attrName + ) + + /// Determine whether this type definition has an attribute with the given name: `[] type Blah = ...` + let hasAttribute (attrName : string) (defn : SynTypeDefn) : bool = + getAttribute attrName defn |> Option.isSome diff --git a/WoofWare.Whippet.Fantomas/SynTypeDefnRepr.fs b/WoofWare.Whippet.Fantomas/SynTypeDefnRepr.fs new file mode 100644 index 0000000..22135f1 --- /dev/null +++ b/WoofWare.Whippet.Fantomas/SynTypeDefnRepr.fs @@ -0,0 +1,32 @@ +namespace WoofWare.Whippet.Fantomas + +open Fantomas.FCS.Syntax +open Fantomas.FCS.Text.Range + +/// Methods for manipulating SynTypeDefnRepr, which represents the "body" of a type definition: what actually makes up +/// the type, as opposed to e.g. its name. +[] +module SynTypeDefnRepr = + + /// A definition like `abstract Blah : int`, as you would use to define an interface type. + let inline interfaceType (mems : SynMemberDefns) : SynTypeDefnRepr = + SynTypeDefnRepr.ObjectModel (SynTypeDefnKind.Unspecified, mems, range0) + + /// Indicates the body of a `type Foo with {body}` extension type declaration. + let inline augmentation () : SynTypeDefnRepr = + SynTypeDefnRepr.ObjectModel (SynTypeDefnKind.Augmentation range0, [], range0) + + /// An F# discriminated union with the given accessibility modifier on the implementation: + /// `type Foo = private | Blah`. + let inline unionWithAccess (implAccess : SynAccess option) (cases : SynUnionCase list) : SynTypeDefnRepr = + SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Union (implAccess, cases, range0), range0) + + /// An F# discriminated union. + let inline union (cases : SynUnionCase list) : SynTypeDefnRepr = unionWithAccess None cases + + /// An F# record with the given accessibility modifier on the implementation: `type Foo = private { blah }`. + let inline recordWithAccess (implAccess : SynAccess option) (fields : SynField list) : SynTypeDefnRepr = + SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Record (implAccess, fields, range0), range0) + + /// An F# record. + let inline record (fields : SynField list) : SynTypeDefnRepr = recordWithAccess None fields diff --git a/WoofWare.Whippet.Fantomas/SynUnionCase.fs b/WoofWare.Whippet.Fantomas/SynUnionCase.fs new file mode 100644 index 0000000..5e8dd19 --- /dev/null +++ b/WoofWare.Whippet.Fantomas/SynUnionCase.fs @@ -0,0 +1,57 @@ +namespace WoofWare.Whippet.Fantomas + +open Fantomas.FCS.Syntax +open Fantomas.FCS.Text.Range +open Fantomas.FCS.Xml +open Fantomas.FCS.SyntaxTrivia + +/// Represents everything you need to know about a union case. +/// This is generic on whether each field of this case must be named. +type UnionCase<'ident> = + { + /// The name of the case: e.g. `| Foo of blah` has this being `Foo`. + Name : Ident + /// Any docstring associated with this case. + XmlDoc : PreXmlDoc option + /// Any accessibility modifier: e.g. `type Foo = private | Blah`. + Access : SynAccess option + /// Attributes on the case: for example, `| [] Foo of blah`. + Attributes : SynAttribute list + /// The data contained within the case: for example, `[blah]` in `| Foo of blah`. + Fields : SynFieldData<'ident> list + } + +/// Methods for manipulating `SynUnionCase`, which represents a single case of a discriminated union. +[] +module SynUnionCase = + /// Build a SynUnionCase from our structured `UnionCase` type. + let create (case : UnionCase) : SynUnionCase = + let fields = + case.Fields + |> List.map (fun field -> + SynField.SynField ( + SynAttributes.ofAttrs field.Attrs, + false, + field.Ident, + field.Type, + false, + PreXmlDoc.Empty, + None, + range0, + { + LeadingKeyword = None + } + ) + ) + + SynUnionCase.SynUnionCase ( + SynAttributes.ofAttrs case.Attributes, + SynIdent.createI case.Name, + SynUnionCaseKind.Fields fields, + case.XmlDoc |> Option.defaultValue PreXmlDoc.Empty, + case.Access, + range0, + { + BarRange = Some range0 + } + ) diff --git a/WoofWare.Whippet.Fantomas/SynValInfo.fs b/WoofWare.Whippet.Fantomas/SynValInfo.fs new file mode 100644 index 0000000..8ae6d5f --- /dev/null +++ b/WoofWare.Whippet.Fantomas/SynValInfo.fs @@ -0,0 +1,9 @@ +namespace WoofWare.Whippet.Fantomas + +open Fantomas.FCS.Syntax + +/// Module for manipulating the SynValInfo AST type (which represents the arguments and return type of a `let` binding). +[] +module SynValInfo = + /// No arguments, no return info. + let empty = SynValInfo.SynValInfo ([], SynArgInfo.empty) diff --git a/WoofWare.Whippet.Fantomas/WoofWare.Whippet.Fantomas.fsproj b/WoofWare.Whippet.Fantomas/WoofWare.Whippet.Fantomas.fsproj new file mode 100644 index 0000000..e55093c --- /dev/null +++ b/WoofWare.Whippet.Fantomas/WoofWare.Whippet.Fantomas.fsproj @@ -0,0 +1,65 @@ + + + + netstandard2.1 + true + Patrick Stevens + Copyright (c) Patrick Stevens 2024 + Helpers for accessing Fantomas syntax trees. + git + https://github.com/Smaug123/WoofWare.Whippet + MIT + README.md + fsharp;fantomas + true + FS3559 + WoofWare.Whippet.Fantomas + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + / + README.md + + + + + + + + + + + + + diff --git a/WoofWare.Whippet.Fantomas/version.json b/WoofWare.Whippet.Fantomas/version.json new file mode 100644 index 0000000..ff2b96d --- /dev/null +++ b/WoofWare.Whippet.Fantomas/version.json @@ -0,0 +1,12 @@ +{ + "version": "0.1", + "publicReleaseRefSpec": [ + "^refs/heads/main$" + ], + "pathFilters": [ + "./", + ":/WoofWare.Whippet.Fantomas/", + ":/global.json", + ":/Directory.Build.props" + ] +} diff --git a/WoofWare.Whippet.Test/TestSurface.fs b/WoofWare.Whippet.Test/TestSurface.fs index 163aadf..cb1b5b7 100644 --- a/WoofWare.Whippet.Test/TestSurface.fs +++ b/WoofWare.Whippet.Test/TestSurface.fs @@ -16,7 +16,7 @@ module TestSurface = [] // https://github.com/nunit/nunit3-vs-adapter/issues/876 let CheckVersionAgainstRemote () = - MonotonicVersion.validate assembly "WoofWare.Myriad.Core" + MonotonicVersion.validate assembly "WoofWare.Whippet.Core" *) [] diff --git a/WoofWare.Whippet.sln b/WoofWare.Whippet.sln index 00c7d31..5cecdae 100644 --- a/WoofWare.Whippet.sln +++ b/WoofWare.Whippet.sln @@ -6,6 +6,10 @@ Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "WoofWare.Whippet.Core", "Wo EndProject Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "WoofWare.Whippet.Test", "WoofWare.Whippet.Test\WoofWare.Whippet.Test.fsproj", "{A2E2A639-D17D-426D-B424-0139B4DBCF8F}" EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "WoofWare.Whippet.Fantomas", "WoofWare.Whippet.Fantomas\WoofWare.Whippet.Fantomas.fsproj", "{2D4FC000-94DC-4456-A7AA-A5776B91FB65}" +EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "WoofWare.Whippet.Fantomas.Test", "WoofWare.Whippet.Fantomas.Test\WoofWare.Whippet.Fantomas.Test.fsproj", "{E220B17E-D608-43CB-B117-329BA240B13B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -24,5 +28,13 @@ Global {A2E2A639-D17D-426D-B424-0139B4DBCF8F}.Debug|Any CPU.Build.0 = Debug|Any CPU {A2E2A639-D17D-426D-B424-0139B4DBCF8F}.Release|Any CPU.ActiveCfg = Release|Any CPU {A2E2A639-D17D-426D-B424-0139B4DBCF8F}.Release|Any CPU.Build.0 = Release|Any CPU + {2D4FC000-94DC-4456-A7AA-A5776B91FB65}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2D4FC000-94DC-4456-A7AA-A5776B91FB65}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2D4FC000-94DC-4456-A7AA-A5776B91FB65}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2D4FC000-94DC-4456-A7AA-A5776B91FB65}.Release|Any CPU.Build.0 = Release|Any CPU + {E220B17E-D608-43CB-B117-329BA240B13B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E220B17E-D608-43CB-B117-329BA240B13B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E220B17E-D608-43CB-B117-329BA240B13B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E220B17E-D608-43CB-B117-329BA240B13B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/WoofWare.Whippet/Program.fs b/WoofWare.Whippet/Program.fs index acdec5a..a1139e5 100644 --- a/WoofWare.Whippet/Program.fs +++ b/WoofWare.Whippet/Program.fs @@ -61,7 +61,7 @@ module Program = failwith $"Expected GenerateRawFromRaw to take exactly one parameter: a RawSourceGenerationArgs. Got %i{pars.Length}" - if pars.[0].ParameterType <> typeof then + if pars.[0].ParameterType.FullName <> typeof.FullName then failwith $"Expected GenerateRawFromRaw to take exactly one parameter: a RawSourceGenerationArgs. Got %s{pars.[0].ParameterType.FullName}" @@ -96,7 +96,7 @@ module Program = ) let projectOptions = - defaultLoader.LoadProjects ([ args.InputFile.FullName ]) |> Seq.toArray + defaultLoader.LoadProjects [ args.InputFile.FullName ] |> Seq.toArray let desiredProject = projectOptions @@ -104,7 +104,7 @@ module Program = let toGenerate = desiredProject.Items - |> List.choose (fun (ProjectItem.Compile (name, fullPath, metadata)) -> + |> List.choose (fun (ProjectItem.Compile (_name, fullPath, metadata)) -> match metadata with | None -> None | Some metadata -> @@ -122,7 +122,9 @@ module Program = ) Console.Error.WriteLine $"Loading plugin: %s{args.PluginDll.FullName}" + let pluginAssembly = Assembly.LoadFrom args.PluginDll.FullName + // We will look up any member called GenerateRawFromRaw and/or GenerateFromRaw. // It's your responsibility to decide whether to do anything with this call; you return null if you don't want // to do anything. diff --git a/WoofWare.Whippet/WoofWare.Whippet.fsproj b/WoofWare.Whippet/WoofWare.Whippet.fsproj index 4f1a25e..3ae6bfc 100644 --- a/WoofWare.Whippet/WoofWare.Whippet.fsproj +++ b/WoofWare.Whippet/WoofWare.Whippet.fsproj @@ -24,7 +24,6 @@ -