Header menu logo Fabulous.AST

Fabulous.AST

Welcome to the Fabulous.AST, an Abstract Syntax Tree (AST) Domain Specific Language (DSL) for F#.

Fabulous.AST uses Fantomas to generate F# code from AST. This means that you can use Fabulous.AST to generate F# code that is formatted according to the Fantomas style guide. It's designed to provide a simple and expressive way to represent code as a tree of nodes. This makes it easier to manipulate and analyze code programmatically.

Plain Fantomas

Let's take a look at an AST example in Fantomas:

#r "../src/Fabulous.AST/bin/Release/netstandard2.1/publish/Fantomas.FCS.dll"
#r "../src/Fabulous.AST/bin/Release/netstandard2.1/publish/Fantomas.Core.dll"

open Fantomas.FCS.Text
open Fantomas.Core
open Fantomas.Core.SyntaxOak

let implementationSyntaxTree =
    Oak(
        [],
        [ ModuleOrNamespaceNode(
              None,
              [ BindingNode(
                    None,
                    None,
                    MultipleTextsNode([ SingleTextNode("let", Range.Zero) ], Range.Zero),
                    false,
                    None,
                    None,
                    Choice1Of2(IdentListNode([ IdentifierOrDot.Ident(SingleTextNode("x", Range.Zero)) ], Range.Zero)),
                    None,
                    [],
                    None,
                    SingleTextNode("=", Range.Zero),
                    Expr.Constant(Constant.FromText(SingleTextNode("12", Range.Zero))),
                    Range.Zero
                )
                |> ModuleDecl.TopLevelBinding ],
              Range.Zero
          ) ],
        Range.Zero
    )

CodeFormatter.FormatOakAsync(implementationSyntaxTree)
|> Async.RunSynchronously
|> printfn "%s"

// produces the following code:
let x = 12
let x = 12

Using Fabulous.AST

Now let's take a look at same example using Fabulous.AST:

#r "../src/Fabulous.AST/bin/Release/netstandard2.1/publish/Fabulous.AST.dll"

open Fabulous.AST
open type Fabulous.AST.Ast

Oak() { AnonymousModule() { Value("y", Unquoted "12") } }
|> Gen.mkOak
|> CodeFormatter.FormatOakAsync
|> Async.RunSynchronously
|> printfn "%s"
// produces the following code:
let y = 12
let y = 12

Escape Hatch

You can use an Escape Hatch to generate code that is not supported by Fabulous.AST yet by constructing the raw Fantomas.SyntaxOak node. For example, the following code:

open type Fabulous.AST.Ast

let topLevelBinding: ModuleDecl =
    BindingNode(
        xmlDoc = None,
        attributes = None,
        leadingKeyword = MultipleTextsNode([ SingleTextNode("let", Range.Zero) ], Range.Zero),
        isMutable = false,
        inlineNode = None,
        // We cannot define private yet using Fabulous.AST
        accessibility = Some(SingleTextNode("private", Range.Zero)),
        functionName = Choice1Of2(IdentListNode([ IdentifierOrDot.Ident(SingleTextNode("x", Range.Zero)) ], Range.Zero)),
        genericTypeParameters = None,
        parameters = List.Empty,
        returnType = None,
        equals = SingleTextNode("=", Range.Zero),
        expr = Expr.Constant(Constant.FromText(SingleTextNode("12", Range.Zero))),
        range = Range.Zero
    )
    |> ModuleDecl.TopLevelBinding

let sourceWithEscapeHatch =
    Oak() {
        AnonymousModule() {
            Value("a", Unquoted "11")
            EscapeHatch(topLevelBinding)
        }
    }

Gen.mkOak sourceWithEscapeHatch
|> CodeFormatter.FormatOakAsync
|> Async.RunSynchronously
|> printfn "%s"

// produces the following code:
let a = 11
let private x = 12

Key takeaway

Using Fabulous.AST, you can easily create and manipulate ASTs like this one using F# functions. For example, you can add new nodes to the AST, modify existing nodes, or traverse the AST to perform analysis or transformation tasks.

Fabulous.AST is a powerful tool for anyone who works with code and wants to automate or streamline their development workflow. Whether you're a compiler writer, a code generator, or just someone who wants to write better code faster, Fabulous.AST can help you achieve your goals.

