ionide / tree-sitter-fsharp

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

`for` expression is parsed like `do` expression #17

Closed vzarytovskii closed 1 year ago

vzarytovskii commented 1 year ago

Two issues actually, the following expression:

let valInfosForFslib = 
    for vref in entries do 
        if somePredicate vkey then 
            failwithf "Something went wrong %A" vkey
        someDictionary.Add(vkey, "foo")

will be parsed into:

(value_declaration_left [0, 4] - [0, 20]
    (identifier_pattern [0, 4] - [0, 20]
      (long_identifier [0, 4] - [0, 20]
        (identifier [0, 4] - [0, 20]))))
  (identifier_pattern [1, 8] - [1, 12]
    (long_identifier [1, 8] - [1, 12]
      (identifier [1, 8] - [1, 12])))
  (long_identifier_or_op [1, 16] - [1, 23]
    (long_identifier [1, 16] - [1, 23]
      (identifier [1, 16] - [1, 23])))
  (do_expression [1, 24] - [4, 39]
    (if_expression [2, 8] - [4, 39]
      guard: (application_expression [2, 11] - [2, 29]
        (long_identifier_or_op [2, 11] - [2, 24]
          (long_identifier [2, 11] - [2, 24]
            (identifier [2, 11] - [2, 24])))
        (long_identifier_or_op [2, 25] - [2, 29]
          (long_identifier [2, 25] - [2, 29]
            (identifier [2, 25] - [2, 29]))))
      then: (application_expression [3, 12] - [4, 39]
        (long_identifier_or_op [3, 12] - [3, 21]
          (long_identifier [3, 12] - [3, 21]
            (identifier [3, 12] - [3, 21])))
        (application_expression [3, 22] - [4, 39]
          (const [3, 22] - [3, 47]
            (string [3, 22] - [3, 47]))
          (call_expression [3, 48] - [4, 39]
            (dot_expression [3, 48] - [4, 26]
              base: (long_identifier_or_op [3, 48] - [3, 52]
                (long_identifier [3, 48] - [3, 52]
                  (identifier [3, 48] - [3, 52])))
              (ERROR [3, 52] - [4, 22]
                (ERROR [4, 8] - [4, 10])
                (ERROR [4, 13] - [4, 14])
                (ERROR [4, 15] - [4, 18])
                (ERROR [4, 20] - [4, 21]))
              field: (long_identifier_or_op [4, 23] - [4, 26]
                (long_identifier [4, 23] - [4, 26]
                  (identifier [4, 23] - [4, 26]))))
            (tuple_expression [4, 27] - [4, 38]
              (long_identifier_or_op [4, 27] - [4, 31]
                (long_identifier [4, 27] - [4, 31]
                  (identifier [4, 27] - [4, 31])))
              (const [4, 33] - [4, 38]
                (string [4, 33] - [4, 38]))))))))

Notice that it was parsed into bunch of identifiers, patters and do_expression, in this case for_expression should take priority. Second issue is that expressions inside the, after the condition resulting in multiple errors (pointing to someDictionary.Add).

vzarytovskii commented 1 year ago

a simpler example:

let main _ = 
    for i in is do 
        ignore i
Eliemer commented 1 year ago

to add to this. a bare for or while loop will error

while true do
    ()

Output

(ERROR [0, 0] - [2, 0]
  (ERROR [0, 11] - [0, 13])) ; this is the `do`

Debug log

new_parse
process version:0, version_count:1, state:1, row:0, col:0
lex_internal state:889, row:0, column:0
  consume character:'w'
  consume character:'h'
lex_external state:1, row:0, column:0
lex_internal state:0, row:0, column:0
  consume character:'w'
  consume character:'h'
  consume character:'i'
  consume character:'l'
  consume character:'e'
lexed_lookahead sym:while, size:5
detect_error
resume version:0
skip_token symbol:while
process version:0, version_count:1, state:0, row:0, col:5
lex_external state:1, row:0, column:5
lex_internal state:0, row:0, column:5
  skip character:' '
  consume character:'t'
  consume character:'r'
  consume character:'u'
  consume character:'e'
lexed_lookahead sym:true, size:5
skip_token symbol:true
process version:0, version_count:1, state:0, row:0, col:10
lex_external state:1, row:0, column:10
lex_internal state:0, row:0, column:10
  skip character:' '
skip_unrecognized_character
  consume character:'d'
lex_external state:1, row:0, column:12
lex_internal state:0, row:0, column:12
  consume character:'o'
lex_external state:1, row:0, column:13
lex_internal state:0, row:0, column:13
  skip character:10
  skip character:' '
  skip character:' '
  skip character:' '
  skip character:' '
  consume character:'('
lexed_lookahead sym:ERROR, size:3
skip_token symbol:ERROR
process version:0, version_count:1, state:0, row:0, col:13
lex_external state:1, row:0, column:13
lex_internal state:0, row:0, column:13
  skip character:10
  skip character:' '
  skip character:' '
  skip character:' '
  skip character:' '
  consume character:'('
lexed_lookahead sym:(, size:6
skip_token symbol:(
process version:0, version_count:1, state:0, row:1, col:5
lex_external state:1, row:1, column:5
lex_internal state:0, row:1, column:5
  consume character:')'
lexed_lookahead sym:), size:1
skip_token symbol:)
process version:0, version_count:1, state:0, row:1, col:6
lex_external state:1, row:1, column:6
lex_internal state:0, row:1, column:6
  skip character:10
lexed_lookahead sym:end, size:1
recover_eof
done
Nsidorenco commented 1 year ago

I'll have a jab at fixing this one.

@Eliemer the grammar does not allow for top level expressions, but your example should work if you wrap it in a let or do expression

Nsidorenco commented 1 year ago

This issue is compounded of two things: 1) for expressions failed to parse in any form 2) The particular body of this for expression also fails to parse in isolation.

I'll continue the issue of 2 in #19