DEV Community

jkone27
jkone27

Posted on

Learning some Fantomas AST

Made a test encoder/decored for a simple namespace declaration with Fantomas OAK Ast, see fantomas tools page

given

Oak (1,0-1,15)
        ModuleOrNamespaceNode (1,0-1,15)
            ModuleOrNamespaceHeaderNode (1,0-1,15)
                MultipleTextsNode (1,0-1,9)
                    namespace (1,0-1,9)
                IdentListNode (1,10-1,15)
                    A (1,10-1,11)
                    B (1,12-1,13)
                    C (1,14-1,15)
Enter fullscreen mode Exit fullscreen mode

it will output A.B.C (lowered)

you can run it as dotnet fsi script.fsx or in vscode with dotnet sdk installed. cheers!

let oakAst = 
    "hello.one.two"
    |> FantomasWriter.makeNamespace  []

oakAst 
|> FantomasReader.extractNsFromOak 
|> Option.iter (printfn "NAMESPACE: %s")
Enter fullscreen mode Exit fullscreen mode

Fabulous.AST

if you want to make your life easier and drop the custom write part for Oak, you can make use of the awesome Fabulous.AST DSL nuget package

#r "nuget:Fabulous.AST"

open System
open Fabulous.AST
open type Fabulous.AST.Ast

// using Fabulous.AST life becomes easier for writing oak
let oakAst = 
    Ast.Oak(){
        Ast.Namespace("hello.one.two") {}
    }
    |> Gen.mkOak

Enter fullscreen mode Exit fullscreen mode

Here a Gist of It

#r "nuget:Fantomas.Core"
open Fantomas.Core
open Fantomas.Core.SyntaxOak
open Fantomas.FCS.Text
open System
(** `namespace A.B.C` : https://fsprojects.github.io/fantomas-tools
Oak (1,0-1,15)
ModuleOrNamespaceNode (1,0-1,15)
ModuleOrNamespaceHeaderNode (1,0-1,15)
MultipleTextsNode (1,0-1,9)
namespace (1,0-1,9)
IdentListNode (1,10-1,15)
A (1,10-1,11)
B (1,12-1,13)
C (1,14-1,15)
*)
module FantomasWriter =
let text v = SingleTextNode(v, Range.Zero)
let toMultipleTexts lst = MultipleTextsNode(lst |> Seq.map text |> Seq.toList ,Range.Zero)
let toIdentList (lst: string seq) =
let ll =
lst
|> Seq.map (fun l ->
let content = l |> text
if l = "." then
content |> IdentifierOrDot.KnownDot
else
content |> IdentifierOrDot.Ident
)
|> Seq.toList
if ll.Length > 0 then
IdentListNode(ll, Range.Zero) |> Some
else
None
let makeModuleOrNamespaceHeaderNode (nameSpaceString: string) =
let ns = nameSpaceString.ToLower().Trim().Split([|'.'|])
if ns.Length > 0 then
ModuleOrNamespaceHeaderNode(
None,
None,
[ "namespace" ] |> toMultipleTexts,
None,
false,
ns |> toIdentList,
Range.Zero) |> Some
else
None
let makeNamespace (nameSpaceString: string) decls =
let nsOpt = makeModuleOrNamespaceHeaderNode nameSpaceString
Oak([],
[
ModuleOrNamespaceNode(nsOpt, decls, Range.Zero)
], Range.Zero)
module FantomasReader =
let extractNs (iln: IdentListNode) =
iln.Content
|> List.collect (fun iod ->
match iod with
| IdentifierOrDot.Ident(stn) -> [ stn.Text ]
| _ -> []
)
|> fun l ->
if l.Length > 0 then
String.Join('.',l) |> Some
else
None
// Define a function to extract the namespace from an Oak AST
let extractNsFromOak (oak: Oak) =
oak.ModulesOrNamespaces.Head.Header
|> Option.bind (
fun header ->
match header.LeadingKeyword.Content with
| [x] when x.Text = "namespace" ->
header.Name
|> Option.bind extractNs
| _ -> None
)
// Example usage
let oakAst = FantomasWriter.makeNamespace "hello.one.two" []
oakAst
|> FantomasReader.extractNsFromOak
|> Option.iter (printfn "NAMESPACE: %s")
Retry later

Top comments (0)

Retry later
Retry later