Skip to content

Some banter #62

@nojaf

Description

@nojaf

Hi Patrick, thanks again for the demo.

Some stuff, that came to mind (in random order):

  • The untyped tree is used to represent both valid and invalid code. That is why certain trivia will be represented as optional.
let a

still gets parsed to a tree with errors:

trivia = {
    LeadingKeyword = SynLeadingKeyword.Let(R("(1,0--1,3)"))
    InlineKeyword = None
    EqualsRange = None
}

Incomplete structured construct at or before this point in binding. Expected '=' or other token.

We don't process any tree with errors (and certain warnings), so we can assume in ASTTransformer that stuff is just there. This means that you need to put in Some range0 in certain places. Just to tell Fantomas that the keyword is present.

We transform the ParsedInput to an Oak internally, to only print out the code again.
It is possible to print source code from an Oak, this is more convenient than the untyped tree.
I hoped that one day https://edgarfgp.github.io/Fabulous.AST/ would have taken off and become the recommended way to generate code.

  • I think as a rule of thumb, every token or keyword that you want to see in the output code should be present (as Some range0). So, when generating a let binding, you know that let and = should be in the tree. The same with commas in tuples, = in record fields. These days, there are few tokens not represented in the tree.

  • Myriad seems like a bit of a mixed bag. I think it isn't that hard to cook up some tool that is similar to how the analyzers work. Crack a fsproj, get type-check information of a file and use Fantomas to generate a new file.

Did you ever explore other options?

  • It is ok to take some shortcuts when generating code. For example:
#r "nuget: Fantomas.Core, 6.3.0-alpha-005"

open Fantomas.FCS.Syntax

let cheatABit (e: SynExpr) : SynBinding =
    let source = "let a b = ()"

    let parsedInput =
        CodeFormatter.ParseAsync(false, source)
        |> Async.RunSynchronously
        |> Array.head
        |> fst

    match parsedInput with
    | ParsedInput.ImplFile(ParsedImplFileInput(
        contents = [ SynModuleOrNamespace(decls = [ SynModuleDecl.Let(bindings = [ b ]) ]) ])) ->
        match b with
        | SynBinding(accessibility,
                     kind,
                     isInline,
                     isMutable,
                     attributes,
                     xmlDoc,
                     valData,
                     headPat,
                     returnInfo,
                     _,
                     range,
                     debugPoint,
                     trivia) ->
            SynBinding(
                accessibility,
                kind,
                isInline,
                isMutable,
                attributes,
                xmlDoc,
                valData,
                headPat,
                returnInfo,
                e,
                range,
                debugPoint,
                trivia
            )
    | _ -> failwith "unexpected"

Parsing small strings to grab the AST nodes is a trick I do in Telplin from time to time.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions