Files
WoofWare.Whippet/WoofWare.Whippet.Fantomas/Ast.fs
Patrick Stevens da609db2ce
Some checks are pending
.NET / build (Debug) (push) Waiting to run
.NET / build (Release) (push) Waiting to run
.NET / analyzers (push) Waiting to run
.NET / check-dotnet-format (push) Waiting to run
.NET / check-nix-format (push) Waiting to run
.NET / Check links (push) Waiting to run
.NET / Check flake (push) Waiting to run
.NET / nuget-pack (push) Waiting to run
.NET / expected-pack (push) Blocked by required conditions
.NET / check-accurate-generations (push) Waiting to run
.NET / all-required-checks-complete (push) Blocked by required conditions
.NET / nuget-publish (push) Blocked by required conditions
.NET / nuget-publish-fantomas (push) Blocked by required conditions
.NET / nuget-publish-json-plugin (push) Blocked by required conditions
.NET / nuget-publish-json-attrs (push) Blocked by required conditions
.NET / nuget-publish-argparser-plugin (push) Blocked by required conditions
.NET / nuget-publish-argparser-attrs (push) Blocked by required conditions
First release (#10)
2024-10-07 13:35:43 +01:00

127 lines
5.1 KiB
Forth

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.
[<RequireQualifiedAccess>]
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
let output = CodeFormatter.FormatASTAsync (parseTree, cfg) |> Async.RunSynchronously
Some output
/// For each namespace in the AST, returns the types defined therein.
let getTypes (ast : ParsedInput) : (LongIdent * SynTypeDefn list) list =
match ast with
| ParsedInput.SigFile psfi -> failwith "Signature files not supported"
| ParsedInput.ImplFile pifi ->
pifi.Contents
|> List.collect (fun (SynModuleOrNamespace.SynModuleOrNamespace (ns, _, _, decls, _, _, _, _, _)) ->
decls
|> List.collect (fun decl ->
match decl with
| SynModuleDecl.Types (defns, _) -> defns |> List.map (fun defn -> ns, defn)
| _ -> []
)
)
|> List.map (fun (li, ty) -> (li |> List.map _.idText), (ty, li))
|> List.groupBy fst
|> List.map (fun (_name, data) ->
let ns = snd (snd data.[0])
let data = data |> List.map (fun (_, (ty, _)) -> ty)
ns, data
)
/// For each namespace in the AST, returns the records contained therein.
let getRecords (ast : ParsedInput) : (LongIdent * RecordType list) list =
match ast with
| ParsedInput.SigFile psfi ->
psfi.Contents
|> List.collect (fun (SynModuleOrNamespaceSig.SynModuleOrNamespaceSig (ns, _, _, decls, _, _, _, _, _)) ->
decls
|> List.collect (fun decl ->
match decl with
| SynModuleSigDecl.Types (defns, _) ->
defns
|> List.choose (fun defn ->
match defn with
| SynTypeDefnSig.SynTypeDefnSig (sci, typeRepr, members, _, _) ->
match typeRepr with
| SynTypeDefnSigRepr.Simple (SynTypeDefnSimpleRepr.Record (access, fields, _), _) ->
(ns,
RecordType.OfRecord
sci
(failwith "Signature files not yet supported")
access
fields)
|> Some
| _ -> None
)
| _ -> []
)
)
| ParsedInput.ImplFile pifi ->
pifi.Contents
|> List.collect (fun (SynModuleOrNamespace.SynModuleOrNamespace (ns, _, _, decls, _, _, _, _, _)) ->
decls
|> List.collect (fun decl ->
match decl with
| SynModuleDecl.Types (defns, _) ->
defns
|> List.choose (fun defn ->
match defn with
| SynTypeDefn.SynTypeDefn (sci, typeRepr, smd, _, _, _) ->
match typeRepr with
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Record (access, fields, _), _) ->
(ns, RecordType.OfRecord sci smd access fields) |> Some
| _ -> None
)
| _ -> []
)
)
|> List.map (fun (li, ty) -> (li |> List.map _.idText), (ty, li))
|> List.groupBy fst
|> List.map (fun (_name, data) ->
let ns = snd (snd data.[0])
let data = data |> List.map (fun (_, (ty, _)) -> ty)
ns, data
)