namespace Fantomas
namespace Fantomas.FCS
namespace Fantomas.FCS.Text
namespace Fantomas.Core
module SyntaxOak from Fantomas.Core
val implementationSyntaxTree: Oak
Multiple items
type Oak = inherit NodeBase new: parsedHashDirectives: ParsedHashDirectiveNode list * modulesOrNamespaces: ModuleOrNamespaceNode list * m: range -> Oak override Children: Node array member ModulesOrNamespaces: ModuleOrNamespaceNode list member ParsedHashDirectives: ParsedHashDirectiveNode list

--------------------
new: parsedHashDirectives: ParsedHashDirectiveNode list * modulesOrNamespaces: ModuleOrNamespaceNode list * m: range -> Oak
Multiple items
type ModuleOrNamespaceNode = inherit NodeBase new: header: ModuleOrNamespaceHeaderNode option * decls: ModuleDecl list * range: range -> ModuleOrNamespaceNode override Children: Node array member Declarations: ModuleDecl list member Header: ModuleOrNamespaceHeaderNode option member IsNamed: bool

--------------------
new: header: ModuleOrNamespaceHeaderNode option * decls: ModuleDecl list * range: range -> ModuleOrNamespaceNode
union case Option.None: Option<'T>
Multiple items
type BindingNode = inherit NodeBase new: xmlDoc: XmlDocNode option * attributes: MultipleAttributeListNode option * leadingKeyword: MultipleTextsNode * isMutable: bool * inlineNode: SingleTextNode option * accessibility: SingleTextNode option * functionName: Choice<IdentListNode,Pattern> * genericTypeParameters: TyparDecls option * parameters: Pattern list * returnType: BindingReturnInfoNode option * equals: SingleTextNode * expr: Expr * range: range -> BindingNode member Accessibility: SingleTextNode option member Attributes: MultipleAttributeListNode option override Children: Node array member Equals: SingleTextNode member Expr: Expr member FunctionName: Choice<IdentListNode,Pattern> member GenericTypeParameters: TyparDecls option member Inline: SingleTextNode option ...

--------------------
new: xmlDoc: XmlDocNode option * attributes: MultipleAttributeListNode option * leadingKeyword: MultipleTextsNode * isMutable: bool * inlineNode: SingleTextNode option * accessibility: SingleTextNode option * functionName: Choice<IdentListNode,Pattern> * genericTypeParameters: TyparDecls option * parameters: Pattern list * returnType: BindingReturnInfoNode option * equals: SingleTextNode * expr: Expr * range: range -> BindingNode
Multiple items
type MultipleTextsNode = inherit NodeBase new: content: SingleTextNode list * range: range -> MultipleTextsNode override Children: Node array member Content: SingleTextNode list

--------------------
new: content: SingleTextNode list * range: range -> MultipleTextsNode
Multiple items
type SingleTextNode = inherit NodeBase new: idText: string * range: range -> SingleTextNode override Children: Node array member Text: string

--------------------
new: idText: string * range: range -> SingleTextNode
Multiple items
module Range from Fantomas.FCS.Text

--------------------
[<Struct>] type Range = member End: pos member EndColumn: int member EndLine: int member EndRange: range member FileName: string member IsSynthetic: bool member Start: pos member StartColumn: int member StartLine: int member StartRange: range ...
property Range.Zero: range with get
union case Choice.Choice1Of2: 'T1 -> Choice<'T1,'T2>
Multiple items
type IdentListNode = inherit NodeBase new: content: IdentifierOrDot list * range: range -> IdentListNode override Children: Node array member Content: IdentifierOrDot list member IsEmpty: bool static member Empty: IdentListNode

