ionide / tree-sitter-fsharp

F# grammar for treesitter
MIT License
81 stars 18 forks source link

feat: compiler directives #58

Closed Nsidorenco closed 4 months ago

Nsidorenco commented 4 months ago

Working PR for adding #if compiler directive support.

Challenges with this feature is dealing with the indentation stack inside the directive block, i.e

type A() =
// new scope for type decl is entered
#if A
  member _.F() = () // new scope for method body. Scope ends after the unit
#endif
// scope for type decl ends

In cases like this, we need a dedent token to make the member definition parse, and we need a dedent token to terminate the type declaration. However, the last like of the file is #endif, which does not expect a dedent token, so we end up wanting to:

  1. read #endif in external scanner and push dedent token
  2. read #endif in external scanner and do nothing.
  3. let internal scanner parse the #endif token.
  4. parse EOF as dedent token in external scanner.

The problem is there is no good way to determine how many dedent tokens should be pushed in step 1

Nsidorenco commented 4 months ago

To solve this, more complexity has been moved to the external scanner. The external scanner now keeps a stack of indentations levels where the directive was started. To end a directive scope we then pop dedent tokens until the indentation stack matches the level on the directive stack