--------------------
new: content: IdentifierOrDot list * range: range -> IdentListNode
type IdentifierOrDot = | Ident of SingleTextNode | KnownDot of SingleTextNode | UnknownDot member IsIdent: bool member IsKnownDot: bool member IsUnknownDot: bool member Range: range option
union case IdentifierOrDot.Ident: SingleTextNode -> IdentifierOrDot
type Expr = | Lazy of ExprLazyNode | Single of ExprSingleNode | Constant of Constant | Null of SingleTextNode | Quote of ExprQuoteNode | Typed of ExprTypedNode | New of ExprNewNode | Tuple of ExprTupleNode | StructTuple of ExprStructTupleNode | ArrayOrList of ExprArrayOrListNode ... static member Node: x: Expr -> Node member HasParentheses: bool member IsAnonStructRecord: bool member IsApp: bool member IsAppLongIdentAndSingleParenArg: bool member IsAppSingleParenArg: bool member IsAppWithLambda: bool member IsArrayOrList: bool member IsBeginEnd: bool member IsChain: bool ...
union case Expr.Constant: Constant -> Expr
type Constant = | FromText of SingleTextNode | Unit of UnitNode | Measure of ConstantMeasureNode static member Node: c: Constant -> NodeBase member IsFromText: bool member IsMeasure: bool member IsUnit: bool
union case Constant.FromText: SingleTextNode -> Constant
type ModuleDecl = | OpenList of OpenListNode | HashDirectiveList of HashDirectiveListNode | Attributes of ModuleDeclAttributesNode | DeclExpr of Expr | Exception of ExceptionDefnNode | ExternBinding of ExternBindingNode | TopLevelBinding of BindingNode | ModuleAbbrev of ModuleAbbrevNode | NestedModule of NestedModuleNode | TypeDefn of TypeDefn ... static member Node: x: ModuleDecl -> Node member IsAttributes: bool member IsDeclExpr: bool member IsException: bool member IsExternBinding: bool member IsHashDirectiveList: bool member IsModuleAbbrev: bool member IsNestedModule: bool member IsOpenList: bool member IsTopLevelBinding: bool ...
union case ModuleDecl.TopLevelBinding: BindingNode -> ModuleDecl
type CodeFormatter = static member FormatASTAsync: ast: ParsedInput -> Async<string> + 2 overloads static member FormatDocumentAsync: isSignature: bool * source: string -> Async<FormatResult> + 2 overloads static member FormatOakAsync: oak: Oak -> Async<string> + 1 overload static member FormatSelectionAsync: isSignature: bool * source: string * selection: range -> Async<string * range> + 1 overload static member GetVersion: unit -> string static member IsValidFSharpCodeAsync: isSignature: bool * source: string -> Async<bool> static member MakePosition: line: int * column: int -> pos static member MakeRange: fileName: string * startLine: int * startCol: int * endLine: int * endCol: int -> range static member ParseAsync: isSignature: bool * source: string -> Async<(ParsedInput * string list) array> static member ParseOakAsync: isSignature: bool * source: string -> Async<(Oak * string list) array> ...
static member CodeFormatter.FormatOakAsync: oak: Oak -> Async<string>
static member CodeFormatter.FormatOakAsync: oak: Oak * config: FormatConfig -> Async<string>
Multiple items
module Async from Fantomas.Core

--------------------
type Async = static member AsBeginEnd: computation: ('Arg -> Async<'T>) -> ('Arg * AsyncCallback * obj -> IAsyncResult) * (IAsyncResult -> 'T) * (IAsyncResult -> unit) static member AwaitEvent: event: IEvent<'Del,'T> * ?cancelAction: (unit -> unit) -> Async<'T> (requires delegate and 'Del :> Delegate) static member AwaitIAsyncResult: iar: IAsyncResult * ?millisecondsTimeout: int -> Async<bool> static member AwaitTask: task: Task<'T> -> Async<'T> + 1 overload static member AwaitWaitHandle: waitHandle: WaitHandle * ?millisecondsTimeout: int -> Async<bool> static member CancelDefaultToken: unit -> unit static member Catch: computation: Async<'T> -> Async<Choice<'T,exn>> static member Choice: computations: Async<'T option> seq -> Async<'T option> static member FromBeginEnd: beginAction: (AsyncCallback * obj -> IAsyncResult) * endAction: (IAsyncResult -> 'T) * ?cancelAction: (unit -> unit) -> Async<'T> + 3 overloads static member FromContinuations: callback: (('T -> unit) * (exn -> unit) * (OperationCanceledException -> unit) -> unit) -> Async<'T> ...

--------------------
type Async<'T>
static member Async.RunSynchronously: computation: Async<'T> * ?timeout: int * ?cancellationToken: System.Threading.CancellationToken -> 'T
val printfn: format: Printf.TextWriterFormat<'T> -> 'T
val x: int
namespace Fabulous
namespace Fabulous.AST
type Ast = class end
Multiple items
static member Ast.Oak: unit -> CollectionBuilder<Oak,ModuleOrNamespaceNode>

--------------------
module Oak from Fabulous.AST

--------------------
type Oak = inherit NodeBase new: parsedHashDirectives: ParsedHashDirectiveNode list * modulesOrNamespaces: ModuleOrNamespaceNode list * m: range -> Oak override Children: Node array member ModulesOrNamespaces: ModuleOrNamespaceNode list member ParsedHashDirectives: ParsedHashDirectiveNode list

--------------------
new: parsedHashDirectives: ParsedHashDirectiveNode list * modulesOrNamespaces: ModuleOrNamespaceNode list * m: range -> Oak
static member Ast.AnonymousModule: unit -> CollectionBuilder<ModuleOrNamespaceNode,ModuleDecl>
static member Ast.Value: name: string * value: StringVariant -> WidgetBuilder<BindingNode>
static member Ast.Value: name: WidgetBuilder<Pattern> * value: StringVariant -> WidgetBuilder<BindingNode>
static member Ast.Value: name: string * value: WidgetBuilder<Expr> -> WidgetBuilder<BindingNode>
static member Ast.Value: name: WidgetBuilder<Pattern> * value: WidgetBuilder<Expr> -> WidgetBuilder<BindingNode>
union case StringVariant.Unquoted: string -> StringVariant
module Gen from Fabulous.AST
<summary> It takes the root of the widget tree and create the corresponding Fantomas node, and recursively creating all children nodes </summary>
val mkOak: root: WidgetBuilder<'node> -> 'node
val y: int
val topLevelBinding: ModuleDecl
Multiple items
module BindingNode from Fabulous.AST

--------------------
type BindingNode = inherit NodeBase new: xmlDoc: XmlDocNode option * attributes: MultipleAttributeListNode option * leadingKeyword: MultipleTextsNode * isMutable: bool * inlineNode: SingleTextNode option * accessibility: SingleTextNode option * functionName: Choice<IdentListNode,Pattern> * genericTypeParameters: TyparDecls option * parameters: Pattern list * returnType: BindingReturnInfoNode option * equals: SingleTextNode * expr: Expr * range: range -> BindingNode member Accessibility: SingleTextNode option member Attributes: MultipleAttributeListNode option override Children: Node array member Equals: SingleTextNode member Expr: Expr member FunctionName: Choice<IdentListNode,Pattern> member GenericTypeParameters: TyparDecls option member Inline: SingleTextNode option ...

--------------------
new: xmlDoc: XmlDocNode option * attributes: MultipleAttributeListNode option * leadingKeyword: MultipleTextsNode * isMutable: bool * inlineNode: SingleTextNode option * accessibility: SingleTextNode option * functionName: Choice<IdentListNode,Pattern> * genericTypeParameters: TyparDecls option * parameters: Pattern list * returnType: BindingReturnInfoNode option * equals: SingleTextNode * expr: Expr * range: range -> BindingNode
Multiple items
module SingleTextNode from Fabulous.AST
<summary> Contains all the single text nodes that are used in the AST </summary>

--------------------
type SingleTextNode = static member Create: idText: string -> SingleTextNode
union case Option.Some: Value: 'T -> Option<'T>
Multiple items
module List from Fabulous.AST

--------------------
module List from Fantomas.Core

--------------------
module List from Microsoft.FSharp.Collections

--------------------
type List<'T> = | op_Nil | op_ColonColon of Head: 'T * Tail: 'T list interface IReadOnlyList<'T> interface IReadOnlyCollection<'T> interface IEnumerable interface IEnumerable<'T> member GetReverseIndex: rank: int * offset: int -> int member GetSlice: startIndex: int option * endIndex: int option -> 'T list static member Cons: head: 'T * tail: 'T list -> 'T list member Head: 'T member IsEmpty: bool member Item: index: int -> 'T with get ...
property List.Empty: 'T list with get
Multiple items
module Expr from Fabulous.AST

--------------------
type Expr = | Lazy of ExprLazyNode | Single of ExprSingleNode | Constant of Constant | Null of SingleTextNode | Quote of ExprQuoteNode | Typed of ExprTypedNode | New of ExprNewNode | Tuple of ExprTupleNode | StructTuple of ExprStructTupleNode | ArrayOrList of ExprArrayOrListNode ... static member Node: x: Expr -> Node member HasParentheses: bool member IsAnonStructRecord: bool member IsApp: bool member IsAppLongIdentAndSingleParenArg: bool member IsAppSingleParenArg: bool member IsAppWithLambda: bool member IsArrayOrList: bool member IsBeginEnd: bool member IsChain: bool ...
Multiple items
static member Ast.Constant: value: StringVariant -> WidgetBuilder<Constant>

--------------------
module Constant from Fabulous.AST

--------------------
type Constant = | FromText of SingleTextNode | Unit of UnitNode | Measure of ConstantMeasureNode static member Node: c: Constant -> NodeBase member IsFromText: bool member IsMeasure: bool member IsUnit: bool
type range = Range
val sourceWithEscapeHatch: WidgetBuilder<Oak>
Multiple items
static member Ast.EscapeHatch: node: 'T -> WidgetBuilder<'T>

--------------------
module EscapeHatch from Fabulous.AST

Type something to start searching.